diff --git a/lib/shoulda/matchers/active_model/allow_value_matcher.rb b/lib/shoulda/matchers/active_model/allow_value_matcher.rb index 4793e770..fbb9e061 100644 --- a/lib/shoulda/matchers/active_model/allow_value_matcher.rb +++ b/lib/shoulda/matchers/active_model/allow_value_matcher.rb @@ -2,7 +2,10 @@ module Shoulda # :nodoc: module Matchers module ActiveModel # :nodoc: - # Ensures that the attribute can be set to the given value. + # Ensures that the attribute can be set to the given value or values. If + # multiple values are given the match succeeds only if all given values + # are allowed. Otherwise, the matcher fails at the first bad value in the + # argument list (the remaining arguments are not processed then). # # Options: # * with_message - value the test expects to find in @@ -13,15 +16,16 @@ module Shoulda # :nodoc: # it { should_not allow_value('bad').for(:isbn) } # it { should allow_value("isbn 1 2345 6789 0").for(:isbn) } # - def allow_value(value) - AllowValueMatcher.new(value) + def allow_value(*values) + raise ArgumentError.new("need at least one argument") if values.empty? + AllowValueMatcher.new(*values) end class AllowValueMatcher # :nodoc: include Helpers - def initialize(value) - @value = value + def initialize(*values) + @values_to_match = values end def for(attribute) @@ -39,8 +43,12 @@ module Shoulda # :nodoc: if Symbol === @expected_message @expected_message = default_error_message(@expected_message, :model_name => @instance.class.to_s.underscore, :attribute => @attribute) end - @instance.send("#{@attribute}=", @value) - !errors_match? + @values_to_match.each do |value| + @value = value + @instance.send("#{@attribute}=", @value) + return false if errors_match? + end + true end def failure_message @@ -52,7 +60,12 @@ module Shoulda # :nodoc: end def description - "allow #{@attribute} to be set to #{@value.inspect}" + "allow #{@attribute} to be set to " << + if @values_to_match.length > 1 + "any of [#{@values_to_match.map {|v| v.inspect }.join(', ')}]" + else + @values_to_match.first.inspect + end end private diff --git a/spec/shoulda/active_model/allow_value_matcher_spec.rb b/spec/shoulda/active_model/allow_value_matcher_spec.rb index f06de588..4c79573d 100644 --- a/spec/shoulda/active_model/allow_value_matcher_spec.rb +++ b/spec/shoulda/active_model/allow_value_matcher_spec.rb @@ -17,6 +17,14 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do it "should not allow a bad value" do @model.should_not allow_value("xyz").for(:attr) end + + it "should allow several good values" do + @model.should allow_value("abcde", "deabc").for(:attr) + end + + it "should not allow several bad values" do + @model.should_not allow_value("xyz", "zyx", nil, []).for(:attr) + end end context "an attribute with a format validation and a custom message" do @@ -51,12 +59,32 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher do @model.should allow_value("12345").for(:attr) end - bad_values = [nil, "", "abc", "0", "50001", "123456"] + bad_values = [nil, "", "abc", "0", "50001", "123456", []] + bad_values.each do |value| it "should not allow a bad value (#{value.inspect})" do @model.should_not allow_value(value).for(:attr) end end + + it "should not allow bad values (#{bad_values.map {|v| v.inspect}.join(', ')})" do + @model.should_not allow_value(*bad_values).for(:attr) + end end + context "an AllowValueMatcher with multiple values" do + before { @matcher = allow_value("foo", "bar").for(:baz) } + + it "should describe itself" do + @matcher.description.should eq('allow baz to be set to any of ["foo", "bar"]') + end + end + + context "an AllowValueMatcher with a single value" do + before { @matcher = allow_value("foo").for(:baz) } + + it "should describe itself" do + @matcher.description.should eq('allow baz to be set to "foo"') + end + end end