active_model_serializers 0.9.9 → 0.10.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +634 -61
  3. data/MIT-LICENSE +3 -2
  4. data/README.md +194 -846
  5. data/lib/action_controller/serialization.rb +35 -66
  6. data/lib/active_model/serializable_resource.rb +13 -0
  7. data/lib/active_model/serializer/adapter/attributes.rb +17 -0
  8. data/lib/active_model/serializer/adapter/base.rb +20 -0
  9. data/lib/active_model/serializer/adapter/json.rb +17 -0
  10. data/lib/active_model/serializer/adapter/json_api.rb +17 -0
  11. data/lib/active_model/serializer/adapter/null.rb +17 -0
  12. data/lib/active_model/serializer/adapter.rb +26 -0
  13. data/lib/active_model/serializer/array_serializer.rb +14 -0
  14. data/lib/active_model/serializer/association.rb +55 -40
  15. data/lib/active_model/serializer/attribute.rb +27 -0
  16. data/lib/active_model/serializer/belongs_to_reflection.rb +13 -0
  17. data/lib/active_model/serializer/collection_serializer.rb +99 -0
  18. data/lib/active_model/serializer/concerns/caching.rb +305 -0
  19. data/lib/active_model/serializer/error_serializer.rb +16 -0
  20. data/lib/active_model/serializer/errors_serializer.rb +34 -0
  21. data/lib/active_model/serializer/field.rb +92 -0
  22. data/lib/active_model/serializer/fieldset.rb +33 -0
  23. data/lib/active_model/serializer/has_many_reflection.rb +12 -0
  24. data/lib/active_model/serializer/has_one_reflection.rb +9 -0
  25. data/lib/active_model/serializer/lazy_association.rb +99 -0
  26. data/lib/active_model/serializer/link.rb +23 -0
  27. data/lib/active_model/serializer/lint.rb +152 -0
  28. data/lib/active_model/serializer/null.rb +19 -0
  29. data/lib/active_model/serializer/reflection.rb +212 -0
  30. data/lib/active_model/serializer/version.rb +3 -1
  31. data/lib/active_model/serializer.rb +363 -269
  32. data/lib/active_model_serializers/adapter/attributes.rb +36 -0
  33. data/lib/active_model_serializers/adapter/base.rb +85 -0
  34. data/lib/active_model_serializers/adapter/json.rb +23 -0
  35. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +215 -0
  36. data/lib/active_model_serializers/adapter/json_api/error.rb +98 -0
  37. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +51 -0
  38. data/lib/active_model_serializers/adapter/json_api/link.rb +85 -0
  39. data/lib/active_model_serializers/adapter/json_api/meta.rb +39 -0
  40. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +94 -0
  41. data/lib/active_model_serializers/adapter/json_api/relationship.rb +106 -0
  42. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +68 -0
  43. data/lib/active_model_serializers/adapter/json_api.rb +535 -0
  44. data/lib/active_model_serializers/adapter/null.rb +11 -0
  45. data/lib/active_model_serializers/adapter.rb +100 -0
  46. data/lib/active_model_serializers/callbacks.rb +57 -0
  47. data/lib/active_model_serializers/deprecate.rb +56 -0
  48. data/lib/active_model_serializers/deserialization.rb +17 -0
  49. data/lib/active_model_serializers/json_pointer.rb +16 -0
  50. data/lib/active_model_serializers/logging.rb +124 -0
  51. data/lib/active_model_serializers/lookup_chain.rb +82 -0
  52. data/lib/active_model_serializers/model.rb +132 -0
  53. data/lib/active_model_serializers/railtie.rb +52 -0
  54. data/lib/active_model_serializers/register_jsonapi_renderer.rb +80 -0
  55. data/lib/active_model_serializers/serializable_resource.rb +84 -0
  56. data/lib/active_model_serializers/serialization_context.rb +41 -0
  57. data/lib/active_model_serializers/test/schema.rb +140 -0
  58. data/lib/active_model_serializers/test/serializer.rb +127 -0
  59. data/lib/active_model_serializers/test.rb +9 -0
  60. data/lib/active_model_serializers.rb +60 -17
  61. data/lib/generators/rails/USAGE +6 -0
  62. data/lib/{active_model/serializer/generators → generators/rails}/resource_override.rb +3 -4
  63. data/lib/{active_model/serializer/generators/serializer → generators/rails}/serializer_generator.rb +6 -5
  64. data/lib/grape/active_model_serializers.rb +18 -0
  65. data/lib/grape/formatters/active_model_serializers.rb +34 -0
  66. data/lib/grape/helpers/active_model_serializers.rb +19 -0
  67. data/lib/tasks/rubocop.rake +60 -0
  68. metadata +248 -155
  69. data/CONTRIBUTING.md +0 -20
  70. data/DESIGN.textile +0 -586
  71. data/lib/action_controller/serialization_test_case.rb +0 -79
  72. data/lib/active_model/array_serializer.rb +0 -68
  73. data/lib/active_model/default_serializer.rb +0 -28
  74. data/lib/active_model/serializable/utils.rb +0 -16
  75. data/lib/active_model/serializable.rb +0 -59
  76. data/lib/active_model/serializer/association/has_many.rb +0 -39
  77. data/lib/active_model/serializer/association/has_one.rb +0 -25
  78. data/lib/active_model/serializer/config.rb +0 -31
  79. data/lib/active_model/serializer/generators/serializer/USAGE +0 -9
  80. data/lib/active_model/serializer/generators/serializer/scaffold_controller_generator.rb +0 -14
  81. data/lib/active_model/serializer/generators/serializer/templates/controller.rb +0 -93
  82. data/lib/active_model/serializer/railtie.rb +0 -22
  83. data/lib/active_model/serializer_support.rb +0 -5
  84. data/test/benchmark/app.rb +0 -60
  85. data/test/benchmark/benchmarking_support.rb +0 -67
  86. data/test/benchmark/bm_active_record.rb +0 -41
  87. data/test/benchmark/setup.rb +0 -75
  88. data/test/benchmark/tmp/miniprofiler/mp_timers_6eqewtfgrhitvq5gqm25 +0 -0
  89. data/test/benchmark/tmp/miniprofiler/mp_timers_8083sx03hu72pxz1a4d0 +0 -0
  90. data/test/benchmark/tmp/miniprofiler/mp_timers_fyz2gsml4z0ph9kpoy1c +0 -0
  91. data/test/benchmark/tmp/miniprofiler/mp_timers_hjry5rc32imd42oxoi48 +0 -0
  92. data/test/benchmark/tmp/miniprofiler/mp_timers_m8fpoz2cvt3g9agz0bs3 +0 -0
  93. data/test/benchmark/tmp/miniprofiler/mp_timers_p92m2drnj1i568u3sta0 +0 -0
  94. data/test/benchmark/tmp/miniprofiler/mp_timers_qg52tpca3uesdfguee9i +0 -0
  95. data/test/benchmark/tmp/miniprofiler/mp_timers_s15t1a6mvxe0z7vjv790 +0 -0
  96. data/test/benchmark/tmp/miniprofiler/mp_timers_x8kal3d17nfds6vp4kcj +0 -0
  97. data/test/benchmark/tmp/miniprofiler/mp_views_127.0.0.1 +0 -0
  98. data/test/fixtures/active_record.rb +0 -96
  99. data/test/fixtures/poro.rb +0 -254
  100. data/test/fixtures/template.html.erb +0 -1
  101. data/test/integration/action_controller/namespaced_serialization_test.rb +0 -105
  102. data/test/integration/action_controller/serialization_test.rb +0 -287
  103. data/test/integration/action_controller/serialization_test_case_test.rb +0 -71
  104. data/test/integration/active_record/active_record_test.rb +0 -94
  105. data/test/integration/generators/resource_generator_test.rb +0 -26
  106. data/test/integration/generators/scaffold_controller_generator_test.rb +0 -64
  107. data/test/integration/generators/serializer_generator_test.rb +0 -41
  108. data/test/test_app.rb +0 -14
  109. data/test/test_helper.rb +0 -26
  110. data/test/tmp/app/assets/javascripts/accounts.js +0 -2
  111. data/test/tmp/app/assets/stylesheets/accounts.css +0 -4
  112. data/test/tmp/app/controllers/accounts_controller.rb +0 -3
  113. data/test/tmp/app/helpers/accounts_helper.rb +0 -3
  114. data/test/tmp/app/serializers/account_serializer.rb +0 -4
  115. data/test/tmp/config/routes.rb +0 -2
  116. data/test/unit/active_model/array_serializer/except_test.rb +0 -18
  117. data/test/unit/active_model/array_serializer/key_format_test.rb +0 -18
  118. data/test/unit/active_model/array_serializer/meta_test.rb +0 -53
  119. data/test/unit/active_model/array_serializer/only_test.rb +0 -18
  120. data/test/unit/active_model/array_serializer/options_test.rb +0 -16
  121. data/test/unit/active_model/array_serializer/root_test.rb +0 -102
  122. data/test/unit/active_model/array_serializer/scope_test.rb +0 -24
  123. data/test/unit/active_model/array_serializer/serialization_test.rb +0 -239
  124. data/test/unit/active_model/default_serializer_test.rb +0 -13
  125. data/test/unit/active_model/serializer/associations/build_serializer_test.rb +0 -36
  126. data/test/unit/active_model/serializer/associations_test.rb +0 -49
  127. data/test/unit/active_model/serializer/attributes_test.rb +0 -57
  128. data/test/unit/active_model/serializer/config_test.rb +0 -91
  129. data/test/unit/active_model/serializer/filter_test.rb +0 -69
  130. data/test/unit/active_model/serializer/has_many_polymorphic_test.rb +0 -189
  131. data/test/unit/active_model/serializer/has_many_test.rb +0 -265
  132. data/test/unit/active_model/serializer/has_one_and_has_many_test.rb +0 -27
  133. data/test/unit/active_model/serializer/has_one_polymorphic_test.rb +0 -196
  134. data/test/unit/active_model/serializer/has_one_test.rb +0 -253
  135. data/test/unit/active_model/serializer/key_format_test.rb +0 -25
  136. data/test/unit/active_model/serializer/meta_test.rb +0 -39
  137. data/test/unit/active_model/serializer/options_test.rb +0 -42
  138. data/test/unit/active_model/serializer/root_test.rb +0 -117
  139. data/test/unit/active_model/serializer/scope_test.rb +0 -49
  140. data/test/unit/active_model/serializer/url_helpers_test.rb +0 -35
  141. data/test/unit/active_model/serilizable_test.rb +0 -50
  142. /data/lib/{active_model/serializer/generators/serializer/templates/serializer.rb → generators/rails/templates/serializer.rb.erb} +0 -0
