agi_active_model_serializers 0.10.7

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 (220) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE.md +29 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  4. data/.gitignore +35 -0
  5. data/.rubocop.yml +102 -0
  6. data/.simplecov +110 -0
  7. data/.travis.yml +51 -0
  8. data/CHANGELOG.md +612 -0
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +56 -0
  12. data/MIT-LICENSE +22 -0
  13. data/README.md +307 -0
  14. data/Rakefile +103 -0
  15. data/active_model_serializers.gemspec +63 -0
  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/README.md +41 -0
  21. data/docs/STYLE.md +58 -0
  22. data/docs/general/adapters.md +247 -0
  23. data/docs/general/caching.md +58 -0
  24. data/docs/general/configuration_options.md +169 -0
  25. data/docs/general/deserialization.md +100 -0
  26. data/docs/general/fields.md +31 -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 +279 -0
  32. data/docs/general/serializers.md +461 -0
  33. data/docs/how-open-source-maintained.jpg +0 -0
  34. data/docs/howto/add_pagination_links.md +138 -0
  35. data/docs/howto/add_relationship_links.md +137 -0
  36. data/docs/howto/add_root_key.md +55 -0
  37. data/docs/howto/grape_integration.md +42 -0
  38. data/docs/howto/outside_controller_use.md +65 -0
  39. data/docs/howto/passing_arbitrary_options.md +27 -0
  40. data/docs/howto/serialize_poro.md +32 -0
  41. data/docs/howto/test.md +154 -0
  42. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  43. data/docs/integrations/ember-and-json-api.md +144 -0
  44. data/docs/integrations/grape.md +19 -0
  45. data/docs/jsonapi/errors.md +56 -0
  46. data/docs/jsonapi/schema.md +151 -0
  47. data/docs/jsonapi/schema/schema.json +366 -0
  48. data/docs/rfcs/0000-namespace.md +106 -0
  49. data/docs/rfcs/template.md +15 -0
  50. data/lib/action_controller/serialization.rb +66 -0
  51. data/lib/active_model/serializable_resource.rb +11 -0
  52. data/lib/active_model/serializer.rb +231 -0
  53. data/lib/active_model/serializer/adapter.rb +24 -0
  54. data/lib/active_model/serializer/adapter/attributes.rb +15 -0
  55. data/lib/active_model/serializer/adapter/base.rb +18 -0
  56. data/lib/active_model/serializer/adapter/json.rb +15 -0
  57. data/lib/active_model/serializer/adapter/json_api.rb +15 -0
  58. data/lib/active_model/serializer/adapter/null.rb +15 -0
  59. data/lib/active_model/serializer/array_serializer.rb +12 -0
  60. data/lib/active_model/serializer/association.rb +34 -0
  61. data/lib/active_model/serializer/attribute.rb +25 -0
  62. data/lib/active_model/serializer/belongs_to_reflection.rb +7 -0
  63. data/lib/active_model/serializer/collection_reflection.rb +7 -0
  64. data/lib/active_model/serializer/collection_serializer.rb +87 -0
  65. data/lib/active_model/serializer/concerns/associations.rb +102 -0
  66. data/lib/active_model/serializer/concerns/attributes.rb +82 -0
  67. data/lib/active_model/serializer/concerns/caching.rb +292 -0
  68. data/lib/active_model/serializer/concerns/configuration.rb +59 -0
  69. data/lib/active_model/serializer/concerns/links.rb +35 -0
  70. data/lib/active_model/serializer/concerns/meta.rb +29 -0
  71. data/lib/active_model/serializer/concerns/type.rb +25 -0
  72. data/lib/active_model/serializer/error_serializer.rb +14 -0
  73. data/lib/active_model/serializer/errors_serializer.rb +32 -0
  74. data/lib/active_model/serializer/field.rb +90 -0
  75. data/lib/active_model/serializer/fieldset.rb +31 -0
  76. data/lib/active_model/serializer/has_many_reflection.rb +7 -0
  77. data/lib/active_model/serializer/has_one_reflection.rb +7 -0
  78. data/lib/active_model/serializer/lint.rb +150 -0
  79. data/lib/active_model/serializer/null.rb +17 -0
  80. data/lib/active_model/serializer/reflection.rb +163 -0
  81. data/lib/active_model/serializer/singular_reflection.rb +7 -0
  82. data/lib/active_model/serializer/version.rb +5 -0
  83. data/lib/active_model_serializers.rb +53 -0
  84. data/lib/active_model_serializers/adapter.rb +98 -0
  85. data/lib/active_model_serializers/adapter/attributes.rb +13 -0
  86. data/lib/active_model_serializers/adapter/base.rb +83 -0
  87. data/lib/active_model_serializers/adapter/json.rb +21 -0
  88. data/lib/active_model_serializers/adapter/json_api.rb +517 -0
  89. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
  90. data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
  91. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
  92. data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
  93. data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
  94. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +69 -0
  95. data/lib/active_model_serializers/adapter/json_api/relationship.rb +63 -0
  96. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +51 -0
  97. data/lib/active_model_serializers/adapter/null.rb +9 -0
  98. data/lib/active_model_serializers/callbacks.rb +55 -0
  99. data/lib/active_model_serializers/deprecate.rb +54 -0
  100. data/lib/active_model_serializers/deserialization.rb +15 -0
  101. data/lib/active_model_serializers/json_pointer.rb +14 -0
  102. data/lib/active_model_serializers/logging.rb +122 -0
  103. data/lib/active_model_serializers/lookup_chain.rb +80 -0
  104. data/lib/active_model_serializers/model.rb +71 -0
  105. data/lib/active_model_serializers/railtie.rb +48 -0
  106. data/lib/active_model_serializers/register_jsonapi_renderer.rb +78 -0
  107. data/lib/active_model_serializers/serializable_resource.rb +82 -0
  108. data/lib/active_model_serializers/serialization_context.rb +39 -0
  109. data/lib/active_model_serializers/test.rb +7 -0
  110. data/lib/active_model_serializers/test/schema.rb +138 -0
  111. data/lib/active_model_serializers/test/serializer.rb +125 -0
  112. data/lib/generators/rails/USAGE +6 -0
  113. data/lib/generators/rails/resource_override.rb +10 -0
  114. data/lib/generators/rails/serializer_generator.rb +36 -0
  115. data/lib/generators/rails/templates/serializer.rb.erb +15 -0
  116. data/lib/grape/active_model_serializers.rb +16 -0
  117. data/lib/grape/formatters/active_model_serializers.rb +32 -0
  118. data/lib/grape/helpers/active_model_serializers.rb +17 -0
  119. data/test/action_controller/adapter_selector_test.rb +53 -0
  120. data/test/action_controller/explicit_serializer_test.rb +135 -0
  121. data/test/action_controller/json/include_test.rb +246 -0
  122. data/test/action_controller/json_api/deserialization_test.rb +112 -0
  123. data/test/action_controller/json_api/errors_test.rb +40 -0
  124. data/test/action_controller/json_api/fields_test.rb +66 -0
  125. data/test/action_controller/json_api/linked_test.rb +202 -0
  126. data/test/action_controller/json_api/pagination_test.rb +116 -0
  127. data/test/action_controller/json_api/transform_test.rb +189 -0
  128. data/test/action_controller/lookup_proc_test.rb +49 -0
  129. data/test/action_controller/namespace_lookup_test.rb +232 -0
  130. data/test/action_controller/serialization_scope_name_test.rb +229 -0
  131. data/test/action_controller/serialization_test.rb +472 -0
  132. data/test/active_model_serializers/adapter_for_test.rb +208 -0
  133. data/test/active_model_serializers/json_pointer_test.rb +22 -0
  134. data/test/active_model_serializers/logging_test.rb +77 -0
  135. data/test/active_model_serializers/model_test.rb +69 -0
  136. data/test/active_model_serializers/railtie_test_isolated.rb +63 -0
  137. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +161 -0
  138. data/test/active_model_serializers/serialization_context_test_isolated.rb +71 -0
  139. data/test/active_model_serializers/test/schema_test.rb +131 -0
  140. data/test/active_model_serializers/test/serializer_test.rb +62 -0
  141. data/test/active_record_test.rb +9 -0
  142. data/test/adapter/attributes_test.rb +43 -0
  143. data/test/adapter/deprecation_test.rb +100 -0
  144. data/test/adapter/json/belongs_to_test.rb +45 -0
  145. data/test/adapter/json/collection_test.rb +104 -0
  146. data/test/adapter/json/has_many_test.rb +45 -0
  147. data/test/adapter/json/transform_test.rb +93 -0
  148. data/test/adapter/json_api/belongs_to_test.rb +155 -0
  149. data/test/adapter/json_api/collection_test.rb +96 -0
  150. data/test/adapter/json_api/errors_test.rb +76 -0
  151. data/test/adapter/json_api/fields_test.rb +96 -0
  152. data/test/adapter/json_api/has_many_embed_ids_test.rb +43 -0
  153. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
  154. data/test/adapter/json_api/has_many_test.rb +165 -0
  155. data/test/adapter/json_api/has_one_test.rb +80 -0
  156. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +168 -0
  157. data/test/adapter/json_api/json_api_test.rb +33 -0
  158. data/test/adapter/json_api/linked_test.rb +413 -0
  159. data/test/adapter/json_api/links_test.rb +95 -0
  160. data/test/adapter/json_api/pagination_links_test.rb +193 -0
  161. data/test/adapter/json_api/parse_test.rb +137 -0
  162. data/test/adapter/json_api/relationship_test.rb +397 -0
  163. data/test/adapter/json_api/resource_identifier_test.rb +110 -0
  164. data/test/adapter/json_api/resource_meta_test.rb +100 -0
  165. data/test/adapter/json_api/toplevel_jsonapi_test.rb +82 -0
  166. data/test/adapter/json_api/transform_test.rb +512 -0
  167. data/test/adapter/json_api/type_test.rb +61 -0
  168. data/test/adapter/json_test.rb +46 -0
  169. data/test/adapter/null_test.rb +22 -0
  170. data/test/adapter/polymorphic_test.rb +171 -0
  171. data/test/adapter_test.rb +67 -0
  172. data/test/array_serializer_test.rb +22 -0
  173. data/test/benchmark/app.rb +65 -0
  174. data/test/benchmark/benchmarking_support.rb +67 -0
  175. data/test/benchmark/bm_active_record.rb +81 -0
  176. data/test/benchmark/bm_adapter.rb +38 -0
  177. data/test/benchmark/bm_caching.rb +119 -0
  178. data/test/benchmark/bm_lookup_chain.rb +83 -0
  179. data/test/benchmark/bm_transform.rb +45 -0
  180. data/test/benchmark/config.ru +3 -0
  181. data/test/benchmark/controllers.rb +83 -0
  182. data/test/benchmark/fixtures.rb +219 -0
  183. data/test/cache_test.rb +595 -0
  184. data/test/collection_serializer_test.rb +123 -0
  185. data/test/fixtures/active_record.rb +113 -0
  186. data/test/fixtures/poro.rb +232 -0
  187. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  188. data/test/generators/serializer_generator_test.rb +74 -0
  189. data/test/grape_test.rb +178 -0
  190. data/test/lint_test.rb +49 -0
  191. data/test/logger_test.rb +20 -0
  192. data/test/poro_test.rb +9 -0
  193. data/test/serializable_resource_test.rb +79 -0
  194. data/test/serializers/association_macros_test.rb +37 -0
  195. data/test/serializers/associations_test.rb +383 -0
  196. data/test/serializers/attribute_test.rb +153 -0
  197. data/test/serializers/attributes_test.rb +52 -0
  198. data/test/serializers/caching_configuration_test_isolated.rb +170 -0
  199. data/test/serializers/configuration_test.rb +32 -0
  200. data/test/serializers/fieldset_test.rb +14 -0
  201. data/test/serializers/meta_test.rb +202 -0
  202. data/test/serializers/options_test.rb +32 -0
  203. data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
  204. data/test/serializers/root_test.rb +21 -0
  205. data/test/serializers/serialization_test.rb +55 -0
  206. data/test/serializers/serializer_for_test.rb +136 -0
  207. data/test/serializers/serializer_for_with_namespace_test.rb +88 -0
  208. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  209. data/test/support/isolated_unit.rb +82 -0
  210. data/test/support/rails5_shims.rb +53 -0
  211. data/test/support/rails_app.rb +36 -0
  212. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  213. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
  214. data/test/support/schemas/custom/show.json +7 -0
  215. data/test/support/schemas/hyper_schema.json +93 -0
  216. data/test/support/schemas/render_using_json_api.json +43 -0
  217. data/test/support/schemas/simple_json_pointers.json +10 -0
  218. data/test/support/serialization_testing.rb +71 -0
  219. data/test/test_helper.rb +58 -0
  220. metadata +602 -0
