active_model_serializers 0.10.0 → 0.10.3

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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -4
  3. data/.travis.yml +9 -1
  4. data/CHANGELOG.md +81 -2
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +5 -2
  7. data/README.md +24 -24
  8. data/Rakefile +3 -3
  9. data/active_model_serializers.gemspec +20 -24
  10. data/docs/ARCHITECTURE.md +6 -7
  11. data/docs/README.md +2 -0
  12. data/docs/general/adapters.md +4 -2
  13. data/docs/general/caching.md +7 -1
  14. data/docs/general/configuration_options.md +70 -1
  15. data/docs/general/deserialization.md +1 -1
  16. data/docs/general/fields.md +31 -0
  17. data/docs/general/rendering.md +42 -3
  18. data/docs/general/serializers.md +97 -8
  19. data/docs/howto/add_pagination_links.md +4 -5
  20. data/docs/howto/add_relationship_links.md +137 -0
  21. data/docs/howto/add_root_key.md +4 -0
  22. data/docs/howto/grape_integration.md +42 -0
  23. data/docs/howto/outside_controller_use.md +9 -2
  24. data/docs/howto/passing_arbitrary_options.md +2 -2
  25. data/docs/howto/test.md +2 -0
  26. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  27. data/docs/integrations/ember-and-json-api.md +64 -32
  28. data/docs/jsonapi/schema.md +1 -1
  29. data/lib/action_controller/serialization.rb +13 -3
  30. data/lib/active_model/serializer/adapter/base.rb +2 -0
  31. data/lib/active_model/serializer/array_serializer.rb +8 -5
  32. data/lib/active_model/serializer/association.rb +19 -4
  33. data/lib/active_model/serializer/belongs_to_reflection.rb +0 -3
  34. data/lib/active_model/serializer/collection_serializer.rb +35 -12
  35. data/lib/active_model/serializer/{associations.rb → concerns/associations.rb} +13 -11
  36. data/lib/active_model/serializer/{attributes.rb → concerns/attributes.rb} +0 -0
  37. data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +72 -113
  38. data/lib/active_model/serializer/{configuration.rb → concerns/configuration.rb} +25 -1
  39. data/lib/active_model/serializer/{links.rb → concerns/links.rb} +0 -0
  40. data/lib/active_model/serializer/{meta.rb → concerns/meta.rb} +0 -0
  41. data/lib/active_model/serializer/{type.rb → concerns/type.rb} +0 -0
  42. data/lib/active_model/serializer/error_serializer.rb +11 -7
  43. data/lib/active_model/serializer/errors_serializer.rb +25 -20
  44. data/lib/active_model/serializer/has_many_reflection.rb +0 -3
  45. data/lib/active_model/serializer/has_one_reflection.rb +0 -3
  46. data/lib/active_model/serializer/lint.rb +134 -130
  47. data/lib/active_model/serializer/reflection.rb +37 -21
  48. data/lib/active_model/serializer/version.rb +1 -1
  49. data/lib/active_model/serializer.rb +76 -37
  50. data/lib/active_model_serializers/adapter/attributes.rb +3 -66
  51. data/lib/active_model_serializers/adapter/base.rb +38 -38
  52. data/lib/active_model_serializers/adapter/json_api/link.rb +1 -1
  53. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +8 -1
  54. data/lib/active_model_serializers/adapter/json_api/relationship.rb +30 -19
  55. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +23 -9
  56. data/lib/active_model_serializers/adapter/json_api.rb +44 -43
  57. data/lib/active_model_serializers/adapter.rb +6 -0
  58. data/lib/active_model_serializers/deprecate.rb +1 -2
  59. data/lib/active_model_serializers/deserialization.rb +2 -0
  60. data/lib/active_model_serializers/key_transform.rb +4 -0
  61. data/lib/active_model_serializers/lookup_chain.rb +80 -0
  62. data/lib/active_model_serializers/model.rb +4 -2
  63. data/lib/active_model_serializers/railtie.rb +3 -1
  64. data/lib/active_model_serializers/register_jsonapi_renderer.rb +44 -31
  65. data/lib/active_model_serializers/serializable_resource.rb +6 -5
  66. data/lib/active_model_serializers/serialization_context.rb +10 -3
  67. data/lib/active_model_serializers.rb +7 -0
  68. data/lib/generators/rails/serializer_generator.rb +4 -4
  69. data/lib/grape/active_model_serializers.rb +7 -5
  70. data/lib/grape/formatters/active_model_serializers.rb +19 -2
  71. data/lib/grape/helpers/active_model_serializers.rb +1 -0
  72. data/test/action_controller/adapter_selector_test.rb +4 -4
  73. data/test/action_controller/explicit_serializer_test.rb +5 -4
  74. data/test/action_controller/json/include_test.rb +106 -27
  75. data/test/action_controller/json_api/errors_test.rb +6 -7
  76. data/test/action_controller/json_api/fields_test.rb +57 -0
  77. data/test/action_controller/json_api/linked_test.rb +29 -24
  78. data/test/action_controller/json_api/pagination_test.rb +19 -19
  79. data/test/action_controller/json_api/transform_test.rb +3 -3
  80. data/test/action_controller/lookup_proc_test.rb +49 -0
  81. data/test/action_controller/namespace_lookup_test.rb +226 -0
  82. data/test/action_controller/serialization_test.rb +10 -7
  83. data/test/active_model_serializers/json_pointer_test.rb +15 -13
  84. data/test/active_model_serializers/key_transform_test.rb +286 -252
  85. data/test/active_model_serializers/model_test.rb +17 -4
  86. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +143 -0
  87. data/test/active_model_serializers/serialization_context_test_isolated.rb +23 -10
  88. data/test/adapter/attributes_test.rb +43 -0
  89. data/test/adapter/json/collection_test.rb +14 -0
  90. data/test/adapter/json/transform_test.rb +15 -15
  91. data/test/adapter/json_api/collection_test.rb +4 -3
  92. data/test/adapter/json_api/errors_test.rb +17 -19
  93. data/test/adapter/json_api/fields_test.rb +4 -3
  94. data/test/adapter/json_api/has_many_test.rb +39 -18
  95. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +166 -0
  96. data/test/adapter/json_api/json_api_test.rb +5 -7
  97. data/test/adapter/json_api/linked_test.rb +33 -12
  98. data/test/adapter/json_api/links_test.rb +4 -2
  99. data/test/adapter/json_api/pagination_links_test.rb +35 -8
  100. data/test/adapter/json_api/relationship_test.rb +309 -73
  101. data/test/adapter/json_api/resource_identifier_test.rb +27 -2
  102. data/test/adapter/json_api/resource_meta_test.rb +3 -3
  103. data/test/adapter/json_api/transform_test.rb +255 -253
  104. data/test/adapter/json_api/type_test.rb +1 -1
  105. data/test/adapter/json_test.rb +8 -7
  106. data/test/adapter/null_test.rb +1 -2
  107. data/test/adapter/polymorphic_test.rb +5 -5
  108. data/test/adapter_test.rb +1 -1
  109. data/test/benchmark/app.rb +1 -1
  110. data/test/benchmark/benchmarking_support.rb +1 -1
  111. data/test/benchmark/bm_active_record.rb +81 -0
  112. data/test/benchmark/bm_adapter.rb +38 -0
  113. data/test/benchmark/bm_caching.rb +16 -16
  114. data/test/benchmark/bm_lookup_chain.rb +83 -0
  115. data/test/benchmark/bm_transform.rb +16 -5
  116. data/test/benchmark/controllers.rb +16 -17
  117. data/test/benchmark/fixtures.rb +72 -72
  118. data/test/cache_test.rb +143 -49
  119. data/test/collection_serializer_test.rb +3 -3
  120. data/test/fixtures/poro.rb +52 -48
  121. data/test/generators/serializer_generator_test.rb +22 -5
  122. data/test/grape_test.rb +152 -56
  123. data/test/lint_test.rb +1 -1
  124. data/test/logger_test.rb +13 -11
  125. data/test/serializable_resource_test.rb +18 -22
  126. data/test/serializers/association_macros_test.rb +3 -2
  127. data/test/serializers/associations_test.rb +107 -32
  128. data/test/serializers/attribute_test.rb +2 -2
  129. data/test/serializers/attributes_test.rb +1 -1
  130. data/test/serializers/fieldset_test.rb +1 -1
  131. data/test/serializers/meta_test.rb +12 -6
  132. data/test/serializers/root_test.rb +1 -1
  133. data/test/serializers/serializer_for_test.rb +6 -4
  134. data/test/serializers/serializer_for_with_namespace_test.rb +87 -0
  135. data/test/support/isolated_unit.rb +5 -2
  136. data/test/support/rails5_shims.rb +8 -2
  137. data/test/support/rails_app.rb +0 -9
  138. data/test/support/serialization_testing.rb +23 -5
  139. data/test/test_helper.rb +1 -0
  140. metadata +85 -34
  141. data/.rubocop_todo.yml +0 -167
  142. data/lib/active_model/serializer/include_tree.rb +0 -111
  143. data/test/adapter/json_api/relationships_test.rb +0 -199
  144. data/test/include_tree/from_include_args_test.rb +0 -26
  145. data/test/include_tree/from_string_test.rb +0 -94
  146. data/test/include_tree/include_args_to_hash_test.rb +0 -64
