class Sqreen::Graft::HookPoint

Constants

DEFAULT_STRATEGY

Attributes

klass_name[R]
method_kind[R]
method_name[R]

Public Class Methods

new(hook_point, strategy = DEFAULT_STRATEGY) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 41
def initialize(hook_point, strategy = DEFAULT_STRATEGY)
  @klass_name, @method_kind, @method_name = Sqreen::Graft::HookPoint.parse(hook_point)
  @strategy = strategy
end
parse(hook_point) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 28
def self.parse(hook_point)
  klass_name, separator, method_name = hook_point.split(/(\#|\.)/, 2)

  raise ArgumentError, hook_point if klass_name.nil? || separator.nil? || method_name.nil?
  raise ArgumentError, hook_point unless ['.', '#'].include?(separator)

  method_kind = separator == '.' ? :klass_method : :instance_method

  [klass_name.to_sym, method_kind, method_name.to_sym]
end

Public Instance Methods

apply(obj, suffix, *args, &block) click to toggle source
# File lib/sqreen/graft/hook_point.ruby_2.rb, line 11
def apply(obj, suffix, *args, &block)
  raise 'use super' if super?

  obj.send("#{method_name}_without_#{suffix}", *args, &block)
end
disable(key) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 156
def disable(key)
  case @strategy
  when :chain
    unchain(key)
  when :prepend
    unoverride(key)
  end
end
disabled?(key) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 165
def disabled?(key)
  case @strategy
  when :chain
    !chained?(key)
  when :prepend
    !overridden?(key)
  end
end
enable(key) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 147
def enable(key)
  case @strategy
  when :chain
    chain(key)
  when :prepend
    raise HookPointError, "enable called on prepend mode"
  end
end
exist?() click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 50
def exist?
  return false unless Sqreen::Dependency.const_exist?(@klass_name)

  if klass_method?
    (klass.singleton_class.public_instance_methods(false) + klass.singleton_class.protected_instance_methods(false) + klass.singleton_class.private_instance_methods(false)).include?(@method_name)
  elsif instance_method?
    (klass.public_instance_methods(false) + klass.protected_instance_methods(false) + klass.private_instance_methods(false)).include?(@method_name)
  else
    Sqreen::Graft.logger.error { "[#{Process.pid}] #{self} unknown hook point kind" }
    raise HookPointError, "#{self} unknown hook point kind"
  end
end
install(key, &block) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 108
def install(key, &block)
  if installed?(key)
    Sqreen::Graft.logger.debug { "[#{Process.pid}] #{self} already installed" }
    return
  end
  unless exist?
    Sqreen::Graft.logger.debug { "[#{Process.pid}] #{self} hook point not found" }
    return
  end

  case @strategy
  when :chain
    define(key, &block)
    chain(key)
  when :prepend
    prepend(key)
    override(key, &block)
  end
end
installed?(key) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 97
def installed?(key)
  case @strategy
  when :chain then defined(key)
  when :prepend then prepended?(key) && overridden?(key)
  end
end
instance_method?() click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 93
def instance_method?
  @method_kind == :instance_method
end
klass() click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 63
def klass
  Sqreen::Dependency.resolve_const(@klass_name)
end
klass_method?() click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 67
def klass_method?
  @method_kind == :klass_method
end
private_method?() click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 71
def private_method?
  if klass_method?
    klass.private_methods.include?(@method_name)
  elsif instance_method?
    klass.private_instance_methods.include?(@method_name)
  else
    Sqreen::Graft.logger.error { "[#{Process.pid}] #{self} unknown hook point kind" }
    raise HookPointError, "#{self} unknown hook point kind"
  end
end
protected_method?() click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 82
def protected_method?
  if klass_method?
    klass.protected_methods.include?(@method_name)
  elsif instance_method?
    klass.protected_instance_methods.include?(@method_name)
  else
    Sqreen::Graft.logger.error { "[#{Process.pid}] #{self} unknown hook point kind" }
    raise HookPointError, "#{self} unknown hook point kind"
  end
end
super?() click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 104
def super?
  @strategy == :prepend
end
to_s() click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 46
def to_s
  @to_s ||= "#{@klass_name}#{@method_kind == :instance_method ? '#' : '.'}#{@method_name}"
end
uninstall(key) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 128
def uninstall(key)
  unless installed?(key)
    Sqreen::Graft.logger.debug { "[#{Process.pid}] #{self} not installed" }
    return
  end
  unless exist?
    Sqreen::Graft.logger.debug { "[#{Process.pid}] #{self} hook point not found" }
    return
  end

  case @strategy
  when :chain
    disable(key)
    remove(key)
  when :prepend
    unoverride(key) if overridden?(key)
  end
end

Private Instance Methods

chain(suffix) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 307
def chain(suffix)
  method_name = @method_name

  if klass_method?
    klass.singleton_class.instance_eval do
      alias_method :"#{method_name}_without_#{suffix}", :"#{method_name}"
      alias_method :"#{method_name}", :"#{method_name}_with_#{suffix}"
    end
  elsif instance_method?
    klass.class_eval do
      alias_method :"#{method_name}_without_#{suffix}", :"#{method_name}"
      alias_method :"#{method_name}", :"#{method_name}_with_#{suffix}"
    end
  else
    raise HookPointError, 'unknown hook point kind'
  end
end
chained?(suffix) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 291
def chained?(suffix)
  method_name = @method_name

  if klass_method?
    klass.singleton_class.instance_eval do
      instance_method(:"#{method_name}").original_name == :"#{method_name}_with_#{suffix}"
    end
  elsif instance_method?
    klass.class_eval do
      instance_method(:"#{method_name}").original_name == :"#{method_name}_with_#{suffix}"
    end
  else
    raise HookPointError, 'unknown hook point kind'
  end
end
define(suffix, &block) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 242
def define(suffix, &block)
  hook_point = self
  method_name = @method_name

  if klass_method?
    klass.singleton_class.instance_eval do
      if hook_point.private_method?
        private
      elsif hook_point.protected_method?
        protected
      else
        public
      end

      define_method(:"#{method_name}_with_#{suffix}", &block)
    end
  elsif instance_method?
    klass.class_eval do
      if hook_point.private_method?
        private
      elsif hook_point.protected_method?
        protected
      else
        public
      end

      define_method(:"#{method_name}_with_#{suffix}", &block)
    end
  else
    raise HookPointError, 'unknown hook point kind'
  end
end
defined(suffix) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 231
def defined(suffix)
  if klass_method?
    (klass.methods + klass.protected_methods + klass.private_methods).include?(:"#{method_name}_with_#{suffix}")
  elsif instance_method?
    (klass.instance_methods + klass.protected_instance_methods + klass.private_instance_methods).include?(:"#{method_name}_with_#{suffix}")
  else
    Sqreen::Graft.logger.error { "[#{Process.pid}] #{self} unknown hook point kind" }
    raise HookPointError, "#{self} unknown hook point kind"
  end
end
hook_spot(key) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 176
def hook_spot(key)
  target = klass_method? ? klass.singleton_class : klass
  mod = target.ancestors.each { |e| break if e == target; break(e) if e.class == HookSpot && e.key == key }
  raise "Inconsistency detected: #{target} missing from its own ancestors" if mod.is_a?(Array)

  [target, mod]
end
overridden?(key) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 198
def overridden?(key)
  _, mod = hook_spot(key)

  (mod.instance_methods(false) + mod.protected_instance_methods(false) + mod.private_instance_methods(false)).include?(method_name)
end
override(key, &block) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 204
def override(key, &block)
  hook_point = self
  method_name = @method_name

  _, mod = hook_spot(key)

  mod.instance_eval do
    if hook_point.private_method?
      private
    elsif hook_point.protected_method?
      protected
    else
      public
    end

    define_method(:"#{method_name}", &block)
  end
end
prepend(key) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 184
def prepend(key)
  target, mod = hook_spot(key)

  mod ||= HookSpot.new(key)

  target.instance_eval { prepend(mod) }
end
prepended?(key) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 192
def prepended?(key)
  _, mod = hook_spot(key)

  mod != nil
end
remove(suffix) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 275
def remove(suffix)
  method_name = @method_name

  if klass_method?
    klass.singleton_class.instance_eval do
      remove_method(:"#{method_name}_with_#{suffix}")
    end
  elsif instance_method?
    klass.class_eval do
      remove_method(:"#{method_name}_with_#{suffix}")
    end
  else
    raise HookPointError, 'unknown hook point kind'
  end
end
unchain(suffix) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 325
def unchain(suffix)
  method_name = @method_name

  if klass_method?
    klass.singleton_class.instance_eval do
      alias_method :"#{method_name}", :"#{method_name}_without_#{suffix}"
    end
  elsif instance_method?
    klass.class_eval do
      alias_method :"#{method_name}", :"#{method_name}_without_#{suffix}"
    end
  end
end
unoverride(key) click to toggle source
# File lib/sqreen/graft/hook_point.rb, line 223
def unoverride(key)
  method_name = @method_name

  _, mod = hook_spot(key)

  mod.instance_eval { remove_method(method_name) }
end