active_model_serializers 0.10.0 → 0.10.9

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 (206) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +10 -5
  3. data/.travis.yml +41 -21
  4. data/CHANGELOG.md +200 -2
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +25 -4
  7. data/README.md +166 -28
  8. data/Rakefile +5 -32
  9. data/active_model_serializers.gemspec +23 -25
  10. data/appveyor.yml +10 -6
  11. data/bin/rubocop +38 -0
  12. data/docs/README.md +2 -1
  13. data/docs/general/adapters.md +35 -11
  14. data/docs/general/caching.md +7 -1
  15. data/docs/general/configuration_options.md +86 -1
  16. data/docs/general/deserialization.md +1 -1
  17. data/docs/general/fields.md +31 -0
  18. data/docs/general/getting_started.md +1 -1
  19. data/docs/general/logging.md +7 -0
  20. data/docs/general/rendering.md +63 -25
  21. data/docs/general/serializers.md +137 -14
  22. data/docs/howto/add_pagination_links.md +16 -17
  23. data/docs/howto/add_relationship_links.md +140 -0
  24. data/docs/howto/add_root_key.md +11 -0
  25. data/docs/howto/grape_integration.md +42 -0
  26. data/docs/howto/outside_controller_use.md +12 -4
  27. data/docs/howto/passing_arbitrary_options.md +2 -2
  28. data/docs/howto/serialize_poro.md +46 -5
  29. data/docs/howto/test.md +2 -0
  30. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  31. data/docs/integrations/ember-and-json-api.md +67 -32
  32. data/docs/jsonapi/schema.md +1 -1
  33. data/lib/action_controller/serialization.rb +15 -3
  34. data/lib/active_model/serializable_resource.rb +2 -0
  35. data/lib/active_model/serializer/adapter/attributes.rb +2 -0
  36. data/lib/active_model/serializer/adapter/base.rb +4 -0
  37. data/lib/active_model/serializer/adapter/json.rb +2 -0
  38. data/lib/active_model/serializer/adapter/json_api.rb +2 -0
  39. data/lib/active_model/serializer/adapter/null.rb +2 -0
  40. data/lib/active_model/serializer/adapter.rb +2 -0
  41. data/lib/active_model/serializer/array_serializer.rb +10 -5
  42. data/lib/active_model/serializer/association.rb +64 -10
  43. data/lib/active_model/serializer/attribute.rb +2 -0
  44. data/lib/active_model/serializer/belongs_to_reflection.rb +6 -3
  45. data/lib/active_model/serializer/collection_serializer.rb +39 -13
  46. data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +87 -116
  47. data/lib/active_model/serializer/error_serializer.rb +13 -7
  48. data/lib/active_model/serializer/errors_serializer.rb +27 -20
  49. data/lib/active_model/serializer/field.rb +2 -0
  50. data/lib/active_model/serializer/fieldset.rb +2 -0
  51. data/lib/active_model/serializer/has_many_reflection.rb +5 -3
  52. data/lib/active_model/serializer/has_one_reflection.rb +3 -4
  53. data/lib/active_model/serializer/lazy_association.rb +99 -0
  54. data/lib/active_model/serializer/link.rb +23 -0
  55. data/lib/active_model/serializer/lint.rb +136 -130
  56. data/lib/active_model/serializer/null.rb +2 -0
  57. data/lib/active_model/serializer/reflection.rb +132 -67
  58. data/lib/active_model/serializer/version.rb +3 -1
  59. data/lib/active_model/serializer.rb +308 -82
  60. data/lib/active_model_serializers/adapter/attributes.rb +5 -66
  61. data/lib/active_model_serializers/adapter/base.rb +41 -39
  62. data/lib/active_model_serializers/adapter/json.rb +2 -0
  63. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +4 -2
  64. data/lib/active_model_serializers/adapter/json_api/error.rb +2 -0
  65. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +2 -0
  66. data/lib/active_model_serializers/adapter/json_api/link.rb +3 -1
  67. data/lib/active_model_serializers/adapter/json_api/meta.rb +2 -0
  68. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +49 -21
  69. data/lib/active_model_serializers/adapter/json_api/relationship.rb +77 -23
  70. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +41 -10
  71. data/lib/active_model_serializers/adapter/json_api.rb +84 -65
  72. data/lib/active_model_serializers/adapter/null.rb +2 -0
  73. data/lib/active_model_serializers/adapter.rb +9 -1
  74. data/lib/active_model_serializers/callbacks.rb +2 -0
  75. data/lib/active_model_serializers/deprecate.rb +3 -2
  76. data/lib/active_model_serializers/deserialization.rb +4 -0
  77. data/lib/active_model_serializers/json_pointer.rb +2 -0
  78. data/lib/active_model_serializers/logging.rb +2 -0
  79. data/lib/active_model_serializers/lookup_chain.rb +82 -0
  80. data/lib/active_model_serializers/model.rb +111 -28
  81. data/lib/active_model_serializers/railtie.rb +7 -1
  82. data/lib/active_model_serializers/register_jsonapi_renderer.rb +46 -31
  83. data/lib/active_model_serializers/serializable_resource.rb +10 -7
  84. data/lib/active_model_serializers/serialization_context.rb +12 -3
  85. data/lib/active_model_serializers/test/schema.rb +4 -2
  86. data/lib/active_model_serializers/test/serializer.rb +2 -0
  87. data/lib/active_model_serializers/test.rb +2 -0
  88. data/lib/active_model_serializers.rb +35 -10
  89. data/lib/generators/rails/resource_override.rb +3 -1
  90. data/lib/generators/rails/serializer_generator.rb +6 -4
  91. data/lib/grape/active_model_serializers.rb +9 -5
  92. data/lib/grape/formatters/active_model_serializers.rb +21 -2
  93. data/lib/grape/helpers/active_model_serializers.rb +3 -0
  94. data/lib/tasks/rubocop.rake +55 -0
  95. data/test/action_controller/adapter_selector_test.rb +16 -5
  96. data/test/action_controller/explicit_serializer_test.rb +7 -4
  97. data/test/action_controller/json/include_test.rb +108 -27
  98. data/test/action_controller/json_api/deserialization_test.rb +3 -1
  99. data/test/action_controller/json_api/errors_test.rb +10 -9
  100. data/test/action_controller/json_api/fields_test.rb +68 -0
  101. data/test/action_controller/json_api/linked_test.rb +31 -24
  102. data/test/action_controller/json_api/pagination_test.rb +33 -23
  103. data/test/action_controller/json_api/transform_test.rb +13 -3
  104. data/test/action_controller/lookup_proc_test.rb +51 -0
  105. data/test/action_controller/namespace_lookup_test.rb +234 -0
  106. data/test/action_controller/serialization_scope_name_test.rb +14 -6
  107. data/test/action_controller/serialization_test.rb +23 -12
  108. data/test/active_model_serializers/adapter_for_test.rb +2 -0
  109. data/test/active_model_serializers/json_pointer_test.rb +17 -13
  110. data/test/active_model_serializers/logging_test.rb +2 -0
  111. data/test/active_model_serializers/model_test.rb +139 -4
  112. data/test/active_model_serializers/railtie_test_isolated.rb +14 -7
  113. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +163 -0
  114. data/test/active_model_serializers/serialization_context_test_isolated.rb +25 -10
  115. data/test/active_model_serializers/test/schema_test.rb +5 -2
  116. data/test/active_model_serializers/test/serializer_test.rb +2 -0
  117. data/test/active_record_test.rb +2 -0
  118. data/test/adapter/attributes_test.rb +42 -0
  119. data/test/adapter/deprecation_test.rb +2 -0
  120. data/test/adapter/json/belongs_to_test.rb +2 -0
  121. data/test/adapter/json/collection_test.rb +16 -0
  122. data/test/adapter/json/has_many_test.rb +12 -2
  123. data/test/adapter/json/transform_test.rb +17 -15
  124. data/test/adapter/json_api/belongs_to_test.rb +2 -0
  125. data/test/adapter/json_api/collection_test.rb +6 -3
  126. data/test/adapter/json_api/errors_test.rb +19 -19
  127. data/test/adapter/json_api/fields_test.rb +14 -3
  128. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +2 -0
  129. data/test/adapter/json_api/has_many_test.rb +51 -20
  130. data/test/adapter/json_api/has_one_test.rb +2 -0
  131. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +215 -0
  132. data/test/adapter/json_api/json_api_test.rb +7 -7
  133. data/test/adapter/json_api/linked_test.rb +35 -12
  134. data/test/adapter/json_api/links_test.rb +22 -3
  135. data/test/adapter/json_api/pagination_links_test.rb +55 -13
  136. data/test/adapter/json_api/parse_test.rb +3 -1
  137. data/test/adapter/json_api/relationship_test.rb +311 -73
  138. data/test/adapter/json_api/resource_meta_test.rb +5 -3
  139. data/test/adapter/json_api/toplevel_jsonapi_test.rb +2 -0
  140. data/test/adapter/json_api/transform_test.rb +265 -253
  141. data/test/adapter/json_api/type_test.rb +170 -36
  142. data/test/adapter/json_test.rb +10 -7
  143. data/test/adapter/null_test.rb +3 -2
  144. data/test/adapter/polymorphic_test.rb +54 -5
  145. data/test/adapter_test.rb +3 -1
  146. data/test/array_serializer_test.rb +2 -0
  147. data/test/benchmark/app.rb +3 -1
  148. data/test/benchmark/benchmarking_support.rb +3 -1
  149. data/test/benchmark/bm_active_record.rb +83 -0
  150. data/test/benchmark/bm_adapter.rb +40 -0
  151. data/test/benchmark/bm_caching.rb +18 -16
  152. data/test/benchmark/bm_lookup_chain.rb +85 -0
  153. data/test/benchmark/bm_transform.rb +23 -10
  154. data/test/benchmark/controllers.rb +18 -17
  155. data/test/benchmark/fixtures.rb +74 -72
  156. data/test/cache_test.rb +301 -69
  157. data/test/collection_serializer_test.rb +33 -14
  158. data/test/fixtures/active_record.rb +47 -10
  159. data/test/fixtures/poro.rb +128 -183
  160. data/test/generators/scaffold_controller_generator_test.rb +2 -0
  161. data/test/generators/serializer_generator_test.rb +25 -5
  162. data/test/grape_test.rb +172 -56
  163. data/test/lint_test.rb +3 -1
  164. data/test/logger_test.rb +15 -11
  165. data/test/poro_test.rb +2 -0
  166. data/test/serializable_resource_test.rb +20 -22
  167. data/test/serializers/association_macros_test.rb +5 -2
  168. data/test/serializers/associations_test.rb +274 -49
  169. data/test/serializers/attribute_test.rb +7 -3
  170. data/test/serializers/attributes_test.rb +3 -1
  171. data/test/serializers/caching_configuration_test_isolated.rb +8 -6
  172. data/test/serializers/configuration_test.rb +2 -0
  173. data/test/serializers/fieldset_test.rb +3 -1
  174. data/test/serializers/meta_test.rb +14 -6
  175. data/test/serializers/options_test.rb +19 -6
  176. data/test/serializers/read_attribute_for_serialization_test.rb +5 -3
  177. data/test/serializers/reflection_test.rb +481 -0
  178. data/test/serializers/root_test.rb +3 -1
  179. data/test/serializers/serialization_test.rb +4 -2
  180. data/test/serializers/serializer_for_test.rb +14 -10
  181. data/test/serializers/serializer_for_with_namespace_test.rb +90 -0
  182. data/test/support/isolated_unit.rb +11 -4
  183. data/test/support/rails5_shims.rb +10 -2
  184. data/test/support/rails_app.rb +4 -9
  185. data/test/support/serialization_testing.rb +33 -5
  186. data/test/test_helper.rb +15 -0
  187. metadata +126 -46
  188. data/.rubocop_todo.yml +0 -167
  189. data/docs/ARCHITECTURE.md +0 -126
  190. data/lib/active_model/serializer/associations.rb +0 -100
  191. data/lib/active_model/serializer/attributes.rb +0 -82
  192. data/lib/active_model/serializer/collection_reflection.rb +0 -7
  193. data/lib/active_model/serializer/configuration.rb +0 -35
  194. data/lib/active_model/serializer/include_tree.rb +0 -111
  195. data/lib/active_model/serializer/links.rb +0 -35
  196. data/lib/active_model/serializer/meta.rb +0 -29
  197. data/lib/active_model/serializer/singular_reflection.rb +0 -7
  198. data/lib/active_model/serializer/type.rb +0 -25
  199. data/lib/active_model_serializers/key_transform.rb +0 -70
  200. data/test/active_model_serializers/key_transform_test.rb +0 -263
  201. data/test/adapter/json_api/has_many_embed_ids_test.rb +0 -43
  202. data/test/adapter/json_api/relationships_test.rb +0 -199
  203. data/test/adapter/json_api/resource_identifier_test.rb +0 -85
  204. data/test/include_tree/from_include_args_test.rb +0 -26
  205. data/test/include_tree/from_string_test.rb +0 -94
  206. data/test/include_tree/include_args_to_hash_test.rb +0 -64
