Factory Patterns in Ruby

Factory is a very common pattern in the java world, in some places
it's almost considered 'Evil (TM)' to have any sort of knowledge of
object construction.

In the Ruby world, where Classes and Objects seem to be de-emphasised,
constructing an object with .new doesn't seem to be held as such a bad
thing.

I know there are a few IOC containers in the RubyWorld, but they dont
seem to be overly popular.

Why might this be so?

cheers
lyndon

I'm not sure that IoC is the only "valid" solution.

Consider Rails, which uses convention (namely, the URL of the request) to
determine which object to create.

···

On 7/25/05, Lyndon Samson <lyndon.samson@gmail.com> wrote:

Factory is a very common pattern in the java world, in some places
it's almost considered 'Evil (TM)' to have any sort of knowledge of
object construction.

In the Ruby world, where Classes and Objects seem to be de-emphasised,
constructing an object with .new doesn't seem to be held as such a bad
thing.

I know there are a few IOC containers in the RubyWorld, but they dont
seem to be overly popular.

Why might this be so?

probably because 'new' is a method and really doesn't give any clue as to how
an object is contructed - this is how i generally implement factory

   class Factory
     class TypeA; end
     class TypeB; end
     class TypeC; end

     def Factory::new(arg, *a, &b)
       klass =
         case arg
           when /a/
             TypeA
           when /b/
             TypeB
           when /b/
             TypeC
         end
       klass::new(*a, &b)
     end
   end

although the returned type might depend on file extension or something else.
since 'new' is just a method on a class object it's always free to return
anything it likes. the nice thing about this is that, in ruby, __every__ call
to new is, by definition, the factory pattern - it just so happens that there
is a default implementation inherited by class 'Class'. simplicity.

cheers.

-a

···

On Tue, 26 Jul 2005, Lyndon Samson wrote:

Factory is a very common pattern in the java world, in some places
it's almost considered 'Evil (TM)' to have any sort of knowledge of
object construction.

In the Ruby world, where Classes and Objects seem to be de-emphasised,
constructing an object with .new doesn't seem to be held as such a bad
thing.

I know there are a few IOC containers in the RubyWorld, but they dont
seem to be overly popular.

Why might this be so?

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

There is a joke going around the Ruby community that there are more DI/IoC
frameworks in Ruby than there are people actually using the frameworks.

<OSCON teaser>
BTW, this is a good lead in to my OSCON talk on dependency injection.
</OSCON teaser>

···

On Monday 25 July 2005 10:17 pm, Lyndon Samson wrote:

I know there are a few IOC containers in the RubyWorld, but they dont
seem to be overly popular.

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Would you mind giving an example of how you'd use that class? How
does it benefit the programmer?

···

On 7/25/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

On Tue, 26 Jul 2005, Lyndon Samson wrote:

> Factory is a very common pattern in the java world, in some places
> it's almost considered 'Evil (TM)' to have any sort of knowledge of
> object construction.
>
> In the Ruby world, where Classes and Objects seem to be de-emphasised,
> constructing an object with .new doesn't seem to be held as such a bad
> thing.
>
> I know there are a few IOC containers in the RubyWorld, but they dont
> seem to be overly popular.
>
> Why might this be so?

probably because 'new' is a method and really doesn't give any clue as to how
an object is contructed - this is how i generally implement factory

   class Factory
     class TypeA; end
     class TypeB; end
     class TypeC; end

     def Factory::new(arg, *a, &b)
       klass =
         case arg
           when /a/
             TypeA
           when /b/
             TypeB
           when /b/
             TypeC
         end
       klass::new(*a, &b)
     end
   end

although the returned type might depend on file extension or something else.
since 'new' is just a method on a class object it's always free to return
anything it likes. the nice thing about this is that, in ruby, __every__ call
to new is, by definition, the factory pattern - it just so happens that there
is a default implementation inherited by class 'Class'. simplicity.

* Jim Weirich <jim@weirichhouse.org> [2005-07-26 13:11:43 +0900]:

···

On Monday 25 July 2005 10:17 pm, Lyndon Samson wrote:
> I know there are a few IOC containers in the RubyWorld, but they dont
> seem to be overly popular.

There is a joke going around the Ruby community that there are more DI/IoC
frameworks in Ruby than there are people actually using the frameworks.

Yes, and aren't there only two DI/IoC frameworks that exist? :wink:

Didn't I read somewhere that an IoC in Ruby was a way to make
something really easy very hard? (Yes, you read that correctly.)
--
Jim Freeze

Rails also uses an abstract factory approach in ActiveRecord to
establish a database connection at run-time using the correct database
adapter.

Take a look at the ActiveRecord::Base.establish_connection method:

well, __that__ class isn't useful to anyone :wink: but if you have something
like this perhaps

   class JobRunner
     class AbstractJobRunner
       def initialize job
         @job = job
         @logger = Logger::new
       end
       def run
         @job.run 'logger' => @logger
       end
       def download_input src, dst
         raise NotImplementedError
       end
       def upload_output src, dst
         raise NotImplementedError
       end
     end
     class FTPJobRunner < AbstractJobRunner
       def download_input src, dst
         ...
       end
       def upload_output src, dst
         ...
       end
     end
     class HTTPJobRunner < AbstractJobRunner
       def download_input src, dst
         ...
       end
       def upload_output src, dst
         ...
       end
     end
     class SCPJobRunner < AbstractJobRunner
       def download_input src, dst
         ...
       end
       def upload_output src, dst
         ...
       end
     end
     class << self
       def new job
         klass =
           case job.input
             when %r|^ftp://|
               FTPJobRunner
             when %r|^scp://|
               SCPJobRunner
             when %r|^http://|
               HTTPJobRunner
           end
         klass::new job
       end
     end
   end

   class Job
     def initialize command, input, output
       ...
     end
   end

then all over your code just reads

   job_runner = JobRunner::new job
   job_runner.run

and you don't really care what kind of JobRunner it is... if this stuff were
in a library, and more kinds of JobRunners are later added, client code need
not change. of course there are about a million ways to do this in ruby, but
an object factory is nice wherever your code needs an object that does 'x'
based on 'y' - you can encapsulate that logic and, later, change it only in
one place with a factory. the built-in uri class does something similar:

   jib:~/eg/ruby > irb -r uri
   irb(main):001:0> URI::parse 'http://foobar'
   => #<URI::HTTP:0x…fdba9cc00 URL:http://foobar>

   irb(main):002:0> URI::parse 'ftp://foobar'
   => #<URI::FTP:0x..fdba9bc06 URL:ftp://foobar>

   irb(main):003:0> URI::parse 'scp://foobar'
   => #<URI::Generic:0x..fdba9ac16 URL:scp://foobar>

of course it's using 'parse' instead of 'new' - but the idea is the same. you
just want a url-type object, but you don't really care what kind of object it
is so long as it supports some set of methods you'll be using later - nice with
duck typing :wink:

hth.

-a

···

On Tue, 26 Jul 2005, Joe Van Dyk wrote:

On 7/25/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

On Tue, 26 Jul 2005, Lyndon Samson wrote:

Factory is a very common pattern in the java world, in some places
it's almost considered 'Evil (TM)' to have any sort of knowledge of
object construction.

In the Ruby world, where Classes and Objects seem to be de-emphasised,
constructing an object with .new doesn't seem to be held as such a bad
thing.

I know there are a few IOC containers in the RubyWorld, but they dont
seem to be overly popular.

Why might this be so?

probably because 'new' is a method and really doesn't give any clue as to how
an object is contructed - this is how i generally implement factory

   class Factory
     class TypeA; end
     class TypeB; end
     class TypeC; end

     def Factory::new(arg, *a, &b)
       klass =
         case arg
           when /a/
             TypeA
           when /b/
             TypeB
           when /b/
             TypeC
         end
       klass::new(*a, &b)
     end
   end

although the returned type might depend on file extension or something else.
since 'new' is just a method on a class object it's always free to return
anything it likes. the nice thing about this is that, in ruby, __every__ call
to new is, by definition, the factory pattern - it just so happens that there
is a default implementation inherited by class 'Class'. simplicity.

Would you mind giving an example of how you'd use that class? How
does it benefit the programmer?

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

There's three. Joel Van Der Werf has made one, too. Copland certainly
fits your description; Needle and Joel's framework less so, as I
understand it.

-austin

···

On 7/26/05, Jim Freeze <jim@freeze.org> wrote:

* Jim Weirich <jim@weirichhouse.org> [2005-07-26 13:11:43 +0900]:
> On Monday 25 July 2005 10:17 pm, Lyndon Samson wrote:
> > I know there are a few IOC containers in the RubyWorld, but they dont
> > seem to be overly popular.
> There is a joke going around the Ruby community that there are more DI/IoC
> frameworks in Ruby than there are people actually using the frameworks.
Yes, and aren't there only two DI/IoC frameworks that exist? :wink:
Didn't I read somewhere that an IoC in Ruby was a way to make
something really easy very hard? (Yes, you read that correctly.)

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

Jim Freeze wrote:

* Jim Weirich <jim@weirichhouse.org> [2005-07-26 13:11:43 +0900]:

I know there are a few IOC containers in the RubyWorld, but they dont
seem to be overly popular.

There is a joke going around the Ruby community that there are more DI/IoC
frameworks in Ruby than there are people actually using the frameworks.

