orangain flavor

じっくりコトコト煮込んだみかん2。知らないことを知りたい。

書籍執筆でお世話になったツール 〜Re:VIEW, textlint, prh, goemon, GitHub, CircleCI〜

Pythonクローリング&スクレイピング」という書籍を執筆しました。執筆にあたって色々なツール・サービスのお世話になったので、記録を残しておきます。

大まかな流れは、以前の記事に書いたとおりです。ツールの選定は2015年1月頃(textlintとprhは2016年1月頃)に行ったので、現在では状況が変わっているものもあるかもしれません。ご了承ください。

目次

Re:VIEW

原稿の形式は著者の自由とのことでしたので、Re:VIEWで書きました。

Re:VIEW - Digital Publishing System for Books and eBooks

採用理由

技術評論社さんではWordやMarkdownが多いということでしたが、WordはGitでバージョン管理しづらいし、Markdownは表現力が乏しいのが気になるところでした*1

SphinxやAsciiDocなども検討しましたが、Sphinxは書籍向けを想定したものではなさそうなこと、AsciiDocよりもRe:VIEWのほうが日本の出版社で実績がありそうだったことから、Re:VIEWを採用しました。あとなんか面白そうでした。

良いところ

1つのソースからPDF、ePubを作ることができ、書籍の表現に必要な要素(画像・表・リストの参照、別の章・節・項の参照や、リード文、キーワードなど)が充実しているのが良かったです。あと表の記法がMarkdownより圧倒的に書きやすいのが好きです。

review-ext.rbというファイルにRubyのコードを書くことで、挙動を簡単にカスタマイズできるのも便利でした。

振り返って

結果的にRe:VIEWという選択が正しかったのかよくわかりません。Re:VIEWが生成する成果物は最終的な成果物としては使われていないからです。

紙面のデータはRe:VIEWで出力したPDFとは別にDTPで作成したものが使われています。DTPの際もRe:VIEWで出力したInDesign XMLがうまく処理できないということで、Re:VIEWのフォーマットから無理矢理Markdownに変換してもらいました*2

電子書籍についても、紙版をベースにPDF版を作成する都合上、ePub版だけRe:VIEWで生成したものを使うと確認コストが上がるということで、ePUB版もPDF版をベースに作られています。

結局、Re:VIEWで生成したPDFは紙面のイメージを掴むためと、レビュアーの皆様に読んでいただくためだけに使用しました。PDFで思い通りの見た目にするためにLaTeXとの格闘に時間を費やしましたが、LaTeXで詰まると本当に辛いので、細かい見た目の問題はもっと割り切れば良かったです。

もちろんこの辺はRe:VIEWの経験が豊富な出版社さんであれば違うと思います。書籍制作のフローをよくわかっていない著者が最適なツールを選ぶのは難しいなという感想です。

実装を見る機会も多かったですが、実装やドキュメントから意図が読み取れず、疑問に思ったところがいくつかありました。

  • 章への参照は番号や見出しを表示するかどうかに応じて@<chap>{} @<title>{} @<chapref>{}と3種類あるのに、節や項への参照は@<hd>{}しかないのはなぜなんだろう。
  • //tsize//latextsizeはPDFで表の見た目を整えるのに必須なのにマニュアルに載ってないのはなぜなんだろう。
  • //infoとかが一部のBuilderでしか実装されていないのはなぜなんだろう。 他のBuilderでも使えるようになり、ドキュメントにも記載されていました。
  • リスト(ソースコード)は外部ファイルにわけるべきなんだろうか。#@mapfileの使い方がよくわからない。 プリプロセッサのドキュメントが追加されていました。

作ったものとか

書いている途中にvim-reviewのforkとDash用のdocsetを作りました。

textlint

原稿の校正にはtextlintを使用しました。

採用理由

RedPenも検討しましたが、設定をXMLで書いたりプラグインJavaで書いたりするのは辛そうとなことと、(当時調べた限りは)すぐに使える表記ゆれ修正の辞書が見つからなかったことからtextlintを採用しました。あとなんか面白そうでした。

