aquarium 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ require 'aquarium/utils/array_utils'
2
+
1
3
  module Aquarium
2
4
  module Aspects
3
5
  # Some classes and modules support a :default_object flag and use it if no type or
@@ -5,21 +7,31 @@ module Aquarium
5
7
  # this module have a hash @specification defined with keys :default_object, :types,
6
8
  # and :objects.
7
9
  module DefaultObjectHandler
8
- def default_object_given
9
- @specification[:default_object]
10
+ include Aquarium::Utils::ArrayUtils
11
+
12
+ def default_objects_given
13
+ if @default_objects.nil?
14
+ ary1 = make_array(@specification[:default_objects])
15
+ ary2 = make_array(@specification[:default_object])
16
+ @default_objects = ary1 + ary2
17
+ end
18
+ @default_objects
10
19
  end
11
20
 
12
- def default_object_given?
13
- not (default_object_given.nil? or default_object_given.empty?)
21
+ def default_objects_given?
22
+ not default_objects_given.empty?
14
23
  end
15
24
 
16
25
  def use_default_object_if_defined
17
- return unless default_object_given?
18
- object = default_object_given.to_a.first # there will be only one...
19
- if (object.kind_of?(Class) || object.kind_of?(Module))
20
- @specification[:types] = default_object_given
21
- else
22
- @specification[:objects] = default_object_given
26
+ return unless default_objects_given?
27
+ default_objects_given.each do |object|
28
+ if (object.kind_of?(Class) || object.kind_of?(Module))
29
+ @specification[:types] ||= []
30
+ @specification[:types] << default_objects_given
31
+ else
32
+ @specification[:objects] ||= []
33
+ @specification[:objects] << default_objects_given
34
+ end
23
35
  end
24
36
  end
25
37
  end
@@ -0,0 +1,61 @@
1
+
2
+ module Aquarium
3
+ module Aspects
4
+
5
+ # Defines methods shared by several classes that take :exclude_* arguments.
6
+ module ExclusionHandler
7
+
8
+ def join_point_excluded? jp
9
+ is_excluded_pointcut?(jp) or is_excluded_join_point?(jp) or is_excluded_type_or_object?(jp.type_or_object) or is_excluded_method?(jp.method_name)
10
+ end
11
+
12
+ def is_excluded_pointcut? jp
13
+ return false if @specification[:exclude_pointcuts].nil?
14
+ @specification[:exclude_pointcuts].find do |pc|
15
+ pc.join_points_matched.find do |jp2|
16
+ jp2 == jp || jp2.eql?(jp)
17
+ end
18
+ end
19
+ end
20
+
21
+ # Using @specification[:exclude_join_points].include?(jp) doesn't always work correctly (it probably uses equal())!
22
+ def is_excluded_join_point? jp
23
+ return false if @specification[:exclude_join_points].nil?
24
+ @specification[:exclude_join_points].find {|jp2| jp2 == jp || jp2.eql?(jp)}
25
+ end
26
+
27
+ def is_excluded_type_or_object? type_or_object
28
+ unless @specification[:exclude_objects].nil?
29
+ return true if @specification[:exclude_objects].include?(type_or_object)
30
+ end
31
+ unless @specification[:exclude_types].nil?
32
+ return true if @specification[:exclude_types].find do |t|
33
+ case t
34
+ when String: type_or_object.name.eql?(t)
35
+ when Symbol: type_or_object.name.eql?(t.to_s)
36
+ when Regexp: type_or_object.name =~ t
37
+ else type_or_object == t
38
+ end
39
+ end
40
+ end
41
+ false
42
+ end
43
+
44
+ def is_excluded_method? method
45
+ is_explicitly_excluded_method?(method) or matches_excluded_method_regex?(method)
46
+ end
47
+
48
+ def is_explicitly_excluded_method? method
49
+ return false if @specification[:exclude_methods].nil?
50
+ @specification[:exclude_methods].include? method
51
+ end
52
+
53
+ def matches_excluded_method_regex? method
54
+ return false if @specification[:exclude_methods].nil?
55
+ regexs = @specification[:exclude_methods].find_all {|s| s.kind_of? Regexp}
56
+ return false if regexs.empty?
57
+ regexs.find {|re| method.to_s =~ re}
58
+ end
59
+ end
60
+ end
61
+ end
@@ -38,17 +38,18 @@ module Aquarium
38
38
  # We require the same object id, not just equal objects.
