[ANN] My dependency-injection library

OK. So Ruby has enough dependency-injection/inversion-of-control
libraries as it is, but mine is quite different so I figured I’d post
it. It’s about as simple as dependency-injection can possibly become
and there’s no support for lifecycles, interception, or other framework
stuff. Nor is there any support for parents, children, or other
relatives, so keep it to yourself.

Anyway, here’s the code (as released under Ruby’s license, so go ahead
and use it in whatever manner you see fit):

injector.rb:

···

------------

# contents: Dependency injection made simple.
#
# Copyright © 2005 Nikolai Weibull <nikolai@bitwi.se>

module Injector
  Dependencies = {}

  def needs(path)
    const_set path.sub(%r<.*/>, "").capitalize,
      if Dependencies.include? path
        Dependencies[path]
      else
        require path
        path.split(%r</>).inject(Object){ |o, e| o = o.const_get(e.capitalize) }
      end
  end

  def self.inject(deps)
    deps.each{ |path, value| Dependencies[path] = value }
  end
end

Yes, that’s right! All it does is make it easy to substitute the
definitions of classes. But hey, that’s precisely what I wanted (and
needed) for one of my projects. And to be honest, I think that it’s
enough under most circumstances.

An example of how to use it follows:

scanner.rb:
-----------

require 'injector'

class Scanner
  extend Injector
  needs 'error'

  def initialize(io)
    raise Error, "can only work with open io-ports" if io.closed?
    @io = io
  end

  ⋮
end

error.rb:
---------

class Error < StandardError; end

driver.rb:
----------

require 'scanner'

class Driver
  ⋮
end

tc_driver.rb:
--------------

require 'test/unit'
require 'injector'

require 'scanner'

class Error < ScriptError; end # Or something more creative, perhaps?

Injector.inject(
  'error' => Error
)

class TC_Driver < Test::Unit::TestCase
  ⋮
end

So the idea is that under normal circumstances, ‘needs’ is just a
circumvention for not using ‘require’ directly, but for trying times the
dependency can be changed to something more suitable.

I’ll probably make a real release of this soon, but I was looking for
some comments first, so please, indulge my interest,
        nikolai

--
Nikolai Weibull: now available free of charge at http://bitwi.se/!
Born in Chicago, IL USA; currently residing in Gothenburg, Sweden.
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}

Interesting way to create a DI variation of require. Perhaps you
're on to something. There are some issues though.

  1) The class name is tied tightly to the file's path.
  2) There is only one global Injector.
  3) I'm not sure, but the order in which things occur might
      be a problem.

T.

Nikolai Weibull <mailing-lists.ruby-talk@rawuncut.elitemail.org> writes:

OK. So Ruby has enough dependency-injection/inversion-of-control
libraries as it is, but mine is quite different so I figured I’d post
it. It’s about as simple as dependency-injection can possibly become
and there’s no support for lifecycles, interception, or other framework
stuff. Nor is there any support for parents, children, or other
relatives, so keep it to yourself.

Interesting idea, but you are only pushing constants around?
Can't we do that easier?

class Scanner
  def initialize(io)
    raise Error, "can only work with open io-ports" if io.closed?
    @io = io
  end
end

class Error < ScriptError; end # Or something more creative, perhaps?

  Scanner::Error = Error

class TC_Driver < Test::Unit::TestCase
  ⋮
end

So the idea is that under normal circumstances, ‘needs’ is just a
circumvention for not using ‘require’ directly, but for trying times the
dependency can be changed to something more suitable.

I really fail to see the advantage of using this over manual require
and dependency setting. :slight_smile:

I personally would expect a DI framework to at least provide a
singleton instantiation scheme, else it wouldn't make any sense to me
at all.

Einstein once said "Everything should be made as simple as possible,
but not simpler". I think anything more simple than Matz-DI is too
simple.

···

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

        nikolai

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

Christian Neukirchen wrote:

Nikolai Weibull <mailing-lists.ruby-talk@rawuncut.elitemail.org> writes:

> OK. So Ruby has enough dependency-injection/inversion-of-control
> libraries as it is, but mine is quite different so I figured I’d
> post it. It’s about as simple as dependency-injection can possibly
> become and there’s no support for lifecycles, interception, or other
> framework stuff. Nor is there any support for parents, children, or
> other relatives, so keep it to yourself.

Interesting idea, but you are only pushing constants around?

I’d like to think that we’re doing a bit more than that.

Can't we do that easier?

Maybe.

> class Scanner
> def initialize(io)
> raise Error, "can only work with open io-ports" if io.closed?
> @io = io
> end
> end

> class Error < ScriptError; end # Or something more creative, perhaps?
  Scanner::Error = Error

> class TC_Driver < Test::Unit::TestCase
> ⋮
> end

> So the idea is that under normal circumstances, ‘needs’ is just a
> circumvention for not using ‘require’ directly, but for trying times the
> dependency can be changed to something more suitable.

I really fail to see the advantage of using this over manual require
and dependency setting. :slight_smile:

Well, the thing is, what do you do when you have a compiler class

class Compiler
  ⋮
    raise Error, …
  ⋮
end

that also ‘needs’ our error class?

I personally would expect a DI framework to at least provide a
singleton instantiation scheme, else it wouldn't make any sense to me
at all.

Well, maybe so, but the idea of my library is that all you really need
is to be able to bind constants to classes. It’s definitely not much,
but I found that that was precisely what I was looking for.

Einstein once said "Everything should be made as simple as possible,
but not simpler".

Or something to that effect, yes. (I have yet to find the actual source
of this quotation. Not that I’m doubting that he said it, but I am
wondering if he said/wrote it in English or German and if there was some
context to it as well.)

I think anything more simple than Matz-DI is too simple.
> http://onestepback.org/articles/depinj/matz/index.html

Perhaps, but again, all I was looking for is done by my library. I’m
certainly not trying to force people to stop using needle or some other
library (e.g., dissident). To me, needle is doing a bit too much and I
seem to have a hard time reading your examples for dissident (how do you
do namespacing in dissident?), so I haven’t given it a chance yet,
        nikolai

···

--
Nikolai Weibull: now available free of charge at http://bitwi.se/!
Born in Chicago, IL USA; currently residing in Gothenburg, Sweden.
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}

Nikolai Weibull <mailing-lists.ruby-talk@rawuncut.elitemail.org> writes:

Christian Neukirchen wrote:

I really fail to see the advantage of using this over manual require
and dependency setting. :slight_smile:

Well, the thing is, what do you do when you have a compiler class

class Compiler
  ⋮
    raise Error, …

      raise Injections::Error

  ⋮
end

  Injections::Error = ScriptError

that also ‘needs’ our error class?

Stuffing everything into Injections makes it all use the same
namespace, as with yours.

I personally would expect a DI framework to at least provide a
singleton instantiation scheme, else it wouldn't make any sense to me
at all.

Well, maybe so, but the idea of my library is that all you really need
is to be able to bind constants to classes. It’s definitely not much,
but I found that that was precisely what I was looking for.

That's fine, go ahead with it. I like people solving *their* problems
in *their* way. I'm one of them too. :slight_smile:

I think anything more simple than Matz-DI is too simple.
> http://onestepback.org/articles/depinj/matz/index.html

Perhaps, but again, all I was looking for is done by my library. I’m
certainly not trying to force people to stop using needle or some other
library (e.g., dissident). To me, needle is doing a bit too much and I
seem to have a hard time reading your examples for dissident (how do you
do namespacing in dissident?), so I haven’t given it a chance yet,

You declare the classes to belong to a given "library". You can setup
different containers for each library.

···

        nikolai

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org