What is the Rails equivalent of Dependency Injection?

Coming from a Java background I'm trying to get used to the various best practices of doing things in Rails. I'm used to Dependency Injection and XML in the Java world.

I realize Ruby does not have interfaces and Rails avoids the use of XML. So how does one solve certain problems that were once solved by DI and XML? Example, if you have a shopping cart that uses Active Merchant which comes with all sorts of payment gateways. How would the user of the shopping cart specify the gateway they wanted? I'm thinking there are some interesting possibilities with Ruby's dynamic typing. Like maybe a YAML config file listing the gateway you are using (with other options not chose commented out)?

Does anyone have any thoughts on how this sort of problem is best addressed in Rails?

Sean

Maybe this will help:

http://onestepback.org/articles/depinj/index.html

Before you go look at Needle, or any other "dependency injection" framework for Ruby, it's worth seeing what Jamis Buck (original author of Needle) has to say now about it now[1].

In Java, DI was all about not caring what the actual implementation class was. In Ruby, however, we don't need to worry about that ... a class either responds to a method call or it doesn't. In Ruby, why should you bother to care what the underlying type is? Or, care about whether or not it already has predefined methods for all your config parameters? Or, whether the class actually has any notion of "properties" at all?

Imagine what "routes.rb" would look like if we had to do it in Java ...

Craig

[1] Buckblog: Net::SSH revisited

It's also worth noting that Jamis is on the Rails Core team, and they *don't* use needle. I remember reading DHH saying that he (they?) considered using Needle, but didn't end up needing it. Anyone remember where that was?

Forrest

Schof -

In Java, DI was all about not caring what the actual implementation class was. In Ruby, however, we don't need to worry about that ... a class either responds to a method call or it doesn't. In Ruby, why should you bother to care what the underlying type is? Or, care about whether or not it already has predefined methods for all your config parameters? Or, whether the class actually has any notion of "properties" at all?

I realize that we don't really need true DI anymore in Rails. Lets go back to my scenario where you want to plug in one of many possible payment gateways from Active Merchant. What is the best way to configure my Rails app so another developer can easily substitute in the gateway of their choice? I understand that it only matters that the object have the right methods but how do you pick the object?

The most obvious solution is to just change the code but surely there is a better way to do this? Instead of searching and replacing how does one configure something like this in Rails?

Craig

Sean

What is the best way to configure my Rails app so another developer can easily substitute in the gateway of their choice?

http://softiesonrails.com/2007/3/13/ruby-101-for-net-developers-use-blocks-to-help-you-refactor

apologies if this has already been posted to this thread, but this post is an interesting insight from the author of Needle - one of the main ruby DI libraries

http://weblog.jamisbuck.org/2007/7/29/net-ssh-revisited

jemminger wrote:

Hey Sean, if the type of gateway is to be selected from UI, then you’ll can use Factory Method design pattern to create the appropriate instance based on user’s selection. Another possibility would be to use a pluggable adapter or Adapter design pattern. Thus, you should be able to do the following:

a) create a common interface (i.e. GateWay) for all your gateways and subclass it to create new gateways.

b) each subclass of GateWay would be an adpater for communicating to the underlying gateway.

c) create a factory method for creating the appropriate gateway based on developer or user

selection.

d) create the appropriate gateway instance

gateway_a = GateWayFactory.create( “A” ) # create gateway A

gateway_b = GateWayFactory.create( “B” ) # create gateway B

Good luck,

-Conrad

Usually in Ruby & Rails there's an application-specific configuration file (not XML, but Ruby). A little improvisation:

# config.rb ACTIVE_MERCHANT_GATEWAYS = [ SomeKindOfGateway, SomeOtherKindOfGateway ]

# view, Settings page or something # I don't remember, but something like this: select_tag :current_gateway, options_for_select(ACTIVE_MERCHANT_GATEWAYS.map do |gw| [gw.name, gw.name] end

# controller, def set_current_gateway   gateway = params[:current_gateway].constantize # this variable HOLDS THE CLASS ITSELF   GatewayBase.current = gateway end

This is really untested and I don't think I would implement it this way (I don't have enough information to decide that), but let me explain:

* Classes are just objects. You can store them in an Array. * Their references are not necessarily the class name. They are just constant (they are capitalized).     - That's why if you do CurrentGateway.name, you get "SomeKindOfGateway" for example. * Think of "Constantize" as Class.forName() (BUT IT'S NOT, Java doesn't have real reflection). The name is confusing (at first I thought it was yet another inflection, just as e.g. :dasherize). * So, what I do is have a central configuration Ruby file (as per convention). Being Ruby, you can reference objects (not only its name). Loading the array with the classes themselves (instead of , say, their names, is useful because you get an error on initialization if it can't find it).    - That array is accesible anywhere in the application (though I wouldn't reference that global from a view). * When you say SomeClass.new, you are sending the message :new (not an operator, but just another message, just like an_array.length). SomeClass can reference any class you like, not only a "SomeClass" class. It's a constant. You could have used a variable...    - If you have any central class (a Base class for all your gateways or something like that), make that know which is the current gateway (I've called it GatewayBase, with its :current= method).

I'm sorry if I'm being confusing. I think the most important thing I want to say is that a class reference is not coupled with a specific implementation, it's just another constant, so it can reference to any other. Normally we configure it statically, so we could use a Constant name for holding the class (and then any Constant.new would do). But if, instead of GatewayBase.current you used e.g. CurrentGateway you could only set it once at startup (and you want that to be changes in runtime).

Bye,

nachokb