From 86a4d803758f07ff4dc9147df88b9a97ae6fe82a Mon Sep 17 00:00:00 2001 From: Matheus Sales Date: Fri, 6 Oct 2023 15:16:06 -0300 Subject: [PATCH] Refine `ActiveRecord::SerializeMatcher` for Rails 7.1 compatibility This commit refines the `ActiveRecord::SerializeMatcher` to accommodate changes in the public API introduced in Rails 7.1. The `serialize` method's API has been updated, necessitating adjustments to our matcher specs to align with the new API. While this PR addresses the immediate need for compatibility, there is potential for further enhancements and refinements in the matcher's implementation to bring it in line with the revised public API. However, such improvements will be explored in a future PR. For reference, the changes in the public API can be found in the following PR: https://github.com/rails/rails/pull/47463 This commit adjusts the `ActiveRecord::SerializeMatcher` the public API for the `serialize` method has changed on Rails 7.1, so we had to --- spec/support/unit/attribute.rb | 2 +- .../validate_presence_of_matcher_spec.rb | 97 ++++++++++++++----- .../active_record/serialize_matcher_spec.rb | 18 ++-- 3 files changed, 85 insertions(+), 32 deletions(-) diff --git a/spec/support/unit/attribute.rb b/spec/support/unit/attribute.rb index 68e9e3810..f3e8ad49f 100644 --- a/spec/support/unit/attribute.rb +++ b/spec/support/unit/attribute.rb @@ -3,7 +3,7 @@ class Attribute DEFAULT_COLUMN_TYPE = :string DEFAULT_COLUMN_OPTIONS = { null: false, - array: false + array: false, }.freeze def initialize(args) diff --git a/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb b/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb index afa52e816..b9c09b110 100644 --- a/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb @@ -56,51 +56,102 @@ expect(&assertion).to fail_with_message(message) end - context 'when the attribute is decorated with serialize' do - context 'and the serializer is a built-in Ruby type' do - context 'and the type is a string' do + if rails_version >= '7.1.0' + context 'when the attribute is decorated with serialize' do + context 'and the serializer is a built-in Ruby type' do + context 'and the type is a string' do + it 'still works' do + record = record_validating_presence_of(:traits) do + serialize :traits, type: String, coder: YAML + end + + expect(record).to validate_presence_of(:traits) + end + end + + context 'and the type is not a string' do + it 'still works' do + record = record_validating_presence_of(:traits) do + serialize :traits, type: Array, coder: YAML + end + + expect(record).to validate_presence_of(:traits) + end + end + end + + context 'and the serializer is JSON' do it 'still works' do record = record_validating_presence_of(:traits) do - serialize :traits, String + serialize :traits, coder: JSON end expect(record).to validate_presence_of(:traits) end end - context 'and the type is not a string' do + context 'and the serializer is something custom' do it 'still works' do - record = record_validating_presence_of(:traits) do - serialize :traits, Array + serializer = Class.new do + define_singleton_method(:dump) { |value| value } + define_singleton_method(:load) { |value| value } end - expect(record).to validate_presence_of(:traits) + record = record_validating_presence_of(:data) do + serialize :data, coder: serializer + end + + expect(record).to validate_presence_of(:data) end end end + else + context 'when the attribute is decorated with serialize' do + context 'and the serializer is a built-in Ruby type' do + context 'and the type is a string' do + it 'still works' do + record = record_validating_presence_of(:traits) do + serialize :traits, String + end - context 'and the serializer is JSON' do - it 'still works' do - record = record_validating_presence_of(:traits) do - serialize :traits, JSON + expect(record).to validate_presence_of(:traits) + end end - expect(record).to validate_presence_of(:traits) - end - end + context 'and the type is not a string' do + it 'still works' do + record = record_validating_presence_of(:traits) do + serialize :traits, Array + end - context 'and the serializer is something custom' do - it 'still works' do - serializer = Class.new do - define_singleton_method(:dump) { |value| value } - define_singleton_method(:load) { |value| value } + expect(record).to validate_presence_of(:traits) + end end + end + + context 'and the serializer is JSON' do + it 'still works' do + record = record_validating_presence_of(:traits) do + serialize :traits, JSON + end - record = record_validating_presence_of(:data) do - serialize :data, serializer + expect(record).to validate_presence_of(:traits) end + end + + context 'and the serializer is something custom' do + it 'still works' do + serializer = Class.new do + define_singleton_method(:dump) { |value| value } + define_singleton_method(:load) { |value| value } + end + + record = record_validating_presence_of(:data) do + serialize :data, serializer + end - expect(record).to validate_presence_of(:data) + expect(record).to validate_presence_of(:data) + end end end end diff --git a/spec/unit/shoulda/matchers/active_record/serialize_matcher_spec.rb b/spec/unit/shoulda/matchers/active_record/serialize_matcher_spec.rb index 6f90583c3..9906e3137 100644 --- a/spec/unit/shoulda/matchers/active_record/serialize_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_record/serialize_matcher_spec.rb @@ -41,38 +41,40 @@ def unserialized_model context 'an attribute that will end up being serialized as YAML' do it 'accepts when the types match' do - expect(with_serialized_attr(Hash)).to serialize(:attr).as(Hash) + expect(with_serialized_attr(type: Hash, coder: JSON)).to serialize(:attr).as(Hash) end it 'rejects when the types do not match' do - expect(with_serialized_attr(Hash)).not_to serialize(:attr).as(String) + expect(with_serialized_attr(type: Hash)).not_to serialize(:attr).as(String) end it 'rejects when using as_instance_of' do - expect(with_serialized_attr(Hash)).not_to serialize(:attr).as_instance_of(Hash) + expect(with_serialized_attr(type: Hash)).not_to serialize(:attr).as_instance_of(Hash) end end context 'a serializer that is an instance of a class' do it 'accepts when using #as_instance_of' do define_serializer(:ExampleSerializer) - expect(with_serialized_attr(ExampleSerializer.new)). + expect(with_serialized_attr(coder: ExampleSerializer.new)). to serialize(:attr).as_instance_of(ExampleSerializer) end it 'rejects when using #as' do define_serializer(:ExampleSerializer) - expect(with_serialized_attr(ExampleSerializer.new)). + expect(with_serialized_attr(coder: ExampleSerializer.new)). not_to serialize(:attr).as(ExampleSerializer) end end - def with_serialized_attr(type = nil) + def with_serialized_attr(type: nil, coder: YAML) + type_or_coder = rails_version >= '7.1.0' ? nil : type || coder + define_model(:example, attr: :string) do if type - serialize :attr, type + serialize :attr, type_or_coder, type: type, coder: coder else - serialize :attr + serialize :attr, type_or_coder, coder: coder end end.new end