module RSpec module Core module Let module ClassMethods # Generates a method whose return value is memoized # after the first call. # # == Examples # # describe Thing do # let(:thing) { Thing.new } # # it "does something" do # # first invocation, executes block, memoizes and returns result # thing.do_something # # # second invocation, returns the memoized value # thing.should be_something # end # end def let(name, &block) define_method(name) do __memoized.fetch(name) {|k| __memoized[k] = instance_eval(&block) } end end # Just like let(), except the block is invoked # by an implicit before hook. This serves a dual # purpose of setting up state and providing a memoized # reference to that state. # # == Examples # # class Thing # def self.count # @count ||= 0 # end # # def self.count=(val) # @count += val # end # # def self.reset_count # @count = 0 # end # # def initialize # self.class.count += 1 # end # end # # describe Thing do # after(:each) { Thing.reset_count } # # context "using let" do # let(:thing) { Thing.new } # # it "is not invoked implicitly" do # Thing.count.should eq(0) # end # # it "can be invoked explicitly" do # thing # Thing.count.should eq(1) # end # end # # context "using let!" do # let!(:thing) { Thing.new } # # it "is invoked implicitly" do # Thing.count.should eq(1) # end # # it "returns memoized version on first invocation" do # thing # Thing.count.should eq(1) # end # end # end def let!(name, &block) let(name, &block) before { __send__(name) } end end module InstanceMethods def __memoized @__memoized ||= {} end end def self.included(mod) mod.extend ClassMethods mod.__send__ :include, InstanceMethods end end end end