aquarium 0.1.0 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/CHANGES +53 -0
  2. data/README +20 -4
  3. data/Rakefile +19 -4
  4. data/UPGRADE +41 -1
  5. data/examples/aspect_design_example.rb +4 -1
  6. data/examples/aspect_design_example_spec.rb +41 -0
  7. data/examples/design_by_contract_example.rb +2 -3
  8. data/examples/design_by_contract_example_spec.rb +92 -0
  9. data/examples/method_missing_example.rb +1 -1
  10. data/examples/method_missing_example_spec.rb +59 -0
  11. data/examples/method_tracing_example.rb +4 -2
  12. data/examples/method_tracing_example_spec.rb +141 -0
  13. data/lib/aquarium/aspects/advice.rb +2 -2
  14. data/lib/aquarium/aspects/aspect.rb +20 -35
  15. data/lib/aquarium/aspects/dsl.rb +2 -1
  16. data/lib/aquarium/aspects/dsl/aspect_dsl.rb +12 -8
  17. data/lib/aquarium/aspects/dsl/object_dsl.rb +8 -0
  18. data/lib/aquarium/aspects/join_point.rb +16 -10
  19. data/lib/aquarium/aspects/pointcut.rb +3 -3
  20. data/lib/aquarium/extras/design_by_contract.rb +20 -11
  21. data/lib/aquarium/utils.rb +1 -0
  22. data/lib/aquarium/utils/method_utils.rb +41 -0
  23. data/lib/aquarium/utils/name_utils.rb +35 -0
  24. data/lib/aquarium/utils/type_utils.rb +9 -0
  25. data/lib/aquarium/version.rb +3 -5
  26. data/rake_tasks/examples.rake +1 -1
  27. data/spec/aquarium/aspects/aspect_invocation_spec.rb +30 -28
  28. data/spec/aquarium/aspects/aspect_spec.rb +90 -0
  29. data/spec/aquarium/aspects/concurrent_aspects_spec.rb +5 -3
  30. data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +3 -1
  31. data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +174 -110
  32. data/spec/aquarium/aspects/join_point_spec.rb +120 -19
  33. data/spec/aquarium/aspects/pointcut_spec.rb +24 -14
  34. data/spec/aquarium/extras/design_by_contract_spec.rb +3 -0
  35. data/spec/aquarium/finders/finder_result_spec.rb +1 -1
  36. data/spec/aquarium/finders/method_finder_spec.rb +3 -4
  37. data/spec/aquarium/spec_example_classes.rb +4 -0
  38. data/spec/aquarium/utils/method_utils_spec.rb +124 -1
  39. data/spec/aquarium/utils/name_utils_spec.rb +56 -0
  40. data/spec/aquarium/utils/type_utils_spec.rb +17 -0
  41. metadata +12 -4
  42. data/spec/aquarium/finders/method_sorting_spec.rb +0 -16
@@ -59,7 +59,7 @@ module Aquarium
59
59
  # <tt>:attribute => attribute || [attribute_list]</tt>::
60
60
  # One or an array of attribute names and/or regular expessions to match.
61
61
  # This is syntactic sugar for the corresponding attribute readers and/or writers
62
- # methods, as specified using the <tt>:attrbute_options. Any matches will be
62
+ # methods, as specified using the <tt>:attrbute_options</tt>. Any matches will be
63
63
  # joined with the matched :methods.</tt>.
64
64
  #
65
65
  # <tt>:attribute_options => [options]</tt>::
@@ -202,13 +202,13 @@ module Aquarium
202
202
  end
203
203
 
204
204
  def add_join_points which_join_points_list, results_hash, type_or_object_sym
205
- is_instance_method = @specification[:method_options].include?(:class) ? false : true
205
+ instance_method = @specification[:method_options].include?(:class) ? false : true
206
206
  results_hash.each_pair do |type_or_object, method_name_list|
207
207
  method_name_list.each do |method_name|
208
208
  which_join_points_list << Aquarium::Aspects::JoinPoint.new(
209
209
  type_or_object_sym => type_or_object,
210
210
  :method_name => method_name,
211
- :is_instance_method => is_instance_method)
211
+ :instance_method => instance_method)
212
212
  end
213
213
  end
214
214
  end
@@ -1,13 +1,15 @@
1
1
  # A simple Design by Contract module. Adds advice to test that the contract, which is specified with
2
2
  # a block passes. Note that it doesn't attempt to handle the correct behavior under contract
