lib/aquarium/aspects/pointcut.rb in aquarium-0.1.6 vs lib/aquarium/aspects/pointcut.rb in aquarium-0.1.7

- old
+ new

@@ -46,15 +46,15 @@ # # <tt>:methods => method || [method_list]</tt>:: # <tt>:method => method || [method_list]</tt>:: # One or an array of methods, method names and/or method regular expessions to match. # By default, unless :attributes are specified, searches for public instance methods - # with the method option :suppress_ancestor_methods implied, unless explicit method + # with the method option :exclude_ancestor_methods implied, unless explicit method # options are given. # # <tt>:method_options => [options]</tt>:: - # One or more options supported by Aquarium::Finders::MethodFinder. The :suppress_ancestor_methods + # One or more options supported by Aquarium::Finders::MethodFinder. The :exclude_ancestor_methods # option is most useful. # # <tt>:attributes => attribute || [attribute_list]</tt>:: # <tt>:attribute => attribute || [attribute_list]</tt>:: # One or an array of attribute names and/or regular expessions to match. @@ -86,12 +86,11 @@ candidate_objects == other.candidate_objects && join_points_matched == other.join_points_matched && join_points_not_matched == other.join_points_not_matched) end - alias :== :eql? - alias :=== :eql? + alias :== :eql? def empty? return join_points_matched.empty? && join_points_not_matched.empty? end @@ -121,10 +120,14 @@ @specification[:method_options] = Set.new(make_array(options[:method_options])) @specification[:attribute_options] = Set.new(make_array(options[:attribute_options]) ) @specification[:types] = Set.new(make_array(options[:types], options[:type])) @specification[:objects] = Set.new(make_array(options[:objects], options[:object])) @specification[:join_points] = Set.new(make_array(options[:join_points], options[:join_point])) + @specification[:exclude_types] = Set.new(make_array(options[:exclude_type], options[:exclude_types])) + @specification[:exclude_objects] = Set.new(make_array(options[:exclude_object], options[:exclude_objects])) + @specification[:exclude_join_points] = Set.new(make_array(options[:exclude_join_point], options[:exclude_join_points])) + @specification[:exclude_methods] = Set.new(make_array(options[:exclude_method], options[:exclude_methods])) @specification[:default_object] = Set.new(make_array(options[:default_object])) use_default_object_if_defined unless (types_given? || objects_given?) @specification[:attributes] = Set.new(make_array(options[:attributes], options[:attribute])) raise Aquarium::Utils::InvalidOptions.new(":all is not yet supported for :attributes.") if @specification[:attributes] == Set.new([:all]) init_methods_specification options @@ -134,11 +137,14 @@ @specification[:methods] = Set.new(make_array(options[:methods], options[:method])) @specification[:methods].add(:all) if @specification[:methods].empty? and @specification[:attributes].empty? end def validate_options options - knowns = %w[object objects type types join_point join_points method methods attribute attributes method_options attribute_options default_object].map {|x| x.intern} + knowns = %w[object objects type types join_point join_points + exclude_object exclude_objects exclude_type exclude_types exclude_join_point exclude_join_points exclude_method exclude_methods + method methods attribute attributes + method_options attribute_options default_object].map {|x| x.intern} 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 @@ -167,23 +173,42 @@ not (#{name}_given.nil? or #{name}_given.empty?) end EOF end + %w[types objects join_points methods].each do |name| + class_eval(<<-EOF, __FILE__, __LINE__) + def exclude_#{name}_given + @specification[:exclude_#{name}] + end + + def exclude_#{name}_given? + not (exclude_#{name}_given.nil? or exclude_#{name}_given.empty?) + end + EOF + end + private def init_candidate_types explicit_types, type_regexps_or_names = @specification[:types].partition do |type| - is_type? type + Aquarium::Utils::TypeUtils.is_type? type end - @candidate_types = Aquarium::Finders::TypeFinder.new.find :types => type_regexps_or_names - @candidate_types.append_matched(make_hash(explicit_types) {|x| Set.new([])}) # Append already-known types + 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} end def init_candidate_objects object_hash = {} - @specification[:objects].each {|o| object_hash[o] = Set.new([])} + (@specification[:objects].flatten - @specification[:exclude_objects].flatten).each do |o| + object_hash[o] = Set.new([]) + end @candidate_objects = Aquarium::Finders::FinderResult.new object_hash end def init_candidate_join_points @candidate_join_points = Aquarium::Finders::FinderResult.new @@ -202,16 +227,14 @@ find_join_points_for :type, candidate_types, make_all_method_names find_join_points_for :object, candidate_objects, make_all_method_names add_join_points_for_candidate_join_points end - def is_type? candidate_type - candidate_type.kind_of?(Module) || candidate_type.kind_of?(Class) - end - def add_join_points_for_candidate_join_points - @join_points_matched += @candidate_join_points.matched.keys + @join_points_matched += @candidate_join_points.matched.keys.find_all do |jp| + not (is_excluded_join_point?(jp) or is_excluded_type_or_object?(jp.type_or_object) or is_excluded_method?(jp.method_name)) + end @join_points_not_matched += @candidate_join_points.not_matched.keys end def find_join_points_for type_or_object_sym, candidates, method_names results = find_methods_for type_or_object_sym, candidates, method_names @@ -219,12 +242,13 @@ end def find_methods_for type_or_object_sym, candidates, which_methods return Aquarium::Finders::FinderResult::NIL_OBJECT if candidates.matched.size == 0 Aquarium::Finders::MethodFinder.new.find type_or_object_sym => candidates.matched_keys, - :methods => which_methods, - :options => @specification[:method_options].to_a + :methods => which_methods, + :exclude_methods => @specification[:exclude_methods], + :options => @specification[:method_options].to_a end def add_join_points search_results, type_or_object_sym add_join_points_to @join_points_matched, search_results.matched, type_or_object_sym add_join_points_to @join_points_not_matched, search_results.not_matched, type_or_object_sym @@ -241,12 +265,43 @@ end end end def make_all_method_names - @specification[:methods] + Pointcut.make_attribute_method_names(@specification[:attributes], @specification[:attribute_options]) + @specification[:methods] + + Pointcut.make_attribute_method_names(@specification[:attributes], @specification[:attribute_options]) - + @specification[:exclude_methods] end + def is_excluded_join_point? jp + @specification[:exclude_join_points].include? jp + end + + def is_excluded_type_or_object? type_or_object + return true if @specification[:exclude_objects].include?(type_or_object) + @specification[:exclude_types].find do |t| + case t + when String: type_or_object.name.eql?(t) + when Symbol: type_or_object.name.eql?(t.to_s) + when Regexp: type_or_object.name =~ t + end + end + end + + def is_excluded_method? method + is_explicitly_excluded_method?(method) or matches_excluded_method_regex?(method) + end + + def is_explicitly_excluded_method? method + @specification[:exclude_methods].include? method + end + + def matches_excluded_method_regex? method + regexs = @specification[:exclude_methods].find_all {|s| s.kind_of? Regexp} + return false if regexs.empty? + regexs.find {|re| method.to_s =~ re} + end + def self.make_attribute_readers attributes readers = attributes.map do |regexp_or_name| if regexp_or_name.kind_of? Regexp exp = remove_trailing_equals_and_or_dollar regexp_or_name.source Regexp.new(remove_leading_colon_or_at_sign(exp + '.*\b$'))