Skip to content

Commit 036604b

Browse files
committed
Extract Serializer Attributes into its own file
1 parent eceb2d5 commit 036604b

File tree

2 files changed

+112
-92
lines changed

2 files changed

+112
-92
lines changed

lib/active_model/serializer.rb

Lines changed: 5 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require 'active_model/serializer/array_serializer'
44
require 'active_model/serializer/include_tree'
55
require 'active_model/serializer/associations'
6+
require 'active_model/serializer/attributes'
67
require 'active_model/serializer/configuration'
78
require 'active_model/serializer/fieldset'
89
require 'active_model/serializer/lint'
@@ -13,35 +14,8 @@ module ActiveModel
1314
class Serializer
1415
include Configuration
1516
include Associations
17+
include Attributes
1618
require 'active_model/serializer/adapter'
17-
class Attribute
18-
delegate :call, to: :reader
19-
attr_reader :name, :reader
20-
def initialize(name)
21-
@name = name
22-
@reader = nil
23-
end
24-
25-
def self.build(name, block)
26-
if block
27-
AttributeBlock.new(name, block)
28-
else
29-
AttributeReader.new(name)
30-
end
31-
end
32-
end
33-
class AttributeReader < Attribute
34-
def initialize(name)
35-
super(name)
36-
@reader = ->(instance) { instance.read_attribute_for_serialization(name) }
37-
end
38-
end
39-
class AttributeBlock < Attribute
40-
def initialize(name, block)
41-
super(name)
42-
@reader = ->(instance) { instance.instance_eval(&block) }
43-
end
44-
end
4519

4620
# Matches
4721
# "c:/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb:1:in `<top (required)>'"
@@ -73,12 +47,9 @@ def self.digest_caller_file(caller_line)
7347
end
7448

7549
with_options instance_writer: false, instance_reader: false do |serializer|
76-
class_attribute :_type, instance_reader: true
77-
class_attribute :_attribute_mappings # @api private : maps attribute key names to names to names of implementing methods, @see Serializer#attribute
78-
self._attribute_mappings ||= {}
79-
class_attribute :_links # @api private : links definitions, @see Serializer#link
50+
serializer.class_attribute :_type, instance_reader: true
51+
serializer.class_attribute :_links # @api private : links definitions, @see Serializer#link
8052
self._links ||= {}
81-
8253
serializer.class_attribute :_cache # @api private : the cache object
8354
serializer.class_attribute :_fragmented # @api private : @see ::fragmented
8455
serializer.class_attribute :_cache_key # @api private : when present, is first item in cache_key
@@ -95,11 +66,10 @@ def self.digest_caller_file(caller_line)
9566
serializer.class_attribute :_cache_digest # @api private : Generated
9667
end
9768

98-
# Serializers inherit serialized_attributes, _attributes_keys, and _reflections.
69+
# Serializers inherit _attribute_mappings, _reflections, and _links.
9970
# Generates a unique digest for each serializer at load.
10071
def self.inherited(base)
10172
caller_line = caller.first
102-
base._attribute_mappings = _attribute_mappings.dup
10373
base._links = _links.dup
10474
base._cache_digest = digest_caller_file(caller_line)
10575
super
@@ -116,54 +86,6 @@ def self.link(name, value = nil, &block)
11686
_links[name] = block || value
11787
end
11888

119-
# @example
120-
# class AdminAuthorSerializer < ActiveModel::Serializer
121-
# attributes :id, :name, :recent_edits
122-
def self.attributes(*attrs)
123-
attrs = attrs.first if attrs.first.class == Array
124-
125-
attrs.each do |attr|
126-
attribute(attr)
127-
end
128-
end
129-
130-
# TODO: remove the dynamic method definition
131-
# @example
132-
# class AdminAuthorSerializer < ActiveModel::Serializer
133-
# attributes :id, :recent_edits
134-
# attribute :name, key: :title
135-
#
136-
# attribute :full_name do
137-
# "#{object.first_name} #{object.last_name}"
138-
# end
139-
#
140-
# def recent_edits
141-
# object.edits.last(5)
142-
# end
143-
def self.attribute(attr, options = {}, &block)
144-
key = options.fetch(:key, attr)
145-
_attribute_mappings[key] = Attribute.build(attr, block)
146-
end
147-
148-
# @api private
149-
# names of attribute methods
150-
# @see Serializer::attribute
151-
def self._attributes
152-
_attribute_mappings.keys
153-
end
154-
155-
# @api private
156-
# maps attribute value to explict key name
157-
# @see Serializer::attribute
158-
# @see Adapter::FragmentCache#fragment_serializer
159-
def self._attributes_keys
160-
_attribute_mappings
161-
.each_with_object({}) do |(key, attribute_mapping), hash|
162-
next if key == attribute_mapping.name
163-
hash[attribute_mapping.name] = { key: key }
164-
end
165-
end
166-
16789
# @api private
16890
# Used by FragmentCache on the CachedSerializer
16991
# to call attribute methods on the fragmented cached serializer.
@@ -286,15 +208,6 @@ def json_key
286208
root || object.class.model_name.to_s.underscore
287209
end
288210

