dchelimsky-rspec 1.1.11.1 → 1.1.11.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.
Files changed (99) hide show
  1. data/History.txt +11 -3
  2. data/Manifest.txt +35 -46
  3. data/README.txt +30 -12
  4. data/Rakefile +9 -9
  5. data/{stories/configuration/before_blocks.story → features/configuration/before_blocks.feature} +2 -2
  6. data/{stories/example_groups/autogenerated_docstrings → features/example_groups/autogenerated_docstrings.feature} +1 -1
  7. data/{stories/example_groups/example_group_with_should_methods → features/example_groups/example_group_with_should_methods.feature} +3 -3
  8. data/{stories/example_groups/nested_groups → features/example_groups/nested_groups.feature} +1 -1
  9. data/{stories/example_groups/output → features/example_groups/output.feature} +3 -8
  10. data/{stories/interop/examples_and_tests_together → features/interop/examples_and_tests_together.feature} +5 -4
  11. data/{stories/interop/test_but_not_test_unit → features/interop/test_but_not_test_unit.feature} +2 -2
  12. data/{stories/interop/test_case_with_should_methods → features/interop/test_case_with_should_methods.feature} +2 -2
  13. data/{stories/mock_framework_integration/use_flexmock.story → features/mock_framework_integration/use_flexmock.feature} +1 -1
  14. data/features/step_definitions/running_rspec.rb +48 -0
  15. data/features/support/env.rb +25 -0
  16. data/{stories/resources → features/support}/helpers/cmdline.rb +0 -0
  17. data/{stories/resources → features/support}/helpers/story_helper.rb +0 -0
  18. data/{stories/resources → features/support}/matchers/smart_match.rb +0 -0
  19. data/{plugins → lib/adapters}/mock_frameworks/flexmock.rb +0 -0
  20. data/{plugins → lib/adapters}/mock_frameworks/mocha.rb +0 -0
  21. data/{plugins → lib/adapters}/mock_frameworks/rr.rb +0 -0
  22. data/{plugins → lib/adapters}/mock_frameworks/rspec.rb +1 -1
  23. data/lib/autotest/rspec.rb +3 -2
  24. data/lib/spec/dsl/main.rb +8 -4
  25. data/lib/spec/example/configuration.rb +1 -1
  26. data/lib/spec/example/example_group_factory.rb +6 -2
  27. data/lib/spec/example/example_group_methods.rb +47 -54
  28. data/lib/spec/example/shared_example_group.rb +2 -2
  29. data/lib/spec/example.rb +163 -14
  30. data/lib/spec/interop/test/unit/testresult.rb +1 -1
  31. data/lib/spec/interop/test/unit/testsuite_adapter.rb +1 -1
  32. data/lib/spec/matchers/match_array.rb +75 -0
  33. data/lib/spec/matchers/operator_matcher.rb +34 -7
  34. data/lib/spec/matchers.rb +2 -1
  35. data/lib/spec/mocks/argument_constraints.rb +43 -5
  36. data/lib/spec/runner/example_group_runner.rb +2 -2
  37. data/lib/spec/runner/option_parser.rb +12 -6
  38. data/lib/spec/runner/options.rb +9 -9
  39. data/lib/spec/runner/spec_parser.rb +3 -2
  40. data/lib/spec/runner.rb +54 -188
  41. data/lib/spec/version.rb +1 -1
  42. data/lib/spec.rb +0 -29
  43. data/{rake_tasks → resources/rake}/examples.rake +0 -0
  44. data/{rake_tasks → resources/rake}/examples_with_rcov.rake +0 -0
  45. data/{rake_tasks → resources/rake}/failing_examples_with_html.rake +0 -0
  46. data/{rake_tasks → resources/rake}/verify_rcov.rake +0 -0
  47. data/{stories/resources → resources}/spec/before_blocks_example.rb +1 -1
  48. data/{stories/resources → resources}/spec/example_group_with_should_methods.rb +1 -1
  49. data/{stories/resources → resources}/spec/simple_spec.rb +1 -1
  50. data/resources/spec/spec_with_flexmock.rb +19 -0
  51. data/{stories/resources → resources}/test/spec_and_test_together.rb +1 -1
  52. data/{stories/resources → resources}/test/spec_including_test_but_not_unit.rb +1 -1
  53. data/{stories/resources → resources}/test/test_case_with_should_methods.rb +2 -2
  54. data/rspec.gemspec +5 -4
  55. data/spec/autotest/rspec_spec.rb +2 -1
  56. data/spec/spec/dsl/main_spec.rb +8 -5
  57. data/spec/spec/example/configuration_spec.rb +9 -9
  58. data/spec/spec/example/example_group_factory_spec.rb +22 -3
  59. data/spec/spec/example/example_group_methods_spec.rb +19 -15
  60. data/spec/spec/example/example_group_spec.rb +41 -41
  61. data/spec/spec/example/example_methods_spec.rb +5 -5
  62. data/spec/spec/example/helper_method_spec.rb +24 -0
  63. data/spec/spec/example/pending_module_spec.rb +2 -8
  64. data/spec/spec/example/shared_example_group_spec.rb +5 -5
  65. data/spec/spec/matchers/description_generation_spec.rb +5 -0
  66. data/spec/spec/matchers/handler_spec.rb +7 -7
  67. data/spec/spec/matchers/match_array_spec.rb +83 -0
  68. data/spec/spec/matchers/raise_error_spec.rb +18 -0
  69. data/spec/spec/mocks/hash_including_matcher_spec.rb +39 -2
  70. data/spec/spec/mocks/hash_not_including_matcher_spec.rb +67 -0
  71. data/spec/spec/mocks/mock_spec.rb +5 -6
  72. data/spec/spec/mocks/nil_expectation_warning_spec.rb +1 -1
  73. data/spec/spec/runner/formatter/base_text_formatter_spec.rb +22 -0
  74. data/spec/spec/runner/option_parser_spec.rb +4 -17
  75. data/spec/spec/runner/options_spec.rb +19 -5
  76. data/spec/spec/runner/resources/custom_example_group_runner.rb +14 -0
  77. data/spec/spec/runner/spec_parser_spec.rb +10 -0
  78. data/spec/spec/spec_spec.rb +21 -0
  79. metadata +39 -49
  80. data/lib/spec/adapters/ruby_engine/mri.rb +0 -8
  81. data/lib/spec/adapters/ruby_engine/rubinius.rb +0 -8
  82. data/lib/spec/adapters/ruby_engine.rb +0 -26
  83. data/lib/spec/adapters.rb +0 -1
  84. data/lib/spec/extensions/class.rb +0 -24
  85. data/lib/spec/extensions.rb +0 -1
  86. data/spec/spec/adapters/ruby_engine_spec.rb +0 -16
  87. data/stories/all.rb +0 -5
  88. data/stories/configuration/stories.rb +0 -7
  89. data/stories/example_groups/stories.rb +0 -7
  90. data/stories/helper.rb +0 -6
  91. data/stories/interop/stories.rb +0 -7
  92. data/stories/mock_framework_integration/stories.rb +0 -7
  93. data/stories/pending_stories/README +0 -3
  94. data/stories/resources/spec/spec_with_flexmock.rb +0 -18
  95. data/stories/resources/steps/running_rspec.rb +0 -50
  96. data/stories/resources/stories/failing_story.rb +0 -15
  97. data/stories/stories/multiline_steps.story +0 -23
  98. data/stories/stories/steps/multiline_steps.rb +0 -13
  99. data/stories/stories/stories.rb +0 -6
