Ruby: Alter class static method in a code block

一世执手 提交于 2020-01-04 05:17:07

问题


Given the Thread class with it current method. Now inside a test, I want to do this:

def test_alter_current_thread
  Thread.current = a_stubbed_method
  # do something that involve the work of Thread.current
  Thread.current = default_thread_current
end

Basically, I want to alter the method of a class inside a test method and recover it after that. I know it sound complex for another language, like Java & C# (in Java, only powerful mock framework can do it). But it's ruby and I hope such nasty stuff would be available


回答1:


You might want to take a look at a Ruby mocking framework like Mocha, but in terms of using plain Ruby it can be done using alias_method (documentation here) e.g.

beforehand:

class Thread
  class << self
    alias_method :old_current, :current
  end
end

then define your new method

class Thread
  def self.current
    # implementation here
  end
end

then afterwards restore the old method:

class Thread
  class << self
    alias_method :current, :old_current
  end
end

Update to illustrate doing this from within a test

If you want to do this from within a test you could define some helper methods as follows:

def replace_class_method(cls, meth, new_impl)
  cls.class_eval("class << self; alias_method :old_#{meth}, :#{meth}; end")
  cls.class_eval(new_impl)
end

def restore_class_method(cls, meth)
  cls.class_eval("class << self; alias_method :#{meth}, :old_#{meth}; end")
end

replace_class_method is expecting a class constant, the name of a class method and the new method definition as a string. restore_class_method takes the class and the method name and then aliases the original method back in place.

Your test would then be along the lines of:

def test
  new_impl = <<EOM
  def self.current
    "replaced!"
  end
EOM
  replace_class_method(Thread, 'current', s)
  puts "Replaced method call: #{Thread.current}"
  restore_class_method(Thread, 'current')
  puts "Restored method call: #{Thread.current}"
end

You could also write a little wrapper method which would replace a method, yield to a block and then ensure that the original method was reinstated afterwards e.g.

def with_replaced_method(cls, meth, new_impl)
    replace_class_method(cls, meth, new_impl)
    begin
        result = yield
    ensure
        restore_class_method(cls, meth)
    end
    return result
end

Inside your test method this could then be used as:

with_replaced_method(Thread, 'current', new_impl) do
  # test code using the replaced method goes here
end
# after this point the original method definition is restored

As mentioned in the original answer, you can probably find a framework to do this for you but hopefully the above code is interesting and useful anyway.



来源:https://stackoverflow.com/questions/3014048/ruby-alter-class-static-method-in-a-code-block

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!