はるか昔に rirb (Remote IRB) というのを作って放置していましたが、github に登録して gem を公開してみました。せっかくなので再紹介。
インストール
$ gem install mame-rirb --source=http://gems.github.com/
使い方
以下は数字をカウントアップするだけのサンプルコード。
$i = 0 loop do p $i $i += 1 sleep 0.5 end
これを -rrirb 付きで実行します *1 。
$ ruby -rrirb count.rb 0 1 2 3
普通にカウントアップしますね。
別のターミナルから同じディレクトリで rirb を起動すると、count.rb にアタッチします。
$ rirb irb(rirb:count.rb):001:0>
普通の irb に見えますが、実はターゲットプロセス内で動いています。グローバル変数などを参照したり変更したりできます。
irb(rirb:count.rb):001:0> $i 202 irb(rirb:count.rb):002:0> $i = 10000 => 10000 irb(rirb:count.rb):003:0>
10000 を代入したので元のターミナルにも影響します。
240 241 242 10000 10001 10002 10003
p とかを呼ぶと、irb ではなくターゲットプロセスの出力になります。
irb(rirb:count.rb):003:0> p :foo => :foo irb(rirb:count.rb):004:0>
10123 10124 10125 :foo 10126 10127 10128
どうしても irb に出力させたいときは rp を使います。
irb(rirb:count.rb):004:0> rp :foo :foo => :foo irb(rirb:count.rb):005:0>
rirb を終了してもターゲットプロセス自体は終了しません。終了後には再接続できます。
irb(rirb:count.rb):005:0> exit $ rirb irb(rirb:count.rb):001:0>
いじめてみる。
irb(rirb:count.rb):001:0> def p(*args); raise; end => nil irb(rirb:count.rb):002:0>
10282 10283 10284 (irb):1:in `p': unhandled exception from count.rb:3:in `block in <main>' from count.rb:2:in `loop' from count.rb:2:in `<main>'
rirb の使いどころ
常駐型のプログラム (チャットの無能とか、Web アプリとか) を動的にデバッグしたり、その場しのぎの修正を適用したり、ちょっとした修正を再起動前にテストしたりできるかも。
再現性のないバグが出て今の状態がどうなってるか知りたいけど、ログを取るコードを書き足して再起動する必要があって、困ったなーというときとか。
再起動するのに結構時間がかかるから、ちょっとメソッド再定義して修正できるか試してみたいなー、というときとか。
rirb のやってること
ターゲットを -rrirb つきで起動すると、rirb 接続用の情報を記録したファイル count-
クライアントの rirb を起動すると、カレントディレクトリから *.rirb というファイルを探して、Unix ソケットに接続します。
すると -rrirb は接続を accept して、irb を require して、その入出力と Unix ソケットをつなぎます。以上。
rirb の問題点
グローバル変数や定数など、グローバルに参照できるところはアクセスしやすいけど、そうでないところは何かとアクセスしにくいです。ObjectSpace 、callcc 、set_trace_func などを駆使して頑張れば何とかなるところも結構あります。失敗したら致命的な変更しちゃって例外とかで落ちますけど。君の黒魔術力が試されている。
あと、セキュリティは一切考えていません。たぶん unix ソケットに読み書きできる人は誰でも乗っ取れます。バックドアを仕込んでるようなもんです。というかバックドアそのものです。
1.8 で使うために
もう 1.8 を使ってる人なんてほとんどいない (ことにしたい) 今日この頃ですが、一応 1.8 に対応しています。ただし -rrirb ではロードできません。
require "rubygems" require "rirb"
という start-rirb.rb とかいうファイルを作って、-rstart-rirb とでもしてください。ruby 1.8 の制約で -rrubygems -rrirb としてもダメなので注意。
*1:RUBYOPT=-rrirb などとしとくといいかもしれません。どうなっても責任は取りませんが。