Ruby Forum Ruby > instance_exec

Posted by unknown (Guest)
on 07.07.2006 19:24
(Received via mailing list)
i seems to recall someone came up with an impl of instance_exec 
(instance_eval
that takes args) a while back - might have even been me! ;-)  anyone 
remember?

-a
Posted by Marcel Molina Jr. (Guest)
on 07.07.2006 19:30
(Received via mailing list)
On Sat, Jul 08, 2006 at 02:22:31AM +0900, ara.t.howard@noaa.gov wrote:
> 
> i seems to recall someone came up with an impl of instance_exec 
> (instance_eval
> that takes args) a while back - might have even been me! ;-)  anyone 
> remember?

There is one in Rails' ActiveSupport:

class Object
  unless defined? instance_exec # 1.9
    def instance_exec(*arguments, &block)
      block.bind(self)[*arguments]
    end
  end
end

class Proc
  def bind(object)
    block, time = self, Time.now
    (class << object; self end).class_eval do
      method_name = "__bind_#{time.to_i}_#{time.usec}"
      define_method(method_name, &block)
      method = instance_method(method_name)
      remove_method(method_name)
      method
    end.bind(object)
  end
end

marcel
Posted by Sean O'halpin (sean)
on 07.07.2006 19:33
(Received via mailing list)
On 7/7/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
>
> i seems to recall someone came up with an impl of instance_exec (instance_eval
> that takes args) a while back - might have even been me! ;-)  anyone remember?
>
> -a
> --

You did indeed! And so did I. However, Mauricio Fernandez came up with
what seems to be a fairly complete implementation (thread safe,
handles frozen objects, etc.) at:

http://eigenclass.org/hiki.rb?cmd=view&p=instance_exec&key=instance_exec

Regards,
Sean
Posted by unknown (Guest)
on 07.07.2006 19:40
(Received via mailing list)
On Sat, 8 Jul 2006, Marcel Molina Jr. wrote:

>  unless defined? instance_exec # 1.9
>      method_name = "__bind_#{time.to_i}_#{time.usec}"
>      define_method(method_name, &block)
>      method = instance_method(method_name)
>      remove_method(method_name)
>      method
>    end.bind(object)
>  end
> end

thanks!  i just hacked this one out:

   harp:~ > cat a.rb
   class Object
     unless instance_methods.include? 'instance_exec'
       def instance_exec *a, &b
         m = "__instance_exec_#{ Thread.current.object_id.abs }__"
         singleton_class{ define_method m, &b  }
         send m, *a
       ensure
         singleton_class{ undef_method m  } rescue nil
       end
     end
     unless instance_methods.include? 'singleton_class'
       def singleton_class &b
         sc = class << self; self; end
         sc.module_eval &b if b
         sc
       end
     end
   end

   a = []
   a.instance_exec 42 do |elem|
     push elem
   end
   p a

   harp:~ > ruby a.rb
   [42]

regards.

-a
Posted by Sean O'halpin (sean)
on 07.07.2006 19:51
(Received via mailing list)
On 7/7/06, Marcel Molina Jr. <marcel@vernix.org> wrote:
>   unless defined? instance_exec # 1.9
>       method_name = "__bind_#{time.to_i}_#{time.usec}"
> Marcel Molina Jr. <marcel@vernix.org>
>
>
This is not thread-safe - relying on time.usec may not work on
sufficiently fast or clock-impaired machines.

Regards,
Sean
Posted by unknown (Guest)
on 07.07.2006 20:25
(Received via mailing list)
Marcel Molina Jr. wrote:
>   unless defined? instance_exec # 1.9
>       method_name = "__bind_#{time.to_i}_#{time.usec}"
>       define_method(method_name, &block)
>       method = instance_method(method_name)
>       remove_method(method_name)
>       method
>     end.bind(object)
>   end
> end

Huh. That looks like the one I wrote a while back (minus the time
stamp).

Mauricio Fernandez's looks better though --think I'll have to rip it
for Facets ;-)

