当我在String上使用method_missing重定向到字符时,为什么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以便挂起。 我甚至试过这个:

 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 `' 

它严格地发生在任何变平的字符串数组中。

 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 `' 

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

如果我告诉它有多深,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? 这将有助于知道什么测试变平对内部对象的调用,因此我可以在字符串中定义它并避免循环。

展平时没有内置的帽子。 因此,通过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