rspec 1.1.4 → 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. data/{CHANGES → History.txt} +116 -64
  2. data/Manifest.txt +403 -0
  3. data/{MIT-LICENSE → README.txt} +43 -0
  4. data/Rakefile +39 -212
  5. data/{TODO → TODO.txt} +0 -0
  6. data/bin/autospec +4 -0
  7. data/bin/spec +1 -1
  8. data/examples/pure/yielding_example.rb +33 -0
  9. data/examples/stories/game-of-life/.loadpath +5 -0
  10. data/examples/stories/game-of-life/behaviour/everything.rb +1 -1
  11. data/examples/stories/game-of-life/behaviour/stories/CellsWithMoreThanThreeNeighboursDie.story +17 -17
  12. data/init.rb +9 -0
  13. data/lib/autotest/discover.rb +1 -1
  14. data/lib/autotest/rspec.rb +3 -29
  15. data/lib/spec.rb +10 -12
  16. data/lib/spec/adapters.rb +1 -0
  17. data/lib/spec/adapters/ruby_engine.rb +26 -0
  18. data/lib/spec/adapters/ruby_engine/mri.rb +8 -0
  19. data/lib/spec/adapters/ruby_engine/rubinius.rb +8 -0
  20. data/lib/spec/example/errors.rb +6 -0
  21. data/lib/spec/example/example_group_methods.rb +17 -14
  22. data/lib/spec/example/example_matcher.rb +2 -0
  23. data/lib/spec/example/example_methods.rb +4 -9
  24. data/lib/spec/example/module_inclusion_warnings.rb +2 -1
  25. data/lib/spec/expectations/extensions/object.rb +2 -2
  26. data/lib/spec/expectations/handler.rb +8 -16
  27. data/lib/spec/extensions/main.rb +2 -17
  28. data/lib/spec/matchers.rb +8 -2
  29. data/lib/spec/matchers/be.rb +0 -3
  30. data/lib/spec/matchers/change.rb +44 -40
  31. data/lib/spec/matchers/has.rb +1 -1
  32. data/lib/spec/matchers/have.rb +17 -12
  33. data/lib/spec/matchers/operator_matcher.rb +10 -4
  34. data/lib/spec/matchers/simple_matcher.rb +113 -10
  35. data/lib/spec/mocks.rb +1 -1
  36. data/lib/spec/mocks/argument_constraints.rb +185 -0
  37. data/lib/spec/mocks/argument_expectation.rb +35 -173
  38. data/lib/spec/mocks/framework.rb +1 -1
  39. data/lib/spec/mocks/message_expectation.rb +30 -5
  40. data/lib/spec/mocks/methods.rb +14 -2
  41. data/lib/spec/mocks/mock.rb +4 -0
  42. data/lib/spec/mocks/proxy.rb +46 -5
  43. data/lib/spec/mocks/spec_methods.rb +9 -1
  44. data/lib/spec/rake/spectask.rb +14 -22
  45. data/lib/spec/rake/verify_rcov.rb +3 -3
  46. data/lib/spec/runner.rb +18 -6
  47. data/lib/spec/runner/backtrace_tweaker.rb +6 -7
  48. data/lib/spec/runner/command_line.rb +6 -17
  49. data/lib/spec/runner/drb_command_line.rb +1 -1
  50. data/lib/spec/runner/formatter/base_formatter.rb +3 -1
  51. data/lib/spec/runner/formatter/base_text_formatter.rb +5 -9
  52. data/lib/spec/runner/formatter/html_formatter.rb +1 -1
  53. data/lib/spec/runner/formatter/nested_text_formatter.rb +1 -1
  54. data/lib/spec/runner/formatter/progress_bar_formatter.rb +2 -2
  55. data/lib/spec/runner/formatter/specdoc_formatter.rb +1 -1
  56. data/lib/spec/runner/formatter/story/html_formatter.rb +62 -16
  57. data/lib/spec/runner/formatter/story/plain_text_formatter.rb +68 -16
  58. data/lib/spec/runner/formatter/story/progress_bar_formatter.rb +42 -0
  59. data/lib/spec/runner/heckle_runner.rb +2 -2
  60. data/lib/spec/runner/option_parser.rb +2 -1
  61. data/lib/spec/runner/options.rb +18 -9
  62. data/lib/spec/runner/reporter.rb +24 -4
  63. data/lib/spec/runner/spec_parser.rb +1 -1
  64. data/lib/spec/story/runner.rb +1 -2
  65. data/lib/spec/story/runner/story_mediator.rb +14 -0
  66. data/lib/spec/story/runner/story_parser.rb +20 -0
  67. data/lib/spec/story/step.rb +40 -28
  68. data/lib/spec/story/step_mother.rb +2 -1
  69. data/lib/spec/story/world.rb +6 -2
  70. data/lib/spec/version.rb +13 -22
  71. data/rake_tasks/failing_examples_with_html.rake +1 -1
  72. data/rake_tasks/verify_rcov.rake +2 -2
  73. data/rspec.gemspec +33 -0
  74. data/spec/autotest/rspec_spec.rb +90 -141
  75. data/spec/spec/adapters/ruby_engine_spec.rb +16 -0
  76. data/spec/spec/example/base_formatter_spec.rb +112 -0
  77. data/spec/spec/example/example_group_factory_spec.rb +2 -2
  78. data/spec/spec/example/example_group_methods_spec.rb +55 -4
  79. data/spec/spec/example/example_group_spec.rb +4 -3
  80. data/spec/spec/example/example_methods_spec.rb +18 -14
  81. data/spec/spec/example/pending_module_spec.rb +38 -0
  82. data/spec/spec/example/shared_example_group_spec.rb +1 -1
  83. data/spec/spec/expectations/extensions/object_spec.rb +0 -12
  84. data/spec/spec/extensions/main_spec.rb +3 -8
  85. data/spec/spec/matchers/change_spec.rb +16 -6
  86. data/spec/spec/matchers/handler_spec.rb +58 -37
  87. data/spec/spec/matchers/has_spec.rb +10 -0
  88. data/spec/spec/matchers/have_spec.rb +105 -2
  89. data/spec/spec/matchers/operator_matcher_spec.rb +35 -2
  90. data/spec/spec/matchers/simple_matcher_spec.rb +64 -2
  91. data/spec/spec/mocks/any_number_of_times_spec.rb +7 -0
  92. data/spec/spec/mocks/bug_report_496.rb +17 -0
  93. data/spec/spec/mocks/failing_mock_argument_constraints_spec.rb +7 -1
  94. data/spec/spec/mocks/hash_including_matcher_spec.rb +45 -24
  95. data/spec/spec/mocks/mock_spec.rb +55 -10
  96. data/spec/spec/mocks/nil_expectation_warning_spec.rb +54 -0
  97. data/spec/spec/mocks/null_object_mock_spec.rb +14 -0
  98. data/spec/spec/mocks/options_hash_spec.rb +18 -28
  99. data/spec/spec/mocks/partial_mock_spec.rb +2 -0
  100. data/spec/spec/mocks/passing_mock_argument_constraints_spec.rb +20 -6
  101. data/spec/spec/mocks/stub_spec.rb +7 -0
  102. data/spec/spec/runner/command_line_spec.rb +5 -12
  103. data/spec/spec/runner/drb_command_line_spec.rb +13 -6
  104. data/spec/spec/runner/formatter/html_formatter_spec.rb +2 -1
  105. data/spec/spec/runner/formatter/nested_text_formatter_spec.rb +3 -3
  106. data/spec/spec/runner/formatter/progress_bar_formatter_spec.rb +20 -2
  107. data/spec/spec/runner/formatter/spec_mate_formatter_spec.rb +2 -1
  108. data/spec/spec/runner/formatter/specdoc_formatter_spec.rb +3 -3
  109. data/spec/spec/runner/formatter/story/html_formatter_spec.rb +76 -2
  110. data/spec/spec/runner/formatter/story/plain_text_formatter_spec.rb +161 -0
  111. data/spec/spec/runner/formatter/story/progress_bar_formatter_spec.rb +82 -0
  112. data/spec/spec/runner/heckle_runner_spec.rb +8 -8
  113. data/spec/spec/runner/option_parser_spec.rb +21 -6
  114. data/spec/spec/runner/output_one_time_fixture_runner.rb +1 -1
  115. data/spec/spec/runner/quiet_backtrace_tweaker_spec.rb +6 -0
  116. data/spec/spec/runner/reporter_spec.rb +51 -5
  117. data/spec/spec/runner/spec_parser_spec.rb +4 -4
  118. data/spec/spec/story/runner/plain_text_story_runner_spec.rb +2 -5
  119. data/spec/spec/story/runner/story_mediator_spec.rb +10 -0
  120. data/spec/spec/story/runner/story_parser_spec.rb +23 -6
  121. data/spec/spec/story/scenario_spec.rb +1 -3
  122. data/spec/spec/story/step_mother_spec.rb +12 -0
  123. data/spec/spec/story/step_spec.rb +57 -4
  124. data/spec/spec/story/story_spec.rb +1 -3
  125. data/spec/spec/story/world_spec.rb +1 -1
  126. data/spec/spec_helper.rb +21 -68
  127. data/stories/all.rb +1 -1
  128. data/stories/configuration/before_blocks.story +21 -0
  129. data/stories/configuration/stories.rb +7 -0
  130. data/stories/example_groups/stories.rb +3 -4
  131. data/stories/resources/spec/before_blocks_example.rb +32 -0
  132. data/stories/stories/multiline_steps.story +23 -0
  133. data/stories/stories/steps/multiline_steps.rb +13 -0
  134. data/stories/stories/stories.rb +6 -0
  135. data/story_server/prototype/javascripts/builder.js +136 -0
  136. data/story_server/prototype/javascripts/controls.js +972 -0
  137. data/story_server/prototype/javascripts/dragdrop.js +976 -0
  138. data/story_server/prototype/javascripts/effects.js +1117 -0
  139. data/story_server/prototype/javascripts/prototype.js +4140 -0
  140. data/story_server/prototype/javascripts/rspec.js +149 -0
  141. data/story_server/prototype/javascripts/scriptaculous.js +58 -0
  142. data/story_server/prototype/javascripts/slider.js +276 -0
  143. data/story_server/prototype/javascripts/sound.js +55 -0
  144. data/story_server/prototype/javascripts/unittest.js +568 -0
  145. data/story_server/prototype/lib/server.rb +24 -0
  146. data/story_server/prototype/stories.html +176 -0
  147. data/story_server/prototype/stylesheets/rspec.css +136 -0
  148. data/story_server/prototype/stylesheets/test.css +90 -0
  149. metadata +166 -166
  150. data/README +0 -36
  151. data/UPGRADE +0 -7
  152. data/bin/spec_translator +0 -8
  153. data/lib/spec/mocks/argument_constraint_matchers.rb +0 -31
  154. data/lib/spec/translator.rb +0 -114
  155. data/spec/spec/example/example_spec.rb +0 -53
  156. data/spec/spec/runner/execution_context_spec.rb +0 -37
  157. data/spec/spec/translator_spec.rb +0 -265
