Class: Aquarium::Aspects::Pointcut

Inherits:
Object
  • Object
show all
Includes:
DefaultObjectsHandler, ExclusionHandler, Utils::ArrayUtils, Utils::HashUtils, Utils::OptionsUtils, Utils::SetUtils
Defined in:
lib/aquarium/aspects/pointcut.rb,
lib/aquarium/aspects/pointcut_composition.rb

Overview

Pointcut (composition)

Since Pointcuts are queries, they can be composed, i.e., unions and intersections of them can be computed, yielding new Pointcuts.

Constant Summary collapse

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]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DefaultObjectsHandler

#default_objects_given, #default_objects_given?, #use_default_objects_if_defined

Methods included from Utils::ArrayUtils

#make_array, make_array, #strip_array_nils, strip_array_nils

Methods included from ExclusionHandler

#all_excluded_pointcuts, #is_excluded_join_point?, #is_excluded_method?, #is_excluded_pointcut?, #is_excluded_type_or_object?, #is_explicitly_excluded_method?, #join_point_excluded?, #matches_excluded_method_regex?, #set_calculated_excluded_pointcuts

Methods included from Utils::HashUtils

#make_hash, #strip_nil_keys

Methods included from Utils::SetUtils

#make_set, #strip_set_nils, strip_set_nils

Methods included from Utils::OptionsUtils

append_features, #hashify, #init_specification, universal_options, universal_prepositions, #validate_options

Constructor Details

#initialize(options = {}) ⇒ Pointcut

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.

Join Points

Specify one or an array of join_points.

  • :join_points => join_point || [join_point_list]

  • :join_point => join_point || [join_point_list]

  • :for_join_points => join_point || [join_point_list]

  • :for_join_point => join_point || [join_point_list]

  • :on_join_points => join_point || [join_point_list]

  • :on_join_point => join_point || [join_point_list]

  • :within_join_points => join_point || [join_point_list]

  • :within_join_point => join_point || [join_point_list]

Types

Specify a type, type name, type name regular expression or an array of the same. (Mixed is allowed.)

  • :types => type || [type_list]

  • :type => type || [type_list]

  • :for_types => type || [type_list]

  • :for_type => type || [type_list]

  • :on_types => type || [type_list]

  • :on_type => type || [type_list]

  • :within_types => type || [type_list]

  • :within_type => type || [type_list]

Types and Ancestors or Descendents

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.

  • :types_and_descendents => type || [type_list]

  • :type_and_descendents => type || [type_list]

  • :types_and_ancestors => type || [type_list]

  • :type_and_ancestors => type || [type_list]

  • :for_types_and_ancestors => type || [type_list]

  • :for_type_and_ancestors => type || [type_list]

  • :on_types_and_descendents => type || [type_list]

  • :on_type_and_descendents => type || [type_list]

  • :on_types_and_ancestors => type || [type_list]

  • :on_type_and_ancestors => type || [type_list]

  • :within_types_and_descendents => type || [type_list]

  • :within_type_and_descendents => type || [type_list]

  • :within_types_and_ancestors => type || [type_list]

  • :within_type_and_ancestors => type || [type_list]

Types and Nested Types

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.

  • :types_and_nested_types => type || [type_list]

  • :type_and_nested_types => type || [type_list]

  • :types_and_nested => type || [type_list]

  • :type_and_nested => type || [type_list]

  • :for_types_and_nested_types => type || [type_list]

  • :for_type_and_nested_types => type || [type_list]

  • :for_types_and_nested => type || [type_list]

  • :for_type_and_nested => type || [type_list]

  • :on_types_and_nested_types => type || [type_list]

  • :on_type_and_nested_types => type || [type_list]

  • :on_types_and_nested => type || [type_list]

  • :on_type_and_nested => type || [type_list]

  • :within_types_and_nested_types => type || [type_list]

  • :within_type_and_nested_types => type || [type_list]

  • :within_types_and_nested => type || [type_list]

  • :within_type_and_nested => type || [type_list]

Objects
  • :objects => object || [object_list]

  • :object => object || [object_list]

  • :for_objects => object || [object_list]

  • :for_object => object || [object_list]

  • :on_objects => object || [object_list]

  • :on_object => object || [object_list]

  • :within_objects => object || [object_list]

  • :within_object => object || [object_list]

“Default” Objects

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!

  • :default_objects => object || [object_list]

  • :default_object => object || [object_list]

