rspec-core 2.13.1 → 2.14.0.rc1

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 (112) hide show
  1. data/Changelog.md +80 -0
  2. data/exe/autospec +1 -1
  3. data/features/README.md +2 -2
  4. data/features/command_line/format_option.feature +8 -14
  5. data/features/command_line/line_number_appended_to_path.feature +4 -4
  6. data/features/command_line/line_number_option.feature +1 -1
  7. data/features/command_line/rake_task.feature +2 -2
  8. data/features/command_line/require_option.feature +43 -0
  9. data/features/command_line/ruby.feature +2 -2
  10. data/features/command_line/warnings_option.feature +27 -0
  11. data/features/configuration/backtrace_clean_patterns.feature +2 -2
  12. data/features/configuration/deprecation_stream.feature +58 -0
  13. data/features/configuration/pattern.feature +8 -0
  14. data/features/configuration/profile.feature +59 -2
  15. data/features/configuration/read_options_from_file.feature +8 -5
  16. data/features/configuration/run_all_when_everything_filtered.feature +20 -4
  17. data/features/example_groups/basic_structure.feature +1 -1
  18. data/features/example_groups/shared_context.feature +1 -1
  19. data/features/example_groups/shared_examples.feature +72 -0
  20. data/features/filtering/exclusion_filters.feature +10 -10
  21. data/features/formatters/custom_formatter.feature +1 -1
  22. data/features/hooks/before_and_after_hooks.feature +19 -19
  23. data/features/mock_framework_integration/use_any_framework.feature +6 -6
  24. data/features/mock_framework_integration/use_flexmock.feature +3 -3
  25. data/features/mock_framework_integration/use_mocha.feature +3 -3
  26. data/features/mock_framework_integration/use_rr.feature +3 -3
  27. data/features/mock_framework_integration/use_rspec.feature +3 -3
  28. data/features/subject/implicit_subject.feature +1 -1
  29. data/lib/rspec/core.rb +20 -3
  30. data/lib/rspec/core/backtrace_cleaner.rb +46 -0
  31. data/lib/rspec/core/backward_compatibility.rb +3 -13
  32. data/lib/rspec/core/configuration.rb +136 -49
  33. data/lib/rspec/core/configuration_options.rb +19 -8
  34. data/lib/rspec/core/deprecation.rb +18 -30
  35. data/lib/rspec/core/example.rb +3 -3
  36. data/lib/rspec/core/example_group.rb +4 -3
  37. data/lib/rspec/core/extensions/kernel.rb +1 -1
  38. data/lib/rspec/core/filter_manager.rb +1 -1
  39. data/lib/rspec/core/formatters.rb +1 -1
  40. data/lib/rspec/core/formatters/base_formatter.rb +10 -1
  41. data/lib/rspec/core/formatters/base_text_formatter.rb +47 -10
  42. data/lib/rspec/core/formatters/deprecation_formatter.rb +35 -0
  43. data/lib/rspec/core/formatters/helpers.rb +12 -5
  44. data/lib/rspec/core/formatters/html_formatter.rb +7 -6
  45. data/lib/rspec/core/formatters/html_printer.rb +13 -12
  46. data/lib/rspec/core/formatters/json_formatter.rb +1 -2
  47. data/lib/rspec/core/formatters/text_mate_formatter.rb +1 -1
  48. data/lib/rspec/core/hooks.rb +9 -0
  49. data/lib/rspec/core/memoized_helpers.rb +19 -8
  50. data/lib/rspec/core/metadata.rb +3 -1
  51. data/lib/rspec/core/mocking/with_flexmock.rb +1 -1
  52. data/lib/rspec/core/mocking/with_rr.rb +1 -1
  53. data/lib/rspec/core/option_parser.rb +6 -2
  54. data/lib/rspec/core/pending.rb +1 -0
  55. data/lib/rspec/core/rake_task.rb +11 -19
  56. data/lib/rspec/core/reporter.rb +33 -4
  57. data/lib/rspec/core/shared_example_group.rb +56 -16
  58. data/lib/rspec/core/shared_example_group/collection.rb +42 -0
  59. data/lib/rspec/core/version.rb +1 -1
  60. data/lib/rspec/core/world.rb +2 -3
  61. data/spec/autotest/rspec_spec.rb +2 -2
  62. data/spec/rspec/core/backtrace_cleaner_spec.rb +68 -0
  63. data/spec/rspec/core/configuration_options_spec.rb +15 -4
  64. data/spec/rspec/core/configuration_spec.rb +202 -19
  65. data/spec/rspec/core/deprecation_spec.rb +41 -0
  66. data/spec/rspec/core/deprecations_spec.rb +10 -12
  67. data/spec/rspec/core/drb_command_line_spec.rb +1 -1
  68. data/spec/rspec/core/example_group_spec.rb +37 -36
  69. data/spec/rspec/core/example_spec.rb +25 -4
  70. data/spec/rspec/core/filter_manager_spec.rb +6 -6
  71. data/spec/rspec/core/formatters/base_text_formatter_spec.rb +101 -36
  72. data/spec/rspec/core/formatters/deprecation_formatter_spec.rb +78 -0
  73. data/spec/rspec/core/formatters/documentation_formatter_spec.rb +2 -2
  74. data/spec/rspec/core/formatters/helpers_spec.rb +23 -7
  75. data/spec/rspec/core/formatters/html_formatted-1.8.7-jruby.html +20 -14
  76. data/spec/rspec/core/formatters/html_formatted-1.8.7-rbx.html +69 -169
  77. data/spec/rspec/core/formatters/html_formatted-1.8.7.html +28 -23
  78. data/spec/rspec/core/formatters/html_formatted-1.9.2.html +42 -33
  79. data/spec/rspec/core/formatters/html_formatted-1.9.3-jruby.html +17 -23
  80. data/spec/rspec/core/formatters/html_formatted-1.9.3-rbx.html +57 -157
  81. data/spec/rspec/core/formatters/html_formatted-1.9.3.html +42 -33
  82. data/spec/rspec/core/formatters/html_formatted-2.0.0.html +42 -33
  83. data/spec/rspec/core/formatters/html_formatter_spec.rb +1 -0
  84. data/spec/rspec/core/formatters/progress_formatter_spec.rb +3 -3
  85. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7-jruby.html +11 -14
  86. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7-rbx.html +103 -203
  87. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7.html +30 -25
  88. data/spec/rspec/core/formatters/text_mate_formatted-1.9.2.html +42 -33
  89. data/spec/rspec/core/formatters/text_mate_formatted-1.9.3-jruby.html +20 -14
  90. data/spec/rspec/core/formatters/text_mate_formatted-1.9.3-rbx.html +103 -203
  91. data/spec/rspec/core/formatters/text_mate_formatted-1.9.3.html +42 -33
  92. data/spec/rspec/core/formatters/text_mate_formatted-2.0.0.html +42 -33
  93. data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +1 -0
  94. data/spec/rspec/core/memoized_helpers_spec.rb +28 -0
  95. data/spec/rspec/core/metadata_spec.rb +8 -3
  96. data/spec/rspec/core/option_parser_spec.rb +8 -0
  97. data/spec/rspec/core/project_initializer_spec.rb +2 -2
  98. data/spec/rspec/core/rake_task_spec.rb +8 -8
  99. data/spec/rspec/core/reporter_spec.rb +26 -6
  100. data/spec/rspec/core/resources/formatter_specs.rb +3 -3
  101. data/spec/rspec/core/shared_context_spec.rb +20 -0
  102. data/spec/rspec/core/shared_example_group/collection_spec.rb +70 -0
  103. data/spec/rspec/core/shared_example_group_spec.rb +4 -4
  104. data/spec/rspec/core/world_spec.rb +1 -3
  105. data/spec/rspec/core_spec.rb +20 -0
  106. data/spec/spec_helper.rb +29 -29
  107. data/spec/support/helper_methods.rb +9 -1
  108. data/spec/support/isolate_load_path_mutation.rb +6 -0
  109. data/spec/support/sandboxed_mock_space.rb +100 -0
  110. metadata +28 -13
  111. data/features/filtering/run_all_when_everything_filtered.feature +0 -46
  112. data/lib/rspec/core/load_path.rb +0 -3
