株式会社クリアコード > ククログ

ククログ


国際化サポート付きのYARD 0.8.0がリリース

Ruby用のドキュメンテーションツールであるYARD 0.8.0がリリース(英語)されました。0.8.0でついに国際化サポートが入りました!

0.8.0ではタグまわりの改良やMix-inしているモジュールもクラスのドキュメントの中に一緒に入れられる改良*1などたくさんの改良がありますが、その中の1つとして国際化サポートが追加されています。

国際化サポートの追加といっても、まだ、ドキュメントから翻訳対象のメッセージを抽出する機能があるだけです*2。メッセージを翻訳し、それを使って英語のドキュメントから日本語のドキュメントを生成する機能などはまだありません。そのため、今すぐ一気に便利になるわけではありません。しかし、これまで国際化をサポートしたRuby用のドキュメンテーションツールがなかったので、これは大きな一歩です。不足している機能についてはこれからもpull requestを送って徐々に追加してもらう予定です。

Python用のドキュメンテーションツールであるSphinx*3も国際化のサポートが進んでいます。Ruby用のドキュメンテーションツールであるYARDでも国際化のサポートが進み、より多くの人が母国語でRubyのライブラリのドキュメントを読めるようになるとよいですね*4

*1 Railsまわりのライブラリなど、たくさんのモジュールをMix-inしているような場合に便利そう。

*2 抽出したメッセージはPOTファイルとして出力します。

*3 Python以外にもgroongaなどで使われていたりと、広く使われています。

*4 ドキュメンテーションツールが国際化をサポートする以外に、機械翻訳をがんばるという方法もあります。

タグ: Ruby
2012-05-01

みんなで管理するapt/yumリポジトリに署名するキーの作り方

注: セキュリティーの専門家が書いた文章ではありません。セキュリティー面で心配のある方は専門家に相談することをお勧めします。

複数人で管理しているapt/yumリポジトリの署名用GPGキーをどうやって作って運用するのがよいかという案とその具体的な実現方法を紹介します。

背景

groongamilter managnerは最新バージョンをすぐに簡単に利用できるように独自にdeb/rpmパッケージを作成し、apt/yumリポジトリ(以下、単にリポジトリとする)を提供しています。どちらのプロジェクトも1人だけがリポジトリを更新できるのではなく、リポジトリを更新できる権限を持つ人(以下、パッケージマネージャーとする)が複数人います。

リポジトリではGPGキーで署名することにより*1、そのリポジトリが改ざんされていないかを確認することができます*2。パッケージマネージャーが複数人いる場合、だれか1人の個人用のGPGキーを署名用のキーとして使うとその人しかリポジトリを更新できなくなります。個人的なGPGキーの秘密鍵*3とパスフレーズはその人だけしか知らない情報だからです。それでは、複数人でリポジトリを更新できるようにするはどうしたらよいでしょうか。以下の2つの案が考えられます。

  1. パッケージマネージャー全員のGPGキーをそのリポジトリの公式な署名用GPGキーとする。
  2. 新しく署名用GPGキーを作成し、パッケージマネージャー全員で共有する。リポジトリの公式な署名用GPGキーはその新しく作ったキー1つとする。

どちらの方法も実現できますが、署名用のGPGキーを作成する方が一般的です。Debian GNU/LinuxやCentOSなどの公式リポジトリでは署名用のGPGキーを作っています。aptではdebian-keyringやdebian-multimedia-keyringなどXXX-keyringというパッケージ名が一般的で、yumではcentos-releaseやfedora-releaseなどXXX-releaseというパッケージ名が一般的です*4

個人のGPGキーを使う場合、新しくパッケージマネージャーが増えるとユーザーすべてにその人のGPGキーの公開鍵を配布しなくてはいけません。署名用の公開鍵はdeb/rpmパッケージで配布することが一般的です。そのため、apt/yumで更新できるので、ユーザーにとってそんなに難しい作業ではありません。しかし、ユーザーが通常のパッケージを更新する前に公開鍵用パッケージを更新しなくてはいけないため、通常のパッケージ更新手順に比べるとひと手間増えてしまいます。

