Skip to content

Commit c703d0f

Browse files
committed
Merge pull request #985 from bolshakov/feature/each_association
Associations implementation refactoring
2 parents 88eabdf + 2952a33 commit c703d0f

13 files changed

+381
-175
lines changed

lib/active_model/serializer.rb

Lines changed: 22 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@
33
module ActiveModel
44
class Serializer
55
extend ActiveSupport::Autoload
6+
67
autoload :Configuration
78
autoload :ArraySerializer
89
autoload :Adapter
910
autoload :Lint
11+
autoload :Associations
1012
include Configuration
13+
include Associations
1114

1215
class << self
1316
attr_accessor :_attributes
1417
attr_accessor :_attributes_keys
15-
attr_accessor :_associations
1618
attr_accessor :_urls
1719
attr_accessor :_cache
1820
attr_accessor :_fragmented
@@ -24,12 +26,12 @@ class << self
2426
end
2527

2628
def self.inherited(base)
27-
base._attributes = self._attributes.try(:dup) || []
29+
base._attributes = self._attributes.try(:dup) || []
2830
base._attributes_keys = self._attributes_keys.try(:dup) || {}
29-
base._associations = self._associations.try(:dup) || {}
3031
base._urls = []
3132
serializer_file = File.open(caller.first[/^[^:]+/])
3233
base._cache_digest = Digest::MD5.hexdigest(serializer_file.read)
34+
super
3335
end
3436

3537
def self.attributes(*attrs)
@@ -46,7 +48,7 @@ def self.attributes(*attrs)
4648

4749
def self.attribute(attr, options = {})
4850
key = options.fetch(:key, attr)
49-
@_attributes_keys[attr] = {key: key} if key != attr
51+
@_attributes_keys[attr] = { key: key } if key != attr
5052
@_attributes << key unless @_attributes.include?(key)
5153
define_method key do
5254
object.read_attribute_for_serialization(attr)
@@ -59,58 +61,13 @@ def self.fragmented(serializer)
5961

6062
# Enables a serializer to be automatically cached
6163
def self.cache(options = {})
62-
@_cache = ActionController::Base.cache_store if Rails.configuration.action_controller.perform_caching
63-
@_cache_key = options.delete(:key)
64-
@_cache_only = options.delete(:only)
65-
@_cache_except = options.delete(:except)
64+
@_cache = ActionController::Base.cache_store if Rails.configuration.action_controller.perform_caching
65+
@_cache_key = options.delete(:key)
66+
@_cache_only = options.delete(:only)
67+
@_cache_except = options.delete(:except)
6668
@_cache_options = (options.empty?) ? nil : options
6769
end
6870

69-
# Defines an association in the object should be rendered.
70-
#
71-
# The serializer object should implement the association name
72-
# as a method which should return an array when invoked. If a method
73-
# with the association name does not exist, the association name is
74-
# dispatched to the serialized object.
75-
def self.has_many(*attrs)
76-
associate(:has_many, attrs)
77-
end
78-
79-
# Defines an association in the object that should be rendered.
80-
#
81-
# The serializer object should implement the association name
82-
# as a method which should return an object when invoked. If a method
83-
# with the association name does not exist, the association name is
84-
# dispatched to the serialized object.
85-
def self.belongs_to(*attrs)
86-
associate(:belongs_to, attrs)
87-
end
88-
89-
# Defines an association in the object should be rendered.
90-
#
91-
# The serializer object should implement the association name
92-
# as a method which should return an object when invoked. If a method
93-
# with the association name does not exist, the association name is
94-
# dispatched to the serialized object.
95-
def self.has_one(*attrs)
96-
associate(:has_one, attrs)
97-
end
98-
99-
def self.associate(type, attrs) #:nodoc:
100-
options = attrs.extract_options!
101-
self._associations = _associations.dup
102-
103-
attrs.each do |attr|
104-
unless method_defined?(attr)
105-
define_method attr do
106-
object.send attr
107-
end
108-
end
109-
110-
self._associations[attr] = {type: type, association_options: options}
111-
end
112-
end
113-
11471
def self.url(attr)
11572
@_urls.push attr
11673
end
@@ -125,19 +82,17 @@ def self.serializer_for(resource, options = {})
12582
elsif resource.respond_to?(:to_ary)
12683
config.array_serializer
12784
else
128-
options
129-
.fetch(:association_options, {})
130-
.fetch(:serializer, get_serializer_for(resource.class))
85+
options.fetch(:serializer, get_serializer_for(resource.class))
13186
end
13287
end
13388

13489
def self.adapter
13590
adapter_class = case config.adapter
136-
when Symbol
137-
ActiveModel::Serializer::Adapter.adapter_class(config.adapter)
138-
when Class
139-
config.adapter
140-
end
91+
when Symbol
92+
ActiveModel::Serializer::Adapter.adapter_class(config.adapter)
93+
when Class
94+
config.adapter
95+
end
14196
unless adapter_class
14297
valid_adapters = Adapter.constants.map { |klass| ":#{klass.to_s.downcase}" }
14398
raise ArgumentError, "Unknown adapter: #{config.adapter}. Valid adapters are: #{valid_adapters}"
@@ -153,12 +108,12 @@ def self.root_name
153108
attr_accessor :object, :root, :meta, :meta_key, :scope
154109

155110
def initialize(object, options = {})
156-
@object = object
157-
@options = options
158-
@root = options[:root]
159-
@meta = options[:meta]
160-
@meta_key = options[:meta_key]
161-
@scope = options[:scope]
111+
@object = object
112+
@options = options
113+
@root = options[:root]
114+
@meta = options[:meta]
115+
@meta_key = options[:meta_key]
116+
@scope = options[:scope]
162117

