2011-05-06 11:51:12 +00:00
|
|
|
require 'shoulda/matchers/active_model/helpers'
|
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.
2013-10-09 23:20:07 +00:00
|
|
|
require 'shoulda/matchers/active_model/uniqueness'
|
2011-05-06 11:51:12 +00:00
|
|
|
require 'shoulda/matchers/active_model/validation_matcher'
|
2012-09-13 00:23:18 +00:00
|
|
|
require 'shoulda/matchers/active_model/validation_message_finder'
|
2012-09-13 00:41:43 +00:00
|
|
|
require 'shoulda/matchers/active_model/exception_message_finder'
|
2011-05-06 11:51:12 +00:00
|
|
|
require 'shoulda/matchers/active_model/allow_value_matcher'
|
2012-10-16 17:45:06 +00:00
|
|
|
require 'shoulda/matchers/active_model/disallow_value_matcher'
|
2011-05-06 11:51:12 +00:00
|
|
|
require 'shoulda/matchers/active_model/ensure_length_of_matcher'
|
2014-04-24 04:25:34 +00:00
|
|
|
require 'shoulda/matchers/active_model/validate_inclusion_of_matcher'
|
2014-05-10 08:59:21 +00:00
|
|
|
require 'shoulda/matchers/active_model/validate_exclusion_of_matcher'
|
2013-12-05 16:38:30 +00:00
|
|
|
require 'shoulda/matchers/active_model/validate_absence_of_matcher'
|
2011-05-06 11:51:12 +00:00
|
|
|
require 'shoulda/matchers/active_model/validate_presence_of_matcher'
|
|
|
|
require 'shoulda/matchers/active_model/validate_uniqueness_of_matcher'
|
|
|
|
require 'shoulda/matchers/active_model/validate_acceptance_of_matcher'
|
2011-10-23 13:41:37 +00:00
|
|
|
require 'shoulda/matchers/active_model/validate_confirmation_of_matcher'
|
2011-05-06 11:51:12 +00:00
|
|
|
require 'shoulda/matchers/active_model/validate_numericality_of_matcher'
|
2014-01-13 01:43:36 +00:00
|
|
|
require 'shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher'
|
2013-11-01 21:57:42 +00:00
|
|
|
require 'shoulda/matchers/active_model/numericality_matchers/comparison_matcher'
|
2014-01-13 01:43:36 +00:00
|
|
|
require 'shoulda/matchers/active_model/numericality_matchers/odd_number_matcher'
|
|
|
|
require 'shoulda/matchers/active_model/numericality_matchers/even_number_matcher'
|
2013-11-01 21:57:42 +00:00
|
|
|
require 'shoulda/matchers/active_model/numericality_matchers/only_integer_matcher'
|
2011-05-06 11:51:12 +00:00
|
|
|
require 'shoulda/matchers/active_model/allow_mass_assignment_of_matcher'
|
2012-09-28 17:35:15 +00:00
|
|
|
require 'shoulda/matchers/active_model/errors'
|
2013-08-18 17:17:19 +00:00
|
|
|
require 'shoulda/matchers/active_model/have_secure_password_matcher'
|
2011-05-06 11:51:12 +00:00
|
|
|
|
|
|
|
module Shoulda
|
|
|
|
module Matchers
|
|
|
|
module ActiveModel
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|