The maze book for programmers!
mazesforprogrammers.com

Algorithms, circle mazes, hex grids, masking, weaving, braiding, 3D and 4D grids, spheres, and more!

DRM-Free Ebook

The Buckblog

assorted ramblings by Jamis Buck

Now that's cool

13 November 2004 — 2-minute read

Christian Neukirchen asked me a question this morning on IRC. He wanted to know if there was a way to pass parameters to a service’s constructor (in Needle) at the moment the service was requested. This is particularly useful for prototype services, where each request of the service returns a new instance—often, you will want to pass some context-specific data to the constructor of that service.

The short answer is “no”, you can’t really do specifically that in Needle. When I’ve needed something like that, I’ve usually split the constructor in two—creating an “injectible” constructor, and a separate #init method that must be invoked manually after obtaining the service handle in order to pass in the context-specific stuff:

  class Foo
    def initialize( bar )
      @bar = bar
    end

    def init( baz )
      @baz = baz
      do_initialization
      ...
      self
    end
  end

  registry.define.foo( :model => :prototype ) { |c,| Foo.new( c[:bar] ) }

  foo1 = registry.foo.init( "hello" )
  foo2 = registry.foo.init( "world" )

It works, but it is clunky.

This morning, I found a better way, thanks to the power of Ruby’s closures. Ladies and gentlemen, consider the following:

  class Foo
    def initialize( bar, baz )
      @bar = bar
      @baz = baz
      do_initialization
      ...
    end
  end

  registry.define.foo( :model => :prototype ) do |c,|
    lambda { |baz| Foo.new( c[:bar], baz ) }
  end

  foo1 = registry.foo.call( "hello" )
  foo2 = registry.foo.call( "world" )

I love closures! True, the invocation to #call is still a bit clunky, but there’s not really a way to get around it. Besides, if you think about it, what you’ve done is turned the foo service (above) into a factory, which accepts parameters and returns object instances tailored according to those parameters.

I’ve already reworked portions of the Net::SSH rewrite to take advantage of this approach—it’s really slick.

(Oh, and speaking of Net::SSH…connections and channels work! You can now execute processes remotely and respond to events. Remaining to finish: the port forwarding manager, the process manager, and the SFTP subsystem. Whew!)