Как запустить IRB.запуск в контексте текущего класса
Я просто PragProg Непрерывное Тестирование С 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
есть ли способ обойти это, чтобы получить доступ к локальным и переменным экземпляра в текущем классе?
8 ответов
Я бы предложил попробовать это в рипл, альтернатива irb. Приведенный выше пример работает:
a = 'hello'
require 'ripl'
Ripl.start :binding => binding
обратите внимание, что локальные переменные работают, потому что вы передаете текущую привязку с параметром: binding.
вы могли бы сделать то же самое в irb, но поскольку это плохо документировано и непроверено, ваши шансы сделать это чисто ничтожны.
как вы уже обнаружили, 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
вместо глобальных вы можете использовать переменные экземпляра, например:
require 'irb'
@a = "hello"
ARGV.clear
IRB.start
>> @a
=> "hello"
вот как вызвать IRB из вашего скрипта в контексте того, где вы вызываете IRB.начать..
require 'irb'
class C
def my_method
@var = 'hi'
$my_binding = binding
IRB.start(__FILE__)
end
end
C.new.my_method
выполнение скрипта вызовет 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"
наслаждайтесь!
мое решение для Ruby 2.2.3. Это очень похоже на Брайанта
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>
Я могу получить доступ к локальным переменным и переменным экземпляра. The require 'irb'
конечно может быть в верхней части файла. Настройка banana
и @banana
просто чтобы доказать, что я могу получить к ним доступ из приглашения irb. To_s-это один из способов получить довольно быстрое приглашение, но есть и другие варианты. И нет никакой реальной причины делать отдельный метод, но как он стоит, вы можете плюхнуть это в любом месте, и он должен работа.
начиная с Ruby 2.4.0, вы можете сделать следующее:
require 'irb'
binding.irb
это запустит IBR REPL, где у вас будет правильное значение для self
и вы сможете получить доступ ко всем локальным переменным и переменным экземпляра, которые находятся в области. Введите Ctrl+D или quit
для того, чтобы возобновить программу Ruby.
на ruby-debug-base gem добавляет метод binding_n в ядро модуль, и это даст вам доступ к объекту привязки, который можно использовать в оценку чтобы предоставить доступ к переменным стека вызовов. Не забудьте выдать отладчик.старт чтобы включить отслеживание стека вызовов.
вот пример, показывающий его использование для анализа того, что происходит внутри irb (программа Ruby). Можно поставить требуютсяи отладчик.старт внутри вашего собственного кода 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 для того, что я думаю, это лучший доступ к стеку вызовов. Рубиниус обеспечивает эту возможность, встроенную в via Rubinius:: VM.backtrace.