Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit a7be2af

Browse files
committed
Merge branch 'better-fix-for-let-definitions-eagerly-included'
2 parents 3efa3fd + 690a294 commit a7be2af

File tree

3 files changed

+47
-52
lines changed

3 files changed

+47
-52
lines changed

lib/rspec/core/example_group.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,13 @@ def self.subclass(parent, args, &example_group_block)
237237
subclass = Class.new(parent)
238238
subclass.set_it_up(*args)
239239
subclass.module_eval(&example_group_block) if example_group_block
240+
241+
# The LetDefinitions module must be included _after_ other modules
242+
# to ensure that it takes precendence when there are name collisions.
243+
# Thus, we delay including it until after the example group block
244+
# has been eval'd.
245+
MemoizedHelpers.define_helpers_on(subclass)
246+
240247
subclass
241248
end
242249

lib/rspec/core/memoized_helpers.rb

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,12 @@ module MemoizedHelpers
4545
#
4646
# @see #should
4747
def subject
48-
raise NotImplementedError, 'This definition is here for documentation purposes only'
49-
' - it is overriden anyway below when this module gets included.'
48+
__memoized.fetch(:subject) do
49+
__memoized[:subject] = begin
50+
described = described_class || self.class.description
51+
Class === described ? described.new : described
52+
end
53+
end
5054
end
5155

5256
# When `should` is called with no explicit receiver, the call is
@@ -148,12 +152,6 @@ def preserve_accessed_lets
148152

149153
def self.included(mod)
150154
mod.extend(ClassMethods)
151-
152-
# This logic defines an implicit subject
153-
mod.subject do
154-
described = described_class || self.class.description
155-
Class === described ? described.new : described
156-
end
157155
end
158156

159157
module ClassMethods
@@ -190,12 +188,16 @@ module ClassMethods
190188
# end
191189
# end
192190
def let(name, &block)
193-
MemoizedHelpers.define_memoized_method(
194-
self,
195-
name,
196-
MemoizedHelpers.memoized_method_name_for(name, 'let'),
197-
&block
198-
)
191+
# We have to pass the block directly to `define_method` to
192+
# allow it to use method constructs like `super` and `return`.
193+
raise "#let or #subject called without a block" if block.nil?
194+
MemoizedHelpers.module_for(self).send(:define_method, name, &block)
195+
196+
# Apply the memoization. The method has been defined in an ancestor
197+
# module so we can use `super` here to get the value.
198+
define_method(name) do
199+
__memoized.fetch(name) { |k| __memoized[k] = super(&nil) }
200+
end
199201
end
200202

201203
# Just like `let`, except the block is invoked by an implicit `before`
@@ -287,11 +289,10 @@ def let!(name, &block)
287289
# @see MemoizedHelpers#should
288290
def subject(name=nil, &block)
289291
if name
290-
subject_method_name = MemoizedHelpers.memoized_method_name_for(name, "subject")
291-
MemoizedHelpers.define_memoized_method(self, name, subject_method_name, &block)
292+
let(name, &block)
292293
alias_method :subject, name
293294

294-
self::NamedSubjectPreventSuper.__send__(:define_method, subject_method_name) do
295+
self::NamedSubjectPreventSuper.send(:define_method, name) do
295296
raise NotImplementedError, "`super` in named subjects is not supported"
296297
end
297298
else
@@ -468,30 +469,14 @@ def self.module_for(example_group)
468469
}
469470
end
470471

471-
example_group.__send__(:include, mod)
472472
example_group.const_set(:LetDefinitions, mod)
473473
mod
474474
end
475475
end
476476

477477
# @api private
478-
def self.memoized_method_name_for(name, let_or_subject)
479-
"__rspec_#{let_or_subject}_definition_#{name}"
480-
end
481-
482-
# @api private
483-
def self.define_memoized_method(example_group, name, memoized_method_name, &block)
484-
# We have to pass the block directly to `define_method` to
485-
# allow it to use method constructs like `super` and `return`.
486-
raise "#let or #subject called without a block" if block.nil?
487-
488-
MemoizedHelpers.module_for(example_group).__send__(:define_method, memoized_method_name, &block)
489-
490-
# Apply the memoization. The method has been defined in an ancestor
491-
# module so we can use `super` here to get the value.
492-
example_group.__send__(:define_method, name) do
493-
__memoized.fetch(name) { |k| __memoized[k] = send(memoized_method_name, &nil) }
494-
end
478+
def self.define_helpers_on(example_group)
479+
example_group.send(:include, module_for(example_group))
495480
end
496481

497482
if Module.method(:const_defined?).arity == 1 # for 1.8

lib/rspec/core/shared_context.rb

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,34 @@ module Core
1717
# # ...
1818
# end
1919
module SharedContext
20-
include Hooks
21-
include MemoizedHelpers::ClassMethods
22-
2320
def included(group)
24-
[:before, :after].each do |type|
25-
[:all, :each].each do |scope|
26-
group.hooks[type][scope].concat hooks[type][scope]
27-
end
28-
end
29-
_nested_group_declarations.each do |name, block, *args|
30-
group.describe name, *args, &block
21+
__shared_context_recordings.each do |recording|
22+
recording.playback_onto(group)
3123
end
3224
end
3325

34-
def describe(name, *args, &block)
35-
_nested_group_declarations << [name, block, *args]
26+
def __shared_context_recordings
27+
@__shared_context_recordings ||= []
3628
end
3729

38-
alias_method :context, :describe
39-
40-
private
30+
Recording = Struct.new(:method_name, :args, :block) do
31+
def playback_onto(group)
32+
group.__send__(method_name, *args, &block)
33+
end
34+
end
4135

42-
def _nested_group_declarations
43-
@_nested_group_declarations ||= []
36+
def self.record(*methods)
37+
methods.each do |meth|
38+
class_eval <<-EOS, __FILE__, __LINE__ + 1
39+
def #{meth}(*args, &block)
40+
__shared_context_recordings << Recording.new(:#{meth}, args, block)
41+
end
42+
EOS
43+
end
4444
end
45+
46+
record :describe, :context, *Hooks.instance_methods(false),
47+
*MemoizedHelpers::ClassMethods.instance_methods(false)
4548
end
4649
end
4750

0 commit comments

Comments
 (0)