Ruby's archives

NetBeans on windowsでLinux上のrubyプログラムをリモートデバッグするためのproxy

by ginriki | 9 月 27th, 2010 

NetBeansのリモートデバッグについて、以前の記事で、以下のように書きましたが、

注意点としては、リモートと同じフルパス上にソースコードを置く必要がある点です。リモート先で、/var/www/railsprjにRoRのプロジェクトコードが置いてあるなら、ローカルでも/var/www/railsprjにプロジェクトコードを置く必要があります。

このままだと、Windows上のNetBeansでLinux上のRailsがリモートデバッグできません。
そこで、NetBeansとFast Debugger(ruby-debug-ide)の間に挟むproxyを用意して、proxyの中でファイルパス変換することでリモートデバッグできるようにしました。1

せっかくなので、以下にproxyのソースコードを置いておきます(MIT Licenseにしときました)。

以下の構成で、基本的な機能(変数チェック、ステップ実行、breakpointセット辺り)が動作するのは確認しました。
Railsがデバッグできるかどうかは、まだ試してません。

[NetBeans (Debuger GUI)] on Windows
  ↓↑
[proxy] on Windows/Linux
  ↓↑
[ruby process (+ruby-debug-ide)]  on Linux

・CRuby 1.8.7 および JRuby 1.5.1でproxy動作確認した。

なお、実際にデバッグするときは、NetBeansが動くマシン上にもデバッグ対象のソースコードを置かないとソースコードデバッグできません。
例えば、ソースコードは以下のように配置します。

・ruby-debug-ide側
/home/user/script
|---test.rb
\---lib
      \---somelib.rb

・NetBeans側
C:\win_script
|---test.rb
\---lib
      \---somelib.rb


proxyを使ったデバッグの仕方


以下の手順でproxyの起動やNetBeansからproxyへの接続を行えば、後は普通にデバッグできます。

  1. ruby-debug-ide付きでRuby process起動(以前の記事参照)
  2. proxy起動(Linux上で起動するか、NetBeans on Windows上で起動するかはお好みで)。
  3. NetBeansでproxyへの接続。

1, 2の手順はどちらが先でもOKです。
1は以前の記事と同じなので、2, 3の説明だけ書きます。

2. proxy起動

proxyスクリプトをオプション指定して起動するだけです。proxyスクリプトのオプションは以下の通り。

Usage: ruby-debug-ide-proxy.rb -t  [-p listen_port] [--rdbprefix prefix] [--ideprefix prefix] [-d]

Example: ruby-debug-ide-proxy.rb -t localhost --rdbprefix "/home/user/script" --ideprefix "C:\\win_script" -d

Exampleの例を説明すると、

  • localhostのruby-debug-ideに接続して(接続先portは1234。指定するときはlocalhost:6000のようにする。)、
  • proxyを経由してNetBeansにデバッグデータ(XML)を送るときは、/home/user/script –> C:\\win_script への変換を行い、
  • proxyを経由してNetBeansからデバッグコマンド(breakpointセットなど)を指示するときは、C:\\win_script –> /home/user/scriptへの変換を行い、
  • proxy実行時は、DEBUGログを出力する

という指定になります。

3. NetBeansからproxyへの接続

NetBeansのメニューから「デバッグ -> デバッガを接続」を選択し、proxyが動作するホスト名やポート番号を指定して接続してください。
NetBeansからproxyに接続した時、proxyからruby-debug-ideへの接続が自動的に行われます。

正しくデバッガ接続できた場合、以下のようなログがproxyプログラムの端末に出力されます。

