rspec-core 3.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.document +5 -0
  5. data/.yardopts +8 -0
  6. data/Changelog.md +2243 -0
  7. data/LICENSE.md +26 -0
  8. data/README.md +384 -0
  9. data/exe/rspec +4 -0
  10. data/lib/rspec/autorun.rb +3 -0
  11. data/lib/rspec/core.rb +185 -0
  12. data/lib/rspec/core/backtrace_formatter.rb +65 -0
  13. data/lib/rspec/core/bisect/coordinator.rb +62 -0
  14. data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
  15. data/lib/rspec/core/bisect/fork_runner.rb +134 -0
  16. data/lib/rspec/core/bisect/server.rb +61 -0
  17. data/lib/rspec/core/bisect/shell_command.rb +126 -0
  18. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  19. data/lib/rspec/core/bisect/utilities.rb +58 -0
  20. data/lib/rspec/core/configuration.rb +2308 -0
  21. data/lib/rspec/core/configuration_options.rb +233 -0
  22. data/lib/rspec/core/drb.rb +113 -0
  23. data/lib/rspec/core/dsl.rb +98 -0
  24. data/lib/rspec/core/example.rb +656 -0
  25. data/lib/rspec/core/example_group.rb +889 -0
  26. data/lib/rspec/core/example_status_persister.rb +235 -0
  27. data/lib/rspec/core/filter_manager.rb +231 -0
  28. data/lib/rspec/core/flat_map.rb +20 -0
  29. data/lib/rspec/core/formatters.rb +269 -0
  30. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  31. data/lib/rspec/core/formatters/base_formatter.rb +70 -0
  32. data/lib/rspec/core/formatters/base_text_formatter.rb +75 -0
  33. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  34. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
  35. data/lib/rspec/core/formatters/console_codes.rb +68 -0
  36. data/lib/rspec/core/formatters/deprecation_formatter.rb +223 -0
  37. data/lib/rspec/core/formatters/documentation_formatter.rb +70 -0
  38. data/lib/rspec/core/formatters/exception_presenter.rb +508 -0
  39. data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
  40. data/lib/rspec/core/formatters/helpers.rb +110 -0
  41. data/lib/rspec/core/formatters/html_formatter.rb +153 -0
  42. data/lib/rspec/core/formatters/html_printer.rb +414 -0
  43. data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
  44. data/lib/rspec/core/formatters/json_formatter.rb +102 -0
  45. data/lib/rspec/core/formatters/profile_formatter.rb +68 -0
  46. data/lib/rspec/core/formatters/progress_formatter.rb +29 -0
  47. data/lib/rspec/core/formatters/protocol.rb +182 -0
  48. data/lib/rspec/core/formatters/snippet_extractor.rb +134 -0
  49. data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
  50. data/lib/rspec/core/hooks.rb +624 -0
  51. data/lib/rspec/core/invocations.rb +87 -0
  52. data/lib/rspec/core/memoized_helpers.rb +554 -0
  53. data/lib/rspec/core/metadata.rb +498 -0
  54. data/lib/rspec/core/metadata_filter.rb +255 -0
  55. data/lib/rspec/core/minitest_assertions_adapter.rb +31 -0
  56. data/lib/rspec/core/mocking_adapters/flexmock.rb +31 -0
  57. data/lib/rspec/core/mocking_adapters/mocha.rb +57 -0
  58. data/lib/rspec/core/mocking_adapters/null.rb +14 -0
  59. data/lib/rspec/core/mocking_adapters/rr.rb +31 -0
  60. data/lib/rspec/core/mocking_adapters/rspec.rb +32 -0
  61. data/lib/rspec/core/notifications.rb +521 -0
  62. data/lib/rspec/core/option_parser.rb +309 -0
  63. data/lib/rspec/core/ordering.rb +158 -0
  64. data/lib/rspec/core/output_wrapper.rb +29 -0
  65. data/lib/rspec/core/pending.rb +165 -0
  66. data/lib/rspec/core/profiler.rb +34 -0
  67. data/lib/rspec/core/project_initializer.rb +48 -0
  68. data/lib/rspec/core/project_initializer/.rspec +1 -0
  69. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +100 -0
  70. data/lib/rspec/core/rake_task.rb +168 -0
  71. data/lib/rspec/core/reporter.rb +257 -0
  72. data/lib/rspec/core/ruby_project.rb +53 -0
  73. data/lib/rspec/core/runner.rb +199 -0
  74. data/lib/rspec/core/sandbox.rb +37 -0
  75. data/lib/rspec/core/set.rb +54 -0
  76. data/lib/rspec/core/shared_context.rb +55 -0
  77. data/lib/rspec/core/shared_example_group.rb +269 -0
  78. data/lib/rspec/core/shell_escape.rb +49 -0
  79. data/lib/rspec/core/test_unit_assertions_adapter.rb +30 -0
  80. data/lib/rspec/core/version.rb +9 -0
  81. data/lib/rspec/core/warnings.rb +40 -0
  82. data/lib/rspec/core/world.rb +275 -0
  83. metadata +292 -0
  84. metadata.gz.sig +0 -0
