APIがカオスってたプロダクトでOpenAPI対応やってみた


このエントリは、 SmartHR Advent Calendar 2019 1日目の記事です

こんにちは、SmartHRでエンジニアやっているppydです。いま会社では、SmartHRに蓄積されたデータを可視化して分析する簡易BIツールを開発するチームに居て、フロントエンドとバックエンドとインフラのエンジニアをやっています。それくらい人手が足りてません、みんなSmartHRにきてください。助けて。

カオスったAPI

いまのプロダクトには途中参加なので、先人たちの名誉のために言っておきますが、そもそものAPIの設計がカオスっていたというわけではありません。グラフを描写するフロントライブラリのAPIの都合や、検索フィルタの条件が複雑すぎるために、エンティティの一部がカオスっていたというのが正しい表現です。とはいえ、それのおかげでAPIのレスポンスの全容がつかみにくく、またきちんとした型定義が無かったために、せっかくフロントはTypeScriptを使っているにも関わらず型の恩恵を受けられない箇所があったことは残念でした。他にも、現在のバックエンドはRailsを使っているのですが、ActiveRecordを使った実直な実装が果たしてこの先BIツールの要求に対して耐えられるのであろうかということも検討する段階にあり、もしバックのアーキテクチャを変えたときにフロントと不整合を起こさないためにも、APIの厳密な仕様を確定させることが必要だと感じていました。

そのため、フロント側のAPIクライアントを型付きで自動生成したい、そしてバックエンドの変更でフロントを破壊したくない、という2つのモチベーションで、プロダクトにOpenAPIを入れることにしました。

OpenAPI

すでに有名なので軽く触れるくらいにしておきますが、OpenAPIはRESTのWebAPIにおける仕様の記述方法です。もともとSwaggerという名でしたが、後にOpenAPIという名前に変更になり、現在はバージョン3が公開されています。RESTのAPIのかなり厳密な仕様を、YAMLもしくはJSONで記述します。

RubyとOpenAPI

Ruby製のプロダクトで(別にRubyに限った話ではなく、ほとんどの言語でこうだと思いますが)OpenAPIに対するアプローチは2つの方法があります。

1つは、何かしらの方法でOpenAPIの仕様に則った定義ファイルを書き、その定義をcommitteeなどのGemで常にチェックする方法です。これはOpenAPIの定義そのものに則りAPIサーバーを作成する、いわばTDD的な手法です。この方法の優れた点は、正しいテストがあればOpenAPIの定義を守ることを実装に強制することができることです。もちろん、APIを作成してからOpenAPIの定義を書くことも多々あると思いますし、これからお話する内容もそうなので、完全にTDD的とは言えません。が、方法としてはまず定義があり、実装を合わせていく、ということに変わりはありません。

もう1つは、swagger-grapeなどを使い、実際のAPIの実装仕様そのものからOpenAPIの定義を作り出す方法です。この方法の優れた点は、OpenAPIの定義が絶対にAPIサーバーの実装と乖離しないことです。そのため、APIサーバーから自動で生成されるOpenAPIの定義から、更に自動で生成される各言語用のAPIクライアントは、常に間違いなく最新のAPIサーバーの仕様を満たしていると保証されている点です。

この2つの方法には、それぞれ利点と欠点があり、そしてほぼお互いに真逆の特性を持っていると言えます。前者のメリットは、APIの定義は決してズレないということであり、後者のデメリットはAPIサーバーの実装によって定義はすぐに変わるということです。そして後者のメリットは、極力少ないコードで型情報付きのAPIのクライアントを自動生成できることで、前者のデメリットはAPIの定義の作成や変更に大きなコストがかかることです。

この相反するメリットとデメリットに対して、今年はいろいろな人と意見交換をしましたが、概ね次のような話に集約しました。

  • OpenAPIの定義を自分で作成したほうが良い時
    • APIの定義がほぼ定まっていて、大きな変更があまり無さそうな場合
    • APIの定義を変えないまま、バックエンドの大きな変更が考えられる場合
    • APIの定義にフロントの仕様が関わる場合
  • OpenAPIの定義をAPIサーバーのコードから自動生成したほうが良い時
    • APIの定義の変更が頻繁に考えられる、つまり開発スピードが求められるケース
    • 常に最新のOpenAPI定義が必要な場合
    • バックエンドの大きな変更があまり無さそうな場合

