active_model_serializers 0.9.3 → 0.9.8
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +107 -0
- data/README.md +14 -13
- data/lib/action_controller/serialization.rb +9 -1
- data/lib/active_model/array_serializer.rb +4 -8
- data/lib/active_model/default_serializer.rb +2 -6
- data/lib/active_model/serializable.rb +12 -15
- data/lib/active_model/serializer.rb +25 -9
- data/lib/active_model/serializer/association/has_one.rb +1 -1
- data/lib/active_model/serializer/generators/serializer/scaffold_controller_generator.rb +1 -1
- data/lib/active_model/serializer/railtie.rb +4 -0
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model_serializers/model/caching.rb +25 -0
- data/test/benchmark/app.rb +60 -0
- data/test/benchmark/benchmarking_support.rb +67 -0
- data/test/benchmark/bm_active_record.rb +41 -0
- data/test/benchmark/setup.rb +75 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_6eqewtfgrhitvq5gqm25 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_8083sx03hu72pxz1a4d0 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_fyz2gsml4z0ph9kpoy1c +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_hjry5rc32imd42oxoi48 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_m8fpoz2cvt3g9agz0bs3 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_p92m2drnj1i568u3sta0 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_qg52tpca3uesdfguee9i +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_s15t1a6mvxe0z7vjv790 +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_timers_x8kal3d17nfds6vp4kcj +0 -0
- data/test/benchmark/tmp/miniprofiler/mp_views_127.0.0.1 +0 -0
- data/test/fixtures/active_record.rb +4 -0
- data/test/fixtures/poro.rb +46 -3
- data/test/integration/action_controller/namespaced_serialization_test.rb +10 -1
- data/test/integration/action_controller/serialization_test.rb +5 -5
- data/test/integration/active_record/active_record_test.rb +17 -0
- data/test/tmp/app/assets/javascripts/accounts.js +2 -0
- data/test/tmp/app/assets/stylesheets/accounts.css +4 -0
- data/test/tmp/app/controllers/accounts_controller.rb +2 -0
- data/test/tmp/app/helpers/accounts_helper.rb +2 -0
- data/test/{serializers/tmp → tmp}/app/serializers/account_serializer.rb +0 -0
- data/test/tmp/config/routes.rb +1 -0
- data/test/unit/active_model/array_serializer/options_test.rb +16 -0
- data/test/unit/active_model/array_serializer/serialization_test.rb +17 -0
- data/test/unit/active_model/serializer/associations_test.rb +30 -0
- data/test/unit/active_model/serializer/has_many_test.rb +1 -1
- data/test/unit/active_model/serializer/has_one_test.rb +14 -0
- data/test/unit/active_model/serializer/options_test.rb +8 -0
- data/test/unit/active_model/serilizable_test.rb +50 -0
- metadata +94 -38
@@ -0,0 +1,25 @@
|
|
1
|
+
module ActiveModelSerializers
|
2
|
+
class Model
|
3
|
+
module Caching
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
attr_writer :updated_at
|
8
|
+
attributes :id
|
9
|
+
end
|
10
|
+
|
11
|
+
# Defaults to the downcased model name and updated_at
|
12
|
+
def cache_key
|
13
|
+
ActiveSupport::Cache.expand_cache_key([
|
14
|
+
self.class.model_name.name.downcase,
|
15
|
+
"#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}"
|
16
|
+
].compact)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Defaults to the time the serializer file was modified.
|
20
|
+
def updated_at
|
21
|
+
defined?(@updated_at) ? @updated_at : File.mtime(__FILE__)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# https://github.com/rails-api/active_model_serializers/pull/872
|
2
|
+
# approx ref 792fb8a9053f8db3c562dae4f40907a582dd1720 to test against
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
5
|
+
require 'rails'
|
6
|
+
require 'active_model'
|
7
|
+
require 'active_support'
|
8
|
+
require 'active_support/json'
|
9
|
+
require 'action_controller'
|
10
|
+
require 'action_controller/test_case'
|
11
|
+
require 'action_controller/railtie'
|
12
|
+
abort "Rails application already defined: #{Rails.application.class}" if Rails.application
|
13
|
+
|
14
|
+
class NullLogger < Logger
|
15
|
+
def initialize(*_args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(*_args, &_block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
class BenchmarkLogger < ActiveSupport::Logger
|
22
|
+
def initialize
|
23
|
+
@file = StringIO.new
|
24
|
+
super(@file)
|
25
|
+
end
|
26
|
+
|
27
|
+
def messages
|
28
|
+
@file.rewind
|
29
|
+
@file.read
|
30
|
+
end
|
31
|
+
end
|
32
|
+
# ref: https://gist.github.com/bf4/8744473
|
33
|
+
class BenchmarkApp < Rails::Application
|
34
|
+
# Set up production configuration
|
35
|
+
config.eager_load = true
|
36
|
+
config.cache_classes = true
|
37
|
+
# CONFIG: CACHE_ON={on,off}
|
38
|
+
config.action_controller.perform_caching = ENV['CACHE_ON'] != 'off'
|
39
|
+
config.action_controller.cache_store = ActiveSupport::Cache.lookup_store(:memory_store)
|
40
|
+
|
41
|
+
config.active_support.test_order = :random
|
42
|
+
config.secret_token = 'S' * 30
|
43
|
+
config.secret_key_base = 'abc123'
|
44
|
+
config.consider_all_requests_local = false
|
45
|
+
|
46
|
+
# otherwise deadlock occurred
|
47
|
+
config.middleware.delete 'Rack::Lock'
|
48
|
+
|
49
|
+
# to disable log files
|
50
|
+
config.logger = NullLogger.new
|
51
|
+
config.active_support.deprecation = :log
|
52
|
+
config.log_level = :info
|
53
|
+
end
|
54
|
+
|
55
|
+
require 'active_model_serializers'
|
56
|
+
|
57
|
+
# Initialize app before any serializers are defined, for running across revisions.
|
58
|
+
# ref: https://github.com/rails-api/active_model_serializers/pull/1478
|
59
|
+
Rails.application.initialize!
|
60
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'benchmark/ips'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
# Add benchmarking runner from ruby-bench-suite
|
5
|
+
# https://github.com/ruby-bench/ruby-bench-suite/blob/master/rails/benchmarks/support/benchmark_rails.rb
|
6
|
+
module Benchmark
|
7
|
+
module ActiveModelSerializers
|
8
|
+
module TestMethods
|
9
|
+
def request(method, path)
|
10
|
+
response = Rack::MockRequest.new(BenchmarkApp).send(method, path)
|
11
|
+
if response.status.in?([404, 500])
|
12
|
+
fail "omg, #{method}, #{path}, '#{response.status}', '#{response.body}'"
|
13
|
+
end
|
14
|
+
response
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# extend Benchmark with an `ams` method
|
19
|
+
def ams(label = nil, time:, disable_gc: true, warmup: 3, &block)
|
20
|
+
fail ArgumentError.new, 'block should be passed' unless block_given?
|
21
|
+
|
22
|
+
if disable_gc
|
23
|
+
GC.disable
|
24
|
+
else
|
25
|
+
GC.enable
|
26
|
+
end
|
27
|
+
|
28
|
+
report = Benchmark.ips(time, warmup, true) do |x|
|
29
|
+
x.report(label) { yield }
|
30
|
+
end
|
31
|
+
|
32
|
+
entry = report.entries.first
|
33
|
+
|
34
|
+
output = {
|
35
|
+
label: label,
|
36
|
+
version: ::ActiveModel::Serializer::VERSION.to_s,
|
37
|
+
rails_version: ::Rails.version.to_s,
|
38
|
+
iterations_per_second: entry.ips,
|
39
|
+
iterations_per_second_standard_deviation: entry.error_percentage,
|
40
|
+
total_allocated_objects_per_iteration: count_total_allocated_objects(&block)
|
41
|
+
}.to_json
|
42
|
+
|
43
|
+
puts output
|
44
|
+
output
|
45
|
+
end
|
46
|
+
|
47
|
+
def count_total_allocated_objects
|
48
|
+
if block_given?
|
49
|
+
key =
|
50
|
+
if RUBY_VERSION < '2.2'
|
51
|
+
:total_allocated_object
|
52
|
+
else
|
53
|
+
:total_allocated_objects
|
54
|
+
end
|
55
|
+
|
56
|
+
before = GC.stat[key]
|
57
|
+
yield
|
58
|
+
after = GC.stat[key]
|
59
|
+
after - before
|
60
|
+
else
|
61
|
+
-1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
extend Benchmark::ActiveModelSerializers
|
67
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative './benchmarking_support'
|
2
|
+
require_relative './app'
|
3
|
+
require_relative './setup'
|
4
|
+
|
5
|
+
time = 10
|
6
|
+
disable_gc = true
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
authors_query = Author.preload(:posts).preload(:profile)
|
11
|
+
author = authors_query.first
|
12
|
+
authors = authors_query.to_a
|
13
|
+
|
14
|
+
|
15
|
+
Benchmark.ams('Single: DefaultSerializer', time: time, disable_gc: disable_gc) do
|
16
|
+
ActiveModel::DefaultSerializer.new(author).to_json
|
17
|
+
end
|
18
|
+
|
19
|
+
Benchmark.ams('ArraySerializer', time: time, disable_gc: disable_gc) do
|
20
|
+
ActiveModel::ArraySerializer.new(authors).to_json
|
21
|
+
end
|
22
|
+
|
23
|
+
Benchmark.ams('ArraySerializer: each_serializer: DefaultSerializer', time: time, disable_gc: disable_gc) do
|
24
|
+
ActiveModel::ArraySerializer.new(authors, each_serializer:ActiveModel::DefaultSerializer).to_json
|
25
|
+
end
|
26
|
+
|
27
|
+
Benchmark.ams('FlatAuthorSerializer', time: time, disable_gc: disable_gc) do
|
28
|
+
FlatAuthorSerializer.new(author).to_json
|
29
|
+
end
|
30
|
+
|
31
|
+
Benchmark.ams('ArraySerializer: each_serializer: FlatAuthorSerializer', time: time, disable_gc: disable_gc) do
|
32
|
+
ActiveModel::ArraySerializer.new(authors, each_serializer: FlatAuthorSerializer).to_json
|
33
|
+
end
|
34
|
+
|
35
|
+
Benchmark.ams('AuthorWithDefaultRelationshipsSerializer', time: time, disable_gc: disable_gc) do
|
36
|
+
AuthorWithDefaultRelationshipsSerializer.new(author).to_json
|
37
|
+
end
|
38
|
+
|
39
|
+
Benchmark.ams('ArraySerializer: each_serializer: AuthorWithDefaultRelationshipsSerializer', time: time, disable_gc: disable_gc) do
|
40
|
+
ActiveModel::ArraySerializer.new(authors, each_serializer: AuthorWithDefaultRelationshipsSerializer).to_json
|
41
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
###########################################
|
2
|
+
# Setup active record models
|
3
|
+
##########################################
|
4
|
+
require 'active_record'
|
5
|
+
require 'sqlite3'
|
6
|
+
|
7
|
+
|
8
|
+
# Change the following to reflect your database settings
|
9
|
+
ActiveRecord::Base.establish_connection(
|
10
|
+
adapter: 'sqlite3',
|
11
|
+
database: ':memory:'
|
12
|
+
)
|
13
|
+
|
14
|
+
# Don't show migration output when constructing fake db
|
15
|
+
ActiveRecord::Migration.verbose = false
|
16
|
+
|
17
|
+
ActiveRecord::Schema.define do
|
18
|
+
create_table :authors, force: true do |t|
|
19
|
+
t.string :name
|
20
|
+
end
|
21
|
+
|
22
|
+
create_table :posts, force: true do |t|
|
23
|
+
t.text :body
|
24
|
+
t.string :title
|
25
|
+
t.references :author
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table :profiles, force: true do |t|
|
29
|
+
t.text :project_url
|
30
|
+
t.text :bio
|
31
|
+
t.date :birthday
|
32
|
+
t.references :author
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Author < ActiveRecord::Base
|
37
|
+
has_one :profile
|
38
|
+
has_many :posts
|
39
|
+
end
|
40
|
+
|
41
|
+
class Post < ActiveRecord::Base
|
42
|
+
belongs_to :author
|
43
|
+
end
|
44
|
+
|
45
|
+
class Profile < ActiveRecord::Base
|
46
|
+
belongs_to :author
|
47
|
+
end
|
48
|
+
|
49
|
+
# Build out the data to serialize
|
50
|
+
author = Author.create(name: 'Preston Sego')
|
51
|
+
Profile.create(project_url: 'https://github.com/NullVoxPopuli', author: author)
|
52
|
+
50.times do
|
53
|
+
Post.create(
|
54
|
+
body: 'something about how password restrictions are evil, and less secure, and with the math to prove it.',
|
55
|
+
title: 'Your bank is does not know how to do security',
|
56
|
+
author: author
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
ActiveModel::Serializer.root = false
|
61
|
+
ActiveModel::ArraySerializer.root = false
|
62
|
+
|
63
|
+
class FlatAuthorSerializer < ActiveModel::Serializer
|
64
|
+
attributes :id, :name
|
65
|
+
end
|
66
|
+
|
67
|
+
class AuthorWithDefaultRelationshipsSerializer < ActiveModel::Serializer
|
68
|
+
attributes :id, :name
|
69
|
+
|
70
|
+
has_one :profile
|
71
|
+
has_many :posts
|
72
|
+
end
|
73
|
+
|
74
|
+
# For debugging SQL output
|
75
|
+
#ActiveRecord::Base.logger = Logger.new(STDERR)
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -74,6 +74,10 @@ class ARSectionSerializer < ActiveModel::Serializer
|
|
74
74
|
attributes 'name'
|
75
75
|
end
|
76
76
|
|
77
|
+
class AREmbeddedSerializer < ActiveModel::Serializer
|
78
|
+
has_many :ar_tags, :ar_comments
|
79
|
+
end
|
80
|
+
|
77
81
|
ARPost.create(title: 'New post',
|
78
82
|
body: 'A body!!!',
|
79
83
|
ar_section: ARSection.create(name: 'ruby')).tap do |post|
|
data/test/fixtures/poro.rb
CHANGED
@@ -49,7 +49,27 @@ end
|
|
49
49
|
|
50
50
|
class SpecialPost < Post
|
51
51
|
def special_comment
|
52
|
-
@
|
52
|
+
@special_comment ||= Comment.new(content: 'special')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Type < Model
|
57
|
+
end
|
58
|
+
|
59
|
+
class SelfReferencingUser < Model
|
60
|
+
def type
|
61
|
+
@type ||= Type.new(name: 'N1')
|
62
|
+
end
|
63
|
+
def parent
|
64
|
+
@parent ||= SelfReferencingUserParent.new(name: 'N1')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class SelfReferencingUserParent < Model
|
69
|
+
def type
|
70
|
+
@type ||= Type.new(name: 'N2')
|
71
|
+
end
|
72
|
+
def parent
|
53
73
|
end
|
54
74
|
end
|
55
75
|
|
@@ -87,8 +107,24 @@ class UserSerializer < ActiveModel::Serializer
|
|
87
107
|
has_one :profile
|
88
108
|
end
|
89
109
|
|
110
|
+
class TypeSerializer < ActiveModel::Serializer
|
111
|
+
attributes :name
|
112
|
+
end
|
113
|
+
|
114
|
+
class SelfReferencingUserParentSerializer < ActiveModel::Serializer
|
115
|
+
attributes :name
|
116
|
+
has_one :type, serializer: TypeSerializer, embed: :ids, include: true
|
117
|
+
end
|
118
|
+
|
119
|
+
class SelfReferencingUserSerializer < ActiveModel::Serializer
|
120
|
+
attributes :name
|
121
|
+
|
122
|
+
has_one :type, serializer: TypeSerializer, embed: :ids, include: true
|
123
|
+
has_one :parent, serializer: SelfReferencingUserSerializer, embed: :ids, include: true
|
124
|
+
end
|
125
|
+
|
90
126
|
class UserInfoSerializer < ActiveModel::Serializer
|
91
|
-
has_one :user
|
127
|
+
has_one :user, serializer: UserSerializer
|
92
128
|
end
|
93
129
|
|
94
130
|
class ProfileSerializer < ActiveModel::Serializer
|
@@ -109,6 +145,13 @@ end
|
|
109
145
|
class PostSerializer < ActiveModel::Serializer
|
110
146
|
attributes :title, :body
|
111
147
|
|
148
|
+
def title
|
149
|
+
keyword = serialization_options[:highlight_keyword]
|
150
|
+
title = object.read_attribute_for_serialization(:title)
|
151
|
+
title = title.gsub(keyword,"'#{keyword}'") if keyword
|
152
|
+
title
|
153
|
+
end
|
154
|
+
|
112
155
|
has_many :comments
|
113
156
|
end
|
114
157
|
|
@@ -169,7 +212,7 @@ end
|
|
169
212
|
|
170
213
|
class NameKeyPostSerializer < ActiveModel::Serializer
|
171
214
|
attributes :title, :body
|
172
|
-
|
215
|
+
|
173
216
|
has_many :comments
|
174
217
|
end
|
175
218
|
|
@@ -19,6 +19,10 @@ module ActionController
|
|
19
19
|
def render_comments
|
20
20
|
render json: [Comment.new(content: 'Comment 1')]
|
21
21
|
end
|
22
|
+
|
23
|
+
def render_hash
|
24
|
+
render json: {message: 'not found'}, status: 404
|
25
|
+
end
|
22
26
|
end
|
23
27
|
|
24
28
|
tests TestNamespace::MyController
|
@@ -42,6 +46,11 @@ module ActionController
|
|
42
46
|
get :render_comments
|
43
47
|
assert_serializer CommentSerializer
|
44
48
|
end
|
49
|
+
|
50
|
+
def test_render_hash_regression
|
51
|
+
get :render_hash
|
52
|
+
assert_equal JSON.parse(response.body), {'message' => 'not found'}
|
53
|
+
end
|
45
54
|
end
|
46
55
|
|
47
56
|
class OptionNamespacedSerializationTest < ActionController::TestCase
|
@@ -93,4 +102,4 @@ module ActionController
|
|
93
102
|
end
|
94
103
|
|
95
104
|
end
|
96
|
-
end
|
105
|
+
end
|
@@ -213,8 +213,8 @@ module ActionController
|
|
213
213
|
class LowerCamelWoRootSerializerTest < ActionController::TestCase
|
214
214
|
class WebLogController < ActionController::Base
|
215
215
|
def render_without_root
|
216
|
-
render json: WebLog.new({name: 'Name 1', display_name: 'Display Name 1'}),
|
217
|
-
root: false,
|
216
|
+
render json: WebLog.new({name: 'Name 1', display_name: 'Display Name 1'}),
|
217
|
+
root: false,
|
218
218
|
serializer: WebLogLowerCamelSerializer
|
219
219
|
end
|
220
220
|
end
|
@@ -231,9 +231,9 @@ module ActionController
|
|
231
231
|
class LowerCamelArrayWoRootSerializerTest < ActionController::TestCase
|
232
232
|
class WebLogController < ActionController::Base
|
233
233
|
def render_array_without_root
|
234
|
-
render json: [WebLog.new({name: 'Name 1', display_name: 'Display Name 1'}),
|
235
|
-
WebLog.new({name: 'Name 2', display_name: 'Display Name 2'})],
|
236
|
-
root: false,
|
234
|
+
render json: [WebLog.new({name: 'Name 1', display_name: 'Display Name 1'}),
|
235
|
+
WebLog.new({name: 'Name 2', display_name: 'Display Name 2'})],
|
236
|
+
root: false,
|
237
237
|
each_serializer: WebLogLowerCamelSerializer
|
238
238
|
end
|
239
239
|
end
|