@@ -0,0 +1,87 @@
1
+ module RSpec
2
+ module Core
3
+ # @private
4
+ module Invocations
5
+ # @private
6
+ class InitializeProject
7
+ def call(*_args)
8
+ RSpec::Support.require_rspec_core "project_initializer"
9
+ ProjectInitializer.new.run
10
+ 0
11
+ end
12
+ end
13
+
14
+ # @private
15
+ class DRbWithFallback
16
+ def call(options, err, out)
17
+ require 'rspec/core/drb'
18
+ begin
19
+ return DRbRunner.new(options).run(err, out)
20
+ rescue DRb::DRbConnError
21
+ err.puts "No DRb server is running. Running in local process instead ..."
22
+ end
23
+ RSpec::Core::Runner.new(options).run(err, out)
24
+ end
25
+ end
26
+
27
+ # @private
28
+ class Bisect
29
+ def call(options, err, out)
30
+ RSpec::Support.require_rspec_core "bisect/coordinator"
31
+ runner = Runner.new(options).tap { |r| r.configure(err, out) }
32
+ formatter = bisect_formatter_klass_for(options.options[:bisect]).new(
33
+ out, runner.configuration.bisect_runner
34
+ )
35
+
36
+ success = RSpec::Core::Bisect::Coordinator.bisect_with(
37
+ runner, options.args, formatter
38
+ )
39
+
40
+ success ? 0 : 1
41
+ end
42
+
43
+ private
44
+
45
+ def bisect_formatter_klass_for(argument)
46
+ return Formatters::BisectDebugFormatter if argument == "verbose"
47
+ Formatters::BisectProgressFormatter
48
+ end
49
+ end
50
+
51
+ # @private
52
+ class PrintVersion
53
+ def call(_options, _err, out)
54
+ overall_version = RSpec::Core::Version::STRING
55
+ unless overall_version =~ /[a-zA-Z]+/
56
+ overall_version = overall_version.split('.').first(2).join('.')
57
+ end
58
+
59
+ out.puts "RSpec #{overall_version}"
60
+
61
+ [:Core, :Expectations, :Mocks, :Rails, :Support].each do |const_name|
62
+ lib_name = const_name.to_s.downcase
63
+ begin
64
+ require "rspec/#{lib_name}/version"
65
+ rescue LoadError
66
+ # Not worth mentioning libs that are not installed
67
+ nil
68
+ else
69
+ out.puts " - rspec-#{lib_name} #{RSpec.const_get(const_name)::Version::STRING}"
70
+ end
71
+ end
72
+
73
+ 0
74
+ end
75
+ end
76
+
77
+ # @private
78
+ PrintHelp = Struct.new(:parser, :hidden_options) do
79
+ def call(_options, _err, out)
80
+ # Removing the hidden options from the output.
81
+ out.puts parser.to_s.gsub(/^\s+(#{hidden_options.join('|')})\b.*$\n/, '')
82
+ 0
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,554 @@
1
+ RSpec::Support.require_rspec_support 'reentrant_mutex'
2
+
3
+ module RSpec
4
+ module Core
5
+ # This module is included in {ExampleGroup}, making the methods
6
+ # available to be called from within example blocks.
7
+ #
8
+ # @see ClassMethods
9
+ module MemoizedHelpers
10
+ # @note `subject` was contributed by Joe Ferris to support the one-liner
11
+ # syntax embraced by shoulda matchers:
12
+ #
13
+ # describe Widget do
14
+ # it { is_expected.to validate_presence_of(:name) }
15
+ # # or
16
+ # it { should validate_presence_of(:name) }
17
+ # end
18
+ #
19
+ # While the examples below demonstrate how to use `subject`
20
+ # explicitly in examples, we recommend that you define a method with
21
+ # an intention revealing name instead.
22
+ #
23
+ # @example
24
+ #
25
+ # # Explicit declaration of subject.
26
+ # describe Person do
27
+ # subject { Person.new(:birthdate => 19.years.ago) }
28
+ # it "should be eligible to vote" do
29
+ # subject.should be_eligible_to_vote
30
+ # # ^ ^ explicit reference to subject not recommended
31
+ # end
32
+ # end
33
+ #
34
+ # # Implicit subject => { Person.new }.
35
+ # describe Person do
36
+ # it "should be eligible to vote" do
37
+ # subject.should be_eligible_to_vote
38
+ # # ^ ^ explicit reference to subject not recommended
39
+ # end
40
+ # end
41
+ #
42
+ # # One-liner syntax - expectation is set on the subject.
43
+ # describe Person do
44
+ # it { is_expected.to be_eligible_to_vote }
45
+ # # or
46
+ # it { should be_eligible_to_vote }
47
+ # end
48
+ #
49
+ # @note Because `subject` is designed to create state that is reset
50
+ # between each example, and `before(:context)` is designed to setup
51
+ # state that is shared across _all_ examples in an example group,
52
+ # `subject` is _not_ intended to be used in a `before(:context)` hook.
53
+ #
54
+ # @see #should
55
+ # @see #should_not
56
+ # @see #is_expected
57
+ def subject
58
+ __memoized.fetch_or_store(:subject) do
59
+ described = described_class || self.class.metadata.fetch(:description_args).first
60
+ Class === described ? described.new : described
61
+ end
62
+ end
63
+
64
+ # When `should` is called with no explicit receiver, the call is
65
+ # delegated to the object returned by `subject`. Combined with an
66
+ # implicit subject this supports very concise expressions.
67
+ #
68
+ # @example
69
+ #
70
+ # describe Person do
71
+ # it { should be_eligible_to_vote }
72
+ # end
73
+ #
74
+ # @see #subject
75
+ # @see #is_expected
76
+ #
77
+ # @note This only works if you are using rspec-expectations.
78
+ # @note If you are using RSpec's newer expect-based syntax you may
79
+ # want to use `is_expected.to` instead of `should`.
80
+ def should(matcher=nil, message=nil)
81
+ RSpec::Expectations::PositiveExpectationHandler.handle_matcher(subject, matcher, message)
82
+ end
83
+
84
+ # Just like `should`, `should_not` delegates to the subject (implicit or
85
+ # explicit) of the example group.
86
+ #
87
+ # @example
88
+ #
89
+ # describe Person do
90
+ # it { should_not be_eligible_to_vote }
91
+ # end
92
+ #
93
+ # @see #subject
94
+ # @see #is_expected
95
+ #
96
+ # @note This only works if you are using rspec-expectations.
97
+ # @note If you are using RSpec's newer expect-based syntax you may
98
+ # want to use `is_expected.to_not` instead of `should_not`.
99
+ def should_not(matcher=nil, message=nil)
100
+ RSpec::Expectations::NegativeExpectationHandler.handle_matcher(subject, matcher, message)
101
+ end
102
+
103
+ # Wraps the `subject` in `expect` to make it the target of an expectation.
104
+ # Designed to read nicely for one-liners.
105
+ #
106
+ # @example
107
+ #
108
+ # describe [1, 2, 3] do
109
+ # it { is_expected.to be_an Array }
110
+ # it { is_expected.not_to include 4 }
111
+ # end
112
+ #
113
+ # @see #subject
114
+ # @see #should
115
+ # @see #should_not
116
+ #
117
+ # @note This only works if you are using rspec-expectations.
118
+ def is_expected
119
+ expect(subject)
120
+ end
121
+
122
+ # @private
123
+ # should just be placed in private section,
124
+ # but Ruby issues warnings on private attributes.
125
+ # and expanding it to the equivalent method upsets Rubocop,
126
+ # b/c it should obviously be a reader
127
+ attr_reader :__memoized
128
+ private :__memoized
129
+
130
+ private
131
+
132
+ # @private
133
+ def initialize(*)
134
+ __init_memoized
135
+ super
136
+ end
137
+
138
+ # @private
139
+ def __init_memoized
140
+ @__memoized = if RSpec.configuration.threadsafe?
141
+ ThreadsafeMemoized.new
142
+ else
143
+ NonThreadSafeMemoized.new
144
+ end
145
+ end
146
+
147
+ # @private
148
+ class ThreadsafeMemoized
149
+ def initialize
150
+ @memoized = {}
151
+ @mutex = Support::ReentrantMutex.new
152
+ end
153
+
154
+ def fetch_or_store(key)
155
+ @memoized.fetch(key) do # only first access pays for synchronization
156
+ @mutex.synchronize do
157
+ @memoized.fetch(key) { @memoized[key] = yield }
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ # @private
164
+ class NonThreadSafeMemoized
165
+ def initialize
166
+ @memoized = {}
167
+ end
168
+
169
+ def fetch_or_store(key)
170
+ @memoized.fetch(key) { @memoized[key] = yield }
171
+ end
172
+ end
173
+
174
+ # Used internally to customize the behavior of the
175
+ # memoized hash when used in a `before(:context)` hook.
176
+ #
177
+ # @private
178
+ class ContextHookMemoized
179
+ def self.isolate_for_context_hook(example_group_instance)
180
+ exploding_memoized = self
181
+
182
+ example_group_instance.instance_exec do
183
+ @__memoized = exploding_memoized
184
+
185
+ begin
186
+ yield
187
+ ensure
188
+ # This is doing a reset instead of just isolating for context hook.
189
+ # Really, this should set the old @__memoized back into place.
190
+ #
191
+ # Caller is the before and after context hooks
192
+ # which are both called from self.run
193
+ # I didn't look at why it made tests fail, maybe an object was getting reused in RSpec tests,
194
+ # if so, then that probably already works, and its the tests that are wrong.
195
+ __init_memoized
196
+ end
197
+ end
198
+ end
199
+
200
+ def self.fetch_or_store(key, &_block)
201
+ description = if key == :subject
202
+ "subject"
203
+ else
204
+ "let declaration `#{key}`"
205
+ end
206
+
207
+ raise <<-EOS
208
+ #{description} accessed in #{article} #{hook_expression} hook at:
209
+ #{CallerFilter.first_non_rspec_line}
210
+
211
+ `let` and `subject` declarations are not intended to be called
212
+ in #{article} #{hook_expression} hook, as they exist to define state that
213
+ is reset between each example, while #{hook_expression} exists to
214
+ #{hook_intention}.
215
+ EOS
216
+ end
217
+
218
+ # @private
219
+ class Before < self
220
+ def self.hook_expression
221
+ "`before(:context)`"
222
+ end
223
+
224
+ def self.article
225
+ "a"
226
+ end
227
+
228
+ def self.hook_intention
229
+ "define state that is shared across examples in an example group"
230
+ end
231
+ end
232
+
233
+ # @private
234
+ class After < self
235
+ def self.hook_expression
236
+ "`after(:context)`"
237
+ end
238
+
239
+ def self.article
240
+ "an"
241
+ end
242
+
243
+ def self.hook_intention
244
+ "cleanup state that is shared across examples in an example group"
245
+ end
246
+ end
247
+ end
248
+
249
+ # This module is extended onto {ExampleGroup}, making the methods
250
+ # available to be called from within example group blocks.
251
+ # You can think of them as being analagous to class macros.
252
+ module ClassMethods
253
+ # Generates a method whose return value is memoized after the first
254
+ # call. Useful for reducing duplication between examples that assign
255
+ # values to the same local variable.
256
+ #
257
+ # @note `let` _can_ enhance readability when used sparingly (1,2, or
258
+ # maybe 3 declarations) in any given example group, but that can
259
+ # quickly degrade with overuse. YMMV.
260
+ #
261
+ # @note `let` can be configured to be threadsafe or not.
262
+ # If it is threadsafe, it will take longer to access the value.
263
+ # If it is not threadsafe, it may behave in surprising ways in examples
264
+ # that spawn separate threads. Specify this on `RSpec.configure`
265
+ #
266
+ # @note Because `let` is designed to create state that is reset between
267
+ # each example, and `before(:context)` is designed to setup state that
268
+ # is shared across _all_ examples in an example group, `let` is _not_
269
+ # intended to be used in a `before(:context)` hook.
270
+ #
271
+ # @example
272
+ #
273
+ # describe Thing do
274
+ # let(:thing) { Thing.new }
275
+ #
276
+ # it "does something" do
277
+ # # First invocation, executes block, memoizes and returns result.
278
+ # thing.do_something
279
+ #
280
+ # # Second invocation, returns the memoized value.
281
+ # thing.should be_something
282
+ # end
283
+ # end
284
+ def let(name, &block)
285
+ # We have to pass the block directly to `define_method` to
286
+ # allow it to use method constructs like `super` and `return`.
287
+ raise "#let or #subject called without a block" if block.nil?
288
+ raise(
289
+ "#let or #subject called with a reserved name #initialize"
290
+ ) if :initialize == name
291
+ our_module = MemoizedHelpers.module_for(self)
292
+
293
+ # If we have a module clash in our helper module
294
+ # then we need to remove it to prevent a warning.
295
+ #
296
+ # Note we do not check ancestor modules (see: `instance_methods(false)`)
297
+ # as we can override them.
298
+ if our_module.instance_methods(false).include?(name)
299
+ our_module.__send__(:remove_method, name)
300
+ end
301
+ our_module.__send__(:define_method, name, &block)
302
+
303
+ # If we have a module clash in the example module
304
+ # then we need to remove it to prevent a warning.
305
+ #
306
+ # Note we do not check ancestor modules (see: `instance_methods(false)`)
307
+ # as we can override them.
308
+ if instance_methods(false).include?(name)
309
+ remove_method(name)
310
+ end
311
+
312
+ # Apply the memoization. The method has been defined in an ancestor
313
+ # module so we can use `super` here to get the value.
314
+ if block.arity == 1
315
+ define_method(name) { __memoized.fetch_or_store(name) { super(RSpec.current_example, &nil) } }
316
+ else
317
+ define_method(name) { __memoized.fetch_or_store(name) { super(&nil) } }
318
+ end
319
+ end
320
+
321
+ # Just like `let`, except the block is invoked by an implicit `before`
322
+ # hook. This serves a dual purpose of setting up state and providing a
323
+ # memoized reference to that state.
324
+ #
325
+ # @example
326
+ #
327
+ # class Thing
328
+ # def self.count
329
+ # @count ||= 0
330
+ # end
331
+ #
332
+ # def self.count=(val)
333
+ # @count += val
334
+ # end
335
+ #
336
+ # def self.reset_count
337
+ # @count = 0
338
+ # end
339
+ #
340
+ # def initialize
341
+ # self.class.count += 1
342
+ # end
343
+ # end
344
+ #
345
+ # describe Thing do
346
+ # after(:example) { Thing.reset_count }
347
+ #
348
+ # context "using let" do
349
+ # let(:thing) { Thing.new }
350
+ #
351
+ # it "is not invoked implicitly" do
352
+ # Thing.count.should eq(0)
353
+ # end
354
+ #
355
+ # it "can be invoked explicitly" do
356
+ # thing
357
+ # Thing.count.should eq(1)
358
+ # end
359
+ # end
360
+ #
361
+ # context "using let!" do
362
+ # let!(:thing) { Thing.new }
363
+ #
364
+ # it "is invoked implicitly" do
365
+ # Thing.count.should eq(1)
366
+ # end
367
+ #
368
+ # it "returns memoized version on first invocation" do
369
+ # thing
370
+ # Thing.count.should eq(1)
371
+ # end
372
+ # end
373
+ # end
374
+ def let!(name, &block)
375
+ let(name, &block)
376
+ before { __send__(name) }
377
+ end
378
+
379
+ # Declares a `subject` for an example group which can then be wrapped
380
+ # with `expect` using `is_expected` to make it the target of an
381
+ # expectation in a concise, one-line example.
382
+ #
383
+ # Given a `name`, defines a method with that name which returns the
384
+ # `subject`. This lets you declare the subject once and access it
385
+ # implicitly in one-liners and explicitly using an intention revealing
386
+ # name.
387
+ #
388
+ # When given a `name`, calling `super` in the block is not supported.
389
+ #
390
+ # @note `subject` can be configured to be threadsafe or not.
391
+ # If it is threadsafe, it will take longer to access the value.
392
+ # If it is not threadsafe, it may behave in surprising ways in examples
393
+ # that spawn separate threads. Specify this on `RSpec.configure`
394
+ #
395
+ # @param name [String,Symbol] used to define an accessor with an
396
+ # intention revealing name
397
+ # @param block defines the value to be returned by `subject` in examples
398
+ #
399
+ # @example
400
+ #
401
+ # describe CheckingAccount, "with $50" do
402
+ # subject { CheckingAccount.new(Money.new(50, :USD)) }
403
+ # it { is_expected.to have_a_balance_of(Money.new(50, :USD)) }
404
+ # it { is_expected.not_to be_overdrawn }
405
+ # end
406
+ #
407
+ # describe CheckingAccount, "with a non-zero starting balance" do
408
+ # subject(:account) { CheckingAccount.new(Money.new(50, :USD)) }
409
+ # it { is_expected.not_to be_overdrawn }
410
+ # it "has a balance equal to the starting balance" do
411
+ # account.balance.should eq(Money.new(50, :USD))
412
+ # end
413
+ # end
414
+ #
415
+ # @see MemoizedHelpers#should
416
+ # @see MemoizedHelpers#should_not
417
+ # @see MemoizedHelpers#is_expected
418
+ def subject(name=nil, &block)
419
+ if name
420
+ let(name, &block)
421
+ alias_method :subject, name
422
+
423
+ self::NamedSubjectPreventSuper.__send__(:define_method, name) do
424
+ raise NotImplementedError, "`super` in named subjects is not supported"
425
+ end
426
+ else
427
+ let(:subject, &block)
428
+ end
429
+ end
430
+
431
+ # Just like `subject`, except the block is invoked by an implicit
432
+ # `before` hook. This serves a dual purpose of setting up state and
433
+ # providing a memoized reference to that state.
434
+ #
435
+ # @example
436
+ #
437
+ # class Thing
438
+ # def self.count
439
+ # @count ||= 0
440
+ # end
441
+ #
442
+ # def self.count=(val)
443
+ # @count += val
444
+ # end
445
+ #
446
+ # def self.reset_count
447
+ # @count = 0
448
+ # end
449
+ #
450
+ # def initialize
451
+ # self.class.count += 1
452
+ # end
453
+ # end
454
+ #
455
+ # describe Thing do
456
+ # after(:example) { Thing.reset_count }
457
+ #
458
+ # context "using subject" do
459
+ # subject { Thing.new }
460
+ #
461
+ # it "is not invoked implicitly" do
462
+ # Thing.count.should eq(0)
463
+ # end
464
+ #
465
+ # it "can be invoked explicitly" do
466
+ # subject
467
+ # Thing.count.should eq(1)
468
+ # end
469
+ # end
470
+ #
471
+ # context "using subject!" do
472
+ # subject!(:thing) { Thing.new }
473
+ #
474
+ # it "is invoked implicitly" do
475
+ # Thing.count.should eq(1)
476
+ # end
477
+ #
478
+ # it "returns memoized version on first invocation" do
479
+ # subject
480
+ # Thing.count.should eq(1)
481
+ # end
482
+ # end
483
+ # end
484
+ def subject!(name=nil, &block)
485
+ subject(name, &block)
486
+ before { subject }
487
+ end
488
+ end
489
+
490
+ # @private
491
+ #
492
+ # Gets the LetDefinitions module. The module is mixed into
493
+ # the example group and is used to hold all let definitions.
494
+ # This is done so that the block passed to `let` can be
495
+ # forwarded directly on to `define_method`, so that all method
496
+ # constructs (including `super` and `return`) can be used in
497
+ # a `let` block.
498
+ #
499
+ # The memoization is provided by a method definition on the
500
+ # example group that supers to the LetDefinitions definition
501
+ # in order to get the value to memoize.
502
+ def self.module_for(example_group)
503
+ get_constant_or_yield(example_group, :LetDefinitions) do
504
+ mod = Module.new do
505
+ include(Module.new {
506
+ example_group.const_set(:NamedSubjectPreventSuper, self)
507
+ })
508
+ end
509
+
510
+ example_group.const_set(:LetDefinitions, mod)
511
+ mod
512
+ end
513
+ end
514
+
515
+ # @private
516
+ def self.define_helpers_on(example_group)
517
+ example_group.__send__(:include, module_for(example_group))
518
+ end
519
+
520
+ if Module.method(:const_defined?).arity == 1 # for 1.8
521
+ # @private
522
+ #
523
+ # Gets the named constant or yields.
524
+ # On 1.8, const_defined? / const_get do not take into
525
+ # account the inheritance hierarchy.
526
+ # :nocov:
527
+ def self.get_constant_or_yield(example_group, name)
528
+ if example_group.const_defined?(name)
529
+ example_group.const_get(name)
530
+ else
531
+ yield
532
+ end
533
+ end
534
+ # :nocov:
535
+ else
536
+ # @private
537
+ #
538
+ # Gets the named constant or yields.
539
+ # On 1.9, const_defined? / const_get take into account the
540
+ # the inheritance by default, and accept an argument to
541
+ # disable this behavior. It's important that we don't
542
+ # consider inheritance here; each example group level that
543
+ # uses a `let` should get its own `LetDefinitions` module.
544
+ def self.get_constant_or_yield(example_group, name)
545
+ if example_group.const_defined?(name, (check_ancestors = false))
546
+ example_group.const_get(name, check_ancestors)
547
+ else
548
+ yield
549
+ end
550
+ end
551
+ end
552
+ end
553
+ end
554
+ end