agi_active_model_serializers 0.10.7

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.
Files changed (220) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE.md +29 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  4. data/.gitignore +35 -0
  5. data/.rubocop.yml +102 -0
  6. data/.simplecov +110 -0
  7. data/.travis.yml +51 -0
  8. data/CHANGELOG.md +612 -0
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +56 -0
  12. data/MIT-LICENSE +22 -0
  13. data/README.md +307 -0
  14. data/Rakefile +103 -0
  15. data/active_model_serializers.gemspec +63 -0
  16. data/appveyor.yml +24 -0
  17. data/bin/bench +171 -0
  18. data/bin/bench_regression +316 -0
  19. data/bin/serve_benchmark +39 -0
  20. data/docs/README.md +41 -0
  21. data/docs/STYLE.md +58 -0
  22. data/docs/general/adapters.md +247 -0
  23. data/docs/general/caching.md +58 -0
  24. data/docs/general/configuration_options.md +169 -0
  25. data/docs/general/deserialization.md +100 -0
  26. data/docs/general/fields.md +31 -0
  27. data/docs/general/getting_started.md +133 -0
  28. data/docs/general/instrumentation.md +40 -0
  29. data/docs/general/key_transforms.md +40 -0
  30. data/docs/general/logging.md +14 -0
  31. data/docs/general/rendering.md +279 -0
  32. data/docs/general/serializers.md +461 -0
  33. data/docs/how-open-source-maintained.jpg +0 -0
  34. data/docs/howto/add_pagination_links.md +138 -0
  35. data/docs/howto/add_relationship_links.md +137 -0
  36. data/docs/howto/add_root_key.md +55 -0
  37. data/docs/howto/grape_integration.md +42 -0
  38. data/docs/howto/outside_controller_use.md +65 -0
  39. data/docs/howto/passing_arbitrary_options.md +27 -0
  40. data/docs/howto/serialize_poro.md +32 -0
  41. data/docs/howto/test.md +154 -0
  42. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  43. data/docs/integrations/ember-and-json-api.md +144 -0
  44. data/docs/integrations/grape.md +19 -0
  45. data/docs/jsonapi/errors.md +56 -0
  46. data/docs/jsonapi/schema.md +151 -0
  47. data/docs/jsonapi/schema/schema.json +366 -0
  48. data/docs/rfcs/0000-namespace.md +106 -0
  49. data/docs/rfcs/template.md +15 -0
  50. data/lib/action_controller/serialization.rb +66 -0
  51. data/lib/active_model/serializable_resource.rb +11 -0
  52. data/lib/active_model/serializer.rb +231 -0
  53. data/lib/active_model/serializer/adapter.rb +24 -0
  54. data/lib/active_model/serializer/adapter/attributes.rb +15 -0
  55. data/lib/active_model/serializer/adapter/base.rb +18 -0
  56. data/lib/active_model/serializer/adapter/json.rb +15 -0
  57. data/lib/active_model/serializer/adapter/json_api.rb +15 -0
  58. data/lib/active_model/serializer/adapter/null.rb +15 -0
  59. data/lib/active_model/serializer/array_serializer.rb +12 -0
  60. data/lib/active_model/serializer/association.rb +34 -0
  61. data/lib/active_model/serializer/attribute.rb +25 -0
  62. data/lib/active_model/serializer/belongs_to_reflection.rb +7 -0
  63. data/lib/active_model/serializer/collection_reflection.rb +7 -0
  64. data/lib/active_model/serializer/collection_serializer.rb +87 -0
  65. data/lib/active_model/serializer/concerns/associations.rb +102 -0
  66. data/lib/active_model/serializer/concerns/attributes.rb +82 -0
  67. data/lib/active_model/serializer/concerns/caching.rb +292 -0
  68. data/lib/active_model/serializer/concerns/configuration.rb +59 -0
  69. data/lib/active_model/serializer/concerns/links.rb +35 -0
  70. data/lib/active_model/serializer/concerns/meta.rb +29 -0
  71. data/lib/active_model/serializer/concerns/type.rb +25 -0
  72. data/lib/active_model/serializer/error_serializer.rb +14 -0
  73. data/lib/active_model/serializer/errors_serializer.rb +32 -0
  74. data/lib/active_model/serializer/field.rb +90 -0
  75. data/lib/active_model/serializer/fieldset.rb +31 -0
  76. data/lib/active_model/serializer/has_many_reflection.rb +7 -0
  77. data/lib/active_model/serializer/has_one_reflection.rb +7 -0
  78. data/lib/active_model/serializer/lint.rb +150 -0
  79. data/lib/active_model/serializer/null.rb +17 -0
  80. data/lib/active_model/serializer/reflection.rb +163 -0
  81. data/lib/active_model/serializer/singular_reflection.rb +7 -0
  82. data/lib/active_model/serializer/version.rb +5 -0
  83. data/lib/active_model_serializers.rb +53 -0
  84. data/lib/active_model_serializers/adapter.rb +98 -0
  85. data/lib/active_model_serializers/adapter/attributes.rb +13 -0
  86. data/lib/active_model_serializers/adapter/base.rb +83 -0
  87. data/lib/active_model_serializers/adapter/json.rb +21 -0
  88. data/lib/active_model_serializers/adapter/json_api.rb +517 -0
  89. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
  90. data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
  91. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
  92. data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
  93. data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
  94. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +69 -0
  95. data/lib/active_model_serializers/adapter/json_api/relationship.rb +63 -0
  96. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +51 -0
  97. data/lib/active_model_serializers/adapter/null.rb +9 -0
  98. data/lib/active_model_serializers/callbacks.rb +55 -0
  99. data/lib/active_model_serializers/deprecate.rb +54 -0
  100. data/lib/active_model_serializers/deserialization.rb +15 -0
  101. data/lib/active_model_serializers/json_pointer.rb +14 -0
  102. data/lib/active_model_serializers/logging.rb +122 -0
  103. data/lib/active_model_serializers/lookup_chain.rb +80 -0
  104. data/lib/active_model_serializers/model.rb +71 -0
  105. data/lib/active_model_serializers/railtie.rb +48 -0
  106. data/lib/active_model_serializers/register_jsonapi_renderer.rb +78 -0
  107. data/lib/active_model_serializers/serializable_resource.rb +82 -0
  108. data/lib/active_model_serializers/serialization_context.rb +39 -0
  109. data/lib/active_model_serializers/test.rb +7 -0
  110. data/lib/active_model_serializers/test/schema.rb +138 -0
  111. data/lib/active_model_serializers/test/serializer.rb +125 -0
  112. data/lib/generators/rails/USAGE +6 -0
  113. data/lib/generators/rails/resource_override.rb +10 -0
  114. data/lib/generators/rails/serializer_generator.rb +36 -0
  115. data/lib/generators/rails/templates/serializer.rb.erb +15 -0
  116. data/lib/grape/active_model_serializers.rb +16 -0
  117. data/lib/grape/formatters/active_model_serializers.rb +32 -0
  118. data/lib/grape/helpers/active_model_serializers.rb +17 -0
  119. data/test/action_controller/adapter_selector_test.rb +53 -0
  120. data/test/action_controller/explicit_serializer_test.rb +135 -0
  121. data/test/action_controller/json/include_test.rb +246 -0
  122. data/test/action_controller/json_api/deserialization_test.rb +112 -0
  123. data/test/action_controller/json_api/errors_test.rb +40 -0
  124. data/test/action_controller/json_api/fields_test.rb +66 -0
  125. data/test/action_controller/json_api/linked_test.rb +202 -0
  126. data/test/action_controller/json_api/pagination_test.rb +116 -0
  127. data/test/action_controller/json_api/transform_test.rb +189 -0
  128. data/test/action_controller/lookup_proc_test.rb +49 -0
  129. data/test/action_controller/namespace_lookup_test.rb +232 -0
  130. data/test/action_controller/serialization_scope_name_test.rb +229 -0
  131. data/test/action_controller/serialization_test.rb +472 -0
  132. data/test/active_model_serializers/adapter_for_test.rb +208 -0
  133. data/test/active_model_serializers/json_pointer_test.rb +22 -0
  134. data/test/active_model_serializers/logging_test.rb +77 -0
  135. data/test/active_model_serializers/model_test.rb +69 -0
  136. data/test/active_model_serializers/railtie_test_isolated.rb +63 -0
  137. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +161 -0
  138. data/test/active_model_serializers/serialization_context_test_isolated.rb +71 -0
  139. data/test/active_model_serializers/test/schema_test.rb +131 -0
  140. data/test/active_model_serializers/test/serializer_test.rb +62 -0
  141. data/test/active_record_test.rb +9 -0
  142. data/test/adapter/attributes_test.rb +43 -0
  143. data/test/adapter/deprecation_test.rb +100 -0
  144. data/test/adapter/json/belongs_to_test.rb +45 -0
  145. data/test/adapter/json/collection_test.rb +104 -0
  146. data/test/adapter/json/has_many_test.rb +45 -0
  147. data/test/adapter/json/transform_test.rb +93 -0
  148. data/test/adapter/json_api/belongs_to_test.rb +155 -0
  149. data/test/adapter/json_api/collection_test.rb +96 -0
  150. data/test/adapter/json_api/errors_test.rb +76 -0
  151. data/test/adapter/json_api/fields_test.rb +96 -0
  152. data/test/adapter/json_api/has_many_embed_ids_test.rb +43 -0
  153. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
  154. data/test/adapter/json_api/has_many_test.rb +165 -0
  155. data/test/adapter/json_api/has_one_test.rb +80 -0
  156. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +168 -0
  157. data/test/adapter/json_api/json_api_test.rb +33 -0
  158. data/test/adapter/json_api/linked_test.rb +413 -0
  159. data/test/adapter/json_api/links_test.rb +95 -0
  160. data/test/adapter/json_api/pagination_links_test.rb +193 -0
  161. data/test/adapter/json_api/parse_test.rb +137 -0
  162. data/test/adapter/json_api/relationship_test.rb +397 -0
  163. data/test/adapter/json_api/resource_identifier_test.rb +110 -0
  164. data/test/adapter/json_api/resource_meta_test.rb +100 -0
  165. data/test/adapter/json_api/toplevel_jsonapi_test.rb +82 -0
  166. data/test/adapter/json_api/transform_test.rb +512 -0
  167. data/test/adapter/json_api/type_test.rb +61 -0
  168. data/test/adapter/json_test.rb +46 -0
  169. data/test/adapter/null_test.rb +22 -0
  170. data/test/adapter/polymorphic_test.rb +171 -0
  171. data/test/adapter_test.rb +67 -0
  172. data/test/array_serializer_test.rb +22 -0
  173. data/test/benchmark/app.rb +65 -0
  174. data/test/benchmark/benchmarking_support.rb +67 -0
  175. data/test/benchmark/bm_active_record.rb +81 -0
  176. data/test/benchmark/bm_adapter.rb +38 -0
  177. data/test/benchmark/bm_caching.rb +119 -0
  178. data/test/benchmark/bm_lookup_chain.rb +83 -0
  179. data/test/benchmark/bm_transform.rb +45 -0
  180. data/test/benchmark/config.ru +3 -0
  181. data/test/benchmark/controllers.rb +83 -0
  182. data/test/benchmark/fixtures.rb +219 -0
  183. data/test/cache_test.rb +595 -0
  184. data/test/collection_serializer_test.rb +123 -0
  185. data/test/fixtures/active_record.rb +113 -0
  186. data/test/fixtures/poro.rb +232 -0
  187. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  188. data/test/generators/serializer_generator_test.rb +74 -0
  189. data/test/grape_test.rb +178 -0
  190. data/test/lint_test.rb +49 -0
  191. data/test/logger_test.rb +20 -0
  192. data/test/poro_test.rb +9 -0
  193. data/test/serializable_resource_test.rb +79 -0
  194. data/test/serializers/association_macros_test.rb +37 -0
  195. data/test/serializers/associations_test.rb +383 -0
  196. data/test/serializers/attribute_test.rb +153 -0
  197. data/test/serializers/attributes_test.rb +52 -0
  198. data/test/serializers/caching_configuration_test_isolated.rb +170 -0
  199. data/test/serializers/configuration_test.rb +32 -0
  200. data/test/serializers/fieldset_test.rb +14 -0
  201. data/test/serializers/meta_test.rb +202 -0
  202. data/test/serializers/options_test.rb +32 -0
  203. data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
  204. data/test/serializers/root_test.rb +21 -0
  205. data/test/serializers/serialization_test.rb +55 -0
  206. data/test/serializers/serializer_for_test.rb +136 -0
  207. data/test/serializers/serializer_for_with_namespace_test.rb +88 -0
  208. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  209. data/test/support/isolated_unit.rb +82 -0
  210. data/test/support/rails5_shims.rb +53 -0
  211. data/test/support/rails_app.rb +36 -0
  212. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  213. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
  214. data/test/support/schemas/custom/show.json +7 -0
  215. data/test/support/schemas/hyper_schema.json +93 -0
  216. data/test/support/schemas/render_using_json_api.json +43 -0
  217. data/test/support/schemas/simple_json_pointers.json +10 -0
  218. data/test/support/serialization_testing.rb +71 -0
  219. data/test/test_helper.rb +58 -0
  220. metadata +602 -0