一方、署名用GPGキーを作成する場合、パッケージマネージャーが増えてもユーザー側は特に追加の作業をする必要はありません。リポジトリ提供側だけの作業で済みます。問題はパッケージマネージャー間でどうやってセキュアに署名用GPGキーの秘密鍵とパスフレーズを共有するかです。

ということでようやく本題です。

署名用GPGキーの運用方法案

新しくパッケージマネージャーが増えたとします。その人に署名用GPGキーの秘密鍵とパスフレーズをセキュアに渡すにはUSBメモリなどを手渡しするのが確実です*5。しかし、住んでいる場所が離れているなどの理由で手渡しするのが難しいこともあるでしょう。その場合はネットワーク経由でセキュアに渡します。

ここでは、新しいパッケージマネージャーが個人的なGPGキーをすでに持っていて、既存のパッケージマネージャーから信頼されている*6とします。この場合、新しいパッケージマネージャーの公開鍵で署名用のGPGキーの秘密鍵とパスフレーズを暗号化してネットワーク経由で渡します。こうすれば、新しいパッケージマネージャーの秘密鍵でしか復号できないため、署名用のGPGキーの秘密鍵とパスフレーズをセキュアに渡せます。

それでは、実際にどうやってこれを実現するかを説明します。

実現方法

実現方法は以下のような流れになります。

  1. 署名用GPGキーの生成
  2. 署名用GPGキーの公開鍵に署名し、キーサーバーに送信
  3. 署名用GPGキーの秘密鍵とパスフレーズを暗号化
  4. 暗号化した署名用GPGキーの秘密鍵とパスフレーズを渡す
  5. 署名用GPGキーの秘密鍵とパスフレーズを取り込む
  6. 署名用GPGキーで署名する
署名用GPGキーの生成

まず、署名用のGPGキーを生成します。CentOS 5用のyumリポジトリも作る場合はDSAを選ぶことと鍵の長さを1024ビットにすることがポイントです。CentOS 5で使っているrpmコマンドではRSAや1024ビット以上の鍵の長さを扱うことができないからです*7

以下は実際にコマンドを実行した結果です。コマンド実行時にパスフレーズを聞かれるのですが、そのときに使うランダムなパスフレーズの作り方は後述します。

% gpg --gen-key
gpg (GnuPG) 1.4.12; Copyright (C) 2012 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

ご希望の鍵の種類を選択してください:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (署名のみ)
   (4) RSA (署名のみ)
選択は? 3
DSA keys may be between 1024 and 3072 bits long.
What keysize do you want? (2048) 1024
要求された鍵長は1024ビット
鍵の有効期限を指定してください。
         0 = 鍵は無期限
      <n>  = 鍵は n 日間で満了
      <n>w = 鍵は n 週間で満了
      <n>m = 鍵は n か月間で満了
      <n>y = 鍵は n 年間で満了
鍵の有効期間は? (0)
Key does not expire at all
これで正しいですか? (y/N) y

あなたの鍵を同定するためにユーザーIDが必要です。
このソフトは本名、コメント、電子メール・アドレスから
次の書式でユーザーIDを構成します:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

本名: groonga Key
電子メール・アドレス: packages@groonga.org
コメント: groonga Official Signing Key
次のユーザーIDを選択しました:
    “groonga Key (groonga Official Signing Key) <packages@groonga.org>”

名前(N)、コメント(C)、電子メール(E)の変更、またはOK(O)か終了(Q)? o
秘密鍵を保護するためにパスフレーズがいります。

