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