[ANN] Copland to Needle article on RubyGarden

C

Chad Fowler

For those not subscribed to RubyGarden's rss feed[1], Jamis Buck has
written a new feature piece discussing his journey from
dependency-injection-novice to two-time-IoC-frameworker. He also
discusses the (unusual) decision to start over on a framework that is
less than a year old and demonstrates the differences between these
two frameworks (Copland and Needle). Big thanks go to Jamis for his
fascinating contribution.

The article is available on RubyGarden's front page or at:
http://rubygarden.org/index.cgi/Libraries/copland-to-needle.rdoc

Request: If you'd like to write an article or tutorial or conduct an
interview to be published on RubyGarden.org, please send me an email
directly. I'm working to establish a rhythm of new content releases,
so if you're interested in contributing I would love to hear from you.

Thanks,
Chad Fowler

[1] http://rubygarden.org/index.cgi/index.rss
 
P

Phil Tomson

For those not subscribed to RubyGarden's rss feed[1], ...

[1] http://rubygarden.org/index.cgi/index.rss

I've been meaning to get 'into' this RSS thing for a while now, but
haven't thought too seriously about it until the last couple of weeks.

So for those of us who are only just now figuring out that there's a
party going on out there (which has apparently been going on for quite a
while now - so I guess I'm fashionably late :), how does one get in?
What is the secret handshake? I know that one needs a thingy called an
aggregator. This sounds a bit like a combination of a news reader and an
email client. What are the recommended aggregators (esp. for Linux and OS X)?
Why is it that web pages will have an RSS link which only brings up a bunch
of XML in your browser when clicked? Do some browsers double as aggregators
and thus allow you to subscribe by clicking on the 'RSS' link? Do these
'aggregations' get emailed to you or do you start up a tool that goes and
looks for them? What if you're wanting to get these RSS feeds on multiple
machines in different locations?


....I feel so out of it.

Phil
 
F

Francis Hwang

I've been meaning to get 'into' this RSS thing for a while now, but
haven't thought too seriously about it until the last couple of weeks.

So for those of us who are only just now figuring out that there's a
party going on out there (which has apparently been going on for quite
a
while now - so I guess I'm fashionably late :), how does one get in?

Start by getting yourself an aggregator. There are tons of free apps
around, you might want to pick one from the list at
http://blogspace.com/rss/readers or just Google around. I'm a Mac head,
and my aggregator of choice is NetNewsWire Lite.

Alternately, you can setup an account on a web-based aggregation
service, like http://bloglines.com/ . Same thing, only on a web site.

Use the various RSS URIs ( like
http://rubygarden.org/index.cgi/index.rss ) to subscribe to a few
starter feeds. You could start with the list at
http://rubygarden.org/ruby?RssFeeds , though that misses quite a lot,
too. You'll find yourself reading entries that refer to other sites,
then subscribing to those new sites' RSS feeds. Typically my
subscription list slowly grows, and then from time to time I get
disgusted with my info bingeing and I try to pare down the subscription
list. (I have the same problem with magazines.)
Why is it that web pages will have an RSS link which only brings up a
bunch
of XML in your browser when clicked?

What I do with those (using Safari on Mac) is right-click those links,
select "Copy Link to Clipboard", and then switch to NNW and subscribe
to that feed. Yeah, you're not really meant to read it in a browser,
though some browsers make it easier to scan through XML files than
others.
Do some browsers double as aggregators
and thus allow you to subscribe by clicking on the 'RSS' link?

Don't know of any yet, but I believe that browser from both Apple and
Microsoft will have more RSS integration in their next major versions.
Do these
'aggregations' get emailed to you or do you start up a tool that goes
and
looks for them?

Usually you start up the tool. You can get them emailed to you, but
that usually gives you less control over your time. Besides ruby-talk,
I'm not actively involved in a lot of email lists. I prefer blogish
conversations more, because I get to make a harder separation between
personal messages (email) and group messages (blogs). When I'm trying
to focus, I shut down my aggregator and only have the email running in
the background.
What if you're wanting to get these RSS feeds on multiple
machines in different locations?

Then a web-based service, like bloglines, is probably a better match
for you.

One cool thing is that RSS has all sorts of bizarre uses besides just
content from a single author. PubSub Concepts, for example, is a
company that has lightning-fast, free searching of millions of data
sources, and you can make URIs that represent feeds for strings you're
looking for. For example, this URI gives me every time it finds the
string "Francis Hwang", usually within hours of the content being
posted:

http://rss.pubsub.com/e7/93/819df23244dbc3a86c3fa46306.xml

Sw33t.

F.
 
C

Curt Hibbs

Francis said:
One cool thing is that RSS has all sorts of bizarre uses besides just
content from a single author. PubSub Concepts, for example, is a
company that has lightning-fast, free searching of millions of data
sources, and you can make URIs that represent feeds for strings you're
looking for. For example, this URI gives me every time it finds the
string "Francis Hwang", usually within hours of the content being
posted:

http://rss.pubsub.com/e7/93/819df23244dbc3a86c3fa46306.xml

I've been a longtime RSS user and have used many different readers over the
years. But I was unaware of PubSub. It looks like a fantastic resource --
thanks!

Also, linked off of the PubSub home page is an RSS client called Gush that I
also had not hear of. It has a very interesting mix of features and looks
very promising. I'm downloading it right now and it may replace my current
reader. So, thanks again!

Curt

PS
For those that want to check it out, here is the home page for Gush:

http://www.2entwine.com/

Be sure to read the "About Us" page... these guys have a great sense of
humor.
 
C

Curt Hibbs

Curt Hibbs rote:
I've been a longtime RSS user and have used many different
readers over the
years. But I was unaware of PubSub. It looks like a fantastic resource --
thanks!

Also, linked off of the PubSub home page is an RSS client called
Gush that I
also had not hear of. It has a very interesting mix of features and looks
very promising. I'm downloading it right now and it may replace my current
reader. So, thanks again!

Curt

PS
For those that want to check it out, here is the home page for Gush:

http://www.2entwine.com/

Be sure to read the "About Us" page... these guys have a great sense of
humor.

Well... I didn't really like Gush all that much, so I just uninstalled it.
My favorite reader for Windows is still RSS Bandit
(http://www.rssbandit.org/).

Curt
 
F

Francis Hwang

Well... I didn't really like Gush all that much, so I just uninstalled
it.
My favorite reader for Windows is still RSS Bandit
(http://www.rssbandit.org/).

Curt

I tried out Gush (for OS X, I think it's mostly the same thing) and
didn't like it either. Its polling model is quite interesting, though:
It opens a Jabber connection to the PubSub server so it receives
updates from PubSub instead of asking for them periodically. Bob Wyman
(PubSub CTO, also I know him since he sits on the board of the
non-profit I work at) writes about this issue quite a lot on his own
blog. POP clients get updates every few minutes, and we should be able
to get the same update rate with feeds like RSS and Atom.

F.
 
S

Sam Roberts

Quoteing (e-mail address removed), on Sat, Nov 13, 2004 at 04:14:37PM +0900:
I tried out Gush (for OS X, I think it's mostly the same thing) and
didn't like it either. Its polling model is quite interesting, though:
It opens a Jabber connection to the PubSub server so it receives
updates from PubSub instead of asking for them periodically. Bob Wyman
(PubSub CTO, also I know him since he sits on the board of the
non-profit I work at) writes about this issue quite a lot on his own
blog. POP clients get updates every few minutes, and we should be able
to get the same update rate with feeds like RSS and Atom.

Yeah, but pop clients do it by polling. Theres not difference between
that and polling for RSS with http.

Sam
 
F

Francis Hwang

Quoteing (e-mail address removed), on Sat, Nov 13, 2004 at 04:14:37PM +0900:

Yeah, but pop clients do it by polling. Theres not difference between
that and polling for RSS with http.

There is. If you've downloaded 30 items from an RSS feed, and then the
feed gets updated with item 31, the basic HTTP get will download all
the items and leave it up to the client to sort out what's new or not.
POP doesn't work this way: If you've downloaded 2000 emails off of your
mail server, then your mail server receives email 2001, when you check
your email again you won't download all 2001 emails just to get the
last one.

RFC3229 ("Delta Encoding in HTTP") describes a way to solve this,
though nobody's implemented it for RSS or Atom. Here's a blog entry
that describes the details at a finer grain of detail than I can
manage:

http://bobwyman.pubsub.com/main/2004/09/using_rfc3229_w.html

Of course, for many feed consumers this is pretty far-out, most of us
are still getting to work with conditional GET.

F.
 
J

Joel VanderWerf

Chad said:
For those not subscribed to RubyGarden's rss feed[1], Jamis Buck has
written a new feature piece discussing his journey from
dependency-injection-novice to two-time-IoC-frameworker. He also
discusses the (unusual) decision to start over on a framework that is
less than a year old and demonstrates the differences between these
two frameworks (Copland and Needle). Big thanks go to Jamis for his
fascinating contribution.

The article is available on RubyGarden's front page or at:
http://rubygarden.org/index.cgi/Libraries/copland-to-needle.rdoc

Request: If you'd like to write an article or tutorial or conduct an
interview to be published on RubyGarden.org, please send me an email
directly. I'm working to establish a rhythm of new content releases,
so if you're interested in contributing I would love to hear from you.

Thanks,
Chad Fowler

[1] http://rubygarden.org/index.cgi/index.rss

Jamis Buck's and Jim Weirich's articles are great. I could never get
through the Java-oriented discussion of this subject, and now that the
subject is presented for rubyists a dim bulb is finally going on in my
head.

What really helps me think about this is to reduce it to some simple,
standard Ruby idioms, which don't provide as many features as Needle,
but are close enough to code that I've actually written to make it all
seem meaningful.

Let's take Jim's example[1], which is clear, rubified, and explicit
about DI constructs, and translate it into some concrete,
mundane ruby code without those constructs. Here's the original:

def create_application
container = DI::Container.new
container.register:)logfilename) { "logfile.log" }
container.register:)db_user) { "jim" }
container.register:)db_password) { "secret" }
container.register:)dbi_string) { "DBI:pg:example_data" }