Yes, and aren't there only two DI/IoC frameworks that exist? :wink:

Well, I account for one DI framework (MinDI [1]) and zero users.... I
still haven't been involved in a project that had the right kind of
complexity to justify DI.

But DI frameworks are fun to develop. Maybe we should have a ruby quiz
to develop DI frameworks, and then we will have 20 more frameworks and
no more users :wink:

[1] http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/120297

···

On Monday 25 July 2005 10:17 pm, Lyndon Samson wrote:

Here's the ones I know about:

* Copland -- Hivemind based from Jamis Buck

* DI -- The version I wrote about in response to Jamis's Copland talk. (see
http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc\)

* Needle -- Again from Jamis, based DI.

* MinDI -- Minimal framework from Joel VanderWerf
(http://redshift.sourceforge.net/mindi\)

* seep -- Inspired by Spring (i.e. lots of XML), by Gary Shea
(http://seep.rubyforge.org/index.html\)

* DIM -- Dependency Injection, Minimal, 30 lines of Ruby Code, developed for
my OSCON talk (currently unreleased ... I'll post it after OSCON).

···

On Tuesday 26 July 2005 11:37 am, Jim Freeze wrote:

> There is a joke going around the Ruby community that there are more
> DI/IoC frameworks in Ruby than there are people actually using the
> frameworks.

Yes, and aren't there only two DI/IoC frameworks that exist? :wink:

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Jim Freeze <jim@freeze.org> writes:

* Jim Weirich <jim@weirichhouse.org> [2005-07-26 13:11:43 +0900]:

> I know there are a few IOC containers in the RubyWorld, but they dont
> seem to be overly popular.

There is a joke going around the Ruby community that there are more DI/IoC
frameworks in Ruby than there are people actually using the frameworks.

Yes, and aren't there only two DI/IoC frameworks that exist? :wink:

Count me as a user of one, Needle.

Was it worth using? I think so. In the meantime, I have adopted my
application to several purposes, that are partly quite different to
what I originally thought of. I think using DI helped a great lot
making my software more flexible.

Of course, it depends on the size of your application. If it isn't
even medium, using DI is likely to be a waste of time.

Didn't I read somewhere that an IoC in Ruby was a way to make
something really easy very hard? (Yes, you read that correctly.)

#ruby-lang 28 May 2005
21:53 <slyphon> i think i've finally figured out needle
21:53 <slyphon> it's a library that makes things complicated

···

On Monday 25 July 2005 10:17 pm, Lyndon Samson wrote:

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

    class << self

What dows mean that line of code?

Ara.T.Howard wrote:
...

  class JobRunner
    class AbstractJobRunner

...

    end
    class FTPJobRunner < AbstractJobRunner

...

    end
    class HTTPJobRunner < AbstractJobRunner

...

    end
    class SCPJobRunner < AbstractJobRunner

...

    end
    class << self
      def new job
        klass =
          case job.input
            when %r|^ftp://|
              FTPJobRunner
            when %r|^scp://|
              SCPJobRunner
            when %r|^http://|
              HTTPJobRunner
          end
        klass::new job
      end
    end
  end

In this kind of situation, I like to try to refactor so that the
switching is done in the subclasses rather than a big case (as I'm sure
you would have done if this had been a real example with a large number
of cases). That can be done, for example, by:

  require 'subclass-keeper' # see below

  class JobRunner
    class AbstractJobRunner
      extend SubclassKeeper
      def initialize(*args); end
    end
    class FTPJobRunner < AbstractJobRunner
      PAT = %r|^ftp://|
    end
    class HTTPJobRunner < AbstractJobRunner
      PAT = %r|^http://|
    end
    class SCPJobRunner < AbstractJobRunner
      PAT = %r|^scp://|
    end
    class << self
      def new job
        klass = AbstractJobRunner.proper_subclasses.find do |c|
          c::PAT === job.input
        end
        klass::new job
      end
    end
  end

  job = Object.new
  def job.input; "scp://foo.bar"; end

  jr = JobRunner.new job
  p jr.class # ==> JobRunner::SCPJobRunner

----- subclass-keeper.rb ----

# Extend a class by SubclassKeeper to record all subclasses (including
itself)
# and make the list available with the #subclasses method.
module SubclassKeeper
  def inherited(sub)
    super
    add_subclass(sub)
  end
  def add_subclass(sub)
    superclass.add_subclass(sub) if superclass.respond_to? :add_subclass
    (@proper_subclasses ||= ) << sub
  end
  protected :add_subclass
  def proper_subclasses
    (@proper_subclasses ||= ).dup
  end
  def subclasses
    proper_subclasses.unshift(self)
  end
end

if __FILE__ == $0
  class Base
    extend SubclassKeeper
  end

  class Sub1 < Base; end
  class Sub2 < Sub1; end

  p Base.subclasses
  p Sub1.subclasses
  p Sub2.subclasses
end

Thought I'd poke my head up at this point. I signed on this list a
half-hour or so ago, intending to do a bit of Seep-publicizing, just in
time to see Jim Weirich take care of it for me! Crazy! Thanks!

I'm the author of Seep, which Jim has characterized perfectly...Spring
like, lots of XML. If you hate XML, you'll hate Seep :slight_smile:

The beauty of Seep (and this is entirely stolen from Spring) is that it
is versatile enough to give you 1) all that DI stuff, and 2) a unifying
syntax for configuration. I can (and do) configure pretty much
everything in my applications, from ActiveRecord mappers to Log4r, using
Seep.