@@ -0,0 +1,292 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ UndefinedCacheKey = Class.new(StandardError)
4
+ module Caching
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ with_options instance_writer: false, instance_reader: false do |serializer|
9
+ serializer.class_attribute :_cache # @api private : the cache store
10
+ serializer.class_attribute :_cache_key # @api private : when present, is first item in cache_key. Ignored if the serializable object defines #cache_key.
11
+ serializer.class_attribute :_cache_only # @api private : when fragment caching, whitelists fetch_attributes. Cannot combine with except
12
+ serializer.class_attribute :_cache_except # @api private : when fragment caching, blacklists fetch_attributes. Cannot combine with only
13
+ serializer.class_attribute :_cache_options # @api private : used by CachedSerializer, passed to _cache.fetch
14
+ # _cache_options include:
15
+ # expires_in
16
+ # compress
17
+ # force
18
+ # race_condition_ttl
19
+ # Passed to ::_cache as
20
+ # serializer.cache_store.fetch(cache_key, @klass._cache_options)
21
+ # Passed as second argument to serializer.cache_store.fetch(cache_key, serializer_class._cache_options)
22
+ serializer.class_attribute :_cache_digest_file_path # @api private : Derived at inheritance
23
+ end
24
+ end
25
+
26
+ # Matches
27
+ # "c:/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb:1:in `<top (required)>'"
28
+ # AND
29
+ # "/c/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb:1:in `<top (required)>'"
30
+ # AS
31
+ # c/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb
32
+ CALLER_FILE = /
33
+ \A # start of string
34
+ .+ # file path (one or more characters)
35
+ (?= # stop previous match when
36
+ :\d+ # a colon is followed by one or more digits
37
+ :in # followed by a colon followed by in
38
+ )
39
+ /x
40
+
41
+ module ClassMethods
42
+ def inherited(base)
43
+ super
44
+ caller_line = caller[1]
45
+ base._cache_digest_file_path = caller_line
46
+ end
47
+
48
+ def _cache_digest
49
+ return @_cache_digest if defined?(@_cache_digest)
50
+ @_cache_digest = digest_caller_file(_cache_digest_file_path)
51
+ end
52
+
53
+ # Hashes contents of file for +_cache_digest+
54
+ def digest_caller_file(caller_line)
55
+ serializer_file_path = caller_line[CALLER_FILE]
56
+ serializer_file_contents = IO.read(serializer_file_path)
57
+ Digest::MD5.hexdigest(serializer_file_contents)
58
+ rescue TypeError, Errno::ENOENT
59
+ warn <<-EOF.strip_heredoc
60
+ Cannot digest non-existent file: '#{caller_line}'.
61
+ Please set `::_cache_digest` of the serializer
62
+ if you'd like to cache it.
63
+ EOF
64
+ ''.freeze
65
+ end
66
+
67
+ def _skip_digest?
68
+ _cache_options && _cache_options[:skip_digest]
69
+ end
70
+
71
+ def fragmented_attributes
72
+ cached = _cache_only ? _cache_only : _attributes - _cache_except
73
+ cached = cached.map! { |field| _attributes_keys.fetch(field, field) }
74
+ non_cached = _attributes - cached
75
+ non_cached = non_cached.map! { |field| _attributes_keys.fetch(field, field) }
76
+ {
77
+ cached: cached,
78
+ non_cached: non_cached
79
+ }
80
+ end
81
+
82
+ # Enables a serializer to be automatically cached
83
+ #
84
+ # Sets +::_cache+ object to <tt>ActionController::Base.cache_store</tt>
85
+ # when Rails.configuration.action_controller.perform_caching
86
+ #
87
+ # @param options [Hash] with valid keys:
88
+ # cache_store : @see ::_cache
89
+ # key : @see ::_cache_key
90
+ # only : @see ::_cache_only
91
+ # except : @see ::_cache_except
92
+ # skip_digest : does not include digest in cache_key
93
+ # all else : @see ::_cache_options
94
+ #
95
+ # @example
96
+ # class PostSerializer < ActiveModel::Serializer
97
+ # cache key: 'post', expires_in: 3.hours
98
+ # attributes :title, :body
99
+ #
100
+ # has_many :comments
101
+ # end
102
+ #
103
+ # @todo require less code comments. See
104
+ # https://github.com/rails-api/active_model_serializers/pull/1249#issuecomment-146567837
105
+ def cache(options = {})
106
+ self._cache =
107
+ options.delete(:cache_store) ||
108
+ ActiveModelSerializers.config.cache_store ||
109
+ ActiveSupport::Cache.lookup_store(:null_store)
110
+ self._cache_key = options.delete(:key)
111
+ self._cache_only = options.delete(:only)
112
+ self._cache_except = options.delete(:except)
113
+ self._cache_options = options.empty? ? nil : options
114
+ end
115
+
116
+ # Value is from ActiveModelSerializers.config.perform_caching. Is used to
117
+ # globally enable or disable all serializer caching, just like
118
+ # Rails.configuration.action_controller.perform_caching, which is its
119
+ # default value in a Rails application.
120
+ # @return [true, false]
121
+ # Memoizes value of config first time it is called with a non-nil value.
122
+ # rubocop:disable Style/ClassVars
123
+ def perform_caching
124
+ return @@perform_caching if defined?(@@perform_caching) && !@@perform_caching.nil?
125
+ @@perform_caching = ActiveModelSerializers.config.perform_caching
126
+ end
127
+ alias perform_caching? perform_caching
128
+ # rubocop:enable Style/ClassVars
129
+
130
+ # The canonical method for getting the cache store for the serializer.
131
+ #
132
+ # @return [nil] when _cache is not set (i.e. when `cache` has not been called)
133
+ # @return [._cache] when _cache is not the NullStore
134
+ # @return [ActiveModelSerializers.config.cache_store] when _cache is the NullStore.
135
+ # This is so we can use `cache` being called to mean the serializer should be cached
136
+ # even if ActiveModelSerializers.config.cache_store has not yet been set.
137
+ # That means that when _cache is the NullStore and ActiveModelSerializers.config.cache_store
138
+ # is configured, `cache_store` becomes `ActiveModelSerializers.config.cache_store`.
139
+ # @return [nil] when _cache is the NullStore and ActiveModelSerializers.config.cache_store is nil.
140
+ def cache_store
141
+ return nil if _cache.nil?
142
+ return _cache if _cache.class != ActiveSupport::Cache::NullStore
143
+ if ActiveModelSerializers.config.cache_store
144
+ self._cache = ActiveModelSerializers.config.cache_store
145
+ else
146
+ nil
147
+ end
148
+ end
149
+
150
+ def cache_enabled?
151
+ perform_caching? && cache_store && !_cache_only && !_cache_except
152
+ end
153
+
154
+ def fragment_cache_enabled?
155
+ perform_caching? && cache_store &&
156
+ (_cache_only && !_cache_except || !_cache_only && _cache_except)
157
+ end
158
+
159
+ # Read cache from cache_store
160
+ # @return [Hash]
161
+ def cache_read_multi(collection_serializer, adapter_instance, include_directive)
162
+ return {} if ActiveModelSerializers.config.cache_store.blank?
163
+
164
+ keys = object_cache_keys(collection_serializer, adapter_instance, include_directive)
165
+
166
+ return {} if keys.blank?
167
+
168
+ ActiveModelSerializers.config.cache_store.read_multi(*keys)
169
+ end
170
+
171
+ # Find all cache_key for the collection_serializer
172
+ # @param serializers [ActiveModel::Serializer::CollectionSerializer]
173
+ # @param adapter_instance [ActiveModelSerializers::Adapter::Base]
174
+ # @param include_directive [JSONAPI::IncludeDirective]
175
+ # @return [Array] all cache_key of collection_serializer
176
+ def object_cache_keys(collection_serializer, adapter_instance, include_directive)
177
+ cache_keys = []
178
+
179
+ collection_serializer.each do |serializer|
180
+ cache_keys << object_cache_key(serializer, adapter_instance)
181
+
182
+ serializer.associations(include_directive).each do |association|
183
+ if association.serializer.respond_to?(:each)
184
+ association.serializer.each do |sub_serializer|
185
+ cache_keys << object_cache_key(sub_serializer, adapter_instance)
186
+ end
187
+ else
188
+ cache_keys << object_cache_key(association.serializer, adapter_instance)
189
+ end
190
+ end
191
+ end
192
+
193
+ cache_keys.compact.uniq
194
+ end
195
+
196
+ # @return [String, nil] the cache_key of the serializer or nil
197
+ def object_cache_key(serializer, adapter_instance)
198
+ return unless serializer.present? && serializer.object.present?
199
+
200
+ (serializer.class.cache_enabled? || serializer.class.fragment_cache_enabled?) ? serializer.cache_key(adapter_instance) : nil
201
+ end
202
+ end
203
+
204
+ ### INSTANCE METHODS
205
+ def fetch_attributes(fields, cached_attributes, adapter_instance)
206
+ if serializer_class.cache_enabled?
207
+ key = cache_key(adapter_instance)
208
+ cached_attributes.fetch(key) do
209
+ serializer_class.cache_store.fetch(key, serializer_class._cache_options) do
210
+ attributes(fields, true)
211
+ end
212
+ end
213
+ elsif serializer_class.fragment_cache_enabled?
214
+ fetch_attributes_fragment(adapter_instance, cached_attributes)
215
+ else
216
+ attributes(fields, true)
217
+ end
218
+ end
219
+
220
+ def fetch(adapter_instance, cache_options = serializer_class._cache_options)
221
+ if serializer_class.cache_store
222
+ serializer_class.cache_store.fetch(cache_key(adapter_instance), cache_options) do
223
+ yield
224
+ end
225
+ else
226
+ yield
227
+ end
228
+ end
229
+
230
+ # 1. Determine cached fields from serializer class options
231
+ # 2. Get non_cached_fields and fetch cache_fields
232
+ # 3. Merge the two hashes using adapter_instance#fragment_cache
233
+ # rubocop:disable Metrics/AbcSize
234
+ def fetch_attributes_fragment(adapter_instance, cached_attributes = {})
235
+ serializer_class._cache_options ||= {}
236
+ serializer_class._cache_options[:key] = serializer_class._cache_key if serializer_class._cache_key
237
+ fields = serializer_class.fragmented_attributes
238
+
239
+ non_cached_fields = fields[:non_cached].dup
240
+ non_cached_hash = attributes(non_cached_fields, true)
241
+ include_directive = JSONAPI::IncludeDirective.new(non_cached_fields - non_cached_hash.keys)
242
+ non_cached_hash.merge! resource_relationships({}, { include_directive: include_directive }, adapter_instance)
243
+
244
+ cached_fields = fields[:cached].dup
245
+ key = cache_key(adapter_instance)
246
+ cached_hash =
247
+ cached_attributes.fetch(key) do
248
+ serializer_class.cache_store.fetch(key, serializer_class._cache_options) do
249
+ hash = attributes(cached_fields, true)
250
+ include_directive = JSONAPI::IncludeDirective.new(cached_fields - hash.keys)
251
+ hash.merge! resource_relationships({}, { include_directive: include_directive }, adapter_instance)
252
+ end
253
+ end
254
+ # Merge both results
255
+ adapter_instance.fragment_cache(cached_hash, non_cached_hash)
256
+ end
257
+ # rubocop:enable Metrics/AbcSize
258
+
259
+ def cache_key(adapter_instance)
260
+ return @cache_key if defined?(@cache_key)
261
+
262
+ parts = []
263
+ parts << object_cache_key
264
+ parts << adapter_instance.cache_key
265
+ parts << serializer_class._cache_digest unless serializer_class._skip_digest?
266
+ @cache_key = expand_cache_key(parts)
267
+ end
268
+
269
+ def expand_cache_key(parts)
270
+ ActiveSupport::Cache.expand_cache_key(parts)
271
+ end
272
+
273
+ # Use object's cache_key if available, else derive a key from the object
274
+ # Pass the `key` option to the `cache` declaration or override this method to customize the cache key
275
+ def object_cache_key
276
+ if object.respond_to?(:cache_key)
277
+ object.cache_key
278
+ elsif (serializer_cache_key = (serializer_class._cache_key || serializer_class._cache_options[:key]))
279
+ object_time_safe = object.updated_at
280
+ object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime)
281
+ "#{serializer_cache_key}/#{object.id}-#{object_time_safe}"
282
+ else
283
+ fail UndefinedCacheKey, "#{object.class} must define #cache_key, or the 'key:' option must be passed into '#{serializer_class}.cache'"
284
+ end
285
+ end
286
+
287
+ def serializer_class
288
+ @serializer_class ||= self.class
289
+ end
290
+ end
291
+ end
292
+ end
@@ -0,0 +1,59 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Configuration
4
+ include ActiveSupport::Configurable
5
+ extend ActiveSupport::Concern
6
+
7
+ # Configuration options may also be set in
8
+ # Serializers and Adapters
9
+ included do |base|
10
+ config = base.config
11
+ config.collection_serializer = ActiveModel::Serializer::CollectionSerializer
12
+ config.serializer_lookup_enabled = true
13
+
14
+ def config.array_serializer=(collection_serializer)
15
+ self.collection_serializer = collection_serializer
16
+ end
17
+
18
+ def config.array_serializer
19
+ collection_serializer
20
+ end
21
+
22
+ config.default_includes = '*'
23
+ config.adapter = :attributes
24
+ config.key_transform = nil
25
+ config.jsonapi_pagination_links_enabled = true
26
+ config.jsonapi_resource_type = :plural
27
+ config.jsonapi_namespace_separator = '-'.freeze
28
+ config.jsonapi_version = '1.0'
29
+ config.jsonapi_toplevel_meta = {}
30
+ # Make JSON API top-level jsonapi member opt-in
31
+ # ref: http://jsonapi.org/format/#document-top-level
32
+ config.jsonapi_include_toplevel_object = false
33
+ config.include_data_default = true
34
+
35
+ # For configuring how serializers are found.
36
+ # This should be an array of procs.
37
+ #
38
+ # The priority of the output is that the first item
39
+ # in the evaluated result array will take precedence
40
+ # over other possible serializer paths.
41
+ #
42
+ # i.e.: First match wins.
43
+ #
44
+ # @example output
45
+ # => [
46
+ # "CustomNamespace::ResourceSerializer",
47
+ # "ParentSerializer::ResourceSerializer",
48
+ # "ResourceNamespace::ResourceSerializer" ,
49
+ # "ResourceSerializer"]
50
+ #
51
+ # If CustomNamespace::ResourceSerializer exists, it will be used
52
+ # for serialization
53
+ config.serializer_lookup_chain = ActiveModelSerializers::LookupChain::DEFAULT.dup
54
+
55
+ config.schema_path = 'test/support/schemas'
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,35 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Links
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ with_options instance_writer: false, instance_reader: true do |serializer|
8
+ serializer.class_attribute :_links # @api private
9
+ self._links ||= {}
10
+ end
11
+
12
+ extend ActiveSupport::Autoload
13
+ end
14
+
15
+ module ClassMethods
16
+ def inherited(base)
17
+ super
18
+ base._links = _links.dup
19
+ end
20
+
21
+ # Define a link on a serializer.
22
+ # @example
23
+ # link(:self) { resource_url(object) }
24
+ # @example
25
+ # link(:self) { "http://example.com/resource/#{object.id}" }
26
+ # @example
27
+ # link :resource, "http://example.com/resource"
28
+ #
29
+ def link(name, value = nil, &block)
30
+ _links[name] = block || value
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Meta
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ with_options instance_writer: false, instance_reader: true do |serializer|
8
+ serializer.class_attribute :_meta # @api private
9
+ end
10
+
11
+ extend ActiveSupport::Autoload
12
+ end
13
+
14
+ module ClassMethods
15
+ # Set the JSON API meta attribute of a serializer.
16
+ # @example
17
+ # class AdminAuthorSerializer < ActiveModel::Serializer
18
+ # meta { stuff: 'value' }
19
+ # @example
20
+ # meta do
21
+ # { comment_count: object.comments.count }
22
+ # end
23
+ def meta(value = nil, &block)
24
+ self._meta = block || value
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Type
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ with_options instance_writer: false, instance_reader: true do |serializer|
8
+ serializer.class_attribute :_type # @api private
9
+ end
10
+
11
+ extend ActiveSupport::Autoload
12
+ end
13
+
14
+ module ClassMethods
15
+ # Set the JSON API type of a serializer.
16
+ # @example
17
+ # class AdminAuthorSerializer < ActiveModel::Serializer
18
+ # type 'authors'
19
+ def type(type)
20
+ self._type = type && type.to_s
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class ErrorSerializer < ActiveModel::Serializer
4
+ # @return [Hash<field_name,Array<error_message>>]
5
+ def as_json
6
+ object.errors.messages
7
+ end
8
+
9
+ def success?
10
+ false
11
+ end
12
+ end
13
+ end
14
+ end