Как запустить IRB.start в контексте текущего класса

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

Есть ли способ обойти это, чтобы получить доступ к локальным переменным и переменным экземпляра в текущем классе?


person Jakub Arnold    schedule 15.11.2010    source источник


Ответы (9)


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

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

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

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

person cldwalker    schedule 16.11.2010
comment
К сожалению, в JRuby это невозможно. - person Don Werve; 10.03.2011
comment
Вы должны открыть задачу: github.com/cldwalker/ripl/issues. ripl был проверен для работы на jruby - person cldwalker; 11.03.2011
comment
Во-вторых, совет избегать IRB для этого. Прай - еще один вариант. - person Mark Reed; 27.05.2012

Как вы уже обнаружили, 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
person bryant    schedule 13.02.2011
comment
ИМХО единственный ответ, который отвечает на этот вопрос. - person amoebe; 13.03.2013
comment
Проголосовал, работает для меня и отвечает на исходный вопрос (вместо того, чтобы предлагать альтернативы), спасибо! - person dolzenko; 25.02.2014
comment
Эпичный ответ. Спасибо - у меня полностью работает (ruby 1.9.3) с первой попытки. Спасибо за эту работу, так что мне не нужно. - person Steve Midgley; 27.08.2014
comment
Черт возьми, здесь не работает. Может быть, это потому, что мы используем JRuby. :( - person Trejkaz; 01.08.2016

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

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

>> @a
=> "hello"
person Arkku    schedule 15.11.2010
comment
Что изменит использование переменных экземпляра? - person abcde123483; 09.10.2012
comment
@ulvund Ну, в отличие от локальных переменных, они остаются видимыми после IRB.start (т. Е. Замена @a в этом коде на a не сработает), но, в отличие от глобальных переменных, они не загрязняют глобальное пространство имен (только пространство имен основного объекта). В любом случае, в других ответах уже упоминаются способы заставить работать местных жителей, поэтому, если использование внешней библиотеки или дополнительная работа не являются проблемой, то это лучшие альтернативы. (Я бы предпочел Прай сам.) - person Arkku; 10.10.2012

Используйте Pry:

a = 'hello'
require 'pry'
binding.pry
person horseyguy    schedule 27.05.2012
comment
У меня есть некоторые проблемы с Ripl (который является принятым решением), который дает мне другой контекст, чем я ожидал. Так что при ближайшем рассмотрении я тоже голосую за Прая. - person abcde123483; 02.09.2013
comment
Не удалось заставить pry работать, возможно какая-то несовместимость /usr/local/share/gems/gems/term-ansicolor-1.2.2/lib/term/ansicolor.rb:188:in 'color': wrong number of arguments (0 for 1..2) (ArgumentError) from /usr/local/share/gems/gems/pry-0.10.0/lib/pry/output.rb:35:in 'decolorize_maybe' - person akostadinov; 13.06.2014

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

require 'irb'
binding.irb

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

person David Grayson    schedule 12.01.2017
comment
да! Просто нашел его здесь и захотел написать ответ.. . - person estani; 25.03.2020

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

Наслаждаться!

person Ben    schedule 13.08.2011
comment
Это может быть. Но вопрос в том, как запустить IRB.start в контексте текущего класса. Мой ответ - единственный, который действительно отвечает на вопрос. - person Ben; 09.09.2012
comment
Вы не можете получить доступ к локальным переменным, поэтому это не отвечает на вопрос. - person Nowaker; 10.11.2013

Мое решение для 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 — это один из способов получить красивое приглашение, но есть и другие варианты. И нет никакой реальной причины делать отдельный метод, но в его нынешнем виде вы можете использовать его где угодно, и он должен работать.

person pedz    schedule 05.12.2015

ruby-debug-base< /a> gem добавляет метод 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.

person Community    schedule 22.12.2010

Это своего рода смесь всех ответов в этой теме. Это наименьший фрагмент кода, который будет производить поведение, запрошенное человеком, который спросил:

require 'irb'

module Craft
  class Console
    def start
      IRB.setup(__FILE__)
      workspace = IRB::WorkSpace.new(self)
      IRB::Irb.new(workspace).run(IRB.conf)
    end

    def hello
      puts 'hello world!'
    end
  end
end

Нет необходимости очищать ARGV, а также нет необходимости явно вызывать eval_input.

Вот как выглядит вывод:

bundle exec craft console
irb(#<Craft::Console:0x00007fb0135592c0>):001:0>hello
hello world!
=> nil
person Sergio    schedule 22.07.2021