この文章では、Ruby用のリファクタリングブラウザ、「Ruby Refactoring Browser」 の簡単な使いかたについて説明します。
ここでは、Emacs上から使うための設定を説明します。FreeRIDEでの使用については README.jaに書いてありますので、それを見てください。
まず、<URL:http://www.kmc.gr.jp/projects/rrb/>からRuby Refactoring Browser のソースアーカイブを取得します。そして、ファイルを展開します。 そこで生成された rrb-0.0.3 に移動し、README.jaを読んでください。
ruby setup.rb config ruby setup.rb setup (必要ならsu でルートユーザに移行) ruby setup.rb install
で必要なライブラリがインストールされます。
あとはelisp/rrb.elをEmacsのロードパスの通った場所にコピーすればインストールは 終了します。OSやディストリビューションによって場所は異なりますが、おそらく /usr/local/share/emacs/site-lisp/という名前のディレクトリがあると思います ので、そこにコピーしてください。
まずはアーカイブl(rrb-0.0.3.tar.gz)のdoc/rrbrc.sampleというファイルを ホームディレクトリに.rrbrcという名前でコピーしてください。 そしてファイルの内容を変更します。 RRB::CONF['TAB_WIDTH']でタブ文字1個分に相当するスペースの数を指定します。 RRB::CONF['INDENT_LEVEL']でインデント1個の深さに相当するスペースの数を 指定します。デフォルトはそれぞれ8、2です。
そして.emacsにrrb.elをロードするように記述します。
(load "rrb")
で良いでしょう。また、emacs側での設定は特に必要ないでしょう。
さらに細かい設定をしたい方は、rrb.elやrrbrc.sampleを調べてください。
まずは最もわかりやすいリファクタリングであるローカル変数名の変更をしましょう。 まず、sample.rbという名の以下の内容のファイルを用意します。
class Hello def initialize(people,greeting="Hello") @people = people @greeting = greeting end def hello @people.each do |p| print "#{@greeting} #{p}!\n" end end end if $0 == __FILE__ h = Hello.new(["yoshida","ohbayashi","hara"]) h.hello end
そして、
emacs sample.rb
とし、emacsを起動します。その後、M-x rrb-rename-local-variableと入力します。
するとまずはどのメソッドのローカル変数を変更するかを聞いてきます。 ここではHello#helloを選択しましょう。TABによる入力の補完が利用できます。
次に、リネームする変数を指定しましょう。ここではpを指定します。
最後に、新しい変数名を指定します。personと指定しましょう。
すると以下のように変更されるはずです。
class Hello def initialize(people,greeting="Hello") @people = people @greeting = greeting end def hello @people.each do |person| print "#{@greeting} #{person}!\n" end end end if $0 == __FILE__ h = Hello.new(["yoshida","ohbayashi","hara"]) h.hello end
さて、これで成功です。@peopleの「p」という文字が変わっていないのが単なる 置換をしているわけではないということを示しています。
次に、undoをしてみましょう。M-x rrb-undoで、今の変更がundoされます。
さて、次にHello#initializeのpeopleをgreetingに変更しようとしてみましょう。 上の場合と同じようにすれば良いです。すると、エラーが発生し、
fail to refactor: greeting: already used
と表示されます。このように、変更後の変数名が被る場合などは適切にエラーを 発生させます。
Ruby Refactoring Browserでは、ローカル変数名の変更の他に、以下のような リファクタリングが可能です。
[rename なんとか]というリファクタリングは、上で説明したのと同じようにして 動かすことができます。
Extract methodは、C-SPCなどでmarkした所と、カーソルの間が切り出す対象となります。 基本的にRuby Refactoring Browserは行単位の動作が基本なので、指定したリージョンを 適当に何行目から何行目を指定したと見なして動作します。
Pull up, Push down method, extract superclassは、現在カーソルがある行に新たな メソッド、クラスが生成されるということに注意してください。
現在のRuby Refactoring Browserには、様々な制限があります。それは主に Ruby という 言語が非常に動的な性質を持つということによるものですが、それとは別に、 Ruby Refactoring Browserの実装によるものも多くあります。その内容を説明するため、 Ruby Refactoring Browserの実装について簡単に解説します。
全体的な構図は以下の通りです。
----------------------------- | emacs interface(rrb.el) | ----------------------------- | pipeによるプロセス間通信 ----------------------------- | bin/rrb | |---------------------------| | コアライブラリ | | |--------------| | | Ripper改 | -----------------------------
emacs側では入力の処理などしかせず、メインの機能はすべてRubyで記述されています。 elisp上でする処理は以下の通りです。
で、その中では以下のような手順で処理がなされます。
ここで重要なのが(2)です。Rubyのリフレクション機能を使うためには実際にその スクリプトをRubyのインタプリタで動かす必要があるのです。しかし、それを してしまうと、リファクタリング中にファイルの編集がされたりwindowが開いたり しかねません。ここでRubyで良く使われるイディオム、 if $0 == __FILE__ ... end というのを利用します。 これで囲まれた部分は、他のファイルからrequireやloadで読みこまれた場合は 実行されないようになります。それを利用し、リフレクション利用時に 動作してほしくない部分をこれで囲んでしまえば良いのです。
というわけで、Ruby Refactoring Browserには以下のような制限があります。
スクリプトを定義部(リファクタリング時に実行されても良い部分)と 動作部(リファクタリング時に実行されると困る部分)に分け、動作部を if $0 == __FILE__ ... endで囲む必要がある。
理由は上に書いた通りです。
rename-methodは、同じ名前のメソッドをすべて変更してしまう「rename-method-all」と 指定したクラスのみのメソッドの名前のみを変更してくれるが、レシーバ付きの メソッドの面倒は見てくれない「rename-method」のどちらかしかできない。
Rubyは動的型付けな言語なので、Javaなどのような静的型付けな言語とちがって どこからどこまでを変えれば良いかは静的には判断できないため、このような 2つの機能を用意して、ユーザに選ばせるようにしています。
以上のことは、Ruby Refactoring Browserの基本的な設計やRubyの特徴上、 避けがたい機能制限です。開発が進んでもこのあたりの制限がなくなることはないでしょう。
emacs上に読みこまれているRubyのスクリプトとおぼしきファイル(*.rbという名の ファイルや、一行目に「#!/usr/bin/ruby」と書かれたファイル)は、すべて リファクタリング対象とみなされる。
これは、emacsにはIDEによくある「プロジェクト」という概念がないため、 どのファイルがリファクタリング対象でどれがそうでないのかを判定するの が難しいため、「それらしきもの全て」という安直な判定方法がユーザに とってもわかりやすいであろうと考えられるためです。
複数の関係ないrubyファイルを編集したければその数だけemacsを 起動してください。
その他、実装の不備等による以下の制限があります。
rename-methodやrename-instance-variableにおいて、attr_reader, attr_accessor, attr_writerで生成されたメソッドの面倒を見てくれない
なんとかしようとは考えていますがちょっと時間がかかりそうです。
使用できる文法に制限がある。
使っているパーサが古いripperを元にしているためです。RubyのCVSで実装されている Ripperの仕様が固まったらこちらに移行しようかと考えています。
リフレクションのための実行時には、グローバル変数「$rrb_run_for_reflection」が trueになっています。if $0 == __FILE__ ... end ではうまく処理できない部分がある 場合は、この変数を使いましょう。ただ、これを使うとプログラムが不自然になるので あまり使いすぎるのも良くないでしょう。
このツールは、まだまだ発展途上にあります。多くの人の要望、バグレポート、 patch、その他の開発に対する寄与を期待しています。新たな開発者も歓迎いたします。 RubyForgeを利用しており、CVSへのanonymous accessやBTSはここのを利用してください。 <URL:http://www.rubyforge.org/projects/rrb/>です。 また、私に直接メールを下さってもかまいません。
また、現在ではRubyRefactoringBroserを使えるのはemacsからとFreeRIDEからのみ です。vimから使えるようにしたり、コンソールからの利用をできるようにする 予定ですが、その他にも様々なエディタにも対応したいと思っています。ただ 我々だけではそれは大変なので、○○というエディタ(or IDE)に対応させよう、と いう人がいないかなあと思っています。そういうことに対する詳しい解説もその うち書きたいと思っています。