Forum: Ruby on Rails What is the Rails equivalent of Dependency Injection?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
schof (Guest)
on 2007-07-29 01:21
(Received via mailing list)
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
Robert W. (Guest)
on 2007-07-29 05:30
(Received via mailing list)
Roderick v. (Guest)
on 2007-07-29 15:08
schof wrote:
> 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?

I've never given it a whirl from within Rails, but Needle [1] is
probably be able to handle this.

[1] http://needle.rubyforge.org/

--
Roderick van Domburg
http://www.nedforce.nl
Craig McClanahan (Guest)
on 2007-07-30 10:25
(Received via mailing list)
On 7/29/07, Roderick van Domburg <removed_email_address@domain.invalid>
wrote:

> [1] http://needle.rubyforge.org/

Before you go look at Needle, or any other "dependency injection"
framework for Ruby, it's worth seeing what Jamis B. (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] http://weblog.jamisbuck.org/2007/7/29/net-ssh-revisited
Forrest C. (Guest)
on 2007-07-30 10:29
(Received via mailing list)
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
Ilan B. (Guest)
on 2007-07-30 20:07
schof wrote:

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

Change your line of thinking to: "Why was DI needed for full stack
containers written with statically binded languages?"

Because due to the fact that the only way to incorporate cross cutting
concerns into those containers that were written with statically binded
languages was to either:
- Modify the byte code directly: (AspectJ, JBoss)
- Register callbacks directly at defined points within the container via
constructor/setter/interface injection. (Spring, Pico, etc.. )
- Incorporate meta-level "aspects" into the code itself (EJB3.0,
Hibernate 3, etc...)

With Ruby, there is simply no need to this as either the container
and/or the 3rd party component can open of the entity it wants to modify
itself and "inject, modify, or remove" operations as required!

In fact, this concept is a core to the Ruby language itself.  If you
look at attr_reader, it's actually a method that "injects" code within
your class as it's being parsed by the Ruby runtime! (It's not a keyword
as many people with a Java background believe)

In summary, Ruby's dynamic binding doesn't suffer from the crutch of
statically binded languages and therefore doesn't need DI.  IMHO is one
of the major reasons why Rails is so successful in the first place.

hth

ilan
Jodi S. (Guest)
on 2007-07-30 20:12
(Received via mailing list)
Schof -

On 30-Jul-07, at 12:07 PM, Ilan B. wrote:

> containers written with statically binded languages?"
> Hibernate 3, etc...)
> as many people with a Java background believe)
>
> In summary, Ruby's dynamic binding doesn't suffer from the crutch of
> statically binded languages and therefore doesn't need DI.  IMHO is
> one
> of the major reasons why Rails is so successful in the first place.
>
> hth
>
> ilan


I know this isn't the answer to your question, but Jamis, who wrote
needle, just posted some thought on on DI while re-writing another
lib (net::ssh).

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

cheers,
Jodi
schof (Guest)
on 2007-07-31 06:26
(Received via mailing list)
> 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
Jeff E. (Guest)
on 2007-07-31 07:46
(Received via mailing list)
> 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-n...
Joe Francis (Guest)
on 2007-07-31 07:48
(Received via mailing list)
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
Conrad T. (Guest)
on 2007-07-31 07:54
(Received via mailing list)
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
nachokb (Guest)
on 2007-07-31 17:37
(Received via mailing list)
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
This topic is locked and can not be replied to.