Как правильно инициализировать константу в Ruby? - PullRequest
10 голосов
/ 26 декабря 2010

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

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 ]

11 голосов
/ 26 декабря 2010

Единственный способ, которым это может произойти, это когда bar.rb равен require d несколько раз.Этого не должно произойти, поскольку 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 в любом случае.

6 голосов
/ 26 декабря 2010

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

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

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

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