container.register:)app) { |c|
app = WebApp.new(c.quotes, c.authenticator, c.database)
app.logger = c.logger
app.set_error_handler c.error_handler
app
}

container.register:)quotes) { |c|
StockQuotes.new(c.error_handler, c.logger)
}

container.register:)authenticator) { |c|
Authenticator.new(c.database, c.logger, c.error_handler)
}

container.register:)database) { |c|
DBI.connect(c.dbi_string, c.db_user, c.db_password)
}

container.register:)logger) { |c| Logger.new(c.logfilename) }
container.register:)error_handler) { |c|
errh = ErrorHandler.new
errh.logger = c.logger
errh
}
end

And here's a rewrite that functions in about the same way, but without
being explicit about containers and services.

class Application
def logfilename
@logfilename ||= "logfile.log"
end

def db_user
@db_user ||= "jim"
end

def db_password
@db_password ||= "secret"
end

def dbi_string
@dbi_string ||= "DBI:pg:example_data"
end

def app
@app ||= WebApp.new(quotes, authenticator, database)
end

def quotes
@quotes ||= StockQuotes.new(error_handler, logger)
end

def authenticator
@authenticator ||=
Authenticator.new(database, logger, error_handler)
end

def database
@database ||= DBI.connect(dbi_string, db_user, db_password)
end

