Skip to content

Commit 2375420

Browse files
committed
Merge pull request #1103 from beauby/fix-jsonapi-ri
Move `id` and `json_api_type` methods from `Serializer` to `JsonApi`.
2 parents f227994 + f27f13c commit 2375420

File tree

3 files changed

+81
-108
lines changed

3 files changed

+81
-108
lines changed

lib/active_model/serializer.rb

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,18 +145,6 @@ def json_key
145145
@root || object.class.model_name.to_s.underscore
146146
end
147147

148-
def id
149-
object.id if object
150-
end
151-
152-
def json_api_type
153-
if config.jsonapi_resource_type == :plural
154-
object.class.model_name.plural
155-
else
156-
object.class.model_name.singular
157-
end
158-
end
159-
160148
def attributes(options = {})
161149
attributes =
162150
if options[:fields]
@@ -165,8 +153,6 @@ def attributes(options = {})
165153
self.class._attributes.dup
166154
end
167155

168-
attributes += options[:required_fields] if options[:required_fields]
169-
170156
attributes.each_with_object({}) do |name, hash|
171157
unless self.class._fragmented
172158
hash[name] = send(name)

lib/active_model/serializer/adapter/json_api.rb

Lines changed: 81 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ def initialize(serializer, options = {})
99
super
1010
@hash = { data: [] }
1111

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
1319
@fieldset = ActiveModel::Serializer::Fieldset.new(fields, serializer.json_key)
1420
else
1521
@fieldset = options[:fieldset]
@@ -31,150 +37,136 @@ def serializable_hash(options = nil)
3137

3238
add_links(options)
3339
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?
3646
end
3747
@hash
3848
end
3949

4050
def fragment_cache(cached_hash, non_cached_hash)
4151
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)
4353
end
4454

4555
private
4656

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
5163
end
5264

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
5970
end
6071
end
6172

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)
7076

71-
serializers.each do |serializer|
72-
attrs = attributes_for_serializer(serializer, @options)
77+
{ id: id.to_s, type: type }
78+
end
7379

74-
add_resource_relationships(attrs, serializer, add_included: false)
80+
def resource_object_for(serializer, options = {})
81+
options[:fields] = @fieldset && @fieldset.fields_for(serializer)
7582

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
7888
end
89+
end
7990

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)
8696
end
8797
end
8898

89-
def attributes_for_serializer(serializer, options)
99+
def relationship_value_for(serializer, options = {})
90100
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) }
95102
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
97108
end
98-
result
99109
end
100110

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
104114

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
107118

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('.')
112126

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
114142
result
115143
end
116144
end
117145

118146
def include_assoc?(assoc)
119-
return false unless @options[:include]
120147
check_assoc("#{assoc}$")
121148
end
122149

123150
def include_nested_assoc?(assoc)
124-
return false unless @options[:include]
125151
check_assoc("#{assoc}.")
126152
end
127153

128154
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('.', '\.')}/) }
162156
end
163157

164158
def add_links(options)
165159
links = @hash.fetch(:links) { {} }
166160
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)
170162
end
171163

172164
def add_pagination_links(links, collection, options)
173165
pagination_links = JsonApi::PaginationLinks.new(collection, options[:context]).serializable_hash(options)
174166
links.update(pagination_links)
175167
end
176168

177-
def is_paginated?(collection)
169+
def paginated?(collection)
178170
collection.respond_to?(:current_page) &&
179171
collection.respond_to?(:total_pages) &&
180172
collection.respond_to?(:size)

test/serializers/attributes_test.rb

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ def test_attributes_with_fields_option
2323
@profile_serializer.attributes(fields: [:name]))
2424
end
2525

26-
def test_required_fields
27-
assert_equal({ name: 'Name 1', description: 'Description 1' },
28-
@profile_serializer.attributes(fields: [:name, :description], required_fields: [:name]))
29-
end
30-
3126
def test_attributes_inheritance_definition
3227
assert_equal([:id, :body], @serializer_klass._attributes)
3328
end

0 commit comments

Comments
 (0)