active_model_serializers 0.8.3 → 0.10.0

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 (232) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +29 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  4. data/.gitignore +17 -0
  5. data/.rubocop.yml +104 -0
  6. data/.rubocop_todo.yml +167 -0
  7. data/.simplecov +110 -0
  8. data/.travis.yml +39 -24
  9. data/CHANGELOG.md +465 -6
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +50 -1
  12. data/{MIT-LICENSE.txt → MIT-LICENSE} +3 -2
  13. data/README.md +102 -590
  14. data/Rakefile +93 -8
  15. data/active_model_serializers.gemspec +65 -23
  16. data/appveyor.yml +24 -0
  17. data/bin/bench +171 -0
  18. data/bin/bench_regression +316 -0
  19. data/bin/serve_benchmark +39 -0
  20. data/docs/ARCHITECTURE.md +126 -0
  21. data/docs/README.md +40 -0
  22. data/docs/STYLE.md +58 -0
  23. data/docs/general/adapters.md +245 -0
  24. data/docs/general/caching.md +52 -0
  25. data/docs/general/configuration_options.md +100 -0
  26. data/docs/general/deserialization.md +100 -0
  27. data/docs/general/getting_started.md +133 -0
  28. data/docs/general/instrumentation.md +40 -0
  29. data/docs/general/key_transforms.md +40 -0
  30. data/docs/general/logging.md +14 -0
  31. data/docs/general/rendering.md +255 -0
  32. data/docs/general/serializers.md +372 -0
  33. data/docs/how-open-source-maintained.jpg +0 -0
  34. data/docs/howto/add_pagination_links.md +139 -0
  35. data/docs/howto/add_root_key.md +51 -0
  36. data/docs/howto/outside_controller_use.md +58 -0
  37. data/docs/howto/passing_arbitrary_options.md +27 -0
  38. data/docs/howto/serialize_poro.md +32 -0
  39. data/docs/howto/test.md +152 -0
  40. data/docs/integrations/ember-and-json-api.md +112 -0
  41. data/docs/integrations/grape.md +19 -0
  42. data/docs/jsonapi/errors.md +56 -0
  43. data/docs/jsonapi/schema/schema.json +366 -0
  44. data/docs/jsonapi/schema.md +151 -0
  45. data/docs/rfcs/0000-namespace.md +106 -0
  46. data/docs/rfcs/template.md +15 -0
  47. data/lib/action_controller/serialization.rb +31 -36
  48. data/lib/active_model/serializable_resource.rb +11 -0
  49. data/lib/active_model/serializer/adapter/attributes.rb +15 -0
  50. data/lib/active_model/serializer/adapter/base.rb +16 -0
  51. data/lib/active_model/serializer/adapter/json.rb +15 -0
  52. data/lib/active_model/serializer/adapter/json_api.rb +15 -0
  53. data/lib/active_model/serializer/adapter/null.rb +15 -0
  54. data/lib/active_model/serializer/adapter.rb +24 -0
  55. data/lib/active_model/serializer/array_serializer.rb +9 -0
  56. data/lib/active_model/serializer/association.rb +19 -0
  57. data/lib/active_model/serializer/associations.rb +87 -220
  58. data/lib/active_model/serializer/attribute.rb +25 -0
  59. data/lib/active_model/serializer/attributes.rb +82 -0
  60. data/lib/active_model/serializer/belongs_to_reflection.rb +10 -0
  61. data/lib/active_model/serializer/caching.rb +333 -0
  62. data/lib/active_model/serializer/collection_reflection.rb +7 -0
  63. data/lib/active_model/serializer/collection_serializer.rb +64 -0
  64. data/lib/active_model/serializer/configuration.rb +35 -0
  65. data/lib/active_model/serializer/error_serializer.rb +10 -0
  66. data/lib/active_model/serializer/errors_serializer.rb +27 -0
  67. data/lib/active_model/serializer/field.rb +90 -0
  68. data/lib/active_model/serializer/fieldset.rb +31 -0
  69. data/lib/active_model/serializer/has_many_reflection.rb +10 -0
  70. data/lib/active_model/serializer/has_one_reflection.rb +10 -0
  71. data/lib/active_model/serializer/include_tree.rb +111 -0
  72. data/lib/active_model/serializer/links.rb +35 -0
  73. data/lib/active_model/serializer/lint.rb +146 -0
  74. data/lib/active_model/serializer/meta.rb +29 -0
  75. data/lib/active_model/serializer/null.rb +17 -0
  76. data/lib/active_model/serializer/reflection.rb +147 -0
  77. data/lib/active_model/serializer/singular_reflection.rb +7 -0
  78. data/lib/active_model/serializer/type.rb +25 -0
  79. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  80. data/lib/active_model/serializer.rb +158 -481
  81. data/lib/active_model_serializers/adapter/attributes.rb +76 -0
  82. data/lib/active_model_serializers/adapter/base.rb +83 -0
  83. data/lib/active_model_serializers/adapter/json.rb +21 -0
  84. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
  85. data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
  86. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
  87. data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
  88. data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
  89. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +62 -0
  90. data/lib/active_model_serializers/adapter/json_api/relationship.rb +52 -0
  91. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +37 -0
  92. data/lib/active_model_serializers/adapter/json_api.rb +516 -0
  93. data/lib/active_model_serializers/adapter/null.rb +9 -0
  94. data/lib/active_model_serializers/adapter.rb +92 -0
  95. data/lib/active_model_serializers/callbacks.rb +55 -0
  96. data/lib/active_model_serializers/deprecate.rb +55 -0
  97. data/lib/active_model_serializers/deserialization.rb +13 -0
  98. data/lib/active_model_serializers/json_pointer.rb +14 -0
  99. data/lib/active_model_serializers/key_transform.rb +70 -0
  100. data/lib/active_model_serializers/logging.rb +122 -0
  101. data/lib/active_model_serializers/model.rb +49 -0
  102. data/lib/active_model_serializers/railtie.rb +46 -0
  103. data/lib/active_model_serializers/register_jsonapi_renderer.rb +65 -0
  104. data/lib/active_model_serializers/serializable_resource.rb +81 -0
  105. data/lib/active_model_serializers/serialization_context.rb +32 -0
  106. data/lib/active_model_serializers/test/schema.rb +138 -0
  107. data/lib/active_model_serializers/test/serializer.rb +125 -0
  108. data/lib/active_model_serializers/test.rb +7 -0
  109. data/lib/active_model_serializers.rb +32 -89
  110. data/lib/generators/rails/USAGE +6 -0
  111. data/lib/generators/rails/resource_override.rb +10 -0
  112. data/lib/generators/rails/serializer_generator.rb +36 -0
  113. data/lib/generators/rails/templates/serializer.rb.erb +8 -0
  114. data/lib/grape/active_model_serializers.rb +14 -0
  115. data/lib/grape/formatters/active_model_serializers.rb +15 -0
  116. data/lib/grape/helpers/active_model_serializers.rb +16 -0
  117. data/test/action_controller/adapter_selector_test.rb +53 -0
  118. data/test/action_controller/explicit_serializer_test.rb +134 -0
  119. data/test/action_controller/json/include_test.rb +167 -0
  120. data/test/action_controller/json_api/deserialization_test.rb +112 -0
  121. data/test/action_controller/json_api/errors_test.rb +41 -0
  122. data/test/action_controller/json_api/linked_test.rb +197 -0
  123. data/test/action_controller/json_api/pagination_test.rb +116 -0
  124. data/test/action_controller/json_api/transform_test.rb +181 -0
  125. data/test/action_controller/serialization_scope_name_test.rb +229 -0
  126. data/test/action_controller/serialization_test.rb +469 -0
  127. data/test/active_model_serializers/adapter_for_test.rb +208 -0
  128. data/test/active_model_serializers/json_pointer_test.rb +20 -0
  129. data/test/active_model_serializers/key_transform_test.rb +263 -0
  130. data/test/active_model_serializers/logging_test.rb +77 -0
  131. data/test/active_model_serializers/model_test.rb +9 -0
  132. data/test/active_model_serializers/railtie_test_isolated.rb +63 -0
  133. data/test/active_model_serializers/serialization_context_test_isolated.rb +58 -0
  134. data/test/active_model_serializers/test/schema_test.rb +130 -0
  135. data/test/active_model_serializers/test/serializer_test.rb +62 -0
  136. data/test/active_record_test.rb +9 -0
  137. data/test/adapter/deprecation_test.rb +100 -0
  138. data/test/adapter/json/belongs_to_test.rb +45 -0
  139. data/test/adapter/json/collection_test.rb +90 -0
  140. data/test/adapter/json/has_many_test.rb +45 -0
  141. data/test/adapter/json/transform_test.rb +93 -0
  142. data/test/adapter/json_api/belongs_to_test.rb +155 -0
  143. data/test/adapter/json_api/collection_test.rb +95 -0
  144. data/test/adapter/json_api/errors_test.rb +78 -0
  145. data/test/adapter/json_api/fields_test.rb +87 -0
  146. data/test/adapter/json_api/has_many_embed_ids_test.rb +43 -0
  147. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
  148. data/test/adapter/json_api/has_many_test.rb +144 -0
  149. data/test/adapter/json_api/has_one_test.rb +80 -0
  150. data/test/adapter/json_api/json_api_test.rb +35 -0
  151. data/test/adapter/json_api/linked_test.rb +392 -0
  152. data/test/adapter/json_api/links_test.rb +93 -0
  153. data/test/adapter/json_api/pagination_links_test.rb +166 -0
  154. data/test/adapter/json_api/parse_test.rb +137 -0
  155. data/test/adapter/json_api/relationship_test.rb +161 -0
  156. data/test/adapter/json_api/relationships_test.rb +199 -0
  157. data/test/adapter/json_api/resource_identifier_test.rb +85 -0
  158. data/test/adapter/json_api/resource_meta_test.rb +100 -0
  159. data/test/adapter/json_api/toplevel_jsonapi_test.rb +82 -0
  160. data/test/adapter/json_api/transform_test.rb +502 -0
  161. data/test/adapter/json_api/type_test.rb +61 -0
  162. data/test/adapter/json_test.rb +45 -0
  163. data/test/adapter/null_test.rb +23 -0
  164. data/test/adapter/polymorphic_test.rb +171 -0
  165. data/test/adapter_test.rb +67 -0
  166. data/test/array_serializer_test.rb +20 -73
  167. data/test/benchmark/app.rb +65 -0
  168. data/test/benchmark/benchmarking_support.rb +67 -0
  169. data/test/benchmark/bm_caching.rb +119 -0
  170. data/test/benchmark/bm_transform.rb +34 -0
  171. data/test/benchmark/config.ru +3 -0
  172. data/test/benchmark/controllers.rb +84 -0
  173. data/test/benchmark/fixtures.rb +219 -0
  174. data/test/cache_test.rb +485 -0
  175. data/test/collection_serializer_test.rb +110 -0
  176. data/test/fixtures/active_record.rb +78 -0
  177. data/test/fixtures/poro.rb +282 -0
  178. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  179. data/test/generators/serializer_generator_test.rb +57 -0
  180. data/test/grape_test.rb +82 -0
  181. data/test/include_tree/from_include_args_test.rb +26 -0
  182. data/test/include_tree/from_string_test.rb +94 -0
  183. data/test/include_tree/include_args_to_hash_test.rb +64 -0
  184. data/test/lint_test.rb +49 -0
  185. data/test/logger_test.rb +18 -0
  186. data/test/poro_test.rb +9 -0
  187. data/test/serializable_resource_test.rb +83 -0
  188. data/test/serializers/association_macros_test.rb +36 -0
  189. data/test/serializers/associations_test.rb +295 -0
  190. data/test/serializers/attribute_test.rb +151 -0
  191. data/test/serializers/attributes_test.rb +52 -0
  192. data/test/serializers/caching_configuration_test_isolated.rb +170 -0
  193. data/test/serializers/configuration_test.rb +32 -0
  194. data/test/serializers/fieldset_test.rb +14 -0
  195. data/test/serializers/meta_test.rb +196 -0
  196. data/test/serializers/options_test.rb +21 -0
  197. data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
  198. data/test/serializers/root_test.rb +21 -0
  199. data/test/serializers/serialization_test.rb +55 -0
  200. data/test/serializers/serializer_for_test.rb +134 -0
  201. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  202. data/test/support/isolated_unit.rb +79 -0
  203. data/test/support/rails5_shims.rb +47 -0
  204. data/test/support/rails_app.rb +45 -0
  205. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  206. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
  207. data/test/support/schemas/custom/show.json +7 -0
  208. data/test/support/schemas/hyper_schema.json +93 -0
  209. data/test/support/schemas/render_using_json_api.json +43 -0
  210. data/test/support/schemas/simple_json_pointers.json +10 -0
  211. data/test/support/serialization_testing.rb +53 -0
  212. data/test/test_helper.rb +48 -23
  213. metadata +449 -43
  214. data/DESIGN.textile +0 -586
  215. data/Gemfile.edge +0 -9
  216. data/bench/perf.rb +0 -43
  217. data/cruft.md +0 -19
  218. data/lib/active_model/array_serializer.rb +0 -104
  219. data/lib/active_record/serializer_override.rb +0 -16
  220. data/lib/generators/resource_override.rb +0 -13
  221. data/lib/generators/serializer/USAGE +0 -9
  222. data/lib/generators/serializer/serializer_generator.rb +0 -42
  223. data/lib/generators/serializer/templates/serializer.rb +0 -19
  224. data/test/association_test.rb +0 -592
  225. data/test/caching_test.rb +0 -96
  226. data/test/generators_test.rb +0 -85
  227. data/test/no_serialization_scope_test.rb +0 -34
  228. data/test/serialization_scope_name_test.rb +0 -67
  229. data/test/serialization_test.rb +0 -392
  230. data/test/serializer_support_test.rb +0 -51
  231. data/test/serializer_test.rb +0 -1465
  232. data/test/test_fakes.rb +0 -217
