【问题标题】:How to undefine class in Ruby?如何在 Ruby 中取消定义类?
【发布时间】:2012-07-15 06:09:14
【问题描述】:

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

一个类有什么类似的吗?我在MRI 1.9.2。

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

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

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

【问题讨论】:

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


    【解决方案1】:
    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 类,因此您必须再次删除它。我真的会建议为您的课程命名空间。这也将帮助您避免任何加载问题

    【讨论】:

    • 不,您可能需要从$LOADED_FEATURES 中删除contact.rb 文件,然后重新加载它,但这可能会产生一些副作用。我第一次写答案时没有注意到您的编辑,所以我更新了它。也就是说,您也许可以暂时重命名它。
    • 如何临时重命名?我将重命名,运行代码并将其重命名。听起来不错?
    • 检查我的更新,我再次不推荐这种方法,但可以这样做
    【解决方案2】:

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

    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 mock 的更好方法:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-04
      • 2020-08-09
      • 2018-06-28
      • 1970-01-01
      相关资源
      最近更新 更多