Rubyを使ってはてブ数をEmacsで表示する?凄いね。帰っていいよ。

この記事は、はてブ数をAPIで得る方法も書いてるので、Emacs使いじゃなくても役立つかもしれない?

やっぱりブログをやっていると、評価は気になるものである。はてなならばはてなブックマーク数だ。
かといってちまちまブラウザではてブ数を確認しにいくのはだるいし時間の無駄である。数秒であったとしても、塵も積もれば山となるっていうじゃない。

はてブ数が気になる、かといっていちいち確認するのは面倒。この問題に対して、Emacs使いならば「モードラインにはてブ数を表示する」という解決策を選ぶもの。これを実現する display-hatebu.el を速攻で作ってみた。

M-x install-elisp-from-emacswiki display-hatebu.el

でインストールしてくれ。さらにRubyjsonライブラリが必要だ。Rubyインタプリタを入れた後で

gem install json

jsonライブラリをインストールしよう。

使い方は簡単。M-x display-hatebu-set-urlではてブ数監視対象のURLをセットすると、モードラインにはてブ数が出てくるようになる。そして、1分ごとにはてブ数を自動更新する。

http://www.rubyist.net/~rubikitch/archive/display-hatebu.png
下部の44usersと出ているのがソレだ。

以下は動作原理の解説なので、興味ない人は読み飛ばしてよい。

display-hatebu.elの動作原理

モードラインはmode-line-formatによって制御される。で、そのデフォルト値は default-mode-line-format である。mode-line-formatは書式文字列が使えるが解説するのはめんどいので M-x describe-variable mode-line-format でdocstringを読んでね。その中に書かれたシンボルは変数名で、 mode-line-format と同じように解釈される。

その中に含まれている global-mode-string はいわばユーザ定義の mode-line-format だ。これにはてブ用の変数 display-hatebu-string を追加する。これではてブをモードラインに表示させることが可能になる。

あとは、 display-hatebu-string に表示させる内容を格納すりゃいい。

コマンドをEmacs内で非同期に実行して、その結果を変数に格納する

はてブ数を得るには外部コマンドを使って非同期化している。でないとはてブ数を得るたびにEmacsが固まってしまって、やってられんことになる。
そこでこの start-process-to-variable 関数が役立つ。これは、コマンドを非同期実行して、実行終了時に変数に格納する。