@@ -1,32 +1,18 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+ require 'active_model_serializers/serialization_context'
3
+
1
4
  module ActionController
2
- # Action Controller Serialization
3
- #
4
- # Overrides render :json to check if the given object implements +active_model_serializer+
5
- # as a method. If so, use the returned serializer instead of calling +to_json+ on the object.
6
- #
7
- # This module also provides a serialization_scope method that allows you to configure the
8
- # +serialization_scope+ of the serializer. Most apps will likely set the +serialization_scope+
9
- # to the current user:
10
- #
11
- # class ApplicationController < ActionController::Base
12
- # serialization_scope :current_user
13
- # end
14
- #
15
- # If you need more complex scope rules, you can simply override the serialization_scope:
16
- #
17
- # class ApplicationController < ActionController::Base
18
- # private
19
- #
20
- # def serialization_scope
21
- # current_user
22
- # end
23
- # end
24
- #
25
5
  module Serialization
26
6
  extend ActiveSupport::Concern
27
7
 
28
8
  include ActionController::Renderers
29
9
 
10
+ module ClassMethods
11
+ def serialization_scope(scope)
12
+ self._serialization_scope = scope
13
+ end
14
+ end
15
+
30
16
  included do
31
17
  class_attribute :_serialization_scope
32
18
  self._serialization_scope = :current_user
