class Shoulda::Matchers::ActiveRecord::ValidateUniquenessOfMatcher

@private

Public Class Methods

new(attribute) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 269
def initialize(attribute)
  super(attribute)
  @expected_message = :taken
  @options = {
    case_sensitivity_strategy: :sensitive,
  }
  @existing_record_created = false
  @failure_reason = nil
  @failure_reason_when_negated = nil
  @attribute_setters = {
    existing_record: AttributeSetters.new,
    new_record: AttributeSetters.new,
  }
end

Public Instance Methods

allow_blank() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 308
def allow_blank
  @options[:allow_blank] = true
  self
end
allow_nil() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 299
def allow_nil
  @options[:allow_nil] = true
  self
end
case_insensitive() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 289
def case_insensitive
  @options[:case_sensitivity_strategy] = :insensitive
  self
end
does_not_match?(given_record) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 345
def does_not_match?(given_record)
  @given_record = given_record
  @all_records = model.all

  does_not_match_presence_of_scopes? ||
    does_not_match_scopes_configuration? ||
    does_not_match_uniqueness_without_scopes? ||
    does_not_match_uniqueness_with_case_sensitivity_strategy? ||
    does_not_match_uniqueness_with_scopes? ||
    does_not_match_allow_nil? ||
    does_not_match_allow_blank?
ensure
  Uniqueness::TestModels.remove_all
end
expects_to_allow_blank?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 313
def expects_to_allow_blank?
  @options[:allow_blank] == true
end
expects_to_allow_nil?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 304
def expects_to_allow_nil?
  @options[:allow_nil] == true
end
ignoring_case_sensitivity() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 294
def ignoring_case_sensitivity
  @options[:case_sensitivity_strategy] = :ignore
  self
end
matches?(given_record) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 329
def matches?(given_record)
  @given_record = given_record
  @all_records = model.all

  matches_presence_of_attribute? &&
    matches_presence_of_scopes? &&
    matches_scopes_configuration? &&
    matches_uniqueness_without_scopes? &&
    matches_uniqueness_with_case_sensitivity_strategy? &&
    matches_uniqueness_with_scopes? &&
    matches_allow_nil? &&
    matches_allow_blank?
ensure
  Uniqueness::TestModels.remove_all
end
scoped_to(*scopes) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 284
def scoped_to(*scopes)
  @options[:scopes] = [*scopes].flatten.map(&:to_sym)
  self
end
simple_description() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 317
def simple_description
  description = "validate that :#{@attribute} is"
  description << description_for_case_sensitive_qualifier
  description << ' unique'

  if @options[:scopes].present?
    description << " within the scope of #{inspected_expected_scopes}"
  end

  description
end

Protected Instance Methods

build_allow_or_disallow_value_matcher(args) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 370
def build_allow_or_disallow_value_matcher(args)
  super.tap do |matcher|
    matcher.failure_message_preface = method(:failure_message_preface)
    matcher.attribute_changed_value_message =
      method(:attribute_changed_value_message)
  end
end
failure_reason() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 362
def failure_reason
  @failure_reason || super
end
failure_reason_when_negated() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 366
def failure_reason_when_negated
  @failure_reason_when_negated || super
end

Private Instance Methods

actual_sets_of_scopes() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 485
def actual_sets_of_scopes
  validations.map do |validation|
    Array.wrap(validation.options[:scope]).map(&:to_sym)
  end.reject(&:empty?)
end
all_scopes_are_booleans?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 836
def all_scopes_are_booleans?
  @options[:scopes].all? do |scope|
    @all_records.map(&scope).all? { |s| boolean_value?(s) }
  end
end
arbitrary_non_blank_value() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 561
def arbitrary_non_blank_value
  non_blank_value = dummy_value_for(@attribute)
  limit = column_limit_for(@attribute)

  is_string_value = non_blank_value.is_a?(String)
  if is_string_value && limit && limit < non_blank_value.length
    'x' * limit
  else
    non_blank_value
  end
end
attribute_changed_value_message() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 980
        def attribute_changed_value_message
          <<-MESSAGE.strip
As indicated in the message above,
:#{last_attribute_setter_used_on_new_record.attribute_name} seems to be
changing certain values as they are set, and this could have something
to do with why this test is failing. If you or something else has
overridden the writer method for this attribute to normalize values by
changing their case in any way (for instance, ensuring that the
attribute is always downcased), then try adding
`ignoring_case_sensitivity` onto the end of the uniqueness matcher.
Otherwise, you may need to write the test yourself, or do something
different altogether.
          MESSAGE
        end
