From: Robert Date: 2003-05-22T07:39:40+09:00 Subject: Re: How I'd like method-wrapping to work... This is a multi-part message in MIME format. ------=_NextPart_000_0079_01C31FF9.09646CC0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit "Hal E. Fulton" schrieb im Newsbeitrag news:096701c31fc3$4f78b3a0$0300a8c0@austin.rr.com... > OK, I read Matz's blog entries as well as I could. > > One thing that occurs to me is that I don't really > want to distinguish between "before" and "after" > in an artificial way. > > Here's my tentative dream of how I'd like to do > this kind of thing in Ruby. (I'm using the keyword > 'prior' for lack of anything better right now.) > > > > class String > > define_hook(:length) do > puts "I'm calculating the length" > prior > end > > define_hook(:upcase) do > val = prior > puts "I just did an upcase operation" > val > end > > define_hook(:reverse) do > puts "I'm about to do a reverse operation" > val = prior > puts "I just did a reverse" > val > end > > end > > str = "abcde" > > x = str.length > puts x > > y = str.upcase > puts y > > z = str.reverse > puts z > > # Output: > # I'm calculating the length > # 5 > # I just did an upcase operation > # ABCDE > # I'm about to do a reverse operation > # I just did a reverse > # edcba > > That's no fantasy. Dreams come true... > Notes: > > 1. Obviously parameter-passing needs to be addressed. > 2. It would be nice to get around the return-value > problem (where have I seen this before? 'ensure'?) > 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). 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. - Argument lists have to be doubled for the block Pros: - It nearly looks like what you asked for. - Return values work reasonably. - It even works for nested invocations, i.e. recursion - It works with several instances in the call chain - You can exactly do what you put in Regards robert ------=_NextPart_000_0079_01C31FF9.09646CC0 Content-Type: application/octet-stream; name="hook_test.rb" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="hook_test.rb" require 'hooks.rb' class String define_hook(:length) do puts "I'm calculating the length" prior end define_hook(:upcase) do val = prior puts "I just did an upcase operation" val end define_hook(:reverse) do puts "I'm about to do a reverse operation" val = prior puts "I just did a reverse" val end end str = "abcde" x = str.length puts x y = str.upcase puts y z = str.reverse puts z ------=_NextPart_000_0079_01C31FF9.09646CC0 Content-Type: application/octet-stream; name="hooks.rb" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="hooks.rb" class Object private def prior thr = Thread.current ths = thr[ :__hook_self ] sym = thr[ :__hook_next ] raise "No more methods in chain" unless sym begin # puts '>>> nest' result = ths.send( sym, *(thr[ :__hook_args ])) # puts '<<< unnest' 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 &block ) x = <<-"EO_HOOK" alias #{al} #{symbol} def #{symbol}(*args) thr = Thread.current thr[ :__hook_self ]= self thr[ :__hook_args ]= args thr[ :__hook_next ]= :#{al} #{self}.get_hook_proc( "#{al}" ).call( *args ) 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 ------=_NextPart_000_0079_01C31FF9.09646CC0--