def logger
@logger ||= Logger.new(logfilename)
end

def error_handler
@errh ||= (
ErrorHandler.new
@errh.logger = logger
@errh
)
end
end

def create_application
Application.new
end

So we're using the Application class itself as the container, and
we're using the instance methods of this class as the service points.
The methods return the services, and also store them in instance
variables. A service is registered using "def". Instead of using the
block parameter to pass the container to the service definitions, you
just use self. The familiar ||= idiom provides the singleton service
model.

It's quick and dirty, and it obscures the conceptual structure, but if
you're familiar with ruby, you can see immediately what is going on.
You've probably even written code a little like this. Why would you
want to "upgrade" your code to a more explicit form of DI? There are
several disadvantages to this implicit DI style, aside from the
implicitness itself:

- It doesn't help much with things like interceptors, although you
could bring in your favorite AOP library.

- It mixes your service namespace with the namespace inherited from
Object and Kernel: maybe you want a service called "dup" or "puts",
but then you cannot call the Object and Kernel implementation of
these methods from within methods of the Application class.

- Reflection must be done using standard ruby reflection on classes,
so you have to, for example, filter out methods that are not really
services.

OTOH, you can use class inheritance and module inclusion to build
trees of container definitions in a very natural and familiar way,
emulating some of the functionality of Needle:

module LoggingServices
def logger; ...; end
end

module GUIServices
end

module DatabaseServices
end

class MyServiceContainer
include LoggingServices
include GUIServices
include DatabaseServices

def app
MyApp.new(logger, gui, database)
end
end

The other service models besides the singleton model are also easy to
implement with quick and dirty ruby code:

- Threaded:

def per_thread_logger
@per_thread_logger ||= {}
@per_thread_logger[Thread.current] ||= Logger.new
end

- Prototype:

def gui_button
MyButtonClass.new
end

- Deferred (ok, this one gets a little messy, and the details should
be abstracted away by some fancy metaprogramming):

def big_resource
@big_resource || big_resource_proxy
end

def big_resource_proxy
@big_resource_proxy ||= (
proxy = [proc {|br| @big_resource = br}]
def proxy.method_missing(*args, &block)
big_resource = BigResource.new
at(0).call(big_resource)
big_resource.send(*args, &block)
end
proxy
)
end
private :big_resource_proxy

There are other service models that are very easy to construct with
methods, but I'm not aware of an equivalent construct in Needle:

def printer(kind)
@printers ||= {}
@printers[kind] = Printer.new(kind)
end

def printer_for_code_files
@printer_for_code_files ||= printer:)monochrome)
end

def printer_for_images
@printer_for_images ||= printer:)color)
end

It might be possible to do the above in Needle using Pipelines.[2]

Namespaces, if I understand the Needle docs correctly, work something
like this:

class PictureApp
class ColorNamespace
def red; PrimaryColor:)red); end
def green; PrimaryColor:)green); end
def yellow; @yellow ||= red+green; end
end

def colors
@colors ||= ColorNamespace.new
end

def picture
@picture ||= Picture.new:)background => colors.yellow)
end
end

Alternately, if you want color instances to shared by all PictureApps,
you might want to define the colors service like this:

def colors
@@colors ||= ColorNamespace.new
end

I hope there aren't too many inaccuracies in the above, and that this
helps other folks move from older ruby idioms to the new idioms
that Needle gives us.

--

[1]
http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc

[2] Anybody know? Or are parameterized services a misuse of the DI
pattern?
 
J

Jamis Buck

Joel said:
Jamis Buck's and Jim Weirich's articles are great. I could never get
through the Java-oriented discussion of this subject, and now that the
subject is presented for rubyists a dim bulb is finally going on in my
head.

Yay! :)
What really helps me think about this is to reduce it to some simple,
standard Ruby idioms, which don't provide as many features as Needle,
but are close enough to code that I've actually written to make it all
seem meaningful.

Excellent approach, Joel. You've hit the nail right on the head. DI is
nothing new--most of us have been doing something at least similar to it
for a long time. It's like any of the design patterns--it's not that
they are new, so much as they've been given a name so that they can be
communicated better.

As far as the DI frameworks are concerned (like Needle and Copland),
they don't let you do anything you couldn't before. They just make it so
that you don't have to reimplement a DI framework yourself for every
project you create. Plus, they are tested and stable, so you can focus
on your application logic instead of your own custom DI infrastructure.
There are other service models that are very easy to construct with
methods, but I'm not aware of an equivalent construct in Needle:

def printer(kind)
@printers ||= {}
@printers[kind] = Printer.new(kind)
end

def printer_for_code_files
@printer_for_code_files ||= printer:)monochrome)
end

