Singleton methods without the singleton class

Discussion in 'Ruby' started by Charles Oliver Nutter, Mar 15, 2010.

  1. Hi all!

    JRuby currently allows you to add singleton methods to normal Java
    objects like this:

    foo = java.lang.String.new('blah')
    def foo.bar
    # do something
    end

    Unfortunately, this feature requires us to keep a weak table of all
    the Java objects that enter Ruby space, since we can't attache this
    singleton method directly to the object itself. I would like to
    eliminate this feature at some point, but I recognize that since
    people are using it we need an alternative.

    So I've hacked together a short script that provides a "Singletonizer"
    module that can simulate singleton methods without actually creating a
    new singleton class:

    http://gist.github.com/333174

    The basic idea is to go ahead and add the method to the actual class,
    but add it via a hashed lookup on a per-instance basis. Objects which
    have had the method added will have a corresponding entry in their
    attached_methods table. Objects that don't will raise NoMethodError as
    usual.

    Thoughts? Comments? This version is obviously not threadsafe, but it
    gets you pretty close to singleton methods without requiring singleton
    classes.

    - Charlie
     
    Charles Oliver Nutter, Mar 15, 2010
    #1
    1. Advertising

  2. Charles Nutter wrote:
    > Thoughts? Comments? This version is obviously not threadsafe, but it
    > gets you pretty close to singleton methods without requiring singleton
    > classes.


    What if you are defining a singleton method to override a method which
    already exists in the class? It's fixable...

    module Singletonizer
    def attach_method(name, &block)
    (@attached_methods ||= {})[name] = block
    class_method = "__class_#{name}"
    return if respond_to? class_method
    if respond_to? name
    self.class.class_eval "alias :#{class_method} :#{name}"
    else
    self.class.class_eval <<-RUBY
    def #{class_method}(*args)
    ex = NoMethodError.new("undefined method `#{name}' for
    \#{self.inspect}:\#{self.class}")
    ex.set_backtrace caller(2)
    raise ex
    end
    RUBY
    end
    self.class.class_eval <<-RUBY, __FILE__, __LINE__
    def #{name}(*args)
    if (defined? @attached_methods) && (block =
    @attached_methods[:#{name}])
    instance_exec(*args, &block)
    else
    __class_#{name}(*args)
    end
    end
    RUBY
    end
    end

    ...unless someone comes along and decides to change the definition in
    the class :-(

    I note that using the block syntax for defining singleton methods means
    that singleton methods can't take a block. But that's not an problem if
    this is just a demo of something which will take place under the hood.
    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Mar 22, 2010
    #2
    1. Advertising

  3. Charles Oliver Nutter

    Intransition Guest

    On Mar 15, 2:53=A0pm, Charles Oliver Nutter <> wrote:
    > JRuby currently allows you to add singleton methods to normal Java
    > objects like this:
    >
    > foo =3D java.lang.String.new('blah')
    > def foo.bar
    > =A0 # do something
    > end
    >
    > Unfortunately, this feature requires us to keep a weak table of all
    > the Java objects that enter Ruby space, since we can't attache this
    > singleton method directly to the object itself. I would like to
    > eliminate this feature at some point, but I recognize that since
    > people are using it we need an alternative.
    >
    > So I've hacked together a short script that provides a "Singletonizer"
    > module that can simulate singleton methods without actually creating a
    > new singleton class:
    >
    > http://gist.github.com/333174
    >
    > The basic idea is to go ahead and add the method to the actual class,
    > but add it via a hashed lookup on a per-instance basis. Objects which
    > have had the method added will have a corresponding entry in their
    > attached_methods table. Objects that don't will raise NoMethodError as
    > usual.
    >
    > Thoughts? Comments? This version is obviously not threadsafe, but it
    > gets you pretty close to singleton methods without requiring singleton
    > classes.


    How do you handle #extend?

    I've always thought it would be wise if singleton methods were added
    to an anonymous module rather then directly to the object.* Perhaps
    taking this approach will work for JRuby. While not exactly like MRI
    it should be close enough for all practical purposes.

    (* Which is why Facets extends the #extend method to do exactly that
    if you supply it a block.)
     
    Intransition, Mar 22, 2010
    #3
  4. On Mon, Mar 22, 2010 at 9:26 AM, Brian Candler <> wrote:
    > What if you are defining a singleton method to override a method which
    > already exists in the class? It's fixable...


    The problem with this is that it then makes all callers to all
    instances of that class go through all this logic. Maybe that's not a
    big deal?

    I've thrown this stuff into a github project here (without your
    changes for the moment):

    http://github.com/headius/singletonizer

    I also added a mutex around the whole body of attach_method and
    renamed attach_method to "def", so the singletonizing looks a bit more
    like normal singleton methods:

    instead of

    def obj.foo
    blah
    end

    you do

    obj.def :foo do
    blah
    end

    > I note that using the block syntax for defining singleton methods means
    > that singleton methods can't take a block. But that's not an problem if
    > this is just a demo of something which will take place under the hood.


    Yeah, I don't know of a way to make that work using just Ruby
    features; it would be possible to do it under the covers in JRuby,
    though.

    - Charlie
     
    Charles Oliver Nutter, Mar 22, 2010
    #4
  5. On Mon, Mar 22, 2010 at 1:44 PM, Intransition <> wrote:
    > How do you handle #extend?


    I don't :)

    #extend would be hard to support with this, since module methods are
    often only callable against the object themselves; in other words, if
    the module doesn't *actually* get inserted into the object's class
    hierarchy, the methods won't be invokable.

    Again, this can be forced under the covers in JRuby, but not through
    any normal Ruby mechanisms I know of.

    > I've always thought it would be wise if singleton methods were added
    > to an anonymous module rather then directly to the object.* Perhaps
    > taking this approach will work for JRuby. While not exactly like MRI
    > it should be close enough for all practical purposes.
    >
    > (* Which is why Facets extends the #extend method to do exactly that
    > if you supply it a block.)


    Yes, that's probably wise, but #extend is mostly a deal-breaker in any
    form because it forces a singleton object to be created.

    - Charlie
     
    Charles Oliver Nutter, Mar 22, 2010
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?RGlmZmlkZW50?=
    Replies:
    5
    Views:
    471
    Bruce Barker
    Jan 13, 2006
  2. Oltmans
    Replies:
    6
    Views:
    362
    Terry Reedy
    Mar 11, 2009
  3. Paul McMahon
    Replies:
    3
    Views:
    213
    David A. Black
    Jun 9, 2008
  4. Kenneth McDonald
    Replies:
    5
    Views:
    345
    Kenneth McDonald
    Sep 26, 2008
  5. Daniel DeLorme
    Replies:
    14
    Views:
    249
    Brian Candler
    Dec 14, 2008
Loading...

Share This Page