How to redefine a Ruby constant without warning?
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_w开发者_如何学Cithout_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.
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
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
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.
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.
精彩评论