@@ -1,15 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # A Grape response formatter that can be used as 'formatter :json, Grape::Formatters::ActiveModelSerializers'
2
4
  #
3
5
  # Serializer options can be passed as a hash from your Grape endpoint using env[:active_model_serializer_options],
4
6
  # or better yet user the render helper in Grape::Helpers::ActiveModelSerializers
7
+
8
+ require 'active_model_serializers/serialization_context'
9
+
5
10
  module Grape
6
11
  module Formatters
7
12
  module ActiveModelSerializers
8
13
  def self.call(resource, env)
9
- serializer_options = {}
10
- serializer_options.merge!(env[:active_model_serializer_options]) if env[:active_model_serializer_options]
14
+ serializer_options = build_serializer_options(env)
11
15
  ::ActiveModelSerializers::SerializableResource.new(resource, serializer_options).to_json
12
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
13
32
  end
14
33
  end
15
34
  end
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Helpers can be included in your Grape endpoint as: helpers Grape::Helpers::ActiveModelSerializers
4
+
2
5
  module Grape
3
6
  module Helpers
4
7
  module ActiveModelSerializers
@@ -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
@@ -1,21 +1,32 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  module ActionController
4
6
  module Serialization
5
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
+
6
17
  class AdapterSelectorTestController < ActionController::Base
