[ANN] Copland to Needle article on RubyGarden

Jamis Buck wrote:

Joel VanderWerf wrote:

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.

I am confused about the '.' before the on second output 'in setup'.
what does that '.' mean?

Oh, that's just the ConsoleRunners' way of saying a test has succeeded.

Going back to my original example.
If I want to use @foo, @run, @rerun (already initiated) in #test_rerun,
what should I do?

You could put both asserts in one test_function. There is no rule saying you can only have one assertion per test function. Don't know if this is feasible in your environment/setting though.

For example:

class TestClass < Test::Unit::TestCase

    def setup
       @foo = Foo.new
       @run = Foo.run(@foo.key)
       @rerun = Foo.rerun(@run.key)
    end

    def test_foo_runs
       assert_equal(@foo.key, @run.key)
       assert_equal(@run.key, @rerun.key)
    end
end

Or, is there any convention something like, #setup_run (would be used by
only #test_run) or #setup_rerun (would be used by only #test_rerun)

Not that I know of (which more or less doesn't mean anything). But since only test_* methods get called from the test framework and TestClass otherwise is a perfectly fine 'regular' class, you could write your own setup_rerun method and call it from within your test function before calling assert. But somehow I get the impression that this will result in pretty bad code duplication or bloated test functions.

IANATDE
( I am not a test design expert )

  :)

Henrik

require "test/unit"

class TestClass < Test::Unit::TestCase

         def initialize(arg)
                 super(arg)
                 puts "in setup"
         end

         def test_run
                 assert_equal(true, true)
         end

         def test_rerun
                 assert_equal(true, true)
         end

         def test_rererun
                 assert_equal(true, true)
         end

end

gives:

>ruby test_setup.rb
in setup
Loaded suite test_setup
Started
...
Finished in 0.0 seconds.

3 tests, 3 assertions, 0 failures, 0 errors
>Exit code: 0

close, but no cigar

Henrik

Thanks a lot, Brian and Nathaniel.

···

On Mon, 2004-11-15 at 13:31, Nathaniel Talbott wrote:

On Nov 15, 2004, at 12:08, Brian Schröder wrote:

> If you want to setup the variables once for the test case, you should
> use:
>
> class TestClass < Test::Unit::TestCase
> def initialize
> super
> @foo = Foo.new
> @run = Foo.run(@foo.key)
> @rerun = Foo.rerun(@run.key)
> end
>
> def test_run
> assert_equal(@foo.key, @run.key)
> end
>
> def test_rerun
> assert_equal(@run.key, @rerun.key)
> end
> end
>
> Would fill each variable only once.

