Dependencies

Dependencies are "things your application needs". When you call a method with an argument then you are supplying a dependency. Raindeer provides 2 main ways to manage dependencies; Providers for global dependencies and Plugs for local dependencies.

Providers

Providers are "global" dependencies that can be setup once at boot time and injected anywhere; such as a logger.

Defining

Provide the dependency with:

Providers.define(:logger) do
  Logger.new
end

Namespaced string keys are fine too:

Providers.define('billing.payment_provider') do
  PaymentProvider.new
end

Eager load a provider by adding an eager: true keyword argument:

Providers.define(:logger, eager: true) do
  Logger.new # Initialised immediately, not when the dependency is requested.
end

Injecting

Warning

Work in progress. These APIs will have bugs, which you're welcome to submit a PR for.

Providers Hash

logger = Providers[:my_provider]

As a default argument (leave off the argument when initialising):

class MyClass
  def initialize(logger: Providers[:my_provider])
    @logger = logger # => "logger" is injected.
  end
end

Dependency Expressions

Warning

Dependency Expressions require types to be enabled.

Place a Dependency expression as the default value of your dependency:

class MyClass
  def initialize(logger: Dependency)
    @logger = logger # => "logger" is injected.
  end
end

To define a provider with a different name to that of the local variable do:

def initialize(dependency_one: Dependency | :provider_one)
  dependency_one # => Dependency injected from :provider_one.
end

Constructor Include

Or you may like to use the more traditional include syntax:

class MyClass
  include Dependencies[:logger]

  def my_method
    @logger # => "@logger" is injected.
  end
end

This method hides and creates the constructor on your behalf.

Plugs

Tip

Plugs will also be the way to use dependencies inside Ractors in future. It needs just a bit more work to be thread-safe.

Plugs are "local" dependencies, defined globally but can be instantiated anywhere. Each time you call Plug[] it insantiates a new object, so you can use them inside parallelized code and move them around from method to method.

Defining plugs

class MyPlugs
  include Plugs

  plug(:html) do
    plug(:node) do
      require_relative '../nodes/html_node'
      HTMLNode
    end
  end

  plug(:form) do
    plug(:lexeme) do
      require_relative '../lexemes/form_lexeme'
      FormLexeme
    end

    plug(:node) do
      require_relative '../nodes/form_node'
      FormNode
    end
  end
end

Getting plugs

# Get all "html" and "form" plugs and their children.
def new(plugs: MyPlugs[:html, :form])
  plugs.to_a # => [HTMLNode, FormLexeme, FormNode].
end

# Get all "node" plugs regardless of their parent.
def new(plugs: MyPlugs[:node])
  plugs.to_a # => [HTMLNode, FormNode]
end

See: https://github.com/raindeer-rb/plugs