module Interjectable::ClassMethods

Constants

BLANK
RESTORE_HOOKS

Public Class Methods

test_inject(rspec_example_group, target, dependency, value, &setter) click to toggle source
# File lib/interjectable/rspec.rb, line 80
def self.test_inject(rspec_example_group, target, dependency, value, &setter)
  unless value || setter
    raise ArgumentError, "missing value or setter for #{target}'s #{dependency.inspect}"
  end

  unless rspec_example_group < RSpec::Core::ExampleGroup
    raise "#test_inject can only be called from an RSpec ExampleGroup (e.g.: it, before, after)"
  end

  injector = if target.singleton_methods(false).include?(dependency) # inject_static(dependency) on this class
    InjectStatic.new(target, dependency)
  elsif target.singleton_methods.include?(dependency) # inject_static(dependency) on a superclass of this class
    SuperclassInjectStatic.new(target, dependency)
  elsif target.instance_methods(false).include?(dependency) # inject(dependency) on this class
    Inject.new(target, dependency)
  elsif target.instance_methods.include?(dependency) # inject(dependency) on a superclass of this class
    SuperclassInject.new(target, dependency)
  else
    raise ArgumentError, "tried to override a non-existent dependency: #{dependency.inspect}"
  end


  injector.override(value, &setter)

  scope = rspec_example_group.currently_executing_a_context_hook? ? :context : :each

  key = [target, dependency, scope]
  # If we already have a restore after(:each) hook for this class +
  # dependency + scope, don't add another. To check if we already have an
  # after(:each) hook, we look at all previous after(:each) hooks we've
  # registered and see if we are currently in a subclass (i.e. we are
  # nested within) of any of them.
  #
  # We don't need to guard against multiple after(:context / :all) hooks
  # for the same #test_inject call since those before hooks only run once,
  # and therefore only setup a single after hook.
  return if scope == :each && RESTORE_HOOKS[key].any? { |group| rspec_example_group <= group }
  RESTORE_HOOKS[key] << rspec_example_group

  rspec_example_group.after(scope) do
    injector.restore
  end
end

Public Instance Methods

inject(dependency, &default_block) click to toggle source

Defines a helper methods on instances that memoize values per-instance.

Calling a second time is an error. Use `#test_inject` for overriding in RSpec tests. You need to `require “interjectable/rspec”` to use `#test_inject`. See the README.md.

Similar to writing

attr_writer :dependency

def dependency
  @dependency ||= instance_eval(&default_block)
end
# File lib/interjectable.rb, line 30
def inject(dependency, &default_block)
  if instance_methods(false).include?(dependency)
    raise MethodAlreadyDefined, "#{dependency} is already defined"
  end

  attr_writer dependency

  define_method(dependency) do
    ivar_name = :"@#{dependency}"
    if instance_variable_defined?(ivar_name)
      instance_variable_get(ivar_name)
    else
      instance_variable_set(ivar_name, instance_eval(&default_block))
    end
  end
end
inject_static(dependency, &default_block) click to toggle source

Defines helper methods on instances that memoize values per-class. (shared across all instances of a class, including instances of subclasses).

Calling a second time is an error. Use `#test_inject` for overriding in RSpec tests. You need to `require “interjectable/rspec”` to use `#test_inject`. See the README.md.

Similar to writing

cattr_writer :dependency

def dependency
  @@dependency ||= instance_eval(&default_block)
end
# File lib/interjectable.rb, line 62
def inject_static(dependency, &default_block)
  if instance_methods(false).include?(dependency) || methods(false).include?(dependency)
    raise MethodAlreadyDefined, "#{dependency} is already defined"
  end

  cvar_name = :"@@#{dependency}"
  setter = :"#{dependency}="

  define_method(setter) do |value|
    self.class.send(setter, value)
  end

  define_singleton_method(setter) do |value|
    class_variable_set(cvar_name, value)
  end

  define_method(dependency) do
    self.class.send(dependency)
  end

  define_singleton_method(dependency) do
    if class_variable_defined?(cvar_name)
      class_variable_get(cvar_name)
    else
      class_variable_set(cvar_name, instance_eval(&default_block))
    end
  end
end
test_inject(dependency, &setter) click to toggle source
# File lib/interjectable/rspec.rb, line 71
def test_inject(dependency, &setter)
  unless setter
    raise ArgumentError, "missing setter #{dependency.inspect}, correct usage: #test_inject(#{dependency.inspect}) { FakeDependency.new }"
  end
  rspec_example_group = setter.binding.receiver.class

  ClassMethods.test_inject(rspec_example_group, self, dependency, BLANK, &setter)
end