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
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
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!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!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 FacetsT.
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
T.
On Sat, 8 Jul 2006 [email protected] wrote:
Mauricio F.'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
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 Facetsi 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:
# 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
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
endend
-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:
#ā
#ā
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.
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
[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
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
I donāt like having to point to my own site repeatedly, but you should
really
have a look at
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.
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.
Sponsor our Newsletter | Privacy Policy | Terms of Service | Remote Ruby Jobs