active_model_serializers 0.10.0.rc4 → 0.10.0.rc5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) 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 +1 -0
  5. data/.rubocop.yml +19 -1
  6. data/.rubocop_todo.yml +30 -103
  7. data/.simplecov +0 -1
  8. data/.travis.yml +20 -8
  9. data/CHANGELOG.md +89 -5
  10. data/CONTRIBUTING.md +54 -179
  11. data/Gemfile +7 -2
  12. data/{LICENSE.txt → MIT-LICENSE} +0 -0
  13. data/README.md +27 -5
  14. data/Rakefile +44 -16
  15. data/active_model_serializers.gemspec +9 -1
  16. data/appveyor.yml +1 -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 +13 -7
  21. data/docs/README.md +5 -1
  22. data/docs/STYLE.md +58 -0
  23. data/docs/general/adapters.md +99 -16
  24. data/docs/general/configuration_options.md +87 -14
  25. data/docs/general/deserialization.md +100 -0
  26. data/docs/general/getting_started.md +35 -0
  27. data/docs/general/instrumentation.md +1 -1
  28. data/docs/general/key_transforms.md +40 -0
  29. data/docs/general/rendering.md +115 -13
  30. data/docs/general/serializers.md +138 -6
  31. data/docs/howto/add_pagination_links.md +36 -18
  32. data/docs/howto/outside_controller_use.md +4 -4
  33. data/docs/howto/passing_arbitrary_options.md +27 -0
  34. data/docs/jsonapi/errors.md +56 -0
  35. data/docs/jsonapi/schema.md +29 -18
  36. data/docs/rfcs/0000-namespace.md +106 -0
  37. data/docs/rfcs/template.md +15 -0
  38. data/lib/action_controller/serialization.rb +10 -19
  39. data/lib/active_model/serializable_resource.rb +4 -65
  40. data/lib/active_model/serializer.rb +73 -18
  41. data/lib/active_model/serializer/adapter.rb +15 -82
  42. data/lib/active_model/serializer/adapter/attributes.rb +5 -56
  43. data/lib/active_model/serializer/adapter/base.rb +5 -47
  44. data/lib/active_model/serializer/adapter/json.rb +6 -12
  45. data/lib/active_model/serializer/adapter/json_api.rb +5 -213
  46. data/lib/active_model/serializer/adapter/null.rb +7 -3
  47. data/lib/active_model/serializer/array_serializer.rb +3 -3
  48. data/lib/active_model/serializer/association.rb +4 -5
  49. data/lib/active_model/serializer/attributes.rb +1 -1
  50. data/lib/active_model/serializer/caching.rb +56 -5
  51. data/lib/active_model/serializer/collection_serializer.rb +30 -13
  52. data/lib/active_model/serializer/configuration.rb +7 -0
  53. data/lib/active_model/serializer/error_serializer.rb +10 -0
  54. data/lib/active_model/serializer/errors_serializer.rb +27 -0
  55. data/lib/active_model/serializer/links.rb +4 -2
  56. data/lib/active_model/serializer/lint.rb +14 -0
  57. data/lib/active_model/serializer/meta.rb +29 -0
  58. data/lib/active_model/serializer/null.rb +17 -0
  59. data/lib/active_model/serializer/reflection.rb +57 -1
  60. data/lib/active_model/serializer/type.rb +1 -1
  61. data/lib/active_model/serializer/version.rb +1 -1
  62. data/lib/active_model_serializers.rb +17 -0
  63. data/lib/active_model_serializers/adapter.rb +92 -0
  64. data/lib/active_model_serializers/adapter/attributes.rb +94 -0
  65. data/lib/active_model_serializers/adapter/base.rb +90 -0
  66. data/lib/active_model_serializers/adapter/json.rb +11 -0
  67. data/lib/active_model_serializers/adapter/json_api.rb +513 -0
  68. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
  69. data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
  70. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
  71. data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
  72. data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
  73. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +57 -0
  74. data/lib/active_model_serializers/adapter/json_api/relationship.rb +52 -0
  75. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +37 -0
  76. data/lib/active_model_serializers/adapter/null.rb +10 -0
  77. data/lib/active_model_serializers/cached_serializer.rb +87 -0
  78. data/lib/active_model_serializers/callbacks.rb +1 -1
  79. data/lib/active_model_serializers/deprecate.rb +55 -0
  80. data/lib/active_model_serializers/deserialization.rb +2 -2
  81. data/lib/active_model_serializers/fragment_cache.rb +118 -0
  82. data/lib/active_model_serializers/json_pointer.rb +14 -0
  83. data/lib/active_model_serializers/key_transform.rb +70 -0
  84. data/lib/active_model_serializers/logging.rb +4 -1
  85. data/lib/active_model_serializers/model.rb +11 -1
  86. data/lib/active_model_serializers/railtie.rb +9 -1
  87. data/lib/active_model_serializers/register_jsonapi_renderer.rb +64 -0
  88. data/lib/active_model_serializers/serializable_resource.rb +81 -0
  89. data/lib/active_model_serializers/serialization_context.rb +24 -2
  90. data/lib/active_model_serializers/test/schema.rb +2 -2
  91. data/lib/grape/formatters/active_model_serializers.rb +1 -1
  92. data/test/action_controller/adapter_selector_test.rb +1 -1
  93. data/test/action_controller/json_api/deserialization_test.rb +56 -3
  94. data/test/action_controller/json_api/errors_test.rb +41 -0
  95. data/test/action_controller/json_api/linked_test.rb +10 -9
  96. data/test/action_controller/json_api/pagination_test.rb +2 -2
  97. data/test/action_controller/json_api/transform_test.rb +180 -0
  98. data/test/action_controller/serialization_scope_name_test.rb +201 -35
  99. data/test/action_controller/serialization_test.rb +39 -7
  100. data/test/active_model_serializers/adapter_for_test.rb +208 -0
  101. data/test/active_model_serializers/cached_serializer_test.rb +80 -0
  102. data/test/active_model_serializers/fragment_cache_test.rb +34 -0
  103. data/test/active_model_serializers/json_pointer_test.rb +20 -0
  104. data/test/active_model_serializers/key_transform_test.rb +263 -0
  105. data/test/active_model_serializers/logging_test.rb +8 -8
  106. data/test/active_model_serializers/railtie_test_isolated.rb +6 -0
  107. data/test/active_model_serializers/serialization_context_test_isolated.rb +58 -0
  108. data/test/adapter/deprecation_test.rb +100 -0
  109. data/test/adapter/json/belongs_to_test.rb +32 -34
  110. data/test/adapter/json/collection_test.rb +73 -75
  111. data/test/adapter/json/has_many_test.rb +36 -38
  112. data/test/adapter/json/transform_test.rb +93 -0
  113. data/test/adapter/json_api/belongs_to_test.rb +127 -129
  114. data/test/adapter/json_api/collection_test.rb +80 -82
  115. data/test/adapter/json_api/errors_test.rb +78 -0
  116. data/test/adapter/json_api/fields_test.rb +68 -70
  117. data/test/adapter/json_api/has_many_embed_ids_test.rb +32 -34
  118. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +75 -77
  119. data/test/adapter/json_api/has_many_test.rb +121 -123
  120. data/test/adapter/json_api/has_one_test.rb +59 -61
  121. data/test/adapter/json_api/json_api_test.rb +28 -30
  122. data/test/adapter/json_api/linked_test.rb +319 -321
  123. data/test/adapter/json_api/links_test.rb +75 -50
  124. data/test/adapter/json_api/pagination_links_test.rb +115 -82
  125. data/test/adapter/json_api/parse_test.rb +114 -116
  126. data/test/adapter/json_api/relationship_test.rb +161 -0
  127. data/test/adapter/json_api/relationships_test.rb +199 -0
  128. data/test/adapter/json_api/resource_identifier_test.rb +85 -0
  129. data/test/adapter/json_api/resource_meta_test.rb +100 -0
  130. data/test/adapter/json_api/toplevel_jsonapi_test.rb +61 -63
  131. data/test/adapter/json_api/transform_test.rb +500 -0
  132. data/test/adapter/json_api/type_test.rb +61 -0
  133. data/test/adapter/json_test.rb +35 -37
  134. data/test/adapter/null_test.rb +13 -15
  135. data/test/adapter/polymorphic_test.rb +72 -0
  136. data/test/adapter_test.rb +27 -29
  137. data/test/array_serializer_test.rb +7 -8
  138. data/test/benchmark/app.rb +65 -0
  139. data/test/benchmark/benchmarking_support.rb +67 -0
  140. data/test/benchmark/bm_caching.rb +117 -0
  141. data/test/benchmark/bm_transform.rb +34 -0
  142. data/test/benchmark/config.ru +3 -0
  143. data/test/benchmark/controllers.rb +77 -0
  144. data/test/benchmark/fixtures.rb +167 -0
  145. data/test/cache_test.rb +388 -0
  146. data/test/collection_serializer_test.rb +10 -0
  147. data/test/fixtures/active_record.rb +12 -0
  148. data/test/fixtures/poro.rb +28 -3
  149. data/test/grape_test.rb +5 -5
  150. data/test/lint_test.rb +9 -0
  151. data/test/serializable_resource_test.rb +59 -3
  152. data/test/serializers/associations_test.rb +8 -8
  153. data/test/serializers/attribute_test.rb +7 -7
  154. data/test/serializers/caching_configuration_test_isolated.rb +170 -0
  155. data/test/serializers/meta_test.rb +74 -6
  156. data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
  157. data/test/serializers/serialization_test.rb +55 -0
  158. data/test/support/isolated_unit.rb +3 -0
  159. data/test/support/rails5_shims.rb +26 -8
  160. data/test/support/rails_app.rb +38 -18
  161. data/test/support/serialization_testing.rb +5 -5
  162. data/test/test_helper.rb +6 -10
  163. metadata +132 -37
  164. data/docs/DESIGN.textile +7 -1
  165. data/lib/active_model/serializer/adapter/cached_serializer.rb +0 -45
  166. data/lib/active_model/serializer/adapter/fragment_cache.rb +0 -111
  167. data/lib/active_model/serializer/adapter/json/fragment_cache.rb +0 -13
  168. data/lib/active_model/serializer/adapter/json_api/deserialization.rb +0 -207
  169. data/lib/active_model/serializer/adapter/json_api/fragment_cache.rb +0 -21
  170. data/lib/active_model/serializer/adapter/json_api/link.rb +0 -44
  171. data/lib/active_model/serializer/adapter/json_api/pagination_links.rb +0 -58
  172. data/test/active_model_serializers/serialization_context_test.rb +0 -18
  173. data/test/adapter/fragment_cache_test.rb +0 -38
  174. data/test/adapter/json_api/resource_type_config_test.rb +0 -71
  175. data/test/serializers/adapter_for_test.rb +0 -166
  176. data/test/serializers/cache_test.rb +0 -209
  177. data/test/support/simplecov.rb +0 -6
  178. data/test/support/stream_capture.rb +0 -50
  179. data/test/support/test_case.rb +0 -19
