Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Adds accept_nested_attributes_for matcher

Currently supports the options "allow_destroy", "limit", and
"update_only"
  • Loading branch information...
commit ee7422280dc91b758b90303ab035159c622bae80 1 parent 6297c73
@bloudermilk bloudermilk authored gabebw committed
View
1  lib/shoulda/matchers/active_record.rb
@@ -4,6 +4,7 @@
require 'shoulda/matchers/active_record/have_readonly_attribute_matcher'
require 'shoulda/matchers/active_record/serialize_matcher'
require 'shoulda/matchers/active_record/query_the_database_matcher'
+require 'shoulda/matchers/active_record/accept_nested_attributes_for_matcher'
module Shoulda
module Matchers
View
127 lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb
@@ -0,0 +1,127 @@
+module Shoulda
+ module Matchers
+ module ActiveRecord
+ # Ensures that the model can accept nested attributes for the specified
+ # association.
+ #
+ # Options:
+ # * <tt>allow_destroy</tt> - Whether or not to allow destroy
+ # * <tt>limit</tt> - Max number of nested attributes
+ # * <tt>update_only</tt> - Only allow updates
+ #
+ # Example:
+ # it { should accept_nested_attributes_for(:friends) }
+ # it { should accept_nested_attributes_for(:friends).
+ # allow_destroy(true).
+ # limit(4) }
+ # it { should accept_nested_attributes_for(:friends).
+ # update_only(true) }
+ #
+ def accept_nested_attributes_for(name)
+ AcceptNestedAttributesForMatcher.new(name)
+ end
+
+ class AcceptNestedAttributesForMatcher
+ def initialize(name)
+ @name = name
+ self
+ end
+
+ def allow_destroy(allow_destroy)
+ @allow_destroy = allow_destroy
+ self
+ end
+
+ def limit(limit)
+ @limit = limit
+ self
+ end
+
+ def update_only(update_only)
+ @update_only = update_only
+ self
+ end
+
+ def matches?(subject)
+ @subject = subject
+ exists? &&
+ allow_destroy_correct? &&
+ limit_correct? &&
+ update_only_correct?
+ end
+
+ def failure_message
+ "Expected #{expectation} (#{@problem})"
+ end
+
+ def negative_failure_message
+ "Did not expect #{expectation}"
+ end
+
+ def description
+ description = "accepts_nested_attributes_for :#{@name}"
+ description += " allow_destroy => #{@allow_destroy}" if @allow_destroy
+ description += " limit => #{@limit}" if @limit
+ description += " update_only => #{@update_only}" if @update_only
+ description
+ end
+
+ protected
+
+ def exists?
+ if config
+ true
+ else
+ @problem = "is not declared"
+ false
+ end
+ end
+
+ def allow_destroy_correct?
+ if @allow_destroy.nil? || @allow_destroy == config[:allow_destroy]
+ true
+ else
+ @problem = (@allow_destroy ? "should" : "should not") +
+ " allow destroy"
+ false
+ end
+ end
+
+ def limit_correct?
+ if @limit.nil? || @limit == config[:limit]
+ true
+ else
+ @problem = "limit should be #@limit, got #{config[:limit]}"
+ false
+ end
+ end
+
+ def update_only_correct?
+ if @update_only.nil? || @update_only == config[:update_only]
+ true
+ else
+ @problem = (@update_only ? "should" : "should not") +
+ " be update only"
+ false
+ end
+ end
+
+ def config
+ model_config[@name]
+ end
+
+ def model_config
+ model_class.nested_attributes_options
+ end
+
+ def model_class
+ @subject.class
+ end
+
+ def expectation
+ "#{model_class.name} to accept nested attributes for #{@name}"
+ end
+ end
+ end
+ end
+end
View
84 spec/shoulda/active_record/accept_nested_attributes_for_matcher_spec.rb
@@ -0,0 +1,84 @@
+require 'spec_helper'
+
+describe Shoulda::Matchers::ActiveRecord::AcceptNestedAttributesForMatcher do
+ before do
+ define_model :child, :parent_id => :integer
+ define_model :parent do
+ has_many :children
+ end
+ end
+
+ let(:parent) { Parent.new }
+ let(:matcher) { accept_nested_attributes_for(:children) }
+
+ it "should accept an existing declaration" do
+ Parent.accepts_nested_attributes_for :children
+ matcher.matches?(parent).should be_true
+ end
+
+ it "should reject a missing declaration" do
+ matcher.matches?(parent).should be_false
+ matcher.failure_message.should == "Expected Parent to accept nested attributes for children (is not declared)"
+ end
+
+ context "allow_destroy" do
+ it "should accept a valid truthy value" do
+ Parent.accepts_nested_attributes_for :children, :allow_destroy => true
+ matcher.allow_destroy(true).matches?(parent).should be_true
+ end
+
+ it "should accept a valid falsey value" do
+ Parent.accepts_nested_attributes_for :children, :allow_destroy => false
+ matcher.allow_destroy(false).matches?(parent).should be_true
+ end
+
+ it "should reject an invalid truthy value" do
+ Parent.accepts_nested_attributes_for :children, :allow_destroy => true
+ matcher.allow_destroy(false).matches?(parent).should be_false
+ matcher.failure_message.should =~ /should not allow destroy/
+ end
+
+ it "should reject an invalid falsey value" do
+ Parent.accepts_nested_attributes_for :children, :allow_destroy => false
+ matcher.allow_destroy(true).matches?(parent).should be_false
+ matcher.failure_message.should =~ /should allow destroy/
+ end
+ end
+
+ context "limit" do
+ it "should accept a correct value" do
+ Parent.accepts_nested_attributes_for :children, :limit => 3
+ matcher.limit(3).matches?(parent).should be_true
+ end
+
+ it "should reject a false value" do
+ Parent.accepts_nested_attributes_for :children, :limit => 3
+ matcher.limit(2).matches?(parent).should be_false
+ matcher.failure_message.should =~ /limit should be 2, got 3/
+ end
+ end
+
+ context "update_only" do
+ it "should accept a valid truthy value" do
+ Parent.accepts_nested_attributes_for :children, :update_only => true
+ matcher.update_only(true).matches?(parent).should be_true
+ end
+
+ it "should accept a valid falsey value" do
+ Parent.accepts_nested_attributes_for :children, :update_only => false
+ matcher.update_only(false).matches?(parent).should be_true
+ end
+
+ it "should reject an invalid truthy value" do
+ Parent.accepts_nested_attributes_for :children, :update_only => true
+ matcher.update_only(false).matches?(parent).should be_false
+ matcher.failure_message.should =~ /should not be update only/
+ end
+
+ it "should reject an invalid falsey value" do
+ Parent.accepts_nested_attributes_for :children, :update_only => false
+ matcher.update_only(true).matches?(parent).should be_false
+ matcher.failure_message.should =~ /should be update only/
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.