0.0
No commit activity in last 3 years
No release in over 3 years
Utility for automatic dependency injection with support for aliases
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 1.13
~> 11.0
~> 3.0

Runtime

>= 1.0.0
 Project Readme

carb-inject

carb-inject is an utility library for automated dependency injection. Together with a generic container (even a simple Hash!), it will cover all the needs for an IoC container. Check out carb-container for more advanced ones.

Installation

Add this line to your application's Gemfile:

gem 'carb-inject'

And then execute:

$ bundle

Or install it yourself as:

$ gem install carb-inject

Glossary

Term Meaning
Dependency The actual Object a dependency is (a number for example). Can be extracted from the container with container[dependency_name]
Dependency name An object which allows extracting a Dependency from the container
Dependency alias A symbol representing Dependency name, must be a valid method name
Array of dependency names An array of Dependency name. When passed to the injector, every object must support to_s and the returned String must be a valid method name
Hash of dependency aliases (or hash of aliases) A hash consisting of Dependency alias => Dependency name

Usage

First you'll need a container object. carb-container provides a simple RegistryContainer which you can use. Alternatively, you can use a simple ruby hashmap, or use carb-inject in an entirely containerless fashion

container = { name: "john", age: 30 }

Create an injector that you'll use across your application. Usually you want to put this in a constant

require "carb-inject"
Inject = Carb::Inject::Injector.new(container)

Then, create a class you want dependencies injected automatically

class JohnPerson
  include Inject[:name, :age]

  def initialize(**deps)
    inject_dependencies!(deps)
  end

  def hello
    "Hello I'm #{name}, #{age} years old"
  end
end

And finally, use the class!

john = JohnPerson.new
john.hello # => Hello I'm john, 30 years old

You can overwrite dependencies on the fly

john = JohnPerson.new(age: 20)
john.hello # => Hello I'm john, 20 years old

You can still require different arguments in the constructor

class JohnPerson
  include Inject[:name, :age]

  def initialize(last_name, **dependencies)
    inject_dependencies!(dependencies)
    @last_name = last_name
  end

  def hello
    "Hello I'm #{name} #{@last_name}, #{age} years old"
  end
end

john = JohnPerson.new("snow", age: 20)
john.hello # => Hello I'm john snow, 20 years old

Finally, you can alias dependencies

class JohnPerson
  include Inject[special_name: :name, a_number: :age]

  def initialize(**deps)
    inject_dependencies!(deps)
  end

  def hello
    "special_name is #{special_name}, a_number is #{a_number}"
  end
end

john = JohnPerson.new(a_number: 20)
john.hello # => special_name is john, a_number is 20

Be aware, you can't pass on-the-fly dependencies that were not defined on that class. If you do, you must be the one taking care of them!

class JohnPerson
  include Inject[:name]

  def initialize(**deps)
    inject_dependencies!(deps)
  end

  def hello
    "Hello I'm #{name}, #{age} years old"
  end
end

john = JohnPerson.new(age: 20)
john.hello # => NameError: undefined local variable or method `age'

Auto invoke inject_dependencies!

Instead of manually calling inject_dependencies!, you can invoke the injector with true as second argument. This has the downsides of including a module which creates an initializer, with all the consequences it creates (some issues with inheritance). It's not recommended, but if you don't use inheritance, it does the trick.

require "carb-inject"

container = { name: "john", age: 30 }
Inject = Carb::Inject::Injector.new(container, true)

class JohnPerson
  include Inject[:name, :age]

  def hello
    "Hello I'm #{name}, #{age} years old"
  end
end

john = JohnPerson.new
john.hello # => Hello I'm john, 30 years old

Passing lambdas

There is an alternative way to use the library in a containerless fashion. You will pass a list of dependency as usual, but instead of aliasing them, pass a lambda and it will be resolved when used

require "carb-inject"

container = { name: "John", last_name: "Snow" }
Inject = Carb::Inject::Injector.new(container, true)

class JohnPerson
  include Inject[:last_name, foo: :name, age: -> { 30 }]

  def hello
    "Hello I'm #{foo} #{last_name}, #{age} years old"
  end
end

john = JohnPerson.new
john.hello # => Hello I'm John Snow, 30 years old

Gotchas

  • Alias hash must have symbols as keys
  • Straight dependency names, when used in array and not as values for alias hash, must support to_s and the resulting String must be a valid method name (an exception is raised otherwise)

Features

  • Supports inheritance
  • Can write your own injector if you don't like the syntax of the existing one
  • Can alias dependencies
  • Supports any container which responds to [] and has_key?
  • Can write your own initializer with your own arguments

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/Carburetor/carb-inject.