7
18
  def render_using_default_adapter
8
- @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })
19
+ @profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
9
20
  render json: @profile
10
21
  end
11
22
 
12
23
  def render_using_adapter_override
13
- @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })
24
+ @profile = Profile.new(id: 'render_using_adapter_override', name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
14
25
  render json: @profile, adapter: :json_api
15
26
  end
16
27
 
17
28
  def render_skipping_adapter
18
- @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' })
29
+ @profile = Profile.new(id: 'render_skipping_adapter_id', name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
19
30
  render json: @profile, adapter: false
20
31
  end
21
32
  end
@@ -32,7 +43,7 @@ module ActionController
32
43
 
33
44
  expected = {
34
45
  data: {
35
- id: assigns(:profile).id.to_s,
46
+ id: 'render_using_adapter_override',
36
47
  type: 'profiles',
37
48
  attributes: {
38
49
  name: 'Name 1',
@@ -46,7 +57,7 @@ module ActionController
46
57
 
47
58
  def test_render_skipping_adapter
48
59
  get :render_skipping_adapter
49
- assert_equal '{"name":"Name 1","description":"Description 1","comments":"Comments 1"}', response.body
60
+ assert_equal '{"id":"render_skipping_adapter_id","name":"Name 1","description":"Description 1"}', response.body
50
61
  end
51
62
  end
52
63
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  module ActionController
@@ -100,11 +102,12 @@ module ActionController
100
102
  get :render_array_using_explicit_serializer_and_custom_serializers
101
103
 
102
104
  expected = [
103
- { 'title' => 'New Post',
105
+ {
106
+ 'title' => 'New Post',
104
107
  'body' => 'Body',
105
- 'id' => assigns(:post).id,
108
+ 'id' => @controller.instance_variable_get(:@post).id,
106
109
  'comments' => [{ 'id' => 1 }, { 'id' => 2 }],
107
- 'author' => { 'id' => assigns(:author).id }
110
+ 'author' => { 'id' => @controller.instance_variable_get(:@author).id }
108
111
  }
109
112
  ]
110
113
 
@@ -122,7 +125,7 @@ module ActionController
122
125
  id: 42,
123
126
  lat: '-23.550520',
124
127
  lng: '-46.633309',
125
- place: 'Nowhere' # is a virtual attribute on LocationSerializer
128
+ address: 'Nowhere' # is a virtual attribute on LocationSerializer
126
129
  }
127
130
  ]
128
131
  }
@@ -1,9 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  module ActionController
4
6
  module Serialization
5
7
  class Json
6
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
+
7
13
  class IncludeTestController < ActionController::Base
8
14
  def setup_data
9
15
  ActionController::Base.cache_store.clear
@@ -38,17 +44,28 @@ module ActionController
38
44
 
39
45
  def render_resource_with_include_hash
40
46
  setup_data
41
- render json: @author, include: { posts: :comments }, adapter: :json
47
+ render json: @author, include: INCLUDE_HASH, adapter: :json
42
48
  end
43
49
 
44
50
  def render_resource_with_include_string
45
51
  setup_data
46
- render json: @author, include: 'posts.comments', adapter: :json
52
+ render json: @author, include: INCLUDE_STRING, adapter: :json
47
53
  end
48
54
 
49
55
  def render_resource_with_deep_include
50
56
  setup_data
51
- render json: @author, include: 'posts.comments.author', adapter: :json
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
52
69
  end
53
70
  end
54
71
 
@@ -77,34 +94,90 @@ module ActionController
77
94
  def test_render_resource_with_include_hash
78
95
  get :render_resource_with_include_hash
79
96
  response = JSON.parse(@response.body)
80
- expected = {
81
- 'author' => {
82
- 'id' => 1,
83
- 'name' => 'Steve K.',
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,
84
141
  'posts' => [
85
142
  {
86
- 'id' => 42, 'title' => 'New Post', 'body' => 'Body',
143
+ 'id' => 12,
144
+ 'title' => 'Hello World',
145
+ 'body' => 'My first post',
87
146
  'comments' => [
88
147
  {
89
- 'id' => 1, 'body' => 'ZOMG A COMMENT'
90
- },
91
- {
92
- 'id' => 2, 'body' => 'ZOMG ANOTHER COMMENT'
148
+ 'id' => 13,
149
+ 'body' => 'Commentary',
150
+ 'post' => nil, # not set to avoid infinite recursion
151
+ 'author' => nil, # not set to avoid infinite recursion
93
152
  }
94
- ]
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
95
161
  }
96
162
  ]
97
163
  }
98
- }
164
+ assert_equal(expected, response)
165
+ end
166
+ end
99
167
 
100
- assert_equal(expected, response)
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
101
175
  end
102
176
 
103
- def test_render_resource_with_include_string
104
- get :render_resource_with_include_string
177
+ private
105
178
 
106
- response = JSON.parse(@response.body)
107
- expected = {
179
+ def expected_include_response
180
+ {
108
181
  'author' => {
109
182
  'id' => 1,
110
183
  'name' => 'Steve K.',
@@ -123,15 +196,10 @@ module ActionController
123
196
  ]
124
197
  }
125
198
  }
126
-
127
- assert_equal(expected, response)
128
199
  end
129
200
 
130
- def test_render_resource_with_deep_include
131
- get :render_resource_with_deep_include
132
-
133
- response = JSON.parse(@response.body)
134
- expected = {
201
+ def expected_deep_include_response
202
+ {
135
203
  'author' => {
136
204
  'id' => 1,
137
205
  'name' => 'Steve K.',
@@ -158,8 +226,21 @@ module ActionController
158
226
  ]
159
227
  }
160
228
  }
229
+ end
161
230
 
162
- assert_equal(expected, response)
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)
163
244
  end
164
245
  end
165
246
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  module ActionController
@@ -45,7 +47,7 @@ module ActionController
45
47
  response = JSON.parse(@response.body)
46
48
  expected = {
47
49
  'restriction_for_id' => '67',
48
- 'restriction_for_type' => 'discounts',
50
+ 'restriction_for_type' => 'Discount',
49
51
  'restricted_to_id' => nil,
50
52
  'restricted_to_type' => nil
51
53
  }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  module ActionController
@@ -7,18 +9,17 @@ module ActionController
7
9
  def test_active_model_with_multiple_errors
8
10
  get :render_resource_with_errors
9
11
 
10
- expected_errors_object =
11
- { :errors =>
12
- [
13
- { :source => { :pointer => '/data/attributes/name' }, :detail => 'cannot be nil' },
14
- { :source => { :pointer => '/data/attributes/name' }, :detail => 'must be longer' },
15
- { :source => { :pointer => '/data/attributes/id' }, :detail => 'must be a uuid' }
16
- ]
12
+ expected_errors_object = {
13
+ errors: [
14
+ { source: { pointer: '/data/attributes/name' }, detail: 'cannot be nil' },
15
+ { source: { pointer: '/data/attributes/name' }, detail: 'must be longer' },
16
+ { source: { pointer: '/data/attributes/id' }, detail: 'must be a uuid' }
17
+ ]
17
18
  }.to_json
18
- assert_equal json_reponse_body.to_json, expected_errors_object
19
+ assert_equal json_response_body.to_json, expected_errors_object
19
20
  end
20
21
 
21
- def json_reponse_body
22
+ def json_response_body
22
23
  JSON.load(@response.body)
23
24
  end
24
25
 
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module ActionController
6
+ module Serialization
7
+ class JsonApi
8
+ class FieldsTest < ActionController::TestCase
9
+ class FieldsTestController < ActionController::Base
10
+ class AuthorWithName < Author
11
+ attributes :first_name, :last_name
12
+ end
13
+ class AuthorWithNameSerializer < AuthorSerializer
14
+ type 'authors'
15
+ end
16
+ class PostWithPublishAt < Post
17
+ attributes :publish_at
18
+ end
19
+ class PostWithPublishAtSerializer < ActiveModel::Serializer
20
+ type 'posts'
21
+ attributes :title, :body, :publish_at
22
+ belongs_to :author
23
+ has_many :comments
24
+ end
25
+
26
+ def setup_post
27
+ ActionController::Base.cache_store.clear
28
+ @author = AuthorWithName.new(id: 1, first_name: 'Bob', last_name: 'Jones')
29
+ @comment1 = Comment.new(id: 7, body: 'cool', author: @author)
30
+ @comment2 = Comment.new(id: 12, body: 'awesome', author: @author)
31
+ @post = PostWithPublishAt.new(id: 1337, title: 'Title 1', body: 'Body 1',
32
+ author: @author, comments: [@comment1, @comment2],
33
+ publish_at: '2020-03-16T03:55:25.291Z')
34
+ @comment1.post = @post
35
+ @comment2.post = @post
36
+ end
37
+
38
+ def render_fields_works_on_relationships
39
+ setup_post
40
+ render json: @post, serializer: PostWithPublishAtSerializer, adapter: :json_api, fields: { posts: [:author] }
41
+ end
42
+ end
43
+
44
+ tests FieldsTestController
45
+
46
+ test 'fields works on relationships' do
47
+ get :render_fields_works_on_relationships
48
+ response = JSON.parse(@response.body)
49
+ expected = {
50
+ 'data' => {
51
+ 'id' => '1337',
52
+ 'type' => 'posts',
53
+ 'relationships' => {
54
+ 'author' => {
55
+ 'data' => {
56
+ 'id' => '1',
57
+ 'type' => 'authors'
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ assert_equal expected, response
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,11 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  module ActionController
4
6
  module Serialization
5
7
  class JsonApi
6
- class LinkedTest < ActionController::TestCase
8
+ class LinkedTest < ActionDispatch::IntegrationTest
7
9
  class LinkedTestController < ActionController::Base
8
- require 'active_model_serializers/register_jsonapi_renderer'
9
10
  def setup_post
10
11
  ActionController::Base.cache_store.clear
11
12
  @role1 = Role.new(id: 1, name: 'admin')
@@ -39,62 +40,68 @@ module ActionController
39
40
 
40
41
  def render_resource_without_include
41
42
  setup_post
42
- render jsonapi: @post
43
+ render json: @post
43
44
  end
44
45
 
45
46
  def render_resource_with_include
46
47
  setup_post
47
- render jsonapi: @post, include: [:author]
48
+ render json: @post, adapter: :json_api, include: [:author]
48
49
  end
49
50
 
50
51
  def render_resource_with_include_of_custom_key_by_original
51
52
  setup_post
52
- render jsonapi: @post, include: [:reviews], serializer: PostWithCustomKeysSerializer
53
+ render json: @post, adapter: :json_api, include: [:reviews], serializer: PostWithCustomKeysSerializer
53
54
  end
54
55
 
55
56
  def render_resource_with_nested_include
56
57
  setup_post
57
- render jsonapi: @post, include: [comments: [:author]]
58
+ render json: @post, adapter: :json_api, include: [comments: [:author]]
58
59
  end
59
60
 
60
61
  def render_resource_with_nested_has_many_include_wildcard
61
62
  setup_post
62
- render jsonapi: @post, include: 'author.*'
63
+ render json: @post, adapter: :json_api, include: 'author.*'
63
64
  end
64
65
 
65
66
  def render_resource_with_missing_nested_has_many_include
66
67
  setup_post
67
68
  @post.author = @author2 # author2 has no roles.
68
- render jsonapi: @post, include: [author: [:roles]]
69
+ render json: @post, adapter: :json_api, include: [author: [:roles]]
69
70
  end
70
71
 
71
72
  def render_collection_with_missing_nested_has_many_include
72
73
  setup_post
73
74
  @post.author = @author2
74
- render jsonapi: [@post, @post2], include: [author: [:roles]]
75
+ render json: [@post, @post2], adapter: :json_api, include: [author: [:roles]]
75
76
  end
76
77
 
77
78
  def render_collection_without_include
78
79
  setup_post
79
- render jsonapi: [@post]
80
+ render json: [@post], adapter: :json_api
80
81
  end
81
82
 
82
83
  def render_collection_with_include
83
84
  setup_post
84
- render jsonapi: [@post], include: 'author, comments'
85
+ render json: [@post], adapter: :json_api, include: 'author, comments'
85
86
  end
86
87
  end
87
88
 
88
- tests LinkedTestController
89
+ setup do
90
+ @routes = Rails.application.routes.draw do
91
+ ActiveSupport::Deprecation.silence do
92
+ match ':action', to: LinkedTestController, via: [:get, :post]
93
+ end
94
+ end
95
+ end
89
96
 
90
97
  def test_render_resource_without_include
91
- get :render_resource_without_include
98
+ get '/render_resource_without_include'
92
99
  response = JSON.parse(@response.body)
93
100
  refute response.key? 'included'
94
101
  end
95
102
 
96
103
  def test_render_resource_with_include
97
- get :render_resource_with_include
104
+ get '/render_resource_with_include'
98
105
  response = JSON.parse(@response.body)
99
106
  assert response.key? 'included'
100
107
  assert_equal 1, response['included'].size
@@ -102,7 +109,7 @@ module ActionController
102
109
  end
103
110
 
104
111
  def test_render_resource_with_nested_has_many_include
105
- get :render_resource_with_nested_has_many_include_wildcard
112
+ get '/render_resource_with_nested_has_many_include_wildcard'
106
113
  response = JSON.parse(@response.body)
107
114
  expected_linked = [
108
115
  {
@@ -144,7 +151,7 @@ module ActionController
144
151
  end
145
152
 
146
153
  def test_render_resource_with_include_of_custom_key_by_original
147
- get :render_resource_with_include_of_custom_key_by_original
154
+ get '/render_resource_with_include_of_custom_key_by_original'
148
155
  response = JSON.parse(@response.body)
149
156
  assert response.key? 'included'
150
157
 
@@ -156,39 +163,39 @@ module ActionController
156
163
  end
157
164
 
158
165
  def test_render_resource_with_nested_include
159
- get :render_resource_with_nested_include
166
+ get '/render_resource_with_nested_include'
160
167
  response = JSON.parse(@response.body)
161
168
  assert response.key? 'included'
162
169
  assert_equal 3, response['included'].size
163
170
  end
164
171
 
165
172
  def test_render_collection_without_include
166
- get :render_collection_without_include
173
+ get '/render_collection_without_include'
167
174
  response = JSON.parse(@response.body)
168
175
  refute response.key? 'included'
169
176
  end
170
177
 
171
178
  def test_render_collection_with_include
172
- get :render_collection_with_include
179
+ get '/render_collection_with_include'
173
180
  response = JSON.parse(@response.body)
174
181
  assert response.key? 'included'
175
182
  end
176
183
 
177
184
  def test_render_resource_with_nested_attributes_even_when_missing_associations
178
- get :render_resource_with_missing_nested_has_many_include
185
+ get '/render_resource_with_missing_nested_has_many_include'
179
186
  response = JSON.parse(@response.body)
180
187
  assert response.key? 'included'
181
- refute has_type?(response['included'], 'roles')
188
+ refute include_type?(response['included'], 'roles')
182
189
  end
183
190
 
184
191
  def test_render_collection_with_missing_nested_has_many_include
185
- get :render_collection_with_missing_nested_has_many_include
192
+ get '/render_collection_with_missing_nested_has_many_include'
186
193
  response = JSON.parse(@response.body)
187
194
  assert response.key? 'included'
188
- assert has_type?(response['included'], 'roles')
195
+ assert include_type?(response['included'], 'roles')
189
196
  end
190
197
 
191
- def has_type?(collection, value)
198
+ def include_type?(collection, value)
192
199
  collection.detect { |i| i['type'] == value }
193
200
  end
194
201
  end