diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b2df5e74..91a2c7869 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ Breaking changes: Features: +- [#2361](https://github.com/rails-api/active_model_serializers/pull/2361) Add `ActiveModelSerializers.config.use_sha1_digests` to allow customization of the hashing algorithm used for serializer caching (@alexzherdev) + Fixes: - [#2344](https://github.com/rails-api/active_model_serializers/pull/2344) Fixes #2341 introduced since #2223 (@wasifhossain) diff --git a/docs/general/configuration_options.md b/docs/general/configuration_options.md index a07d71ee8..0f9b2aaee 100644 --- a/docs/general/configuration_options.md +++ b/docs/general/configuration_options.md @@ -111,6 +111,18 @@ ActiveModelSerializers.config.serializer_lookup_chain.unshift( See [lookup_chain.rb](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/lookup_chain.rb) for further explanations and examples. +#### use_sha1_digests + +Determines which hashing algorithm to use internally when caching serializers. + +Possible values: + +- `true` +- `false` (default) + +When `true`, ActiveModelSerializers will use SHA1. Otherwise, it will default to using MD5. +Be warned that changing this value may result in serializer caches being invalidated. + ## JSON API ##### jsonapi_resource_type diff --git a/lib/active_model/serializer/concerns/caching.rb b/lib/active_model/serializer/concerns/caching.rb index 35bd8e649..cdcd56852 100644 --- a/lib/active_model/serializer/concerns/caching.rb +++ b/lib/active_model/serializer/concerns/caching.rb @@ -56,7 +56,8 @@ def _cache_digest def digest_caller_file(caller_line) serializer_file_path = caller_line[CALLER_FILE] serializer_file_contents = IO.read(serializer_file_path) - Digest::MD5.hexdigest(serializer_file_contents) + algorithm = ActiveModelSerializers.config.use_sha1_digests ? Digest::SHA1 : Digest::MD5 + algorithm.hexdigest(serializer_file_contents) rescue TypeError, Errno::ENOENT warn <<-EOF.strip_heredoc Cannot digest non-existent file: '#{caller_line}'. diff --git a/test/cache_test.rb b/test/cache_test.rb index fdab362a2..112e366cb 100644 --- a/test/cache_test.rb +++ b/test/cache_test.rb @@ -446,17 +446,41 @@ def test_a_serializer_rendered_by_two_adapter_returns_differently_fetch_attribut # rubocop:enable Metrics/AbcSize def test_uses_file_digest_in_cache_key + reset_cache_digest(@blog_serializer) render_object_with_cache(@blog) file_digest = Digest::MD5.hexdigest(File.open(__FILE__).read) key = "#{@blog.cache_key}/#{adapter.cache_key}/#{file_digest}" assert_equal(@blog_serializer.attributes, cache_store.fetch(key)) end + def test_uses_sha1_digest_in_cache_key_when_configured + reset_cache_digest(@blog_serializer) + previous_use_sha1_digests = ActiveModelSerializers.config.use_sha1_digests + ActiveModelSerializers.config.use_sha1_digests = true + render_object_with_cache(@blog) + file_digest = Digest::SHA1.hexdigest(File.open(__FILE__).read) + key = "#{@blog.cache_key}/#{adapter.cache_key}/#{file_digest}" + assert_equal(@blog_serializer.attributes, cache_store.fetch(key)) + ensure + ActiveModelSerializers.config.use_sha1_digests = previous_use_sha1_digests + end + def test_cache_digest_definition + reset_cache_digest(@post_serializer) file_digest = Digest::MD5.hexdigest(File.open(__FILE__).read) assert_equal(file_digest, @post_serializer.class._cache_digest) end + def test_cache_sha1_digest_definition + reset_cache_digest(@post_serializer) + previous_use_sha1_digests = ActiveModelSerializers.config.use_sha1_digests + ActiveModelSerializers.config.use_sha1_digests = true + file_digest = Digest::SHA1.hexdigest(File.open(__FILE__).read) + assert_equal(file_digest, @post_serializer.class._cache_digest) + ensure + ActiveModelSerializers.config.use_sha1_digests = previous_use_sha1_digests + end + def test_object_cache_keys serializable = ActiveModelSerializers::SerializableResource.new([@comment, @comment]) include_directive = JSONAPI::IncludeDirective.new('*', allow_wildcard: true) @@ -715,5 +739,10 @@ def render_object_with_cache(obj, options = {}) def adapter @serializable_resource.adapter end + + def reset_cache_digest(serializer) + return unless serializer.class.instance_variable_defined?(:@_cache_digest) + serializer.class.remove_instance_variable(:@_cache_digest) + end end end