aquarium 0.1.0 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 {}