class NoBrainer::Matchers::Associations::HaveAssociationMatcher

The `belong_to` matcher is used to ensure that a `belongs_to` association exists on your model.

class Person
  include NoBrainer::Document

  belongs_to :organization
end

# RSpec
RSpec.describe Person, type: :model do
  it { is_expected.to belong_to(:organization) }
end

#### Qualifiers

##### class_name

Use `class_name` to test usage of the `:class_name` option. This asserts that the model you're referring to actually exists.

class Person
  include NoBrainer::Document

  belongs_to :ancient_city, class_name: 'City'
end

# RSpec
RSpec.describe Person, type: :model do
  it { is_expected.to belong_to(:ancient_city).class_name('City') }
end

##### with_primary_key

Use `with_primary_key` to test usage of the `:primary_key` option.

class Person
  include NoBrainer::Document

  belongs_to :great_country, primary_key: 'country_id'
end

# RSpec
RSpec.describe Person, type: :model do
  it do
    is_expected.to belong_to(:great_country)
      .with_primary_key('country_id')
  end
end

##### with_foreign_key

Use `with_foreign_key` to test usage of the `:foreign_key` option.

class Person
  include NoBrainer::Document

  belongs_to :great_country, foreign_key: 'country_id'
end

# RSpec
RSpec.describe Person, type: :model do
  it do
    is_expected.to belong_to(:great_country)
      .with_foreign_key('country_id')
  end
end

##### with_dependent

Use `with_dependent` to assert that the `:dependent` option was specified.

class Person
  include NoBrainer::Document

  belongs_to :world, dependent: :destroy
end

# RSpec
RSpec.describe Person, type: :model do
  it { is_expected.to belong_to(:world).with_dependent(:destroy) }
end

##### required

Use `required` to assert that the association is not allowed to be nil.

class Person
  include NoBrainer::Document

  belongs_to :organization, required: true
end

# RSpec
describe Person
  it { is_expected.to belong_to(:organization).with_required(true) }
end

Public Class Methods

new(name, association_type) click to toggle source
# File lib/matchers/associations.rb, line 112
def initialize(name, association_type)
  @association = {}
  @association[:name] = name.to_sym
  @association[:type] = association_type
  @expectation_message = "#{type_description} #{@association[:name].inspect}"
  @expectation_message += " of type #{@association[:class].inspect}" unless @association[:class].nil?
end

Public Instance Methods

class_name(klass) click to toggle source
# File lib/matchers/associations.rb, line 120
def class_name(klass)
  @association[:class] = klass
  @expectation_message += " with class_name #{@association[:class].inspect}"
  self
end
description() click to toggle source
# File lib/matchers/associations.rb, line 242
def description
  @expectation_message
end
failure_message()
failure_message_for_should() click to toggle source
# File lib/matchers/associations.rb, line 231
def failure_message_for_should
  "Expected #{@actual.inspect} to #{@expectation_message}, got #{@negative_result_message}"
end
Also aliased as: failure_message
failure_message_for_should_not() click to toggle source
# File lib/matchers/associations.rb, line 235
def failure_message_for_should_not
  "Expected #{@actual.inspect} to not #{@expectation_message}, got #{@positive_result_message}"
