2014-10-26 20:51:42 -04:00
|
|
|
module UnitTests
|
|
|
|
module ClassBuilder
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
def define_module(*args, &block)
|
|
|
|
ClassBuilder.define_module(*args, &block)
|
2014-10-26 20:51:42 -04:00
|
|
|
end
|
2014-10-08 01:19:07 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
def define_class(*args, &block)
|
|
|
|
ClassBuilder.define_class(*args, &block)
|
2014-10-26 20:51:42 -04:00
|
|
|
end
|
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 19:20:07 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
class << self
|
|
|
|
def configure_example_group(example_group)
|
|
|
|
example_group.include(self)
|
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 19:20:07 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
example_group.after do
|
|
|
|
ClassBuilder.reset
|
|
|
|
end
|
|
|
|
end
|
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 19:20:07 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
def reset
|
|
|
|
remove_defined_classes
|
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 19:20:07 -04:00
|
|
|
end
|
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
def define_module(module_name, &block)
|
|
|
|
module_name = module_name.to_s.camelize
|
2014-10-26 20:51:42 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
namespace, name_without_namespace =
|
|
|
|
ClassBuilder.parse_constant_name(module_name)
|
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 19:20:07 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
if namespace.const_defined?(name_without_namespace, false)
|
|
|
|
namespace.__send__(:remove_const, name_without_namespace)
|
2014-10-26 20:51:42 -04:00
|
|
|
end
|
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 19:20:07 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
eval <<-RUBY
|
|
|
|
module #{namespace}::#{name_without_namespace}
|
|
|
|
end
|
|
|
|
RUBY
|
2012-04-21 20:21:30 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
namespace.const_get(name_without_namespace).tap do |constant|
|
|
|
|
constant.unloadable
|
2012-04-21 20:21:30 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
if block
|
|
|
|
constant.module_eval(&block)
|
|
|
|
end
|
|
|
|
end
|
2014-10-08 01:19:07 -04:00
|
|
|
end
|
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
def define_class(class_name, parent_class = Object, &block)
|
|
|
|
class_name = class_name.to_s.camelize
|
|
|
|
|
|
|
|
namespace, name_without_namespace =
|
|
|
|
ClassBuilder.parse_constant_name(class_name)
|
|
|
|
|
|
|
|
if namespace.const_defined?(name_without_namespace, false)
|
|
|
|
namespace.__send__(:remove_const, name_without_namespace)
|
2014-10-26 20:51:42 -04:00
|
|
|
end
|
2012-04-21 20:21:30 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
eval <<-RUBY
|
|
|
|
class #{namespace}::#{name_without_namespace} < ::#{parent_class}
|
|
|
|
end
|
|
|
|
RUBY
|
|
|
|
|
|
|
|
namespace.const_get(name_without_namespace).tap do |constant|
|
|
|
|
constant.unloadable
|
2014-10-26 20:51:42 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
if block
|
|
|
|
if block.arity == 0
|
|
|
|
constant.class_eval(&block)
|
|
|
|
else
|
|
|
|
block.call(constant)
|
|
|
|
end
|
2015-02-19 12:33:39 -05:00
|
|
|
end
|
2014-10-26 20:51:42 -04:00
|
|
|
end
|
2012-04-21 20:21:30 -04:00
|
|
|
end
|
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
def parse_constant_name(name)
|
|
|
|
namespace = Shoulda::Matchers::Util.deconstantize(name)
|
|
|
|
qualified_namespace = (namespace.presence || 'Object').constantize
|
|
|
|
name_without_namespace = name.to_s.demodulize
|
|
|
|
[qualified_namespace, name_without_namespace]
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
2014-10-26 20:51:42 -04:00
|
|
|
|
Extract classes for defining models in tests
The main driver behind this commit is to provide a programmatic way to
define models in tests. We already have ways of doing this, of course,
with `define_model` and `define_active_model_class`, but these methods
are very low-level, and in writing tests we have historically made our
own methods inside of test files to define full and complete models. So
we have this common pattern of defining a model with a validation, and
that's repeated across many different files.
What we would like to do, right now, is extract some commonly used
assertions to a shared example group. These assertions need to define
models inside of the tests, but the issue is that sometimes the models
are ActiveRecord models, and sometimes they are ActiveModel models, and
when the shared example group is used within a test file, we need a way
to choose the strategy we'd like to use at runtime. Since the way we
currently define models is via methods, we can't really provide a
strategy very easily. Also, if we need to customize how those models are
defined (say, the attribute needs to be a has-many association instead
of a normal attribute) then the methods only go so far in providing us
that level of customization before things get really complicated.
So, to help us with this, this commit takes the pattern of
model-plus-validation previously mentioned and places it in multiple
classes.
Note that this is also a precursor to a later commit in which we
introduce `ignoring_interference_by_writer` across the board. The way we
will do this is by adding a shared example group that then uses these
model creation classes internally to build objects instead of relying
upon methods that the outer example group -- to which the shared example
group is being mixed into -- provides.
2015-12-23 18:10:40 -05:00
|
|
|
def remove_defined_classes
|
|
|
|
::ActiveSupport::Dependencies.clear
|
|
|
|
end
|
2014-10-26 20:51:42 -04:00
|
|
|
end
|
|
|
|
end
|
2012-04-21 20:21:30 -04:00
|
|
|
end
|