Skip to content

Commit d46a080

Browse files
committed
Rework model_name in derived resources
Fixes an issue where the recreated relationships might be using the wrong model_name if the model_name is changed in a resource that is derived from a non abstract resource, such as done in many of the tests. Breaking change: Derived resources now use the model name of their base resource, if it is not abstract. Cleans up the tests to not have the warnings about missing models. Fixes issue in _model_class related to #952, and renames @model (class level) to @model_class to avoid confusion with instance level @model Fixes #952, #654 (cherry picked from commit 280ccea)
1 parent f272f80 commit d46a080

4 files changed

Lines changed: 72 additions & 43 deletions

File tree

lib/jsonapi/resource.rb

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -416,18 +416,15 @@ def inherited(subclass)
416416
subclass.immutable(false)
417417
subclass.caching(false)
418418
subclass._attributes = (_attributes || {}).dup
419+
419420
subclass._model_hints = (_model_hints || {}).dup
420421

421-
subclass._relationships = {}
422-
# Add the relationships from the base class to the subclass using the original options
423-
if _relationships.is_a?(Hash)
424-
_relationships.each_value do |relationship|
425-
options = relationship.options.dup
426-
options[:parent_resource] = subclass
427-
subclass._add_relationship(relationship.class, relationship.name, options)
428-
end
422+
unless _model_name.empty?
423+
subclass.model_name(_model_name, add_model_hint: (_model_hints && !_model_hints[_model_name].nil?) == true)
429424
end
430425

426+
subclass.rebuild_relationships(_relationships || {})
427+
431428
subclass._allowed_filters = (_allowed_filters || Set.new).dup
432429

433430
type = subclass.name.demodulize.sub(/Resource$/, '').underscore
@@ -438,6 +435,20 @@ def inherited(subclass)
438435
check_reserved_resource_name(subclass._type, subclass.name)
439436
end
440437

438+
def rebuild_relationships(relationships)
439+
original_relationships = relationships.deep_dup
440+
441+
@_relationships = {}
442+
443+
if original_relationships.is_a?(Hash)
444+
original_relationships.each_value do |relationship|
445+
options = relationship.options.dup
446+
options[:parent_resource] = self
447+
_add_relationship(relationship.class, relationship.name, options)
448+
end
449+
end
450+
end
451+
441452
def resource_for(type)
442453
type = type.underscore
443454
type_with_module = type.include?('/') ? type : module_path + type
@@ -554,6 +565,8 @@ def model_name(model, options = {})
554565
@_model_name = model.to_sym
555566

556567
model_hint(model: @_model_name, resource: self) unless options[:add_model_hint] == false
568+
569+
rebuild_relationships(_relationships)
557570
end
558571

559572
def model_hint(model: _model_name, resource: _type)
@@ -900,10 +913,11 @@ def _model_name
900913
if _abstract
901914
return ''
902915
else
903-
return @_model_name if defined?(@_model_name)
916+
return @_model_name.to_s if defined?(@_model_name)
904917
class_name = self.name
905918
return '' if class_name.nil?
906-
return @_model_name = class_name.demodulize.sub(/Resource$/, '')
919+
@_model_name = class_name.demodulize.sub(/Resource$/, '')
920+
return @_model_name.to_s
907921
end
908922
end
909923

@@ -974,11 +988,17 @@ def attribute_caching_context(context)
974988
def _model_class
975989
return nil if _abstract
976990

977-
return @model if defined?(@model)
978-
return nil if self.name.to_s.blank? && _model_name.to_s.blank?
979-
@model = _model_name.to_s.safe_constantize
980-
warn "[MODEL NOT FOUND] Model could not be found for #{self.name}. If this a base Resource declare it as abstract." if @model.nil?
981-
@model
991+
return @model_class if @model_class
992+
993+
model_name = _model_name
994+
return nil if model_name.to_s.blank?
995+
996+
@model_class = model_name.to_s.safe_constantize
997+
if @model_class.nil?
998+
warn "[MODEL NOT FOUND] Model could not be found for #{self.name}. If this a base Resource declare it as abstract."
999+
end
1000+
1001+
@model_class
9821002
end
9831003

