Skip to content

Commit 83abe84

Browse files
mmartinsonlgebhardt
authored andcommitted
Refactor Resource._add_relationships into helper class
1 parent f672d51 commit 83abe84

2 files changed

Lines changed: 160 additions & 114 deletions

File tree

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
require 'pry'
2+
3+
module JSONAPI
4+
class RelationshipBuilder
5+
attr_reader :_model_class, :options
6+
7+
class DefinitionProxy
8+
def initialize(target)
9+
@target = target
10+
end
11+
12+
def define_method(name, &body)
13+
@target.inject_class_method(name, body)
14+
end
15+
16+
def method_defined?(name)
17+
@target.method_defined?(name)
18+
end
19+
end
20+
21+
def initialize(relationship_class, options, model_class, resource_relationships, resource_klass)
22+
@relationship_class = relationship_class
23+
@options = options
24+
@_model_class = model_class
25+
@_relationships = resource_relationships
26+
@resource_klass = resource_klass
27+
end
28+
29+
#TODO change reference in code once refactor is complete
30+
def klass
31+
@relationship_class
32+
end
33+
34+
def target_resource
35+
@proxy ||= DefinitionProxy.new(@resource_klass)
36+
end
37+
38+
def build_relationship(attrs)
39+
attrs.each do |attr|
40+
relationship_name = attr.to_sym
41+
42+
@resource_klass.check_reserved_relationship_name(relationship_name)
43+
44+
# Initialize from an ActiveRecord model's properties
45+
if _model_class && _model_class.ancestors.collect{|ancestor| ancestor.name}.include?('ActiveRecord::Base')
46+
model_association = _model_class.reflect_on_association(relationship_name)
47+
if model_association
48+
options[:class_name] ||= model_association.class_name
49+
end
50+
end
51+
52+
@_relationships[relationship_name] = relationship = klass.new(relationship_name, options)
53+
54+
associated_records_method_name = case relationship
55+
when JSONAPI::Relationship::ToOne then "record_for_#{relationship_name}"
56+
when JSONAPI::Relationship::ToMany then "records_for_#{relationship_name}"
57+
end
58+
59+
foreign_key = relationship.foreign_key
60+
61+
target_resource.define_method "#{foreign_key}=" do |value|
62+
@model.method("#{foreign_key}=").call(value)
63+
end unless target_resource.method_defined?("#{foreign_key}=")
64+
65+
target_resource.define_method associated_records_method_name do
66+
relationship = self.class._relationships[relationship_name]
67+
relation_name = relationship.relation_name(context: @context)
68+
records_for(relation_name)
69+
end unless target_resource.method_defined?(associated_records_method_name)
70+
71+
if relationship.is_a?(JSONAPI::Relationship::ToOne)
72+
if relationship.belongs_to?
73+
target_resource.define_method foreign_key do
74+
@model.method(foreign_key).call
75+
end unless target_resource.method_defined?(foreign_key)
76+
77+
target_resource.define_method relationship_name do |options = {}|
78+
relationship = self.class._relationships[relationship_name]
79+
80+
if relationship.polymorphic?
81+
associated_model = public_send(associated_records_method_name)
82+
resource_klass = self.class.resource_for_model(associated_model) if associated_model
83+
return resource_klass.new(associated_model, @context) if resource_klass
84+
else
85+
resource_klass = relationship.resource_klass
86+
if resource_klass
87+
associated_model = public_send(associated_records_method_name)
88+
return associated_model ? resource_klass.new(associated_model, @context) : nil
89+
end
90+
end
91+
end unless target_resource.method_defined?(relationship_name)
92+
else
93+
target_resource.define_method foreign_key do
94+
relationship = self.class._relationships[relationship_name]
95+
96+
record = public_send(associated_records_method_name)
97+
return nil if record.nil?
98+
record.public_send(relationship.resource_klass._primary_key)
99+
end unless target_resource.method_defined?(foreign_key)
100+
101+
target_resource.define_method relationship_name do |options = {}|
102+
relationship = self.class._relationships[relationship_name]
103+
104+
resource_klass = relationship.resource_klass
105+
if resource_klass
106+
associated_model = public_send(associated_records_method_name)
107+
return associated_model ? resource_klass.new(associated_model, @context) : nil
108+
end
109+
end unless target_resource.method_defined?(relationship_name)
110+
end
111+
elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
112+
target_resource.define_method foreign_key do
113+
records = public_send(associated_records_method_name)
114+
return records.collect do |record|
115+
record.public_send(relationship.resource_klass._primary_key)
116+
end
117+
end unless target_resource.method_defined?(foreign_key)
118+
119+
target_resource.define_method relationship_name do |options = {}|
120+
relationship = self.class._relationships[relationship_name]
121+
122+
resource_klass = relationship.resource_klass
123+
records = public_send(associated_records_method_name)
124+
125+
filters = options.fetch(:filters, {})
126+
unless filters.nil? || filters.empty?
127+
records = resource_klass.apply_filters(records, filters, options)
128+
end
129+
130+
sort_criteria = options.fetch(:sort_criteria, {})
131+
unless sort_criteria.nil? || sort_criteria.empty?
132+
order_options = relationship.resource_klass.construct_order_options(sort_criteria)
133+
records = resource_klass.apply_sort(records, order_options, @context)
134+
end
135+
136+
paginator = options[:paginator]
137+
if paginator
138+
records = resource_klass.apply_pagination(records, paginator, order_options)
139+
end
140+
141+
return records.collect do |record|
142+
if relationship.polymorphic?
143+
resource_klass = self.class.resource_for_model(record)
144+
end
145+
resource_klass.new(record, @context)
146+
end
147+
end unless target_resource.method_defined?(relationship_name)
148+
end
149+
end
150+
end
151+
end
152+
end