@@ -1,64 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/class/attribute'
4
+ require 'active_model_serializers/serialization_context'
2
5
 
3
6
  module ActionController
4
- # Action Controller Serialization
5
- #
6
- # Overrides render :json to check if the given object implements +active_model_serializer+
7
- # as a method. If so, use the returned serializer instead of calling +to_json+ on the object.
8
- #
9
- # This module also provides a serialization_scope method that allows you to configure the
10
- # +serialization_scope+ of the serializer. Most apps will likely set the +serialization_scope+
11
- # to the current user:
12
- #
13
- # class ApplicationController < ActionController::Base
14
- # serialization_scope :current_user
15
- # end
16
- #
17
- # If you need more complex scope rules, you can simply override the serialization_scope:
18
- #
19
- # class ApplicationController < ActionController::Base
20
- # private
21
- #
22
- # def serialization_scope
23
- # current_user
24
- # end
25
- # end
26
- #
27
7
  module Serialization
28
8
  extend ActiveSupport::Concern
29
9
 
30
10
  include ActionController::Renderers
31
11
 
32
- class << self
33
- attr_accessor :enabled
34
- end
35
- self.enabled = true
36
-
37
- included do
38
- class_attribute :_serialization_scope
39
- self._serialization_scope = :current_user
40
- end
41
-
42
12
  module ClassMethods
