2019年3月18日月曜日

NativeExtensions な gem に rdoc のコメントを入れてドキュメントを生成する方法

概要

c のソースファイル上で普通に rdoc のタグが使えます
今回はいろいろ試して実際にドキュメントを作成してみました
またプロジェクトなどは前回までのものを流用しています

環境

  • macOS 10.14.3
  • Ruby 2.5.1p57

とりあえずコメントを入れてみる

例えば NativeExtensions として定義した C の関数に以下のようにコメントを入れてみます

  • vim ext/firstext/firstext.c
/*
 * Set data to struct members.
 */
static VALUE wrap_set(VALUE self, VALUE _index) {
  int index = FIX2INT(_index);
  MyStruct *s;
  Data_Get_Struct(self, MyStruct, s);
  char *name = (char *)"hawk1";
  if (index == 0) {
    s->name1 = name;
    s->age = 10;
  } else if (index == 1) {
    s->name2 = name;
    s->age = 20;
  } else {
    s->name3 = name;
    s->age = 30;
  }
  return Qnil;
}

ドキュメントを作成する

rdoc コマンドが使えるのでそれで生成します

  • bundle exec bundle rdoc ext/ lib/

ext/lib/ 配下を対象に rdoc を作成します
これを指定しないと Gemfile や README.md やらいろいろと含まれてしまうので対象のディレクトリを指定しています
今回は YARD ではなく rdoc の Darkfish を使います

成功すると doc/ ディレクトリができているのでその配下の index.html を開きましょう

  • open doc/index.html

こんな感じで関数に設定したコメントがドキュメントにも反映されていると思います
右側に「toggle a source」もあるので選択すると C のソースファイルを確認することもできます

クラスにコメントを入れる

NativeExtensions でクラスを追加 (rb_define_class) した場合に、そのクラスにコメントを入れる方法です

  • vim ext/firstext/firstext.c
/*
 * My first native extensions.
 */
void Init_firstext(void) {
  VALUE fe = rb_define_class("FirstExt", rb_cObject);
  rb_define_alloc_func(fe, my_struct_alloc);
  rb_define_method(fe, "set", wrap_set, 1);
  rb_define_method(fe, "show", wrap_show, 1);
  Init_secondext();
}

こんな感じで Init_ から始まる関数の前にコメントを入れれば OK です
rdoc の parser が rb_define_class で定義しているクラス名から Init_ が付くメソッドを自動で探してコメントを解析しているようです

ちなみに Init_ がない場合には Document-class: name という命令を使って好きな箇所にコメントを入れることでクラスの説明を追加することもできます

引数の説明や返り値の説明を入れる

NativeExtensions で作成した関数の場合、引数などの表示が p1 みたいな感じでポインタを表す文字だけになってしまうようです
これだとよくわからないので関数の引数や返り値に対して説明を追記してあげましょう
例えば以下のような感じです

/*
 * Debug struct members.
 *
 * call-seq:
 *   show(index) -> nil
 *
 * ===== Parameters
 * - index - Member's index.
 *
 * ===== Return
 * - nil
 */
static VALUE wrap_show(VALUE self, VALUE _index) {
  // ...
}

結論から言うと rdoc には引数や返り値を説明するための命令はありません
代わりに markup が用意されておりそれらを駆使してそれっぽい説明に見せる感じです
例えば上記の場合だと ===== は見出しようの markup です
- はリストを表示することができる markup でこれで各引数の説明を記載しています
これらはあくまでも自分のやり方になるので、こうじゃなくても OK です
再度説明しますが YARD のような @param@return のような命令が rdoc にはないので markup を駆使する感じになります

ただ rdoc にも call-seq: という命令がありこれで「関数をどう呼び出すか」を定義することができます
なのでデフォルトで引数が p1 となっていた部分を適切な引数名に変えることができるようになります
上記で生成したドキュメントは以下のようになります

Constants と VERSION を設定する

まず Constants ですがこれは定数がそもそもないと表示されません
rb_define_const で定義した定数に対してコメントを入れれば rdoc でも表示されます

/*
 * My first native extensions.
 */
void Init_firstext(void) {
  VALUE fe = rb_define_class("FirstExt", rb_cObject);
  rb_define_alloc_func(fe, my_struct_alloc);
  rb_define_method(fe, "set", wrap_set, 1);
  rb_define_method(fe, "show", wrap_show, 1);
  /*
   * Set max score with 100.
   */
  rb_define_const(fe, "SCORE", INT2FIX(100));
  Init_secondext();
}

こんな感じです
次に VERSION です
VERSION は lib/firstext/version.rb ですでに定義されている定数です
versions.rb でコメントしても OK ですが今回は C 側でコメントする方法を紹介します
先程の SCORE 同様 VERSION 定数を rb_define_const で定義してあげれば OK です

/*
 * My first native extensions.
 */
void Init_firstext(void) {
  VALUE fe = rb_define_class("FirstExt", rb_cObject);
  rb_define_alloc_func(fe, my_struct_alloc);
  rb_define_method(fe, "set", wrap_set, 1);
  rb_define_method(fe, "show", wrap_show, 1);
  /*
   * Set max score with 100.
   */
  rb_define_const(fe, "SCORE", INT2FIX(100));
  /* 
   * 1.0.0: The version of this package
   */
  rb_define_const(fe, "VERSION", rb_str_new2("1.0.0"));
  Init_secondext();
}

ちなみに値は version.rb が優先されます
これで以下のように表示されます

Pages と Table of Contents

よく Pages と Table of Contents が表示されている rdoc があると思います
これらは .rdoc ファイルや .md ファイルが直接表示されています
例えば rdoc 実行時に以下のように README.md を指定しましょう

  • bundle exec rdoc ext/ lib/ README.md

すると左ペインに Pages が追加されて README.md が項目にあると思います
それを選択すると Markdown がパースされて Table of Contents が自動的に生成されるというようになっています

最後に

NativeExtensions として開発した gem にコメントを入れてみました
今回は rdoc 形式なのでご注意ください
最近だと YARD 形式しか見ない気もします、、

rdoc を使う場合には参考にしてみてください

参考サイト

0 件のコメント:

コメントを投稿