9841004
def _allowed_filter?(filter)

test/fixtures/active_record.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,7 @@ class CompanyResource < JSONAPI::Resource
961961
end
962962

963963
class FirmResource < CompanyResource
964+
model_name "Firm"
964965
end
965966

966967
class TagResource < JSONAPI::Resource

test/unit/jsonapi_request/jsonapi_request_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class CatResource < JSONAPI::Resource
44
attribute :name
55
attribute :breed
66

7-
belongs_to :mother, class_name: 'Cat'
7+
has_one :mother, class_name: 'Cat'
88
has_one :father, class_name: 'Cat'
99

1010
filters :name

test/unit/resource/resource_test.rb

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ class NoMatchAbstractResource < JSONAPI::Resource
4747
abstract
4848
end
4949

50-
class CatResource < JSONAPI::Resource
50+
class FelineResource < JSONAPI::Resource
51+
model_name 'Cat'
52+
5153
attribute :name
5254
attribute :breed
5355
attribute :kind, :delegate => :breed
@@ -98,6 +100,12 @@ class RelatedResource < MyModule::RelatedResource
98100
end
99101
end
100102

103+
class PostWithReadonlyAttributesResource < JSONAPI::Resource
104+
model_name 'Post'
105+
attribute :title, readonly: true
106+
has_one :author, readonly: true
107+
end
108+
101109
class ResourceTest < ActiveSupport::TestCase
102110
def setup
103111
@post = Post.first
@@ -175,7 +183,7 @@ def test_derived_not_abstract
175183
def test_nil_model_class
176184
# ToDo:Figure out why this test does not work on Rails 4.0
177185
# :nocov:
178-
if Rails::VERSION::MAJOR >= 4 && Rails::VERSION::MINOR >= 1
186+
if (Rails::VERSION::MAJOR >= 4 && Rails::VERSION::MINOR >= 1) || (Rails::VERSION::MAJOR >= 5)
179187
assert_output nil, "[MODEL NOT FOUND] Model could not be found for NoMatchResource. If this a base Resource declare it as abstract.\n" do
180188
assert_nil NoMatchResource._model_class
181189
end
@@ -194,13 +202,13 @@ def test_model_alternate
194202
end
195203

196204
def test_class_attributes
197-
attrs = CatResource._attributes
205+
attrs = FelineResource._attributes
198206
assert_kind_of(Hash, attrs)
199207
assert_equal(attrs.keys.size, 4)
200208
end
201209

202210
def test_class_relationships
203-
relationships = CatResource._relationships
211+
relationships = FelineResource._relationships
204212
assert_kind_of(Hash, relationships)
205213
assert_equal(relationships.size, 2)
206214
end
@@ -214,16 +222,16 @@ def test_replace_polymorphic_to_one_link
214222
end
215223

216224
def test_duplicate_relationship_name
217-
assert_output nil, "[DUPLICATE RELATIONSHIP] `mother` has already been defined in CatResource.\n" do
218-
CatResource.instance_eval do
225+
assert_output nil, "[DUPLICATE RELATIONSHIP] `mother` has already been defined in FelineResource.\n" do
226+
FelineResource.instance_eval do
219227
has_one :mother, class_name: 'Cat'
220228
end
221229
end
222230
end
223231

224232
def test_duplicate_attribute_name
225-
assert_output nil, "[DUPLICATE ATTRIBUTE] `name` has already been defined in CatResource.\n" do
226-
CatResource.instance_eval do
233+
assert_output nil, "[DUPLICATE ATTRIBUTE] `name` has already been defined in FelineResource.\n" do
234+
FelineResource.instance_eval do
227235
attribute :name
228236
end
229237
end
@@ -294,7 +302,7 @@ def test_find_by_key_with_customized_base_records
294302
end
295303