39
39
  def <=> other
40
40
  return 0 if object_id == other.object_id
41
+ return 1 if other.nil?
41
42
  result = self.class <=> other.class
42
43
  return result unless result == 0
43
- result = (self.advice_kind.nil? && other.advice_kind.nil?) ? 0 : self.advice_kind <=> other.advice_kind
44
+ result = (self.advice_kind.nil? and other.advice_kind.nil?) ? 0 : self.advice_kind <=> other.advice_kind
44
45
  return result unless result == 0
45
- result = (self.advised_object.object_id.nil? && other.advised_object.object_id.nil?) ? 0 : self.advised_object.object_id <=> other.advised_object.object_id
46
+ result = (self.advised_object.object_id.nil? and other.advised_object.object_id.nil?) ? 0 : self.advised_object.object_id <=> other.advised_object.object_id
46
47
  return result unless result == 0
47
- result = (self.parameters.nil? && other.parameters.nil?) ? 0 : self.parameters <=> other.parameters
48
+ result = (self.parameters.nil? and other.parameters.nil?) ? 0 : self.parameters <=> other.parameters
48
49
  return result unless result == 0
49
- result = (self.returned_value.nil? && other.returned_value.nil?) ? 0 : self.returned_value <=> other.returned_value
50
+ result = (self.returned_value.nil? and other.returned_value.nil?) ? 0 : self.returned_value <=> other.returned_value
50
51
  return result unless result == 0
51
- (self.raised_exception.nil? && other.raised_exception.nil?) ? 0 : self.raised_exception <=> other.raised_exception
52
+ (self.raised_exception.nil? and other.raised_exception.nil?) ? 0 : self.raised_exception <=> other.raised_exception
52
53
  end
53
54
 
54
55
  def eql? other
@@ -68,7 +69,7 @@ module Aquarium
68
69
  end
69
70
 
70
71
  %w[type object].each do |attr|
71
- class_eval <<-EOF
72
+ class_eval(<<-EOF, __FILE__, __LINE__)
72
73
  # Deprecated, as JoinPoint#type overrides Module#type in a non-substitutable way!
73
74
  # JoinPoint#target_#{attr} will be removed in the next release.
74
75
  # Use JoinPoint#target_#{attr} instead
@@ -105,16 +106,15 @@ module Aquarium
105
106
  @target_type = options[:type]
106
107
  @target_object = options[:object]
107
108
  @method_name = options[:method_name] || options[:method]
108
- @instance_method = options[:instance_method]
109
109
  class_method = options[:class_method].nil? ? false : options[:class_method]
110
- @instance_method = (!class_method) if @instance_method.nil?
110
+ @instance_method = options[:instance_method].nil? ? (!class_method) : options[:instance_method]
111
111
  @instance_or_class_method = @instance_method ? :instance : :class
112
112
  @visibility = Aquarium::Utils::MethodUtils.visibility(type_or_object, @method_name, class_or_instance_method_flag)
113
113
  assert_valid options
114
114
  end
115
115
 
116
116
  def type_or_object
117
- @target_type || @target_object
117
+ target_type || target_object
118
118
  end
119
119
 
120
120
  alias_method :target_type_or_object, :type_or_object
@@ -128,7 +128,6 @@ module Aquarium
128
128
  return results.matched.size == 1 ? true : false
129
129
  end
130
130
 
