require File.dirname(__FILE__) + '/../spec_helper' require 'aquarium/spec_example_types' require 'aquarium/utils/type_utils_sample_nested_types' require 'aquarium/aspects/aspect' require 'aquarium/dsl' require 'aquarium/utils/array_utils' require 'aquarium/finders/pointcut_finder_spec_test_classes' require 'stringio' require 'profiler' include Aquarium::Aspects include Aquarium::Utils::ArrayUtils include Aquarium::PointcutFinderTestClasses def aspects_should_be_equal num_jps, aspect1, aspect2 # We don't use @aspect1.should eql(@aspect2) because the "specifications" are different. aspect1.pointcuts.size.should == 1 aspect2.pointcuts.size.should == 1 aspect1.pointcuts.should eql(aspect2.pointcuts) aspect1.advice.should eql(@advice) aspect2.advice.should eql(@advice) join_points_should_be_equal num_jps, aspect1, aspect2 end def join_points_should_be_equal num_jps, aspect1, aspect2 aspect1.join_points_matched.size.should == num_jps aspect2.join_points_matched.size.should == num_jps aspect1.join_points_matched.each {|jp| @expected_methods.should include(jp.method_name)} aspect2.join_points_matched.each {|jp| @expected_methods.should include(jp.method_name)} aspect1.join_points_matched.should eql(aspect2.join_points_matched) aspect1.join_points_not_matched.should eql(aspect2.join_points_not_matched) end module Aquarium class AspectInvocationTestClass include Aquarium::DSL attr_accessor :public_test_method_args def public_test_method *args; @args=args; end protected def protected_test_method *args; @args=args; end private def private_test_method *args; @args=args; end def self.public_class_test_method *args; end def self.private_class_test_method *args; end private_class_method :private_class_test_method end class AspectInvocationTestClass2 include Aquarium::DSL attr_accessor :public_test_method_args def public_test_method *args; @args=args; end end class AspectInvocationTestClass3 attr_accessor :public_test_method_args attr_accessor :public_test_method_args2 def public_test_method *args; @args=args; end end end describe Aspect, "methods" do include Aquarium::TypeUtilsStub before :all do stub_type_utils_descendents end after :all do unstub_type_utils_descendents end describe Aspect, ".new (the :ignore_no_matching_join_points parameter that specifies whether or not to warn about no join point matches)" do before :each do @log_stream = StringIO.new end it "should warn about no join point matches if the :ignore_no_matching_join_points is not specified." do expect {Aspect.new(:after, :logger_stream => @log_stream) {true}}.to raise_error(Aquarium::Utils::InvalidOptions) @log_stream.string.should_not be_empty end it "should warn about no join point matches if :ignore_no_matching_join_points => false is specified." do expect {Aspect.new(:after, :logger_stream => @log_stream, :ignore_no_matching_join_points => false) {true}}.to raise_error(Aquarium::Utils::InvalidOptions) @log_stream.string.should_not be_empty end it "should not warn about no join point matches if :ignore_no_matching_join_points => true is specified." do expect {Aspect.new(:after, :logger_stream => @log_stream, :ignore_no_matching_join_points => true) {true}}.to raise_error(Aquarium::Utils::InvalidOptions) @log_stream.string.should be_empty end end describe Aspect, ".new (parameters that specify the kind of advice)" do before :all do @pointcut_opts = {:type => Aquarium::AspectInvocationTestClass} end it "should require the kind of advice as the first parameter." do expect { Aspect.new :pointcut => @pointcut_opts }.to raise_error(Aquarium::Utils::InvalidOptions) end it "should contain no other advice types if :around advice specified." do expect { Aspect.new :around, :before, :pointcut => @pointcut_opts }.to raise_error(Aquarium::Utils::InvalidOptions) expect { Aspect.new :around, :after, :pointcut => @pointcut_opts }.to raise_error(Aquarium::Utils::InvalidOptions) expect { Aspect.new :around, :after_returning, :pointcut => @pointcut_opts }.to raise_error(Aquarium::Utils::InvalidOptions) expect { Aspect.new :around, :after_raising, :pointcut => @pointcut_opts }.to raise_error(Aquarium::Utils::InvalidOptions) end it "should allow only one of :after, :after_returning, or :after_raising advice to be specified." do expect { Aspect.new :after, :after_returning, :pointcut => @pointcut_opts }.to raise_error(Aquarium::Utils::InvalidOptions) expect { Aspect.new :after, :after_raising, :pointcut => @pointcut_opts }.to raise_error(Aquarium::Utils::InvalidOptions) expect { Aspect.new :after_returning, :after_raising, :pointcut => @pointcut_opts }.to raise_error(Aquarium::Utils::InvalidOptions) end it "should allow :before to be specified with :after." do expect { Aspect.new :before, :after, :pointcut => @pointcut_opts, :noop => true }.not_to raise_error end it "should allow :before to be specified with :after_returning." do expect { Aspect.new :before, :after_returning, :pointcut => @pointcut_opts, :noop => true }.not_to raise_error end it "should allow :before to be specified with :after_raising." do expect { Aspect.new :before, :after_raising, :pointcut => @pointcut_opts, :noop => true }.not_to raise_error end it "should accept a single exception specified with :after_raising." do expect { Aspect.new :before, :after_raising => Exception, :pointcut => @pointcut_opts, :noop => true }.not_to raise_error end it "should accept a list of exceptions specified with :after_raising." do expect { Aspect.new :before, :after_raising => [Exception, String], :pointcut => @pointcut_opts, :noop => true }.not_to raise_error end it "should accept a separate :exceptions => list of exceptions specified with :after_raising." do expect { Aspect.new :before, :after_raising, :exceptions => [Exception, String], :pointcut => @pointcut_opts, :noop => true }.not_to raise_error end it "should reject the :exceptions argument unless specified with :after_raising." do expect { Aspect.new :before, :after, :exceptions => [Exception, String], :pointcut => @pointcut_opts, :noop => true }.to raise_error(Aquarium::Utils::InvalidOptions) end end describe Aspect, ".new (parameters that specify pointcuts)" do before :all do @pointcut_opts = {:type => Aquarium::AspectInvocationTestClass} end it "should contain at least one of :method(s), :pointcut(s), :named_pointcut(s), :type(s), or :object(s)." do expect {Aspect.new(:after, :ignore_no_matching_join_points => true) {true}}.to raise_error(Aquarium::Utils::InvalidOptions) end it "should contain at least one of :pointcut(s), :named_pointcut(s), :type(s), or :object(s) unless :default_objects => object is given." do aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass.new, :method => :public_test_method, :noop => true) {true} end it "should ignore the :default_objects if at least one other :object is given and the :default_objects are objects." do object1 = Aquarium::AspectInvocationTestClass.new object2 = Aquarium::AspectInvocationTestClass2.new aspect = Aspect.new(:after, :default_objects => object1, :object => object2, :method => :public_test_method) {true} aspect.join_points_matched.size.should == 1 aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == object1} aspect.unadvise end it "should ignore the :default_objects if at least one other :object is given and the :default_objects are types." do object = Aquarium::AspectInvocationTestClass2.new aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass, :object => object, :method => :public_test_method) {true} aspect.join_points_matched.size.should == 1 aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == Aquarium::AspectInvocationTestClass} aspect.unadvise end it "should ignore the :default_objects if at least one :pointcut is given even if the :default_objects => object are given." do object = Aquarium::AspectInvocationTestClass.new aspect = Aspect.new(:after, :default_objects => object, :pointcut => {:type => Aquarium::AspectInvocationTestClass2, :method => :public_test_method}, :method => :public_test_method) {true} aspect.join_points_matched.size.should == 1 aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == object} aspect.unadvise end it "should ignore the :default_objects if at least one :pointcut is given even if the :default_objects => type are given." do aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass, :pointcut => {:type => Aquarium::AspectInvocationTestClass2, :method => :public_test_method}, :method => :public_test_method) {true} aspect.join_points_matched.size.should == 1 aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == Aquarium::AspectInvocationTestClass} aspect.unadvise end it "should ignore the :default_objects if at least one :join_point is given and the :default_objects are objects." do join_point = JoinPoint.new :type => Aquarium::AspectInvocationTestClass2, :method => :public_test_method object = Aquarium::AspectInvocationTestClass.new aspect = Aspect.new(:after, :default_objects => object, :join_point => join_point, :method => :public_test_method) {true} aspect.join_points_matched.size.should == 1 aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == object} aspect.unadvise end it "should ignore the :default_objects if at least one :join_point is given and the :default_objects are types." do join_point = JoinPoint.new :type => Aquarium::AspectInvocationTestClass2, :method => :public_test_method aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass, :join_point => join_point, :method => :public_test_method) {true} aspect.join_points_matched.size.should == 1 aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == Aquarium::AspectInvocationTestClass} aspect.unadvise end [:type, :type_and_descendents, :type_and_ancestors, :type_and_nested_types].each do |type_key| it "should ignore the :default_objects if at least one :#{type_key} is given and the :default_objects are objects." do object = Aquarium::AspectInvocationTestClass.new aspect = Aspect.new(:after, :default_objects => object, type_key => Aquarium::AspectInvocationTestClass2, :method => :public_test_method, :method => :public_test_method) {true} aspect.join_points_matched.size.should == 1 aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == object} aspect.unadvise end it "should ignore the :default_objects if at least one :#{type_key} is given and the :default_objects are types." do aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass, type_key => Aquarium::AspectInvocationTestClass2, :method => :public_test_method, :method => :public_test_method) {true} aspect.join_points_matched.size.should == 1 aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == Aquarium::AspectInvocationTestClass} aspect.unadvise end end Aspect::CANONICAL_OPTIONS["default_objects"].each do |key| it "should accept :#{key} as a synonym for :default_objects." do aspect = Aspect.new(:after, key.intern => Aquarium::AspectInvocationTestClass.new, :method => :public_test_method, :noop => true) {true} aspect.unadvise end end it "should not contain :pointcut(s) and either :type(s) or :object(s)." do expect {Aspect.new(:after, :pointcuts => @pointcut_opts, :type => Aquarium::AspectInvocationTestClass, :method => :public_test_method) {true}}.to raise_error(Aquarium::Utils::InvalidOptions) expect {Aspect.new(:after, :pointcuts => @pointcut_opts, :object => Aquarium::AspectInvocationTestClass.new, :method => :public_test_method) {true}}.to raise_error(Aquarium::Utils::InvalidOptions) end end describe Aspect, ".new (parameters that specify named constant and/or class variable pointcuts)" do it "should contain at least one :types or TypeFinder synonym for :types." do expect {Aspect.new(:after, :named_pointcuts => {}, :noop => true) {true}}.to raise_error(Aquarium::Utils::InvalidOptions) expect {Aspect.new(:after, :named_pointcuts => {:types => Aquarium::PointcutFinderTestClasses.all_pointcut_classes}, :noop => true) {true}}.not_to raise_error end it "should ignore the :default_objects if at least one :named_pointcut is given even if the :default_objects => object are given." do object = Aquarium::AspectInvocationTestClass.new aspect = Aspect.new(:after, :default_objects => object, :named_pointcut => {:types => Aquarium::PointcutFinderTestClasses::PointcutClassVariableHolder1}) {true} aspect.join_points_matched.size.should == 1 aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == object} aspect.unadvise end it "should ignore the :default_objects if at least one :named_pointcut is given even if the :default_objects => type are given." do aspect = Aspect.new(:after, :default_objects => Aquarium::AspectInvocationTestClass, :named_pointcut => {:types => Aquarium::PointcutFinderTestClasses::PointcutClassVariableHolder1}) {true} aspect.join_points_matched.size.should == 1 aspect.join_points_matched.each {|jp| jp.type_or_object.should_not == Aquarium::AspectInvocationTestClass} aspect.unadvise end Aspect::CANONICAL_OPTIONS["named_pointcuts"].each do |key| it "should accept :#{key} as a synonym for :named_pointcuts." do aspect = Aspect.new :before, key.intern => {:types => Aquarium::PointcutFinderTestClasses.all_pointcut_classes}, :noop => true do; end aspect.unadvise end end it "should not contain :named_pointcut(s) and either :type(s) or :object(s)." do expect {Aspect.new(:after, :named_pointcuts => {:types => Aquarium::PointcutFinderTestClasses::PointcutClassVariableHolder1}, :type => Aquarium::AspectInvocationTestClass, :method => :public_test_method) {true}}.to raise_error(Aquarium::Utils::InvalidOptions) expect {Aspect.new(:after, :named_pointcuts => {:types => Aquarium::PointcutFinderTestClasses::PointcutClassVariableHolder1}, :object => Aquarium::AspectInvocationTestClass.new, :method => :public_test_method) {true}}.to raise_error(Aquarium::Utils::InvalidOptions) end end describe Aspect, ".new with :types parameter" do it "should advise the specified types." do @advice_called = false aspect = Aspect.new :before, :types => Aquarium::AspectInvocationTestClass, :method => :public_test_method do; @advice_called = true; end Aquarium::AspectInvocationTestClass.new.public_test_method aspect.unadvise @advice_called.should be_truthy end Aspect::CANONICAL_OPTIONS["types"].each do |key| it "should accept :#{key} as a synonym for :types." do aspect = Aspect.new :before, key.intern => Aquarium::AspectInvocationTestClass, :method => :public_test_method, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new with :pointcuts parameter" do it "should advise the specified pointcuts." do @advice_called = false aspect = Aspect.new :before, :pointcuts => {:types => Aquarium::AspectInvocationTestClass, :method => :public_test_method} do; @advice_called = true; end Aquarium::AspectInvocationTestClass.new.public_test_method aspect.unadvise @advice_called.should be_truthy end Aspect::CANONICAL_OPTIONS["pointcuts"].each do |key| it "should accept :#{key} as a synonym for :pointcuts." do aspect = Aspect.new :before, key.intern => {:type => Aquarium::AspectInvocationTestClass, :method => :public_test_method}, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new with :objects parameter" do it "should advise the specified objects." do @advice_called = false object = Aquarium::AspectInvocationTestClass.new aspect = Aspect.new :before, :objects => object, :method => :public_test_method do; @advice_called = true; end object.public_test_method aspect.unadvise @advice_called.should be_truthy end Aspect::CANONICAL_OPTIONS["objects"].each do |key| it "should accept :#{key} as a synonym for :objects." do object = Aquarium::AspectInvocationTestClass.new aspect = Aspect.new :before, key.intern => object, :method => :public_test_method, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new with :methods parameter" do it "should advise the specified methods." do @advice_called = false aspect = Aspect.new :before, :types => Aquarium::AspectInvocationTestClass, :methods => :public_test_method do; @advice_called = true; end Aquarium::AspectInvocationTestClass.new.public_test_method aspect.unadvise @advice_called.should be_truthy end Aspect::CANONICAL_OPTIONS["methods"].each do |key| it "should accept :#{key} as a synonym for :methods." do aspect = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, key.intern => :public_test_method, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new (:attributes parameter)" do Aspect::CANONICAL_OPTIONS["attributes"].each do |key| it "should accept :#{key} as a synonym for :attributes." do @advice_called = false aspect = Aspect.new :before, :types => Aquarium::AspectInvocationTestClass, :attributes => :public_test_method_args do; @advice_called = true; end Aquarium::AspectInvocationTestClass.new.public_test_method_args aspect.unadvise end end it "should require the values for :reading => ... and :writing => ... to be equal if both are specified." do @advice = Proc.new {} expect {Aspect.new :before, :type => Aquarium::AspectInvocationTestClass3, :reading => :public_test_method_args, :writing => :public_test_method_args2, :advice => @advice}.to raise_error(Aquarium::Utils::InvalidOptions) end it "should require the values for :reading => ... and :changing => ... to be equal if both are specified." do @advice = Proc.new {} expect {Aspect.new :before, :type => Aquarium::AspectInvocationTestClass3, :reading => :public_test_method_args, :changing => :public_test_method_args2, :advice => @advice}.to raise_error(Aquarium::Utils::InvalidOptions) end it "should accept :reading => ... as a synonym for :attributes => ..., :attribute_options => [:readers]." do @advice = Proc.new {} @expected_methods = [:public_test_method_args] aspect1 = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :reading => :public_test_method_args, :advice => @advice aspect2 = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :attributes => :public_test_method_args, :attribute_options => [:readers], :advice => @advice aspects_should_be_equal 1, aspect1, aspect2 aspect1.unadvise aspect2.unadvise end it "should accept :writing => ... as a synonym for :attributes => ..., :attribute_options => [:writer]." do @advice = Proc.new {} @expected_methods = [:public_test_method_args=] aspect1 = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :writing => :public_test_method_args, :advice => @advice aspect2 = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :attributes => :public_test_method_args, :attribute_options => [:writers], :advice => @advice aspects_should_be_equal 1, aspect1, aspect2 aspect1.unadvise aspect2.unadvise end it "should accept :changing => ... as a synonym for :attributes => ..., :attribute_options => [:writer]." do @advice = Proc.new {} @expected_methods = [:public_test_method_args=] aspect1 = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :changing => :public_test_method_args, :advice => @advice aspect2 = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :attributes => :public_test_method_args, :attribute_options => [:writers], :advice => @advice aspects_should_be_equal 1, aspect1, aspect2 aspect1.unadvise aspect2.unadvise end end describe Aspect, ".new (with a :type(s) parameter and a :method(s) parameter)" do before :each do @protection = 'public' @are_class_methods = false @types_option = :types @method_options = [] end def do_type_spec aspect = nil advice_called = false aspect = Aspect.new :before, @types_option => @type_spec, :methods => @method_spec, :method_options => @method_options do |jp, obj, *args| advice_called = true jp.should_not be_nil args.size.should == 4 args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}] end if @are_class_methods Aquarium::AspectInvocationTestClass.method("#{@protection}_class_test_method").call :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' else Aquarium::AspectInvocationTestClass.new.method("#{@protection}_test_method").call :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' end advice_called.should be_truthy aspect.unadvise end it "should accept :type(s) => T1, :methods => m" do @type_spec = Aquarium::AspectInvocationTestClass @method_spec = :public_test_method do_type_spec end it "should accept :type(s) => T1, :methods => [m, ...]" do @type_spec = Aquarium::AspectInvocationTestClass @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s) => T1, :methods => /m/" do @type_spec = Aquarium::AspectInvocationTestClass @method_spec = /test_method/ do_type_spec end it "should accept :type(s) => [T1, ...], :methods => m" do @type_spec = [Aquarium::AspectInvocationTestClass] @method_spec = :public_test_method do_type_spec end it "should accept :type(s) => [T1, ...], :methods => [m, ...]" do @type_spec = [Aquarium::AspectInvocationTestClass] @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s) => [T1, ...], :methods => /m/" do @type_spec = [Aquarium::AspectInvocationTestClass] @method_spec = /test_method/ do_type_spec end it "should accept :type(s) => /T1/, :methods => m" do @type_spec = /Aquarium::AspectInvocationTestClass/ @method_spec = :public_test_method do_type_spec end it "should accept :type(s) => /T1/, :methods => [m, ...]" do @type_spec = /Aquarium::AspectInvocationTestClass/ @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s) => /T1/, :methods => /m/" do @type_spec = /Aquarium::AspectInvocationTestClass/ @method_spec = /test_method/ do_type_spec end it "should accept :type(s)_and_ancestors => T1, :methods => [m, ...]" do @types_option = :types_and_ancestors @type_spec = Aquarium::AspectInvocationTestClass @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s)_and_ancestors => [T1, ...], :methods => [m, ...]" do @types_option = :types_and_ancestors @type_spec = [Aquarium::AspectInvocationTestClass] @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s)_and_ancestors => /T1/, :methods => [m, ...]" do @types_option = :types_and_ancestors @type_spec = /Aquarium::AspectInvocationTestClass/ @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s)_and_descendents => T1, :methods => [m, ...]" do @types_option = :types_and_descendents @type_spec = Aquarium::AspectInvocationTestClass @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s)_and_descendents => [T1, ...], :methods => [m, ...]" do @types_option = :types_and_descendents @type_spec = [Aquarium::AspectInvocationTestClass] @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s)_and_descendents => /T1/, :methods => [m, ...]" do @types_option = :types_and_descendents @type_spec = /Aquarium::AspectInvocationTestClass/ @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s)_and_nested_types => T1, :methods => [m, ...]" do @types_option = :types_and_nested_types @type_spec = Aquarium::AspectInvocationTestClass @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s)_and_nested_types => [T1, ...], :methods => [m, ...]" do @types_option = :types_and_nested_types @type_spec = [Aquarium::AspectInvocationTestClass] @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s)_and_nested_types => /T1/, :methods => [m, ...]" do @types_option = :types_and_nested_types @type_spec = /Aquarium::AspectInvocationTestClass/ @method_spec = [:public_test_method] do_type_spec end it "should accept :type(s) => ..., :methods => ..., :method_options => [:exclude_ancestor_methods] to exclude methods defined in ancestors" do @type_spec = /Aquarium::AspectInvocationTestClass/ @method_spec = /test_method/ @method_options = [:exclude_ancestor_methods] do_type_spec end it "should accept :type(s) => ..., :methods => ..., :method_options => [:instance, :public] to match only instance and public (both are the defaults) methods" do @type_spec = /Aquarium::AspectInvocationTestClass/ @method_spec = /test_method/ @method_options = [:instance, :public] do_type_spec end %w[public protected private].each do |protection| it "should accept :type(s) => ..., :methods => ..., :method_options => [#{protection.intern}] to match only instance (default) #{protection} methods" do @type_spec = /Aquarium::AspectInvocationTestClass/ @method_spec = /test_method/ @method_options = [protection.intern] @protection = protection do_type_spec end end it "should accept :type(s) => ..., :methods => ..., :method_options => [:class] to match only public (default) class methods" do @type_spec = /Aquarium::AspectInvocationTestClass/ @method_spec = /test_method/ @method_options = [:class] @are_class_methods = true do_type_spec end %w[public private].each do |protection| it "should accept :type(s) => ..., :methods => ..., :method_options => [:class, :#{protection.intern}] to match only class #{protection} methods" do @type_spec = /Aquarium::AspectInvocationTestClass/ @method_spec = /test_method/ @method_options = [:class, protection.intern] @protection = protection @are_class_methods = true do_type_spec end end end describe Aspect, ".new (with a :type(s) parameter and a :attribute(s) parameter)" do before :each do @protection = 'public' @attribute_options = [] @are_class_methods = false end def do_type_attribute_spec aspect = nil advice_called = false aspect = Aspect.new :before, :types => @type_spec, :attributes => @attribute_spec, :attribute_options => @attribute_options do |jp, obj, *args| advice_called = true jp.should_not be_nil expected_args = make_array(@expected_args) args.should == expected_args args.size.should == expected_args.size end object = Aquarium::AspectInvocationTestClass.new @expected_args = nil object.method("#{@protection}_test_method_args".intern).call @expected_args = :a1 object.method("#{@protection}_test_method_args=".intern).call @expected_args advice_called.should be_truthy aspect.unadvise end it "should accept :type(s) => [T1, ...], :attribute(s) => [a, ...]" do @type_spec = [Aquarium::AspectInvocationTestClass] @attribute_spec = [:public_test_method_args] do_type_attribute_spec end it "should accept :type(s) => [T1, ...], :attribute(s) => a" do @type_spec = [Aquarium::AspectInvocationTestClass] @attribute_spec = :public_test_method_args do_type_attribute_spec end it "should accept :type(s) => [T1, ...], :attribute(s) => /a/" do @type_spec = [Aquarium::AspectInvocationTestClass] @attribute_spec = /test_method_args/ do_type_attribute_spec end it "should accept :type(s) => T1, :attribute(s) => [a]" do @type_spec = Aquarium::AspectInvocationTestClass @attribute_spec = [:public_test_method_args] do_type_attribute_spec end it "should accept :type(s) => T1, :attribute(s) => a" do @type_spec = Aquarium::AspectInvocationTestClass @attribute_spec = :public_test_method_args do_type_attribute_spec end it "should accept :type(s) => T1, :attribute(s) => /a/" do @type_spec = Aquarium::AspectInvocationTestClass @attribute_spec = /test_method_args/ do_type_attribute_spec end it "should accept :type(s) => /T1/, :attribute(s) => [a, ...]" do @type_spec = /Aquarium::AspectInvocationTestClass/ @attribute_spec = [:public_test_method_args] do_type_attribute_spec end it "should accept :type(s) => /T1/, :attribute(s) => a" do @type_spec = /Aquarium::AspectInvocationTestClass/ @attribute_spec = :public_test_method_args do_type_attribute_spec end it "should accept :type(s) => /T1/, :attribute(s) => a" do @type_spec = /Aquarium::AspectInvocationTestClass/ @attribute_spec = /test_method_args/ do_type_attribute_spec end it "should accept :type(s) => ..., :attributes => ..., :attribute_options => [:readers, :writers] to include both attribute reader and writer methods (default)" do @type_spec = /Aquarium::AspectInvocationTestClass/ @attribute_spec = /test_method_args/ @attribute_options = [:readers, :writers] do_type_attribute_spec end it "should accept :type(s) => ..., :attributes => ..., :attribute_options => [:readers] to include only attribute reader methods" do @type_spec = /Aquarium::AspectInvocationTestClass/ @attribute_spec = /test_method_args/ @attribute_options = [:readers] do_type_attribute_spec end it "should accept attribute option :reader as a synonym for :readers" do @type_spec = /Aquarium::AspectInvocationTestClass/ @attribute_spec = /test_method_args/ @attribute_options = [:reader] do_type_attribute_spec end it "should accept :type(s) => ..., :attributes => ..., :attribute_options => [:writers] to include only attribute writer methods" do @type_spec = /Aquarium::AspectInvocationTestClass/ @attribute_spec = /test_method_args/ @attribute_options = [:writers] do_type_attribute_spec end it "should accept attribute option :writer as a synonym for :writers" do @type_spec = /Aquarium::AspectInvocationTestClass/ @attribute_spec = /test_method_args/ @attribute_options = [:writer] do_type_attribute_spec end it "should accept :type(s) => ..., :attributes => ..., :attribute_options => [:class, :readers, :writers] to include both attribute reader and writer methods (default) for class methods" do @type_spec = /Aquarium::AspectInvocationTestClass/ @attribute_spec = /test_method_args/ @attribute_options = [:class, :readers, :writers] do_type_attribute_spec end it "should accept :type(s) => ..., :attributes => ..., :attribute_options => [:class, :readers] to include only attribute reader class methods" do @type_spec = /Aquarium::AspectInvocationTestClass/ @attribute_spec = /test_method_args/ @attribute_options = [:class, :readers] do_type_attribute_spec end it "should accept :type(s) => ..., :attributes => ..., :attribute_options => [:class, :writers] to include only attribute writer class methods" do @type_spec = /Aquarium::AspectInvocationTestClass/ @attribute_spec = /test_method_args/ @attribute_options = [:class, :writers] do_type_attribute_spec end end describe Aspect, ".new (with a :object(s) parameter and a :method(s) parameter)" do before :each do @object1 = Aquarium::AspectInvocationTestClass.new @object2 = Aquarium::AspectInvocationTestClass.new @protection = 'public' @method_options = [] end def do_object_spec aspect = nil advice_called = false aspect = Aspect.new :before, :objects => @object_spec, :methods => @method_spec, :method_options => @method_options do |jp, obj, *args| advice_called = true jp.should_not be_nil args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}] end make_array(@object_spec).each do |object| object.method("#{@protection}_test_method".intern).call :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' end advice_called.should be_truthy aspect.unadvise end it "should accept :object(s) => [o1, ...], :methods => [m, ...]" do @object_spec = [@object1, @object2] @method_spec = [:public_test_method] do_object_spec end it "should accept :object(s) => [o1, ...], :methods => m" do @object_spec = [@object1, @object2] @method_spec = :public_test_method do_object_spec end it "should accept :object(s) => [o1, ...], :methods => /m/" do @object_spec = [@object1, @object2] @method_spec = /test_method/ do_object_spec end it "should accept :object(s) => o1, :methods => [m, ...]" do @object_spec = @object1 @method_spec = [:public_test_method] do_object_spec end it "should accept :object(s) => o1, :methods => m" do @object_spec = @object1 @method_spec = :public_test_method do_object_spec end it "should accept :object(s) => o1, :methods => /m/" do @object_spec = @object1 @method_spec = /test_method/ do_object_spec end it "should accept :object(s) => ..., :methods => ..., :method_options => [:exclude_ancestor_methods] to exclude methods defined in ancestors" do @object_spec = @object1 @method_spec = /test_method/ @method_options = [:exclude_ancestor_methods] do_object_spec end it "should accept :object(s) => ..., :methods => ..., :method_options => [:instance, :public] to match only instance and public (both are the defaults) methods" do @object_spec = @object1 @method_spec = /test_method/ @method_options = [:instance, :public] do_object_spec end %w[public protected private].each do |protection| it "should accept :object(s) => ..., :methods => ..., :method_options => [#{protection.intern}] to match only instance (default) #{protection} methods" do @object_spec = @object1 @method_spec = /test_method/ @method_options = [protection.intern] @protection = protection do_object_spec end it "should accept :object(s) => ..., :methods => ..., :method_options => [:instance, #{protection.intern}] to match only instance #{protection} methods" do @object_spec = @object1 @method_spec = /test_method/ @method_options = [:instance, protection.intern] @protection = protection do_object_spec end end end describe Aspect, ".new (with a :object(s) parameter and a :attribute(s) parameter)" do before :each do @object1 = Aquarium::AspectInvocationTestClass.new @object2 = Aquarium::AspectInvocationTestClass.new @protection = 'public' @attribute_options = [] end def do_object_attribute_spec aspect = nil advice_called = false aspect = Aspect.new :before, :objects => @object_spec, :attributes => @attribute_spec, :attribute_options => @attribute_options do |jp, obj, *args| advice_called = true jp.should_not be_nil expected_args = make_array(@expected_args) args.should == expected_args args.size.should == expected_args.size end make_array(@object_spec).each do |object| @expected_args = nil object.method("#{@protection}_test_method_args".intern).call @expected_args = :a1 object.method("#{@protection}_test_method_args=".intern).call @expected_args advice_called.should be_truthy end aspect.unadvise end it "should accept :object(s) => [T1, ...], :attribute(s) => [a, ...]" do @object_spec = [@object1, @object2] @attribute_spec = [:public_test_method_args] do_object_attribute_spec end it "should accept :object(s) => [T1, ...], :attribute(s) => a" do @object_spec = [@object1, @object2] @attribute_spec = :public_test_method_args do_object_attribute_spec end it "should accept :object(s) => [T1, ...], :attribute(s) => /a/" do @object_spec = [@object1, @object2] @attribute_spec = /test_method_args/ do_object_attribute_spec end it "should accept :object(s) => T1, :attribute(s) => [a]" do @object_spec = @object1 @attribute_spec = [:public_test_method_args] do_object_attribute_spec end it "should accept :object(s) => T1, :attribute(s) => a" do @object_spec = @object1 @attribute_spec = :public_test_method_args do_object_attribute_spec end it "should accept :object(s) => T1, :attribute(s) => /a/" do @object_spec = @object1 @attribute_spec = /test_method_args/ do_object_attribute_spec end it "should accept :object(s) => ..., :attributes => ..., :attribute_options => [:readers, :writers] to include both attribute reader and writer methods (default)" do @object_spec = @object1 @attribute_spec = /test_method_args/ @attribute_options = [:readers, :writers] do_object_attribute_spec end it "should accept :object(s) => ..., :attributes => ..., :attribute_options => [:readers] to include only attribute reader methods" do @object_spec = @object1 @attribute_spec = /test_method_args/ @attribute_options = [:readers] do_object_attribute_spec end it "should accept attribute option :reader as a synonym for :readers" do @object_spec = @object1 @attribute_spec = /test_method_args/ @attribute_options = [:reader] do_object_attribute_spec end it "should accept :object(s) => ..., :attributes => ..., :attribute_options => [:writers] to include only attribute writer methods" do @object_spec = @object1 @attribute_spec = /test_method_args/ @attribute_options = [:writers] do_object_attribute_spec end it "should accept attribute option :writer as a synonym for :writers" do @object_spec = @object1 @attribute_spec = /test_method_args/ @attribute_options = [:writer] do_object_attribute_spec end end describe Aspect, ".new (with a :pointcut parameter taking a hash with type specifications)" do before :each do @protection = 'public' @are_class_methods = false end def do_type_pointcut_spec aspect = nil advice_called = false aspect = Aspect.new :before, :pointcut => @pointcut_hash do |jp, obj, *args| advice_called = true jp.should_not be_nil args.size.should == 4 args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}] end if @are_class_methods Aquarium::AspectInvocationTestClass.method("#{@protection}_class_test_method".intern).call :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' else Aquarium::AspectInvocationTestClass.new.method("#{@protection}_test_method".intern).call :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' end advice_called.should be_truthy aspect.unadvise end it "should accept {:type(s) => [T1, ...], :methods => [m, ...]} " do @pointcut_hash = {:type => [Aquarium::AspectInvocationTestClass], :methods => [:public_test_method]} do_type_pointcut_spec end it "should accept {:type(s) => [T1, ...], :methods => m} " do @pointcut_hash = {:type => [Aquarium::AspectInvocationTestClass], :methods => :public_test_method} do_type_pointcut_spec end it "should accept {:type(s) => [T1, ...], :methods => /m/} " do @pointcut_hash = {:type => [Aquarium::AspectInvocationTestClass], :methods => /test_method/} do_type_pointcut_spec end it "should accept {:type(s)_and_ancestors => [T1, ...], :methods => /m/} " do @pointcut_hash = {:type_and_ancestors => [Aquarium::AspectInvocationTestClass], :methods => /test_method/} do_type_pointcut_spec end it "should accept {:type(s)_and_descendents => [T1, ...], :methods => /m/} " do @pointcut_hash = {:type_and_descendents => [Aquarium::AspectInvocationTestClass], :methods => /test_method/} do_type_pointcut_spec end it "should accept {:type(s)_and_nested_types => [T1, ...], :methods => /m/} " do @pointcut_hash = {:type_and_nested_types => [Aquarium::AspectInvocationTestClass], :methods => /test_method/} do_type_pointcut_spec end it "should accept {:type(s) => T1, :methods => [m, ...]} " do @pointcut_hash = {:type => Aquarium::AspectInvocationTestClass, :methods => [:public_test_method]} do_type_pointcut_spec end it "should accept {:type(s) => T1, :methods => m} " do @pointcut_hash = {:type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method} do_type_pointcut_spec end it "should accept {:type(s) => T1, :methods => /m/} " do @pointcut_hash = {:type => Aquarium::AspectInvocationTestClass, :methods => /test_method/} do_type_pointcut_spec end it "should accept {:type(s)_and_ancestors => T1, :methods => /m/} " do @pointcut_hash = {:type_and_ancestors => Aquarium::AspectInvocationTestClass, :methods => /test_method/} do_type_pointcut_spec end it "should accept {:type(s)_and_descendents => T1, :methods => /m/} " do @pointcut_hash = {:type_and_descendents => Aquarium::AspectInvocationTestClass, :methods => /test_method/} do_type_pointcut_spec end it "should accept {:type(s)_and_nested_types => T1, :methods => /m/} " do @pointcut_hash = {:type_and_nested_types => Aquarium::AspectInvocationTestClass, :methods => /test_method/} do_type_pointcut_spec end it "should accept {:type(s) => /T1/, :methods => [m, ...]} " do @pointcut_hash = {:type => /Aquarium::AspectInvocationTestClass/, :methods => [:public_test_method]} do_type_pointcut_spec end it "should accept {:type(s) => /T1/, :methods => m} " do @pointcut_hash = {:type => /Aquarium::AspectInvocationTestClass/, :methods => :public_test_method} do_type_pointcut_spec end it "should accept {:type(s) => /T1/, :methods => /m/} " do @pointcut_hash = {:type => /Aquarium::AspectInvocationTestClass/, :methods => /test_method/} do_type_pointcut_spec end it "should accept {:type(s)_and_ancestors => /T1/, :methods => /m/} " do @pointcut_hash = {:type_and_ancestors => /Aquarium::AspectInvocationTestClass/, :methods => /test_method/} do_type_pointcut_spec end it "should accept {:type(s)_and_descendents => /T1/, :methods => /m/} " do @pointcut_hash = {:type_and_descendents => /Aquarium::AspectInvocationTestClass/, :methods => /test_method/} do_type_pointcut_spec end it "should accept {:type(s)_and_nested_types => /T1/, :methods => /m/} " do @pointcut_hash = {:type_and_nested_types => /Aquarium::AspectInvocationTestClass/, :methods => /test_method/} do_type_pointcut_spec end %w[public protected private].each do |protection| it "should accept {:type(s) => T1, :methods => /m/, :method_options =>[:instance, #{protection}]} " do @protection = protection @pointcut_hash = {:type => Aquarium::AspectInvocationTestClass, :methods => /test_method/, :method_options =>[:instance, protection.intern]} do_type_pointcut_spec end end %w[public private].each do |protection| it "should accept {:type(s) => T1, :methods => /m/, :method_options =>[:class, #{protection}]} " do @pointcut_hash = {:type => Aquarium::AspectInvocationTestClass, :methods => /class_test_method/, :method_options =>[:class, protection.intern]} @protection = protection @are_class_methods = true do_type_pointcut_spec end end it "should accept {:type(s) => T1, :methods => /m/, :method_options =>[:instance]} defaults to public methods" do @pointcut_hash = {:type => Aquarium::AspectInvocationTestClass, :methods => /test_method/, :method_options =>[:instance]} do_type_pointcut_spec end it "should accept {:type(s) => T1, :methods => /m/, :method_options =>[:class]} defaults to public class methods" do @pointcut_hash = {:type => Aquarium::AspectInvocationTestClass, :methods => /test_method/, :method_options =>[:class]} @are_class_methods = true do_type_pointcut_spec end end describe Aspect, ".new (with a :pointcut parameter taking a hash with object specifications)" do before :each do @protection = 'public' @expected_advice_count = 2 @object1 = Aquarium::AspectInvocationTestClass.new @object2 = Aquarium::AspectInvocationTestClass.new end def do_object_pointcut_spec aspect = nil advice_count = 0 aspect = Aspect.new :before, :pointcut => @pointcut_hash do |jp, obj, *args| advice_count += 1 jp.should_not be_nil args.size.should == 4 args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}] end @object1.method("#{@protection}_test_method".intern).call :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' @object2.method("#{@protection}_test_method".intern).call :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' advice_count.should == @expected_advice_count aspect.unadvise end it "should accept {:objects => [o1, ...], :methods => [m, ...]} " do @pointcut_hash = {:objects => [@object1, @object2], :methods => [:public_test_method]} do_object_pointcut_spec end it "should accept {:objects => [o1, ...], :methods => m} " do @pointcut_hash = {:objects => [@object1, @object2], :methods => :public_test_method} do_object_pointcut_spec end it "should accept {:objects => [o1, ...], :methods => /m/} " do @pointcut_hash = {:objects => [@object1, @object2], :methods => /test_method/} do_object_pointcut_spec end it "should accept {:object => o1, :methods => [m, ...]} " do @expected_advice_count = 1 @pointcut_hash = {:object => @object1, :methods => [:public_test_method]} do_object_pointcut_spec end it "should accept {:objects => o1, :methods => m} " do @expected_advice_count = 1 @pointcut_hash = {:objects => @object1, :methods => :public_test_method} do_object_pointcut_spec end it "should accept {:objects => o1, :methods => /m/} " do @expected_advice_count = 1 @pointcut_hash = {:objects => @object1, :methods => /test_method/} do_object_pointcut_spec end end describe Aspect, ".new (with a :pointcut parameter and a Pointcut object or an array of Pointcuts)" do def do_pointcut_pointcut_spec aspect = nil advice_called = false aspect = Aspect.new :before, :pointcut => @pointcuts do |jp, obj, *args| advice_called = true jp.should_not be_nil args.size.should == 4 args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}] end Aquarium::AspectInvocationTestClass.new.public_test_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' advice_called.should be_truthy aspect.unadvise end it "should accept a single Pointcut object." do @pointcuts = Pointcut.new :type => [Aquarium::AspectInvocationTestClass], :methods => :public_test_method do_pointcut_pointcut_spec end it "should accept an array of Pointcut objects." do pointcut1 = Pointcut.new :type => [Aquarium::AspectInvocationTestClass], :methods => :public_test_method pointcut2 = Pointcut.new :type => [Aquarium::AspectInvocationTestClass], :methods => :public_class_test_method, :method_options => [:class] @pointcuts = [pointcut1, pointcut2] do_pointcut_pointcut_spec end end describe Aspect, ".new (with a :pointcut parameter and an array of Pointcuts)" do it "should treat the array as if it is one Pointcut \"or'ed\" together." do advice_called = 0 advice = Proc.new {|jp, obj, *args| advice_called += 1 jp.should_not be_nil args.size.should == 4 args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}] } pointcut1 = Pointcut.new :type => [Aquarium::AspectInvocationTestClass], :methods => :public_test_method pointcut2 = Pointcut.new :type => [Aquarium::AspectInvocationTestClass], :methods => :public_class_test_method, :method_options => [:class] pointcut12 = pointcut1.or pointcut2 aspect1 = Aspect.new :before, :pointcut => [pointcut1, pointcut2], :advice => advice Aquarium::AspectInvocationTestClass.new.public_test_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' Aquarium::AspectInvocationTestClass.public_class_test_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' advice_called.should == 2 aspect1.unadvise advice_called = 0 aspect2 = Aspect.new :before, :pointcut => pointcut12, :advice => advice Aquarium::AspectInvocationTestClass.new.public_test_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' Aquarium::AspectInvocationTestClass.public_class_test_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' advice_called.should == 2 aspect2.unadvise aspect1.join_points_matched.should eql(aspect2.join_points_matched) end end describe Aspect, ".new (with a :type(s) parameter and a :method(s) parameter or one of several equivalent :pointcut parameters)" do before :each do @advice = proc {|jp, obj, *args| "advice"} @expected_methods = [:public_test_method] end after :each do @aspect1.unadvise @aspect2.unadvise end it "should advise equivalent join points when :type => T and :method => m is used or :pointcut =>{:type => T, :method => m} is used." do @aspect1 = Aspect.new :after, :type => Aquarium::AspectInvocationTestClass, :method => :public_test_method, &@advice @aspect2 = Aspect.new :after, :pointcut => {:type => Aquarium::AspectInvocationTestClass, :method => :public_test_method}, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :type => T and :method => m is used or :pointcut => pointcut is used, where pointcut matches :type => T and :method => m." do @aspect1 = Aspect.new :after, :type => Aquarium::AspectInvocationTestClass, :method => :public_test_method, &@advice pointcut = Aquarium::Aspects::Pointcut.new :type => Aquarium::AspectInvocationTestClass, :method => :public_test_method @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :pointcut =>{:type => T, :method => m} is used or :pointcut => pointcut is used, where pointcut matches :type => T and :method => m." do @aspect1 = Aspect.new :after, :pointcut => {:type => Aquarium::AspectInvocationTestClass, :method => :public_test_method}, &@advice pointcut = Aquarium::Aspects::Pointcut.new :type => Aquarium::AspectInvocationTestClass, :method => :public_test_method @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise an equivalent join point when :type => T and :method => m is used or :pointcut => join_point is used, where join_point matches :type => T and :method => m." do @aspect1 = Aspect.new :after, :type => Aquarium::AspectInvocationTestClass, :method => :public_test_method, &@advice join_point = Aquarium::Aspects::JoinPoint.new :type => Aquarium::AspectInvocationTestClass, :method => :public_test_method @aspect2 = Aspect.new :after, :pointcut => join_point, &@advice join_points_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :type_and_ancestors => T and :method => m is used or :pointcut =>{:type_and_ancestors => T, :method => m} is used." do @aspect1 = Aspect.new :after, :type_and_ancestors => Aquarium::AspectInvocationTestClass, :method => :public_test_method, &@advice @aspect2 = Aspect.new :after, :pointcut => {:type_and_ancestors => Aquarium::AspectInvocationTestClass, :method => :public_test_method}, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :type_and_ancestors => T and :method => m is used or :pointcut => pointcut is used, where pointcut matches :type_and_ancestors => T and :method => m." do @aspect1 = Aspect.new :after, :type_and_ancestors => Aquarium::AspectInvocationTestClass, :method => :public_test_method, &@advice pointcut = Aquarium::Aspects::Pointcut.new :type_and_ancestors => Aquarium::AspectInvocationTestClass, :method => :public_test_method @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :pointcut =>{:type_and_ancestors => T, :method => m} is used or :pointcut => pointcut is used, where pointcut matches :type_and_ancestors => T and :method => m." do @aspect1 = Aspect.new :after, :pointcut => {:type_and_ancestors => Aquarium::AspectInvocationTestClass, :method => :public_test_method}, &@advice pointcut = Aquarium::Aspects::Pointcut.new :type_and_ancestors => Aquarium::AspectInvocationTestClass, :method => :public_test_method @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :type_and_descendents => T and :method => m is used or :pointcut =>{:type_and_descendents => T, :method => m} is used." do @aspect1 = Aspect.new :after, :type_and_descendents => Aquarium::AspectInvocationTestClass, :method => :public_test_method, &@advice @aspect2 = Aspect.new :after, :pointcut => {:type_and_descendents => Aquarium::AspectInvocationTestClass, :method => :public_test_method}, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :type_and_descendents => T and :method => m is used or :pointcut => pointcut is used, where pointcut matches :type_and_descendents => T and :method => m." do @aspect1 = Aspect.new :after, :type_and_descendents => Aquarium::AspectInvocationTestClass, :method => :public_test_method, &@advice pointcut = Aquarium::Aspects::Pointcut.new :type_and_descendents => Aquarium::AspectInvocationTestClass, :method => :public_test_method @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :pointcut =>{:type_and_descendents => T, :method => m} is used or :pointcut => pointcut is used, where pointcut matches :type_and_descendents => T and :method => m." do @aspect1 = Aspect.new :after, :pointcut => {:type_and_descendents => Aquarium::AspectInvocationTestClass, :method => :public_test_method}, &@advice pointcut = Aquarium::Aspects::Pointcut.new :type_and_descendents => Aquarium::AspectInvocationTestClass, :method => :public_test_method @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :type_and_nested_types => T and :method => m is used or :pointcut =>{:type_and_nested_types => T, :method => m} is used." do @aspect1 = Aspect.new :after, :type_and_nested_types => Aquarium::AspectInvocationTestClass, :method => :public_test_method, &@advice @aspect2 = Aspect.new :after, :pointcut => {:type_and_nested_types => Aquarium::AspectInvocationTestClass, :method => :public_test_method}, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :type_and_nested_types => T and :method => m is used or :pointcut => pointcut is used, where pointcut matches :type_and_nested_types => T and :method => m." do @aspect1 = Aspect.new :after, :type_and_nested_types => Aquarium::AspectInvocationTestClass, :method => :public_test_method, &@advice pointcut = Aquarium::Aspects::Pointcut.new :type_and_nested_types => Aquarium::AspectInvocationTestClass, :method => :public_test_method @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :pointcut =>{:type_and_nested_types => T, :method => m} is used or :pointcut => pointcut is used, where pointcut matches :type_and_nested_types => T and :method => m." do @aspect1 = Aspect.new :after, :pointcut => {:type_and_nested_types => Aquarium::AspectInvocationTestClass, :method => :public_test_method}, &@advice pointcut = Aquarium::Aspects::Pointcut.new :type_and_nested_types => Aquarium::AspectInvocationTestClass, :method => :public_test_method @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end end describe Aspect, ".new (with a :type(s) parameter and an :attributes(s) parameter or one of several equivalent :pointcut parameters)" do class ClassWithAttrib1 def dummy; end attr_accessor :state end before :each do @advice = proc {|jp, obj, *args| "advice"} @expected_methods = [:state, :state=] end after :each do @aspect1.unadvise @aspect2.unadvise end it "should not advise any method join points except those corresponding to attribute methods." do @aspect1 = Aspect.new :after, :type => ClassWithAttrib1, :attribute => :state, &@advice @aspect2 = Aspect.new :after, :pointcut => {:type => ClassWithAttrib1, :attribute => :state}, &@advice aspects_should_be_equal 2, @aspect1, @aspect2 end it "should advise equivalent join points when :type => T and :attribute => a is used or :pointcut =>{:type => T, :attribute => a} is used." do @aspect1 = Aspect.new :after, :type => ClassWithAttrib1, :attribute => :state, &@advice @aspect2 = Aspect.new :after, :pointcut => {:type => ClassWithAttrib1, :attribute => :state}, &@advice aspects_should_be_equal 2, @aspect1, @aspect2 end it "should advise equivalent join points when :type => T and :attribute => a is used or :pointcut => pointcut is used, where pointcut matches :type => T and :attribute => a." do @aspect1 = Aspect.new :after, :type => ClassWithAttrib1, :attribute => :state, &@advice pointcut = Aquarium::Aspects::Pointcut.new :type => ClassWithAttrib1, :attribute => :state @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 2, @aspect1, @aspect2 end it "should advise equivalent join points when :pointcut =>{:type => T, :attribute => a} is used or :pointcut => pointcut is used, where pointcut matches :type => T and :attribute => a." do @aspect1 = Aspect.new :after, :pointcut => {:type => ClassWithAttrib1, :attribute => :state}, &@advice pointcut = Aquarium::Aspects::Pointcut.new :type => ClassWithAttrib1, :attribute => :state @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 2, @aspect1, @aspect2 end it "should advise equivalent join points when :type => T and :attribute => a (the attribute's reader and writer) is used or :pointcut => [join_points] is used, where the join_points match :type => T and :method => :a and :method => :a=." do @aspect1 = Aspect.new :after, :type => ClassWithAttrib1, :attribute => :state, &@advice join_point1 = Aquarium::Aspects::JoinPoint.new :type => ClassWithAttrib1, :method => :state join_point2 = Aquarium::Aspects::JoinPoint.new :type => ClassWithAttrib1, :method => :state= @aspect2 = Aspect.new :after, :pointcut => Pointcut.new(:join_points => [join_point1, join_point2]), &@advice join_points_should_be_equal 2, @aspect1, @aspect2 end it "should advise an equivalent join point when :type => T and :method => :a= (the attribute's writer) is used or :pointcut => join_point is used, where join_point matches :type => T and :method => a=." do @aspect1 = Aspect.new :after, :type => ClassWithAttrib1, :attribute => :state, :attribute_options => [:writer], &@advice join_point = Aquarium::Aspects::JoinPoint.new :type => ClassWithAttrib1, :method => :state= @aspect2 = Aspect.new :after, :pointcut => join_point, &@advice join_points_should_be_equal 1, @aspect1, @aspect2 end end describe Aspect, ".new (with a :object(s) parameter and a :method(s) parameter or one of several equivalent :pointcut parameters)" do before :each do @advice = proc {|jp, obj, *args| "advice"} @expected_methods = [:public_test_method] end after :each do @aspect1.unadvise @aspect2.unadvise end it "should advise equivalent join points when :object => o and :method => m is used or :pointcut =>{:object => o, :method => m} is used." do object = Aquarium::AspectInvocationTestClass.new @aspect1 = Aspect.new :after, :object => object, :method => :public_test_method, &@advice @aspect2 = Aspect.new :after, :pointcut => {:object => object, :method => :public_test_method}, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :object => o and :method => m is used or :pointcut => pointcut is used, where pointcut matches :object => o and :method => m." do object = Aquarium::AspectInvocationTestClass.new @aspect1 = Aspect.new :after, :object => object, :method => :public_test_method, &@advice pointcut = Aquarium::Aspects::Pointcut.new :object => object, :method => :public_test_method @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end it "should advise equivalent join points when :pointcut =>{:object => o, :method => m} is used or :pointcut => pointcut is used, where pointcut matches :object => o and :method => m." do object = Aquarium::AspectInvocationTestClass.new @aspect1 = Aspect.new :after, :pointcut => {:object => object, :method => :public_test_method}, &@advice pointcut = Aquarium::Aspects::Pointcut.new :object => object, :method => :public_test_method @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 1, @aspect1, @aspect2 end end describe Aspect, ".new (with a :object(s) parameter and an :attributes(s) parameter or one of several equivalent :pointcut parameters)" do class ClassWithAttrib2 def initialize *args @state = args end def dummy; end attr_accessor :state end before :each do @advice = proc {|jp, obj, *args| "advice"} @object = ClassWithAttrib2.new @expected_methods = [:state, :state=] end after :each do @aspect1.unadvise @aspect2.unadvise end it "should not advise any method join points except those corresponding to attribute methods." do @aspect1 = Aspect.new :after, :object => @object, :attribute => :state, &@advice @aspect2 = Aspect.new :after, :pointcut => {:object => @object, :attribute => :state}, &@advice aspects_should_be_equal 2, @aspect1, @aspect2 end it "should advise equivalent join points when :type => T and :attribute => a is used or :pointcut =>{:type => T, :attribute => a} is used." do @aspect1 = Aspect.new :after, :object => @object, :attribute => :state, &@advice @aspect2 = Aspect.new :after, :pointcut => {:object => @object, :attribute => :state}, &@advice aspects_should_be_equal 2, @aspect1, @aspect2 end it "should advise equivalent join points when :type => T and :attribute => a is used or :pointcut => pointcut is used, where pointcut matches :type => T and :attribute => a." do @aspect1 = Aspect.new :after, :object => @object, :attribute => :state, &@advice pointcut = Aquarium::Aspects::Pointcut.new :object => @object, :attribute => :state @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 2, @aspect1, @aspect2 end it "should advise equivalent join points when :pointcut =>{:type => T, :attribute => a} is used or :pointcut => pointcut is used, where pointcut matches :type => T and :attribute => a." do @aspect1 = Aspect.new :after, :pointcut => {:object => @object, :attribute => :state}, &@advice pointcut = Aquarium::Aspects::Pointcut.new :object => @object, :attribute => :state @aspect2 = Aspect.new :after, :pointcut => pointcut, &@advice aspects_should_be_equal 2, @aspect1, @aspect2 end it "should advise equivalent join points when :type => T and :attribute => a (the attribute's reader and writer) is used or :pointcut => [join_points] is used, where the join_points match :type => T and :method => :a and :method => :a=." do @aspect1 = Aspect.new :after, :object => @object, :attribute => :state, &@advice join_point1 = Aquarium::Aspects::JoinPoint.new :object => @object, :method => :state join_point2 = Aquarium::Aspects::JoinPoint.new :object => @object, :method => :state= @aspect2 = Aspect.new :after, :pointcut => Pointcut.new(:join_points => [join_point1, join_point2]), &@advice join_points_should_be_equal 2, @aspect1, @aspect2 end it "should advise an equivalent join point when :type => T and :method => :a= (the attribute's writer) is used or :pointcut => join_point is used, where join_point matches :type => T and :method => a=." do @aspect1 = Aspect.new :after, :object => @object, :attribute => :state, :attribute_options => [:writer], &@advice join_point = Aquarium::Aspects::JoinPoint.new :object => @object, :method => :state= @aspect2 = Aspect.new :after, :pointcut => join_point, &@advice join_points_should_be_equal 1, @aspect1, @aspect2 end end describe Aspect, ".new (block for advice)" do it "should accept a block as the advice to use." do object = Aquarium::AspectInvocationTestClass.new advice_called = false aspect = Aspect.new :before, :object => object, :methods => :public_test_method do |jp, obj, *args| advice_called = true jp.should_not be_nil args.size.should == 4 args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}] end object.public_test_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' advice_called.should be_truthy aspect.unadvise end it "should accept an :advice => Proc parameter indicating the advice to use." do object = Aquarium::AspectInvocationTestClass.new advice_called = false advice = Proc.new {|jp, obj, *args| advice_called = true jp.should_not be_nil args.size.should == 4 args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}] } aspect = Aspect.new :before, :object => object, :methods => :public_test_method, :advice => advice object.public_test_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' advice_called.should be_truthy aspect.unadvise end Aspect::CANONICAL_OPTIONS["advice"].each do |key| it "should accept :#{key} => proc as a synonym for :advice." do object = Aquarium::AspectInvocationTestClass.new advice_called = false advice = Proc.new {|jp, obj, *args| advice_called = true jp.should_not be_nil args.size.should == 4 args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}] } aspect = Aspect.new :before, :object => object, :methods => :public_test_method, key.intern => advice object.public_test_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' advice_called.should be_truthy aspect.unadvise end end it "should allow only one :advice object to be specified (including synonyms)." do object = Aquarium::AspectInvocationTestClass.new advice_called = false advice1 = Proc.new {|jp, obj, *args| fail "advice1"} advice2 = Proc.new {|jp, obj, *args| fail "advice2"} expect {Aspect.new :before, :object => object, :methods => :public_test_method, :advice => advice1, :invoke => advice2}.to raise_error(Aquarium::Utils::InvalidOptions) end it "should allow ignore an :advice option if a block is given." do object = Aquarium::AspectInvocationTestClass.new advice_called = false advice1 = Proc.new {|jp, obj, *args| fail "advice1"} advice2 = Proc.new {|jp, obj, *args| fail "advice2"} aspect = Aspect.new :before, :object => object, :methods => :public_test_method, :advice => advice1 do |jp, obj, *args| advice_called = true end object.public_test_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2' advice_called.should be_truthy aspect.unadvise end end describe Aspect, ".new (advice block or proc parameter list)" do it "should raise unless an advice block or :advice => advice parameter is specified." do expect { Aspect.new(:after, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method)}.to raise_error(Aquarium::Utils::InvalidOptions) end it "should raise if obsolete |jp, *args| list is used." do expect { Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method do |jp, *args|; end }.to raise_error(Aquarium::Utils::InvalidOptions) end it "should accept an argument list matching |jp, object, *args|." do aspect = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method, :noop => true do |jp, object, *args|; end aspect.unadvise end it "should accept an argument list matching |jp, object|." do aspect = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method, :noop => true do |jp, object|; end aspect.unadvise end it "should accept an argument list matching |jp|." do aspect = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method, :noop => true do |jp|; end aspect.unadvise end it "should accept an argument list matching ||." do aspect = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method, :noop => true do ||; end aspect.unadvise end it "should accept no argument list." do aspect = Aspect.new :before, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method, :noop => true do; end aspect.unadvise end end describe Aspect, ".new (advice block to around advice with just the join_point parameter - Bug #19262)" do it "should not raise an error" do aspect = Aspect.new :around, :type => Aquarium::AspectInvocationTestClass, :methods => :public_test_method do |jp|; jp.proceed; end Aquarium::AspectInvocationTestClass.new.public_test_method aspect.unadvise end end class ExcludeBase def doit; end def initialize arg; @value = arg; end attr_accessor :value def inspect; "(#{self.class}: #{@value})"; end def eql? other return false unless other.kind_of?(self.class) @value == other.value end alias_method :==, :eql? end class DontExclude1 < ExcludeBase; end class DontExclude2 < ExcludeBase; end class Exclude1 < ExcludeBase; end class Exclude2 < ExcludeBase; end class Exclude1b < Exclude1 def initialize arg; super(arg); end def doit2; end end class Exclude1c < Exclude1 def initialize arg; super(arg); end def doit3; end end describe Aspect, ".new (with a :type(s) parameter and an :exclude_type(s), and :exclude_type(s)_and_ancestors, an :exclude_type(s)_and_descendents, or an :exclude_type(s)_and_nested_types parameter)" do before :all do @included_types = [DontExclude1, DontExclude2] @excluded_types = [Exclude1, Exclude2] @all_types = @included_types + @excluded_types end def do_exclude_types exclude_type_sym aspect = nil advice_called = false aspect = Aspect.new :before, :types => @all_types, exclude_type_sym => @excluded_types, :methods => :doit do |jp, obj, *args| advice_called = true @excluded_types.should_not include(jp.target_type) end @included_types.each do |type| advice_called = false type.new(1).doit advice_called.should be_truthy end @excluded_types.each do |type| advice_called = false type.new(1).doit advice_called.should_not be_truthy end aspect.unadvise end it "should accept :type(s) => [T1, ...], :exclude_types => [T2, ...] and exclude join points in the excluded types" do do_exclude_types :exclude_types end Aspect::CANONICAL_OPTIONS["exclude_types"].each do |key| it "should accept :#{key} as a synonym for :exclude_types." do aspect = Aspect.new :before, :types => @all_types, key.intern => @excluded_types, :methods => :doit, :noop => true do; end aspect.unadvise end end it "should accept :type(s) => [T1, ...], :exclude_types_and_ancestors => [T2, ...] and exclude join points in the excluded types" do do_exclude_types :exclude_types_and_ancestors end Aspect::CANONICAL_OPTIONS["exclude_types_and_ancestors"].each do |key| it "should accept :#{key} as a synonym for :exclude_types_and_ancestors." do aspect = Aspect.new :before, :types => @all_types, key.intern => @excluded_types, :methods => :doit, :noop => true do; end aspect.unadvise end end it "should accept :type(s) => [T1, ...], :exclude_types_and_descendents => [T2, ...] and exclude join points in the excluded types" do do_exclude_types :exclude_types_and_descendents end Aspect::CANONICAL_OPTIONS["exclude_types_and_descendents"].each do |key| it "should accept :#{key} as a synonym for :exclude_types_and_descendents." do aspect = Aspect.new :before, :types => @all_types, key.intern => @excluded_types, :methods => :doit, :noop => true do; end aspect.unadvise end end it "should accept :type(s) => [T1, ...], :exclude_types_and_nested_types => [T2, ...] and exclude join points in the excluded types" do do_exclude_types :exclude_types_and_nested_types end Aspect::CANONICAL_OPTIONS["exclude_types_and_nested_types"].each do |key| it "should accept :#{key} as a synonym for :exclude_types_and_nested_types." do aspect = Aspect.new :before, :types => @all_types, key.intern => @excluded_types, :methods => :doit, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new (with a :object(s) parameter and an :exclude_object(s) parameter)" do before :all do dontExclude1 = DontExclude1.new(1) dontExclude2 = DontExclude1.new(2) exclude1 = DontExclude1.new(3) exclude2 = DontExclude1.new(4) @included_objects = [dontExclude1, dontExclude2] @excluded_objects = [exclude1, exclude2] @all_objects = @included_objects + @excluded_objects end it "should accept :object(s) => [o1, ...], :exclude_object(s) => [o2, ...] and exclude join points in the excluded objects" do aspect = nil advice_called = false aspect = Aspect.new :before, :objects => @all_objects, :exclude_objects => @excluded_objects, :methods => :doit do |jp, obj, *args| advice_called = true @excluded_objects.should_not include(obj) end @included_objects.each do |object| advice_called = false object.doit advice_called.should be_truthy end @excluded_objects.each do |object| advice_called = false object.doit advice_called.should_not be_truthy end aspect.unadvise end Aspect::CANONICAL_OPTIONS["exclude_objects"].each do |key| it "should accept :#{key} as a synonym for :exclude_objects." do aspect = Aspect.new :before, :objects => @all_objects, key.intern => @excluded_objects, :methods => :doit, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new (with an :object(s) and an :exclude_join_point(s) parameter)" do before :all do dontExclude1 = DontExclude1.new(1) dontExclude2 = DontExclude1.new(2) exclude1 = DontExclude1.new(3) exclude2 = DontExclude1.new(4) @included_objects = [dontExclude1, dontExclude2] @excluded_objects = [exclude1, exclude2] @all_objects = @included_objects + @excluded_objects excluded_join_point1 = JoinPoint.new :object => exclude1, :method => :doit excluded_join_point2 = JoinPoint.new :object => exclude2, :method => :doit @excluded_join_points = [excluded_join_point1, excluded_join_point2] end it "should accept :type(s) => [T1, ...], :exclude_join_point(s) => [jps], where [jps] are the list of join points for the types and methods to exclude" do aspect = nil advice_called = false aspect = Aspect.new :before, :objects => @all_objects, :exclude_join_points => @excluded_join_points, :methods => :doit do |jp, obj, *args| advice_called = true @excluded_objects.should_not include(obj) end @included_objects.each do |object| advice_called = false object.doit advice_called.should be_truthy end @excluded_objects.each do |object| advice_called = false object.doit advice_called.should_not be_truthy end aspect.unadvise end Aspect::CANONICAL_OPTIONS["exclude_join_points"].each do |key| it "should accept :#{key} as a synonym for :exclude_join_points." do aspect = Aspect.new :before, :objects => @all_objects, key.intern => @excluded_join_points, :methods => :doit, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new (with a :type(s) parameter and an :exclude_join_point(s) parameter)" do it "should accept :type(s) => [T1, ...], :exclude_join_point(s) => [jps], where [jps] are the list of join points for the types and methods to exclude" do included_types = [DontExclude1, DontExclude2] excluded_types = [Exclude1, Exclude2] excluded_join_point1 = JoinPoint.new :type => Exclude1, :method => :doit excluded_join_point2 = JoinPoint.new :type => Exclude2, :method => :doit excluded_join_points = [excluded_join_point1, excluded_join_point2] aspect = nil advice_called = false aspect = Aspect.new :before, :types => (included_types + excluded_types), :exclude_join_points => excluded_join_points, :methods => :doit do |jp, obj, *args| advice_called = true excluded_types.should_not include(jp.target_type) end included_types.each do |type| advice_called = false type.new(1).doit advice_called.should be_truthy end excluded_types.each do |type| advice_called = false type.new(1).doit advice_called.should_not be_truthy end aspect.unadvise end end describe Aspect, ".new (with a :type(s)_and_ancestors parameter and an :exclude_join_point(s) parameter)" do it "should accept :type(s)_and_ancestors => [T1, ...], :exclude_join_point(s) => [jps], where [jps] are the list of join points for the types and methods to exclude" do included_types = [ClassWithPublicInstanceMethod, ModuleWithPublicInstanceMethod] excluded_join_point1 = JoinPoint.new :type => ClassWithPublicInstanceMethod, :method => :public_instance_test_method excluded_join_point2 = JoinPoint.new :type => ModuleWithPublicInstanceMethod, :method => :public_instance_module_test_method excluded_join_points = [excluded_join_point1, excluded_join_point2] aspect = nil advice_called = false aspect = Aspect.new :before, :types_and_ancestors => included_types, :methods => :doit, :exclude_join_points => excluded_join_points, :ignore_no_matching_join_points => true do |jp, obj, *args|; advice_called = true; end advice_called = false ClassWithPublicInstanceMethod.new.public_instance_test_method advice_called.should be_falsey advice_called = false ClassIncludingModuleWithPublicInstanceMethod.new.public_instance_module_test_method advice_called.should be_falsey aspect.unadvise end end describe Aspect, ".new (with a :type(s)_and_descendents parameter and an :exclude_join_point(s) parameter)" do it "should accept :type(s)_and_descendents => [T1, ...], :exclude_join_point(s) => [jps], where [jps] are the list of join points for the types and methods to exclude" do included_types = [ClassWithPublicInstanceMethod, ModuleWithPublicInstanceMethod] excluded_join_point1 = JoinPoint.new :type => ClassWithPublicInstanceMethod, :method => :public_instance_test_method excluded_join_point2 = JoinPoint.new :type => ModuleWithPublicInstanceMethod, :method => :public_instance_module_test_method excluded_join_points = [excluded_join_point1, excluded_join_point2] aspect = nil advice_called = false aspect = Aspect.new :before, :types_and_descendents => included_types, :methods => :doit, :exclude_join_points => excluded_join_points, :ignore_no_matching_join_points => true do |jp, obj, *args|; advice_called = true; end advice_called = false ClassWithPublicInstanceMethod.new.public_instance_test_method advice_called.should be_falsey advice_called = false ClassIncludingModuleWithPublicInstanceMethod.new.public_instance_module_test_method advice_called.should be_falsey aspect.unadvise end end describe Aspect, ".new (with a :type(s)_and_nested_types parameter and an :exclude_join_point(s) parameter)" do it "should accept :type(s)_and_nested_types => [T1, ...], :exclude_join_point(s) => [jps], where [jps] are the list of join points for the types and methods to exclude" do included_types = [ClassWithPublicInstanceMethod, ModuleWithPublicInstanceMethod] excluded_join_point1 = JoinPoint.new :type => ClassWithPublicInstanceMethod, :method => :public_instance_test_method excluded_join_point2 = JoinPoint.new :type => ModuleWithPublicInstanceMethod, :method => :public_instance_module_test_method excluded_join_points = [excluded_join_point1, excluded_join_point2] aspect = nil advice_called = false aspect = Aspect.new :before, :types_and_nested_types => included_types, :methods => :doit, :exclude_join_points => excluded_join_points, :ignore_no_matching_join_points => true do |jp, obj, *args|; advice_called = true; end advice_called = false ClassWithPublicInstanceMethod.new.public_instance_test_method advice_called.should be_falsey advice_called = false ClassIncludingModuleWithPublicInstanceMethod.new.public_instance_module_test_method advice_called.should be_falsey aspect.unadvise end end describe Aspect, ".new (with a :pointcut(s) parameter and an :exclude_join_point(s) parameter)" do it "should accept :pointcut(s) => [P1, ...], :exclude_join_point(s) => [jps], where [jps] are the list of join points for the types and methods to exclude" do included_types = [DontExclude1, DontExclude2] excluded_types = [Exclude1, Exclude2] excluded_join_point1 = JoinPoint.new :type => Exclude1, :method => :doit excluded_join_point2 = JoinPoint.new :type => Exclude2, :method => :doit excluded_join_points = [excluded_join_point1, excluded_join_point2] pointcut1 = Pointcut.new :types => included_types, :method => :doit pointcut2 = Pointcut.new :types => excluded_types, :method => :doit aspect = nil advice_called = false aspect = Aspect.new :before, :pointcuts => [pointcut1, pointcut2], :exclude_join_points => excluded_join_points do |jp, obj, *args| advice_called = true excluded_types.should_not include(jp.target_type) end included_types.each do |type| advice_called = false type.new(1).doit advice_called.should be_truthy end excluded_types.each do |type| advice_called = false type.new(1).doit advice_called.should_not be_truthy end aspect.unadvise end end describe Aspect, ".new (with an :object(s) and an :exclude_pointcut(s) parameter)" do before :all do dontExclude1 = DontExclude1.new(1) dontExclude2 = DontExclude1.new(2) exclude1 = DontExclude1.new(3) exclude2 = DontExclude1.new(4) @included_objects = [dontExclude1, dontExclude2] @excluded_objects = [exclude1, exclude2] @all_objects = @included_objects + @excluded_objects excluded_pointcut1 = Pointcut.new :object => exclude1, :method => :doit excluded_pointcut2 = Pointcut.new :object => exclude2, :method => :doit @excluded_pointcuts = [excluded_pointcut1, excluded_pointcut2] end it "should accept :object(s) => [o1, ...], :exclude_pointcut(s) => [pcs], where [pcs] are the list of pointcuts for the objects and methods to exclude" do aspect = nil advice_called = false aspect = Aspect.new :before, :objects => @all_objects, :methods => :doit, :exclude_pointcuts => @excluded_pointcuts, :ignore_no_matching_join_points => true do |jp, obj, *args| advice_called = true @excluded_objects.should_not include(obj) end @included_objects.each do |object| advice_called = false object.doit advice_called.should be_truthy end @excluded_objects.each do |object| advice_called = false object.doit advice_called.should_not be_truthy end aspect.unadvise end Aspect::CANONICAL_OPTIONS["exclude_pointcuts"].each do |key| it "should accept :#{key} as a synonym for :exclude_pointcuts." do aspect = Aspect.new :before, :objects => @all_objects, key.intern => @excluded_pointcuts, :methods => :doit, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new (with a :type(s) and an :exclude_pointcut(s) parameter)" do before :all do @included_types = [DontExclude1, DontExclude2] @excluded_types = [Exclude1, Exclude2] @all_types = @included_types + @excluded_types excluded_pointcut1 = Pointcut.new :type => Exclude1, :method => :doit excluded_pointcut2 = Pointcut.new :type => Exclude2, :method => :doit @excluded_pointcuts = [excluded_pointcut1, excluded_pointcut2] end it "should accept :type(s) => [T1, ...], :exclude_pointcut(s) => [pcs], where [pcs] are the list of pointcuts for the types and methods to exclude" do aspect = nil advice_called = false aspect = Aspect.new :before, :types => @all_types, :exclude_pointcuts => @excluded_pointcuts, :methods => :doit do |jp, obj, *args| advice_called = true @excluded_types.should_not include(jp.target_type) end @included_types.each do |type| advice_called = false type.new(1).doit advice_called.should be_truthy end @excluded_types.each do |type| advice_called = false type.new(1).doit advice_called.should_not be_truthy end aspect.unadvise end end describe Aspect, ".new (with a :pointcut(s) and an :exclude_pointcut(s) parameter)" do before :all do @included_types = [DontExclude1, DontExclude2] @excluded_types = [Exclude1, Exclude2] @all_types = @included_types + @excluded_types excluded_pointcut1 = Pointcut.new :type => Exclude1, :method => :doit excluded_pointcut2 = Pointcut.new :type => Exclude2, :method => :doit @excluded_pointcuts = [excluded_pointcut1, excluded_pointcut2] pointcut1 = Pointcut.new :types => @included_types, :method => :doit pointcut2 = Pointcut.new :types => @excluded_types, :method => :doit @all_pointcuts = [pointcut1, pointcut2] end it "should accept :pointcut(s) => [P1, ...], :exclude_pointcut(s) => [pcs], where [pcs] are the list of pointcuts for the types and methods to exclude" do aspect = nil advice_called = false aspect = Aspect.new :before, :pointcuts => @all_pointcuts, :exclude_pointcuts => @excluded_pointcuts do |jp, obj, *args| advice_called = true @excluded_types.should_not include(jp.target_type) end @included_types.each do |type| advice_called = false type.new(1).doit advice_called.should be_truthy end @excluded_types.each do |type| advice_called = false type.new(1).doit advice_called.should_not be_truthy end aspect.unadvise end end describe Aspect, ".new (with a :pointcut(s) and an :exclude_named_pointcut(s) parameter)" do it "should accept :pointcut(s) => [P1, ...], :exclude_named_pointcut(s) => {...}, where any pointcuts matching the latter are excluded" do aspect = nil advice_called = false aspect = Aspect.new :before, :pointcuts => Aquarium::PointcutFinderTestClasses.all_pointcuts, :exclude_named_pointcuts => {:matching => /POINTCUT/, :in_types => Aquarium::PointcutFinderTestClasses.all_pointcut_classes} do |jp, obj, *args| advice_called = true Aquarium::PointcutFinderTestClasses.all_constants_pointcut_classes.should_not include(jp.target_type) end Aquarium::PointcutFinderTestClasses.all_class_variables_pointcut_classes.each do |type| advice_called = false type.new.doit advice_called.should be_truthy end Aquarium::PointcutFinderTestClasses.all_constants_pointcut_classes.each do |type| advice_called = false type.new.doit advice_called.should_not be_truthy end aspect.unadvise end Aspect::CANONICAL_OPTIONS["exclude_named_pointcuts"].each do |key| it "should accept :#{key} as a synonym for :exclude_named_pointcuts." do aspect = Aspect.new :before, :pointcuts => Aquarium::PointcutFinderTestClasses.all_pointcuts, key.intern => {:matching => /POINTCUT/, :in_types => Aquarium::PointcutFinderTestClasses.all_pointcut_classes}, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new (with type-based :pointcut(s) and :exclude_type(s) parameter)" do before :all do @included_types = [DontExclude1, DontExclude2] @excluded_types = [Exclude1, Exclude2] @pointcut1 = Pointcut.new :types => @included_types, :method => :doit @pointcut2 = Pointcut.new :types => @excluded_types, :method => :doit end it "should accept :pointcut(s) => [P1, ...], :exclude_type(s) => [types], where join points with [types] are excluded" do advice_called = false aspect = Aspect.new :before, :pointcuts => [@pointcut1, @pointcut2], :exclude_types => @excluded_types do |jp, obj, *args| advice_called = true @excluded_types.should_not include(jp.target_type) end @included_types.each do |type| advice_called = false type.new(1).doit advice_called.should be_truthy end @excluded_types.each do |type| advice_called = false type.new(1).doit advice_called.should_not be_truthy end aspect.unadvise end Aspect::CANONICAL_OPTIONS["exclude_types"].each do |key| it "should accept :#{key} as a synonym for :exclude_types." do aspect = Aspect.new :before, :pointcuts => [@pointcut1, @pointcut2], key.intern => @excluded_types, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new (with type-based :pointcut(s) and :exclude_type(s)_and_ancestors parameter)" do before :all do @excluded_types = [ClassWithPublicInstanceMethod, ModuleWithPublicInstanceMethod] @types = @excluded_types + [ClassDerivedFromClassIncludingModuleWithPublicInstanceMethod] @pointcut1 = Pointcut.new :types => @types, :method => :all, :method_options => [:exclude_ancestor_methods] end it "should accept :pointcut(s) => [P1, ...], :exclude_type(s)_and_ancestors => [types], where join points with [types] are excluded" do aspect = Aspect.new :before, :pointcuts => @pointcut1, :exclude_types_and_ancestors => @excluded_types do |jp, obj, *args|; end aspect.pointcuts.each do |pc| pc.join_points_matched.each do |jp| jp.target_type.should == ClassDerivedFromClassIncludingModuleWithPublicInstanceMethod end end aspect.unadvise end Aspect::CANONICAL_OPTIONS["exclude_types_and_ancestors"].each do |key| it "should accept :#{key} as a synonym for :exclude_types_and_ancestors." do aspect = Aspect.new :before, :pointcuts => @pointcut1, key.intern => @excluded_types, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new (with type-based :pointcut(s) and :exclude_type(s)_and_descendents parameter)" do before :all do @excluded_types = [ClassWithPublicInstanceMethod, ModuleWithPublicInstanceMethod] @types = @excluded_types + [ClassDerivedFromClassIncludingModuleWithPublicInstanceMethod] @pointcut1 = Pointcut.new :types => @types, :method => :all, :method_options => [:exclude_ancestor_methods] end it "should accept :pointcut(s) => [P1, ...], :exclude_type(s)_and_descendents => [types], where join points with [types] are excluded" do aspect = Aspect.new :before, :pointcuts => @pointcut1, :exclude_types_and_descendents => @excluded_types, :ignore_no_matching_join_points => true do |jp, obj, *args|; end aspect.pointcuts.size.should == 0 aspect.unadvise end Aspect::CANONICAL_OPTIONS["exclude_types_and_descendents"].each do |key| it "should accept :#{key} as a synonym for :exclude_types_and_descendents." do aspect = Aspect.new :before, :pointcuts => @pointcut1, key.intern => @excluded_types, :ignore_no_matching_join_points => true, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new (with type-based :pointcut(s) and :exclude_type(s)_and_nested_types parameter)" do before :all do @excluded_types = [ClassWithPublicInstanceMethod, ModuleWithPublicInstanceMethod] @types = @excluded_types + [ClassDerivedFromClassIncludingModuleWithPublicInstanceMethod] @pointcut1 = Pointcut.new :types => @types, :method => :all, :method_options => [:exclude_ancestor_methods] end it "should accept :pointcut(s) => [P1, ...], :exclude_type(s)_and_nested_types => [types], where join points with [types] are excluded" do aspect = Aspect.new :before, :pointcuts => @pointcut1, :exclude_types_and_nested_types => @excluded_types, :ignore_no_matching_join_points => true do |jp, obj, *args|; end aspect.pointcuts.size.should == 1 aspect.unadvise end Aspect::CANONICAL_OPTIONS["exclude_types_and_nested_types"].each do |key| it "should accept :#{key} as a synonym for :exclude_types_and_nested_types." do aspect = Aspect.new :before, :pointcuts => @pointcut1, key.intern => @excluded_types, :ignore_no_matching_join_points => true, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new (with object-based :pointcut(s) and :exclude_object(s) parameter)" do before :all do @dontExclude1 = DontExclude1.new(1) @dontExclude2 = DontExclude1.new(2) @exclude1 = DontExclude1.new(3) @exclude2 = DontExclude1.new(4) @included_objects = [@dontExclude1, @dontExclude2] @excluded_objects = [@exclude1, @exclude2] @pointcut1 = Pointcut.new :objects => @included_objects, :method => :doit @pointcut2 = Pointcut.new :objects => @excluded_objects, :method => :doit end it "should accept :pointcut(s) => [P1, ...], :exclude_object(s) => [objects], where join points with [objects] are excluded" do aspect = nil advice_called = false aspect = Aspect.new :before, :pointcuts => [@pointcut1, @pointcut2], :exclude_objects => @excluded_objects do |jp, obj, *args| advice_called = true @excluded_objects.should_not include(obj) end @included_objects.each do |object| advice_called = false object.doit advice_called.should be_truthy end @excluded_objects.each do |object| advice_called = false object.doit advice_called.should_not be_truthy end aspect.unadvise end Aspect::CANONICAL_OPTIONS["exclude_objects"].each do |key| it "should accept :#{key} as a synonym for :exclude_objects." do aspect = Aspect.new :before, :pointcuts => [@pointcut1, @pointcut2], key.intern => @excluded_objects, :noop => true do; end aspect.unadvise end end end describe Aspect, ".new (with :method(s) and :exclude_method(s) parameter)" do before :each do @dontExclude1 = DontExclude1.new(1) @dontExclude2 = DontExclude1.new(2) @exclude1 = DontExclude1.new(3) @exclude2 = DontExclude1.new(4) @exclude1c = Exclude1c.new(5) @included_objects = [@dontExclude1, @dontExclude2, @exclude1, @exclude2] @excluded_objects = [@exclude1c] @included_types = [DontExclude1, DontExclude2, Exclude1, Exclude2] @excluded_types = [Exclude1c] @excluded_methods = [:doit3] @pointcut1 = Pointcut.new :objects => @included_objects, :method => /doit/ @pointcut2 = Pointcut.new :objects => @excluded_objects, :method => /doit/ @pointcut3 = Pointcut.new :types => @included_types, :method => /doit/ @pointcut4 = Pointcut.new :types => @excluded_types, :method => /doit/ end def do_method_exclusion parameter_hash, types_were_specified parameter_hash[:before] = '' parameter_hash[:exclude_methods] = :doit3 aspect = nil advice_called = false aspect = Aspect.new parameter_hash do |jp, obj, *args| advice_called = true @excluded_methods.should_not include(jp.method_name) end if types_were_specified (@included_types + @excluded_types).each do |type| advice_called = false type.new(1).doit advice_called.should be_truthy end @excluded_types.each do |type| advice_called = false type.new(1).doit3 advice_called.should be_falsey end end (@included_objects + @excluded_objects).each do |object| advice_called = false object.doit advice_called.should be_truthy end @excluded_objects.each do |object| advice_called = false object.doit3 advice_called.should_not be_truthy end aspect.unadvise end Aspect::CANONICAL_OPTIONS["exclude_methods"].each do |key| it "should accept :#{key} as a synonym for :exclude_methods." do aspect = Aspect.new :before, :pointcuts => [@pointcut1, @pointcut2, @pointcut3, @pointcut4], key.intern => :doit3, :noop => true aspect.unadvise end end it "should accept :pointcut(s) => [P1, ...], :exclude_method(s) => [methods], where join points with [methods] are excluded" do parameter_hash = { :pointcuts => [@pointcut1, @pointcut2, @pointcut3, @pointcut4] } do_method_exclusion parameter_hash, true end it "should accept :type(s) => ..., :method(s) => ..., :exclude_method(s) => [methods], where join points with [methods] are excluded" do parameter_hash = { :types => (@included_types + @excluded_types), :methods => /doit/ } do_method_exclusion parameter_hash, true end # it "should accept :object(s) => ..., :method(s) => ..., :exclude_method(s) => [methods], where join points with [methods] are excluded" do # pending "bug fix" # Aspect.echo = true # parameter_hash = { :objects => (@included_objects + @excluded_objects), :methods => /doit/ } # do_method_exclusion parameter_hash, false # Aspect.echo = false # end # # def do_method_exclusion2 parameter_hash, types_were_specified # parameter_hash[:before] = '' # parameter_hash[:exclude_method] = :doit3 # parameter_hash[:method] = /doit/ # aspect = nil # advice_called = false # aspect = Aspect.new parameter_hash do |jp, obj, *args| # advice_called = true # @excluded_methods.should_not include(jp.method_name) # end # (@excluded_objects).each do |object| # advice_called = false # object.doit # advice_called.should be_truthy # end # aspect.unadvise # end # # def buggy parameter_hash # parameter_hash[:before] = '' # parameter_hash[:exclude_method] = :doit3 # aspect = Aspect.new parameter_hash do |jp, obj, *args| # end # @excluded_objects.each do |object| # object.doit # end # aspect.unadvise # end # # it "#15202 bug..." do # pending "bug fix" # @pointcut5 = Pointcut.new :types => [Exclude1, Exclude1c], :method => /doit/ # parameter_hash = { :pointcuts => [@pointcut5] } #[@pointcut1, @pointcut2, @pointcut3, @pointcut4] } # buggy parameter_hash # parameter_hash = { :objects => (@excluded_objects), :method => /doit/ } # buggy parameter_hash # end end end