@@ -2,38 +2,29 @@ module Spec
2
2
  module Expectations
3
3
  class InvalidMatcherError < ArgumentError; end
4
4
 
5
- module MatcherHandlerHelper
6
- def describe_matcher(matcher)
7
- matcher.respond_to?(:description) ? matcher.description : "[#{matcher.class.name} does not provide a description]"
8
- end
9
- end
10
-
11
5
  class ExpectationMatcherHandler
12
6
  class << self
13
- include MatcherHandlerHelper
14
7
  def handle_matcher(actual, matcher, &block)
15
- if :use_operator_matcher == matcher
16
- return Spec::Matchers::PositiveOperatorMatcher.new(actual)
17
- end
8
+ ::Spec::Matchers.last_should = "should"
9
+ return Spec::Matchers::PositiveOperatorMatcher.new(actual) if matcher.nil?
18
10
 
19
11
  unless matcher.respond_to?(:matches?)
20
12
  raise InvalidMatcherError, "Expected a matcher, got #{matcher.inspect}."
21
13
  end
22
14
 
23
15
  match = matcher.matches?(actual, &block)
24
- ::Spec::Matchers.generated_description = "should #{describe_matcher(matcher)}"
16
+ ::Spec::Matchers.last_matcher = matcher
25
17
  Spec::Expectations.fail_with(matcher.failure_message) unless match