@@ -0,0 +1,57 @@
1
+ module ActiveModelSerializers
2
+ module Adapter
3
+ class JsonApi < Base
4
+ class PaginationLinks
5
+ FIRST_PAGE = 1
6
+
7
+ attr_reader :collection, :context
8
+
9
+ def initialize(collection, context)
10
+ @collection = collection
11
+ @context = context
12
+ end
13
+
14
+ def serializable_hash(options = {})
15
+ per_page = collection.try(:per_page) || collection.try(:limit_value) || collection.size
16
+ pages_from.each_with_object({}) do |(key, value), hash|
17
+ params = query_parameters.merge(page: { number: value, size: per_page }).to_query
18
+
19
+ hash[key] = "#{url(options)}?#{params}"
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def pages_from
26
+ return {} if collection.total_pages == FIRST_PAGE
27
+
28
+ {}.tap do |pages|
29
+ pages[:self] = collection.current_page
30
+
31
+ unless collection.current_page == FIRST_PAGE
32
+ pages[:first] = FIRST_PAGE
33
+ pages[:prev] = collection.current_page - FIRST_PAGE
34
+ end
35
+
36
+ unless collection.current_page == collection.total_pages
37
+ pages[:next] = collection.current_page + FIRST_PAGE
38
+ pages[:last] = collection.total_pages
39
+ end
40
+ end
41
+ end
42
+
43
+ def url(options)
44
+ @url ||= options.fetch(:links, {}).fetch(:self, nil) || request_url
45
+ end
46
+
47
+ def request_url
48
+ @request_url ||= context.request_url
49
+ end
50
+
51
+ def query_parameters
52
+ @query_parameters ||= context.query_parameters
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,52 @@
1
+ module ActiveModelSerializers
2
+ module Adapter
3
+ class JsonApi
4
+ class Relationship
5
+ # {http://jsonapi.org/format/#document-resource-object-related-resource-links Document Resource Object Related Resource Links}
6
+ # {http://jsonapi.org/format/#document-links Document Links}
7
+ # {http://jsonapi.org/format/#document-resource-object-linkage Document Resource Relationship Linkage}
8
+ # {http://jsonapi.org/format/#document-meta Docment Meta}
9
+ def initialize(parent_serializer, serializer, serializable_resource_options, args = {})
10
+ @object = parent_serializer.object
11
+ @scope = parent_serializer.scope
12
+ @association_options = args.fetch(:options, {})
13
+ @serializable_resource_options = serializable_resource_options
14
+ @data = data_for(serializer)
15
+ @links = args.fetch(:links, {}).each_with_object({}) do |(key, value), hash|
16
+ hash[key] = ActiveModelSerializers::Adapter::JsonApi::Link.new(parent_serializer, value).as_json
17
+ end
18
+ meta = args.fetch(:meta, nil)
19
+ @meta = meta.respond_to?(:call) ? parent_serializer.instance_eval(&meta) : meta
20
+ end
21
+
22
+ def as_json
23
+ hash = {}
24
+ hash[:data] = data if association_options[:include_data]
25
+ links = self.links
26
+ hash[:links] = links if links.any?
27
+ meta = self.meta
28
+ hash[:meta] = meta if meta
29
+
30
+ hash
31
+ end
32
+
33
+ protected
34
+
35
+ attr_reader :object, :scope, :data, :serializable_resource_options,
36
+ :association_options, :links, :meta
37
+
38
+ private
39
+
40
+ def data_for(serializer)
41
+ if serializer.respond_to?(:each)
42
+ serializer.map { |s| ResourceIdentifier.new(s, serializable_resource_options).as_json }
43
+ elsif association_options[:virtual_value]
44
+ association_options[:virtual_value]
45
+ elsif serializer && serializer.object
46
+ ResourceIdentifier.new(serializer, serializable_resource_options).as_json
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,37 @@
1
+ module ActiveModelSerializers
2
+ module Adapter
3
+ class JsonApi
4
+ class ResourceIdentifier
5
+ # {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects}
6
+ def initialize(serializer, options)
7
+ @id = id_for(serializer)
8
+ @type = JsonApi.send(:transform_key_casing!, type_for(serializer),
9
+ options)
10
+ end
11
+
12
+ def as_json
13
+ { id: id, type: type }
14
+ end
15
+
16
+ protected
17
+
18
+ attr_reader :id, :type
19
+
20
+ private
21
+
22
+ def type_for(serializer)
23
+ return serializer._type if serializer._type
24
+ if ActiveModelSerializers.config.jsonapi_resource_type == :singular
25
+ serializer.object.class.model_name.singular
26
+ else
27
+ serializer.object.class.model_name.plural
28
+ end
29
+ end
30
+
31
+ def id_for(serializer)
32
+ serializer.read_attribute_for_serialization(:id).to_s
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ module ActiveModelSerializers
2
+ module Adapter
3
+ class Null < Base
4
+ # Since options param is not being used, underscored naming of the param
5
+ def serializable_hash(_options = nil)
6
+ {}
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,87 @@
1
+ module ActiveModelSerializers
2
+ class CachedSerializer
3
+ UndefinedCacheKey = Class.new(StandardError)
4
+
5
+ def initialize(serializer)
6
+ @cached_serializer = serializer
7
+ @klass = @cached_serializer.class
8
+ end
9
+
10
+ def cache_check(adapter_instance)
11
+ if cached?
12
+ @klass._cache.fetch(cache_key(adapter_instance), @klass._cache_options) do
13
+ yield
14
+ end
15
+ elsif fragment_cached?
16
+ FragmentCache.new(adapter_instance, @cached_serializer, adapter_instance.instance_options).fetch
17
+ else
18
+ yield
19
+ end
20
+ end
21
+
22
+ def cached?
23
+ @klass.cache_enabled?
24
+ end
25
+
26
+ def fragment_cached?
27
+ @klass.fragment_cache_enabled?
28
+ end
29
+
30
+ def cache_key(adapter_instance)
31
+ return @cache_key if defined?(@cache_key)
32
+
33
+ parts = []
34
+ parts << object_cache_key
35
+ parts << adapter_instance.cached_name
36
+ parts << @klass._cache_digest unless @klass._skip_digest?
37
+ @cache_key = parts.join('/')
38
+ end
39
+
40
+ # Use object's cache_key if available, else derive a key from the object
41
+ # Pass the `key` option to the `cache` declaration or override this method to customize the cache key
42
+ def object_cache_key
43
+ if @cached_serializer.object.respond_to?(:cache_key)
44
+ @cached_serializer.object.cache_key
45
+ elsif (cache_key = (@klass._cache_key || @klass._cache_options[:key]))
46
+ object_time_safe = @cached_serializer.object.updated_at
47
+ object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime)
48
+ "#{cache_key}/#{@cached_serializer.object.id}-#{object_time_safe}"
49
+ else
50
+ fail UndefinedCacheKey, "#{@cached_serializer.object.class} must define #cache_key, or the 'key:' option must be passed into '#{@klass}.cache'"
51
+ end
52
+ end
53
+
54
+ # find all cache_key for the collection_serializer
55
+ # @param serializers [ActiveModel::Serializer::CollectionSerializer]
56
+ # @param adapter_instance [ActiveModelSerializers::Adapter::Base]
57
+ # @param include_tree [ActiveModel::Serializer::IncludeTree]
58
+ # @return [Array] all cache_key of collection_serializer
59
+ def self.object_cache_keys(serializers, adapter_instance, include_tree)
60
+ cache_keys = []
61
+
62
+ serializers.each do |serializer|
63
+ cache_keys << object_cache_key(serializer, adapter_instance)
64
+
65
+ serializer.associations(include_tree).each do |association|
66
+ if association.serializer.respond_to?(:each)
67
+ association.serializer.each do |sub_serializer|
68
+ cache_keys << object_cache_key(sub_serializer, adapter_instance)
69
+ end
70
+ else
71
+ cache_keys << object_cache_key(association.serializer, adapter_instance)
72
+ end
73
+ end
74
+ end
75
+
76
+ cache_keys.compact.uniq
77
+ end
78
+
79
+ # @return [String, nil] the cache_key of the serializer or nil
80
+ def self.object_cache_key(serializer, adapter_instance)
81
+ return unless serializer.present? && serializer.object.present?
82
+
83
+ cached_serializer = new(serializer)
84
+ cached_serializer.cached? ? cached_serializer.cache_key(adapter_instance) : nil
85
+ end
86
+ end
87
+ end
@@ -24,7 +24,7 @@ module ActiveModelSerializers
24
24
  # Defines a callback that will get called around the render method,