今から長い乱数を生成します。キーボードを打つとか、マウスを動かす
とか、ディスクにアクセスするとかの他のことをすると、乱数生成子で
乱雑さの大きないい乱数を生成しやすくなるので、お勧めいたします。
++++++++++.+++++...++++++++++.+++++++++++++++.++++++++++++++++++++.++++++++++.++++++++++++++++++++.+++++..+++++.++++++++++++++++++++++++++++++>++++++++++...>+++++....+++++

十分な長さの乱数が得られません。OSがもっと乱雑さを収集
できるよう、何かしてください! (あと241バイトいります)
gpg: 鍵45499429を絶対的に信用するよう記録しました
公開鍵と秘密鍵を作成し、署名しました。

gpg: 信用データベースの検査
gpg: 最小の「ある程度の信用」3、最小の「全面的信用」1、PGP信用モデル
gpg: 深さ: 0  有効性:   2  署名:   0  信用: 0-, 0q, 0n, 0m, 0f, 2u
pub   1024D/45499429 2012-04-28
                 指紋 = C97E 4649 A205 1D0C EA1A  73F9 72A7 496B 4549 9429
uid                  groonga Key (groonga Official Signing Key) <packages@groonga.org>

Note that this key cannot be used for encryption.  You may want to use
the command "--edit-key" to generate a subkey for this purpose.

パスフレーズは十分に長いものを使います。例えば、以下のコマンドでランダムな文字列を生成できます。(実行するたびに結果は変わります。)

% head /dev/urandom | base64 --wrap=0
1FMbKE8tAa4lZ2AgxBL3+J1U4M5MKhfDHNt(...省略...)snCg==

これで署名用のGPGキーができました。

署名用GPGキーの公開鍵に署名し、キーサーバーに送信

署名用GPGキーの公開鍵に自分の秘密鍵で署名しましょう。そうすれば、あなたを信頼している人がこの新しく生成した署名用GPGキーを信用しやすくなります。

署名用GPGキーの公開鍵に署名するには署名用GPGキーの鍵IDが必要です。鍵IDはgpg --list-keysで確認できます。

% gpg --list-keys
...
pub   1024D/45499429 2012-04-28
uid                  groonga Key (groonga Official Signing Key) <packages@groonga.org>
...

「pub ...」の行の「/」のあとの「45499429」が鍵IDです。

鍵IDを指定して署名します。

% gpg --sign-key 45499429

署名したらキーサーバーに送信して簡単に取得できるようにしましょう。

% gpg --keyserver keyserver.ubuntu.com --send-keys 45499429
署名用GPGキーの秘密鍵とパスフレーズを暗号化

署名用GPGキーができて、公開鍵をキーサーバーに送信したので、新しいパッケージマネージャーに秘密鍵を渡すために秘密鍵をエクスポートします。

鍵IDを指定して秘密鍵をエクスポートします。

% gpg --armor --export-secret-keys 45499429
-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG v1.4.12 (GNU/Linux)

...
-----END PGP PRIVATE KEY BLOCK-----

このエクスポートした秘密鍵とキー作成時に設定したパスフレーズを一緒にしたファイルを作ります*8

release-key-secret.asc:

1FMbKE8tAa4lZ2AgxBL3+J1U4M5MKhfDHNt...snCg==

-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG v1.4.12 (GNU/Linux)

...
-----END PGP PRIVATE KEY BLOCK-----

このファイルを新しいパッケージマネージャーの公開鍵で暗号化します。ここでは鍵ID「079F8007」が新しいパッケージマネージャーのGPGキーだとします。

% gpg --encrypt --recipient 079F8007 release-key-secret.asc

これで、release-key-secret.asc.gpgができるので、その人だとわかる情報を最後につけます。例えば、release-key-secret.asc.gpg.kouというようにします。

暗号化した署名用GPGキーの秘密鍵とパスフレーズを渡す

新しいパッケージマネージャーでしか復号できないように暗号化した署名用GPGキーの秘密鍵とパスフレーズができたので、これを新しいパッケージマネージャーに渡します。メールで送ったり、リポジトリ*9にコミットして渡すとよいでしょう*10

