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