I'm a Ruby beginner, so I hope some of you hard-core Ruby folk take a
look at Seep. I could use some feedback on what would help it be more
useful.

Regards,

    Gary

···

On Wed, 2005-07-27 at 02:36 +0900, Jim Weirich wrote:

On Tuesday 26 July 2005 11:37 am, Jim Freeze wrote:
> > There is a joke going around the Ruby community that there are more
> > DI/IoC frameworks in Ruby than there are people actually using the
> > frameworks.
>
> Yes, and aren't there only two DI/IoC frameworks that exist? :wink:

Here's the ones I know about:

* Copland -- Hivemind based from Jamis Buck

* DI -- The version I wrote about in response to Jamis's Copland talk. (see
http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc\)

* Needle -- Again from Jamis, based DI.

* MinDI -- Minimal framework from Joel VanderWerf
(http://redshift.sourceforge.net/mindi\)

* seep -- Inspired by Spring (i.e. lots of XML), by Gary Shea
(http://seep.rubyforge.org/index.html\)

* DIM -- Dependency Injection, Minimal, 30 lines of Ruby Code, developed for
my OSCON talk (currently unreleased ... I'll post it after OSCON).

Christian Neukirchen said:

There is a joke going around the Ruby community that there are more
DI/IoC
frameworks in Ruby than there are people actually using the frameworks.

Count me as a user of one, Needle.

Was it worth using? I think so. In the meantime, I have adopted my
application to several purposes, that are partly quite different to
what I originally thought of. I think using DI helped a great lot
making my software more flexible.

nukumi2, right? I think Jamis told me you were using needle for that.

Would you mind filling me in on how you are using Needle and what your
impressions are? Would you use it in another project? If you don't mind,
I would like to use your feedback in the summary section of my talk on
Dep/Inj in Ruby.

Thanks.

···

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

EdUarDo wrote:

    class << self

What dows mean that line of code?

A way to get at the "singleton class" for an object. For more info, see:

http://phrogz.net/ProgrammingRuby/classes.html#objectspecificclasses

Devin

<snip nice code>

i totally agree - defintely put the pattern in the class since it belongs
there... otth you could abort early quite easily with conflicting/poorly
written patterns (remembering that part of a framework like this would be to
allow others to plugin classes) and this bug would be very hard to find, eg

   client code:

     class FOOJobRunner < AbstractJobRunner
       PAT = %r|foo|
     end
     class BARJobRunner < AbstractJobRunner
       PAT = %r|bar|
     end
     class FOOBARJobRunner < AbstractJobRunner
       PAT = %r|foobar|
     end

now, depending on the order of classes given by proper_subclasses you may or
may not to the right thing so probably you'd need

   class << self
     def new job
       scs = AbstractJobRunner.proper_subclasses
       klass = nil
       scs.each do |sc|
         if sc::PAT === job.input
           raise if klass
           klass = sc
         end
       end
       klass.first::new job
     end
   end

or similar in order to make sure that there is one, and only one, match
amongst subclasses...

cheers.

-a

···

On Wed, 27 Jul 2005, Joel VanderWerf wrote:

Ara.T.Howard wrote:
...

  class JobRunner
    class AbstractJobRunner

...

    end
    class FTPJobRunner < AbstractJobRunner

...

    end
    class HTTPJobRunner < AbstractJobRunner

...

    end
    class SCPJobRunner < AbstractJobRunner

...

    end
    class << self
      def new job
        klass =
          case job.input
            when %r|^ftp://|
              FTPJobRunner
            when %r|^scp://|
              SCPJobRunner
            when %r|^http://|
              HTTPJobRunner
          end
        klass::new job
      end
    end
  end

In this kind of situation, I like to try to refactor so that the
switching is done in the subclasses rather than a big case (as I'm sure
you would have done if this had been a real example with a large number
of cases). That can be done, for example, by:

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================