Needle and Parameterized Services

R

Rob Lally

Hi,

I've been using Needle to try to create an iBatis like framework in Ruby. I want register parameterized blocks but when
I do this they have to take the container and service point as the first two parameters. At least I think they need to
... I'm hoping they don't, can anyone suggest a way round it?

Thanks in advance,

R.
 
J

Jamis Buck

Hi,

I've been using Needle to try to create an iBatis like framework in
Ruby. I want register parameterized blocks but when I do this they
have to take the container and service point as the first two
parameters. At least I think they need to ... I'm hoping they don't,
can anyone suggest a way round it?

First off, I'm not familiar at all with iBatis--could you give a little
background on that? I'm also not entirely sure I understand what you're
asking, but I *think* you're saying the following:

registry.register:)my_service, :model => :multiton) do |c,p,*args|
# do something with *args, like:
args.map { |i| "sing: #{i}" }
end

And you want to be able to register a block that does NOT use the first
two parameters for "c" (the container) and "p" (the service point),
right?

Please keep in mind that even though the block accepts those first two
parameters, the service point itself is parameterized only on the
remaining values. In other words, when you go to query the "my_service"
service, you do NOT pass in the "c" and "p" arguments--Needle does that
for you. Thus:

my_service = registry.my_service("do", "re", "mi")
p my_service

Would print

["sing: do", "sing: re", "sing: mi"]

Does that make sense? Or did I miss the gist of your question?

- Jamis
 
R

Rob Lally

Jamis said:
First off, I'm not familiar at all with iBatis--could you give a little
background on that? I'm also not entirely sure I understand what you're
asking, but I *think* you're saying the following:

iBatis is an OR mapping tool from the Java world based on the DAO pattern. I mentioned it more for colour than because
it was useful information. How I expected anyone else to know that I'm not sure ...
registry.register:)my_service, :model => :multiton) do |c,p,*args|
# do something with *args, like:
args.map { |i| "sing: #{i}" }
end

And you want to be able to register a block that does NOT use the first
two parameters for "c" (the container) and "p" (the service point), right?

Please keep in mind that even though the block accepts those first two
parameters, the service point itself is parameterized only on the
remaining values. In other words, when you go to query the "my_service"
service, you do NOT pass in the "c" and "p" arguments--Needle does that
for you. Thus:

my_service = registry.my_service("do", "re", "mi")
p my_service

Would print

["sing: do", "sing: re", "sing: mi"]

Does that make sense? Or did I miss the gist of your question?

For this particular framework the container and service point are not likely ever to be useful to the party writing the
blocks; it is using Needle "under the covers" rather than as a top level object. So if the client wants to write
parameterized blocks they would need to include two mystery parameters. When I say mystery parameters I only mean
because the use of Needle under the covers isn't transparent.

Perhaps some code would be clearer:

A user of the framework would write

mapr.register_statement:)select_person_by_id) do |id|
"SELECT * FROM person WHERE id = #{id}"
end

Under the covers this stores this block in a needle registry. In order to be able to call the service with a parameter
it would need to have c and p at the start of the block parameter list.

At the moment I'm wrapping this block under the cover with another block

def register_statement(name, &statement)
@registry.statements.register(name, :model => :prototype) {|c, p, *args| statement.call(*args) }
end

Which works fine but feels a bit off.

I was hoping that there would be an option looking like:

def register_statement(name, &statement)
@registry.statements.register(name, :model => :prototype, :container_agnostic => true, &statement)
end

Which would enable parameterised services that don't get the c and p parameters.

Thanks for taking the time to respond.

R.
 
J

Jamis Buck

A user of the framework would write

mapr.register_statement:)select_person_by_id) do |id|
"SELECT * FROM person WHERE id = #{id}"
end

Under the covers this stores this block in a needle registry. In order
to be able to call the service with a parameter it would need to have
c and p at the start of the block parameter list.

At the moment I'm wrapping this block under the cover with another
block

def register_statement(name, &statement)
@registry.statements.register(name, :model => :prototype) {|c,
p, *args| statement.call(*args) }
end

Which works fine but feels a bit off.