署名用GPGキーの秘密鍵とパスフレーズを取り込む

新しいパッケージマネージャーは暗号化された署名用GPGキーの秘密鍵とパスフレーズを受け取ったら、復号し、署名用GPGキーの秘密鍵を取り込みます。

% gpg --decrypt release-key-secret.asc.gpg.kou | gpg --import

公開鍵も取り込み、自分の秘密鍵で署名しましょう。

% gpg --keyserver keyserver.ubuntu.com --recv-keys 45499429
% gpg --sign-key 45499429
% gpg --keyserver keyserver.ubuntu.com --send-keys 45499429

これで新しいパッケージマネージャーは署名用GPGキーが用意できました。

署名用GPGキーで署名する

いよいよ署名します。実際はシェルスクリプトなどを作って以下のコマンドを打たなくてもすむようにします。(参考: Debianパッケージの作り方と公開方法: groongaを例にして - ククログ(2010-01-18)(情報の一部が古いので注意)、Debian GNU/Linux上でRPMパッケージ・Yumリポジトリを作って公開する方法: milter managerを例にして - ククログ(2010-03-03)

aptリポジトリの場合はReleaseファイルを署名します。

% gpg2 \
    --sign \
    --detach-sign \
    --armor \
    --loca-user 45499429 \
    --output Release.gpg \
    Release

yumリポジトリの場合はrpmファイルを署名します。以下はDebian GNU/Linux sid上でrpmファイルに署名するコマンドです。いろいろマクロを定義しているのは無理やりgpg2を使うためです*11

% rpm \
    -D "_gpg_name 45499429" \
    -D "__gpg /usr/bin/gpg2" \
    -D "__gpg_check_password_cmd /bin/true true" \
    -D "__gpg_sign_cmd %{__gpg} gpg --batch --no-verbose --no-armor %{?_gpg_digest_algo:--digest-algo %{_gpg_digest_algo}} --no-secmem-warning -u \"%{_gpg_name}\" -sbo %{__signature_filename} %{__plaintext_filename}" \
    --resign *.rpm

これで新しいパッケージマネージャーでもリポジトリに署名できるようになりました。

まとめ

apt/yumリポジトリを更新できる人が複数人いる場合にリポジトリに署名する方法を説明しました。このようにすればセキュアに署名用GPGキーを共有できるのではないかという案とその具体的な実現方法という内容です。複数人でapt/yumリポジトリを更新したいという場合は参考にしてみてはいかがでしょうか。

ただし、セキュリティーの専門家が書いた文章ではないため、セキュリティー面で心配のある方は専門家に相談することをお勧めします。

*1 aptリポジトリではリポジトリのメタデータに署名し、yumリポジトリではrpmファイルに署名する。参考: SecureApt - Debian Wiki第5章 Yum

*2 改ざんされていないこととそのリポジトリが信用できるものかどうかは別問題なことに注意すること。

*3 gpgコマンドの出力では私有鍵ではなく秘密鍵となっているのでここでも秘密鍵とする。

*4 groongaやmilter managerもgroonga-repositoryやmilter-manager-repositoryではなくgroonga-releaseやmilter-manager-releaseというrpmパッケージ名にすればよかった…

*5 物理的に盗まれないように注意。

*6 新しいパッケージマネージャーのGPGキーの公開鍵が既存のパッケージマネージャーのGPGキーの秘密鍵で署名されている。参考: Debian -- 鍵署名 (Keysigning)

*7 RPMのCHANGESには4.4.2でpermit gpg to be used for RSA signatures.やpermit RSA key sizes larger than 1024 bits.と書かれているのでCentOS 5にインストールされているrpm-4.4.2.3ではRSAや1024ビット以上の鍵長も使えそうな気がするが署名を検証できなかった。

*8 同じファイルに秘密鍵とパスフレーズを入れてしまっているので、このファイルの中身を盗まれたらこの鍵は信用できなくなる。ファイルを分けた方がよりセキュアになりそうな気もするが、どちらかのファイルの中身が盗まれたら復号されたということなので、もう一方のファイルもすぐに復号されるだろう。そのため、分けてもあまりセキュアにならない気がする。

*9 これはapt/yumリポジトリではなくGitやSubversionなどのバージョン管理システムのリポジトリのこと

*10 メールの方が暗号化したファイルにアクセスできる人が少ないのでよりセキュアになりそうだが、公開鍵から秘密鍵とパスフレーズを探すことはこの暗号化したファイルがあるなしに関わらずできるため、リポジトリにコミットしてあってもあまり関係ないのではないかという気がする。そのため、groongaやmilter managerの場合はリポジトリにコミットしている。

*11 gpg-agentを使いたいのでgpg2を使いたい。

2012-05-09

The dRuby Book

2012年3月にdRuby作者がdRubyについて解説するという内容の本の翻訳(日本語から英語への翻訳)がThe Pragmatic Bookshelfから出版されました。

The dRuby Book: Distributed and Parallel Computing with Ruby
Masatoshi Seki/Makoto Inoue
Pragmatic Bookshelf
¥ 10,831

内容は原書の「dRubyによる分散・Webプログラミング」とストリーム指向のストレージDrip - +bを合わせた感じです。そのため、英語より日本語の方が得意な人は原書(日本語)とDripの話を読む方がよいでしょう。

dRubyによる分散・Webプログラミング
関 将俊
オーム社
¥ 3,456

参考: dRubyによる分散・Webプログラミング(関 将俊) - ただのにっき(2005-08-13)

もし、まわりに日本語より英語の方が得意な人がいて、その人にdRubyを紹介したいときに使えるのがThe dRuby Bookです。

説明のしかた

このdRubyの本はirbを使ってdRubyのことを説明していきます。このスタイルはdRubyを説明するときの正統派スタイルと言われています。irbを使ってRubyのオブジェクトを触りながら説明する方法はとても直感的です。1つ1つの操作で何が起こるのかを確認しながら進められるからです。困ったときはRubyに助けを求めることもできます*1。なお、札幌Ruby会議02でのライブコーディングでirbを使いましたが、これは、このdRubyの本から影響を受けた結果です。

irbを使って説明することで、複雑なはずの分散処理が直感的にわかります。この本では、複数の端末でirbを起動し、その端末間で分散処理を試しながら説明をします。例えば、片方の端末でデータ待ちの状態にします。その後、もう一方の端末からデータを送ります。すると、データを待っていた最初の端末がデータを受け取って動き出します。

それぞれの操作は自分でメソッドを呼び出すことになるので、その様子を1つずつ確認し、自分のペースで理解していくことができます。もしピンとこなかったら、そこで寄り道していろいろオブジェクトを触って疑問に思ったところを調べることだってできます。

まとめ

dRuby本の翻訳版(英語)と原書(日本語)を紹介しました。

irbでオブジェクトを触りながら理解していくということを体験してみてください。楽しかったら他の人にも紹介してみるとよいでしょう。英語が得意な人には翻訳版を、日本語が得意な人には原書を勧めるとよいでしょう。

すでに原書を読んだ人でも、英語も読めて、咳さんに興味のある人なら翻訳版も読んでみるのもよいでしょう。あの咳さんがRubyGemsを使っていることに驚くことでしょう。ただし、原書にあった「ここは不思議に思って欲しいところです」という脚注*2が翻訳版にはないというのは残念に思うでしょう。

*1 例えば、object.methodsとすれば使えるメソッドを教えてくれます。

*2 「なんてね」にも通じる咳さんらしい脚注と言える。

タグ: Ruby
2012-05-18

クリアなコードの作り方: 余計なことを書かない

最近、以下のようなコードを何度か見ました*1

1
FileUtils.mkdir_p assets_path unless FileTest.exist? assets_path

このコードを元に、「余計なコードを書かない」ことがどうして大事かを説明します。

余計なコード

まずは、どこが余計なコードなのかを考えてみましょう。このコードではFileUtils.mkdir_pFileTest.exist?メソッドを使っています。

FileUtils.mkdir_pは引数で指定されたディレクトリがなかったら親ディレクトリも含めて作成するメソッドです。すでにディレクトリが存在した場合は何もしませんし、エラーにもなりません。mkdir_pというメソッド名はmkdir -pコマンドが由来でしょう。

FileTest.exist?は引数で指定されたファイルが存在したら真を返すメソッドです。

このコードではunless FileTest.exist?のときだけFileUtils.mkdir_pをしていますが、FileUtils.mkdir_pはすでにないときだけディレクトリを作るという動作なのでunless FileTest.exist?というチェックは必要ありません。つまり、このunless FileTest.exist?が余計なコードだということです。

余計なコードを読むときに考えること

それでは、余計なコード入りのコードがどうしてよくないかを説明します。

余計なコードが入っているとコードを読むときに「この一見余計なコードには実はなにか深い意味があるんじゃないか?」と勘ぐってしまいます。そうするとコードをすーっと読めずにつかえてしまいます。読むときにたくさんつかえてしまうコードは理解しづらいためよくありません。

今回の例ではどのように勘ぐってしまうかを説明します。実際は一気にまとめて考えますが、わかりやすくするために順を追って1つずつ説明します。

まず、以下の部分で「この場所で確実にassets_pathを準備しておくんだな。この後のコードではassets_pathが必ず存在すると仮定して大丈夫だな。」と読みます。シェルスクリプトでもmkdir -pがあったら「これ以降の処理ではこのディレクトリは必ず存在するんだな。」と読むのと一緒ですね。

1
FileUtils.mkdir_p assets_path

続いてFileUtils.mkdir_pが特定のときだけ実行されることに気づきます。ここで違和感を覚えます。「あれ?FileUtils.mkdir_pはすでにディレクトリがあってもエラーにしないからいつも実行しても問題ないはずだけど…あ、ディレクトリが存在するときはエラーにならないけどパーミッションがなくてディレクトリを作れないときはエラーになるからそれを防ごうとしているのかな?」

1
FileUtils.mkdir_p assets_path unless # ...

しかし、FileTest.exist?でファイル(ディレクトリを含む)が存在するかどうかをチェックしています。ここから深読みが始まります。「ディレクトリがすでに存在するかどうかはFileUtils.mkdir_pでチェックしているからこのFileTest.exist?にはなにか別の意図があるのではないか。もしかしたら、assets_pathはディレクトリではなくてファイルでもよいのではないか。あるいは、ここは実行効率が気になる場面でFileUtils.mkdir_pよりもFileText.exist?の方が実行効率がよいのではないか。」

1
FileUtils.mkdir_p assets_path unless FileTest.exist? assets_path

気になる点が2つでてきました。

  1. assets_pathはディレクトリではなくてファイルでもよいのではないか。
  2. ここは実行効率が求められるのではないか。

確認してみましょう*2

余計なコードかどうかの確認

まず、ファイルでも問題ないかを確認します。FileUtils.mkdir_pは「これ以降はこのディレクトリは確実に存在するよ!」という意図を持ったコードなので、これ以降の処理を確認します。するとこんな感じになっています。

1
2
3
4
5
FileUtils.mkdir_p assets_path unless FileTest.exist? assets_path

FileList['{js,theme}/*'].each do |file|
        FileUtils.cp_r(file, "#{assets_path}/#{Pathname.new(file).basename}")
end

"#{assets_path}/#{Pathname.new(file).basename}"としているのでassets_pathがファイルの場合はエラーになります。つまり、unless FileTest.exist?があることで問題の発見が遅れてしまっています。もし、assets_pathが存在する場合はFileUtils.mkdir_pはエラーになるからです。多くの場合、問題の原因を早めに報告するコードの方が問題を直しやすいのでよいコードです*3。自分でfree()を呼んだときにクラッシュする問題と、GCでたまにクラッシュする問題は前者の方が直しやすい*4です。

よって、unless FileTest.exist?assets_pathがファイルでもディレクトリでもよいから、という理由ではなさそうです。

それでは、実行効率が求められるからでしょうか。まず、FileUtils.mkdir_pよりもFileTest.exist?の方が実行効率がよいことを確認します。もし、FileTest.exist?の方が遅ければunless FileTest.exist?を書かないほうが実行効率がよくなります。実行時間を測る場合はbenchmarkが便利です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require "benchmark"
require "fileutils"

assets_path = "/tmp/asserts"
FileUtils.mkdir_p(assets_path)

Benchmark.bmbm do |benchmark|
  benchmark.report("FileUtils.mkdir_p") do
    100.times do
      FileUtils.mkdir_p(assets_path)
    end
  end

  benchmark.report("FileTest.exist?") do
    100.times do
      FileTest.exist?(assets_path)
    end
  end
end

実行してみましょう。

% ruby1.9.1 -v /tmp/bench_fileutils.rb
ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-linux]
Rehearsal -----------------------------------------------------
FileUtils.mkdir_p   0.000000   0.000000   0.000000 (  0.002940)
FileTest.exist?     0.000000   0.000000   0.000000 (  0.000104)
-------------------------------------------- total: 0.000000sec

                        user     system      total        real