3
3
  # inheritance (TODO).
4
-
4
+ # Warning: This module automatically includes Aquarium::Aspects::DSL::AspectDSL into the class with
5
+ # the contract and it adds the :precondition, :postcondition, and the :invariant methods to Object!
5
6
  require 'aquarium'
6
7
 
7
8
  module Aquarium
8
9
  module Extras
9
10
  module DesignByContract
10
-
11
+ include Aquarium::Aspects
12
+
11
13
  class ContractError < Exception
12
14
  def initialize(message)
13
15
  super
@@ -15,21 +17,21 @@ module Aquarium
15
17
  end
16
18
 
17
19
  def precondition *args, &contract_block
18
- message = handle_message_arg *args
20
+ message = handle_message_arg args
19
21
  add_advice :before, "precondition", message, *args, &contract_block
20
22
  end
21
23
 
22
24
  def postcondition *args, &contract_block
23
- message = handle_message_arg *args
25
+ message = handle_message_arg args
24
26
  add_advice :after_returning, "postcondition", message, *args, &contract_block
25
27
  end
26
28
 
27
29
  def invariant *args, &contract_block
28
- message = handle_message_arg *args
29
- around *args do |jp, *args2|
30
- DesignByContract.test_condition "invariant failure (before invocation): #{message}", jp, *args2, &contract_block
30
+ message = handle_message_arg args
31
+ Aspect.new make_args(:around, *args) do |jp, *params|
32
+ DesignByContract.test_condition "invariant failure (before invocation): #{message}", jp, *params, &contract_block
31
33
  jp.proceed
32
- DesignByContract.test_condition "invariant failure (after invocation): #{message}", jp, *args2, &contract_block
34
+ DesignByContract.test_condition "invariant failure (after invocation): #{message}", jp, *params, &contract_block
33
35
  end
34
36
  end
35
37
 
@@ -42,12 +44,19 @@ module Aquarium
42
44
  end
43
45
 
44
46
  def add_advice kind, test_kind, message, *args, &contract_block
45
- self.send(kind, *args) do |jp, *args2|
46
- DesignByContract.test_condition "#{test_kind} failure: #{message}", jp, *args2, &contract_block
47
+ Aspect.new make_args(kind, *args) do |jp, *params|
48
+ DesignByContract.test_condition "#{test_kind} failure: #{message}", jp, *params, &contract_block
47
49
  end
48
50
  end
49
51
 
50
- def handle_message_arg *args
52
+ def make_args advice_kind, *args
53
+ args2 = args.dup.unshift advice_kind
54
+ args2 << {} unless args2.last.kind_of?(Hash)
55
+ args2.last[:type] = self.name
56
+ args2
57
+ end
58
+
59
+ def handle_message_arg args
51
60
  options = args[-1]
52
61
  return unless options.kind_of?(Hash)
53
62
  message = options[:message]
@@ -3,6 +3,7 @@ require 'aquarium/utils/hash_utils'
3
3
  require 'aquarium/utils/set_utils'
4
4
 
5
5
  require 'aquarium/utils/method_utils'
6
+ require 'aquarium/utils/name_utils'
6
7
 
7
8
  require 'aquarium/utils/html_escaper'
8
9
  require 'aquarium/utils/invalid_options'
@@ -1,6 +1,7 @@
1
1
  module Aquarium
2
2
  module Utils
3
3
  module MethodUtils
4
+
4
5
  def self.method_args_to_hash *args
5
6
  return {} if args.empty? || (args.size == 1 && args[0].nil?)
6
7
  hash = (args[-1] and args[-1].kind_of? Hash) ? args.pop : {}
@@ -13,6 +14,46 @@ module Aquarium
13
14
  end
14
15
  hash
15
16
  end