もちろん、最初はコードから自動生成して、ある程度固まったらOpenAPIの定義ファイルだけ分離して管理するという方法も考えられます。しかし、自動生成される定義ファイルはJSONの場合が多く、かつ人間が編集するのはやや大変なファイルが生成されるケースがほとんどなので、それはそれで大変だと思います。grape-swagger-entityなどを使って人間にもまだわかる自動生成ファイルを作ることもできるでしょうが、どっちにしろ大変であることに変わりはないと思います。また、プロジェクトの途中からこの方法を選択することも現実的ではありません。

すでにあるRailsのプロダクトにOpenAPIを入れる

いくつかの複合的な話になりますが、すでにある複雑なAPIの仕様を実装から完全に理解するのは難しいです。特に自分のプロダクトの場合は、フロントのライブラリの都合による箇所もあったので、尚更把握が大変でした。そのためまずやったのは、OpenAPIのAST(のようなもの)をJSONから生成するライブラリを書いて、すべてのリクエスト/レスポンスに適用するRack Middlewareを作成し、ある程度自動で定義ファイルを作成することでした。実はこのライブラリを書いた時点で、OpenAPIの仕様を一部勘違いしているところがあり、実際にはOpenAPIの自動生成としてはあまりうまく動かなかったのですが、副産物としてすべてのAPIのリクエスト/レスポンスに対する型情報を得ることが出来ました。このライブラリの詳細に関しては、SmartHR Advent Calendar がまだ全日埋まっていないので、この先も埋まらなかった場合はその枠でOSS化して詳細に書きます。

すべてのAPIの型情報が手に入ったら、次はその情報を元に定義を書いていきます。とはいえ、OpenAPIの定義くらい巨大になるとYAMLもJSONももはや人間が書くべきものではないので、ビジュアルエディタを使う必要があります。今回は、Stoplight Studioを使いました。OpenAPIのビジュアルエディタって、どれもなんとも言えない書き味のものばかりなんですが、Stoplicht Studioはその中でもまあそんなに違和感がないと言えるのではないかな、と思います。とはいえ、これもそんなに使いやすいとはいえないので、どちらかといえば一番マシという消極的な選択です。特に自動でモックサーバーを立てる機能があって、何も考えずにローカルのRailsと同じポートを定義に指定したりするとバッティングして死にます。どうにかならんのかこれ。

ビジュアルエディタを使えば、最初に集めた型定義ファイルをもとにサクサクとOpenAPIを書いていくことが出来ます。しかしここで気づいたのですが、先におすすめしないと言った「自動生成した定義ファイルから手動で定義ファイルを編集する」というパターンを自分で踏んでいることに気づきました。自分でやったからはっきり言えますが、これは大変な作業です。みんななるべく早めにOpenAPI定義しましょう。

また、OpenAPIを記述するのであれば、私はYAMLをおすすめします。ビジュアライザに取り込んだり、ライブラリで読んだりするための取り回しは1ファイルのJSONのほうが良いのですが、100行を超えるJSONは人間の扱える代物では無いので、素直にファイル分割したYAMLで書くべきだと思います。そしてYAMLでファイル分割した記述した定義を、openapi-generatorなどのツールを使いCIで1ファイルのJSONに変換してパブリッシュするのが最も良いと思います。しかし、自分の環境ではなぜかStoplicht Studioで書いたYAMLの定義ファイルは、swagger-codegenを使ってYAMLからJSONに変換すると、変換は成功するのに$refを一部うまく解釈してくれず、OpenAPI Parserに入れると音もなくcommitteeが死ぬ問題がありました。OpenAPI Parserはエラーを返さない上に、committeeはnilのメソッドを読んで死ぬ情報しかくれなかった(つまり、OpenAPI Parserの成果物が間違っているのに、それに気づかずcommitteeが死ぬ)という問題に直面したため、もうどうにもならず最後はJSONを直接編集してcommitteeにデバッグコードを入れながらJSONを修正していました。これはかなり時間がかかる辛い作業でした。本当に数千行のJSONは人間が扱うものではありません。そうなる前にYAMLをもっとちゃんとチェックしましょう。(この問題に関しては、後にopenapi-generatorに突っ込むとYAMLがぬるぽで死ぬという問題に直面し、どうやらSpotlight Studioが書き出したYAMLに問題があるのではないかという推測をしましたが、未だにちゃんとした原因がわかっておらず、解決できていません。けれど今ならOpenAPI完全に理解しているので、YAMLをただしく書き直せると思います!)

