class Shoulda::Matchers::ActiveRecord::AssociationMatcher

@private

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 993
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 1064
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 1074
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 1059
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 1032
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 1014
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 1129
def description
  description = "#{macro_description} #{name}"
  description += " class_name => #{options[:class_name]}" if options.key?(:class_name)
  [description, submatchers.map(&:description)].flatten.join(' ')
end
failure_message() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1135
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 1139
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 1069
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 1041
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 1119
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 1160
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 1143
def matches?(subject)
  @subject = subject
  association_exists? &&
    macro_correct? &&
    (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 1164
def option_verifier
  @option_verifier ||= AssociationMatchers::OptionVerifier.new(reflector)
end
optional() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1099
def optional
  remove_submatcher(AssociationMatchers::RequiredMatcher)
  add_submatcher(
    AssociationMatchers::OptionalMatcher,
    name,
    true,
  )
  self
end
order(order) click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1023
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 1089
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 1050
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 1005
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 1114
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 1109
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 1079
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 1084
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 1124
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 1176
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 1223
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 1302
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 1257
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 1414
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 1294
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 1362
def class_has_foreign_key?(klass)
  if options.key?(:foreign_key)
    option_verifier.correct_for_string?(:foreign_key, options[:foreign_key])
  elsif column_names_for(klass).include?(foreign_key)
    true
  else
    @missing = "#{klass} does not have a #{foreign_key} foreign key."
    false
  end
end
class_name_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1267
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 1408
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 1331
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 1200
def expectation
  "#{model_class.name} to have a #{macro} association called #{name}"
end
failing_submatchers() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1209
def failing_submatchers
  @failing_submatchers ||= submatchers.select do |matcher|
    !matcher.matches?(subject)
  end
end
foreign_key() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1386
def foreign_key
  if foreign_key_reflection
    if foreign_key_reflection.respond_to?(:foreign_key)
      foreign_key_reflection.foreign_key.to_s
    else
      foreign_key_reflection.primary_key_name.to_s
    end
  end
end
foreign_key_exists?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1249
def foreign_key_exists?
  !(belongs_foreign_key_missing? || has_foreign_key_missing?)
end
foreign_key_reflection() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1396
def foreign_key_reflection
  if [:has_one, :has_many].include?(macro) && reflection.options.include?(:inverse_of)
    associated_class.reflect_on_association(reflection.options[:inverse_of])
  else
    reflection
  end
end
has_foreign_key_missing?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1261
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 1315
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]}"
    false
  end
end
join_table_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1280
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 1289
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 1232
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 1187
def macro_description
  case macro.to_s
  when 'belongs_to'
    'belong to'
  when 'has_many'
    'have many'
  when 'has_one'
    'have one'
  when 'has_and_belongs_to_many'
    'have and belong to many'
  end
end
macro_supports_primary_key?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1244
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 1204
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 1215
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 1373
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 1253
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 1172
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 1181
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 1404
def submatchers_match?
  failing_submatchers.empty?
end
touch_correct?() click to toggle source
# File lib/shoulda/matchers/active_record/association_matcher.rb, line 1353
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 1344
def validate_correct?
  if option_verifier.correct_for_boolean?(:validate, options[:validate])
    true
  else
    @missing = "#{name} should have validate: #{options[:validate]}"
    false
  end
end