shoulda-matchers 1.4.1 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,33 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActiveModel # :nodoc:
4
+ class OnlyIntegerMatcher # :nodoc:
5
+ NON_INTEGER_VALUE = 0.1
6
+
7
+ def initialize(attribute)
8
+ @attribute = attribute
9
+ @disallow_value_matcher = DisallowValueMatcher.new(NON_INTEGER_VALUE).
10
+ for(attribute).
11
+ with_message(:not_an_integer)
12
+ end
13
+
14
+ def matches?(subject)
15
+ @disallow_value_matcher.matches?(subject)
16
+ end
17
+
18
+ def with_message(message)
19
+ @disallow_value_matcher.with_message(message)
20
+ self
21
+ end
22
+
23
+ def allowed_types
24
+ "integer"
25
+ end
26
+
27
+ def failure_message
28
+ @disallow_value_matcher.failure_message
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -17,55 +17,74 @@ module Shoulda # :nodoc:
17
17
  ValidateNumericalityOfMatcher.new(attr)
18
18
  end
19
19
 
20
- class ValidateNumericalityOfMatcher < ValidationMatcher # :nodoc:
20
+ class ValidateNumericalityOfMatcher
21
+ NON_NUMERIC_VALUE = 'abcd'
22
+
21
23
  def initialize(attribute)
22
- super(attribute)
24
+ @attribute = attribute
23
25
  @options = {}
26
+ @submatchers = []
27
+
28
+ add_disallow_value_matcher
24
29
  end
25
30
 
26
31
  def only_integer
27
- @options[:only_integer] = true
32
+ only_integer_matcher = OnlyIntegerMatcher.new(@attribute)
33
+ add_submatcher(only_integer_matcher)
28
34
  self
29
35
  end
30
36
 
31
37
  def with_message(message)
32
- if message
33
- @expected_message = message
34
- end
38
+ @submatchers.each { |matcher| matcher.with_message(message) }
35
39
  self
36
40
  end
37
41
 
38
42
  def matches?(subject)
39
- super(subject)
40
- disallows_non_integers? && disallows_text?
43
+ @subject = subject
44
+ submatchers_match?
41
45
  end
42
46
 
43
47
  def description
44
- "only allow #{allowed_type} values for #{@attribute}"
48
+ "only allow #{allowed_types} values for #{@attribute}"
49
+ end
50
+
51
+ def failure_message
52
+ submatcher_failure_messages.last
45
53
  end
46
54
 
47
55
  private
48
56
 
49
- def allowed_type
50
- if @options[:only_integer]
51
- "integer"
52
- else
53
- "numeric"
54
- end
57
+ def add_disallow_value_matcher
58
+ disallow_value_matcher = DisallowValueMatcher.new(NON_NUMERIC_VALUE).
59
+ for(@attribute).
60
+ with_message(:not_a_number)
61
+
62
+ add_submatcher(disallow_value_matcher)
63
+ end
64
+
65
+ def add_submatcher(submatcher)
66
+ @submatchers << submatcher
67
+ end
68
+
69
+ def submatchers_match?
70
+ failing_submatchers.empty?
71
+ end
72
+
73
+ def submatcher_failure_messages
74
+ failing_submatchers.map(&:failure_message)
75
+ end
76
+
77
+ def failing_submatchers
78
+ @failing_submatchers ||= @submatchers.select { |matcher| !matcher.matches?(@subject) }
55
79
  end
56
80
 
57
- def disallows_non_integers?
58
- if @options[:only_integer]
59
- message = @expected_message || :not_an_integer
60
- disallows_value_of(0.1, message)
61
- else
62
- true
63
- end
81
+ def allowed_types
82
+ allowed = ["numeric"] + submatcher_allowed_types
83
+ allowed.join(", ")
64
84
  end
65
85
 
66
- def disallows_text?
67
- message = @expected_message || :not_a_number
68
- disallows_value_of('abcd', message)
86
+ def submatcher_allowed_types
87
+ @submatchers.map(&:allowed_types).reject(&:empty?)
69
88
  end
70
89
  end
71
90
  end