296304
def test_updatable_fields_does_not_include_id
297-
assert(!CatResource.updatable_fields.include?(:id))
305+
assert(!FelineResource.updatable_fields.include?(:id))
298306
end
299307

300308
def test_filter_on_to_many_relationship_id
@@ -438,60 +446,60 @@ def apply_pagination(records, criteria, order_options)
438446
end
439447

440448
def test_key_type_integer
441-
CatResource.instance_eval do
449+
FelineResource.instance_eval do
442450
key_type :integer
443451
end
444452

445-
assert CatResource.verify_key('45')
446-
assert CatResource.verify_key(45)
453+
assert FelineResource.verify_key('45')
454+
assert FelineResource.verify_key(45)
447455

448456
assert_raises JSONAPI::Exceptions::InvalidFieldValue do
449-
CatResource.verify_key('45,345')
457+
FelineResource.verify_key('45,345')
450458
end
451459

452460
ensure
453-
CatResource.instance_eval do
461+
FelineResource.instance_eval do
454462
key_type nil
455463
end
456464
end
457465

458466
def test_key_type_string
459-
CatResource.instance_eval do
467+
FelineResource.instance_eval do
460468
key_type :string
461469
end
462470

463-
assert CatResource.verify_key('45')
464-
assert CatResource.verify_key(45)
471+
assert FelineResource.verify_key('45')
472+
assert FelineResource.verify_key(45)
465473

466474
assert_raises JSONAPI::Exceptions::InvalidFieldValue do
467-
CatResource.verify_key('45,345')
475+
FelineResource.verify_key('45,345')
468476
end
469477

470478
ensure
471-
CatResource.instance_eval do
479+
FelineResource.instance_eval do
472480
key_type nil
473481
end
474482
end
475483

476484
def test_key_type_uuid
477-
CatResource.instance_eval do
485+
FelineResource.instance_eval do
478486
key_type :uuid
479487
end
480488

481-
assert CatResource.verify_key('f1a4d5f2-e77a-4d0a-acbb-ee0b98b3f6b5')
489+
assert FelineResource.verify_key('f1a4d5f2-e77a-4d0a-acbb-ee0b98b3f6b5')
482490

483491
assert_raises JSONAPI::Exceptions::InvalidFieldValue do
484-
CatResource.verify_key('f1a-e77a-4d0a-acbb-ee0b98b3f6b5')
492+
FelineResource.verify_key('f1a-e77a-4d0a-acbb-ee0b98b3f6b5')
485493
end
486494

487495
ensure
488-
CatResource.instance_eval do
496+
FelineResource.instance_eval do
489497
key_type nil
490498
end
491499
end
492500

493501
def test_key_type_proc
494-
CatResource.instance_eval do
502+
FelineResource.instance_eval do
495503
key_type -> (key, context) {
496504
return key if key.nil?
497505
if key.to_s.match(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/)
@@ -502,14 +510,14 @@ def test_key_type_proc
502510
}
503511
end
504512

505-
assert CatResource.verify_key('f1a4d5f2-e77a-4d0a-acbb-ee0b98b3f6b5')
513+
assert FelineResource.verify_key('f1a4d5f2-e77a-4d0a-acbb-ee0b98b3f6b5')
506514

507515
assert_raises JSONAPI::Exceptions::InvalidFieldValue do
508-
CatResource.verify_key('f1a-e77a-4d0a-acbb-ee0b98b3f6b5')
516+
FelineResource.verify_key('f1a-e77a-4d0a-acbb-ee0b98b3f6b5')
509517
end
510518

511519
ensure
512-
CatResource.instance_eval do
520+
FelineResource.instance_eval do
513521
key_type nil
514522
end
515523
end

0 commit comments

Comments
 (0)