@@ -0,0 +1,265 @@
1
+ [Back to Guides](../README.md)
2
+
3
+ # How to migrate from `0.8` to `0.10` safely
4
+
5
+ ## Disclaimer
6
+ ### Proceed at your own risk
7
+ This document attempts to outline steps to upgrade your app based on the collective experience of
8
+ developers who have done this already. It may not cover all edge cases and situations that may cause issues,
9
+ so please proceed with a certain level of caution.
10
+
11
+ ## Overview
12
+ This document outlines the steps needed to migrate from `0.8` to `0.10`. The method described
13
+ below has been created via the collective knowledge of contributions of those who have done
14
+ the migration successfully. The method has been tested specifically for migrating from `0.8.3`
15
+ to `0.10.2`.
16
+
17
+ The high level approach is to upgrade to `0.10` and change all serializers to use
18
+ a backwards-compatible `ActiveModel::V08::Serializer`or `ActiveModel::V08::CollectionSerializer`
19
+ and a `ActiveModelSerializers::Adapter::V08Adapter`. After a few more manual changes, you should have the same
20
+ functionality as you had with `AMS 0.8`. Then, you can continue to develop in your app by creating
21
+ new serializers that don't use these backwards compatible versions and slowly migrate
22
+ existing serializers to the `0.10` versions as needed.
23
+
24
+ ### `0.10` breaking changes
25
+ - Passing a serializer to `render json:` is no longer supported
26
+
27
+ ```ruby
28
+ render json: CustomerSerializer.new(customer) # rendered in 0.8, errors in 0.10
29
+ ```
30
+
31
+ - Passing a nil resource to serializer now fails
32
+
33
+ ```ruby
34
+ CustomerSerializer.new(nil) # returned nil in 0.8, throws error in 0.10
35
+ ```
36
+
37
+ - Attribute methods are no longer defined on the serializer, and must be explicitly
38
+ accessed through `object`
39
+
40
+ ```ruby
41
+ class MySerializer
42
+ attributes :foo, :bar
43
+
44
+ def foo
45
+ bar + 1 # bar does not work, needs to be object.bar in 0.10
46
+ end
47
+ end
48
+ ```
49
+
50
+ - `root` option to collection serializer behaves differently
51
+
52
+ ```ruby
53
+ # in 0.8
54
+ ActiveModel::ArraySerializer.new(resources, root: "resources")
55
+ # resulted in { "resources": <serialized_resources> }, does not work in 0.10
56
+ ```
57
+
58
+ - No default serializer when serializer doesn't exist
59
+ - `@options` changed to `instance_options`
60
+ - Nested relationships are no longer walked by default. Use the `:include` option at **controller `render`** level to specify what relationships to walk. E.g. `render json: @post, include: {comments: :author}` if you want the `author` relationship walked, otherwise the json would only include the post with comments. See: https://github.com/rails-api/active_model_serializers/pull/1127
61
+ - To emulate `0.8`'s walking of arbitrarily deep relationships use: `include: '**'`. E.g. `render json: @post, include: '**'`
62
+
63
+ ## Steps to migrate
64
+
65
+ ### 1. Upgrade the `active_model_serializer` gem in you `Gemfile`
66
+ Change to `gem 'active_model_serializers', '~> 0.10'` and run `bundle install`
67
+
68
+ ### 2. Add `ActiveModel::V08::Serializer`
69
+
70
+ ```ruby
71
+ module ActiveModel
72
+ module V08
73
+ class Serializer < ActiveModel::Serializer
74
+ include Rails.application.routes.url_helpers
75
+
76
+ # AMS 0.8 would delegate method calls from within the serializer to the
77
+ # object.
78
+ def method_missing(*args)
79
+ method = args.first
80
+ read_attribute_for_serialization(method)
81
+ end
82
+
83
+ alias_method :options, :instance_options
84
+
85
+ # Since attributes could be read from the `object` via `method_missing`,
86
+ # the `try` method did not behave as before. This patches `try` with the
87
+ # original implementation plus the addition of
88
+ # ` || object.respond_to?(a.first, true)` to check if the object responded to
89
+ # the given method.
90
+ def try(*a, &b)
91
+ if a.empty? || respond_to?(a.first, true) || object.respond_to?(a.first, true)
92
+ try!(*a, &b)
93
+ end
94
+ end
95
+
96
+ # AMS 0.8 would return nil if the serializer was initialized with a nil
97
+ # resource.
98
+ def serializable_hash(adapter_options = nil,
99
+ options = {},
100
+ adapter_instance =
101
+ self.class.serialization_adapter_instance)
102
+ object.nil? ? nil : super
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ ```
109
+ Add this class to your app however you see fit. This is the class that your existing serializers
110
+ that inherit from `ActiveMode::Serializer` should inherit from.
111
+
112
+ ### 3. Add `ActiveModel::V08::CollectionSerializer`
113
+ ```ruby
114
+ module ActiveModel
115
+ module V08
116
+ class CollectionSerializer < ActiveModel::Serializer::CollectionSerializer
117
+ # In AMS 0.8, passing an ArraySerializer instance with a `root` option
118
+ # properly nested the serialized resources within the given root.
119
+ # Ex.
120
+ #
121
+ # class MyController < ActionController::Base
122
+ # def index
123
+ # render json: ActiveModel::Serializer::ArraySerializer
124
+ # .new(resources, root: "resources")
125
+ # end
126
+ # end
127
+ #
128
+ # Produced
129
+ #
130
+ # {
131
+ # "resources": [
132
+ # <serialized_resource>,
133
+ # ...
134
+ # ]
135
+ # }
136
+ def as_json(options = {})
137
+ if root
138
+ {
139
+ root => super
140
+ }
141
+ else
142
+ super
143
+ end
144
+ end
145
+
146
+ # AMS 0.8 used `DefaultSerializer` if it couldn't find a serializer for
147
+ # the given resource. When not using an adapter, this is not true in
148
+ # `0.10`
149
+ def serializer_from_resource(resource, serializer_context_class, options)
150
+ serializer_class =
151
+ options.fetch(:serializer) { serializer_context_class.serializer_for(resource) }
152
+
153
+ if serializer_class.nil? # rubocop:disable Style/GuardClause
154
+ DefaultSerializer.new(resource, options)
155
+ else
156
+ serializer_class.new(resource, options.except(:serializer))
157
+ end
158
+ end
159
+
160
+ class DefaultSerializer
161
+ attr_reader :object, :options
162
+
163
+ def initialize(object, options={})
164
+ @object, @options = object, options
165
+ end
166
+
167
+ def serializable_hash
168
+ @object.as_json(@options)
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
174
+ ```
175
+ Add this class to your app however you see fit. This is the class that existing uses of
176
+ `ActiveModel::ArraySerializer` should be changed to use.
177
+
178
+ ### 4. Add `ActiveModelSerializers::Adapter::V08Adapter`
179
+ ```ruby
180
+ module ActiveModelSerializers
181
+ module Adapter
182
+ class V08Adapter < ActiveModelSerializers::Adapter::Base
183
+ def serializable_hash(options = nil)
184
+ options ||= {}
185
+
186
+ if serializer.respond_to?(:each)
187
+ if serializer.root
188
+ delegate_to_json_adapter(options)
189
+ else
190
+ serializable_hash_for_collection(options)
191
+ end
192
+ else
193
+ serializable_hash_for_single_resource(options)
194
+ end
195
+ end
196
+
197
+ def serializable_hash_for_collection(options)
198
+ serializer.map do |s|
199
+ V08Adapter.new(s, instance_options)
200
+ .serializable_hash(options)
201
+ end
202
+ end
203
+
204
+ def serializable_hash_for_single_resource(options)
205
+ if serializer.object.is_a?(ActiveModel::Serializer)
206
+ # It is recommended that you add some logging here to indicate
207
+ # places that should get converted to eventually allow for this
208
+ # adapter to get removed.
209
+ @serializer = serializer.object
210
+ end
211
+
212
+ if serializer.root
213
+ delegate_to_json_adapter(options)
214
+ else
215
+ options = serialization_options(options)
216
+ serializer.serializable_hash(instance_options, options, self)
217
+ end
218
+ end
219
+
220
+ def delegate_to_json_adapter(options)
221
+ ActiveModelSerializers::Adapter::Json
222
+ .new(serializer, instance_options)
223
+ .serializable_hash(options)
224
+ end
225
+ end
226
+ end
227
+ end
228
+ ```
229
+ Add this class to your app however you see fit.
230
+
231
+ Add
232
+ ```ruby
233
+ ActiveModelSerializers.config.adapter =
234
+ ActiveModelSerializers::Adapter::V08Adapter
235
+ ```
236
+ to `config/active_model_serializer.rb` to configure AMS to use this
237
+ class as the default adapter.
238
+
239
+ ### 5. Change inheritors of `ActiveModel::Serializer` to inherit from `ActiveModel::V08::Serializer`
240
+ Simple find/replace
241
+
242
+ ### 6. Remove `private` keyword from serializers
243
+ Simple find/replace. This is required to allow the `ActiveModel::V08::Serializer`
244
+ to have proper access to the methods defined in the serializer.
245
+
246
+ You may be able to change the `private` to `protected`, but this is hasn't been tested yet.
247
+
248
+ ### 7. Remove references to `ActiveRecord::Base#active_model_serializer`
249
+ This method is no longer supported in `0.10`.
250
+
251
+ `0.10` does a good job of discovering serializers for `ActiveRecord` objects.
252
+
253
+ ### 8. Rename `ActiveModel::ArraySerializer` to `ActiveModel::V08::CollectionSerializer`
254
+ Find/replace uses of `ActiveModel::ArraySerializer` with `ActiveModel::V08::CollectionSerializer`.
255
+
256
+ Also, be sure to change the `each_serializer` keyword to `serializer` when calling making the replacement.
257
+
258
+ ### 9. Replace uses of `@options` to `instance_options` in serializers
259
+ Simple find/replace
260
+
261
+ ## Conclusion
262
+ After you've done the steps above, you should test your app to ensure that everything is still working properly.
263
+
264
+ If you run into issues, please contribute back to this document so others can benefit from your knowledge.
265
+
@@ -17,25 +17,72 @@ To solve this, in Ember, both the adapter and the serializer will need some modi
17
17
 