@@ -30,7 +30,7 @@ module RSpec
30
30
  @output.puts " <dd class=\"example passed\"><span class=\"passed_spec_name\">#{h(description)}</span><span class='duration'>#{formatted_run_time}s</span></dd>"
31
31
  end
32
32
 
33
- def print_example_failed( pending_fixed, description, run_time, failure_id, exception, extra_content )
33
+ def print_example_failed( pending_fixed, description, run_time, failure_id, exception, extra_content, escape_backtrace = false )
34
34
  formatted_run_time = sprintf("%.5f", run_time)
35
35
 
36
36
  @output.puts " <dd class=\"example #{pending_fixed ? 'pending_fixed' : 'failed'}\">"
@@ -39,7 +39,11 @@ module RSpec
39
39
  @output.puts " <div class=\"failure\" id=\"failure_#{failure_id}\">"
40
40
  if exception
41
41
  @output.puts " <div class=\"message\"><pre>#{h(exception[:message])}</pre></div>"
42
- @output.puts " <div class=\"backtrace\"><pre>#{exception[:backtrace]}</pre></div>"
42
+ if escape_backtrace
43
+ @output.puts " <div class=\"backtrace\"><pre>#{h exception[:backtrace]}</pre></div>"
44
+ else
45
+ @output.puts " <div class=\"backtrace\"><pre>#{exception[:backtrace]}</pre></div>"
46
+ end
43
47
  end