43
13
  def serialization_scope(scope)
44
14
  self._serialization_scope = scope
45
15
  end
46
16
  end
47
17
 
48
- [:_render_option_json, :_render_with_renderer_json].each do |renderer_method|
49
- define_method renderer_method do |resource, options|
50
- serializer = build_json_serializer(resource, options)
18
+ included do
19
+ class_attribute :_serialization_scope
20
+ self._serialization_scope = :current_user
51
21
 
52
- if serializer
53
- super(serializer, options)
54
- else
55
- super(resource, options)
56
- end
57
- end
22
+ attr_writer :namespace_for_serializer
58
23
  end
59
24
 
60
- private
61
-
62
25
  def namespace_for_serializer
63
26
  @namespace_for_serializer ||= namespace_for_class(self.class) unless namespace_for_class(self.class) == Object
64
27
  end
@@ -71,36 +34,42 @@ module ActionController
71
34
  end
72
35
  end
73
36
 
74
- def default_serializer(resource)
75
- options = {}.tap do |o|
76
- o[:namespace] = namespace_for_serializer if namespace_for_serializer
77
- end
37
+ def serialization_scope
38
+ return unless _serialization_scope && respond_to?(_serialization_scope, true)
78
39
 
79
- ActiveModel::Serializer.serializer_for(resource, options)
40
+ send(_serialization_scope)
80
41
  end
