Commit Graph

33 Commits

Author SHA1 Message Date
Elliot Winkler afa6a7b666 Update RSpec test style across docs
Instead of using

    describe Foo do
      # ...
    end

use

    RSpec.describe Foo, type: :model do
      # ...
    end

instead. This is not exactly official, as the former style still works,
but the latter style is highly suggested in RSpec documentation and the
like, so this is what people are used to.

[ci skip]
2016-06-15 18:02:07 -06:00
Elliot Winkler 1189934806 Add ignoring_interference_by_writer to all matchers
`allow_value` matcher is, of course, concerned with setting values on a
particular attribute on a particular record, and then checking that the
record is valid after doing so. That comes with a caveat: if the
attribute is overridden in such a way so that the same value going into
the attribute isn't the same value coming out of it, then `allow_value`
will balk -- it'll say, "I can't do that because that changes how I
work."

That's all well and good, but what the attribute intentionally changes
incoming values? ActiveRecord's typecasting behavior, for instance,
would trigger such an exception. What if the developer needs a way to
get around this? This is where `ignoring_interference_by_writer` comes
into play. You can tack it on to the end of the matcher, and you're free
to go on your way.

So, prior to this commit you could already apply it to `allow_value`,
but now in this commit it also works on any other matcher.

But, one little thing: sometimes using this qualifier isn't going to
work. Perhaps you or something else actually *is* overriding the
attribute to change incoming values in a specific way, and perhaps the
value that comes out makes the record fail validation, and there's
nothing you can do about it. So in this case, even if you're using
`ignoring_interference_by_writer`, we want to inform you about what the
attribute is doing -- what the input and output was. And so we do.
2016-01-05 00:58:16 -07:00
Elliot Winkler 2962112114 allow_value: pre-set attributes before validation
While attempting to add support for `ignoring_interference_by_writer` to
the confirmation matcher, I was noticing that there are two attributes
we are concerned with: the attribute under test, and the confirmation
attribute -- for instance, `password` and `password_confirmation`. The
way that the matcher works, `password_confirmation` is set first on the
record before `password` is set, and then the whole record is validated.
This is fine, but I also noticed that `allow_value` has a specific way
of setting attributes -- not only does it check whether the attribute
being set exists and fail properly if it is does not, but it also
raises a CouldNotSetAttribute error if the attribute changes incoming
values. This logic needs to be performed on both `password_confirmation`
as well as `password`.

With that in mind, `allow_value` now supports a `values_to_preset=`
writer method which allows one to assign additional attributes unrelated
to the one being tested prior to validation. This will be used by the
confirmation matcher in a future commit.

This means that `allow_value` now operates in two steps:

1. Set attributes unrelated to the test, raising an error if any of the
   attributes do not exist on the model.
2. Set the attribute under test to one or more values, raising an error
   if the attribute does not exist, then running validations on the
   record, failing with an appropriate error message if the validations
   fail.

Note that the second step is similar to the first, although there are
more things involved. To that end, `allow_value` has been completely
refactored so that the logic for setting and validating attributes
happens in other places. Specifically, the core logic to set an
attribute (and capture the results) is located in a new AttributeSetter
class.

Also, the CouldNotSetAttributeError class has been moved to a namespace
and renamed to AttributeChangedValueError.

Finally, this commit fixes DisallowValueMatcher so that it is the true
opposite of AllowValueMatcher: DVM#matches? calls AVM#does_not_match?
and DVM#does_not_match? calls AVM#matches?.
2015-12-30 21:34:02 -05:00
Elliot Winkler 583be384c3 Refactor allow_value & disallow_value
This is part of a collection of commits that aim to improve failure
messages across the board, in order to make matchers easier to debug
when something goes wrong.

* Have the failure message describe more clearly what the `allow_value`
  matcher was trying to do when it failed.
* Make the description of the matcher more readable.
* For each value that `allow_value` sets, use a different Validator
  instance. The matcher still changes state as it runs, but a future
  commit will refactor this further.
* Merge StrictValidator back into Validator, and remove it. The way that
  StrictValidator worked (as a module that was mixed into an instance of
  Validator at runtime) was confusing, and there's really no need to
  split out the logic anymore.
* Fix or fill in tests involving failure messages and descriptions.
2015-12-13 20:22:22 -07:00
Elliot Winkler 8361f39579 Refactor ValidationMatcher
This is part of a collection of commits that aim to improve failure
messages across the board, in order to make matchers easier to debug
when something goes wrong.

In this case, we're improving ValidationMatcher, which applies to most
matchers except for numericality.