131
- # TODO while convenient, it couples advice-type information where it doesn't belong!
132
131
  def proceed *args, &block
133
132
  context.method(:proceed).call self, *args, &block
134
133
  end
@@ -146,20 +145,22 @@ module Aquarium
146
145
 
147
146
  # We require the same object id, not just equal objects.
148
147
  def <=> other
149
- return 0 if object_id == other.object_id
148
+ return 0 if object_id == other.object_id
149
+ return 1 if other.nil?
150
150
  result = self.class <=> other.class
151
151
  return result unless result == 0
152
- result = (self.target_type.nil? && other.target_type.nil?) ? 0 : self.target_type.to_s <=> other.target_type.to_s
152
+ result = (self.target_type.nil? and other.target_type.nil?) ? 0 : self.target_type.to_s <=> other.target_type.to_s
153
153
  return result unless result == 0
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
154
+ result = (self.target_object.nil? and other.target_object.nil?) ? 0 : self.target_object.object_id <=> other.target_object.object_id
156
155
  return result unless result == 0
157
- result = (self.method_name.nil? && other.method_name.nil?) ? 0 : self.method_name.to_s <=> other.method_name.to_s
156
+ result = (self.method_name.nil? and other.method_name.nil?) ? 0 : self.method_name.to_s <=> other.method_name.to_s
158
157
  return result unless result == 0
159
158
  result = self.instance_method? == other.instance_method?
160
159
  return 1 unless result == true
161
- result = (self.context.nil? && other.context.nil?) ? 0 : self.context <=> other.context
162
- return result
160
+ return 0 if self.context.nil? and other.context.nil?
161
+ return -1 if self.context.nil? and !other.context.nil?
162
+ return 1 if !self.context.nil? and other.context.nil?
163
+ return self.context <=> other.context
163
164
  end
164
165
 
165
166
  def eql? other
@@ -1,5 +1,6 @@
1
1
  require 'set'
2
2
  require 'aquarium/aspects/join_point'
3
+ require 'aquarium/aspects/exclusion_handler'
3
4
  require 'aquarium/utils'
4
5
  require 'aquarium/extensions'
5
6
  require 'aquarium/finders/finder_result'
@@ -18,8 +19,9 @@ module Aquarium
18
19
  include Aquarium::Utils::ArrayUtils
19
20
  include Aquarium::Utils::HashUtils
20
21
  include Aquarium::Utils::SetUtils
22
+ include ExclusionHandler
21
23
  include DefaultObjectHandler
22
-
24
+
23
25
  attr_reader :specification
24
26
 
25
27
  # Construct a Pointcut for methods in types or objects.
@@ -66,6 +68,24 @@ module Aquarium
66
68
  # One or more of <tt>:readers</tt>, <tt>:reader</tt> (synonymous),
67
69
  # <tt>:writers</tt>, and/or <tt>:writer</tt> (synonymous). By default, both
68
70
  # readers and writers are matched.
71
+ #
72
+ # <tt>:exclude_pointcuts => pc || [pc_list]</tt>::
73
+ # <tt>:exclude_pointcut => pc || [pc_list]</tt>::
74
+ # <tt>:exclude_join_points => jp || [jp_list]</tt>::
75
+ # <tt>:exclude_join_point => jp || [jp_list]</tt>::
76
+ # <tt>:exclude_types => type || [type_list]</tt>::
77
+ # <tt>:exclude_types => type || [type_list]</tt>::
78
+ # <tt>:exclude_type => type || [type_list]</tt>::
79
+ # <tt>:exclude_objects => object || [object_list]</tt>::
80
+ # <tt>:exclude_object => object || [object_list]</tt>::
81
+ # <tt>:exclude_methods => method || [method_list]</tt>::
82
+ # <tt>:exclude_method => method || [method_list]</tt>::
83
+ # <tt>:exclude_attributes => attribute || [attribute_list]</tt>::
84
+ # <tt>:exclude_attribute => attribute || [attribute_list]</tt>::
85
+ # Exclude the specified "things" from the matched join points. If pointcuts are
86
+ # excluded, they should be subsets of the matched pointcuts. Otherwise, the
87
+ # resulting pointcut will be empty!
88
+ #
69
89
  def initialize options = {}
70
90
  init_specification options
71
91
  init_candidate_types
@@ -113,22 +133,20 @@ module Aquarium
113
133
 
114
134
  attr_writer :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_objects, :candidate_join_points
115
135
 
136
+ ALLOWED_OPTIONS_SINGULAR = %w[type object join_point method exclude_type exclude_object exclude_join_point exclude_pointcut exclude_method
137
+ default_object attribute method_option attribute_option]
138
+
116
139
  def init_specification options
117
140
  @specification = {}
118
141
  options ||= {}
119
142
  validate_options options
120
- @specification[:method_options] = Set.new(make_array(options[:method_options]))
121
- @specification[:attribute_options] = Set.new(make_array(options[:attribute_options]) )
122
- @specification[:types] = Set.new(make_array(options[:types], options[:type]))
123
- @specification[:objects] = Set.new(make_array(options[:objects], options[:object]))
124
- @specification[:join_points] = Set.new(make_array(options[:join_points], options[:join_point]))
125
- @specification[:exclude_types] = Set.new(make_array(options[:exclude_type], options[:exclude_types]))
126
- @specification[:exclude_objects] = Set.new(make_array(options[:exclude_object], options[:exclude_objects]))
127
- @specification[:exclude_join_points] = Set.new(make_array(options[:exclude_join_point], options[:exclude_join_points]))
128
- @specification[:exclude_methods] = Set.new(make_array(options[:exclude_method], options[:exclude_methods]))
129
- @specification[:default_object] = Set.new(make_array(options[:default_object]))
143
+ ALLOWED_OPTIONS_SINGULAR.each do |option|
144
+ self.instance_eval(<<-EOF, __FILE__, __LINE__)
145
+ @specification[:#{option}s]= Set.new(make_array(options[:#{option}], options[:#{option}s]))
146
+ EOF
147
+ end
130
148
  use_default_object_if_defined unless (types_given? || objects_given?)
131
- @specification[:attributes] = Set.new(make_array(options[:attributes], options[:attribute]))
149
+
132
150
  raise Aquarium::Utils::InvalidOptions.new(":all is not yet supported for :attributes.") if @specification[:attributes] == Set.new([:all])
133
151
  init_methods_specification options
134
152
  end
@@ -139,10 +157,11 @@ module Aquarium
139
157
  end
140
158
 
141
159
  def validate_options options
142
- knowns = %w[object objects type types join_point join_points
143
- exclude_object exclude_objects exclude_type exclude_types exclude_join_point exclude_join_points exclude_method exclude_methods
144
- method methods attribute attributes
145
- method_options attribute_options default_object].map {|x| x.intern}
160
+ knowns = []
161
+ ALLOWED_OPTIONS_SINGULAR.each do |x|
162
+ knowns << x.intern
163
+ knowns << "#{x}s".intern
164
+ end
146
165
  unknowns = options.keys - knowns
147
166
  raise Aquarium::Utils::InvalidOptions.new("Unknown options specified: #{unknowns.inspect}") if unknowns.size > 0
148
167
  end
@@ -175,7 +194,7 @@ module Aquarium
175
194
  EOF
176
195
  end
177
196
 
178
- %w[types objects join_points methods].each do |name|
197
+ %w[types objects join_points pointcuts methods].each do |name|
179
198
  class_eval(<<-EOF, __FILE__, __LINE__)
180
199
  def exclude_#{name}_given
181
200
  @specification[:exclude_#{name}]
@@ -227,15 +246,20 @@ module Aquarium
227
246
  find_join_points_for :type, candidate_types, make_all_method_names
228
247
  find_join_points_for :object, candidate_objects, make_all_method_names
229
248
  add_join_points_for_candidate_join_points
249
+ remove_excluded_join_points
230
250
  end
231
251
 
232
252
  def add_join_points_for_candidate_join_points
233
- @join_points_matched += @candidate_join_points.matched.keys.find_all do |jp|
234
- not (is_excluded_join_point?(jp) or is_excluded_type_or_object?(jp.type_or_object) or is_excluded_method?(jp.method_name))
235
- end
253
+ @join_points_matched += @candidate_join_points.matched.keys
236
254
  @join_points_not_matched += @candidate_join_points.not_matched.keys
237
255
  end
238
256
 
257
+ def remove_excluded_join_points
258
+ @join_points_matched.delete_if do |jp|
259
+ join_point_excluded? jp
260
+ end
261
+ end
262
+
239
263
  def find_join_points_for type_or_object_sym, candidates, method_names
240
264
  results = find_methods_for type_or_object_sym, candidates, method_names
241
265
  add_join_points results, type_or_object_sym
@@ -272,35 +296,6 @@ module Aquarium
272
296
  @specification[:exclude_methods]
273
297
  end
274
298
 
275
- def is_excluded_join_point? jp
276
- @specification[:exclude_join_points].include? jp
277
- end
278
-
279
- def is_excluded_type_or_object? type_or_object
280
- return true if @specification[:exclude_objects].include?(type_or_object)
281
- @specification[:exclude_types].find do |t|
282
- case t
283
- when String: type_or_object.name.eql?(t)
284
- when Symbol: type_or_object.name.eql?(t.to_s)
285
- when Regexp: type_or_object.name =~ t
286
- end
287
- end
288
- end
289
-
290
- def is_excluded_method? method
291
- is_explicitly_excluded_method?(method) or matches_excluded_method_regex?(method)
292
- end
293
-
294
- def is_explicitly_excluded_method? method
295
- @specification[:exclude_methods].include? method
296
- end
297
-
298
- def matches_excluded_method_regex? method
299
- regexs = @specification[:exclude_methods].find_all {|s| s.kind_of? Regexp}
300
- return false if regexs.empty?
301
- regexs.find {|re| method.to_s =~ re}
302
- end
303
-
304
299
  def self.make_attribute_readers attributes
305
300
  readers = attributes.map do |regexp_or_name|
306
301
  if regexp_or_name.kind_of? Regexp
@@ -173,7 +173,7 @@ module Aquarium
173
173
  # Must recalc reflect methods if we've switched to the type of the input object.
174
174
  reflection_method_names = make_methods_reflection_method_names type, "methods"
175
175
  end
176
- ancestors = eval "#{type.to_s}.ancestors + #{type.to_s}.included_modules"
176
+ ancestors = type.ancestors + type.included_modules
177
177
  return method_array if ancestors.nil? || ancestors.size <= 1 # 1 for type_or_object itself!
178
178
  ancestors.each do |ancestor|
179
179
  unless ancestor.name == type.to_s
@@ -8,6 +8,7 @@ require File.dirname(__FILE__) + '/type_finder'
8
8
 
9
9
  module Aquarium
10
10
  module Finders
11
+ # Deprecated - Will be removed in a future release!
11
12
  class ObjectFinder
12
13
  include Aquarium::Utils::ArrayUtils
13
14
 
@@ -16,27 +16,72 @@ module Aquarium
16
16
  end
17
17
  hash
18
18
  end
19
+
20
+ def self.visibility type_or_instance, method_sym, class_or_instance_only = nil, include_ancestors = true
21
+ find_method(type_or_instance, method_sym, class_or_instance_only, include_ancestors) do |type_or_instance, method_sym, protection|
22
+ return protection
23
+ end
24
+ end
25
+
26
+ def self.has_method type_or_instance, method_sym, class_or_instance_only = nil, include_ancestors = true
27
+ found = find_method(type_or_instance, method_sym, class_or_instance_only, include_ancestors) do |type_or_instance, method_sym, protection|
28
+ return true
29
+ end
30
+ found ? true : false # found could be nil; return false, if so
31
+ end
19
32
 
20
- def self.visibility type_or_instance, method_sym, class_or_instance_only = nil
33
+ def self.find_method type_or_instance, method_sym, class_or_instance_only = nil, include_ancestors = true
21
34
  meta_method_suffixes = determine_meta_method_suffixes type_or_instance, class_or_instance_only
22
35
  meta_method_suffixes.each do |suffix|
23
36
  %w[public protected private].each do |protection|
24
37
  meta_method = "#{protection}_#{suffix}"
25
- if find_method(type_or_instance, method_sym, meta_method)
26
- return protection.intern
38
+ methods = type_or_instance.send(meta_method, include_ancestors)
39
+ if methods.include?(method_sym.to_s)
40
+ return yield(type_or_instance, method_sym, protection.intern)
27
41
  end
28
42
  end
29
43
  end
30
44
  nil
31
45
  end
32
46
 
47
+ # Which type in a hierarchy actually defines a method?
48
+ def self.definer type_or_instance, method_sym, class_or_instance_only = nil
49
+ return nil if type_or_instance.nil? or method_sym.nil?
50
+ return nil unless has_method(type_or_instance, method_sym, class_or_instance_only)
51
+ ancestors = ancestors_for type_or_instance
52
+ determine_definer ancestors, type_or_instance, method_sym, class_or_instance_only
53
+ end
54
+
33
55
  private
34
56
 
57
+ # For objects, include the singleton/eigenclass in case a method of interest was actually defined just for the object.
58
+ def self.ancestors_for object
59
+ if Aquarium::Utils::TypeUtils.is_type? object
60
+ object.ancestors
61
+ else
62
+ eigen = (class << object; self; end)
63
+ [eigen] + eigen.ancestors
64
+ end
65
+ end
66
+
67
+ def self.determine_definer ancestors, type_or_instance, method_sym, class_or_instance_only
68
+ candidates = ancestors.find_all {|a| has_method(a, method_sym, class_or_instance_only, false) { true }}
69
+ if candidates.size == 2 and Aquarium::Utils::TypeUtils.is_type?(type_or_instance) == false
70
+ return determine_actual_parent(type_or_instance, candidates)
71
+ end
72
+ candidates.size == 1 ? candidates.first : raise("Bug: Got multiple types #{candidates.inspect} that implement method #{method_sym}")
73
+ end
74
+
75
+ def self.determine_actual_parent object, candidates
76
+ return nil unless (object.is_a?(candidates[0]) and object.is_a?(candidates[1]))
77
+ candidates[0].name == "" ? candidates[1] : candidates[0]
78
+ end
79
+
35
80
  def self.determine_meta_method_suffixes type_or_instance, class_or_instance_only
36
81
  limits = class_or_instance_only.nil? ? [:instance_method_only, :class_method_only] : [class_or_instance_only]
37
82
  meta_method_suffixes = []
38
83
  limits.each do |limit|
39
- if (Aquarium::Utils::TypeUtils.is_type? type_or_instance)
84
+ if Aquarium::Utils::TypeUtils.is_type? type_or_instance
40
85
  meta_method_suffixes << "instance_methods" if limit == :instance_method_only
41
86
  meta_method_suffixes << "methods" if limit == :class_method_only
42
87
  else
@@ -45,10 +90,6 @@ module Aquarium
45
90
  end
46
91
  meta_method_suffixes
47
92
  end
48
-
49
- def self.find_method type_or_instance, method_sym, meta_method
50
- type_or_instance.send(meta_method).include?(method_sym.to_s)
51
- end
52
93
  end
53
94
  end
54
95
  end