L
Logan Capaldo
I hammered this out today. It has some limitations (works only for
classes, not modules), and when using the block only form of patch
your method redefinitions are evaluated twice. I'm looking for
commentary before I take the step of writing some testcases and
packaging it up for human consumption:
Usage:
class A
def b(x, y)
x + y
end
end
a = A.new
require 'patch'
class A
include Patchable
end
A.patchb) do |y|
hyper(1, y) # I only ever call b with an x argument of 1
end
a.b(5)
#=> 6
class B
def foof
yield
end
end
class B
include Patchable
end
B.patch do
def foof
hyper { yield + 1 }
end
end
b = B.new
b.foof { 3 }
#=> 4
And heres the code:
patch.rb:
module Patchable
module ClassMethods
def patch(method_name = nil, &new_body)
if method_name
method_name = method_name.to_sym
pre_patched_versions[method_name] = instance_method
(method_name)
define_method(method_name, &new_body)
else
klass = Class.new
imeths = klass.instance_methods
klass.class_eval(&new_body)
new_meths = klass.instance_methods - imeths
new_meths.each do |m|
pre_patched_versions[m.to_sym] = instance_method(m)
end
class_eval(&new_body)
end
self
end
def pre_patched_versions
@pre_patched_versions ||= {}
end
end
def hyper(*args, &block)
meth_name = caller[0][/`([^']+)'/, 1].to_sym
self.class.pre_patched_versions[meth_name].bind(self).call
(*args, &block)
end
def self.included(other)
other.extend(ClassMethods)
end
end
classes, not modules), and when using the block only form of patch
your method redefinitions are evaluated twice. I'm looking for
commentary before I take the step of writing some testcases and
packaging it up for human consumption:
Usage:
class A
def b(x, y)
x + y
end
end
a = A.new
require 'patch'
class A
include Patchable
end
A.patchb) do |y|
hyper(1, y) # I only ever call b with an x argument of 1
end
a.b(5)
#=> 6
class B
def foof
yield
end
end
class B
include Patchable
end
B.patch do
def foof
hyper { yield + 1 }
end
end
b = B.new
b.foof { 3 }
#=> 4
And heres the code:
patch.rb:
module Patchable
module ClassMethods
def patch(method_name = nil, &new_body)
if method_name
method_name = method_name.to_sym
pre_patched_versions[method_name] = instance_method
(method_name)
define_method(method_name, &new_body)
else
klass = Class.new
imeths = klass.instance_methods
klass.class_eval(&new_body)
new_meths = klass.instance_methods - imeths
new_meths.each do |m|
pre_patched_versions[m.to_sym] = instance_method(m)
end
class_eval(&new_body)
end
self
end
def pre_patched_versions
@pre_patched_versions ||= {}
end
end
def hyper(*args, &block)
meth_name = caller[0][/`([^']+)'/, 1].to_sym
self.class.pre_patched_versions[meth_name].bind(self).call
(*args, &block)
end
def self.included(other)
other.extend(ClassMethods)
end
end