@@ -0,0 +1,9 @@
1
+ require 'shoulda/matchers/independent/delegate_matcher'
2
+
3
+ module Shoulda
4
+ module Matchers
5
+ # = Matchers for non-Rails-dependent code.
6
+ module Independent
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,124 @@
1
+ require 'bourne'
2
+
3
+ module Shoulda # :nodoc:
4
+ module Matchers
5
+ module Independent # :nodoc:
6
+
7
+ # Ensure that a given method is delegated properly.
8
+ #
9
+ # Basic Syntax:
10
+ # it { should delegate_method(:deliver_mail).to(:mailman) }
11
+ #
12
+ # Options:
13
+ # * <tt>:as</tt> - tests that the object being delegated to is called with a certain method (defaults to same name as delegating method)
14
+ # * <tt>:with_arguments</tt> - tests that the method on the object being delegated to is called with certain arguments
15
+ #
16
+ # Examples:
17
+ # it { should delegate_method(:deliver_mail).to(:mailman).as(:deliver_with_haste)
18
+ # it { should delegate_method(:deliver_mail).to(:mailman).with_arguments('221B Baker St.', :hastily => true)
19
+ #
20
+ def delegate_method(delegating_method)
21
+ DelegateMatcher.new(delegating_method)
22
+ end
23
+
24
+ class DelegateMatcher
25
+ def initialize(delegating_method)
26
+ @delegating_method = delegating_method
27
+ end
28
+
29
+ def matches?(subject)
30
+ @subject = subject
31
+ ensure_target_method_is_present!
32
+
33
+ begin
34
+ extend Mocha::API
35
+
36
+ stubbed_object = stub(method_on_target)
37
+ subject.stubs(@target_method).returns(stubbed_object)
38
+ subject.send(@delegating_method)
39
+
40
+ stubbed_object.should have_received(method_on_target).with(*@delegated_arguments)
41
+ rescue NoMethodError, RSpec::Expectations::ExpectationNotMetError, Mocha::ExpectationError
42
+ false
43
+ end
44
+ end
45
+
46
+ def does_not_match?(subject)
47
+ raise InvalidDelegateMatcher
48
+ end
49
+
50
+ def to(target_method)
51
+ @target_method = target_method
52
+ self
53
+ end
54
+
55
+ def as(method_on_target)
56
+ @method_on_target = method_on_target
57
+ self
58
+ end
59
+
60
+ def with_arguments(*arguments)
61
+ @delegated_arguments = arguments
62
+ self
63
+ end
64
+
65
+ def failure_message
66
+ base = "Expected #{delegating_method_name} to delegate to #{target_method_name}"
67
+ add_clarifications_to(base)
68
+ end
69
+
70
+ private
71
+
72
+ def add_clarifications_to(message)
73
+ if @delegated_arguments.present?
74
+ message << " with arguments: #{@delegated_arguments.inspect}"
75
+ end
76
+
77
+ if @method_on_target.present?
78
+ message << " as :#{@method_on_target}"
79
+ end
80
+
81
+ message
82
+ end
83
+
84
+ def delegating_method_name
85
+ method_name_with_class(@delegating_method)
86
+ end
87
+
88
+ def target_method_name
89
+ method_name_with_class(@target_method)
90
+ end
91
+
92
+ def method_name_with_class(method)
93
+ if Class === @subject
94
+ @subject.name + '.' + method.to_s
95
+ else
96
+ @subject.class.name + '#' + method.to_s
97
+ end
98
+ end
99
+
100
+ def method_on_target
101
+ @method_on_target || @delegating_method
102
+ end
103
+
104
+ def ensure_target_method_is_present!
105
+ if @target_method.blank?
106
+ raise TargetNotDefinedError
107
+ end
108
+ end
109
+ end
110
+
111
+ class DelegateMatcher::TargetNotDefinedError < StandardError
112
+ def message
113
+ 'Delegation needs a target. Use the #to method to define one, e.g. `post_office.should delegate(:deliver_mail).to(:mailman)`'
114
+ end
115
+ end
116
+
117
+ class DelegateMatcher::InvalidDelegateMatcher < StandardError
118
+ def message
119
+ '#delegate_to does not support #should_not syntax.'
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -1,5 +1,10 @@
1
1
  # :enddoc:
2
2
 
3
+ require 'shoulda/matchers/independent'
4
+ module RSpec::Matchers
5
+ include Shoulda::Matchers::Independent
6
+ end
7
+
3
8
  if defined?(::ActiveRecord)
4
9
  require 'shoulda/matchers/active_record'
5
10
  require 'shoulda/matchers/active_model'
@@ -1,4 +1,15 @@
1
1
  # :enddoc:
2
+ require 'test/unit/testcase'
3
+ require 'shoulda/matchers/independent'
4
+
5
+ module Test
6
+ module Unit
7
+ class TestCase
8
+ include Shoulda::Matchers::Independent
9
+ extend Shoulda::Matchers::Independent
10
+ end
11
+ end
12
+ end
2
13
 
3
14
  if defined?(ActionController)
4
15
  require 'shoulda/matchers/action_controller'
@@ -1,5 +1,5 @@
1
1
  module Shoulda
2
2
  module Matchers
3
- VERSION = '1.4.1'.freeze
3
+ VERSION = '1.4.2'.freeze
4
4
  end
5
5
  end
@@ -18,10 +18,10 @@ Gem::Specification.new do |s|
18
18
  s.require_paths = ["lib"]
19
19
 
20
20
  s.add_dependency('activesupport', '>= 3.0.0')
21
+ s.add_dependency('bourne', '~> 1.1.2')
21
22
 
22
23
  s.add_development_dependency('appraisal', '~> 0.4.0')
23
24
  s.add_development_dependency('aruba')
24
- s.add_development_dependency('bourne', '~> 1.1.2')
25
25
  s.add_development_dependency('bundler', '~> 1.1')
