1
0
Fork 0
mirror of https://github.com/thoughtbot/shoulda-matchers.git synced 2022-11-09 12:01:38 -05:00
thoughtbot--shoulda-matchers/spec/support
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
..
shared_examples Update numericality comarison matcher more accurate 2014-02-26 19:48:29 -08:00
active_model_versions.rb Fix allow_mass_assignment specs in Rails 4 2013-08-16 16:17:26 -06:00
active_resource_builder.rb Add Appraisal for Rails 4.0.1 2013-12-18 22:32:28 -07:00
activemodel_helpers.rb allow_value.with_message takes i18n interp values 2014-04-26 11:27:11 -04:00
capture_helpers.rb Handle ensure_inclusion_of for boolean columns 2014-02-17 15:40:18 -07:00
class_builder.rb Fix uniqueness matcher when scope is a *_type attr 2014-10-08 23:22:54 -06:00
controller_builder.rb Fix several issues with strong parameters matcher 2014-04-22 09:37:30 -05:00
fail_with_message_including_matcher.rb Using RSpec 3 for development. 2014-08-14 12:16:54 -04:00
fail_with_message_matcher.rb Using RSpec 3 for development. 2014-08-14 12:16:54 -04:00
i18n_faker.rb Allow the use of %{attribute} or %{model} in i18n translations. 2013-04-12 17:10:18 -04:00
mailer_builder.rb Move define_mailer into its own module. 2012-06-29 15:33:39 -05:00
model_builder.rb Fix define_class/model to support namespaces 2014-10-08 23:22:50 -06:00
rails_versions.rb Create the define_enum_for matcher 2014-08-26 14:44:27 -06:00
test_application.rb Speed up repeated execution of unit tests 2014-05-09 15:16:48 -06:00