18
+ match
26
19
  end
27
20
  end
28
21
  end
29
22
 
30
23
  class NegativeExpectationMatcherHandler
31
24
  class << self
32
- include MatcherHandlerHelper
33
25
  def handle_matcher(actual, matcher, &block)
34
- if :use_operator_matcher == matcher
35
- return Spec::Matchers::NegativeOperatorMatcher.new(actual)
36
- end
26
+ ::Spec::Matchers.last_should = "should not"
27
+ return Spec::Matchers::NegativeOperatorMatcher.new(actual) if matcher.nil?
37
28
 
38
29
  unless matcher.respond_to?(:matches?)
39
30
  raise InvalidMatcherError, "Expected a matcher, got #{matcher.inspect}."
@@ -49,8 +40,9 @@ EOF
49
40
  )
50
41
  end
51
42
  match = matcher.matches?(actual, &block)
52
- ::Spec::Matchers.generated_description = "should not #{describe_matcher(matcher)}"
43
+ ::Spec::Matchers.last_matcher = matcher
53
44
  Spec::Expectations.fail_with(matcher.negative_failure_message) if match
45
+ match
54
46
  end
55
47
  end
56
48
  end
@@ -23,7 +23,7 @@ module Spec
23
23
  raise ArgumentError if args.empty?
