class Shoulda::Matchers::ActiveRecord::AssociationMatcher

@private

Constants

MACROS

Attributes

macro[R]
missing[R]
name[R]
options[R]
subject[R]
submatchers[R]

Public Class Methods

new(macro, name) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1000
def initialize(macro, name)
  @macro = macro
  @name = name
  @options = {}
  @submatchers = []
  @missing = ''

  if macro == :belongs_to && RailsShim.active_record_gte_5?
    required(belongs_to_required_by_default?)
  end
end

Public Instance Methods

autosave(autosave) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1071
def autosave(autosave)
  @options[:autosave] = autosave
  self
end
class_name(class_name) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1081
def class_name(class_name)
  @options[:class_name] = class_name
  self
end
conditions(conditions) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1066
def conditions(conditions)
  @options[:conditions] = conditions
  self
end
counter_cache(counter_cache = true) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1039
def counter_cache(counter_cache = true)
  add_submatcher(
    AssociationMatchers::CounterCacheMatcher,
    counter_cache,
    name,
  )
  self
end
dependent(dependent) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1021
def dependent(dependent)
  add_submatcher(
    AssociationMatchers::DependentMatcher,
    dependent,
    name,
  )
  self
end
description() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1136
def description
  description = "#{macro_description} #{name}"
  if options.key?(:class_name)
    description += " class_name => #{options[:class_name]}"
  end
  [description, submatchers.map(&:description)].flatten.join(' ')
end
failure_message() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1144
def failure_message
  "Expected #{expectation} (#{missing_options})"
end
failure_message_when_negated() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1148
def failure_message_when_negated
  "Did not expect #{expectation}"
end
index_errors(index_errors) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1076
def index_errors(index_errors)
  @options[:index_errors] = index_errors
  self
end
inverse_of(inverse_of) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1048
def inverse_of(inverse_of)
  add_submatcher(
    AssociationMatchers::InverseOfMatcher,
    inverse_of,
    name,
  )
  self
end
join_table(join_table_name) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1126
def join_table(join_table_name)
  @options[:join_table_name] = join_table_name
  self
end
join_table_name() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1170
def join_table_name
  options[:join_table_name] || reflector.join_table_name
end
matches?(subject) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1152
def matches?(subject)
  @subject = subject
  association_exists? &&
    macro_correct? &&
    validate_inverse_of_through_association &&
    (polymorphic? || class_exists?) &&
    foreign_key_exists? &&
    primary_key_exists? &&
    class_name_correct? &&
    join_table_correct? &&
    autosave_correct? &&
    index_errors_correct? &&
    conditions_correct? &&
    validate_correct? &&
    touch_correct? &&
    submatchers_match?
end
option_verifier() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1174
def option_verifier
  @_option_verifier ||=
    AssociationMatchers::OptionVerifier.new(reflector)
end
optional(optional = true) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1106
def optional(optional = true)
  remove_submatcher(AssociationMatchers::RequiredMatcher)
  add_submatcher(
    AssociationMatchers::OptionalMatcher,
    name,
    optional,
  )
  self
end
order(order) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1030
def order(order)
  add_submatcher(
    AssociationMatchers::OrderMatcher,
    order,
    name,
  )
  self
end
required(required = true) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1096
def required(required = true)
  remove_submatcher(AssociationMatchers::OptionalMatcher)
  add_submatcher(
    AssociationMatchers::RequiredMatcher,
    name,
    required,
  )
  self
end
source(source) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1057
def source(source)
  add_submatcher(
    AssociationMatchers::SourceMatcher,
    source,
    name,
  )
  self
end
through(through) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1012
def through(through)
  add_submatcher(
    AssociationMatchers::ThroughMatcher,
    through,
    name,
  )
  self
end
touch(touch = true) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1121
def touch(touch = true)
  @options[:touch] = touch
  self
end
validate(validate = true) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1116
def validate(validate = true)
  @options[:validate] = validate
  self
