如何在没有警告的情况下重新定义一个Ruby常量?

问题描述 投票:56回答:4

我正在运行一些Ruby代码,每当Ruby文件的日期发生变化时,它都会对其进行评估。在文件中,我有一些常量定义,例如

Tau = 2 * Pi

当然,它们会让解释器每次都显示 "已经初始化常量 "的警告,所以,我希望有下面的函数。

def_if_not_defined(:Tau, 2 * Pi)
redef_without_warning(:Tau, 2 * Pi)

我可以把所有的常量定义都写成这样来避免警告:

Tau = 2 * Pi unless defined?(Tau)

但这样做既不优雅,又有点湿(不是)。干燥).

有没有更好的方法来 def_if_not_defined? 那怎么做呢?redef_without_warning?

--

解决办法感谢史蒂夫。

class Object
  def def_if_not_defined(const, value)
    mod = self.is_a?(Module) ? self : self.class
    mod.const_set(const, value) unless mod.const_defined?(const)
  end

  def redef_without_warning(const, value)
    mod = self.is_a?(Module) ? self : self.class
    mod.send(:remove_const, const) if mod.const_defined?(const)
    mod.const_set(const, value)
  end
end

A = 1
redef_without_warning :A, 2
fail 'unit test' unless A == 2
module M
  B = 10
  redef_without_warning :B, 20
end
fail 'unit test' unless M::B == 20

--

这个问题是老问题了。上面的代码只有在Ruby 1.8中才需要。在Ruby 1.9中,P3t3rU5的答案不会产生任何警告,而且更好。

ruby constants redefine redefinition
4个回答
61
投票

下面的模块可能会做你想要的事情。如果不是的话,它可能会给你的解决方案提供一些提示。

module RemovableConstants

  def def_if_not_defined(const, value)
    self.class.const_set(const, value) unless self.class.const_defined?(const)
  end

  def redef_without_warning(const, value)
    self.class.send(:remove_const, const) if self.class.const_defined?(const)
    self.class.const_set(const, value)
  end
end

而作为使用它的一个例子

class A
  include RemovableConstants

  def initialize
    def_if_not_defined("Foo", "ABC")
    def_if_not_defined("Bar", "DEF")
  end

  def show_constants
    puts "Foo is #{Foo}"
    puts "Bar is #{Bar}"
  end

  def reload
    redef_without_warning("Foo", "GHI")
    redef_without_warning("Bar", "JKL")
  end

end

a = A.new
a.show_constants
a.reload
a.show_constants

给出以下输出

Foo is ABC
Bar is DEF
Foo is GHI
Bar is JKL

如果我在这里触犯了Ruby的任何禁忌,请原谅我,因为我仍然对Ruby中的Module:Class:Eigenclass结构有一定的了解。


4
投票

如果你想重新定义一个值,那么不要使用常量,用全局变量来代替($tau = 2 * Pi),但这也不是一个好的做法。你应该让它成为一个合适的类的实例变量。

对于另一种情况。Tau = 2 * Pi unless defined?(Tau) 是完全正确的,也是最易读的,因此是最优雅的解决方案。


3
投票

这里讨论另一种方法,使用$VERBOSE来抑制警告。 http:/mentalized.netjournal20100402suppress_warnings_from_ruby。

更新202056:针对链接已失效的评论,我在这里粘贴一个我以前项目中的例子,虽然我不能说这是否是一个好办法,在什么情况下是一个好办法。

original_verbose = $VERBOSE
$VERBOSE = nil # suppress warnings
# do stuff that raises warnings you don't care about
$VERBOSE = original_verbose

2
投票

除非常量的值很奇怪(比如你把常量设置成了... nilfalse),最好的选择是使用条件赋值运算符。Tau ||= 2*Pi

这将把Tau设为2π,如果它是: nil, false 或未定义,否则不要管它。

© www.soinside.com 2019 - 2024. All rights reserved.