24
24
  raise ArgumentError unless block
25
25
  args << {} unless Hash === args.last
26
- args.last[:spec_path] = caller(0)[1]
26
+ args.last[:spec_path] = File.expand_path(caller(0)[1])
27
27
  Spec::Example::ExampleGroupFactory.create_example_group(*args, &block)
28
28
  end
29
29
  alias :context :describe
@@ -80,23 +80,8 @@ module Spec
80
80
  raise NameError.new(e.message + "\nThe first argument to share_as must be a legal name for a constant\n")
81
81
  end
82
82
  end
83
-
84
- private
85
-
86
- def rspec_options
87
- $rspec_options ||= begin; \
88
- parser = ::Spec::Runner::OptionParser.new(STDERR, STDOUT); \
89
- parser.order!(ARGV); \
90
- $rspec_options = parser.options; \
91
- end
92
- $rspec_options
93
- end
94
-
95
- def init_rspec_options(options)
96
- $rspec_options = options if $rspec_options.nil?
97
- end
98
83
  end
99
84
  end
100
85
  end
101
86
 
102
- include Spec::Extensions::Main
87
+ include Spec::Extensions::Main
@@ -134,10 +134,16 @@ module Spec
134
134
  #
135
135
  module Matchers
136
136
  module ModuleMethods
137
- attr_accessor :generated_description
137
+ attr_accessor :last_matcher, :last_should
138
138
 
139
139
  def clear_generated_description
140
- self.generated_description = nil
140
+ self.last_matcher = nil
141
+ self.last_should = nil
142
+ end
143
+
144
+ def generated_description
145
+ last_should.nil? ? nil :
146
+ "#{last_should} #{last_matcher.respond_to?(:description) ? last_matcher.description : 'NO NAME'}"
141
147
  end
142
148
  end
143
149
 
@@ -23,9 +23,6 @@ module Spec
23
23
  rcov_error_report = "http://eigenclass.org/hiki.rb?rcov-0.8.0"
24
24
  end
25
25
 
26
- # This supports should_exist > target.exists? in the old world.
27
- # We should consider deprecating that ability as in the new world
28
- # you can't write "should exist" unless you have your own custom matcher.
29
26
  begin
30
27
  return @result = actual.__send__(present_tense_predicate, *@args)
