active_model_serializers_custom 0.10.90

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 (215) 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 +109 -0
  6. data/.simplecov +110 -0
  7. data/.travis.yml +63 -0
  8. data/CHANGELOG.md +727 -0
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +74 -0
  12. data/MIT-LICENSE +22 -0
  13. data/README.md +305 -0
  14. data/Rakefile +76 -0
  15. data/active_model_serializers.gemspec +64 -0
  16. data/appveyor.yml +28 -0
  17. data/bin/bench +171 -0
  18. data/bin/bench_regression +316 -0
  19. data/bin/rubocop +38 -0
  20. data/bin/serve_benchmark +39 -0
  21. data/docs/README.md +41 -0
  22. data/docs/STYLE.md +58 -0
  23. data/docs/general/adapters.md +269 -0
  24. data/docs/general/caching.md +58 -0
  25. data/docs/general/configuration_options.md +185 -0
  26. data/docs/general/deserialization.md +100 -0
  27. data/docs/general/fields.md +31 -0
  28. data/docs/general/getting_started.md +133 -0
  29. data/docs/general/instrumentation.md +40 -0
  30. data/docs/general/key_transforms.md +40 -0
  31. data/docs/general/logging.md +21 -0
  32. data/docs/general/rendering.md +293 -0
  33. data/docs/general/serializers.md +495 -0
  34. data/docs/how-open-source-maintained.jpg +0 -0
  35. data/docs/howto/add_pagination_links.md +138 -0
  36. data/docs/howto/add_relationship_links.md +140 -0
  37. data/docs/howto/add_root_key.md +62 -0
  38. data/docs/howto/grape_integration.md +42 -0
  39. data/docs/howto/outside_controller_use.md +66 -0
  40. data/docs/howto/passing_arbitrary_options.md +27 -0
  41. data/docs/howto/serialize_poro.md +73 -0
  42. data/docs/howto/test.md +154 -0
  43. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  44. data/docs/integrations/ember-and-json-api.md +147 -0
  45. data/docs/integrations/grape.md +19 -0
  46. data/docs/jsonapi/errors.md +56 -0
  47. data/docs/jsonapi/schema.md +151 -0
  48. data/docs/jsonapi/schema/schema.json +366 -0
  49. data/docs/rfcs/0000-namespace.md +106 -0
  50. data/docs/rfcs/template.md +15 -0
  51. data/lib/action_controller/serialization.rb +76 -0
  52. data/lib/active_model/serializable_resource.rb +13 -0
  53. data/lib/active_model/serializer.rb +418 -0
  54. data/lib/active_model/serializer/adapter.rb +26 -0
  55. data/lib/active_model/serializer/adapter/attributes.rb +17 -0
  56. data/lib/active_model/serializer/adapter/base.rb +20 -0
  57. data/lib/active_model/serializer/adapter/json.rb +17 -0
  58. data/lib/active_model/serializer/adapter/json_api.rb +17 -0
  59. data/lib/active_model/serializer/adapter/null.rb +17 -0
  60. data/lib/active_model/serializer/array_serializer.rb +14 -0
  61. data/lib/active_model/serializer/association.rb +91 -0
  62. data/lib/active_model/serializer/attribute.rb +27 -0
  63. data/lib/active_model/serializer/belongs_to_reflection.rb +13 -0
  64. data/lib/active_model/serializer/collection_serializer.rb +90 -0
  65. data/lib/active_model/serializer/concerns/caching.rb +304 -0
  66. data/lib/active_model/serializer/error_serializer.rb +16 -0
  67. data/lib/active_model/serializer/errors_serializer.rb +34 -0
  68. data/lib/active_model/serializer/field.rb +92 -0
  69. data/lib/active_model/serializer/fieldset.rb +33 -0
  70. data/lib/active_model/serializer/has_many_reflection.rb +12 -0
  71. data/lib/active_model/serializer/has_one_reflection.rb +9 -0
  72. data/lib/active_model/serializer/lazy_association.rb +99 -0
  73. data/lib/active_model/serializer/link.rb +23 -0
  74. data/lib/active_model/serializer/lint.rb +152 -0
  75. data/lib/active_model/serializer/null.rb +19 -0
  76. data/lib/active_model/serializer/reflection.rb +212 -0
  77. data/lib/active_model/serializer/version.rb +7 -0
  78. data/lib/active_model_serializers.rb +63 -0
  79. data/lib/active_model_serializers/adapter.rb +100 -0
  80. data/lib/active_model_serializers/adapter/attributes.rb +15 -0
  81. data/lib/active_model_serializers/adapter/base.rb +85 -0
  82. data/lib/active_model_serializers/adapter/json.rb +23 -0
  83. data/lib/active_model_serializers/adapter/json_api.rb +535 -0
  84. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +215 -0
  85. data/lib/active_model_serializers/adapter/json_api/error.rb +98 -0
  86. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +51 -0
  87. data/lib/active_model_serializers/adapter/json_api/link.rb +85 -0
  88. data/lib/active_model_serializers/adapter/json_api/meta.rb +39 -0
  89. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +90 -0
  90. data/lib/active_model_serializers/adapter/json_api/relationship.rb +106 -0
  91. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +68 -0
  92. data/lib/active_model_serializers/adapter/null.rb +11 -0
  93. data/lib/active_model_serializers/callbacks.rb +57 -0
  94. data/lib/active_model_serializers/deprecate.rb +56 -0
  95. data/lib/active_model_serializers/deserialization.rb +17 -0
  96. data/lib/active_model_serializers/json_pointer.rb +16 -0
  97. data/lib/active_model_serializers/logging.rb +124 -0
  98. data/lib/active_model_serializers/lookup_chain.rb +82 -0
  99. data/lib/active_model_serializers/model.rb +132 -0
  100. data/lib/active_model_serializers/railtie.rb +52 -0
  101. data/lib/active_model_serializers/register_jsonapi_renderer.rb +80 -0
  102. data/lib/active_model_serializers/serializable_resource.rb +84 -0
  103. data/lib/active_model_serializers/serialization_context.rb +41 -0
  104. data/lib/active_model_serializers/test.rb +9 -0
  105. data/lib/active_model_serializers/test/schema.rb +140 -0
  106. data/lib/active_model_serializers/test/serializer.rb +127 -0
  107. data/lib/generators/rails/USAGE +6 -0
  108. data/lib/generators/rails/resource_override.rb +12 -0
  109. data/lib/generators/rails/serializer_generator.rb +38 -0
  110. data/lib/generators/rails/templates/serializer.rb.erb +8 -0
  111. data/lib/grape/active_model_serializers.rb +18 -0
  112. data/lib/grape/formatters/active_model_serializers.rb +34 -0
  113. data/lib/grape/helpers/active_model_serializers.rb +19 -0
  114. data/lib/tasks/rubocop.rake +55 -0
  115. data/test/action_controller/adapter_selector_test.rb +64 -0
  116. data/test/action_controller/explicit_serializer_test.rb +137 -0
  117. data/test/action_controller/json/include_test.rb +248 -0
  118. data/test/action_controller/json_api/deserialization_test.rb +114 -0
  119. data/test/action_controller/json_api/errors_test.rb +42 -0
  120. data/test/action_controller/json_api/fields_test.rb +68 -0
  121. data/test/action_controller/json_api/linked_test.rb +204 -0
  122. data/test/action_controller/json_api/pagination_test.rb +126 -0
  123. data/test/action_controller/json_api/transform_test.rb +191 -0
  124. data/test/action_controller/lookup_proc_test.rb +51 -0
  125. data/test/action_controller/namespace_lookup_test.rb +239 -0
  126. data/test/action_controller/serialization_scope_name_test.rb +237 -0
  127. data/test/action_controller/serialization_test.rb +480 -0
  128. data/test/active_model_serializers/adapter_for_test.rb +210 -0
  129. data/test/active_model_serializers/json_pointer_test.rb +24 -0
  130. data/test/active_model_serializers/logging_test.rb +79 -0
  131. data/test/active_model_serializers/model_test.rb +144 -0
  132. data/test/active_model_serializers/railtie_test_isolated.rb +70 -0
  133. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +163 -0
  134. data/test/active_model_serializers/serialization_context_test_isolated.rb +73 -0
  135. data/test/active_model_serializers/test/schema_test.rb +133 -0
  136. data/test/active_model_serializers/test/serializer_test.rb +64 -0
  137. data/test/active_record_test.rb +11 -0
  138. data/test/adapter/attributes_test.rb +42 -0
  139. data/test/adapter/deprecation_test.rb +102 -0
  140. data/test/adapter/json/belongs_to_test.rb +47 -0
  141. data/test/adapter/json/collection_test.rb +106 -0
  142. data/test/adapter/json/has_many_test.rb +55 -0
  143. data/test/adapter/json/transform_test.rb +95 -0
  144. data/test/adapter/json_api/belongs_to_test.rb +157 -0
  145. data/test/adapter/json_api/collection_test.rb +98 -0
  146. data/test/adapter/json_api/errors_test.rb +78 -0
  147. data/test/adapter/json_api/fields_test.rb +98 -0
  148. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +98 -0
  149. data/test/adapter/json_api/has_many_test.rb +175 -0
  150. data/test/adapter/json_api/has_one_test.rb +82 -0
  151. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +215 -0
  152. data/test/adapter/json_api/json_api_test.rb +35 -0
  153. data/test/adapter/json_api/linked_test.rb +415 -0
  154. data/test/adapter/json_api/links_test.rb +112 -0
  155. data/test/adapter/json_api/pagination_links_test.rb +208 -0
  156. data/test/adapter/json_api/parse_test.rb +139 -0
  157. data/test/adapter/json_api/relationship_test.rb +399 -0
  158. data/test/adapter/json_api/resource_meta_test.rb +102 -0
  159. data/test/adapter/json_api/toplevel_jsonapi_test.rb +84 -0
  160. data/test/adapter/json_api/transform_test.rb +514 -0
  161. data/test/adapter/json_api/type_test.rb +195 -0
  162. data/test/adapter/json_test.rb +48 -0
  163. data/test/adapter/null_test.rb +24 -0
  164. data/test/adapter/polymorphic_test.rb +220 -0
  165. data/test/adapter_test.rb +69 -0
  166. data/test/array_serializer_test.rb +24 -0
  167. data/test/benchmark/app.rb +67 -0
  168. data/test/benchmark/benchmarking_support.rb +69 -0
  169. data/test/benchmark/bm_active_record.rb +83 -0
  170. data/test/benchmark/bm_adapter.rb +40 -0
  171. data/test/benchmark/bm_caching.rb +121 -0
  172. data/test/benchmark/bm_lookup_chain.rb +85 -0
  173. data/test/benchmark/bm_transform.rb +47 -0
  174. data/test/benchmark/config.ru +3 -0
  175. data/test/benchmark/controllers.rb +85 -0
  176. data/test/benchmark/fixtures.rb +221 -0
  177. data/test/cache_test.rb +717 -0
  178. data/test/collection_serializer_test.rb +129 -0
  179. data/test/fixtures/active_record.rb +115 -0
  180. data/test/fixtures/poro.rb +227 -0
  181. data/test/generators/scaffold_controller_generator_test.rb +26 -0
  182. data/test/generators/serializer_generator_test.rb +77 -0
  183. data/test/grape_test.rb +198 -0
  184. data/test/lint_test.rb +51 -0
  185. data/test/logger_test.rb +22 -0
  186. data/test/poro_test.rb +11 -0
  187. data/test/serializable_resource_test.rb +81 -0
  188. data/test/serializers/association_macros_test.rb +39 -0
  189. data/test/serializers/associations_test.rb +520 -0
  190. data/test/serializers/attribute_test.rb +155 -0
  191. data/test/serializers/attributes_test.rb +54 -0
  192. data/test/serializers/caching_configuration_test_isolated.rb +172 -0
  193. data/test/serializers/configuration_test.rb +34 -0
  194. data/test/serializers/fieldset_test.rb +16 -0
  195. data/test/serializers/meta_test.rb +204 -0
  196. data/test/serializers/options_test.rb +34 -0
  197. data/test/serializers/read_attribute_for_serialization_test.rb +81 -0
  198. data/test/serializers/reflection_test.rb +481 -0
  199. data/test/serializers/root_test.rb +23 -0
  200. data/test/serializers/serialization_test.rb +57 -0
  201. data/test/serializers/serializer_for_test.rb +138 -0
  202. data/test/serializers/serializer_for_with_namespace_test.rb +90 -0
  203. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  204. data/test/support/isolated_unit.rb +86 -0
  205. data/test/support/rails5_shims.rb +55 -0
  206. data/test/support/rails_app.rb +40 -0
  207. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  208. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
  209. data/test/support/schemas/custom/show.json +7 -0
  210. data/test/support/schemas/hyper_schema.json +93 -0
  211. data/test/support/schemas/render_using_json_api.json +43 -0
  212. data/test/support/schemas/simple_json_pointers.json +10 -0
  213. data/test/support/serialization_testing.rb +81 -0
  214. data/test/test_helper.rb +72 -0
  215. metadata +622 -0