FileUtils.mkdir_p   0.000000   0.000000   0.000000 (  0.002047)
FileTest.exist?     0.000000   0.000000   0.000000 (  0.000105)

たしかにFileTest.exist?の方が速いので、ディレクトリがすでに存在するケースがほとんどと考えるならunless FileTest.exist?があった方が速そうです。もちろん、ディレクトリが存在しない場合はunless FileTest.exist?がない方が*5速いでしょう。

それでは、この処理が実行効率を求められているかを確認しましょう。ベンチマークの結果、1回あたり0.00002秒ほど違いがでるようです。数回しか実行しない処理ならあまり気にならないでしょう。1万回くらい実行するなら気になりそうです。このコードがどのくらい実行されるかを確認すれば実行効率を求められているかどうかがわかりそうです。このコードはRakeのタスクの中にあります。

1
2
3
4
5
6
7
8
9
10
11
namespace :assets do
        task :copy do
                require 'fileutils'
                assets_path = File.dirname(__FILE__) + '/public/assets'
                FileUtils.mkdir_p assets_path unless FileTest.exist? assets_path

                FileList['{js,theme}/*'].each do |file|
                        FileUtils.cp_r(file, "#{assets_path}/#{Pathname.new(file).basename}")
                end
        end
end

Rakeのタスクは1回しか実行されないので、ここで実行効率を考える必要はなさそうです。よって、実行効率を求めてunless FileTest.exist?を使ったわけではないのでしょう。