Methods

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.

  • :methods => method || [method_list]

  • :method => method || [method_list]

  • :within_methods => method || [method_list]

  • :within_method => method || [method_list]

  • :calling => method || [method_list]

  • :calls_to => method || [method_list]

  • :invoking => method || [method_list]

  • :invocations_of => method || [method_list]

  • :sending_message_to => method || [method_list]

Method Options

One or more options supported by Aquarium::Finders::MethodFinder. The :exclude_ancestor_methods option is most useful.

  • :method_options => [options]

Attributes

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.

  • :attributes => attribute || [attribute_list]

  • :attribute => attribute || [attribute_list]

  • :reading => attribute || [attribute_list]

  • :writing => attribute || [attribute_list]

  • :changing => attribute || [attribute_list]

  • :accessing => attribute || [attribute_list]

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..

Attribute Options

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 => ....

  • :attribute_options => [options]

Exclusion Options

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!

  • :exclude_pointcuts => pc || [pc_list]

  • :exclude_pointcut => pc || [pc_list]

  • :exclude_join_points => jp || [jp_list]

  • :exclude_join_point => jp || [jp_list]

  • :exclude_types => type || [type_list]

  • :exclude_types => type || [type_list]

  • :exclude_type => type || [type_list]

  • :exclude_types_and_descendents => type || [type_list]

  • :exclude_type_and_descendents => type || [type_list]

  • :exclude_types_and_ancestors => type || [type_list]

  • :exclude_type_and_ancestors => type || [type_list]

  • :exclude_types_and_nested_types => type || [type_list]

  • :exclude_type_and_nested_types => type || [type_list]

  • :exclude_types_and_nested => type || [type_list]

  • :exclude_type_and_nested => type || [type_list]

  • :exclude_objects => object || [object_list]

  • :exclude_object => object || [object_list]

  • :exclude_methods => method || [method_list]

  • :exclude_method => method || [method_list]

  • :exclude_attributes => attribute || [attribute_list]

  • :exclude_attribute => attribute || [attribute_list]

The exclude_ prefix works with the synonyms of the options shown.

Pointcut.new also accepts all the “universal” options documented in Aquarium::Utils::OptionsUtils.



191
192
193
194
195
196
197
198
199
200
# File 'lib/aquarium/aspects/pointcut.rb', line 191

def initialize options = {} 
  init_specification options, CANONICAL_OPTIONS, (ATTRIBUTE_OPTIONS_VALUES + Advice::KINDS_IN_PRIORITY_ORDER) do 
    finish_specification_initialization
  end
  return if noop
  init_candidate_types 
  init_candidate_objects
  init_candidate_join_points
  init_join_points
end

Instance Attribute Details

#candidate_join_pointsObject

Returns the value of attribute candidate_join_points.



202
203
204
# File 'lib/aquarium/aspects/pointcut.rb', line 202

def candidate_join_points
  @candidate_join_points
end

#candidate_objectsObject

Returns the value of attribute candidate_objects.



202
203
204
# File 'lib/aquarium/aspects/pointcut.rb', line 202

def candidate_objects
  @candidate_objects
end

#candidate_typesObject

Returns the value of attribute candidate_types.



202
203
204
# File 'lib/aquarium/aspects/pointcut.rb', line 202

def candidate_types
  @candidate_types
end

#candidate_types_excludedObject

Returns the value of attribute candidate_types_excluded.



202
203
204
# File 'lib/aquarium/aspects/pointcut.rb', line 202

def candidate_types_excluded
  @candidate_types_excluded
end

#join_points_matchedObject

Returns the value of attribute join_points_matched.



202
203
204
# File 'lib/aquarium/aspects/pointcut.rb', line 202

def join_points_matched
  @join_points_matched
end

#join_points_not_matchedObject

Returns the value of attribute join_points_not_matched.



202
203
204
# File 'lib/aquarium/aspects/pointcut.rb', line 202

def join_points_not_matched
  @join_points_not_matched
end

#specificationObject

Returns the value of attribute specification.



26
27
28
# File 'lib/aquarium/aspects/pointcut.rb', line 26

def specification
  @specification
end

Class Method Details

.make_attribute_reading_writing_options(options_hash) ⇒ Object



250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/aquarium/aspects/pointcut.rb', line 250