@@ -37,24 +23,33 @@ module ActionController
37
23
  respond_to?(_serialization_scope, true)
38
24
  end
39
25
 
40
- def default_serializer_options
26
+ def get_serializer(resource, options = {})
27
+ if !use_adapter?
28
+ warn 'ActionController::Serialization#use_adapter? has been removed. '\
29
+ "Please pass 'adapter: false' or see ActiveSupport::SerializableResource.new"
30
+ options[:adapter] = false
31
+ end
32
+ serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)
33
+ serializable_resource.serialization_scope ||= options.fetch(:scope) { serialization_scope }
34
+ serializable_resource.serialization_scope_name = options.fetch(:scope_name) { _serialization_scope }
35
+ # For compatibility with the JSON renderer: `json.to_json(options) if json.is_a?(String)`.
36
+ # Otherwise, since `serializable_resource` is not a string, the renderer would call
37
+ # `to_json` on a String and given odd results, such as `"".to_json #=> '""'`
38
+ serializable_resource.adapter.is_a?(String) ? serializable_resource.adapter : serializable_resource
39
+ end
40
+
41
+ # Deprecated
42
+ def use_adapter?
43
+ true
41
44
  end
42
45
 
43
46
  [:_render_option_json, :_render_with_renderer_json].each do |renderer_method|