attribute_names_under_test() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 899
def attribute_names_under_test
  [@attribute] + expected_scopes
end
attribute_present_on_model?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 610
def attribute_present_on_model?
  model.method_defined?("#{attribute}=") ||
    model.columns_hash.key?(attribute.to_s)
end
attribute_setter_descriptions_for_new_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 1028
def attribute_setter_descriptions_for_new_record
  attribute_setters_for_new_record.map do |attribute_setter|
    same_as_existing = (
      attribute_setter.value_written ==
      existing_value_written
    )
    description_for_attribute_setter(
      attribute_setter,
      same_as_existing: same_as_existing,
    )
  end
end
attribute_setter_for_existing_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 890
def attribute_setter_for_existing_record
  @attribute_setters[:existing_record].last
end
attribute_setters_for_new_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 894
def attribute_setters_for_new_record
  @attribute_setters[:new_record] +
    [last_attribute_setter_used_on_new_record]
end
available_enum_values_for(scope, previous_value) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 855
def available_enum_values_for(scope, previous_value)
  new_record.defined_enums[scope.to_s].reject do |key, _|
    key == previous_value
  end
end
boolean_value?(value) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 842
def boolean_value?(value)
  [true, false].include?(value)
end
build_attribute_setter(record, attribute_name, value) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 903
def build_attribute_setter(record, attribute_name, value)
  Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeSetter.
    new(
      matcher_name: :validate_uniqueness_of,
      object: record,
      attribute_name: attribute_name,
      value: value,
      ignore_interference_by_writer: ignore_interference_by_writer,
    )
end
build_new_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 577
def build_new_record
  @new_record = existing_record.dup

  attribute_names_under_test.each do |attribute_name|
    set_attribute_on_new_record!(
      attribute_name,
      existing_record.public_send(attribute_name),
    )
  end

  @new_record
end
case_sensitivity_strategy() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 380
def case_sensitivity_strategy
  @options[:case_sensitivity_strategy]
end
column_for(scope) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 926
def column_for(scope)
  model.columns_hash[scope.to_s]
end
column_limit_for(attribute) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 930
def column_limit_for(attribute)
  column_for(attribute).try(:limit)
end
create_existing_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 542
def create_existing_record
  @given_record.tap do |existing_record|
    existing_record.save(validate: false)
  end
rescue ::ActiveRecord::StatementInvalid => e
  raise ExistingRecordInvalid.create(underlying_exception: e)
end
defined_as_enum?(scope) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 846
def defined_as_enum?(scope)
  model.respond_to?(:defined_enums) &&
    new_record.defined_enums[scope.to_s]
end
description_for_attribute_setter( attribute_setter, same_as_existing: nil ) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 995
def description_for_attribute_setter(
  attribute_setter,
  same_as_existing: nil
)
  description = "its :#{attribute_setter.attribute_name} to "

  if same_as_existing == false
    description << 'a different value, '
  end

  description << Shoulda::Matchers::Util.inspect_value(
    attribute_setter.value_written,
  )

  if attribute_setter.attribute_changed_value?
    description << ' (read back as '
    description << Shoulda::Matchers::Util.inspect_value(
      attribute_setter.value_read,
    )
    description << ')'
  end

  if same_as_existing == true
    description << ' as well'
  end

  description
end
description_for_case_sensitive_qualifier() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 393
def description_for_case_sensitive_qualifier
  case case_sensitivity_strategy
  when :sensitive
    ' case-sensitively'
  when :insensitive
    ' case-insensitively'
  else
    ''
  end
end
descriptions_for_attribute_setters_for_new_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 1024
def descriptions_for_attribute_setters_for_new_record
  attribute_setter_descriptions_for_new_record.to_sentence
end
does_not_match_allow_blank?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 514
def does_not_match_allow_blank?
  expects_to_allow_blank? && (
    update_existing_record!('') &&
    (@failure_reason = nil || disallows_value_of('', @expected_message))
  )
end
does_not_match_allow_nil?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 498
def does_not_match_allow_nil?
  expects_to_allow_nil? && (
    update_existing_record!(nil) &&
    (@failure_reason = nil ||
      disallows_value_of(nil, @expected_message)
    )
  )