OpenAPI定義ファイルを作り終わったら、そのファイルをCommitteeに読ませて、実際のリクエスト/レスポンスと乖離していないかをチェックします。プロダクトにはユニットテストは大量にあったのですが、E2Eはまだ整備されていなかったので、開発環境でのみ例外を吐きステージングと本番ではエラー通知用のサービスに通知を飛ばすようにしました。その結果、作成したOpenAPIの定義はほとんど問題なく既存のプロダクトに適合することがわかり、今後はこの定義をマスター情報として守っていこうという状態です。本当によかった。

おめでとうございます! ついにカオスなAPIのプロダクトに、OpenAPIが入りました! 私はこれを実施するために想像の3倍の工数がかかりました!

まとめます。

  • やるべきこと
    • OpenAPIやYAMLでファイル分割して書き、CIで1ファイルのJSONに変換してパブリッシュすること
  • やってはいけないこと
    • OpenAPIへの理解が低い状態で便利なツールを書いてはいけない、事前に小さなプロダクトで実験しよう
    • 自動生成されたファイルを人間が読めるようにするのは、つらい、早いうちに決断を
    • ファイル分割したYAMLを書きましょう、巨大なJSONは人間がどうこうできるものではない
  • OpenAPI関連の工数は実際の見積もりの3倍かかる

私が言うんだから間違いないです。

OpenAPIについてよくわかっていなかったこと

もうひとつ、自分がOepnAPIについてよくわかってなかったなということを書いておこうと思います。OpenAPI最高だよみたいなのはよく見るんですが、俺はOpenAPIなんもわかってなかったみたいなのはあまり見ないので、俺は全然わかってないということを伝えたいと思います。

その前にまず最初に伝えたいのは、BOOTHでこの同人誌を買えということです。OpenAPI 3を完全に理解できる本 – ota42y。この本は、おそらく日本語で手に入る最も詳細なOpenAPIの仕様を解説した本です。英語でOpenAPIのSpecを読んでも良いんですが、正直色んな意味で疲れると思うので、この本を買うのが良いです。本当に。

買いましたか? 良いです。それでは、いくつかわかっていなかったことを列挙したいと思います。

allOfって何に使うの

マジでわかっていなかった。allOfは、2つのエンティティを合成したものを表現するときに使うんですね。

具体的に言うと、新規リソースを作るときのPOSTのボティって、大抵の場合はその要素を取得するときのGETのサブセットになりますよね? 例えば、ユーザーリソースがid, name, email持つ場合、GETではその3つを持ってくるけど、POSTするときに必要なのってnameとemailだけですよよね? その2つの要素の違いって、idがあるかどうかだけなんですが、エンティティ的には別になってしまいます。しかしそれは、二重管理では……? となります。allOfは、そんなときにこういう表現を可能にします。

allOfは、配列に含まれるすべてのスキーマの条件を満たしている際に正となります。つまりこの例では、Userエンティティはidという要素をもつことと、UserElementの要素を持つことを同時に要求しています。これは、Userを構成する本質的なElementと、メタ情報であるIdを別のエンティティとして表現できるということです。実際に取得されるUserと、作成時に必要とされるUserElementを分離しています。なんとなく冗長な感じがするかもしれませんが、二重管理よりは遥かにマシですし、ユーザーを表す本質的な情報は何なのかというのを明示的に示せます。

