Voting Badge

Vote++

GitHub Issueで賛成などを :+1: と書いてコメントすることが良くあります。 投票ボタン的な機能としてそういうのが欲しかったので、Travis CIのバッジのように表示+投票できるボタンを作りました。

上記にアクセスしてURL(実はキーなら何でもいい)を書くとバッジのURLを作ってくれます。

img + link というよく見るバッジの仕組みと同じです。

なぜ作ったか

最近ブログをGitHub Pagesに移動したため、 これから書く予定の記事候補をGitHub Issueをつかって管理しはじめていました。

しかし、記事候補に需要があるのか、 よくわからないので簡単な投票機能が欲しいなーと思ったのが始まりです(コメントは敷居高い)

また、shields.ioはバッジを作ってくれるサービスですが、 これを見た時にURLと投票数の関係を管理するサーバだけを作って、画像の生成などはshields.ioにまかせてしまえば、 意外と簡単に+1バッジが作れるかもしれないと思ったのも要因です。

GitHubの要望として既に同様のものがあります。

しかし実装されなさそうな気がしてるので、とりあえず作ってみるかという感じで作りました。

LevelDB

http://shields.io/ がNodeなので、合わせてNodeで書くとして、 保存する値は URL と 投票数 というだけというシンプルな感じで良かったので、KVSで適当なの使おうとしました。

そこで何となく気になってたLevelDBが使いたくなったので、 rvagg/node-levelupを使いました。

仕組み(初代)

仕組みは凄くシンプルで、以下の2つのAPIを実装するだけでした。

  • /vote?url=xxx で+1投票
  • /img?url=xxx でバッジ画像をURLを返す

imgでは、shields.ioのバッジ画像でリダイレクトを書けるというかなりずるい作りにしてました。

"https://img.shields.io/badge/Vote:+1:-" + count + "-red.svg?style=flat"

/img にアクセスすると、そのurlに対するcountを使ったバッジURLを返すという感じですね。

Heroku

どこに置くのかも考えずに完成して、そういえばHerokuは Node.js対応してた事を思い出したのでHerokuにおくことにしました。

LevelDBはファイルベースのDBなので、特に設定などは必要ありませんでした。

後はProcfileを作ってpushするだけでローカルと同じように動きました。

と思ったら、Heroku起動する度にファイルシステムはまっさらになるので、 永続化するには別途アドオンや外部に保存しないと行けないことに気付きました。

そして、HerokuでLevelDBを単純に永続化する方法はなさそうだったので諦めました。

Redis

HerokuではRedisなどはアドオンが無料で使えて、こちらは永続化が簡単にできそうだったので、 Redis版の実装を書きました。

せっかくなので、LevelDB,Redisの上にもう一層軽いラッパを作って 同じAPIで同じように扱えるようにして、LevelDB、Redisどちらでもうごかせるようにしてました。

api-wrapper

これでpushして永続化ができたので、完成!

ではなく、キャッシュ時間が異様に長いというGitHub特有の問題に遭遇しました。

キャッシュの問題

GitHubのREADMEやIssue等に貼った画像は基本的にキャッシュされた画像に差し替えされて表示されます。

shields.ioのカスタムバッジだと、普通にキャッシュされてしまって全然更新されないバッジが出来上がります。 (1日ぐらいキャッシュされる?)

Cache-Control: no-cache と Expires, Last-Modified or Etag あたりをちゃんと設定すれば、 キャッシュされなくなりますが、 shields.io の画像へリダイレクトしてるだけだったのでコントールすることが出来ませんでした。

自前でレンダリング

キャッシュコントールするためには、Herokuからバッジ(svg)を直接配布する必要がありました。

shields.ioのリポジトリを見ていたら、バッジを作成するモジュールが公開されてる事に気付きました。

node-canvas (cairo)のインストールが結構辛い感じでしたが、 Herokuで動かす設定も載っていたので、自分でバッジを作成するように切り替えました。

これで自分のところのコンテンツを返すので、レスポンスヘッダでキャッシュコントールもできるようになりました。

おわりに

まだ、何か以下の心配点がありますが、一応 +1 バッジを作ることが出来ました。

  • Herokuの無料サーバで上手く動き続けるの分からない。
  • 何か微妙にGitHubにキャッシュされてる気がする…
  • DejaVu SansをHerokuにおいていいのかよく分からないため、適応してない
    • そのためレイアウトが怪しい感じ
    • 👍を表示したいけどsvgだと表示環境依存になって不便

サンプルボタン => Vote++

ソースコードはazu/voting-badgeに公開してあるので、修正等送ってくださると嬉しいです。