Подтвердить что ты не робот

Путаное поведение const_get в Ruby?

В соответствии с документацией 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?

Спасибо

4b9b3361

Ответ 1

Вы правы, чтобы вас путали... Док не указал, что 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, тогда только LookHereOnly и ее включенные модули будут найдены. Если область не указана (например, Foo в приведенном выше примере), Ruby рассмотрит определения открытого модуля, чтобы найти константу Foo: M::N, затем M и, наконец, Object. Определение самого верхнего открытого модуля всегда Object.

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

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

Ответ 2

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

def load_constant(name)
  parts = name.split('::')
  klass = Module.const_get(parts.shift)
  klass = klass.const_get(parts.shift) until parts.empty?
  klass
end

Ответ 3

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

def load_constant(name)
    name.split('::').inject(Module) do |mod_path, mod_to_find|
      mod_path.const_get(mod_to_find)
    end
end