Org Babelで文芸的プログラミング
12 Dec 2014(This is a post for the Emacs Advent Calendar 2014 from Japan, a similar English version can be found here)
Org modeの話を聞いたことがある方は、Org modeは「TODOリスト管理ツールの 一つだ」と思われる方が多いかもしれません。それはその通りですが、 Org Babelというモードで、「文芸的プログラミング(Literate Programming)」を実現することができます。
Org Babelの使い方を示すために、あるホストのモニタリング情報を表示する、 単純なSinatraアプリケーションを例えにします。
例: ホストのモニタリング情報を表示するSinatra app
表示したい項目をシステムの uptime
と ESTABLISHEDコネクションの数に絞り込みます。
uptime
20:37 up 4 days, 9:48, 5 users, load averages: 0.67 0.86 1.11
ESTABLISHED
コネクションの数:
netstat -an | grep ESTABLISHED | wc -l
5
この二つのコマンドの結果を集めて、 HTTPクライアントを使えば
GET /vars
で取れるようになります:
curl 127.0.0.1:9292/vars
事前準備
まず、ディレクトリの作成が必要です:
mkdir -p org
mkdir -p src
touch readme.org
org/
ディレクトリの下に置くファイルはOrg mode で書きますsrc/
ディレクトリの下に置くファイルはSinatraアプリケーションのソースコード
(readme.org
の作成は必要ではないですが、best practiceとしておすすめします…)
最終的に、ディレクトリ構成はこんな感じになります:
.
├── org
│ ├── app.org
│ └── run.org
├── readme.org
├── src
│ ├── Gemfile
│ ├── app.rb
│ └── config.ru
実装
アプリケーションの実装自体は org/app.org
に書きます。
touch org/app.org
プロトタイプ
SinatraのWebアプリケーションを実装前に、Org Babel を使ってプロトタイプを作りましょう。
Rubyのcode blockを書いたら、 C-c C-c
で実行することができます。
*** Notes on how to get the results We could use something simple like: #+begin_src ruby :results output code s = <<VARS Uptime: #{`uptime`} ESTABLISHED connections: #{`netstat -an | grep ESTABLISHED | wc -l`} VARS puts s #+end_src #+RESULTS: #+BEGIN_SRC ruby Uptime: 22:23 up 4 days, 11:34, 5 users, load averages: 0.58 0.46 0.30 ESTABLISHED connections: 8 #+END_SRC
:tangle
の使い方
:tangle
はOrg modeのcode block switch argumentsの一つです。
これを使えば、Org modeのcode blockは実装しているファイルを設定できます。
例えば、 :tangle
を使って Gemfile
と config.ru
の内容を書きましょう。
Dependencies from the app, just sinatra and webrick for the server is ok for now.
#+BEGIN_SRC ruby :tangle src/Gemfile
gem 'sinatra'
#+END_SRC
Needed to start the Sinatra application:
#+BEGIN_SRC ruby :tangle src/config.ru
require './app.rb'
run Sinatra::Application
#+END_SRC
全体例
EmacsのOrg modeバッファーで, C-c C-v t
か C-c C-v C-t
を叩けば、
Org modeの org-babel-tangle
の機能を呼び出します。
これで、動かせるプログラムは src/
ディレクトリの下に現れます。
#+TITLE: Monitoring HTTP endpoint This is a simple Sinatra application that provides the following endpoints: ‐ =/= :: which responds =OK= in case all is good with the server. ‐ =/vars= :: to get info about the system ** Bootstrapping the app *** Gemfile Dependencies from the app, just sinatra and webrick for now is ok. #+BEGIN_SRC ruby :tangle src/Gemfile gem 'sinatra' #+END_SRC *** Config.ru Needed to start the sinatra application #+BEGIN_SRC ruby :tangle src/config.ru require './app.rb' run Sinatra::Application #+END_SRC ** The App *** Import dependencies #+BEGIN_SRC ruby :tangle src/app.rb require 'sinatra' #+END_SRC *** =/= endpoint Just respond with =OK=. #+BEGIN_SRC ruby :tangle src/app.rb get '/' do 'OK' end #+END_SRC *** =/vars= endpoint Respond with info about the system: #+BEGIN_SRC ruby :tangle src/app.rb get '/vars' do r = <<VARS Uptime: #{`uptime`} ESTABLISHED connections: #{`netstat -an | grep ESTABLISHED | wc -l`} VARS r end #+END_SRC
動かす方法
次に、プログラムを動かす方法を org/run.org
にまとめます。
プログラムを動かす前に、Sinatraのプログラムに修正があったかも知れないので、
#+include: org/app.org
でこの依存関係を設定します。
#+TITLE: Running the Application #+include: "org/app.org" ** Run it To run it, we will need to get the dependencies first, and then start it with bundler: #+name: server #+BEGIN_SRC sh :dir src bundle install bundle exec rackup #+END_SRC Now let's send some requests to it: #+name: curl #+BEGIN_SRC sh :wait 1 while true; do curl 127.0.0.1:9292/vars 2> /dev/null sleep 1 done #+END_SRC
Emacsでlong running processesを実行できますが、そうするとEmacsがコードブロック
の結果待ちをしてしまいます… それを避けるため、
コードブロックの上に、 #+name
を設定すれば、私が作ったgemで叩くことができます:
gem install org-converge org-run org/run.org
そうすると結果は以下のようになります:
org-run org/run.org
...
Running code blocks now! (2 runnable blocks found in total)
server -- started with pid 71840
curl -- started with pid 71841
server -- Using rack 1.5.2
server -- Using rack-protection 1.5.3
server -- Using tilt 1.4.1
server -- Using sinatra 1.4.5
server -- Using bundler 1.7.1
server -- Your bundle is complete!
server -- Use `bundle show [gemname]` to see where a bundled gem is installed.
server -- [2014-12-11 22:56:42] INFO WEBrick 1.3.1
server -- [2014-12-11 22:56:42] INFO ruby 2.1.2 (2014-05-08) [x86_64-darwin11.0]
server -- [2014-12-11 22:56:42] INFO WEBrick::HTTPServer#start: pid=71848 port=9292
server -- 127.0.0.1 - - [11/Dec/2014 22:56:42] "GET /vars HTTP/1.1" 200 110 0.0527
curl -- Uptime:
curl -- 22:56 up 4 days, 12:07, 5 users, load averages: 0.78 0.84 0.76
curl --
curl --
curl -- ESTABLISHED connections:
curl -- 12
curl --
curl --
server -- 127.0.0.1 - - [11/Dec/2014 22:56:43] "GET /vars HTTP/1.1" 200 110 0.0210
curl -- Uptime:
curl -- 22:56 up 4 days, 12:07, 5 users, load averages: 0.78 0.84 0.76
curl --
curl --
curl -- ESTABLISHED connections:
curl -- 12
curl --
curl --
まとめ
Org modeはとても便利です!皆さんぜひお試しください。
しかも、Githubさんが org-rubyを使って .org
ファイルをHTMLに変換してくれるのです。
Ruby の Org modeパーサはまだOrg mode のすべての機能に追いついていませんが、
私がある程度メンテナンスしています。もしOrg Rubyに何かの問題があれば、
こちらに issue を発行していただければと思います。 :)
(そして、日本語が間違っている場合、こちらに PR ください…)