Ah, I understand now. Yah, currently there is no option to prevent
those parameters, so wrapping it as you did above is about your only
option. I don't know how I feel about adding an option to "turn off"
the c/p parameters. I know DI isn't a hugely popular approach in the
Ruby world, but...anyone out there have an opinion on this matter?
Personally, I think I'd lean towards just having consumers of Needle
use adapters of one kind or another to change the interface, as you did
above. It's an honorable and tried design pattern.

What about it "feels a bit off" to you? Maybe that would help change my
opinion. :)

- Jamis
 
R

Rob Lally

Jamis said:
Ah, I understand now. Yah, currently there is no option to prevent those
parameters, so wrapping it as you did above is about your only option. I
don't know how I feel about adding an option to "turn off" the c/p
parameters. I know DI isn't a hugely popular approach in the Ruby world,
but...anyone out there have an opinion on this matter? Personally, I
think I'd lean towards just having consumers of Needle use adapters of
one kind or another to change the interface, as you did above. It's an
honorable and tried design pattern.

What about it "feels a bit off" to you? Maybe that would help change my
opinion. :)

I'm not exactly sure what makes me uncomfortable about it. I think that it is the lack of symmetry; if you want 0
parameters you define 0 parameters, if you want n you define n + 2.

I think that at the moment DI isn't very popular in the Ruby world for a couple of reasons:

1) People are writing relatively small applications. DI becomes more important on larger code bases with multiple
developers; amongst other things the physical decoupling makes it easier to perform the mental decoupling needed to
concentrate on the corner of the code base you're working on. More and more people seem to be working collaboratively on
Rails based projects so I would expect DI and needle to become more important here.

2) The dynamic nature of Ruby means it isn't as hard to 'roll your own' DI solution. If Needle got some more publicity I
reckon people would use it more.

One of the things I noticed on another thread about performance of unit test was a discussion of combinatoric complexity
in unit tests. The posters solution was to spin of chunks of code into modules, a not unreasonable solution. Another
alternative would be to use DI to rid the units under test of direct dependencies and eliminate the combinatoric explosion.

Something I would like to see would be integration of Needle into Rails. I seem to recall quite a few threads on the
Rails list along the lines of 'How do I make something available to all of my controllers'? The answer normally involves
inheritance of environment.rb. If ActionController::Base had a registry associated with it or even if there was a
(heaven forfend) global rails registry it would provide a single definitive place to put these kind of services. Rails
itself could provide ActionMail, Logging or other services via this registry as well.

IMHO dependency injection is a good thing and Needle is an excellent example of a DI framework. With lots of non OO
developers coming in to Ruby via Rails it would be an excellent opportunity to spread the word about a valuable and
powerful technique. I know that Rails values convention over configuration ... but sometimes configuration is necessary.
When is it Needle provides a solid, flexible solution.

R.
 
J

Jamis Buck

I'm not exactly sure what makes me uncomfortable about it. I think
that it is the lack of symmetry; if you want 0 parameters you define 0
parameters, if you want n you define n + 2.

Well, actually, if you want 0 parameters, you can define 2 parameters,
so the symmetry is still there. It is just that you can optionally
leave the parameters out of the block if you don't need to use them.
But I understand what you are saying, and there is something to be said
about that. However, I still feel like adding an option to specify that
the c/p parameters are optional complicates the API. I'll do some
thinking about it.
2) The dynamic nature of Ruby means it isn't as hard to 'roll your
own' DI solution. If Needle got some more publicity I reckon people
would use it more.

I sure did my best to push Copland about a year ago, and Needle in the
few months after RubyConf '04. But as you said, most Ruby developers
are working on smaller projects, and so it was difficult to demonstrate
the value of DI in those situations. I know a few developers were/are
using Needle in their software, though--Christian Neukirchen is using
it in his Nukumi2 blog software, and the RPA team was using it
(although RPA seems to have become defunct). I used Needle in Net::SSH.

It would help if DI wasn't so hard to explain. :) Jim Weirich did an
excellent job in his dependency injection article of several months
ago, but I think the whole concept still leaves a lot of people
scratching (and shaking) their heads.
Something I would like to see would be integration of Needle into
Rails. I seem to recall quite a few threads on the Rails list along
the lines of 'How do I make something available to all of my
controllers'? The answer normally involves inheritance of
environment.rb. If ActionController::Base had a registry associated
with it or even if there was a (heaven forfend) global rails registry
it would provide a single definitive place to put these kind of
services. Rails itself could provide ActionMail, Logging or other
services via this registry as well.