That actually doesn't work, because the test class is instantiated once
for each run. One way to solve this is like so:

   class TestClass < Test::Unit::TestCase
     @@foo = Foo.new
     @@run = Foo.run(@@foo.key)
     @@rerun = Foo.rerun(@run.key

     def test_run
       assert_equal(@@foo.key, @@run.key)
     end

     def test_rerun
       assert_equal(@run.key, @rerun.key)
     end
   end

Now, while I won't claim there's never a reason to do this, it seems
like a major code smell to me. Might be time to break out the air
freshener :slight_smile:

HTH,

Nathaniel
Terralien, Inc.

<:((><

--
MOhammad

Quoteing sera@fhwang.net, on Sat, Nov 13, 2004 at 04:14:37PM +0900:

>
>Well... I didn't really like Gush all that much, so I just uninstalled
>it.
>My favorite reader for Windows is still RSS Bandit
>(RSS Bandit).
>
>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.

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

Sam

···

On Nov 13, 2004, at 2:06 AM, Curt Hibbs wrote:

Joel VanderWerf wrote:

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.

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.

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

- Jamis

···

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck.org/jamis

Thanks again, Henrik.

I have reason to keep two assert_equal in two #test_methods.
Bottom line, I need @foo, @run and @rerun initiated once and I might
have two or more methods like #test_run, #test_rerun, #test_rererun will
use @foo, @run and @rerun.

···

On Mon, 2004-11-15 at 11:33, Henrik Horneber wrote:

>
> I am confused about the '.' before the on second output 'in setup'.
> what does that '.' mean?
>

Oh, that's just the ConsoleRunners' way of saying a test has succeeded.

> Going back to my original example.
> If I want to use @foo, @run, @rerun (already initiated) in #test_rerun,
> what should I do?
>

You could put both asserts in one test_function. There is no rule saying
you can only have one assertion per test function. Don't know if this is
feasible in your environment/setting though.

For example:

class TestClass < Test::Unit::TestCase

    def setup
       @foo = Foo.new
       @run = Foo.run(@foo.key)
       @rerun = Foo.rerun(@run.key)
    end

    def test_foo_runs
       assert_equal(@foo.key, @run.key)
       assert_equal(@run.key, @rerun.key)
    end
end

--
Mohammad

> Or, is there any convention something like, #setup_run (would be used by
> only #test_run) or #setup_rerun (would be used by only #test_rerun)

Not that I know of (which more or less doesn't mean anything). But since
only test_* methods get called from the test framework and TestClass
otherwise is a perfectly fine 'regular' class, you could write your own
setup_rerun method and call it from within your test function before
calling assert. But somehow I get the impression that this will result
in pretty bad code duplication or bloated test functions.

IANATDE
( I am not a test design expert )

  :)

Henrik

In other words, I am still stuck! :frowning:

···

On Mon, 2004-11-15 at 12:52, Henrik Horneber wrote:

require "test/unit"

class TestClass < Test::Unit::TestCase

         def initialize(arg)
                 super(arg)
                 puts "in setup"
         end

         def test_run
                 assert_equal(true, true)
         end

         def test_rerun
                 assert_equal(true, true)
         end

         def test_rererun
                 assert_equal(true, true)
         end

end

gives:

>ruby test_setup.rb
in setup
in setup
in setup
Loaded suite test_setup
Started
...
Finished in 0.0 seconds.

3 tests, 3 assertions, 0 failures, 0 errors
>Exit code: 0

close, but no cigar

Henrik

--
Mohammad

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.

···

On Nov 13, 2004, at 11:57 AM, Sam Roberts wrote:

Quoteing sera@fhwang.net, 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.

Jamis Buck wrote:

Joel VanderWerf wrote:

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?

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.

Jamis Buck wrote:

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 :wink:

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 :slight_smile:

Don't know if this is feasible in your environment/setting though.

>

I have reason to keep two assert_equal in two #test_methods.
Bottom line, I need @foo, @run and @rerun initiated once and I might
have two or more methods like #test_run, #test_rerun, #test_rererun will
use @foo, @run and @rerun.

That's what I feared. Sorry, I have no idea how to accomplish that. :frowning:

Henrik

Ok, I should test before I post ;).

You can always use this:

require 'test/unit'

class TestClass < Test::Unit::TestCase
  puts "in setup"
  @@foo = Array.new
  
  def test_run
    assert_equal(@@foo, )
  end
  
  def test_rerun
    assert_equal(@@foo, )
  end
  
  def test_rererun
    assert_equal(@@foo, )
  end
end

$ ruby a.rb
in setup
Loaded suite a
Started
...
Finished in 0.002371 seconds.

3 tests, 3 assertions, 0 failures, 0 errors

Regards,

Brian

···

On Tue, 16 Nov 2004 03:10:28 +0900 Mohammad Khan <mkhan@lextranet.com> wrote:


> close, but no cigar

--
Brian Schröder
http://www.brian-schroeder.de/

Joel VanderWerf wrote:

Jamis Buck wrote:

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 :wink:

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 :slight_smile:

Eventually, indeed. :slight_smile: 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

···

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck.org/jamis

I am stuck! :frowning:
Anybody out there can help, please.

···

On Mon, 2004-11-15 at 11:51, Henrik Horneber wrote:

>>Don't know if this is
>>feasible in your environment/setting though.
>
> I have reason to keep two assert_equal in two #test_methods.
> Bottom line, I need @foo, @run and @rerun initiated once and I might
> have two or more methods like #test_run, #test_rerun, #test_rererun will
> use @foo, @run and @rerun.
>

That's what I feared. Sorry, I have no idea how to accomplish that. :frowning:

Henrik

--
Mohammad