27

我刚刚经历了 PragProg Continuous Testing With 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

有什么办法可以访问当前类中的局部变量和实例变量?

4

9 回答 9

39

正如您已经发现的那样,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
于 2011-02-13T23:51:58.897 回答
14

我建议在ripl中尝试这个,一个 irb 替代品。上面的例子有效:

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

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

你可以在 irb 中做同样的事情,但由于它的文档记录和未经测试,你干净地做到这一点的机会微乎其微。

于 2010-11-16T00:12:54.087 回答
11

您可以使用实例变量而不是全局变量,例如:

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

>> @a
=> "hello"
于 2010-11-15T23:28:47.920 回答
11

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

require 'irb'
binding.irb

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

于 2017-01-12T03:14:34.927 回答
10

使用

a = 'hello'
require 'pry'
binding.pry
于 2012-05-27T04:35:48.480 回答
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"

享受!

于 2011-08-13T04:46:14.243 回答
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'当然可以在文件的顶部。bananaand的设置@banana只是为了证明我可以从 irb 提示符访问它们。to_s 是获得漂亮提示的一种方法,但还有其他选择。并且没有真正的理由制作单独的方法,但就目前而言,您可以将它放在任何地方,它应该可以工作。

于 2015-12-05T04:05:39.870 回答
0

ruby-debug-base gem 向Kernel模块添加了 binding_n 方法,这将使您可以访问绑定对象,该对象可用于eval访问调用堆栈变量。请记住发出Debugger.start以打开调用堆栈跟踪。

这是一个示例,展示了它用于内省irb(一个 Ruby 程序)内部发生的事情。也可以将requireDebugger.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提供了这种内置功能。

于 2010-12-22T10:30:02.530 回答
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
于 2021-07-22T11:02:57.717 回答