26
26
  s.add_development_dependency('cucumber', '~> 1.1.9')
27
27
  s.add_development_dependency('rails', '~> 3.0')
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActiveModel::DisallowValueMatcher do
4
+ it "does not allow any types" do
5
+ matcher = Shoulda::Matchers::ActiveModel::DisallowValueMatcher.new("abcde")
6
+ matcher.allowed_types.should == ""
7
+ end
8
+
9
+ context "an attribute with a format validation" do
10
+ before do
11
+ define_model :example, :attr => :string do
12
+ validates_format_of :attr, :with => /abc/
13
+ end
14
+ @model = Example.new
15
+ end
16
+
17
+ it "does not match if the value is allowed" do
18
+ matcher = new_matcher("abcde")
19
+ matcher.for(:attr)
20
+ matcher.matches?(@model).should be_false
21
+ end
22
+
23
+ it "matches if the value is not allowed" do
24
+ matcher = new_matcher("xyz")
25
+ matcher.for(:attr)
26
+ matcher.matches?(@model).should be_true
27
+ end
28
+ end
29
+
30
+ context "an attribute with a format validation and a custom message" do
31
+ before do
32
+ define_model :example, :attr => :string do
33
+ validates_format_of :attr, :with => /abc/, :message => 'good message'
34
+ end
35
+ @model = Example.new
36
+ end
37
+
38
+ it "does not match if the value and message are both correct" do
39
+ matcher = new_matcher("abcde")
40
+ matcher.for(:attr).with_message('good message')
41
+ matcher.matches?(@model).should be_false
42
+ end
43
+
44
+ it "delegates its failure message to its allow matcher's negative failure message" do
45
+ allow_matcher = stub_everything(:negative_failure_message => "allow matcher failure")
46
+ Shoulda::Matchers::ActiveModel::AllowValueMatcher.stubs(:new).returns(allow_matcher)
47
+
48
+ matcher = new_matcher("abcde")
49
+ matcher.for(:attr).with_message('good message')
50
+ matcher.matches?(@model)
51
+
52
+ matcher.failure_message.should == "allow matcher failure"
53
+ end
54
+
55
+ it "matches if the message is correct but the value is not" do
56
+ matcher = new_matcher("xyz")
57
+ matcher.for(:attr).with_message('good message')
58
+ matcher.matches?(@model).should be_true
59
+ end
60
+ end
61
+
62
+ def new_matcher(value)
63
+ matcher = Shoulda::Matchers::ActiveModel::DisallowValueMatcher.new(value)
64
+ end
65
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shoulda::Matchers::ActiveModel::OnlyIntegerMatcher do
4
+ context "given an attribute that only allows integer values" do
5
+ before do
6
+ define_model :example, :attr => :string do
7
+ validates_numericality_of :attr, { :only_integer => true }
8
+ end
9
+ @model = Example.new
10
+ end
11
+
12
+ it "matches" do
13
+ matcher = new_matcher(:attr)
14
+ matcher.matches?(@model).should be_true
15
+ end
16
+
17
+ it "allows integer types" do
18
+ matcher = new_matcher(:attr)
19
+ matcher.allowed_types.should == "integer"
20
+ end
21
+
22
+ it "returns itself when given a message" do
23
+ matcher = new_matcher(:attr)
24
+ matcher.with_message("some message").should == matcher
25
+ end
26
+ end
27
+
28
+ context "given an attribute that only allows integer values with a custom validation message" do
29
+ before do
30
+ define_model :example, :attr => :string do
31
+ validates_numericality_of :attr, { :only_integer => true, :message => 'custom' }
32
+ end
33
+ @model = Example.new
34
+ end
35
+
36
+ it "should only allow integer values for that attribute with that message" do
37
+ matcher = new_matcher(:attr)
38
+ matcher.with_message(/custom/)
39
+ matcher.matches?(@model).should be_true
40
+ end
41
+
42
+ it "should not allow integer values for that attribute with another message" do
43
+ matcher = new_matcher(:attr)
44
+ matcher.with_message(/wrong/)
45
+ matcher.matches?(@model).should be_false
46
+ end
47
+ end
48
+
49
+ context "given an attribute that allows values other than integers" do
50
+ before do
51
+ @model = define_model(:example, :attr => :string).new
52
+ end
53
+
54
+ it "does not match" do
55
+ matcher = new_matcher(:attr)
56
+ matcher.matches?(@model).should be_false
57
+ end
58
+
59
+ it "should fail with the ActiveRecord :not_an_integer message" do
60
+ matcher = new_matcher(:attr)
61
+ matcher.matches?(@model)
62
+ matcher.failure_message.should include 'Expected errors to include "must be an integer"'
63
+ end
64
+ end
65
+
66
+ def new_matcher(attribute)
67
+ matcher = Shoulda::Matchers::ActiveModel::OnlyIntegerMatcher.new(attribute)
68
+ end
69
+ end