Непонятное поведение const_get в Ruby? - PullRequest
20 голосов
/ 02 июля 2010

Согласно документации mod.const_get(sym) «Возвращает значение именованной константы в мод.»

Я также знаю, что const_get по умолчанию может искать цепочку наследования получателя. Итак, следующие работы:

class A; HELLO = :hello; end
class B < A; end
B.const_get(:HELLO) #=> :hello

Я также знаю, что классы в подклассе Ruby Object, так что вы можете использовать const_get для поиска «глобальных» констант, даже если получатель является нормальным классом:

class C; end
C.const_get(:Array) #=> Array

Однако, и вот тут я запутался - модули не подкласс Object. Так почему же я все еще могу искать «глобальные» константы из модуля, используя const_get? Почему работает следующее?

module M; end
M.const_get(:Array) #=> Array

Если документация верна - const_get просто ищет константу, определенную в получателе или его суперклассах. Но в приведенном выше коде Object не является суперклассом M, так почему же можно искать Array?

Спасибо

Ответы [ 3 ]

11 голосов
/ 02 июля 2010

Вы правы, что запутались ... В документе не указывалось, что Ruby делает особый случай поиска констант в Modules и был изменен для явного указания этого . Если константа не была найдена в нормальной иерархии, Ruby перезапускает поиск с Object, как это может быть в источнике .

Постоянный поиск может немного сбить с толку. Возьмите следующий пример:

module M
  Foo = :bar
  module N
    # Accessing Foo here is fine:
    p Foo # => bar
  end
end
module M::N
  # Accessing Foo here isn't
  p Foo  # => uninitialized constant M::N::Foo
end
p M::N.const_get :Foo  # => uninitialized constant M::N::Foo

В обоих местах, однако, доступ к константам уровня Object, таким как Array, в порядке (слава богу!). Что происходит, так это то, что Ruby поддерживает список «определений открытых модулей». Если константа имеет явную область видимости, скажем, LookHereOnly::Foo, то only LookHereOnly и ее включенные модули будут найдены. Если область не указана (например, Foo в приведенном выше примере), Ruby просматривает определения открытого модуля, чтобы найти константу Foo: M::N, затем M и, наконец, Object. Определение самого открытого открытого модуля всегда Object.

Таким образом, M::N.const_get :Foo эквивалентно доступу к Foo, когда открытые классы только M::N и Object, как в последней части моего примера.

Надеюсь, я все понял, потому что я все еще смущен постоянными поисками: -)

2 голосов
/ 03 сентября 2014

Я придумал следующий скрипт для загрузки констант с именами:

def load_constant(name)
  parts = name.split('::')
  klass = Module.const_get(parts.shift)
  klass = klass.const_get(parts.shift) until parts.empty?
  klass
end
0 голосов
/ 10 ноября 2014

Пока мы не проверяем ошибки, вы можете:

def load_constant(name)
    name.split('::').inject(Module) do |mod_path, mod_to_find|
      mod_path.const_get(mod_to_find)
    end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...