如何在Ruby中取消定义类?

时间:2012-07-16 11:43:22

标签: ruby ruby-on-rails-3 activerecord

在Ruby中取消定义方法非常简单,我可以使用undef METHOD_NAME

班级有类似的东西吗?我在MRI 1.9.2

我必须取消定义ActiveRecord模型,运行两行代码,然后将模型恢复为原始形式。

问题是,我有一个模型Contact,我正在使用公司的API,而且他们有一个名为Contact的类,并且更改我的模型名称对我来说将是很多工作。

在这种情况下我该怎么办?

2 个答案:

答案 0 :(得分:83)

>> class Foo; end
=> nil
>> Object.constants.include?(:Foo)
=> true
>> Object.send(:remove_const, :Foo)
=> Foo
>> Object.constants.include?(:Foo)
=> false
>> Foo
NameError: uninitialized constant Foo

编辑刚刚注意到您的编辑,删除常量可能不是实现您所需要的最佳方式。为什么不将Contact类之一移动到单独的命名空间中。

EDIT2 您也可以暂时重命名您的课程:

class Foo
  def bar
    'here'
  end
end

TemporaryFoo = Foo
Object.send(:remove_const, :Foo)
# do some stuff
Foo = TemporaryFoo
Foo.new.bar #=> "here"

同样,问题在于您仍然拥有较新的Contact课程,因此您必须再次删除它。我真的建议你的类名称间距。这也可以帮助您避免任何加载问题

答案 1 :(得分:1)

在类似的情况下 - 模拟我试图测试的另一个类在内部使用的类 - 我发现这是一个可行的解决方案:

describe TilesAuth::Communicator do
  class FakeTCPSocket
    def initialize(*_); end
    def puts(*_); end
  end

  context "when the response is SUCCESS" do
    before do
      class TilesAuth::Communicator::TCPSocket < FakeTCPSocket
        def gets; 'SUCCESS'; end
      end
    end
    after { TilesAuth::Communicator.send :remove_const, :TCPSocket }

    it "returns success" do
      communicator = TilesAuth::Communicator.new host: nil, port: nil, timeout: 0.2
      response = communicator.call({})
      expect(response["success"]).to eq(true)
      expect(response).not_to have_key("error")
      expect(response).not_to have_key("invalid_response")
    end
  end
end

我原本以为会有更好的方法来做到这一点 - 也就是说我无法找到一种方法来传递所需的返回值以供重用 - 但现在这似乎已经足够了。我是嘲笑/工厂的新手,我很乐意听到任何替代方法。

编辑:

好吧,毕竟不那么相似。

由于an excellent explanation in the RSpec Google Group

,我找到了使用RSpec模拟的更好方法
context "with socket response mocked" do
  let(:response) do
    tcp_socket_object = instance_double("TCPSocket", puts: nil, gets: socket_response)
    class_double("TCPSocket", new: tcp_socket_object).as_stubbed_const
    communicator = TilesAuth::Communicator.new host: nil, port: nil, timeout: 0.2
    communicator.call({})
  end

  context "as invalid JSON" do
    let(:socket_response) { 'test invalid json' }

    it "returns an error response including the invalid socket response" do
      expect(response["success"]).to eq(false)
      expect(response).to have_key("error")
      expect(response["invalid_response"]).to eq(socket_response)
    end
  end

  context "as SUCCESS" do
    let(:socket_response) { 'SUCCESS' }

    it "returns success" do
      expect(response["success"]).to eq(true)
      expect(response).not_to have_key("error")
      expect(response).not_to have_key("invalid_response")
    end
  end
end