thoughtbot--shoulda-matchers/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb

139 lines
4.0 KiB
Ruby
Raw Normal View History

module Shoulda
2013-12-05 16:38:30 +00:00
module Matchers
module ActiveModel
# The `validate_absence_of` matcher tests the usage of the
# `validates_absence_of` validation.
#
# class PowerHungryCountry
# include ActiveModel::Model
# attr_accessor :nuclear_weapons
#
# validates_absence_of :nuclear_weapons
# end
#
# # RSpec
# RSpec.describe PowerHungryCountry, type: :model do
# it { should validate_absence_of(:nuclear_weapons) }
# end
#
# # Minitest (Shoulda)
# class PowerHungryCountryTest < ActiveSupport::TestCase
# should validate_absence_of(:nuclear_weapons)
# end
#
# #### Qualifiers
#
# ##### on
#
# Use `on` if your validation applies only under a certain context.
#
# class PowerHungryCountry
# include ActiveModel::Model
# attr_accessor :nuclear_weapons
#
# validates_absence_of :nuclear_weapons, on: :create
# end
#
# # RSpec
# RSpec.describe PowerHungryCountry, type: :model do
# it { should validate_absence_of(:nuclear_weapons).on(:create) }
# end
#
# # Minitest (Shoulda)
# class PowerHungryCountryTest < ActiveSupport::TestCase
# should validate_absence_of(:nuclear_weapons).on(:create)
# end
#
# ##### with_message
2013-12-05 16:38:30 +00:00
#
# Use `with_message` if you are using a custom validation message.
#
# class PowerHungryCountry
# include ActiveModel::Model
# attr_accessor :nuclear_weapons
#
# validates_absence_of :nuclear_weapons,
# message: "there shall be peace on Earth"
# end
#
# # RSpec
# RSpec.describe PowerHungryCountry, type: :model do
# it do
# should validate_absence_of(:nuclear_weapons).
# with_message("there shall be peace on Earth")
# end
# end
#
# # Minitest (Shoulda)
# class PowerHungryCountryTest < ActiveSupport::TestCase
# should validate_absence_of(:nuclear_weapons).
# with_message("there shall be peace on Earth")
# end
#
# @return [ValidateAbsenceOfMatcher}
2013-12-05 16:38:30 +00:00
#
def validate_absence_of(attr)
ValidateAbsenceOfMatcher.new(attr)
end
# @private
class ValidateAbsenceOfMatcher < ValidationMatcher
def initialize(attribute)
super
@expected_message = :present
2013-12-05 16:38:30 +00:00
end
def matches?(subject)
super(subject)
disallows_value_of(value, @expected_message)
end
def simple_description
"validate that :#{@attribute} is empty/falsy"
2013-12-05 16:38:30 +00:00
end
private
def value
if reflection
obj = reflection.klass.new
if collection?
[ obj ]
else
obj
end
else
Tighten CouldNotSetAttributeError restriction Why: * Previously, `allow_value` would raise a CouldNotSetAttributeError if the value being set didn't match the value the attribute had after being set, but only if the attribute was being changed from nil to non-nil or non-nil to nil. * It turns out it doesn't matter which value you're trying to set the attribute to -- if the attribute rejects that change it's confusing either way. (In fact, I was recently bit by a case in which I was trying to validate numericality of an attribute, where the writer method for that attribute was overridden to ensure that the attribute always stored a number and never contained non-number characters. This ended up making the numericality validation useless, of course -- but it caused confusion because the test acted in a way I didn't expect.) To satisfy the above: * `allow_value` now raises a CouldNotSetAttributeError if the attribute rejects the value being set in *any* way. * However, add a `ignoring_interference_by_writer` qualifier so that it is possible to manually override this behavior. * Fix tests that are failing now because of this new change: * Fix tests for allow_value matcher * Fix tests for numericality matcher * Remove tests for numericality matcher + integer column * An integer column will typecast any non-integer value to an integer. * Because of the typecasting, our tests for the numericality matcher against an integer column don't quite work, because we can't really test what happens when the attribute is set to a non-integer value. Now that `allow_value` is more strict, we're getting a CouldNotSetAttributeError when attempting to do so. * The tests mentioned were originally added to ensure that we are handling RangeErrors that ActiveRecord used to emit. This doesn't happen anymore, so the tests aren't necessary anymore either. * Fix tests for acceptance matcher * Fix tests for absence matcher
2015-09-26 05:10:00 +00:00
case column_type
when :integer, :float then 1
when :decimal then BigDecimal(1, 0)
Tighten CouldNotSetAttributeError restriction Why: * Previously, `allow_value` would raise a CouldNotSetAttributeError if the value being set didn't match the value the attribute had after being set, but only if the attribute was being changed from nil to non-nil or non-nil to nil. * It turns out it doesn't matter which value you're trying to set the attribute to -- if the attribute rejects that change it's confusing either way. (In fact, I was recently bit by a case in which I was trying to validate numericality of an attribute, where the writer method for that attribute was overridden to ensure that the attribute always stored a number and never contained non-number characters. This ended up making the numericality validation useless, of course -- but it caused confusion because the test acted in a way I didn't expect.) To satisfy the above: * `allow_value` now raises a CouldNotSetAttributeError if the attribute rejects the value being set in *any* way. * However, add a `ignoring_interference_by_writer` qualifier so that it is possible to manually override this behavior. * Fix tests that are failing now because of this new change: * Fix tests for allow_value matcher * Fix tests for numericality matcher * Remove tests for numericality matcher + integer column * An integer column will typecast any non-integer value to an integer. * Because of the typecasting, our tests for the numericality matcher against an integer column don't quite work, because we can't really test what happens when the attribute is set to a non-integer value. Now that `allow_value` is more strict, we're getting a CouldNotSetAttributeError when attempting to do so. * The tests mentioned were originally added to ensure that we are handling RangeErrors that ActiveRecord used to emit. This doesn't happen anymore, so the tests aren't necessary anymore either. * Fix tests for acceptance matcher * Fix tests for absence matcher
2015-09-26 05:10:00 +00:00
when :datetime, :time, :timestamp then Time.now
when :date then Date.new
when :binary then '0'
Tighten CouldNotSetAttributeError restriction Why: * Previously, `allow_value` would raise a CouldNotSetAttributeError if the value being set didn't match the value the attribute had after being set, but only if the attribute was being changed from nil to non-nil or non-nil to nil. * It turns out it doesn't matter which value you're trying to set the attribute to -- if the attribute rejects that change it's confusing either way. (In fact, I was recently bit by a case in which I was trying to validate numericality of an attribute, where the writer method for that attribute was overridden to ensure that the attribute always stored a number and never contained non-number characters. This ended up making the numericality validation useless, of course -- but it caused confusion because the test acted in a way I didn't expect.) To satisfy the above: * `allow_value` now raises a CouldNotSetAttributeError if the attribute rejects the value being set in *any* way. * However, add a `ignoring_interference_by_writer` qualifier so that it is possible to manually override this behavior. * Fix tests that are failing now because of this new change: * Fix tests for allow_value matcher * Fix tests for numericality matcher * Remove tests for numericality matcher + integer column * An integer column will typecast any non-integer value to an integer. * Because of the typecasting, our tests for the numericality matcher against an integer column don't quite work, because we can't really test what happens when the attribute is set to a non-integer value. Now that `allow_value` is more strict, we're getting a CouldNotSetAttributeError when attempting to do so. * The tests mentioned were originally added to ensure that we are handling RangeErrors that ActiveRecord used to emit. This doesn't happen anymore, so the tests aren't necessary anymore either. * Fix tests for acceptance matcher * Fix tests for absence matcher
2015-09-26 05:10:00 +00:00
else 'an arbitrary value'
end
2013-12-05 16:38:30 +00:00
end
end
Tighten CouldNotSetAttributeError restriction Why: * Previously, `allow_value` would raise a CouldNotSetAttributeError if the value being set didn't match the value the attribute had after being set, but only if the attribute was being changed from nil to non-nil or non-nil to nil. * It turns out it doesn't matter which value you're trying to set the attribute to -- if the attribute rejects that change it's confusing either way. (In fact, I was recently bit by a case in which I was trying to validate numericality of an attribute, where the writer method for that attribute was overridden to ensure that the attribute always stored a number and never contained non-number characters. This ended up making the numericality validation useless, of course -- but it caused confusion because the test acted in a way I didn't expect.) To satisfy the above: * `allow_value` now raises a CouldNotSetAttributeError if the attribute rejects the value being set in *any* way. * However, add a `ignoring_interference_by_writer` qualifier so that it is possible to manually override this behavior. * Fix tests that are failing now because of this new change: * Fix tests for allow_value matcher * Fix tests for numericality matcher * Remove tests for numericality matcher + integer column * An integer column will typecast any non-integer value to an integer. * Because of the typecasting, our tests for the numericality matcher against an integer column don't quite work, because we can't really test what happens when the attribute is set to a non-integer value. Now that `allow_value` is more strict, we're getting a CouldNotSetAttributeError when attempting to do so. * The tests mentioned were originally added to ensure that we are handling RangeErrors that ActiveRecord used to emit. This doesn't happen anymore, so the tests aren't necessary anymore either. * Fix tests for acceptance matcher * Fix tests for absence matcher
2015-09-26 05:10:00 +00:00
def column_type
2013-12-05 16:38:30 +00:00
@subject.class.respond_to?(:columns_hash) &&
Tighten CouldNotSetAttributeError restriction Why: * Previously, `allow_value` would raise a CouldNotSetAttributeError if the value being set didn't match the value the attribute had after being set, but only if the attribute was being changed from nil to non-nil or non-nil to nil. * It turns out it doesn't matter which value you're trying to set the attribute to -- if the attribute rejects that change it's confusing either way. (In fact, I was recently bit by a case in which I was trying to validate numericality of an attribute, where the writer method for that attribute was overridden to ensure that the attribute always stored a number and never contained non-number characters. This ended up making the numericality validation useless, of course -- but it caused confusion because the test acted in a way I didn't expect.) To satisfy the above: * `allow_value` now raises a CouldNotSetAttributeError if the attribute rejects the value being set in *any* way. * However, add a `ignoring_interference_by_writer` qualifier so that it is possible to manually override this behavior. * Fix tests that are failing now because of this new change: * Fix tests for allow_value matcher * Fix tests for numericality matcher * Remove tests for numericality matcher + integer column * An integer column will typecast any non-integer value to an integer. * Because of the typecasting, our tests for the numericality matcher against an integer column don't quite work, because we can't really test what happens when the attribute is set to a non-integer value. Now that `allow_value` is more strict, we're getting a CouldNotSetAttributeError when attempting to do so. * The tests mentioned were originally added to ensure that we are handling RangeErrors that ActiveRecord used to emit. This doesn't happen anymore, so the tests aren't necessary anymore either. * Fix tests for acceptance matcher * Fix tests for absence matcher
2015-09-26 05:10:00 +00:00
@subject.class.columns_hash[@attribute.to_s].respond_to?(:type) &&
@subject.class.columns_hash[@attribute.to_s].type
2013-12-05 16:38:30 +00:00
end
def collection?
if reflection
[:has_many, :has_and_belongs_to_many].include?(reflection.macro)
else
false
end
end
def reflection
@subject.class.respond_to?(:reflect_on_association) &&
@subject.class.reflect_on_association(@attribute)
end
end
end
end
end