end
Also aliased as: failure_message_when_negated
failure_message_when_negated()
matches?(actual) click to toggle source
# File lib/matchers/associations.rb, line 157
def matches?(actual)
  @actual = actual.is_a?(Class) ? actual : actual.class
  metadata = @actual.association_metadata[@association[:name]]

  if metadata.nil?
    @negative_result_message = "no association named #{@association[:name]}"
    return false
  else
    @positive_result_message = "association named #{@association[:name]}"
  end

  relation = metadata.class.name.gsub(/::Metadata$/, '').constantize
  if relation != @association[:type]
    @negative_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name].inspect}"
    return false
  else
    @positive_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name].inspect}"
  end

  if !@association[:class].nil? && (@association[:class] != metadata.options[:class_name])
    @negative_result_message = "#{@positive_result_message} with class_name #{metadata.options[:class_name].inspect}"
    return false
  else
    @positive_result_message = "#{@positive_result_message}#{" with class_name #{metadata.options[:class_name].inspect}" if @association[:class]}"
  end

  if @association[:dependent]
    if @association[:dependent].to_sym != (metadata.options[:dependent] && metadata.options[:dependent].to_sym)
      @negative_result_message = "#{@positive_result_message} which specified dependent as #{metadata.options[:dependent]}"
      return false
    else
      @positive_result_message = "#{@positive_result_message} which specified dependent as #{metadata.options[:dependent]}"
    end
  end

  if @association[:primary_key]
    if metadata.options[:primary_key] != @association[:primary_key]
      @negative_result_message = "#{@positive_result_message} with primary key #{metadata.options[:primary_key].inspect}"
      return false
    else
      @positive_result_message = "#{@positive_result_message} with primary key #{metadata.options[:primary_key].inspect}"
    end
  end

  if @association[:foreign_key]
    if metadata.foreign_key != @association[:foreign_key]
      @negative_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
      return false
    else
      @positive_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
    end
  end

  if @association[:through]
    if metadata.options[:through] != @association[:through]
      @negative_result_message = "#{@positive_result_message} through #{metadata.options[:through].inspect}"
      return false
    else
      @positive_result_message = "#{@positive_result_message} through #{metadata.options[:through].inspect}"
    end
  end

  if @association[:required]
    if metadata.options[:required] != @association[:required]
      @negative_result_message = "#{@positive_result_message} with required #{metadata.options[:required].inspect}"
      return false
    else
      @positive_result_message = "#{@positive_result_message} with required #{metadata.options[:required].inspect}"
    end
  end

  true
end
through(association_key) click to toggle source
# File lib/matchers/associations.rb, line 126
def through(association_key)
  @association[:through] = association_key
  @association[:type] = @association[:type] == HAS_ONE ? HAS_ONE_THROUGH : HAS_MANY_THROUGH
  @expectation_message += " through #{association_key.inspect}"
  self
end
type_description(type = nil, passive = true) click to toggle source
# File lib/matchers/associations.rb, line 246
def type_description(type = nil, passive = true)
  type ||= @association[:type]
  case type.name
  when HAS_ONE.name
    (passive ? 'have one' : 'having one')
  when HAS_ONE_THROUGH.name
    (passive ? 'have one through' : 'having one')
  when HAS_MANY.name
    (passive ? 'have many' : 'having many')
  when HAS_MANY_THROUGH.name
    (passive ? 'have many through' : 'having many')
  when BELONGS_TO.name
    (passive ? 'belong to' : 'belonging to')
  else
    raise format("Unknown association type #{type}")
  end
end
with_dependent(method_name) click to toggle source
# File lib/matchers/associations.rb, line 133
def with_dependent(method_name)
  @association[:dependent] = method_name
  @expectation_message += " which specifies dependent as #{@association[:dependent]}"
  self
end
with_foreign_key(foreign_key) click to toggle source
# File lib/matchers/associations.rb, line 145
def with_foreign_key(foreign_key)
  @association[:foreign_key] = foreign_key.to_sym
  @expectation_message += " using foreign key #{@association[:foreign_key].inspect}"
  self
end
with_primary_key(primary_key) click to toggle source
# File lib/matchers/associations.rb, line 139
def with_primary_key(primary_key)
  @association[:primary_key] = primary_key.to_sym
  @expectation_message += " using primary key #{@association[:primary_key].inspect}"
  self
end
with_required(required) click to toggle source
# File lib/matchers/associations.rb, line 151
def with_required(required)
  @association[:required] = required
  @expectation_message += " with required #{@association[:required].inspect}"
  self
end

Private Instance Methods

association_kind_of() click to toggle source
# File lib/matchers/associations.rb, line 266
def association_kind_of
  Mongoid::Compatibility::Version.mongoid5_or_older? ? Origin::Key : Mongoid::Criteria::Queryable::Key
end