以上から、コードから深読みした範囲ではコードを書いた人の意図はわかりませんでした。そのため、このコードからはわからない何か別の意図があるか、このコードに単に余計なコードが入っているだけではないかと考えます。

もし、本当にunless FileTest.exist?が余計なコードだったとしたら、このコードがない方が上記のことを考えないで済むため、読む人にやさしいよいコードと言えます。

まとめ

最近見たコードを例にして、余計なコードがある場合は読む人が大変ということを説明しました。自分がコードを書くときは余計なコードを入れず、どういう意図でこのコードを書いたかが読めばすぐにわかるコードを書きましょう。

余談ですが、もし、実行効率を求めている場合は以下のようにそれがわかる名前のメソッドを定義してそれを使うのがよいでしょう。そうすれば、読む人は「実行効率を求めてこういうコードにしているんだな。」ということがすぐにわかります*6

1
2
3
4
5
def fast_mkdir_p(directory)
        FileUtils.mkdir_p(directory) unless FileTest.exist?(directory)
end

fast_mkdir_p(assets_path)

もうひとつ余談をすると、Rakefileの中ではFileUtils.mkdir_pと書くよりも以下のようにmkdir_pだけ書くほうが好ましいです。

1
2
3
4
task :default do
        assets_path = File.dirname(__FILE__) + '/public/assets'
        mkdir_p(assets_path)
