如何在当前类的上下文中运行IRB.start

时间:2010-11-15 23:17:00

标签: ruby 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

有什么方法可以访问当前类中的本地变量和实例变量吗?

9 个答案:

答案 0 :(得分:36)

正如您已经发现的那样,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

答案 1 :(得分:14)

我建议在ripl中尝试这个,这是一个替代品。以上示例有效:

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

请注意,局部变量有效,因为您使用:binding选项传递当前绑定。

你可能会在irb中做同样的事情,但由于记录不完整且未经测试,你干净利落的可能性很小。

答案 2 :(得分:10)

您可以使用实例变量代替全局变量,例如:

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

>> @a
=> "hello"

答案 3 :(得分:10)

使用Pry

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

答案 4 :(得分:6)

以下是如何在调用IRB.start的上下文中从脚本调用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"

享受!

答案 5 :(得分:3)

从Ruby 2.4.0开始,你可以这样做:

require 'irb'
binding.irb

这将启动一个IBR REPL,您将拥有self的正确值,并且您将能够访问范围内的所有局部变量和实例变量。键入Ctrl + D或quit以恢复Ruby程序。

答案 6 :(得分:2)

我的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是一种获得漂亮提示的方法,但还有其他选择。并没有真正的理由来制作一个单独的方法,但就目前而言,你可以将它放在任何地方,它应该可以工作。

答案 7 :(得分:0)

ruby-debug-base gem为 Kernel 模块添加了binding_n方法,这将为您提供可在 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 > 

如果您愿意为1.9.2运行修补版本的Ruby,请参阅http://gitnub.com/rocky/rb-threadframe,我认为可以更好地访问调用堆栈。 Rubinius通过 Rubinius :: VM.backtrace 提供此功能。

答案 8 :(得分:0)

这个是这个线程中所有答案的混合。是产生提问者所请求行为的最小代码段:

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