class Shoulda::Matchers::ActiveModel::ValidateUniquenessOfMatcher
@private
Public Class Methods
new(attribute)
click to toggle source
Calls superclass method
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 178 def initialize(attribute) super(attribute) @options = {} end
Public Instance Methods
allow_nil()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 198 def allow_nil @options[:allow_nil] = true self end
case_insensitive()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 193 def case_insensitive @options[:case_insensitive] = true self end
description()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 203 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
matches?(subject)
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 211 def matches?(subject) @subject = subject.class.new @expected_message ||= :taken set_scoped_attributes && validate_everything_except_duplicate_nils? && validate_after_scope_change? && allows_nil? end
scoped_to(*scopes)
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 183 def scoped_to(*scopes) @options[:scopes] = [*scopes].flatten self end
with_message(message)
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 188 def with_message(message) @expected_message = message self end
Private Instance Methods
allows_nil?()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 222 def allows_nil? if @options[:allow_nil] ensure_nil_record_in_database allows_value_of(nil, @expected_message) else true end end
class_name()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 352 def class_name @subject.class.name end
correct_type_for_column(column)
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 340 def correct_type_for_column(column) if column.type == :string || column.type == :binary '0' elsif column.type == :datetime DateTime.now elsif column.type == :uuid SecureRandom.uuid else 0 end end
create_record_in_database(options = {})
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 249 def create_record_in_database(options = {}) if options[:nil_value] value = nil else value = 'a' end @subject.class.new.tap do |instance| instance.__send__("#{@attribute}=", value) other_non_nullable_columns.each do |non_nullable_column| instance.__send__("#{non_nullable_column.name}=", correct_type_for_column(non_nullable_column)) end if has_secure_password? instance.password = 'password' instance.password_confirmation = 'password' end instance.save(validate: false) end end
create_record_without_nil()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 300 def create_record_without_nil @existing_record = create_record_in_database end
ensure_nil_record_in_database()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 239 def ensure_nil_record_in_database unless existing_record_is_nil? create_record_in_database(nil_value: true) end end
existing_record()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 231 def existing_record @existing_record ||= first_instance end
existing_record_is_nil?()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 245 def existing_record_is_nil? @existing_record.present? && existing_value.nil? end
existing_value()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 356 def existing_value value = existing_record.__send__(@attribute) if @options[:case_insensitive] && value.respond_to?(:swapcase!) value.swapcase! end value end
first_instance()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 235 def first_instance @subject.class.first || create_record_in_database end
has_secure_password?()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 271 def has_secure_password? @subject.class.ancestors.map(&:to_s).include?('ActiveModel::SecurePassword::InstanceMethodsOnActivation') end
other_non_nullable_columns()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 364 def other_non_nullable_columns @subject.class.columns.select do |column| column.name != @attribute && !column.null && !column.primary end end
set_scoped_attributes()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 275 def set_scoped_attributes if @options[:scopes].present? @options[:scopes].all? do |scope| setter = :"#{scope}=" if @subject.respond_to?(setter) @subject.__send__(setter, existing_record.__send__(scope)) true else @failure_message = "#{class_name} doesn't seem to have a #{scope} attribute." false end end else true end end
validate_after_scope_change?()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 304 def validate_after_scope_change? if @options[:scopes].blank? true else all_records = @subject.class.all @options[:scopes].all? do |scope| previous_value = all_records.map(&scope).max # 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 elsif previous_value.respond_to?(:to_datetime) previous_value.to_datetime.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_when_negated << " (with different value of #{scope})" true else @failure_message << " (with different value of #{scope})" false end end end end
validate_everything_except_duplicate_nils?()
click to toggle source
# File lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb, line 292 def validate_everything_except_duplicate_nils? if @options[:allow_nil] && existing_value.nil? create_record_without_nil end disallows_value_of(existing_value, @expected_message) end