T.
Posted by Sean O'halpin (sean)
on 08.07.2006 04:14
(Received via mailing list)
On 7/7/06, transfire@gmail.com <transfire@gmail.com> wrote:
> Huh. That looks like the one I wrote a while back (minus the time
> stamp).
>
> Mauricio Fernandez's looks better though --think I'll have to rip it
> for Facets ;-)
>
> T.
>
Do - it's the best version for 1.8 so far (IMHO).

Regards,
Sean
Posted by unknown (Guest)
on 09.07.2006 08:11
(Received via mailing list)
On Sat, 8 Jul 2006 transfire@gmail.com wrote:

> Mauricio Fernandez's looks better though --think I'll have to rip it
> for Facets ;-)

i just came across a bug in it (it's not re-entrant) - here's the fix:

   harp:~ > cat a.rb
   class Object
     unless instance_methods.include? 'instance_exec'
       def instance_exec *a, &b
         m, n = nil, -1
         loop{
           m = "__instance_exec_#{ Thread.current.object_id.abs }_#{ n 
+= 1 }__"
           break unless respond_to? m
         }
         singleton_class{ define_method m, &b  }
         send m, *a
       ensure
         singleton_class{ undef_method m  }
       end
     end

     unless instance_methods.include? 'singleton_class'
       def singleton_class &b
         sc =
           class << self; self; end
         sc.module_eval &b if b
         sc
       end
     end
   end

   a = []

   a.instance_exec 42 do |x|
     instance_exec x do |y|
       push y
       p size
     end
   end


   harp:~ > ruby a.rb
   1


figured you'd use this if anyone would tom... cheers.


