<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: RSpec Matchers code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Sat, 17 May 2008 22:58:10 GMT</pubDate>
    <description>DZone Snippets: RSpec Matchers code</description>
    <item>
      <title>RSpec Association Matchers</title>
      <link>http://snippets.dzone.com/posts/show/4721</link>
      <description>Ok, what I essentially wanted to accomplish was something like:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;describe Product, 'with Group' do&lt;br /&gt;&lt;br /&gt;  it 'should belong to group' do&lt;br /&gt;    Product.should belong_to(:product_group)&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;describe ProductGroup, 'with Product' do&lt;br /&gt;&lt;br /&gt;  it 'should have many products depending (on group)' do&lt;br /&gt;    ProductGroup.should have_many(:products).depending&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Here is the code:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;module AssociationMatchers&lt;br /&gt;&lt;br /&gt;  class AssociationReflection&lt;br /&gt;&lt;br /&gt;    def initialize(type, name)&lt;br /&gt;      @messages = {&lt;br /&gt;        :missing_association =&gt;&lt;br /&gt;          '%s is not associated with %s.',&lt;br /&gt;        :wrong_type =&gt;&lt;br /&gt;          "%s %s %s./nExpected: %s",&lt;br /&gt;        :wrong_options =&gt;&lt;br /&gt;          "Options are incorrect.\nExpected: %s Got: %s",&lt;br /&gt;        :missing_column =&gt;&lt;br /&gt;          "Missing foreign key.\nExpected: %s"&lt;br /&gt;      }&lt;br /&gt;      @name = name&lt;br /&gt;      @expected_type = type&lt;br /&gt;      @expected_options = {}&lt;br /&gt;    end&lt;br /&gt;    &lt;br /&gt;    def matches?(target)&lt;br /&gt;      Class === target or&lt;br /&gt;      raise ArgumentError, 'class expected'&lt;br /&gt;&lt;br /&gt;      @target = target&lt;br /&gt;&lt;br /&gt;      unless @assoc = target.reflect_on_association(@name)&lt;br /&gt;        @failure = :missing_association&lt;br /&gt;        return false&lt;br /&gt;      end &lt;br /&gt;&lt;br /&gt;      unless @assoc.macro.eql?(@expected_type)&lt;br /&gt;        @failure = :wrong_type&lt;br /&gt;        return false&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;      if @expected_options.any? { |o| @assoc.options[o.first] != o.last }&lt;br /&gt;        @failure = :wrong_options&lt;br /&gt;        return false&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;      @column ||= @assoc.primary_key_name || @assoc.klass.name.foreign_key&lt;br /&gt;&lt;br /&gt;      @failure = case @assoc.macro.to_s&lt;br /&gt;      when 'belongs_to'&lt;br /&gt;        if @target.column_names.include?(@column.to_s) then nil&lt;br /&gt;        else&lt;br /&gt;          :missing_column&lt;br /&gt;        end&lt;br /&gt;      when /(?:has_many|has_one)/&lt;br /&gt;        if    @assoc.options[:through] then nil&lt;br /&gt;        elsif @assoc.klass.column_names.include?(@column.to_s) then nil&lt;br /&gt;        else&lt;br /&gt;          :missing_column&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;&lt;br /&gt;      return @failure.nil?&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def failure_message&lt;br /&gt;      case @failure&lt;br /&gt;      when :missing_association&lt;br /&gt;        @messages[@failure] % [@target.name, @name]&lt;br /&gt;      when :wrong_type&lt;br /&gt;        @messages[@failure] % [&lt;br /&gt;          @target.name,&lt;br /&gt;          @assoc.macro,&lt;br /&gt;          @name,&lt;br /&gt;          @expected_type&lt;br /&gt;        ]&lt;br /&gt;      when :wrong_options&lt;br /&gt;        @messages[@failure] % [&lt;br /&gt;          @expected_options.inspect,&lt;br /&gt;          @assoc.options.inspect&lt;br /&gt;        ]&lt;br /&gt;      when :missing_column&lt;br /&gt;        @messages[@failure] % @column&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;    def negative_failure_message&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    ### Generic Options&lt;br /&gt;&lt;br /&gt;    def of(class_name)&lt;br /&gt;      class_name = class_name.name if Class === class_name&lt;br /&gt;      @expected_options[:class_name] = class_name&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;    def for(foreign_key)&lt;br /&gt;      @column = foreign_key&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;    def due_to(conditions)&lt;br /&gt;      @expected_options[:conditions] = conditions&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;    def ordered_by(statement)&lt;br /&gt;      @expected_options[:order] = statement&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;    def including(*models)&lt;br /&gt;      @expected_options[:include] = (models.length == 1)? models.first: models&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;  end&lt;br /&gt;  class BelongsToReflection &lt; AssociationReflection&lt;br /&gt;&lt;br /&gt;    def initialize(name)&lt;br /&gt;      super :belongs_to, name&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def counted(column)&lt;br /&gt;      @expected_options[:counter_cache] = column&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;    def polymorphic(true_or_false = true)&lt;br /&gt;      @expected_options[:polymorphic] = true_or_false&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  class HasOneReflection &lt; AssociationReflection&lt;br /&gt;&lt;br /&gt;    def initialize(name)&lt;br /&gt;      super :has_one, name&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def as(interface_name)&lt;br /&gt;      @expected_options[:as] = interface_name&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;    def depending(dependency = true)&lt;br /&gt;      @expected_options[:dependent] = dependency&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;    def extended_by(mod)&lt;br /&gt;      @expected_options[:extend] = mod&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;  end&lt;br /&gt;  class HasManyReflection &lt; AssociationReflection&lt;br /&gt;&lt;br /&gt;    def initialize(name)&lt;br /&gt;      super :has_many, name&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def as(interface_name)&lt;br /&gt;      @expected_options[:as] = interface_name&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;    def depending(dependency = :destroy)&lt;br /&gt;      @expected_options[:dependent] = dependency&lt;br /&gt;      self&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;  end&lt;br /&gt;  class HasAndBelongsToManyReflection &lt; AssociationReflection&lt;br /&gt;&lt;br /&gt;    def initialize(name)&lt;br /&gt;      super :has_and_belongs_to_many, name&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def belong_to(model)&lt;br /&gt;    BelongsToReflection.new model&lt;br /&gt;  end&lt;br /&gt;  def have_one(model)&lt;br /&gt;    HasOneReflection.new model&lt;br /&gt;  end&lt;br /&gt;  def have_many(models)&lt;br /&gt;    HasManyReflection.new models&lt;br /&gt;  end&lt;br /&gt;  def have_and_belong_to_many(models)&lt;br /&gt;    HasAndBelongsToManyReflection.new models&lt;br /&gt;  end&lt;br /&gt;  alias_method :habtm, :have_and_belong_to_many&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;It checks:&lt;br /&gt;* association exists&lt;br /&gt;* association macro&lt;br /&gt;* foreign key exists (except for habtm)&lt;br /&gt;* options match (only a subset is supported)&lt;br /&gt;&lt;br /&gt;Setup:&lt;br /&gt;* put the code in RAILS_ROOT + "/lib/association_matchers.rb"&lt;br /&gt;* put "config.include AssociationMatchers # lib/association_matchers.rb" in your spec_helper.rb configure block&lt;br /&gt;* refactor your model specs...</description>
      <pubDate>Wed, 31 Oct 2007 09:42:07 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/4721</guid>
      <author>boof (Florian A&#223;mann)</author>
    </item>
  </channel>
</rss>