@@ -0,0 +1,8 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %>Serializer < <%= parent_class_name %>
3
+ attributes <%= attributes_names.map(&:inspect).join(", ") %>
4
+ <% association_names.each do |attribute| -%>
5
+ has_one :<%= attribute %>
6
+ <% end -%>
7
+ end
8
+ <% end -%>
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # To add Grape support, require 'grape/active_model_serializers' in the base of your Grape endpoints
4
+ # Then add 'include Grape::ActiveModelSerializers' to enable the formatter and helpers
5
+ require 'active_model_serializers'
6
+ require 'grape/formatters/active_model_serializers'
7
+ require 'grape/helpers/active_model_serializers'
8
+
9
+ module Grape
10
+ module ActiveModelSerializers
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ formatter :json, Grape::Formatters::ActiveModelSerializers
15
+ helpers Grape::Helpers::ActiveModelSerializers
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A Grape response formatter that can be used as 'formatter :json, Grape::Formatters::ActiveModelSerializers'
4
+ #
5
+ # Serializer options can be passed as a hash from your Grape endpoint using env[:active_model_serializer_options],
6
+ # or better yet user the render helper in Grape::Helpers::ActiveModelSerializers
7
+
8
+ require 'active_model_serializers/serialization_context'
9
+
10
+ module Grape
11
+ module Formatters
12
+ module ActiveModelSerializers
13
+ def self.call(resource, env)
14
+ serializer_options = build_serializer_options(env)
15
+ ::ActiveModelSerializers::SerializableResource.new(resource, serializer_options).to_json
16
+ end
17
+
18
+ def self.build_serializer_options(env)
19
+ ams_options = env[:active_model_serializer_options] || {}
20
+
21
+ # Add serialization context
22
+ ams_options.fetch(:serialization_context) do
23
+ request = env['grape.request']
24
+ ams_options[:serialization_context] = ::ActiveModelSerializers::SerializationContext.new(
25
+ request_url: request.url[/\A[^?]+/],
26
+ query_parameters: request.params
27
+ )
28
+ end
29
+
30
+ ams_options
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Helpers can be included in your Grape endpoint as: helpers Grape::Helpers::ActiveModelSerializers
4
+
5
+ module Grape
6
+ module Helpers
7
+ module ActiveModelSerializers
8
+ # A convenience method for passing ActiveModelSerializers serializer options
9
+ #
10
+ # Example: To include relationships in the response: render(post, include: ['comments'])
11
+ #
12
+ # Example: To include pagination meta data: render(posts, meta: { page: posts.page, total_pages: posts.total_pages })
13
+ def render(resource, active_model_serializer_options = {})
14
+ env[:active_model_serializer_options] = active_model_serializer_options
15
+ resource
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'rubocop'
5
+ require 'rubocop/rake_task'
6
+ rescue LoadError # rubocop:disable Lint/HandleExceptions
7
+ else
8
+ require 'rbconfig'
9
+ # https://github.com/bundler/bundler/blob/1b3eb2465a/lib/bundler/constants.rb#L2
10
+ windows_platforms = /(msdos|mswin|djgpp|mingw)/
11
+ if RbConfig::CONFIG['host_os'] =~ windows_platforms
12
+ desc 'No-op rubocop on Windows-- unsupported platform'
13
+ task :rubocop do
14
+ puts 'Skipping rubocop on Windows'
15
+ end
16
+ elsif defined?(::Rubinius)
17
+ desc 'No-op rubocop to avoid rbx segfault'
18
+ task :rubocop do
19
+ puts 'Skipping rubocop on rbx due to segfault'
20
+ puts 'https://github.com/rubinius/rubinius/issues/3499'
21
+ end
22
+ else
23
+ Rake::Task[:rubocop].clear if Rake::Task.task_defined?(:rubocop)
24
+ patterns = [
25
+ 'Gemfile',
26
+ 'Rakefile',
27
+ 'lib/**/*.{rb,rake}',
28
+ 'config/**/*.rb',
29
+ 'app/**/*.rb',
30
+ 'test/**/*.rb'
31
+ ]
32
+ desc 'Execute rubocop'
33
+ RuboCop::RakeTask.new(:rubocop) do |task|
34
+ task.options = ['--rails', '--display-cop-names', '--display-style-guide']
35
+ task.formatters = ['progress']
36
+ task.patterns = patterns
37
+ task.fail_on_error = true
38
+ end
39
+
40
+ namespace :rubocop do
41
+ desc 'Auto-gen rubocop config'
42
+ task :auto_gen_config do
43
+ options = ['--auto-gen-config'].concat patterns
44
+ require 'benchmark'
45
+ result = 0
46
+ cli = RuboCop::CLI.new
47
+ time = Benchmark.realtime do
48
+ result = cli.run(options)
49
+ end
50
+ puts "Finished in #{time} seconds" if cli.options[:debug]
51
+ abort('RuboCop failed!') if result.nonzero?
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module ActionController
6
+ module Serialization
7
+ class AdapterSelectorTest < ActionController::TestCase
8
+ class Profile < Model
9
+ attributes :id, :name, :description
10
+ associations :comments
11
+ end
12
+ class ProfileSerializer < ActiveModel::Serializer
13
+ type 'profiles'
14
+ attributes :name, :description
15
+ end
16
+
17
+ class AdapterSelectorTestController < ActionController::Base
18
+ def render_using_default_adapter
19
+ @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
20
+ render json: @profile
21
+ end
22
+
23
+ def render_using_adapter_override
24
+ @profile = Profile.new(id: 'render_using_adapter_override', name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
25
+ render json: @profile, adapter: :json_api
26
+ end
27
+
28
+ def render_skipping_adapter
29
+ @profile = Profile.new(id: 'render_skipping_adapter_id', name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
30
+ render json: @profile, adapter: false
31
+ end
32
+ end
33
+
34
+ tests AdapterSelectorTestController
35
+
36
+ def test_render_using_default_adapter
37
+ get :render_using_default_adapter
38
+ assert_equal '{"name":"Name 1","description":"Description 1"}', response.body
39
+ end
40
+
41
+ def test_render_using_adapter_override
42
+ get :render_using_adapter_override
43
+
44
+ expected = {
45
+ data: {
46
+ id: 'render_using_adapter_override',
47
+ type: 'profiles',
48
+ attributes: {
49
+ name: 'Name 1',
50
+ description: 'Description 1'
51
+ }
52
+ }
53
+ }
54
+
55
+ assert_equal expected.to_json, response.body
56
+ end
57
+
58
+ def test_render_skipping_adapter
59
+ get :render_skipping_adapter
60
+ assert_equal '{"id":"render_skipping_adapter_id","name":"Name 1","description":"Description 1"}', response.body
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module ActionController
6
+ module Serialization
7
+ class ExplicitSerializerTest < ActionController::TestCase
8
+ class ExplicitSerializerTestController < ActionController::Base
9
+ def render_using_explicit_serializer
10
+ @profile = Profile.new(name: 'Name 1',
11
+ description: 'Description 1',
12
+ comments: 'Comments 1')
13
+ render json: @profile, serializer: ProfilePreviewSerializer
14
+ end
15
+
16
+ def render_array_using_explicit_serializer
17
+ array = [
18
+ Profile.new(name: 'Name 1',
19
+ description: 'Description 1',
20
+ comments: 'Comments 1'),
21
+ Profile.new(name: 'Name 2',
22
+ description: 'Description 2',
23
+ comments: 'Comments 2')
24
+ ]
25
+ render json: array,
26
+ serializer: PaginatedSerializer,
27
+ each_serializer: ProfilePreviewSerializer
28
+ end
29
+
30
+ def render_array_using_implicit_serializer
31
+ array = [
32
+ Profile.new(name: 'Name 1',
33
+ description: 'Description 1',
34
+ comments: 'Comments 1'),
35
+ Profile.new(name: 'Name 2',
36
+ description: 'Description 2',
37
+ comments: 'Comments 2')
38
+ ]
39
+ render json: array,
40
+ each_serializer: ProfilePreviewSerializer
41
+ end
42
+
43
+ def render_array_using_explicit_serializer_and_custom_serializers
44
+ @post = Post.new(title: 'New Post', body: 'Body')
45
+ @author = Author.new(name: 'Jane Blogger')
46
+ @author.posts = [@post]
47
+ @post.author = @author
48
+ @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
49
+ @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
50
+ @post.comments = [@first_comment, @second_comment]
51
+ @first_comment.post = @post
52
+ @first_comment.author = nil
53
+ @second_comment.post = @post
54
+ @second_comment.author = nil
55
+ @blog = Blog.new(id: 23, name: 'AMS Blog')
56
+ @post.blog = @blog
57
+
58
+ render json: [@post], each_serializer: PostPreviewSerializer
59
+ end
60
+
61
+ def render_using_explicit_each_serializer
62
+ location = Location.new(id: 42, lat: '-23.550520', lng: '-46.633309')
63
+ place = Place.new(id: 1337, name: 'Amazing Place', locations: [location])
64
+
65
+ render json: place, each_serializer: PlaceSerializer
66
+ end
67
+ end
68
+
69
+ tests ExplicitSerializerTestController
70
+
71
+ def test_render_using_explicit_serializer
72
+ get :render_using_explicit_serializer
73
+
74
+ assert_equal 'application/json', @response.content_type
75
+ assert_equal '{"name":"Name 1"}', @response.body
76
+ end
77
+
78
+ def test_render_array_using_explicit_serializer
79
+ get :render_array_using_explicit_serializer
80
+ assert_equal 'application/json', @response.content_type
81
+
82
+ expected = [
83
+ { 'name' => 'Name 1' },
84
+ { 'name' => 'Name 2' }
85
+ ]
86
+
87
+ assert_equal expected.to_json, @response.body
88
+ end
89
+
90
+ def test_render_array_using_implicit_serializer
91
+ get :render_array_using_implicit_serializer
92
+ assert_equal 'application/json', @response.content_type
93
+
94
+ expected = [
95
+ { 'name' => 'Name 1' },
96
+ { 'name' => 'Name 2' }
97
+ ]
98
+ assert_equal expected.to_json, @response.body
99
+ end
100
+
101
+ def test_render_array_using_explicit_serializer_and_custom_serializers
102
+ get :render_array_using_explicit_serializer_and_custom_serializers
103
+
104
+ expected = [
105
+ {
106
+ 'title' => 'New Post',
107
+ 'body' => 'Body',
108
+ 'id' => @controller.instance_variable_get(:@post).id,
109
+ 'comments' => [{ 'id' => 1 }, { 'id' => 2 }],
110
+ 'author' => { 'id' => @controller.instance_variable_get(:@author).id }
111
+ }
112
+ ]
113
+
114
+ assert_equal expected.to_json, @response.body
115
+ end
116
+
117
+ def test_render_using_explicit_each_serializer
118
+ get :render_using_explicit_each_serializer
119
+
120
+ expected = {
121
+ id: 1337,
122
+ name: 'Amazing Place',
123
+ locations: [
124
+ {
125
+ id: 42,
126
+ lat: '-23.550520',
127
+ lng: '-46.633309',
128
+ address: 'Nowhere' # is a virtual attribute on LocationSerializer
129
+ }
130
+ ]
131
+ }
132
+
133
+ assert_equal expected.to_json, response.body
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module ActionController
6
+ module Serialization
7
+ class Json
8
+ class IncludeTest < ActionController::TestCase
9
+ INCLUDE_STRING = 'posts.comments'.freeze
10
+ INCLUDE_HASH = { posts: :comments }.freeze
11
+ DEEP_INCLUDE = 'posts.comments.author'.freeze
12
+
13
+ class IncludeTestController < ActionController::Base
14
+ def setup_data
15
+ ActionController::Base.cache_store.clear
16
+
17
+ @author = Author.new(id: 1, name: 'Steve K.')
18
+
19
+ @post = Post.new(id: 42, title: 'New Post', body: 'Body')
20
+ @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
21
+ @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
22
+
23
+ @post.comments = [@first_comment, @second_comment]
24
+ @post.author = @author
25
+
26
+ @first_comment.post = @post
27
+ @second_comment.post = @post
28
+
29
+ @blog = Blog.new(id: 1, name: 'My Blog!!')
30
+ @post.blog = @blog
31
+ @author.posts = [@post]
32
+
33
+ @first_comment.author = @author
34
+ @second_comment.author = @author
35
+ @author.comments = [@first_comment, @second_comment]
36
+ @author.roles = []
37
+ @author.bio = {}
38
+ end
39
+
40
+ def render_without_include
41
+ setup_data
42
+ render json: @author, adapter: :json
43
+ end
44
+
45
+ def render_resource_with_include_hash
46
+ setup_data
47
+ render json: @author, include: INCLUDE_HASH, adapter: :json
48
+ end
49
+
50
+ def render_resource_with_include_string
51
+ setup_data
52
+ render json: @author, include: INCLUDE_STRING, adapter: :json
53
+ end
54
+
55
+ def render_resource_with_deep_include
56
+ setup_data
57
+ render json: @author, include: DEEP_INCLUDE, adapter: :json
58
+ end
59
+
60
+ def render_without_recursive_relationships
61
+ # testing recursive includes ('**') can't have any cycles in the
62
+ # relationships, or we enter an infinite loop.
63
+ author = Author.new(id: 11, name: 'Jane Doe')
64
+ post = Post.new(id: 12, title: 'Hello World', body: 'My first post')
65
+ comment = Comment.new(id: 13, body: 'Commentary')
66
+ author.posts = [post]
67
+ post.comments = [comment]
68
+ render json: author
69
+ end
70
+ end
71
+
72
+ tests IncludeTestController
73
+
74
+ def test_render_without_include
75
+ get :render_without_include
76
+ response = JSON.parse(@response.body)
77
+ expected = {
78
+ 'author' => {
79
+ 'id' => 1,
80
+ 'name' => 'Steve K.',
81
+ 'posts' => [
82
+ {
83
+ 'id' => 42, 'title' => 'New Post', 'body' => 'Body'
84
+ }
85
+ ],
86
+ 'roles' => [],
87
+ 'bio' => {}
88
+ }
89
+ }
90
+
91
+ assert_equal(expected, response)
92
+ end
93
+
94
+ def test_render_resource_with_include_hash
95
+ get :render_resource_with_include_hash
96
+ response = JSON.parse(@response.body)
97
+
98
+ assert_equal(expected_include_response, response)
99
+ end
100
+
101
+ def test_render_resource_with_include_string
102
+ get :render_resource_with_include_string
103
+
104
+ response = JSON.parse(@response.body)
105
+
106
+ assert_equal(expected_include_response, response)
107
+ end
108
+
109
+ def test_render_resource_with_deep_include
110
+ get :render_resource_with_deep_include
111
+
112
+ response = JSON.parse(@response.body)
113
+
114
+ assert_equal(expected_deep_include_response, response)
115
+ end
116
+
117
+ def test_render_with_empty_default_includes
118
+ with_default_includes '' do
119
+ get :render_without_include
120
+ response = JSON.parse(@response.body)
121
+ expected = {
122
+ 'author' => {
123
+ 'id' => 1,
124
+ 'name' => 'Steve K.'
125
+ }
126
+ }
127
+ assert_equal(expected, response)
128
+ end
129
+ end
130
+
131
+ def test_render_with_recursive_default_includes
132
+ with_default_includes '**' do
133
+ get :render_without_recursive_relationships
134
+ response = JSON.parse(@response.body)
135
+
136
+ expected = {
137
+ 'id' => 11,
138
+ 'name' => 'Jane Doe',
139
+ 'roles' => nil,
140
+ 'bio' => nil,
141
+ 'posts' => [
142
+ {
143
+ 'id' => 12,
144
+ 'title' => 'Hello World',
145
+ 'body' => 'My first post',
146
+ 'comments' => [
147
+ {
148
+ 'id' => 13,
149
+ 'body' => 'Commentary',
150
+ 'post' => nil, # not set to avoid infinite recursion
151
+ 'author' => nil, # not set to avoid infinite recursion
152
+ }
153
+ ],
154
+ 'blog' => {
155
+ 'id' => 999,
156
+ 'name' => 'Custom blog',
157
+ 'writer' => nil,
158
+ 'articles' => nil
159
+ },
160
+ 'author' => nil # not set to avoid infinite recursion
161
+ }
162
+ ]
163
+ }
164
+ assert_equal(expected, response)
165
+ end
166
+ end
167
+
168
+ def test_render_with_includes_overrides_default_includes
169
+ with_default_includes '' do
170
+ get :render_resource_with_include_hash
171
+ response = JSON.parse(@response.body)
172
+
173
+ assert_equal(expected_include_response, response)
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ def expected_include_response
180
+ {
181
+ 'author' => {
182
+ 'id' => 1,
183
+ 'name' => 'Steve K.',
184
+ 'posts' => [
185
+ {
186
+ 'id' => 42, 'title' => 'New Post', 'body' => 'Body',
187
+ 'comments' => [
188
+ {
189
+ 'id' => 1, 'body' => 'ZOMG A COMMENT'
190
+ },
191
+ {
192
+ 'id' => 2, 'body' => 'ZOMG ANOTHER COMMENT'
193
+ }
194
+ ]
195
+ }
196
+ ]
197
+ }
198
+ }
199
+ end
200
+
201
+ def expected_deep_include_response
202
+ {
203
+ 'author' => {
204
+ 'id' => 1,
205
+ 'name' => 'Steve K.',
206
+ 'posts' => [
207
+ {
208
+ 'id' => 42, 'title' => 'New Post', 'body' => 'Body',
209
+ 'comments' => [
210
+ {
211
+ 'id' => 1, 'body' => 'ZOMG A COMMENT',
212
+ 'author' => {
213
+ 'id' => 1,
214
+ 'name' => 'Steve K.'
215
+ }
216
+ },
217
+ {
218
+ 'id' => 2, 'body' => 'ZOMG ANOTHER COMMENT',
219
+ 'author' => {
220
+ 'id' => 1,
221
+ 'name' => 'Steve K.'
222
+ }
223
+ }
224
+ ]
225
+ }
226
+ ]
227
+ }
228
+ }
229
+ end
230
+
231
+ def with_default_includes(include_directive)
232
+ original = ActiveModelSerializers.config.default_includes
233
+ ActiveModelSerializers.config.default_includes = include_directive
234
+ clear_include_directive_cache
235
+ yield
236
+ ensure
237
+ ActiveModelSerializers.config.default_includes = original
238
+ clear_include_directive_cache
239
+ end
240
+
241
+ def clear_include_directive_cache
242
+ ActiveModelSerializers
243
+ .instance_variable_set(:@default_include_directive, nil)
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end