44
47
  define_method renderer_method do |resource, options|
45
- json = ActiveModel::Serializer.build_json(self, resource, options)
46
-
47
- if json
48
- super(json, options)
49
- else
50
- super(resource, options)
48
+ options.fetch(:serialization_context) do
49
+ options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request, options)
51
50
  end
52
- end
53
- end
54
-
55
- module ClassMethods
56
- def serialization_scope(scope)
57
- self._serialization_scope = scope
51
+ serializable_resource = get_serializer(resource, options)
52
+ super(serializable_resource, options)
58
53
  end
59
54
  end
60
55
  end
@@ -0,0 +1,11 @@
1
+ require 'set'
2
+
3
+ module ActiveModel
4
+ class SerializableResource
5
+ class << self
6
+ extend ActiveModelSerializers::Deprecate
7
+
8
+ delegate_and_deprecate :new, ActiveModelSerializers::SerializableResource
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Adapter
4
+ class Attributes < DelegateClass(ActiveModelSerializers::Adapter::Attributes)
5
+ def initialize(serializer, options = {})
6
+ super(ActiveModelSerializers::Adapter::Attributes.new(serializer, options))
7
+ end
8
+ class << self
9
+ extend ActiveModelSerializers::Deprecate
10
+ deprecate :new, 'ActiveModelSerializers::Adapter::Json.'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Adapter
4
+ class Base < DelegateClass(ActiveModelSerializers::Adapter::Base)
5
+ class << self
6
+ extend ActiveModelSerializers::Deprecate
7
+ deprecate :inherited, 'ActiveModelSerializers::Adapter::Base.'
8
+ end
9
+
10
+ def initialize(serializer, options = {})
11
+ super(ActiveModelSerializers::Adapter::Base.new(serializer, options))
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Adapter
4
+ class Json < DelegateClass(ActiveModelSerializers::Adapter::Json)
5
+ def initialize(serializer, options = {})
6
+ super(ActiveModelSerializers::Adapter::Json.new(serializer, options))
7
+ end
8
+ class << self
9
+ extend ActiveModelSerializers::Deprecate
10
+ deprecate :new, 'ActiveModelSerializers::Adapter::Json.new'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Adapter
4
+ class JsonApi < DelegateClass(ActiveModelSerializers::Adapter::JsonApi)
5
+ def initialize(serializer, options = {})
6
+ super(ActiveModelSerializers::Adapter::JsonApi.new(serializer, options))
7
+ end
8
+ class << self
9
+ extend ActiveModelSerializers::Deprecate
10
+ deprecate :new, 'ActiveModelSerializers::Adapter::JsonApi.new'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Adapter
4
+ class Null < DelegateClass(ActiveModelSerializers::Adapter::Null)
5
+ def initialize(serializer, options = {})
6
+ super(ActiveModelSerializers::Adapter::Null.new(serializer, options))
7
+ end
8
+ class << self
9
+ extend ActiveModelSerializers::Deprecate
10
+ deprecate :new, 'ActiveModelSerializers::Adapter::Null.new'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ require 'active_model_serializers/adapter'
2
+ require 'active_model_serializers/deprecate'
3
+
4
+ module ActiveModel
5
+ class Serializer
6
+ # @deprecated Use ActiveModelSerializers::Adapter instead
7
+ module Adapter
8
+ class << self
9
+ extend ActiveModelSerializers::Deprecate
10
+
11
+ DEPRECATED_METHODS = [:create, :adapter_class, :adapter_map, :adapters, :register, :lookup].freeze
12
+ DEPRECATED_METHODS.each do |method|
13
+ delegate_and_deprecate method, ActiveModelSerializers::Adapter
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ require 'active_model/serializer/adapter/base'
21
+ require 'active_model/serializer/adapter/null'
22
+ require 'active_model/serializer/adapter/attributes'
23
+ require 'active_model/serializer/adapter/json'
24
+ require 'active_model/serializer/adapter/json_api'
@@ -0,0 +1,9 @@
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.'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # This class hold all information about serializer's association.
4
+ #
5
+ # @attr [Symbol] name
6
+ # @attr [ActiveModel::Serializer] serializer
7
+ # @attr [Hash{Symbol => Object}] options
8
+ #
9
+ # @example
10
+ # Association.new(:comments, CommentSummarySerializer)
11
+ #
12
+ Association = Struct.new(:name, :serializer, :options, :links, :meta) do
13
+ # @return [Symbol]
14
+ def key
15
+ options.fetch(:key, name)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,230 +1,97 @@
1
1
  module ActiveModel
