Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Monkey Patcher
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
You can’t perform that action at this time.