Skip to content

Commit

Permalink
Adds accept_nested_attributes_for matcher
Browse files Browse the repository at this point in the history
Currently supports the options "allow_destroy", "limit", and
"update_only"
  • Loading branch information
Brendan Loudermilk authored and Gabe Berke-Williams committed Apr 13, 2012
1 parent 6297c73 commit ee74222
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/shoulda/matchers/active_record.rb
Expand Up @@ -4,6 +4,7 @@
require 'shoulda/matchers/active_record/have_readonly_attribute_matcher' require 'shoulda/matchers/active_record/have_readonly_attribute_matcher'
require 'shoulda/matchers/active_record/serialize_matcher' require 'shoulda/matchers/active_record/serialize_matcher'
require 'shoulda/matchers/active_record/query_the_database_matcher' require 'shoulda/matchers/active_record/query_the_database_matcher'
require 'shoulda/matchers/active_record/accept_nested_attributes_for_matcher'


module Shoulda module Shoulda
module Matchers module Matchers
Expand Down
@@ -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
@@ -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

0 comments on commit ee74222

Please sign in to comment.