25
25
  # whether it is as_json, to_json, or serializable_hash
26
26
  #
27
- # class ActiveModel::SerializableResource
27
+ # class ActiveModelSerializers::SerializableResource
28
28
  # include ActiveModelSerializers::Callbacks
29
29
  #
30
30
  # around_render do |args, block|
@@ -0,0 +1,55 @@
1
+ ##
2
+ # Provides a single method +deprecate+ to be used to declare when
3
+ # something is going away.
4
+ #
5
+ # class Legacy
6
+ # def self.klass_method
7
+ # # ...
8
+ # end
9
+ #
10
+ # def instance_method
11
+ # # ...
12
+ # end
13
+ #
14
+ # extend ActiveModelSerializers::Deprecate
15
+ # deprecate :instance_method, "ActiveModelSerializers::NewPlace#new_method"
16
+ #
17
+ # class << self
18
+ # extend ActiveModelSerializers::Deprecate
19
+ # deprecate :klass_method, :none
20
+ # end
21
+ # end
22
+ #
23
+ # Adapted from https://github.com/rubygems/rubygems/blob/1591331/lib/rubygems/deprecate.rb
24
+ module ActiveModelSerializers
25
+ module Deprecate
26
+ ##
27
+ # Simple deprecation method that deprecates +name+ by wrapping it up
28
+ # in a dummy method. It warns on each call to the dummy method
29
+ # telling the user of +replacement+ (unless +replacement+ is :none) that it is planned to go away.
30
+
31
+ def deprecate(name, replacement)
32
+ old = "_deprecated_#{name}"
33
+ alias_method old, name
34
+ class_eval do
35
+ define_method(name) do |*args, &block|
36
+ target = is_a?(Module) ? "#{self}." : "#{self.class}#"
37
+ msg = ["NOTE: #{target}#{name} is deprecated",
38
+ replacement == :none ? ' with no replacement' : "; use #{replacement} instead",
39
+ "\n#{target}#{name} called from #{ActiveModelSerializers.location_of_caller.join(":")}"
40
+ ]
41
+ warn "#{msg.join}."
42
+ send old, *args, &block
43
+ end
44
+ end
45
+ end
46
+
47
+ def delegate_and_deprecate(method, delegee)
48
+ delegate method, to: delegee
49
+ deprecate method, "#{delegee.name}."
50
+ end
51
+
52
+ module_function :deprecate
53
+ module_function :delegate_and_deprecate
54
+ end
55
+ end
@@ -3,11 +3,11 @@ module ActiveModelSerializers
3
3
  module_function
