lib/aquarium/aspects/pointcut.rb in aquarium-0.1.8 vs lib/aquarium/aspects/pointcut.rb in aquarium-0.2.0
- old
+ new
@@ -34,10 +34,17 @@
#
# <tt>:types => type || [type_list]</tt>::
# <tt>:type => type || [type_list]</tt>::
# One or an array of types, type names and/or type regular expessions to match.
#
+ # <tt>:types_and_descendents => type || [type_list]</tt>::
+ # <tt>:type_and_descendents => type || [type_list]</tt>::
+ # <tt>:types_and_ancestors => type || [type_list]</tt>::
+ # <tt>:type_and_ancestors => type || [type_list]</tt>::
+ # One or an array of types and either their descendents or ancestors.
+ # If you want both the descendents _and_ ancestors, use both options.
+ #
# <tt>:objects => object || [object_list]</tt>::
# <tt>:object => object || [object_list]</tt>::
# Objects to match.
#
# <tt>:default_object => object</tt>::
@@ -84,27 +91,35 @@
# <tt>:exclude_attribute => attribute || [attribute_list]</tt>::
# 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!
#
+ # <tt>:exclude_types_and_descendents => type || [type_list]</tt>::
+ # <tt>:exclude_type_and_descendents => type || [type_list]</tt>::
+ # <tt>:exclude_types_and_ancestors => type || [type_list]</tt>::
+ # <tt>:exclude_type_and_ancestors => type || [type_list]</tt>::
+ # Exclude the specified types and their descendents, ancestors.
+ # If you want to exclude both the descendents _and_ ancestors, use both options.
+ #
def initialize options = {}
init_specification options
init_candidate_types
init_candidate_objects
init_candidate_join_points
init_join_points
end
- attr_reader :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_objects, :candidate_join_points
+ attr_reader :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_types_excluded, :candidate_objects, :candidate_join_points
# 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
def eql? other
object_id == other.object_id ||
(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
@@ -113,11 +128,11 @@
def empty?
return join_points_matched.empty? && join_points_not_matched.empty?
end
def inspect
- "Pointcut: {specification: #{specification.inspect}, candidate_types: #{candidate_types.inspect}, candidate_objects: #{candidate_objects.inspect}, join_points_matched: #{join_points_matched.inspect}, join_points_not_matched: #{join_points_not_matched.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
alias to_s inspect
def self.make_attribute_method_names attribute_name_regexps_or_names, attribute_options = []
@@ -129,24 +144,42 @@
return readers + writers
end
protected
- attr_writer :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_objects, :candidate_join_points
+ attr_writer :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_types_excluded, :candidate_objects, :candidate_join_points
- ALLOWED_OPTIONS_SINGULAR = %w[type object join_point method exclude_type exclude_object exclude_join_point exclude_pointcut exclude_method
- default_object attribute method_option attribute_option]
+ ALLOWED_OPTIONS_SINGULAR = %w[
+ type object join_point method
+ exclude_type exclude_object exclude_join_point exclude_pointcut exclude_method
+ default_object attribute method_option attribute_option]
+ OTHER_ALLOWED_OPTIONS = %w[
+ type_and_descendents types_and_descendents type_and_ancestors types_and_ancestors
+ exclude_type_and_descendents exclude_types_and_descendents exclude_type_and_ancestors exclude_types_and_ancestors]
+
+ OTHER_ALLOWED_SPEC_KEYS = {
+ "types_and_descendents" => "type_and_descendents",
+ "types_and_ancestors" => "type_and_ancestors",
+ "exclude_types_and_descendents" => "exclude_type_and_descendents",
+ "exclude_types_and_ancestors" => "exclude_type_and_ancestors" }
+
def init_specification options
@specification = {}
options ||= {}
validate_options options
ALLOWED_OPTIONS_SINGULAR.each do |option|
self.instance_eval(<<-EOF, __FILE__, __LINE__)
@specification[:#{option}s]= Set.new(make_array(options[:#{option}], options[:#{option}s]))
EOF
end
+ OTHER_ALLOWED_SPEC_KEYS.keys.each do |option|
+ self.instance_eval(<<-EOF, __FILE__, __LINE__)
+ @specification[:#{option}]= Set.new(make_array(options[:#{option}], options[:#{OTHER_ALLOWED_SPEC_KEYS[option]}]))
+ EOF
+ end
+
use_default_object_if_defined unless (types_given? || objects_given?)
raise Aquarium::Utils::InvalidOptions.new(":all is not yet supported for :attributes.") if @specification[:attributes] == Set.new([:all])
init_methods_specification options
end
@@ -160,10 +193,13 @@
knowns = []
ALLOWED_OPTIONS_SINGULAR.each do |x|
knowns << x.intern
knowns << "#{x}s".intern
end
+ OTHER_ALLOWED_OPTIONS.each do |x|
+ knowns << x.intern
+ end
unknowns = options.keys - knowns
raise Aquarium::Utils::InvalidOptions.new("Unknown options specified: #{unknowns.inspect}") if unknowns.size > 0
end
def self.read_only attribute_options
@@ -207,20 +243,30 @@
end
private
def init_candidate_types
- explicit_types, type_regexps_or_names = @specification[:types].partition do |type|
- Aquarium::Utils::TypeUtils.is_type? type
+ finder_options = {}
+ exclude_finder_options = {}
+ ['', 'exclude_'].each do |prefix|
+ ['', '_and_ancestors', '_and_descendents'].each do |suffix|
+ # Because the user might be asking for descendents and/or ancestors, we convert explicitly-specified
+ # types into names, then "refind" them. While less efficient, it makes the code more uniform.
+ eval <<-EOF
+ #{prefix}type_regexps_or_names#{suffix} = @specification[:#{prefix}types#{suffix}].map do |t|
+ Aquarium::Utils::TypeUtils.is_type?(t) ? t.name : t
+ end
+ unless #{prefix}type_regexps_or_names#{suffix}.nil?
+ finder_options[:"#{prefix}types#{suffix}"] = #{prefix}type_regexps_or_names#{suffix}
+ exclude_finder_options[:"types#{suffix}"] = #{prefix}type_regexps_or_names#{suffix} if "#{prefix}".length > 0
+ end
+ EOF
+ end
end
- excluded_explicit_types, excluded_type_regexps_or_names = @specification[:exclude_types].partition do |type|
- Aquarium::Utils::TypeUtils.is_type? type
- end
- possible_types = Aquarium::Finders::TypeFinder.new.find :types => type_regexps_or_names, :exclude_types => excluded_type_regexps_or_names
- possible_types.append_matched(make_hash(explicit_types) {|x| Set.new([])})
- @candidate_types = possible_types - Aquarium::Finders::TypeFinder.new.find(:types => excluded_type_regexps_or_names)
- @candidate_types.matched.delete_if {|type, value| excluded_explicit_types.include? type}
+ @candidate_types = Aquarium::Finders::TypeFinder.new.find finder_options
+ @candidate_types_excluded = Aquarium::Finders::TypeFinder.new.find exclude_finder_options
+ @specification[:exclude_types_calculated] = Set.new(@candidate_types_excluded.matched.keys)
end
def init_candidate_objects
object_hash = {}
(@specification[:objects].flatten - @specification[:exclude_objects].flatten).each do |o|
@@ -241,10 +287,10 @@
end
def init_join_points
@join_points_matched = Set.new
@join_points_not_matched = Set.new
- find_join_points_for :type, candidate_types, make_all_method_names
+ find_join_points_for :type, (candidate_types - candidate_types_excluded), make_all_method_names
find_join_points_for :object, candidate_objects, make_all_method_names
add_join_points_for_candidate_join_points
remove_excluded_join_points
end