class Peeek::Hook
Constants
- INSTANCE_METHOD_PREFIXES
- METHOD_PREFIXES
- SINGLETON_METHOD_PREFIXES
Attributes
@attribute [r] calls @return [Peeek::Calls] calls to the method that the hook captured
@attribute [r] method_name
@return [Symbol] method name of the object
@attribute [r] object @return [Module, Class, Object] a target object that hook
Public Class Methods
Determine if an object is an instance of any class.
@param [Module, Class, Object] object an object @return whether an object is an instance of any class
# File lib/peeek/hook.rb, line 58 def self.any_instance?(object) !any_module?(object) end
Determine if an object is a module or a class.
@param [Module, Class, Object] object an object @return whether an object is a module or a class
# File lib/peeek/hook.rb, line 50 def self.any_module?(object) object.class == Module || object.class == Class end
Create a hook to method of an object. The hook can apply to a instance method or a singleton method.
@example Hook
to an instance method
Peeek::Hook.create(IO, '#puts') # => #<Peeek::Hook IO#puts> # Hook implicitly to the instance method if the object is a module or # a class. Peeek::Hook.create(IO, :puts) # => #<Peeek::Hook IO#puts> # Can't hook to the instance method if the object is an instance of any # class. Peeek::Hook.create($stdout, '#puts') # => raise #<ArgumentError: can't create a hook of instance method to an instance of any class>
@example Hook
to an singleton method
Peeek::Hook.create($stdout, '.puts') # => #<Peeek::Hook #<IO:<STDOUT>>.puts> # hook implicitly to the singleton method if the object is an instance # of any class. Peeek::Hook.create($stdout, :puts) # => #<Peeek::Hook #<IO:<STDOUT>>.puts>
@param [Module, Class, Object] object a target object that hook @param [String, Symbol] method_spec method specifier of the object @yield [call] process a call to the method. give optionally @yieldparam [Peeek::Call] call a call to the method @return [Peeek::Hook] a hook to the method of the object
# File lib/peeek/hook.rb, line 40 def self.create(object, method_spec, &process) linker_class, method_name = parse(method_spec) linker_class = any_module?(object) ? Instance : Singleton unless linker_class new(object, method_name, linker_class, &process) end
Initialize the hook.
@param [Module, Class, Object] object a target object that hook @param [Symbol] method_name
method name of the object @param [Class] linker_class class of an object to link the hook @yield [call] process a call to the method. give optionally @yieldparam [Peeek::Call] call a call to the method
# File lib/peeek/hook.rb, line 69 def initialize(object, method_name, linker_class, &process) raise ArgumentError, "invalid as linker class, #{Linker.classes.join(' or ')} are valid" unless Linker.classes.include?(linker_class) @object = object @method_name = method_name @linker = linker_class.new(object, method_name) @process = process @calls = Calls.new raise ArgumentError, "can't create a hook of instance method to an instance of any class" if self.class.any_instance?(object) and instance? end
Private Class Methods
# File lib/peeek/hook.rb, line 156 def self.parse(method_spec) method_spec = method_spec.to_s method_prefix = METHOD_PREFIXES.sort_by(&:length).reverse.find do |method_prefix| method_spec.start_with?(method_prefix) end return nil, method_spec.to_sym unless method_prefix linker_class = INSTANCE_METHOD_PREFIXES.include?(method_prefix) ? Instance : Singleton method_name = method_spec.to_s.sub(/^#{Regexp.quote(method_prefix || '')}/, '').to_sym [linker_class, method_name] end
Public Instance Methods
Determine if the method is defined in the object
@return whether the method is defined in the object
# File lib/peeek/hook.rb, line 108 def defined? @linker.defined? end
# File lib/peeek/hook.rb, line 143 def inspect state = [] state << 'linked' if linked? state_string = state.empty? ? '' : " (#{state * ', '})" "#<#{self.class} #{self}#{state_string}>" end
Determine if the hook to an instance method.
@return whether the hook to an instance method
# File lib/peeek/hook.rb, line 94 def instance? @linker.is_a?(Instance) end
Link the hook to the method.
# File lib/peeek/hook.rb, line 120 def link unless linked? @original_method = @linker.target_method @linker.link(&method(:call)) end self end
Determine if the hook is linked to the method
@return whether the hook is linked to the method
# File lib/peeek/hook.rb, line 115 def linked? instance_variable_defined?(:@original_method) end
Determine if the hook to a singleton method.
@return whether the hook to a singleton method
# File lib/peeek/hook.rb, line 101 def singleton? @linker.is_a?(Singleton) end
# File lib/peeek/hook.rb, line 139 def to_s @object.inspect + @linker.method_prefix + @method_name.to_s end
Unlink the hook from the method.
# File lib/peeek/hook.rb, line 130 def unlink if linked? @linker.unlink(@original_method) remove_instance_variable(:@original_method) end self end
Private Instance Methods
# File lib/peeek/hook.rb, line 174 def call(backtrace, receiver, args, block) method = @original_method.is_a?(UnboundMethod) ? @original_method.bind(receiver) : @original_method result = begin Call::ReturnValue.new(method[*args]) rescue Exception => e e.set_backtrace(backtrace) Call::Exception.new(e) end call = Call.new(self, backtrace, receiver, args, block, result) unless call.file =~ %r(lib/peeek(?:\.rb|/)) @calls << call @process[call] if @process end raise call.exception if call.raised? call.return_value end