Instance_exec

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

    begin
      klass.module_eval{ define_method(name, &me) }
    ensure
        klass.module_eval{ remove_method(name) } #rescue nil }
    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)

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

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

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

···

On Mon, Jul 10, 2006 at 07:33:34PM +0900, Erik Veenstra wrote:

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

           ======================
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 :wink:

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.

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

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/

-a

···

On Mon, 10 Jul 2006 transfire@gmail.com wrote:

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

--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

> 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

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

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

···

On Mon, Jul 10, 2006 at 08:29:46PM +0900, transfire@gmail.com wrote:
--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

Erik Veenstra wrote:

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

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.