def printer_for_images
@printer_for_images ||= printer:)color)
end

It might be possible to do the above in Needle using Pipelines.[2]

Hmm. I wouldn't call this a service model so much as a factory service.
In fact, I have a new favorite way of doing this kind of thing, as of
this morning. To complicate things a bit, suppose that the Printer class
needs to be dependency injected (with a logger instance, for example),
and the following approach really shines:

reg.define do |b|
b.printer do |ctr,info|
logger = ctr[:logs].get( info )
lambda { |kind| Printer.new( logger, kind ) }
end

b.code_printer { |ctr,| ctr.printer.call( :monochrome ) }
b.image_printer { |ctr,| ctr.printer.call( :color ) }
end

I've actually incorporated this approach into various places in the
upcoming new release of Net::SSH, and it works beautifully.
I hope there aren't too many inaccuracies in the above, and that this
helps other folks move from older ruby idioms to the new idioms
that Needle gives us.

Excellent write-up, Joel. You ought to publish this somewhere on a
website...although the fact that you posted it _here_ means it will be
archived in the ruby-talk archives, which is probably good enough.

Thanks!

- Jamis
 
D

David Ross

Excellent write-up, Joel. You ought to publish this somewhere on a
website...although the fact that you posted it _here_ means it will be
archived in the ruby-talk archives, which is probably good enough.
Publish on Rubymine (rubymine.org), its one of the other points of the
site is to bring all news together kind of like a slashdot for the ruby
community without all the fuzz of other topics. ;)

David Ross
 
J

Joel VanderWerf

Jamis said:
Joel VanderWerf wrote: ...
There are other service models that are very easy to construct with
methods, but I'm not aware of an equivalent construct in Needle:

def printer(kind)
@printers ||= {}
@printers[kind] = Printer.new(kind)
end

def printer_for_code_files
@printer_for_code_files ||= printer:)monochrome)
end

def printer_for_images
@printer_for_images ||= printer:)color)
end

It might be possible to do the above in Needle using Pipelines.[2]


Hmm. I wouldn't call this a service model so much as a factory service.
In fact, I have a new favorite way of doing this kind of thing, as of
this morning. To complicate things a bit, suppose that the Printer class
needs to be dependency injected (with a logger instance, for example),
and the following approach really shines:

reg.define do |b|
b.printer do |ctr,info|
logger = ctr[:logs].get( info )
lambda { |kind| Printer.new( logger, kind ) }
end

b.code_printer { |ctr,| ctr.printer.call( :monochrome ) }
b.image_printer { |ctr,| ctr.printer.call( :color ) }
end

I've actually incorporated this approach into various places in the
upcoming new release of Net::SSH, and it works beautifully.

