active_model_serializers_custom 0.10.90
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 +109 -0
- data/.simplecov +110 -0
- data/.travis.yml +63 -0
- data/CHANGELOG.md +727 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +105 -0
- data/Gemfile +74 -0
- data/MIT-LICENSE +22 -0
- data/README.md +305 -0
- data/Rakefile +76 -0
- data/active_model_serializers.gemspec +64 -0
- data/appveyor.yml +28 -0
- data/bin/bench +171 -0
- data/bin/bench_regression +316 -0
- data/bin/rubocop +38 -0
- data/bin/serve_benchmark +39 -0
- data/docs/README.md +41 -0
- data/docs/STYLE.md +58 -0
- data/docs/general/adapters.md +269 -0
- data/docs/general/caching.md +58 -0
- data/docs/general/configuration_options.md +185 -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 +21 -0
- data/docs/general/rendering.md +293 -0
- data/docs/general/serializers.md +495 -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 +140 -0
- data/docs/howto/add_root_key.md +62 -0
- data/docs/howto/grape_integration.md +42 -0
- data/docs/howto/outside_controller_use.md +66 -0
- data/docs/howto/passing_arbitrary_options.md +27 -0
- data/docs/howto/serialize_poro.md +73 -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 +147 -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 +76 -0
- data/lib/active_model/serializable_resource.rb +13 -0
- data/lib/active_model/serializer.rb +418 -0
- data/lib/active_model/serializer/adapter.rb +26 -0
- data/lib/active_model/serializer/adapter/attributes.rb +17 -0
- data/lib/active_model/serializer/adapter/base.rb +20 -0
- data/lib/active_model/serializer/adapter/json.rb +17 -0
- data/lib/active_model/serializer/adapter/json_api.rb +17 -0
- data/lib/active_model/serializer/adapter/null.rb +17 -0
- data/lib/active_model/serializer/array_serializer.rb +14 -0
- data/lib/active_model/serializer/association.rb +91 -0
- data/lib/active_model/serializer/attribute.rb +27 -0
- data/lib/active_model/serializer/belongs_to_reflection.rb +13 -0
- data/lib/active_model/serializer/collection_serializer.rb +90 -0
- data/lib/active_model/serializer/concerns/caching.rb +304 -0
- data/lib/active_model/serializer/error_serializer.rb +16 -0
- data/lib/active_model/serializer/errors_serializer.rb +34 -0
- data/lib/active_model/serializer/field.rb +92 -0
- data/lib/active_model/serializer/fieldset.rb +33 -0
- data/lib/active_model/serializer/has_many_reflection.rb +12 -0
- data/lib/active_model/serializer/has_one_reflection.rb +9 -0
- data/lib/active_model/serializer/lazy_association.rb +99 -0
- data/lib/active_model/serializer/link.rb +23 -0
- data/lib/active_model/serializer/lint.rb +152 -0
- data/lib/active_model/serializer/null.rb +19 -0
- data/lib/active_model/serializer/reflection.rb +212 -0
- data/lib/active_model/serializer/version.rb +7 -0
- data/lib/active_model_serializers.rb +63 -0
- data/lib/active_model_serializers/adapter.rb +100 -0
- data/lib/active_model_serializers/adapter/attributes.rb +15 -0
- data/lib/active_model_serializers/adapter/base.rb +85 -0
- data/lib/active_model_serializers/adapter/json.rb +23 -0
- data/lib/active_model_serializers/adapter/json_api.rb +535 -0
- data/lib/active_model_serializers/adapter/json_api/deserialization.rb +215 -0
- data/lib/active_model_serializers/adapter/json_api/error.rb +98 -0
- data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +51 -0
- data/lib/active_model_serializers/adapter/json_api/link.rb +85 -0
- data/lib/active_model_serializers/adapter/json_api/meta.rb +39 -0
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +90 -0
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +106 -0
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +68 -0
- data/lib/active_model_serializers/adapter/null.rb +11 -0
- data/lib/active_model_serializers/callbacks.rb +57 -0
- data/lib/active_model_serializers/deprecate.rb +56 -0
- data/lib/active_model_serializers/deserialization.rb +17 -0
- data/lib/active_model_serializers/json_pointer.rb +16 -0
- data/lib/active_model_serializers/logging.rb +124 -0
- data/lib/active_model_serializers/lookup_chain.rb +82 -0
- data/lib/active_model_serializers/model.rb +132 -0
- data/lib/active_model_serializers/railtie.rb +52 -0
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +80 -0
- data/lib/active_model_serializers/serializable_resource.rb +84 -0
- data/lib/active_model_serializers/serialization_context.rb +41 -0
- data/lib/active_model_serializers/test.rb +9 -0
- data/lib/active_model_serializers/test/schema.rb +140 -0
- data/lib/active_model_serializers/test/serializer.rb +127 -0
- data/lib/generators/rails/USAGE +6 -0
- data/lib/generators/rails/resource_override.rb +12 -0
- data/lib/generators/rails/serializer_generator.rb +38 -0
- data/lib/generators/rails/templates/serializer.rb.erb +8 -0
- data/lib/grape/active_model_serializers.rb +18 -0
- data/lib/grape/formatters/active_model_serializers.rb +34 -0
- data/lib/grape/helpers/active_model_serializers.rb +19 -0
- data/lib/tasks/rubocop.rake +55 -0
- data/test/action_controller/adapter_selector_test.rb +64 -0
- data/test/action_controller/explicit_serializer_test.rb +137 -0
- data/test/action_controller/json/include_test.rb +248 -0
- data/test/action_controller/json_api/deserialization_test.rb +114 -0
- data/test/action_controller/json_api/errors_test.rb +42 -0
- data/test/action_controller/json_api/fields_test.rb +68 -0
- data/test/action_controller/json_api/linked_test.rb +204 -0
- data/test/action_controller/json_api/pagination_test.rb +126 -0
- data/test/action_controller/json_api/transform_test.rb +191 -0
- data/test/action_controller/lookup_proc_test.rb +51 -0
- data/test/action_controller/namespace_lookup_test.rb +239 -0
- data/test/action_controller/serialization_scope_name_test.rb +237 -0
- data/test/action_controller/serialization_test.rb +480 -0
- data/test/active_model_serializers/adapter_for_test.rb +210 -0
- data/test/active_model_serializers/json_pointer_test.rb +24 -0
- data/test/active_model_serializers/logging_test.rb +79 -0
- data/test/active_model_serializers/model_test.rb +144 -0
- data/test/active_model_serializers/railtie_test_isolated.rb +70 -0
- data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +163 -0
- data/test/active_model_serializers/serialization_context_test_isolated.rb +73 -0
- data/test/active_model_serializers/test/schema_test.rb +133 -0
- data/test/active_model_serializers/test/serializer_test.rb +64 -0
- data/test/active_record_test.rb +11 -0
- data/test/adapter/attributes_test.rb +42 -0
- data/test/adapter/deprecation_test.rb +102 -0
- data/test/adapter/json/belongs_to_test.rb +47 -0
- data/test/adapter/json/collection_test.rb +106 -0
- data/test/adapter/json/has_many_test.rb +55 -0
- data/test/adapter/json/transform_test.rb +95 -0
- data/test/adapter/json_api/belongs_to_test.rb +157 -0
- data/test/adapter/json_api/collection_test.rb +98 -0
- data/test/adapter/json_api/errors_test.rb +78 -0
- data/test/adapter/json_api/fields_test.rb +98 -0
- data/test/adapter/json_api/has_many_explicit_serializer_test.rb +98 -0
- data/test/adapter/json_api/has_many_test.rb +175 -0
- data/test/adapter/json_api/has_one_test.rb +82 -0
- data/test/adapter/json_api/include_data_if_sideloaded_test.rb +215 -0
- data/test/adapter/json_api/json_api_test.rb +35 -0
- data/test/adapter/json_api/linked_test.rb +415 -0
- data/test/adapter/json_api/links_test.rb +112 -0
- data/test/adapter/json_api/pagination_links_test.rb +208 -0
- data/test/adapter/json_api/parse_test.rb +139 -0
- data/test/adapter/json_api/relationship_test.rb +399 -0
- data/test/adapter/json_api/resource_meta_test.rb +102 -0
- data/test/adapter/json_api/toplevel_jsonapi_test.rb +84 -0
- data/test/adapter/json_api/transform_test.rb +514 -0
- data/test/adapter/json_api/type_test.rb +195 -0
- data/test/adapter/json_test.rb +48 -0
- data/test/adapter/null_test.rb +24 -0
- data/test/adapter/polymorphic_test.rb +220 -0
- data/test/adapter_test.rb +69 -0
- data/test/array_serializer_test.rb +24 -0
- data/test/benchmark/app.rb +67 -0
- data/test/benchmark/benchmarking_support.rb +69 -0
- data/test/benchmark/bm_active_record.rb +83 -0
- data/test/benchmark/bm_adapter.rb +40 -0
- data/test/benchmark/bm_caching.rb +121 -0
- data/test/benchmark/bm_lookup_chain.rb +85 -0
- data/test/benchmark/bm_transform.rb +47 -0
- data/test/benchmark/config.ru +3 -0
- data/test/benchmark/controllers.rb +85 -0
- data/test/benchmark/fixtures.rb +221 -0
- data/test/cache_test.rb +717 -0
- data/test/collection_serializer_test.rb +129 -0
- data/test/fixtures/active_record.rb +115 -0
- data/test/fixtures/poro.rb +227 -0
- data/test/generators/scaffold_controller_generator_test.rb +26 -0
- data/test/generators/serializer_generator_test.rb +77 -0
- data/test/grape_test.rb +198 -0
- data/test/lint_test.rb +51 -0
- data/test/logger_test.rb +22 -0
- data/test/poro_test.rb +11 -0
- data/test/serializable_resource_test.rb +81 -0
- data/test/serializers/association_macros_test.rb +39 -0
- data/test/serializers/associations_test.rb +520 -0
- data/test/serializers/attribute_test.rb +155 -0
- data/test/serializers/attributes_test.rb +54 -0
- data/test/serializers/caching_configuration_test_isolated.rb +172 -0
- data/test/serializers/configuration_test.rb +34 -0
- data/test/serializers/fieldset_test.rb +16 -0
- data/test/serializers/meta_test.rb +204 -0
- data/test/serializers/options_test.rb +34 -0
- data/test/serializers/read_attribute_for_serialization_test.rb +81 -0
- data/test/serializers/reflection_test.rb +481 -0
- data/test/serializers/root_test.rb +23 -0
- data/test/serializers/serialization_test.rb +57 -0
- data/test/serializers/serializer_for_test.rb +138 -0
- data/test/serializers/serializer_for_with_namespace_test.rb +90 -0
- data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
- data/test/support/isolated_unit.rb +86 -0
- data/test/support/rails5_shims.rb +55 -0
- data/test/support/rails_app.rb +40 -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 +81 -0
- data/test/test_helper.rb +72 -0
- metadata +622 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative './benchmarking_support'
|
|
4
|
+
require_relative './app'
|
|
5
|
+
|
|
6
|
+
time = 10
|
|
7
|
+
disable_gc = true
|
|
8
|
+
ActiveModelSerializers.config.key_transform = :unaltered
|
|
9
|
+
|
|
10
|
+
module AmsBench
|
|
11
|
+
module Api
|
|
12
|
+
module V1
|
|
13
|
+
class PrimaryResourceSerializer < ActiveModel::Serializer
|
|
14
|
+
attributes :title, :body
|
|
15
|
+
|
|
16
|
+
has_many :has_many_relationships
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class HasManyRelationshipSerializer < ActiveModel::Serializer
|
|
20
|
+
attribute :body
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
class PrimaryResourceSerializer < ActiveModel::Serializer
|
|
25
|
+
attributes :title, :body
|
|
26
|
+
|
|
27
|
+
has_many :has_many_relationships
|
|
28
|
+
|
|
29
|
+
class HasManyRelationshipSerializer < ActiveModel::Serializer
|
|
30
|
+
attribute :body
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
resource = PrimaryResource.new(
|
|
36
|
+
id: 1,
|
|
37
|
+
title: 'title',
|
|
38
|
+
body: 'body',
|
|
39
|
+
has_many_relationships: [
|
|
40
|
+
HasManyRelationship.new(id: 1, body: 'body1'),
|
|
41
|
+
HasManyRelationship.new(id: 2, body: 'body1')
|
|
42
|
+
]
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
serialization = lambda do
|
|
46
|
+
ActiveModelSerializers::SerializableResource.new(resource, serializer: AmsBench::PrimaryResourceSerializer).as_json
|
|
47
|
+
ActiveModelSerializers::SerializableResource.new(resource, namespace: AmsBench::Api::V1).as_json
|
|
48
|
+
ActiveModelSerializers::SerializableResource.new(resource).as_json
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def clear_cache
|
|
52
|
+
AmsBench::PrimaryResourceSerializer.serializers_cache.clear
|
|
53
|
+
AmsBench::Api::V1::PrimaryResourceSerializer.serializers_cache.clear
|
|
54
|
+
ActiveModel::Serializer.serializers_cache.clear
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
configurable = lambda do
|
|
58
|
+
clear_cache
|
|
59
|
+
Benchmark.ams('Configurable Lookup Chain', time: time, disable_gc: disable_gc, &serialization)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
old = lambda do
|
|
63
|
+
clear_cache
|
|
64
|
+
module ActiveModel
|
|
65
|
+
class Serializer
|
|
66
|
+
def self.serializer_lookup_chain_for(klass, namespace = nil)
|
|
67
|
+
chain = []
|
|
68
|
+
|
|
69
|
+
resource_class_name = klass.name.demodulize
|
|
70
|
+
resource_namespace = klass.name.deconstantize
|
|
71
|
+
serializer_class_name = "#{resource_class_name}Serializer"
|
|
72
|
+
|
|
73
|
+
chain.push("#{namespace}::#{serializer_class_name}") if namespace
|
|
74
|
+
chain.push("#{name}::#{serializer_class_name}") if self != ActiveModel::Serializer
|
|
75
|
+
chain.push("#{resource_namespace}::#{serializer_class_name}")
|
|
76
|
+
chain
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
Benchmark.ams('Old Lookup Chain (v0.10)', time: time, disable_gc: disable_gc, &serialization)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
configurable.call
|
|
85
|
+
old.call
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative './benchmarking_support'
|
|
4
|
+
require_relative './app'
|
|
5
|
+
|
|
6
|
+
time = 10
|
|
7
|
+
disable_gc = true
|
|
8
|
+
ActiveModelSerializers.config.key_transform = :unaltered
|
|
9
|
+
has_many_relationships = (0..50).map do |i|
|
|
10
|
+
HasManyRelationship.new(id: i, body: 'ZOMG A HAS MANY RELATIONSHIP')
|
|
11
|
+
end
|
|
12
|
+
has_one_relationship = HasOneRelationship.new(
|
|
13
|
+
id: 42,
|
|
14
|
+
first_name: 'Joao',
|
|
15
|
+
last_name: 'Moura'
|
|
16
|
+
)
|
|
17
|
+
primary_resource = PrimaryResource.new(
|
|
18
|
+
id: 1337,
|
|
19
|
+
title: 'New PrimaryResource',
|
|
20
|
+
virtual_attribute: nil,
|
|
21
|
+
body: 'Body',
|
|
22
|
+
has_many_relationships: has_many_relationships,
|
|
23
|
+
has_one_relationship: has_one_relationship
|
|
24
|
+
)
|
|
25
|
+
serializer = PrimaryResourceSerializer.new(primary_resource)
|
|
26
|
+
adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer)
|
|
27
|
+
serialization = adapter.as_json
|
|
28
|
+
|
|
29
|
+
Benchmark.ams('camel', time: time, disable_gc: disable_gc) do
|
|
30
|
+
CaseTransform.camel(serialization)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Benchmark.ams('camel_lower', time: time, disable_gc: disable_gc) do
|
|
34
|
+
CaseTransform.camel_lower(serialization)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
Benchmark.ams('dash', time: time, disable_gc: disable_gc) do
|
|
38
|
+
CaseTransform.dash(serialization)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
Benchmark.ams('unaltered', time: time, disable_gc: disable_gc) do
|
|
42
|
+
CaseTransform.unaltered(serialization)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Benchmark.ams('underscore', time: time, disable_gc: disable_gc) do
|
|
46
|
+
CaseTransform.underscore(serialization)
|
|
47
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class PrimaryResourceController < ActionController::Base
|
|
4
|
+
PRIMARY_RESOURCE =
|
|
5
|
+
begin
|
|
6
|
+
if ENV['BENCH_STRESS']
|
|
7
|
+
has_many_relationships = (0..50).map do |i|
|
|
8
|
+
HasManyRelationship.new(id: i, body: 'ZOMG A HAS MANY RELATIONSHIP')
|
|
9
|
+
end
|
|
10
|
+
else
|
|
11
|
+
has_many_relationships = [HasManyRelationship.new(id: 1, body: 'ZOMG A HAS MANY RELATIONSHIP')]
|
|
12
|
+
end
|
|
13
|
+
has_one_relationship = HasOneRelationship.new(id: 42, first_name: 'Joao', last_name: 'Moura')
|
|
14
|
+
PrimaryResource.new(id: 1337, title: 'New PrimaryResource', virtual_attribute: nil, body: 'Body', has_many_relationships: has_many_relationships, has_one_relationship: has_one_relationship)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render_with_caching_serializer
|
|
18
|
+
toggle_cache_status
|
|
19
|
+
render json: PRIMARY_RESOURCE, serializer: CachingPrimaryResourceSerializer, adapter: :json, meta: { caching: perform_caching }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render_with_fragment_caching_serializer
|
|
23
|
+
toggle_cache_status
|
|
24
|
+
render json: PRIMARY_RESOURCE, serializer: FragmentCachingPrimaryResourceSerializer, adapter: :json, meta: { caching: perform_caching }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def render_with_non_caching_serializer
|
|
28
|
+
toggle_cache_status
|
|
29
|
+
render json: PRIMARY_RESOURCE, adapter: :json, meta: { caching: perform_caching }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def render_cache_status
|
|
33
|
+
toggle_cache_status
|
|
34
|
+
# Uncomment to debug
|
|
35
|
+
# STDERR.puts cache_store.class
|
|
36
|
+
# STDERR.puts cache_dependencies
|
|
37
|
+
# ActiveSupport::Cache::Store.logger.debug [ActiveModelSerializers.config.cache_store, ActiveModelSerializers.config.perform_caching, CachingPrimaryResourceSerializer._cache, perform_caching, params].inspect
|
|
38
|
+
render json: { caching: perform_caching, meta: { cache_log: cache_messages, cache_status: cache_status } }.to_json
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def clear
|
|
42
|
+
ActionController::Base.cache_store.clear
|
|
43
|
+
# Test caching is on
|
|
44
|
+
# Uncomment to turn on logger; possible performance issue
|
|
45
|
+
# logger = BenchmarkLogger.new
|
|
46
|
+
# ActiveSupport::Cache::Store.logger = logger # seems to be the best way
|
|
47
|
+
#
|
|
48
|
+
# the below is used in some rails tests but isn't available/working in all versions, so far as I can tell
|
|
49
|
+
# https://github.com/rails/rails/pull/15943
|
|
50
|
+
# ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args|
|
|
51
|
+
# logger.debug ActiveSupport::Notifications::Event.new(*args)
|
|
52
|
+
# end
|
|
53
|
+
render json: 'ok'.to_json
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def cache_status
|
|
59
|
+
{
|
|
60
|
+
controller: perform_caching,
|
|
61
|
+
app: Rails.configuration.action_controller.perform_caching,
|
|
62
|
+
serializers: Rails.configuration.serializers.each_with_object({}) { |serializer, data| data[serializer.name] = serializer._cache.present? }
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def cache_messages
|
|
67
|
+
ActiveSupport::Cache::Store.logger.is_a?(BenchmarkLogger) && ActiveSupport::Cache::Store.logger.messages.split("\n")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def toggle_cache_status
|
|
71
|
+
case params[:on]
|
|
72
|
+
when 'on'.freeze then self.perform_caching = true
|
|
73
|
+
when 'off'.freeze then self.perform_caching = false
|
|
74
|
+
else nil # no-op
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
Rails.application.routes.draw do
|
|
80
|
+
get '/status(/:on)' => 'primary_resource#render_cache_status'
|
|
81
|
+
get '/clear' => 'primary_resource#clear'
|
|
82
|
+
get '/caching(/:on)' => 'primary_resource#render_with_caching_serializer'
|
|
83
|
+
get '/fragment_caching(/:on)' => 'primary_resource#render_with_fragment_caching_serializer'
|
|
84
|
+
get '/non_caching(/:on)' => 'primary_resource#render_with_non_caching_serializer'
|
|
85
|
+
end
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Rails.configuration.serializers = []
|
|
4
|
+
class HasOneRelationshipSerializer < ActiveModel::Serializer
|
|
5
|
+
attributes :id, :first_name, :last_name
|
|
6
|
+
|
|
7
|
+
has_many :primary_resources, embed: :ids
|
|
8
|
+
has_one :bio
|
|
9
|
+
end
|
|
10
|
+
Rails.configuration.serializers << HasOneRelationshipSerializer
|
|
11
|
+
|
|
12
|
+
class VirtualAttributeSerializer < ActiveModel::Serializer
|
|
13
|
+
attributes :id, :name
|
|
14
|
+
end
|
|
15
|
+
Rails.configuration.serializers << VirtualAttributeSerializer
|
|
16
|
+
|
|
17
|
+
class HasManyRelationshipSerializer < ActiveModel::Serializer
|
|
18
|
+
attributes :id, :body
|
|
19
|
+
|
|
20
|
+
belongs_to :primary_resource
|
|
21
|
+
belongs_to :has_one_relationship
|
|
22
|
+
end
|
|
23
|
+
Rails.configuration.serializers << HasManyRelationshipSerializer
|
|
24
|
+
|
|
25
|
+
class PrimaryResourceSerializer < ActiveModel::Serializer
|
|
26
|
+
attributes :id, :title, :body
|
|
27
|
+
|
|
28
|
+
has_many :has_many_relationships, serializer: HasManyRelationshipSerializer
|
|
29
|
+
belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer
|
|
30
|
+
belongs_to :has_one_relationship, serializer: HasOneRelationshipSerializer
|
|
31
|
+
|
|
32
|
+
link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' }
|
|
33
|
+
|
|
34
|
+
meta do
|
|
35
|
+
{
|
|
36
|
+
rating: 5,
|
|
37
|
+
favorite_count: 10
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def virtual_attribute
|
|
42
|
+
VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute')
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
Rails.configuration.serializers << PrimaryResourceSerializer
|
|
46
|
+
|
|
47
|
+
class CachingHasOneRelationshipSerializer < HasOneRelationshipSerializer
|
|
48
|
+
cache key: 'writer', skip_digest: true
|
|
49
|
+
end
|
|
50
|
+
Rails.configuration.serializers << CachingHasOneRelationshipSerializer
|
|
51
|
+
|
|
52
|
+
class CachingHasManyRelationshipSerializer < HasManyRelationshipSerializer
|
|
53
|
+
cache expires_in: 1.day, skip_digest: true
|
|
54
|
+
end
|
|
55
|
+
Rails.configuration.serializers << CachingHasManyRelationshipSerializer
|
|
56
|
+
|
|
57
|
+
# see https://github.com/rails-api/active_model_serializers/pull/1690/commits/68715b8f99bc29677e8a47bb3f305f23c077024b#r60344532
|
|
58
|
+
class CachingPrimaryResourceSerializer < ActiveModel::Serializer
|
|
59
|
+
cache key: 'primary_resource', expires_in: 0.1, skip_digest: true
|
|
60
|
+
|
|
61
|
+
attributes :id, :title, :body
|
|
62
|
+
|
|
63
|
+
belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer
|
|
64
|
+
belongs_to :has_one_relationship, serializer: CachingHasOneRelationshipSerializer
|
|
65
|
+
has_many :has_many_relationships, serializer: CachingHasManyRelationshipSerializer
|
|
66
|
+
|
|
67
|
+
link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' }
|
|
68
|
+
|
|
69
|
+
meta do
|
|
70
|
+
{
|
|
71
|
+
rating: 5,
|
|
72
|
+
favorite_count: 10
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def virtual_attribute
|
|
77
|
+
VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute')
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
Rails.configuration.serializers << CachingPrimaryResourceSerializer
|
|
81
|
+
|
|
82
|
+
class FragmentCachingHasOneRelationshipSerializer < HasOneRelationshipSerializer
|
|
83
|
+
cache key: 'writer', only: [:first_name, :last_name], skip_digest: true
|
|
84
|
+
end
|
|
85
|
+
Rails.configuration.serializers << FragmentCachingHasOneRelationshipSerializer
|
|
86
|
+
|
|
87
|
+
class FragmentCachingHasManyRelationshipSerializer < HasManyRelationshipSerializer
|
|
88
|
+
cache expires_in: 1.day, except: [:body], skip_digest: true
|
|
89
|
+
end
|
|
90
|
+
Rails.configuration.serializers << CachingHasManyRelationshipSerializer
|
|
91
|
+
|
|
92
|
+
# see https://github.com/rails-api/active_model_serializers/pull/1690/commits/68715b8f99bc29677e8a47bb3f305f23c077024b#r60344532
|
|
93
|
+
class FragmentCachingPrimaryResourceSerializer < ActiveModel::Serializer
|
|
94
|
+
cache key: 'primary_resource', expires_in: 0.1, skip_digest: true
|
|
95
|
+
|
|
96
|
+
attributes :id, :title, :body
|
|
97
|
+
|
|
98
|
+
belongs_to :virtual_attribute, serializer: VirtualAttributeSerializer
|
|
99
|
+
belongs_to :has_one_relationship, serializer: FragmentCachingHasOneRelationshipSerializer
|
|
100
|
+
has_many :has_many_relationships, serializer: FragmentCachingHasManyRelationshipSerializer
|
|
101
|
+
|
|
102
|
+
link(:primary_resource_has_one_relationships) { 'https://example.com/primary_resource_has_one_relationships' }
|
|
103
|
+
|
|
104
|
+
meta do
|
|
105
|
+
{
|
|
106
|
+
rating: 5,
|
|
107
|
+
favorite_count: 10
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def virtual_attribute
|
|
112
|
+
VirtualAttribute.new(id: 999, name: 'Free-Range Virtual Attribute')
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
Rails.configuration.serializers << FragmentCachingPrimaryResourceSerializer
|
|
116
|
+
|
|
117
|
+
if ENV['ENABLE_ACTIVE_RECORD'] == 'true'
|
|
118
|
+
require 'active_record'
|
|
119
|
+
|
|
120
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
|
121
|
+
ActiveRecord::Schema.define do
|
|
122
|
+
self.verbose = false
|
|
123
|
+
|
|
124
|
+
create_table :virtual_attributes, force: true do |t|
|
|
125
|
+
t.string :name
|
|
126
|
+
t.timestamps null: false
|
|
127
|
+
end
|
|
128
|
+
create_table :has_one_relationships, force: true do |t|
|
|
129
|
+
t.string :first_name
|
|
130
|
+
t.string :last_name
|
|
131
|
+
t.timestamps null: false
|
|
132
|
+
end
|
|
133
|
+
create_table :primary_resources, force: true do |t|
|
|
134
|
+
t.string :title
|
|
135
|
+
t.text :body
|
|
136
|
+
t.references :has_one_relationship
|
|
137
|
+
t.references :virtual_attribute
|
|
138
|
+
t.timestamps null: false
|
|
139
|
+
end
|
|
140
|
+
create_table :has_many_relationships, force: true do |t|
|
|
141
|
+
t.text :body
|
|
142
|
+
t.references :has_one_relationship
|
|
143
|
+
t.references :primary_resource
|
|
144
|
+
t.timestamps null: false
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
class HasManyRelationship < ActiveRecord::Base
|
|
149
|
+
belongs_to :has_one_relationship
|
|
150
|
+
belongs_to :primary_resource
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class HasOneRelationship < ActiveRecord::Base
|
|
154
|
+
has_many :primary_resources
|
|
155
|
+
has_many :has_many_relationships
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
class PrimaryResource < ActiveRecord::Base
|
|
159
|
+
has_many :has_many_relationships
|
|
160
|
+
belongs_to :has_one_relationship
|
|
161
|
+
belongs_to :virtual_attribute
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
class VirtualAttribute < ActiveRecord::Base
|
|
165
|
+
has_many :primary_resources
|
|
166
|
+
end
|
|
167
|
+
else
|
|
168
|
+
# ActiveModelSerializers::Model is a convenient
|
|
169
|
+
# serializable class to inherit from when making
|
|
170
|
+
# serializable non-activerecord objects.
|
|
171
|
+
class BenchmarkModel
|
|
172
|
+
include ActiveModel::Model
|
|
173
|
+
include ActiveModel::Serializers::JSON
|
|
174
|
+
|
|
175
|
+
attr_reader :attributes
|
|
176
|
+
|
|
177
|
+
def initialize(attributes = {})
|
|
178
|
+
@attributes = attributes
|
|
179
|
+
super
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Defaults to the downcased model name.
|
|
183
|
+
def id
|
|
184
|
+
attributes.fetch(:id) { self.class.name.downcase }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Defaults to the downcased model name and updated_at
|
|
188
|
+
def cache_key
|
|
189
|
+
attributes.fetch(:cache_key) { "#{self.class.name.downcase}/#{id}" }
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Defaults to the time the serializer file was modified.
|
|
193
|
+
def updated_at
|
|
194
|
+
@updated_at ||= attributes.fetch(:updated_at) { File.mtime(__FILE__) }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def read_attribute_for_serialization(key)
|
|
198
|
+
if key == :id || key == 'id'
|
|
199
|
+
attributes.fetch(key) { id }
|
|
200
|
+
else
|
|
201
|
+
attributes[key]
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
class HasManyRelationship < BenchmarkModel
|
|
207
|
+
attr_accessor :id, :body
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
class HasOneRelationship < BenchmarkModel
|
|
211
|
+
attr_accessor :id, :first_name, :last_name, :primary_resources
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
class PrimaryResource < BenchmarkModel
|
|
215
|
+
attr_accessor :id, :title, :body, :has_many_relationships, :virtual_attribute, :has_one_relationship
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
class VirtualAttribute < BenchmarkModel
|
|
219
|
+
attr_accessor :id, :name
|
|
220
|
+
end
|
|
221
|
+
end
|
data/test/cache_test.rb
ADDED
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'tmpdir'
|
|
5
|
+
require 'tempfile'
|
|
6
|
+
|
|
7
|
+
module ActiveModelSerializers
|
|
8
|
+
class CacheTest < ActiveSupport::TestCase
|
|
9
|
+
class Article < ::Model
|
|
10
|
+
attributes :title
|
|
11
|
+
# To confirm error is raised when cache_key is not set and cache_key option not passed to cache
|
|
12
|
+
undef_method :cache_key
|
|
13
|
+
end
|
|
14
|
+
class ArticleSerializer < ActiveModel::Serializer
|
|
15
|
+
cache only: [:place], skip_digest: true
|
|
16
|
+
attributes :title
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Author < ::Model
|
|
20
|
+
attributes :id, :name
|
|
21
|
+
associations :posts, :bio, :roles
|
|
22
|
+
end
|
|
23
|
+
# Instead of a primitive cache key (i.e. a string), this class
|
|
24
|
+
# returns a list of objects that require to be expanded themselves.
|
|
25
|
+
class AuthorWithExpandableCacheElements < Author
|
|
26
|
+
# For the test purposes it's important that #to_s for HasCacheKey differs
|
|
27
|
+
# between instances, hence not a Struct.
|
|
28
|
+
class HasCacheKey
|
|
29
|
+
attr_reader :cache_key
|
|
30
|
+
def initialize(cache_key)
|
|
31
|
+
@cache_key = cache_key
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_s
|
|
35
|
+
"HasCacheKey##{object_id}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def cache_key
|
|
40
|
+
[
|
|
41
|
+
HasCacheKey.new(name),
|
|
42
|
+
HasCacheKey.new(id)
|
|
43
|
+
]
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
class UncachedAuthor < Author
|
|
47
|
+
# To confirm cache_key is set using updated_at and cache_key option passed to cache
|
|
48
|
+
undef_method :cache_key
|
|
49
|
+
end
|
|
50
|
+
class AuthorSerializer < ActiveModel::Serializer
|
|
51
|
+
cache key: 'writer', skip_digest: true
|
|
52
|
+
attributes :id, :name
|
|
53
|
+
|
|
54
|
+
has_many :posts
|
|
55
|
+
has_many :roles
|
|
56
|
+
has_one :bio
|
|
57
|
+
end
|
|
58
|
+
class AuthorSerializerWithCache < ActiveModel::Serializer
|
|
59
|
+
cache
|
|
60
|
+
|
|
61
|
+
attributes :name
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class Blog < ::Model
|
|
65
|
+
attributes :name
|
|
66
|
+
associations :writer
|
|
67
|
+
end
|
|
68
|
+
class BlogSerializer < ActiveModel::Serializer
|
|
69
|
+
cache key: 'blog'
|
|
70
|
+
attributes :id, :name
|
|
71
|
+
|
|
72
|
+
belongs_to :writer
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class Comment < ::Model
|
|
76
|
+
attributes :id, :body
|
|
77
|
+
associations :post, :author
|
|
78
|
+
|
|
79
|
+
# Uses a custom non-time-based cache key
|
|
80
|
+
def cache_key
|
|
81
|
+
"comment/#{id}"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
class CommentSerializer < ActiveModel::Serializer
|
|
85
|
+
cache expires_in: 1.day, skip_digest: true
|
|
86
|
+
attributes :id, :body
|
|
87
|
+
belongs_to :post
|
|
88
|
+
belongs_to :author
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class Post < ::Model
|
|
92
|
+
attributes :id, :title, :body
|
|
93
|
+
associations :author, :comments, :blog
|
|
94
|
+
end
|
|
95
|
+
class PostSerializer < ActiveModel::Serializer
|
|
96
|
+
cache key: 'post', expires_in: 0.1, skip_digest: true
|
|
97
|
+
attributes :id, :title, :body
|
|
98
|
+
|
|
99
|
+
has_many :comments
|
|
100
|
+
belongs_to :blog
|
|
101
|
+
belongs_to :author
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class Role < ::Model
|
|
105
|
+
attributes :name, :description, :special_attribute
|
|
106
|
+
associations :author
|
|
107
|
+
end
|
|
108
|
+
class RoleSerializer < ActiveModel::Serializer
|
|
109
|
+
cache only: [:name, :slug], skip_digest: true
|
|
110
|
+
attributes :id, :name, :description
|
|
111
|
+
attribute :friendly_id, key: :slug
|
|
112
|
+
belongs_to :author
|
|
113
|
+
|
|
114
|
+
def friendly_id
|
|
115
|
+
"#{object.name}-#{object.id}"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
class InheritedRoleSerializer < RoleSerializer
|
|
119
|
+
cache key: 'inherited_role', only: [:name, :special_attribute]
|
|
120
|
+
attribute :special_attribute
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
setup do
|
|
124
|
+
cache_store.clear
|
|
125
|
+
@comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
|
|
126
|
+
@post = Post.new(id: 'post', title: 'New Post', body: 'Body')
|
|
127
|
+
@bio = Bio.new(id: 1, content: 'AMS Contributor')
|
|
128
|
+
@author = Author.new(id: 'author', name: 'Joao M. D. Moura')
|
|
129
|
+
@blog = Blog.new(id: 999, name: 'Custom blog', writer: @author)
|
|
130
|
+
@role = Role.new(name: 'Great Author')
|
|
131
|
+
@location = Location.new(lat: '-23.550520', lng: '-46.633309')
|
|
132
|
+
@place = Place.new(name: 'Amazing Place')
|
|
133
|
+
@author.posts = [@post]
|
|
134
|
+
@author.roles = [@role]
|
|
135
|
+
@role.author = @author
|
|
136
|
+
@author.bio = @bio
|
|
137
|
+
@bio.author = @author
|
|
138
|
+
@post.comments = [@comment]
|
|
139
|
+
@post.author = @author
|
|
140
|
+
@comment.post = @post
|
|
141
|
+
@comment.author = @author
|
|
142
|
+
@post.blog = @blog
|
|
143
|
+
@location.place = @place
|
|
144
|
+
|
|
145
|
+
@location_serializer = LocationSerializer.new(@location)
|
|
146
|
+
@bio_serializer = BioSerializer.new(@bio)
|
|
147
|
+
@role_serializer = RoleSerializer.new(@role)
|
|
148
|
+
@post_serializer = PostSerializer.new(@post)
|
|
149
|
+
@author_serializer = AuthorSerializer.new(@author)
|
|
150
|
+
@comment_serializer = CommentSerializer.new(@comment)
|
|
151
|
+
@blog_serializer = BlogSerializer.new(@blog)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def test_expiring_of_cache_at_update_of_record
|
|
155
|
+
original_cache_versioning = :none
|
|
156
|
+
|
|
157
|
+
if ARModels::Author.respond_to?(:cache_versioning)
|
|
158
|
+
original_cache_versioning = ARModels::Author.cache_versioning
|
|
159
|
+
ARModels::Author.cache_versioning = true
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
author = ARModels::Author.create(name: 'Foo')
|
|
163
|
+
author_json = AuthorSerializerWithCache.new(author).as_json
|
|
164
|
+
|
|
165
|
+
assert_equal 'Foo', author_json[:name]
|
|
166
|
+
|
|
167
|
+
author.update_attributes(name: 'Bar')
|
|
168
|
+
author_json = AuthorSerializerWithCache.new(author).as_json
|
|
169
|
+
|
|
170
|
+
expected = 'Bar'
|
|
171
|
+
actual = author_json[:name]
|
|
172
|
+
if ENV['APPVEYOR'] && actual != expected
|
|
173
|
+
skip('Cache expiration tests sometimes fail on Appveyor. FIXME :)')
|
|
174
|
+
else
|
|
175
|
+
assert_equal expected, actual
|
|
176
|
+
end
|
|
177
|
+
ensure
|
|
178
|
+
ARModels::Author.cache_versioning = original_cache_versioning unless original_cache_versioning == :none
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def test_cache_expiration_in_collection_on_update_of_record
|
|
182
|
+
original_cache_versioning = :none
|
|
183
|
+
|
|
184
|
+
if ARModels::Author.respond_to?(:cache_versioning)
|
|
185
|
+
original_cache_versioning = ARModels::Author.cache_versioning
|
|
186
|
+
ARModels::Author.cache_versioning = true
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
foo = 'Foo'
|
|
190
|
+
foo2 = 'Foo2'
|
|
191
|
+
author = ARModels::Author.create(name: foo)
|
|
192
|
+
author2 = ARModels::Author.create(name: foo2)
|
|
193
|
+
author_collection = [author, author, author2]
|
|
194
|
+
|
|
195
|
+
collection_json = render_object_with_cache(author_collection, each_serializer: AuthorSerializerWithCache)
|
|
196
|
+
actual = collection_json
|
|
197
|
+
expected = [{ name: foo }, { name: foo }, { name: foo2 }]
|
|
198
|
+
if ENV['APPVEYOR'] && actual != expected
|
|
199
|
+
skip('Cache expiration tests sometimes fail on Appveyor. FIXME :)')
|
|
200
|
+
else
|
|
201
|
+
assert_equal expected, actual
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
bar = 'Bar'
|
|
205
|
+
author.update!(name: bar)
|
|
206
|
+
|
|
207
|
+
collection_json = render_object_with_cache(author_collection, each_serializer: AuthorSerializerWithCache)
|
|
208
|
+
assert_equal [{ name: bar }, { name: bar }, { name: foo2 }], collection_json
|
|
209
|
+
ensure
|
|
210
|
+
ARModels::Author.cache_versioning = original_cache_versioning unless original_cache_versioning == :none
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def test_explicit_cache_store
|
|
214
|
+
default_store = Class.new(ActiveModel::Serializer) do
|
|
215
|
+
cache
|
|
216
|
+
end
|
|
217
|
+
explicit_store = Class.new(ActiveModel::Serializer) do
|
|
218
|
+
cache cache_store: ActiveSupport::Cache::FileStore
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
assert ActiveSupport::Cache::MemoryStore, ActiveModelSerializers.config.cache_store
|
|
222
|
+
assert ActiveSupport::Cache::MemoryStore, default_store.cache_store
|
|
223
|
+
assert ActiveSupport::Cache::FileStore, explicit_store.cache_store
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def test_inherited_cache_configuration
|
|
227
|
+
inherited_serializer = Class.new(PostSerializer)
|
|
228
|
+
|
|
229
|
+
assert_equal PostSerializer._cache_key, inherited_serializer._cache_key
|
|
230
|
+
assert_equal PostSerializer._cache_options, inherited_serializer._cache_options
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def test_override_cache_configuration
|
|
234
|
+
inherited_serializer = Class.new(PostSerializer) do
|
|
235
|
+
cache key: 'new-key'
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
assert_equal PostSerializer._cache_key, 'post'
|
|
239
|
+
assert_equal inherited_serializer._cache_key, 'new-key'
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def test_cache_definition
|
|
243
|
+
assert_equal(cache_store, @post_serializer.class._cache)
|
|
244
|
+
assert_equal(cache_store, @author_serializer.class._cache)
|
|
245
|
+
assert_equal(cache_store, @comment_serializer.class._cache)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def test_cache_key_definition
|
|
249
|
+
assert_equal('post', @post_serializer.class._cache_key)
|
|
250
|
+
assert_equal('writer', @author_serializer.class._cache_key)
|
|
251
|
+
assert_nil(@comment_serializer.class._cache_key)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def test_cache_key_interpolation_with_updated_at_when_cache_key_is_not_defined_on_object
|
|
255
|
+
uncached_author = UncachedAuthor.new(name: 'Joao M. D. Moura')
|
|
256
|
+
uncached_author_serializer = AuthorSerializer.new(uncached_author)
|
|
257
|
+
|
|
258
|
+
render_object_with_cache(uncached_author)
|
|
259
|
+
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')}"
|
|
260
|
+
key = "#{key}/#{adapter.cache_key}"
|
|
261
|
+
assert_equal(uncached_author_serializer.attributes.to_json, cache_store.fetch(key).to_json)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def test_cache_key_expansion
|
|
265
|
+
author = AuthorWithExpandableCacheElements.new(id: 10, name: 'hello')
|
|
266
|
+
same_author = AuthorWithExpandableCacheElements.new(id: 10, name: 'hello')
|
|
267
|
+
diff_author = AuthorWithExpandableCacheElements.new(id: 11, name: 'hello')
|
|
268
|
+
|
|
269
|
+
author_serializer = AuthorSerializer.new(author)
|
|
270
|
+
same_author_serializer = AuthorSerializer.new(same_author)
|
|
271
|
+
diff_author_serializer = AuthorSerializer.new(diff_author)
|
|
272
|
+
adapter = AuthorSerializer.serialization_adapter_instance
|
|
273
|
+
|
|
274
|
+
assert_equal(author_serializer.cache_key(adapter), same_author_serializer.cache_key(adapter))
|
|
275
|
+
refute_equal(author_serializer.cache_key(adapter), diff_author_serializer.cache_key(adapter))
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def test_default_cache_key_fallback
|
|
279
|
+
render_object_with_cache(@comment)
|
|
280
|
+
key = "#{@comment.cache_key}/#{adapter.cache_key}"
|
|
281
|
+
assert_equal(@comment_serializer.attributes.to_json, cache_store.fetch(key).to_json)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def test_error_is_raised_if_cache_key_is_not_defined_on_object_or_passed_as_cache_option
|
|
285
|
+
article = Article.new(title: 'Must Read')
|
|
286
|
+
e = assert_raises ActiveModel::Serializer::UndefinedCacheKey do
|
|
287
|
+
render_object_with_cache(article)
|
|
288
|
+
end
|
|
289
|
+
assert_match(/ActiveModelSerializers::CacheTest::Article must define #cache_key, or the 'key:' option must be passed into 'ActiveModelSerializers::CacheTest::ArticleSerializer.cache'/, e.message)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def test_cache_options_definition
|
|
293
|
+
assert_equal({ expires_in: 0.1, skip_digest: true }, @post_serializer.class._cache_options)
|
|
294
|
+
assert_nil(@blog_serializer.class._cache_options)
|
|
295
|
+
assert_equal({ expires_in: 1.day, skip_digest: true }, @comment_serializer.class._cache_options)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def test_fragment_cache_definition
|
|
299
|
+
assert_equal([:name, :slug], @role_serializer.class._cache_only)
|
|
300
|
+
assert_equal([:content], @bio_serializer.class._cache_except)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def test_associations_separately_cache
|
|
304
|
+
cache_store.clear
|
|
305
|
+
assert_nil(cache_store.fetch(@post.cache_key))
|
|
306
|
+
assert_nil(cache_store.fetch(@comment.cache_key))
|
|
307
|
+
|
|
308
|
+
Timecop.freeze(Time.current) do
|
|
309
|
+
render_object_with_cache(@post)
|
|
310
|
+
|
|
311
|
+
key = "#{@post.cache_key}/#{adapter.cache_key}"
|
|
312
|
+
assert_equal(@post_serializer.attributes, cache_store.fetch(key))
|
|
313
|
+
key = "#{@comment.cache_key}/#{adapter.cache_key}"
|
|
314
|
+
assert_equal(@comment_serializer.attributes, cache_store.fetch(key))
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def test_associations_cache_when_updated
|
|
319
|
+
Timecop.freeze(Time.current) do
|
|
320
|
+
# Generate a new Cache of Post object and each objects related to it.
|
|
321
|
+
render_object_with_cache(@post)
|
|
322
|
+
|
|
323
|
+
# Check if it cached the objects separately
|
|
324
|
+
key = "#{@post.cache_key}/#{adapter.cache_key}"
|
|
325
|
+
assert_equal(@post_serializer.attributes, cache_store.fetch(key))
|
|
326
|
+
key = "#{@comment.cache_key}/#{adapter.cache_key}"
|
|
327
|
+
assert_equal(@comment_serializer.attributes, cache_store.fetch(key))
|
|
328
|
+
|
|
329
|
+
# Simulating update on comments relationship with Post
|
|
330
|
+
new_comment = Comment.new(id: 2567, body: 'ZOMG A NEW COMMENT')
|
|
331
|
+
new_comment_serializer = CommentSerializer.new(new_comment)
|
|
332
|
+
@post.comments = [new_comment]
|
|
333
|
+
|
|
334
|
+
# Ask for the serialized object
|
|
335
|
+
render_object_with_cache(@post)
|
|
336
|
+
|
|
337
|
+
# Check if the the new comment was cached
|
|
338
|
+
key = "#{new_comment.cache_key}/#{adapter.cache_key}"
|
|
339
|
+
assert_equal(new_comment_serializer.attributes, cache_store.fetch(key))
|
|
340
|
+
key = "#{@post.cache_key}/#{adapter.cache_key}"
|
|
341
|
+
assert_equal(@post_serializer.attributes, cache_store.fetch(key))
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def test_fragment_fetch_with_virtual_associations
|
|
346
|
+
expected_result = {
|
|
347
|
+
id: @location.id,
|
|
348
|
+
lat: @location.lat,
|
|
349
|
+
lng: @location.lng,
|
|
350
|
+
address: 'Nowhere'
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
hash = render_object_with_cache(@location)
|
|
354
|
+
|
|
355
|
+
assert_equal(hash, expected_result)
|
|
356
|
+
key = "#{@location.cache_key}/#{adapter.cache_key}"
|
|
357
|
+
assert_equal({ address: 'Nowhere' }, cache_store.fetch(key))
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def test_fragment_cache_with_inheritance
|
|
361
|
+
inherited = render_object_with_cache(@role, serializer: InheritedRoleSerializer)
|
|
362
|
+
base = render_object_with_cache(@role)
|
|
363
|
+
|
|
364
|
+
assert_includes(inherited.keys, :special_attribute)
|
|
365
|
+
refute_includes(base.keys, :special_attribute)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def test_uses_adapter_in_cache_key
|
|
369
|
+
render_object_with_cache(@post)
|
|
370
|
+
key = "#{@post.cache_key}/#{adapter.class.to_s.demodulize.underscore}"
|
|
371
|
+
assert_equal(@post_serializer.attributes, cache_store.fetch(key))
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Based on original failing test by @kevintyll
|
|
375
|
+
# rubocop:disable Metrics/AbcSize
|
|
376
|
+
def test_a_serializer_rendered_by_two_adapter_returns_differently_fetch_attributes
|
|
377
|
+
Object.const_set(:Alert, Class.new(ActiveModelSerializers::Model) do
|
|
378
|
+
attributes :id, :status, :resource, :started_at, :ended_at, :updated_at, :created_at
|
|
379
|
+
end)
|
|
380
|
+
Object.const_set(:UncachedAlertSerializer, Class.new(ActiveModel::Serializer) do
|
|
381
|
+
attributes :id, :status, :resource, :started_at, :ended_at, :updated_at, :created_at
|
|
382
|
+
end)
|
|
383
|
+
Object.const_set(:AlertSerializer, Class.new(UncachedAlertSerializer) do
|
|
384
|
+
cache
|
|
385
|
+
end)
|
|
386
|
+
|
|
387
|
+
alert = Alert.new(
|
|
388
|
+
id: 1,
|
|
389
|
+
status: 'fail',
|
|
390
|
+
resource: 'resource-1',
|
|
391
|
+
started_at: Time.new(2016, 3, 31, 21, 36, 35, 0),
|
|
392
|
+
ended_at: nil,
|
|
393
|
+
updated_at: Time.new(2016, 3, 31, 21, 27, 35, 0),
|
|
394
|
+
created_at: Time.new(2016, 3, 31, 21, 37, 35, 0)
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
expected_fetch_attributes = {
|
|
398
|
+
id: 1,
|
|
399
|
+
status: 'fail',
|
|
400
|
+
resource: 'resource-1',
|
|
401
|
+
started_at: alert.started_at,
|
|
402
|
+
ended_at: nil,
|
|
403
|
+
updated_at: alert.updated_at,
|
|
404
|
+
created_at: alert.created_at
|
|
405
|
+
}.with_indifferent_access
|
|
406
|
+
expected_cached_jsonapi_attributes = {
|
|
407
|
+
id: '1',
|
|
408
|
+
type: 'alerts',
|
|
409
|
+
attributes: {
|
|
410
|
+
status: 'fail',
|
|
411
|
+
resource: 'resource-1',
|
|
412
|
+
started_at: alert.started_at,
|
|
413
|
+
ended_at: nil,
|
|
414
|
+
updated_at: alert.updated_at,
|
|
415
|
+
created_at: alert.created_at
|
|
416
|
+
}
|
|
417
|
+
}.with_indifferent_access
|
|
418
|
+
|
|
419
|
+
# Assert attributes are serialized correctly
|
|
420
|
+
serializable_alert = serializable(alert, serializer: AlertSerializer, adapter: :attributes)
|
|
421
|
+
attributes_serialization = serializable_alert.as_json.with_indifferent_access
|
|
422
|
+
assert_equal expected_fetch_attributes, alert.attributes
|
|
423
|
+
assert_equal alert.attributes, attributes_serialization
|
|
424
|
+
attributes_cache_key = serializable_alert.adapter.serializer.cache_key(serializable_alert.adapter)
|
|
425
|
+
assert_equal attributes_serialization, cache_store.fetch(attributes_cache_key).with_indifferent_access
|
|
426
|
+
|
|
427
|
+
serializable_alert = serializable(alert, serializer: AlertSerializer, adapter: :json_api)
|
|
428
|
+
jsonapi_cache_key = serializable_alert.adapter.serializer.cache_key(serializable_alert.adapter)
|
|
429
|
+
# Assert cache keys differ
|
|
430
|
+
refute_equal attributes_cache_key, jsonapi_cache_key
|
|
431
|
+
# Assert (cached) serializations differ
|
|
432
|
+
jsonapi_serialization = serializable_alert.as_json
|
|
433
|
+
assert_equal alert.status, jsonapi_serialization.fetch(:data).fetch(:attributes).fetch(:status)
|
|
434
|
+
serializable_alert = serializable(alert, serializer: UncachedAlertSerializer, adapter: :json_api)
|
|
435
|
+
assert_equal serializable_alert.as_json, jsonapi_serialization
|
|
436
|
+
|
|
437
|
+
cached_serialization = cache_store.fetch(jsonapi_cache_key).with_indifferent_access
|
|
438
|
+
assert_equal expected_cached_jsonapi_attributes, cached_serialization
|
|
439
|
+
ensure
|
|
440
|
+
Object.send(:remove_const, :Alert)
|
|
441
|
+
Object.send(:remove_const, :AlertSerializer)
|
|
442
|
+
Object.send(:remove_const, :UncachedAlertSerializer)
|
|
443
|
+
end
|
|
444
|
+
# rubocop:enable Metrics/AbcSize
|
|
445
|
+
|
|
446
|
+
def test_uses_file_digest_in_cache_key
|
|
447
|
+
render_object_with_cache(@blog)
|
|
448
|
+
file_digest = Digest::MD5.hexdigest(File.open(__FILE__).read)
|
|
449
|
+
key = "#{@blog.cache_key}/#{adapter.cache_key}/#{file_digest}"
|
|
450
|
+
assert_equal(@blog_serializer.attributes, cache_store.fetch(key))
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def test_cache_digest_definition
|
|
454
|
+
file_digest = Digest::MD5.hexdigest(File.open(__FILE__).read)
|
|
455
|
+
assert_equal(file_digest, @post_serializer.class._cache_digest)
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def test_object_cache_keys
|
|
459
|
+
serializable = ActiveModelSerializers::SerializableResource.new([@comment, @comment])
|
|
460
|
+
include_directive = JSONAPI::IncludeDirective.new('*', allow_wildcard: true)
|
|
461
|
+
|
|
462
|
+
actual = ActiveModel::Serializer.object_cache_keys(serializable.adapter.serializer, serializable.adapter, include_directive)
|
|
463
|
+
|
|
464
|
+
assert_equal 3, actual.size
|
|
465
|
+
expected_key = "comment/1/#{serializable.adapter.cache_key}"
|
|
466
|
+
assert actual.any? { |key| key == expected_key }, "actual '#{actual}' should include #{expected_key}"
|
|
467
|
+
expected_key = %r{post/post-\d+}
|
|
468
|
+
assert actual.any? { |key| key =~ expected_key }, "actual '#{actual}' should match '#{expected_key}'"
|
|
469
|
+
expected_key = %r{author/author-\d+}
|
|
470
|
+
assert actual.any? { |key| key =~ expected_key }, "actual '#{actual}' should match '#{expected_key}'"
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
# rubocop:disable Metrics/AbcSize
|
|
474
|
+
def test_fetch_attributes_from_cache
|
|
475
|
+
serializers = ActiveModel::Serializer::CollectionSerializer.new([@comment, @comment])
|
|
476
|
+
|
|
477
|
+
Timecop.freeze(Time.current) do
|
|
478
|
+
render_object_with_cache(@comment)
|
|
479
|
+
|
|
480
|
+
options = {}
|
|
481
|
+
adapter_options = {}
|
|
482
|
+
adapter_instance = ActiveModelSerializers::Adapter::Attributes.new(serializers, adapter_options)
|
|
483
|
+
serializers.serializable_hash(adapter_options, options, adapter_instance)
|
|
484
|
+
cached_attributes = options.fetch(:cached_attributes).with_indifferent_access
|
|
485
|
+
|
|
486
|
+
include_directive = ActiveModelSerializers.default_include_directive
|
|
487
|
+
manual_cached_attributes = ActiveModel::Serializer.cache_read_multi(serializers, adapter_instance, include_directive).with_indifferent_access
|
|
488
|
+
assert_equal manual_cached_attributes, cached_attributes
|
|
489
|
+
|
|
490
|
+
assert_equal cached_attributes["#{@comment.cache_key}/#{adapter_instance.cache_key}"], Comment.new(id: 1, body: 'ZOMG A COMMENT').attributes
|
|
491
|
+
assert_equal cached_attributes["#{@comment.post.cache_key}/#{adapter_instance.cache_key}"], Post.new(id: 'post', title: 'New Post', body: 'Body').attributes
|
|
492
|
+
|
|
493
|
+
writer = @comment.post.blog.writer
|
|
494
|
+
writer_cache_key = writer.cache_key
|
|
495
|
+
assert_equal cached_attributes["#{writer_cache_key}/#{adapter_instance.cache_key}"], Author.new(id: 'author', name: 'Joao M. D. Moura').attributes
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
# rubocop:enable Metrics/AbcSize
|
|
499
|
+
|
|
500
|
+
def test_cache_read_multi_with_fragment_cache_enabled
|
|
501
|
+
post_serializer = Class.new(ActiveModel::Serializer) do
|
|
502
|
+
cache except: [:body]
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
serializers = ActiveModel::Serializer::CollectionSerializer.new([@post, @post], serializer: post_serializer)
|
|
506
|
+
|
|
507
|
+
Timecop.freeze(Time.current) do
|
|
508
|
+
# Warming up.
|
|
509
|
+
options = {}
|
|
510
|
+
adapter_options = {}
|
|
511
|
+
adapter_instance = ActiveModelSerializers::Adapter::Attributes.new(serializers, adapter_options)
|
|
512
|
+
serializers.serializable_hash(adapter_options, options, adapter_instance)
|
|
513
|
+
|
|
514
|
+
# Should find something with read_multi now
|
|
515
|
+
options = {}
|
|
516
|
+
serializers.serializable_hash(adapter_options, options, adapter_instance)
|
|
517
|
+
cached_attributes = options.fetch(:cached_attributes)
|
|
518
|
+
|
|
519
|
+
include_directive = ActiveModelSerializers.default_include_directive
|
|
520
|
+
manual_cached_attributes = ActiveModel::Serializer.cache_read_multi(serializers, adapter_instance, include_directive)
|
|
521
|
+
|
|
522
|
+
refute_equal 0, cached_attributes.size
|
|
523
|
+
refute_equal 0, manual_cached_attributes.size
|
|
524
|
+
assert_equal manual_cached_attributes, cached_attributes
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
def test_serializer_file_path_on_nix
|
|
529
|
+
path = '/Users/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb'
|
|
530
|
+
caller_line = "#{path}:1:in `<top (required)>'"
|
|
531
|
+
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def test_serializer_file_path_on_windows
|
|
535
|
+
path = 'c:/git/emberjs/ember-crm-backend/app/serializers/lead_serializer.rb'
|
|
536
|
+
caller_line = "#{path}:1:in `<top (required)>'"
|
|
537
|
+
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def test_serializer_file_path_with_space
|
|
541
|
+
path = '/Users/git/ember js/ember-crm-backend/app/serializers/lead_serializer.rb'
|
|
542
|
+
caller_line = "#{path}:1:in `<top (required)>'"
|
|
543
|
+
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
def test_serializer_file_path_with_submatch
|
|
547
|
+
# The submatch in the path ensures we're using a correctly greedy regexp.
|
|
548
|
+
path = '/Users/git/ember js/ember:123:in x/app/serializers/lead_serializer.rb'
|
|
549
|
+
caller_line = "#{path}:1:in `<top (required)>'"
|
|
550
|
+
assert_equal caller_line[ActiveModel::Serializer::CALLER_FILE], path
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
def test_digest_caller_file
|
|
554
|
+
contents = "puts 'AMS rocks'!"
|
|
555
|
+
dir = Dir.mktmpdir('space char')
|
|
556
|
+
file = Tempfile.new('some_ruby.rb', dir)
|
|
557
|
+
file.write(contents)
|
|
558
|
+
path = file.path
|
|
559
|
+
caller_line = "#{path}:1:in `<top (required)>'"
|
|
560
|
+
file.close
|
|
561
|
+
assert_equal ActiveModel::Serializer.digest_caller_file(caller_line), Digest::MD5.hexdigest(contents)
|
|
562
|
+
ensure
|
|
563
|
+
file.unlink
|
|
564
|
+
FileUtils.remove_entry dir
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
def test_warn_on_serializer_not_defined_in_file
|
|
568
|
+
called = false
|
|
569
|
+
serializer = Class.new(ActiveModel::Serializer)
|
|
570
|
+
assert_output(nil, /_cache_digest/) do
|
|
571
|
+
serializer.digest_caller_file('')
|
|
572
|
+
called = true
|
|
573
|
+
end
|
|
574
|
+
assert called
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def test_cached_false_without_cache_store
|
|
578
|
+
cached_serializer = build_cached_serializer do |serializer|
|
|
579
|
+
serializer._cache = nil
|
|
580
|
+
end
|
|
581
|
+
refute cached_serializer.class.cache_enabled?
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
def test_cached_true_with_cache_store_and_without_cache_only_and_cache_except
|
|
585
|
+
cached_serializer = build_cached_serializer do |serializer|
|
|
586
|
+
serializer._cache = Object
|
|
587
|
+
end
|
|
588
|
+
assert cached_serializer.class.cache_enabled?
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
def test_cached_false_with_cache_store_and_with_cache_only
|
|
592
|
+
cached_serializer = build_cached_serializer do |serializer|
|
|
593
|
+
serializer._cache = Object
|
|
594
|
+
serializer._cache_only = [:name]
|
|
595
|
+
end
|
|
596
|
+
refute cached_serializer.class.cache_enabled?
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
def test_cached_false_with_cache_store_and_with_cache_except
|
|
600
|
+
cached_serializer = build_cached_serializer do |serializer|
|
|
601
|
+
serializer._cache = Object
|
|
602
|
+
serializer._cache_except = [:content]
|
|
603
|
+
end
|
|
604
|
+
refute cached_serializer.class.cache_enabled?
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
def test_fragment_cached_false_without_cache_store
|
|
608
|
+
cached_serializer = build_cached_serializer do |serializer|
|
|
609
|
+
serializer._cache = nil
|
|
610
|
+
serializer._cache_only = [:name]
|
|
611
|
+
end
|
|
612
|
+
refute cached_serializer.class.fragment_cache_enabled?
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
def test_fragment_cached_true_with_cache_store_and_cache_only
|
|
616
|
+
cached_serializer = build_cached_serializer do |serializer|
|
|
617
|
+
serializer._cache = Object
|
|
618
|
+
serializer._cache_only = [:name]
|
|
619
|
+
end
|
|
620
|
+
assert cached_serializer.class.fragment_cache_enabled?
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
def test_fragment_cached_true_with_cache_store_and_cache_except
|
|
624
|
+
cached_serializer = build_cached_serializer do |serializer|
|
|
625
|
+
serializer._cache = Object
|
|
626
|
+
serializer._cache_except = [:content]
|
|
627
|
+
end
|
|
628
|
+
assert cached_serializer.class.fragment_cache_enabled?
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def test_fragment_cached_false_with_cache_store_and_cache_except_and_cache_only
|
|
632
|
+
cached_serializer = build_cached_serializer do |serializer|
|
|
633
|
+
serializer._cache = Object
|
|
634
|
+
serializer._cache_except = [:content]
|
|
635
|
+
serializer._cache_only = [:name]
|
|
636
|
+
end
|
|
637
|
+
refute cached_serializer.class.fragment_cache_enabled?
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
def test_fragment_fetch_with_virtual_attributes
|
|
641
|
+
author = Author.new(name: 'Joao M. D. Moura')
|
|
642
|
+
role = Role.new(name: 'Great Author', description: nil)
|
|
643
|
+
role.author = [author]
|
|
644
|
+
role_serializer = RoleSerializer.new(role)
|
|
645
|
+
adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(role_serializer)
|
|
646
|
+
expected_result = {
|
|
647
|
+
id: role.id,
|
|
648
|
+
description: role.description,
|
|
649
|
+
slug: "#{role.name}-#{role.id}",
|
|
650
|
+
name: role.name
|
|
651
|
+
}
|
|
652
|
+
cache_store.clear
|
|
653
|
+
|
|
654
|
+
role_hash = role_serializer.fetch_attributes_fragment(adapter_instance)
|
|
655
|
+
assert_equal(role_hash, expected_result)
|
|
656
|
+
|
|
657
|
+
role.id = 'this has been updated'
|
|
658
|
+
role.name = 'this was cached'
|
|
659
|
+
|
|
660
|
+
role_hash = role_serializer.fetch_attributes_fragment(adapter_instance)
|
|
661
|
+
assert_equal(expected_result.merge(id: role.id), role_hash)
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
def test_fragment_fetch_with_except
|
|
665
|
+
adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(@bio_serializer)
|
|
666
|
+
expected_result = {
|
|
667
|
+
id: @bio.id,
|
|
668
|
+
rating: nil,
|
|
669
|
+
content: @bio.content
|
|
670
|
+
}
|
|
671
|
+
cache_store.clear
|
|
672
|
+
|
|
673
|
+
bio_hash = @bio_serializer.fetch_attributes_fragment(adapter_instance)
|
|
674
|
+
assert_equal(expected_result, bio_hash)
|
|
675
|
+
|
|
676
|
+
@bio.content = 'this has been updated'
|
|
677
|
+
@bio.rating = 'this was cached'
|
|
678
|
+
|
|
679
|
+
bio_hash = @bio_serializer.fetch_attributes_fragment(adapter_instance)
|
|
680
|
+
assert_equal(expected_result.merge(content: @bio.content), bio_hash)
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
def test_fragment_fetch_with_namespaced_object
|
|
684
|
+
@spam = Spam::UnrelatedLink.new(id: 'spam-id-1')
|
|
685
|
+
@spam_serializer = Spam::UnrelatedLinkSerializer.new(@spam)
|
|
686
|
+
adapter_instance = ActiveModelSerializers::Adapter.configured_adapter.new(@spam_serializer)
|
|
687
|
+
@spam_hash = @spam_serializer.fetch_attributes_fragment(adapter_instance)
|
|
688
|
+
expected_result = {
|
|
689
|
+
id: @spam.id
|
|
690
|
+
}
|
|
691
|
+
assert_equal(@spam_hash, expected_result)
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
private
|
|
695
|
+
|
|
696
|
+
def cache_store
|
|
697
|
+
ActiveModelSerializers.config.cache_store
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
def build_cached_serializer
|
|
701
|
+
serializer = Class.new(ActiveModel::Serializer)
|
|
702
|
+
serializer._cache_key = nil
|
|
703
|
+
serializer._cache_options = nil
|
|
704
|
+
yield serializer if block_given?
|
|
705
|
+
serializer.new(Object)
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
def render_object_with_cache(obj, options = {})
|
|
709
|
+
@serializable_resource = serializable(obj, options)
|
|
710
|
+
@serializable_resource.serializable_hash
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
def adapter
|
|
714
|
+
@serializable_resource.adapter
|
|
715
|
+
end
|
|
716
|
+
end
|
|
717
|
+
end
|