@@ -13,13 +13,13 @@ module Spec
13
13
  end
14
14
 
15
15
  def self.description_text(*args)
16
- args.inject("") do |result, arg|
17
- result << " " unless (result == "" || arg.to_s =~ /^(\s|\.|#)/)
18
- result << arg.to_s
16
+ args.inject("") do |description, arg|
17
+ description << " " unless (description == "" || arg.to_s =~ /^(\s|\.|#)/)
18
+ description << arg.to_s
19
19
  end
20
20
  end
21
21
 
22
- attr_reader :description_text, :description_options, :spec_path
22
+ attr_reader :description_options, :spec_path
23
23
  alias :options :description_options
24
24
 
25
25
  # Provides the backtrace up to where this example_group was declared.
@@ -42,7 +42,7 @@ WARNING
42
42
 
43
43
  def inherited(klass)
44
44
  super
45
- klass.register
45
+ Spec::Runner.options.add_example_group klass
46
46
  Spec::Runner.register_at_exit_hook
47
47
  end
48
48
 
@@ -61,11 +61,9 @@ WARNING
61
61
  # end
62
62
  #
63
63
  def describe(*args, &example_group_block)
64
- args << {} unless Hash === args.last
65
64
  if example_group_block
65
+ Spec::Example::add_spec_path_to(args)
66
66
  options = args.last
67
- # Ruby 1.9 - the next line uses example_group_block.binding instead of example_group_block
68
- options[:spec_path] = eval("caller(0)[1]", example_group_block.binding) unless options[:spec_path]
69
67
  if options[:shared]
70
68
  create_shared_example_group(*args, &example_group_block)
71
69
  else
@@ -82,12 +80,28 @@ WARNING
82
80
  end
83
81
 
84
82
  def create_subclass(*args, &example_group_block) # :nodoc:
85
- self.subclass("Subclass") do
83
+ subclass("Subclass") do
86
84
  set_description(*args)
87
85
  module_eval(&example_group_block)
88
86
  end
89
87
  end
90
88
 
89
+ # Creates a new subclass of self, with a name "under" our own name.
90
+ # Example:
91
+ #
92
+ # x = Foo::Bar.subclass('Zap'){}
93
+ # x.name # => Foo::Bar::Zap_1
94
+ # x.superclass.name # => Foo::Bar
95
+ def subclass(base_name, &body) # :nodoc:
96
+ @class_count ||= 0
97
+ @class_count += 1
98
+ klass = Class.new(self)
99
+ class_name = "#{base_name}_#{@class_count}"
100
+ const_set(class_name, klass)
101
+ klass.instance_eval(&body)
102
+ klass
103
+ end
104
+
91
105
  # Use this to pull in examples from shared example groups.
92
106
  # See Spec::Runner for information about shared example groups.
93
107
  def it_should_behave_like(*shared_example_groups)
@@ -147,18 +161,18 @@ WARNING
147
161
  alias_method :xit, :xexample
148
162
  alias_method :xspecify, :xexample
149
163
 
150
- def run
151
- examples = examples_to_run
152
- reporter.add_example_group(self) unless examples_to_run.empty?
164
+ def run(run_options)
165
+ examples = examples_to_run(run_options)
166
+ run_options.reporter.add_example_group(self) unless examples.empty?
153
167
  return true if examples.empty?
154
- return dry_run(examples) if dry_run?
168
+ return dry_run(examples, run_options) if run_options.dry_run?
155
169
 
156
170
  plugin_mock_framework
157
171
  define_methods_from_predicate_matchers
158
172
 
159
- success, before_all_instance_variables = run_before_all
160
- success, after_all_instance_variables = execute_examples(success, before_all_instance_variables, examples)
161
- success = run_after_all(success, after_all_instance_variables)
173
+ success, before_all_instance_variables = run_before_all(run_options)
174
+ success, after_all_instance_variables = execute_examples(success, before_all_instance_variables, examples, run_options)
175
+ success = run_after_all(success, after_all_instance_variables, run_options)
162
176
  end
163
177
 
164
178
  def description
@@ -208,10 +222,10 @@ WARNING
208
222
  self
209
223
  end
210
224
 
211
- def examples #:nodoc:
225
+ def examples(run_options=nil) #:nodoc:
212
226
  examples = example_objects.dup
213
227
  add_method_examples(examples)
214
- Spec::Runner.options.reverse ? examples.reverse : examples
228
+ (run_options && run_options.reverse) ? examples.reverse : examples
215
229
  end
216
230
 
217
231
  def number_of_examples #:nodoc:
@@ -226,14 +240,6 @@ WARNING
226
240
  @after_each_parts = nil
227
241
  end
228
242
 
229
- def register
230
- Spec::Runner.options.add_example_group self
231
- end
232
-
233
- def unregister #:nodoc:
234
- Spec::Runner.options.remove_example_group self
235
- end
236
-
237
243
  def run_before_each(example)
238
244
  each_ancestor_example_group_class do |example_group_class|
239
245
  example.eval_each_fail_fast(example_group_class.before_each_parts)
@@ -247,15 +253,14 @@ WARNING
247
253
  end
248
254
 
249
255
  private
250
- def dry_run(examples)
256
+ def dry_run(examples, run_options)
251
257
  examples.each do |example|
252
- Spec::Runner.options.reporter.example_started(example)
253
- Spec::Runner.options.reporter.example_finished(example)
258
+ run_options.reporter.example_started(example)
259
+ run_options.reporter.example_finished(example)
254
260
  end
255
- return true
256
261
  end
257
262
 
258
- def run_before_all
263
+ def run_before_all(run_options)
259
264
  before_all = new("before(:all)")
260
265
  begin
261
266
  each_ancestor_example_group_class do |example_group_class|
@@ -263,23 +268,23 @@ WARNING
263
268
  end
264
269
  return [true, before_all.instance_variable_hash]
265
270
  rescue Exception => e
266
- reporter.failure(before_all, e)
271
+ run_options.reporter.failure(before_all, e)
267
272
  return [false, before_all.instance_variable_hash]
268
273
  end
269
274
  end
270
275
 
271
- def execute_examples(success, instance_variables, examples)
276
+ def execute_examples(success, instance_variables, examples, run_options)
272
277
  return [success, instance_variables] unless success
273
278
 
274
279
  after_all_instance_variables = instance_variables
275
280
  examples.each do |example_group_instance|
276
- success &= example_group_instance.execute(Spec::Runner.options, instance_variables)
281
+ success &= example_group_instance.execute(run_options, instance_variables)
277
282
  after_all_instance_variables = example_group_instance.instance_variable_hash
278
283
  end
279
284
  return [success, after_all_instance_variables]
280
285
  end
281
286
 
282
- def run_after_all(success, instance_variables)
287
+ def run_after_all(success, instance_variables, run_options)
283
288
  after_all = new("after(:all)")
284
289
  after_all.set_instance_variables_from_hash(instance_variables)
285
290
  each_ancestor_example_group_class(:superclass_first) do |example_group_class|
@@ -287,34 +292,22 @@ WARNING
287
292
  end
288
293
  return success
289
294
  rescue Exception => e
290
- reporter.failure(after_all, e)
295
+ run_options.reporter.failure(after_all, e)
291
296
  return false
292
297
  end
293
298
 
294
- def examples_to_run
295
- all_examples = examples
296
- return all_examples unless specified_examples?
299
+ def examples_to_run(run_options)
300
+ all_examples = examples(run_options)
301
+ return all_examples unless specified_examples?(run_options)
297
302
  all_examples.reject do |example|
298
303
  matcher = ExampleGroupMethods.matcher_class.
299
304
  new(description.to_s, example.description)
300
- !matcher.matches?(specified_examples)
305
+ !matcher.matches?(run_options.examples)
301
306
  end
302
307
  end
303
308
 
304
- def specified_examples?
305
- specified_examples && !specified_examples.empty?
306
- end
307
-
308
- def specified_examples
309
- Spec::Runner.options.examples
310
- end
311
-
312
- def reporter
313
- Spec::Runner.options.reporter
314
- end
315
-
316
- def dry_run?
317
- Spec::Runner.options.dry_run
309
+ def specified_examples?(run_options)
310
+ run_options.examples && !run_options.examples.empty?
318
311
  end
319
312
 
320
313
  def example_objects
@@ -34,11 +34,11 @@ module Spec
34
34
  existing_example_group = find(new_example_group.description)
35
35
  return false unless existing_example_group
36
36
  return true if new_example_group.equal?(existing_example_group)
37
- return true if spec_path(new_example_group) == spec_path(existing_example_group)
37
+ return true if expanded_path(new_example_group) == expanded_path(existing_example_group)
38
38
  raise ArgumentError.new("Shared Example '#{existing_example_group.description}' already exists")
39
39
  end
40
40
 
41
- def spec_path(example_group)
41
+ def expanded_path(example_group)
42
42
  File.expand_path(example_group.spec_path)
43
43
  end
44
44
  end
data/lib/spec/example.rb CHANGED
@@ -1,24 +1,173 @@
1
1
  module Spec
2
+ # == Example Groups and Code Examples
3
+ #
4
+ # A Code Example is an executable example of how a bit of code is expected
5
+ # to behave.
6
+ #
7
+ # An Example Group is a group of code examples.
8
+ #
9
+ # RSpec exposes a DSL to describe groups of examples.
10
+ #
11
+ # describe Account do
12
+ # it "should have a balance of $0" do
13
+ # account = Account.new
14
+ # account.balance.should == Money.new(0, :dollars)
15
+ # end
16
+ # end
17
+ #
18
+ # == Before and After
19
+ #
20
+ # You can use the <tt>before()</tt> and <tt>after()</tt> methods to extract
21
+ # common code within an Example Group. Both methods take an optional scope
22
+ # argument so you can run the block before :each example or before :all
23
+ # examples
24
+ #
25
+ # describe "..." do
26
+ # before :all do
27
+ # ...
28
+ # end
29
+ #
30
+ # before :each do
31
+ # ...
32
+ # end
33
+ #
34
+ # it "should do something" do
35
+ # ...
36
+ # end
37
+ #
38
+ # it "should do something else" do
39
+ # ...
40
+ # end
41
+ #
42
+ # after :each do
43
+ # ...
44
+ # end
45
+ #
46
+ # after :all do
47
+ # ...
48
+ # end
49
+ #
50
+ # end
51
+ #
52
+ # The <tt>before :each</tt> block will run before each of the examples, once
53
+ # for each example. Likewise, the <tt>after :each</tt> block will run after
54
+ # each of the examples.
55
+ #
56
+ # It is also possible to specify a <tt>before :all</tt> and <tt>after
57
+ # :all</tt> block that will run only once for each example group, before the
58
+ # first <code>before :each</code> and after the last <code>after
59
+ # :each</code> respectively. The use of these is generally discouraged,
60
+ # because it introduces dependencies between the examples. Still, it might
61
+ # prove useful for very expensive operations if you know what you are doing.
62
+ #
63
+ # == Local helper methods
64
+ #
65
+ # You can include local helper methods by simply expressing them within an
66
+ # example group:
67
+ #
68
+ # describe "..." do
69
+ #
70
+ # it "..." do
71
+ # helper_method
72
+ # end
73
+ #
74
+ # def helper_method
75
+ # ...
76
+ # end
77
+ #
78
+ # end
79
+ #
80
+ # == Included helper methods
81
+ #
82
+ # You can include helper methods in multiple example groups by expressing
83
+ # them within a module, and then including that module in your example
84
+ # groups:
85
+ #
86
+ # module AccountExampleHelperMethods
87
+ # def helper_method
88
+ # ...
89
+ # end
90
+ # end
91
+ #
92
+ # describe "A new account" do
93
+ # include AccountExampleHelperMethods
94
+ # before do
95
+ # @account = Account.new
96
+ # end
97
+ #
98
+ # it "should have a balance of $0" do
99
+ # helper_method
100
+ # @account.balance.should eql(Money.new(0, :dollars))
101
+ # end
102
+ # end
103
+ #
104
+ # == Shared Example Groups
105
+ #
106
+ # You can define a shared example group, that may be used on other groups
107
+ #
108
+ # share_examples_for "All Editions" do
109
+ # it "all editions behaviour" ...
110
+ # end
111
+ #
112
+ # describe SmallEdition do
113
+ # it_should_behave_like "All Editions"
114
+ #
115
+ # it "should do small edition stuff" do
116
+ # ...
117
+ # end
118
+ # end
119
+ #
120
+ # You can also assign the shared group to a module and include that
121
+ #
122
+ # share_as :AllEditions do
123
+ # it "should do all editions stuff" ...
124
+ # end
125
+ #
126
+ # describe SmallEdition do
127
+ # it_should_behave_like AllEditions
128
+ #
129
+ # it "should do small edition stuff" do
130
+ # ...
131
+ # end
132
+ # end
133
+ #
134
+ # And, for those of you who prefer to use something more like Ruby, you can
135
+ # just include the module directly
136
+ #
137
+ # describe SmallEdition do
138
+ # include AllEditions
139
+ #
140
+ # it "should do small edition stuff" do
141
+ # ...
142
+ # end
143
+ # end
2
144
  module Example
3
- def self.args_and_options(*args)
4
- with_options_from(args) do |options|
5
- return args, options
145
+ class << self
146
+ def args_and_options(*args) # :nodoc:
147
+ with_options_from(args) do |options|
148
+ return args, options
149
+ end
6
150
  end
7
- end
8
151
 
9
- def self.scope_from(*args)
10
- args[0] || :each
11
- end
152
+ def scope_from(*args) # :nodoc:
153
+ args[0] || :each
154
+ end
12
155
 
13
- def self.scope_and_options(*args)
14
- args, options = args_and_options(*args)
15
- return scope_from(*args), options
16
- end
156
+ def scope_and_options(*args) # :nodoc:
157
+ args, options = args_and_options(*args)
158
+ return scope_from(*args), options
159
+ end
160
+
161
+ def add_spec_path_to(args) # :nodoc:
162
+ args << {} unless Hash === args.last
163
+ args.last[:spec_path] ||= caller(0)[2]
164
+ end
17
165
 
18
- private
166
+ private
19
167
 
20
- def self.with_options_from(args)
21
- yield Hash === args.last ? args.pop : {} if block_given?
168
+ def with_options_from(args)
169
+ yield Hash === args.last ? args.pop : {} if block_given?
170
+ end
22
171
  end
23
172
  end
24
173
  end
@@ -1,6 +1,6 @@
1
1
  class Test::Unit::TestResult
2
2
  alias_method :tu_passed?, :passed?
3
3
  def passed?
4
- return tu_passed? & ::Spec.run
4
+ return tu_passed? & ::Spec::Runner.run
5
5
  end
6
6
  end
@@ -14,7 +14,7 @@ module Test
14
14
 
15
15
  def run(*args)
16
16
  return true unless args.empty?
17
- example_group.run
17
+ example_group.run(Spec::Runner.options)
18
18
  end
19
19
 
20
20
  def size
@@ -0,0 +1,75 @@
1
+ module Spec
2
+ module Matchers
3
+
4
+ class MatchArray #:nodoc:
5
+
6
+ def initialize(expected)
7
+ @expected = expected
8
+ end
9
+
10
+ def matches?(actual)
11
+ @actual = actual
12
+ @extra_items = difference_between_arrays(@actual, @expected)
13
+ @missing_items = difference_between_arrays(@expected, @actual)
14
+ @extra_items.empty? && @missing_items.empty?
15
+ end
16
+
17
+ def failure_message
18
+ message = "expected collection contained: #{@expected.sort.inspect}\n"
19
+ message += "actual collection contained: #{@actual.sort.inspect}\n"
20
+ message += "the missing elements were: #{@missing_items.sort.inspect}\n" unless @missing_items.empty?
21
+ message += "the extra elements were: #{@extra_items.sort.inspect}\n" unless @extra_items.empty?
22
+ message
23
+ end
24
+
25
+ def description
26
+ "contain exactly #{_pretty_print(@expected)}"
27
+ end
28
+
29
+ private
30
+
31
+ def difference_between_arrays(array_1, array_2)
32
+ difference = array_1.dup
33
+ array_2.each do |element|
34
+ if index = difference.index(element)
35
+ difference.delete_at(index)
36
+ end
37
+ end
38
+ difference
39
+ end
40
+
41
+ def _pretty_print(array)
42
+ result = ""
43
+ array.each_with_index do |item, index|
44
+ if index < (array.length - 2)
45
+ result << "#{item.inspect}, "
46
+ elsif index < (array.length - 1)
47
+ result << "#{item.inspect} and "
48
+ else
49
+ result << "#{item.inspect}"
50
+ end
51
+ end
52
+ result
53
+ end
54
+
55
+ end
56
+
57
+ # :call-seq:
58
+ # should =~ expected
59
+ #
60
+ # Passes if actual contains all of the expected regardless of order.
61
+ # This works for collections. Pass in multiple args and it will only
62
+ # pass if all args are found in collection.
63
+ #
64
+ # NOTE: there is no should_not version of array.should =~ other_array
65
+ #
66
+ # == Examples
67
+ #
68
+ # [1,2,3].should =~ [1,2,3] # => would pass
69
+ # [1,2,3].should =~ [2,3,1] # => would pass
70
+ # [1,2,3,4].should =~ [1,2,3] # => would fail
71
+ # [1,2,2,3].should =~ [1,2,3] # => would fail
72
+ # [1,2,3].should =~ [1,2,3,4] # => would fail
73
+ OperatorMatcher.register(Array, '=~', Spec::Matchers::MatchArray)
74
+ end
75
+ end
@@ -1,22 +1,43 @@
1
1
  module Spec
2
2
  module Matchers
3
+
3
4
  class OperatorMatcher
5
+ @operator_registry = {}
6
+
7
+ def self.register(klass, operator, matcher)
8
+ @operator_registry[klass] ||= {}
9
+ @operator_registry[klass][operator] = matcher
10
+ end
11
+
12
+ def self.get(klass, operator)
13
+ return @operator_registry[klass][operator] if @operator_registry[klass]
14
+ nil
15
+ end
16
+
4
17
  def initialize(actual)
5
18
  @actual = actual
6
19
  end
7
-
8
- ['==','===','<','<=','>=','>','=~'].each do |operator|
9
- define_method operator do |expected|
10
- ::Spec::Matchers.last_matcher = self
11
- @operator, @expected = operator, expected
12
- __delegate_operator(@actual, operator, expected)
20
+
21
+ def self.use_custom_matcher_or_delegate(operator)
22
+ define_method(operator) do |expected|
23
+ if matcher = OperatorMatcher.get(@actual.class, operator)
24
+ return @actual.send(matcher_method, matcher.new(expected))
25
+ else
26
+ ::Spec::Matchers.last_matcher = self
27
+ @operator, @expected = operator, expected
28
+ __delegate_operator(@actual, operator, expected)
29
+ end
13
30
  end
14
31
  end
15
32
 
33
+ ['==', '===', '=~', '>', '>=', '<', '<='].each do |operator|
34
+ use_custom_matcher_or_delegate operator
35
+ end
36
+
16
37
  def fail_with_message(message)
17
38
  Spec::Expectations.fail_with(message, @expected, @actual)
18
39
  end
19
-
40
+
20
41
  def description
21
42
  "#{@operator} #{@expected.inspect}"
22
43
  end
@@ -24,6 +45,9 @@ module Spec
24
45
  end
25
46
 
26
47
  class PositiveOperatorMatcher < OperatorMatcher #:nodoc:
48
+ def matcher_method
49
+ :should
50
+ end
27
51
 
28
52
  def __delegate_operator(actual, operator, expected)
29
53
  return true if actual.__send__(operator, expected)
@@ -37,6 +61,9 @@ module Spec
37
61
  end
38
62
 
39
63
  class NegativeOperatorMatcher < OperatorMatcher #:nodoc:
64
+ def matcher_method
65
+ :should_not
66
+ end
40
67
 
41
68
  def __delegate_operator(actual, operator, expected)
42
69
  return true unless actual.__send__(operator, expected)
data/lib/spec/matchers.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'spec/matchers/operator_matcher'
1
2
  require 'spec/matchers/generated_descriptions'
2
3
  require 'spec/matchers/errors'
3
4
  require 'spec/matchers/method_missing'
@@ -5,6 +6,7 @@ require 'spec/matchers/simple_matcher'
5
6
  require 'spec/matchers/be'
6
7
  require 'spec/matchers/be_close'
7
8
  require 'spec/matchers/change'
9
+ require 'spec/matchers/match_array'
8
10
  require 'spec/matchers/eql'
9
11
  require 'spec/matchers/equal'
10
12
  require 'spec/matchers/exist'
@@ -16,7 +18,6 @@ require 'spec/matchers/raise_error'
16
18
  require 'spec/matchers/respond_to'
17
19
  require 'spec/matchers/satisfy'
18
20
  require 'spec/matchers/throw_symbol'
19
- require 'spec/matchers/operator_matcher'
20
21
 
21
22
  module Spec
22
23
 
@@ -73,6 +73,25 @@ module Spec
73
73
  end
74
74
  end
75
75
 
76
+ class HashNotIncludingConstraint
77
+ def initialize(expected)
78
+ @expected = expected
79
+ end
80
+
81
+ def ==(actual)
82
+ @expected.each do | key, value |
83
+ return false if actual.has_key?(key) && value == actual[key]
84
+ end
85
+ true
86
+ rescue NoMethodError => ex
87
+ return false
88
+ end
89
+
90
+ def description
91
+ "hash_not_including(#{@expected.inspect.sub(/^\{/,"").sub(/\}$/,"")})"
92
+ end
93
+ end
94
+
76
95
  class DuckTypeConstraint
77
96
  def initialize(*methods_to_respond_to)
78
97
  @methods_to_respond_to = methods_to_respond_to
@@ -153,12 +172,31 @@ module Spec
153
172
  end
154
173
 
155
174
  # :call-seq:
156
- # object.should_receive(:message).with(hash_including(:this => that))
157
- #
158
- # Passes if the argument is a hash that includes the specified key/value
175
+ # object.should_receive(:message).with(hash_including(:key => val))
176
+ # object.should_receive(:message).with(hash_including(:key))
177
+ # object.should_receive(:message).with(hash_including(:key, :key2 => val2))
178
+ # Passes if the argument is a hash that includes the specified key(s) or key/value
159
179
  # pairs. If the hash includes other keys, it will still pass.
160
- def hash_including(expected={})
161
- HashIncludingConstraint.new(expected)
180
+ def hash_including(*args)
181
+ HashIncludingConstraint.new(anythingize_lonely_keys(*args))
182
+ end
183
+
184
+ # :call-seq:
185
+ # object.should_receive(:message).with(hash_not_including(:key => val))
186
+ # object.should_receive(:message).with(hash_not_including(:key))
187
+ # object.should_receive(:message).with(hash_not_including(:key, :key2 => :val2))
188
+ #
189
+ # Passes if the argument is a hash that doesn't include the specified key(s) or key/value
190
+ def hash_not_including(*args)
191
+ HashNotIncludingConstraint.new(anythingize_lonely_keys(*args))
192
+ end
193
+
194
+ private
195
+
196
+ def anythingize_lonely_keys(*args)
197
+ hash = args.last.class == Hash ? args.delete_at(-1) : {}
198
+ args.each { | arg | hash[arg] = anything }
199
+ hash
162
200
  end
163
201
  end
164
202
  end
@@ -15,11 +15,11 @@ module Spec
15
15
  end
16
16
  end
17
17
 
18
- def run
18
+ def run(run_options)
19
19
  prepare
20
20
  success = true
21
21
  example_groups.each do |example_group|
22
- success = success & example_group.run
22
+ success = success & example_group.run(run_options)
23
23
  end
24
24
  return success
25
25
  ensure