So, I want to define a singleton method for an object, but I want to do it using a closure.
For example,
def define_say(obj, msg)
def obj.say
puts msg
end
end
o = Object.new
define_say o, "hello world!"
o.say
This doesn't work because defining a singleton method via "def" is not a closure, so I get an exception that "msg" is an undefined variable or method.
What I would like to do is something like using the "define_method" method in the Module class, but as far as I can tell, this can only be used to define a method on a class... but I want a Singleton Method...
So, I would love to write it something like this:
def define_say(obj, msg)
obj.define_singleton_method(:say) {
puts msg
}
end
Does anyone know how I can achieve this without having to create a method to store a Proc and then use the Proc within a singleton method? (basically, I want a clean, non-hacky way of doing this)
Object#define_singleton_method was added to ruby-1.9.2 by the way:
def define_say(obj, msg)
obj.define_singleton_method(:say) do
puts msg
end
end
Here's an answer which does what you're looking for
def define_say(obj, msg)
# Get a handle to the singleton class of obj
metaclass = class << obj; self; end
# add the method using define_method instead of def x.say so we can use a closure
metaclass.send :define_method, :say do
puts msg
end
end
Usage (paste from IRB)
>> s = "my string"
=> "my string"
>> define_say(s, "I am S")
=> #<Proc:0xb6ed55b0#(irb):11>
>> s.say
I am S
=> nil
For more info (and a little library which makes it less messy) read this:
http://viewsourcecode.org/why/hacking/seeingMetaclassesClearly.html
As an aside, If you're a ruby programmer, and you HAVEN'T read that, go do it now~!
Related
I would like to implement something with a rescue-like syntax.
begin
raise "Failed"
rescue Exception => e
puts e
end
This works, and e is assigned to the corresponding value. But used in a method, this will raise an exception saying that e is undefined. In other words, how can rescue assigns something to e this way without throwing an undefined error?
class MyClass
def to_s
"hello"
end
end
def my_method
puts e.to_s
end
my_method(MyClass => e)
#=> undefined local variable or method `e' for main:Object
Perhaps what you are looking for is:
class MyClass
def self.hello
puts "This is a class method."
end
def bye
puts "This is an instance method."
end
end
def my_method(params)
klass = params[:class]
puts klass.hello # Call a class method
inst = klass.new # Create an instance
puts inst.bye # Call an instance method
end
my_method(:class => MyClass)
Three things to note:
Although the rescue syntax and the "named parameter" syntax look the same, all they have in common is the => operator. In the first case, you are telling to rescue the Exception "into" the variable e, effectively storing it in that variable. In the second case, you are telling Ruby to collect all parameters passed to the method and store them in a hash, using the supplied key/value pairs. Effectively, you are storing MyClass in the params hash, under the key :class.
In your above example, the to_s definition will not be callable on MyClass itself, because you defined it as an instance method. Instance methods are only available when you create an "instance" of the class with inst = MyClass.new. Then, you can call inst.to_s. Think of the class as an abstract "type" of thing, and of the instance as a concrete thing of that type. If you want the method to be available on the class, not the instances, you need to prefix it with self. I have illustrated the two different syntaxes above.
Again in your example, you are using def MyClass, which Ruby will interpret as "define a method with the name MyClass". If you want to define a class, you need to use class MyClass instead.
Hope this clarifies things a bit.
I am having a bit trouble to understand when "super" can be called and when not. In the below example the super method leads to a no superclass error.
class Bacterium
def eats
puts "Nam"
end
end
class Bacterium
def eats
super # -> no superclass error
puts "Yam"
end
end
b = Bacterium.new
b.eats
But this works:
class Fixnum
def times
super # -> works
puts "done"
end
end
5.times { |i| puts i.to_s }
Is 5 not just also an instance of Fixnum. And am I not redefining an existing method like in the Bacterium example above?
No, not really. Fixnum inherits from Integer class, and you are in fact overriding Integer#times, so super works, as it calls implementation from the parent.
In order to achieve something similar when monkeypatching, you should alias method before redefining it, and there call it by alias.
class Bacterium
alias_method :eats_original, :eats
def eats
eats_original # -> "Nam"
puts "Yam"
end
end
Class reopening is not a form of inheritance and super is of no use there.
Just as Mladen said, and you can check that with Class#superclass:
irb> Fixnum.superclass
=> Integer
And does Integer implement #times?:
irb> Integer.instance_methods.grep /times/
=> [:times]
Yes it does.
So, in a simplified way, we can say, that super invokes the method you are in of a superclass. In your case the superclass of a Bacterium is Object, which doesn't implement #eats.
I said this is very simplified, because look at this example:
module One
def hi
" World" << super()
end
end
module Two
def hi
"Hello" << super()
end
end
class SayHi
def hi
"!!!"
end
end
h = SayHi.new
h.extend(One)
h.extend(Two)
puts h.hi
#=> Hello World!!
Don't take to serious what I wrote here, it is actually tip of the iceberg of the Ruby object model, which is important to understand (I am still learning it) - then you will get most, or all of those concepts.
Use some Google-fu for "Ruby object model"...
I'm re-defining a method in an object in ruby and I need the new method to be a closure. For example:
def mess_it_up(o)
x = "blah blah"
def o.to_s
puts x # Wrong! x doesn't exists here, a method is not a closure
end
end
Now if I define a Proc, it is a closure:
def mess_it_up(o)
x = "blah blah"
xp = Proc.new {||
puts x # This works
end
# but how do I set it to o.to_s.
def o.to_s
xp.call # same problem as before
end
end
Any ideas how to do it?
Thanks.
This works (tested in irb):
NOTE: This changes only str - not all instances of String. Read below for details as to why this works
another_str = "please don't change me!"
str = "ha, try to change my to_s! hahaha!"
proc = Proc.new { "take that, Mr. str!" }
singleton_class = class << str; self; end
singleton_class.send(:define_method, :to_s) do
proc.call
end
puts str.to_s #=> "take that, Mr. str!"
puts another_str.to_s #=> "please don't change me!"
# What! We called String#define_method, right?
puts String #=> String
puts singleton_class #=> #<Class:#<String:0x3c788a0>>
# ... nope! singleton_class is *not* String
# Keep reading if you're curious :)
This works because you are opening str's singleton class and defining a method there. Because this, as well as the call to Module#define_method, have what some call a "flat scope", you're able to access variables that would be out of scope if you used def to_s; 'whatever'; end.
You may want to check out some of these other "metaprogramming spells" here:
media.pragprog.com/titles/ppmetr/spells.pdf
Why does it only change str?
Because Ruby has a couple interesting tricks up it's sleeves. In the Ruby object model, a method invocation results in the reciever searching not only it's class (and it's ancestors), but also it's singleton class (or as Matz would call it, it's eigenclass). This singleton class is what allows you to [re]define a method for a single object. These methods are called "singleton methods". In the example above, we are doing just that - defining a singleton method name to_s. It's functionaly identical to this:
def str.to_s
...
end
The only difference is that we get to use a closure when calling Module#define_method, whereas def is a keyword, which results in a change of scope.
Why can't it be simpler?
Well, the good news is that you're programming in Ruby, so feel free to go crazy:
class Object
def define_method(name, &block)
singleton = class << self; self; end
singleton.send(:define_method, name) { |*args| block.call(*args) }
end
end
str = 'test'
str.define_method(:to_s) { "hello" }
str.define_method(:bark) { "woof!" }
str.define_method(:yell) { "AAAH!" }
puts str.to_s #=> hello
puts str.bark #=> woof!
puts str.yell #=> AAAH!
And, if you're curious...
You know class methods? Or, in some languages, we'd call them static methods? Well, those don't really exist in Ruby. In Ruby, class methods are really just methods defined in the Class object's singleton class.
If that all sounds crazy, take a look at the links I provided above. A lot of Ruby's power can only be tapped into if you know how to metaprogram - in which case you'll really want to know about singleton classes/methods, and more generally, the Ruby object model.
HTH
-Charles
Feature #1082 implemented in Ruby 1.9.2 makes this an easy task with Object#define_singleton_method:
def mess_it_up(o)
x = "blah blah"
# Use Object#define_singleton_method to redefine `to_s'
o.define_singleton_method(:to_s) { x }
end
The concepts involved are still the same as in my previous answer, which provides a more in-depth description of how this works in Ruby's object model, as well as a Object#define_method definition that is conceptually the same as Ruby 1.9.2's Object#define_singleton_method.
Other methods that you might find useful for similar tasks:
Object#singleton_class
Object#singleton_methods
Object#respond_to_missing? (great blog post here)
This seems to work.
class Foo
def mess_it_up(o)
x = "blah blah"
o.instance_variable_set :#to_s_proc, Proc.new { puts x }
def o.to_s
#to_s_proc.call
end
end
end
var = Object.new
Foo.new.mess_it_up(var)
var.to_s
The problem is that code in def is not evaluated until it's run, and in a new scope. So you have to save the block to an instance variable on the object first and retieve it later.
And define_method doesn't work because it's a class method, meaning you would have to call it on the class of your object, giving that code to ALL instances of that class, and not just this instance.
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>
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
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
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
Previously, I asked about a clever way to execute a method on a given condition "Ruby a clever way to execute a function on a condition."
The solutions and response time was great, though, upon implementation, having a hash of lambdas gets ugly quite quickly. So I started experimenting.
The following code works:
def a()
puts "hello world"
end
some_hash = { 0 => a() }
some_hash[0]
But if I wrap this in a class it stops working:
class A
#a = { 0 => a()}
def a()
puts "hello world"
end
def b()
#a[0]
end
end
d = A.new()
d.b()
I can't see why it should stop working, can anyone suggest how to make it work?
that code doesn't work. it executes a at the time it is added to the hash, not when it is retrieved from the hash (try it in irb).
It doesn't work in the class because there is no a method defined on the class (you eventually define a method a on the instance.
Try actually using lambdas like
{0 => lambda { puts "hello world" }}
instead
First of all, you are not putting a lambda in the hash. You're putting the result of calling a() in the current context.
Given this information, consider what the code in your class means. The context of a class definition is the class. So you define an instance method called a, and assign a class instance variable to the a hash containing the result of calling a in the current context. The current context is the class A, and class A does not have a class method called a, so you're trying to put the result of a nonexistent method there. Then in the instance method b, you try to access an instance variable called #a -- but there isn't one. The #a defined in the class context belongs to the class itself, not any particular instance.
So first of all, if you want a lambda, you need to make a lambda. Second, you need to be clear about the difference between a class and an instance of that class.
If you want to make a list of method names to be called on certain conditions, you can do it like this:
class A
def self.conditions() { 0 => :a } end
def a
puts "Hello!"
end
def b(arg)
send self.class.conditions[arg]
end
end
This defines the conditions hash as a method of the class (making it easy to access), and the hash merely contains the name of the method to call rather than a lambda or anything like that. So when you call b(0), it sends itself the message contained in A.conditions[0], which is a.
If you really just want to pretty this sort of thing up,
why not wrap all your methods in a class like so:
# a container to store all your methods you want to use a hash to access
class MethodHash
alias [] send
def one
puts "I'm one"
end
def two
puts "I'm two"
end
end
x = MethodHash.new
x[:one] # prints "I'm one"
x.two # prints "I'm one"
or, to use your example:
# a general purpose object that transforms a hash into calls on methods of some given object
class DelegateHash
def initialize(target, method_hash)
#target = target
#method_hash = method_hash.dup
end
def [](k)
#target.send(#method_hash[k])
end
end
class A
def initialize
#a = DelegateHash.new(self, { 0 => :a })
end
def a()
puts "hello world"
end
def b()
#a[0]
end
end
x = A.new
x.a #=> prints "hello world"
x.b #=> prints "hello world"
One other basic error that you made is that you initialized #a outside of any instance method -
just bare inside of the definition of A. This is a big time no-no, because it just doesn't work.
Remember, in ruby, everything is an object, including classes, and the # prefix means the instance
variable of whatever object is currently self. Inside an instance method definitions, self is an instance
of the class. But outside of that, just inside the class definition, self is the class object - so you defined
an instance variable named #a for the class object A, which none of the instances of A can get to directly.
Ruby does have a reason for this behaviour (class instance variables can be really handy if you know what
you're doing), but this is a more advanced technique.
In short, only initialize instance variables in the initialize method.
table = {
:a => 'test',
:b => 12,
:c => lambda { "Hallo" },
:d => def print(); "Hallo in test"; end
}
puts table[:a]
puts table[:b]
puts table[:c].call
puts table[:d].send( :print )
Well, the first line in your class calls a method that doesn't exist yet. It won't even exist after the whole class is loaded though, since that would be a call to the class method and you've only defined instance methods.
Also keep in mind that {0 => a()} will call the method a(), not create a reference to the method a(). If you wanted to put a function in there that doesn't get evaluated until later, you'd have to use some kind of Lambda.
I am pretty new to using callbacks in Ruby and this is how I explained it to myself using an example:
require 'logger'
log = Logger.new('/var/tmp/log.out')
def callit(severity, msg, myproc)
myproc.call(sev, msg)
end
lookup_severity = {}
lookup_severity['info'] = Proc.new { |x| log.info(x) }
lookup_severity['debug'] = Proc.new { |x| log.debug(x) }
logit = Proc.new { |x,y| lookup_sev[x].call(y) }
callit('info', "check4", logit)
callit('debug', "check5", logit)
a = ->(string="No string passed") do
puts string
end
some_hash = { 0 => a }
some_hash[0].call("Hello World")
some_hash[0][]