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

Commit 3efa3fd

Browse files
committed
Merge pull request #937 from alindeman/refactor_memoized_helpers
Refactor memoized helpers
2 parents bbc83ce + ec4056a commit 3efa3fd

File tree

2 files changed

+47
-12
lines changed

2 files changed

+47
-12
lines changed

lib/rspec/core/memoized_helpers.rb

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -190,16 +190,12 @@ module ClassMethods
190190
# end
191191
# end
192192
def let(name, &block)
193-
# We have to pass the block directly to `define_method` to
194-
# allow it to use method constructs like `super` and `return`.
195-
raise "#let or #subject called without a block" if block.nil?
196-
MemoizedHelpers.module_for(self).send(:define_method, name, &block)
197-
198-
# Apply the memoization. The method has been defined in an ancestor
199-
# module so we can use `super` here to get the value.
200-
define_method(name) do
201-
__memoized.fetch(name) { |k| __memoized[k] = super(&nil) }
202-
end
193+
MemoizedHelpers.define_memoized_method(
194+
self,
195+
name,
196+
MemoizedHelpers.memoized_method_name_for(name, 'let'),
197+
&block
198+
)
203199
end
204200

205201
# Just like `let`, except the block is invoked by an implicit `before`
@@ -291,10 +287,11 @@ def let!(name, &block)
291287
# @see MemoizedHelpers#should
292288
def subject(name=nil, &block)
293289
if name
294-
let(name, &block)
290+
subject_method_name = MemoizedHelpers.memoized_method_name_for(name, "subject")
291+
MemoizedHelpers.define_memoized_method(self, name, subject_method_name, &block)
295292
alias_method :subject, name
296293

297-
self::NamedSubjectPreventSuper.send(:define_method, name) do
294+
self::NamedSubjectPreventSuper.__send__(:define_method, subject_method_name) do
298295
raise NotImplementedError, "`super` in named subjects is not supported"
299296
end
300297
else
@@ -477,6 +474,26 @@ def self.module_for(example_group)
477474
end
478475
end
479476

477+
# @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
495+
end
496+
480497
if Module.method(:const_defined?).arity == 1 # for 1.8
481498
# @api private
482499
#

spec/rspec/core/memoized_helpers_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,24 @@ def define_and_run_group
616616
})
617617
end
618618
end
619+
620+
context "when included modules have hooks that define memoized helpers" do
621+
it "allows memoized helpers to override methods in previously included modules" do
622+
group = ExampleGroup.describe do
623+
include Module.new {
624+
def self.included(m); m.let(:unrelated) { :unrelated }; end
625+
}
626+
627+
include Module.new {
628+
def hello_message; "Hello from module"; end
629+
}
630+
631+
let(:hello_message) { "Hello from let" }
632+
end
633+
634+
expect(group.new.hello_message).to eq("Hello from let")
635+
end
636+
end
619637
end
620638

621639
describe "#let!" do

0 commit comments

Comments
 (0)