Ruby 1.9 const_defined? («Timeout») renvoie true lorsque Timeout n'est pas dans la liste des constantes
Question
J'essaye de mettre à jour Puppet pour utiliser Ruby 1.9 et j'ai des problèmes avec les constantes.const_defined? ("Timeout") renvoie true même si: Timeout n'est pas dans la liste des constantes.Cela ne se produit pas sur Ruby 1.8.7.Des idées pourquoi?
[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
La solution
Le comportement de const_defined? dans Ruby 1.9 peut être rendu identique à Ruby 1.8 en définissant le nouveau paramètre inherit sur false.
mod.const_defined?(sym, inherit=true)
Voici un exemple pour illustrer les différents comportements.
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
Sous Ruby 1.9.2, le résultat de l'exécution de ceci est:
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>'
Donc, même si const_defined? reconnaît que Timeout est défini dans la portée supérieure en tant que classe, remove_const est uniquement autorisé à supprimer les constantes dans la portée de Foo.
Dans Ruby 1.8.7, le résultat est:
The constant I got was Timeout
nil
Donc, const_get regarde les portées des ancêtres comme dans Ruby 1.9.2, mais const_defined? pas, ce qui empêche remove_const d'être appelé.
Ruby 1.9.2 peut être fait pour se comporter comme 1.8.7 comme ceci:
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
Cependant, ce n'est plus rétrocompatible avec Ruby 1.8 depuis const_defined? n'a pas de deuxième paramètre en 1.8. Pour contourner cela, j'ai créé la méthode suivante qui peut être appelée au lieu de const_defined? et utilisé dans les deux versions de Ruby.
def is_constant_defined?(const)
if ::RUBY_VERSION =~ /1.9/
const_defined?(const, false)
else
const_defined?(const)
end
end
Cela a résolu ce problème de mise à niveau de Ruby 1.9. Ce n'est peut-être pas la meilleure solution à long terme, et le vrai problème est qu'il existe une classe appelée Timeout chez topscope ET parfois une constante appelée Timeout dans d'autres classes qui doit être vérifiée, mais ce changement rapproche le code de son exécution. Ruby 1.9.
Autres conseils
Je ne peux pas dire avec certitude ce qui se passe.
Cependant, le RDoc pour const_defined?
et constants
est différent dans 1.8.7, alors qu'il est assez similaire dans 1.9.
Dans 1.8.7 , const_defined?
dit:
Renvoie true si une constante avec le nom donné est définie par mod.
et constants
dit
Renvoie un tableau des noms des constantes accessibles dans mod. Cela inclut les noms des constantes dans tous les modules inclus (exemple au début de la section).
Cependant, dans 1.9 , const_defined?
dit
Renvoie true si une constante avec le nom donné est définie par mod, ou ses ancêtres si inherit n'est pas false. [par défaut,
inherit
est vrai]
et constants
dit
Renvoie un tableau des noms des constantes accessibles dans mod. Cela inclut les noms des constantes dans tous les modules inclus (exemple au début de la section), sauf si le paramètre all est défini sur false. [par défaut,
all
est vrai]
Il semble donc que le comportement des deux méthodes soit cohérent dans 1.9, mais pas cohérent dans 1.8.7. Mais je peux me tromper.
Cela étant dit, je suggère ce qui suit:
- Créez un exemple d'utilisation de
const_defined?
etconstants
, de préférence n'impliquant pasTimeout
, et jouez avec jusqu'à ce que vous soyez sûr de comprendre ce que font les deux méthodes, sous 1.8 et 1.9. - Déterminez où appartient la constante
Timeout
. Vérifiez également si IRB ou le débogueur peut entraîner la définition de Timeout alors qu'il était précédemment indéfini, et s'il est chargé par défaut par une version de Ruby mais pas par l'autre.
Je suis également tombé sur http://redmine.ruby-lang.org/issues/1915 lors de la recherche sur google pour const_defined? 1.8 1.9
. Je ne sais pas si c'est pertinent ou non.
J'espère que cela aide - je ne suis pas sûr cependant!