18
18
  ### Server-Side Changes
19
19
 
20
- there are multiple mimetypes for json that should all be parsed similarly, so
21
- in `config/initializers/mime_types.rb`:
20
+ First, set the adapter type in an initializer file:
21
+
22
+ ```ruby
23
+ # config/initializers/active_model_serializers.rb
24
+ ActiveModelSerializers.config.adapter = :json_api
25
+ ```
26
+
27
+ or:
28
+
29
+ ```ruby
30
+ # config/initializers/active_model_serializers.rb
31
+ ActiveModelSerializers.config.adapter = ActiveModelSerializers::Adapter::JsonApi
32
+ ```
33
+
34
+ You will also want to set the `key_transform` to `:unaltered` since you will adjust the attributes in your Ember serializer to use underscores instead of dashes later. You could also use `:underscore`, but `:unaltered` is better for performance.
35
+
22
36
  ```ruby
23
- api_mime_types = %W(
24
- application/vnd.api+json
25
- text/x-json
26
- application/json
27
- )
28
-
29
- Mime::Type.unregister :json
30
- Mime::Type.register 'application/json', :json, api_mime_types
37
+ # config/initializers/active_model_serializers.rb
38
+ ActiveModelSerializers.config.key_transform = :unaltered
31
39
  ```
32
40
 