David and I actually discussed this possibility quite seriously a while
ago, but for several reasons Needle is not a very good fit inside Rails
itself. I know several people are using it on top of Rails, which I
think is great, but adding Needle support to Rails itself is not
necessarily adding value. A few tweaks to your environment.rb and you
can add the Needle support yourself (thanks to Ruby's ability to reopen
classes). Or, you can do like I did
(http://ruby.jamisbuck.org/rails-injected.html) and work out a more
object-oriented solution, which is what I originally proposed to David.
(That link, incidentally, builds on a pre-0.9 version of Rails, so you
may need to tweak it considerably to get it to work in modern releases.
Also, the document describes how Rails now solves the problems I was
addressing).
IMHO dependency injection is a good thing and Needle is an excellent
example of a DI framework. With lots of non OO developers coming in to
Ruby via Rails it would be an excellent opportunity to spread the word
about a valuable and powerful technique. I know that Rails values
convention over configuration ... but sometimes configuration is
necessary. When is it Needle provides a solid, flexible solution.

Thank-you, Rob. I'm glad you have found Needle useful. What I think
would really help the understanding of DI in Ruby is if you could maybe
write a brief article about how, specifically, you are using DI in your
projects. I'd be happy to host that article on my blog if you'd like,
or perhaps even Chad could host it on RubyGarden.

- Jamis
 
P

pat eyler

Thank-you, Rob. I'm glad you have found Needle useful. What I think
would really help the understanding of DI in Ruby is if you could maybe
write a brief article about how, specifically, you are using DI in your
projects. I'd be happy to host that article on my blog if you'd like,
or perhaps even Chad could host it on RubyGarden.

Another good option might be to write it for Linux Journal. You'd get paid
for it and, after 30 days, it could be incorporated into the Needle
distribution
as documentation -- specifics are available from the LJ editors. (Not to
mention the boost in Ruby publicity everytime a magazine publishes an
article about it.)
 
R

Rob Lally

Jamis said:
Thank-you, Rob. I'm glad you have found Needle useful. What I think
would really help the understanding of DI in Ruby is if you could maybe
write a brief article about how, specifically, you are using DI in your
projects. I'd be happy to host that article on my blog if you'd like, or
perhaps even Chad could host it on RubyGarden.


I will dig out my writers hat and see what I can put together. If people here think it is worthwhile I'll see if it's
worth putting up somewhere permenantly.

R.
 
J

Joel VanderWerf

Rob said:
Hi,

I've been using Needle to try to create an iBatis like framework in
Ruby. I want register parameterized blocks but when I do this they have
to take the container and service point as the first two parameters. At
least I think they need to ... I'm hoping they don't, can anyone suggest
a way round it?

It's not Needle, but MinDI does have parameterized services with the
same signature for both the service definition and invocation. See
http://raa.ruby-lang.org/project/mindi. Example:

-----------
require 'mindi'

class SimpleContainer
extend MinDI::Container

string { "Hello, world\n" }

point_at { |x,y| [x,y] }

stuff { [string, point_at(100,200)] }
end

cont = SimpleContainer.new

p cont.stuff # ==> ["Hello, world\n", [100, 200]]
 
D

Dick Davies

* Joel VanderWerf said:
It's not Needle, but MinDI does have parameterized services with the
same signature for both the service definition and invocation.

*please* tell me there is a library in the pipeline called mork :)
 
H

Hal Fulton

Dick said:
* Joel VanderWerf said:
Dick said:
* Joel VanderWerf <[email protected]> [0414 20:14]:


It's not Needle, but MinDI does have parameterized services with the
same signature for both the service definition and invocation.


*please* tell me there is a library in the pipeline called mork :)

Yes, but this is all it does:

Na.new
Na.new


mork = Proc.new { |o| puts "come in, #{o}" }
mork.call('orson')

...
I'll get my coat.

Well, Mork used to call him Laserbreath sometimes... and the original
laser was built using a ruby... see how it all fits?


Hal
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,996
Messages
2,570,238
Members
46,826
Latest member
robinsontor

Latest Threads

Top