Как новый сеанс IRB получает свою переменную область?

IRB - хороший способ поиграть и проверить что-то в Ruby. Также возможно выполнить некоторые настройки, используя скрипт, например:

require 'irb'

class Pirate
  def greet
    puts "Arrrrr, nice to meet ya"
  end
end

IRB.start # You can now instantiate Pirate in IRB

Однако одна вещь, по которой я неясен, это переменная область действия при этом. Если я добавлю эти строки раньше IRB.start:

smithy     = Pirate.new
@blackbard = Pirate.new

... @blackbeard будет доступен в IRB, но ссылка smithy получите undefined local variable or method 'smithy' for main:Object,

Зачем?

1 ответ

Решение

Binding который используется для оценки кода установлен в irb/workspace.rb:51 (Я имею в виду Ruby 1.9.3 rev 35410 здесь):

@binding = eval("def irb_binding; binding; end; irb_binding",
                TOPLEVEL_BINDING,
                __FILE__,
                __LINE__ - 3)

Это означает, что ваш сеанс IRB выполняется в том же контексте, что и код внутри метода верхнего уровня. Заметим:

puts "Outer object ID: %d" % self.object_id
puts "Outer binding: " + binding.inspect
smithy     = Pirate.new
@blackbard = Pirate.new

def test
  puts "Inner object ID: %d" % self.object_id
  puts "Inner binding: " + binding.inspect
  p @blackbard
  p smithy
end
test

Выход:

Outer object ID: 13230960
Outer binding: #<Binding:0x00000001c9aee0>
Inner object ID: 13230960
Inner binding: #<Binding:0x00000001c9acd8>
#<Pirate:0x00000001c9ada0>
/test.rb:18:in `test': undefined local variable or method `smithy' for main:Object (NameError)
    ...

Обратите внимание, что контекст объекта (self) одинаково как внутри, так и снаружи функции. Это потому, что каждый метод верхнего уровня добавляется в глобальный main объект.

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

Честно говоря, IRB - не самая лучшая часть программного обеспечения Ruby. Я обычно использую Pry для такого рода вещей, используя которые вы можете просто сделать:

require 'pry'
binding.pry

И есть сеанс с доступом к текущим локальным переменным.

Другие вопросы по тегам