81
42
 
82
- def default_serializer_options
83
- {}
84
- end
43
+ def get_serializer(resource, options = {})
44
+ unless use_adapter?
45
+ warn 'ActionController::Serialization#use_adapter? has been removed. '\
46
+ "Please pass 'adapter: false' or see ActiveSupport::SerializableResource.new"
47
+ options[:adapter] = false
48
+ end
85
49
 
86
- def serialization_scope
87
- _serialization_scope = self.class._serialization_scope
88
- send(_serialization_scope) if _serialization_scope && respond_to?(_serialization_scope, true)
89
- end
50
+ options.fetch(:namespace) { options[:namespace] = namespace_for_serializer }
90
51
 
91
- def build_json_serializer(resource, options = {})
92
- options = default_serializer_options.merge(options)
93
- @namespace_for_serializer = options.fetch(:namespace, nil)
52
+ serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)
53
+ serializable_resource.serialization_scope ||= options.fetch(:scope) { serialization_scope }
54
+ serializable_resource.serialization_scope_name = options.fetch(:scope_name) { _serialization_scope }
55
+ # For compatibility with the JSON renderer: `json.to_json(options) if json.is_a?(String)`.
56
+ # Otherwise, since `serializable_resource` is not a string, the renderer would call
57
+ # `to_json` on a String and given odd results, such as `"".to_json #=> '""'`
58
+ serializable_resource.adapter.is_a?(String) ? serializable_resource.adapter : serializable_resource
59
+ end
94
60
 
95
- if serializer = options.fetch(:serializer, default_serializer(resource))
96
- options[:scope] = serialization_scope unless options.has_key?(:scope)
61
+ # Deprecated
62
+ def use_adapter?
63
+ true
64
+ end
97
65
 
98
- if resource.respond_to?(:to_ary)
99
- options[:resource_name] = controller_name
100
- options[:namespace] = namespace_for_serializer if namespace_for_serializer
66
+ [:_render_option_json, :_render_with_renderer_json].each do |renderer_method|
67
+ define_method renderer_method do |resource, options|
68
+ options.fetch(:serialization_context) do
69
+ options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request, options)
101
70
  end
102
-
103
- serializer.new(resource, options)
71
+ serializable_resource = get_serializer(resource, options)
72
+ super(serializable_resource, options)
104
73
  end
105
74
  end