That captures the parameterization, but it doesn't capture the
multiton[1] service model of my code. A unique instance is generated for
each distinct argument list (distinct in the sense of hash keys, which
is to say #eql?). What's the most natural way to do that with Needle?
 
J

Jamis Buck

Joel said:
Jamis Buck wrote:
reg.define do |b|
b.printer do |ctr,info|
logger = ctr[:logs].get( info )
lambda { |kind| Printer.new( logger, kind ) }
end

b.code_printer { |ctr,| ctr.printer.call( :monochrome ) }
b.image_printer { |ctr,| ctr.printer.call( :color ) }
end

I've actually incorporated this approach into various places in the
upcoming new release of Net::SSH, and it works beautifully.


That captures the parameterization, but it doesn't capture the
multiton[1] service model of my code. A unique instance is generated for
each distinct argument list (distinct in the sense of hash keys, which
is to say #eql?). What's the most natural way to do that with Needle?

Actually, the example does--I just enforced the singleton (multiton?)
constraint in the code_printer and image_printer services. The +printer+
service itself, though, will generate a new instance for each invocation.

However, you could also create a service factory. It would do just as
you did, with a Hash for caching created instances.

class Printer
def self.get( name )
@printers ||= Hash.new
@printers[ name ] ||= new( name )
end

private_class_method :new
def initialize( name )
...
end
end

reg.define.printers { Printer }

p1 = reg.printers.get( :monochrome )
p2 = reg.printers.get( :monochrome )
assert_same p1, p2

In fact, this is almost exactly how the log-factory service in Needle
works, although the LogFactory is a distinct class from the Logger.

I'm still thinking through how to implement something like this in
Needle itself. It'll probably wind up being, as you suggested, a
pipeline element of some sort, although the parameterization issue goes
a bit deeper.

- Jamis
 
J

Joel VanderWerf

Jamis said:
Joel said:
Jamis Buck wrote:
reg.define do |b|
b.printer do |ctr,info|
logger = ctr[:logs].get( info )
lambda { |kind| Printer.new( logger, kind ) }
end

b.code_printer { |ctr,| ctr.printer.call( :monochrome ) }
b.image_printer { |ctr,| ctr.printer.call( :color ) }
end

I've actually incorporated this approach into various places in the
upcoming new release of Net::SSH, and it works beautifully.



That captures the parameterization, but it doesn't capture the
multiton[1] service model of my code. A unique instance is generated
for each distinct argument list (distinct in the sense of hash keys,
which is to say #eql?). What's the most natural way to do that with
Needle?


Actually, the example does--I just enforced the singleton (multiton?)
constraint in the code_printer and image_printer services. The +printer+
service itself, though, will generate a new instance for each invocation.

Right, I missed that. But that way makes it hard to ensure that there is
only one printer of each type. There might be a log_printer service that
also requests the monochrome printer. I guess you could define a
#{kind}_printer service for each possible kind of printer, and
implement, in terms of these services, a #{usage}_printer for each
possible usage, including code, image, and log.

In this case there are just a few kinds, but what if the printer
arguments have more possible variation? What if, in addition to kind,
there is an integer argument (a port number, say)?

Btw, I'd now rewrite my printer snippet as:

def printer_hash
@printers ||= {}
end
private :printer_hash

def printer(kind)
printer_hash[kind] ||= Printer.new(kind)
end

both fixing a typo ( = where I meant ||= ) and using a separate service
point, for the printer_hash, which can be declared private.

It seems like private services would be awkward to implement in Needle,
at least with the hash-based interface. (With the method sending
interface, I guess you could just declare it private in the same way.)
Thoughts?
However, you could also create a service factory. It would do just as
you did, with a Hash for caching created instances.

class Printer
def self.get( name )
@printers ||= Hash.new
@printers[ name ] ||= new( name )
end

private_class_method :new
def initialize( name )
...
end
end

reg.define.printers { Printer }

p1 = reg.printers.get( :monochrome )
p2 = reg.printers.get( :monochrome )
assert_same p1, p2

This feels like it breaks encapsulation: the Printer class shouldn't
know about the service model that the container is using for it. In this
case, the service model may be an unavoidable consequence of how the
Printer class works, and so it should be enforced by the class itself.

But there are classes that could be used with different service models
in different coexisting containers: a general log class that, in one
container, is threaded, but in another container is managed by a
multiton service whose keys are log type, (e.g. :security, :information,
:warnings, and :errors).
In fact, this is almost exactly how the log-factory service in Needle
works, although the LogFactory is a distinct class from the Logger.

I'm still thinking through how to implement something like this in
Needle itself. It'll probably wind up being, as you suggested, a
pipeline element of some sort, although the parameterization issue goes
a bit deeper.

I'm looking forward to watching Needle evolve.
 
J

Jamis Buck

Joel said:
In this case there are just a few kinds, but what if the printer
arguments have more possible variation? What if, in addition to kind,
there is an integer argument (a port number, say)?

Well... you'd just use an array for the hash key, right? Or am I
misunderstanding your question?
It seems like private services would be awkward to implement in Needle,
at least with the hash-based interface. (With the method sending
interface, I guess you could just declare it private in the same way.)
Thoughts?

I've thought about this. Copland supports the concept of a private
service, but I'm not sure how to implement the same thing in Needle. The
tricky part is that currently, Needle can't really detect whether a
service is being requested by another service in the same container, or
by a service in a different container. I would very much like to support
private/public services in Needle, but I just haven't seen my way clear
on how to do it, yet.
However, you could also create a service factory. It would do just as
you did, with a Hash for caching created instances.

class Printer
def self.get( name )
@printers ||= Hash.new
@printers[ name ] ||= new( name )
end

private_class_method :new
def initialize( name )
...
end
end

reg.define.printers { Printer }

p1 = reg.printers.get( :monochrome )
p2 = reg.printers.get( :monochrome )
assert_same p1, p2


This feels like it breaks encapsulation: the Printer class shouldn't
know about the service model that the container is using for it. In this
case, the service model may be an unavoidable consequence of how the
Printer class works, and so it should be enforced by the class itself.

The Printer class doesn't know about its service model. Am I
misunderstanding you? What in the code given above causes you to think
that the service model is known to the Printer class itself?
Incidentally, because the class itself IS the service, service models
are actually rather inconsequential. Ignoring that fact, however, you
could do the following and the Printer class would have no knowledge of
which model was being used for it:

reg.define do |b|
b.printers1( :model => :prototype ) { Printer }
b.printers2( :model => :threaded ) { Printer }
b.printers3( :model => :singleton_deferred_initialize ) { Printer }
...
end
But there are classes that could be used with different service models
in different coexisting containers: a general log class that, in one
container, is threaded, but in another container is managed by a
multiton service whose keys are log type, (e.g. :security, :information,
:warnings, and :errors).

Very true.
I'm looking forward to watching Needle evolve.

So am I. :) You bring up some very interesting points. I'm glad there
are people that are challenging my assumptions.

- Jamis
 
J

Joel VanderWerf

Jamis said:
Well... you'd just use an array for the hash key, right? Or am I
misunderstanding your question?

Your suggestion, if I understood correctly, was to enforce a multiton
service model of printers by defining a fixed number of singleton
services, one for each printer. Now suppose specifying a printer
requires not just a kind, but a kind and a port number. In that case,
there are a lot of singletons...

That seems to be a good reason to find a way to express the multiton
model explicitly.
 
J

Joel VanderWerf

Jamis said:
Joel VanderWerf wrote: ...
However, you could also create a service factory. It would do just as
you did, with a Hash for caching created instances.

class Printer
def self.get( name )
@printers ||= Hash.new
@printers[ name ] ||= new( name )
end

private_class_method :new
def initialize( name )
...
end
end

reg.define.printers { Printer }

p1 = reg.printers.get( :monochrome )
p2 = reg.printers.get( :monochrome )
assert_same p1, p2



This feels like it breaks encapsulation: the Printer class shouldn't
know about the service model that the container is using for it. In
this case, the service model may be an unavoidable consequence of how
the Printer class works, and so it should be enforced by the class
itself.


The Printer class doesn't know about its service model. Am I
misunderstanding you? What in the code given above causes you to think
that the service model is known to the Printer class itself?

The Printer.get implementation is what I would call a service model: it
defines a protocol where a request for a key returns a Printer unique to
that key. But I've only been using the phrase "service model" for, oh,
about 10 hours.

Should the Printer class be designed to think about managing collections
of printers? I'm trying to wean myself off of practices like that ;)
Incidentally, because the class itself IS the service, service models
are actually rather inconsequential. Ignoring that fact, however, you
could do the following and the Printer class would have no knowledge of
which model was being used for it:

reg.define do |b|
b.printers1( :model => :prototype ) { Printer }
b.printers2( :model => :threaded ) { Printer }
b.printers3( :model => :singleton_deferred_initialize ) { Printer }
...
end

That's true, you don't have to use Printer.get. Printer.new is still the
same as it always was, and so all of the existing service models will
work normally with Printer. But if you're using Printer.new and
Printer.get(kind) in the same container, then there are two (or more)
service models of Printer operating together, and you can't guarantee
that at most one printer of each kind exists. It's not really a multiton
pattern any more. (That may be perfectly ok in some cases--this Printer
example is really too vague to tell.)

Anyway, having this logic manually coded within an application class
(whether in the service or in the container) is too much like my rewrite
of Jim's original example. It seems to me that logic should be embedded
in the DI framework, as a model option. Eventually :)
 
J

Jamis Buck

Joel said:
Jamis said:
Joel VanderWerf wrote:
...
However, you could also create a service factory. It would do just
as you did, with a Hash for caching created instances.

class Printer
def self.get( name )
@printers ||= Hash.new
@printers[ name ] ||= new( name )
end

private_class_method :new
def initialize( name )
...
end
end

reg.define.printers { Printer }

p1 = reg.printers.get( :monochrome )
p2 = reg.printers.get( :monochrome )
assert_same p1, p2




This feels like it breaks encapsulation: the Printer class shouldn't
know about the service model that the container is using for it. In
this case, the service model may be an unavoidable consequence of how
the Printer class works, and so it should be enforced by the class
itself.



The Printer class doesn't know about its service model. Am I
misunderstanding you? What in the code given above causes you to think
that the service model is known to the Printer class itself?


The Printer.get implementation is what I would call a service model: it
defines a protocol where a request for a key returns a Printer unique to
that key. But I've only been using the phrase "service model" for, oh,
about 10 hours.

True. The nature of a factory is such that it will be the arbiter of how
its products are obtained. Thus, in that since, the factory enforces a
lifecycle model for the services it returns.
Should the Printer class be designed to think about managing collections
of printers? I'm trying to wean myself off of practices like that ;)

Well, I may have muddied the waters by having the Printer class be the
factory class as well. Ruby works well for things like that--I prefer to
think of it as "the factory is designed to think about managing
collections of its products." It's a very powerful design pattern, not
at all conflicting with dependency injection. Done right, the two
patterns can be very complimentary.

I believe what you are asking for (and what I'm still looking for a nice
solution to) is a way for Needle itself to become the factory for these
kinds of services. Right now, the most elegant solution I've found is to
create lambda services that return curried functions, which when invoked
return the desired services. The drawback is that the curried functions
are outside of the control of Needle's service models, so you have to
further wrap those services in other services...
Anyway, having this logic manually coded within an application class
(whether in the service or in the container) is too much like my rewrite
of Jim's original example. It seems to me that logic should be embedded
in the DI framework, as a model option. Eventually :)