289-
# Return the +attributes+ of +object+ as presented
290-
# by the serializer.
291-
def attributes(requested_attrs = nil)
292-
self.class._attribute_mappings.each_with_object({}) do |(key, attribute_mapping), hash|
293-
next unless requested_attrs.nil? || requested_attrs.include?(key)
294-
hash[key] = attribute_mapping.call(self)
295-
end
296-
end
297-
298211
def read_attribute_for_serialization(attr)
299212
if _serializer_method_defined?(attr)
300213
send(attr)
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
module ActiveModel
2+
class Serializer
3+
module Attributes
4+
class Attribute
5+
delegate :call, to: :reader
6+
attr_reader :name, :reader
7+
def initialize(name)
8+
@name = name
9+
@reader = nil
10+
end
11+
12+
def self.build(name, block)
13+
if block
14+
AttributeBlock.new(name, block)
15+
else
16+
AttributeReader.new(name)
17+
end
18+
end
19+
end
20+
class AttributeReader < Attribute
21+
def initialize(name)
22+
super(name)
23+
@reader = ->(instance) { instance.read_attribute_for_serialization(name) }
24+
end
25+
end
26+
class AttributeBlock < Attribute
27+
def initialize(name, block)
28+
super(name)
29+
@reader = ->(instance) { instance.instance_eval(&block) }
30+
end
31+
end
32+
33+
extend ActiveSupport::Concern
34+
35+
included do
36+
with_options instance_writer: false, instance_reader: false do |serializer|
37+
serializer.class_attribute :_attribute_mappings # @api private : maps attribute key names to names to names of implementing methods, @see #attribute
38+
self._attribute_mappings ||= {}
39+
end
40+
41+
# Return the +attributes+ of +object+ as presented
42+
# by the serializer.
43+
def attributes(requested_attrs = nil)
44+
self.class._attribute_mappings.each_with_object({}) do |(key, attribute_mapping), hash|
45+
next unless requested_attrs.nil? || requested_attrs.include?(key)
46+
hash[key] = attribute_mapping.call(self)
47+
end
48+
end
49+
end
50+
51+
module ClassMethods
52+
def inherited(base)
53+
super
54+
base._attribute_mappings = _attribute_mappings.dup
55+
end
56+
57+
# @example
58+
# class AdminAuthorSerializer < ActiveModel::Serializer
59+
# attributes :id, :name, :recent_edits
60+
def attributes(*attrs)
61+
attrs = attrs.first if attrs.first.class == Array
62+
63+
attrs.each do |attr|
64+
attribute(attr)
65+
end
66+
end
67+
68+
# TODO: remove the dynamic method definition
69+
# @example
70+
# class AdminAuthorSerializer < ActiveModel::Serializer
71+
# attributes :id, :recent_edits
72+
# attribute :name, key: :title
73+
#
74+
# attribute :full_name do
75+
# "#{object.first_name} #{object.last_name}"
76+
# end
77+
#
78+
# def recent_edits
79+
# object.edits.last(5)
80+
# end
81+
def attribute(attr, options = {}, &block)
82+
key = options.fetch(:key, attr)
83+
_attribute_mappings[key] = Attribute.build(attr, block)
84+
end
85+
86+
# @api private
87+
# names of attribute methods
88+
# @see Serializer::attribute
89+
def _attributes
90+
_attribute_mappings.keys
91+
end
92+
93+
# @api private
94+
# maps attribute value to explict key name
95+
# @see Serializer::attribute
96+
# @see Adapter::FragmentCache#fragment_serializer
97+
def _attributes_keys
98+
_attribute_mappings
99+
.each_with_object({}) do |(key, attribute_mapping), hash|
100+
next if key == attribute_mapping.name
101+
hash[attribute_mapping.name] = { key: key }
102+
end
103+
end
104+
end
105+
end
106+
end
107+
end

0 commit comments

Comments
 (0)