Skip to content

Commit

Permalink
Refine ActiveRecord::SerializeMatcher for Rails 7.1 compatibility
Browse files Browse the repository at this point in the history
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: rails/rails#47463

This commit adjusts the `ActiveRecord::SerializeMatcher` the public
API for the `serialize` method has changed on Rails 7.1, so we
had to
  • Loading branch information
matsales28 committed Oct 27, 2023
1 parent 2be4162 commit e836d24
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 33 deletions.
2 changes: 1 addition & 1 deletion lib/shoulda/matchers/rails_shim.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def supports_full_attributes_api?(model)
end

def validates_column_options?
active_record_version >= '7.1.0'
Gem::Requirement.new('>= 7.1.0').satisfied_by?(active_record_version)
end

private
Expand Down
2 changes: 1 addition & 1 deletion spec/support/unit/attribute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class Attribute
DEFAULT_COLUMN_TYPE = :string
DEFAULT_COLUMN_OPTIONS = {
null: false,
array: false
array: false,
}.freeze

def initialize(args)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
Expand Down
18 changes: 10 additions & 8 deletions spec/unit/shoulda/matchers/active_record/serialize_matcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 ? 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
Expand Down

0 comments on commit e836d24

Please sign in to comment.