If you aren’t already aware of what dependency injection (and containers) are then you should begin by reading my post What Is Dependency Injection?
A little over 6 months ago I took a full-time Ruby development job at CoverMyMeds My past has primarily been as a Microsoft stack developer, but I’ve dabbled with Ruby (and other languages) for side projects since around 2008. Going full-time with Ruby made me want to work much harder on making sure my Ruby code was idiomatic. I’ve learned some really cool things, but one thing that was somewhat challenging was giving up dependency injection. This post will cover the top 3 reasons I think dependency injection has diminished value in Ruby.
1: Isolated Testing Doesn’t Require Abstract Types In Ruby
In a static language like C#, you’ll typically want to depend on an interface so you can use stubbed or mocked implementations during testing.
In a dynamic language like Ruby, the interface isn’t needed.
Also, you can stub methods on objects even without injecting your own implementation. Here’s an example test using RSpec to setup expectations.
2: Simplified Initializers
One of the advantages of using a container for dependency injection in C# is that you can avoid using the ‘new’ keyword.
This means that the constructor signature can change without going through your code and changing every place that this class is being instantiated.
In Ruby however, if you’re avoiding the dependency injection pattern altogether, then you are much less likely to suffer from this problem.
Adding a dependency to a Ruby class doesn’t typically change the initializer.
This might seem super-obvious, but if you’re used to C# like me, you know what it’s like to be bitten by code like this when trying to isolate classes for testing. Remember though, that’s not a problem anymore because of #1!
Another nifty thing you might utilize would be hash initializers. This language feature is useful in some situations when you have a
3: Dependency Injection Without a Container
You don’t need a container to do dependency injection! If you have a case where you really want to pass dependencies in to the initializer, just do it.
The great thing is that you’re not preemptively doing this just for testing purposes.
You can use this when it fits into your design, and avoid it otherwise. And no need to define an interface. Yay dynamic typing!
If you want to hear me babble about this topic in a lightning talk at Steel City Ruby Conf, check out the video! This was a spontaneous talk so I didn’t cover everything I wanted, but I did my best!
As always, I’m curious for feedback.