end

このように書くと、実行したときに以下のようにどのような操作を実行したかを表示してくれます。これはmkdir_pに限らずcpなどFileUtilsが提供しているコマンド全部で有効です。

% rake
mkdir -p /tmp/public/assets

*1 このコードはtDiaryだが他のプロジェクトでも似たようなコードを見た。

*2 どうでもよい感じで読むときはここで「そんなわけないよなぁ。ここのコードにそんな意図があるわけないよ。書いた人があんまり考えないで書いたんだよ、きっと。」と気にしないことにします。

*3 「原因があって、それのせいで問題が発生するかもしれないけど問題が発生しないことも多い」という場合は、多くの場合はうまく動くので本当に問題が起こったときまで問題を報告しない方がよいかもしれない。

*4 ことが多い

*5 すこーしだけ

*6 この例ではそんなに実行効率に差はないけど。

2012-05-21

milter manager 1.8.3, groonga 2.0.3, mroonga 2.03

開発に関わっているフリーソフトウェアの中から最近リリースされたソフトウェアを紹介します。

milter manager 1.8.3 - 2012-05-22

milter managerは迷惑メール対策用のフリーソフトウェアです。

ちょうど一週間前の2012-05-22に1.8.3をリリースしました。このリリースでは3000万通ほどメールを処理すると発生する問題を修正しています。もし、大量のメールを処理する環境で使っている場合はアップグレードをおすすめします。(定期的に再起動して回避する方法もあります。)

