By Jeremy Brooks under CC BY-NC
先日、cURL as DSLというツールを公開しました。その後、何度も同じような質問を受けたりしたので、ブログにまとめてみます。
なぜこのツールを作ったの?
RESTfulというものは大分一般的になってきました。HTTPでAPIを提供というのもよく見かけます。ですが、僕はこのRESTfulというやつが嫌いです。
GETのURLをシェアすればいつでも同じページがある(変な状態を持たない)、みたいな思想はいいんですが、HTTPのAPIはどうも使いにくい。ドキュメントのHTTPのサンプルを見て、ドキュメントをじっくり読み込んで、パラメータをJSONやらXMLで組み立ててボディに乗っけて(しかも大抵パラメータがアホのように多い)、いろいろ決められたルールで認証してヘッダに埋め込んで・・・・ちょっとでも違うと、400しか返してくれないとかザラです。どこがうまくいかないのかきちんと情報を返してくれればいいんですけどね。やりたいこと(機能を1つ呼び出す)に対して、そこまでなかなか遠い。だいたい、ここを読んでいる人たちも、RESTful APIと、自分が使っている言語(RubyでもPythonでもnode.jsでも)のクライアントライブラリの両方があったら、迷わず後者を選びますよね?RESTful APIを触っている時の気持ちは、libffi経由でCで書かれた共有ライブラリを呼び出しているのと似ている気がします。で、バリデーションは送信側にしろ受信側にしろ、出て実装しないといけない。
だいぶ前にやったXMLRPCの方がまだ開発のしやすさは圧倒的に高い。また、WSDLとか、CORBAとか、我々人類はもっと便利なものを一度は手にしたのに、なぜ退化しちゃっているのか?ブラウザ主体だと、JSができることがメインとならざるを得なくて、それでXMLHttpRequestとかjQueryのAjax APIなんかで扱いやすい仕組みとしてRESTfulが主流になってきたのかなぁ・・・とか考えていますが、実際のところはよくわかりません。
RESTful時代にドキュメントとして書かれるのはHTTPのテキストと、たまにcURLコマンドです。それらのドキュメントの中にある情報の断片でクライアントが作れれば、試行錯誤の回数は確実に減ります。人間向けのドキュメントを機械が読んでくれたらうれしいですよね?本当はセマンティックウェブを実現しているSphinxのhttpドメインを使いたかったんですが・・・
とは言え、便利に使いたい、というだけであれば、Swaggerもあるし、これの開発を初めて、1-2個の言語出力ができるようになった時に、GoogleがgRPCを発表しました。使えるのであれば絶対にこっちの方がいいと思います。インタフェース生成の仕組みのお陰で、送信前に変なリクエストは事前に除外できますし、バリデータを自分で書かなくてもいいし、開発の効率は良いと思います。
用途としては、ウェブアプリのテストとか作るのが簡単になったらいいなぁ、とちょびっと思っています。
GolangのHTTPのAPI
コードを眺めた方は、やろうとしていることに対してコードがかなり長いとか、テンプレート使っているくせに中で文字列をがんばって組み立ててたりして、かなりムダがいっぱいありそうなコードだなぁ、と感じたと思います。これには理由があります。
GolangのHTTPのAPIは、多くの関数があります。Get, Post, PostForm, http.Client()オブジェクトです。本当にGetアクセスを1回したいだけなら、Getを使うのが簡単です。余計なパラメータとかヘッダを送らないで、A=B形式のデータをPOSTで送信したいなら、PostForm関数がよいでしょう。コードジェネレータを作るという観点で見れば、なんでもできるhttp.Client()を使えば簡単です。
このプログラムでは、「人間だったらこういうコードを出したかったはず」という、なるべくムダのないコードを目指すことにしました。使える時はGet/Post/PostFormといった、便利関数をなるべく使うようにしています。というより「Goは関数が多すぎて、出したい内容に対して何使えばいいのかわからん!」というのもこのツールを作るきっかけの1つだったりします。また、どの出力であっても、そのまま処理系に投げれば動作する完全なコードになっています。
Javaコードに関しては、try with resourcesというのが今どきのコードというフィードバックをもらったので、近々直す予定です。今回は不慣れな言語にもいろいろ挑戦したのですが、いろいろ周りの人に教えてもらうことができて勉強になりました。
なんでcURLなの?
このツールを発想したきっかけが、Google Chromeの開発者ツールの「Copy as cURL」というメニューです。cURL形式でコピーできるなら、cURLコマンドは多くのユーザが気軽に作れます。コミュニケーション手段になると思いました。例え冗長でも、自動生成できるというのは、非プログラマにとってはとてもありがたい選択肢になります。ExcelのVBAだって、FlashのJSFLだって、プログラマじゃない人がたくさん使っていますからね。
また、PythonやらRubyやらnode.jsを使ったことがある人は、インタラクティブモードとかirbとかREPLといった環境の便利さはよく理解されていると思います。cURLが広まって、みんながウェブのドキュメントにcURLの擬似コードを書いてくれるようになれば、cURLはHTTP界におけるREPLになる可能性もあると思っています。
ちなみに、cURLコマンドを模しているからといって、SFTPとかSMTPとかSAMBAとか、そういうのはサポートするつもりはありませんので、期待しないように。
なんで(ほぼ)標準ライブラリなの?
npmのツラミを味わったことがある人は、標準ライブラリだけで済む安心感は共有してもらえると思います。巨大なウェブアプリになると、node_modules以下はカオスです。似たような仕事をするモジュールが3つも4つも入っていることも。また、途中でgitリポジトリを挟むとさらに酷いことになりますが、まあそれは置いておきます。
ソフトウェアの歴史って同じところを行ったり来たりしながら進歩しています。基本的な流れは、中間層を作って柔軟性を高める⇔ムダな層を破壊して効率を高める、の2点です。中間層を作るアプローチはCからLLへみたいなやつだったり、ライブラリを作る、といった流れです。後者はコードの改善だったりもしますが、最近は最適化コンパイラだったり、JITだったり、というツールの進化が比較的多かったと思います。このツールは、どちらかというと、後者的な、余計なランタイムの依存を減らしてコンパクトに機能を実現していくのっていいな、という方針で作っています。
そもそも、一人でライブラリをいくつもメンテし続けるのは大変ですし、どの言語も、言語のコアはだいたい安定しています。よく互換性と言うとやり玉に上がるのがRubyですが、Ruby本体は互換性はかなり気にしているように(Twitterでのコアメンバーのツイートを見ると)見えます。
ちなみに、VimだけはWebAPI-vimに依存しているんですが、こいつは、ラッパーで、実際の処理はcurlコマンドに投げます。つまり、cURLのコードからvim scriptに変換され、vim scriptがcurlを呼ぶという。このツールを作り始めた時には既に「絶対に最後にVim対応させる。ネタとして!」と思っていたのですが、一人だけこの意図に気づいてくれる人がいました。
cURL as DSLのVimスクリプト出力が最高にロックなのは、webapi.vim使っててそいつが下回りに(だいたい)cURL使うってことだよな。コードのジェネレータも生成されたコードも、本質的には無くてよかったものだってのが、最高にイカしている。
— MURAOKA Taro (@kaoriya) March 20, 2015
ウェブ版
ツール自体はGoで書いていて、コマンドラインで動作します。開したウェブサイトでも全機能をそのまま試せます。今回は、gopher.jsを使って、goのコードをjsにコンパイルしています。クライアントのブラウザ上で動作しています。Nexus 5でも動きました。JSで出すにあたってコアの部分は一切変更せずにそのまま使っています。今はUIとのやりとりにJSコードを少し書いたのですが、jQueryラッパーもあるので、DOMへのアクセス部分も含めて全部Goにできるんじゃないかな、と思っています。Selector APIも増えるとはいうものの、Polyfil系はaltJSにキビシーし、古いIE含めた後方互換性も保てるという意味でjQueryはもう少し使うと思います。
使ってみて思ったのは、ファイルサイズがやっぱり大きいということ。ネイティブのやつよりもビルド結果のファイルのサイズは半分ぐらいだったのですが、これで大きなウェブアプリを作るのはしんどいかもなぁ、とちょっと思っています。ただ、互換性は凄まじく高いので、今回みたいなちょっとしたツールにはいいんじゃないですかね?