kennethkalmer/powerdns-on-rails

View on GitHub
lib/hijacker.rb

Summary

Maintainability
A
1 hr
Test Coverage
# Hijacker class
#
# This class is used by RoleRequirementTestHelper to temporarily hijack a controller action for testing
#
# It can be used for other tests as well.
#
# You can contract the author with questions
#   Tim C. Harper - irb(main):001:0> ( 'tim_see_harperATgmail._see_om'.gsub('_see_', 'c').gsub('AT', '@') )
#
#
# Example usage:
#   hijacker = Hijacker.new(ListingsController)
#   hijacker.hijack_instance_method("index", "render :text => 'hello world!'" )
#   get :index        # will return "hello world"
#   hijacker.restore  # put things back the way you found it

class Hijacker
  def initialize(klass)
    @target_klass = klass
    @method_stores = {}
  end
  
  def hijack_class_method(method_name, eval_string = nil, arg_names = [], &block)
    hijack_method(class_self_instance, method_name, eval_string, arg_names, &block )
  end
  
  def hijack_instance_method(method_name, eval_string = nil, arg_names = [], &block)
    hijack_method(@target_klass, method_name, eval_string, arg_names, &block )
  end
  
  # restore all 
  def restore
    @method_stores.each_pair{|klass, method_stores|
      method_stores.reverse_each{ |method_name, method| 
        klass.send :undef_method, method_name
        klass.send :define_method, method_name, method if method
      }
    }
    @method_stores.clear
    true
  rescue
    false
  end
  
protected  

  def class_self_instance
    @target_klass.send :eval, "class << self; self; end;"
  end
  
  def hijack_method(klass, method_name, eval_string = nil, arg_names = [], &block)
    method_name = method_name.to_s
    # You have got love ruby!  What other language allows you to pillage and plunder a class like this? 
    
    (@method_stores[klass]||=[]) << [
      method_name, 
      klass.instance_methods.include?(method_name) && klass.instance_method(method_name)
    ]
    
    klass.send :undef_method, method_name
    if Symbol === eval_string
      klass.send :define_method, method_name, klass.instance_methods(eval_string)
    elsif String === eval_string
      klass.class_eval <<-EOF 
        def #{method_name}(#{arg_names * ','})
          #{eval_string}
        end
      EOF
    elsif block_given?
      klass.send :define_method, method_name, block
    end
    
    true
  rescue
    false
  end
  
end