module RSpec::Core::MemoizedHelpers::ClassMethods
def let(name, &block)
- Note: - Because `let` is designed to create state that is reset between
Note: - `let` can be configured to be threadsafe or not.
Note: - `let` _can_ enhance readability when used sparingly (1,2, or
def let(name, &block) # We have to pass the block directly to `define_method` to # allow it to use method constructs like `super` and `return`. raise "#let or #subject called without a block" if block.nil? # A list of reserved words that can't be used as a name for a memoized helper # Matches for both symbols and passed strings if [:initialize, :to_s].include?(name.to_sym) raise ArgumentError, "#let or #subject called with reserved name `#{name}`" end our_module = MemoizedHelpers.module_for(self) # If we have a module clash in our helper module # then we need to remove it to prevent a warning. # # Note we do not check ancestor modules (see: `instance_methods(false)`) # as we can override them. if our_module.instance_methods(false).include?(name) our_module.__send__(:remove_method, name) end our_module.__send__(:define_method, name, &block) # If we have a module clash in the example module # then we need to remove it to prevent a warning. # # Note we do not check ancestor modules (see: `instance_methods(false)`) # as we can override them. if instance_methods(false).include?(name) remove_method(name) end # Apply the memoization. The method has been defined in an ancestor # module so we can use `super` here to get the value. if block.arity == 1 define_method(name) { __memoized.fetch_or_store(name) { super(RSpec.current_example, &nil) } } else define_method(name) { __memoized.fetch_or_store(name) { super(&nil) } } end end