Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Numericality validation of virtual attributes #822

Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,8 @@ def given_numeric_column?
private

def column_type
if @subject.class.respond_to?(:columns_hash)
if @subject.class.respond_to?(:columns_hash) &&
@subject.class.columns_hash[@attribute.to_s]
@subject.class.columns_hash[@attribute.to_s].type
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ def default_validation_values
expect(record).to validate_numericality
end

context 'when the attribute is a virtual' do
it 'accepts' do
record = build_record_validating_numericality_of_virtual_attribute
expect(record).to validate_virtual_attribute_numericality
end
end

context 'when the column is an integer column' do
it 'raises an IneffectiveTestError' do
record = build_record_validating_numericality(
Expand Down Expand Up @@ -224,6 +231,15 @@ def default_validation_values
expect(record).to validate_numericality.odd
end

context 'when the attribute is a virtual' do
it 'accepts' do
record = build_record_validating_numericality_of_virtual_attribute(
odd: true,
)
expect(record).to validate_virtual_attribute_numericality.odd
end
end

context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
Expand Down Expand Up @@ -278,6 +294,15 @@ def default_validation_values
expect(record).to validate_numericality.even
end

context 'when the attribute is a virtual' do
it 'accepts' do
record = build_record_validating_numericality_of_virtual_attribute(
even: true,
)
expect(record).to validate_virtual_attribute_numericality.even
end
end

context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
Expand Down Expand Up @@ -332,6 +357,16 @@ def default_validation_values
expect(record).to validate_numericality.is_less_than_or_equal_to(18)
end

context 'when the attribute is a virtual' do
it 'accepts' do
record = build_record_validating_numericality_of_virtual_attribute(
less_than_or_equal_to: 18,
)
expect(record).to validate_virtual_attribute_numericality.
is_less_than_or_equal_to(18)
end
end

context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
Expand Down Expand Up @@ -390,6 +425,16 @@ def default_validation_values
is_less_than(18)
end

context 'when the attribute is a virtual' do
it 'accepts' do
record = build_record_validating_numericality_of_virtual_attribute(
less_than: 18,
)
expect(record).to validate_virtual_attribute_numericality.
is_less_than(18)
end
end

context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
Expand Down Expand Up @@ -446,6 +491,16 @@ def default_validation_values
expect(record).to validate_numericality.is_equal_to(18)
end

context 'when the attribute is a virtual' do
it 'accepts' do
record = build_record_validating_numericality_of_virtual_attribute(
equal_to: 18,
)
expect(record).to validate_virtual_attribute_numericality.
is_equal_to(18)
end
end

context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
Expand Down Expand Up @@ -504,6 +559,16 @@ def default_validation_values
is_greater_than_or_equal_to(18)
end

context 'when the attribute is a virtual' do
it 'accepts' do
record = build_record_validating_numericality_of_virtual_attribute(
greater_than_or_equal_to: 18,
)
expect(record).to validate_virtual_attribute_numericality.
is_greater_than_or_equal_to(18)
end
end

context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
Expand Down Expand Up @@ -568,6 +633,16 @@ def default_validation_values
is_greater_than(18)
end

context 'when the attribute is a virtual' do
it 'accepts' do
record = build_record_validating_numericality_of_virtual_attribute(
greater_than: 18,
)
expect(record).to validate_virtual_attribute_numericality.
is_greater_than(18)
end
end

context 'when the column is an integer column' do
it 'accepts (and does not raise an error)' do
record = build_record_validating_numericality(
Expand Down Expand Up @@ -1141,6 +1216,21 @@ def define_model_validating_numericality(options = {})
end
end

def define_model_validating_numericality_of_virtual_attribute(options = {})
virtual_attr = options.delete(:attribute_name) do
attribute_name
end

define_model 'Example' do |model|
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

define_model creates an ActiveRecord model. We also have a define_active_model helper you could use that defines an ActiveModel::Model model instead. That means you could drop the next line actually. So it would look like:

define_active_model('Example', accessors: [virtual_attr]) do |model|

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize that's not what your use case is, but the numericality matcher should work for both ActiveRecord and ActiveModel models, and we don't have tests for ActiveModel here I'm noticing. So we could kill two birds with one stone. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An ActiveModel model would not have tripped this problem with virtual attributes though because it would not have responded to columns_hash in the first place. That check was already there. This specifically happens with an ActiveRecord model using virtual attributes as in has_secure_password.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah... yup, that makes sense.

model.send(:attr_accessor, virtual_attr)
model.validates_numericality_of(virtual_attr, options)
end
end

def build_record_validating_numericality_of_virtual_attribute(options = {})
define_model_validating_numericality_of_virtual_attribute(options).new
end

def build_record_validating_numericality(options = {})
define_model_validating_numericality(options).new
end
Expand All @@ -1157,6 +1247,10 @@ def validate_numericality
validate_numericality_of(attribute_name)
end

def validate_virtual_attribute_numericality
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably remove this too, right? Since this is now the same as validate_numericality.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

validate_numericality_of(attribute_name)
end

def attribute_name
:attr
end
Expand Down