Почему прямое назначение константы в блоке кода Class.new не работает - PullRequest
0 голосов
/ 27 сентября 2018

Рассмотрим следующий код:

c1 = Class.new { ANSWER = 42 }
#⇒ #<Class:0x00556c8fc09c60> < Object
c1.constants
#⇒ []
c1.new.class.constants
#⇒ []
c1.new.singleton_class.constants
#⇒ []
Object.constants.grep /ANS/
#⇒ [:ANSWER]

Кажется, константа определена в Object.Но с явным вызовом const_set константное назначение работает отлично:

c2 = Class.new { const_set :ANSWER, 42 }
#⇒ #<Class:0x00556c8f7f3568> < Object
c2.constants
#⇒ [:ANSWER]
c2.new.class.constants
#⇒ [:ANSWER]
c2.new.singleton_class.constants
#⇒ [:ANSWER]

Мой вопрос: что мешает правильно назначить константу в Class.new экземпляре в первом фрагменте

Ответы [ 2 ]

0 голосов
/ 27 сентября 2018

Существует три неявных контекста в Ruby :

  • self (контекст для отправки сообщений без получения и переменных экземпляра)
  • the определение по умолчанию (контекст для def выражений определения метода без явной цели, т.е. def bar вместо def foo.bar)
  • точка определения константы по умолчанию

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

В любом случае, важно помнить об этих трех контекстах и ​​знать, когда они изменяются, а когда нет.

В частности, блок only меняет лексический контекст и ничего больше .Блок не меняет self, он не меняет стандартное определение и не меняет стандартную точку определения константы .

Однако есть некоторыеметоды, для которых явное предназначено для изменения одного или нескольких из этих трех контекстов.

Семейство методов *_eval меняет self (контекст # 1) и определитель по умолчанию (контекст)# 2), но не меняет точку определения константы по умолчанию (контекст # 3).В частности, все методы *_eval (*_exec) устанавливают self для получателя.Версии instance_* устанавливают определение по умолчанию для одноэлементного класса получателя, версии module_* и class_* устанавливают определение по умолчанию для получателя.

Однако точка определения константы по умолчанию равна не изменено, поэтому определение константы (и поиск) работает так же, как и раньше: определения переходят к определению ближайшего лексически заключенного модуля, поиск начинается с определения ближайшего лексически заключающего модуля и продолжается лексически наружу и динамически вверх -by-наследование.

Насколько я мог найти, только конструкция , которая изменяет стандартную точку определения константы, является определением module / class.

0 голосов
/ 27 сентября 2018

Документ для Class :: new гласит: «Если дан блок, ему передается объект класса, и блок оценивается в контексте этого класса, как class_eval.»,это означает, что

c = Class.new { ANSWER = 42 }

эквивалентно

c = Class.new
c.class_eval  { ANSWER = 42 }

class_eval создает константу на верхнем уровне, поскольку она вызывается на c на верхнем уровне.c.class_eval { ANSWER = 42 } фактически совпадает с

ANSWER = 42

, который создает константу для Object.

Object::ANSWER
  #=> 43

Здесь все наоборот.

NUMBER = 43
Object::NUMBER
  #=> 43

c = Class.new
c.const_set(:NUMBER, 48)
c::NUMBER
  #=> 48
c.class_eval { puts NUMBER }
43

Вот еще один пример.

class F
  class G; end
  G.class_eval { HIPPO = 5 }
end

F::HIPPO
  #=> 5
F::G::HIPPO
  #=> #NameError (uninitialized constant F::G::HIPPO)
  #   Did you mean?  F::HIPPO

Однако,

class F
  class G
    class_eval { HIPPO = 5 }
  end
end

F::HIPPO
  #=> NameError (uninitialized constant F::HIPPO)
F::G::HIPPO
  #=> 5

Читатели: см. Модуль # class_eval и это обсуждение о константепоиск с использованием class_eval (дополнение к созданию константы).(Поиск по "class_eval").

...