Ruby 1.9 const_defined?("Timeout") returns true when Timeout not in the list of constants
I'm trying to upgrade Puppet to use Ruby 1.9 and running into trouble with constants. const_defined?("Timeout") is returning true even though :Timeout isn't in the list of constants. This doesn't happen on Ruby 1.8.7. Any ideas why?
[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,开发者_如何学C :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
The behavior of const_defined? in Ruby 1.9 can be made to be the same as in Ruby 1.8 by setting the new inherit parameter to false.
mod.const_defined?(sym, inherit=true)
Here's a example to illustrate the different behavior.
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
Under Ruby 1.9.2 the output from running this is:
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>'
So even though const_defined? recognizes that Timeout is defined at top scope as a class, remove_const is only allowed to remove constants in Foo's scope.
In Ruby 1.8.7 the output is:
The constant I got was Timeout
nil
So const_get looks at ancestor scopes just like in Ruby 1.9.2, but const_defined? does not, which prevents remove_const from getting called.
Ruby 1.9.2 can be made to behave like 1.8.7 like so:
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
However, this is now not backwards compatible with Ruby 1.8 since const_defined? doesn't have a second parameter in 1.8. To get around this I made the following method that can be called instead of const_defined? and used in either version of Ruby.
def is_constant_defined?(const)
if ::RUBY_VERSION =~ /1.9/
const_defined?(const, false)
else
const_defined?(const)
end
end
This solved this particular Ruby 1.9 upgrade issue. It may not be the best long term solution, and the real issue is that there's a class called Timeout at topscope AND sometimes a constant called Timeout in other classes that needs to be checked for, but this change gets the code much closer to running on Ruby 1.9.
I can't say for sure what's going on.
However, the RDoc for const_defined?
and constants
is different in 1.8.7, whereas it's fairly similar in 1.9.
In 1.8.7, const_defined?
says:
Returns true if a constant with the given name is defined by mod.
and constants
says
Returns an array of the names of the constants accessible in mod. This includes the names of constants in any included modules (example at start of section).
However, in 1.9, const_defined?
says
Returns true if a constant with the given name is defined by mod, or its ancestors if inherit is not false. [by default,
inherit
is true]
and constants
says
Returns an array of the names of the constants accessible in mod. This includes the names of constants in any included modules (example at start of section), unless the all parameter is set to false. [by default,
all
is true]
So it seems like the behaviour of the two methods is consistent in 1.9, but not consistent in 1.8.7. But I could be wrong.
That being said, I'd suggest the following:
- Create a toy example of using
const_defined?
andconstants
, preferably not involvingTimeout
, and play around with it until you are confident you understand what the two methods do, under both 1.8 and 1.9. - Work out where the
Timeout
constant belongs to. Also check whether IRB or the debugger may cause Timeout to become defined when it previously was undefined, and whether it gets loaded by default by one version of Ruby but not the other.
I also came across http://redmine.ruby-lang.org/issues/1915 when googling for const_defined? 1.8 1.9
. I'm not sure if it's relevant or not.
I hope this helps - I'm not sure though!
精彩评论