Mucking about with dynamically adding methods to objects

P

Paul Smith

I've been toying with Ruby for a while, but only now am I beginning to
understand some of the amazing properties Ruby has. I was simply
floored when I realise you could add methods to an object dynamically.

So I saw this:

obj=Object.new

def obj.talk
puts "Hello!"
end

and was stunned. obj now has a talk method!

I took it to the next logical step, making a class which could add
methods to objects:

class FairyGodmother
def talkify(obj)
def obj.talk
puts "I'm a real boy!"
end
end
end

fairy = FairyGodother.new()
pinocchio = Object.new()

fairy.talkify(pinochio)

pinochio.talk # Success!

But my next logical leap doesn't work:

Changing the talkify method to this:

def talkify(obj, str)
def obj.talk
puts str
end
end

doesn't do what I mean - what it does is create a method called talk
which tries to output obj.str which doesn't exist. What I want is for
the obj.talk method to return the literal string given to the
fairy.talkify call.

How do I convince Ruby to evaluate str?

(Apologies if this is answered somewhere in The Well Grounded Rubyist
- I haven't finished the book yet :) )
 
J

Jesús Gabriel y Galán

But my next logical leap doesn't work:

Changing the talkify method to this:

def talkify(obj, str)
=A0def obj.talk
=A0 =A0puts str
=A0end
end

doesn't do what I mean - what it does is create a method called talk
which tries to output obj.str which doesn't exist. =A0What I want is for
the obj.talk method to return the literal string given to the
fairy.talkify call.

The problem here is that def starts a new scope, so str is undefined there.
This is a try with define_object instead:

irb(main):012:0> class A
irb(main):013:1> def talkify(obj,str)
irb(main):014:2> (class << obj;self;end).instance_eval do
irb(main):015:3* define_method :talk do
irb(main):016:4* puts str
irb(main):017:4> end
irb(main):018:3> end
irb(main):019:2> end
irb(main):020:1> end
=3D> nil
irb(main):021:0> o =3D Object.new
=3D> #<Object:0xb7d62cf4>
irb(main):023:0> A.new.talkify o,"hi"
=3D> #<Proc:0xb7d64f40@(irb):15>
irb(main):024:0> o.talk
hi
=3D> nil

We call define_method in the singleton class of obj. We need the
instance_eval because define_method is private.

Hope this helps,

Jesus.
 
P

Paul Smith

2009/9/17 Jes=FAs Gabriel y Gal=E1n said:
The problem here is that def starts a new scope, so str is undefined ther= e.
This is a try with define_object instead:

irb(main):012:0> class A
irb(main):013:1> def talkify(obj,str)
irb(main):014:2> (class << obj;self;end).instance_eval do
irb(main):015:3* define_method :talk do
irb(main):016:4* puts str
irb(main):017:4> end
irb(main):018:3> end
irb(main):019:2> end
irb(main):020:1> end
=3D> nil
irb(main):021:0> o =3D Object.new
=3D> #<Object:0xb7d62cf4>
irb(main):023:0> A.new.talkify o,"hi"
=3D> #<Proc:0xb7d64f40@(irb):15>
irb(main):024:0> o.talk
hi
=3D> nil

We call define_method in the singleton class of obj. We need the
instance_eval because define_method is private.

Hope this helps,

Thanks

I think I have a lot more reading to do before

irb(main):014:2> (class << obj;self;end).instance_eval do
irb(main):015:3* define_method :talk do

makes any kind of sense.
--=20
Paul Smith
http://www.nomadicfun.co.uk

(e-mail address removed)
 
J

Jesús Gabriel y Galán

2009/9/17 Jes=FAs Gabriel y Gal=E1n said:
I think I have a lot more reading to do before

irb(main):014:2> (class << obj;self;end).instance_eval do
irb(main):015:3* define_method :talk do

makes any kind of sense.

David's article is a good starting point to understand singleton
classes and methods. Anyway,
(class << obj; self; end) is a very common idiom to access the
singleton class of an object.
Some people even do:

class Object
def singleton_class
class << self; self; end;
end
end

Maybe with this addition, what I wrote is a little bit more clear:

obj.singleton_class.instance_eval do
define_method :talk do
puts str
end
end

As we need to refer to str, we use define_method, which doesn't define
a new scope, so str is available in the block passed to define_method,
which is a closure. In order to call define_method, which is private,
we need an environment in which self is the object that we wan't to
call define_method on*. In our case this object is the singleton class
of "obj". The method instance_eval does exactly that: evaluate the
block in a context in which self is set to the receiver of
instance_eval.

Hope this clarifies a little bit more.

* Another way is to use send

obj.singleton_class.send:)define_method, :talk) do
puts str
end

