Fix inclusion matcher w/ date & datetime attrs
Why: * The inclusion matcher (when used with the `in_array` qualifier) makes the assertion that when the attribute is set to a value that is outside the given array, the record in question is invalid. The issue is that when used with a date or datetime attribute, the arbitrary value the matcher chose was a string. This was getting typecast and so the matcher was throwing a CouldNotSetAttributeError. To satisfy the above: * If the column is a date, use a Date for the arbitrary value * If the column is a datetime, use a DateTime for the arbitrary value * If the column is a time, use a Time for the arbitrary value
This commit is contained in:
parent
5a5af1089a
commit
8fa97b4ff3
7
NEWS.md
7
NEWS.md
|
@ -1,3 +1,10 @@
|
||||||
|
# HEAD
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
* Fix `validate_inclusion_of` + `in_array` when used against a date or datetime
|
||||||
|
attribute so that it does not raise a CouldNotSetAttributeError.
|
||||||
|
|
||||||
# 3.0.0
|
# 3.0.0
|
||||||
|
|
||||||
### Backward-incompatible changes
|
### Backward-incompatible changes
|
||||||
|
|
|
@ -263,9 +263,12 @@ module Shoulda
|
||||||
|
|
||||||
# @private
|
# @private
|
||||||
class ValidateInclusionOfMatcher < ValidationMatcher
|
class ValidateInclusionOfMatcher < ValidationMatcher
|
||||||
ARBITRARY_OUTSIDE_STRING = 'shouldamatchersteststring'
|
ARBITRARY_OUTSIDE_STRING = 'shoulda-matchers test string'
|
||||||
ARBITRARY_OUTSIDE_FIXNUM = 123456789
|
ARBITRARY_OUTSIDE_FIXNUM = 123456789
|
||||||
ARBITRARY_OUTSIDE_DECIMAL = BigDecimal.new('0.123456789')
|
ARBITRARY_OUTSIDE_DECIMAL = BigDecimal.new('0.123456789')
|
||||||
|
ARBITRARY_OUTSIDE_DATE = Date.jd(9999999)
|
||||||
|
ARBITRARY_OUTSIDE_DATETIME = DateTime.jd(9999999)
|
||||||
|
ARBITRARY_OUTSIDE_TIME = Time.at(9999999999)
|
||||||
BOOLEAN_ALLOWS_BOOLEAN_MESSAGE = <<EOT
|
BOOLEAN_ALLOWS_BOOLEAN_MESSAGE = <<EOT
|
||||||
You are using `validate_inclusion_of` to assert that a boolean column allows
|
You are using `validate_inclusion_of` to assert that a boolean column allows
|
||||||
boolean values and disallows non-boolean ones. Be aware that it is not possible
|
boolean values and disallows non-boolean ones. Be aware that it is not possible
|
||||||
|
@ -447,6 +450,12 @@ EOT
|
||||||
[ARBITRARY_OUTSIDE_FIXNUM]
|
[ARBITRARY_OUTSIDE_FIXNUM]
|
||||||
when :decimal
|
when :decimal
|
||||||
[ARBITRARY_OUTSIDE_DECIMAL]
|
[ARBITRARY_OUTSIDE_DECIMAL]
|
||||||
|
when :date
|
||||||
|
[ARBITRARY_OUTSIDE_DATE]
|
||||||
|
when :datetime
|
||||||
|
[ARBITRARY_OUTSIDE_DATETIME]
|
||||||
|
when :time
|
||||||
|
[ARBITRARY_OUTSIDE_TIME]
|
||||||
else
|
else
|
||||||
[ARBITRARY_OUTSIDE_STRING]
|
[ARBITRARY_OUTSIDE_STRING]
|
||||||
end
|
end
|
||||||
|
@ -492,9 +501,9 @@ EOT
|
||||||
|
|
||||||
def column_type_to_attribute_type(type)
|
def column_type_to_attribute_type(type)
|
||||||
case type
|
case type
|
||||||
when :boolean, :decimal then type
|
|
||||||
when :integer, :float then :fixnum
|
when :integer, :float then :fixnum
|
||||||
else :default
|
when :timestamp then :datetime
|
||||||
|
else type
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -503,7 +512,10 @@ EOT
|
||||||
when true, false then :boolean
|
when true, false then :boolean
|
||||||
when BigDecimal then :decimal
|
when BigDecimal then :decimal
|
||||||
when Fixnum then :fixnum
|
when Fixnum then :fixnum
|
||||||
else :default
|
when Date then :date
|
||||||
|
when DateTime then :datetime
|
||||||
|
when Time then :time
|
||||||
|
else :unknown
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -75,7 +75,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "against a float attribute" do
|
context 'against a float attribute' do
|
||||||
it_behaves_like 'it supports in_array',
|
it_behaves_like 'it supports in_array',
|
||||||
possible_values: [1.0, 2.0, 3.0, 4.0, 5.0],
|
possible_values: [1.0, 2.0, 3.0, 4.0, 5.0],
|
||||||
zero: 0.0,
|
zero: 0.0,
|
||||||
|
@ -97,7 +97,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "against a decimal attribute" do
|
context 'against a decimal attribute' do
|
||||||
it_behaves_like 'it supports in_array',
|
it_behaves_like 'it supports in_array',
|
||||||
possible_values: [1.0, 2.0, 3.0, 4.0, 5.0].map { |number|
|
possible_values: [1.0, 2.0, 3.0, 4.0, 5.0].map { |number|
|
||||||
BigDecimal.new(number.to_s)
|
BigDecimal.new(number.to_s)
|
||||||
|
@ -121,6 +121,72 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'against a date attribute' do
|
||||||
|
today = Date.today
|
||||||
|
|
||||||
|
it_behaves_like 'it supports in_array',
|
||||||
|
possible_values: (1..5).map { |n| today + n },
|
||||||
|
reserved_outside_value: described_class::ARBITRARY_OUTSIDE_DATE
|
||||||
|
|
||||||
|
it_behaves_like 'it supports in_range',
|
||||||
|
possible_values: (today .. today + 5)
|
||||||
|
|
||||||
|
define_method :build_object do |options = {}, &block|
|
||||||
|
build_object_with_generic_attribute(
|
||||||
|
options.merge(column_type: :date, value: today),
|
||||||
|
&block
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_outside_value_to(values)
|
||||||
|
values + [values.last + 1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'against a datetime attribute (using DateTime)' do
|
||||||
|
now = DateTime.now
|
||||||
|
|
||||||
|
it_behaves_like 'it supports in_array',
|
||||||
|
possible_values: (1..5).map { |n| now + n },
|
||||||
|
reserved_outside_value: described_class::ARBITRARY_OUTSIDE_DATETIME
|
||||||
|
|
||||||
|
it_behaves_like 'it supports in_range',
|
||||||
|
possible_values: (now .. now + 5)
|
||||||
|
|
||||||
|
define_method :build_object do |options = {}, &block|
|
||||||
|
build_object_with_generic_attribute(
|
||||||
|
options.merge(column_type: :datetime, value: now),
|
||||||
|
&block
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_outside_value_to(values)
|
||||||
|
values + [values.last + 1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'against a datetime attribute (using Time)' do
|
||||||
|
now = Time.now
|
||||||
|
|
||||||
|
it_behaves_like 'it supports in_array',
|
||||||
|
possible_values: (1..5).map { |n| now + n },
|
||||||
|
reserved_outside_value: described_class::ARBITRARY_OUTSIDE_TIME
|
||||||
|
|
||||||
|
it_behaves_like 'it supports in_range',
|
||||||
|
possible_values: (now .. now + 5)
|
||||||
|
|
||||||
|
define_method :build_object do |options = {}, &block|
|
||||||
|
build_object_with_generic_attribute(
|
||||||
|
options.merge(column_type: :time, value: now),
|
||||||
|
&block
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_outside_value_to(values)
|
||||||
|
values + [values.last + 1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'against a string attribute' do
|
context 'against a string attribute' do
|
||||||
it_behaves_like 'it supports in_array',
|
it_behaves_like 'it supports in_array',
|
||||||
possible_values: %w(foo bar baz),
|
possible_values: %w(foo bar baz),
|
||||||
|
@ -270,7 +336,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
||||||
end
|
end
|
||||||
|
|
||||||
if zero
|
if zero
|
||||||
it 'matches when one of the given values is a 0' do
|
it 'matches when one of the given values is a zero' do
|
||||||
valid_values = possible_values + [zero]
|
valid_values = possible_values + [zero]
|
||||||
builder = build_object_allowing(valid_values)
|
builder = build_object_allowing(valid_values)
|
||||||
expect_to_match_on_values(builder, valid_values)
|
expect_to_match_on_values(builder, valid_values)
|
||||||
|
@ -527,7 +593,6 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def build_object_with_generic_attribute(options = {}, &block)
|
def build_object_with_generic_attribute(options = {}, &block)
|
||||||
attribute_name = :attr
|
attribute_name = :attr
|
||||||
column_type = options.fetch(:column_type)
|
column_type = options.fetch(:column_type)
|
||||||
|
|
Loading…
Reference in New Issue