-a
Posted by Mauricio Fernandez (Guest)
on 09.07.2006 10:16
(Received via mailing list)
On Sun, Jul 09, 2006 at 03:09:52PM +0900, ara.t.howard@noaa.gov wrote:
> >Mauricio Fernandez's looks better though --think I'll have to rip it
> >for Facets ;-)
> 
> i just came across a bug in it (it's not re-entrant) - here's the fix:
> 
[#instance_exec implementation]

The example you gave does work with my implementation too:

  RUBY_VERSION                                   # => "1.8.5"
  RUBY_RELEASE_DATE                              # => "2006-06-24"
  class Object
    module InstanceExecHelper; end
    include InstanceExecHelper
    def instance_exec(*args, &block)
      mname = 
"__instance_exec_#{Thread.current.object_id.abs}_#{object_id.abs}"
      InstanceExecHelper.module_eval{ define_method(mname, &block) }
      begin
        ret = send(mname, *args)
      ensure
        InstanceExecHelper.module_eval{ remove_method(mname) } rescue 
nil
                                                      # 
==========
                                                      #       thx to 
this
      end
      ret
    end
  end

  a = []

  a.instance_exec 42 do |x|
    instance_exec x do |y|
      push y
      p size
    end
  end

  a                                              # => [42]
  a.size                                         # => 1
# >> 1


I had actually a test for that (see test_instance_exec_nested below). Do 
you
have any additional assertions handy?
(So far test_instance_exec_with_frozen_obj fails in all the other
implementations I've seen.)

  require 'test/unit'
  class TestInstanceEvalWithArgs < Test::Unit::TestCase
    def test_instance_exec
      # Create a block that returns the value of an argument and a value
      # of a method call to +self+.
      block = lambda { |a| [a, f] }

      assert_equal [:arg_value, :dummy_value],
        Dummy.new.instance_exec(:arg_value, &block)
    end

    def test_instance_exec_with_frozen_obj
      block = lambda { |a| [a, f] }

      obj = Dummy.new
      obj.freeze
      assert_equal [:arg_value, :dummy_value],
        obj.instance_exec(:arg_value, &block)
    end

    def test_instance_exec_nested
      i = 0
      obj = Dummy.new
      block = lambda do |arg|
        [arg] + instance_exec(1){|a| [f, a] }
      end

      # the following assertion expanded by the xmp filter automagically 
from:
      # obj.instance_exec(:arg_value, &block) #=>
      assert_equal([:arg_value, :dummy_value, 1], 
obj.instance_exec(:arg_value, &block))
    end
  end
Posted by unknown (Guest)
on 09.07.2006 13:07
(Received via mailing list)
Thanks Ara.

Do you have a testcase I can use?

T.
Posted by Erik Veenstra (Guest)
on 10.07.2006 12:14
(Received via mailing list)
> The example you gave does work with my implementation too:
>
>  RUBY_VERSION                                   # => "1.8.5"
>  RUBY_RELEASE_DATE                              # => "2006-06-24"
>  class Object
>    module InstanceExecHelper; end
>    include InstanceExecHelper
>    def instance_exec(*args, &block)
>      mname = "__instance_exec_#{Thread.current.object_id.abs}_#{object_id.abs}"

If instance_exec is called recursively, like in the example,
the mname for the outer method is reused for defining the inner
method. This isn't really a problem, but conceptually not very
"clean"...

Did it work "by accident", or did you really intent to reuse
the name? ;]

>  InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil

This "rescue nil" might have been an indication of this "bad
design"... ;]

I wonder, why did you use a module for the context, instead of
the good old meta class?

gegroet,
Erik V. - http://www.erikveen.dds.nl/
Posted by Erik Veenstra (Guest)
on 10.07.2006 12:36
(Received via mailing list)
Okay, criticizing somebody's work without providing a
"solution", isn't nice at all...

So, here's my version...

gegroet,
Erik V. - http://www.erikveen.dds.nl/

PS: I'm still curious about the reason for using a module.

----------------------------------------------------------------

 class Object
   def instance_exec(*args, &block)
     object      = self
     res         = nil

     class << self
       self
     end.instance_eval do
       begin
         Thread.critical = true
         @_instance_exec_count_ ||= 0
         @_instance_exec_count_ += 1
         method_name = "_instance_exec_#{@_instance_exec_count_}_"
       ensure
         Thread.critical = false
       end

       begin
         define_method(method_name, &block)

         res = object.send(method_name, *args)
       ensure
         remove_method(method_name) rescue nil
       end
     end

     res
   end
 end
Posted by unknown (Guest)
on 10.07.2006 13:32
(Received via mailing list)
Erik Veenstra wrote:
> Okay, criticizing somebody's work without providing a
> "solution", isn't nice at all...
>
> So, here's my version...
>
> gegroet,
> Erik V. - http://www.erikveen.dds.nl/
>
> PS: I'm still curious about the reason for using a module.

I think the idea was to save frm creting a singleton class, but at the
expense of a module I don't see why either. Anyway, here the version I
put in Facets. Please evaluate.

module Kernel

  # Like instace_eval but allows parameters to be passed.
  #
  # The implementation is thread-safe thanks to the
  # Thread.current.object_id trick, but it doesn't work with
  # immediate values (Fixnums and friends), and most
  # importantly it bombs when given a frozen object.

  def instance_exec(*args, &block)
    mname =
"__instance_exec(#{Thread.current.object_id},#{caller.object_id})"
    Object.class_eval{ define_method(mname, &block) }
    begin
      ret = send(mname, *args)
    ensure
      Object.class_eval{ undef_method(mname) }
    end
    ret
  end

end
Posted by Erik Veenstra (Guest)
on 10.07.2006 14:28
(Received via mailing list)
> Anyway, here the version I put in Facets. Please evaluate.
>
>   # The implementation is thread-safe thanks to the
>   # Thread.current.object_id trick, but it doesn't work with
>   # immediate values (Fixnums and friends),...

(You're version _does_ work with immediate versions.)

Mmh... The Module version does work for immediate versions,
too. That might be a good reason... ;]

Mine doesn't...

>   # ...and most
>   # importantly it bombs when given a frozen object.
>
>     Object.class_eval{ define_method(mname, &block) }

This pollutes the global class Object. Not funny... ;] But,
yes, it does work...

The Module versions only pollutes the class Object with one,
dedicated Module. That's not too bad.

If we want to handle immediate values and we can't create
singleton classes for them, we have to abuse something.

Maybe, after all, the Module version isn't that bad... ;]

gegroet,
Erik V. - http://www.erikveen.dds.nl/
Posted by unknown (Guest)
on 10.07.2006 15:14
(Received via mailing list)
Erik Veenstra wrote:
>
> Mine doesn't...
>
> >   # ...and most
> >   # importantly it bombs when given a frozen object.
> >
> >     Object.class_eval{ define_method(mname, &block) }
>
> This pollutes the global class Object. Not funny... ;] But,
> yes, it does work...

