class Aspector::Interception

Constants

METHOD_TEMPLATE

Attributes

aspect[R]
options[R]
target[R]

Public Class Methods

new(aspect, target, options) click to toggle source
# File lib/aspector/interception.rb, line 7
def initialize aspect, target, options
  @aspect  = aspect
  @target  = target
  @options = options
  @wrapped_methods = {}
end

Public Instance Methods

advices() click to toggle source
# File lib/aspector/interception.rb, line 18
def advices
  @aspect.class.advices
end
apply() click to toggle source
# File lib/aspector/interception.rb, line 26
def apply
  invoke_deferred_logics
  define_methods_for_advice_blocks
  add_to_instances unless @options[:existing_methods_only]
  apply_to_methods unless @options[:new_methods_only]
  add_method_hooks unless @options[:existing_methods_only]
  # TODO: clear deferred logic results if they are not used in any advice
  return
end
apply_to_method(method, scope = nil) click to toggle source
# File lib/aspector/interception.rb, line 68
def apply_to_method method, scope = nil
  filtered_advices = filter_advices advices, method
  return if filtered_advices.empty?

  logger.debug 'apply-to-method', method

  scope ||=
      if context.private_instance_methods.include?(method.to_sym)
        :private
      elsif context.protected_instance_methods.include?(method.to_sym)
        :protected
      else
        :public
      end

  recreate_method method, filtered_advices, scope
end
apply_to_methods() click to toggle source
# File lib/aspector/interception.rb, line 36
def apply_to_methods
  return if advices.empty?

  # If method/methods option is set and all are String or Symbol, apply to those only, instead of
  # iterating through all methods
  methods = [@options[:method] || @options[:methods]]
  methods.compact!
  methods.flatten!

  if not methods.empty? and methods.all?{|method| method.is_a? String or method.is_a? Symbol }
    methods.each do |method|
      apply_to_method(method.to_s)
    end

    return
  end

  context.public_instance_methods.each do |method|
    apply_to_method(method.to_s, :public)
  end

  context.protected_instance_methods.each do |method|
    apply_to_method(method.to_s, :protected)
  end

  if @options[:private_methods]
    context.private_instance_methods.each do |method|
      apply_to_method(method.to_s, :private)
    end
  end
end
disabled?() click to toggle source
# File lib/aspector/interception.rb, line 14
def disabled?
  @aspect.disabled?
end
logger() click to toggle source
# File lib/aspector/interception.rb, line 22
def logger
  @logger ||= Logging.get_logger(self)
end

Private Instance Methods

add_method_hooks() click to toggle source
# File lib/aspector/interception.rb, line 137
def add_method_hooks
  return if advices.empty?

  if @options[:class_methods]
    return unless @target.is_a?(Module)

    eigen_class = class << @target; self; end
    orig_singleton_method_added = @target.method(:singleton_method_added)

    eigen_class.send :define_method, :singleton_method_added do |method|
      aop_singleton_method_added(method) do
        orig_singleton_method_added.call(method)
      end
    end
  else
    eigen_class = class << @target; self; end

    if @target.is_a? Module
      orig_method_added = @target.method(:method_added)
    else
      orig_method_added = eigen_class.method(:method_added)
    end

    eigen_class.send :define_method, :method_added do |method|
      aop_method_added(method) do
        orig_method_added.call(method)
      end
    end
  end
end
add_to_instances() click to toggle source
# File lib/aspector/interception.rb, line 126
def add_to_instances
  return if advices.empty?

  aspect_instances = context.instance_variable_get(:@aop_instances)
  unless aspect_instances
    aspect_instances = AspectInstances.new
    context.instance_variable_set(:@aop_instances, aspect_instances)
  end
  aspect_instances << self
end
context() click to toggle source

context is where advices will be applied (i.e. where methods are modified), can be different from target

# File lib/aspector/interception.rb, line 97
def context
  return @target if @target.is_a?(Module) and not @options[:class_methods]

  class << @target
    self
  end
end
deferred_logic_results(logic) click to toggle source
# File lib/aspector/interception.rb, line 88
def deferred_logic_results logic
  @deferred_logic_results[logic]
end
define_methods_for_advice_blocks() click to toggle source
# File lib/aspector/interception.rb, line 117
def define_methods_for_advice_blocks
  advices.each do |advice|
    next if advice.raw?
    next unless advice.advice_block
    context.send :define_method, advice.with_method, advice.advice_block
    context.send :private, advice.with_method
  end
end
filter_advices(advices, method) click to toggle source
# File lib/aspector/interception.rb, line 168
def filter_advices advices, method
  advices.select do |advice|
    advice.match?(method, self)
  end
end
get_wrapped_method_of(method) click to toggle source
# File lib/aspector/interception.rb, line 92
def get_wrapped_method_of method
  @wrapped_methods[method]
end
invoke_deferred_logics() click to toggle source
# File lib/aspector/interception.rb, line 105
def invoke_deferred_logics
  return unless (logics = @aspect.class.send :_deferred_logics_)

  logics.each do |logic|
    result = logic.apply context, aspect
    if advices.detect {|advice| advice.use_deferred_logic? logic }
      @deferred_logic_results ||= {}
      @deferred_logic_results[logic] = result
    end
  end
end
recreate_method(method, advices, scope) click to toggle source
# File lib/aspector/interception.rb, line 174
def recreate_method method, advices, scope
  context.instance_variable_set(:@aop_creating_method, true)

  raw_advices = advices.select {|advice| advice.raw? }

  if raw_advices.size > 0
    raw_advices.each do |advice|
      if @target.is_a? Module and not @options[:class_methods]
        @target.class_exec method, self, &advice.advice_block
      else
        @target.instance_exec method, self, &advice.advice_block
      end
    end

    return if raw_advices.size == advices.size
  end

  begin
    @wrapped_methods[method] = context.instance_method(method)
  rescue
    # ignore undefined method error
    if @options[:existing_methods_only]
      logger.log Logging::WARN, 'method-not-found', method
    end

    return
  end

  before_advices = advices.select {|advice| advice.before? || advice.before_filter? }
  after_advices  = advices.select {|advice| advice.after?  }
  around_advices = advices.select {|advice| advice.around? }

  (around_advices.size - 1).downto(1) do |i|
    advice = around_advices[i]
    recreate_method_with_advices method, [], [], advice
  end

  recreate_method_with_advices method, before_advices, after_advices, around_advices.first, true

  context.send scope, method if scope != :public
ensure
  context.send :remove_instance_variable, :@aop_creating_method
end
recreate_method_with_advices(method, before_advices, after_advices, around_advice, is_outermost = false) click to toggle source
# File lib/aspector/interception.rb, line 218
def recreate_method_with_advices method, before_advices, after_advices, around_advice, is_outermost = false
  aspect = @aspect
  logger = @logger
  interception = self
  orig_method = get_wrapped_method_of method

  code = METHOD_TEMPLATE.result(binding)
  logger.debug 'generate-code', method, code
  context.class_eval code, __FILE__, __LINE__ + 4
end