31
28
  rescue
@@ -4,60 +4,60 @@ module Spec
4
4
  #Based on patch from Wilson Bilkovich
5
5
  class Change #:nodoc:
6
6
  def initialize(receiver=nil, message=nil, &block)
7
- @receiver = receiver
8
- @message = message
9
- @block = block
7
+ @message = message || "result"
8
+ @value_proc = block || lambda {
9
+ receiver.__send__(message)
10
+ }
10
11
  end
11
12
 
12
- def matches?(target, &block)
13
- if block
14
- raise MatcherError.new(<<-EOF
15
- block passed to should or should_not change must use {} instead of do/end
16
- EOF
17
- )
18
- end
19
- @target = target
20
- execute_change
21
- return false if @from && (@from != @before)
22
- return false if @to && (@to != @after)
13
+ def matches?(event_proc)
14
+ raise_block_syntax_error if block_given?
15
+
16
+ @before = evaluate_value_proc
17
+ event_proc.call
18
+ @after = evaluate_value_proc
19
+
20
+ return false if @from unless @from == @before
21
+ return false if @to unless @to == @after
23
22
  return (@before + @amount == @after) if @amount
24
23
  return ((@after - @before) >= @minimum) if @minimum
25
24
  return ((@after - @before) <= @maximum) if @maximum
26
25
  return @before != @after
27
26
  end
28
27
 
29
- def execute_change
30
- @before = @block.nil? ? @receiver.send(@message) : @block.call
31
- @target.call
32
- @after = @block.nil? ? @receiver.send(@message) : @block.call
28
+ def raise_block_syntax_error
29
+ raise MatcherError.new(<<-MESSAGE
30
+ block passed to should or should_not change must use {} instead of do/end
31
+ MESSAGE
32
+ )
33
+ end
34
+
35
+ def evaluate_value_proc
36
+ @value_proc.call
33
37
  end
34
38
 
35
39
  def failure_message
36
40
  if @to
37
- "#{result} should have been changed to #{@to.inspect}, but is now #{@after.inspect}"
41
+ "#{@message} should have been changed to #{@to.inspect}, but is now #{@after.inspect}"
38
42
  elsif @from
39
- "#{result} should have initially been #{@from.inspect}, but was #{@before.inspect}"
43
+ "#{@message} should have initially been #{@from.inspect}, but was #{@before.inspect}"
40
44
  elsif @amount
41
- "#{result} should have been changed by #{@amount.inspect}, but was changed by #{actual_delta.inspect}"
45
+ "#{@message} should have been changed by #{@amount.inspect}, but was changed by #{actual_delta.inspect}"
42
46
  elsif @minimum
43
- "#{result} should have been changed by at least #{@minimum.inspect}, but was changed by #{actual_delta.inspect}"
47
+ "#{@message} should have been changed by at least #{@minimum.inspect}, but was changed by #{actual_delta.inspect}"
44
48
  elsif @maximum
45
- "#{result} should have been changed by at most #{@maximum.inspect}, but was changed by #{actual_delta.inspect}"
49
+ "#{@message} should have been changed by at most #{@maximum.inspect}, but was changed by #{actual_delta.inspect}"
46
50
  else
47
- "#{result} should have changed, but is still #{@before.inspect}"
51
+ "#{@message} should have changed, but is still #{@before.inspect}"
48
52
  end
49
53
  end
50
54
 
51
- def result
52
- @message || "result"
53
- end
54
-
55
55
  def actual_delta
56
56
  @after - @before
57
57
  end
58
58
 
59
59
  def negative_failure_message
60
- "#{result} should not have changed, but did change from #{@before.inspect} to #{@after.inspect}"
60
+ "#{@message} should not have changed, but did change from #{@before.inspect} to #{@after.inspect}"
61
61
  end
62
62
 
63
63
  def by(amount)
@@ -125,20 +125,24 @@ EOF
125
125
  # employee.develop_great_new_social_networking_app
126
126
  # }.should change(employee, :title).from("Mail Clerk").to("CEO")
127
127
  #