It should delete it when done, so it's a temporary pollution. But I
think to do that right, instead of undef_method that should be
remove_method:

  Object.class_eval{ remove_method(mname) }

Good catch thanks.

T.
Posted by unknown (Guest)
on 10.07.2006 15:18
(Received via mailing list)
On Mon, 10 Jul 2006 transfire@gmail.com wrote:

>> PS: I'm still curious about the reason for using a module.
>  # Thread.current.object_id trick, but it doesn't work with
>      Object.class_eval{ undef_method(mname) }
>    end
>    ret
>  end
>
> end

-a
Posted by unknown (Guest)
on 10.07.2006 15:48
(Received via mailing list)
Duh! I'm sitting here screwing with this and the answer's sitting one
directory over.. with only a minor modification (I added recv=nil) I
introduce you to Facets' Proc#to_method:

  #--
  # Credit for #to_method goes to Forian Gross (flgr).
  #--

  require 'thread'

  class Proc

    MethodMutexes = Hash.new do |hash, key|
      hash[key] = Mutex.new
    end

    # Creates a local method based on a Proc.
    def to_method(name=nil, recv=nil)
      name ||= "!to_method_temp#{self.object_id}"
      recv ||= eval("self", self)
      klass = recv.class
      MethodMutexes[klass => name].synchronize do
        begin
          klass.send(:define_method, name, &self)
          return recv.method(name)
        ensure
          klass.send(:remove_method, name) if not name
        end
      end
    end

  end

Then instance_exec is simply:

  module Kernel

    def instance_exec(*args, &block)
      block.to_method(nil,self).call(*args)
    end

  end

Proof is in the pudding. Facets' really does pay off!

T.
Posted by Mauricio Fernandez (Guest)
on 10.07.2006 16:15
(Received via mailing list)
On Mon, Jul 10, 2006 at 08:29:46PM +0900, transfire@gmail.com wrote:
> > PS: I'm still curious about the reason for using a module.

* it allows instance_exec to work on frozen objects and immediate values
* it doesn't pollute Object with several methods (even if temporarily), 
only
  one module

>   # immediate values (Fixnums and friends), and most
>     ret
>   end
> 
> end

heh you copied the comment from my blog entry[1] but that's not the
implementation it applied to ;). The above does work with immediate 
values and
frozen objects. But I now consider it flawed nonetheless, read

 http://eigenclass.org/hiki.rb?bounded+space+instance_exec

[1] http://eigenclass.org/hiki.rb?instance_exec
Posted by Mauricio Fernandez (Guest)
on 10.07.2006 16:59
(Received via mailing list)
On Mon, Jul 10, 2006 at 10:44:32PM +0900, transfire@gmail.com wrote:
> Duh! I'm sitting here screwing with this and the answer's sitting one
> directory over.. with only a minor modification (I added recv=nil) I
> introduce you to Facets' Proc#to_method:
> 
>   #--
>   # Credit for #to_method goes to Forian Gross (flgr).
                                    (Florian)
>     # Creates a local method based on a Proc.
>     def to_method(name=nil, recv=nil)
>       name ||= "!to_method_temp#{self.object_id}"
                  ================================

This can create a lot of different symbols, leading to large memory 
leaks.

>       recv ||= eval("self", self)
>       klass = recv.class
>       MethodMutexes[klass => name].synchronize do
        ============================

This makes the memleak even heavier, going from potentially ~70 bytes to
nearly 300 bytes per call...

