Давайте пройдемся по слегка измененной версии вашего тестового кода, как это видно по irb
и как отдельный скрипт:
def test_method;end
symbols = Symbol.all_symbols # This is already a "fixed" array, no need for map
puts symbols.include?(:test_method)
puts symbols.include?('test_method_nonexistent'.to_sym)
puts symbols.include?(:test_method_nonexistent)
eval 'puts symbols.include?(:really_not_there)'
Когда вы попробуете это в irb
, каждая строкабудет проанализирован и оценен до следующей строки.Когда вы нажмете вторую строку, symbols
будет содержать :test_method
, потому что def test_method;end
уже был оценен.Но символ :test_method_nonexistent
нигде не был замечен, когда мы добрались до строки 2, поэтому строки 4 и 5 скажут «ложь».Строка 6, конечно, даст нам еще одно ложное значение, потому что :really_not_there
не существует до тех пор, пока не вернется eval
.Итак, irb
говорит следующее:
true
false
false
false
Если вы запускаете это как скрипт на Ruby, все происходит в несколько ином порядке.Сначала Ruby проанализирует сценарий во внутреннем формате, который понимает виртуальная машина Ruby, а затем вернется к первой строке и начнет выполнение сценария.Когда анализируется скрипт, символ :test_method
будет существовать после разбора первой строки, а :test_method_nonexistent
будет существовать после разбора пятой строки;Итак, перед запуском скрипта известны два символа, которые нас интересуют.Когда мы добрались до шестой строки, Руби просто видит eval
и строку, но еще не знает, что eval
вызывает появление символа.
Теперь у нас есть два наших символа (:test_method
и :test_method_nonexistent
) и простая строка, которая при подаче на eval
создаст символ (:really_not_there
).Затем мы возвращаемся к началу, и виртуальная машина начинает выполнять код.Когда мы запускаем строку 2 и кешируем наш массив символов, оба :test_method
и :test_method_nonexistent
будут существовать и появляться в массиве symbols
, потому что анализатор их создал.Таким образом, строки с 3 по 5:
puts symbols.include?(:test_method)
puts symbols.include?('test_method_nonexistent'.to_sym)
puts symbols.include?(:test_method_nonexistent)
выведут «true».Затем мы нажимаем строку 6:
eval 'puts symbols.include?(:really_not_there)'
и выводится «false», потому что :really_not_there
создается eval
во время выполнения, а не во время синтаксического анализа.В результате Ruby говорит:
true
true
true
false
Если мы добавим это в конце:
symbols = Symbol.all_symbols
puts symbols.include?('really_not_there'.to_sym)
Тогда мы получим еще одно «истинное» из irb
иавтономный скрипт, потому что eval
создаст :really_not_there
и мы получим свежую копию списка символов.