Как переопределить константу Ruby без предупреждения? - PullRequest
55 голосов
/ 31 июля 2010

Я запускаю некоторый код 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 ]

60 голосов
/ 31 июля 2010

Следующий модуль может делать то, что вы хотите. Если нет, то он может дать некоторые указания на ваше решение

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

Простите, если я нарушил какие-либо рубиновые табу, поскольку я все еще разбираюсь с некоторыми из модулей Module: Class: Eigenclass в Ruby

4 голосов
/ 31 июля 2010

Если вы хотите переопределить значение, тогда не используйте константы, вместо этого используйте глобальную переменную ($ tau = 2 * Pi), но это тоже не очень хорошая практика. Вы должны сделать это переменной экземпляра подходящего класса.

Для другого случая, Tau = 2 * Pi unless defined?(Tau) совершенно в порядке и является наиболее читабельным, поэтому самым элегантным решением.

3 голосов
/ 30 августа 2012

Другой подход, использующий $ VERBOSE для подавления предупреждений, обсуждается здесь: http://mentalized.net/journal/2010/04/02/suppress_warnings_from_ruby/

2 голосов
/ 31 июля 2010

Если значения констант не слишком странные (т. Е. У вас установлены константы nil или false), лучшим вариантом будет использование оператора условного присваивания: Tau ||= 2*Pi

Это установит Тау на 2π, если оно равно nil, false или не определено, и оставит его в покое в противном случае.

...