Forum: Ruby Mucking about with dynamically adding methods to objects

5afcc79e025fb4840980cc167bb3f236?d=identicon&s=25 Paul Smith (Guest)
on 2009-09-17 11:44
(Received via mailing list)
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 :) )
--
Paul Smith
http://www.nomadicfun.co.uk

paul@pollyandpaul.co.uk
E088bb5c80fd3c4fd02c2020cdacbaf0?d=identicon&s=25 Jesús Gabriel y Galán (Guest)
on 2009-09-17 13:59
(Received via mailing list)
On Thu, Sep 17, 2009 at 11:44 AM, Paul Smith <paul@pollyandpaul.co.uk>
wrote:

> 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.

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
=> nil
irb(main):021:0> o = Object.new
=> #<Object:0xb7d62cf4>
irb(main):023:0> A.new.talkify o,"hi"
=> #<Proc:0xb7d64f40@(irb):15>
irb(main):024:0> o.talk
hi
=> 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.
5afcc79e025fb4840980cc167bb3f236?d=identicon&s=25 Paul Smith (Guest)
on 2009-09-17 17:30
(Received via mailing list)
2009/9/17 Jesús Gabriel y Galán <jgabrielygalan@gmail.com>:
>> end
> irb(main):013:1> def talkify(obj,str)
> irb(main):023:0> A.new.talkify o,"hi"
> => #<Proc:0xb7d64f40@(irb):15>
> irb(main):024:0> o.talk
> hi
> => 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.
--
Paul Smith
http://www.nomadicfun.co.uk

paul@pollyandpaul.co.uk
F53b05cdbdf561cfe141f69b421244f3?d=identicon&s=25 David A. Black (Guest)
on 2009-09-17 18:15
(Received via mailing list)
Hi --

On Fri, 18 Sep 2009, Paul Smith wrote:

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

Here's a starting point: http://www.wobblini.net/singletons.html


David
E088bb5c80fd3c4fd02c2020cdacbaf0?d=identicon&s=25 Jesús Gabriel y Galán (Guest)
on 2009-09-17 19:02
(Received via mailing list)
On Thu, Sep 17, 2009 at 5:28 PM, Paul Smith <paul@pollyandpaul.co.uk>
wrote:
> 2009/9/17 Jesús Gabriel y Galán <jgabrielygalan@gmail.com>:
>> On Thu, Sep 17, 2009 at 11:44 AM, Paul Smith <paul@pollyandpaul.co.uk> wrote:

> 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.
1bc63d01bd3fcccc36fb030a62039352?d=identicon&s=25 David Masover (Guest)
on 2009-09-17 20:07
(Received via mailing list)
On Thursday 17 September 2009 10:28:25 am Paul Smith wrote:

> 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.
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-09-17 20:11
Paul Smith wrote:
> 2009/9/17 Jes�s Gabriel y Gal�n <jgabrielygalan@gmail.com>:
>>> end
>> irb(main):013:1> def talkify(obj,str)
>> irb(main):023:0> A.new.talkify o,"hi"
>> => #<Proc:0xb7d64f40@(irb):15>
>> irb(main):024:0> o.talk
>> hi
>> => 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.
>

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
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-09-17 20:17
7stud -- wrote:
> 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
5afcc79e025fb4840980cc167bb3f236?d=identicon&s=25 Paul Smith (Guest)
on 2009-09-17 23:46
(Received via mailing list)
On Thu, Sep 17, 2009 at 7:17 PM, 7stud -- <bbxx789_05ss@yahoo.com>
wrote:
> 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.

--
Paul Smith
http://www.nomadicfun.co.uk

paul@pollyandpaul.co.uk
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-09-19 04:10
Paul Smith wrote:
> 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.

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?
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-09-19 04:14
7stud -- wrote:
>
> ...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.
54404bcac0f45bf1c8e8b827cd9bb709?d=identicon&s=25 7stud -- (7stud)
on 2009-09-19 07:58
7stud -- wrote:
>
> 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.
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.