Как запустить IRB.start в контексте текущего класса
Я прохожу через PragProg Continuous Testing With Ruby, где они говорят о вызове IRB
в контексте текущего класса для проверки кода вручную.
Однако они цитируют, что если вы вызываете IRB.start
в классе, self предопределен и ссылается на объект, в котором мы были, когда start был вызван, что в моем случае неверно.
Даже для очень простого примера, например
a = "hello"
require 'irb'
ARGV.clear # otherwise all script parameters get passed to IRB
IRB.start
Когда я пытаюсь получить доступ к переменной a
, я получаю очевидную
NameError: undefined local variable or method `a' for main:Object
Он работает только тогда, когда я изменяю a
на глобальную переменную
$a = "hello"
require 'irb'
ARGV.clear # otherwise all script parameters get passed to IRB
IRB.start
тогда я могу получить к нему доступ
irb(main):001:0> $a
=> 1
Есть ли какой-либо путь для доступа к локальным и переменным экземпляра в текущем классе?
Ответы
Ответ 1
Я предлагаю попробовать это в ripl, альтернативе irb. Вышеприведенный пример работает:
a = 'hello'
require 'ripl'
Ripl.start :binding => binding
Обратите внимание, что локальные переменные работают потому, что вы передаете текущую привязку с опцией: binding.
Возможно, вы можете сделать то же самое в irb, но поскольку он плохо документирован и не проверен, ваши шансы сделать это чисто невелики.
Ответ 2
Как вы уже обнаружили, self
не ссылается на объект, где был запущен IRB, а на TOPLEVEL_BINDING
, который, кажется, является экземпляром самого класса Object
.
Вы все еще можете запускать сеанс IRB с определенным классом или объектом в качестве контекста, но это не так просто, как только запуск IRB.
Если вам интересно, запускается IRB с конкретным контекстом, тогда это действительно легко сделать, когда вы запускаете IRB вручную. Просто запустите IRB, а затем вызовите метод irb
, передав ему объект/класс, который вы хотите в качестве контекста.
$ irb
irb(main):002:0> require 'myclass'
=> true
irb(main):003:0> irb MyClass
irb#1(MyClass):001:0> self
=> MyClass
Вы также можете запустить сеанс IRB программно и указать контекст, но это не так просто, как должно быть, потому что вам нужно воспроизвести некоторый код запуска IRB. После много экспериментов и копания в исходном коде IRB я смог придумать что-то, что работает:
require 'irb'
IRB.setup nil
IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
require 'irb/ext/multi-irb'
IRB.irb nil, self
Ответ 3
Вместо глобального можно использовать переменные экземпляра, например:
require 'irb'
@a = "hello"
ARGV.clear
IRB.start
>> @a
=> "hello"
Ответ 4
Используйте Pry:
a = 'hello'
require 'pry'
binding.pry
Ответ 5
Здесь, как вызвать IRB из вашего script в контексте того, где вы вызываете IRB.start..
require 'irb'
class C
def my_method
@var = 'hi'
$my_binding = binding
IRB.start(__FILE__)
end
end
C.new.my_method
Выполнение вашего script вызовет IRB. Когда вы дойдете до подсказки, вам нужно еще кое-что сделать...
% ./my_script.rb
irb(main):001:0> @var.nil?
=> true
irb(main):002:0> cb $my_binding
=> #<C:0x000000009da300 @var="hi">
irb(#<C:0x000000009da300>):003:0> @var.nil?
=> false
irb(#<C:0x000000009da300>):004:0> @var
=> "hi"
Наслаждайтесь!
Ответ 6
Мое решение для Ruby 2.2.3. Он очень похож на Bryant's
def to_s
"Sample"
end
def interactive
banana = "Hello"
@banana = "There"
require 'irb'
IRB.setup(nil)
workspace = IRB::WorkSpace.new(binding)
irb = IRB::Irb.new(workspace)
IRB.conf[:MAIN_CONTEXT] = irb.context
irb.eval_input
end
irb(Sample):001:0> puts banana
Hello
=> nil
irb(Sample):002:0> puts @banana
There
=> nil
irb(Sample):003:0>
Я могу получить доступ к локальным переменным и переменным экземпляра. Конечно, require 'irb'
может находиться в верхней части файла. Настройка banana
и @banana
заключается в том, чтобы доказать, что я могу получить к ним доступ из запроса irb. To_s - это один из способов получения довольно быстрой подсказки, но есть и другие варианты. И нет никакой реальной причины сделать отдельный метод, но в его нынешнем виде вы можете использовать это в любом месте, и он должен работать.
Ответ 7
Как и Ruby 2.4.0, вы можете сделать это:
require 'irb'
binding.irb
Это запустит IBR REPL, где у вас будет правильное значение для self
, и вы сможете получить доступ ко всем локальным переменным и переменным экземпляра, находящимся в области видимости. Введите Ctrl + D или quit
, чтобы возобновить свою программу Ruby.
Ответ 8
ruby-debug-base gem добавляет метод binding_n к модулю ядра, и это даст вам объект привязки доступа, который можно использовать в eval для доступа к переменным столбца вызова. Не забудьте запустить Debugger.start, чтобы включить отслеживание стека вызовов.
Вот пример, показывающий его использование, чтобы понять, что происходит внутри irb (программа Ruby). Можно также установить требование и Debugger.start внутри вашего собственного кода Ruby.
$ irb
ruby-1.8.7-p302 > require 'rubygems'
=> true
ruby-1.8.7-p302 > require 'ruby-debug-base'
=> true
ruby-1.8.7-p302 > Debugger.start
=> true
ruby-1.8.7-p302 > puts caller
/tmp/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/irb/workspace.rb:52 :i n `irb_binding' #`
/tmp/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/irb/workspace.rb:52
=> nil
ruby-1.8.7-p302 > eval "main", binding_n(2)
=> #<Object:0xb7762958 @prompt={:PROMPT_I=>"ruby-1.8.7-p302 > ", :PROMPT_N=>" ruby-1.8.7-p302 ?> ", :PROMPT_S=>"ruby-1.8.7-p302%l> ", :PROMPT_C=>"ruby-1.8.7-p302 > ", :AUTO_INDENT=>true, :RETURN=>" => %s \n"}>
ruby-1.8.7-p302 >
Если вы готовы запустить исправленную версию Ruby для 1.9.2, см. http://gitnub.com/rocky/rb-threadframe для лучшего, по моему мнению, доступа к стек вызовов. Rubinius предоставляет эту возможность, встроенную через Rubinius:: VM.backtrace.