rubyconstantsredefineredefinition

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_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.


Solution

  • 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