class Peeek::Hook

Constants

INSTANCE_METHOD_PREFIXES
METHOD_PREFIXES
SINGLETON_METHOD_PREFIXES

Attributes

calls[R]

@attribute [r] calls @return [Peeek::Calls] calls to the method that the hook captured

method_name[R]

@attribute [r] method_name @return [Symbol] method name of the object

object[R]

@attribute [r] object @return [Module, Class, Object] a target object that hook

Public Class Methods

any_instance?(object) click to toggle source

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
any_module?(object) click to toggle source

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(object, method_spec, &process) click to toggle source

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
new(object, method_name, linker_class, &process) click to toggle source

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

parse(method_spec) click to toggle source
# 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

defined?() click to toggle source

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
inspect() click to toggle source
# 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
instance?() click to toggle source

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
linked?() click to toggle source

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
singleton?() click to toggle source

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
to_s() click to toggle source
# File lib/peeek/hook.rb, line 139
def to_s
  @object.inspect + @linker.method_prefix + @method_name.to_s
end

Private Instance Methods

call(backtrace, receiver, args, block) click to toggle source
# 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