aquarium 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,67 @@
1
+ == Version 0.1.6
2
+
3
+ Bug fixes:
4
+ 14353 Advising subclass method that calls super raises exception when method executed
5
+ 14356 Regexps for types must cover the whole name, which is inconsistent with method/attribute regexps
6
+ 14384 Design by Contract "extra" does not return correct value "invar" handling
7
+ 13410 Fix funky navigation bar on website
8
+
9
+ 14353 was kind of bad, but it's actually a Ruby bug with a good workaround. If you
10
+ advised a method that called "super", Ruby would use the wrong method name to lookup the
11
+ class in the parent. See the bug description for the details.
12
+
13
+ For 14356, type regular expressions now match on parts of names; they don't have to match
14
+ the whole name. The exception is regular expressions with module separators "::". In this
15
+ case, it seems to make more sense for the regular expression to be interpreted as follows:
16
+ If the expression is /A::B::C::D/, then for the the outermost types, the expression behaves
17
+ as /^.*A/, for the types between two "::", the expressions behave as /^B$/ and /^C$/, and
18
+ the trailing expression behaves as /D.*$/.
19
+
20
+ 14384 was an easy mistake to make with "around" advice; you have to remember to return the
21
+ result of the "join_point.proceed" call, unless you specifically want to change the returned
22
+ value! Here are two ways to do it:
23
+
24
+ do_something_before(...)
25
+ result = join_point.proceed
26
+ do_something_after(...)
27
+ return result
28
+
29
+ or
30
+
31
+ begin
32
+ do_something_before(...)
33
+ join_point.proceed
34
+ ensure
35
+ do_something_after(...)
36
+ end
37
+
38
+ The latter approach looks "asymmetrical" and it will behave differently if "proceed" raises!
39
+ However, it eliminates the temporary, if you find that desirable.
40
+
41
+ Enhancements:
42
+ 13407 Pick a better method name for JoinPoint#type, which hides the Module#type
43
+ 14385 Pointcut.new should accept a :join_point => jp argument
44
+ 14386 Aspect.new ..., :pointcut => should accept a join point object
45
+ 14440 Add good warning message when "proceed" used for non-around advice
46
+
47
+ For 13407, new attribute methods have been added
48
+ * JoinPoint#target_type return the type that the join_point matches.
49
+ * JoinPoint#target_type= set the type that the join_point matches.
50
+ * JoinPoint#target_object return the object that the join_point matches.
51
+ * JoinPoint#target_object= set the object that the join_point matches.
52
+
53
+ The following, older methods are now deprecated and will be removed in the 0.2.0 release (#14053):
54
+ * JoinPoint#type
55
+ * JoinPoint#type=
56
+ * JoinPoint#object
57
+ * JoinPoint#object=
58
+
59
+ JoinPoint#type method is deprecated because it hides Module#type, which returns the type of
60
+ the corresponding object. For "symmetry", the other three methods are also now deprecated and
61
+ they will be removed in a future release. Until then, all will print a warning message to
62
+ STDOUT. (If you really want the type of what could be a JoinPoint object, you should use #class
63
+ anyway, as Module#type is also deprecated!)
64
+
1
65
  == Version 0.1.5
2
66
 
3
67
  Bug fixes:
data/README CHANGED
@@ -29,6 +29,7 @@ Only around advice can prevent execution of the join point, except for the speci
29
29
 
30
30
  === Known Limitations
31
31
 
32
+ * You cannot advice "String", "Symbol" or instances there of, because trying to specify either one will be confused with naming a type.
32
33
  * Concurrent type- and object-based advice can't be removed cleanly.
33
34
  * See also the comparison with AspectJ behavior next.
34
35
  * The API and wrapper DSL will probably evolve until the 1.0.0 release. Backwards compatibility will be maintained between releases as much as possible and translation tools will be provided, when necessary.
@@ -59,18 +60,20 @@ Here is an example that traces invocations of all public instance methods of the
59
60
 
60
61
  require 'aquarium'
61
62
  Aspect.new :around, :types => [Foo, Bar], :methods => :all do |execution_point, *args|
62
- p "Entering: #{execution_point.type.name}##{execution_point.method_name}"
63
- execution_point.proceed
64
- p "Leaving: #{execution_point.type.name}##{execution_point.method_name}"
63
+ p "Entering: #{execution_point.target_type.name}##{execution_point.method_name}"
64
+ result = execution_point.proceed
65
+ p "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}"
66
+ result # block needs to return the result of the "proceed"!
65
67
  end
66
68
 
67
69
  The advice to execute at each join point is the block. The pointcut is the set of all public instance methods in Foo and Bar. (There are additional options available for specifying class methods, protected methods, etc.) Here is the same example using the convenience DSL that adds aspect methods to Object (available only if you require aquarium/aspects/dsl/object_dsl', since other toolkits, like Rails, define similar methods on Object!).
68
70
 
69
71
  require 'aquarium/aspects/dsl/object_dsl'
70
72
  around :types => [Foo, Bar], :methods => :all do |execution_point, *args|
71
- p "Entering: #{execution_point.type.name}##{execution_point.method_name}"
72
- execution_point.proceed
73
- p "Leaving: #{execution_point.type.name}##{execution_point.method_name}"
73
+ p "Entering: #{execution_point.target_type.name}##{execution_point.method_name}"
74
+ result = execution_point.proceed
75
+ p "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}"
76
+ result # block needs to return the result of the "proceed"!
74
77
  end
75
78
 
76
79
  See "examples/method_tracing_example.rb" for a more detailed version of this example.
@@ -103,8 +106,9 @@ If you use the DSL inside a class and omit the :type(s) and :object(s) options,
103
106
  class Foo
104
107
  around :critical_operation do |execution_point, *args|
105
108
  p "Entering: Foo#critical_operation"
106
- execution_point.proceed
109
+ result = execution_point.proceed
107
110
  p "Leaving: Foo#critical_operation"
111
+ result
108
112
  end
109
113
  end
110
114
 
@@ -156,6 +160,12 @@ very much like the :method options. Note that :all is NOT supported in this case
156
160
  around :attributes = /^foo/, ...
157
161
  around :attributes = [/^foo/, /bar$/], ...
158
162
 
163
+ You can specify a "Pointcut" that encapsulates one or more pre-defined Pointcuts or JoinPoints.
164
+
165
+ around :pointcut = pc, ... # for pre-defined pointcut "pc"
166
+ around :pointcuts = [pc, ...], ... # for pre-defined pointcut list
167
+ around :pointcut = {:type => T, :method => :m}, ... # same as around :type => T, :method => :m, ..
168
+
159
169
  You can advice methods before execution:
160
170
 
161
171
  before :types => ...
@@ -193,8 +203,9 @@ You can pass a block as the advice. Notice that all advice blocks and Procs (see
193
203
 
194
204
  around :type => [...], :methods => :all do |join_point, *args|
195
205
  advice_to_execute_before_the_jp
196
- join_point.proceed # Invoke the join point, passing *args implicitly (you can override...)
206
+ result = join_point.proceed # Invoke the join point, passing *args implicitly (you can override...)
197
207
  advice_to_execute_after_the_jp
208
+ result # return the result of the "proceed", unless you override the value.
198
209
  end
199
210
  around(:type => [...], :methods => :all) {|join_point, *args| ...} # (...) necessary for precedence...
200
211
 
data/UPGRADE CHANGED
@@ -1,4 +1,10 @@
1
- == Upgrading existing code to Aquarium-0.1.5
1
+ == Upgrading to Aquarium-0.1.6
2
+
3
+ As described in the CHANGES, the JoinPoint#type, JoinPoint#type=, JoinPoint#object, and JoinPoint#object=
4
+ are now deprecated. Client code that uses these methods will still work, but warning messages will be
5
+ written to STDOUT. See CHANGES for more details.
6
+
7
+ == Upgrading to Aquarium-0.1.5
2
8
 
3
9
  This is mostly a bug-fix release, but it did have to introduce one API change, as described in the
4
10
  CHANGES. In particular, the aspect "DSL" methods are no longer automatically to Object, as some of
@@ -38,10 +38,14 @@ bar1.do_something_else :b3, :b4
38
38
 
39
39
  include Aquarium::Aspects
40
40
 
41
- Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :all, :method_options => :suppress_ancestor_methods do |execution_point, *args|
42
- p "Entering: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
43
- execution_point.proceed
44
- p "Leaving: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
41
+ Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :all,
42
+ :method_options => :suppress_ancestor_methods do |execution_point, *args|
43
+ begin
44
+ p "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
45
+ execution_point.proceed
46
+ ensure
47
+ p "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
48
+ end
45
49
  end
46
50
 
47
51
  p "After advising the methods. Notice that #intialize isn't advised:"
@@ -51,10 +55,16 @@ foo2.do_it :b5, :b6
51
55
  bar1 = Aquarium::Bar.new :a7, :a8
52
56
  bar1.do_something_else :b7, :b8
53
57
 
54
- Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :initialize, :method_options => :private do |execution_point, *args|
55
- p "Entering: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
56
- execution_point.proceed
57
- p "Leaving: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
58
+ # The "begin/ensure/end" idiom shown causes the advice to return the correct value; the result
59
+ # of the "proceed", rather than the value returned by "p"!
60
+ Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :initialize,
61
+ :method_options => :private do |execution_point, *args|
62
+ begin
63
+ p "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
64
+ execution_point.proceed
65
+ ensure
66
+ p "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
67
+ end
58
68
  end
59
69
 
60
70
  p "After advising the private methods. Notice that #intialize is advised:"
@@ -54,13 +54,18 @@ end
54
54
 
55
55
  describe "An example with advice on the public instance methods (excluding ancestor methods) of Foo" do
56
56
  it "should trace all calls to the public methods defined by Foo" do
57
+ # The "begin/ensure/end" idiom shown causes the advice to return the correct value; the result
58
+ # of the "proceed", rather than the value returned by "p"!
57
59
  aspect = Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Foo, :methods => :all, :method_options => :suppress_ancestor_methods do |execution_point, *args|
58
- o = execution_point.context.advised_object
59
- o.log "Entering: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
60
- execution_point.proceed
61
- o.log "Leaving: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
60
+ begin
61
+ o = execution_point.context.advised_object
62
+ o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
63
+ execution_point.proceed
64
+ ensure
65
+ o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
66
+ end
62
67
  end
63
-
68
+
64
69
  foo = Aquarium::Foo.new :a5, :a6
65
70
  foo.do_it :b5, :b6
66
71
  foo.logged_messages.size.should == 4
@@ -75,12 +80,15 @@ end
75
80
  describe "An example with advice on the public instance methods (excluding ancestor methods) of Bar" do
76
81
  it "should not trace any calls to the public methods defined by the included BarModule" do
77
82
  aspect = Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Bar, :methods => :all, :method_options => :suppress_ancestor_methods do |execution_point, *args|
78
- o = execution_point.context.advised_object
79
- o.log "Entering: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
80
- execution_point.proceed
81
- o.log "Leaving: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
83
+ begin
84
+ o = execution_point.context.advised_object
85
+ o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
86
+ execution_point.proceed
87
+ ensure
88
+ o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
89
+ end
82
90
  end
83
-
91
+
84
92
  bar = Aquarium::Bar.new :a7, :a8
85
93
  bar.do_something_else :b7, :b8
86
94
  bar.logged_messages.size.should == 2
@@ -91,12 +99,15 @@ end
91
99
  describe "An example with advice on the public instance methods (including ancestor methods) of Bar" do
92
100
  it "should trace all calls to the public methods defined by the included BarModule" do
93
101
  aspect = Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Bar, :methods => /^do_/ do |execution_point, *args|
94
- o = execution_point.context.advised_object
95
- o.log "Entering: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
96
- execution_point.proceed
97
- o.log "Leaving: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
102
+ begin
103
+ o = execution_point.context.advised_object
104
+ o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
105
+ execution_point.proceed
106
+ ensure
107
+ o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
108
+ end
98
109
  end
99
-
110
+
100
111
  bar = Aquarium::Bar.new :a9, :a10
101
112
  bar.do_something_else :b9, :b10
102
113
  bar.logged_messages.size.should == 4
@@ -113,12 +124,15 @@ describe "An example with advice on the private initialize method of Foo and Bar
113
124
  it "should trace all calls to initialize" do
114
125
  before_methods = Aquarium::Foo.private_instance_methods.sort #- Object.private_methods.sort
115
126
  aspect = Aquarium::Aspects::Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :initialize, :method_options => :private do |execution_point, *args|
116
- o = execution_point.context.advised_object
117
- o.log "Entering: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
118
- execution_point.proceed
119
- o.log "Leaving: #{execution_point.type.name}##{execution_point.method_name}: args = #{args.inspect}"
127
+ begin
128
+ o = execution_point.context.advised_object
129
+ o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
130
+ execution_point.proceed
131
+ ensure
132
+ o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
133
+ end
120
134
  end
121
-
135
+
122
136
  foo = Aquarium::Foo.new :a11, :a12
123
137
  foo.do_it :b11, :b12
124
138
  foo.logged_messages.size.should == 4
@@ -74,17 +74,25 @@ module Aquarium
74
74
  NIL_OBJECT = Aquarium::Utils::NilObject.new
75
75
  end
76
76
 
77
+ # When invoking the original method, we use object.send(original_method_name, *args)
78
+ # rather than object.method(...).call(*args). The latter fails when the original method
79
+ # calls super. This is a Ruby bug: http://www.ruby-forum.com/topic/124276
77
80
  class NoAdviceChainNode < AdviceChainNode
78
81
  # Note that we extract the block passed to the original method call, if any,
79
82
  # from the context and pass it to method invocation.
80
83
  def initialize options = {}
81
84
  super(options) { |jp, *args|
82
85
  block_for_method = jp.context.block_for_method
83
- invoking_object = jp.instance_method? ? jp.context.advised_object : jp.type
86
+ invoking_object = jp.instance_method? ? jp.context.advised_object : jp.target_type
84
87
  method = invoking_object.method(@alias_method_name)
85
88
  block_for_method.nil? ?
86
- method.call(*args) :
87
- method.call(*args, &block_for_method)
89
+ invoking_object.send(@alias_method_name, *args) :
90
+ invoking_object.send(@alias_method_name, *args, &block_for_method)
91
+ # Buggy!!
92
+ # method = invoking_object.method(@alias_method_name)
93
+ # block_for_method.nil? ?
94
+ # method.call(*args) :
95
+ # method.call(*args, &block_for_method)
88
96
  }
89
97
  end
90
98
  end
@@ -60,7 +60,7 @@ module Aquarium
60
60
  # <tt>:pointcut => pointcut || [pointcut_list]</tt>::
61
61
  # <tt>:within_pointcut => pointcut || [pointcut_list]</tt>::
62
62
  # <tt>:within_pointcuts => pointcut || [pointcut_list]</tt>::
63
- # One or an array of Pointcut objects. Mutually-exclusive with the :types, :objects,
63
+ # One or an array of Pointcut or JoinPoint objects. Mutually-exclusive with the :types, :objects,
64
64
  # :methods, :attributes, :method_options, and :attribute_options parameters.
65
65
  #
66
66
  # <tt>:types => type || [type_list]</tt>::
@@ -177,6 +177,8 @@ module Aquarium
177
177
  pointcuts_given.each do |pointcut|
178
178
  if pointcut.kind_of?(Aquarium::Aspects::Pointcut)
179
179
  pointcuts << pointcut
180
+ elsif pointcut.kind_of?(Aquarium::Aspects::JoinPoint)
181
+ pointcuts << Aquarium::Aspects::Pointcut.new(:join_point => pointcut)
180
182
  else
181
183
  pointcuts << Aquarium::Aspects::Pointcut.new(pointcut)
182
184
  end
@@ -236,7 +238,7 @@ module Aquarium
236
238
  end
237
239
 
238
240
  def add_advice_framework join_point
239
- type_to_advise = join_point.type || (class << join_point.object; self; end)
241
+ type_to_advise = join_point.target_type || (class << join_point.target_object; self; end)
240
242
  alias_method_name = (saved_method_name join_point).intern
241
243
  return if private_method_defined? join_point.type_or_object, alias_method_name
242
244
  type_to_advise.class_eval(<<-EVAL_WRAPPER, __FILE__, __LINE__)
@@ -270,8 +272,8 @@ module Aquarium
270
272
  # of the advised classes. This also means that we have to use the Class.method(name).call()
271
273
  # idiom when invoking it.
272
274
  def alias_original_method_text alias_method_name, join_point
273
- self_name = join_point.type.nil? ? "self" : join_point.type.name
274
- target_self = join_point.instance_method? ? "self" : join_point.type.name
275
+ self_name = join_point.target_type.nil? ? "self" : join_point.target_type.name
276
+ target_self = join_point.instance_method? ? "self" : join_point.target_type.name
275
277
  <<-EOF
276
278
  alias_method :#{alias_method_name}, :#{join_point.method_name}
277
279
  def #{join_point.method_name} *args, &block_for_method
@@ -286,7 +288,7 @@ module Aquarium
286
288
  end
287
289
 
288
290
  def unalias_original_method_text alias_method_name, join_point
289
- self_name = join_point.type.nil? ? "self" : join_point.type.name
291
+ self_name = join_point.target_type.nil? ? "self" : join_point.target_type.name
290
292
  <<-EOF
291
293
  alias_method :#{join_point.method_name}, :#{alias_method_name}
292
294
  #{join_point.visibility.to_s} :#{join_point.method_name}
@@ -295,7 +297,7 @@ module Aquarium
295
297
  end
296
298
 
297
299
  def remove_advice_chain_class_variable_text alias_method_name, join_point
298
- self_name = join_point.type.nil? ? "self" : join_point.type.name
300
+ self_name = join_point.target_type.nil? ? "self" : join_point.target_type.name
299
301
  <<-EOF
300
302
  advice_chain_name = :@@#{Aspect.advice_chain_attr_name join_point.type_or_object, join_point.method_name}
301
303
  remove_class_variable advice_chain_name
@@ -345,7 +347,7 @@ module Aquarium
345
347
 
346
348
  def restore_type_method join_point
347
349
  alias_method_name = (saved_method_name join_point).intern
348
- join_point.type.class_eval(<<-EVAL_WRAPPER, __FILE__, __LINE__)
350
+ join_point.target_type.class_eval(<<-EVAL_WRAPPER, __FILE__, __LINE__)
349
351
  #{static_method_prefix join_point.instance_method?}
350
352
  #{unalias_original_method_text alias_method_name, join_point}
351
353
  #{static_method_suffix join_point.instance_method?}
@@ -358,14 +360,14 @@ module Aquarium
358
360
 
359
361
  def restore_object_method join_point
360
362
  saved = saved_method_name join_point
361
- singleton = class << join_point.object; self; end
363
+ singleton = class << join_point.target_object; self; end
362
364
  singleton.class_eval do
363
365
  alias_method join_point.method_name, saved
364
366
  public join_point.method_name
365
367
  undef_method saved.intern
366
368
  end
367
369
  advice_chain_name = "@#{Aspect.advice_chain_attr_name join_point.type_or_object, join_point.method_name}".intern
368
- join_point.object.method(:remove_instance_variable).call advice_chain_name
370
+ join_point.target_object.method(:remove_instance_variable).call advice_chain_name
369
371
  end
370
372
 
371
373
  def self.set_advice_chain type_or_object, method_name, advice_chain
@@ -26,7 +26,7 @@ module Aquarium
26
26
  end
27
27
 
28
28
  def proceed enclosing_join_point, *args, &block
29
- raise "JoinPoint#proceed can only be called if @proceed_proc is set." unless @proceed_proc
29
+ raise "It looks like you tried to call \"JoinPoint#proceed\" (or \"JoinPoint::Context#proceed\") from within advice that isn't \"around\" advice. Only around advice can call proceed. (Specific error: JoinPoint#proceed cannot be called because no \"@proceed_proc\" attribute was set on the corresponding JoinPoint::Context object.)" unless @proceed_proc
30
30
  args = parameters if (args.nil? or args.size == 0)
31
31
  enclosing_join_point.context.block_for_method = block if block
32
32
  proceed_proc.call enclosing_join_point, *args
@@ -67,30 +67,65 @@ module Aquarium
67
67
  end
68
68
  end
69
69
 
70
- attr_accessor :type, :object, :method_name, :visibility, :context
71
-
70
+ %w[type object].each do |attr|
71
+ class_eval <<-EOF
72
+ # Deprecated, as JoinPoint#type overrides Module#type in a non-substitutable way!
73
+ # JoinPoint#target_#{attr} will be removed in the next release.
74
+ # Use JoinPoint#target_#{attr} instead
75
+ def #{attr}
76
+ p "WARNING: JoinPoint##{attr} is deprecated. It will be removed in the next release."
77
+ target_#{attr}
78
+ end
79
+ # Deprecated
80
+ def #{attr}= new_#{attr}
81
+ p "WARNING: JoinPoint##{attr}= is deprecated. It will be removed in the next release."
82
+ target_#{attr}= new_#{attr}
83
+ end
84
+ EOF
85
+ end
86
+
87
+ attr_accessor :target_type, :target_object, :method_name, :visibility, :context
88
+ attr_reader :instance_or_class_method
89
+
90
+ # For "symmetry" with JoinPoint#target_type
91
+ alias_method :object, :target_object
92
+
93
+ # For "symmetry" with JoinPoint#target_type=
94
+ alias_method :object=, :target_object=
95
+
72
96
  def instance_method?
73
97
  @instance_method
74
98
  end
75
99
 
100
+ def class_method?
101
+ !@instance_method
102
+ end
103
+
76
104
  def initialize options = {}
77
- @type = options[:type]
78
- @object = options[:object]
79
- @method_name = options[:method_name] || options[:method]
105
+ @target_type = options[:type]
106
+ @target_object = options[:object]
107
+ @method_name = options[:method_name] || options[:method]
80
108
  @instance_method = options[:instance_method]
81
- class_method = options[:class_method].nil? ? false : options[:class_method]
109
+ class_method = options[:class_method].nil? ? false : options[:class_method]
82
110
  @instance_method = (!class_method) if @instance_method.nil?
111
+ @instance_or_class_method = @instance_method ? :instance : :class
83
112
  @visibility = Aquarium::Utils::MethodUtils.visibility(type_or_object, @method_name, class_or_instance_method_flag)
84
113
  assert_valid options
85
114
  end
86
115
 
87
- # deal with warnings for Object#type being obsolete:
88
- def get_type
89
- @type
90
- end
91
-
92
116
  def type_or_object
93
- @type || @object
117
+ @target_type || @target_object
118
+ end
119
+
120
+ alias_method :target_type_or_object, :type_or_object
121
+
122
+ def exists?
123
+ type_or_object_sym = @target_type ? :type : :object
124
+ results = Aquarium::Finders::MethodFinder.new.find type_or_object_sym => type_or_object,
125
+ :method => method_name,
126
+ :options => [visibility, instance_or_class_method]
127
+ raise Exception("MethodFinder returned more than one item! #{results.inspect}") if (results.matched.size + results.not_matched.size) != 1
128
+ return results.matched.size == 1 ? true : false
94
129
  end
95
130
 
96
131
  # TODO while convenient, it couples advice-type information where it doesn't belong!
@@ -114,10 +149,10 @@ module Aquarium
114
149
  return 0 if object_id == other.object_id
115
150
  result = self.class <=> other.class
116
151
  return result unless result == 0
117
- result = (self.get_type.nil? && other.get_type.nil?) ? 0 : self.get_type.to_s <=> other.get_type.to_s
152
+ result = (self.target_type.nil? && other.target_type.nil?) ? 0 : self.target_type.to_s <=> other.target_type.to_s
118
153
  return result unless result == 0
119
- result = (self.object.object_id.nil? && other.object.object_id.nil?) ? 0 : self.object.object_id <=> other.object.object_id
120
- result = self.object.object_id <=> other.object.object_id
154
+ result = (self.target_object.object_id.nil? && other.target_object.object_id.nil?) ? 0 : self.target_object.object_id <=> other.target_object.object_id
155
+ result = self.target_object.object_id <=> other.target_object.object_id
121
156
  return result unless result == 0
122
157
  result = (self.method_name.nil? && other.method_name.nil?) ? 0 : self.method_name.to_s <=> other.method_name.to_s
123
158
  return result unless result == 0
@@ -135,7 +170,7 @@ module Aquarium
135
170
  alias :=== :eql?
136
171
 
137
172
  def inspect
138
- "JoinPoint: {type = #{type.inspect}, object = #{object.inspect}, method_name = #{method_name}, instance_method? #{instance_method?}, context = #{context.inspect}}"
173
+ "JoinPoint: {target_type = #{target_type.inspect}, target_object = #{target_object.inspect}, method_name = #{method_name}, instance_method? #{instance_method?}, context = #{context.inspect}}"
139
174
  end
140
175
 
141
176
  alias :to_s :inspect
@@ -147,13 +182,13 @@ module Aquarium
147
182
  def assert_valid options
148
183
  error_message = ""
149
184
  error_message << "Must specify a :method_name. " unless method_name
150
- error_message << "Must specify either a :type or :object. " unless (type or object)
151
- error_message << "Can't specify both a :type or :object. " if (type and object)
185
+ error_message << "Must specify either a :type or :object. " unless (target_type or target_object)
186
+ error_message << "Can't specify both a :type or :object. " if (target_type and target_object)
152
187
  bad_attributes(error_message, options) if error_message.length > 0
153
188
  end
154
189
 
155
190
  def class_or_instance_method_flag
156
- @instance_method ? :instance_method_only : :class_method_only
191
+ "#{instance_or_class_method.to_s}_method_only".intern
157
192
  end
158
193
 
159
194
  public