How to redefine a Ruby constant without warning?

RubyConstantsRedefineRedefinition

Ruby Problem Overview


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.

Ruby Solutions


Solution 1 - Ruby

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

Solution 2 - 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

Solution 3 - Ruby

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.

Solution 4 - Ruby

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEldritch ConundrumView Question on Stackoverflow
Solution 1 - RubySteve WeetView Answer on Stackoverflow
Solution 2 - RubyPaul LynchView Answer on Stackoverflow
Solution 3 - RubyLeventixView Answer on Stackoverflow
Solution 4 - RubyChuckView Answer on Stackoverflow