mirror of
https://github.com/thoughtbot/shoulda-matchers.git
synced 2022-11-09 12:01:38 -05:00
Scoping of uniqueness validations on multiple attributes
Previously the :scoped_to option on the 'should_require_unique_attributes' only allowed a single attribute to scope uniqueness. This adds the ability to scope to multiple attributes. This also improves the documentation for the method.
This commit is contained in:
parent
9c78fe05a1
commit
911b2f7cb4
7 changed files with 29 additions and 9 deletions
|
@ -51,25 +51,32 @@ module ThoughtBot # :nodoc:
|
|||
# Options:
|
||||
# * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
|
||||
# Regexp or string. Default = <tt>/taken/</tt>
|
||||
# * <tt>:scoped_to</tt> - field(s) to scope the uniqueness to.
|
||||
#
|
||||
# Example:
|
||||
# Examples:
|
||||
# should_require_unique_attributes :keyword, :username
|
||||
# should_require_unique_attributes :name, :message => "O NOES! SOMEONE STOELED YER NAME!"
|
||||
# should_require_unique_attributes :email, :scoped_to => :name
|
||||
# should_require_unique_attributes :address, :scoped_to => [:first_name, :last_name]
|
||||
#
|
||||
def should_require_unique_attributes(*attributes)
|
||||
message, scope = get_options!(attributes, :message, :scoped_to)
|
||||
scope = [*scope].compact
|
||||
message ||= /taken/
|
||||
|
||||
klass = model_class
|
||||
attributes.each do |attribute|
|
||||
attribute = attribute.to_sym
|
||||
should "require unique value for #{attribute}#{" scoped to #{scope}" if scope}" do
|
||||
should "require unique value for #{attribute}#{" scoped to #{scope.join(', ')}" if scope}" do
|
||||
assert existing = klass.find(:first), "Can't find first #{klass}"
|
||||
object = klass.new
|
||||
|
||||
object.send(:"#{attribute}=", existing.send(attribute))
|
||||
if scope
|
||||
assert_respond_to object, :"#{scope}=", "#{klass.name} doesn't seem to have a #{scope} attribute."
|
||||
object.send(:"#{scope}=", existing.send(scope))
|
||||
if !scope.blank?
|
||||
scope.each do |s|
|
||||
assert_respond_to object, :"#{s}=", "#{klass.name} doesn't seem to have a #{s} attribute."
|
||||
object.send(:"#{s}=", existing.send(s))
|
||||
end
|
||||
end
|
||||
|
||||
assert !object.valid?, "#{klass.name} does not require a unique value for #{attribute}."
|
||||
|
@ -82,13 +89,18 @@ module ThoughtBot # :nodoc:
|
|||
# to a value that's already taken. An alternative implementation
|
||||
# could actually find all values for scope and create a unique
|
||||
# one.
|
||||
if scope
|
||||
if !scope.blank?
|
||||
scope.each do |s|
|
||||
# Assume the scope is a foreign key if the field is nil
|
||||
object.send(:"#{scope}=", existing.send(scope).nil? ? 1 : existing.send(scope).next)
|
||||
object.send(:"#{s}=", existing.send(s).nil? ? 1 : existing.send(s).next)
|
||||
end
|
||||
|
||||
object.errors.clear
|
||||
object.valid?
|
||||
assert_does_not_contain(object.errors.on(attribute), message,
|
||||
"after :#{scope} set to #{object.send(scope.to_sym)}")
|
||||
scope.each do |s|
|
||||
assert_does_not_contain(object.errors.on(attribute), message,
|
||||
"after :#{s} set to #{object.send(s.to_sym)}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
3
test/fixtures/addresses.yml
vendored
Normal file
3
test/fixtures/addresses.yml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
first:
|
||||
title: Home
|
||||
addressable: first (User)
|
|
@ -1,3 +1,4 @@
|
|||
class Address < ActiveRecord::Base
|
||||
belongs_to :addressable, :polymorphic => true
|
||||
validates_uniqueness_of :title, :scope => [:addressable_type, :addressable_id]
|
||||
end
|
||||
|
|
|
@ -10,4 +10,5 @@ class User < ActiveRecord::Base
|
|||
validates_length_of :email, :in => 1..100
|
||||
validates_inclusion_of :age, :in => 1..100
|
||||
validates_acceptance_of :eula
|
||||
validates_uniqueness_of :email, :scope => :name
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
class CreateAddresses < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :addresses do |t|
|
||||
t.column :title, :string
|
||||
t.column :addressable_id, :integer
|
||||
t.column :addressable_type, :string
|
||||
end
|
||||
|
|
|
@ -3,4 +3,5 @@ require File.dirname(__FILE__) + '/../test_helper'
|
|||
class AddressTest < Test::Unit::TestCase
|
||||
load_all_fixtures
|
||||
should_belong_to :addressable
|
||||
should_require_unique_attributes :title, :scoped_to => [:addressable_id, :addressable_type]
|
||||
end
|
||||
|
|
|
@ -23,4 +23,5 @@ class UserTest < Test::Unit::TestCase
|
|||
should_have_db_column :email, :type => "string", :default => nil, :precision => nil, :limit => 255,
|
||||
:null => true, :primary => false, :scale => nil, :sql_type => 'varchar(255)'
|
||||
should_require_acceptance_of :eula
|
||||
should_require_unique_attributes :email, :scoped_to => :name
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue