class Injector

Public Class Methods

new(**params) click to toggle source
# File lib/injector.rb, line 7
def initialize(**params)
  @rules = {}
  @injected = {injector: self}
  @callbacks = {}
  params.each do |k, v|
    set(k, v)
  end
end

Public Instance Methods

[](key, overrides = {})
Alias for: get
[]=(key, value)
Alias for: set
get(key, overrides = {}) click to toggle source
# File lib/injector.rb, line 16
def get(key, overrides = {})
  # (lazily) evaluate rule, then store it
  @injected[key] ||= begin
    arr = @rules[key]
    if arr.nil?
      raise RuleNotFound,
        "No rule for #{key.inspect}. Available fields: #{@rules.keys.map(&:inspect).join(', ')}"
    end
    result = nil
    arr.to_a.reverse.each do |rule|
      rule.value.tap do |x|
        # if it's a callable value, call that and return its result, else return itself
        result = if x.is_a?(Proc) or x.is_a?(Method)
          inject(overrides, &x)
        else
          x
        end
      end
      unless result.nil?
        fire(:after_fetch, key, result, rule.identifier)
        break
      end
    end
    result
  end
end
Also aliased as: []
inject(overrides = {}, &block) click to toggle source
# File lib/injector.rb, line 43
def inject(overrides = {}, &block)
  params = []
  keys = {}
  block.parameters.each_with_index do |(type, value), index|
    raise(
      ArgumentError,
      [
        "splash operator arguments are not permitted when injecting.",
        ":#{value} in #{ block.source_location }"
      ].join(" ")
    ) if type == :rest or type == :keyrest

    # fetch by index first! e.g. inject(0 => 40) { |x| x + 2 }
    result = overrides.fetch(index) do
      # then fetch by value
      overrides.fetch(value) do
        # Unfortunately, optional arguments are not possible, as
        # a Ruby block passed to `inject` will have exclusively optional arguments.
        # Instead, key arguments introduced in Ruby 2.0 should be used.
        if type == :key
          begin self.get(value)
          rescue RuleNotFound
          end
        else
          self.get(value)
        end
      end
    end
    if type == :key
      keys[value] = result
    else
      params << result
    end
  end

  if keys.empty?
    block.call(*params)
  else
    block.call(*params, keys)
  end
end
insert(key, identifier = nil, block, **options) click to toggle source
# File lib/injector.rb, line 96
def insert(key, identifier = nil, block, **options)
  insert_rule(Inject::Rule.new(key, identifier, block, **options))
end
Also aliased as: provide
insert_rule(rule) click to toggle source
# File lib/injector.rb, line 105
def insert_rule(rule)
  (@rules[rule.key] ||= PQueue.new) << rule
  define_singleton_method(rule.key) { get(rule.key) }
end
insert_rules(rules) click to toggle source
# File lib/injector.rb, line 101
def insert_rules(rules)
  rules.each(&method(:insert_rule))
end
invalidate(key, identifier = :all) click to toggle source
# File lib/injector.rb, line 110
def invalidate(key, identifier = :all)
  if queue = @rules[key]
    new_queue = case identifier
    when :all then []
    else
      queue.to_a.delete_if do |rule|
        rule.identifier == identifier
      end
    end
    queue.replace(new_queue)
    true
  end
end
provide(key, identifier = nil, block, **options)
Alias for: insert
set(key, value) click to toggle source
# File lib/injector.rb, line 85
def set(key, value)
  insert(key, :setter, value, before: :all)
end
Also aliased as: []=
when_fetched(key = nil, &block) click to toggle source
# File lib/injector.rb, line 92
def when_fetched(key = nil, &block)
  add_callback(:after_fetch, key, &block)
end

Private Instance Methods

add_callback(event, key, &block) click to toggle source
# File lib/injector.rb, line 125
def add_callback(event, key, &block)
  key ||= block.parameters.first.last
  ((@callbacks[event] ||= {})[key] ||= []) << block
end
fire(event, key, value, identifier) click to toggle source
# File lib/injector.rb, line 130
def fire(event, key, value, identifier)
  @callbacks.fetch(event, {}).fetch(key, []).each do |block|
    inject(method: identifier, 0 => value, &block)
  end
end
method_missing(method, *args) click to toggle source
Calls superclass method
# File lib/injector.rb, line 136
def method_missing(method, *args)
  super unless method.to_s.end_with? "="
  self.set(method[0...-1].to_sym, *args)
end