Encapsulates information about a Join Point that might be advised. JoinPoint objects are almost value objects; you can change the context object. TODO Separate out the read-only part from the variable part. This might require an API change!
Create a join point object, specifying either one type or one object and a method. Only method join points are currently supported by Aquarium.
The supported options are
:type => type | type_name | type_name_regexp
A single type, type name or regular expression matching only one type. One and only one type or object is required. An error is raised otherwise.
:object => object
A single object. One and only one type or object is required. An error is raised otherwise.
:method_name | :method => method_name_or_symbol
A single method name or symbol. Only one is allowed, although the special
flag :all
is allowed, as long as only one method will be
found, subject to the next option.
:class_method | :instance_method => true | false
Is the method a class or instance method? Defaults to
:instance_method => true
.
Note: The range of options is not as rich as for Pointcut, because it is expected that JoinPoint objects will be explicitly created only rarely by users of Aquarium. Most of the time, Pointcuts will be created.
# File lib/aquarium/aspects/join_point.rb, line 120 def initialize options = {} @target_type = resolve_type options @target_object = options[:object] @method_name = options[:method_name] || options[:method] class_method = options[:class_method].nil? ? false : options[:class_method] @instance_method = options[:instance_method].nil? ? (!class_method) : options[:instance_method] @instance_or_class_method = @instance_method ? :instance : :class @visibility = Aquarium::Utils::MethodUtils.visibility(type_or_object, @method_name, class_or_instance_method_flag) @context = options[:context] || JoinPoint::Context.new assert_valid options end
# File lib/aquarium/aspects/join_point.rb, line 174 def <=> other return 0 if object_id == other.object_id return 1 if other.nil? result = self.class <=> other.class return result unless result == 0 result = compare_field(:target_object, other) {|f1,f2| f1.object_id <=> f2.object_id} return result unless result == 0 result = compare_field(:instance_method, other) {|f1,f2| boolean_compare(f1,f2)} return result unless result == 0 [:target_type, :method_name, :context].each do |field| result = compare_field field, other return result unless result == 0 end 0 end
# File lib/aquarium/aspects/join_point.rb, line 99 def class_method? !@instance_method end
# File lib/aquarium/aspects/join_point.rb, line 132 def dup jp = super jp.context = @context.dup unless @context.nil? jp end
# File lib/aquarium/aspects/join_point.rb, line 190 def eql? other return (self <=> other) == 0 end
# File lib/aquarium/aspects/join_point.rb, line 144 def exists? type_or_object_sym = @target_type ? :type : :object results = Aquarium::Finders::MethodFinder.new.find type_or_object_sym => type_or_object, :method => method_name, :method_options => [visibility, instance_or_class_method] raise Aquarium::Utils::LogicError("MethodFinder returned more than one item! #{results.inspect}") if (results.matched.size + results.not_matched.size) != 1 return results.matched.size == 1 ? true : false end
todo: restore context output.
# File lib/aquarium/aspects/join_point.rb, line 198 def inspect "JoinPoint: {target_type = #{target_type.nil? ? target_type : target_type.name}, target_object = #{target_object.inspect}, method_name = #{method_name}, instance_method? #{instance_method?}, context = #{context.inspect}" end
# File lib/aquarium/aspects/join_point.rb, line 170 def instance_method @instance_method end
# File lib/aquarium/aspects/join_point.rb, line 95 def instance_method? @instance_method end
Invoke the join point itself, skipping any intermediate advice. This method can only be called if the join point has a context object defined that represents an actual runtime “state”. Use this method cautiously, at it could be “surprising” if some advice is not executed!
# File lib/aquarium/aspects/join_point.rb, line 165 def invoke_original_join_point *args, &block raise ContextNotCorrectlyDefined.new(":invoke_original_join_point can't be called unless the join point has a context object.") if context.nil? context.invoke_original_join_point self, *args, &block end
Invoke the join point itself (which could actually be aspect advice wrapping the original join point…). This method can only be called if the join point has a context object defined that represents an actual runtime “state”.
# File lib/aquarium/aspects/join_point.rb, line 156 def proceed *args, &block raise ContextNotCorrectlyDefined.new(":proceed can't be called unless the join point has a context object.") if context.nil? context.proceed self, *args, &block end
# File lib/aquarium/aspects/join_point.rb, line 138 def type_or_object target_type || target_object end
Since JoinPoints can be declared for non-existent methods, tolerate “nil” for the visibility.
# File lib/aquarium/aspects/join_point.rb, line 242 def assert_valid options, error_message = "" error_message << "Must specify a :method_name. " unless method_name error_message << "Must specify either a :type or :object. " unless (target_type or target_object) error_message << "Can't specify both a :type or :object. " if (target_type and target_object) bad_attributes(error_message, options) if error_message.length > 0 end
# File lib/aquarium/aspects/join_point.rb, line 223 def boolean_compare b1, b2 return 0 if b1 == b2 return b1 == true ? 1 : -1 end
# File lib/aquarium/aspects/join_point.rb, line 249 def class_or_instance_method_flag "#{instance_or_class_method.to_s}_method_only".intern end
# File lib/aquarium/aspects/join_point.rb, line 206 def compare_field field_reader, other field1 = self.method(field_reader).call field2 = other.method(field_reader).call if field1.nil? return field2.nil? ? 0 : -1 else return 1 if field2.nil? end if block_given? yield field1, field2 elsif field1.respond_to?(:<=>) field1 <=> field2 else field1.to_s <=> field2.to_s end end
# File lib/aquarium/aspects/join_point.rb, line 228 def resolve_type options type = options[:type] return type if type.nil? # okay, if they specified an object! return type if type.kind_of? Module found = Aquarium::Finders::TypeFinder.new.find :type => type if found.matched.empty? bad_attributes("No type matched the string or regular expression: #{type.inspect}", options) elsif found.matched.size > 1 bad_attributes("More than one type matched the string or regular expression: #{type.inspect}", options) end found.matched.keys.first end