rspec 1.1.4 → 1.1.5

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.
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