nullable

OpenAPI3からtypeのnullが消えてた、知らんかった

required

これは根本的に勘違いしてたんだけど、propertiesって定義してても実際にキーが無かったら通過するのね……propertiesと同じ階層でrequireに必要なキーを配列で渡さないと、キー消えてても何も言ってくれない

any

仕様上 {} で良いと思ってた。そうじゃない、こんな感じに書かないと死ぬ

あまりに妙なので、これに関してはまだ理解してない可能性がある

最後に

OpenAPIのいい感じの書き方、実はOpenAPI Parserのテストを読むのがよく分かります。各個のスペックを見てもいいし、そもそもリソースとして用意されてるYAMLを見ても良い。みんな読もう。

そんなわけで、SmartHR Advent Calendar 2019 よろしくおねがいします。


カテゴリー: 未分類 | コメント / トラックバック: 0個

技術書典7で、「ActiveRecord完全に理解した」という本を出します。


免責事項

本書のタイトルにある「完全に理解した」とは、ActiveRecordを完全に理解することではなく、あくまで社会通念上相当のActiveRecord「完全に理解した」であり、本書はActiveRecordを完全に理解することを何ら保証するものではありません。

 

技術書典7

ドワンゴを退職してSmartHRで働き始めそろそろ一ヶ月のkinoppydです。技術書典に出ます。場所は「◎貴サークル「トレイリア学園」は、 か01C に配置されました。」です。ブックマークはここからどうぞ。今回は、ActiveRecordのソースコードリーディングの本を出そうと思います。500円です。前回はメタプログラミングRubyの解説本でしたが、それを書いている過程で「そろそろActiveRecordのソースとか、俺でも読めたりするんじゃないかな……」という気持ちになったので書いてみました。

cover

そんな思いつきから、ひたすらActiveRecordのソースを読んでみた結果を、簡単にまとめて本にしてみました。

内容としては、ActiveRecordで最もよく使われている(であろう)機能のestablish_connection, find, where あたりのメソッドがどうやって動いているのかを、実際にソースコードを追いながら見ていく本です。実は7月の頭あたりからこの本の構想を考え、がんばってActiveRecordのソースコードめっちゃくちゃ読んだんですが、いかんせん相手は巨大すぎて、全てを読むことはできませんでした。そのため、一応自信を持って解説できるであろう範囲に狭めて深堀りしていく内容の本となっています。

そのため、AssociationやMigrationやSTIの内容などは全く出てきません。というより、それらのソースコードをまだ読めていないので、多分次の技術書典では「ActiveRecordなにもわからない」という本を出すと思います。

この本を読むためには

この本を読むためには、メタプログラミングRubyを通して読んだことがある程度の知識が必要である前提になっています。

そのため少し敷居が高いかもしれませんが、この本はRubyを書く上で必読の本のひとつなので、いい機会なので読んでみましょう。なんなら前回の技術書典で頒布した「Real World Metaprogramming Ruby」も一緒に頒布するので、ご一緒にどうぞ。

幾つかのトピック

今回の本は、電子書籍版のみの頒布です。物理版を作るにはちょっと締切の関係で間に合いませんでした。

また、この本が対象にしているActiveRecordのコードは、5.2.3です。原稿を書き始めてしばらくしたら、6出ちゃったんですよね。どうしようかと思ったけど、書き直してる時間なさそうなので5系にしました。

本を書いた感想

ActiveRecordのソースって、想像していたより全然きれいだな、というのが素直な感想です。その前に読んでいたのがSinatraだったから、余計にそう感じるのかもしれません。さすがにもう十数年メンテされている第一線のコードは、様々な人の目に触れているだけあって圧巻でした。そしてそれ以上にすごいなと思ったのが、RDocの充実っぷりです。ActiveRecordとActievRecord::BaseのRDoc読むだけで、もう大体ActiveRecord完全に理解できるんじゃないかという気持ちすらありました。すごい。

執筆環境

前回とあまり変わらず、まず草稿をesa.io上でMarkdownで書き、その後清書と校正をRe:VIEW形式でAtomとAtom用のRe:VIEWプラグインを使って書きました。ファイルの管理はGitHubでやっており、CIは特にありません(書いてるの一人なんで、手元で都度回せば困らない)。本当にほとんど何も変わっていないので、前回の記事を参照してください。

唯一前回と変わった点は、Re:VIEW Starterを使った点です(参考:技術系同人誌を書く人の味方「Re:VIEW Starter」の紹介 – Qiita)。そもそもRe:VIEWをそのまま使った出力にあまり満足していなかったというのはありますが、色々カスタムするにはTeXの知識なさすぎて詰み太郎だったため、色々いい感じにカスタムしてくれそうなこれを選びました。

大きな誤算が2つほどあり、1つ目は良い意味の誤算で、Re:VIEW Starterを使うことで前回の執筆時に悩んでいた「長いソースコードをどうやって行番号付きで折り返すか」問題が勝手に解決してくれたことです。素晴らしい。

2つ目は悪い意味の誤算で、Atomが何故か勝手に改行コードをCRLFにしてくれちゃったせいで、それに気づかずしばらくビルドが通らなくて締め切り直前に泣いていました。

近況報告

私は元気ですが、毎日ちゃんと期待されたパフォーマンス出せてるか不安になります。これは転職直後にはよくあることだと思うので、そのうち慣れます。

夜型の生活時間はやや改善されつつありますが、最近は原稿やるために朝四時に寝て朝八時に起きて会社行くとかいうよくわからないことやっています。まあでも、飲酒量がものすごく減りました。技術書典が終わったら、完全に人間の生活リズムを取り戻せる予感があります。

また、今回は時期の関係で寄稿できませんでしたが、SmartHRのエンジニアたちも技術書典で本を出すようです。

こちらもぜひよろしく。

追記

技術書典の日程が、ぱんっあふぉー23と完全に被ってて若干やる気がありません。ぱんっあ行きたかった。


カテゴリー: 未分類 | コメント / トラックバック: 0個

株式会社ドワンゴを退職します(5年2ヶ月ぶり1回目)


2019年8月末で、株式会社ドワンゴを退職します。これは主に社内の友人に向けた文章ですが、一応自己紹介は書いておきます。

私はkinoppydという名前で、2014年にドワンゴへ入社し、Scala/Rubyエンジニアをやっていました。最初に配属されたプロジェクトはニコニコ生放送のScala化プロジェクトで、ニコ生のScala化、ニコ生のHTML5化のお手伝い、公式生放送の老朽化した機能のマイクロサービス化などを行いました。その後はチームを異動し、ニコナレのバックエンド開発に従事しました。

他にも、社内でSlack芸人やBot芸人をやっており、なんか記事にされたりしたこともありました。

【bot、暴走中!】「Slackは福利厚生」と言い切る、ドワンゴ流・Slackの超活用術とは

絵文字コミュニケーション術、サーバーワークスとドワンゴが明かす

趣味はRubyのコードを書くことで、OSS活動としてSinatraライクな書き味のBotフレームワーク Mobb の開発などをやっていたり、技術書典でSinatraのコードリーディング本やメタプログラミングRubyの副読本などを書いていたりしました。次の技術書典では「ActiveRecord完全に理解した」という本を出します。

退職の理由

大まかに言うと、ライフスタイルが変わったためです。具体的に言うと、結婚をしました。

人生にはお金が必要です。ドワンゴからもらっていた給与は、私一人で生きていく分には十分なものでした。しかし、私自身の人生と将来生まれてくる子供の人生の2つを支えるには不十分でした。また、昇給のペースなどを考えても今後私が求める水準に達するにはかなりの努力と時間がかかると思い、転職して給与を上げる決断をしました。