128
- # Evaluates +receiver.message+ or +block+ before and
129
- # after it evaluates the c object (generated by the lambdas in the examples above).
128
+ # Evaluates <tt>receiver.message</tt> or <tt>block</tt> before and after
129
+ # it evaluates the c object (generated by the lambdas in the examples
130
+ # above).
131
+ #
132
+ # Then compares the values before and after the <tt>receiver.message</tt>
133
+ # and evaluates the difference compared to the expected difference.
130
134
  #
131
- # Then compares the values before and after the +receiver.message+ and
132
- # evaluates the difference compared to the expected difference.
135
+ # == WARNING
136
+ # <tt>should_not change</tt> only supports the form with no
137
+ # subsequent calls to <tt>by</tt>, <tt>by_at_least</tt>,
138
+ # <tt>by_at_most</tt>, <tt>to</tt> or <tt>from</tt>.
133
139
  #
134
- # == Warning
135
- # +should_not+ +change+ only supports the form with no subsequent calls to
136
- # +by+, +by_at_least+, +by_at_most+, +to+ or +from+.
140
+ # blocks passed to <tt>should</tt> <tt>change</tt> and <tt>should_not</tt>
141
+ # <tt>change</tt> must use the <tt>{}</tt> form (<tt>do/end</tt> is not
142
+ # supported).
137
143
  #
138
- # blocks passed to +should+ +change+ and +should_not+ +change+
139
- # must use the <tt>{}</tt> form (<tt>do/end</tt> is not supported)
140
- def change(target=nil, message=nil, &block)
141
- Matchers::Change.new(target, message, &block)
144
+ def change(receiver=nil, message=nil, &block)
145
+ Matchers::Change.new(receiver, message, &block)
142
146
  end
143
147
  end
144
148
  end
@@ -8,7 +8,7 @@ module Spec
8
8
  end
9
9
 
10
10
  def matches?(target)
11
- target.send(predicate, *@args)
11
+ target.__send__(predicate, *@args)
12
12
  end
13
13
 
14
14
  def failure_message
@@ -1,6 +1,5 @@
1
1
  module Spec
2
2
  module Matchers
3
-
4
3
  class Have #:nodoc:
5
4
  def initialize(expected, relativity=:exactly)
6
5
  @expected = (expected == :no ? 0 : expected)
@@ -15,23 +14,15 @@ module Spec
15
14
  }
16
15
  end
17
16
 
18
- def method_missing(sym, *args, &block)
19
- @collection_name = sym
20
- @plural_collection_name = Inflector.pluralize(sym.to_s) if Object.const_defined?(:Inflector)
21
- @args = args
22
- @block = block
23
- self
24
- end
25
-
26
17
  def matches?(collection_owner)
27
18
  if collection_owner.respond_to?(@collection_name)
28
- collection = collection_owner.send(@collection_name, *@args, &@block)
19
+ collection = collection_owner.__send__(@collection_name, *@args, &@block)
29
20
  elsif (@plural_collection_name && collection_owner.respond_to?(@plural_collection_name))
30
- collection = collection_owner.send(@plural_collection_name, *@args, &@block)
21
+ collection = collection_owner.__send__(@plural_collection_name, *@args, &@block)
31
22
  elsif (collection_owner.respond_to?(:length) || collection_owner.respond_to?(:size))
32
23
  collection = collection_owner
33
24
  else
34
- collection_owner.send(@collection_name, *@args, &@block)
25
+ collection_owner.__send__(@collection_name, *@args, &@block)
35
26
  end
36
27
  @actual = collection.size if collection.respond_to?(:size)
37
28
  @actual = collection.length if collection.respond_to?(:length)
@@ -75,8 +66,22 @@ EOF
75
66
  "have #{relative_expectation} #{@collection_name}"
76
67
  end
77
68
 
69
+ def respond_to?(sym)
70
+ @expected.respond_to?(sym) || super
71
+ end
72
+
78
73
  private
79
74
 