end
with_foreign_key(foreign_key) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1086
def with_foreign_key(foreign_key)
  @options[:foreign_key] = foreign_key
  self
end
with_primary_key(primary_key) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1091
def with_primary_key(primary_key)
  @options[:primary_key] = primary_key
  self
end
without_validating_presence() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1131
def without_validating_presence
  remove_submatcher(AssociationMatchers::RequiredMatcher)
  self
end

Protected Instance Methods

add_submatcher(matcher_class, *args) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1187
def add_submatcher(matcher_class, *args)
  remove_submatcher(matcher_class)
  submatchers << matcher_class.new(*args)
end
association_exists?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1232
def association_exists?
  if reflection.nil?
    @missing = "no association called #{name}"
    false
  else
    true
  end
end
autosave_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1328
def autosave_correct?
  if options.key?(:autosave)
    if option_verifier.correct_for_boolean?(
      :autosave,
      options[:autosave],
    )
      true
    else
      @missing = "#{name} should have autosave set to"\
        " #{options[:autosave]}"
      false
    end
  else
    true
  end
end
belongs_foreign_key_missing?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1274
def belongs_foreign_key_missing?
  macro == :belongs_to && !class_has_foreign_key?(model_class)
end
belongs_to_required_by_default?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1485
def belongs_to_required_by_default?
  ::ActiveRecord::Base.belongs_to_required_by_default
end
class_exists?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1320
def class_exists?
  associated_class
  true
rescue NameError
  @missing = "#{reflection.class_name} does not exist"
  false
end
class_has_foreign_key?(klass) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1396
def class_has_foreign_key?(klass)
  if options.key?(:foreign_key) && !foreign_key_correct?
    @missing = foreign_key_failure_message(klass, options[:foreign_key])
    false
  elsif !has_column?(klass, foreign_key)
    @missing = foreign_key_failure_message(klass, foreign_key)
    false
  else
    true
  end
end
class_name_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1284
def class_name_correct?
  if options.key?(:class_name)
    if option_verifier.correct_for_constant?(
      :class_name,
      options[:class_name],
    )
      true
    else
      @missing = "#{name} should resolve to #{options[:class_name]}"\
        ' for class_name'
      false
    end
  else
    true
  end
end
column_names_for(klass) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1479
def column_names_for(klass)
  klass.column_names
rescue ::ActiveRecord::StatementInvalid
  []
end
conditions_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1361
def conditions_correct?
  if options.key?(:conditions)
    if option_verifier.correct_for_relation_clause?(
      :conditions,
      options[:conditions],
    )
      true
    else
      @missing = "#{name} should have the following conditions:" +
                 " #{options[:conditions]}"
      false
    end
  else
    true
  end
end
expectation() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1202
def expectation
  expectation =
    "#{model_class.name} to have a #{macro} association called #{name}"

  if through?
    expectation << " through #{reflector.has_and_belongs_to_many_name}"
  end

  expectation
end
failing_submatchers() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1218
def failing_submatchers
  @_failing_submatchers ||= submatchers.reject do |matcher|
    matcher.matches?(subject)
  end
end
foreign_key() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1445
def foreign_key
  key = if foreign_key_reflection
          if foreign_key_reflection.respond_to?(:foreign_key)
            foreign_key_reflection.foreign_key
          else
            foreign_key_reflection.primary_key_name
          end
        end

  if key.is_a?(Array)
    key.map(&:to_s)
  else
    key.to_s
  end
end
foreign_key_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1417
def foreign_key_correct?
  option_verifier.correct_for_string?(
    :foreign_key,
    options[:foreign_key],
  )
end
foreign_key_exists?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1266
def foreign_key_exists?
  !(belongs_foreign_key_missing? || has_foreign_key_missing?)
end
foreign_key_failure_message(klass, foreign_key) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1424
def foreign_key_failure_message(klass, foreign_key)
  "#{klass} does not have a #{foreign_key} foreign key."
