This is a multi-part message in MIME format. ------ extPart_000_001F_01C32050.04CF41B0 Content-Type: text/plain; charset so-8859-1" Content-Transfer-Encoding: 7bit "Hal E. Fulton" <hal9000 / hypermetrics.com> schrieb im Newsbeitrag news:0d1f01c31fed$2b5978e0$0300a8c0 / austin.rr.com... > ----- Original Message ----- > From: "Robert" <bob.news / gmx.net> > Newsgroups: comp.lang.ruby > To: "ruby-talk ML" <ruby-talk / ruby-lang.org> > Sent: Wednesday, May 21, 2003 5:39 PM > Subject: Re: How I'd like method-wrapping to work... > > > > That's no fantasy. Dreams come true... > > The plane, the plane! (Sorry, showing my age...) > (For non-Americans and/or people under 35: A reference > to the TV show _Fantasy Island_.) I just know a hit by, I think it was "Tight Fit" with the same name. The 80's... > Thanks for this fascinating reply... You're welcome! I'm always amazed what can be done with Ruby, it's really a jewel. Sometimes it's even scaring... > > > 3. I've probably overlooked a lot of issues. Please > > > educate me (Matz, Guy, Dave, David, others...) > > > Although not listed this bugged me. See what I came up with (attached). > > Not listed? :) > > others.include?(Robert) # true Oooops, my implementation of include? seems to be broken. Thank's for the fix. :-) > > Cons: > > - 'this' had to be introduced since I found no way to > > rebind the proc (even if there were a way we had > > to deal with multiple concurrent invocations). The > > deeper reason for this problem is, that the block > > comes into existence in class context but should be > > evaluated in instance context later. > > I was having a hard time with that, too, and I wasn't > even worrying about threads. That's the single biggest problem for the hook approach with blocks. If that could be solved, maybe by a method Proc#rebind( binding ) Proc returning a new proc with the given binding we had the perfect solution. I think there were discussions about rebinding somethread else. > > - Argument lists have to be doubled for the block > > Hmm, not sure what you mean here. But I am going to > play with this a little. If you want to access method arguments in the block you have to include them like I did in the second test script (attached). > > Pros: > > - It nearly looks like what you asked for. > > - Return values work reasonably. > > By "reasonably" do you mean that the return value of > prior will be used even if that call is not the last > one inside the hook block? Ideally we shouldn't have > to do the "val" stuff that I do. I'll test. No, I meant, that you can decide whether you want to propagate them or override them. But it can be fixed to always return the original. Lemme see... Done. If I find some time I might even include a test for duplicate prior calls from the same block. Oops, that was easy. I'll have to stop that... Please don't come up with further feature requests. :-)) > > - It even works for nested invocations, i.e. recursion > > - It works with several instances in the call chain > > This is essential if I understand you rightly. It's also > one reason we need this -- methods are sometimes aliased > more than once, e.g., in a library and in your own code. Yep, that was what I suspected was the typical case. You don't want to worry whether someone else hooked a method already. > > - You can exactly do what you put in <fantasy> > > So I can! I see that the code in hook_test is line for line > the same as mine. Si! > However, on 1.7.3 on Windows I got a syntax error on > line 40 of hooks.rb (had to remove the "proc &"). Not > sure what the issue is here -- assume it worked for you > that way? Strange, I got no error with 1.7.3 on Win. Need to investigate this. The version I have here at work complains, too. > Thanks for a quick reply... Oh, I took some detours in between... (I dropped a version with a String instead a block.) > this is another bit of code > that I'll certainly save. That's very kind of you. > I do hope the discussion continues, though... Of course! There's always room for improvement (see attached :-)) Kind regards robert ------ extPart_000_001F_01C32050.04CF41B0 Content-Type: application/octet-stream; name ooks.rb" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename ooks.rb" class Object private def prior thr = Thread.current raise "Just one invocation of prior allowed!" if thr[ :__hook_prior ] thr[ :__hook_prior ] = true ths = thr[ :__hook_self ] sym = thr[ :__hook_next ] raise "No more methods in chain" unless sym begin puts '>>> nest' if $VERBOSE result = ths.send( sym, *(thr[ :__hook_args ])) puts '<<< unnest' if $VERBOSE thr[ :__hook_result ] = result return result ensure thr[ :__hook_next ]= sym thr[ :__hook_self ]= ths end end def this Thread.current[ :__hook_self ] end end class Module private def set_hook_proc( key, pr ) (@hook_procs ||= {})[ key ]= pr end public def get_hook_proc( key ) (@hook_procs ||= {})[ key ] end def define_hook( symbol, &block ) al = generate_hook_alias symbol set_hook_proc( al, proc ) x = <<-"EO_HOOK" alias #{al} #{symbol} def #{symbol}(*args) thr = Thread.current thr[ :__hook_self ]= self thr[ :__hook_args ]= args thr[ :__hook_next ]= :#{al} thr[ :__hook_prior ]= false result = #{self}.get_hook_proc( "#{al}" ).call( *args ) thr.key?( :__hook_result ) ? thr[ :__hook_result ] : result end EO_HOOK # puts x self.class_eval x # p @hook_procs end private def generate_hook_alias( symbol ) im = instance_methods count = 0 begin s = "__hook__#{symbol}_#{count}" count += 1 end while im.include? s s end end ------ extPart_000_001F_01C32050.04CF41B0 Content-Type: application/octet-stream; name nother_test.rb" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename nother_test.rb" require 'hooks.rb' class Foo attr :len, true def initialize; @len ; end def bar puts "bar: #{self.len}" "bar!" end define_hook :bar do puts "hook 1: #{this.len}" this.len + prior end define_hook :bar do puts "hook 2: #{this.len}" this.len + puts "prior 1" prior puts "prior 2" prior "gotcha!" end def foo(x) p this puts "foo #{x}" Foo.new.foo nil if x "foo!" end define_hook :foo do |x| puts "hook foo 1: #{this.len}" puts "hook foo 1: x x}" this.len + prior end end f oo.new ["__hook__bar_0", "__hook__bar_1", "bar"].each do |m| puts "calling #{m}" p f.send( m ) puts "" end p f.foo("xxx") ------ extPart_000_001F_01C32050.04CF41B0 Content-Type: application/octet-stream; name ook_test.rb" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename ook_test.rb" require 'hooks.rb' class String define_hook(:length) do puts "I'm calculating the length" prior end define_hook(:upcase) do prior puts "I just did an upcase operation" end define_hook(:reverse) do puts "I'm about to do a reverse operation" prior puts "I just did a reverse" end end str abcde" x tr.length puts x y tr.upcase puts y z tr.reverse puts z ------ extPart_000_001F_01C32050.04CF41B0--