2
2
  class Serializer
3
- module Associations #:nodoc:
4
- class Config #:nodoc:
5
- class_attribute :options
6
-
7
- def self.refine(name, class_options)
8
- current_class = self
9
-
10
- Class.new(self) do
11
- singleton_class.class_eval do
12
- define_method(:to_s) do
13
- "(subclass of #{current_class.name})"
14
- end
15
-
16
- alias inspect to_s
17
- end
18
-
19
- self.options = class_options
20
-
21
- # cache the root so we can reuse it without falling back on a per-instance basis
22
- begin
23
- self.options[:root] ||= self.new(name, nil).root
24
- rescue
25
- # this could fail if it needs a valid source, for example a polymorphic association
26
- end
27
-
28
- end
29
- end
30
-
31
- self.options = {}
32
-
33
- def initialize(name, source, options={})
34
- @name = name
35
- @source = source
36
- @options = options
37
- end
38
-
39
- def option(key, default=nil)
40
- if @options.key?(key)
41
- @options[key]
42
- elsif self.class.options.key?(key)
43
- self.class.options[key]
44
- else
45
- default
46
- end
47
- end
48
-
49
- def target_serializer
50
- serializer = option(:serializer)
51
- serializer.is_a?(String) ? serializer.constantize : serializer
52
- end
53
-
54
- def source_serializer
55
- @source
56
- end
57
-
58
- def key
59
- option(:key) || @name
60
- end
61
-
62
- def root
63
- option(:root) || @name
64
- end
65
-
66
- def name
67
- option(:name) || @name
68
- end
69
-
70
- def associated_object
71
- option(:value) || source_serializer.send(name)
72
- end
73
-
74
- def embed_ids?
75
- [:id, :ids].include? option(:embed, source_serializer._embed)
76
- end
77
-
78
- def embed_objects?
79
- [:object, :objects].include? option(:embed, source_serializer._embed)
80
- end
81
-
82
- def embed_in_root?
83
- option(:include, source_serializer._root_embed)
84
- end
85
-
86
- def embeddable?
87
- !associated_object.nil?
88
- end
89
-
90
- protected
91
-
92
- def find_serializable(object)
93
- if target_serializer
94
- target_serializer.new(object, source_serializer.options)
95
- elsif object.respond_to?(:active_model_serializer) && (ams = object.active_model_serializer)
96
- ams.new(object, source_serializer.options)
97
- else
98
- object
99
- end
100
- end
3
+ # Defines an association in the object should be rendered.
4
+ #
5
+ # The serializer object should implement the association name
6
+ # as a method which should return an array when invoked. If a method
7
+ # with the association name does not exist, the association name is
8
+ # dispatched to the serialized object.
9
+ #
10
+ module Associations
11
+ extend ActiveSupport::Concern
12
+
13
+ DEFAULT_INCLUDE_TREE = ActiveModel::Serializer::IncludeTree.from_string('*')
14
+
15
+ included do
16
+ with_options instance_writer: false, instance_reader: true do |serializer|
17
+ serializer.class_attribute :_reflections
18
+ self._reflections ||= []
19
+ end
20
+
21
+ extend ActiveSupport::Autoload
22
+ autoload :Association
23
+ autoload :Reflection
24
+ autoload :SingularReflection
25
+ autoload :CollectionReflection
26
+ autoload :BelongsToReflection
27
+ autoload :HasOneReflection
28
+ autoload :HasManyReflection
101
29
  end