end
foreign_key_reflection() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1461
def foreign_key_reflection
  if (
    [:has_one, :has_many].include?(macro) &&
    reflection.options.include?(:inverse_of) &&
    reflection.options[:inverse_of] != false
  )
    associated_class.reflect_on_association(
      reflection.options[:inverse_of],
    )
  else
    reflection
  end
end
has_column?(klass, column) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1408
def has_column?(klass, column)
  case column
  when Array
    column.all? { |c| has_column?(klass, c) }
  else
    column_names_for(klass).include?(column)
  end
end
has_foreign_key_missing?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1278
def has_foreign_key_missing?
  [:has_many, :has_one].include?(macro) &&
    !through? &&
    !class_has_foreign_key?(associated_class)
end
index_errors_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1345
def index_errors_correct?
  return true unless options.key?(:index_errors)

  if option_verifier.correct_for_boolean?(
    :index_errors,
    options[:index_errors],
  )
    true
  else
    @missing =
      "#{name} should have index_errors set to " +
      options[:index_errors].to_s
    false
  end
end
join_table_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1301
def join_table_correct?
  if (
    macro != :has_and_belongs_to_many ||
    join_table_matcher.matches?(@subject)
  )
    true
  else
    @missing = join_table_matcher.failure_message
    false
  end
end
join_table_matcher() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1313
def join_table_matcher
  @_join_table_matcher ||= AssociationMatchers::JoinTableMatcher.new(
    self,
    reflector,
  )
end
macro_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1241
def macro_correct?
  if reflection.macro == macro
    true
  elsif reflection.macro == :has_many
    macro == :has_and_belongs_to_many &&
      reflection.name == @name
  else
    @missing = "actual association type was #{reflection.macro}"
    false
  end
end
macro_description() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1198
def macro_description
  MACROS[macro.to_s]
end
macro_supports_primary_key?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1261
def macro_supports_primary_key?
  macro == :belongs_to ||
    ([:has_many, :has_one].include?(macro) && !through?)
end
missing_options() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1213
def missing_options
  missing_options = [missing, missing_options_for_failing_submatchers]
  missing_options.flatten.select(&:present?).join(', ')
end
missing_options_for_failing_submatchers() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1224
def missing_options_for_failing_submatchers
  if defined?(@_failing_submatchers)
    @_failing_submatchers.map(&:missing_option)
  else
    []
  end
end
primary_key_correct?(klass) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1428
def primary_key_correct?(klass)
  if options.key?(:primary_key)
    if option_verifier.correct_for_string?(
      :primary_key,
      options[:primary_key],
    )
      true
    else
      @missing = "#{klass} does not have a #{options[:primary_key]}"\
        ' primary key'
      false
    end
  else
    true
  end
end
primary_key_exists?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1270
def primary_key_exists?
  !macro_supports_primary_key? || primary_key_correct?(model_class)
end
reflector() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1183
def reflector
  @_reflector ||= AssociationMatchers::ModelReflector.new(subject, name)
end
remove_submatcher(matcher_class) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1192
def remove_submatcher(matcher_class)
  submatchers.delete_if do |submatcher|
    submatcher.is_a?(matcher_class)
  end
end
submatchers_match?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1475
def submatchers_match?
  failing_submatchers.empty?
end
touch_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1387
def touch_correct?
  if option_verifier.correct_for_boolean?(:touch, options[:touch])
    true
  else
    @missing = "#{name} should have touch: #{options[:touch]}"
    false
  end
end
validate_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1378
def validate_correct?
  if option_verifier.correct_for_boolean?(:validate, options[:validate])
    true
  else
    @missing = "#{name} should have validate: #{options[:validate]}"
    false
  end
end
validate_inverse_of_through_association() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1253
def validate_inverse_of_through_association
  reflector.validate_inverse_of_through_association!
  true
rescue ::ActiveRecord::ActiveRecordError => e
  @missing = e.message
  false
end