* Reword the overall failure message so that it includes the failure
  message for the last submatcher that failed.
* Extract code to build the description to BuildDescription. This code
  now checks to see whether certain qualifiers have been specified and
  includes information about them in the description, if present.
* Add a base implementation of `with_message` to ValidationMatcher.
* Add simple booleans to check whether `with_message` or `strict` have
  been specified.
2015-12-13 20:22:21 -07:00
Elliot Winkler ca0dbd8c0f Fix header in docs
[ci skip]
2015-09-30 13:19:52 -06:00
Elliot Winkler 142366ef16 Refer to Minitest in docs over Test::Unit
Minitest has been used instead of Test::Unit for quite some time now,
let's get with the times.

[ci skip]
2015-09-30 13:18:23 -06:00
Mauro George 73cf275d10 Stop handling AR RangeError when assign a attribute
Active Record 4.2.1 and upper no more raises a `RangeError` on the
assign, only at database level [1].

This will remove all code related to handling of Active Record `RangeError`.

Also, this upgrades the internal dependency on Rails 4.2 to 4.2.3.

[1]: fed7d7cd7c
2015-09-23 15:46:32 -06:00
Mauro George 2a45fc6e5a Document how to test conditional validations
Secondary-Author: Elliot Winkler <elliot.winkler@gmail.com>

[ci skip]
2015-09-23 14:04:42 -06:00
Elliot Winkler 9ba21381d7 Handle RangeErrors emitted now in ActiveRecord 4.2
In Rails 4.2, ActiveRecord was changed such that if you attempt to set
an attribute to a value and that value is outside the range of the
column, then it will raise a RangeError. For instance, an integer column
with a limit of 2 (i.e. a smallint) only accepts values between -32768
and +32767.

This means that if you try to do any of these three things, a RangeError
could be raised:

* Use validate_numericality_of along with any of the comparison
  submatchers and a value that sits on either side of the boundary.
* Use allow_value with a value that sits outside the range.
* Use validates_inclusion_of against an integer column. (Here we attempt
  to set that column to a non-integer value to verify that the attribute
  does not allow said value. That value is really a string version of a
  large number, so if the column does not take large numbers then the
  matcher could blow up.)

Ancillary changes in this commit:

* Remove ValidationMessageFinder and ExceptionMessageFinder in favor of
  Validator, StrictValidator, and ValidatorWithCapturedRangeError.
* The allow_value matcher now uses an instance of Validator under the
  hood. StrictValidator and/or ValidatorWithCapturedRangeError may be
  mixed into the Validator object as needed.
2015-01-22 21:05:09 -07:00
Elliot Winkler cf76d747ee Move uniqueness validation to ActiveRecord module 2014-12-13 17:53:16 -05:00
Elliot Winkler 7da90b1781 Deprecate ensure_length_of for validate_length_of 2014-11-05 11:39:53 -07:00
Adrià Planas 4ad15205a5 Fix uniqueness matcher when scope is a *_type attr
Secondary author: Elliot Winkler <elliot.winkler@gmail.com>

This commit fixes `validate_uniqueness_of` when used with `scoped_to` so
that when one of the scope attributes is a polymorphic *_type attribute
and the model has another validation on the same attribute, the matcher
does not fail with an error.

As a part of the matching process, `validate_uniqueness_of` tries to
create two records that have the same values for each of the attributes
passed to `scoped_to`, except one of the attributes has a different
value. This way, the second record should be valid because it shouldn't
clash with the first one. It does this one attribute at a time.

Let's say the attribute in question is a polymorphic *_type attribute.
The value of this attribute is intended to be the name of a model as a
string. Let's assume the first record has a meaningful value for this
attribute already and we're trying to find a value for the second
record. In order to produce such a value, `validate_uniqueness_of`
generally calls #next (a common method in Ruby to generate a succeeding
version of an object sequentially) on the first object's corresponding
value. So in the case of a *_type attribute, since it's a string, it
would call #next on that string. For instance, "User" would become
"Uses".

You might have noticed a problem with this, which is "what if Uses is
not a valid model?" This is okay as long as there's nothing that is
trying to access the polymorphic association. Because as soon as this
happens, Rails will attempt to find a record using the polymorphic type
-- in other words, it will try to find the model that the *_type
attribute corresponds to. One of the ways this can happen is if the
*_type attribute in question has a validation on it itself.

Let's look at an example.

Given these models:

