gpi-active_model_serializers 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/.travis.yml +18 -0
- data/CHANGELOG.md +113 -0
- data/CONTRIBUTING.md +20 -0
- data/DESIGN.textile +586 -0
- data/Gemfile +12 -0
- data/Gemfile.edge +9 -0
- data/MIT-LICENSE.txt +21 -0
- data/README.md +716 -0
- data/Rakefile +18 -0
- data/active_model_serializers.gemspec +28 -0
- data/bench/perf.rb +43 -0
- data/cruft.md +19 -0
- data/lib/action_controller/serialization.rb +58 -0
- data/lib/active_model/array_serializer.rb +65 -0
- data/lib/active_model/serializable.rb +49 -0
- data/lib/active_model/serializer.rb +475 -0
- data/lib/active_model/serializer/associations.rb +191 -0
- data/lib/active_model/serializer/caching.rb +37 -0
- data/lib/active_model/serializer/version.rb +5 -0
- data/lib/active_model_serializers.rb +95 -0
- data/lib/active_record/serializer_override.rb +16 -0
- data/lib/generators/resource_override.rb +13 -0
- data/lib/generators/serializer/USAGE +9 -0
- data/lib/generators/serializer/serializer_generator.rb +36 -0
- data/lib/generators/serializer/templates/serializer.rb +8 -0
- data/test/array_serializer_test.rb +85 -0
- data/test/association_test.rb +592 -0
- data/test/caching_test.rb +96 -0
- data/test/generators_test.rb +73 -0
- data/test/no_serialization_scope_test.rb +34 -0
- data/test/serialization_scope_name_test.rb +99 -0
- data/test/serialization_test.rb +394 -0
- data/test/serializer_support_test.rb +51 -0
- data/test/serializer_test.rb +1521 -0
- data/test/test_fakes.rb +202 -0
- data/test/test_helper.rb +41 -0
- metadata +176 -0
@@ -0,0 +1,191 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
class Serializer
|
3
|
+
class Association #:nodoc:
|
4
|
+
# name: The name of the association.
|
5
|
+
#
|
6
|
+
# options: A hash. These keys are accepted:
|
7
|
+
#
|
8
|
+
# value: The object we're associating with.
|
9
|
+
#
|
10
|
+
# serializer: The class used to serialize the association.
|
11
|
+
#
|
12
|
+
# embed: Define how associations should be embedded.
|
13
|
+
# - :objects # Embed associations as full objects.
|
14
|
+
# - :ids # Embed only the association ids.
|
15
|
+
# - :ids, include: true # Embed the association ids and include objects in the root.
|
16
|
+
#
|
17
|
+
# include: Used in conjunction with embed :ids. Includes the objects in the root.
|
18
|
+
#
|
19
|
+
# root: Used in conjunction with include: true. Defines the key used to embed the objects.
|
20
|
+
#
|
21
|
+
# key: Key name used to store the ids in.
|
22
|
+
#
|
23
|
+
# embed_key: Method used to fetch ids. Defaults to :id.
|
24
|
+
#
|
25
|
+
# polymorphic: Is the association is polymorphic?. Values: true or false.
|
26
|
+
def initialize(name, options={}, serializer_options={})
|
27
|
+
@name = name
|
28
|
+
@object = options[:value]
|
29
|
+
|
30
|
+
embed = options[:embed]
|
31
|
+
@embed_ids = embed == :id || embed == :ids
|
32
|
+
@embed_objects = embed == :object || embed == :objects
|
33
|
+
@embed_key = options[:embed_key] || :id
|
34
|
+
@embed_in_root = options[:include]
|
35
|
+
|
36
|
+
serializer = options[:serializer]
|
37
|
+
@serializer_class = serializer.is_a?(String) ? serializer.constantize : serializer
|
38
|
+
|
39
|
+
@options = options
|
40
|
+
@serializer_options = serializer_options
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :object, :root, :name, :embed_ids, :embed_objects, :embed_in_root
|
44
|
+
alias embeddable? object
|
45
|
+
alias embed_objects? embed_objects
|
46
|
+
alias embed_ids? embed_ids
|
47
|
+
alias use_id_key? embed_ids?
|
48
|
+
alias embed_in_root? embed_in_root
|
49
|
+
|
50
|
+
def key
|
51
|
+
if key = options[:key]
|
52
|
+
key
|
53
|
+
elsif use_id_key?
|
54
|
+
id_key
|
55
|
+
else
|
56
|
+
name
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
attr_reader :embed_key, :serializer_class, :options, :serializer_options
|
63
|
+
|
64
|
+
def find_serializable(object)
|
65
|
+
if serializer_class
|
66
|
+
serializer_class.new(object, serializer_options)
|
67
|
+
elsif object.respond_to?(:active_model_serializer) && (ams = object.active_model_serializer)
|
68
|
+
options = serializer_options.except(:only, :include)
|
69
|
+
|
70
|
+
if serializer_options.key?(:include) && serializer_options[:include].include?(name)
|
71
|
+
options.merge!(serializer_options[:include][name])
|
72
|
+
end
|
73
|
+
|
74
|
+
ams.new(object, options)
|
75
|
+
else
|
76
|
+
object
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class HasMany < Association #:nodoc:
|
81
|
+
def root
|
82
|
+
options[:root] || name
|
83
|
+
end
|
84
|
+
|
85
|
+
def id_key
|
86
|
+
"#{name.to_s.singularize}_ids".to_sym
|
87
|
+
end
|
88
|
+
|
89
|
+
def serializables
|
90
|
+
object.map do |item|
|
91
|
+
find_serializable(item)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def serialize
|
96
|
+
object.map do |item|
|
97
|
+
find_serializable(item).serializable_hash
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def serialize_ids
|
102
|
+
object.map do |item|
|
103
|
+
serializer = find_serializable(item)
|
104
|
+
if serializer.respond_to?(embed_key)
|
105
|
+
serializer.send(embed_key)
|
106
|
+
else
|
107
|
+
item.read_attribute_for_serialization(embed_key)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class HasOne < Association #:nodoc:
|
114
|
+
def initialize(name, options={}, serializer_options={})
|
115
|
+
super
|
116
|
+
@polymorphic = options[:polymorphic]
|
117
|
+
end
|
118
|
+
|
119
|
+
def root
|
120
|
+
if root = options[:root]
|
121
|
+
root
|
122
|
+
elsif polymorphic?
|
123
|
+
object.class.to_s.pluralize.demodulize.underscore.to_sym
|
124
|
+
else
|
125
|
+
name.to_s.pluralize.to_sym
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def id_key
|
130
|
+
"#{name}_id".to_sym
|
131
|
+
end
|
132
|
+
|
133
|
+
def embeddable?
|
134
|
+
super || !polymorphic?
|
135
|
+
end
|
136
|
+
|
137
|
+
def serializables
|
138
|
+
value = object && find_serializable(object)
|
139
|
+
value ? [value] : []
|
140
|
+
end
|
141
|
+
|
142
|
+
def serialize
|
143
|
+
if object
|
144
|
+
if polymorphic?
|
145
|
+
{
|
146
|
+
:type => polymorphic_key,
|
147
|
+
polymorphic_key => find_serializable(object).serializable_hash
|
148
|
+
}
|
149
|
+
else
|
150
|
+
find_serializable(object).serializable_hash
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def serialize_ids
|
156
|
+
if object
|
157
|
+
serializer = find_serializable(object)
|
158
|
+
id =
|
159
|
+
if serializer.respond_to?(embed_key)
|
160
|
+
serializer.send(embed_key)
|
161
|
+
else
|
162
|
+
object.read_attribute_for_serialization(embed_key)
|
163
|
+
end
|
164
|
+
|
165
|
+
if polymorphic?
|
166
|
+
{
|
167
|
+
type: polymorphic_key,
|
168
|
+
id: id
|
169
|
+
}
|
170
|
+
else
|
171
|
+
id
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
attr_reader :polymorphic
|
179
|
+
alias polymorphic? polymorphic
|
180
|
+
|
181
|
+
def use_id_key?
|
182
|
+
embed_ids? && !polymorphic?
|
183
|
+
end
|
184
|
+
|
185
|
+
def polymorphic_key
|
186
|
+
object.class.to_s.demodulize.underscore.to_sym
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
class Serializer
|
3
|
+
module Caching
|
4
|
+
def to_json(*args)
|
5
|
+
if caching_enabled?
|
6
|
+
key = expand_cache_key([self.class.to_s.underscore, cache_key, 'to-json'])
|
7
|
+
cache.fetch key do
|
8
|
+
super
|
9
|
+
end
|
10
|
+
else
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def serialize(*args)
|
16
|
+
if caching_enabled?
|
17
|
+
key = expand_cache_key([self.class.to_s.underscore, cache_key, 'serialize'])
|
18
|
+
cache.fetch key do
|
19
|
+
serialize_object
|
20
|
+
end
|
21
|
+
else
|
22
|
+
serialize_object
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def caching_enabled?
|
29
|
+
perform_caching && cache && respond_to?(:cache_key)
|
30
|
+
end
|
31
|
+
|
32
|
+
def expand_cache_key(*args)
|
33
|
+
ActiveSupport::Cache.expand_cache_key(args)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext/string/inflections"
|
3
|
+
require "active_support/notifications"
|
4
|
+
require "active_model"
|
5
|
+
require "active_model/array_serializer"
|
6
|
+
require "active_model/serializer"
|
7
|
+
require "active_model/serializer/associations"
|
8
|
+
require "set"
|
9
|
+
|
10
|
+
if defined?(Rails)
|
11
|
+
module ActiveModel
|
12
|
+
class Railtie < Rails::Railtie
|
13
|
+
generators do |app|
|
14
|
+
Rails::Generators.configure!(app.config.generators)
|
15
|
+
Rails::Generators.hidden_namespaces.uniq!
|
16
|
+
require_relative "generators/resource_override"
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer "include_routes.active_model_serializer" do |app|
|
20
|
+
ActiveSupport.on_load(:active_model_serializers) do
|
21
|
+
include AbstractController::UrlFor
|
22
|
+
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
|
23
|
+
include app.routes.mounted_helpers
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
initializer "caching.active_model_serializer" do |app|
|
28
|
+
ActiveModel::Serializer.perform_caching = app.config.action_controller.perform_caching
|
29
|
+
ActiveModel::ArraySerializer.perform_caching = app.config.action_controller.perform_caching
|
30
|
+
|
31
|
+
ActiveModel::Serializer.cache = Rails.cache
|
32
|
+
ActiveModel::ArraySerializer.cache = Rails.cache
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module ActiveModel::SerializerSupport
|
39
|
+
extend ActiveSupport::Concern
|
40
|
+
|
41
|
+
module ClassMethods #:nodoc:
|
42
|
+
if "".respond_to?(:safe_constantize)
|
43
|
+
def active_model_serializer
|
44
|
+
"#{self.name}Serializer".safe_constantize
|
45
|
+
end
|
46
|
+
else
|
47
|
+
def active_model_serializer
|
48
|
+
begin
|
49
|
+
"#{self.name}Serializer".constantize
|
50
|
+
rescue NameError => e
|
51
|
+
raise unless e.message =~ /uninitialized constant/
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns a model serializer for this object considering its namespace.
|
58
|
+
def active_model_serializer
|
59
|
+
self.class.active_model_serializer
|
60
|
+
end
|
61
|
+
|
62
|
+
alias :read_attribute_for_serialization :send
|
63
|
+
end
|
64
|
+
|
65
|
+
module ActiveModel::ArraySerializerSupport
|
66
|
+
def active_model_serializer
|
67
|
+
ActiveModel::ArraySerializer
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
Array.send(:include, ActiveModel::ArraySerializerSupport)
|
72
|
+
Set.send(:include, ActiveModel::ArraySerializerSupport)
|
73
|
+
|
74
|
+
{
|
75
|
+
active_record: 'ActiveRecord::Relation',
|
76
|
+
mongoid: 'Mongoid::Criteria'
|
77
|
+
}.each do |orm, rel_class|
|
78
|
+
ActiveSupport.on_load(orm) do
|
79
|
+
include ActiveModel::SerializerSupport
|
80
|
+
rel_class.constantize.send(:include, ActiveModel::ArraySerializerSupport)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
begin
|
85
|
+
require 'action_controller'
|
86
|
+
require 'action_controller/serialization'
|
87
|
+
|
88
|
+
ActiveSupport.on_load(:action_controller) do
|
89
|
+
include ::ActionController::Serialization
|
90
|
+
end
|
91
|
+
rescue LoadError => ex
|
92
|
+
# rails on installed, continuing
|
93
|
+
end
|
94
|
+
|
95
|
+
ActiveSupport.run_load_hooks(:active_model_serializers, ActiveModel::Serializer)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# We do not recommend that you use AM::S in this way, but if you must, here
|
2
|
+
# is a mixin that overrides ActiveRecord::Base#to_json and #as_json.
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module SerializerOverride
|
6
|
+
def to_json options = {}
|
7
|
+
active_model_serializer.new(self).to_json options
|
8
|
+
end
|
9
|
+
|
10
|
+
def as_json options={}
|
11
|
+
active_model_serializer.new(self).as_json options
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Base.send(:include, SerializerOverride)
|
16
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Description:
|
2
|
+
Generates a serializer for the given resource with tests.
|
3
|
+
|
4
|
+
Example:
|
5
|
+
`rails generate serializer Account name created_at`
|
6
|
+
|
7
|
+
For TestUnit it creates:
|
8
|
+
Serializer: app/serializers/account_serializer.rb
|
9
|
+
TestUnit: test/unit/account_serializer_test.rb
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Rails
|
2
|
+
module Generators
|
3
|
+
class SerializerGenerator < NamedBase
|
4
|
+
source_root File.expand_path("../templates", __FILE__)
|
5
|
+
check_class_collision suffix: "Serializer"
|
6
|
+
|
7
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
8
|
+
|
9
|
+
class_option :parent, type: :string, desc: "The parent class for the generated serializer"
|
10
|
+
|
11
|
+
def create_serializer_file
|
12
|
+
template 'serializer.rb', File.join('app/serializers', class_path, "#{file_name}_serializer.rb")
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def attributes_names
|
18
|
+
[:id] + attributes.select { |attr| !attr.reference? }.map { |a| a.name.to_sym }
|
19
|
+
end
|
20
|
+
|
21
|
+
def association_names
|
22
|
+
attributes.select { |attr| attr.reference? }.map { |a| a.name.to_sym }
|
23
|
+
end
|
24
|
+
|
25
|
+
def parent_class_name
|
26
|
+
if options[:parent]
|
27
|
+
options[:parent]
|
28
|
+
elsif defined?(::ApplicationSerializer)
|
29
|
+
"ApplicationSerializer"
|
30
|
+
else
|
31
|
+
"ActiveModel::Serializer"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "test_fakes"
|
3
|
+
|
4
|
+
class ArraySerializerTest < ActiveModel::TestCase
|
5
|
+
# serialize different typed objects
|
6
|
+
def test_array_serializer
|
7
|
+
model = Model.new
|
8
|
+
user = User.new
|
9
|
+
comments = Comment.new(title: "Comment1", id: 1)
|
10
|
+
|
11
|
+
array = [model, user, comments]
|
12
|
+
serializer = array.active_model_serializer.new(array, scope: { scope: true })
|
13
|
+
assert_equal([
|
14
|
+
{ model: "Model" },
|
15
|
+
{ last_name: "Valim", ok: true, first_name: "Jose", scope: true },
|
16
|
+
{ title: "Comment1" }
|
17
|
+
], serializer.as_json)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_array_serializer_with_root
|
21
|
+
comment1 = Comment.new(title: "Comment1", id: 1)
|
22
|
+
comment2 = Comment.new(title: "Comment2", id: 2)
|
23
|
+
|
24
|
+
array = [ comment1, comment2 ]
|
25
|
+
|
26
|
+
serializer = array.active_model_serializer.new(array, root: :comments)
|
27
|
+
|
28
|
+
assert_equal({ comments: [
|
29
|
+
{ title: "Comment1" },
|
30
|
+
{ title: "Comment2" }
|
31
|
+
]}, serializer.as_json)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_active_model_with_root
|
35
|
+
comment1 = ModelWithActiveModelSerializer.new(title: "Comment1")
|
36
|
+
comment2 = ModelWithActiveModelSerializer.new(title: "Comment2")
|
37
|
+
|
38
|
+
array = [ comment1, comment2 ]
|
39
|
+
|
40
|
+
serializer = array.active_model_serializer.new(array, root: :comments)
|
41
|
+
|
42
|
+
assert_equal({ comments: [
|
43
|
+
{ title: "Comment1" },
|
44
|
+
{ title: "Comment2" }
|
45
|
+
]}, serializer.as_json)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_array_serializer_with_hash
|
49
|
+
hash = { value: "something" }
|
50
|
+
array = [hash]
|
51
|
+
serializer = array.active_model_serializer.new(array, root: :items)
|
52
|
+
assert_equal({ items: [hash.as_json] }, serializer.as_json)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_array_serializer_with_specified_serializer
|
56
|
+
post1 = Post.new(title: "Post1", author: "Author1", id: 1)
|
57
|
+
post2 = Post.new(title: "Post2", author: "Author2", id: 2)
|
58
|
+
|
59
|
+
array = [ post1, post2 ]
|
60
|
+
|
61
|
+
serializer = array.active_model_serializer.new array, each_serializer: CustomPostSerializer
|
62
|
+
|
63
|
+
assert_equal([
|
64
|
+
{ title: "Post1" },
|
65
|
+
{ title: "Post2" }
|
66
|
+
], serializer.as_json)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_array_serializer_using_default_serializer
|
70
|
+
hash = { "value" => "something" }
|
71
|
+
class << hash
|
72
|
+
def active_model_serializer
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
array = [hash]
|
78
|
+
|
79
|
+
serializer = array.active_model_serializer.new array
|
80
|
+
|
81
|
+
assert_equal([
|
82
|
+
{ "value" => "something" }
|
83
|
+
], serializer.as_json)
|
84
|
+
end
|
85
|
+
end
|