如何修补内部红宝石类?

时间:2015-05-06 16:16:34

标签: ruby

旧版本的Net :: FTP存在一个错误:

f = Net::FTP.new
f.close

Net::FTPConnectionError: not connected
        from /usr/local/lib/ruby/1.9.1/net/ftp.rb:1032:in `method_missing'
        from /usr/local/lib/ruby/1.9.1/net/ftp.rb:908:in `close'
        from (irb):17
        from /usr/local/bin/irb:12:in `<main>'

通过向NullSocket添加null方法#close但仍在1.9.3

中,已在Ruby 2+中修复了此错误

我想像这样修补内部类NullSocket:

if defined?(Net::FTP::NullSocket) and !Net::FTP::NullSocket.instance_methods.include?(:close)
  class Net::FTP::NullSocket
     def close
      # Do nothing 'cause it's a null-method in a null-object
    end
  end
end

但它不起作用。我得到完全相同的错误,就像调用method_missing而不是我的新关闭方法一样。

如果我获得对内部私有@sock变量的引用,我可以调用close而不会引发异常,这有多奇怪。

irb(main):010:0* f = Net::FTP.new
=> #<Net::FTP:0x00000000f5d048 @mon_owner=nil, @mon_count=0, @mon_mutex=#<Mutex:0x00000000f5cff8>, @binary=true, @passive=false, @debug_mode=false, @resume=false, @sock=#<Net::FTP::NullSocket:0x00000000f5cfd0>, @logged_in=false>
irb(main):011:0> z = f.instance_variable_get('@sock')
=> #<Net::FTP::NullSocket:0x00000000f5cfd0>
irb(main):012:0> z.methods
=> [:method_missing, :close, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]
irb(main):013:0> z.close

有没有办法修补这样的内部类?

更新

我最后通过修补Net :: FTP #close

来解决问题
class Net::FTP
  def close
    @sock.close if @sock && !@sock.is_a?(NullSocket) && !@sock.closed?
  end
end

但这个问题的真正答案是修正David所解释的正确方法:

if defined?(Net::FTP::NullSocket) and !Net::FTP::NullSocket.instance_methods.include?(:'closed?')
  class Net::FTP::NullSocket
    def closed?
      true
    end
  end
end

1 个答案:

答案 0 :(得分:1)

Ruby提供的回溯显示了ftp.rb的第908行引发的异常,其中包含:

def close
  @sock.close if @sock and not @sock.closed?
end

您可以在堆栈跟踪中指示的路径上看到代码on GitHub或本地计算机。

@sock对象是一个NullSocket对象,因此当调用@sock.closed?时,它只会引发异常。您应该使用closed?修补程序返回true,然后可能不需要修补close,因为它不会被调用。

回答你的问题:是的,可以使用Ruby标准库中包含的monkeypatch类。