17
+
18
+ def self.visibility type_or_instance, method_sym, class_or_instance_only = nil
19
+ meta_method_suffixes = determine_meta_method_suffixes type_or_instance, class_or_instance_only
20
+ meta_method_suffixes.each do |suffix|
21
+ %w[public protected private].each do |protection|
22
+ meta_method = "#{protection}_#{suffix}"
23
+ if find_method(type_or_instance, method_sym, meta_method)
24
+ return protection.intern
25
+ end
26
+ end
27
+ end
28
+ nil
29
+ end
30
+
31
+ private
32
+ def self.determine_meta_method_suffixes2 type_or_instance, class_or_instance_only
33
+ ["method_defined"]
34
+ end
35
+
36
+ def self.determine_meta_method_suffixes type_or_instance, class_or_instance_only
37
+ limits = class_or_instance_only.nil? ? [:instance_method_only, :class_method_only] : [class_or_instance_only]
38
+ meta_method_suffixes = []
39
+ limits.each do |limit|
40
+ if (type_or_instance.kind_of?(Class) || type_or_instance.kind_of?(Module))
41
+ meta_method_suffixes << "instance_methods" if limit == :instance_method_only
42
+ meta_method_suffixes << "methods" if limit == :class_method_only
43
+ else
44
+ meta_method_suffixes << "methods" if limit == :instance_method_only
45
+ end
46
+ end
47
+ meta_method_suffixes
48
+ end
49
+
50
+ def self.find_method2 type_or_instance, method_sym, meta_method
51
+ type_or_instance.send(meta_method, method_sym.to_s)
52
+ end
53
+
54
+ def self.find_method type_or_instance, method_sym, meta_method
55
+ type_or_instance.send(meta_method).include?(method_sym.to_s)
56
+ end
16
57
  end
17
58
  end
18
59
  end
@@ -0,0 +1,35 @@
1
+ require 'aquarium/utils/type_utils'
2
+
3
+ # Convert various strings, symbols, object ids, etc. into valid "names" that
4
+ # can be used as method names, etc.
5
+ module Aquarium
6
+ module Utils
7
+ module NameUtils
8
+
9
+ def self.make_type_or_object_key type_or_object
10
+ if Aquarium::Utils::TypeUtils.is_type?(type_or_object)
11
+ make_valid_type_name type_or_object
12
+ else
13
+ make_valid_object_name type_or_object
14
+ end
15
+ end
16
+
17
+ def self.make_valid_type_name type
18
+ type.name.gsub(/:/, '_')
19
+ end
20
+
21
+ def self.make_valid_object_name type_or_object
22
+ "#{make_valid_type_name(type_or_object.class)}_#{make_valid_object_id_name(type_or_object.object_id)}"
23
+ end
24
+
25
+ # Fixes Tracker #13864.
26
+ def self.make_valid_object_id_name object_id
27
+ object_id.to_s.gsub(/^-/, "_neg_")
28
+ end
29
+
30
+ def self.make_valid_attr_name_from_method_name method_name
31
+ method_name.to_s.gsub("=","_equalsign_").gsub("?", "_questionmark_").gsub("!", "_exclamationmark_").gsub("~", "_tilde_").intern
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ module Aquarium
2
+ module Utils
3
+ module TypeUtils
4
+ def self.is_type? type_or_object
5
+ type_or_object.kind_of?(Class) or type_or_object.kind_of?(Module)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -2,17 +2,15 @@ module Aquarium
2
2
  module VERSION
3
3
  def self.build_tag
4
4
  tag = "REL_" + [MAJOR, MINOR, TINY].join('_')
5
- if defined?(RELEASE_CANDIDATE)
6
- tag << "_" << RELEASE_CANDIDATE
7
- end
5
+ tag << "_" << RELEASE_CANDIDATE unless RELEASE_CANDIDATE.nil? or RELEASE_CANDIDATE.empty?
8
6
  tag
9
7
  end
10
8
 
11
9
  unless defined? MAJOR
12
10
  MAJOR = 0
13
11
  MINOR = 1
14
- TINY = 0
15
- RELEASE_CANDIDATE = ""
12
+ TINY = 5
13
+ RELEASE_CANDIDATE = nil
16
14
 
17
15
  # RANDOM_TOKEN: 0.598704893979657
18
16
  REV = "$LastChangedRevision: 7 $".match(/LastChangedRevision: (\d+)/)[1]
@@ -3,5 +3,5 @@ require 'spec/rake/spectask'
3
3
 
4
4
  desc "Run all examples"
5
5
  Spec::Rake::SpecTask.new('examples') do |t|
6
- t.spec_files = FileList['examples/**/*.rb']
6
+ t.spec_files = FileList['examples/**/*_spec.rb']
7
7
  end
@@ -3,44 +3,46 @@ require File.dirname(__FILE__) + '/../spec_example_classes'
3
3
  require 'aquarium/aspects/aspect'
4
4
  require 'aquarium/aspects/dsl'
5
5
 