75
+ def method_missing(sym, *args, &block)
76
+ @collection_name = sym
77
+ if inflector = (defined?(ActiveSupport::Inflector) ? ActiveSupport::Inflector : (defined?(Inflector) ? Inflector : nil))
78
+ @plural_collection_name = inflector.pluralize(sym.to_s)
79
+ end
80
+ @args = args
81
+ @block = block
82
+ self
83
+ end
84
+
80
85
  def relative_expectation
81
86
  "#{relativities[@relativity]}#{@expected}"
82
87
  end
@@ -45,14 +45,19 @@ module Spec
45
45
  def fail_with_message(message)
46
46
  Spec::Expectations.fail_with(message, @expected, @target)
47
47
  end
48
+
49
+ def description
50
+ "#{@operator} #{@expected.inspect}"
51
+ end
48
52
 
49
53
  end
50
54
 
51
55
  class PositiveOperatorMatcher < BaseOperatorMatcher #:nodoc:
52
56
 
53
57
  def __delegate_method_missing_to_target(operator, expected)
54
- ::Spec::Matchers.generated_description = "should #{operator} #{expected.inspect}"
55
- return if @target.send(operator, expected)
58
+ @operator = operator
59
+ ::Spec::Matchers.last_matcher = self
60
+ return true if @target.__send__(operator, expected)
56
61
  return fail_with_message("expected: #{expected.inspect},\n got: #{@target.inspect} (using #{operator})") if ['==','===', '=~'].include?(operator)
57
62
  return fail_with_message("expected: #{operator} #{expected.inspect},\n got: #{operator.gsub(/./, ' ')} #{@target.inspect}")
58
63
  end
@@ -62,8 +67,9 @@ module Spec
62
67
  class NegativeOperatorMatcher < BaseOperatorMatcher #:nodoc:
63
68
 
64
69
  def __delegate_method_missing_to_target(operator, expected)
65
- ::Spec::Matchers.generated_description = "should not #{operator} #{expected.inspect}"
66
- return unless @target.send(operator, expected)
70
+ @operator = operator
71
+ ::Spec::Matchers.last_matcher = self
72
+ return true unless @target.__send__(operator, expected)
67
73
  return fail_with_message("expected not: #{operator} #{expected.inspect},\n got: #{operator.gsub(/./, ' ')} #{@target.inspect}")
68
74
  end
69
75
 
@@ -1,7 +1,7 @@
1
1
  module Spec
2
2
  module Matchers
3
3
  class SimpleMatcher
4
- attr_reader :description
4
+ attr_writer :failure_message, :negative_failure_message, :description
5
5
 
6
6
  def initialize(description, &match_block)
7
7
  @description = description
@@ -10,20 +10,123 @@ module Spec
10
10
 
11
11
  def matches?(actual)
12
12
  @actual = actual
13
- return @match_block.call(@actual)
13
+ case @match_block.arity
14
+ when 2
15
+ @match_block.call(@actual, self)
16
+ else
17
+ @match_block.call(@actual)
18
+ end
19
+ end
20
+
21
+ def description
22
+ @description || explanation
14
23
  end
15
24
 
