Instance_exec

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! :wink: anyone
remember?

-a

On Sat, Jul 08, 2006 at 02:22:31AM +0900, [email protected] 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! :wink: 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

On 7/7/06, [email protected] [email protected] 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! :wink: anyone remember?

-a

You did indeed! And so did I. However, Mauricio F. 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

On 7/7/06, Marcel Molina Jr. [email protected] wrote:

unless defined? instance_exec # 1.9
method_name = ā€œ_bind#{time.to_i}_#{time.usec}ā€
Marcel Molina Jr. [email protected]

This is not thread-safe - relying on time.usec may not work on
sufficiently fast or clock-impaired machines.

Regards,
Sean

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

On 7/7/06, [email protected] [email protected] wrote:

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

Mauricio F.'s looks better though --think Iā€™ll have to rip it
for Facets :wink:

T.

Do - itā€™s the best version for 1.8 so far (IMHO).

Regards,
Sean

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 F.'s looks better though --think Iā€™ll have to rip it
for Facets :wink:

T.

On Sat, 8 Jul 2006 [email protected] wrote:

Mauricio F.'s looks better though --think Iā€™ll have to rip it
for Facets :wink:

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

Thanks Ara.

Do you have a testcase I can use?

T.

On Sun, Jul 09, 2006 at 03:09:52PM +0900, [email protected] wrote:

Mauricio F.'s looks better though --think Iā€™ll have to rip it
for Facets :wink:

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

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/

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

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/

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

On Mon, 10 Jul 2006 [email protected] 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

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.

On Mon, Jul 10, 2006 at 08:29:46PM +0900, [email protected] 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

eigenclass.org

[1] eigenclass.org

On Mon, Jul 10, 2006 at 10:44:32PM +0900, [email protected] 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.

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

On Mon, Jul 10, 2006 at 07:33:34PM +0900, Erik V. 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
eigenclass.org.

     @_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

eigenclass.org

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.