Browse files

Use submatchers instead of inheritance for ValidatesNumericalityMatcher.

  • Loading branch information...
1 parent f5b5617 commit 6ba8c9f1a1a544ba939fb2acbffaaa1fbc01041c @readeharris readeharris committed with drapergeek Oct 16, 2012
View
2 lib/shoulda/matchers/active_model.rb
@@ -3,6 +3,8 @@
require 'shoulda/matchers/active_model/validation_message_finder'
require 'shoulda/matchers/active_model/exception_message_finder'
require 'shoulda/matchers/active_model/allow_value_matcher'
+require 'shoulda/matchers/active_model/disallow_value_matcher'
+require 'shoulda/matchers/active_model/only_integer_matcher'
require 'shoulda/matchers/active_model/ensure_length_of_matcher'
require 'shoulda/matchers/active_model/ensure_inclusion_of_matcher'
require 'shoulda/matchers/active_model/ensure_exclusion_of_matcher'
View
33 lib/shoulda/matchers/active_model/disallow_value_matcher.rb
@@ -0,0 +1,33 @@
+module Shoulda # :nodoc:
+ module Matchers
+ module ActiveModel # :nodoc:
+ class DisallowValueMatcher # :nodoc:
+ def initialize(value)
+ @allow_matcher = Shoulda::Matchers::ActiveModel::AllowValueMatcher.new(value)
+ end
+
+ def matches?(subject)
+ !@allow_matcher.matches?(subject)
+ end
+
+ def for(attribute)
+ @allow_matcher.for(attribute)
+ self
+ end
+
+ def with_message(message)
+ @allow_matcher.with_message(message)
+ self
+ end
+
+ def failure_message
+ @allow_matcher.negative_failure_message
+ end
+
+ def allowed_types
+ ""
+ end
+ end
+ end
+ end
+end
View
24 lib/shoulda/matchers/active_model/only_integer_matcher.rb
@@ -0,0 +1,24 @@
+module Shoulda # :nodoc:
+ module Matchers
+ module ActiveModel # :nodoc:
+ class OnlyIntegerMatcher # :nodoc:
+ def initialize(attribute)
+ @attribute = attribute
+ end
+
+ def matches?(subject)
+ matcher = AllowValueMatcher.new(0.1).for(@attribute)
+ !matcher.matches?(subject)
+ end
+
+ def with_message(message)
+ self
+ end
+
+ def allowed_types
+ "integer"
+ end
+ end
+ end
+ end
+end
View
61 lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb
@@ -17,55 +17,68 @@ def validate_numericality_of(attr)
ValidateNumericalityOfMatcher.new(attr)
end
- class ValidateNumericalityOfMatcher < ValidationMatcher # :nodoc:
+ class ValidateNumericalityOfMatcher
def initialize(attribute)
- super(attribute)
+ @attribute = attribute
@options = {}
+ @submatchers = []
+
+ add_disallow_value_matcher
end
def only_integer
- @options[:only_integer] = true
+ only_integer_matcher = OnlyIntegerMatcher.new(@attribute)
+ add_submatcher(only_integer_matcher)
+
self
end
def with_message(message)
- if message
- @expected_message = message
- end
+ @expected_message = message
self
end
def matches?(subject)
- super(subject)
- disallows_non_integers? && disallows_text?
+ @subject = subject
+ set_expected_message_on_submatchers
+ submatchers_match?
end
def description
- "only allow #{allowed_type} values for #{@attribute}"
+ "only allow #{allowed_types} values for #{@attribute}"
+ end
+
+ def failure_message
+ @disallow_value_matcher.failure_message
end
private
- def allowed_type
- if @options[:only_integer]
- "integer"
- else
- "numeric"
- end
+ def add_disallow_value_matcher
+ @disallow_value_matcher = DisallowValueMatcher.new('abcd').for(@attribute)
+ add_submatcher(@disallow_value_matcher)
end
- def disallows_non_integers?
- if @options[:only_integer]
- message = @expected_message || :not_an_integer
- disallows_value_of(0.1, message)
- else
- true
- end
+ def add_submatcher(submatcher)
+ @submatchers << submatcher
end
- def disallows_text?
+ def set_expected_message_on_submatchers
message = @expected_message || :not_a_number
- disallows_value_of('abcd', message)
+ @submatchers.each { |matcher| matcher.with_message(message) }
+ end
+
+ def submatchers_match?
+ @submatchers.all? { |matcher| matcher.matches?(@subject) }
+ end
+
+ def allowed_types
+ allowed = ["numeric"] + submatcher_allowed_types
+ allowed.join(", ")
+ end
+
+ def submatcher_allowed_types
+ @submatchers.map(&:allowed_types).reject { |type| type.empty? }
end
end
end
View
65 spec/shoulda/active_model/disallow_value_matcher_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe Shoulda::Matchers::ActiveModel::DisallowValueMatcher do
+ it "does not allow any types" do
+ matcher = Shoulda::Matchers::ActiveModel::DisallowValueMatcher.new("abcde")
+ matcher.allowed_types.should == ""
+ end
+
+ context "an attribute with a format validation" do
+ before do
+ define_model :example, :attr => :string do
+ validates_format_of :attr, :with => /abc/
+ end
+ @model = Example.new
+ end
+
+ it "does not match if the value is allowed" do
+ matcher = new_matcher("abcde")
+ matcher.for(:attr)
+ matcher.matches?(@model).should be_false
+ end
+
+ it "matches if the value is not allowed" do
+ matcher = new_matcher("xyz")
+ matcher.for(:attr)
+ matcher.matches?(@model).should be_true
+ end
+ end
+
+ context "an attribute with a format validation and a custom message" do
+ before do
+ define_model :example, :attr => :string do
+ validates_format_of :attr, :with => /abc/, :message => 'good message'
+ end
+ @model = Example.new
+ end
+
+ it "does not match if the value and message are both correct" do
+ matcher = new_matcher("abcde")
+ matcher.for(:attr).with_message('good message')
+ matcher.matches?(@model).should be_false
+ end
+
+ it "delegates its failure message to its allow matcher's negative failure message" do
+ allow_matcher = stub_everything(negative_failure_message: "allow matcher failure")
+ Shoulda::Matchers::ActiveModel::AllowValueMatcher.stubs(:new).returns(allow_matcher)
+
+ matcher = new_matcher("abcde")
+ matcher.for(:attr).with_message('good message')
+ matcher.matches?(@model)
+
+ matcher.failure_message.should == "allow matcher failure"
+ end
+
+ it "matches if the message is correct but the value is not" do
+ matcher = new_matcher("xyz")
+ matcher.for(:attr).with_message('good message')
+ matcher.matches?(@model).should be_true
+ end
+ end
+
+ def new_matcher(value)
+ matcher = Shoulda::Matchers::ActiveModel::DisallowValueMatcher.new(value)
+ end
+end
View
42 spec/shoulda/active_model/only_integer_matcher_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+
+describe Shoulda::Matchers::ActiveModel::OnlyIntegerMatcher do
+ context "given an attribute that only allows integer values" do
+ before do
+ define_model :example, :attr => :string do
+ validates_numericality_of :attr, { :only_integer => true }
+ end
+ @model = Example.new
+ end
+
+ it "matches" do
+ matcher = new_matcher(:attr)
+ matcher.matches?(@model).should be_true
+ end
+
+ it "allows integer types" do
+ matcher = new_matcher(:attr)
+ matcher.allowed_types.should == "integer"
+ end
+
+ it "returns itself when given a message" do
+ matcher = new_matcher(:attr)
+ matcher.with_message("some message").should == matcher
+ end
+ end
+
+ context "given an attribute that allows values other than integers" do
+ before do
+ @model = define_model(:example, :attr => :string).new
+ end
+
+ it "does not match" do
+ matcher = new_matcher(:attr)
+ matcher.matches?(@model).should be_false
+ end
+ end
+
+ def new_matcher(attribute)
+ matcher = Shoulda::Matchers::ActiveModel::OnlyIntegerMatcher.new(attribute)
+ end
+end
View
65 spec/shoulda/active_model/validate_numericality_of_matcher_spec.rb
@@ -1,8 +1,12 @@
require 'spec_helper'
describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher do
+ it "should state in its description that it allows only numeric values" do
+ matcher = Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher.new(:attr)
+ matcher.description.should == "only allow numeric values for attr"
+ end
- context "a numeric attribute" do
+ context "given a numeric attribute" do
before do
define_model :example, :attr => :string do
validates_numericality_of :attr
@@ -11,28 +15,51 @@
end
it "should only allow numeric values for that attribute" do
- @model.should validate_numericality_of(:attr)
+ matcher = new_matcher(:attr)
+ matcher.matches?(@model).should be_true
end
it "should not override the default message with a blank" do
- @model.should validate_numericality_of(:attr).with_message(nil)
+ matcher = new_matcher(:attr)
+ matcher.with_message(nil)
+ matcher.matches?(@model).should be_true
+ end
+
+ it "should not enforce integer values for that attribute" do
+ matcher = new_matcher(:attr)
+ matcher.only_integer
+ matcher.matches?(@model).should be_false
end
end
- context "a numeric attribute which must be integer" do
+ context "given a numeric attribute which must be integer" do
before do
define_model :example, :attr => :string do
- validates_numericality_of :attr, { :only_integer => true }
+ validates_numericality_of :attr, { :only_integer => true }
end
@model = Example.new
end
- it "should only allow integer values for that attribute" do
- @model.should validate_numericality_of(:attr).only_integer
+ it "allows integer values for that attribute" do
+ matcher = new_matcher(:attr)
+ matcher.only_integer
+ matcher.matches?(@model).should be_true
+ end
+
+ it "does not allow non-integer values for that attribute" do
+ matcher = new_matcher(:attr)
+ matcher.only_integer
+ matcher.matches?(@model).should be_true
+ end
+
+ it "should state in its description that it allows only integer values" do
+ matcher = new_matcher(:attr)
+ matcher.only_integer
+ matcher.description.should == "only allow numeric, integer values for attr"
end
end
- context "a numeric attribute with a custom validation message" do
+ context "given a numeric attribute with a custom validation message" do
before do
define_model :example, :attr => :string do
validates_numericality_of :attr, :message => 'custom'
@@ -41,22 +68,36 @@
end
it "should only allow numeric values for that attribute with that message" do
- @model.should validate_numericality_of(:attr).with_message(/custom/)
+ matcher = new_matcher(:attr)
+ matcher.with_message(/custom/)
+ matcher.matches?(@model).should be_true
end
it "should not allow numeric values for that attribute with another message" do
- @model.should_not validate_numericality_of(:attr)
+ matcher = new_matcher(:attr)
+ matcher.with_message(/wrong/)
+ matcher.matches?(@model).should be_false
end
end
- context "a non-numeric attribute" do
+ context "given a non-numeric attribute" do
before do
@model = define_model(:example, :attr => :string).new
end
it "should not only allow numeric values for that attribute" do
- @model.should_not validate_numericality_of(:attr)
+ matcher = new_matcher(:attr)
+ matcher.matches?(@model).should be_false
+ end
+
+ it "should fail with the ActiveRecord :not_a_number message" do
+ matcher = new_matcher(:attr)
+ matcher.matches?(@model)
+ matcher.failure_message.should include 'Expected errors to include "is not a number"'
end
end
+ def new_matcher(attr)
+ Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher.new(attr)
+ end
end

0 comments on commit 6ba8c9f

Please sign in to comment.