Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds accept_nested_attributes_for matcher
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
Showing
3 changed files
with
212 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
127 changes: 127 additions & 0 deletions
127
lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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 |
84 changes: 84 additions & 0 deletions
84
spec/shoulda/active_record/accept_nested_attributes_for_matcher_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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 |