41
+ In order to properly handle JSON API responses, we need to register a JSON API renderer, like so:
42
+
43
+ ```ruby
44
+ # config/initializers/active_model_serializers.rb
45
+ ActiveSupport.on_load(:action_controller) do
46
+ require 'active_model_serializers/register_jsonapi_renderer'
47
+ end
48
+ ```
49
+ Rails also requires your controller to tell it that you accept and generate JSONAPI data. To do that, you use `respond_to` in your controller handlers to tell rails you are consuming and returning jsonapi format data. Without this, Rails will refuse to parse the request body into params. You can add `ActionController::MimeResponds` to your application controller to enable this:
50
+
51
+ ```ruby
52
+ class ApplicationController < ActionController::API
53
+ include ActionController::MimeResponds
54
+ end
55
+ ```
56
+ Then, in your controller you can tell rails you're accepting and rendering the jsonapi format:
57
+ ```ruby
58
+ # POST /post
59
+ def create
60
+ @post = Post.new(post_params)
61
+ respond_to do |format|
62
+ if @post.save
63
+ format.jsonapi { render jsonapi: @post, status: :created, location: @post }
64
+ else
65
+ format.jsonapi { render jsonapi: @post.errors, status: :unprocessable_entity }
66
+ end
67
+ end
68
+ end
69
+
70
+ # Only allow a trusted parameter "white list" through.
71
+ def post_params
72
+ ActiveModelSerializers::Deserialization.jsonapi_parse!(params, only: [:title, :body] )
73
+ end
74
+ end
75
+ ```
76
+
77
+
33
78
  ### Adapter Changes
