Как правильно инициализировать константу в Ruby?

у меня есть простой класс, который определяет некоторые константы, например:

module Foo
  class Bar
    BAZ = "bof"
    ...

все щенки и радуги, пока я не скажу рейк запустить все мои Test::Unit тесты. Когда это происходит, я получаю предупреждения:

bar.rb:3: warning: already initialized constant BAZ

у меня была привычка избегать этих предупреждений, делая постоянную инициализацию условной, например:

...
BAZ = "bof" unless const_defined? :BAZ
...

это, кажется, решает проблему, но это немного утомительно, и я никогда не видел, чтобы кто-то еще инициализировал константы таким образом. Это заставляет меня думать, что я делаю это неправильно. Есть ли лучший способ инициализации констант, которые не будут генерировать предупреждения?

обновление: в качестве немного более подробной информации о том, как я использую эти константы, скажем, я определил Token класс, который имеет константы для всех символов, которые являются частью синтаксиса некоторого искусственного языка. У меня также есть Scanner класс, который читает поток символов, генерируя Token экземпляру для каждой один.

module Foo
  class Token
    LPAREN = "("
    RPAREN = ")"
    ...
  end

  class Scanner
    def next_token
      case read_char()
        when Token::LPAREN: # Generate a new LPAREN token
        ...

то есть, проверяя, какой токен должен быть сгенерирован для данного символа, я хочу использовать константы, определенные в Token.

обновление 2: ответ Йорга показал, что проблема, вероятно, в том, как я строил пути в моем require операторы, а не в том, как я инициализировал или использовал константы. Я переписал require заявления для устранения любого ручного создания пути , например:

# File: $PROJECT_ROOT/lib/foo.rb; trying to load $PROJECT_ROOT/lib/foo/bar.rb
require File.expand_path(File.dirname(__FILE__)) + "foo/bar"

теперь написано полагаться на $LOAD_PATH:

# File: $PROJECT_ROOT/lib/foo.rb; trying to load $PROJECT_ROOT/lib/foo/bar.rb
require 'lib/foo/bar'

я удалил условные проверки из моих постоянных операторов инициализации, и rake теперь запускает модульные тесты без каких-либо предупреждений.

2 ответов


это может произойти только тогда, когда bar.rb is requireD несколько раз. Чего не должно было случиться, так как require не загружает файлы, которые уже были загружены один раз.

однако он использует только путь, который вы передаете ему, чтобы определить, был ли файл уже загружен, по крайней мере, в Ruby 1.8:

require 'bar'   # => true, file was loaded

require 'bar'   # => false, file had already been loaded

require './bar' # => true, OOPS, I DID IT AGAIN
# bar.rb:3: warning: already initialized constant BAZ

Итак, вы правы: это вполне может быть признаком того, что с вашей зависимостью что-то не так управление.

типичные предупреждающие знаки

  • ручное построение путей к файлам вместо того, чтобы просто полагаться на $LOAD_PATH

    require "File.expand_path('../lib/bar', File.dirname(__FILE__))"
    
  • манипуляции $LOAD_PATH в любом месте, кроме, возможно, основной точки входа в вашу библиотеку:

    path = File.expand_path(File.dirname(__FILE__))
    $LOAD_PATH << path unless $LOAD_PATH.include?(path)
    

В общем, моя философия заключается в том, что это не моя работа как библиотечного писателя, чтобы выяснить, как поставить мою библиотеку на $LOAD_PATH. Это системный администратор работа. Если сисадмин использует RubyGems для установки моей библиотеки, то RubyGems позаботится об этом, иначе любая другая система управления пакетами, которую он использует, должна позаботиться об этом, и если он использует setup.rb, после чего он будет установлен в site_ruby, который уже находится на $LOAD_PATH в любом случае.


в комментариях к этот пост. Я думаю, что следующий ставит его красиво:

Это несколько раздражало меня, когда я впервые подошел к Руби. Это был остаток мой статический тип промывания мозгов. Три вещи смягчить это реальная проблема. 1. Предупреждение. Вы могу поспорить, это должно быть исключением., но на самом деле, как это может быть? в случае, когда другие программатор молча поймал исключение? Что подводит меня к цифре 2) Не работайте с дебилами. Только дебилы молча поймать исключения и продолжить и только дебилы меняют постоянные значения используется таким образом. Что приводит меня к 3) Ни один из нас не идиот, так что вы будете будьте счастливы узнать, что anecdotally это со мной такого никогда не случалось. Это действительно потому что константы выделяются в Ruby во всяком случае, что с их upcase, и как часто вы, вероятно, есть постоянное как L-значение в код?

чтобы напрямую ответить на ваш вопрос, я бы ничего не сделал, чтобы избавиться от предупреждения. Просто примите предупреждение как предупреждение и двигайтесь дальше.