163118
scope_name = options[:scope_name]
164119
if scope_name && !respond_to?(scope_name)
@@ -199,48 +154,10 @@ def attributes(options = {})
199154
end
200155
end
201156

202-
def each_association(&block)
203-
self.class._associations.dup.each do |name, association_options|
204-
next unless object
205-
association_value = send(name)
206-
207-
serializer_class = ActiveModel::Serializer.serializer_for(association_value, association_options)
208-
209-
if serializer_class
210-
begin
211-
serializer = serializer_class.new(
212-
association_value,
213-
options.except(:serializer).merge(serializer_from_options(association_options))
214-
)
215-
rescue ActiveModel::Serializer::ArraySerializer::NoSerializerError
216-
virtual_value = association_value
217-
virtual_value = virtual_value.as_json if virtual_value.respond_to?(:as_json)
218-
association_options[:association_options][:virtual_value] = virtual_value
219-
end
220-
elsif !association_value.nil? && !association_value.instance_of?(Object)
221-
association_options[:association_options][:virtual_value] = association_value
222-
end
223-
224-
association_key = association_options[:association_options][:key] || name
225-
if block_given?
226-
block.call(association_key, serializer, association_options[:association_options])
227-
end
228-
end
229-
end
230-
231-
def serializer_from_options(options)
232-
opts = {}
233-
serializer = options.fetch(:association_options, {}).fetch(:serializer, nil)
234-
opts[:serializer] = serializer if serializer
235-
opts
236-
end
237-
238157
def self.serializers_cache
239158
@serializers_cache ||= ThreadSafe::Cache.new
240159
end
241160

242-
private
243-
244161
attr_reader :options
245162

246163
def self.get_serializer_for(klass)
@@ -255,6 +172,5 @@ def self.get_serializer_for(klass)
255172
end
256173
end
257174
end
258-
259175
end
260176
end

lib/active_model/serializer/adapter/json.rb

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,34 @@ class Json < Adapter
77
def serializable_hash(options = nil)
88
options ||= {}
99
if serializer.respond_to?(:each)
10-
@result = serializer.map{|s| FlattenJson.new(s).serializable_hash(options) }
10+
@result = serializer.map { |s| FlattenJson.new(s).serializable_hash(options) }
1111
else
1212
@hash = {}
1313

1414
@core = cache_check(serializer) do
1515
serializer.attributes(options)
1616
end
1717

18-
serializer.each_association do |key, association, opts|
19-
if association.respond_to?(:each)
20-
array_serializer = association
21-
@hash[key] = array_serializer.map do |item|
18+
serializer.associations.each do |association|
19+
serializer = association.serializer
20+
opts = association.options
21+
22+
if serializer.respond_to?(:each)
23+
array_serializer = serializer
24+
@hash[association.key] = array_serializer.map do |item|
2225
cache_check(item) do
2326
item.attributes(opts)
2427
end
2528
end
2629
else
27-
if association && association.object
28-
@hash[key] = cache_check(association) do
29-
association.attributes(options)
30+
@hash[association.key] =
31+
if serializer && serializer.object
32+
cache_check(serializer) do
33+
serializer.attributes(options)
34+
end
35+
elsif opts[:virtual_value]
36+
opts[:virtual_value]
3037
end
31-
elsif opts[:virtual_value]
32-
@hash[key] = opts[:virtual_value]
33-
else
34-
@hash[key] = nil
35-
end
3638
end
3739
end
3840
@result = @core.merge @hash

lib/active_model/serializer/adapter/json_api.rb

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@ def add_included(resource_name, serializers, parent = nil)
7575
end
7676

7777
serializers.each do |serializer|
78-
serializer.each_association do |key, association, opts|
79-
add_included(key, association, resource_path) if association
78+
serializer.associations.each do |association|
79+
serializer = association.serializer
80+
81+
add_included(association.key, serializer, resource_path) if serializer
8082
end if include_nested_assoc? resource_path
8183
end
8284
end
@@ -131,22 +133,26 @@ def check_assoc(assoc)
131133
def add_resource_relationships(attrs, serializer, options = {})
132134
options[:add_included] = options.fetch(:add_included, true)
133135

134-
serializer.each_association do |key, association, opts|
136+
serializer.associations.each do |association|
137+
key = association.key
138+
serializer = association.serializer
139+
opts = association.options
140+
135141
attrs[:relationships] ||= {}
136142

137-
if association.respond_to?(:each)
138-
add_relationships(attrs, key, association)
143+
if serializer.respond_to?(:each)
144+
add_relationships(attrs, key, serializer)
139145
else
140146
if opts[:virtual_value]
141147
add_relationship(attrs, key, nil, opts[:virtual_value])
142148
else
143-
add_relationship(attrs, key, association)
149+
add_relationship(attrs, key, serializer)
144150
end
145151
end
146152

147153
if options[:add_included]
148-
Array(association).each do |association|
149-
add_included(key, association)
154+
Array(serializer).each do |serializer|
155+
add_included(key, serializer)
150156
end
151157
end
152158
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module ActiveModel
2+
class Serializer
3+
# This class hold all information about serializer's association.
4+
#
5+
# @param [Symbol] name
6+
# @param [ActiveModel::Serializer] serializer
7+
# @param [Hash{Symbol => Object}] options
8+
#
9+
# @example
10+
# Association.new(:comments, CommentSummarySerializer, embed: :ids)
11+
#
12+
Association = Struct.new(:name, :serializer, :options) do
13+
14+
# @return [Symbol]
15+
#
16+
def key
17+
options.fetch(:key, name)
18+
end
19+
end
20+
end
21+
end

0 commit comments

Comments
 (0)