I, [2010-09-27T03:43:21.124000 #2240]  INFO -- : proxy will replace /home/user/script with C:\win_script
I, [2010-09-27T03:43:21.468000 #2240]  INFO -- : proxy listens on 0.0.0.0:7000
I, [2010-09-27T03:43:24.530000 #2240]  INFO -- : proxy connects to 192.168.1.7:1234
I, [2010-09-27T03:43:24.530000 #2240]  INFO -- : debug start

後は、普通にNetBeansのデバッガGUIが使えます。
例えば、デバッガGUIからbreakpointをセットすると、以下のログがproxy端末上に出ます。(-dを指定して起動した場合のみ)

D, [2010-09-27T03:43:24.546000 #2240] DEBUG -- : (ide -> proxy) b C:\win_script\test.rb:5
D, [2010-09-27T03:43:24.546000 #2240] DEBUG -- : (proxy -> rdb) b /home/user/script/test.rb:5
D, [2010-09-27T03:43:24.562000 #2240] DEBUG -- : (rdb -> proxy) 
D, [2010-09-27T03:43:24.624000 #2240] DEBUG -- : (proxy -> ide) 

手順は以上です。

Ruby/Railsの場合、デバッガはプログラムの動作理解に使うものというのが私の認識です2。なので、Ruby/Rails全般に慣れていない人がデバッガ利用者だと思うので、手順はもう少し簡潔にしたいですが、そうするにはIDE内を修正するしかないかな・・・。

  1. 本当は、NetBeansのIDE内部でファイルパス変換するように修正するべきだと思いますが、面倒だったのでproxyにしました。 []
  2. デバッグは、printfデバッグの方が効率がいいと思ってます。 []

NetBeansからRuby on Rails(RoR)のリモートデバッグ

by ginriki | 7 月 12th, 2010 

NetBeansには、rubyのデバッガフロントエンドが同梱されています。
今回は、それを使ってRuby on Rails(以下RoR)のリモートデバッグをするためのメモです。

個人的には、RoRが動いてるマシンにsshでログインした後、emacsとrubydb3x.elを使う方がメリットがある1と思いますが、emacsの場合デバッガ用GUIはありません。GUIに慣れてる人はNetBeansの方が楽なので、そういう人向けにまとめます。

なお、NetBeansをインストールしたローカルマシンで、RoRも動かす場合のデバッグ方法は、NetBeansのwikiを参照してください。

まあ、ローカルマシン上でデバッグする場合も、リモートマシンと接続してデバッグする場合もやり方はほとんど変わりません。

どちらの場合も、デバッギプロセス(と同じプロセス上で動くruby-debug-ide)がTCP(デフォルトだと1234)をListenし、Netbeansからそこに接続してデバッグを開始します。Netbeansから接続するデバッギプロセスがlocalhostにいるか、リモート先のホストにいるかの違いしかないです。
接続後、Netbeansからruby-debug-ideへbreakpointをしかける場所などを指示します。

デバッグ方法そのものは、ローカル・リモートともに一緒ですが、リモートのホストに対してローカルのNetBeansを接続してデバッグするには、リモート先のホストで以下の作業が必要です。

  1. rubyインストール (解説略)
  2. RubyGemsインストール (解説略)
  3. RoRインストール・プロジェクト作成 (解説略)
  4. ruby-debug-ideインストール
  5. リモート先のRoRプロジェクトのソースコードを、ローカルにも展開
  6. ruby-debug-ide付きでRoRプロジェクトのWebサーバ起動

4, 5, 6の手順は、あまりネット上で見かけないので順に解説します。

ruby-debug-ideインストール

Linuxだと、gemで簡単にインストールできます。(gcc, ruby-develをインストールしておけば。)
Fedora8だと以下で終わりでした。

$ gem install ruby-debug
$ gem install ruby-debug-ide

Windows(のmswin版ruby)の場合、コンパイル環境の用意が面倒なので、コンパイル済みgemをダウンロードした上でインストールします。2

> gem install -l linecache-0.43-mswin32.gem
> gem install -l ruby-debug-base-0.10.3-mswin32.gem
> del ruby-debug-base-0.10.3-mswin32.gem
> gem install ruby-debug
> gem install ruby-debug-ide -v 0.4.6

コンパイル済みgem (linecache, ruby-debug-base)は以下2つのリンク先から
それぞれダウンロードしてください。

リモート先のRoRプロジェクトのソースコードを、ローカルにも展開

ローカルにもソースコードを置かないと、breakpointで止まった時にNetbeansにソースコードが読み込まれません。
注意点としては、リモートと同じフルパス上にソースコードを置く必要がある点です。リモート先で、/var/www/railsprjにRoRのプロジェクトコードが置いてあるなら、ローカルでも/var/www/railsprjにプロジェクトコードを置く必要があります。3

この辺の挙動理解には、NetBeans wikiのChecking debugger engine functionalityとか、ruby-debug-ide protocolとかを参考にして、実際にruby-debug-ideとtelnetで会話するといいです。

ruby-debug-ide付きでRuby on RailsプロジェクトのWebサーバ起動

以下な感じでRoR Webサーバ起動します。(-dも付けると、NetBeansとの通信時の処理が良く見えます。)

$ rdebug-ide --stop script/server -h 0.0.0.0
Fast Debugger (ruby-debug-ide 0.4.6) listens on 0.0.0.0:1234

NetBeansからruby-debug-ideに接続

NetBeans IDEのメニュー -> デバッグ -> 接続をクリックし、ダイアログにリモート先ホスト名とポート番号(1234)を指定して接続します。
–stopを指定してあるので、RoRの処理先頭でstopするはずです。
あとは、IDEからbreakpoint指定するなり、なんなり自由に操作します。
watch変数として、paramsを追加しておくといい感じです。

  1. sshのポート(TCP 22)が開いてることは普通にありますが、デバッガ・デバッギ通信用のポートがfirewallで遮断されることは良くあります。 []
  2. 最近だと、Visual C++は無料で手に入りますがmswin版rubyをコンパイルしたVisual C++と同じバージョンをインストールする必要しないと「MSC version unmatch」エラーが出てめんどくさいです。http://rubyforge.org/tracker/index.php?func=detail&aid=16774&group_id=1900&atid=7436 []
  3. WindowsとLinuxだとフルパスを一致させることが不可能なので、例えば、Windows上のNetBeansからLinux上のRoRがデバッグができないという点でかなりアレな仕様です。誰もやってないなら修正パッチ作るかな。 []

Ruby/Jrubyの実行時フックの低レベルAPI

in Java, Ruby
by ginriki | 4 月 8th, 2010 

メモ。

RubyやJRubyには、特定の条件(コード行が変化したとか、メソッドを呼び出したなど)を満たした時に呼び出されるフック処理を定義することができます。

Ruby-1.8の低レベルなフック追加API(=C言語API)は、
thread.cのrb_add_event_hook関数。シグネチャは以下。

  • void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data);

JRuby-1.4.0で上記関数にほぼ対応するのは、Ruby.java
Ruby.addEventHookメソッド。シグネチャは以下。

  • public void addEventHook(EventHook hook)

引数の型であるEventHookやその継承クラスは以下参照。

フックAPIの挙動についての仕様書って、どこにもない感じだなあ。

JRuby実行中にjdbでアタッチしてrubyプログラムのbacktrace

in JDB, Ruby
by ginriki | 1 月 12th, 2010 

Ruby自身のデバッガだと、任意のタイミングでアタッチしてbacktrace見るといったことができない(はず)なので、もっと低レベルのデバッガを利用して、それを実現する方法についてメモ。今回はJRubyに対してjdbでアタッチ&backtraceする方法についてのメモです。
なお、CRubyの場合は、以前の記事とか、さらにその先のリンク先を見れば良いです。
ちなみに、今回試したJRubyのversionは以下の通り。OSはCentOS 5.3です。

$ jruby --version
jruby 1.3.1 (ruby 1.8.6p287) (2009-06-15 2fd6c3d) (Java HotSpot(TM) Client VM 1.6.0_16) [i386-java]


jdbによるbacktrace



まず、アタッチ対象例のRubyコードとして以下を用意。

sample.rb

RUBY:
  1. def foo
  2.   p "abc"
  3.   bar
  4. end
  5.  
  6. def bar
  7.   p "foge"
  8.   sleep(1000)
  9. end
  10.  
  11. abc

そして、デバッガattach用のポートを開けて起動します1

$ jruby -J-Xdebug -J-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n sample.rb

barメソッド内のsleepの実行をしているうちに、別のターミナルを開いてjdbでアタッチし、スレッドを停止させます。

$ jdb -attach 8000
> suspend
すべてのスレッドが中断されました

そして、backtraceを表示させます。以下の方法で大体表示できます(いい加減なやり方ですが)。

> threads
グループ system:
  (java.lang.ref.Reference$ReferenceHandler)0x9db Reference Handler 状況待機中
  (java.lang.ref.Finalizer$FinalizerThread)0x9da  Finalizer         状況待機中
  (java.lang.Thread)0x9d9                         Signal Dispatcher 実行中
グループ main:
  (java.lang.Thread)0x1                           main              状況待機中
> thread 0x1
main[1] where
  [1] java.lang.Object.wait (ネイティブ メソッド)
  [2] org.jruby.RubyThread.sleep (RubyThread.java:718)
  [3] org.jruby.RubyKernel.sleep (RubyKernel.java:725)
  [4] org.jruby.RubyKernel$s_method_0_1$RUBYINVOKER$sleep.call (null)
  [5] org.jruby.internal.runtime.methods.JavaMethod$JavaMethodN.call (JavaMethod.java:620)
  ...(以下略)
main[1] up 4
main[5] locals
メソッド引数:
context = instance of org.jruby.runtime.ThreadContext(id=2527)
self = instance of org.jruby.RubyObject(id=2528)
clazz = instance of org.jruby.MetaClass(id=2529)
name = "sleep"
arg0 = instance of org.jruby.RubyFixnum(id=2531)
block = instance of org.jruby.runtime.Block(id=2532)
ローカル変数:
main[5] dump context.frameIndex
 context.frameIndex = 2 #frameStackの上限(たぶん)
main[5] dump context.file + ":" + context.line
 context.file + ":" + context.line = "sample.rb:7" #現在位置
main[5] dump context.frameStack[2].fileName + ": " + context.frameStack[2].name + ":" + context.frameStack[2].line
 ...(省略)... = "sample.rb: bar:2" #1つ前のスタックフレーム
main[5] dump context.frameStack[1].fileName + ": " + context.frameStack[1].name + ":" + context.frameStack[1].line
 ...(省略)... = "sample.rb: foo:10" #2つ前のスタックフレーム
main[5] dump context.frameStack[0].fileName + ": " + context.frameStack[0].name + ":" + context.frameStack[0].line
 ...(省略)... = ": null:0" #トップレベル

以上になります。

  1. ここの「Debugger」の項目を参考にオプション設定しました。 []

autoloadで自動読み込みされないファイルをユーザアクセス毎に再読み込みする

by ginriki | 12 月 31st, 2009 

今回はRuby on Railsについてメモ。

autoloadで自動読み込みされないファイル1をcontrollerの中などでrequireを使って読み込んだ場合、そのファイルはdevelopmentモード時でもユーザアクセス毎に再読み込みされません。そのため、ファイルを編集してもwebサーバを再起動しないと変更が反映されないので、デバッグがやりづらくて困ってました。

解決策がないかと思って、ググってみたところ、以下のページに解決策が書いてありました。
http://www.pistolfly.jp/weblog/2007/06/require-dependency.html

requireじゃなくて、require_dependencyでファイル読み込みすれば良いということでした。
require_dependencyの挙動については、上のリンク先を参照してください。
この辺り(autoload周辺)って、Ruby on Rails公式なドキュメントが見当たらないですねえ。・・・実装を読めってことか。

以下、問題のコード例と解決方法。(ruby 1.8.5, rails 2.3.3, WEBrick 1.3.1で実験しました。)

ファイル読み込みの対象となるファイルを用意します。

<rails_project>/lib/hoge_fuga.rb

RUBY:
  1. class Hoge
  2.   def hoge
  3.     return "abc"
  4.   end
  5. end

上のファイルを読み込むcontrollerを用意します。
このとき、requireの代わりにrequire_dependencyでファイルを読み込みます。

<rails_project>/app/controllers/test_controller.rb

RUBY:
  1. #require 'hoge_fuga'
  2. require_dependency 'hoge_fuga'
  3.  
  4. class TestController <ApplicationController
  5.   def hoge
  6.     a = Hoge.new
  7.     render(:text => a.hoge)
  8.   end
  9. end

んで、Webサーバを起動。

$ cd <rails_project>
$ ruby script/server

で、ブラウザからtest/hogeにアクセスすると、ブラウザ上にabcが出力されます。
この状況で、<rails_project>/lib/hoge_fuga.rbを以下のように書き換えてみます。

RUBY:
  1. class Hoge
  2.   def hoge
  3.     return "123"
  4.   end
  5. end

変更を保存した後、ブラウザからtest/hogeにアクセスすると、ブラウザ上に123が出力されます。
確かに、Webサーバを再起動しなくても、<rails_project>/lib/hoge_fuga.rbが再読み込みされてますね。

これでデバッグがしやすくなるなあ。

  1. Railsの場合、クラス定義と定義が記述されたファイルの名前が対応づいてないとautoloadで自動読み込みされません(たぶん)。例えば、FooBarというクラスの定義はfoo_bar.rbというファイルの中で行われてないとautoloadにされません。 []

rubyプロセスのコアファイルからバックトレースする。(おまけ: python)

by ginriki | 11 月 9th, 2009 

某所で、「rubyプロセスがSEGVとかした時のコアファイルから、rubyスクリプトのバックトレースは取れるの?」って聞かれたので、ちょっと調べてみました。

結論としては、「rubyメソッドの呼び出し位置(ファイル名, 行番号)は取れるけど、呼び出し時の実引数を見るのは難しい」っていう感じです。
rubyのスタックフレームは、当然、rubyプロセスのメモリ上に構築されるので、スタックフレームのデータ構造さえわかれば、ある程度は表示できます。

ただ、コアファイルを出力したrubyプロセスはすでに存在しないので、ruby実装に使われているC関数をデバッガで(正確にはrubyプロセス上で)実行することができません。
そのため、オブジェクトのinspectなどを実行することが難しく、実引数のオブジェクトが何か調べることが困難です。ということで、今回はスタックフレームを表示させる方法を以下で説明します。

rubyスタックフレームを表示するGDBスクリプトは以下になります(rb_dump.gdb)。
(moriyoshiさんのブログ「 GDBで実行中のスクリプト言語のスタックフレームをダンプしてみる試み」のコードをほとんどそのまま使わせていただきました。ありがとうございます。)

CODE:
  1. define dump_rb_bt_from_core
  2.   set $t = ruby_frame
  3.   while $t
  4.     printf "[0x%08x] ", $t
  5.     if $t->node.nd_file
  6.       printf "(%s:%d)\n", $t->node.nd_file, ($t->node.flags>> 19) & ((1 <<(sizeof(NODE*) * 8 - 19)) - 1)
  7.     else
  8.       printf "(UNKNOWN)\n"
  9.     end
  10.     set $t = $t->prev
  11.   end
  12. end
  13.  
  14. document dump_rb_bt_from_core
  15.   dumps the current frame stack from core file. usage: dump_rb_bt_from_core
  16. end

今回はサンプルプログラムとして、0アドレスにアクセスするC拡張ライブラリを実行するrubyスクリプトを用意します。
まず、C拡張ライブラリ(segv.c)、

C:
  1. #include "ruby.h"
  2.  
  3. VALUE do_segv(VALUE self){
  4.         *(char*)(0x0) = 0;
  5.         return Qnil;
  6. }
  7.  
  8. void Init_segv(){
  9.         VALUE module;
  10.         module = rb_define_module("Segv");
  11.         rb_define_module_function(module, "do_segv", &do_segv, 0);
  12. }

で、後は適当にextconf.rbを作って、segv.soを作ります。

次にsegv.soを呼び出すRubyスクリプト(test.rb)。

RUBY:
  1. require 'segv'
  2.  
  3. def test1(a)
  4.   test2(a)
  5. end
  6.  
  7. def test2(b)
  8.   Segv::do_segv
  9. end
  10.  
  11. test1(1)

さて、サンプルプログラムの用意ができたので、SEGVさせてみます。

$ ulimit -c unlimited
$ ruby test.rb
test.rb:8: [BUG] Segmentation fault
ruby 1.8.5 (2006-08-25) [i386-linux]

アボートしました (core dumped)

これでコアファイルが出力されたので、それをGDBで解析します。

今回はCentOS 5.3のyumでインストールしたrubyから出力されたコアファイルなので、
GDBで解析するには、ここからrubyのdebuginfoをインストールしておく必要があります。

$ wget http://debuginfo.centos.org/5/i386/ruby-debuginfo-1.8.5-5.el5_3.7.i386.rpm
# rpm -i ruby-debuginfo-1.8.5-5.el5_3.7.i386.rpm

それでは、GDBでコアファイルからrubyのバックトレースをさせてみます。

$ gdb ruby core.29443
GNU gdb Fedora (6.8-37.el5)
...
(gdb) backtrace   #通常のC関数レベルのバックトレース。
#0  0x00b3a402 in __kernel_vsyscall ()
#1  0x0056fdf0 in raise () from /lib/libc.so.6
#2  0x00571701 in abort () from /lib/libc.so.6
#3  0x00c514c2 in rb_bug (fmt=) at error.c:214
#4  0x00cbd80b in sigsegv (sig=) at signal.c:537
#5  [signal handler called]
#6  do_segv (self=3086662140) at segv.c:4
#7  0x00c54dd5 in call_cfunc (...) at eval.c:5657
#8  0x00c5c4ab in rb_call0 (...) at eval.c:5810

(gdb) source rb_dump.gdb      #この記事の最初に作ったGDBスクリプトをロード
(gdb) dump_rb_bt_from_core  # rubyバックトレース
[0xbfc1da20] (test.rb:8)
[0xbfc1e0e0] (test.rb:4)
[0xbfc1e7c0] (test.rb:11)
[0x00d0f960] Cannot access memory at address 0x4

以上になります。

pythonもコアファイルからスタックフレームが取れるか調べてみましたが、
DebuggingWithGDBのgdbinitスクリプトを読む限り、
rubyと同様に実行していたpythonスクリプトのファイル名と行番号は取れそうです。

rexml vs libxml-ruby

in Ruby, XML
by ginriki | 11 月 15th, 2008 

研究の関係で、37MBのXMLファイルに対して、XPathでデータを取り出したいという状況が起きました。

プロトタイプ実装として、Ruby標準ライブラリのrexmlでその処理プログラムを記述したのですが、
実行時にメモリを700MB以上消費しやがります1
768MBしか積んでない私のマシンでは、メモリスワップが発生しまくりで実行が極端に重いため、
別の手段を考えることにしました。

手段としては2通り思いつきます。

  1. メモリ使用効率の良いXMLライブラリを使う
  2. SAX2を使う

SAXで処理を実装するのはダルイので、今回は1番の手段にしました。

採用したXMLライブラリは、libxml-rubyっていうlibxmlのRuby Bindingです。
libxmlは、C言語で実装されたXMLライブラリで、libxml-rubyは、それをRubyで呼び出せるようにしたものです。

rexmlはRubyコードのみで実装されたXMLライブラリでして、libxml-rubyより実行速度が遅いです3
きっと、メモリ使用効率もlibxml-rubyの方がいいだろうと期待して、採用しました。

rexmlはRuby1.8では標準で入ってますが、libxml-rubyは自分でインストールする必要があります。
インストールは、RubyGemsを使うと楽です。

私のCentOS 5環境下では以下の手順でインストールできました。

$ yum install libxml2-devel
$ yum install ruby-devel
$ gem install libxml-ruby

libxml-rubyを使って実装しなおしてみたところ、実行時の消費メモリは最大でも360MB程度になりました。
これなら、私のマシンで普通に実行できます。

XMLファイルサイズ、ライブラリ、メモリ消費量

DOM操作にしろXPathにしろ、DOM Treeを作るのに一番メモリを消費すると
思うんですが、実際、どの程度メモリを消費するのか気になったので、適当にググって見つけた
リンクを貼っときます。

  1. 途中で実行を中断したため、最大でメモリをどのくらい消費するかは不明 []
  2. Simple API for XMLの略 []
  3. http://libxml.rubyforge.org/参照 []

ActiveRecordの予約語とテーブルのカラム名

by ginriki | 10 月 2nd, 2008 

RubyのActiveRecord1ライブラリを使ってDB操作をした際、ハマッたのでメモ。

ActiveRecordを使うと、DBテーブルのレコードをオブジェクトとして取り出せます。
カラムのデータを取得する際は、カラム名をメソッド名としてオブジェクトを操作すれば良いのですが、
ActiveRecordの予約語と同じカラム名が存在すると正しく動作しません。

例えば、type, displayなどが予約語です。

ためしに、下記のようにテーブルとレコードをつくり、

$ mysql -u root test

CREATE TABLE apples
(
    id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
    type VARCHAR(20) NOT NULL
);
INSERT INTO apples (type) VALUES ('fuji');

以下のRubyコードを書いたとします。

RUBY:
  1. require 'rubygems'
  2. require 'activerecord'
  3.  
  4. ActiveRecord::Base.establish_connection(
  5.  :adapter => 'mysql',
  6.  :host => '127.0.0.1',
  7.  :username => 'root',
  8.  :password => '',
  9.  :database => 'test'
  10. )
  11.  
  12. class Apple <ActiveRecord::Base
  13. end
  14.  
  15. a = Apple.find(1)
  16. p a.type

上記コードを実行すると下記のような例外がでます。

/usr/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/base.rb:1434
:in `instantiate': The single-table inheritance mechanism failed to locate
the subclass: 'fuji'. This error is raised because the column 'type' is reserved
for storing the class in case of inheritance. Please rename this column if you didn't
intend it to be used for storing the inheritance class or overwrite
Apple.inheritance_column to use another column for that information.
(ActiveRecord::SubclassNotFound)

typeの場合は、上記のようにわかりやすい例外がでます。displayという名前のカラムが
ある場合は特に例外が発生しません。でも、a.displayと実行してもカラムのデータとは異なる値が
返ってくるので厄介です。

対処法としてはカラム名を変更(type -> etypeとか)するのが普通です。

カラム名を変更したくない場合はselectで別名を指定すれば、問題を回避できます。

RUBY:
  1. a = Apple.find(1, :select => 'type as etype')
  2. p a.etype  # "fuji"

ActiveRecordの予約語として何があるかは、下記リンクを見てください

その他参考文献

http://uruseiyatsura.way-nifty.com/blog/2007/08/ruby_on_rails.html

  1. ActiveRecordってデザインパターン名だったんだね。知らなかった・・・。http://ja.wikipedia.org/wiki/Active_Record []

Bilingual Debuggerプロジェクト紹介(だけしとく・・・)

by ginriki | 1 月 24th, 2008 

ひさびさ。

実はだいぶ前なんですが、Sourceforge.jpにプロジェクト立ち上げました。

http://sourceforge.jp/projects/bdbg

C/C++とRubyという2つの言語のデバッガ(gdbとrdb)を切り替えて使うことを可能にするツールをアップしてあります。

VC# 2003以降のデバッガに備わっている、C#とC/C++デバッガの切り替え機能を想像してもらえれば大体あってます。

詳しくはWikiに書いたマニュアルを見てください。

Fedora6でしか動作チェックしてないので、うまくコンパイルできないとかあれば、ブログコメントかSorceforgeのフォーラムにでも書いてくれれば対処します。

#返事が遅くても怒らないでくだせえ

バイト+就活+レポートのトリプル攻撃のため、このプロジェクトに割り当てる時間がないれす。

補足

by ginriki | 6 月 5th, 2007 

バックトレースがうまくとれない件ですが、これに対処しようとした場合、test.rbのdebuggerメソッドでbreakした瞬間に、Ruby言語上のstack frameに関するデータ構造をアセンブラ(C言語)レベルで読み込んで解析する必要があると思います。

でも、これを言語実装者側のサポートなしに実現するのはかなり大変でしょう。大変な理由を思いつく限りで列挙すると、

  • Rubyのstack frameを拡張ライブラリが直接読み込むことができない?
    • Rubyの実装に詳しいわけではないので、直接読み込めないというのは勘なのですが、セキュリティの観点から考えて、拡張ライブラリがstack frameをいじれないのは当然だと思う
  • debuggerとdebuggeeが同一プロセスであるため、Rubyのstack frameに直接アクセスできたとしても、stackのどの部分がdebuggerのもので、どの部分がdebuggeeのものか区別が難しい
    • 同一プロセス上にあるから、gdbで言うところのprintコマンドが簡単に実現できるという利点もあるのですが、バックトレースは実装しにくくなります
    • debuggerとdebuggeeが同一プロセスであるというのは、C, Javaのデバッガと大きく異なる点でもあります

こんな感じでしょうか。

まあ、PythonやRubyの場合は、printやpを使ってデバッグしろってことでしょうかねえ。

ブログで紹介した商品

  • Image of デバッガの理論と実装 (ASCII SOFTWARE SCIENCE Language)