2010-07-30 7 views
51

Я запускаю некоторый код Ruby, который анализирует 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 не дает предупреждения и просто лучше.

+5

Почему вы хотите переопределить константу? Лучше использовать константы пространства имен, сохраняя их в своих собственных классах или модулях - таким образом они никогда не конфликтуют с другими константами. –

+1

Я хочу переопределить константу, потому что я хочу использовать константы естественным образом, как если бы я не использовал автоматический перезагрузщик исходного кода, поэтому я не буду принимать никакого ответа «просто не используйте константу». –

+2

Что такое неэлегантное, а не сухое о 'Tau = 2 * Pi, если не определено? (Tau)'? – jrdioko

ответ

58

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

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 

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

+0

Несомненно, ключ к этому ответу - это просто первый вызов 'Object.send (: remove_const, 'Tau'), если Object.const_defined? ('Tau')', который не определяет константу, тем самым вызывая предупреждение. Отличный подход. – ghayes

+0

Да, или просто 'send (: remove_const,: CONST), если const_defined? (: CONST)', если вы находитесь в классе (не в экземпляре). – thewoolleyman

4

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

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

2

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

Это установит Tau его, если nil, false или не определено, и оставьте его в покое.

+1

Хорошая идея ... К сожалению, это не очень портативно: в зависимости от версии ruby ​​и реализации (ruby/jruby), аффектация константы с || = дала мне три разных результата. Либо он работает тихо, как предполагалось (jruby1.5), либо я получаю «неинициализированную постоянную» сбой (ruby1.8), либо получаю предупреждение, даже если не происходит аффектации (jruby1.2). –

3

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

+2

Да. Как упоминалось в вашей ссылке, в Rails существует более эффективная реализация silence_warnings: http://api.rubyonrails.org/classes/Kernel.html#M002564 Но этот подход уступает принятому ответу, поскольку он, вероятно, имеет побочные эффекты для других потоки. –

1

Как насчет следующего?

TAU ||= 2 * Pi 

Это работает на драгоценном камне, над которым я работаю.

+5

Это никогда не переопределит константу, так что это не ответ на вопрос. Как он получил 8 голосов? – jrochkind

+1

Ну, это отвечает def_if_not_defined (: Tau, 2 * Pi), и я думал, что это был исходный вопрос, не уверен, что это было давно – P3t3rU5

+1

Также переопределение констант - это злоупотребление языком, даже если вы можете это сделать , это не всегда означает, что вы должны это делать – P3t3rU5