44
48
  @output.puts extra_content if extra_content
45
49
  @output.puts " </div>"
@@ -114,14 +118,14 @@ module RSpec
114
118
  </div>
115
119
 
116
120
  <div id="display-filters">
117
- <input id="passed_checkbox" name="passed_checkbox" type="checkbox" checked onchange="apply_filters()" value="1"> <label for="passed_checkbox">Passed</label>
118
- <input id="failed_checkbox" name="failed_checkbox" type="checkbox" checked onchange="apply_filters()" value="2"> <label for="failed_checkbox">Failed</label>
119
- <input id="pending_checkbox" name="pending_checkbox" type="checkbox" checked onchange="apply_filters()" value="3"> <label for="pending_checkbox">Pending</label>
121
+ <input id="passed_checkbox" name="passed_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="1" /> <label for="passed_checkbox">Passed</label>
122
+ <input id="failed_checkbox" name="failed_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="2" /> <label for="failed_checkbox">Failed</label>
123
+ <input id="pending_checkbox" name="pending_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="3" /> <label for="pending_checkbox">Pending</label>
120
124
  </div>
121
125
 
122
126
  <div id="summary">
123
- <p id="totals">&nbsp;</p>
124
- <p id="duration">&nbsp;</p>
127
+ <p id="totals">&#160;</p>
128
+ <p id="duration">&#160;</p>
125
129
  </div>
126
130
  </div>
127
131
 
@@ -371,11 +375,8 @@ a {
371
375
  EOF
372
376
 
373
377
  HTML_HEADER = <<-EOF
374
- <?xml version="1.0" encoding="UTF-8"?>
375
- <!DOCTYPE html
376
- PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
377
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
378
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
378
+ <!DOCTYPE html>
379
+ <html lang='en'>
379
380
  <head>
380
381
  <title>RSpec results</title>
381
382
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
@@ -28,8 +28,7 @@ module RSpec
28
28
  }
29
29
  @output_hash[:summary_line] = summary_line(example_count, failure_count, pending_count)
30
30
 
31
- # Don't print out profiled info if there are failures, it just clutters the output
32
- dump_profile if profile_examples? && failure_count == 0
31
+ dump_profile unless mute_profile_output?(failure_count)
33
32
  end
34
33
 
35
34
  def summary_line(example_count, failure_count, pending_count)
@@ -17,7 +17,7 @@ module RSpec
17
17
  def format_backtrace_line_for_textmate(line)
18
18
  return nil unless line
19
19
  CGI.escapeHTML(line).sub(/([^:]*\.e?rb):(\d*)/) do
20
- "<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
20
+ "<a href=\"txmt://open?url=file://#{File.expand_path($1)}&amp;line=#{$2}\">#{$1}:#{$2}</a> "
21
21
  end
22
22
  end
23
23
 
@@ -242,6 +242,15 @@ module RSpec
242
242
  # state of a shared object, resulting in an ordering dependency that can