@@ -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,81 @@
1
+ require_relative './benchmarking_support'
2
+ require_relative './app'
3
+
4
+ time = 10
5
+ disable_gc = true
6
+
7
+ # This is to disable any key transform effects that may impact performance
8
+ ActiveModelSerializers.config.key_transform = :unaltered
9
+
10
+ ###########################################
11
+ # Setup active record models
12
+ ##########################################
13
+ require 'active_record'
14
+ require 'sqlite3'
15
+
16
+ # For debugging SQL output
17
+ # ActiveRecord::Base.logger = Logger.new(STDERR)
18
+
19
+ # Change the following to reflect your database settings
20
+ ActiveRecord::Base.establish_connection(
21
+ adapter: 'sqlite3',
22
+ database: ':memory:'
23
+ )
24
+
25
+ # Don't show migration output when constructing fake db
26
+ ActiveRecord::Migration.verbose = false
27
+
28
+ ActiveRecord::Schema.define do
29
+ create_table :authors, force: true do |t|
30
+ t.string :name
31
+ end
32
+
33
+ create_table :posts, force: true do |t|
34
+ t.text :body
35
+ t.string :title
36
+ t.references :author
37
+ end
38
+
39
+ create_table :profiles, force: true do |t|
40
+ t.text :project_url
41
+ t.text :bio
42
+ t.date :birthday
43
+ t.references :author
44
+ end
45
+ end
46
+
47
+ class Author < ActiveRecord::Base
48
+ has_one :profile
49
+ has_many :posts
50
+ end
51
+
52
+ class Post < ActiveRecord::Base
53
+ belongs_to :author
54
+ end
55
+
56
+ class Profile < ActiveRecord::Base
57
+ belongs_to :author
58
+ end
59
+
60
+ # Build out the data to serialize
61
+ author = Author.create(name: 'Preston Sego')
62
+ Profile.create(project_url: 'https://github.com/NullVoxPopuli', author: author)
63
+ 50.times do
64
+ Post.create(
65
+ body: 'something about how password restrictions are evil, and less secure, and with the math to prove it.',
66
+ title: 'Your bank is does not know how to do security',
67
+ author: author
68
+ )
69
+ end
70
+
71
+ Benchmark.ams('AR: attributes', time: time, disable_gc: disable_gc) do
72
+ ActiveModelSerializers::SerializableResource.new(author, adapter: :attributes, include: 'profile,posts').serializable_hash
73
+ end
74
+
75
+ Benchmark.ams('AR: json', time: time, disable_gc: disable_gc) do
76
+ ActiveModelSerializers::SerializableResource.new(author, adapter: :json, include: 'profile,posts').serializable_hash
77
+ end
78
+
79
+ Benchmark.ams('AR: JSON API', time: time, disable_gc: disable_gc) do
80
+ ActiveModelSerializers::SerializableResource.new(author, adapter: :json_api, include: 'profile,posts').serializable_hash
81
+ end
@@ -0,0 +1,38 @@
1
+ require_relative './benchmarking_support'
2
+ require_relative './app'
3
+
4
+ time = 10
5
+ disable_gc = true
6
+ ActiveModelSerializers.config.key_transform = :unaltered
7
+ has_many_relationships = (0..60).map do |i|
8
+ HasManyRelationship.new(id: i, body: 'ZOMG A HAS MANY RELATIONSHIP')
9
+ end
10
+ has_one_relationship = HasOneRelationship.new(
11
+ id: 42,
12
+ first_name: 'Joao',
13
+ last_name: 'Moura'
14
+ )
15
+ primary_resource = PrimaryResource.new(
16
+ id: 1337,
17
+ title: 'New PrimaryResource',
18
+ virtual_attribute: nil,
19
+ body: 'Body',
20
+ has_many_relationships: has_many_relationships,
21
+ has_one_relationship: has_one_relationship
22
+ )
23
+ serializer = PrimaryResourceSerializer.new(primary_resource)
24
+
25
+ Benchmark.ams('attributes', time: time, disable_gc: disable_gc) do
26
+ attributes = ActiveModelSerializers::Adapter::Attributes.new(serializer)
27
+ attributes.as_json
28
+ end
29
+
30
+ Benchmark.ams('json_api', time: time, disable_gc: disable_gc) do
31
+ json_api = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
32
+ json_api.as_json
33
+ end
34
+
35
+ Benchmark.ams('json', time: time, disable_gc: disable_gc) do
36
+ json = ActiveModelSerializers::Adapter::Json.new(serializer)
37
+ json.as_json
38
+ end
@@ -0,0 +1,119 @@
1
+ require_relative './benchmarking_support'
2
+ require_relative './app'
3
+
4
+ # https://github.com/ruby-bench/ruby-bench-suite/blob/8ad567f7e43a044ae48c36833218423bb1e2bd9d/rails/benchmarks/actionpack_router.rb
5
+ class ApiAssertion
6
+ include Benchmark::ActiveModelSerializers::TestMethods
7
+ class BadRevisionError < StandardError; end
8
+
9
+ def valid?
10
+ caching = get_caching
11
+ caching[:body].delete('meta')
12
+ non_caching = get_non_caching
13
+ non_caching[:body].delete('meta')
14
+ assert_responses(caching, non_caching)
15
+ rescue BadRevisionError => e
16
+ msg = { error: e.message }
17
+ STDERR.puts msg
18
+ STDOUT.puts msg
19
+ exit 1
20
+ end
21
+
22
+ def get_status(on_off = 'on'.freeze)
23
+ get("/status/#{on_off}")
24
+ end
25
+
26
+ def clear
27
+ get('/clear')
28
+ end
29
+
30
+ def get_caching(on_off = 'on'.freeze)
31
+ get("/caching/#{on_off}")
32
+ end
33
+
34
+ def get_fragment_caching(on_off = 'on'.freeze)
35
+ get("/fragment_caching/#{on_off}")
36
+ end
37
+
38
+ def get_non_caching(on_off = 'on'.freeze)
39
+ get("/non_caching/#{on_off}")
40
+ end
41
+
42
+ def debug(msg = '')
43
+ if block_given? && ENV['DEBUG'] =~ /\Atrue|on|0\z/i
44
+ STDERR.puts yield
45
+ else
46
+ STDERR.puts msg
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def assert_responses(caching, non_caching)
53
+ assert_equal(caching[:code], 200, "Caching response failed: #{caching}")
54
+ assert_equal(caching[:body], expected, "Caching response format failed: \n+ #{caching[:body]}\n- #{expected}")
55
+ assert_equal(caching[:content_type], 'application/json; charset=utf-8', "Caching response content type failed: \n+ #{caching[:content_type]}\n- application/json")
56
+ assert_equal(non_caching[:code], 200, "Non caching response failed: #{non_caching}")
57
+ assert_equal(non_caching[:body], expected, "Non Caching response format failed: \n+ #{non_caching[:body]}\n- #{expected}")
58
+ assert_equal(non_caching[:content_type], 'application/json; charset=utf-8', "Non caching response content type failed: \n+ #{non_caching[:content_type]}\n- application/json")
59
+ end
60
+
61
+ def get(url)
62
+ response = request(:get, url)
63
+ { code: response.status, body: JSON.load(response.body), content_type: response.content_type }
64
+ end
65
+
66
+ def expected
67
+ @expected ||=
68
+ {
69
+ 'primary_resource' => {
70
+ 'id' => 1337,
71
+ 'title' => 'New PrimaryResource',
72
+ 'body' => 'Body',
73
+ 'virtual_attribute' => {
74
+ 'id' => 999,
75
+ 'name' => 'Free-Range Virtual Attribute'
76
+ },
77
+ 'has_one_relationship' => {
78
+ 'id' => 42,
79
+ 'first_name' => 'Joao',
80
+ 'last_name' => 'Moura'
81
+ },
82
+ 'has_many_relationships' => [
83
+ {
84
+ 'id' => 1,
85
+ 'body' => 'ZOMG A HAS MANY RELATIONSHIP'
86
+ }
87
+ ]
88
+ }
89
+ }
90
+ end
91
+
92
+ def assert_equal(expected, actual, message)
93
+ return true if expected == actual
94
+ if ENV['FAIL_ASSERTION'] =~ /\Atrue|on|0\z/i # rubocop:disable Style/GuardClause
95
+ fail BadRevisionError, message
96
+ else
97
+ STDERR.puts message unless ENV['SUMMARIZE']
98
+ end
99
+ end
100
+ end
101
+ assertion = ApiAssertion.new
102
+ assertion.valid?
103
+ assertion.debug { assertion.get_status }
104
+
105
+ time = 10
106
+ {
107
+ 'caching on: caching serializers: gc off' => { disable_gc: true, send: [:get_caching, 'on'] },
108
+ 'caching on: fragment caching serializers: gc off' => { disable_gc: true, send: [:get_fragment_caching, 'on'] },
109
+ 'caching on: non-caching serializers: gc off' => { disable_gc: true, send: [:get_non_caching, 'on'] },
110
+ 'caching off: caching serializers: gc off' => { disable_gc: true, send: [:get_caching, 'off'] },
111
+ 'caching off: fragment caching serializers: gc off' => { disable_gc: true, send: [:get_fragment_caching, 'off'] },
112
+ 'caching off: non-caching serializers: gc off' => { disable_gc: true, send: [:get_non_caching, 'off'] }
113
+ }.each do |label, options|
114
+ assertion.clear
115
+ Benchmark.ams(label, time: time, disable_gc: options[:disable_gc]) do
116
+ assertion.send(*options[:send])
117
+ end
118
+ assertion.debug { assertion.get_status(options[:send][-1]) }
119
+ end
@@ -0,0 +1,83 @@
1
+ require_relative './benchmarking_support'
2
+ require_relative './app'
3
+
4
+ time = 10
5
+ disable_gc = true
6
+ ActiveModelSerializers.config.key_transform = :unaltered
7
+
8
+ module AmsBench
9
+ module Api
10
+ module V1
11
+ class PrimaryResourceSerializer < ActiveModel::Serializer
12
+ attributes :title, :body
13
+
14
+ has_many :has_many_relationships
15
+ end
16
+
17
+ class HasManyRelationshipSerializer < ActiveModel::Serializer
18
+ attribute :body
19
+ end
20
+ end
21
+ end
22
+ class PrimaryResourceSerializer < ActiveModel::Serializer
23
+ attributes :title, :body
24
+
25
+ has_many :has_many_relationships
26
+
27
+ class HasManyRelationshipSerializer < ActiveModel::Serializer
28
+ attribute :body
29
+ end
30
+ end
31
+ end
32
+
33
+ resource = PrimaryResource.new(
34
+ id: 1,
35
+ title: 'title',
36
+ body: 'body',
37
+ has_many_relationships: [
38
+ HasManyRelationship.new(id: 1, body: 'body1'),
39
+ HasManyRelationship.new(id: 2, body: 'body1')
40
+ ]
41
+ )
42
+
43
+ serialization = lambda do
44
+ ActiveModelSerializers::SerializableResource.new(resource, serializer: AmsBench::PrimaryResourceSerializer).as_json
45
+ ActiveModelSerializers::SerializableResource.new(resource, namespace: AmsBench::Api::V1).as_json
46
+ ActiveModelSerializers::SerializableResource.new(resource).as_json
47
+ end
48
+
49
+ def clear_cache
50
+ AmsBench::PrimaryResourceSerializer.serializers_cache.clear
51
+ AmsBench::Api::V1::PrimaryResourceSerializer.serializers_cache.clear
52
+ ActiveModel::Serializer.serializers_cache.clear
53
+ end
54
+
55
+ configurable = lambda do
56
+ clear_cache
57
+ Benchmark.ams('Configurable Lookup Chain', time: time, disable_gc: disable_gc, &serialization)
58
+ end
59
+
60
+ old = lambda do
61
+ clear_cache
62
+ module ActiveModel
63
+ class Serializer
64
+ def self.serializer_lookup_chain_for(klass, namespace = nil)
65
+ chain = []
66
+
67
+ resource_class_name = klass.name.demodulize
68
+ resource_namespace = klass.name.deconstantize
69
+ serializer_class_name = "#{resource_class_name}Serializer"
70
+
71
+ chain.push("#{namespace}::#{serializer_class_name}") if namespace
72
+ chain.push("#{name}::#{serializer_class_name}") if self != ActiveModel::Serializer
73
+ chain.push("#{resource_namespace}::#{serializer_class_name}")
74
+ chain
75
+ end
76
+ end
77
+ end
78
+
79
+ Benchmark.ams('Old Lookup Chain (v0.10)', time: time, disable_gc: disable_gc, &serialization)
80
+ end
81
+
82
+ configurable.call
83
+ old.call
@@ -0,0 +1,45 @@
1
+ require_relative './benchmarking_support'
2
+ require_relative './app'
3
+
4
+ time = 10
5
+ disable_gc = true
6
+ ActiveModelSerializers.config.key_transform = :unaltered
7
+ has_many_relationships = (0..50).map do |i|
8
+ HasManyRelationship.new(id: i, body: 'ZOMG A HAS MANY RELATIONSHIP')
9
+ end
10
+ has_one_relationship = HasOneRelationship.new(
11
+ id: 42,
12
+ first_name: 'Joao',
13
+ last_name: 'Moura'
14
+ )
15
+ primary_resource = PrimaryResource.new(
16
+ id: 1337,
17
+ title: 'New PrimaryResource',
18
+ virtual_attribute: nil,
19
+ body: 'Body',
20
+ has_many_relationships: has_many_relationships,
21
+ has_one_relationship: has_one_relationship
22
+ )
23
+ serializer = PrimaryResourceSerializer.new(primary_resource)
24
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
25
+ serialization = adapter.as_json
26
+
27
+ Benchmark.ams('camel', time: time, disable_gc: disable_gc) do
28
+ CaseTransform.camel(serialization)
29
+ end
30
+
31
+ Benchmark.ams('camel_lower', time: time, disable_gc: disable_gc) do
32
+ CaseTransform.camel_lower(serialization)
33
+ end
34
+
35
+ Benchmark.ams('dash', time: time, disable_gc: disable_gc) do
36
+ CaseTransform.dash(serialization)
37
+ end
38
+
39
+ Benchmark.ams('unaltered', time: time, disable_gc: disable_gc) do
40
+ CaseTransform.unaltered(serialization)
41
+ end
42
+
43
+ Benchmark.ams('underscore', time: time, disable_gc: disable_gc) do
44
+ CaseTransform.underscore(serialization)
45
+ end
@@ -0,0 +1,3 @@
1
+ require File.expand_path(['..', 'app'].join(File::SEPARATOR), __FILE__)
2
+
3
+ run Rails.application
@@ -0,0 +1,83 @@
1
+ class PrimaryResourceController < ActionController::Base
2
+ PRIMARY_RESOURCE =
3
+ begin
4
+ if ENV['BENCH_STRESS']
5
+ has_many_relationships = (0..50).map do |i|
6
+ HasManyRelationship.new(id: i, body: 'ZOMG A HAS MANY RELATIONSHIP')
7
+ end
8
+ else
9
+ has_many_relationships = [HasManyRelationship.new(id: 1, body: 'ZOMG A HAS MANY RELATIONSHIP')]
10
+ end
11
+ has_one_relationship = HasOneRelationship.new(id: 42, first_name: 'Joao', last_name: 'Moura')
12
+ PrimaryResource.new(id: 1337, title: 'New PrimaryResource', virtual_attribute: nil, body: 'Body', has_many_relationships: has_many_relationships, has_one_relationship: has_one_relationship)
13
+ end
14
+
15
+ def render_with_caching_serializer
16
+ toggle_cache_status
17
+ render json: PRIMARY_RESOURCE, serializer: CachingPrimaryResourceSerializer, adapter: :json, meta: { caching: perform_caching }
18
+ end
19
+
20
+ def render_with_fragment_caching_serializer
21
+ toggle_cache_status
22
+ render json: PRIMARY_RESOURCE, serializer: FragmentCachingPrimaryResourceSerializer, adapter: :json, meta: { caching: perform_caching }
23
+ end
24
+
25
+ def render_with_non_caching_serializer
26
+ toggle_cache_status
27
+ render json: PRIMARY_RESOURCE, adapter: :json, meta: { caching: perform_caching }
28
+ end
29
+
30
+ def render_cache_status
31
+ toggle_cache_status
32
+ # Uncomment to debug
33
+ # STDERR.puts cache_store.class
34
+ # STDERR.puts cache_dependencies
35
+ # ActiveSupport::Cache::Store.logger.debug [ActiveModelSerializers.config.cache_store, ActiveModelSerializers.config.perform_caching, CachingPrimaryResourceSerializer._cache, perform_caching, params].inspect
36
+ render json: { caching: perform_caching, meta: { cache_log: cache_messages, cache_status: cache_status } }.to_json
37
+ end
38
+
39
+ def clear
40
+ ActionController::Base.cache_store.clear
41
+ # Test caching is on
42
+ # Uncomment to turn on logger; possible performance issue
43
+ # logger = BenchmarkLogger.new
44
+ # ActiveSupport::Cache::Store.logger = logger # seems to be the best way
45
+ #
46
+ # the below is used in some rails tests but isn't available/working in all versions, so far as I can tell
47
+ # https://github.com/rails/rails/pull/15943
48
+ # ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args|
49
+ # logger.debug ActiveSupport::Notifications::Event.new(*args)
50
+ # end
51
+ render json: 'ok'.to_json
52
+ end
53
+
54
+ private
55
+
56
+ def cache_status
57
+ {
58
+ controller: perform_caching,
59
+ app: Rails.configuration.action_controller.perform_caching,
60
+ serializers: Rails.configuration.serializers.each_with_object({}) { |serializer, data| data[serializer.name] = serializer._cache.present? }
61
+ }
62
+ end
63
+
64
+ def cache_messages
65
+ ActiveSupport::Cache::Store.logger.is_a?(BenchmarkLogger) && ActiveSupport::Cache::Store.logger.messages.split("\n")
66
+ end
67
+
68
+ def toggle_cache_status
69
+ case params[:on]
70
+ when 'on'.freeze then self.perform_caching = true
71
+ when 'off'.freeze then self.perform_caching = false
72
+ else nil # no-op
73
+ end
74
+ end
75
+ end
76
+
77
+ Rails.application.routes.draw do
78
+ get '/status(/:on)' => 'primary_resource#render_cache_status'
79
+ get '/clear' => 'primary_resource#clear'
80
+ get '/caching(/:on)' => 'primary_resource#render_with_caching_serializer'
81
+ get '/fragment_caching(/:on)' => 'primary_resource#render_with_fragment_caching_serializer'
82
+ get '/non_caching(/:on)' => 'primary_resource#render_with_non_caching_serializer'
83
+ end