>         begin
>           klass.send(:define_method, name, &self)
            ==========

This will fail if the class was frozen.

See my blog for a better implementation.
Posted by Mauricio Fernandez (Guest)
on 10.07.2006 16:59
(Received via mailing list)
On Mon, Jul 10, 2006 at 07:33:34PM +0900, Erik Veenstra wrote:
> Okay, criticizing somebody's work without providing a
> "solution", isn't nice at all...

nah, what matters is *improving* the code (and focusing on it)

> PS: I'm still curious about the reason for using a module.

As I said, it reduces pollution and makes it work with frozen and 
immediate
values. See the unit tests and the explanation at
http://eigenclass.org/hiki.rb?instance_exec.

>          @_instance_exec_count_ ||= 0
======================
adds an instance variable for each object to which we send the 
instance_exec
message (fails for frozen objects, pollutes and costs more memory)
>          @_instance_exec_count_ += 1
>          method_name = "_instance_exec_#{@_instance_exec_count_}_"
>        ensure
>          Thread.critical = false
           =======================
The old Thread.critical value is lost, so this could easily break
multi-threaded code... It should be
  begin
      old, Thread.critical = Thread.critical, true
      ...
  ensure
      Thread.critical = old
  end

>        end
> 
>        begin
>          define_method(method_name, &block)
> 
>          res = object.send(method_name, *args)
>        ensure
>          remove_method(method_name) rescue nil
>        end
>      end

All in all, this implementation is the second best one as far as space
efficiency is concerned ;)

I don't like having to point to my own site repeatedly, but you should 
really
have a look at

  http://eigenclass.org/hiki.rb?bounded+space+instance_exec

So far all the implementations I've seen posted were worse than the one 
shown
there (and than the original one in my older blog entry). It's the only
thread-safe, reentrant, bounded-space, frozen-object-safe implementation 
as
far as I know.
Posted by unknown (Guest)
on 10.07.2006 17:26
(Received via mailing list)
Mauricio Fernandez wrote:
> On Mon, Jul 10, 2006 at 10:44:32PM +0900, transfire@gmail.com wrote:
> This can create a lot of different symbols, leading to large memory leaks.

> This makes the memleak even heavier, going from potentially ~70 bytes to
> nearly 300 bytes per call...

> This will fail if the class was frozen.

Right. I expected as much. But can you apply yuor fixes to
Proc#to_method?

T.
Posted by unknown (Guest)
on 10.07.2006 17:35
(Received via mailing list)
> Mauricio Fernandez wrote:

> This will fail if the class was frozen.

Actually, it seems to pass the frozen test.

T.
Posted by unknown (Guest)
on 10.07.2006 18:24
(Received via mailing list)
Okay, I think I've improved it some more:

require 'thread'

class Proc
  MethodMutexes = Hash.new do |hash, key|
    hash[key] = Mutex.new
  end

  # Creates a local method based on a Proc.
  def to_method(name=nil, recv=nil)
    unless name
      n = 0
      n += 1 while recv.respond_to?(name = "__to_method_#{n}")
    end
    recv ||= eval("self", self)
    klass = recv.class
    MethodMutexes[klass => name].synchronize do
      begin
        klass.send(:define_method, name, &self)
        return recv.method(name)
      ensure
        klass.send(:remove_method, name) unless name
      end
    end
  end
end

> This can create a lot of different symbols, leading to large memory leaks.
>
> >       recv ||= eval("self", self)
> >       klass = recv.class
> >       MethodMutexes[klass => name].synchronize do
>         ============================
>
> This makes the memleak even heavier, going from potentially ~70 bytes to
> nearly 300 bytes per call...

I don't understand this though. isn't the memory freed when done? Or is
some symbol getting created in the process, or ?

> >         begin
> >           klass.send(:define_method, name, &self)
>             ==========
>
> This will fail if the class was frozen.

Ah, I see if the _klass_ is frozen. But that will fail regardless won't
it? You can;t even include a module (ef InstanceExecHelper) in a frozen
class. Right?