私はとても夜型の人間です。そしてドワンゴには、真の裁量労働が存在しています。成果を出しチームの中で合意があれば、何時に出社しても何時に帰っても何も問題はなく、なんなら一日8時間働く必要すらありませんでした。私は在籍中、月に160時間働いてない月がそこそこあるくらいです。そのため、私は概ね昼の12時から14時の間に出社し、夜の20時から0時の間に帰る、という生活を続けていました。しかし、妻は9時に家を出て20時に帰ってくるという生活を送っており、夫婦間で生活時間の不一致が問題となりました。私は真の裁量労働の権利を持っているので、その時間に合わせて生活すればいいとは思いましたが、努力しても夜型の生活を改善できませんでした。そのため、定時が存在する会社に勤め強制的に妻と同じ時間を生きることを決めました。

また、ドワンゴは2019年2月の経営陣交代以降、事業の集中と選択を進めており、私の所属するチームが持っていたニコナレもクローズの運命からは逃れられませんでした。ニコナレの使っている仕組みの一部をScala化するなどをしていたため、この仕事を離れるとなるとそれなりの引き継ぎが必要でした。しかしサービスのクローズにより、私の持ち物は何もなくなってしまいました。結果としてとても身軽になったことも決断の大きな後押しになりました。

他にもいろいろな理由はありますが、まあこの3つに比べると些細なものです。

思い出

会社では、とても友人に恵まれました。まさか社会人になってからこんなにたくさん友達ができると思っていなかったので、驚きと感動があります。ドワンゴに入ってからの新しい友人に影響された結果様々な趣味に目覚め、今までの人生の中で考えられないほど自分の変化があった5年間でした。ポーカーやロードバイク、創作活動を始めたのもそうですし、そもそも今のメイン言語であるRubyを書き始めたのもドワンゴに入ってからです。ドワンゴは、非常に多種多様な人たちが在籍しており、積極的に関わっていくとプラスだったりマイナスだったりの影響をガンガン受けていきます。

仕事で関わる人達にも恵まれ、多くは良き友人で尊敬できる仲間でした。特に、生放送チームで一緒に働いた先輩と後輩、そしてニコナレチームの上長とチームメイトは、一緒に働けたことが光栄に思えるほどでした。私は本当に幸運で、働いている間に人間関係で困ったことにほぼ出くわすことがありませんでした。後述する理由でドワンゴには大変気難しい人も多い中で、このことは本当に運が良かったとしか言いようがありません。

ドワンゴは、自分の中の感覚で一言で表せば、良くも悪くも十数年前のインターネット・アトモスフィアをだいたい東銀座に再現した会社です。人々はネットでコミュニケーションをとり、その雰囲気は独特で、世間にさらけ出すと微妙な顔をされそうな世界観と価値観がありました。社内コミュニケーションツールのSlackにはその影響が特に色濃く出ており、私にとっては心地よかったけどこの空気に馴染めない人は本当に馴染めないだろうなぁという感じです。他人の認識はアイコンとハンドルで、実際に顔を合わせてもその人の名前すらわからないことも多々あります。Slackで雑談するのが大好きで、古のインターネットのコンテキストをずっと積み重ね続けたような会話が繰り返され、さながら歩くインターネット老人会のようでした。00年代や10年代にネットで流行った出来事をみんな知っているのが半ば当然のような、生まれたときからネットしてたんじゃないかみたいな人たちがたくさん住み着いていました。繰り返しますが、自分はそんな雰囲気が大好きでした。しかしその弊害として、やはりネットの中で繋がることが好きな人が多く、実際の対人関係では頻繁に衝突を起こす人も多かったです(ネットの上で衝突している人もいましたが)。これが私がドワンゴに気難しい人が多いと思う理由で、人と話すときにはその人のコンテキストをきちんと把握する努力が必要だと感じるところです。

挨拶

ドワンゴのインターネッツを去ることは、とても寂しいことです。すっかり自分の中に染み付いてしまった感覚を忘れることは、恐らく一生無理でしょう。同僚としても友人としても仲良く接してくれた人達にはいくら感謝を伝えても足りません。また、何度も退職の決意を鈍らせるくらい、一緒に働き続けたいと思えた上長にも、会えて本当に良かったです。いま、ドワンゴが大変な時期に離れてしまうことをとても心苦しく思いますが、ニコニコという文化をこれからも守り続けてくれると、一人のユーザーとしてこれほど嬉しいことはありません。

