From a3b0051f2c30f65582b23fd82a33870d08ea54dd Mon Sep 17 00:00:00 2001 From: Murray Steele Date: Tue, 20 May 2008 12:32:46 +0100 Subject: [PATCH] Add support for testing scoped attribtues in should_require_unique_attributes that don't respond to .next. If the values of the existing object don't respond to .next (e.g. Time, or perhaps serialized attributes) but you want to use them as the scope for validating the uniqueness of another attribute shoulda will break by attempting to call .next on them. This patch allows you to supply a :change_scoped_to_values option to should_require_unique_attributes to help with this. The :change_scoped_to_values option is expected to be a proc that will be called with the scoped_to attribute name and it's current value, the result of this proc will be set as the attribute on the existing object. --- lib/shoulda/active_record_helpers.rb | 19 ++++++++++++++++--- test/fixtures/meetings.yml | 2 +- ...ate_user_groups_and_memberships_and_meetings.rb | 2 +- test/unit/meeting_test.rb | 10 +++++++++- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/shoulda/active_record_helpers.rb b/lib/shoulda/active_record_helpers.rb index b9c4e36..c893331 100644 --- a/lib/shoulda/active_record_helpers.rb +++ b/lib/shoulda/active_record_helpers.rb @@ -53,12 +53,17 @@ module ThoughtBot # :nodoc: # Regexp or string. Default = /taken/ # * :scoped_to - shoulda analogue to :scope in # ActiveRecord::Base#validates_uniqueness_of. Can be an array of values or just one. + # * :change_scoped_to_values - a proc that should will call with the scoped_to + #  attribute name and the existing value. The result is what shoulda will set the new + # objects value to in order to test that changing the scoped_to attributes does allow + # duplicate values of the attribute in question. If your scoped_to attributes don't + # implement .next, you need to supply this. # # Example: # should_require_unique_attributes :keyword, :username # def should_require_unique_attributes(*attributes) - message, scope = get_options!(attributes, :message, :scoped_to) + message, scope, change_existing_proc = get_options!(attributes, :message, :scoped_to, :change_scoped_to_values) message ||= /taken/ klass = model_class @@ -107,7 +112,11 @@ module ThoughtBot # :nodoc: if scope.is_a? Array scope.each do |scoped_attribute| # Assume the scope is a foreign key if the field is nil - object.send(:"#{scoped_attribute}=", existing.send(scoped_attribute).nil? ? 1 : existing.send(scoped_attribute).next) + if change_existing_proc.nil? + object.send(:"#{scoped_attribute}=", existing.send(scoped_attribute).nil? ? 1 : existing.send(scoped_attribute).next) + else + object.send(:"#{scoped_attribute}=", change_existing_proc.call(scoped_attribute, existing.send(scoped_attribute))) + end object.errors.clear object.valid? assert_does_not_contain(object.errors.on(attribute), message, @@ -115,7 +124,11 @@ module ThoughtBot # :nodoc: end else # Assume the scope is a foreign key if the field is nil - object.send(:"#{scope}=", existing.send(scope).nil? ? 1 : existing.send(scope).next) + if change_existing_proc.nil? + object.send(:"#{scope}=", existing.send(scope).nil? ? 1 : existing.send(scope).next) + else + object.send(:"#{scope}=", change_existing_proc.call(scope, existing.send(scope))) + end object.errors.clear object.valid? assert_does_not_contain(object.errors.on(attribute), message, diff --git a/test/fixtures/meetings.yml b/test/fixtures/meetings.yml index f042149..9497c04 100644 --- a/test/fixtures/meetings.yml +++ b/test/fixtures/meetings.yml @@ -2,4 +2,4 @@ first: id: 1 user_group_id: 1 location: London - happens_on: 2008-01-01 + happens_on: 2008-01-01 18:00:00 diff --git a/test/rails_root/db/migrate/009_create_user_groups_and_memberships_and_meetings.rb b/test/rails_root/db/migrate/009_create_user_groups_and_memberships_and_meetings.rb index 715ee2c..0efaf54 100644 --- a/test/rails_root/db/migrate/009_create_user_groups_and_memberships_and_meetings.rb +++ b/test/rails_root/db/migrate/009_create_user_groups_and_memberships_and_meetings.rb @@ -10,7 +10,7 @@ class CreateUserGroupsAndMembershipsAndMeetings < ActiveRecord::Migration create_table :meetings do |t| t.integer :user_group_id t.string :location - t.date :happens_on + t.time :happens_on end end diff --git a/test/unit/meeting_test.rb b/test/unit/meeting_test.rb index 946a0ef..95dd547 100644 --- a/test/unit/meeting_test.rb +++ b/test/unit/meeting_test.rb @@ -9,5 +9,13 @@ class MeetingTest < Test::Unit::TestCase should_require_attributes :user_group_id, :location, :happens_on - should_require_unique_attributes :user_group_id, :scoped_to => [:location, :happens_on] + should_require_unique_attributes :user_group_id, + :scoped_to => [:location, :happens_on], + :change_scoped_to_values => lambda {|attr, value| + if attr == :happens_on + value + 1.hours + else + value.next + end + } end -- 1.5.4.5