Thanks,
T.
Posted by Mauricio Fernandez (Guest)
on 10.07.2006 19:41
(Received via mailing list)
On Tue, Jul 11, 2006 at 01:21:47AM +0900, transfire@gmail.com wrote:
> Okay, I think I've improved it some more:
> 
[...]
> > >       recv ||= eval("self", self)
> > >       klass = recv.class
> > >       MethodMutexes[klass => name].synchronize do
> >         ============================
> >
> > This makes the memleak even heavier, going from potentially ~70 bytes to
> > nearly 300 bytes per call...
> 
> I don't understand this though. isn't the memory freed when done? Or is
> some symbol getting created in the process, or ?

Two things:
* you're using a klass => name hash as the key (instead of [klass, 
name],
  which would take less memory)
* MethodMutexes grows monotonically since you don't delete the key => 
mutex
  associations when you're done, so you keep the keys _and_ the mutexes,
  which are pretty heavy

In the first version, the name included #{object_id}, so many values 
were
possible (as many as different Procs you call to_method on, modulo 
object_id
recycling). Different symbols and entries in the MethodMutexes hash 
would
accumulate.

> > >         begin
> > >           klass.send(:define_method, name, &self)
> >             ==========
> >
> > This will fail if the class was frozen.
> 
> Ah, I see if the _klass_ is frozen. But that will fail regardless won't
> it? You can;t even include a module (ef InstanceExecHelper) in a frozen
> class. Right?

Yes, that's why InstanceExecHelper must be included in Object (and you 
only
have to do that once).

Here's an improved (hastily written, not thoroughly tested) version:


class Object
  module InstanceExecHelper; end
  include InstanceExecHelper
end

class Proc
  # Creates a local method based on a Proc.
  def to_method(name=nil, recv=nil)
    recv ||= eval("self", self)
    do_remove = false
    begin
      old, Thread.critical = Thread.critical, true
      unless name
        do_remove = true
        n = 0
        n += 1 while recv.respond_to?(name = "__to_method_#{n}")
      end
      me = self
      InstanceExecHelper.module_eval{ define_method(name, &me) }
      return recv.method(name)
    ensure
      if do_remove
        InstanceExecHelper.module_eval{ remove_method(name) rescue nil }
      end
      Thread.critical = old
    end
  end
end

module Kernel
  def instance_exec(*args, &block)
      block.to_method(nil,self).call(*args)
  end
end


class Dummy
  def f
    :dummy_value
  end
end
Dummy.freeze

require 'test/unit'
class TestInstanceEvalWithArgs < Test::Unit::TestCase
  def test_instance_exec
    # Create a block that returns the value of an argument and a value
    # of a method call to +self+.
    block = lambda { |a| [a, f] }

    assert_equal [:arg_value, :dummy_value],
      Dummy.new.instance_exec(:arg_value, &block)
  end

  def test_instance_exec_with_frozen_obj
    block = lambda { |a| [a, f] }

    obj = Dummy.new
    obj.freeze
    assert_equal [:arg_value, :dummy_value],
      obj.instance_exec(:arg_value, &block)
  end

  def test_instance_exec_nested
    i = 0
    obj = Dummy.new
    block = lambda do |arg|
      [arg] + instance_exec(1){|a| [f, a] }
    end

    # the following assertion expanded by the xmp filter automagically 
from:
    # obj.instance_exec(:arg_value, &block) #=>
    assert_equal([:arg_value, :dummy_value, 1], 
obj.instance_exec(:arg_value, &block))
  end
end

# >> Loaded suite -
# >> Started
# >> ...
# >> Finished in 0.00091 seconds.
# >>
# >> 3 tests, 3 assertions, 0 failures, 0 errors
Posted by Erik Veenstra (Guest)
on 10.07.2006 22:16
(Received via mailing list)
> > I don't understand this though. isn't the memory freed when
> > done? Or is some symbol getting created in the process, or
> > ?