これから

9月からは、SmartHRという会社でRubyエンジニアをやっていく予定です。今後もよろしくお願いします。


カテゴリー: 未分類 | コメント / トラックバック: 0個

技術書典6で、メタプログラミングRuby本を出します


技術書典6に参加します

今回も技術書典にサークル参加します。ブースは「か54」です。

今回の本は、メタプログラミングRuby第2版の内容を追いかけつつ、現実のプログラミングという世界ではどのようにメタプログラミングが利用されているのかを解説する本です。具体的には、Sinatraのコードのなかからメタプログラミングが用いられている箇所をピックアップして、それを教本に従って解説します。

メタプログラミングRuby第2版は、本の中でいくつか現実のコードを出して解説をしている箇所がありますが、いずれも断片的で複数の巨大なコードベースにまたがったものなので、追跡が難しいです。そこで、Sinatraというミニマルなコードベースの中から、強力なメタプログラミングを使っている箇所を取り出し、それがプロダクトに対してどのような意味を持っているのかを考えていきます。

本を書いた環境

こっちが本題です。

前回の技術書典5では、StackEdit+GitBookという環境で執筆を行いました。これはいくつかの要件があったけれど、最終的になんかいろいろうまく行かなくてこうなってしまった、という環境の成れの果てです。詳細は前回のブログを見てください。

今回の環境

いくつかの選択肢がありましたが、今回は最初からRe:VIEWを使って本を書くことに決めました。理由としては、次にあげるいくつかの前回の反省があります。

  • GitBookの成果物は、印刷するのにやや難がある
  • Markdownから望むRe:VIEWの形式に変換するのには、現実的に無理がある
  • というよりも、そもそも文章フォーマットの相互変換に無理がある

Markdownで執筆したものを、望むような形で印刷可能なPDFに変換することは、もはや無理だと悟ったというのが最も大きな理由です。PandocやSphinxも、変換は行ってくれますが結局の所は出力の形式が望んだものになるかどうかは確かではありません。そうなった場合、PandocやSphinxの気持ちを感じながらMarkdownを修正していくということはもはや不可能です。ならば最初から余計な相互変換を持たないRe:VIEWを使うべきだという結論になりました。複数の入力を受けつけ、複数の出力をしてくれるソフトは確かに便利ですが、アンコントローラブルになる箇所が多く、執筆時間が限られているときには選択が難しかったのです。

幸い、Re:VIEWのフォーマットは比較的わかりやすく、リストなどにやや不満はありつつも十分に1日で書けるようになるものでした。そのため、今回は最初からRe:VIEW形式で執筆を行おうと考えました。しかしその一方で、いくつか諦める必要のあるものもありました。

  • ライブプレビュー
  • クラウドエディタ

この二点です。

ライブプレビューに関しては、まあそれはそうという感じしか無く、都度ビルドして確認することしかできません。

クラウドエディタに関しては、まあクラウドエディタでRe:VIEWファイルを書けば良いので特に諦める必要はなかったのですが、EditStackに不満を持っていた私はあれからもっと良いクラウドエディタに出会っていませんでした。良いエディタという意味で言えば、esa.ioはかなり気に入っています。しかし、これはMarkdownに特化しているため、使えませんでした。

しかし、原稿をクラウドエディタで書くことを諦められなかった私は、考えました。結果、Re:VEIWフォーマットとMarkdownはなんとなく似てないこともなかったので、今回はまず概要と草稿をesa.io上で執筆し、そのMarkdownデータを手でRe:VEIWに変換した上で、本を作るための細かな命令などを手元のエディタで行いました。おそらく、技術書を書く上で一番つらいのは、最初の草稿をあげるまでで、そこから先は多少の原稿の修正と、印刷用のマークアップ作業です。なので、その辛い箇所をクラウドエディタでなんとかして、機械的にちかい作業はローカルのエディタで行うように分担したのです。

最終的に、Re:VIEWのファイルはGitで管理してGitHubにプッシュしていたので、GitHub上からクラウドエディタのようなこともできなくはなかったですが、それはやらなかったです。

