Reputation: 14524
I wish to add a singleton method to a particular object. I wish that when a instance method on a object is first called, it does some work, and then creates a singleton method for said object of the same name (that contains the work). On all subsequent calls on said object, the singleton method would shadow the instance method and would be called.
I know how to create a singleton method, my problem is that I want the singleton method created to call a lambda (l
in this case). def does not create a closure, so I cannot reference variable l (code below) when the method is subsequently called (l.call()
is commented out in this example) I wish to know how I can create a closure when creating a singleton method on a particular object. Any help would be appreciated. Thank you.
class Thing
end
t = Thing.new
t2 = Thing.new
Thing.instance_eval() do
def speak
puts "I speak for all Things, I am a class method"
end
end
Thing.class_eval() do
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
l = lambda {puts r}
instance_eval() do
def speak()
puts "This is the singleton method in the Thing object #{self}"
#l.call() # I want this to work! How?
end
end
end
end
Thing.speak()
t.speak()
t2.speak()
t.speak()
t2.speak()
Gives the following results when run: (I changed '<' to '#' so they show up in html)
I speak for all Things, I am a class method
This is the instance method referenced by the Thing object #Thing:0x1d204>
This is the instance method referenced by the Thing object #Thing:0x1d1dc>
This is the singleton method in the Thing object #Thing:0x1d204>
This is the singleton method in the Thing object #Thing:0x1d1dc>
Upvotes: 2
Views: 1470
Reputation: 9026
Now that 1.9 is out, you can use define_singleton_method:
jruby --1.9 -S irb
irb(main):019:0> fn = -> { length * 10 }
=> #<Proc:0x77cb8e0f@(irb):19 (lambda)>
irb(main):020:0> s.define_singleton_method :length_times_ten, fn
=> #<Proc:0x77cb8e0f@(irb):19 (lambda)>
irb(main):021:0> s
=> "a string"
irb(main):022:0> s.length_times_ten
=> 80
Upvotes: 2
Reputation: 89063
Well, one way to do it would be to pack it into an instance variable:
(FYI you can just do class Thing
to reopen Thing
(it's a little shorter than using #class_eval
, and you don't need #instance_eval
to define methods from within a method).
class Thing
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
@l = lambda {puts r}
instance_eval do
def speak()
puts "This is the singleton method in the Thing object #{self}"
@l[]
end
end
end
end
This will redefine #speak
, but only for that instance of Thing
. Other instances of Thing
will still have the original definition.
The alternative is, as Chuck pointed out, to use the singleton class (aka metaclass, aka eigenclass) associated with the instance. The singleton class is the object that stores all the singleton methods associated with an object. You can get the context for singleton class evaluation by using the funny class <<object ; ... ; end
syntax (similar to the context given by #class_eval
by normal classes).
class Thing
def speak
puts "This is the instance method referenced by the Thing object #{self}"
r = "something I wish to hold on to, maybe expensive to calculate"
singleton_class = class <<self # open singleton class context for current instance
# in this context, self now refers to the singleton class itself
self
end
l = lambda {puts r}
singleton_class.class_eval do
# since we used #class_eval, local variables are still in scope
define_method(:speak) do
puts "This is the singleton method in the Thing object #{self}"
# since we used #define_method, local variables are still in scope
l[]
end
end
end
end
Upvotes: 1
Reputation: 237060
You can define a method with a block using define_method
.
Example:
class Object
def eigenclass
class <<self; self end
end
end
a = "Hello"
other_word = "World"
a.eigenclass.class_eval do
define_method(:cliche) {"#{self} #{other_word}"}
end
a.cliche # => "Hello World"
"Goodbye".cliche # => NoMethodError: undefined method `cliche' for "Goodbye":String
Here is an implementation of a define_singleton_method
method:
class Object
def define_singleton_method(name, &block)
eigenclass = class<<self; self end
eigenclass.class_eval {define_method name, block}
end
end
Upvotes: 2