また、2012-04-26にリリースされたUbuntu 12.04 LTS Precise Pangolin用のパッケージも提供しています。Ubuntu 12.04で利用する場合はこのバージョンを利用してください。

参考: milter-manager 1.8.3 - milter managerで迷惑メール対策(2012-05-22)

groonga 2.0.3 - 2012-05-29

groongaは高速な全文検索機能を提供するライブラリー・サーバーです。

今日(肉の日)2.0.3をリリースしました。バグ修正が主な変更点で、互換性も壊れていないので2.0.2を使っている人はアップグレードをおすすめします。

参考: groonga - groonga 2.0.3リリース

mroonga 2.03 - 2012-05-29

mroongaはMySQLに高速な日本語対応全文検索機能を追加するストレージエンジンです。全文検索機能はgroongaを使って実現しています。

今日(肉の日)2.03をリリースしました。ストレージモードで利用しているときのリストアが高速になっています。ストレージモードで利用している人はアップグレードをおすすめします。しかし、後方互換性が壊れている部分があるのでアップグレード時は注意してください。具体的にいうとストレージモードでマルチカラムインデックスを使っている場合は特別な作業が必要になります。ラッパーモードを使っている場合、ストレージモードを使っているけどマルチカラムインデックスは使っていない場合は互換性は保たれているので安心してください。ストレージモードでマルチカラムインデックスを使っている場合のアップグレード方法は後述の参考URLを参照して下さい。

また、mroongaをHomebrewで簡単にインストールできるようにformulaを用意しました。開発機としてMac OS Xを使っている方は有効に利用してください。

参考: mroonga - mroonga 2.03リリース

まとめ

最近リリースした開発に関わっているフリーソフトウェアを紹介しました。どれも有用なソフトウェアなのでぜひ使ってみてください。無償で利用できます。もし、有償でも構わないからサポートが必要という場合はお問い合わせフォームからご相談ください。

2012-05-29

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|