[Japanese] [English]


Index


Title: RCtool 〜設定ファイル更新ツール〜

RCtool はユーザ初期設定ファイルを自動的に作成・更新する小さなツールです。

あなたの開発中ソフトウェアにもおひとつどうぞ。

最新Version 1.1.1

1 What's new

1.1 [2005/12/11] 1.1.1 released

Windowsで誤動作するバグを修正。

1.2 [2005/11/15] 1.1.0 released

Unix系OSにおいてユーザ全員がRCtoolによる設定を行えるように、組み込み方を変更しました。 ユーザ側の使い方には影響はありません。

2 概要

RCtool はユーザ初期設定ファイル(rcfiles)を自動的に作成・更新する小さなツールです。

ソフトウェアにはそれぞれ設定ファイルが存在します。 たとえばEmacsには .emacs、zshには .zshrc などです。

Emacsに新たなEmacsLispプログラムを導入する場面を考えてください。 load-pathの通ったディレクトリに .el ファイルをコピー・バイトコンパイルします。 最後に .emacs に設定を書く必要があります。

一度導入するだけなら、 .emacs の更新は楽だと思います。 しかし、EmacsLispプログラムをアップグレードする場面を想像してください。 EmacsLispコードが大幅に書き換えられ、設定にも互換性がなくなった場合、ユーザはいちいち手で .emacs を書き換えなくてはなりません。 非常に面倒な上、エラーに遭遇しやすいですね。 かといって .emacs を何も言わずに勝手に書き換えてしまうのも気がひけます。

RCtoolはこの面倒な作業を肩代りしてくれます。 RCtoolは以下の作業を別個に行います。

ユーザはどの部分が書き変わったのかがはっきりわかります。

開発者は定義スクリプトを書き換えるだけで、ユーザに適切な設定ファイルを提供できます。

3 動作環境

Rubyで記述されているので、Rubyが必要です。

Debian GNU/Linux及びWINEで動作確認しています。 Unix系OS、Windowsともに動作すると思われます。

4 使い方(ユーザ向け)

いちユーザとしてRCtoolを使うのは、とても簡単です。 必要なツールはRubyのみです。

RCtoolが組み込まれたソフトウェアがインストールされているとします。 その中に定義スクリプト foo-rctool があるとします。

設定ファイルを書き換え準備

foo-rctool -p

相異点を出力(要diffコマンド)

foo-rctool -d

設定ファイルの変更を反映する(インストール)

foo-rctool -i

変更を取り消す(アンインストール)

foo-rctool -u

オプションを表示する

foo-rctool

たったこれだけです。

Windowsの人はruby -Sをつける必要があると思います。

4.1 重要な注意点!

Beginning of the foo block:からEnd of the foo block.までを「ブロック」と言いますが、ブロックの内部(コメント含む)を書き換えないでください! 書き換えてしまったら、再度RCtoolを実行したときに変更結果が失われます。 初期値を変更したい場合は、ブロックの下で値を上書きしてください。

ただし、ブロックを丸ごと任意の場所に移動することはできます。 RCtoolに挿入されたブロックの場所が気に入らない場合は、適切な部分に移動してください。 次回RCtoolを実行したとき、RCtoolはブロックを見つけ出して適切に書き換えてくれます。

5 使い方(開発者向け)

設定ファイルがあるプログラムを開発するならば、是非ともRCtoolを組み込みましょう。

5.1 ダウンロード・展開

以下のコマンドを実行してください。

ruby -ropen-uri -e 'URI("http://www.rubyist.net/~rubikitch/archive/rctool-1.1.1.tar.gz").read.display' > rctool-1.1.1.tar.gz
tar xzvf rctool-1.1.1.tar.gz

失敗する場合は次のリンクからダウンロードしてください。

5.2 インストール

バージョン1.1.0よりRCtoolは開発者のシステムにインストールして使うようになりました。

ruby setup.rb

でインストールしてください。

5.3 組み込み方

バージョン1.1.0より組み込み方が変わりました。 開発者は定義スクリプトを作成した後、 mkrctool コマンドでRCtool本体と定義スクリプトをひとつのスクリプトに統合します。 そうすることで統合スクリプトをプロジェクト本体とともにインストールすることができます。

定義スクリプト foo-rctool.rb があるとします。 そして、実行可能ファイルを置くディレクトリを bin/ とします。 このとき次のコマンドを実行すると、統合されたスクリプト bin/foo-rctool が生成されます。

mkrctool foo-rctool.rb > bin/foo-rctool

当然、定義スクリプトを更新するたびに統合スクリプトも更新する必要があります。 MakeやRakeなどのツールを使ってください。

