module ThoughtBot::Shoulda::ActiveRecord::Macros

def should_require_unique_attributes(*attributes)


should_require_unique_attributes :address, :scoped_to => [:first_name, :last_name]
should_require_unique_attributes :email, :scoped_to => :name
should_require_unique_attributes :name, :message => "O NOES! SOMEONE STOELED YER NAME!"
should_require_unique_attributes :keyword, :username
Examples:

* :scoped_to - field(s) to scope the uniqueness to.
Regexp or string. Default = I18n.translate('activerecord.errors.messages.taken')
* :message - value the test expects to find in errors.on(:attribute).
Options:

Requires an existing record
Ensures that the model cannot be saved if one of the attributes listed is not unique.
def should_require_unique_attributes(*attributes)
  message, scope = get_options!(attributes, :message, :scoped_to)
  scope = [*scope].compact
  message ||= default_error_message(:taken)
  klass = model_class
  attributes.each do |attribute|
    attribute = attribute.to_sym
    should "require unique value for #{attribute}#{" scoped to #{scope.join(', ')}" unless scope.blank?}" do
      assert existing = klass.find(:first), "Can't find first #{klass}"
      object = klass.new
      existing_value = existing.send(attribute)
      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_bad_value(object, attribute, existing_value, message)
      # Now test that the object is valid when changing the scoped attribute
      # TODO:  There is a chance that we could change the scoped field
      # to a value that's already taken.  An alternative implementation
      # could actually find all values for scope and create a unique
      # one.
      if !scope.blank?
        scope.each do |s|
          # Assume the scope is a foreign key if the field is nil
          object.send("#{s}=", existing.send(s).nil? ? 1 : existing.send(s).next)
          assert_good_value(object, attribute, existing_value, message)
        end
      end
    end
  end
end