@@ -9,7 +9,13 @@ def initialize(serializer, options = {})
9
9
super
10
10
@hash = { data : [ ] }
11
11
12
- if fields = options . delete ( :fields )
12
+ @options [ :include ] ||= [ ]
13
+ if @options [ :include ] . is_a? ( String )
14
+ @options [ :include ] = @options [ :include ] . split ( ',' )
15
+ end
16
+
17
+ fields = options . delete ( :fields )
18
+ if fields
13
19
@fieldset = ActiveModel ::Serializer ::Fieldset . new ( fields , serializer . json_key )
14
20
else
15
21
@fieldset = options [ :fieldset ]
@@ -31,150 +37,136 @@ def serializable_hash(options = nil)
31
37
32
38
add_links ( options )
33
39
else
34
- @hash [ :data ] = attributes_for_serializer ( serializer , options )
35
- add_resource_relationships ( @hash [ :data ] , serializer )
40
+ primary_data = primary_data_for ( serializer , options )
41
+ relationships = relationships_for ( serializer )
42
+ included = included_for ( serializer )
43
+ @hash [ :data ] = primary_data
44
+ @hash [ :data ] [ :relationships ] = relationships if relationships . any?
45
+ @hash [ :included ] = included if included . any?
36
46
end
37
47
@hash
38
48
end
39
49
40
50
def fragment_cache ( cached_hash , non_cached_hash )
41
51
root = false if @options . include? ( :include )
42
- JsonApi ::FragmentCache . new ( ) . fragment_cache ( root , cached_hash , non_cached_hash )
52
+ JsonApi ::FragmentCache . new . fragment_cache ( root , cached_hash , non_cached_hash )
43
53
end
44
54
45
55
private
46
56
47
- def add_relationships ( resource , name , serializers )
48
- resource [ :relationships ] ||= { }
49
- resource [ :relationships ] [ name ] ||= { data : [ ] }
50
- resource [ :relationships ] [ name ] [ :data ] += serializers . map { |serializer | { type : serializer . json_api_type , id : serializer . id . to_s } }
57
+ def resource_identifier_type_for ( serializer )
58
+ if ActiveModel ::Serializer . config . jsonapi_resource_type == :singular
59
+ serializer . object . class . model_name . singular
60
+ else
61
+ serializer . object . class . model_name . plural
62
+ end
51
63
end
52
64
53
- def add_relationship ( resource , name , serializer , val = nil )
54
- resource [ :relationships ] ||= { }
55
- resource [ :relationships ] [ name ] = { data : val }
56
-
57
- if serializer && serializer . object
58
- resource [ :relationships ] [ name ] [ :data ] = { type : serializer . json_api_type , id : serializer . id . to_s }
65
+ def resource_identifier_id_for ( serializer )
66
+ if serializer . respond_to? ( :id )
67
+ serializer . id
68
+ else
69
+ serializer . object . id
59
70
end
60
71
end
61
72
62
- def add_included ( resource_name , serializers , parent = nil )
63
- unless serializers . respond_to? ( :each )
64
- return unless serializers . object
65
- serializers = Array ( serializers )
66
- end
67
- resource_path = [ parent , resource_name ] . compact . join ( '.' )
68
- if include_assoc? ( resource_path )
69
- @hash [ :included ] ||= [ ]
73
+ def resource_identifier_for ( serializer )
74
+ type = resource_identifier_type_for ( serializer )
75
+ id = resource_identifier_id_for ( serializer )
70
76
71
- serializers . each do | serializer |
72
- attrs = attributes_for_serializer ( serializer , @options )
77
+ { id : id . to_s , type : type }
78
+ end
73
79
74
- add_resource_relationships ( attrs , serializer , add_included : false )
80
+ def resource_object_for ( serializer , options = { } )
81
+ options [ :fields ] = @fieldset && @fieldset . fields_for ( serializer )
75
82
76
- @hash [ :included ] . push ( attrs ) unless @hash [ :included ] . include? ( attrs )
77
- end
83
+ cache_check ( serializer ) do
84
+ result = resource_identifier_for ( serializer )
85
+ attributes = serializer . attributes ( options ) . except ( :id )
86
+ result [ :attributes ] = attributes if attributes . any?
87
+ result
78
88
end
89
+ end
79
90
80
- serializers . each do |serializer |
81
- serializer . associations . each do |association |
82
- serializer = association . serializer
83
-
84
- add_included ( association . key , serializer , resource_path ) if serializer
85
- end if include_nested_assoc? resource_path
91
+ def primary_data_for ( serializer , options )
92
+ if serializer . respond_to? ( :each )
93
+ serializer . map { |s | resource_object_for ( s , options ) }
94
+ else
95
+ resource_object_for ( serializer , options )
86
96
end
87
97
end
88
98
89
- def attributes_for_serializer ( serializer , options )
99
+ def relationship_value_for ( serializer , options = { } )
90
100
if serializer . respond_to? ( :each )
91
- result = [ ]
92
- serializer . each do |object |
93
- result << resource_object_for ( object , options )
94
- end
101
+ serializer . map { |s | resource_identifier_for ( s ) }
95
102
else
96
- result = resource_object_for ( serializer , options )
103
+ if options [ :virtual_value ]
104
+ options [ :virtual_value ]
105
+ elsif serializer && serializer . object
106
+ resource_identifier_for ( serializer )
107
+ end
97
108
end
98
- result
99
109
end
100
110
101
- def resource_object_for ( serializer , options )
102
- options [ :fields ] = @fieldset && @fieldset . fields_for ( serializer )
103
- options [ :required_fields ] = [ :id , :json_api_type ]
111
+ def relationships_for ( serializer )
112
+ Hash [ serializer . associations . map { | association | [ association . key , { data : relationship_value_for ( association . serializer , association . options ) } ] } ]
113
+ end
104
114
105
- cache_check ( serializer ) do
106
- attributes = serializer . attributes ( options )
115
+ def included_for ( serializer )
116
+ serializer . associations . flat_map { |assoc | _included_for ( assoc . key , assoc . serializer ) } . uniq
117
+ end
107
118
108
- result = {
109
- id : attributes . delete ( :id ) . to_s ,
110
- type : attributes . delete ( :json_api_type )
111
- }
119
+ def _included_for ( resource_name , serializer , parent = nil )
120
+ if serializer . respond_to? ( :each )
121
+ serializer . flat_map { |s | _included_for ( resource_name , s , parent ) } . uniq
122
+ else
123
+ return [ ] unless serializer && serializer . object
124
+ result = [ ]
125
+ resource_path = [ parent , resource_name ] . compact . join ( '.' )
112
126
113
- result [ :attributes ] = attributes if attributes . any?
127
+ if include_assoc? ( resource_path )
128
+ primary_data = primary_data_for ( serializer , @options )
129
+ relationships = relationships_for ( serializer )
130
+ primary_data [ :relationships ] = relationships if relationships . any?
131
+ result . push ( primary_data )
132
+ end
133
+
134
+ if include_nested_assoc? ( resource_path )
135
+ non_empty_associations = serializer . associations . select ( &:serializer )
136
+
137
+ non_empty_associations . each do |association |
138
+ result . concat ( _included_for ( association . key , association . serializer , resource_path ) )
139
+ result . uniq!
140
+ end
141
+ end
114
142
result
115
143
end
116
144
end
117
145
118
146
def include_assoc? ( assoc )
119
- return false unless @options [ :include ]
120
147
check_assoc ( "#{ assoc } $" )
121
148
end
122
149
123
150
def include_nested_assoc? ( assoc )
124
- return false unless @options [ :include ]
125
151
check_assoc ( "#{ assoc } ." )
126
152
end
127
153
128
154
def check_assoc ( assoc )
129
- include_opt = @options [ :include ]
130
- include_opt = include_opt . split ( ',' ) if include_opt . is_a? ( String )
131
- include_opt . any? do |s |
132
- s . match ( /^#{ assoc . gsub ( '.' , '\.' ) } / )
133
- end
134
- end
135
-
136
- def add_resource_relationships ( attrs , serializer , options = { } )
137
- options [ :add_included ] = options . fetch ( :add_included , true )
138
-
139
- serializer . associations . each do |association |
140
- key = association . key
141
- serializer = association . serializer
142
- opts = association . options
143
-
144
- attrs [ :relationships ] ||= { }
145
-
146
- if serializer . respond_to? ( :each )
147
- add_relationships ( attrs , key , serializer )
148
- else
149
- if opts [ :virtual_value ]
150
- add_relationship ( attrs , key , nil , opts [ :virtual_value ] )
151
- else
152
- add_relationship ( attrs , key , serializer )
153
- end
154
- end
155
-
156
- if options [ :add_included ]
157
- Array ( serializer ) . each do |s |
158
- add_included ( key , s )
159
- end
160
- end
161
- end
155
+ @options [ :include ] . any? { |s | s . match ( /^#{ assoc . gsub ( '.' , '\.' ) } / ) }
162
156
end
163
157
164
158
def add_links ( options )
165
159
links = @hash . fetch ( :links ) { { } }
166
160
collection = serializer . object
167
- if is_paginated? ( collection )
168
- @hash [ :links ] = add_pagination_links ( links , collection , options )
169
- end
161
+ @hash [ :links ] = add_pagination_links ( links , collection , options ) if paginated? ( collection )
170
162
end
171
163
172
164
def add_pagination_links ( links , collection , options )
173
165
pagination_links = JsonApi ::PaginationLinks . new ( collection , options [ :context ] ) . serializable_hash ( options )
174
166
links . update ( pagination_links )
175
167
end
176
168
177
- def is_paginated ?( collection )
169
+ def paginated ?( collection )
178
170
collection . respond_to? ( :current_page ) &&
179
171
collection . respond_to? ( :total_pages ) &&
180
172
collection . respond_to? ( :size )
0 commit comments