我一直在进行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
有什么方法可以访问当前类中的本地变量和实例变量吗?
答案 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