【问题标题】:Ruby 1.9 const_defined?("Timeout") returns true when Timeout not in the list of constants当 Timeout 不在常量列表中时,Ruby 1.9 const_defined?("Timeout") 返回 true
【发布时间】:2011-03-22 17:44:38
【问题描述】:

我正在尝试升级 Puppet 以使用 Ruby 1.9,但遇到了常量问题。 const_defined?("Timeout") 返回 true,即使 :Timeout 不在常量列表中。这不会发生在 Ruby 1.8.7 上。任何想法为什么?

[128, 137] in /Users/matthewrobinson/work/puppet/lib/puppet/util/classgen.rb
   128    def handleclassconst(klass, name, options)   
   129      const = genconst_string(name, options)
   130
   131      require 'ruby-debug'; 
   132      debugger if const == "Timeout"=> 
   133      if const_defined?(const)
   134        if options[:overwrite]   
   135          Puppet.info "Redefining #{name} in #{self}"
   136          remove_const(const)   
   137        else
(rdb:1) const
=> "Timeout"
(rdb:1) const_defined?(const)
=> true
(rdb:1) constants.grep /Timeout/
=> []
(rdb:1) constants
=> [:Ensure, :ParameterName, :Auth_type, :Allow_root, :Authenticate_user, :Auth_class, :Comment, :Group, :K_of_n, :Mechanisms, :Rule, :Session_owner, :Shared, :MetaParamNoop, :MetaParamSchedule, :MetaParamAudit, :MetaParamCheck, :MetaParamLoglevel, :MetaParamAlias, :MetaParamTag, :RelationshipMetaparam, :MetaParamRequire, :MetaParamSubscribe, :MetaParamBefore, :MetaParamNotify, :MetaParamStage, :Component, :Macauthorization, :Expirer, :ClassMethods, :InstanceMethods, :ExecutionStub, :POSIX, :Errors, :MethodHelper, :ClassGen, :Docs, :Execution, :Tagging, :Log, :Logging, :Package, :Warnings, :Cacher, :Autoload, :LoadedFile, :Settings, :Feature, :SUIDManager, :RunMode, :CommandLine, :InstanceLoader, :Pson, :Metric, :LogPaths, :ProviderFeatures, :InlineDocs, :FileLocking, :Storage, :Checksums]
(rdb:1) constants.grep /Path/
=> [:LogPaths]
(rdb:1) self
=> Puppet::Type::Macauthorization

【问题讨论】:

  • 如果您希望人们阅读代码示例,您可能希望将第 129 行与第 128 行分开。
  • 是的,我没有注意到格式有多么糟糕。谢谢你告诉我,我已经修好了。

标签: ruby ruby-1.9 puppet


【解决方案1】:

const_defined 的行为?在 Ruby 1.9 中可以通过将新的 inherit 参数设置为 false 来与 Ruby 1.8 中的相同。

mod.const_defined?(sym, inherit=true)

这里有一个例子来说明不同的行为。

module Foo
  def self.bar
    puts "The constant I got was #{const_get("Timeout")}"
    if const_defined?("Timeout")
      puts "I found #{Timeout}!"
      remove_const("Timeout")
      puts "Timeout is now #{Timeout}"
    end
  end
end

class Timeout
end

puts Foo.bar

在 Ruby 1.9.2 下运行它的输出是:

The constant I got was Timeout
I found Timeout!
19_test.rb:6:in `remove_const': constant Foo::Timeout not defined (NameError)
        from 19_test.rb:6:in `bar'
        from 19_test.rb:13:in `<main>'

那么即使是 const_defined?认识到 Timeout 在顶级作用域被定义为一个类,remove_const 只允许删除 Foo 作用域内的常量。

在 Ruby 1.8.7 中,输出为:

The constant I got was Timeout
nil

所以 const_get 就像在 Ruby 1.9.2 中一样查看祖先作用域,但是 const_defined 呢?没有,这会阻止 remove_const 被调用。

Ruby 1.9.2 的行为可以像 1.8.7 一样:

module Foo
  def self.bar
    puts "The constant I got was #{const_get("Timeout")}"
    if const_defined?("Timeout", false)
      puts "I found #{Timeout}!"
      remove_const("Timeout")
      puts "Timeout is now #{Timeout}"
    end
  end
end

class Timeout
end

puts Foo.bar

但是,自从 const_defined 以来,这现在不能向后兼容 Ruby 1.8?在 1.8 中没有第二个参数。为了解决这个问题,我做了以下可以调用的方法,而不是 const_defined?并在任一版本的 Ruby 中使用。

def is_constant_defined?(const)
  if ::RUBY_VERSION =~ /1.9/
    const_defined?(const, false)
  else
    const_defined?(const)
  end
end

这解决了这个特殊的 Ruby 1.9 升级问题。它可能不是最好的长期解决方案,真正的问题是 topscope 中有一个名为 Timeout 的类,有时需要检查其他类中的一个名为 Timeout 的常量,但这种更改使代码更接近运行红宝石 1.9。

【讨论】:

    【解决方案2】:

    我不能确定发生了什么。

    但是,const_defined?constants 的 RDoc 在 1.8.7 中有所不同,而在 1.9 中却非常相似。

    1.8.7const_defined? 中说:

    如果具有给定名称的常量由 mod 定义,则返回 true。

    constants

    返回 mod 中可访问的常量名称的数组。 这包括任何包含的模块中的常量名称(示例在部分开头)。

    但是,1.9const_defined?

    如果具有给定名称的常量由 mod 定义,则返回 true,如果 inherit 不为 false,则返回其祖先。 [默认情况下,inherit 为真]

    constants

    返回 mod 中可访问的常量名称的数组。这包括任何包含的模块中的常量名称(部分开头的示例),除非 all 参数设置为 false。 [默认情况下,all 为真]

    所以看起来这两种方法的行为在 1.9 中是一致的,但在 1.8.7 中不一致。但我可能是错的。

    话虽如此,我建议如下:

    • 创建一个使用const_defined?constants 的玩具示例,最好不要涉及Timeout,并尝试使用它,直到您确信自己理解这两种方法在1.8 和1.9 下的作用。
    • 找出Timeout 常量所属的位置。还要检查 IRB 或调试器是否可能导致 Timeout 在之前未定义时被定义,以及是否默认由一个版本的 Ruby 而不是另一个版本加载。

    我在搜索const_defined? 1.8 1.9 时也遇到了http://redmine.ruby-lang.org/issues/1915。我不确定它是否相关。

    我希望这会有所帮助 - 不过我不确定!

    【讨论】:

      猜你喜欢
      • 2011-06-05
      • 1970-01-01
      • 2015-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-11
      • 2016-12-22
      • 2017-03-16
      相关资源
      最近更新 更多