rugby - interactive ruby download
Cómo ejecutar IRB.start en el contexto de la clase actual (8)
A partir de Ruby 2.4.0, puedes hacer esto:
require ''irb''
binding.irb
Esto iniciará un REPL de IBR en el que tendrá el valor correcto para self
y podrá acceder a todas las variables locales y variables de instancia que estén dentro del alcance. Escriba Ctrl + D o quit
para reanudar su programa Ruby.
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 IRB.start
en una clase, self está predefinido y se refiere al objeto en el que estábamos cuando se llamaba a start, lo que no es cierto en mi caso.
Incluso para ejemplos muy simples como
a = "hello"
require ''irb''
ARGV.clear # otherwise all script parameters get passed to IRB
IRB.start
Cuando intento acceder a la variable, obtengo lo obvio
NameError: undefined local variable or method `a'' for main:Object
Funciona solo cuando cambio a
variable global
$a = "hello"
require ''irb''
ARGV.clear # otherwise all script parameters get passed to IRB
IRB.start
entonces puedo acceder
irb(main):001:0> $a
=> 1
¿Hay alguna forma de evitar esto para acceder a las variables locales y de instancia en la clase actual?
Aquí le mostramos cómo invocar IRB desde su script en el contexto de donde llama a IRB.start ..
require ''irb''
class C
def my_method
@var = ''hi''
$my_binding = binding
IRB.start(__FILE__)
end
end
C.new.my_method
La ejecución de su script invocará a IRB. Cuando llegas 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"
¡Disfrutar!
Como ya descubrió, self
no se refiere al objeto donde se inició IRB, sino a TOPLEVEL_BINDING
, que parece ser una instancia de la clase Object
.
Aún puede ejecutar una sesión de IRB con una clase u objeto específico como contexto, pero no es tan simple como simplemente iniciar IRB.
Si lo que te interesa es iniciar IRB con un contexto específico, entonces es muy fácil hacerlo cuando inicias 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 mucha experimentación y excavación en el código fuente de IRB, pude encontrar 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
En lugar de global, podría usar variables de instancia, por ejemplo:
require ''irb''
@a = "hello"
ARGV.clear
IRB.start
>> @a
=> "hello"
La gema ruby-debug-base agrega un método binding_n al módulo Kernel y esto le dará acceso al objeto de enlace que se puede usar en una evaluación para dar acceso a las variables de la pila de llamadas. Recuerde emitir Debugger.start para activar el seguimiento de la pila de llamadas.
Aquí hay un ejemplo que muestra su uso para ver lo que sucede dentro de irb (un programa de Ruby). Uno también podría poner el '' debugger '' y el debugger.start dentro de su propio código 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 >
Si está dispuesto a ejecutar una versión parcheada de Ruby para 1.9.2, visite http://gitnub.com/rocky/rb-threadframe para ver qué es un mejor acceso a la pila de llamadas. Rubinius proporciona esta capacidad incorporada a través de Rubinius :: VM.backtrace .
Mi solución para Ruby 2.2.3. Es muy similar a la de 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 un aviso bastante pero hay otras opciones. Y no hay una razón real para hacer un método separado, pero tal como está, puedes colocarlo en cualquier lugar y debería funcionar.
Usar palanca
a = ''hello''
require ''pry''
binding.pry
Yo sugeriría probar esto en ripl , una alternativa irb. El ejemplo anterior funciona:
a = ''hello''
require ''ripl''
Ripl.start :binding => binding
Tenga en cuenta que las variables locales funcionan porque está pasando el enlace actual con la opción: binding.
Posiblemente podría hacer lo mismo en irb, pero como está poco documentado y no probado, sus posibilidades de hacerlo de manera limpia son escasas o nulas.