class Shoulda::Matchers::ActiveModel::ValidateUniquenessOfMatcher
:nodoc:
def case_insensitive
def case_insensitive @options[:case_insensitive] = true self end
def class_name
def class_name @subject.class.name end
def correct_type_for_column(column)
def correct_type_for_column(column) if column.type == :string '0' else 0 end end
def create_instance_in_database
def create_instance_in_database @subject.class.new.tap do |instance| instance.send("#{@attribute}=", 'arbitrary_string') instance.save(:validate => false) end end
def description
def description result = 'require ' result << 'case sensitive ' unless @options[:case_insensitive] result << "unique value for #{@attribute}" result << " scoped to #{@options[:scopes].join(', ')}" if @options[:scopes].present? result end
def existing
def existing @existing ||= first_instance end
def existing_value
def existing_value value = existing.send(@attribute) if @options[:case_insensitive] && value.respond_to?(:swapcase!) value.swapcase! end value end
def first_instance
def first_instance @subject.class.first || create_instance_in_database end
def initialize(attribute)
def initialize(attribute) super(attribute) @options = {} end
def matches?(subject)
def matches?(subject) @subject = subject.class.new @expected_message ||= :taken set_scoped_attributes && validate_attribute? && validate_after_scope_change? end
def scoped_to(*scopes)
def scoped_to(*scopes) @options[:scopes] = [*scopes].flatten self end
def set_scoped_attributes
def set_scoped_attributes if @options[:scopes].present? @options[:scopes].all? do |scope| setter = :"#{scope}=" if @subject.respond_to?(setter) @subject.send(setter, existing.send(scope)) true else @failure_message_for_should = "#{class_name} doesn't seem to have a #{scope} attribute." false end end else true end end
def validate_after_scope_change?
to a value that's already taken. An alternative implementation
TODO: There is a chance that we could change the scoped field
def validate_after_scope_change? if @options[:scopes].blank? true else @options[:scopes].all? do |scope| previous_value = existing.send(scope) # Assume the scope is a foreign key if the field is nil previous_value ||= correct_type_for_column(@subject.class.columns_hash[scope.to_s]) next_value = if previous_value.respond_to?(:next) previous_value.next else previous_value.to_s.next end @subject.send("#{scope}=", next_value) if allows_value_of(existing_value, @expected_message) @subject.send("#{scope}=", previous_value) @failure_message_for_should_not << " (with different value of #{scope})" true else @failure_message_for_should << " (with different value of #{scope})" false end end end end
def validate_attribute?
def validate_attribute? disallows_value_of(existing_value, @expected_message) end
def with_message(message)
def with_message(message) @expected_message = message self end