Eventually, indeed. :) Please, dig into Needle's internals and see if
you can find a way to implement this. I'll do the same. With enough eyes
and brains working on this puzzle, an acceptible solution is sure to emerge.

- Jamis
 
T

tony summerfelt

I've been meaning to get 'into' this RSS thing for a while now, but
haven't thought too seriously about it until the last couple of weeks.

i think i've used all the free ones. i needed to test out my own rss
feed so i'm made it a point to check a good number of them out.

the ones i liked:

amphetadesk (perl/any browser, cross-platform)
abilon
newzspider
nttp//rss (java/ any newsreader, cross-platform)
newzie
sage (firefox plugin, cross-platform)
thunderbird (built in rss support, cross-platform)

ones i didn't like:

feedreader
blogmatrix (python based, cross-platform
bottomfeeder (java based)
any .net based ones

i use the sage plugin for firefox, as i invevitably want to click on a
a link anyway...
http://home.cogeco.ca/~tsummerfelt1
telnet://ventedspleen.dyndns.org
 
G

Gavin Sinclair

the ones i liked:
amphetadesk (perl/any browser, cross-platform)
abilon
newzspider
nttp//rss (java/ any newsreader, cross-platform)
newzie
sage (firefox plugin, cross-platform)
thunderbird (built in rss support, cross-platform)

Thanks for the roundup.
ones i didn't like:
feedreader
blogmatrix (python based, cross-platform
bottomfeeder (java based)

BottomFeeder is Smalltalk-based, actually.
any .net based ones

BloatWarePlus. But RSS Bandit is popular, Curt rated it, and it has a
cool name.
i use the sage plugin for firefox, as i invevitably want to click on a
a link anyway...

I use that too, but it could do with some work...

And besides, it's not very featureful. I keep wanting to get into a
decent RSS reader, but the "couldn't be bothered" factor is very high.

Gavin
 

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,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top