Module: Interjectable::ClassMethods

Defined in:
lib/interjectable.rb,
lib/interjectable/rspec.rb

Constant Summary collapse

BLANK =
Object.new

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.test_inject(rspec_example_group, target, dependency, value, &setter) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# 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

Instance Method Details

#inject(dependency, &default_block) ⇒ Object

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


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 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) ⇒ Object

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


62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# 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) ⇒ Object



71
72
73
74
75
76
77
78
# 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