shoulda-matchers 1.4.1 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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