34
79
 
35
80
  ```javascript
36
81
  // app/adapters/application.js
82
+ import Ember from 'ember';
37
83
  import DS from 'ember-data';
38
84
  import ENV from "../config/environment";
85
+ const { underscore, pluralize } = Ember.String;
39
86
 
40
87
  export default DS.JSONAPIAdapter.extend({
41
88
  namespace: 'api',
@@ -46,22 +93,10 @@ export default DS.JSONAPIAdapter.extend({
46
93
 
47
94
  // allows the multiword paths in urls to be underscored
48
95
  pathForType: function(type) {
49
- let underscored = Ember.String.underscore(type);
50
- return Ember.String.pluralize(underscored);
96
+ let underscored = underscore(type);
97
+ return pluralize(underscored);
51
98
  },
52
99
 
53
- // allows queries to be sent along with a findRecord
54
- // hopefully Ember / EmberData will soon have this built in
55
- // ember-data issue tracked here:
56
- // https://github.com/emberjs/data/issues/3596
57
- urlForFindRecord(id, modelName, snapshot) {
58
- let url = this._super(...arguments);
59
- let query = Ember.get(snapshot, 'adapterOptions.query');
60
- if(query) {
61
- url += '?' + Ember.$.param(query);
62
- }
63
- return url;
64
- }
65
100
  });
66
101
  ```