end
does_not_match_presence_of_attribute?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 600
def does_not_match_presence_of_attribute?
  if attribute_present_on_model?
    @failure_reason =
      ":#{attribute} seems to be an attribute on #{model.name}."
    false
  else
    true
  end
end
does_not_match_presence_of_scopes?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 640
def does_not_match_presence_of_scopes?
  if scopes_missing_on_model.any?
    true
  else
    inspected_scopes = scopes_present_on_model.map(&:inspect)

    reason = ''

    reason << inspected_scopes.to_sentence

    reason <<
      if inspected_scopes.many?
        ' seem to be attributes'
      else
        ' seems to be an attribute'
      end

    reason << " on #{model.name}."

    @failure_reason = reason

    false
  end
end
does_not_match_scopes_configuration?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 434
def does_not_match_scopes_configuration?
  if scopes_match?
    @failure_reason = 'Expected the validation '

    if expected_scopes.empty?
      @failure_reason << 'to be scoped to nothing, '
      @failure_reason << 'but it was scoped to '
      @failure_reason << "#{inspected_actual_scopes} instead."
    else
      @failure_reason << 'not to be scoped to '
      @failure_reason << inspected_expected_scopes
    end

    false
  else
    true
  end
end
does_not_match_uniqueness_with_case_sensitivity_strategy?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 718
def does_not_match_uniqueness_with_case_sensitivity_strategy?
  if should_test_case_sensitivity?
    @failure_reason = nil

    value = existing_value_read
    swapcased_value = value.swapcase

    if case_sensitivity_strategy == :sensitive
      disallows_value_of(swapcased_value, @expected_message)
    else
      if value == swapcased_value
        raise NonCaseSwappableValueError.create(
          model: model,
          attribute: @attribute,
          value: value,
        )
      end

      allows_value_of(swapcased_value, @expected_message)
    end
  else
    true
  end
end
does_not_match_uniqueness_with_scopes?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 765
def does_not_match_uniqueness_with_scopes?
  expected_scopes.any? &&
    !all_scopes_are_booleans? &&
    expected_scopes.any? do |scope|
      setting_next_value_for(scope) do
        @failure_reason = nil
        disallows_value_of(existing_value_read, @expected_message)
      end
    end
end
does_not_match_uniqueness_without_scopes?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 685
def does_not_match_uniqueness_without_scopes?
  @failure_reason = nil

  if existing_value_read.blank?
    update_existing_record!(arbitrary_non_blank_value)
  end

  allows_value_of(existing_value_read, @expected_message)
end
dummy_scalar_value_for(column) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 803
def dummy_scalar_value_for(column)
  Shoulda::Matchers::Util.dummy_value_for(column.type)
end
dummy_value_for(scope) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 793
def dummy_value_for(scope)
  column = column_for(scope)

  if column.respond_to?(:array) && column.array
    [dummy_scalar_value_for(column)]
  else
    dummy_scalar_value_for(column)
  end
end
existing_and_new_values_are_same?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 1041
def existing_and_new_values_are_same?
  last_value_set_on_new_record == existing_value_written
end
existing_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 521
def existing_record
  unless defined?(@existing_record)
    find_or_create_existing_record
  end

  @existing_record
end
existing_value_read() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 914
def existing_value_read
  existing_record.public_send(@attribute)
end
existing_value_written() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 918
def existing_value_written
  if attribute_setter_for_existing_record
    attribute_setter_for_existing_record.value_written
  else
    existing_value_read
  end
end
expected_scopes() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 481
def expected_scopes
  Array.wrap(@options[:scopes])
end
failure_message_preface() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 938
def failure_message_preface # rubocop:disable Metrics/MethodLength
  prefix = ''

  if @existing_record_created
    prefix << "After taking the given #{model.name}"

    if attribute_setter_for_existing_record
      prefix << ', setting '
      prefix << description_for_attribute_setter(
        attribute_setter_for_existing_record,
      )
    else
      prefix << ", whose :#{attribute} is "
      prefix << "‹#{existing_value_read.inspect}›"
    end

    prefix << ', and saving it as the existing record, then'
  elsif attribute_setter_for_existing_record
    prefix << "Given an existing #{model.name},"
    prefix << ' after setting '
    prefix << description_for_attribute_setter(
      attribute_setter_for_existing_record,
    )
    prefix << ', then'
  else
    prefix << "Given an existing #{model.name} whose :#{attribute}"
    prefix << ' is '
    prefix << Shoulda::Matchers::Util.inspect_value(
      existing_value_read,
    )
    prefix << ', after'
  end

  prefix << " making a new #{model.name} and setting "

  prefix << descriptions_for_attribute_setters_for_new_record

  prefix << ", the matcher expected the new #{model.name} to be"

  prefix
