Как запустить IRB.start в контексте текущего класса - PullRequest
26 голосов
/ 16 ноября 2010

Я только что прошел 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 ]

36 голосов
/ 14 февраля 2011

Как вы уже обнаружили, 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
14 голосов
/ 16 ноября 2010

Я бы предложил попробовать это в ripl , альтернативе irb.Вышеприведенный пример работает:

a = 'hello'
require 'ripl'
Ripl.start :binding => binding

Обратите внимание, что локальные переменные работают, потому что вы передаете текущую привязку с опцией: binding.

Возможно, вы можете сделать то же самое в irb, но так как это плоходокументированные и непроверенные, ваши шансы сделать это чисто невелики.

10 голосов
/ 27 мая 2012

Использование Прай :

a = 'hello'
require 'pry'
binding.pry
10 голосов
/ 16 ноября 2010

Вместо глобальных вы можете использовать переменные экземпляра, например ::100100

require 'irb'
@a = "hello"
ARGV.clear
IRB.start

>> @a
=> "hello"
6 голосов
/ 13 августа 2011

Вот как вызвать IRB из вашего скрипта в контексте того, где вы вызываете IRB.start.

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"

Наслаждайтесь!

3 голосов
/ 12 января 2017

Начиная с Ruby 2.4.0, вы можете сделать это:

require 'irb'
binding.irb

Это запустит REPR IBR, где у вас будет правильное значение для self, и вы сможете получить доступ ко всем локальным переменным и переменным экземпляра, которые находятся в области видимости. Введите Ctrl + D или quit, чтобы возобновить вашу программу Ruby.

2 голосов
/ 05 декабря 2015

Мое решение для 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>

Я могу получить доступ к локальным переменным и переменным экземпляра. require 'irb', конечно, может быть в верхней части файла. Настройка banana и @banana просто подтверждает, что я могу получить к ним доступ из приглашения irb. To_s - это один из способов получить довольно быстрое приглашение, но есть и другие варианты. И нет никакой реальной причины делать отдельный метод, но в его нынешнем виде вы можете использовать его где угодно, и он должен работать.

0 голосов
/ 22 декабря 2010

Gem ruby-debug-base добавляет метод binding_n в модуль Kernel , и это даст вам доступ к объекту привязки, который можно использовать в eval для предоставления доступа к переменным стека вызовов. Не забудьте ввести Debugger.start , чтобы включить отслеживание стека вызовов.

Вот пример, демонстрирующий его использование для анализа того, что происходит внутри irb (программа Ruby). Можно также поместить require и 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 .

...