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.
Related
I would like to access a class' name in its superclass MySuperclass' self.inherited method. It works fine for concrete classes as defined by class Foo < MySuperclass; end but it fails when using anonymous classes. I tend to avoid creating (class-)constants in tests; I would like it to work with anonymous classes.
Given the following code:
class MySuperclass
def self.inherited(subclass)
super
# work with subclass' name
end
end
klass = Class.new(MySuperclass) do
def self.name
'FooBar'
end
end
klass#name will still be nil when MySuperclass.inherited is called as that will be before Class.new yields to its block and defines its methods.
I understand a class gets its name when it's assigned to a constant, but is there a way to set Class#name "early" without creating a constant?
I prepared a more verbose code example with failing tests to illustrate what's expected.
Probably #yield has taken place after the ::inherited is called, I saw the similar behaviour with class definition. However, you can avoid it by using ::klass singleton method instead of ::inherited callback.
def self.klass
#klass ||= (self.name || self.to_s).gsub(/Builder\z/, '')
end
I am trying to understand the benefit of being able to refer to an anonymous class by a name you have assigned to it after it has been created. I thought I might be able to move the conversation along by providing some code that you could look at and then tell us what you'd like to do differently:
class MySuperclass
def self.inherited(subclass)
# Create a class method for the subclass
subclass.instance_eval do
def sub_class() puts "sub_class here" end
end
# Create an instance method for the subclass
subclass.class_eval do
def sub_instance() puts "sub_instance here" end
end
end
end
klass = Class.new(MySuperclass) do
def self.name=(name)
#name = Object.const_set(name, self)
end
def self.name
#name
end
end
klass.sub_class #=> "sub_class here"
klass.new.sub_instance #=> "sub_instance here"
klass.name = 'Fido' #=> "Fido"
kn = klass.name #=> Fido
kn.sub_class #=> "sub_class here"
kn.new.sub_instance #=> "sub_instance here"
klass.name = 'Woof' #=> "Woof"
kn = klass.name #=> Fido (cannot change)
There is no way in pure Ruby to set a class name without assigning it to a constant.
If you're using MRI and want to write yourself a very small C extension, it would look something like this:
VALUE
force_class_name (VALUE klass, VALUE symbol_name)
{
rb_name_class(klass, SYM2ID(symbol_name));
return klass;
}
void
Init_my_extension ()
{
rb_define_method(rb_cClass, "force_class_name", force_class_name, 1);
}
This is a very heavy approach to the problem. Even if it works it won't be guaranteed to work across various versions of ruby, since it relies on the non-API C function rb_name_class. I'm also not sure what the behavior will be once Ruby gets around to running its own class-naming hooks afterward.
The code snippet for your use case would look like this:
require 'my_extension'
class MySuperclass
def self.inherited(subclass)
super
subclass.force_class_name(:FooBar)
# work with subclass' name
end
end
For a better understanding of Ruby I decided to recreate the attr_accessor method. Succesfully. I now understand how it works except for one detail regarding Ruby's syntactic sugar. Here's the attr_accessor method I created:
def attr_accessor(*attributes)
attributes.each do |a|
# Create a setter method (obj.name=)
setter = Proc.new do |val|
instance_variable_set("##{a}", val)
end
# Create a getter method (obj.name)
getter = Proc.new do
instance_variable_get("##{a}")
end
self.class.send(:define_method, "#{a}=", setter)
self.class.send(:define_method, "#{a}", getter)
end
end
The way I see it, I just defined two methods, obj.name as getter and obj.name= as the setter. But when I execute the code in IRB and call obj.name = "A string" it still works, even though I defined the method without a space!
I know this is just part of the magic that defines Ruby, but what exactly makes this work?
When the ruby interpreter sees obj.name = "A string, it will ignore the space between name and = and look for a method named name= on your obj.
Never mind, 'A string' is a perfectly good message name, just try
obj.send "A string" # ^_^
You can even use numbers:
o = Object.new
o.define_singleton_method "555" do "kokot" end
o.send "555"
I am unsure about the difference between this.
def String.hello
puts "hello there"
end
and
x = Person.new
def x.hello
puts "hello there"
end
From my understanding the second code block will create an object of class Person. When I do the def x.hello it creates an anonymous class (singleton class) that will be checked first for methods when sending a message to the x object.
Is this the same case for the def String.hello? String is just an instance of class Class correct? I have read that doing def String.hello will add the method as one of String's class methods.... this would be different than an anonymous class being created that sits in between the object and its class where it gets its instance methods.
What happens with both blocks of code above?
I love this part of ruby. There is this beautiful symmetry where most of the core functionality is just sugar over the advanced functionality, so once you fully grok a concept, you can apply that understanding to a lot of the language.
Is this the same case for the def String.hello? String is just an instance of class Class correct?
Yes, you are creating an instance of Class, and assigning it to a constant.
I have read that doing def String.hello will add the method as one of String's class methods.... this would be different than an anonymous class being created that sits in between the object and its class where it gets its instance methods.
Nope, the piece you are missing is thinking its possible to have a class level method WITHOUT adding it to a singleton class. What you have is an object that is an instance of Class, and you are adding methods to an implicit class that sits inbetween it and Class. You will also see this syntax sometimes
class << self
def method
end
end
That is doing the same thing, just being very explicit about it.
Just to add to the Matt's answer:
Both examples do the same thing, Writting them in another way:
String = Class.new # < done inside ruby initialization
def String.hello
puts "hello there"
end
and
x = Person.new
def x.hello
puts "hello there"
end
On Ruby you can add methods to a class, created with A = Class.new or with the syntax sugar class A; ...; end, or to a Eigenclass, that exists for every object. Class methods are, on really, methods of the Eigenclass of an instance of Class, think about what is "self" in def self.method; ...; end. Eigenclasses can be opened with this sintax:
x = Person.new
class << x
# ...
end
As Eigenclasses are also instances of class (try to add p self.class on last example) they also have Eigenclasses and so on. If it appears to be confusing, just remember that Object is a class and Class is an object. This is why I love Ruby!
The following code will add the "hello" method to the String class, that way all following strings will have the "hello" method:
def String.hello
puts "hello there"
end
Whilst the following code, will add the "hello" method, to the instance x of the class Person. If you create a new Person, that object will not have the "hello" method.
x = Person.new
def x.hello
puts "hello there"
end
That is the main difference, as I understand it.
Does this help?
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"...
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~!