Cómo ejecutar IRB.comenzar en el contexto de la clase actual


Acabo de pasar por PragProg Pruebas Continuas Con Ruby, donde hablan de invocar IRB en el contexto de la clase actual para inspeccionar el código manualmente.

Sin embargo, citan que si invoca IRB.start en una clase, self está predefinido, y se refiere al objeto en el que estábamos cuando se llamó a start lo cual no es cierto en mi caso.

Incluso para un ejemplo muy simple como

a = "hello"
require 'irb'
ARGV.clear # otherwise all script parameters get passed to IRB
IRB.start

Cuando intento acceder a la variable a, obtengo la obvio

NameError: undefined local variable or method `a' for main:Object

Solo funciona cuando cambio a a variable global

$a = "hello"
require 'irb'
ARGV.clear # otherwise all script parameters get passed to IRB
IRB.start

Entonces puedo acceder a él

irb(main):001:0> $a
=> 1

¿Hay alguna forma de evitar esto para acceder a variables locales y de instancia en la clase actual?

 25
Author: Jakub Arnold, 2010-11-16

8 answers

Sugeriría probar esto en ripl, una alternativa de irb. El ejemplo anterior funciona:

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

Tenga en cuenta que las variables locales funcionan porque pasa el enlace actual con la opción :binding.

Posiblemente podría hacer lo mismo en irb, pero ya que está mal documentado y no probado, sus posibilidades de hacerlo limpiamente son escasas a ninguna.

 14
Author: cldwalker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-16 00:12:54

Como ya has descubierto, self no se refiere al objeto donde se inició IRB, sino a la TOPLEVEL_BINDING, que parece ser una instancia de la propia clase Object.

Todavía puede ejecutar una sesión IRB con una clase u objeto específico como contexto, pero no es tan simple como iniciar IRB.

Si te importa iniciar IRB con un contexto específico, entonces es muy fácil de hacer cuando estás iniciando IRB manualmente. Simplemente inicie IRB normalmente y luego llame al método irb, pasándole el objeto / clase que desee como contexto.

$ irb
irb(main):002:0> require 'myclass'
=> true
irb(main):003:0> irb MyClass
irb#1(MyClass):001:0> self
=> MyClass

También puede iniciar una sesión de IRB programáticamente y especificar el contexto, pero no es tan fácil como debería ser porque tiene que reproducir parte del código de inicio de IRB. Después de mucho experimentar y cavar en el código fuente de IRB, fui capaz de llegar a algo que funciona:

require 'irb'
IRB.setup nil
IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
require 'irb/ext/multi-irb'
IRB.irb nil, self
 36
Author: bryant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-14 00:06:23

En lugar de global, podría usar variables de instancia, por ejemplo:

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

>> @a
=> "hello"
 10
Author: Arkku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-15 23:28:47

Use Pry :

a = 'hello'
require 'pry'
binding.pry
 9
Author: horseyguy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-27 04:35:48

Aquí le mostramos cómo invocar IRB desde su script en el contexto de donde llama a IRB.empezar..

require 'irb'
class C
    def my_method
        @var = 'hi'
        $my_binding = binding
        IRB.start(__FILE__)
    end
end

C.new.my_method

Ejecutando su script invocará IRB. Cuando llegues al prompt tienes una cosa más que hacer...

% ./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"

Disfrute!

 6
Author: Ben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-13 04:46:14

Mi solución para Ruby 2.2.3. Es muy similar a Bryant

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>

Puedo acceder a variables locales y variables de instancia. El require 'irb' por supuesto podría estar en la parte superior del archivo. La configuración de banana y @banana es solo para demostrar que puedo acceder a ellos desde el indicador irb. El to_s es un método para obtener una solicitud bastante, pero hay otras opciones. Y no hay una razón real para hacer un método separado, pero tal como está, puede colocar esto en cualquier lugar y debería funcionar.

 2
Author: pedz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-05 04:48:54

A partir de Ruby 2.4.0, puedes hacer esto:

require 'irb'
binding.irb

Esto iniciará un IBR REPL donde tendrá el valor correcto para self y podrá acceder a todas las variables locales y variables de instancia que están en el ámbito. Escriba Ctrl + D o quit para reanudar su programa Ruby.

 2
Author: David Grayson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-12 03:14:34

El ruby-debug-base gem agrega un método binding_n al módulo Kernel y esto le dará acceso al objeto binding que se puede usar en una eval para dar acceso a las variables de la pila de llamadas. Recuerde emitir el depurador .inicie para activar el seguimiento de pila de llamadas.

Aquí hay un ejemplo que muestra su uso para hacer introspección de lo que está pasando dentro de irb (un programa Ruby). Uno podría poner los depuradores require y .start inside tu propio código Ruby también.

$ 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 > 

Si está dispuesto a ejecutar una versión parcheada de Ruby para 1.9.2, consulte http://gitnub.com/rocky/rb-threadframe para lo que creo que es un mejor acceso a la pila de llamadas. Rubinius proporciona esta capacidad incorporada a través de Rubinius::VM.backtrace .

 0
Author: rocky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-22 11:39:49