243
243
  # make it difficult to reason about failures.
244
244
  #
245
+ # #### unsupported rspec constructs
246
+ #
247
+ # RSpec has several constructs that reset state between each example
248
+ # automatically. These are not intended for use from within `before(:all)`:
249
+ #
250
+ # * `let` declarations
251
+ # * `subject` declarations
252
+ # * Any mocking, stubbing or test double declaration
253
+ #
245
254
  # ### other frameworks
246
255
  #
247
256
  # Mock object frameworks and database transaction managers (like
@@ -36,6 +36,13 @@ module RSpec
36
36
  # it { should be_eligible_to_vote }
37
37
  # end
38
38
  #
39
+ # @note Because `subject` is designed to create state that is reset between
40
+ # each example, and `before(:all)` is designed to setup state that is
41
+ # shared across _all_ examples in an example group, `subject` is _not_
42
+ # intended to be used in a `before(:all)` hook. RSpec 2.13.1 prints
43
+ # a warning when you reference a `subject` from `before(:all)` and we plan
44
+ # to have it raise an error in RSpec 3.
45
+ #
39
46
  # @see #should
40
47
  def subject
41
48
  raise NotImplementedError, 'This definition is here for documentation purposes only'
@@ -132,6 +139,7 @@ EOS
132
139
 
133
140
  @example_group_instance.class.class_eval do
134
141
  hash.each do |key, value|
142
+ undef_method(key) if method_defined?(key)
135
143
  define_method(key) { value }
136
144
  end
137
145
  end
@@ -161,6 +169,13 @@ EOS
161
169
  # behave in surprising ways in examples that spawn separate threads,
162
170
  # though we have yet to see this in practice. You've been warned.
163
171
  #
172
+ # @note Because `let` is designed to create state that is reset between
173
+ # each example, and `before(:all)` is designed to setup state that is
174
+ # shared across _all_ examples in an example group, `let` is _not_
175
+ # intended to be used in a `before(:all)` hook. RSpec 2.13.1 prints
176
+ # a warning when you reference a `let` from `before(:all)` and we plan
177
+ # to have it raise an error in RSpec 3.
178
+ #
164
179
  # @example
165
180
  #
166
181
  # describe Thing do
@@ -177,7 +192,8 @@ EOS
177
192
  def let(name, &block)
178
193
  # We have to pass the block directly to `define_method` to
179
194
  # allow it to use method constructs like `super` and `return`.
180
- MemoizedHelpers.module_for(self).define_method(name, &block)
195
+ raise "#let or #subject called without a block" if block.nil?
196
+ MemoizedHelpers.module_for(self).send(:define_method, name, &block)
181
197
 
182
198
  # Apply the memoization. The method has been defined in an ancestor
183
199
  # module so we can use `super` here to get the value.
@@ -276,9 +292,9 @@ EOS
276
292
  def subject(name=nil, &block)
277
293
  if name
278
294
  let(name, &block)
279
- subject { __send__ name }
295
+ alias_method :subject, name
280
296
 
281
- self::NamedSubjectPreventSuper.define_method(name) do
297
+ self::NamedSubjectPreventSuper.send(:define_method, name) do
282
298
  raise NotImplementedError, "`super` in named subjects is not supported"
283
299
  end
284
300
  else
@@ -451,13 +467,8 @@ EOS
451
467
  get_constant_or_yield(example_group, :LetDefinitions) do
452
468
  mod = Module.new do
453
469
  include Module.new {
454
- public_class_method :define_method
455
470
  example_group.const_set(:NamedSubjectPreventSuper, self)
456
471
  }
457
-
458
- # Expose `define_method` as a public method, so we can
459
- # easily use it below.
460
- public_class_method :define_method
461
472
  end
462
473
 
463
474
  example_group.__send__(:include, mod)
@@ -73,6 +73,8 @@ module RSpec
73
73
  store(:full_description, full_description)
74
74
  when :description
75
75
  store(:description, build_description_from(*self[:description_args]))
76
+ when :description_args
77
+ store(:description_args, [])
76
78
  end
77
79
  end
78
80
 
@@ -244,7 +246,7 @@ module RSpec
244
246
  protected
245
247
 
246
248
  def configure_for_example(description, user_metadata)
