Ruse
Ruse is a low friction dependency injection tool for ruby.
It was inspired by the injector in angular.js (and so, transitively, Guice).
Usage
Create an injector at the top level of your application:
injector = Ruse.create_injector
Retrieve instances via that injector:
command = injector.get :create_order_command
#=> #<CreateOrderCommand:0x00000105cbea70>
Example
Suppose you have a command that collaborates with a notifier service:
class CreateOrderCommand
def execute(customer, order_details)
save_order(order_details)
notifier.notify(customer)
end
def notifier
@notifier ||= Notifier.new
end
end
class Notifier
def notify(customer)
# send a notification to customer
end
end
That CreateOrderCommand class is now tightly coupled to the Notifier class.
It has to know how to construct the Notifier. You would have to change
CreateOrderCommand to use a different notifier service.
You can improve the class by using dependency injection:
class CreateOrderCommand
def initialize(notifier)
@notifier = notifier
end
def execute(customer, order_details)
save_order(order_details)
@notifier.notify(customer)
end
end
The CreateOrderCommand class is now open for extension, but closed for
modification. It can use a different notifier service, without any changes.
It also does not need to know how to construct a Notifier instance, which is
really important if Notifier has its own dependencies.
You have now passed the burden of creating and configuring the
CreateOrderCommand, the Notifier, and any of its dependencies on to the
caller. This is where an Inversion of Control/Dependency Injection tool becomes
valuable:
command = injector.get "CreateOrderCommand"
The tool did the tedious work of identifying and populating the dependencies, all the way down the object graph.
Instance Resolution
Dependencies are determined by the identifiers used in constructure parameters. This was lifted directly from angular.js, and I believe may be the key to reducing the overhead in using a tool like this. Your dependency consuming classes do not have to be annotated or registered in any way.
In this early alpha state, identifiers are resolved to types through simple
string manipulation (similiar to ActiveSupport's classify and constantize).
That means you can get an instance of SomeService by requesting
"SomeService", "some_service" or :some_service.
In the future, I can imagine a simple configuration mechanism that lets you
resolve an identifier to some other type, so "notifier" resolves to
EmailNotifier.
Installation
Add this line to your application's Gemfile:
gem 'ruse'
And then execute:
$ bundle
Or install it yourself as:
$ gem install ruse
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request