6
- describe Object, "#advise with invalid invocation parameter list" do
7
- it "should contain at least one of :around, :before, :after, :after_returning, and :after_raising." do
8
- lambda { advise :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
6
+ include Aquarium::Aspects
7
+
8
+ describe Aspect, "#new with invalid invocation parameter list" do
9
+ it "should have as the first parameter at least one of :around, :before, :after, :after_returning, and :after_raising." do
10
+ lambda { Aspect.new :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
9
11
  end
10
12
 
11
13
  it "should contain no other advice types if :around advice specified." do
12
- lambda { advise :around, :before, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
13
- lambda { advise :around, :after, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
14
- lambda { advise :around, :after_returning, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
15
- lambda { advise :around, :after_raising, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
14
+ lambda { Aspect.new :around, :before, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
15
+ lambda { Aspect.new :around, :after, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
16
+ lambda { Aspect.new :around, :after_returning, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
17
+ lambda { Aspect.new :around, :after_raising, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
16
18
  end
17
19
 
18
20
  it "should allow only one of :after, :after_returning, or :after_raising advice to be specified." do
19
- lambda { advise :after, :after_returning, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
20
- lambda { advise :after, :after_raising, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
21
- lambda { advise :after_returning, :after_raising, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
21
+ lambda { Aspect.new :after, :after_returning, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
22
+ lambda { Aspect.new :after, :after_raising, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
23
+ lambda { Aspect.new :after_returning, :after_raising, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
22
24
  end
23
25
  end
24
26
 
25
- describe Object, "#advise arguments can specify more than one advice types" do
27
+ describe Aspect, "#new, when the arguments contain more than one advice type," do
26
28
  it "should allow :before to be specified with :after." do
27
- lambda { advise :before, :after, :pointcut => {:type => Watchful}, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
29
+ lambda { Aspect.new :before, :after, :pointcut => {:type => Watchful}, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
28
30
  end
29
31
 
30
32
  it "should allow :before to be specified with :after_returning." do
31
- lambda { advise :before, :after_returning, :pointcut => {:type => Watchful}, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
33
+ lambda { Aspect.new :before, :after_returning, :pointcut => {:type => Watchful}, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
32
34
  end
33
35
 
34
36
  it "should allow :before to be specified with :after_raising." do
35
- lambda { advise :before, :after_raising, :pointcut => {:type => Watchful}, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
37
+ lambda { Aspect.new :before, :after_raising, :pointcut => {:type => Watchful}, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
36
38
  end
37
39
  end
38
40
 
39
- describe Object, "#advise arguments for specifying the types and methods" do
41
+ describe Aspect, "#new arguments for specifying the types and methods" do
40
42
  it "should advise equivalent join points when :type => T and :method => m is used or :pointcut =>{:type => T, :method => m} is used." do
41
43
  advice = proc {|jp,*args| "advice"}
42
- aspect1 = advise :after, :type => Watchful, :method => :public_watchful_method, &advice
43
- aspect2 = advise :after, :pointcut => {:type => Watchful, :method => :public_watchful_method}, &advice
44
+ aspect1 = Aspect.new :after, :type => Watchful, :method => :public_watchful_method, &advice
45
+ aspect2 = Aspect.new :after, :pointcut => {:type => Watchful, :method => :public_watchful_method}, &advice
44
46
  # We don't use aspect1.should eql(aspect2) because the "specifications" are different.
45
47
  aspect1.pointcuts.should eql(aspect2.pointcuts)
46
48
  aspect1.pointcuts.should eql(aspect2.pointcuts)
@@ -51,9 +53,9 @@ describe Object, "#advise arguments for specifying the types and methods" do
51
53
 
52
54
  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
53
55
  advice = proc {|jp,*args| "advice"}
54
- aspect1 = advise :after, :type => Watchful, :method => :public_watchful_method, &advice
56
+ aspect1 = Aspect.new :after, :type => Watchful, :method => :public_watchful_method, &advice
55
57
  pointcut = Aquarium::Aspects::Pointcut.new :type => Watchful, :method => :public_watchful_method
56
- aspect2 = advise :after, :pointcut => pointcut, &advice
58
+ aspect2 = Aspect.new :after, :pointcut => pointcut, &advice
57
59
  aspect1.pointcuts.should eql(aspect2.pointcuts)
58
60
  aspect1.join_points_matched.should eql(aspect2.join_points_matched)
59
61
  aspect1.advice.should eql(aspect2.advice)
@@ -62,9 +64,9 @@ describe Object, "#advise arguments for specifying the types and methods" do
62
64
 
63
65
  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
64
66
  advice = proc {|jp,*args| "advice"}
65
- aspect1 = advise :after, :pointcut => {:type => Watchful, :method => :public_watchful_method}, &advice
67
+ aspect1 = Aspect.new :after, :pointcut => {:type => Watchful, :method => :public_watchful_method}, &advice
66
68
  pointcut = Aquarium::Aspects::Pointcut.new :type => Watchful, :method => :public_watchful_method
67
- aspect2 = advise :after, :pointcut => pointcut, &advice
69
+ aspect2 = Aspect.new :after, :pointcut => pointcut, &advice
68
70
  aspect1.pointcuts.should eql(aspect2.pointcuts)
69
71
  aspect1.join_points_matched.should eql(aspect2.join_points_matched)
70
72
  aspect1.advice.should eql(aspect2.advice)
@@ -72,12 +74,12 @@ describe Object, "#advise arguments for specifying the types and methods" do
72
74
  end
73
75
  end
74
76
 
75
- describe Object, "#advise arguments for specifying the objects and methods" do
77
+ describe Aspect, "#new arguments for specifying the objects and methods" do
76
78
  it "should advise equivalent join points when :object => o and :method => m is used or :pointcut =>{:object => o, :method => m} is used." do
77
79
  advice = proc {|jp,*args| "advice"}
78
80
  watchful = Watchful.new
79
- aspect1 = advise :after, :object => watchful, :method => :public_watchful_method, &advice
80
- aspect2 = advise :after, :pointcut => {:object => watchful, :method => :public_watchful_method}, &advice
81
+ aspect1 = Aspect.new :after, :object => watchful, :method => :public_watchful_method, &advice
82
+ aspect2 = Aspect.new :after, :pointcut => {:object => watchful, :method => :public_watchful_method}, &advice
81
83
  aspect1.pointcuts.should eql(aspect2.pointcuts)
82
84
  aspect1.join_points_matched.should eql(aspect2.join_points_matched)
83
85
  aspect1.advice.should eql(aspect2.advice)
@@ -87,9 +89,9 @@ describe Object, "#advise arguments for specifying the objects and methods" do
87
89
  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
88
90
  advice = proc {|jp,*args| "advice"}
89
91
  watchful = Watchful.new
90
- aspect1 = advise :after, :object => watchful, :method => :public_watchful_method, &advice
92
+ aspect1 = Aspect.new :after, :object => watchful, :method => :public_watchful_method, &advice
91
93
  pointcut = Aquarium::Aspects::Pointcut.new :object => watchful, :method => :public_watchful_method
92
- aspect2 = advise :after, :pointcut => pointcut, &advice
94
+ aspect2 = Aspect.new :after, :pointcut => pointcut, &advice
93
95
  aspect1.pointcuts.should eql(aspect2.pointcuts)
94
96
  aspect1.join_points_matched.should eql(aspect2.join_points_matched)
95
97
  aspect1.advice.should eql(aspect2.advice)
@@ -99,9 +101,9 @@ describe Object, "#advise arguments for specifying the objects and methods" do
99
101
  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
100
102
  advice = proc {|jp,*args| "advice"}
101
103
  watchful = Watchful.new
102
- aspect1 = advise :after, :pointcut => {:object => watchful, :method => :public_watchful_method}, &advice
104
+ aspect1 = Aspect.new :after, :pointcut => {:object => watchful, :method => :public_watchful_method}, &advice
103
105
  pointcut = Aquarium::Aspects::Pointcut.new :object => watchful, :method => :public_watchful_method
104
- aspect2 = advise :after, :pointcut => pointcut, &advice
106
+ aspect2 = Aspect.new :after, :pointcut => pointcut, &advice
105
107
  aspect1.pointcuts.should eql(aspect2.pointcuts)
106
108
  aspect1.join_points_matched.should eql(aspect2.join_points_matched)
107
109
  aspect1.advice.should eql(aspect2.advice)
@@ -512,6 +512,33 @@ describe Aspect, "#new with :after advice" do
512
512
  end
513
513
  do_watchful_public_protected_private
514
514
  end
515
+ it "should ignore the value returned by the advice" do
516
+ class ReturningValue
517
+ def doit args
518
+ args + ["d"]
519
+ end
520
+ end
521
+ ary = %w[a b c]
522
+ ReturningValue.new.doit(ary).should == %w[a b c d]
523
+ @aspect = Aspect.new :after, :type => ReturningValue, :method => :doit do |jp, *args|
524
+ %w[aa] + jp.context.returned_value + %w[e]
525
+ end
526
+ ReturningValue.new.doit(ary).should == %w[a b c d]
527
+ end
528
+
529
+ it "should all the advice to assign a new return value" do
530
+ class ReturningValue
531
+ def doit args
532
+ args + ["d"]
533
+ end
534
+ end
535
+ ary = %w[a b c]
536
+ ReturningValue.new.doit(ary).should == %w[a b c d]
537
+ @aspect = Aspect.new :after, :type => ReturningValue, :method => :doit do |jp, *args|
538
+ jp.context.returned_value = %w[aa] + jp.context.returned_value + %w[e]
539
+ end
540
+ ReturningValue.new.doit(ary).should == %w[aa a b c d e]
541
+ end
515
542
  end
516
543
 
517
544
  describe Aspect, "#new with :after_returning advice" do
@@ -541,6 +568,34 @@ describe Aspect, "#new with :after_returning advice" do
541
568
  end
542
569
  do_watchful_public_protected_private
543
570
  end
571
+
572
+ it "should ignore the value returned by the advice" do
573
+ class ReturningValue
574
+ def doit args
575
+ args + ["d"]
576
+ end
577
+ end
578
+ ary = %w[a b c]
579
+ ReturningValue.new.doit(ary).should == %w[a b c d]
580
+ @aspect = Aspect.new :after_returning, :type => ReturningValue, :method => :doit do |jp, *args|
581
+ %w[aa] + jp.context.returned_value + %w[e]
582
+ end
583
+ ReturningValue.new.doit(ary).should == %w[a b c d]
584
+ end
585
+
586
+ it "should all the advice to assign a new return value" do
587
+ class ReturningValue
588
+ def doit args
589
+ args + ["d"]
590
+ end
591
+ end
592
+ ary = %w[a b c]
593
+ ReturningValue.new.doit(ary).should == %w[a b c d]
594
+ @aspect = Aspect.new :after_returning, :type => ReturningValue, :method => :doit do |jp, *args|
595
+ jp.context.returned_value = %w[aa] + jp.context.returned_value + %w[e]
596
+ end
597
+ ReturningValue.new.doit(ary).should == %w[aa a b c d e]
598
+ end
544
599
  end
545
600
 
546
601
  describe Aspect, "#new with :after_raising advice" do
@@ -818,6 +873,20 @@ describe Aspect, "#new with :around advice" do
818
873
  orig_block_called.should be_false
819
874
  watchful.public_watchful_method_args.should == [:a4, :a5, :a6]
820
875
  end
876
+
877
+ it "should return the value returned by the advice" do
878
+ class ReturningValue
879
+ def doit args
880
+ args + ["d"]
881
+ end
882
+ end
883
+ ary = %w[a b c]
884
+ ReturningValue.new.doit(ary).should == %w[a b c d]
885
+ @aspect = Aspect.new :around, :type => ReturningValue, :method => :doit do |jp, *args|
886
+ %w[aa] + jp.proceed + %w[e]
887
+ end
888
+ ReturningValue.new.doit(ary).should == %w[aa a b c d e]
889
+ end
821
890
 
822
891
  def do_around_spec *args_passed_to_proceed
823
892
  @aspect = Aspect.new :around, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
@@ -886,6 +955,27 @@ describe Aspect, "#unadvise" do
886
955
  end
887
956
  end
888
957
 
958
+ describe "invariant protection level of methods under advising and unadvising", :shared => true do
959
+ it "should keep the protection level of an advised methods unchanged." do
960
+ %w[public protected private].each do |protection|
961
+ meta = "#{protection}_instance_methods"
962
+ method = "#{protection}_watchful_method"
963
+ Watchful.send(meta).should include(method)
964
+ aspect = Aspect.new(:after, :type => Watchful, :method => method.intern) {|jp, *args| true }
965
+ Watchful.send(meta).should include(method)
966
+ aspect.unadvise
967
+ Watchful.send(meta).should include(method)
968
+ end
969
+ end
970
+ end
971
+
972
+ describe Aspect, "Advising methods should keep the protection level of an advised methods unchanged." do
973
+ it_should_behave_like("invariant protection level of methods under advising and unadvising")
974
+ end
975
+ describe Aspect, "Unadvising methods should restore the original protection level of the methods." do
976
+ it_should_behave_like("invariant protection level of methods under advising and unadvising")
977
+ end
978
+
889
979
  describe Aspect, "#eql?" do
890
980
  before(:all) do
891
981
  @advice = Proc.new {}