247
- store(:description_args, [description])
249
+ store(:description_args, [description]) if description
248
250
  store(:caller, user_metadata.delete(:caller) || caller)
249
251
  update(user_metadata)
250
252
  end
@@ -8,7 +8,7 @@ require 'flexmock/rspec'
8
8
  module RSpec
9
9
  module Core
10
10
  module MockFrameworkAdapter
11
-
11
+
12
12
  def self.framework_name; :flexmock end
13
13
 
14
14
  include FlexMock::MockContainer
@@ -5,7 +5,7 @@ RSpec.configuration.backtrace_clean_patterns.push(RR::Errors::BACKTRACE_IDENTIFI
5
5
  module RSpec
6
6
  module Core
7
7
  module MockFrameworkAdapter
8
-
8
+
9
9
  def self.framework_name; :rr end
10
10
 
11
11
  include RR::Extensions::InstanceMethods
@@ -30,7 +30,7 @@ module RSpec::Core
30
30
  args.map! { |arg|
31
31
  case arg
32
32
  when "--formatter"
33
- RSpec.deprecate("the --formatter option", "-f or --format")
33
+ RSpec.deprecate("the --formatter option", :replacement => "-f or --format")
34
34
  "--format"
35
35
  when "--default_path"
36
36
  "--default-path"
@@ -119,7 +119,7 @@ module RSpec::Core
119
119
  end
120
120
 
121
121
  parser.on('-o', '--out FILE',
122
- 'Write output to a file instead of STDOUT. This option applies',
122
+ 'Write output to a file instead of $stdout. This option applies',
123
123
  ' to the previously specified --format, or the default format',
124
124
  ' if no format is specified.'
125
125
  ) do |o|
@@ -145,6 +145,10 @@ module RSpec::Core
145
145
  end
146
146
  end
147
147
 
148
+ parser.on('-w', '--warnings', 'Enable ruby warnings') do
149
+ options[:warnings] = true
150
+ end
151
+
148
152
  parser.separator <<-FILTERING
149
153
 
150
154
  **** Filtering/tags ****
@@ -92,6 +92,7 @@ module RSpec
92
92
  result = begin
93
93
  yield
94
94
  example.example_group_instance.instance_eval { verify_mocks_for_rspec }
95
+ true
95
96
  end
96
97
  example.metadata[:pending] = false
97
98
  rescue Exception => e
@@ -30,7 +30,7 @@ module RSpec
30
30
  # @deprecated
31
31
  # Has no effect. The rake task now checks ENV['BUNDLE_GEMFILE'] instead.
32
32
  def gemfile=(*)
33
- RSpec.deprecate("RSpec::Core::RakeTask#gemfile=", 'ENV["BUNDLE_GEMFILE"]')
33
+ RSpec.deprecate("RSpec::Core::RakeTask#gemfile=", :replacement => 'ENV["BUNDLE_GEMFILE"]')
34
34
  end
35
35
 
36
36
  # @deprecated
@@ -42,7 +42,7 @@ module RSpec
42
42
  # default:
43
43
  # false
44
44
  def warning=(true_or_false)
45
- RSpec.deprecate("RSpec::Core::RakeTask#warning=", 'ruby_opts="-w"')
45
+ RSpec.deprecate("RSpec::Core::RakeTask#warning=", :replacement => 'ruby_opts="-w"')
46
46
  @warning = true_or_false
47
47
  end
48
48
 
@@ -109,7 +109,7 @@ module RSpec
109
109
  # default:
110
110
  # nil
111
111
  def spec_opts=(opts)
112
- RSpec.deprecate('RSpec::Core::RakeTask#spec_opts=', 'rspec_opts=')
112
+ RSpec.deprecate('RSpec::Core::RakeTask#spec_opts=', :replacement => 'rspec_opts=')
113
113
  @rspec_opts = opts
114
114
  end
115
115
 
@@ -137,24 +137,16 @@ module RSpec
137
137
  @pattern = './spec{,/*/**}/*_spec.rb'
138
138
  end
139
139
 
140
- def has_files?
141
- empty = files_to_run.empty?
142
- puts "No examples matching #{pattern} could be found" if empty
143
- not empty
144
- end
145
-
146
140
  def run_task(verbose)
147
- files = has_files?
148
- if files
149
- command = spec_command
150
- begin
151
- puts command if verbose
152
- success = system(command)
153
- rescue
154
- puts failure_message if failure_message
155
- end
156
- raise("#{command} failed") if fail_on_error unless success
141
+ command = spec_command
142
+
143
+ begin
144
+ puts command if verbose
145
+ success = system(command)
146
+ rescue
147
+ puts failure_message if failure_message
157
148
  end
149
+ abort("#{command} failed") if fail_on_error unless success
158
150
  end
159
151
 
160
152
  private
@@ -1,11 +1,35 @@
1
1
  module RSpec::Core
2
2
  class Reporter
3
+ NOTIFICATIONS = %W[start message example_group_started example_group_finished example_started
4
+ example_passed example_failed example_pending start_dump dump_pending
5
+ dump_failures dump_summary seed close stop deprecation deprecation_summary].map(&:to_sym)
6
+
3
7
  def initialize(*formatters)
4
- @formatters = formatters
8
+ @listeners = Hash.new { |h,k| h[k] = [] }
9
+ formatters.each do |formatter|
10
+ register_listener(formatter, *NOTIFICATIONS)
11
+ end
5
12
  @example_count = @failure_count = @pending_count = 0
6
13
  @duration = @start = nil
7
14
  end
8
15
 
16
+ # @api
17
+ # @param [Object] An obect that wishes to be notified of reporter events
18
+ # @param [Array] Array of symbols represents the events a listener wishes to subscribe too
19
+ #
20
+ # Registers a listener to a list of notifications. The reporter will send notification of
21
+ # events to all registered listeners
22
+ def register_listener(listener, *notifications)
23
+ notifications.each do |notification|
24
+ @listeners[notification.to_sym] << listener if listener.respond_to?(notification)
25
+ end
26
+ true
27
+ end
28
+
29
+ def registered_listeners(notification)
30
+ @listeners[notification]
31
+ end
32
+
9
33
  # @api
10
34
  # @overload report(count, &block)
11
35
  # @overload report(count, seed, &block)
@@ -73,6 +97,10 @@ module RSpec::Core
73
97
  notify :example_pending, example
74
98
  end
75
99
 
100
+ def deprecation(message)
101
+ notify :deprecation, message
102
+ end
103
+
76
104
  def finish(seed)
77
105
  begin
78
106
  stop
@@ -80,6 +108,7 @@ module RSpec::Core
80
108
  notify :dump_pending
81
109
  notify :dump_failures
82
110
  notify :dump_summary, @duration, @example_count, @failure_count, @pending_count
111
+ notify :deprecation_summary
83
112
  notify :seed, seed if seed
84
113
  ensure
85
114
  notify :close
@@ -93,9 +122,9 @@ module RSpec::Core
93
122
  notify :stop
94
123
  end
95
124
 
96
- def notify(method, *args, &block)
97
- @formatters.each do |formatter|
98
- formatter.send method, *args, &block
125
+ def notify(event, *args, &block)
126
+ registered_listeners(event).each do |formatter|
127
+ formatter.send(event, *args, &block)
99
128
  end
100
129
  end
101
130
  end
@@ -28,8 +28,8 @@ module RSpec
28
28
  # @see ExampleGroup.it_behaves_like
29
29
  # @see ExampleGroup.include_examples
30
30
  # @see ExampleGroup.include_context
31
- def shared_examples *args, &block
32
- Registry.add_group(*args, &block)
31
+ def shared_examples(*args, &block)
32
+ Registry.add_group(self, *args, &block)
33
33
  end
34
34
 
35
35
  alias_method :shared_context, :shared_examples
@@ -39,8 +39,32 @@ module RSpec
39
39
  # @deprecated
40
40
  def share_as(name, &block)
41
41
  RSpec.deprecate("Rspec::Core::SharedExampleGroup#share_as",
42
- "RSpec::SharedContext or shared_examples")
43
- Registry.add_const(name, &block)
42
+ :replacement => "RSpec::SharedContext or shared_examples")
43
+ Registry.add_const(self, name, &block)
44
+ end
45
+
46
+ def shared_example_groups
47
+ Registry.shared_example_groups_for('main', *ancestors[0..-1])
48
+ end
49
+
50
+ module TopLevelDSL
51
+ def shared_examples(*args, &block)
52
+ Registry.add_group('main', *args, &block)
53
+ end
54
+
55
+ alias_method :shared_context, :shared_examples
56
+ alias_method :share_examples_for, :shared_examples
57
+ alias_method :shared_examples_for, :shared_examples
58
+
59
+ def share_as(name, &block)
60
+ RSpec.deprecate("Rspec::Core::SharedExampleGroup#share_as",
61
+ :replacement => "RSpec::SharedContext or shared_examples")
62
+ Registry.add_const('main', name, &block)
63
+ end
64
+
65
+ def shared_example_groups
66
+ Registry.shared_example_groups_for('main')
67
+ end
44
68
  end
45
69
 
46
70
  # @private
@@ -53,13 +77,13 @@ module RSpec
53
77
  module Registry
54
78
  extend self
55
79
 
56
- def add_group(*args, &block)
80
+ def add_group(source, *args, &block)
57
81
  ensure_block_has_source_location(block, caller[1])
58
82
 
59
83
  if key? args.first
60
84
  key = args.shift
61
- warn_if_key_taken key, block
62
- RSpec.world.shared_example_groups[key] = block
85
+ warn_if_key_taken source, key, block
86
+ add_shared_example_group source, key, block
63
87
  end
64
88
 
65
89
  unless args.empty?
@@ -71,7 +95,7 @@ module RSpec
71
95
  end
72
96
  end
73
97
 
74
- def add_const(name, &block)
98
+ def add_const(source, name, &block)
75
99
  if Object.const_defined?(name)
76
100
  mod = Object.const_get(name)
77
101
  raise_name_error unless mod.created_from_caller(caller)
@@ -92,12 +116,28 @@ module RSpec
92
116
  end
93
117
 
94
118
  shared_const = Object.const_set(name, mod)
95
- RSpec.world.shared_example_groups[shared_const] = block
119
+ add_shared_example_group source, shared_const, block
120
+ end
121
+
122
+ def shared_example_groups_for(*sources)
123
+ Collection.new(sources, shared_example_groups)
124
+ end
125
+
126
+ def shared_example_groups
127
+ @shared_example_groups ||= Hash.new { |hash,key| hash[key] = Hash.new }
128
+ end
129
+
130
+ def clear
131
+ @shared_example_groups.clear
96
132
  end
97
133
 
98
134
  private
99
135
 
100
- def key? candidate
136
+ def add_shared_example_group(source, key, block)
137
+ shared_example_groups[source][key] = block
138
+ end
139
+
140
+ def key?(candidate)
101
141
  [String, Symbol, Module].any? { |cls| cls === candidate }
102
142
  end
103
143
 
@@ -105,8 +145,8 @@ module RSpec
105
145
  raise NameError, "The first argument (#{name}) to share_as must be a legal name for a constant not already in use."
106
146
  end
107
147
 
108
- def warn_if_key_taken key, new_block
109
- return unless existing_block = example_block_for(key)
148
+ def warn_if_key_taken(source, key, new_block)
149
+ return unless existing_block = example_block_for(source, key)
110
150
 
111
151
  Kernel.warn <<-WARNING.gsub(/^ +\|/, '')
112
152
  |WARNING: Shared example group '#{key}' has been previously defined at:
@@ -117,12 +157,12 @@ module RSpec
117
157
  WARNING
118
158
  end
119
159
 
120
- def formatted_location block
160
+ def formatted_location(block)
121
161
  block.source_location.join ":"
122
162
  end
123
163
 
124
- def example_block_for key
125
- RSpec.world.shared_example_groups[key]
164
+ def example_block_for(source, key)
165
+ shared_example_groups[source][key]
126
166
  end
127
167
 
128
168
  def ensure_block_has_source_location(block, caller_line)
@@ -139,6 +179,6 @@ module RSpec
139
179
  end
140
180
  end
141
181
 
142
- extend RSpec::Core::SharedExampleGroup
182
+ extend RSpec::Core::SharedExampleGroup::TopLevelDSL
143
183
  Module.send(:include, RSpec::Core::SharedExampleGroup)
144
184