Injectable is an extremely simple dependency injection framework for Ruby. It's stupid simple and experimental so don't expect support for now - but may turn out to be something in the future.
Say we have a
UserService that has some basic logic for performing operations
related to a
User and a
FacebookService. We can tell the
its dependencies are via
Injectable. Note that as of
0.0.2 all objects that
can be injected into others must include the
class User include Injectable end class FacebookService include Injectable end class UserService include Injectable dependencies :user, :facebook_service end
UserService would then get a constructor defined that takes a
FacebookService as its arguments:
user = User.new facebook_service = FacebookService.new user_service = UserService.new(user, facebook_service)
Reader methods are also automatically provided for each dependency:
Say we just want to throw a bunch of objects in a container, and ask for an object of a specific type and let the container figure out the dependencies:
user = User.new facebook_service = FacebookService.new container = Injectable::Container.new(user, facebook_service) user_service = container.get(:user_service)
Objects can also be added to the container post instantiation.
user = User.new facebook_service = FacebookService.new container = Injectable::Container.new container.put(user, facebook_service) user_service = container.get(:user_service)
FacebookService take no arguments, we don't even need to
pass them into the container - it will automatically instantiate new ones:
container = Injectable::Container.new user = container.get(:user) user_service = container.get(:user_service)
Injectable also supports depending on roles rather than concrete classes by allowing the registration of classes whose instances perform that role. An implementation can be registered on a single container itself as a "one off":
container = Injectable::Container.new container.register_implementation(:facebook_service, DifferentFacebookService) user_service = container.get(:user_service) # `user_service`'s facebook_service will be an instance of DifferentFacebookService
You can also define concrete implementations at the global level via
Injectable.configure do |config| config.register_implementation(:facebook_service, DifferentFacebookService) end container = Injectable::Container.new user_service = container.get(:user_service) # `user_service`'s facebook_service will be an instance of DifferentFacebookService
Multiple implementations for the same role can be configured - the rules on how they will be used are as follows: Check for an already instantiated instance in the container. If one or both exist, take the first. If none exist, then attempt to resolve the dependency with each registered role until one succeeds. An example:
module Portable def charge # Some logic here. end end class Phone include Injectable include Portable end class Tablet include Injectable include Portable end class Application include Injectable dependencies :portable end Injectable.configure do |config| config.register_implementation(:portable, Phone, Tablet) end container = Injectable::Container.new application = container.get(:application) application.portable #=> Returns a new Phone. tablet = Tablet.new container = Injectable::Container.new(tablet) application = container.get(:application) application.portable #=> Returns the existing instance of a Tablet.
Setter injection is not supported.
How about the real world
Let's look at the above classes, but say we're in a Rails application:
class User < ActiveRecord::Base include Injectable end class FacebookService include Injectable def post_to_wall(id, message) # ... end end class UserService include Injectable dependencies :user, :facebook_service def post_to_wall(message) facebook_service.post_to_wall(user.id, message) end end
Now in our controller, we can just create a new container the user we find and then ask the container to do all the other work. This is more closely related to what one might do in a real application.
class UsersController < ApplicationController def post_to_wall container.get(:user_service).post_to_wall(params[:message]) respond_with container.get(:user) end private def container @container ||= Injectable::Container.new(User.find(params[:id])) end end
Copyright (c) 2012-2013 Durran Jordan
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.