``` ruby
class User < ActiveRecord::Base
end

class Favorite < ActiveRecord::Base
  belongs_to :favoriteable, polymorphic: true
  validates :favoriteable, presence: true
  validates :favoriteable_id, uniqueness: { scope: [:favoriteable_type] }
end

FactoryGirl.define do
  factory :user

  factory :favorite do
    association :favoriteable, factory: :user
  end
end
```

and the following test:

``` ruby
require 'rails_helper'

describe Favorite do
  context 'validations' do
    before { FactoryGirl.create(:favorite) }
    it do
      should validate_uniqueness_of(:favoriteable_id).
        scoped_to(:favoriteable_type)
    end
  end
end
```

prior to this commit, the test would have failed with:

```
Failures:

  1) Favorite validations should require case sensitive unique value for favoriteable_id scoped to favoriteable_type
     Failure/Error: should validate_uniqueness_of(:favoriteable_id).
     NameError:
       uninitialized constant Uses
     # ./spec/models/favorite_spec.rb:6:in `block (3 levels) in <top (required)>'
```

Here, a Favorite record is created where `favoriteable_type` is set to
"Uses", and then validations are run on that record. The presence
validation on `favoriteable_type` is run which tries to access a "Uses"
model. But that model doesn't exist, so the test raises an error.

Now `validates_uniqueness_of` will set the *_type attribute to a
meaningful value. It still does this by calling #next on the first
record's value, but then it makes a new model that is simply an alias
for the original model. Hence, in our example, Uses would become a model
that is aliased to User.
2014-10-08 23:22:54 -06:00
Maxim Kaschenko acb02d0448 ensure_exclusion_of => validate_exclusion_of 2014-07-23 19:39:16 -06:00
Maxim Kaschenko 93c2336e98 ensure_inclusion_of => validate_inclusion_of 2014-07-21 23:01:40 -06:00
Elliot Winkler c22d7c89e0 Extract examples in README to inline documentation 2014-06-20 16:41:27 -06:00
Yukio Mizuta 806716c79c Improve ValidateNumericalityOfMatcher document expression 2014-01-31 09:47:50 -07:00
Elliot Winkler fcbb5c485e Convert to 1.9 hash syntax 2014-01-17 13:20:44 -07:00
jmpage 2cdae242ca Add validates_absence_of matcher 2013-12-23 19:21:22 -07:00
Elliot Winkler 27c065ba55 Move ValidateNumericalityOf submatchers to a subfolder 2013-12-14 12:04:36 -07:00
Matt Daubert 5815d38fa0 Add have_secure_password matcher 2013-09-05 19:51:04 -06:00
Tiago Cardoso 9e77132b34 Add comparison submatchers to validate_numericality_of
You can now use the following with validate_numericality_of:

* is_greater_than (corresponds to :greater_than)
* is_greater_than_or_equal_to (corresponds to :greater_than_or_equal_to)
* is_equal_to (corresponds to :equal_to)
* is_less_than (corresponds_to :less_than)
* is_less_than_or_equal_to (corresponds_to :less_than_or_equal_to)
2013-05-23 21:08:40 -06:00
Abd ar-Rahman 8aa860dc2d Add odd & even number matchers for numericality matcher 2013-04-26 10:51:40 -04:00
Melissa Xie 53246538cf Remove 'validate_format_of' matcher
* See issue #252
2013-04-03 13:08:27 -04:00
Gabe Berke-Williams af8f9a7bff Use current thoughtbot style in specs 2012-12-26 22:45:54 -05:00
Reade Harris 6ba8c9f1a1 Use submatchers instead of inheritance for ValidatesNumericalityMatcher. 2012-11-09 09:35:20 -05:00
Jason Draper 1a71d16238 Try next values in array only.
If we can't find a valid next value outside of each item
in the array, throw a custom exception.
2012-10-05 12:58:43 -04:00
Joe Ferris fde078da02 Add documentation for strict validation testing 2012-09-14 09:59:24 -04:00
Joe Ferris 87327c225a Extract ExceptionMessageFinder from AllowValueMatcher
* Replaces repeated conditional with polymorphism
2012-09-12 20:48:30 -04:00
Joe Ferris 3dbf5d4f0d Extract ValidationMessageFinder from AllowValueMatcher 2012-09-12 20:25:33 -04:00
Blake Thomson 1b5fc27cf8 Add validates_confirmation_of matcher 2011-12-15 11:42:26 -05:00
Vasiliy Ermolovich d8308ec74d add ensure_exclusion_of 2011-10-16 20:49:58 +03:00
Markus Schwed 2a54fdb28d Seperate ActiveRecord and ActiveModel related matchers 2011-05-06 15:56:36 +02:00