active_model_serializers 0.10.3 → 0.10.12

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 (224) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +154 -2
  3. data/README.md +153 -15
  4. data/lib/action_controller/serialization.rb +11 -1
  5. data/lib/active_model/serializable_resource.rb +2 -0
  6. data/lib/active_model/serializer.rb +275 -81
  7. data/lib/active_model/serializer/adapter.rb +2 -0
  8. data/lib/active_model/serializer/adapter/attributes.rb +2 -0
  9. data/lib/active_model/serializer/adapter/base.rb +2 -0
  10. data/lib/active_model/serializer/adapter/json.rb +2 -0
  11. data/lib/active_model/serializer/adapter/json_api.rb +2 -0
  12. data/lib/active_model/serializer/adapter/null.rb +2 -0
  13. data/lib/active_model/serializer/array_serializer.rb +2 -0
  14. data/lib/active_model/serializer/association.rb +53 -14
  15. data/lib/active_model/serializer/attribute.rb +2 -0
  16. data/lib/active_model/serializer/belongs_to_reflection.rb +7 -1
  17. data/lib/active_model/serializer/collection_serializer.rb +8 -5
  18. data/lib/active_model/serializer/concerns/caching.rb +36 -23
  19. data/lib/active_model/serializer/error_serializer.rb +2 -0
  20. data/lib/active_model/serializer/errors_serializer.rb +2 -0
  21. data/lib/active_model/serializer/field.rb +2 -0
  22. data/lib/active_model/serializer/fieldset.rb +3 -1
  23. data/lib/active_model/serializer/has_many_reflection.rb +6 -1
  24. data/lib/active_model/serializer/has_one_reflection.rb +3 -1
  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 +2 -0
  28. data/lib/active_model/serializer/null.rb +2 -0
  29. data/lib/active_model/serializer/reflection.rb +122 -73
  30. data/lib/active_model/serializer/version.rb +3 -1
  31. data/lib/active_model_serializers.rb +29 -11
  32. data/lib/active_model_serializers/adapter.rb +3 -1
  33. data/lib/active_model_serializers/adapter/attributes.rb +23 -0
  34. data/lib/active_model_serializers/adapter/base.rb +4 -2
  35. data/lib/active_model_serializers/adapter/json.rb +2 -0
  36. data/lib/active_model_serializers/adapter/json_api.rb +44 -26
  37. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +4 -2
  38. data/lib/active_model_serializers/adapter/json_api/error.rb +2 -0
  39. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +2 -0
  40. data/lib/active_model_serializers/adapter/json_api/link.rb +2 -0
  41. data/lib/active_model_serializers/adapter/json_api/meta.rb +2 -0
  42. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +42 -21
  43. data/lib/active_model_serializers/adapter/json_api/relationship.rb +52 -9
  44. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +35 -18
  45. data/lib/active_model_serializers/adapter/null.rb +2 -0
  46. data/lib/active_model_serializers/callbacks.rb +2 -0
  47. data/lib/active_model_serializers/deprecate.rb +2 -0
  48. data/lib/active_model_serializers/deserialization.rb +2 -0
  49. data/lib/active_model_serializers/json_pointer.rb +2 -0
  50. data/lib/active_model_serializers/logging.rb +2 -0
  51. data/lib/active_model_serializers/lookup_chain.rb +2 -0
  52. data/lib/active_model_serializers/model.rb +111 -30
  53. data/lib/active_model_serializers/model/caching.rb +25 -0
  54. data/lib/active_model_serializers/railtie.rb +4 -0
  55. data/lib/active_model_serializers/register_jsonapi_renderer.rb +2 -0
  56. data/lib/active_model_serializers/serializable_resource.rb +4 -2
  57. data/lib/active_model_serializers/serialization_context.rb +2 -0
  58. data/lib/active_model_serializers/test.rb +2 -0
  59. data/lib/active_model_serializers/test/schema.rb +4 -2
  60. data/lib/active_model_serializers/test/serializer.rb +2 -0
  61. data/lib/generators/rails/resource_override.rb +3 -1
  62. data/lib/generators/rails/serializer_generator.rb +2 -0
  63. data/lib/grape/active_model_serializers.rb +2 -0
  64. data/lib/grape/formatters/active_model_serializers.rb +2 -0
  65. data/lib/grape/helpers/active_model_serializers.rb +2 -0
  66. data/lib/tasks/rubocop.rake +55 -0
  67. metadata +74 -291
  68. data/.github/ISSUE_TEMPLATE.md +0 -29
  69. data/.github/PULL_REQUEST_TEMPLATE.md +0 -15
  70. data/.gitignore +0 -35
  71. data/.rubocop.yml +0 -102
  72. data/.simplecov +0 -110
  73. data/.travis.yml +0 -51
  74. data/CODE_OF_CONDUCT.md +0 -74
  75. data/CONTRIBUTING.md +0 -105
  76. data/Gemfile +0 -56
  77. data/Rakefile +0 -103
  78. data/active_model_serializers.gemspec +0 -62
  79. data/appveyor.yml +0 -24
  80. data/bin/bench +0 -171
  81. data/bin/bench_regression +0 -316
  82. data/bin/serve_benchmark +0 -39
  83. data/docs/ARCHITECTURE.md +0 -125
  84. data/docs/README.md +0 -42
  85. data/docs/STYLE.md +0 -58
  86. data/docs/general/adapters.md +0 -247
  87. data/docs/general/caching.md +0 -58
  88. data/docs/general/configuration_options.md +0 -169
  89. data/docs/general/deserialization.md +0 -100
  90. data/docs/general/fields.md +0 -31
  91. data/docs/general/getting_started.md +0 -133
  92. data/docs/general/instrumentation.md +0 -40
  93. data/docs/general/key_transforms.md +0 -40
  94. data/docs/general/logging.md +0 -14
  95. data/docs/general/rendering.md +0 -294
  96. data/docs/general/serializers.md +0 -461
  97. data/docs/how-open-source-maintained.jpg +0 -0
  98. data/docs/howto/add_pagination_links.md +0 -138
  99. data/docs/howto/add_relationship_links.md +0 -137
  100. data/docs/howto/add_root_key.md +0 -55
  101. data/docs/howto/grape_integration.md +0 -42
  102. data/docs/howto/outside_controller_use.md +0 -65
  103. data/docs/howto/passing_arbitrary_options.md +0 -27
  104. data/docs/howto/serialize_poro.md +0 -32
  105. data/docs/howto/test.md +0 -154
  106. data/docs/howto/upgrade_from_0_8_to_0_10.md +0 -265
  107. data/docs/integrations/ember-and-json-api.md +0 -144
  108. data/docs/integrations/grape.md +0 -19
  109. data/docs/jsonapi/errors.md +0 -56
  110. data/docs/jsonapi/schema.md +0 -151
  111. data/docs/jsonapi/schema/schema.json +0 -366
  112. data/docs/rfcs/0000-namespace.md +0 -106
  113. data/docs/rfcs/template.md +0 -15
  114. data/lib/active_model/serializer/collection_reflection.rb +0 -7
  115. data/lib/active_model/serializer/concerns/associations.rb +0 -102
  116. data/lib/active_model/serializer/concerns/attributes.rb +0 -82
  117. data/lib/active_model/serializer/concerns/configuration.rb +0 -59
  118. data/lib/active_model/serializer/concerns/links.rb +0 -35
  119. data/lib/active_model/serializer/concerns/meta.rb +0 -29
  120. data/lib/active_model/serializer/concerns/type.rb +0 -25
  121. data/lib/active_model/serializer/singular_reflection.rb +0 -7
  122. data/lib/active_model_serializers/key_transform.rb +0 -74
  123. data/test/action_controller/adapter_selector_test.rb +0 -53
  124. data/test/action_controller/explicit_serializer_test.rb +0 -135
  125. data/test/action_controller/json/include_test.rb +0 -246
  126. data/test/action_controller/json_api/deserialization_test.rb +0 -112
  127. data/test/action_controller/json_api/errors_test.rb +0 -40
  128. data/test/action_controller/json_api/fields_test.rb +0 -57
  129. data/test/action_controller/json_api/linked_test.rb +0 -202
  130. data/test/action_controller/json_api/pagination_test.rb +0 -116
  131. data/test/action_controller/json_api/transform_test.rb +0 -181
  132. data/test/action_controller/lookup_proc_test.rb +0 -49
  133. data/test/action_controller/namespace_lookup_test.rb +0 -226
  134. data/test/action_controller/serialization_scope_name_test.rb +0 -229
  135. data/test/action_controller/serialization_test.rb +0 -472
  136. data/test/active_model_serializers/adapter_for_test.rb +0 -208
  137. data/test/active_model_serializers/json_pointer_test.rb +0 -22
  138. data/test/active_model_serializers/key_transform_test.rb +0 -297
  139. data/test/active_model_serializers/logging_test.rb +0 -77
  140. data/test/active_model_serializers/model_test.rb +0 -22
  141. data/test/active_model_serializers/railtie_test_isolated.rb +0 -63
  142. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +0 -143
  143. data/test/active_model_serializers/serialization_context_test_isolated.rb +0 -71
  144. data/test/active_model_serializers/test/schema_test.rb +0 -130
  145. data/test/active_model_serializers/test/serializer_test.rb +0 -62
  146. data/test/active_record_test.rb +0 -9
  147. data/test/adapter/attributes_test.rb +0 -43
  148. data/test/adapter/deprecation_test.rb +0 -100
  149. data/test/adapter/json/belongs_to_test.rb +0 -45
  150. data/test/adapter/json/collection_test.rb +0 -104
  151. data/test/adapter/json/has_many_test.rb +0 -45
  152. data/test/adapter/json/transform_test.rb +0 -93
  153. data/test/adapter/json_api/belongs_to_test.rb +0 -155
  154. data/test/adapter/json_api/collection_test.rb +0 -96
  155. data/test/adapter/json_api/errors_test.rb +0 -76
  156. data/test/adapter/json_api/fields_test.rb +0 -88
  157. data/test/adapter/json_api/has_many_embed_ids_test.rb +0 -43
  158. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +0 -96
  159. data/test/adapter/json_api/has_many_test.rb +0 -165
  160. data/test/adapter/json_api/has_one_test.rb +0 -80
  161. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +0 -166
  162. data/test/adapter/json_api/json_api_test.rb +0 -33
  163. data/test/adapter/json_api/linked_test.rb +0 -413
  164. data/test/adapter/json_api/links_test.rb +0 -95
  165. data/test/adapter/json_api/pagination_links_test.rb +0 -193
  166. data/test/adapter/json_api/parse_test.rb +0 -137
  167. data/test/adapter/json_api/relationship_test.rb +0 -397
  168. data/test/adapter/json_api/resource_identifier_test.rb +0 -110
  169. data/test/adapter/json_api/resource_meta_test.rb +0 -100
  170. data/test/adapter/json_api/toplevel_jsonapi_test.rb +0 -82
  171. data/test/adapter/json_api/transform_test.rb +0 -504
  172. data/test/adapter/json_api/type_test.rb +0 -61
  173. data/test/adapter/json_test.rb +0 -46
  174. data/test/adapter/null_test.rb +0 -22
  175. data/test/adapter/polymorphic_test.rb +0 -171
  176. data/test/adapter_test.rb +0 -67
  177. data/test/array_serializer_test.rb +0 -22
  178. data/test/benchmark/app.rb +0 -65
  179. data/test/benchmark/benchmarking_support.rb +0 -67
  180. data/test/benchmark/bm_active_record.rb +0 -81
  181. data/test/benchmark/bm_adapter.rb +0 -38
  182. data/test/benchmark/bm_caching.rb +0 -119
  183. data/test/benchmark/bm_lookup_chain.rb +0 -83
  184. data/test/benchmark/bm_transform.rb +0 -45
  185. data/test/benchmark/config.ru +0 -3
  186. data/test/benchmark/controllers.rb +0 -83
  187. data/test/benchmark/fixtures.rb +0 -219
  188. data/test/cache_test.rb +0 -579
  189. data/test/collection_serializer_test.rb +0 -110
  190. data/test/fixtures/active_record.rb +0 -78
  191. data/test/fixtures/poro.rb +0 -286
  192. data/test/generators/scaffold_controller_generator_test.rb +0 -24
  193. data/test/generators/serializer_generator_test.rb +0 -74
  194. data/test/grape_test.rb +0 -178
  195. data/test/lint_test.rb +0 -49
  196. data/test/logger_test.rb +0 -20
  197. data/test/poro_test.rb +0 -9
  198. data/test/serializable_resource_test.rb +0 -79
  199. data/test/serializers/association_macros_test.rb +0 -37
  200. data/test/serializers/associations_test.rb +0 -370
  201. data/test/serializers/attribute_test.rb +0 -151
  202. data/test/serializers/attributes_test.rb +0 -52
  203. data/test/serializers/caching_configuration_test_isolated.rb +0 -170
  204. data/test/serializers/configuration_test.rb +0 -32
  205. data/test/serializers/fieldset_test.rb +0 -14
  206. data/test/serializers/meta_test.rb +0 -202
  207. data/test/serializers/options_test.rb +0 -21
  208. data/test/serializers/read_attribute_for_serialization_test.rb +0 -79
  209. data/test/serializers/root_test.rb +0 -21
  210. data/test/serializers/serialization_test.rb +0 -55
  211. data/test/serializers/serializer_for_test.rb +0 -136
  212. data/test/serializers/serializer_for_with_namespace_test.rb +0 -87
  213. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +0 -6
  214. data/test/support/isolated_unit.rb +0 -82
  215. data/test/support/rails5_shims.rb +0 -53
  216. data/test/support/rails_app.rb +0 -36
  217. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +0 -6
  218. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +0 -6
  219. data/test/support/schemas/custom/show.json +0 -7
  220. data/test/support/schemas/hyper_schema.json +0 -93
  221. data/test/support/schemas/render_using_json_api.json +0 -43
  222. data/test/support/schemas/simple_json_pointers.json +0 -10
  223. data/test/support/serialization_testing.rb +0 -71
  224. data/test/test_helper.rb +0 -58