lib/jsonapi/resource.rb

Lines changed: 8 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require 'jsonapi/callbacks'
2+
require 'jsonapi/relationship_builder'
23

34
module JSONAPI
45
class Resource
@@ -517,7 +518,7 @@ def relationship(*attrs)
517518
def has_one(*attrs)
518519
_add_relationship(Relationship::ToOne, *attrs)
519520
end
520-
521+
521522
def belongs_to(*attrs)
522523
ActiveSupport::Deprecation.warn "In #{name} you exposed a `has_one` relationship "\
523524
" using the `belongs_to` class method. We think `has_one`" \
@@ -967,122 +968,15 @@ def _add_relationship(klass, *attrs)
967968
options = attrs.extract_options!
968969
options[:parent_resource] = self
969970

970-
attrs.each do |attr|
971-
relationship_name = attr.to_sym
972-
973-
check_reserved_relationship_name(relationship_name)
974-
975-
check_duplicate_relationship_name(relationship_name)
971+
JSONAPI::RelationshipBuilder.new(klass, options, _model_class, @_relationships, self)
972+
.build_relationship(attrs)
976973

977-
# Initialize from an ActiveRecord model's properties
978-
if _model_class && _model_class.ancestors.collect{|ancestor| ancestor.name}.include?('ActiveRecord::Base')
979-
model_association = _model_class.reflect_on_association(relationship_name)
980-
if model_association
981-
options[:class_name] ||= model_association.class_name
982-
end
983-
end
984-
985-
@_relationships[relationship_name] = relationship = klass.new(relationship_name, options)
986-
987-
associated_records_method_name = case relationship
988-
when JSONAPI::Relationship::ToOne then "record_for_#{relationship_name}"
989-
when JSONAPI::Relationship::ToMany then "records_for_#{relationship_name}"
990-
end
991-
992-
foreign_key = relationship.foreign_key
993-
994-
define_method "#{foreign_key}=" do |value|
995-
@model.method("#{foreign_key}=").call(value)
996-
end unless method_defined?("#{foreign_key}=")
997-
998-
define_method associated_records_method_name do
999-
relationship = self.class._relationships[relationship_name]
1000-
relation_name = relationship.relation_name(context: @context)
1001-
records_for(relation_name)
1002-
end unless method_defined?(associated_records_method_name)
1003-
1004-
if relationship.is_a?(JSONAPI::Relationship::ToOne)
1005-
if relationship.belongs_to?
1006-
define_method foreign_key do
1007-
@model.method(foreign_key).call
1008-
end unless method_defined?(foreign_key)
1009-
1010-
define_method relationship_name do |options = {}|
1011-
relationship = self.class._relationships[relationship_name]
1012-
1013-
if relationship.polymorphic?
1014-
associated_model = public_send(associated_records_method_name)
1015-
resource_klass = self.class.resource_for_model(associated_model) if associated_model
1016-
return resource_klass.new(associated_model, @context) if resource_klass
1017-
else
1018-
resource_klass = relationship.resource_klass
1019-
if resource_klass
1020-
associated_model = public_send(associated_records_method_name)
1021-
return associated_model ? resource_klass.new(associated_model, @context) : nil
1022-
end
1023-
end
1024-
end unless method_defined?(relationship_name)
1025-
else
1026-
define_method foreign_key do
1027-
relationship = self.class._relationships[relationship_name]
1028-
1029-
record = public_send(associated_records_method_name)
1030-
return nil if record.nil?
1031-
record.public_send(relationship.resource_klass._primary_key)
1032-
end unless method_defined?(foreign_key)
1033-
1034-
define_method relationship_name do |options = {}|
1035-
relationship = self.class._relationships[relationship_name]
1036-
1037-
resource_klass = relationship.resource_klass
1038-
if resource_klass
1039-
associated_model = public_send(associated_records_method_name)
1040-
return associated_model ? resource_klass.new(associated_model, @context) : nil
1041-
end
1042-
end unless method_defined?(relationship_name)
1043-
end
1044-
elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
1045-
define_method foreign_key do
1046-
records = public_send(associated_records_method_name)
1047-
return records.collect do |record|
1048-
record.public_send(relationship.resource_klass._primary_key)
1049-
end
1050-
end unless method_defined?(foreign_key)
1051-
1052-
define_method relationship_name do |options = {}|
1053-
relationship = self.class._relationships[relationship_name]
1054-
1055-
resource_klass = relationship.resource_klass
1056-
records = public_send(associated_records_method_name)
1057-
1058-
filters = options.fetch(:filters, {})
1059-
unless filters.nil? || filters.empty?
1060-
records = resource_klass.apply_filters(records, filters, options)
1061-
end
1062-
1063-
sort_criteria = options.fetch(:sort_criteria, {})
1064-
unless sort_criteria.nil? || sort_criteria.empty?
1065-
order_options = relationship.resource_klass.construct_order_options(sort_criteria)
1066-
records = resource_klass.apply_sort(records, order_options, @context)
1067-
end
1068-
1069-
paginator = options[:paginator]
1070-
if paginator
1071-
records = resource_klass.apply_pagination(records, paginator, order_options)
1072-
end
1073-
1074-
return records.collect do |record|
1075-
if relationship.polymorphic?
1076-
resource_klass = self.class.resource_for_model(record)
1077-
end
1078-
resource_klass.new(record, @context)
1079-
end
1080-
end unless method_defined?(relationship_name)
1081-
end
1082-
end
1083974
end
1084975

1085-
private
976+
#added
977+
def inject_class_method(name, body)
978+
define_method(name, body)
979+
end
1086980

1087981
def check_reserved_resource_name(type, name)
1088982
if [:ids, :types, :hrefs, :links].include?(type)

0 commit comments

Comments
 (0)