106
75
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module ActiveModel
6
+ class SerializableResource
7
+ class << self
8
+ extend ActiveModelSerializers::Deprecate
9
+
10
+ delegate_and_deprecate :new, ActiveModelSerializers::SerializableResource
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ module Adapter
6
+ class Attributes < DelegateClass(ActiveModelSerializers::Adapter::Attributes)
7
+ def initialize(serializer, options = {})
8
+ super(ActiveModelSerializers::Adapter::Attributes.new(serializer, options))
9
+ end
10
+ class << self
11
+ extend ActiveModelSerializers::Deprecate
12
+ deprecate :new, 'ActiveModelSerializers::Adapter::Json.'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ module Adapter
6
+ class Base < DelegateClass(ActiveModelSerializers::Adapter::Base)
7
+ class << self
8
+ extend ActiveModelSerializers::Deprecate
9
+ deprecate :inherited, 'ActiveModelSerializers::Adapter::Base.'
10
+ end
11
+
12
+ # :nocov:
13
+ def initialize(serializer, options = {})
14
+ super(ActiveModelSerializers::Adapter::Base.new(serializer, options))
15
+ end
16
+ # :nocov:
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ module Adapter
6
+ class Json < DelegateClass(ActiveModelSerializers::Adapter::Json)
7
+ def initialize(serializer, options = {})
8
+ super(ActiveModelSerializers::Adapter::Json.new(serializer, options))
9
+ end
10
+ class << self
11
+ extend ActiveModelSerializers::Deprecate
12
+ deprecate :new, 'ActiveModelSerializers::Adapter::Json.new'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ module Adapter
6
+ class JsonApi < DelegateClass(ActiveModelSerializers::Adapter::JsonApi)
7
+ def initialize(serializer, options = {})
8
+ super(ActiveModelSerializers::Adapter::JsonApi.new(serializer, options))
9
+ end
10
+ class << self
11
+ extend ActiveModelSerializers::Deprecate
12
+ deprecate :new, 'ActiveModelSerializers::Adapter::JsonApi.new'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ module Adapter
6
+ class Null < DelegateClass(ActiveModelSerializers::Adapter::Null)
7
+ def initialize(serializer, options = {})
8
+ super(ActiveModelSerializers::Adapter::Null.new(serializer, options))
9
+ end
10
+ class << self
11
+ extend ActiveModelSerializers::Deprecate
12
+ deprecate :new, 'ActiveModelSerializers::Adapter::Null.new'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model_serializers/adapter'
4
+ require 'active_model_serializers/deprecate'
5
+
6
+ module ActiveModel
7
+ class Serializer
8
+ # @deprecated Use ActiveModelSerializers::Adapter instead
9
+ module Adapter
10
+ class << self
11
+ extend ActiveModelSerializers::Deprecate
12
+
13
+ DEPRECATED_METHODS = [:create, :adapter_class, :adapter_map, :adapters, :register, :lookup].freeze
14
+ DEPRECATED_METHODS.each do |method|
15
+ delegate_and_deprecate method, ActiveModelSerializers::Adapter
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ require 'active_model/serializer/adapter/base'
23
+ require 'active_model/serializer/adapter/null'
24
+ require 'active_model/serializer/adapter/attributes'
25
+ require 'active_model/serializer/adapter/json'
26
+ require 'active_model/serializer/adapter/json_api'
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model/serializer/collection_serializer'
4
+
5
+ module ActiveModel
6
+ class Serializer
7
+ class ArraySerializer < CollectionSerializer
8
+ class << self
9
+ extend ActiveModelSerializers::Deprecate
10
+ deprecate :new, 'ActiveModel::Serializer::CollectionSerializer.'
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,58 +1,73 @@
1
- require 'active_model/default_serializer'
2
- require 'active_model/serializer/association/has_one'
3
- require 'active_model/serializer/association/has_many'
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model/serializer/lazy_association'
4
4
 
5
5
  module ActiveModel
6
6
  class Serializer
7
- class Association
8
- def initialize(name, options={})
9
- if options.has_key?(:include)
10
- ActiveSupport::Deprecation.warn <<-WARN
11
- ** Notice: include was renamed to embed_in_root. **
12
- WARN
13
- end
7
+ # This class holds all information about serializer's association.
8
+ #
9
+ # @api private
10
+ Association = Struct.new(:reflection, :association_options) do
11
+ attr_reader :lazy_association
12
+ delegate :object, :include_data?, :virtual_value, :collection?, to: :lazy_association
13
+
14
+ def initialize(*)
15
+ super
16
+ @lazy_association = LazyAssociation.new(reflection, association_options)
17
+ end
14
18
 
