64

我正在运行一些 Ruby 代码,每次更改日期时都会评估 Ruby 文件。在文件中,我有常量定义,比如

Tau = 2 * Pi

而且,当然,它们使解释器每次都显示不需要的“已经初始化的常量”警告,所以,我想要以下功能:

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

我可以通过像这样编写所有常量定义来避免警告:

Tau = 2 * Pi unless defined?(Tau)

但它不优雅而且有点湿(不是DRY)。

有更好的方法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 的答案不会产生任何警告,而且更好。

4

4 回答 4

69

以下模块可能会做你想做的事。如果不是,它可能会为您的解决方案提供一些指示

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 结构有所了解

于 2010-07-31T07:00:57.000 回答
6

此处讨论了另一种使用 $VERBOSE 来抑制警告的方法:http: //mentalized.net/journal/2010/04/02/suppress_warnings_from_ruby/

2020 年 5 月 6 日更新:针对链接现已失效的评论,我从我的旧项目中粘贴了一个示例,但我不能说它是否以及在什么情况下是一个好方法:

original_verbose = $VERBOSE
$VERBOSE = nil # suppress warnings
# do stuff that raises warnings you don't care about
$VERBOSE = original_verbose
于 2012-08-30T18:11:10.573 回答
4

如果要重新定义一个值,则不要使用常量,而是使用全局变量 ($tau = 2 * Pi),但这也不是一个好习惯。您应该使其成为合适类的实例变量。

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

于 2010-07-30T21:15:54.033 回答
2

除非常量的值非常奇怪(即您将常量设置为nilor false),否则最好的选择是使用条件赋值运算符:Tau ||= 2*Pi

如果 Tau 是 或未定义,这会将 Tau 设置为 2π nilfalse否则不理会它。

于 2010-07-30T21:38:41.890 回答