102
30
 
103
- class HasMany < Config #:nodoc:
104
- def key
105
- if key = option(:key)
106
- key
107
- elsif embed_ids?
108
- "#{@name.to_s.singularize}_ids".to_sym
109
- else
110
- @name
111
- end
112
- end
113
-
114
- def embed_key
115
- if key = option(:embed_key)
116
- key
117
- else
118
- :id
119
- end
120
- end
121
-
122
- def serialize
123
- associated_object.map do |item|
124
- find_serializable(item).serializable_hash
125
- end
126
- end
127
-
128
- def serializables
129
- associated_object.map do |item|
130
- find_serializable(item)
131
- end
132
- end
133
-
134
- def serialize_ids
135
- ids_key = "#{@name.to_s.singularize}_ids".to_sym
136
- if !option(:embed_key) && !source_serializer.respond_to?(@name.to_s) && source_serializer.object.respond_to?(ids_key)
137
- source_serializer.object.read_attribute_for_serialization(ids_key)
138
- else
139
- associated_object.map do |item|
140
- item.read_attribute_for_serialization(embed_key)
141
- end
142
- end
31
+ module ClassMethods
32
+ def inherited(base)
33
+ super
34
+ base._reflections = _reflections.dup
35
+ end
36
+
37
+ # @param [Symbol] name of the association
38
+ # @param [Hash<Symbol => any>] options for the reflection
39
+ # @return [void]
40
+ #
41
+ # @example
42
+ # has_many :comments, serializer: CommentSummarySerializer
43
+ #
44
+ def has_many(name, options = {}, &block)
45
+ associate(HasManyReflection.new(name, options, block))
46
+ end
47
+
48
+ # @param [Symbol] name of the association
49
+ # @param [Hash<Symbol => any>] options for the reflection
50
+ # @return [void]
51
+ #
52
+ # @example
53
+ # belongs_to :author, serializer: AuthorSerializer
54
+ #
55
+ def belongs_to(name, options = {}, &block)
56
+ associate(BelongsToReflection.new(name, options, block))
57
+ end
58
+
59
+ # @param [Symbol] name of the association
60
+ # @param [Hash<Symbol => any>] options for the reflection
61
+ # @return [void]
62
+ #
63
+ # @example
64
+ # has_one :author, serializer: AuthorSerializer
65
+ #
66
+ def has_one(name, options = {}, &block)
67
+ associate(HasOneReflection.new(name, options, block))
68
+ end
69
+
70
+ private
71
+
72
+ # Add reflection and define {name} accessor.
73
+ # @param [ActiveModel::Serializer::Reflection] reflection
74
+ # @return [void]
75
+ #
76
+ # @api private
77
+ #
78
+ def associate(reflection)
79
+ self._reflections << reflection
143
80
  end
144
81
  end
145
82
 
146
- class HasOne < Config #:nodoc:
147
- def embeddable?
148
- if polymorphic? && associated_object.nil?
149
- false
150
- else
151
- true
152
- end
153
- end
154
-
155
- def polymorphic?
156
- option :polymorphic
157
- end
158
-
159
- def root
160
- if root = option(:root)
161
- root
162
- elsif polymorphic?
163
- associated_object.class.to_s.pluralize.demodulize.underscore.to_sym
164
- else
165
- @name.to_s.pluralize.to_sym
166
- end
167
- end
168
-
169
- def key
170
- if key = option(:key)
171
- key
172
- elsif embed_ids? && !polymorphic?
173
- "#{@name}_id".to_sym
174
- else
175
- @name
176
- end
177
- end
178
-
179
- def embed_key
180
- if key = option(:embed_key)
181
- key
182
- else
183
- :id
184
- end
185
- end
186
-
187
- def polymorphic_key
188
- associated_object.class.to_s.demodulize.underscore.to_sym
189
- end
190
-
191
- def serialize
192
- object = associated_object
193
-
194
- if object && polymorphic?
195
- {
196
- :type => polymorphic_key,
197
- polymorphic_key => find_serializable(object).serializable_hash
198
- }
199
- elsif object
200
- find_serializable(object).serializable_hash
201
- end
202
- end
203
-
204
- def serializables
205
- object = associated_object
206
- value = object && find_serializable(object)
207
- value ? [value] : []
208
- end
209
-
210
- def serialize_ids
211
- id_key = "#{@name}_id".to_sym
212
-
213
- if polymorphic?
214
- if associated_object
215
- {
216
- :type => polymorphic_key,
217
- :id => associated_object.read_attribute_for_serialization(embed_key)
218
- }
219
- else
220
- nil
221
- end
222
- elsif !option(:embed_key) && !source_serializer.respond_to?(@name.to_s) && source_serializer.object.respond_to?(id_key)
223
- source_serializer.object.read_attribute_for_serialization(id_key)
224
- elsif associated_object
225
- associated_object.read_attribute_for_serialization(embed_key)
226
- else
227
- nil
83
+ # @param [IncludeTree] include_tree (defaults to all associations when not provided)
84
+ # @return [Enumerator<Association>]
85
+ #
86
+ def associations(include_tree = DEFAULT_INCLUDE_TREE)
87
+ return unless object
88
+
89
+ Enumerator.new do |y|
90
+ self.class._reflections.each do |reflection|
91
+ next if reflection.excluded?(self)
92
+ key = reflection.options.fetch(:key, reflection.name)
93
+ next unless include_tree.key?(key)
94
+ y.yield reflection.build_association(self, instance_options)
228
95
  end