15
- @name = name.to_s
16
- @options = options
17
- self.embed = options.fetch(:embed) { CONFIG.embed }
18
- @polymorphic = options.fetch(:polymorphic, false)
19
- @embed_in_root = options.fetch(:embed_in_root) { options.fetch(:include) { CONFIG.embed_in_root } }
20
- @key_format = options.fetch(:key_format) { CONFIG.key_format }
21
- @embed_key = options[:embed_key] || :id
22
- @key = options[:key]
23
- @embedded_key = options[:root] || name
24
- @embed_in_root_key = options.fetch(:embed_in_root_key) { CONFIG.embed_in_root_key }
25
- @embed_namespace = options.fetch(:embed_namespace) { CONFIG.embed_namespace }
19
+ # @return [Symbol]
20
+ delegate :name, to: :reflection
26
21
 
27
- serializer = @options[:serializer]
28
- @serializer_from_options = serializer.is_a?(String) ? serializer.constantize : serializer
22
+ # @return [Symbol]
23
+ def key
24
+ reflection_options.fetch(:key, name)
29
25
  end
30
26
 
31
- attr_reader :name, :embed_ids, :embed_objects, :polymorphic
32
- attr_accessor :embed_in_root, :embed_key, :key, :embedded_key, :root_key, :serializer_from_options, :options, :key_format, :embed_in_root_key, :embed_namespace
33
- alias embed_ids? embed_ids
34
- alias embed_objects? embed_objects
35
- alias embed_in_root? embed_in_root
36
- alias embed_in_root_key? embed_in_root_key
37
- alias embed_namespace? embed_namespace
38
- alias polymorphic? polymorphic
27
+ # @return [True,False]
28
+ def key?
29
+ reflection_options.key?(:key)
30
+ end
39
31
 
40
- def embed=(embed)
41
- @embed_ids = embed == :id || embed == :ids
42
- @embed_objects = embed == :object || embed == :objects
32
+ # @return [Hash]
33
+ def links
34
+ reflection_options.fetch(:links) || {}
43
35
  end
44
36
 
45
- def serializer_from_object(object, options = {})
46
- Serializer.serializer_for(object, options)
37
+ # @return [Hash, nil]
38
+ # This gets mutated, so cannot use the cached reflection_options
39
+ def meta
40
+ reflection.options[:meta]
47
41
  end
48
42
 
49
- def default_serializer
50
- DefaultSerializer
43
+ def belongs_to?
44
+ reflection.foreign_key_on == :self
51
45
  end
52
46
 
53
- def build_serializer(object, options = {})
54
- serializer_class(object, options).new(object, options.merge(self.options))
47
+ def polymorphic?
48
+ true == reflection_options[:polymorphic]
55
49
  end
50
+
51
+ # @api private
52
+ def serializable_hash(adapter_options, adapter_instance)
53
+ association_serializer = lazy_association.serializer
54
+ return virtual_value if virtual_value
55
+ association_object = association_serializer && association_serializer.object
56
+ return unless association_object
57
+
58
+ serialization = association_serializer.serializable_hash(adapter_options, {}, adapter_instance)
59
+
60
+ if polymorphic? && serialization
61
+ polymorphic_type = association_object.class.name.underscore
62
+ serialization = { type: polymorphic_type, polymorphic_type.to_sym => serialization }
63
+ end
64
+
65
+ serialization
66
+ end
67
+
68
+ private
69
+
70
+ delegate :reflection_options, to: :lazy_association
56
71
  end
57
72
  end
