KagemushaでActiveRecordのfindなどのクラスメソッドをswap
しようとすると、
pre>>
singleton method bound for a different object (TypeError)
<<--
というエラーが発生するという問題がありました。
- rubyneko - KagemushaとActiveRecordの不具合
- KagemushaとActiveRecordの不具合(2)
- Non free as in air - KagemushaとActiveRecordの不具合(2)
このままでは困るので、とりあえず動くようにしてみました。
Unboundにしなければ問題は起こらないと思ったので、instance_methodの代わりにalias_methodを使っています。
kagemusha_core_ext.rb
ruby>>
require 'uuidtools'
class Kagemusha #:nodoc:
def swap #:nodoc:
original_class_methods = {}
original_instance_methods = {}
@class_methods.each { |name, proc|
if proc
begin
# replace method
#method = @meta.instance_method(name)
method = random_name
@meta.instance_eval { alias_method method, name }
@meta.instance_eval { define_method(name, proc) }
original_class_methods[name] = method
rescue NameError
# insert method
@meta.instance_eval { define_method(name, proc) }
original_class_methods[name] = false
end
else
begin
# remove method
#method = @meta.instance_method(name)
method = random_name
@meta.instance_eval { alias_method method, name }
@meta.instance_eval { undef_method(name) }
original_class_methods[name] = method
rescue NameError
# nop
end
end
}
@instance_methods.each { |name, proc|
if proc
begin
# replace method
method = @klass.instance_method(name)
@klass.instance_eval { define_method(name, proc) }
original_instance_methods[name] = method
rescue NameError
# insert method
@klass.instance_eval { define_method(name, proc) }
original_instance_methods[name] = false
end
else
begin
# remove method
method = @klass.instance_method(name)
@klass.instance_eval { undef_method(name) }
original_instance_methods[name] = method
rescue NameError
# nop
end
end
}
return yield
ensure
original_class_methods.each { |name, method|
if method
# replace method
#@meta.instance_eval { define_method(name, method) }
@meta.instance_eval { alias_method name, method }
@meta.instance_eval { undef_method method }
else
# remove method
@meta.instance_eval { undef_method(name) }
end
}
original_instance_methods.each { |name, method|
if method
# replace method
@klass.instance_eval { define_method(name, method) }
else
# remove method
@klass.instance_eval { undef_method(name) }
end
}
end
private
def random_name
[UUID.random_128].pack("m").tr("=\n", '')
end
end
<<--
不具合のご検討、ありがとうございます。
違う方法ではありますが、修正を入れてみました。
もしよろしければ、trunk版をお試し頂けると幸いです。
http://kagemusha.rubyforge.org/svn/trunk/kagemusha/lib/kagemusha/core.rb
Specのカバレッジを上げるのに重宝しています。