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

发布于 2024-09-12 13:41:10 字数 1182 浏览 3 评论 0原文

我正在运行一些 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_define?以及如何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 的答案不会产生警告,而且更好。

I'm running some Ruby code which evals a Ruby file every time its date changes. In the file, I have constant definitions, like

Tau = 2 * Pi

and, of course, they make the interpreter display the unwanted "already initialized constant" warning every time, so, I'd like to have the following functions:

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

I could avoid the warning by writing all my constant definitions like this:

Tau = 2 * Pi unless defined?(Tau)

but it is inelegant and a bit wet (not DRY).

Is there a better way to def_if_not_defined? And how to redef_without_warning?

--

Solution thanks to Steve:

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

--

This question is old. The above code is only necessary for Ruby 1.8. In Ruby 1.9, P3t3rU5's answer produces no warning and is simply better.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

木緿 2024-09-19 13:41:10

以下模块可能会执行您想要的操作。如果没有,它可能会为您的解决方案提供一些指示

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​​ 禁忌,请原谅我,因为我仍在了解其中的一些 Module:Class:Eigenclass 结构红宝石

The following module may do what you want. If not it may provide some pointers to your solution

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

And as an example of using it

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

Gives the following output

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

Forgive me if i've broken any ruby taboos here as I am still getting my head around some of the Module:Class:Eigenclass structure within Ruby

愛上了 2024-09-19 13:41:10

这里讨论了另一种方法,使用 $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

Another approach, using $VERBOSE, to suppress warnings, is discussed here: http://mentalized.net/journal/2010/04/02/suppress_warnings_from_ruby/

Update 2020/5/6: In response to the comment that the link is now dead, I am pasting an example here from my old project, though I can't say whether and in what circumstances it is a good approach:

original_verbose = $VERBOSE
$VERBOSE = nil # suppress warnings
# do stuff that raises warnings you don't care about
$VERBOSE = original_verbose
没︽人懂的悲伤 2024-09-19 13:41:10

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

对于另一种情况,Tau = 2 * Pi 除非定义?(Tau) 完全没问题,而且可读性最强,因此是最优雅的解决方案。

If you want to redefine a value then don't use constants, use a global variable instead ($tau = 2 * Pi), but that's not a good practice too. You should make it an instance variable of a suitable class.

For the other case, Tau = 2 * Pi unless defined?(Tau) is perfectly alright and the most readable, therefore the most elegant solution.

金兰素衣 2024-09-19 13:41:10

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

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

Unless the values of the constants are pretty weird (i.e. you have constants set to nil or false), the best choice would be to use the conditional assignment operator: Tau ||= 2*Pi

This will set Tau to 2π if it is nil, false or undefined, and leave it alone otherwise.

明天过后 2024-09-19 13:41:10

(问题底部令人费解的答案是指我没有看到的 P3t3rU5 的答案。答案中有一个重要方面不在 Steve Weet 中,我一开始就错过了 - 使用“class Object” ”。之前我必须在 https://stackoverflow.com/a/11503625/18096 上有效地阅读相同的答案也许其他人会从对问题关键的阐述中受益。)

我的用例通常会丢弃临时的实验性更改,代码如下:

martind@stormy:~$ ruby -we 'Schema = 1; Schema = 2'
-e:1: warning: already initialized constant Schema
-e:1: warning: previous definition of Schema was here
martind@stormy:~$ 

...答案越小越好。不过,按照 Paul Lynch 的说法,删除 -w 并没有什么帮助,nil 删除 $VERBOSE 却可以。不过,也许我们不想在新的初始化程序中禁用警告,所以:

martind@stormy:~$ ruby -we 'Schema = 1; Object.remove_const(:Schema); Schema = 2'
Traceback (most recent call last):
-e:1:in `<main>': private method `remove_const' called for Object:Class (NoMethodError)
Did you mean?  remove_method
martind@stormy:~$

哦,好吧:

martind@stormy:~$ ruby -we 'Schema = 1; Object.send(:remove_const, :Schema); Schema = 2'
martind@stormy:~$ 

(The answer that's puzzlingly at the bottom of the question refers to an answer from P3t3rU5 that I don't see. There's an important aspect of the answer there that's not in Steve Weet's and which I missed at first - the use of "class Object". I had to read effectively the same answer over at https://stackoverflow.com/a/11503625/18096 before understanding the one here. Perhaps others would benefit from an elaboration of the crux of the matter.)

My use-cases are usually throw away temporary experimental changes, in code like this:

martind@stormy:~$ ruby -we 'Schema = 1; Schema = 2'
-e:1: warning: already initialized constant Schema
-e:1: warning: previous definition of Schema was here
martind@stormy:~$ 

... where the smaller the answer the better. Dropping the -w doesn't help, though, per Paul Lynch, niling out $VERBOSE does. Still, perhaps we don't want to disable warnings in the new initializer, so:

martind@stormy:~$ ruby -we 'Schema = 1; Object.remove_const(:Schema); Schema = 2'
Traceback (most recent call last):
-e:1:in `<main>': private method `remove_const' called for Object:Class (NoMethodError)
Did you mean?  remove_method
martind@stormy:~$

Oh, alright then:

martind@stormy:~$ ruby -we 'Schema = 1; Object.send(:remove_const, :Schema); Schema = 2'
martind@stormy:~$ 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文