58
73
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model/serializer/field'
4
+
5
+ module ActiveModel
6
+ class Serializer
7
+ # Holds all the meta-data about an attribute as it was specified in the
8
+ # ActiveModel::Serializer class.
9
+ #
10
+ # @example
11
+ # class PostSerializer < ActiveModel::Serializer
12
+ # attribute :content
13
+ # attribute :name, key: :title
14
+ # attribute :email, key: :author_email, if: :user_logged_in?
15
+ # attribute :preview do
16
+ # truncate(object.content)
17
+ # end
18
+ #
19
+ # def user_logged_in?
20
+ # current_user.logged_in?
21
+ # end
22
+ # end
23
+ #
24
+ class Attribute < Field
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ # @api private
6
+ class BelongsToReflection < Reflection
7
+ # @api private
8
+ def foreign_key_on
9
+ :self
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ class CollectionSerializer
6
+ include Enumerable
7
+ delegate :each, to: :@serializers
8
+
9
+ attr_reader :object, :root
10
+
11
+ def initialize(resources, options = {})
12
+ @object = resources
13
+ @options = options
14
+ @root = options[:root]
15
+ @serializers = serializers_from_resources
16
+ end
17
+
18
+ def success?
19
+ true
20
+ end
21
+
22
+ # @api private
23
+ def serializable_hash(adapter_options, options, adapter_instance)
24
+ options[:include_directive] ||= ActiveModel::Serializer.include_directive_from_options(adapter_options)
25
+ options[:cached_attributes] ||= ActiveModel::Serializer.cache_read_multi(self, adapter_instance, options[:include_directive])
26
+ serializers.map do |serializer|
27
+ serializer.serializable_hash(adapter_options, options, adapter_instance)
28
+ end
29
+ end
30
+
31
+ # TODO: unify naming of root, json_key, and _type. Right now, a serializer's
32
+ # json_key comes from the root option or the object's model name, by default.
33
+ # But, if a dev defines a custom `json_key` method with an explicit value,
34
+ # we have no simple way to know that it is safe to call that instance method.
35
+ # (which is really a class property at this point, anyhow).
36
+ # rubocop:disable Metrics/CyclomaticComplexity
37
+ # Disabling cop since it's good to highlight the complexity of this method by
38
+ # including all the logic right here.
39
+ def json_key
40
+ return root if root
41
+ # 1. get from options[:serializer] for empty resource collection
42
+ key = object.empty? &&
43
+ (explicit_serializer_class = options[:serializer]) &&
44
+ explicit_serializer_class._type
45
+ # 2. get from first serializer instance in collection
46
+ key ||= (serializer = serializers.first) && serializer.json_key
47
+ # 3. get from collection name, if a named collection
48
+ key ||= object.respond_to?(:name) ? object.name && object.name.underscore : nil
49
+ # 4. key may be nil for empty collection and no serializer option
50
+ key &&= key.pluralize
51
+ if raise_cannot_infer_root_key_error?
52
+ # 5. fail if the key cannot be determined
53
+ key || fail(CannotInferRootKeyError, 'Cannot infer root key from collection type. Please specify the root or each_serializer option, or render a JSON String')
54
+ end
55
+ key
56
+ end
57
+ # rubocop:enable Metrics/CyclomaticComplexity
58
+
59
+ def paginated?
60
+ ActiveModelSerializers.config.jsonapi_pagination_links_enabled &&
61
+ object.respond_to?(:current_page) &&
62
+ object.respond_to?(:total_pages) &&
63
+ object.respond_to?(:size)
64
+ end
65
+
66
+ class CannotInferRootKeyError < StandardError; end
67
+
68
+ protected
69
+
70
+ attr_reader :serializers, :options
71
+
72
+ private
73
+
74
+ def raise_cannot_infer_root_key_error?
75
+ ActiveModelSerializers.config.raise_cannot_infer_root_key_error
76
+ end
77
+
78
+ def serializers_from_resources
79
+ serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer)
80
+ object.map do |resource|
81
+ serializer_from_resource(resource, serializer_context_class, options)
82
+ end
83
+ end
84
+
85
+ def serializer_from_resource(resource, serializer_context_class, options)
86
+ serializer_class = options.fetch(:serializer) do
87
+ serializer_context_class.serializer_for(resource, namespace: options[:namespace])
88
+ end
89
+
90
+ if serializer_class.nil?
91
+ ActiveModelSerializers.logger.debug "No serializer found for resource: #{resource.inspect}"
92
+ throw :no_serializer
93
+ else
94
+ serializer_class.new(resource, options.except(:serializer))
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end