define_method creates a symbol, beneath the surface:

 symbols1 = Symbol.all_symbols
 Module.new.instance_eval{define_method("bar"){}}
 symbols2 = Symbol.all_symbols
 p symbols2-symbols1             # ===> [:bar]

> Yes, that's why InstanceExecHelper must be included in Object
> (and you only have to do that once).

You should include the Module before Object gets frozen. Who
wants to freeze Object, anyway?... ;]

>  Thread.critical = old

AFAIK, we need to be in critical mode when determining the
method name. As soon as we know it, we can (and should...)
return to old_critical. At least before we call the
block/method.

>  module Kernel
>    def instance_exec(*args, &block)

Kernel#instance_exec ? Does that make sense?

About $SAFE: It's possible to do an instance_eval up to level
5, whereas instance_exec fails on level 4. Challenge?... ;]

gegroet,
Erik V. - http://www.erikveen.dds.nl/
Posted by unknown (Guest)
on 10.07.2006 22:39
(Received via mailing list)
Mauricio Fernandez wrote:
> Here's an improved (hastily written, not thoroughly tested) version:

Okay, I took your draft and worked on it some more and came up with the
following, but there's something very strange....

require 'thread'

class Object
  module  TemporaryMethods; end
  include TemporaryMethods
end

class Proc

  # Creates a local method based on a Proc.
  def to_method(name=nil, recv=nil)
    recv ||= eval("self", self)
    klass = name ? recv.class : TemporaryMethods
    begin
      old, Thread.critical = Thread.critical, true
      unless name
        n = 0
        n += 1 while klass.method_defined?(name = "__to_method_#{n}")
        #n += 1 while recv.respond_to?(name = "__to_method_#{n}")
      end
      me = self
      klass.module_eval{ define_method(name, &me) }
      recv.method(name) #if klass == TemporaryMethods
    ensure
      if klass == TemporaryMethods
        klass.module_eval{ remove_method(name) } #rescue nil }
        #klass.module_eval{ undef_method(name) } #rescue nil }
      end
      Thread.critical = old
    end
  end

end


Notice the two remarks on n+=1... and klass.module_eval...., if you
unremark the first one, the one with recv.respond_to? it doesn't alwasy
see that the method has been removed -- I have to idea why that is
since it certainly has been. If you use undef_method instead of
remove_method, it work again though! That really blows my min b/c I
though undef_method didn;t actually remove the method. So I'm really
confused about that. Here's the test I use for the Symbols:

# This is dependent on other tests! Be aware!

    def test_memory
      a = 2
      tproc = proc { |x| x + a }
      100.times {
        tmethod = tproc.to_method
        assert_equal( 3, tmethod.call(1) )
      }
      meths = Symbol.all_symbols.select { |s| s.to_s =~ /^__to_method/
}
      assert_equal( 1, meths.size )
    end

Also is the 'resuce nil' really needed?

T.

P.S. Is there any reason you use module_eval { define_method...  rather
then send(:defined_method?...
Posted by Erik Veenstra (Guest)
on 10.07.2006 23:02
(Received via mailing list)
>     end
>
> Also is the 'resuce nil' really needed?

You can't remove a method if it isn't defined. If it's the
define_method which fails, you won't see the root cause.

Compare the scripts below. test1.rb doesn't give you a clue
about the real problem. test2.rb does.

It's probably better to give a real warning in this rescue
block, instead of returning just nil.

gegroet,
Erik V. - http://www.erikveen.dds.nl/

----------------------------------------------------------------

 $ cat test1.rb
 begin
   Object.define_method(:bar){}
 ensure
   Object.module_eval{ remove_method(:bar) }
 end

 $ cat test2.rb
 begin
   Object.define_method(:bar){}
 ensure
   Object.module_eval{ remove_method(:bar) } rescue nil
 end

 $ ruby test1.rb
 test1.rb:4:in `remove_method': method `bar' not defined in Object
(NameError)
         from test1.rb:4
         from test1.rb:4

 $ ruby test2.rb
 test2.rb:2: private method `define_method' called for Object:Class
(NoMethodError)