Microservices 風 Emacs HTTP Server(s) on Heroku

## 概要

- Emacs HTTP Server ([elnode](https://github.com/nicferrier/elnode)) を Heroku で動かしてみた
- Emacs on Heroku 同士でデータのやりとりを行ってみた

といった出来事から得られた感想を述べます。

タイトルで Microservices と書きましたが、要するに **Emacs の環境が Heroku 上に簡単に用意できる** ことを知っていただくことが本記事のメインとなっております。

## 経緯

Emacs ユーザの皆様におかれましては

    「Emacs で HTTP Server を起動できるから Heroku で動かしたい！！」

と日頃より思い悩んでいるはずで、偶然にも最近 [gongo/emacs-heroku-docker](https://github.com/gongo/emacs-heroku-docker) を作成したことで
その悩みは解消されることと存じ上げます。

簡単に説明すると、この Docker Image を利用して Heroku にリリースすることで、
[Cask](https://github.com/cask/cask) で依存パッケージをインストールしつつ、好きな Emacs プロセスを起動できます。
Heroku なのでおそらくは [elnode](https://github.com/nicferrier/elnode) や [simple-httpd](https://github.com/skeeto/emacs-web-server) を起動することになるでしょう。

Example:

- [emacs-heroku-docker/example](https://github.com/gongo/emacs-heroku-docker/tree/5b2d7b49a46613cf6bfd37751646447f57a933ba/example)
- https://emacs-heroku-docker-sample.herokuapp.com/

これにより、いくつでも Emacs on Heroku の環境を起動できるようになりました。

そして、複数の HTTP Server を立てられるようになった人類は、
お互いにデータのやりとりを望むようになります。一つになりたい。

## 作ってみました

- https://emacs-json-formatter.herokuapp.com/
    - 以後 **ejf-front** と呼ぶ
- https://emacs-json-formatter-api.herokuapp.com/
    - 以後 **ejf-api** と呼ぶ

JSON テキストを整形するやつです。
拙作 [gongo/json-reformat](https://github.com/gongo/json-reformat) の Web 版といったところです。

![emacs-json-formatter.gif](https://qiita-image-store.s3.amazonaws.com/0/6635/0652f0e3-4c7c-354b-c1cd-467d23efc5b2.gif)


### 1. 構成

- `ejf-front` = UI を提供する
     - 整形したい JSON テキスト入力フィールド
     - 整形後の JSON テキスト表示フィールド
- `ejf-api` = POST されてきた JSON テキストを整形して返す
     - 整形時、JSON テキストに不備(カンマがない、キーの書き方がおかしい、など)があれば、その旨とおおよその原因の位置を返す(おおよそです)

### 2. 仕組み

2つのサーバ(サービス)で構成されているので、そこまで複雑ではありません。

1. `ejf-front` で、整形したい JSON テキストを入力し、ボタンクリック
    - [front/public/index.html#L25](https://github.com/gongo/emacs-json-formatter-web/blob/628042ecc2f85494e18f6b9cb0b6e58aef760ced/front/public/index.html#L25)
2. 入力された JSON テキストを [superagent.js](https://github.com/visionmedia/superagent) で `ejf-api` に POST する
    - [front/public/app.js#L19-L22](https://github.com/gongo/emacs-json-formatter-web/blob/628042ecc2f85494e18f6b9cb0b6e58aef760ced/front/public/app.js#L19-L22)
3. `ejf-api` で、受けとった JSON テキストを整形する
    - [api/ejf-api.el#L25](https://github.com/gongo/emacs-json-formatter-web/blob/628042ecc2f85494e18f6b9cb0b6e58aef760ced/api/ejf-api.el#L25)
4. 整形に成功すればそのテキストを、失敗すれば原因と詳細な情報をレスポンスとする
    - [api/ejf-api.el#L25-L41](https://github.com/gongo/emacs-json-formatter-web/blob/628042ecc2f85494e18f6b9cb0b6e58aef760ced/api/ejf-api.el#L25-L41)
5. `ejf-front` は整形後のテキスト or エラーメッセージを表示する
    - [front/public/app.js#L24-L3](https://github.com/gongo/emacs-json-formatter-web/blob/628042ecc2f85494e18f6b9cb0b6e58aef760ced/front/public/app.js#L24-L36)

### 3. コメント

概ね動いているのですが、本記事執筆までに達成できなかった点が不満として残りました。皆様方お気付きかと思いますが

**サーバ間のやりとりが Emacs で行われていない**

これは Emacs の限界ではなく、単に私がめんどくさがってやらなかっただけです。

---

Emacs で HTTP 通信を行う際、私はよく [emacs-request](https://github.com/tkf/emacs-request) を愛用しています。
本作も途中までは `emacs-request` で JSON テキストを POST していましたが、
現在の `emacs-request` のバージョン (0.2.0) だと

**GET/POST のレスポンスステータスが失敗系の場合、そのレスポンスボディが破棄される**

という問題がある(仕様である)[^1]ということがわかりました。

`ejf-api` は整形失敗時に JSON でエラーメッセージやら怪しい箇所の情報をレスポンスしてくれるため、
これが破棄されてしまうと `ejf-front` 側では「とりあえずエラーになった」ということしかわかりません。

もしかしたらいい抜け道があったかもしれません。`emacs-request` を使わない手もありましたが、
今回はそこまで追求せず、結局 POST は JavaScript に頼ることにしました。

---

先述の通り私がめんどくさがってやらなかっただけで、
自力で頑張れば Pure Emacs Lisp で Emacs 同士の HTTP 通信は問題ないでしょう。
他にも [ルーターは簡単に書ける](https://github.com/gongo/emacs-json-formatter-web/blob/628042ecc2f85494e18f6b9cb0b6e58aef760ced/api/ejf-api.el#L51-L52) し、Emacs が持つパワフル文字列処理パワーを存分に発揮できるので、
「データを受けとって変換して返す」という基本的な Web サービスとしては充分やっていけるのではないでしょうか。

## まとめ

- Emacs の環境を簡単に Heroku 上に用意できる
- (今回はお見せできなかったが)Pure Emacs Lisp で Microservices もがんばればいける

皆様も是非 Emacs on Heroku で世界にあなただけの Emacs を見せつけましょう

## 参考資料

- [heroku-docker を使って Emacs & Cask がインストールされた Heroku 環境 (Slug) を作成する Docker イメージ作った - Thanks Driven Life](http://gongo.hatenablog.com/entry/2015/11/14/212637)
    - Docker Image を作った時の詳細についてはこちら
- [nicferrier/elnode](https://github.com/nicferrier/elnode)
- [tkf/emacs-request](https://github.com/tkf/emacs-request)


[^1]: 自分が見つけられなかっただけで、実は回避策あるかも
[^2]: `emacs-request` の callback を利用すると、処理終わるまでに elnode の dispatch で呼ばれた関数を抜けて何もレスポンス無い、みたいな悲しい感じになって、結局 `sync: t` すると今度はブロックするし、みたいな