query: tag:lib

KagemushaでActiveRecordのfindなどのクラスメソッドをswap
しようとすると、

pre>>
singleton method bound for a different object (TypeError)
<<--

というエラーが発生するという問題がありました。

このままでは困るので、とりあえず動くようにしてみました。
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
<<--

posted by genki genki on Sun 6 Jul 2008 at 02:28 with 2 comments