end
find_existing_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 538
def find_existing_record
  model.first.presence
end
find_or_create_existing_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 529
def find_or_create_existing_record
  @existing_record = find_existing_record

  unless @existing_record
    @existing_record = create_existing_record
    @existing_record_created = true
  end
end
has_secure_password?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 573
def has_secure_password?
  Shoulda::Matchers::RailsShim.has_secure_password?(subject, @attribute)
end
inspected_actual_scopes() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 462
def inspected_actual_scopes
  inspected_actual_sets_of_scopes.to_sentence(
    words_connector: ' and ',
    last_word_connector: ', and',
  )
end
inspected_actual_sets_of_scopes() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 469
def inspected_actual_sets_of_scopes
  inspected_sets_of_scopes = actual_sets_of_scopes.map do |scopes|
    scopes.map(&:inspect)
  end

  if inspected_sets_of_scopes.many?
    inspected_sets_of_scopes.map { |x| "(#{x.to_sentence})" }
  else
    inspected_sets_of_scopes.map(&:to_sentence)
  end
end
inspected_expected_scopes() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 458
def inspected_expected_scopes
  expected_scopes.map(&:inspect).to_sentence
end
last_attribute_setter_used_on_new_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 1045
def last_attribute_setter_used_on_new_record
  last_submatcher_run.last_attribute_setter_used
end
last_value_set_on_new_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 1049
def last_value_set_on_new_record
  last_submatcher_run.last_value_set
end
matches_allow_blank?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 507
def matches_allow_blank?
  !expects_to_allow_blank? || (
    update_existing_record!('') &&
    allows_value_of('', @expected_message)
  )
end
matches_allow_nil?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 491
def matches_allow_nil?
  !expects_to_allow_nil? || (
    update_existing_record!(nil) &&
    allows_value_of(nil, @expected_message)
  )
end
matches_presence_of_attribute?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 590
def matches_presence_of_attribute?
  if attribute_present_on_model?
    true
  else
    @failure_reason =
      ":#{attribute} does not seem to be an attribute on #{model.name}."
    false
  end
end
matches_presence_of_scopes?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 615
def matches_presence_of_scopes?
  if scopes_missing_on_model.none?
    true
  else
    inspected_scopes = scopes_missing_on_model.map(&:inspect)

    reason = ''

    reason << inspected_scopes.to_sentence

    reason <<
      if inspected_scopes.many?
        ' do not seem to be attributes'
      else
        ' does not seem to be an attribute'
      end

    reason << " on #{model.name}."

    @failure_reason = reason

    false
  end
end
matches_scopes_configuration?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 410
def matches_scopes_configuration?
  if scopes_match?
    true
  else
    @failure_reason = 'Expected the validation '

    @failure_reason <<
      if expected_scopes.empty?
        'not to be scoped to anything, '
      else
        "to be scoped to #{inspected_expected_scopes}, "
      end

    if actual_sets_of_scopes.any?
      @failure_reason << 'but it was scoped to '
      @failure_reason << "#{inspected_actual_scopes} instead."
    else
      @failure_reason << 'but it was not scoped to anything.'
    end

    false
  end
end
matches_uniqueness_with_case_sensitivity_strategy?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 695
def matches_uniqueness_with_case_sensitivity_strategy?
  if should_test_case_sensitivity?
    value = existing_value_read
    swapcased_value = value.swapcase

    if case_sensitivity_strategy == :sensitive
      if value == swapcased_value
        raise NonCaseSwappableValueError.create(
          model: model,
          attribute: @attribute,
          value: value,
        )
      end

      allows_value_of(swapcased_value, @expected_message)
    else
      disallows_value_of(swapcased_value, @expected_message)
    end
  else
    true
  end
end
matches_uniqueness_with_scopes?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 755
def matches_uniqueness_with_scopes?
  expected_scopes.none? ||
    all_scopes_are_booleans? ||
    expected_scopes.all? do |scope|
      setting_next_value_for(scope) do
        allows_value_of(existing_value_read, @expected_message)
      end
    end