229
96
  end
230
97
  end
@@ -0,0 +1,25 @@
1
+ require 'active_model/serializer/field'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ # Holds all the meta-data about an attribute as it was specified in the
6
+ # ActiveModel::Serializer class.
7
+ #
8
+ # @example
9
+ # class PostSerializer < ActiveModel::Serializer
10
+ # attribute :content
11
+ # attribute :name, key: :title
12
+ # attribute :email, key: :author_email, if: :user_logged_in?
13
+ # attribute :preview do
14
+ # truncate(object.content)
15
+ # end
16
+ #
17
+ # def user_logged_in?
18
+ # current_user.logged_in?
19
+ # end
20
+ # end
21
+ #
22
+ class Attribute < Field
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,82 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Attributes
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ with_options instance_writer: false, instance_reader: false do |serializer|
8
+ serializer.class_attribute :_attributes_data # @api private
9
+ self._attributes_data ||= {}
10
+ end
11
+
12
+ extend ActiveSupport::Autoload
13
+ autoload :Attribute
14
+
15
+ # Return the +attributes+ of +object+ as presented
16
+ # by the serializer.
17
+ def attributes(requested_attrs = nil, reload = false)
18
+ @attributes = nil if reload
19
+ @attributes ||= self.class._attributes_data.each_with_object({}) do |(key, attr), hash|
20
+ next if attr.excluded?(self)
21
+ next unless requested_attrs.nil? || requested_attrs.include?(key)
22
+ hash[key] = attr.value(self)
23
+ end
24
+ end
25
+ end
26
+
27
+ module ClassMethods
28
+ def inherited(base)
29
+ super
30
+ base._attributes_data = _attributes_data.dup
31
+ end
32
+
33
+ # @example
34
+ # class AdminAuthorSerializer < ActiveModel::Serializer
35
+ # attributes :id, :name, :recent_edits
36
+ def attributes(*attrs)
37
+ attrs = attrs.first if attrs.first.class == Array
38
+
39
+ attrs.each do |attr|
40
+ attribute(attr)
41
+ end
42
+ end
43
+
44
+ # @example
45
+ # class AdminAuthorSerializer < ActiveModel::Serializer
46
+ # attributes :id, :recent_edits
47
+ # attribute :name, key: :title
48
+ #
49
+ # attribute :full_name do
50
+ # "#{object.first_name} #{object.last_name}"
51
+ # end
52
+ #
53
+ # def recent_edits
54
+ # object.edits.last(5)
55
+ # end
56
+ def attribute(attr, options = {}, &block)
57
+ key = options.fetch(:key, attr)
58
+ _attributes_data[key] = Attribute.new(attr, options, block)
59
+ end
60
+
61
+ # @api private
62
+ # keys of attributes
63
+ # @see Serializer::attribute
64
+ def _attributes
65
+ _attributes_data.keys
66
+ end
67
+
68
+ # @api private
69
+ # maps attribute value to explict key name
70
+ # @see Serializer::attribute
71
+ # @see FragmentCache#fragment_serializer
72
+ def _attributes_keys
73
+ _attributes_data
74
+ .each_with_object({}) do |(key, attr), hash|
75
+ next if key == attr.name
76
+ hash[attr.name] = { key: key }
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ # @api private
4
+ class BelongsToReflection < SingularReflection
5
+ def macro
6
+ :belongs_to
7
+ end
8
+ end
9
+ end
10
+ end