Singleton methods without the singleton class

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:

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

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.

On Mar 15, 2:53 pm, Charles Oliver N. [email protected] wrote:

singleton method directly to the object itself. I would like to
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.)

On Mon, Mar 22, 2010 at 9:26 AM, Brian C. [email protected]
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

On Mon, Mar 22, 2010 at 1:44 PM, Intransition [email protected]
wrote:

How do you handle #extend?

I don’t :slight_smile:

#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

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs