| Class | Aquarium::Aspects::Pointcut |
| In: |
lib/aquarium/aspects/pointcut.rb
lib/aquarium/aspects/pointcut_composition.rb |
| Parent: | Object |
| POINTCUT_CANONICAL_OPTIONS | = | { "default_objects" => %w[default_object], "join_points" => %w[join_point], "exclude_pointcuts" => %w[exclude_pointcut], "attributes" => %w[attribute accessing], "attribute_options" => %w[attribute_option], } |
| CANONICAL_OPTIONS | = | Aquarium::Finders::TypeFinder::CANONICAL_OPTIONS.merge( Aquarium::Finders::MethodFinder::METHOD_FINDER_CANONICAL_OPTIONS.merge(POINTCUT_CANONICAL_OPTIONS)) |
| ATTRIBUTE_OPTIONS_VALUES | = | %w[reading writing changing] |
| candidate_join_points | [W] | |
| candidate_join_points | [R] | |
| candidate_objects | [R] | |
| candidate_objects | [W] | |
| candidate_types | [R] | |
| candidate_types | [W] | |
| candidate_types_excluded | [R] | |
| candidate_types_excluded | [W] | |
| join_points_matched | [R] | |
| join_points_matched | [W] | |
| join_points_not_matched | [R] | |
| join_points_not_matched | [W] | |
| specification | [R] | |
| specification | [W] | |
| specification | [R] |
# File lib/aquarium/aspects/pointcut.rb, line 250
250: def self.make_attribute_reading_writing_options options_hash
251: result = {}
252: [:writing, :changing, :reading].each do |attr_key|
253: next if options_hash[attr_key].nil? or options_hash[attr_key].empty?
254: result[:attributes] ||= Set.new([])
255: result[:attribute_options] ||= Set.new([])
256: result[:attributes].merge(Aquarium::Utils::ArrayUtils.make_array(options_hash[attr_key]))
257: attr_opt = attr_key == :reading ? :readers : :writers
258: result[:attribute_options] << attr_opt
259: end
260: result
261: end
Construct a Pointcut for methods in types or objects.
Pointcut.new :join_points => [...] | :type{s} => [...] | :object{s} => [...]
{, :method{s} => [], :method_options => [...],
:attribute{s} => [...], :attribute_options[...]}
where the "{}" indicate optional elements. Most of the arguments have many synonyms, shown below, to promote an English-like DSL.
The options include the following.
Specify one or an array of join_points.
Specify a type, type name, type name regular expression or an array of the same. (Mixed is allowed.)
Specify a type, type name, type name regular expression or an array of the same. (Mixed is allowed.) The ancestors or descendents will also be found. To find both ancestors and descendents, use both options.
Specify a type, type name, type name regular expression or an array of the same. (Mixed is allowed.) The nested (enclosed) types will also be found.
An "internal" flag used by Aspect::DSL#pointcut. When no object or type is specified explicitly, the value of :default_objects will be used, if defined. Aspect::DSL#pointcut sets the value to self, so the user doesn‘t have to specify a type or object in the contexts where that would be useful, e.g., pointcuts defined within a type for join points within itself. WARNING: This flag is subject to change, so don‘t use it explicitly!
A method name, name regular expession or an array of the same. By default, if neither :methods nor :attributes are specified, all public instance methods will be found, with the method option :exclude_ancestor_methods implied, unless explicit method options are given.
One or more options supported by Aquarium::Finders::MethodFinder. The :exclude_ancestor_methods option is most useful.
An attribute name, regular expession or array of the same. WARNING This is syntactic sugar for the corresponding attribute readers and/or writers methods. The actual attribute accesses are not advised, which can lead to unexpected behavior. A goal before V1.0 is to support actual attribute accesses, if possible.
If :reading is specified, just attribute readers are matched. If :writing is specified, just attribute writers are matched. If :accessing is specified, both readers and writers are matched. Any matches will be joined with the matched :methods..
One or more of :readers, :reader (synonymous), :writers, and/or :writer (synonymous). By default, both readers and writers are matched. :reading => … is synonymous with :attributes => …, :attribute_options => [:readers]. :writing => … and :changing => … are synonymous with :attributes => …, :attribute_options => [:writers]. :accessing => … is synonymous with :attributes => ….
Exclude the specified "things" from the matched join points. If pointcuts are excluded, they should be subsets of the matched pointcuts. Otherwise, the resulting pointcut will be empty!
The exclude_ prefix works with the synonyms of the options shown.
Pointcut.new also accepts all the "universal" options documented in Aquarium::Utils::OptionsUtils.
# File lib/aquarium/aspects/pointcut.rb, line 191
191: def initialize options = {}
192: init_specification options, CANONICAL_OPTIONS, (ATTRIBUTE_OPTIONS_VALUES + Advice::KINDS_IN_PRIORITY_ORDER) do
193: finish_specification_initialization
194: end
195: return if noop
196: init_candidate_types
197: init_candidate_objects
198: init_candidate_join_points
199: init_join_points
200: end
# File lib/aquarium/aspects/pointcut.rb, line 280
280: def self.validate_attribute_options spec_hash, options_hash
281: raise Aquarium::Utils::InvalidOptions.new(":all is not yet supported for :attributes.") if spec_hash[:attributes] == Set.new([:all])
282: if options_hash[:reading] and (options_hash[:writing] or options_hash[:changing])
283: unless options_hash[:reading].eql?(options_hash[:writing]) or options_hash[:reading].eql?(options_hash[:changing])
284: raise Aquarium::Utils::InvalidOptions.new(":reading and :writing/:changing can only be used together if they refer to the same set of attributes.")
285: end
286: end
287: end
# File lib/aquarium/aspects/pointcut_composition.rb, line 24
24: def and pointcut2
25: result = Aquarium::Aspects::Pointcut.new
26: result.specification = specification.and(pointcut2.specification) do |value1, value2|
27: value1 & value2
28: # value1.intersection_using_eql_comparison value2
29: end
30: result.join_points_matched = join_points_matched.intersection_using_eql_comparison pointcut2.join_points_matched
31: result.join_points_not_matched = join_points_not_matched.intersection_using_eql_comparison pointcut2.join_points_not_matched
32: result.candidate_types = candidate_types.intersection pointcut2.candidate_types
33: result.candidate_objects = candidate_objects.intersection pointcut2.candidate_objects
34: result
35: end
# File lib/aquarium/aspects/pointcut.rb, line 276
276: def any_type_related_options_given?
277: objects_given? or join_points_given? or types_given? or types_and_descendents_given? or types_and_ancestors_given? or types_and_nested_types_given?
278: end
# File lib/aquarium/aspects/pointcut.rb, line 220
220: def empty?
221: return join_points_matched.empty? && join_points_not_matched.empty?
222: end
Two Considered equivalent only if the same join points matched and not_matched sets are equal, the specifications are equal, and the candidate types and candidate objects are equal. if you care only about the matched join points, then just compare join_points_matched
# File lib/aquarium/aspects/pointcut.rb, line 207
207: def eql? other
208: object_id == other.object_id ||
209: (self.class === other &&
210: specification == other.specification &&
211: candidate_types == other.candidate_types &&
212: candidate_types_excluded == other.candidate_types_excluded &&
213: candidate_objects == other.candidate_objects &&
214: join_points_not_matched == other.join_points_not_matched &&
215: join_points_matched == other.join_points_matched) # not_matched is probably smaller, so do first.
216: end
# File lib/aquarium/aspects/pointcut.rb, line 263
263: def finish_specification_initialization
264: @specification.merge! Pointcut.make_attribute_reading_writing_options(@original_options)
265: # Map the method options to their canonical values:
266: @specification[:method_options] = Aquarium::Finders::MethodFinder.init_method_options(@specification[:method_options])
267: use_default_objects_if_defined unless any_type_related_options_given?
268: Pointcut::validate_attribute_options @specification, @original_options
269: init_methods_specification
270: end
# File lib/aquarium/aspects/pointcut.rb, line 272
272: def init_methods_specification
273: match_all_methods if ((no_methods_specified? and no_attributes_specified?) or all_methods_specified?)
274: end
# File lib/aquarium/aspects/pointcut.rb, line 224
224: def inspect
225: "Pointcut: {specification: #{specification.inspect}, candidate_types: #{candidate_types.inspect}, candidate_types_excluded: #{candidate_types_excluded.inspect}, candidate_objects: #{candidate_objects.inspect}, join_points_matched: #{join_points_matched.inspect}, join_points_not_matched: #{join_points_not_matched.inspect}}"
226: end
# File lib/aquarium/aspects/pointcut_composition.rb, line 9
9: def or pointcut2
10: result = Aquarium::Aspects::Pointcut.new
11: result.specification = specification.or(pointcut2.specification) do |value1, value2|
12: value1.union_using_eql_comparison value2
13: end
14: result.join_points_matched = join_points_matched.union_using_eql_comparison pointcut2.join_points_matched
15: result.join_points_not_matched = join_points_not_matched.union_using_eql_comparison pointcut2.join_points_not_matched
16: result.candidate_types = candidate_types.union pointcut2.candidate_types
17: result.candidate_objects = candidate_objects.union pointcut2.candidate_objects
18: result
19: end
# File lib/aquarium/aspects/pointcut.rb, line 301
301: def all_methods_specified?
302: methods_spec = @specification[:methods].to_a
303: methods_spec.include?(:all) or methods_spec.include?(:all_methods)
304: end
# File lib/aquarium/aspects/pointcut.rb, line 293
293: def match_all_methods
294: @specification[:methods] = Set.new([:all])
295: end
# File lib/aquarium/aspects/pointcut.rb, line 306
306: def no_attributes_specified?
307: @specification[:attributes].nil? or @specification[:attributes].empty?
308: end