Skip to content

Commit

Permalink
allow_value: Fix compatibility with enum columns
Browse files Browse the repository at this point in the history
When used against an attribute that's an enum, `allow_value` will raise
an AttributeValueChangedError. This commit prevents that from happening.
  • Loading branch information
mcmire committed Jan 5, 2016
1 parent 335971a commit 9e8603e
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 7 deletions.
21 changes: 15 additions & 6 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
# HEAD

### Bug fixes

* Update `validate_numericality_of` so that submatchers are applied lazily
instead of immediately. Previously, qualifiers were order-dependent, meaning
that if you used `strict` before you used, say, `odd`, then `strict` wouldn't
actually apply to `odd`. Now the order that you specify qualifiers doesn't
matter.

* Fix `allow_value` so that it does not raise an AttributeChangedValueError
(formerly CouldNotSetAttributeError) when used against an attribute that is an
enum in an ActiveRecord model.

### Improvements

* Improve failure messages and descriptions of all matchers across the board so
that it is easier to understand what the matcher was doing when it failed.
(You'll see a huge difference in the output of the numericality and uniqueness
matchers in particular.)

* The CouldNotSetAttributeError that you have probably seen is now called
AttributeChangedValueError.

* Matchers now raise an error if any attributes that the matcher is attempting
to set do not exist on the model.

* Update `validate_numericality_of` so that submatchers are applied lazily
instead of immediately. Previously, qualifiers were order-dependent, meaning
that if you used `strict` before you used, say, `odd`, then `strict` wouldn't
actually apply to `odd`. Now the order that you specify qualifiers doesn't
matter.

* Update `validate_numericality_of` so that it doesn't always run all of the
submatchers, but stops on the first one that fails. Since failure messages
now contain information as to what value the matcher set on the attribute when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def set

@result_of_checking = successful_check

if attribute_changed_value? && !ignoring_interference_by_writer?
if raise_attribute_changed_value_error?
@result_of_setting = attribute_changed_value_error
false
else
Expand Down Expand Up @@ -156,6 +156,33 @@ def ignoring_interference_by_writer?
!!@ignoring_interference_by_writer
end

def raise_attribute_changed_value_error?
attribute_changed_value? &&
!(attribute_is_an_enum? && value_read_is_expected_for_an_enum?) &&
!ignoring_interference_by_writer?
end

def attribute_is_an_enum?
enum_values.any?
end

def value_read_is_expected_for_an_enum?
enum_values.key?(value_read) &&
enum_values[value_read] == value_written
end

def enum_values
defined_enums.fetch(attribute_name.to_s, {})
end

def defined_enums
if model.respond_to?(:defined_enums)
model.defined_enums
else
{}
end
end

def successful_check
SuccessfulCheck.new
end
Expand Down Expand Up @@ -192,6 +219,10 @@ def attribute_does_not_exist_error
def active_resource_object?
object.respond_to?(:known_attributes)
end

def model
object.class
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -641,4 +641,18 @@ def name=(_value)
end
end
end

if active_record_supports_enum?
context 'given an ActiveRecord model' do
context 'where the attribute under test is an enum and the given value is a value in that enum' do
it 'accepts' do
model = define_model('Shipment', status: :integer) do
enum status: { pending: 1, shipped: 2, delivered: 3 }
end

expect(model.new).to allow_value(1).for(:status)
end
end
end
end
end

0 comments on commit 9e8603e

Please sign in to comment.