4
4
 
5
5
  def jsonapi_parse(*args)
6
- ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse(*args)
6
+ Adapter::JsonApi::Deserialization.parse(*args)
7
7
  end
8
8
 
9
9
  def jsonapi_parse!(*args)
10
- ActiveModel::Serializer::Adapter::JsonApi::Deserialization.parse!(*args)
10
+ Adapter::JsonApi::Deserialization.parse!(*args)
11
11
  end
12
12
  end
13
13
  end
@@ -0,0 +1,118 @@
1
+ module ActiveModelSerializers
2
+ class FragmentCache
3
+ attr_reader :serializer
4
+
5
+ def initialize(adapter, serializer, options)
6
+ @instance_options = options
7
+ @adapter = adapter
8
+ @serializer = serializer
9
+ end
10
+
11
+ # 1. Create a CachedSerializer and NonCachedSerializer from the serializer class
12
+ # 2. Serialize the above two with the given adapter
13
+ # 3. Pass their serializations to the adapter +::fragment_cache+
14
+ def fetch
15
+ object = serializer.object
16
+
17
+ # It will split the serializer into two, one that will be cached and one that will not
18
+ serializers = fragment_serializer
19
+
20
+ # Get serializable hash from both
21
+ cached_hash = serialize(object, serializers[:cached])
22
+ non_cached_hash = serialize(object, serializers[:non_cached])
23
+
24
+ # Merge both results
25
+ adapter.fragment_cache(cached_hash, non_cached_hash)
26
+ end
27
+
28
+ protected
29
+
30
+ attr_reader :instance_options, :adapter
31
+
32
+ private
33
+
34
+ def serialize(object, serializer_class)
35
+ SerializableResource.new(
36
+ object,
37
+ serializer: serializer_class,
38
+ adapter: adapter.class
39
+ ).serializable_hash
40
+ end
41
+
42
+ # Given a hash of its cached and non-cached serializers
43
+ # 1. Determine cached attributes from serializer class options
44
+ # 2. Add cached attributes to cached Serializer
45
+ # 3. Add non-cached attributes to non-cached Serializer
46
+ def cache_attributes(serializers)
47
+ klass = serializer.class
48
+ attributes = klass._attributes
49
+ cache_only = klass._cache_only
50
+ cached_attributes = cache_only ? cache_only : attributes - klass._cache_except
51
+ non_cached_attributes = attributes - cached_attributes
52
+ attributes_keys = klass._attributes_keys
53
+
54
+ add_attributes_to_serializer(serializers[:cached], cached_attributes, attributes_keys)
55
+ add_attributes_to_serializer(serializers[:non_cached], non_cached_attributes, attributes_keys)
56
+ end
57
+
58
+ def add_attributes_to_serializer(serializer, attributes, attributes_keys)
59
+ attributes.each do |attribute|
60
+ options = attributes_keys[attribute] || {}
61
+ serializer.attribute(attribute, options)
62
+ end
63
+ end
64
+
65
+ # Given a resource name
66
+ # 1. Dynamically creates a CachedSerializer and NonCachedSerializer
67
+ # for a given class 'name'
68
+ # 2. Call
69
+ # CachedSerializer.cache(serializer._cache_options)
70
+ # CachedSerializer.fragmented(serializer)
71
+ # NonCachedSerializer.cache(serializer._cache_options)
72
+ # 3. Build a hash keyed to the +cached+ and +non_cached+ serializers
73
+ # 4. Call +cached_attributes+ on the serializer class and the above hash
74
+ # 5. Return the hash
75
+ #
76
+ # @example
77
+ # When +name+ is <tt>User::Admin</tt>
78
+ # creates the Serializer classes (if they don't exist).
79
+ # CachedUser_AdminSerializer
80
+ # NonCachedUser_AdminSerializer
81
+ #
82
+ def fragment_serializer
83
+ klass = serializer.class
84
+ serializer_class_name = to_valid_const_name(klass.name)
85
+
86
+ cached = "Cached#{serializer_class_name}"
87
+ non_cached = "NonCached#{serializer_class_name}"
88
+
89
+ cached_serializer = get_or_create_serializer(cached)
90
+ non_cached_serializer = get_or_create_serializer(non_cached)
91
+
92
+ klass._cache_options ||= {}
93
+ cache_key = klass._cache_key
94
+ klass._cache_options[:key] = cache_key if cache_key
95
+ cached_serializer.cache(klass._cache_options)
96
+
97
+ type = klass._type
98
+ cached_serializer.type(type)
99
+ non_cached_serializer.type(type)
100
+
101
+ non_cached_serializer.fragmented(serializer)
102
+ cached_serializer.fragmented(serializer)
103
+
104
+ serializers = { cached: cached_serializer, non_cached: non_cached_serializer }
105
+ cache_attributes(serializers)
106
+ serializers
107
+ end
108
+
109
+ def get_or_create_serializer(name)
110
+ return Object.const_get(name) if Object.const_defined?(name)
111
+ Object.const_set(name, Class.new(ActiveModel::Serializer))
112
+ end
113
+
114
+ def to_valid_const_name(name)
115
+ name.gsub('::', '_')
116
+ end
117
+ end
118
+ end