@@ -1,219 +0,0 @@
1
- Rails.configuration.serializers = []
2
- class HasOneRelationshipSerializer < ActiveModel::Serializer
3
- attributes :id, :first_name, :last_name
4
-
5
- has_many :primary_resources, embed: :ids
6
- has_one :bio
7
- end
8
- Rails.configuration.serializers << HasOneRelationshipSerializer
9
-
10
- class VirtualAttributeSerializer < ActiveModel::Serializer
11
- attributes :id, :name
12
- end
13
- Rails.configuration.serializers << VirtualAttributeSerializer
14
-
15
- class HasManyRelationshipSerializer < ActiveModel::Serializer
16
- attributes :id, :body
17
-
18
- belongs_to :primary_resource
19
- belongs_to :has_one_relationship
20
- end
21
- Rails.configuration.serializers << HasManyRelationshipSerializer
22
-
23
- class PrimaryResourceSerializer < ActiveModel::Serializer
24
- attributes :id, :title, :body
25
-
26
- has_many :has_many_relationships, serializer: HasManyRelationshipSerializer
27
- belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer
28
- belongs_to :has_one_relationship, serializer: HasOneRelationshipSerializer
29
-
30
- link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' }
31
-
32
- meta do
33
- {
34
- rating: 5,
35
- favorite_count: 10
36
- }
37
- end
38
-
39
- def virtual_attribute
40
- VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute')
41
- end
42
- end
43
- Rails.configuration.serializers << PrimaryResourceSerializer
44
-
45
- class CachingHasOneRelationshipSerializer < HasOneRelationshipSerializer
46
- cache key: 'writer', skip_digest: true
47
- end
48
- Rails.configuration.serializers << CachingHasOneRelationshipSerializer
49
-
50
- class CachingHasManyRelationshipSerializer < HasManyRelationshipSerializer
51
- cache expires_in: 1.day, skip_digest: true
52
- end
53
- Rails.configuration.serializers << CachingHasManyRelationshipSerializer
54
-
55
- # see https://github.com/rails-api/active_model_serializers/pull/1690/commits/68715b8f99bc29677e8a47bb3f305f23c077024b#r60344532
56
- class CachingPrimaryResourceSerializer < ActiveModel::Serializer
57
- cache key: 'primary_resource', expires_in: 0.1, skip_digest: true
58
-
59
- attributes :id, :title, :body
60
-
61
- belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer
62
- belongs_to :has_one_relationship, serializer: CachingHasOneRelationshipSerializer
63
- has_many :has_many_relationships, serializer: CachingHasManyRelationshipSerializer
64
-
65
- link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' }
66
-
67
- meta do
68
- {
69
- rating: 5,
70
- favorite_count: 10
71
- }
72
- end
73
-
74
- def virtual_attribute
75
- VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute')
76
- end
77
- end
78
- Rails.configuration.serializers << CachingPrimaryResourceSerializer
79
-
80
- class FragmentCachingHasOneRelationshipSerializer < HasOneRelationshipSerializer
81
- cache key: 'writer', only: [:first_name, :last_name], skip_digest: true
82
- end
83
- Rails.configuration.serializers << FragmentCachingHasOneRelationshipSerializer
84
-
85
- class FragmentCachingHasManyRelationshipSerializer < HasManyRelationshipSerializer
86
- cache expires_in: 1.day, except: [:body], skip_digest: true
87
- end
88
- Rails.configuration.serializers << CachingHasManyRelationshipSerializer
89
-
90
- # see https://github.com/rails-api/active_model_serializers/pull/1690/commits/68715b8f99bc29677e8a47bb3f305f23c077024b#r60344532
91
- class FragmentCachingPrimaryResourceSerializer < ActiveModel::Serializer
92
- cache key: 'primary_resource', expires_in: 0.1, skip_digest: true
93
-
94
- attributes :id, :title, :body
95
-
96
- belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer
97
- belongs_to :has_one_relationship, serializer: FragmentCachingHasOneRelationshipSerializer
98
- has_many :has_many_relationships, serializer: FragmentCachingHasManyRelationshipSerializer
99
-
100
- link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' }
101
-
102
- meta do
103
- {
104
- rating: 5,
105
- favorite_count: 10
106
- }
107
- end
108
-
109
- def virtual_attribute
110
- VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute')
111
- end
112
- end
113
- Rails.configuration.serializers << FragmentCachingPrimaryResourceSerializer
114
-
115
- if ENV['ENABLE_ACTIVE_RECORD'] == 'true'
116
- require 'active_record'
117
-
118
- ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
119
- ActiveRecord::Schema.define do
120
- self.verbose = false
121
-
122
- create_table :virtual_attributes, force: true do |t|
123
- t.string :name
124
- t.timestamps null: false
125
- end
126
- create_table :has_one_relationships, force: true do |t|
127
- t.string :first_name
128
- t.string :last_name
129
- t.timestamps null: false
130
- end
131
- create_table :primary_resources, force: true do |t|
132
- t.string :title
133
- t.text :body
134
- t.references :has_one_relationship
135
- t.references :virtual_attribute
136
- t.timestamps null: false
137
- end
138
- create_table :has_many_relationships, force: true do |t|
139
- t.text :body
140
- t.references :has_one_relationship
141
- t.references :primary_resource
142
- t.timestamps null: false
143
- end
144
- end
145
-
146
- class HasManyRelationship < ActiveRecord::Base
147
- belongs_to :has_one_relationship
148
- belongs_to :primary_resource
149
- end
150
-
151
- class HasOneRelationship < ActiveRecord::Base
152
- has_many :primary_resources
153
- has_many :has_many_relationships
154
- end
155
-
156
- class PrimaryResource < ActiveRecord::Base
157
- has_many :has_many_relationships
158
- belongs_to :has_one_relationship
159
- belongs_to :virtual_attribute
160
- end
161
-
162
- class VirtualAttribute < ActiveRecord::Base
163
- has_many :primary_resources
164
- end
165
- else
166
- # ActiveModelSerializers::Model is a convenient
167
- # serializable class to inherit from when making
168
- # serializable non-activerecord objects.
169
- class BenchmarkModel
170
- include ActiveModel::Model
171
- include ActiveModel::Serializers::JSON
172
-
173
- attr_reader :attributes
174
-
175
- def initialize(attributes = {})
176
- @attributes = attributes
177
- super
178
- end
179
-
180
- # Defaults to the downcased model name.
181
- def id
182
- attributes.fetch(:id) { self.class.name.downcase }
183
- end
184
-
185
- # Defaults to the downcased model name and updated_at
186
- def cache_key
187
- attributes.fetch(:cache_key) { "#{self.class.name.downcase}/#{id}" }
188
- end
189
-
190
- # Defaults to the time the serializer file was modified.
191
- def updated_at
192
- @updated_at ||= attributes.fetch(:updated_at) { File.mtime(__FILE__) }
193
- end
194
-
195
- def read_attribute_for_serialization(key)
196
- if key == :id || key == 'id'
197
- attributes.fetch(key) { id }
198
- else
199
- attributes[key]
200
- end
201
- end
202
- end
203
-
204
- class HasManyRelationship < BenchmarkModel
205
- attr_accessor :id, :body
206
- end
207
-
208
- class HasOneRelationship < BenchmarkModel
209
- attr_accessor :id, :first_name, :last_name, :primary_resources
210
- end
211
-
212
- class PrimaryResource < BenchmarkModel
213
- attr_accessor :id, :title, :body, :has_many_relationships, :virtual_attribute, :has_one_relationship
214
- end
215
-
216
- class VirtualAttribute < BenchmarkModel
217
- attr_accessor :id, :name
218
- end
219
- end
data/test/cache_test.rb DELETED
@@ -1,579 +0,0 @@
1
- require 'test_helper'
2
- require 'tmpdir'
3
- require 'tempfile'
4
-
5
- module ActiveModelSerializers
6
- class CacheTest < ActiveSupport::TestCase
7
- # Instead of a primitive cache key (i.e. a string), this class
8
- # returns a list of objects that require to be expanded themselves.
9
- class AuthorWithExpandableCacheElements < Author
10
- # For the test purposes it's important that #to_s for HasCacheKey differs
11
- # between instances, hence not a Struct.
12
- class HasCacheKey
13
- attr_reader :cache_key
14
- def initialize(cache_key)
15
- @cache_key = cache_key
16
- end
17
-
18
- def to_s
19
- "HasCacheKey##{object_id}"
20
- end
21
- end
22
-
23
- def cache_key
24
- [
25
- HasCacheKey.new(name),
26
- HasCacheKey.new(id)
27
- ]
28
- end
29
- end
30
-
31
- class UncachedAuthor < Author
32
- # To confirm cache_key is set using updated_at and cache_key option passed to cache
33
- undef_method :cache_key
34
- end
35
-
36
- class Article < ::Model
37
- # To confirm error is raised when cache_key is not set and cache_key option not passed to cache
38
- undef_method :cache_key
39
- end
40
-
41
- class ArticleSerializer < ActiveModel::Serializer
42
- cache only: [:place], skip_digest: true
43
- attributes :title
44
- end
45
-
46
- class InheritedRoleSerializer < RoleSerializer
47
- cache key: 'inherited_role', only: [:name, :special_attribute]
48
- attribute :special_attribute
49
- end
50
-
51
- setup do
52
- cache_store.clear
53
- @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
54
- @post = Post.new(title: 'New Post', body: 'Body')
55
- @bio = Bio.new(id: 1, content: 'AMS Contributor')
56
- @author = Author.new(name: 'Joao M. D. Moura')
57
- @blog = Blog.new(id: 999, name: 'Custom blog', writer: @author, articles: [])
58
- @role = Role.new(name: 'Great Author')
59
- @location = Location.new(lat: '-23.550520', lng: '-46.633309')
60
- @place = Place.new(name: 'Amazing Place')
61
- @author.posts = [@post]
62
- @author.roles = [@role]
63
- @role.author = @author
64
- @author.bio = @bio
65
- @bio.author = @author
66
- @post.comments = [@comment]
67
- @post.author = @author
68
- @comment.post = @post
69
- @comment.author = @author
70
- @post.blog = @blog
71
- @location.place = @place
72
-
73
- @location_serializer = LocationSerializer.new(@location)
74
- @bio_serializer = BioSerializer.new(@bio)
75
- @role_serializer = RoleSerializer.new(@role)
76
- @post_serializer = PostSerializer.new(@post)
77
- @author_serializer = AuthorSerializer.new(@author)
78
- @comment_serializer = CommentSerializer.new(@comment)
79
- @blog_serializer = BlogSerializer.new(@blog)
80
- end
81
-
82
- def test_explicit_cache_store
83
- default_store = Class.new(ActiveModel::Serializer) do
84
- cache
85
- end
86
- explicit_store = Class.new(ActiveModel::Serializer) do
87
- cache cache_store: ActiveSupport::Cache::FileStore
88
- end
89
-
90
- assert ActiveSupport::Cache::MemoryStore, ActiveModelSerializers.config.cache_store
91
- assert ActiveSupport::Cache::MemoryStore, default_store.cache_store
92
- assert ActiveSupport::Cache::FileStore, explicit_store.cache_store
93
- end
94
-
95
- def test_inherited_cache_configuration
96
- inherited_serializer = Class.new(PostSerializer)
97
-
98
- assert_equal PostSerializer._cache_key, inherited_serializer._cache_key
99
- assert_equal PostSerializer._cache_options, inherited_serializer._cache_options
100
- end
101
-
102
- def test_override_cache_configuration
103
- inherited_serializer = Class.new(PostSerializer) do
104
- cache key: 'new-key'
105
- end
106
-
107
- assert_equal PostSerializer._cache_key, 'post'
108
- assert_equal inherited_serializer._cache_key, 'new-key'
109
- end
110
-
111
- def test_cache_definition
112
- assert_equal(cache_store, @post_serializer.class._cache)
113
- assert_equal(cache_store, @author_serializer.class._cache)
114
- assert_equal(cache_store, @comment_serializer.class._cache)
115
- end
116
-
117
- def test_cache_key_definition
118
- assert_equal('post', @post_serializer.class._cache_key)
119
- assert_equal('writer', @author_serializer.class._cache_key)
120
- assert_equal(nil, @comment_serializer.class._cache_key)
121
- end
122
-
123
- def test_cache_key_interpolation_with_updated_at_when_cache_key_is_not_defined_on_object
124
- uncached_author = UncachedAuthor.new(name: 'Joao M. D. Moura')
125
- uncached_author_serializer = AuthorSerializer.new(uncached_author)
126
-
127
- render_object_with_cache(uncached_author)
128
- key = "#{uncached_author_serializer.class._cache_key}/#{uncached_author_serializer.object.id}-#{uncached_author_serializer.object.updated_at.strftime('%Y%m%d%H%M%S%9N')}"
129
- key = "#{key}/#{adapter.cache_key}"
130
- assert_equal(uncached_author_serializer.attributes.to_json, cache_store.fetch(key).to_json)
131
- end
132
-
133
- def test_cache_key_expansion
134
- author = AuthorWithExpandableCacheElements.new(id: 10, name: 'hello')
135
- same_author = AuthorWithExpandableCacheElements.new(id: 10, name: 'hello')
136
- diff_author = AuthorWithExpandableCacheElements.new(id: 11, name: 'hello')
137
-
138
- author_serializer = AuthorSerializer.new(author)
139
- same_author_serializer = AuthorSerializer.new(same_author)
140
- diff_author_serializer = AuthorSerializer.new(diff_author)
141
- adapter = AuthorSerializer.serialization_adapter_instance
142
-
143
- assert_equal(author_serializer.cache_key(adapter), same_author_serializer.cache_key(adapter))
144
- refute_equal(author_serializer.cache_key(adapter), diff_author_serializer.cache_key(adapter))
145
- end
146
-
147
- def test_default_cache_key_fallback
148
- render_object_with_cache(@comment)
149
- key = "#{@comment.cache_key}/#{adapter.cache_key}"
150
- assert_equal(@comment_serializer.attributes.to_json, cache_store.fetch(key).to_json)
151
- end
152
-
153
- def test_error_is_raised_if_cache_key_is_not_defined_on_object_or_passed_as_cache_option
154
- article = Article.new(title: 'Must Read')
155
- e = assert_raises ActiveModel::Serializer::UndefinedCacheKey do
156
- render_object_with_cache(article)
157
- end
158
- assert_match(/ActiveModelSerializers::CacheTest::Article must define #cache_key, or the 'key:' option must be passed into 'ActiveModelSerializers::CacheTest::ArticleSerializer.cache'/, e.message)
159
- end
160
-
161
- def test_cache_options_definition
162
- assert_equal({ expires_in: 0.1, skip_digest: true }, @post_serializer.class._cache_options)
163
- assert_equal(nil, @blog_serializer.class._cache_options)
164
- assert_equal({ expires_in: 1.day, skip_digest: true }, @comment_serializer.class._cache_options)
165
- end
166
-
167
- def test_fragment_cache_definition
168
- assert_equal([:name, :slug], @role_serializer.class._cache_only)
169
- assert_equal([:content], @bio_serializer.class._cache_except)
170
- end
171
-
172
- def test_associations_separately_cache
173
- cache_store.clear
174
- assert_equal(nil, cache_store.fetch(@post.cache_key))
175
- assert_equal(nil, cache_store.fetch(@comment.cache_key))
176
-
177
- Timecop.freeze(Time.current) do
178
- render_object_with_cache(@post)
179
-
180
- key = "#{@post.cache_key}/#{adapter.cache_key}"
181
- assert_equal(@post_serializer.attributes, cache_store.fetch(key))
182
- key = "#{@comment.cache_key}/#{adapter.cache_key}"
183
- assert_equal(@comment_serializer.attributes, cache_store.fetch(key))
184
- end
185
- end
186
-
187
- def test_associations_cache_when_updated
188
- Timecop.freeze(Time.current) do
189
- # Generate a new Cache of Post object and each objects related to it.
190
- render_object_with_cache(@post)
191
-
192
- # Check if it cached the objects separately
193
- key = "#{@post.cache_key}/#{adapter.cache_key}"
194
- assert_equal(@post_serializer.attributes, cache_store.fetch(key))
195
- key = "#{@comment.cache_key}/#{adapter.cache_key}"
196
- assert_equal(@comment_serializer.attributes, cache_store.fetch(key))
197
-
198
- # Simulating update on comments relationship with Post
199
- new_comment = Comment.new(id: 2567, body: 'ZOMG A NEW COMMENT')
200
- new_comment_serializer = CommentSerializer.new(new_comment)
201
- @post.comments = [new_comment]
202
-
203
- # Ask for the serialized object
204
- render_object_with_cache(@post)
205
-
206
- # Check if the the new comment was cached
207
- key = "#{new_comment.cache_key}/#{adapter.cache_key}"
208
- assert_equal(new_comment_serializer.attributes, cache_store.fetch(key))
209
- key = "#{@post.cache_key}/#{adapter.cache_key}"
210
- assert_equal(@post_serializer.attributes, cache_store.fetch(key))
211
- end
212
- end
213
-
214
- def test_fragment_fetch_with_virtual_associations
215
- expected_result = {
216
- id: @location.id,
217
- lat: @location.lat,
218
- lng: @location.lng,
219
- address: 'Nowhere'
220
- }
221
-
222
- hash = render_object_with_cache(@location)
223
-
224
- assert_equal(hash, expected_result)
225
- key = "#{@location.cache_key}/#{adapter.cache_key}"
226
- assert_equal({ address: 'Nowhere' }, cache_store.fetch(key))
227
- end
228
-
229
- def test_fragment_cache_with_inheritance
230
- inherited = render_object_with_cache(@role, serializer: InheritedRoleSerializer)
231
- base = render_object_with_cache(@role)
232
-
233
- assert_includes(inherited.keys, :special_attribute)
234
- refute_includes(base.keys, :special_attribute)
235
- end
236
-
237
- def test_uses_adapter_in_cache_key
238
- render_object_with_cache(@post)
239
- key = "#{@post.cache_key}/#{adapter.class.to_s.demodulize.underscore}"
240
- assert_equal(@post_serializer.attributes, cache_store.fetch(key))
241
- end
242
-
243
- # Based on original failing test by @kevintyll
244
- # rubocop:disable Metrics/AbcSize
245
- def test_a_serializer_rendered_by_two_adapter_returns_differently_fetch_attributes
246
- Object.const_set(:Alert, Class.new(ActiveModelSerializers::Model) do
247
- attr_accessor :id, :status, :resource, :started_at, :ended_at, :updated_at, :created_at
248
- end)
249
- Object.const_set(:UncachedAlertSerializer, Class.new(ActiveModel::Serializer) do
250
- attributes :id, :status, :resource, :started_at, :ended_at, :updated_at, :created_at
251
- end)
252
- Object.const_set(:AlertSerializer, Class.new(UncachedAlertSerializer) do
253
- cache
254
- end)
255
-
256
- alert = Alert.new(
257
- id: 1,
258
- status: 'fail',
259
- resource: 'resource-1',
260
- started_at: Time.new(2016, 3, 31, 21, 36, 35, 0),
261
- ended_at: nil,
262
- updated_at: Time.new(2016, 3, 31, 21, 27, 35, 0),
263
- created_at: Time.new(2016, 3, 31, 21, 37, 35, 0)
264
- )
265
-
266
- expected_fetch_attributes = {
267
- id: 1,
268
- status: 'fail',
269
- resource: 'resource-1',
270
- started_at: alert.started_at,
271
- ended_at: nil,
272
- updated_at: alert.updated_at,
273
- created_at: alert.created_at
274
- }
275
- expected_cached_jsonapi_attributes = {
276
- id: '1',
277
- type: 'alerts',
278
- attributes: {
279
- status: 'fail',
280
- resource: 'resource-1',
281
- started_at: alert.started_at,
282
- ended_at: nil,
283
- updated_at: alert.updated_at,
284
- created_at: alert.created_at
285
- }
286
- }
287
-
288
- # Assert attributes are serialized correctly
289
- serializable_alert = serializable(alert, serializer: AlertSerializer, adapter: :attributes)
290
- attributes_serialization = serializable_alert.as_json
291
- assert_equal expected_fetch_attributes, alert.attributes
292
- assert_equal alert.attributes, attributes_serialization
293
- attributes_cache_key = serializable_alert.adapter.serializer.cache_key(serializable_alert.adapter)
294
- assert_equal attributes_serialization, cache_store.fetch(attributes_cache_key)
295
-
296
- serializable_alert = serializable(alert, serializer: AlertSerializer, adapter: :json_api)
297
- jsonapi_cache_key = serializable_alert.adapter.serializer.cache_key(serializable_alert.adapter)
298
- # Assert cache keys differ
299
- refute_equal attributes_cache_key, jsonapi_cache_key
300
- # Assert (cached) serializations differ
301
- jsonapi_serialization = serializable_alert.as_json
302
- assert_equal alert.status, jsonapi_serialization.fetch(:data).fetch(:attributes).fetch(:status)
303
- serializable_alert = serializable(alert, serializer: UncachedAlertSerializer, adapter: :json_api)
304
- assert_equal serializable_alert.as_json, jsonapi_serialization
305
-
306
- cached_serialization = cache_store.fetch(jsonapi_cache_key)
307
- assert_equal expected_cached_jsonapi_attributes, cached_serialization
308
- ensure
309
- Object.send(:remove_const, :Alert)
310
- Object.send(:remove_const, :AlertSerializer)
311
- Object.send(:remove_const, :UncachedAlertSerializer)
312
- end
313
- # rubocop:enable Metrics/AbcSize
314
-
315
- def test_uses_file_digest_in_cache_key
316
- render_object_with_cache(@blog)
317
- key = "#{@blog.cache_key}/#{adapter.cache_key}/#{::Model::FILE_DIGEST}"
318
- assert_equal(@blog_serializer.attributes, cache_store.fetch(key))
319
- end
320
-
321
- def test_cache_digest_definition
322
- assert_equal(::Model::FILE_DIGEST, @post_serializer.class._cache_digest)
323
- end
324
-
325
- def test_object_cache_keys
326
- serializable = ActiveModelSerializers::SerializableResource.new([@comment, @comment])
327
- include_directive = JSONAPI::IncludeDirective.new('*', allow_wildcard: true)
328
-
329
- actual = ActiveModel::Serializer.object_cache_keys(serializable.adapter.serializer, serializable.adapter, include_directive)
330
-
331
- assert_equal 3, actual.size
332
- assert actual.any? { |key| key == "comment/1/#{serializable.adapter.cache_key}" }
333
- assert actual.any? { |key| key =~ %r{post/post-\d+} }
334
- assert actual.any? { |key| key =~ %r{author/author-\d+} }
335
- end
336
-
337
- def test_fetch_attributes_from_cache
338
- serializers = ActiveModel::Serializer::CollectionSerializer.new([@comment, @comment])
339
-
340
- Timecop.freeze(Time.current) do
341
- render_object_with_cache(@comment)
342
-
343
- options = {}
344
- adapter_options = {}
345
- adapter_instance = ActiveModelSerializers::Adapter::Attributes.new(serializers, adapter_options)
346
- serializers.serializable_hash(adapter_options, options, adapter_instance)
347
- cached_attributes = adapter_options.fetch(:cached_attributes)
348
-
349
- include_directive = ActiveModelSerializers.default_include_directive
350
- manual_cached_attributes = ActiveModel::Serializer.cache_read_multi(serializers, adapter_instance, include_directive)
351
- assert_equal manual_cached_attributes, cached_attributes
352
-
353
- assert_equal cached_attributes["#{@comment.cache_key}/#{adapter_instance.cache_key}"], Comment.new(id: 1, body: 'ZOMG A COMMENT').attributes
354
- assert_equal cached_attributes["#{@comment.post.cache_key}/#{adapter_instance.cache_key}"], Post.new(id: 'post', title: 'New Post', body: 'Body').attributes
355
-
356
- writer = @comment.post.blog.writer
357
- writer_cache_key = writer.cache_key
358
- assert_equal cached_attributes["#{writer_cache_key}/#{adapter_instance.cache_key}"], Author.new(id: 'author', name: 'Joao M. D. Moura').attributes
359
- end
360
- end
361
-
362
- def test_cache_read_multi_with_fragment_cache_enabled
363
- post_serializer = Class.new(ActiveModel::Serializer) do
364
- cache except: [:body]
365
- end
366
-
367
- serializers = ActiveModel::Serializer::CollectionSerializer.new([@post, @post], serializer: post_serializer)
368
-
369
- Timecop.freeze(Time.current) do
370
- # Warming up.
371
- options = {}
372
- adapter_options = {}
373
- adapter_instance = ActiveModelSerializers::Adapter::Attributes.new(serializers, adapter_options)
374
- serializers.serializable_hash(adapter_options, options, adapter_instance)
375
-
376
- # Should find something with read_multi now
377
- adapter_options = {}
378
- serializers.serializable_hash(adapter_options, options, adapter_instance)
379
- cached_attributes = adapter_options.fetch(:cached_attributes)
380
-
381
- include_directive = ActiveModelSerializers.default_include_directive
382
- manual_cached_attributes = ActiveModel::Serializer.cache_read_multi(serializers, adapter_instance, include_directive)
383
-
384
- refute_equal 0, cached_attributes.size
385
- refute_equal 0, manual_cached_attributes.size
386
- assert_equal manual_cached_attributes, cached_attributes
387
- end
388
- end
389
-
390
- def test_serializer_file_path_on_nix
391
- path = '/Users/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb'
392
- caller_line = "#{path}:1:in `<top (required)>'"
393
- assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
394
- end
395
-
396
- def test_serializer_file_path_on_windows
397
- path = 'c:/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb'
398
- caller_line = "#{path}:1:in `<top (required)>'"
399
- assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
400
- end
401
-
402
- def test_serializer_file_path_with_space
403
- path = '/Users/git/ember js/ember-crm-backend/app/serializers/lead_serializer.rb'
404
- caller_line = "#{path}:1:in `<top (required)>'"
405
- assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
406
- end
407
-
408
- def test_serializer_file_path_with_submatch
409
- # The submatch in the path ensures we're using a correctly greedy regexp.
410
- path = '/Users/git/ember js/ember:123:in x/app/serializers/lead_serializer.rb'
411
- caller_line = "#{path}:1:in `<top (required)>'"
412
- assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
413
- end
414
-
415
- def test_digest_caller_file
416
- contents = "puts 'AMS rocks'!"
417
- dir = Dir.mktmpdir('space char')
418
- file = Tempfile.new('some_ruby.rb', dir)
419
- file.write(contents)
420
- path = file.path
421
- caller_line = "#{path}:1:in `<top (required)>'"
422
- file.close
423
- assert_equal ActiveModel::Serializer.digest_caller_file(caller_line), Digest::MD5.hexdigest(contents)
424
- ensure
425
- file.unlink
426
- FileUtils.remove_entry dir
427
- end
428
-
429
- def test_warn_on_serializer_not_defined_in_file
430
- called = false
431
- serializer = Class.new(ActiveModel::Serializer)
432
- assert_output(nil, /_cache_digest/) do
433
- serializer.digest_caller_file('')
434
- called = true
435
- end
436
- assert called
437
- end
438
-
439
- def test_cached_false_without_cache_store
440
- cached_serializer = build_cached_serializer do |serializer|
441
- serializer._cache = nil
442
- end
443
- refute cached_serializer.class.cache_enabled?
444
- end
445
-
446
- def test_cached_true_with_cache_store_and_without_cache_only_and_cache_except
447
- cached_serializer = build_cached_serializer do |serializer|
448
- serializer._cache = Object
449
- end
450
- assert cached_serializer.class.cache_enabled?
451
- end
452
-
453
- def test_cached_false_with_cache_store_and_with_cache_only
454
- cached_serializer = build_cached_serializer do |serializer|
455
- serializer._cache = Object
456
- serializer._cache_only = [:name]
457
- end
458
- refute cached_serializer.class.cache_enabled?
459
- end
460
-
461
- def test_cached_false_with_cache_store_and_with_cache_except
462
- cached_serializer = build_cached_serializer do |serializer|
463
- serializer._cache = Object
464
- serializer._cache_except = [:content]
465
- end
466
- refute cached_serializer.class.cache_enabled?
467
- end
468
-
469
- def test_fragment_cached_false_without_cache_store
470
- cached_serializer = build_cached_serializer do |serializer|
471
- serializer._cache = nil
472
- serializer._cache_only = [:name]
473
- end
474
- refute cached_serializer.class.fragment_cache_enabled?
475
- end
476
-
477
- def test_fragment_cached_true_with_cache_store_and_cache_only
478
- cached_serializer = build_cached_serializer do |serializer|
479
- serializer._cache = Object
480
- serializer._cache_only = [:name]
481
- end
482
- assert cached_serializer.class.fragment_cache_enabled?
483
- end
484
-
485
- def test_fragment_cached_true_with_cache_store_and_cache_except
486
- cached_serializer = build_cached_serializer do |serializer|
487
- serializer._cache = Object
488
- serializer._cache_except = [:content]
489
- end
490
- assert cached_serializer.class.fragment_cache_enabled?
491
- end
492
-
493
- def test_fragment_cached_false_with_cache_store_and_cache_except_and_cache_only
494
- cached_serializer = build_cached_serializer do |serializer|
495
- serializer._cache = Object
496
- serializer._cache_except = [:content]
497
- serializer._cache_only = [:name]
498
- end
499
- refute cached_serializer.class.fragment_cache_enabled?
500
- end
501
-
502
- def test_fragment_fetch_with_virtual_attributes
503
- author = Author.new(name: 'Joao M. D. Moura')
504
- role = Role.new(name: 'Great Author', description: nil)
505
- role.author = [author]
506
- role_serializer = RoleSerializer.new(role)
507
- adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(role_serializer)
508
- expected_result = {
509
- id: role.id,
510
- description: role.description,
511
- slug: "#{role.name}-#{role.id}",
512
- name: role.name
513
- }
514
- cache_store.clear
515
-
516
- role_hash = role_serializer.fetch_attributes_fragment(adapter_instance)
517
- assert_equal(role_hash, expected_result)
518
-
519
- role.attributes[:id] = 'this has been updated'
520
- role.name = 'this was cached'
521
-
522
- role_hash = role_serializer.fetch_attributes_fragment(adapter_instance)
523
- assert_equal(expected_result.merge(id: role.id), role_hash)
524
- end
525
-
526
- def test_fragment_fetch_with_except
527
- adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(@bio_serializer)
528
- expected_result = {
529
- id: @bio.id,
530
- rating: nil,
531
- content: @bio.content
532
- }
533
- cache_store.clear
534
-
535
- bio_hash = @bio_serializer.fetch_attributes_fragment(adapter_instance)
536
- assert_equal(expected_result, bio_hash)
537
-
538
- @bio.content = 'this has been updated'
539
- @bio.rating = 'this was cached'
540
-
541
- bio_hash = @bio_serializer.fetch_attributes_fragment(adapter_instance)
542
- assert_equal(expected_result.merge(content: @bio.content), bio_hash)
543
- end
544
-
545
- def test_fragment_fetch_with_namespaced_object
546
- @spam = Spam::UnrelatedLink.new(id: 'spam-id-1')
547
- @spam_serializer = Spam::UnrelatedLinkSerializer.new(@spam)
548
- adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(@spam_serializer)
549
- @spam_hash = @spam_serializer.fetch_attributes_fragment(adapter_instance)
550
- expected_result = {
551
- id: @spam.id
552
- }
553
- assert_equal(@spam_hash, expected_result)
554
- end
555
-
556
- private
557
-
558
- def cache_store
559
- ActiveModelSerializers.config.cache_store
560
- end
561
-
562
- def build_cached_serializer
563
- serializer = Class.new(ActiveModel::Serializer)
564
- serializer._cache_key = nil
565
- serializer._cache_options = nil
566
- yield serializer if block_given?
567
- serializer.new(Object)
568
- end
569
-
570
- def render_object_with_cache(obj, options = {})
571
- @serializable_resource = serializable(obj, options)
572
- @serializable_resource.serializable_hash
573
- end
574
-
575
- def adapter
576
- @serializable_resource.adapter
577
- end
578
- end
579
- end