Как запустить 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"

использовать Pry:

a = 'hello'
require 'pry'
binding.pry

вот как вызвать 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.