良いところ

textlintはRe:VIEWなどの構文を解析してマークアップを除いた文章だけを対象として校正できるので、誤判定の少なさがポイントです。その分エディタで保存時に実行すると若干時間がかかります。Vimのsyntasticで使えるようになっていて良かったです。

fixableと呼ばれる誤りを自動で修正可能なルールもあり、一括で修正できるのは最高でした。あとは作者のazuさんのレスポンスの速さと開発スピードに驚きました。

振り返って

textlintを導入したのが原稿が一旦書き上がったぐらいのタイミングだったので、十分に効果を発揮することはできなかったように思います。後述のprhを使った表記ゆれのチェックが主で、他のルールは参考程度という感じになってしまいました。プログラムを書く時に開発初期からCIを取り入れたほうがいいのと同じように、執筆初期からtextlintを導入していれば最初からより良い文章を書けたと思います。

textlintはコンテキストによって異なる規則を適用できます。例えば本文は「ですます調」で書くが、箇条書きは「である調」で書くなどです。これは便利ですが、例えば画像のキャプションは何調で書くべきなのか、ソースコード中のコメントは何調で書くべきなのかなど、なかなか難しいです。コンテキストに応じた場合分けがルール内で定義されていると、それを上書きできなくて困ることもありましたが、どこで定義すべきなのかは自分の中でも答えが出ていません。

作ったものとか

Re:VIEWのプラグインがなかったので、自分で作成しました。 パーサーを雑に書けるのはRe:VIEWの設計思想の良い点だと思います*3

prh

表記ゆれを修正するためにprhを使いました。 prhコマンドを直接使用したわけではなく、textlintのルール textlint-rule-prh 経由で使用しました。

採用理由

手軽に表記ゆれのチェック・修正をしたくて使いました。 Re:VIEW形式に対応できそうなツールは他に見つかりませんでした。

良いところ

ルールベースで表記を統一できて非常に便利でした。textlint-rule-prhはfixableなルールなので、Re:VIEWの文法に合わせて必要な箇所だけを自動で修正できました。

振り返って

WEB+DB PRESSのルールを使用しましたが、カタカナ末尾の長音については、基本的に長音をつけるという表記を採用し、元のルールと反対にしたので書き直すのが大変でした。(prhに限らず)この辺をうまくどちらか選べる仕組みがあるといい気がしています。だいたい語尾の形で場合分けすることになるので、カタカナ語を英語表記に変換する辞書があればできそう。

goemon

Re:VIEW形式のファイルをブラウザでリアルタイムプレビューするために、goemonを使いました。 geomonはGoで書かれたLiveReload的なツールです。

採用理由

LiveReload的なツールは色々なものがありますが、原稿を書くのに特定の言語ランタイムに依存するのも変な気がしたのでGoemonを使いました。あとREADMEの画像がカッコよかったです。

良いところ

言語ランタイムがなくても動いて、YAMLの設定ファイルもわかりやすくて便利でした。

振り返って

OS Xだと変更を監視するファイル数が多すぎて困るという問題がありました。.gitignoreを参照して無視するみたいなことをやりたかったですが、あまり綺麗に実装できなかったので適当に手元でごまかして対処しました。最近この問題に対応するプルリクエストがマージされていました。

Re:VIEWのようなマークアップ言語ではありがちですが、慣れるに従ってリアルタイムプレビューは不要になるので、後半はあまり使いませんでした。

GitHub

編集者さんとの原稿のやり取りや課題の管理、情報共有はGitHubのプライベートリポジトリを使いました。

採用理由

筆者・編集者ともに普段から使っていて、自然と使うことになりました。

良いところ

あえて書くまでもない気もしますが、GitとGitHubは普段から使い慣れてて便利でした。GitHubのプルリクエストベースのフローが使えるかは編集者さん個人に依存すると思うので、ありがたかったです。

振り返って

最初に一通りの原稿を書く(脱稿)までは1章ごとにプルリクエストを作成していました。GitHubには1ファイルあたり1,000行以上のdiffを表示できないという制限があり、インラインでコメントできなくて困ることがありました。今見たら制限が緩和されていました