16
- def failure_message()
17
- return %[expected #{@description.inspect} but got #{@actual.inspect}]
25
+ def failure_message
26
+ @failure_message || (@description.nil? ? explanation : %[expected #{@description.inspect} but got #{@actual.inspect}])
18
27
  end
19
-
20
- def negative_failure_message()
21
- return %[expected not to get #{@description.inspect}, but got #{@actual.inspect}]
28
+
29
+ def negative_failure_message
30
+ @negative_failure_message || (@description.nil? ? explanation : %[expected not to get #{@description.inspect}, but got #{@actual.inspect}])
31
+ end
32
+
33
+ def explanation
34
+ "No description provided. See RDoc for simple_matcher()"
22
35
  end
23
36
  end
24
-
25
- def simple_matcher(message, &match_block)
26
- SimpleMatcher.new(message, &match_block)
37
+
38
+ # simple_matcher makes it easy for you to create your own custom matchers
39
+ # in just a few lines of code when you don't need all the power of a
40
+ # completely custom matcher object.
41
+ #
42
+ # The <tt>description</tt> argument will appear as part of any failure
43
+ # message, and is also the source for auto-generated descriptions.
44
+ #
45
+ # The <tt>match_block</tt> can have an arity of 1 or 2. The first block
46
+ # argument will be the given value. The second, if the block accepts it
47
+ # will be the matcher itself, giving you access to set custom failure
48
+ # messages in favor of the defaults.
49
+ #
50
+ # The <tt>match_block</tt> should return a boolean: <tt>true</tt>
51
+ # indicates a match, which will pass if you use <tt>should</tt> and fail
52
+ # if you use <tt>should_not</tt>. false (or nil) indicates no match,
53
+ # which will do the reverse: fail if you use <tt>should</tt> and pass if
54
+ # you use <tt>should_not</tt>.
55
+ #
56
+ # An error in the <tt>match_block</tt> will bubble up, resulting in a
57
+ # failure.
58
+ #
59
+ # == Example with default messages
60
+ #
61
+ # def be_even
62
+ # simple_matcher("an even number") { |given| given % 2 == 0 }
63
+ # end
64
+ #
65
+ # describe 2 do
66
+ # it "should be even" do
67
+ # 2.should be_even
68
+ # end
69
+ # end
70
+ #
71
+ # Given an odd number, this example would produce an error message stating:
72
+ # expected "an even number", got 3.
73
+ #
74
+ # Unfortunately, if you're a fan of auto-generated descriptions, this will
75
+ # produce "should an even number." Not the most desirable result. You can
76
+ # control that using custom messages:
77
+ #
78
+ # == Example with custom messages
79
+ #
80
+ # def rhyme_with(expected)
81
+ # simple_matcher("rhyme with #{expected.inspect}") do |given, matcher|
82
+ # matcher.failure_message = "expected #{given.inspect} to rhyme with #{expected.inspect}"
83
+ # matcher.negative_failure_message = "expected #{given.inspect} not to rhyme with #{expected.inspect}"
84
+ # actual.rhymes_with? expected
85
+ # end
86
+ # end
87
+ #
88
+ # # OR
89
+ #
90
+ # def rhyme_with(expected)
91
+ # simple_matcher do |given, matcher|
92
+ # matcher.description = "rhyme with #{expected.inspect}"
93
+ # matcher.failure_message = "expected #{given.inspect} to rhyme with #{expected.inspect}"
94
+ # matcher.negative_failure_message = "expected #{given.inspect} not to rhyme with #{expected.inspect}"
95
+ # actual.rhymes_with? expected
96
+ # end
97
+ # end
98
+ #
99
+ # describe "pecan" do
100
+ # it "should rhyme with 'be gone'" do
101
+ # nut = "pecan"
102
+ # nut.extend Rhymer
103
+ # nut.should rhyme_with("be gone")
104
+ # end
105
+ # end
106
+ #
107
+ # The resulting messages would be:
108
+ # description: rhyme with "be gone"
109
+ # failure_message: expected "pecan" to rhyme with "be gone"
110
+ # negative failure_message: expected "pecan" not to rhyme with "be gone"
111
+ #
112
+ # == Wrapped Expectations
113
+ #
114
+ # Because errors will bubble up, it is possible to wrap other expectations
115
+ # in a SimpleMatcher.
116
+ #
117
+ # def be_even
118
+ # simple_matcher("an even number") { |given| (given % 2).should == 0 }
119
+ # end
120
+ #
121
+ # BE VERY CAREFUL when you do this. Only use wrapped expectations for
122
+ # matchers that will always be used in only the positive
123
+ # (<tt>should</tt>) or negative (<tt>should_not</tt>), but not both.
124
+ # The reason is that is you wrap a <tt>should</tt> and call the wrapper
125
+ # with <tt>should_not</tt>, the correct result (the <tt>should</tt>
126
+ # failing), will fail when you want it to pass.
127
+ #
128
+ def simple_matcher(description=nil, &match_block)
129
+ SimpleMatcher.new(description, &match_block)
27
130
  end
28
131
  end
29
132
  end