def self.make_attribute_reading_writing_options options_hash
  result = {}
  [:writing, :changing, :reading].each do |attr_key|
    next if options_hash[attr_key].nil? or options_hash[attr_key].to_s.empty?
    result[:attributes] ||= Set.new([])
    result[:attribute_options] ||= Set.new([])
    result[:attributes].merge(Aquarium::Utils::ArrayUtils.make_array(options_hash[attr_key]))
    attr_opt = attr_key == :reading ? :readers : :writers
    result[:attribute_options] << attr_opt
  end
  result
end

.validate_attribute_options(spec_hash, options_hash) ⇒ Object



280
281
282
283
284
285
286
287
# File 'lib/aquarium/aspects/pointcut.rb', line 280

def self.validate_attribute_options spec_hash, options_hash
  raise Aquarium::Utils::InvalidOptions.new(":all is not yet supported for :attributes.") if spec_hash[:attributes] == Set.new([:all])
  if options_hash[:reading] and (options_hash[:writing] or options_hash[:changing])
    unless options_hash[:reading].eql?(options_hash[:writing]) or options_hash[:reading].eql?(options_hash[:changing])
      raise Aquarium::Utils::InvalidOptions.new(":reading and :writing/:changing can only be used together if they refer to the same set of attributes.") 
    end
  end
end

Instance Method Details

#and(pointcut2) ⇒ Object Also known as: intersection, &



24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/aquarium/aspects/pointcut_composition.rb', line 24

def and pointcut2
  result = Aquarium::Aspects::Pointcut.new
  result.specification           = specification.and(pointcut2.specification) do |value1, value2| 
    value1 & value2
    # value1.intersection_using_eql_comparison value2
  end
  result.join_points_matched     = join_points_matched.intersection_using_eql_comparison      pointcut2.join_points_matched
  result.join_points_not_matched = join_points_not_matched.intersection_using_eql_comparison  pointcut2.join_points_not_matched
  result.candidate_types         = candidate_types.intersection          pointcut2.candidate_types
  result.candidate_objects       = candidate_objects.intersection        pointcut2.candidate_objects
  result
end


276
277
278
# File 'lib/aquarium/aspects/pointcut.rb', line 276

def any_type_related_options_given?
  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?
end

#empty?Boolean



220
221
222
# File 'lib/aquarium/aspects/pointcut.rb', line 220

def empty?
  return join_points_matched.empty? && join_points_not_matched.empty?
end

#eql?(other) ⇒ Boolean Also known as: ==

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



207
208
209
210
211
212
213
214
215
216
# File 'lib/aquarium/aspects/pointcut.rb', line 207

def eql? other
  object_id == other.object_id || 
  (self.class == other.class &&
   specification == other.specification && 
   candidate_types == other.candidate_types && 
   candidate_types_excluded == other.candidate_types_excluded && 
   candidate_objects == other.candidate_objects && 
   join_points_matched == other.join_points_matched &&
   join_points_not_matched == other.join_points_not_matched)
end

#finish_specification_initializationObject



263
264
265
266
267
268
269
270
# File 'lib/aquarium/aspects/pointcut.rb', line 263

def finish_specification_initialization
  @specification.merge! Pointcut.make_attribute_reading_writing_options(@original_options)
  # Map the method options to their canonical values:
  @specification[:method_options] = Aquarium::Finders::MethodFinder.init_method_options(@specification[:method_options])
  use_default_objects_if_defined unless any_type_related_options_given?
  Pointcut::validate_attribute_options @specification, @original_options
  init_methods_specification
end

#init_methods_specificationObject



272
273
274
# File 'lib/aquarium/aspects/pointcut.rb', line 272

def init_methods_specification
  match_all_methods if ((no_methods_specified? and no_attributes_specified?) or all_methods_specified?)
end

#inspectObject Also known as: to_s



224
225
226
# File 'lib/aquarium/aspects/pointcut.rb', line 224

def inspect
  "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}}"
end

#or(pointcut2) ⇒ Object Also known as: union, |



9
10
11
12
13
14
15
16
17
18
19
# File 'lib/aquarium/aspects/pointcut_composition.rb', line 9

def or pointcut2
  result = Aquarium::Aspects::Pointcut.new
  result.specification           = specification.or(pointcut2.specification) do |value1, value2| 
    value1.union_using_eql_comparison value2
  end
  result.join_points_matched     = join_points_matched.union_using_eql_comparison     pointcut2.join_points_matched
  result.join_points_not_matched = join_points_not_matched.union_using_eql_comparison pointcut2.join_points_not_matched
  result.candidate_types         = candidate_types.union         pointcut2.candidate_types
  result.candidate_objects       = candidate_objects.union       pointcut2.candidate_objects
  result
end