5.4 基本的な概念

RCtoolの説明において、以下の概念を定義します。

コード片

ここでは、設定ファイルの内容の一部を指すことにします。

ブロック

RCtoolを実行することによって、追加・更新されるコード片をブロックといいます。 ブロックの名前がfooであるとき、ブロックは

Beginning of the foo block:

から始まり、

End of the foo block.

で終わります。

テンプレート

設定ファイルが存在しない場合、RCtoolは新たに設定ファイルを生成します。 そのとき、ブロックの前後の内容を定義するのがテンプレートです。 ブロックはテンプレート内部では「%s」と表現されます。 (IO#printfが呼ばれます)

ユーザ設定エリア

ブロックの下の部分はユーザ設定エリアです。 ブロックで定義された設定を上書きするのに使います。

パッチ

設定ファイル名、ブロック、テンプレートなどの情報をひとまとめにしたものを「パッチ」と呼びます。 diff/patchコマンドとは無関係です。 RCtoolが独自の方法で設定ファイルにパッチを当てていると考えてください。

5.5 定義スクリプト

インストールしたら、次はRCtool「定義スクリプト」を書きます。 定義スクリプトは純粋なRubyスクリプトです。

定義スクリプトでやることは次の2つです。

  1. RCtoolオブジェクトを作成。
  2. パッチを定義。(複数可)
RCtool.new(params)

RCtoolオブジェクトを作成します。 paramsはHashでキーと値の対応は次の通りです。[]内はデフォルト値。 通常使用では :name さえ定義しておけば十分です。

name ブロック名
backup_dir バックアップディレクトリのパス [ "backup" ]
home_dir 設定ファイルを置くディレクトリのパス。[ ENV['HOME'] ]
RCtool#define_patch(file, block, comment_start, template="%s", where=:append) 設定ファイルfileへのパッチを定義します。

comment_startは行頭コメント開始文字列です。 whereが :append のとき、ブロックはファイルの末尾に置かれます。 whereが :prepend のとき、ブロックはファイルの先頭に置かれます。

RCtoolが定義しているメソッドはこれだけです。 設定のデフォルト値(つまりブロックの内容)を求めるのは定義スクリプトの仕事です。

5.6 使用例

RCtoolの具体的な使い方については、RCtoolを導入しているプログラムの実例を見るのが早いです。 拙作el4rはRCtoolを使って ~/.emacs、 ~/.el4rrc.rb、 ~/.el4r/init.rb を書き換えています。

定義スクリプト el4r-rctool を抜粋します。 長いですが、大部分がヒアドキュメント文字列なので、スクリプト自体は単純です。

# (shell-command "rm ~/.el4rrc.rb")
# (progn (find-sh "rake rctool; el4r-rctool -p ; el4r-rctool -d; el4r-rctool -i") (find-filez "~/.el4rrc.rb ~/.emacs ~/.el4r/init.rb"))
# (find-sh "ruby ~/.el4rrc.rb")
require 'tmpdir'
require 'rbconfig'
include Config

home_dir_expr = %q!ENV['EL4R_HOME'] || File.expand_path("~/.el4r")!
home_dir = eval home_dir_expr

bindir = CONFIG["bindir"]
datadir = CONFIG["datadir"]
sitelibdir = CONFIG["sitelibdir"]
emacsrubydir = File.join(sitelibdir, "el4r/emacsruby")
el_program = File.expand_path('emacs/site-lisp/el4r.el', datadir)
el_dir = File.dirname el_program

rc = RCtool.new(:name=>"el4r")
rc.define_patch(".el4rrc.rb", <<END_OF_BLOCK, "#", <<END_OF_TEMPLATE, :prepend)
### Internal variables
@stdlib_dir = #{emacsrubydir.dump}
@autoload_dir = #{File.join(emacsrubydir, "autoload").dump}
@el_program_relative = "data/emacs/site-lisp/el4r.el"
@instance_program_relative = "bin/el4r-instance"
@el_program = #{el_program.dump}
@instance_program = #{File.expand_path('el4r-instance', bindir).dump}
@lisp_object_gc_trigger_count = 100
@lisp_object_gc_trigger_increment = 100
@ruby_gc_trigger_count = 100
@ruby_gc_trigger_increment = 100
@log_buffer = "*el4r:log*"
@output_buffer = "*el4r:output*"
@unittest_lisp_object_gc_trigger_count = 5000
@unittest_lisp_object_gc_trigger_increment = 5000
@unittest_ruby_gc_trigger_count = 5000
@unittest_ruby_gc_trigger_increment = 5000
@temp_file = "#{Dir.tmpdir}/el4r-\#{ENV['USER'] || ENV['USERNAME'] || 'me'}.tmp"

### El4r bootstrap code
def __conf__
  if ENV['EL4R_ROOT']
    $: << File.join(ENV['EL4R_ROOT'], "lib")
  end
  require 'el4r/el4r-sub'
  ConfigScript.new(__FILE__)
end

def __elisp_init__
  $> << "(setq \\n"
  instance_variables.map{|iv| [iv[1..-1], instance_variable_get(iv)]}.each {|k,v|  $> << "el4r-\#{k.gsub(/_/,'-')} \#{v.inspect}\\n" if Numeric === v or String === v}
  $> << ')' << "\n"
end

at_exit { __elisp_init__  if __FILE__==$0 }

### Customizable variables
### You can override these variables in User-setting area.
# directory containing EmacsRuby scripts
@home_dir = #{home_dir_expr}
# directory containing other package's EmacsRuby scripts
@site_dir = "\#{@home_dir}/site"
# startup EmacsRuby script
@init_script = "init.rb"
# EmacsRuby search path
@el4r_load_path = [ @home_dir, @site_dir, @stdlib_dir, "." ]
END_OF_BLOCK
%s

# Ruby interpreter name used by el4r
@ruby_program = "ruby"
# Emacs program name used by el4r / el4r-runtest.rb
@emacs_program = "emacs"
END_OF_TEMPLATE


rc.define_patch(".emacs", <<END_OF_BLOCK, ";;", "%s", :append)
(add-to-list 'load-path #{el_dir.dump})
(require 'el4r)
(el4r-boot)
END_OF_BLOCK

rc.define_patch("#{home_dir}/init.rb", <<END_OF_BLOCK, "#", "%s", :prepend)
# This is the el4r initialization file.
END_OF_BLOCK

まず、変数rcにRCtoolオブジェクトを代入しています。

rc = RCtool.new(:name=>"el4r")

次の行は、.el4rrc.rbのパッチを定義しています。

rc.define_patch(".el4rrc.rb", <<END_OF_BLOCK, "#", <<END_OF_TEMPLATE, :prepend)

rcは:home_dirを明示していないため、パッチ対象をホームディレクトリからの相対パスで指定しています。 つまり、~/.el4rrc.rbへのパッチなのです。 el4rrc.rbはRubyスクリプトなので、コメント開始文字列は # です。 ~/.el4rrc.rbはスクリプトとして実行するとEmacsLispを吐くので、el4r-rctoolは、 プログラム(EmacsLisp)を生成するプログラム(.el4rrc.rb)を生成するプログラム といえます。

ブロックはEND_OF_BLOCKまでです。 この部分はel4r-rctool実行時に更新される部分です。 ブロック中に

root_dir = #{Dir.pwd.dump}

という行を含んでいるので、カレントディレクトリによってブロックの内容が変化することがわかります。

テンプレートはEND_OF_BLOCKの次の行からEND_OF_TEMPLATEまでです。 *1 初めて ~/.el4rrc.rb を作成するときのみ書き出されます。 Rubyインタプリタの名前や、Emacsの名前は個人によって異なるので、テンプレートとして定義しているのです。 ユーザはこれらの値を自由に書き換えてかまいません。

@ruby_program = "ruby"
@emacs_program = "emacs"

prependが指定されているので、 ~/.el4rrc.rb に内容があるとき、ブロックはファイルの先頭に来ます。

次に ~/.emacs のパッチを定義しています。

rc.define_patch(".emacs", <<END_OF_BLOCK, ";;", "%s", :append)

appendなので、 ~/.emacs の末尾にブロックが追加されます。 コメント開始文字列が ;; であることと、テンプレートが %s であることに注意してください。

最後にel4r起動時に実行されるEmacsRubyスクリプトのパッチを定義しています。

rc.define_patch("#{home_dir}/init.rb", <<END_OF_BLOCK, "#", "%s", :prepend)

デフォルトでは ~/.el4r/init.rb なのだが、環境変数 EL4R_HOME で変えられるため、ファイル名の部分が煩雑になっています。 この指定からもわかるように、パッチ対象ファイル名は絶対パスでも指定できます。

el4r-rctoolが結果的にどう書き換えるかは、el4rを実際にインストールしてみればわかります。


Back to Top

Valid XHTML 1.0!
rubikitch(rubikitch@ruby-lang.org)

Mail Form
Name Mail
URL


*1Rubyのヒアドキュメントは連続して定義できます。