67
102
 
@@ -85,22 +120,19 @@ export default DS.JSONAPISerializer.extend({
85
120
 
86
121
  ```
87
122
 
88
- ## Including Nested Resources
89
123
 
90
- Previously, `store.find` and `store.findRecord` did not allow specification of any query params.
91
- The ActiveModelSerializers default for the `include` parameter is to be `nil` meaning that if any associations are defined in your serializer, only the `id` and `type` will be in the `relationships` structure of the JSON API response.
92
- For more on `include` usage, see: [The JSON API include examples](./../general/adapters.md#JSON-API)
124
+ ## Including Nested Resources
93
125
 
94
- With the above modifications, you can execute code as below in order to include nested resources while doing a find query.
126
+ Ember Data can request related records by using `include`. Below are some examples of how to make Ember Data request the inclusion of related records. For more on `include` usage, see: [The JSON API include examples](./../general/adapters.md#JSON-API)
95
127
 
96
128
  ```javascript
97
- store.findRecord('post', postId, { adapterOptions: { query: { include: 'comments' } } });
129
+ store.findRecord('post', postId, { include: 'comments' } );
98
130
  ```
99
- will generate the path `/posts/{postId}?include='comments'`
131
+ which will generate the path /posts/{postId}?include='comments'
100
132
 
101
133
  So then in your controller, you'll want to be sure to have something like:
102
134
  ```ruby
103
- render json: @post, include: params[:include]
135
+ render jsonapi: @post, include: params[:include]
104
136
  ```
105
137
 
106
138
  If you want to use `include` on a collection, you'd write something like this:
@@ -28,7 +28,7 @@ Example supported requests
28
28
  - Relationships
29
29
  - GET /articles/1/relationships/comments
30
30
  - GET /articles/1/relationships/author
31
- - Optional: [Inclusion of related resources](http://jsonapi.org/format/#fetching-includes) `ActiveModel::Serializer::IncludeTree`
31
+ - Optional: [Inclusion of related resources](http://jsonapi.org/format/#fetching-includes) `JSONAPI::IncludeDirective`
32
32
  - GET /articles/1?`include`=comments
33
33
  - GET /articles/1?`include`=comments.author
34
34
  - GET /articles/1?`include`=author,comments.author
@@ -16,19 +16,29 @@ module ActionController
16
16
  included do
17
17
  class_attribute :_serialization_scope
18
18
  self._serialization_scope = :current_user
19
+
20
+ attr_writer :namespace_for_serializer
21
+ end
22
+
23
+ def namespace_for_serializer
24
+ @namespace_for_serializer ||= self.class.parent unless self.class.parent == Object
19
25
  end
20
26
 
21
27
  def serialization_scope
22
- send(_serialization_scope) if _serialization_scope &&
23
- respond_to?(_serialization_scope, true)
28
+ return unless _serialization_scope && respond_to?(_serialization_scope, true)
29
+
30
+ send(_serialization_scope)
24
31
  end
25
32
 
26
33
  def get_serializer(resource, options = {})
27
- if !use_adapter?
34
+ unless use_adapter?
28
35
  warn 'ActionController::Serialization#use_adapter? has been removed. '\
29
36
  "Please pass 'adapter: false' or see ActiveSupport::SerializableResource.new"
30
37
  options[:adapter] = false
31
38
  end
39
+
40
+ options.fetch(:namespace) { options[:namespace] = namespace_for_serializer }
41
+
32
42
  serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)
33
43
  serializable_resource.serialization_scope ||= options.fetch(:scope) { serialization_scope }
34
44
  serializable_resource.serialization_scope_name = options.fetch(:scope_name) { _serialization_scope }
@@ -7,9 +7,11 @@ module ActiveModel
7
7
  deprecate :inherited, 'ActiveModelSerializers::Adapter::Base.'
8
8
  end
9
9
 
10
+ # :nocov:
10
11
  def initialize(serializer, options = {})
11
12
  super(ActiveModelSerializers::Adapter::Base.new(serializer, options))
12
13
  end
14
+ # :nocov:
13
15
  end
14
16
  end
15
17
  end
@@ -1,9 +1,12 @@
1
1
  require 'active_model/serializer/collection_serializer'
2
- class ActiveModel::Serializer
3
- class ArraySerializer < CollectionSerializer
4
- class << self
5
- extend ActiveModelSerializers::Deprecate
6
- deprecate :new, 'ActiveModel::Serializer::CollectionSerializer.'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ class ArraySerializer < CollectionSerializer
6
+ class << self
7
+ extend ActiveModelSerializers::Deprecate
8
+ deprecate :new, 'ActiveModel::Serializer::CollectionSerializer.'
9
+ end
7
10
  end
8
11
  end
9
12
  end
@@ -1,19 +1,34 @@
1
1
  module ActiveModel
2
2
  class Serializer
3
- # This class hold all information about serializer's association.
3
+ # This class holds all information about serializer's association.
4
4
  #
5
5
  # @attr [Symbol] name
6
- # @attr [ActiveModel::Serializer] serializer
7
6
  # @attr [Hash{Symbol => Object}] options
7
+ # @attr [block]
8
8
  #
9
9
  # @example
10
- # Association.new(:comments, CommentSummarySerializer)
10
+ # Association.new(:comments, { serializer: CommentSummarySerializer })
11
11
  #
12
- Association = Struct.new(:name, :serializer, :options, :links, :meta) do
12
+ class Association < Field
13
13
  # @return [Symbol]
14
14
  def key
15
15
  options.fetch(:key, name)
16
16
  end
17
+
18
+ # @return [ActiveModel::Serializer, nil]
19
+ def serializer
20
+ options[:serializer]
21
+ end
22
+
23
+ # @return [Hash]
24
+ def links
25
+ options.fetch(:links) || {}
26
+ end
27
+
28
+ # @return [Hash, nil]
29
+ def meta
30
+ options[:meta]
31
+ end
17
32
  end
18
33
  end
19
34
  end
@@ -2,9 +2,6 @@ module ActiveModel
2
2
  class Serializer
3
3
  # @api private
4
4
  class BelongsToReflection < SingularReflection
5
- def macro
6
- :belongs_to
7
- end
8
5
  end
9
6
  end
10
7
  end
@@ -1,7 +1,6 @@
1
1
  module ActiveModel
2
2
  class Serializer
3
3
  class CollectionSerializer
4
- NoSerializerError = Class.new(StandardError)
5
4
  include Enumerable
6
5
  delegate :each, to: :@serializers
7
6
 
@@ -11,22 +10,23 @@ module ActiveModel
11
10
  @object = resources
12
11
  @options = options
13
12
  @root = options[:root]
14
- serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer)
15
- @serializers = resources.map do |resource|
16
- serializer_class = options.fetch(:serializer) { serializer_context_class.serializer_for(resource) }
17
-
18
- if serializer_class.nil? # rubocop:disable Style/GuardClause
19
- fail NoSerializerError, "No serializer found for resource: #{resource.inspect}"
20
- else
21
- serializer_class.new(resource, options.except(:serializer))
22
- end
23
- end
13
+ @serializers = serializers_from_resources
24
14
  end
25
15
 
26
16
  def success?
27
17
  true
28
18
  end
29
19
 
20
+ # @api private
21
+ def serializable_hash(adapter_options, options, adapter_instance)
22
+ include_directive = ActiveModel::Serializer.include_directive_from_options(adapter_options)
23
+ adapter_options[:cached_attributes] ||= ActiveModel::Serializer.cache_read_multi(self, adapter_instance, include_directive)
24
+ adapter_opts = adapter_options.merge(include_directive: include_directive)
25
+ serializers.map do |serializer|
26
+ serializer.serializable_hash(adapter_opts, options, adapter_instance)
27
+ end
28
+ end
29
+
30
30
  # TODO: unify naming of root, json_key, and _type. Right now, a serializer's
31
31
  # json_key comes from the root option or the object's model name, by default.
32
32
  # But, if a dev defines a custom `json_key` method with an explicit value,
@@ -51,7 +51,8 @@ module ActiveModel
51
51
  # rubocop:enable Metrics/CyclomaticComplexity
52
52
 
53
53
  def paginated?
54
- object.respond_to?(:current_page) &&
54
+ ActiveModelSerializers.config.jsonapi_pagination_links_enabled &&
55
+ object.respond_to?(:current_page) &&
55
56
  object.respond_to?(:total_pages) &&
56
57
  object.respond_to?(:size)
57
58
  end
@@ -59,6 +60,28 @@ module ActiveModel
59
60
  protected
60
61
 
61
62
  attr_reader :serializers, :options
63
+
64
+ private
65
+
66
+ def serializers_from_resources
67
+ serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer)
68
+ object.map do |resource|
69
+ serializer_from_resource(resource, serializer_context_class, options)
70
+ end
71
+ end
72
+
73
+ def serializer_from_resource(resource, serializer_context_class, options)
74
+ serializer_class = options.fetch(:serializer) do
75
+ serializer_context_class.serializer_for(resource, namespace: options[:namespace])
76
+ end
77
+
78
+ if serializer_class.nil?
79
+ ActiveModelSerializers.logger.debug "No serializer found for resource: #{resource.inspect}"
80
+ throw :no_serializer
81
+ else
82
+ serializer_class.new(resource, options.except(:serializer))
83
+ end
84
+ end
62
85
  end
63
86
  end
64
87
  end
@@ -10,12 +10,10 @@ module ActiveModel
10
10
  module Associations
11
11
  extend ActiveSupport::Concern
12
12
 
13
- DEFAULT_INCLUDE_TREE = ActiveModel::Serializer::IncludeTree.from_string('*')
14
-
15
13
  included do
16
14
  with_options instance_writer: false, instance_reader: true do |serializer|
17
15
  serializer.class_attribute :_reflections
18
- self._reflections ||= []
16
+ self._reflections ||= {}
19
17
  end
20
18
 
21
19
  extend ActiveSupport::Autoload
@@ -41,7 +39,7 @@ module ActiveModel
41
39
  # @example
42
40
  # has_many :comments, serializer: CommentSummarySerializer
43
41
  #
44
- def has_many(name, options = {}, &block)
42
+ def has_many(name, options = {}, &block) # rubocop:disable Style/PredicateName
45
43
  associate(HasManyReflection.new(name, options, block))
46
44
  end
47
45
 
@@ -63,7 +61,7 @@ module ActiveModel
63
61
  # @example
64
62
  # has_one :author, serializer: AuthorSerializer
65
63
  #
66
- def has_one(name, options = {}, &block)
64
+ def has_one(name, options = {}, &block) # rubocop:disable Style/PredicateName
67
65
  associate(HasOneReflection.new(name, options, block))
68
66
  end
69
67
 
@@ -76,22 +74,26 @@ module ActiveModel
76
74
  # @api private
77
75
  #
78
76
  def associate(reflection)
79
- self._reflections << reflection
77
+ key = reflection.options[:key] || reflection.name
78
+ self._reflections[key] = reflection
80
79
  end
81
80
  end
82
81
 
83
- # @param [IncludeTree] include_tree (defaults to all associations when not provided)
82
+ # @param [JSONAPI::IncludeDirective] include_directive (defaults to the
83
+ # +default_include_directive+ config value when not provided)
84
84
  # @return [Enumerator<Association>]
85
85
  #
86
- def associations(include_tree = DEFAULT_INCLUDE_TREE)
86
+ def associations(include_directive = ActiveModelSerializers.default_include_directive, include_slice = nil)
87
+ include_slice ||= include_directive
87
88
  return unless object
88
89
 
89
90
  Enumerator.new do |y|
90
- self.class._reflections.each do |reflection|
91
+ self.class._reflections.values.each do |reflection|
91
92
  next if reflection.excluded?(self)
92
93
  key = reflection.options.fetch(:key, reflection.name)
93
- next unless include_tree.key?(key)
94
- y.yield reflection.build_association(self, instance_options)
94
+ next unless include_directive.key?(key)
95
+
96
+ y.yield reflection.build_association(self, instance_options, include_slice)
95
97
  end
96
98
  end
97
99
  end