-
-
Save jbowles/1406739 to your computer and use it in GitHub Desktop.
Monkey Patcher
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MONKEY_PATCHES = {} | |
module MonkeyPatcher | |
begin | |
errors = [] | |
port = Mongo::Connection::DEFAULT_PORT | |
begin | |
db_conn = Mongo::Connection.new('host', port).db('db') | |
db_error = Mongo::Connection.new('host', port).db('db') | |
db_error = {"created_at_local" => "#{Time.now}", "created_at_utc" => "#{Time.now.utc}", "error_message" => "was error of some sort connecting to the proper environment"} | |
coll_db_error = db["db_error"] | |
coll_db_error.insert(db_error) | |
rescue Exception => e | |
puts msg = "Mongo Connection Error for #{RAILS_ENV}: #{db_conn.name}\n#{e}\n#{e.backtrace.join("\n")}" | |
errors << msg | |
end | |
if !errors.empty? | |
Pony.mail( | |
:to => 'bar.com', | |
:from => 'foob.com', | |
:subject => "ERRORS connecting to MongoDB! #{Time.now}", | |
:body => errors.join("\n\n") | |
) | |
end | |
rescue Exception => e | |
Pony.mail( | |
:to => 'bar.com', | |
:from => 'foob.com', | |
:subject => "FAILED to connect to MongoDB #{Time.now}", | |
:body => "#{e}\n\n#{e.backtrace.join("\n")}" | |
) | |
end | |
# Indicates if this Object is either a Class or Module. | |
def definition? | |
is_a?(Class) || is_a?(Module) | |
end | |
# A Hash of all monkey patches that have been applied directly to this object. | |
def monkey_patches | |
patch_context.instance_eval { @monkey_patches ||= {} } | |
end | |
# Returns all monkey patches that have been applied to this Object and its definition. | |
def all_monkey_patches(method_name=nil) | |
if method_name | |
list = [] | |
finder = Proc.new do |context, patches| | |
patches.each do |patch| | |
list << patch if patch[:method] == method_name | |
end | |
end | |
self.class.monkey_patches.each &finder | |
patch_context.monkey_patches.each &finder | |
list | |
else | |
hash = self.class.monkey_patches.merge(patch_context.monkey_patches) | |
hash.to_a.map(&:last).flatten | |
end | |
end | |
# Returns this Object's eigenclass. | |
def eigenclass | |
class << self | |
self | |
end | |
end | |
# Indicates if this Object is an eigenclass. | |
def eigenclass? | |
definition? && name.nil? | |
end | |
# Returns the context to use for monkey patching. | |
def patch_context | |
return self if definition? | |
eigenclass | |
end | |
# Returns the context name used for monkey patching. | |
def patch_context_name | |
return superclass.name if eigenclass? | |
return name if definition? | |
"#{self.class.name}_#{object_id}" | |
end | |
# Indicates whether or not this Object has been monkey patched. | |
def patched?(method_name=nil) | |
list = all_monkey_patches(method_name) | |
!list.empty? | |
end | |
#Some psuedo code for now | |
def revision | |
#cd RAILS_ROOT/.git | |
#version = get HEAD | |
version | |
end | |
# Monkey patches an existing method, overriding its behavior while preserving the ability to invoke all previous definitions. | |
def monkey_patch(method_name, &block) | |
key = patch_context_name | |
pointer_method_name = "#{method_name}_#{block.object_id}" | |
patch_context.instance_eval do | |
patched_by = nil | |
caller.each do |line| | |
next if line.include?(__FILE__) | |
patched_by = line | |
break | |
end | |
patch_info = { | |
:context => key, | |
:method => method_name, | |
:orig_method => pointer_method_name, | |
:patch_stack => caller, | |
#Psuedo code for now. | |
:revision => revision | |
} | |
# keep a record of what has been monkey patched on this object | |
monkey_patches[key] ||= [] | |
monkey_patches[key] << patch_info | |
# keep a record of all Class/Module patches at the top level | |
# this allows us to easily see all patches applied in the current process | |
unless eigenclass? | |
MONKEY_PATCHES[key] ||= [] | |
MONKEY_PATCHES[key] << patch_info | |
end | |
alias_method pointer_method_name, method_name | |
define_method method_name, &block | |
end | |
monkey_patch_updates = { :patch_info => patch_info } | |
collection_monkey_patch = db_conn["monkey_patches"] | |
collection_monkey_patch.insert(monkey_patch_updates) | |
end | |
# Invokes the unpatched version of a method. | |
def unpatched(name, *args) | |
return send(name, *args) unless patched?(name) | |
send(all_monkey_patches(name).first[:orig_method], *args) | |
end | |
end | |
Object.send(:include, MonkeyPatcher) | |
# Examples of how to use this thing... | |
class Foo | |
def bar | |
:bar | |
end | |
end | |
f = Foo.new | |
f.bar # => :bar | |
f.monkey_patch(:bar) { :instance_bar } | |
f.bar # => :instance_bar | |
f.unpatched(:bar) # => :bar | |
MONKEY_PATCHES # => {} | |
Foo.monkey_patches # => {} | |
f.monkey_patches # => {"Foo_2168605280"=> | |
# [{:context=>"Foo_2168605280", | |
# :method=>:bar, | |
# :orig_method=>"bar_2153351500", | |
# :patched_by=>"(pry):3:in `irb_binding'"}]} | |
f.public_methods.select {|m| m =~ /bar/} # => [:bar_2153351500, :bar] | |
Foo.monkey_patch(:bar) { :class_bar } | |
f = Foo.new | |
f.bar # => :class_bar | |
f.unpatched(:bar) # => :bar | |
f.monkey_patch(:bar) { :instance_bar } | |
f.bar # => :instance_bar | |
f.unpatched(:bar) # => :bar | |
MONKEY_PATCHES # => {"Foo"=> | |
# [{:context=>"Foo", | |
# :method=>:bar, | |
# :orig_method=>"bar_2181857840", | |
# :patched_by=>"(pry):10:in `irb_binding'"}]} | |
Foo.monkey_patches # => {"Foo"=> | |
# [{:context=>"Foo", | |
# :method=>:bar, | |
# :orig_method=>"bar_2181857840", | |
# :patched_by=>"(pry):10:in `irb_binding'"}]} | |
f.monkey_patches # => {"Foo_2181947720"=> | |
# [{:context=>"Foo_2181947720", | |
# :method=>:bar, | |
# :orig_method=>"bar_2183922780", | |
# :patched_by=>"(pry):14:in `irb_binding'"}]} | |
f.public_methods.select {|m| m =~ /bar/} # => [:bar_2183922780, :bar, :bar_2181857840] | |
Foo.new.public_methods.select {|m| m =~ /bar/} # => [:bar, :bar_2181857840] | |
Foo.all_monkey_patches # => [{:context=>"Foo", | |
# :method=>:bar, | |
# :orig_method=>"bar_2181857840", | |
# :patched_by=>"(pry):10:in `irb_binding'"}] | |
Foo.all_monkey_patches(:bar) # => [{:context=>"Foo", | |
# :method=>:bar, | |
# :orig_method=>"bar_2181857840", | |
# :patched_by=>"(pry):10:in `irb_binding'"}] | |
f.all_monkey_patches # => [{:context=>"Foo", | |
# :method=>:bar, | |
# :orig_method=>"bar_2181857840", | |
# :patched_by=>"(pry):10:in `irb_binding'"}, | |
# {:context=>"Foo_2181947720", | |
# :method=>:bar, | |
# :orig_method=>"bar_2183922780", | |
# :patched_by=>"(pry):14:in `irb_binding'"}] | |
f.all_monkey_patches(:bar) # => [{:context=>"Foo", | |
# :method=>:bar, | |
# :orig_method=>"bar_2181857840", | |
# :patched_by=>"(pry):10:in `irb_binding'"}, | |
# {:context=>"Foo_2181947720", | |
# :method=>:bar, | |
# :orig_method=>"bar_2183922780", | |
# :patched_by=>"(pry):14:in `irb_binding'"}] | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment