lib/peekaboo.rb in peekaboo-0.2.1 vs lib/peekaboo.rb in peekaboo-0.3.0
- old
+ new
@@ -1,73 +1,97 @@
require 'peekaboo/configuration'
+require 'peekaboo/singleton_methods'
-# The workhorse of this "Unobtrusive Tracing System".
+# This system has been designed to provide you with an easy and unobtrusive way to trace:
+# * _When_ certain methods are being called
+# * _What_ values they are being supplied
+# * _What_ values they return
+# * _If_ they raise an exception
+#
+# Its API supports both class and instance method tracing inside any of your custom types.
+# You can enable tracing for existing methods and/or pre-register method signatures for any of your types.
+# The latter option gives you the ability to trace any methods that are defined _dynamically_ at runtime.
+#
+# ( see {SingletonMethods#enable_tracing_for} for details )
+#
+# You can also setup *auto-inclusion*, which will allow you _dynamically_ include this module into any of
+# your types at runtime. This alleviates the hassle of having to "+include Peekaboo+" inside all of the
+# classes that you intend use it.
+#
+# ( see {Configuration#autoinclude_with} for details )
module Peekaboo
class << self
- # @return [Configuration] the current configuration
+ # @private
def configuration
@configuration ||= Configuration.new
end
- # Convenience method added to assist in configuring the system
- # ( see {Configuration} for details on all options ).
+ # Use this to configure various aspects of tracing in your application.
#
- # @example Configuring the system inside your project
- # Peekaboo.configure do |config|
- # config.trace_with MyCustomerLogger.new
- # config.autoinclude_with SomeBaseClass, AnotherSoloClass
- # end
+ # See {Configuration} for option details.
+ # @yieldparam [Configuration] config current configuration
def configure
yield configuration
end
- # Callback used to hook tracing system into any class.
- #
- # @param [Class] klass including class
+ # @private
def included klass
- klass.const_set :PEEKABOO_METHOD_LIST, []
+ klass.const_set :PEEKABOO_METHOD_MAP, { :singleton_methods => Set.new, :instance_methods => Set.new }.freeze
klass.instance_variable_set :@_hooked_by_peekaboo, true
klass.extend SingletonMethods
def klass.method_added name
- Peekaboo.wrap_method self, name if peek_list.include? name
+ Peekaboo.wrap self, name, :instance if traced_instance_methods.include? name
end
+
+ def klass.singleton_method_added name
+ Peekaboo.wrap self, name, :singleton if traced_singleton_methods.include? name
+ end
end
- # Modifies a class, and its child classes, to dynamically include module
- # at runtime. This method is used by {Configuration#autoinclude_with}.
- #
- # @param [Class] klass class to modify
+ # @private
def setup_autoinclusion klass
+ # @note changes made to this methods to support backwards
+ # compatibility with {#enable_tracing_on}. This will become
+ # much simpler when that method is removed.
def klass.method_missing(method_name, *args, &block)
- if method_name.to_s =~ /^enable_tracing_on$/
+ if method_name.to_s =~ /^enable_tracing_(on|for)$/
instance_eval { include Peekaboo }
- enable_tracing_on *args
+ __send__ method_name, *args
else
super
end
end
end
- # Takes a class object and method name, aliases the original method,
- # and redefines the method with injected tracing.
- #
- # @note Should I add execution time to tracing? Configurable?
- #
- # @param [Class] klass method owner
- # @param [Symbol] name method to trace
- def wrap_method klass, name
+ # @private
+ def wrap klass, method_name, target
return if @_adding_a_method
- @_adding_a_method = true
-
- original_method = "original_#{name}"
+ begin
+ @_adding_a_method = true
+ original_method = "original_#{method_name}"
+ case target
+ when :singleton then wrap_singleton_method klass, method_name, original_method
+ when :instance then wrap_instance_method klass, method_name, original_method
+ else raise 'Only :class and :instance are valid targets'
+ end
+ rescue => exe
+ raise exe
+ ensure
+ @_adding_a_method = false
+ end
+ end
+
+ private
+
+ def wrap_instance_method klass, method_name, original_method_name
method_wrapping = %{
- alias_method :#{original_method}, :#{name}
- def #{name} *args, &block
- trace = "\#{caller(1)[0]}\n\t( Invoking: #{klass}\##{name} with \#{args.inspect} "
+ alias_method :#{original_method_name}, :#{method_name}
+ def #{method_name} *args, &block
+ trace = "\#{caller(1)[0]}\n\t( Invoking: #{klass}\##{method_name} with \#{args.inspect} "
begin
- result = #{original_method} *args, &block
+ result = #{original_method_name} *args, &block
trace << "==> Returning: \#{result.inspect} )"
result
rescue Exception => exe
trace << "!!! Raising: \#{exe.message.inspect} )"
raise exe
@@ -75,52 +99,30 @@
Peekaboo.configuration.tracer.info trace
end
end
}
klass.class_eval method_wrapping
-
- @_adding_a_method = false
end
- end
-
- # Contains methods added to every class that includes the *Peekaboo* module,
- # either through _direct_ or _auto_ inclusion.
- module SingletonMethods
- # @return [Array<Symbol>]
- # a list of instance methods that are being traced inside calling class
- def peek_list
- self::PEEKABOO_METHOD_LIST
- end
- # Enables instance method tracing on calling class.
- #
- # @example Trace a couple of methods
- # class SomeClass
- # include Peekaboo
- #
- # def method1; end
- # def method2; end
- # def method3; end
- # end
- #
- # # Tracing will be performed on method1(), method2(), but NOT method3()
- # SomeClass.enable_tracing_on :method1, :method2
- #
- # @param [*Symbol] method_names
- # the list of methods that you want to trace
- # @raise [RuntimeError]
- # when attempting to add a method that is already being traced
- def enable_tracing_on *method_names
- include Peekaboo unless @_hooked_by_peekaboo
-
- method_names.each do |method_name|
- unless peek_list.include? method_name
- peek_list << method_name
- method_list = self.instance_methods(false).map(&:to_sym)
- Peekaboo.wrap_method self, method_name if method_list.include? method_name
- else
- raise "Already tracing `#{method_name}'"
+ def wrap_singleton_method klass, method_name, original_method_name
+ method_wrapping = %{
+ class << self
+ alias_method :#{original_method_name}, :#{method_name}
+ def #{method_name} *args, &block
+ trace = "\#{caller(1)[0]}\n\t( Invoking: #{klass}.#{method_name} with \#{args.inspect} "
+ begin
+ result = #{original_method_name} *args, &block
+ trace << "==> Returning: \#{result.inspect} )"
+ result
+ rescue Exception => exe
+ trace << "!!! Raising: \#{exe.message.inspect} )"
+ raise exe
+ ensure
+ Peekaboo.configuration.tracer.info trace
+ end
+ end
end
- end
+ }
+ klass.instance_eval method_wrapping
end
end
end