(defun start-process-to-variable (variable command &rest args)
  "コマンドの実行結果を変数に格納する。(非同期)"
  (lexical-let ((variable variable)
                (buf (get-buffer-create " start-process-to-variable")))
    (set-process-sentinel
     (apply 'start-process "start-process-to-variable" buf command args)
     (lambda (&rest ignore)
       (with-current-buffer buf
         (set variable (buffer-substring 1 (1- (point-max))))
         (erase-buffer))))))

試しに (start-process-to-variable 'display-hatebu-string "date") を評価すれば日付が出てくるよ。

はてブ数を得る方法 in Ruby

はてブにはAPIが提供されていて、「http://b.hatena.ne.jp/entry/json/?url=対象URL」にアクセスするとはてブの情報をJSON形式で得ることができる。
display-hatebu.elでは以下のスクリプトワンライナー化してあるので、インストールしなくてもよい。

# -*- coding: utf-8 -*-
require 'json'
require 'open-uri' # !> character class has duplicated range
url = "http://d.hatena.ne.jp/rubikitch/20100210/emacs"
api_url = "http://b.hatena.ne.jp/entry/json/?url=#{url}"
json = JSON.parse(open(api_url).read)
# はてブ数
json["count"]     # => "44"
# JSONのデータ
json
# => {"related"=>
#      [{"count"=>"24",
#        "url"=>"http://gitready.com/",
#        "eid"=>"11697368",
#        "title"=>"git ready » daily tips for the noob to the guru",
#        "entry_url"=>"http://b.hatena.ne.jp/entry/gitready.com/"},
#       {"count"=>"63",
#        "url"=>"http://coreblog.org/ats/cui-based-debugger-pudb",
#        "eid"=>"19191666",
#        "title"=>"\n        CUIベースなのに無駄にビジュアルなPython用デバッガ - pudb — TRIVIAL TECHNOLOGIES 2.0\n    ",
#        "entry_url"=>"http://b.hatena.ne.jp/entry/coreblog.org/ats/cui-based-debugger-pudb"},
#       {"count"=>"32",
#        "url"=>"http://d.hatena.ne.jp/hiratara/20091112/1258023732",
#        "eid"=>"17305307",
#        "title"=>"Gitリポジトリ中のサブディレクトリを別のリポジトリにする - a geek born in Tomakomai",
#        "entry_url"=>"http://b.hatena.ne.jp/entry/d.hatena.ne.jp/hiratara/20091112/1258023732"},
#       {"count"=>"5",
#        "url"=>"http://d.hatena.ne.jp/m-hiyama/20091127/1259283952",
#        "eid"=>"17588533",
#        "title"=>"本日のtip:Emacsで、文字をアンパサンドによる文字参照に置き換える - 檜山正幸のキマイラ飼育記",
#        "entry_url"=>"http://b.hatena.ne.jp/entry/d.hatena.ne.jp/m-hiyama/20091127/1259283952"}],
#     "count"=>"44",
#     "url"=>"http://d.hatena.ne.jp/rubikitch/20100210/emacs",
#     "bookmarks"=>
#      [{"comment"=>"other-window-or-sprit", "timestamp"=>"2010/02/11 15:16:22", "user"=>"hyrum", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 14:11:27", "user"=>"hmaro", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 12:51:05", "user"=>"ranryu", "tags"=>["keybind", "elisp", "tips", "emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 12:34:12", "user"=>"hshinoda", "tags"=>["Emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 12:05:34", "user"=>"sattzi", "tags"=>[]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 10:51:24", "user"=>"Shinnya", "tags"=>["emacs", "editor", "Tips"]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 08:35:58", "user"=>"taka222", "tags"=>["rssflashg"]}, # !> character class has duplicated range
#       {"comment"=>"", "timestamp"=>"2010/02/11 06:31:35", "user"=>"kei10in", "tags"=>["Emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 04:42:44", "user"=>"IceNine", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 04:21:52", "user"=>"wakarany", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 02:49:19", "user"=>"lifesan", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 02:27:37", "user"=>"ryskosn", "tags"=>["Emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 02:07:35", "user"=>"aoi-sora", "tags"=>["emacs"]},
#       {"comment"=>" これは便利ー", "timestamp"=>"2010/02/11 01:26:43", "user"=>"hiro_nemu", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/11 00:08:31", "user"=>"delico69", "tags"=>[]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 23:26:28", "user"=>"tyru", "tags"=>["!share-with", "!twitter", "!tumblr-tech", "emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 23:13:58", "user"=>"uhiaha888", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 22:44:59", "user"=>"funayama1116", "tags"=>["emacs", "elisp"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 20:59:49", "user"=>"gemini7", "tags"=>["emacs", "elisp"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 20:58:10", "user"=>"m_kisara", "tags"=>[]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 20:44:37", "user"=>"Akineko", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 20:39:47", "user"=>"aki77", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 20:36:57", "user"=>"Watson", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 20:23:48", "user"=>"gogatsu26", "tags"=>["Emacs"]},
#       {"comment"=>"デフォルトだと C-x o な other-window を1ストロークに。かつ、ウィンドウが1つのときは分割するように。", "timestamp"=>"2010/02/10 20:16:59", "user"=>"june29", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 17:53:06", "user"=>"wideangle", "tags"=>[]},
#       {"comment"=>"window の扱いについて。僕も C-t で切り替えてたけど、コンテキストで切り替えるのは考えてなかった。", "timestamp"=>"2010/02/10 17:52:03", "user"=>"tomoya", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 17:51:30", "user"=>"kshimo69", "tags"=>["emacs", "from_greader", "from_plagger"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 17:36:40", "user"=>"nanolia", "tags"=>["emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 17:35:47", "user"=>"n2s", "tags"=>["Emacs"]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 17:35:16", "user"=>"RanTairyu", "tags"=>[]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 17:31:02", "user"=>"yokochie", "tags"=>["emacs", "elisp"]},
#       {"comment"=>"Emacsで左右に垂直分割するとPuTTY上でマウスコピペしようとするときにムキーってなるので使ってないがよく考えるまでもなくEmacsさんは悪くなかった関係なかった",
#        "timestamp"=>"2010/02/10 17:30:24",
#        "user"=>"ezookojo",
#        "tags"=>[]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 17:15:26", "user"=>"nmti", "tags"=>[]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 17:11:08", "user"=>"inc_aka", "tags"=>[]},
#       {"comment"=>"Emacs: そろそろEmacsのウィンドウについて一言いっとくか - (rubikitch loves (Emacs Ruby CUI)) :", "timestamp"=>"2010/02/10 16:57:02", "user"=>"bsiyo", "tags"=>[]},
#       {"comment"=>"", "timestamp"=>"2010/02/10 16:29:57", "user"=>"yinkyweb", "tags"=>["emacs"]},
#       {"comment"=>" ウィンドウ切り替え", "timestamp"=>"2010/02/10 16:25:53", "user"=>"tettsyun", "tags"=>["emacs"]}],
#     "title"=>"そろそろEmacsのウィンドウについて一言いっとくか - (rubikitch loves (Emacs Ruby CUI))",
#     "eid"=>"19224459",
#     "entry_url"=>"http://b.hatena.ne.jp/entry/d.hatena.ne.jp/rubikitch/20100210/emacs",
#     "screenshot"=>"http://screenshot.hatena.ne.jp/images/200x150/6/1/4/5/1/519174ac408c0347a36fd530b1ce9f396d7.jpg"}