but I like instance_eval better.

Jesus.
 
D

David Masover

I think I have a lot more reading to do before

irb(main):014:2> (class << obj;self;end).instance_eval do
irb(main):015:3* define_method :talk do

makes any kind of sense.

Break it down into pieces. You already know how to do:

def obj.talk
...
end

Or, to define a class method:

Class Foo
def self.bar
...
end
end
Foo.bar

The next step is to realize that this is equivalent to:

class << obj
def talk
...
end
end

It might not be immediately obvious, but just take my word for it: that is
equivalent to 'def obj.talk'.

But it looks like a class definition -- and it sort of is. It's the metaclass
-- you can think of it as a class that only that object belongs to, which
inherits from the object's real, official class.

Now, think about this example:

class Foo
def self.bar

You might've already done it this way:

def Foo.bar

So what is self inside a class? Easy, it's the class itself.

So, what's self inside "class << object"? It's the metaclass I just described.

So, if you do this:

meta_class = class << object
self
end

you extract the metaclass. (The last statement in a class definition is the
return value of the class definition.)

Once you understand that much, instance_eval may make sense. In fact, you
could do this:

class Object
def metaclass
class << self; self; end
end
end

Now you can do object.metaclass.instance_eval. This is so incredibly common
that it could be shortened, too:

class Object
def meta_eval &block
metaclass.instance_eval &block
end
end

I use this a lot -- even when "class << obj" would work just as well. And I'm
blatantly stealing this from _why -- he distributed it as a gem called
'metaid'. His Rubyforge account seems to work, and the gem seems to be alive,
so I think it's safe -- especially since it's so fundamental and obvious (17
lines of code) that it's not hard to replace if you have to.

So, do this instead:

gem install metaid

require 'metaid'
def talkify(obj, str)
obj.meta_eval do
define_method :talk do
str
end
end
end

I might shorten it like this, if it fits well:

require 'metaid'
def talkify obj, str
obj.metaclass.send :define_method, :talk do
str
end
end

It's probably covered somewhere in that book, but even once it is, I still
suggest metaid.
 
7

7stud --

Paul said:
Thanks

I think I have a lot more reading to do before

irb(main):014:2> (class << obj;self;end).instance_eval do
irb(main):015:3* define_method :talk do

makes any kind of sense.

This isn't going to work:

def talkify(obj, str)
def obj.talk
puts str
end
end

because in ruby nested methods do not form "closures". That means a
nested method cannot see the local variables(including the parameter
variables) in the enclosing method.

However, blocks can see variables in the surrounding scope, for example:

def meth
x = 10
arr = [1, 2, 3]

arr.each{|num| puts num + x}
end

--output:--
11
12
13

So that suggests a strategy for devising a solution to your problem:
try to employ a block to capture the variables in the surrounding scope
instead of a method definition.

This is what I came up with:

class FairyGodmother


def get_singleton_class(obj)

class << obj
self
end

end


end


1) Inside a class definition, like:

class << obj
self
end

and outside any method definitions, self is the class object, which in
this case is the singleton class of obj.


2) A class definition actually returns the last statement evaluated
inside the class, for instance:

return_val = class A
10
end

p return_val

--output:--
10

3) A method returns the value of the last statement evaluated in the
method.

The method get_singleton_class(obj) returns the singelton class of obj
because this:

def get_singleton_class(obj)

class << obj
self
end

end

becomes this:

def get_singleton_class(obj)

obj_singleton

end

which returns obj_singleton. (See p. 392 in your book if that's not
clear.)


Then you can define talkify() like this:

class FairyGodmother

def talkify(obj, str)
obj_singleton = get_singleton_class(obj)
p = Proc.new {puts str}
obj_singleton.send:)define_method, "talk", p)
end

end

fairy = FairyGodmother.new()
pinochio = Object.new()

fairy.talkify(pinochio, "hello")
pinochio.talk


--output:--
hello
 
7

7stud --

7stud said:
However, blocks can see variables in the surrounding scope, for example:

def meth
x = 10
arr = [1, 2, 3]

arr.each{|num| puts num + x}
end

--output:--
11
12
13

I guess a more pertinent example would be:

def meth(val)
arr = [1, 2, 3]

arr.each{|num| puts num + val}
end

meth(10)

--output:--
11
12
13
 
P

Paul Smith

7stud -- wrote:

<snip>

Thank you very much for all your careful explanations - Ruby is making
a lot more sense to me as the days go by :)

To put it in context, I learned C++ some 7 years ago or so, and was
blown away by how OO changed what I'd learnt wih Basic and Assembler
up to that point. I thought I knew about OO until I hit Ruby about 2
years ago, but didn't seriously look into the language. This time
around, things are making even more sense to me in Ruby, and I'm
enjoying the ride - I'm even using Ruby in the Google Code Jam this
year just for the fun of it.

It's been said before, but Ruby programming really is fun.
 
7

7stud --

Paul said:
I think I have a lot more reading to do before
makes any kind of sense.

I just started chapter 14 of the "The Well Grounded Rubyist", and
instance_eval() and define_method() are explained in regards to a
similar example.

Something that bothered me when I initially read that code was the use
of instance_eval(). I was left wondering why the more natural-reading
class_eval() wasn't used? After all, the goal is to create a method in
the singleton *class*.

You can do this with class_eval():

class FairyGodmother

def get_singleton_class(obj)

class << obj
self
end
#explained on p.392

end


def talkify(obj, str)
obj_singleton = get_singleton_class(obj)

obj_singleton.class_eval %Q{

def talk
puts "hello"
end

}

end
end


...but if you try this:


def talkify(obj, str)
obj_singleton = get_singleton_class(obj)

obj_singleton.class_eval %Q{

def talk
puts str #<---CHANGE HERE****
end

}

end


...it doesn't work. The def creates a new scope, and str is not defined
inside the def.

Using a string as an argument for class_eval() is unwieldy. Luckily,
class_eval() will take a block--instead of a string:

def talkify(obj, str)
obj_singleton = get_singleton_class(obj)

obj_singleton.class_eval do

def talk
puts 'hello'
end

end

But once again, the def creates a new scope, so the code inside the def
can't reference variables outside the def--like str(which is one of
talkify()'s parameter variables).

define_method() to the rescue:

def talkify(obj, str)
obj_singleton = get_singleton_class(obj)

obj_singleton.class_eval do

define_method("talk") do
puts str
end

end

Neither of the blocks used with class_eva() or define_method() creates a
new scope, so code inside them can make use of variables defined in the
surrounding scope.

Anyway, after working through that example, I wonder if there is a
difference between instance_eval() and class_eval() when the receiver is
a class?
 
7

7stud --

7stud said:
...but if you try this:


def talkify(obj, str)
obj_singleton = get_singleton_class(obj)

obj_singleton.class_eval %Q{

def talk
puts str #<---CHANGE HERE****
end

}

end


...it doesn't work. The def creates a new scope, and str is not defined
inside the def.

To be clearer, that should read:

...it doesn't work. When the string is evaluated inside the singleton
class, the def creates a new scope, and the str variable is not defined
inside the def.
 
7

7stud --

7stud said:
define_method() to the rescue:

def talkify(obj, str)
obj_singleton = get_singleton_class(obj)

obj_singleton.class_eval do

define_method("talk") do
puts str
end

end

Neither of the blocks used with class_eva() or define_method() creates a
new scope, so code inside them can make use of variables defined in the
surrounding scope.

Hmm...I've decided that I find that explanation totally unsatisfying.
If define_method() really created a method, then inside the created
method str would be undefined. Since str isn't undefined when the talk
'method' is called, define_method() must really create a closure
somewhere along the line, i.e. something like a Proc object. Maybe
define_method() creates a method that calls a Proc object? For
instance, something like this:

def talk
a_proc.call
end

where a_proc is created like this:

a_proc = Proc.new {puts str}

which forms a closure, so all the variables in scope at the time a_proc
was created can be referenced inside a_proc.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,152
Members
46,697
Latest member
AugustNabo

Latest Threads

Top