From pedro.delgallego at gmail.com Sat Sep 15 23:04:46 2007 From: pedro.delgallego at gmail.com (Pedro Del Gallego) Date: Sun, 16 Sep 2007 05:04:46 +0200 Subject: [Aquarium-users] Calling an instance method in an advice. Message-ID: <2e93189b0709152004n6d792d53pe039ea45406e37eb@mail.gmail.com> Hi, I implemented the dynamic finders from Active record using Aquarium. I have some questions (mabe i did something wrong, i have no experience in AOP) require 'rubygems' require 'aquarium' class Base def method_missing(method_id, *arguments) puts :method_missing end def self.extract_attribute_names_from_match(match) match.captures.last.split('_and_') end before :method=>:method_missing do |execution_point, method_name, *args| if match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(method_name.to_s) attribute_names = Base.extract_attribute_names_from_match(match) # more rails code ,,, end end end The problem with the implementation is that i can not call a instance method inside of the advice : Look at the following code/spec : require 'rubygems' require 'aquarium' class A def foo puts :foo end def bar puts :bar end before :method=>:foo do |execution_point, method_name, *args| puts bar end end describe "Call an intance method in a before block" do before :each do @a = A.new end it "should run the method" do lambda{@a.foo}.should_not raise_error() #fail om my computer. a.foo.should == :bar end end Is there any any form to workaround and call an instance method ? But the most curious behaviour its that if i run two times the following code in the irb console the first one raise an exception , and the second one ignore the advice. require 'rubygems' require 'aquarium' class A def foo puts :foo end def bar puts :bar end before :method=>:foo do |execution_point, method_name, *args| puts bar end end A.new.foo A.new.foo NameError: Exception raised while executing "before" advice for "A#foo": undefined local variable or method `bar' for A:Class from (irb):30 from /usr/lib/ruby/gems/1.8/gems/aquarium-0.1.0/lib/aquarium/aspects/advice.rb:96:in `call' from /usr/lib/ruby/gems/1.8/gems/aquarium-0.1.0/lib/aquarium/aspects/advice.rb:96:in `initialize' from /usr/lib/ruby/gems/1.8/gems/aquarium-0.1.0/lib/aquarium/aspects/advice.rb:42:in `call' from /usr/lib/ruby/gems/1.8/gems/aquarium-0.1.0/lib/aquarium/aspects/advice.rb:42:in `call' from /usr/lib/ruby/gems/1.8/gems/aquarium-0.1.0/lib/aquarium/aspects/aspect.rb:248:in `foo' from (irb):33 from :0 My ruby box : ---------------------------------- ruby -v ruby 1.8.6 (2007-06-07 patchlevel 36) [i486-linux] Best regards. -- ------------------------------------- Pedro Del Gallego Email : pedro.delgallego at gmail.com From dean at aspectprogramming.com Sat Sep 15 23:10:39 2007 From: dean at aspectprogramming.com (Dean Wampler) Date: Sat, 15 Sep 2007 22:10:39 -0500 Subject: [Aquarium-users] Calling an instance method in an advice. In-Reply-To: <2e93189b0709152004n6d792d53pe039ea45406e37eb@mail.gmail.com> References: <2e93189b0709152004n6d792d53pe039ea45406e37eb@mail.gmail.com> Message-ID: Pedro, I'll take a closer look in the morning (Central time, U.S.), but I noticed immediately that the arguments to the "advice" block must be | execution_point, *args| (you can use any names you want, of course). The type of "execution_point" is actually JoinPoint and it has a :method_name method that will give you the method name that's being advised. So, your first example for "Base" would become: > before :method=>:method_missing do |execution_point, *args| > if match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match > (execution_point.method_name.to_s) > attribute_names = Base.extract_attribute_names_from_match(match) > # more rails code ,,, > end > end I hope this helps. Yours, Dean On Sep 15, 2007, at 10:04 PM, Pedro Del Gallego wrote: > Hi, > > I implemented the dynamic finders from Active record using Aquarium. I > have some questions (mabe i did something wrong, i have no experience > in AOP) > > > require 'rubygems' > require 'aquarium' > > class Base > > def method_missing(method_id, *arguments) > puts :method_missing > end > > def self.extract_attribute_names_from_match(match) > match.captures.last.split('_and_') > end > > before :method=>:method_missing do |execution_point, method_name, > *args| > if match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match > (method_name.to_s) > attribute_names = Base.extract_attribute_names_from_match(match) > # more rails code ,,, > end > end > end > > The problem with the implementation is that i can not call a instance > method inside of the advice : Look at the following code/spec : > > require 'rubygems' > require 'aquarium' > > class A > def foo > puts :foo > end > def bar > puts :bar > end > > before :method=>:foo do |execution_point, method_name, *args| > puts bar > end > end > > describe "Call an intance method in a before block" do > before :each do > @a = A.new > end > > it "should run the method" do > lambda{@a.foo}.should_not raise_error() #fail om my computer. > a.foo.should == :bar > end > end > > > Is there any any form to workaround and call an instance method ? > > But the most curious behaviour its that if i run two times the > following code in the irb console the first one raise an exception , > and the second one ignore the advice. > > > require 'rubygems' > require 'aquarium' > > class A > def foo > puts :foo > end > def bar > puts :bar > end > > before :method=>:foo do |execution_point, method_name, *args| > puts bar > end > end > > A.new.foo > > A.new.foo > NameError: Exception raised while executing "before" advice for > "A#foo": undefined local variable or method `bar' for A:Class > from (irb):30 > from /usr/lib/ruby/gems/1.8/gems/aquarium-0.1.0/lib/ > aquarium/aspects/advice.rb:96:in > `call' > from /usr/lib/ruby/gems/1.8/gems/aquarium-0.1.0/lib/ > aquarium/aspects/advice.rb:96:in > `initialize' > from /usr/lib/ruby/gems/1.8/gems/aquarium-0.1.0/lib/ > aquarium/aspects/advice.rb:42:in > `call' > from /usr/lib/ruby/gems/1.8/gems/aquarium-0.1.0/lib/ > aquarium/aspects/advice.rb:42:in > `call' > from /usr/lib/ruby/gems/1.8/gems/aquarium-0.1.0/lib/ > aquarium/aspects/aspect.rb:248:in > `foo' > from (irb):33 > from :0 > > > My ruby box : ---------------------------------- > ruby -v > ruby 1.8.6 (2007-06-07 patchlevel 36) [i486-linux] > > > Best regards. > > -- > ------------------------------------- > Pedro Del Gallego > > Email : pedro.delgallego at gmail.com > _______________________________________________ > Aquarium-users mailing list > Aquarium-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/aquarium-users Dean Wampler, Ph.D. dean at objectmentor.com http://www.objectmentor.com See also: http://www.aspectprogramming.com AOP advocacy site http://aquarium.rubyforge.org AOP for Ruby http://www.contract4j.org Design by Contract for Java5 I want my tombstone to say: Unknown Application Error in Dean Wampler.exe. Application Terminated. [Okay] [Cancel] From dean at aspectprogramming.com Sat Sep 15 23:06:00 2007 From: dean at aspectprogramming.com (Dean Wampler) Date: Sat, 15 Sep 2007 22:06:00 -0500 Subject: [Aquarium-users] Proposals for "0.1.5" release of Aquarium Message-ID: <68D39E2E-3429-4A52-B612-7F906C11CDFB@aspectprogramming.com> Hi, I'm working on the next release, which will fix the following bugs: 13650 Loading Aquarium interferes with Rails filters 13864 Bug with negative object_id I want to tag the release "0.1.5" because fixing 13650 will require an API change, but it doesn't seem to warrant a jump to "0.2.0". Here is what I propose to fix 13650: Previously, requiring "aquarium.rb" in the top-level "lib" directory would implicitly require lib/aquarium/aspects/dsl/aspect_dsl.rb, which has Object include the AspectDSL module. This module adds methods like :before and :after to Object. Unfortunately, those methods collide with methods of the same name that Rails adds to Object. It was also a bit presumptuous of me to assume that everyone wanted those methods on Object ;) In this release, aspect_dsl.rb is still implicitly included and it still defines the AspectDSL module. Now, however, it does not include the AspectDSL module in Object. Instead, if you want this behavior for all types, you'll have to require a new lib/aquarium/aspects/dsl/ object_dsl.rb explicitly. As an alternative, if you just want the AspectDSL module included selectively in certain types (a good suggestion by the bug submitter - thanks if you're reading!), then do the following: require 'aquarium/aspects/dsl/aspect_dsl' class MyClass # reopen "MyClass" include Aquarium::Aspects::DSL::AspectDSL end (or something along those lines...) Feedback is welcome. Although I've almost completed the work, I'll wait until Monday evening in case anyone has any alternative suggestions or concerns. Thanks, dean Dean Wampler, Ph.D. dean at objectmentor.com http://www.objectmentor.com See also: http://www.aspectprogramming.com AOP advocacy site http://aquarium.rubyforge.org AOP for Ruby http://www.contract4j.org Design by Contract for Java5 I want my tombstone to say: Unknown Application Error in Dean Wampler.exe. Application Terminated. [Okay] [Cancel] -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/aquarium-users/attachments/20070915/ab7bca86/attachment.html From dean at aspectprogramming.com Sun Sep 16 09:56:03 2007 From: dean at aspectprogramming.com (Dean Wampler) Date: Sun, 16 Sep 2007 08:56:03 -0500 Subject: [Aquarium-users] Calling an instance method in an advice. In-Reply-To: <2e93189b0709160547v508bb026gf9d8bed7c9f6f4e2@mail.gmail.com> References: <2e93189b0709152004n6d792d53pe039ea45406e37eb@mail.gmail.com> <2e93189b0709160547v508bb026gf9d8bed7c9f6f4e2@mail.gmail.com> Message-ID: <6BB37927-5A85-463E-95E0-4175B35C7FC7@aspectprogramming.com> Pedro, I can see your point that it's tedious to have to use "execution_point.context.advised_object" just to get at the object. I'll open an enhancement request to pass the object as a block argument. I'll have to consider how to handle the API change, since currently, all advice calls assume "|join_point, *method_args|" will be the argument list. Still, I think it's worth considering. I hope it's clear that calling "bar" in your advice: > class A > def foo > puts :foo > end > def bar > puts :bar > end > > before :method=>:foo do |execution_point, *args| > bar() # exception !! it call A#bar, but self reference to > A, not my_instance_of_a > end > end behaves just like regular Ruby. In this case, it looks for a class method A.bar(), since A is still "self" at the point the block is defined, etc. I'll improve the documentation to make "scoping" details like this more clear. I chose "|join_point, *method_args|" originally to keep the parameter list short, but still provide everything you might need. The reason for the "context" object inside "join_point" is to draw the distinction between the statically-known information (what was specified when the aspect was created) and the dynamic information that changes on each invocation. This is similar to what AspectJ does and the main advantage is the potential for performance optimization later, where the context part is not generated when it isn't needed. (Perhaps this was premature optimization, though... ;) Since I'm already passing method call's argument list second, I'm thinking it would be better to do this: |join_point, the_self, *method_args| Does anyone know of a way to determine what argument list was specified when the block is created? If so, I could easily look at the list and infer what to pass later when the block is invoked. In other words, it would be great if the user could specify combinations like these: |join_point, the_self, *method_args| |join_point| |the_self| |the_self, *method_args| |join_point, the_self| |join_point, *method_args| and so forth, and Aquarium would just work. If there is no easy way to do this, perhaps a labeled parameter list option (like most of the API) would work: |:join_point => join_point, :self => the_self, :method_args => *method_args| A bit more tedious, though. Would anyone else reading the list object to these changes? Other opinions? Thanks for the feedback. Yours, dean On Sep 16, 2007, at 7:47 AM, Pedro Del Gallego wrote: > 2007/9/16, Dean Wampler : >> Pedro, > >> The type of "execution_point" is actually JoinPoint and it has >> a :method_name method that will give you the method name that's being >> advised. So, your first example for "Base" would become: >> >>> before :method=>:method_missing do |execution_point, *args| >>> if match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match >>> (execution_point.method_name.to_s) >>> attribute_names = Base.extract_attribute_names_from_match >>> (match) >>> # more rails code ,,, >>> end >>> end >> >> I hope this helps. > > Umm, thats not the main problem. > > class A > def foo > puts :foo > end > def bar > puts :bar > end > > before :method=>:foo do |execution_point, *args| > bar() # exception !! it call A#bar, but self reference to A, > not my_instance_of_a > end > end > > my_instance_of_a = A.new > my_instance_of_a.foo() > > More info from the debugger : > > execution_point : JoinPoint: { > type = A, > object = nil, > method_name = foo, > is_instance_method? true, > context = # @parameters=[], @advised_object=#, @advice_kind=:before, > @block_for_method=nil> > } > > self == A > > Just for testing I modified the BeforeAdviceChainNode#initialize > method adding a new parameter to the > > class BeforeAdviceChainNode < AdviceChainNode > def initialize options = {} > super(options) { |jp, *args| > before_jp = > jp.make_current_context_join_point :advice_kind => :before > advice.call(before_jp.context.advised_object, before_jp, > *args) > next_node.call(jp, *args) > } > end > end > > now we can code the advices like : > > before :method=>:foo do |this, execution_point, *args| > # this is only a reference to the caller object > # execution_point.context.advised_object > this.bar > > > end > > Its just a dirty trick, but at least less painful that write all the > path in Joinpoint. > > Probably Im wrong, and i missed some point, somewhere. > > Thanks. > -- > ------------------------------------- > Pedro Del Gallego > > Email : pedro.delgallego at gmail.com Dean Wampler, Ph.D. dean at objectmentor.com http://www.objectmentor.com See also: http://www.aspectprogramming.com AOP advocacy site http://aquarium.rubyforge.org AOP for Ruby http://www.contract4j.org Design by Contract for Java5 I want my tombstone to say: Unknown Application Error in Dean Wampler.exe. Application Terminated. [Okay] [Cancel] From dean at aspectprogramming.com Mon Sep 17 22:27:32 2007 From: dean at aspectprogramming.com (Dean Wampler) Date: Mon, 17 Sep 2007 21:27:32 -0500 Subject: [Aquarium-users] New V0.1.5 Release of Aquarium Message-ID: <2F9DF84F-2344-409F-AE40-7E096EC55FC1@aspectprogramming.com> As discussed previously, this is primarily a bug-fix release that I labeled as "v0.1.5" instead of "v0.1.1", because of the required API change. Aquarium no longer automatically adds methods to Object, due to collisions with Rails. This means that users of the "DSL methods" will need to require a new file or include a new module. The "CHANGES" file in the distribution (or here: http:// aquarium.rubyforge.org/changes.html) has the details. In a nutshell, "require 'aquarium'" still defines the "AspectDSL" module, but it doesn't include it automatically in "Object". If you want that behavior, then require 'aquarium/aspects/dsl/object_dsl' You now also have the option of including the DSL methods in individual modules, classes, or even objects. For modules and classes, just include Aquarium::Aspects::DSL::AspectDSL This adds the methods as _class_ methods on the module or class. For objects, just my_object.extend (Aquarium::Aspects::DSL::AspectDSL) This adds the methods as _instance_ methods on the object. There are some other bug fixes and minor enhancements listed in the CHANGES. dean Dean Wampler, Ph.D. dean at objectmentor.com http://www.objectmentor.com See also: http://www.aspectprogramming.com AOP advocacy site http://aquarium.rubyforge.org AOP for Ruby http://www.contract4j.org Design by Contract for Java5 I want my tombstone to say: Unknown Application Error in Dean Wampler.exe. Application Terminated. [Okay] [Cancel] -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/aquarium-users/attachments/20070917/5c49e572/attachment.html