end
matches_uniqueness_without_scopes?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 677
def matches_uniqueness_without_scopes?
  if existing_value_read.blank?
    update_existing_record!(arbitrary_non_blank_value)
  end

  disallows_value_of(existing_value_read, @expected_message)
end
model() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 934
def model
  @given_record.class
end
model_class?(model_name) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 749
def model_class?(model_name)
  model_name.constantize.ancestors.include?(::ActiveRecord::Base)
rescue NameError
  false
end
new_record() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 384
def new_record
  unless defined?(@new_record)
    build_new_record
  end

  @new_record
end
Also aliased as: subject
next_scalar_value_for(scope, previous_value) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 815
def next_scalar_value_for(scope, previous_value)
  column = column_for(scope)

  if column.type == :uuid
    SecureRandom.uuid
  elsif defined_as_enum?(scope)
    available_values = available_enum_values_for(scope, previous_value)
    available_values.keys.last
  elsif polymorphic_type_attribute?(scope, previous_value)
    Uniqueness::TestModels.create(previous_value).to_s
  elsif previous_value.respond_to?(:next)
    previous_value.next
  elsif previous_value.respond_to?(:to_datetime)
    previous_value.to_datetime.in(60).next
  elsif boolean_value?(previous_value)
    !previous_value
  else
    previous_value.to_s.next
  end
end
next_value_for(scope, previous_value) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 807
def next_value_for(scope, previous_value)
  if previous_value.is_a?(Array)
    [next_scalar_value_for(scope, previous_value[0])]
  else
    next_scalar_value_for(scope, previous_value)
  end
end
polymorphic_type_attribute?(scope, previous_value) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 851
def polymorphic_type_attribute?(scope, previous_value)
  scope.to_s =~ /_type$/ && model_class?(previous_value)
end
scopes_match?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 453
def scopes_match?
  actual_sets_of_scopes.empty? && expected_scopes.empty? ||
    actual_sets_of_scopes.any? { |scopes| scopes == expected_scopes }
end
scopes_missing_on_model() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 671
def scopes_missing_on_model
  @_scopes_missing_on_model ||= expected_scopes.reject do |scope|
    model.method_defined?("#{scope}=")
  end
end
scopes_present_on_model() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 665
def scopes_present_on_model
  @_scopes_present_on_model ||= expected_scopes.select do |scope|
    model.method_defined?("#{scope}=")
  end
end
set_attribute_on!(record_type, record, attribute_name, value) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 861
def set_attribute_on!(record_type, record, attribute_name, value)
  attribute_setter = build_attribute_setter(
    record,
    attribute_name,
    value,
  )
  attribute_setter.set!

  @attribute_setters[record_type] << attribute_setter
end
set_attribute_on_existing_record!(attribute_name, value) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 872
def set_attribute_on_existing_record!(attribute_name, value)
  set_attribute_on!(
    :existing_record,
    existing_record,
    attribute_name,
    value,
  )
end
set_attribute_on_new_record!(attribute_name, value) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 881
def set_attribute_on_new_record!(attribute_name, value)
  set_attribute_on!(
    :new_record,
    new_record,
    attribute_name,
    value,
  )
end
setting_next_value_for(scope) { || ... } click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 776
def setting_next_value_for(scope)
  previous_value = @all_records.map(&scope).compact.max

  next_value =
    if previous_value.blank?
      dummy_value_for(scope)
    else
      next_value_for(scope, previous_value)
    end

  set_attribute_on_new_record!(scope, next_value)

  yield
ensure
  set_attribute_on_new_record!(scope, previous_value)
end
should_test_case_sensitivity?() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 743
def should_test_case_sensitivity?
  case_sensitivity_strategy != :ignore &&
    existing_value_read.respond_to?(:swapcase) &&
    !existing_value_read.empty?
end
subject()
Alias for: new_record
update_existing_record!(value) click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 550
def update_existing_record!(value)
  if existing_value_read != value
    set_attribute_on_existing_record!(@attribute, value)
    # It would be nice if we could ensure that the record was valid,
    # but that would break users' existing tests
    existing_record.save(validate: false)
  end

  true
end
validations() click to toggle source
# File lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb, line 404
def validations
  model.validators_on(@attribute).select do |validator|
    validator.is_a?(::ActiveRecord::Validations::UniquenessValidator)
  end
end