当我在String上使用method_missing重定向到字符时,为什么Ruby会挂起?

时间:2014-09-14 20:25:18

标签: ruby

当我实现此代码时:

class String

  def method_missing(meth,*args, &block)
    if self.chars.respond_to? meth
      self.chars.send meth, *args, &block
    else
      super
    end
  end

  def respond_to?(meth)
    if self.chars.respond_to? meth
      true
    else
      super
    end
  end

end

Ruby陷入flatten。即使需要另一个文件内部调用flatten,它也会挂起。我甚至试过这个:

class String

  def method_missing(meth,*args, &block)
    if meth.to_sym == :flatten
      super
    elsif self.chars.respond_to? meth
      self.chars.send meth, *args, &block
    else
      super
    end
  end

  def respond_to?(meth)
    if meth.to_sym == :flatten
      super
    elsif self.chars.respond_to? meth
      true
    else
      super
    end
  end

end

但是会出现同样的结果。内部发生了什么导致flatten失败?

这里是错误输出:

2.1.2 :003 > require 'mygem'
 => true 
2.1.2 :004 > require 'pry'
^CIRB::Abort: abort then interrupt!
  from /home/user/dev/MyGem/lib/mygem/string_method_missing.rb:17:in `call'
  from /home/user/dev/MyGem/lib/mygem/string_method_missing.rb:17:in `respond_to?'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems/requirement.rb:112:in `flatten'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems/requirement.rb:112:in `initialize'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems/requirement.rb:70:in `new'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems/requirement.rb:70:in `default'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems/dependency.rb:260:in `merge'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems/specification.rb:1323:in `block in activate_dependencies'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems/specification.rb:1306:in `each'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems/specification.rb:1306:in `activate_dependencies'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems/specification.rb:1288:in `activate'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems.rb:194:in `try_activate'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:132:in `rescue in require'
  from /home/user/.rvm/rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:144:in `require'
  from (irb):4
  from /home/user/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'

它严格地适用于任何变平的字符串数组。

2.1.2 :005 > [1,2,3].flatten
 => [1, 2, 3] 
2.1.2 :006 > ["1","2","3"].flatten
^CIRB::Abort: abort then interrupt!
  from /home/user/dev/MyGem/lib/mygem/string_method_missing.rb:17:in `call'
  from /home/user/dev/MyGem/lib/mygem/string_method_missing.rb:17:in `respond_to?'
  from (irb):6:in `flatten'
  from (irb):6
  from /home/user/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'

更新 我已更新评论中建议的修补程序的代码。问题仍然存在。我期待着答案。

如果我告诉它有多深,那么Flatten似乎有效。所以这似乎表明在字符串对象上展平数组时存在无限循环。例如:

2.1.2 :013 > ["1","2","3"].flatten(0)
 => ["1", "2", "3"] 
2.1.2 :021 > ["1","2","3"].flatten(8)
 => ["1", "2", "3"] 
2.1.2 :022 > ["1","2","3"].flatten(9)
 => ["1", "2", "3"] 

如何避免无限循环仍然执行此method_missing?这将有助于知道什么测试变平对内部对象的调用,因此我可以在字符串中定义它并避免循环。

1 个答案:

答案 0 :(得分:0)

没有内置的帽子。因此,通过method_missing将所有内容重定向到String中的字符会创建一个类似于Array的对象,这将导致flatten方法无限循环。

添加此项,这是一个有效的解决方案:

class Array
  # To fix a bug that our method_missing creates
  # we need to set a MAXIMUM for FLATTEN
  alias_method :_old_flatten, :flatten
  alias_method :_old_flatten!, :flatten!

  def flatten(level = 99)
    _old_flatten(level)
  end

  def flatten!(level = 99)
    _old_flatten!(level)
  end
end