YARDというRuby用のドキュメンテーションツールがあります。この記事ではCで書かれたRubyのライブラリにYARD用のドキュメントを書く方法を紹介します。
YARDはソースコード中にドキュメントを埋め込むタイプのドキュメンテーションツールです。ドキュメントはコメントとして書きます。ドキュメントに@タグ名
という記法でメタデータを書けることが特徴*1です。YARDに添付されているyardoc
というコマンドを使うことで、ソースコード中に書いたドキュメントからHTMLのリファレンスマニュアルを作成することができます。
Ruby*2はライブラリをRubyでもCでも書けます*3。Cでライブラリを書くと、処理を高速化したり、既存のC/C++で書かれたライブラリをRubyから使えるようにできます。例えば、rroonga*4はC/C++で書かれた全文検索エンジンライブラリgroongaをRubyから使えるようにするライブラリです。
YARDはRubyで書いたライブラリもCで書いたライブラリもサポートしています*5。Cで書いたライブラリにYARD用のドキュメントを書くには少しコツがいります。ここでは、例をつけながら、Cで書いたライブラリにYARD用のドキュメントを書く方法を紹介します。具体的には次の5つについて説明します。
Hash
に指定できるキーの説明を書く方法まず、例として使うCのコードを示します。このコードは、rroongaで実際に使われているコードの一部です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
static VALUE rb_grn_database_defrag (int argc, VALUE *argv, VALUE self) { grn_ctx *context; grn_obj *database; int n_segments; VALUE options, rb_threshold; int threshold = 0; rb_scan_args(argc, argv, "01", &options); rb_grn_scan_options(options, "threshold", &rb_threshold, NULL); if (!NIL_P(rb_threshold)) { threshold = NUM2INT(rb_threshold); } rb_grn_database_deconstruct(SELF(self), &database, &context, NULL, NULL, NULL, NULL); n_segments = grn_obj_defrag(context, database, threshold); rb_grn_context_check(context, self); return INT2NUM(n_segments); } void Init_database () { VALUE mGrn; mGrn = rb_define_module("Groonga"); rb_cGrnDatabase = rb_define_class_under(mGrn, "Database", rb_cObject); rb_define_method(rb_cGrnDatabase, "defrag", rb_grn_database_defrag, -1); } |
このコードで何をしているかを簡単に説明します。
このコードでは次の2つの関数を定義しています。
rb_grn_database_defrag()
関数Init_database()
関数1つ目のrb_grn_database_defrag()
関数は、Groonga::Database
オブジェクトのdefrag
メソッドの実体です。defrag
メソッドを呼ぶと、この関数が実行されます。
2つ目のInit_database()
関数は、Groonga::Database
オブジェクトのdefrag
メソッドとrb_grn_database_defrag()
関数を結びつけています。初期化をしている関数です。
それでは、このCのコードにYARD用のドキュメントを書きながら、冒頭で挙げた次の5つについて説明します。
Hash
に指定できるキーの説明を書く方法なお、この記事ではどこにどうタグ*6を書くかに焦点を当てているため、個別のタグに対する詳細な説明は省いています。タグの詳細についてはYARDのドキュメント(英語)を参照してください。
YARD用のドキュメントはコメント内に書きます。ドキュメント用のコメントはメソッドの実体となる関数の直前に書きます。関数の直前に書くと、関数定義とメソッドのドキュメントが結びつきます。結びつけられるとYARDが生成するHTMLのリファレンスマニュアルでは「View source」のリンク先に関数定義が表示されます。
例を示します。以下のコード内の「ここにYARD用のドキュメントを書く」と書かれた部分にドキュメントを書きます。こうすることにより、これから書くdefrag
メソッドのドキュメントとrb_grn_database_defrag()
関数の定義が結びつきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
/* * ここにYARD用のドキュメントを書く */ static VALUE rb_grn_database_defrag (int argc, VALUE *argv, VALUE self) { grn_ctx *context; grn_obj *database; int n_segments; VALUE options, rb_threshold; int threshold = 0; rb_scan_args(argc, argv, "01", &options); rb_grn_scan_options(options, "threshold", &rb_threshold, NULL); if (!NIL_P(rb_threshold)) { threshold = NUM2INT(rb_threshold); } rb_grn_database_deconstruct(SELF(self), &database, &context, NULL, NULL, NULL, NULL); n_segments = grn_obj_defrag(context, database, threshold); rb_grn_context_check(context, self); return INT2NUM(n_segments); } void Init_database () { VALUE mGrn; mGrn = rb_define_module("Groonga"); rb_cGrnDatabase = rb_define_class_under(mGrn, "Database", rb_cObject); rb_define_method(rb_cGrnDatabase, "defrag", rb_grn_database_defrag, -1); } |
なお、これ以降、例にはrb_grn_database_defrag()
関数の定義部分とドキュメントのみを載せます。それ以外の部分はYARD用のドキュメントとは関係ないため省略します。
それではメソッドのドキュメントを書いていきます。
メソッド定義の直前のコメントにタグ*7を使わずにドキュメントを書くと、YARDはその文章をメソッドの説明として扱います。メソッドの説明にはどのような処理をするメソッドかということを書きます。
defrag
メソッドの場合は以下のようになります。
1 2 3 4 5 6 7 8 |
/* * Defrags all variable size columns in the database. */ static VALUE rb_grn_database_defrag (int argc, VALUE *argv, VALUE self) { /* ... */ } |
このコードをexample.cというファイルに保存し、yardoc
コマンドを実行するとHTMLのリファレンスマニュアルを生成できます。
% yardoc example.c
リファレンスマニュアルには以下のようにコメントに書いたドキュメントがメソッドの説明として表示されています。
メソッドの説明は通常の文章として書きました。
メソッドの説明の後は、メソッドの引数についての説明を書きます。メソッドの引数の説明はタグを使って書きます。タグとはメタデータを指定するためのYARDの機能です。YARDではメソッドの引数をメタデータとして扱うため、統一感のある読みやすいリファレンスマニュアルを生成することができます*8。
引数の説明には@param
タグを使います。@param
タグの書式は以下の通りです。
@param [引数のクラス] 引数名 引数の説明
では、実際に@param
タグで引数の説明を書きましょう。@param
タグを書く位置はメソッドの説明の下がよいでしょう。HTMLのリファレンスマニュアル上では順序は関係ありませんが、コード中のドキュメントを読む場合に読みやすくなります。この順序にすると、メソッド全体の説明を読み、次に引数の説明に入る、という順序になります。メソッドの全体像を把握してから細部を読めるのでドキュメントを理解しやすくなります。
1 2 3 4 5 6 7 8 9 10 |
/* * Defrags all variable size columns in the database. * * @param [Hash] options custom options. */ static VALUE rb_grn_database_defrag (int argc, VALUE *argv, VALUE self) { /* ... */ } |
「引数の説明」の「custom options.」の最後には常に「.」を付けておいたほうがよいでしょう。説明が「custom options. Optional」と2文以上になった場合に最後の文だけ「.」がついていないと、もやっとするからです。
例のdefrag
メソッドのシグニチャー*9はRubyで書くと以下の通りです。
1 2 3 |
def defrag(options={}) # ... end |
options
引数はHash
なので、@param
タグで[Hash]
と書いてその情報を伝えています*10。
ここまでで書いたドキュメントからyardoc
コマンドでHTMLのリファレンスマニュアルを生成すると次のようになります。
リファレンスマニュアルに引数の説明が追加されています。引数の説明を追加するために@param
タグを使いました。
戻り値の説明には@return
タグを使います。@return
タグの書式は以下の通りです。
@return [戻り値] 戻り値の説明
では、実際に@return
タグで戻り値の説明を書きましょう。@return
タグを書く位置は@param
タグの下がよいでしょう。入力を読んでから出力を確認する、という順序で読めます。
1 2 3 4 5 6 7 8 9 10 11 |
/* * Defrags all variable size columns in the database. * * @param [Hash] options custom options. * @return [Integer] the number of defraged segments. */ static VALUE rb_grn_database_defrag (int argc, VALUE *argv, VALUE self) { /* ... */ } |
例のdefrag
メソッドは戻り値として整数を返すので、@return
タグで[Integer]
と書いてその情報を伝えています。
ここまでで書いたドキュメントからyardoc
コマンドでHTMLのリファレンスマニュアルを生成すると次のようになります。
リファレンスマニュアルに戻り値の説明が追加されています。戻り値の説明を追加するために@return
タグを使いました。
Hash
に指定できるキーの説明を書く方法最後に、Hash
を引数として受け取るメソッドのドキュメントを書きます。Rubyでは、Pythonのキーワード引数相当のことを実現するために、引数をHash
として受け取り、メソッド内で必要な値を取り出します。このようなメソッドを使う側は、Hash
に指定できるキーと、その値が何を意味するのかが気になります。これをドキュメントに書いておくことで、有用なドキュメントになります。
Hash
にどんなキーを指定できるのかというドキュメントを書くには、次の3つのタグを使います。
@overload
タグ@param
タグ@option
タグまず、@overload
タグを使ってメソッドのシグニチャーを指定します。@overload
タグの書式は以下の通りです。
@overload メソッド名(メソッドの引数)
なお、@overload
タグは引数にHash
を指定しない場合でも常に指定することをオススメします。Rubyで書かれたメソッドは@overload
タグを書かなくても引数名などの引数の情報がつきますが、Cで書かれたメソッドには@overload
タグを書かないと引数の情報がつかないからです*11。
では、実際に@overload
タグでメソッドのシグニチャーを書きましょう。@overload
タグを書く位置は@param
タグの上がよいでしょう。まず、引数全体を確認してから個々の引数を確認する、という順序で読めます。
1 2 3 4 5 6 7 8 9 10 11 12 |
/* * Defrags all variable size columns in the database. * * @overload defrag(options={}) * @param [Hash] options custom options. * @return [Integer] the number of defraged segments. */ static VALUE rb_grn_database_defrag (int argc, VALUE *argv, VALUE self) { /* ... */ } |
ここまでで書いたドキュメントからyardoc
コマンドでHTMLのリファレンスマニュアルを生成すると次のようになります。
リファレンスマニュアル内の、「Instance Method Summary」と「Instance Method Details」にあるdefrag
メソッド名のところに、「(options = {})
」が追加されています。
次に、@param
タグでHash
で指定するオプション全体についての説明を書きます。@param
タグの説明では「Hash
でオプションを渡すことができる」ということを説明するのがよいでしょう。
@param
タグの書き方で注意するポイントは、必ず@overload
タグよりも下に書き、さらにその@overload
タグよりもインデントして書かなければいけないという点です。インデントして書くことで、YARDがその@param
タグは@overload
タグで書いたシグニチャーに対応していると認識します。
実は@overload
タグを複数指定することにより複数のシグニチャーを指定することができます。もし、@param
タグが@overload
タグと同じインデントレベルにある場合は「すべての@overload
タグで共有される@param
タグ」と認識されます。多くの場合はそれぞれのシグニチャー毎に引数の説明は異なるため、@overload
タグ毎に@param
タグが認識される書き方の方が適切です。
例ではすでに@param
タグが書かれていますが、@overload
タグと同じインデントレベルになっています。そのため、@overload
タグよりもインデントして@param
タグを書くように修正します。同様に@return
タグもインデントします。インデントする理由は@param
タグと同じです。
1 2 3 4 5 6 7 8 9 10 11 12 |
/* * Defrags all variable size columns in the database. * * @overload defrag(options={}) * @param [Hash] options custom options. * @return [Integer] the number of defraged segments. */ static VALUE rb_grn_database_defrag (int argc, VALUE *argv, VALUE self) { /* ... */ } |
これで、@param
タグと@return
タグが@overload
タグで書いたシグニチャーに対応しているとYARDが認識するようになります。
ここまでで書いたドキュメントからyardoc
コマンドでHTMLのリファレンスマニュアルを生成すると次のようになります。
@return
タグに書いた戻り値の説明が、メソッドの説明の後ろに追加されています。@param
タグに書いた引数の説明は追加されていませんが、これは、@overload
タグが1つしかないため、1つの@overload
タグで使われているかすべての@overload
タグで共有されているかの見分けがつかないためです。*12
@overload
タグで書いたシグニチャーに@param
タグと@return
タグが対応しているとYARDに認識させるために、@param
タグと@return
タグをインデントしました。
最後に、@option
タグでHash
に指定できるキーとその説明について書きます。@option
タグの書式は以下の通りです。
@option Hash引数の名前 Hashのキー名 (デフォルト値) 値の説明
ここでの「デフォルト値」とは、Hash
にキーを指定しなかったときに、そのキーに対応する値として使用される値のことです。
@option
タグは@param
タグの下に同じインデントで書きます。
では、実際に@option
タグを書きましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* * Defrags all variable size columns in the database. * * @overload defrag(options={}) * @param [Hash] options custom options. * @option options [Integer] :threshold (0) the threshold to * determine whether a segment is defraged. Available * values are -4..22. -4 means all segments are defraged. * 22 means no segment is defraged. * @return [Integer] the number of defraged segments */ static VALUE rb_grn_database_defrag (int argc, VALUE *argv, VALUE self) { /* ... */ } |
ここまでで書いたドキュメントからyardoc
コマンドでHTMLのリファレンスマニュアルを生成すると次のようになります。
オプションについての説明が追加され、引数のHashに指定できるキーが:threshold
であることと、:threshold
に対応する値は整数を指定することと、指定しなかったときには0が使われることがわかります。Hashでオプションを指定する場合のドキュメントには@overload
タグ、@param
タグ、@option
タグを使いました。
Cで書いたライブラリにYARD用のドキュメントを書く方法を説明しました。ポイントは@overload
タグを使うことです。Rubyで書いたライブラリの場合は@overload
タグは必須ではありませんが、Cで書いたライブラリの場合は必須と言ってよいでしょう。YARD用のドキュメントを書くことで、ユーザーにとって有用なドキュメントを書いてみてはいかがでしょうか。よいソフトウェアを書くためにドキュメントを書くことが役に立つこともありますよ。
*1 RDocでは:XXX:
という記法でディレクティブを指定できます。ディレクティブはメタデータを指定するというよりは出力を制御するものです。ただし、ディレクティブの中には:category:
などメタデータを指定するものもあります。
*2 CRubyやMRIと呼ばれている実装。
*3 Cで書かれたRubyのライブラリを拡張ライブラリと呼びます。
*4 rubyforge.orgからranguba.orgに移動しました。
*5 RDocもRubyとCを両方サポートしています。
*6 後述。
*7 もっと後述。
*8 RDocはドキュメントを書く人それぞれが引数の説明っぽくドキュメントを書くという方式で、RDoc自身は引数の説明を特別扱いしません。そのため、書く人により表示のされ方は様々です。
*9 メソッドの名前、引数、戻り値に関する情報のこと。Rubyのメソッド定義の構文には戻り値に関する情報は含まれない。
*10 options
引数が省略可能という情報は後で指定します。
*11 Rubyで書かれたメソッドの場合はメソッド定義から引数の情報を抽出しているが、Cではそもそも引数の情報が書かれていないため。
*12 もう少し言うと、もう1つ@overload
タグがあると、その@overload
タグの下には現在ある@param
タグの説明が表示されないので見分けがつきます。