用意した環境

最終的に用意した環境は次のとおりです

  • esa.ioで草稿執筆
  • vimでRe:VIEWファイルへ書き換え
  • 文章の管理はGitHub
  • Dockerコンテナでビルド

CI等は使っていません。複数人で執筆なら必要だったと思いますが、一人の場合は手元でガンガンビルドするので、特に恩恵はないと思ったからです。

Dockerのイメージには、[vvakame/docker-review: Re:VIEW build container by docker](https://github.com/vvakame/docker-review) を使わせてもらいました。

問題点

草稿までをMarkdown+クラウドエディタで書き、仕上げをRe:VEIWとGitで行うというのはなかなかいい選択肢でした。ただ、問題も一つありました。Re:VIEWは、TeXを中間表現として使っているという点です。

印刷用にマークアップを施し、Re:VEIWで実際にPDFを出力してみて、何か調整したいと思ったときに求められるのは、TeXの知識なのです。残念なことに、私は情報系にしては珍しく大学でTeXを学ぶことがなく、またその後も学ぶつもりがなかったので、TeXのことが全くわかりませんでした。

今回の原稿で具体的に困ったのは、引用したソースコードが行番号付きリストの横幅に収まらなかったケースです。何かしらの方法で改行が必要でしたが、そのために必要だったのはTeXの知識でした。

[【Re:VIEW】表のセル内でURLを折り返しする方法 – himajin315の記録](http://d.hatena.ne.jp/himajin315/20150312/1426146268)

[Re:VIEWで長い文字列を折り返すためにやったこと – patorashのブログ](https://patorash.hatenablog.com/entry/2018/12/12/160511)

これらのページに行き着いても、TeXのことが書いてあって、雰囲気ではわかっても何かエラーが発生したときにそれがどういう原因で出て、どうやって治すのかの当たりをつけるのに困難を極めました。私がうまく行かなかった理由は、設定を記述する順番が間違っていたというものでしたが、そんなもんわかるかという感じです。

とはいえ、Re:VIEWの良い点は、日本語のドキュメントが充実している点と、Twitterで泣き言を言っていると作者の方が拾ってくれたりするところです。

これは本当に助かりました……

次回の執筆環境

今回、Re:VIEWはとてもいいソリューションだと思いました。Markdown2Re:VIEWは諦めて、最初からRe:VIEWで書くことをおすすめします。細かい表現とか、変換時に失われますし。

クラウドエディタに関しては、いくらか思うところがあるので、次回までにはまたいろいろと選定したいと思っています。


カテゴリー: 未分類 | コメント / トラックバック: 0個

Mobb 0.5 and Repp 0.4 out now


このエントリは Mobb/Repp Advent Calendar の二十五日目です

Mobb 0.5.0 out now

🎉

クリスマスなので、超急ぎでリリースしました。Ruby 2.6.0 も出たし。

Mobb 0.5.0では、Advent Calendar で予告していたいくつかの機能がリリースされます。

Mobbのメソッド呼び出しをチェーンする、 chain/trigger シンタックス

BotはBotと会話するべきかどうか?

Mobbの正規表現解釈と、MatchDataの行方

Mobb製のBotになにか処理をさせたが、何も反応を返したくないときはどうするのか

Mobbにおけるマッチのパッシング

これらの機能の新規実装により、 chain/trigger, react_to_bot/include_myself, matched, say_nothing/silent, pass キーワードが新たにMobbに追加されました。

また、次の機能は予告していましたが0.5.0には入りませんでした。

MobbのLogger

Mobbのcronを秒単位で動かす

Mobbのマッチングにどれもヒットしなかった場合のフック

理由としては、実装そのものは概ね出来ているのですが、大きな機能追加が入りきちんとリリース前の検証が出来なかったからです。この機能は、検証が終わり次第リリースします。

Happy Mobb

25日間なんとかACを完走できました、これからもMobbをよろしくおねがいします。

 


カテゴリー: 未分類 | コメント / トラックバック: 0個