From: "T. Onoma" Date: 2003-11-30T11:53:25+09:00 Subject: Re: Underpinnings of Method Wrapping On Sunday 30 November 2003 01:01 am, Peter wrote: > Oh, I agree completely, Ruby will be able to specify the most general > pointcuts possible since you can specify it in code. Any code. Wildcards > is limiting. Though maybe I should mention that the AspectJ guys said > nobody ever missed anything more general then wildcards in AspectJ. How would they know? ;-) > I agree we should support both scenarios. But I don't agree yet on > discarding wrappers by default. Aspects are supposed to model orhtogonal > aspects of an application and are supposed to be uncoupled. Which means > wrapper and wrappee ought to be uncoupled. But maybe that's one of those > cool ideas that are infeasible in practice. And the use of method > combination may not be limited to AOP... I'm had some varying thoughts on this. But I think I've come to a good solution. First let me point out that, although unmentioned in my RCR, I'm all for pre and post, and actually, I'm now in favor of dblack's suggestion of simply using pre and post as keywords [yes, db, bygones &c.] b/c they are ONLY for "meta-tasks" and can't get invovled in the underlying exectution; so in this manner they are quite distinct from def. In other words, defs "get in line" while pres and posts "hang about", if you take my meaning. In light of this, it seems appropriate for redef to flush the wrap stack, but all pre's and post's still remain. So we have proper seperation of these concerns: pre and post for extrinsic, def wrap for intrinsic. > > This reminds me of an RCR I put in once. I suggested that every object > > have a keyword reference back to its "brith parent". So if I cerated > > object foo = Foo.new from within object bar, foo#parent would reference > > bar. In this case it seems their might be good reason for each method to > > have a keyword method (Method#cflow) referenceing the method object it > > was called from. In that way I think we could offer cflow support. > > Yes, except that Method#cflow will only allow looking back one step in the > calling stack. In AspectJ, cflow looks back along the complete calling > stack, which is quite expensive to do if you'd use a Method#cflow for > looking at the calling method. Is it very expensive? ameth.cflow.cflow... hmmm, I guess your right, if your looking for a method that could be anywhere up the reference list. Although I think in practice the list will only get so long. (And shortcutted for recursive calls). But I definitely see your point (though I think Method#cflow would still have its uses). > Actually AspectJ's implementation can be > done in Ruby using only wraps as follows: > > def foo > # ... > end > > def foo > oldInFooCFlow = $inFooCFlow > $inFooCFlow = true > super > $inFooCFlow = oldInFooCFlow > end > > def bar > # ... > end > > def bar > if $inFooCFlow > # advice added to bar in the cflow of foo > end > super > if $inFooCFlow > # advice added to bar in the cflow of foo > end > end > > I used your RCR syntax. And forgive me the use of the global var, but that > least clutters the point. So it is possible foo calls some foo2, which > calls some foo3, ... until eventually bar is called, and it will work > without the need for looking all the way back up the call stack. I noticed right away that you were using my syntax :-) I've heard so many people say how ridiculous such a syntax is (funny that they keep mentioning it), but right now I can tell you it feels completely natural --seeing the defs come in order parallels their stacking order. Anyway... > As for the use of the global variable, in AspectJ this is easily done. > Every aspect in AspectJ is a singleton, with its own data members, and all > the advice from that aspect (read: wraps) can access these data members. > As such it effectively provides global, yet private variables. Definitely, and I'm certain we can achieve the same thing with module's and their instance variables. Define a module containing aspect methods and the module variables they need and apply as desired. Hell, we can even wrap the aspect methods! > > I think a lot of this is pointing to the simple fact that Ruby needs to > > make its Methods more capable and introspective, much in the same way > > that classes/objects are. For instance, cflow, and my suggestion for > > built-in keywords for args, keys and block, plus a way to see what > > class/module the method is in (from the inside out, we can already do > > from the outside in), and so on. Having such capabilities helps exploit > > the full potential of AOP. > > Agreed. Have to add to RCR. > > I cann see it to some degree: off the top of my head: If the method #kill > > was called from method #hunt, then you might want to trigger the method > > #excitement, but if the same method #kill was called from > > #protect_thyself, then it should instead trigger #relief. Very abstract, > > but I think it paints the proper idea. > > I understand. It offers a way to find out about the callers of a method - > or more abstract: the context of a call. Though the abstract idea sounds > mighty interesting, I'm not so crazy about using method names to find out > about the context. But I don't know about a better way. Just weird that > you have to name your method in a particular way to get the right behavior > from a method you're calling. Ah...but this is what the indicators are for. def eat:eating end def gorge:eating end def stuffyourface:eating end We can aspect all the eating methods. ( no, i'm not hungry :-) > Peter > > > P.S. So what did you think of the singleton foundation? > > Are you referring to the line "wraps act very much like singletons" from > your RCR? In that case, I think I missed the point. But that's probably > because it was the explanation to something that's so obvious to me :-) I really need to go back and specify this. It was still a little fuzzy in my mind when I started the RCR, but now it's quite clear --perfectly clear. So much so that I pulled up the Ruby source and hacked it in. Changed about 6 lines of code and got it working, albiet it still needs TLC to fix-up and add all the bells and whistles, but the proof is in pudding, and I got pudding. But I'm getting ahead of myself. I need to explain the point. Lets take this example: class Classic def x print "X" end end c = Classic.new def c.x print "(" super print ")" end c.x; puts # => (X) We have created an object c, that has a method x, then we defined a singleton also called x. The singleton effectively wraps the original method. This is explained well in the Pickaxe. Check out page 246, Fig. 19.3. The diagram is describe a couple pages back under Object-Specific Class; or you can surf here: http://www.rubycentral.com/book/classes.html So then what happens if we try to add another singleton of the same name? def c.x print "[" super print "]" end c.x; puts # => [X] Why didn't it wrap again? Let me quote the end of that Pickaxe section: "Ruby performs a slight optimization with these singleton classes. If an object's klass reference already points to a singleton class, a new one will not be created." Obviously when Matz created this, it didn't occur to him that unlimted wrapping may be useful. He just saw that it was wasted overhead to have a separate singleton class for each and every singleton method. But his optimization had the above side effect --you can only define one layer. So I went back and changed the optimization such that when a method of the same name already exists in the singleton class, a new singleton class (metaclass in the code) is created. So my version of Ruby instead does the following: def c.x print "[" super print "]" end c.x; puts # => [(X)] That's all there was to it, and it serves as the perfect starting point/basis for building all the rest of the AOP features. -t0