仕方ないのでRe:VIEWのreファイルをch05a.re, ch05b.re, ... のように複数に分けて対応しました。最初に作成した目次に合わせて節または項単位で書いていければ1ファイルが肥大化することもなかったと思いますが、最初は全体像が見えていなかったのでそのような書き方はできませんでした。

GitとGitHubは便利ですが、原稿の執筆に向いているかと言われるとちょっと違うような気がします。Gitは行単位で差分を管理しますが、原稿はソースコードに比べて1行が長くなりがちです。私は1行に1文を書くスタイルで書いていました。

1行が長くなると、修正の際に2つの異なるプルリクエストで同じ行を変更することが起こりえます。 プルリクエストAでは文の最初の方を修正し、プルリクエストBでは文の最後の方を修正するといった具合です。 すると後にマージされるプルリクエストは見事にコンフリクトします。

なので、Wordの校閲ツールのように、1行の中でも一部だけを修正して順次マージしていけるツールがオンラインで使えればそのほうがやりやすかったと思います。もちろん他のサービス・ツールとの連携しやすさもGitやGitHubの重要なポイントなので、総合的に判断する必要はありますが。

あとは、レビュー時には指摘事項をGitHubにIssueとして作成してもらいました。これはレビュアーの皆様にとってはやや面倒だったかとは思いますが、挙げてもらう側としてはその後の議論や追跡が楽で助かりました。ちょうどIssue Templateが使えるようになって良かったです。

最終的に300のIssueと200のプルリクエストが作られました。

CircleCI

継続的インテグレーションにはCircleCIを使いました。

採用理由

ある程度の使用経験があったことと、プライベートリポジトリでも1並列は無料で使えて助かることが採用理由です。

良いところ

CircleCIではRe:VIEWのビルドと、インタラクティブシェルやソースコードのテストを行いました。クローリングの書籍なので、クロール対象とするWebサイトの構造が変化したときに素早く気づけて良かったです。

振り返って

CircleCIのビルドでDocker Hub上のDockerイメージを使う場合、毎回docker pullを実行することになります。大きなイメージ*4だと時間がかかるので、docker loaddocker saveでキャッシュを作ることもできますが、それでもイメージの復元には時間がかかります。 この辺の時間を短くできて透過的にキャッシュされるような仕組みがあると嬉しいです。

インタラクティブシェルのテストはdoctestで行い、ソースコードのテストはユニットテストを書きました。テストが書きづらいコード(認証が必要、費用がかかる、Python以外のコマンドが関係するなど)は自動テストを書けず、エラーの発見が遅れてしまうこともありました。

成功したり失敗したりするテストケース*5もあり、ビルドの失敗に鈍感になってしまったのは反省点です。Gitのpushをトリガーとして開始するテストしか実行していませんでしたが、原稿の変更がない時期はテストが実行されないのも良くなかったです。ビルドに時間がかかる原因にもなるので、pushをトリガーとするテストは最小限にして、残りはNightly Buildなどで定期的に実行すればよかったです。

textlintは途中から導入したこともあり、textlintの指摘をビルド失敗にするとずっと成功しなさそうだったので、チェックを実行してCircleCI上で結果を見られるようにするのみとしました。

まとめ

改めて振り返ると色々なツール・サービスのお世話になりました。先人の積み上げてきたものに感謝します。

特にRe:VIEW, textlint, prhについては、使っていく中で気づいた点をIssueで報告したりプルリクエストを送ったりと、少しは貢献できたかと思います。今後も発展していくことを陰ながら応援しています。

*1:表現力を補うために独自記法を多用するのも可搬性が気になりました。

*2:Re:VIEWにもMarkdownBuilderはありますが、限定的な実装なので使用できませんでした。

*3:参考: http://www.slideshare.net/kenshimuto/review-41284727

*4:今回使用したイメージは2GB程度です。

*5:ビルドが実行されるインスタンスのIPの違いのせい?