Inspired by the Stop hating Java post by my friend Andrzej I started to identify the pros and cons of a pattern called Dependency Injection in the Ruby/Rails world. Before I explain the pattern, let me discuss the problem – a dependency.
What is a Dependency?
Let’s say we’re working in a casual Rails controller instance. Whenever we access an object (or class) instance different to the current controller instance (i.e. self), we’re creating a dependency. Dependency here means the controller needs to know class and method names in another domain. Here are some typical dependencies found in many controller actions.
def show @comments = Comment.find(:all)
Here, the action knows both where and how to find the comments. It’s a dependency to the model layer.
def update # ... logger.notice "Comment #{@text} updated."
In this example, we have a strong dependency to the logger component – looking into the Rails code we can see that the #logger method in turn creates a logger instance, so we have to know about instantiation and usage of the logger.
%h1 Welcome, #{current_user.name}This partial uses a helper method #current_user, which in turn queries controller and request in order to find the current user instance.
What’s the Problem with Dependencies?
Now, the examples above are code you’re gonna find in almost any Rails application – and there’s nothing wrong with that! However, problems might appear as soon as you want to test components separately.
For instance, what if I’d like to test my logging code within the controller, like asserting that the correct message is logged when something special happens?
it "logs correctly" do put :update assert_equal "Comment Yo! updated.", @controller.logger.last_notice
I’d have to mock the logger instance to make it “testable”, here, to provide the #last_notice method.
Let’s say the original #logger method looks like this.
def logger @logger ||= Rails::SmokeSignalLogger.new end
To mock the logger, people overwrite the respective method in the test case.
ActionController.class_eval do def logger @logger ||= Test::MockedLogger.new end
Code like this changes the logger for the entire test suite and might break other tests. A common solution is to reset the original method after each test. In other words, we change a global property for a local test – and this is wrong.
Another Problem: Configurability
In addition to the testing problems we get with dependencies, what if my colleagues love the way I program and plan to use one of my modules in their software (highly improbable). However, they don’t want the SmokeSignalLogger since they’re not speaking Indian, but they want to use another logging layer JungleDrumsLogger?
As soon as my component is imported in their application they’d probably monkey-patch my code to “configure” it.
NicksControllerMethods.class_eval do def logger @logger ||= JungleDrumsLogger.new(:flavor => :coconut) end
I personally consider monkey-patching a code smell.
What’s a component?
Ok. I showed a couple of examples about dependencies and I used the word component a lot. The reason for this is: The Dependency Injection pattern is only applicable in software systems consisting of separated components. The idea is that outer components inject dependencies into smaller components.
A component as it might be a piece of software like a controller instance, an ActiveRecord row instance or a logger object. I see three attributes to make something a component.
- First, a component has a limited scope of interest. It’s field of work is bounded to some special duty (logging messages, rendering a comments box, …).
- Second, a component instance cannot access properties of other components, unless you’re providing a way to do so. For example, the logger may not access variables of the controller and vice-versa.
- Last, components are reusable and might be used in different systems in a way that the using system does not have to change code on the imported component to make it work. I found this in Martin Fowler’s excellent Inversion of Control post.
Dependency Injections in Rails
To get back to our examples, let’s see how we could apply DI to get rid of the logger dependency. Don’t let the controller create it itself, but do that on the outside and pass – “inject” – the object into the controller.
One flavor of DI is called Constructor Injection and would imply we inject the logger in the constructor.
class ActionController < ... def initialize(..., logger) @logger = logger end
Obviously, the outer framework would have to take care of creating and passing the logger.
logger = BushDrumsLogger.new controller = ActionController.new(..., logger)
A second form is called Setter Injection where the controller exposes a writer for the injected attribute.
class ActionController < ... attr_writer :logger
Both the using frameworks or the test case can set the logger without overriding any code at all.
controller.logger = TubeMailLogger.new
Cells and DI
One huge problem I see with Rails and DI is: currently, there are not enough components. We got one monolithic ActionController instance, a couple of row models and a global view instance – that’s it. This doesn’t make Rails a bad thing, or thousands of well-running apps out there!
However, the missing components in Rails make it hard to write reusable software and test these in isolation. That’s why we got the Cells gem – it provides reusable view components for Rails and goes perfectly together with DI.
Say we have a sidebar widget that displays the recent comments from your blog. A cell encapsulates that part of the page, the data aggregation and the rendering (read this post if you need a quick introduction to Cells).
Injecting the logger, ouch!
If we needed a special logging mechanism for the widget we could pass it into the component. I don’t know why a widget displaying comments would need to log, but let’s assume it.
%h1 My great blog #sidebar = render_cell(:sidebar, :comments, logger)
The cell could then use the external logger without creating a dependency.
class SidebarCell < Cell::Base def comments(logger) logger.notice "Displaying comments at #{Time.now}!" render end
The additional arguments from the #render_cell invocation are directly passed as method arguments – this is what we call state-args in Cells.
It’s obvious that another project using that sidebar component could inject a completely different logger instance.
Testing with DI
The limited scope of a cell makes it pretty easy to test. Not only can we test that object-oriented “partial” in complete isolation but also can we pass in a mocked logger easily.
it "should render beautifully" do render_cell(:sidebar, :comments, mock_logger). should have_selector("ul") end it "should log correctly" do logger = ArrayLogger.new render_cell(:sidebar, :comments, logger) logger.last.should match /Logging/ end
No need to change or reset any global here, just throw-away instances and we’re done!
Conclusion
I can’t think of a cleaner solution for dissolving dependencies like these. The DI pattern makes it easy to keep your components dumb. The problem is that you first have to identify your dependencies, then refactor to components and inject instances from the controller (or wherever else).
Especially in Rails, Cells help to build a real MVC application with a data-aggregating ActionController (I call this FrontController) and fine-grained view components that get additional dependencies from the outside.





















