<?xml version="1.0" encoding="utf-8" ?>


<?xml-stylesheet href="http://d.hatena.ne.jp/nitro_idiot/rssxsl" type="text/xsl" media="screen"?>


<rdf:RDF
xmlns="http://purl.org/rss/1.0/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xml:lang="ja">
<channel rdf:about="http://d.hatena.ne.jp/nitro_idiot/rss">
<title>八発白中</title>
<link>http://d.hatena.ne.jp/nitro_idiot/</link>
<description>八発白中</description>

<dc:creator>nitro_idiot</dc:creator>
<dc:date>2013-03-21T06:12:08+09:00</dc:date>
<items>
<rdf:Seq>
<rdf:li rdf:resource="http://d.hatena.ne.jp/nitro_idiot/20130320/1363787619"/>
<rdf:li rdf:resource="http://d.hatena.ne.jp/nitro_idiot/20130223/1361634089"/>
<rdf:li rdf:resource="http://d.hatena.ne.jp/nitro_idiot/20130215/1360931962"/>
<rdf:li rdf:resource="http://d.hatena.ne.jp/nitro_idiot/20130208/1360335474"/>
<rdf:li rdf:resource="http://d.hatena.ne.jp/nitro_idiot/20121210/1355118962"/>
</rdf:Seq>
</items>
</channel>



<item rdf:about="http://d.hatena.ne.jp/nitro_idiot/20130320/1363787619">
<title> Quickdocs.org で学ぶCommon LispのWebアプリ運用ノウハウ</title>
<link>http://d.hatena.ne.jp/nitro_idiot/20130320/1363787619</link>
<description> 先日、QuickdocsというWebサービスを作りました。 Lispライブラリのドキュメント集約サイト「Quickdocs.org」を作りました - 八発白中 アプリ部分はほとんどCommon Lispで書かれています。今までもいくつかCommon LispでWebアプリを書いたことはありますが、公開されたWebサ</description>

<content:encoded><![CDATA[
<div class="section">
<p>先日、<a href="http://quickdocs.org" target="_blank">Quickdocs</a>というWebサービスを作りました。</p>
<ul>
<li> <a href="http://d.hatena.ne.jp/nitro_idiot/20130223/1361634089" target="_blank"> Lispライブラリのドキュメント集約サイト「Quickdocs.org」を作りました - 八発白中</a></li>
</ul>
<p>アプリ部分はほとんどCommon Lispで書かれています。今までもいくつかCommon LispでWebアプリを書いたことはありますが、公開されたWebサービスはこれが初めてです。<br><br>公開当初は半日に1回落ちたり、表示が変になったりしていました。そこで2週間ほどかけて、不具合の原因を突き止めたり、それを修正して堅牢にしたり、レスポンス速度を改善したりしてきました。</p>
<div class="twitter-detail twitter-detail-left">
  <div class="twitter-detail-user">
    <a class="twitter-user-screen-name" href="http://twitter.com/nitro_idiot">
      <img src="http://a0.twimg.com/profile_images/2796694955/79f4540aa574c63d7b3200bea7a0b9d2_normal.png" alt="nitro_idiot" height="48" width="48">
    </a>
  </div>
  <div class="twitter-detail-tweet">
    <p class="twitter-detail-text">
      Common LispのWebアプリの運用ノウハウが少し溜まってきたから車にはねられるなどする前に共有したい
    </p>
    <p class="twitter-detail-info">
      <a href="http://twitter.com/nitro_idiot/status/308986849848856576" class="twitter-detail-info-permalink"><span class="twitter-detail-info-date">2013-03-06</span> <span class="twitter-detail-info-time">02:06:20</span></a> <span class="twitter-detail-info-source">via web</span>
    </p>
  </div>
</div>
<p>試行錯誤を繰り返してきており、伝えたいことも多いです。書いてみるとかなり雑然としてしまったのですべての人向けにおすすめできるものではなくなってしまいましたが、少しでも他にCommon Lispの運用をしている人や後に続く人の助けになれば良いと思います。<br><br>ちなみに以前にも似たような記事を書きました。いくつかやり方が変わっていますが、少しは参考になるかもしれません。</p>
<ul>
<li> <a href="http://d.hatena.ne.jp/nitro_idiot/20120323/1332516342" target="_blank"> Common LispのWebアプリケーションを社内運用してみた - 八発白中</a></li>
<br></ul>
<h4> Overview</h4>
<p>Quickdocsの構成はシンプルです。</p>
<ul>
<li> <a href="http://fukamachi.github.com/ningle" target="_blank">ningle</a> (Webアプリケーションフレームワーク)</li>
<li> <a href="http://nginx.org" target="_blank">Nginx</a> (フロントサーバ)</li>
<li> <a href="http://supervisord.org" target="_blank">Supervisord</a> (死活監視)</li>
<li> SBCL 1.0.58</li>
</ul>
<p>Webフレームワークとして<a href="https://fukamachi.github.com/ningle/" target="_blank">ningle</a>を使い、<a href="http://clacklisp.org" target="_blank">Clack</a>のFastCGIバックエンドを使っています。フロントサーバには<a href="http://nginx.org" target="_blank">Nginx</a>を置いています。<br><br>レスポンスのキャッシュに<a href="http://www.squid-cache.org/" target="_blank">Squid</a>の導入も考えましたが、Nginx自身にキャッシュの機能がついていたのでそれを使っています。<br><br>Quickdocsの特殊なところではあるのですが、DBはありません。ユーザからの投稿を受け付ける機能がなく、トランザクションが不要なので、必要なデータはすべてS式でファイルに記録しておいて、サーバ起動時にロードするようにしています。<br><br>サーバはさくらVPSのメモリ1Gを使っています。1台でJenkinsやQuickdocsのステージング環境、バッチ処理なども行なっています。まだリソースには余裕はありますが、1台でなんでもこなすので、お互いの処理が干渉しないように注意する必要があります。この方法は後ほど紹介します。<br><br>以下では主にこの構成を前提に話を書きます。<br></p>
<h4> サーバを起動</h4>
<p>Common Lispでプロセスをdaemon化する方法はいくつかありますが、僕は使っていません。単純に<code>--eval</code>をつけてワンライナーでサーバを立ちあげています。<br><br>毎回起動するのを楽にするために、Makefileを書きました。<br></p>
<h5> Makefile</h5>
<p>以下はQuickdocsで使っているMakefileの抜粋です。<code>sbcl</code>というマクロを作って、ワンライナーを書きやすくしています。</p>
<pre class="syntax-highlight">
<span class="synIdentifier">SERVER_PORT</span>=8080
<span class="synIdentifier">SWANK_PORT</span>=4005
<span class="synIdentifier">PROJECT_ROOT</span>=<span class="synIdentifier">$(abspath $(</span><span class="synStatement">dir</span><span class="synIdentifier"> $(lastword $(MAKEFILE_LIST))))</span>

<span class="synPreProc">define sbcl</span>
<span class="synPreProc">sbcl --noinform --disable-debugger \</span>
<span class="synPreProc">--eval '(pushnew #P&#34;</span><span class="synIdentifier">$(PROJECT_ROOT)</span><span class="synPreProc">/&#34; asdf:*central-registry*)' \</span>
<span class="synPreProc">--eval '(progn </span><span class="synIdentifier">$1</span><span class="synPreProc">)' \</span>
<span class="synPreProc">--eval '(progn </span><span class="synIdentifier">$2</span><span class="synPreProc">)'</span>
<span class="synPreProc">endef</span>

<span class="synIdentifier">start:</span>
<span class="synConstant"></span><span class="synIdentifier">$(</span><span class="synStatement">call</span><span class="synIdentifier"> sbcl, \</span>
<span class="synIdentifier">(ql:quickload :quickdocs)</span><span class="synConstant"> (ql:quickload :swank), </span><span class="synSpecial">\</span>
<span class="synConstant">(quickdocs.server:start-server :mode :production :debug nil :server :fcgi :port </span><span class="synIdentifier">$(SERVER_PORT)</span><span class="synConstant">) </span><span class="synSpecial">\</span>
<span class="synConstant">(swank:create-server :port </span><span class="synIdentifier">$(SWANK_PORT)</span><span class="synConstant"> :style :spawn :dont-close t))</span>
</pre>

<p><code>swank:create-server</code>は必須ではありませんが、デバッグや、後述するホットデプロイに必要です。<br><br>これで以下のように実行すればサーバが起動できます。</p>
<pre>
make -f /srv/www/quickdocs/Makefile start SERVER_PORT=10080 SWANK_PORT=4025
</pre>

<p>どのディレクトリにいるときでも大丈夫なように、PROJECT_ROOTはカレントディレクトリではなくMakefileがあるディレクトリにする必要があります。<br><br>もしHunchentootを使いたいなら、<code>:server</code>の部分を<code>:fcgi</code>ではなく<code>:hunchentoot</code>などにしないといけません。<br></p>
<h5> Nginx</h5>
<p>Hunchentootを使って直接リクエストを捌く気なら、SERVER_PORT=80を指定すればNginxなどは必要ありません。別ポートで起動して、リバースプロキシを使ったり、上記のようにFastCGIバックエンドを使うならNginxやApacheが必要です。<br><br>以下はNginxの設定の抜粋です。</p>
<pre>
server {
    listen 80;
    server_name quickdocs.org;
    access_log  /var/log/nginx/quickdocs_access.log;
    error_log   /var/log/nginx/quickdocs_error.log;

    error_page 500 502 503 504 /html/50x.html;

    location = /html/50x.html {
        root /srv/www/quickdocs/static/;
    }
    location ~&#42; ^/(css|images|js)/(.+)$ {
        root /srv/www/quickdocs/static/;
        access_log off;
        expires max;
        break;
    }
    location ~&#42; ^/(favicon.ico|robots.txt|apple-touch-icon) {
        log_not_found off;
        access_log off;
        root /srv/www/quickdocs_stage/static;
    }

    location / {
        fastcgi_pass 127.0.0.1:10080;
        fastcgi_connect_timeout 300;
        include fastcgi_params;
    }
}
</pre>

<p>ここでは以下のことを行なっています。</p>
<ul>
<li> 50x系エラーが出たときに静的なエラーページを表示</li>
<li> 静的ファイルは直接配信</li>
<li> それ以外はFastCGIサーバに投げる</li>
</ul>
<p>server_nameには自分が持っているドメインを指定し、DNSレコードを適切に設定しておきます。<br><br>上記のファイルを/etc/nginx/sites-available/quickdocsに置いてsites-enabledにシンボリックリンクを貼り、Nginxを再起動します。<br><br>これでサーバの80番ポートにアクセスしてサイトが表示されれば成功です。<br></p>
<h4> 死活監視</h4>
<p>そのまま起動しておくと、いくらエラーハンドリングをしていても、メモリ不足などの致命的なエラーが出ればサービスが落ちてしまいます。そのための死活監視です。<br><br>僕は <a href="http://supervisord.org" target="_blank">Supervisord</a> を使いましたが、daemontoolsなど自分が慣れたものを使えばいいと思います。<br><br>Supervisorでは以下のようにcommandのところでmake startするようにすれば良いです。</p>
<pre>
&#91;program:quickdocs]
command=make -f /srv/www/quickdocs/Makefile start SERVER_PORT=10080 SWANK_PORT=4025
numprocs=1
autostart=true
autorestart=true
user=quickdocs
redirect_stderr=true
stdout_logfile=/var/log/supervisor/quickdocs.log
</pre>

<p>良いことなのかどうかわかりませんが、僕はSupervisorでサーバの再起動も行なっています。</p>
<pre>
sudo supervisorctl restart quickdocs
</pre>

<p>起動時にプロセスIDとか記録しておいてkillするの面倒ですからね。<br></p>
<h4> デプロイ</h4>
<p>デプロイするのに毎回サーバにSSHするのは面倒なので、途中からデプロイツールを導入しました。<br></p>
<h5> Fabric</h5>
<p>僕は<a href="http://fabfile.org" target="_blank">Fabric</a>を使いましたが、Rubyが好きな人は<a href="http://capistranorb.com/" target="_blank">Capistrano</a>が良いかもしれません。<br><br>以下はfabfile.pyの抜粋です。</p>
<pre class="syntax-highlight">
<span class="synPreProc">from</span> fabric.api <span class="synPreProc">import</span> sudo, run, env, cd
env.hosts = ['<span class="synConstant">yourquickdocs.org</span>']
env.user = '<span class="synConstant">quickdocs</span>'
env.directory = '<span class="synConstant">/srv/www/quickdocs</span>'
env.project_name = '<span class="synConstant">quickdocs</span>'

<span class="synStatement">def</span> <span class="synIdentifier">update</span>():
    with cd(env.directory):
        run('<span class="synConstant">git pull</span>')

<span class="synStatement">def</span> <span class="synIdentifier">restart</span>():
    sudo('<span class="synConstant">supervisorctl restart %s</span>' % env.project_name, shell=False)

<span class="synStatement">def</span> <span class="synIdentifier">deploy</span>():
    update()
    restart()

<span class="synStatement">def</span> <span class="synIdentifier">tail_access</span>():
    run('<span class="synConstant">tail -f /var/log/nginx/%s_access.log</span>' % env.project_name)

<span class="synStatement">def</span> <span class="synIdentifier">tail_error</span>():
    run('<span class="synConstant">tail -n 100 -f /var/log/apps/%s_error.log</span>' % env.project_name)
</pre>

<p>これで <code>fab deploy</code> などとすればデプロイできます。<br></p>
<h5> ホットデプロイ</h5>
<p>始めの頃はデプロイするとき、素直にプロセスを再度立ちあげていたため、デプロイするたびに5秒くらいダウンタイムがありました。<br><br>けれど、せっかくのLispなのでサーバを無停止でデプロイしたいです。<br><br>サーバ起動時にswankサーバを立ち上げるようにしていれば、<a href="http://quickdocs.org/swank-client/" target="_blank">swank-client</a>というライブラリを使ってサーバに繋いで任意のコードを実行させられます。これを利用して以下のようにMakefileとfabfile.pyに追記します。</p>
<pre class="syntax-highlight">
<span class="synIdentifier">hot_deploy:</span>
<span class="synConstant"></span><span class="synIdentifier">$(</span><span class="synStatement">call</span><span class="synIdentifier"> $(LISP), \</span>
<span class="synIdentifier">(ql:quickload :swank-client)</span><span class="synConstant">, </span><span class="synSpecial">\</span>
<span class="synConstant">(swank-client:with-slime-connection (conn &#34;localhost&#34; </span><span class="synIdentifier">$(SWANK_PORT)</span><span class="synConstant">) </span><span class="synSpecial">\</span>
<span class="synConstant">(swank-client:slime-eval (quote (handler-bind ((error (function continue))) (ql:quickload :quickdocs))) conn)) </span><span class="synSpecial">\</span>
<span class="synConstant">(sb-ext:quit))</span>
</pre>

<pre class="syntax-highlight">
<span class="synStatement">def</span> <span class="synIdentifier">hot_deploy</span>():
    with cd(env.directory):
        run('<span class="synConstant">make hot_deploy SWANK_PORT=%s</span>' % env.swank_port)
</pre>

<p><code>fab update hot_deploy</code> などとすればホットデプロイできます。<br></p>
<h4> サーバにテスト環境を立てる</h4>
<p>ローカルでは動いたのにデプロイすると動かないみたいなことがあります。Lisp処理系の違いだったり、OSの違いだったり、依存パッケージが足りなかったり。そこで同じサーバか似たサーバにテスト環境を置いて一通りテストします。<br><br>このとき、テスト環境用に新しくユーザを作る (たとえばquickdocs_dev) か、Quicklispのdistを分けるほうが賢明です。これはテスト環境だけQuicklispの新しいバージョンを試してちゃんと動くかをチェックしたりするのに使います。<br><br>同じユーザでQuicklispのディレクトリだけを分けたいというときは、.sbclrcを複数用意して、<code>--userinit</code>の指定を変更すれば実現できます。<br><br>試してはいませんが、quicklispのディレクトリ自身をリポジトリに含めればRubyの<a href="http://gembundler.com" target="_blank">Bundler</a>やPerlの<a href="http://search.cpan.org/~miyagawa/carton-v0.9.10/docs/carton.pod" target="_blank">Carton</a>のような使い方ができるかもしれません。<br></p>
<h4> パフォーマンス改善</h4>
<p>NginxでFastCGIキャッシュを使えばアプリ側にビューキャッシュの機構を作ったりSquidなどキャッシュサーバを導入せずに済みます。<br><br>ただ、ページ単位でのキャッシュの削除機能がデフォルトでついていないため、<a href="https://github.com/FRiCKLE/ngx_cache_purge" target="_blank">ngx_cache_purge</a>というプラグインを含めてNginxをビルドすると扱いやすくなります。<br><br>以下はキャッシュを利用するときのNginxの設定の抜粋です。</p>
<pre>
    # HEADアクセスはキャッシュを使わない
    if ($request_method = &#34;HEAD&#34;) {
        set $do_not_cache 1;
    }

    location / {
        fastcgi_pass 127.0.0.1:9080;
        fastcgi_connect_timeout 30;
        fastcgi_cache qd_cache;
        fastcgi_no_cache $do_not_cache;
        fastcgi_cache_bypass $do_not_cache;
        fastcgi_cache_key $scheme://$host$request_uri;
        fastcgi_cache_valid  200 1d;
        fastcgi_cache_valid  301 1d;
        fastcgi_cache_valid  any 15m;
        include fastcgi_params;
    }
</pre>

<p>Quickdocsの場合は内容が変更されることが基本的にないので、キャッシュの期間を長めにしています。アプリによって3hなどに変更すると良いと思います。<br></p>
<h4> LispとShell</h4>
<h5> シェルコマンドを使う</h5>
<p>Common Lisperなら堂々とすべてのコードをCommon Lispで完結すべきだ、という意見もあると思いますが、実際にアプリケーションを書いていると、やはり理想論だと感じます。<br><br>Common Lispのライブラリは有名なスクリプト言語と比べればずっと少ないですし、無いものを自前で一から作ってそれなりのクオリティで保守し続けられるほどLispコミュニティは成熟していません。必要な部分はうまく他の言語の力を借りたほうが現状ではうまくいきます。<br><br>具体例をあげます。たとえば、Common Lispで<a href="http://www.imagemagick.org/script/index.php" target="_blank">ImageMagick</a>を使いたい、と思ったときに<a href="http://quickdocs.org/lisp-magick/" target="_blank">lisp-magick</a>というImageMagickのCLバインディングを使おうとしたことがあります。<br><br>けれど、Quicklispでインストールして試してみるも、素直に動かず、調べてみると長くメンテナンスもされていないようなので使うのを諦めました。そんな怪しいライブラリを使うよりは、標準でついてる<code>convert</code>コマンドを<code>asdf:run-shell-command</code>で叩くほうがずっと良いです。<br><br>Quickdocsでは他にMarkdownからHTMLへの変換にHaskellの<a href="http://johnmacfarlane.net/pandoc/" target="_blank">Pandoc</a>を使っています。<a href="http://quickdocs.org/cl-markdown" target="_blank">CL-Markdown</a>はよく出来たライブラリですが、GitHub Flavored Markdownに対応していなかったので使うのをやめました。<br></p>
<h5> Lisp内でシェルを叩くときの注意</h5>
<p>Common Lispでシェルコマンドを実行するときに注意すべき点ももちろんあります。もしそのシェルコマンドの実行にかなり時間がかかるケースがある場合は、適切にタイムアウト処理をしなければいけません。<br><br><a href="http://quickdocs.org/trivial-timeout/" target="_blank">trivial-timeout</a>というライブラリがありますが、使い物になりません。タイムアウトはしますが、例外を投げるだけでシェルプロセスがそのままゾンビ化してしまうからです。<br><br>これは普通に<code>timeout</code>コマンドを使えば良いです。</p>
<pre class="syntax-highlight">
timeout <span class="synConstant">5</span> pandoc /path/to/README.markdown
</pre>

<p>また、別プロセスで実行されたシェルコマンドがリソースを食いつぶしてしまって、Webサーバのレスポンスに影響が出ることがあります。これを防ぐために<code>ulimit</code>や<a href="http://cpulimit.sourceforge.net/" target="_blank">cpulimit</a>を適切に使うと良いです。<br><br>まあ、毎回実行する必要もないものはキャッシュしておいたり、あらかじめバッチ処理で生成しておくほうが確実とは思います。<br></p>
<h4> おわりに</h4>
<p>いくつか抜粋して紹介しましたが、Quickdocsの全ソースコードはGitHubに上がっています。</p>
<ul>
<li> <a href="https://github.com/fukamachi/quickdocs" target="_blank">fukamachi/quickdocs ? GitHub</a></li>
</ul>
<p><a href="http://clacklisp.org" target="_blank">Clack</a>を使っていて実際に公開され動いているWebアプリケーションとしては初めてかもしれません。参考にしていただければと思います。<br><br>長々と書きましたが、僕はCommon LispでのWebアプリの運用に長けているわけではありませんし、もちろん運用を長く経験された方からみると拙い解決方法が多いと思います。<br><br>とはいえ、Common Lispのような小さい言語コミュニティでは、開発も運用もとりあえずやってみて、まず道を作っていくことが重要だと思いました。<br><br>何かわからないことがあれば遠慮なく<a href="https://twitter.com/nitro_idiot" target="_blank">Twitter</a>やE-mail (e&#46;arrows&#64;gmail&#46;com) などで聞いてください。<br><br>逆に、他にもCommon Lispのアプリケーションの運用をしている方はぜひノウハウを教えてください！</p>
</div>
]]></content:encoded>
<dc:creator>nitro_idiot</dc:creator>
<dc:date>2013-03-20T22:53:39+09:00</dc:date>
</item>
<item rdf:about="http://d.hatena.ne.jp/nitro_idiot/20130223/1361634089">
<title> Lispライブラリのドキュメント集約サイト「Quickdocs.org」を作りました</title>
<link>http://d.hatena.ne.jp/nitro_idiot/20130223/1361634089</link>
<description> Quicklispに登録されているCommon Lispライブラリの情報をWebで見られるサイト「Quickdocs.org」を作りました。 http://quickdocs.org/ さくらVPS 1Gで動いてます。この2日で突貫で作ったみたいな感じなのであまりいじめないでください。 機能 とてもシンプルなので、見てい</description>

<content:encoded><![CDATA[
<div class="section">
<p><a href="http://f.hatena.ne.jp/nitro_idiot/20130224000003" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20130224/20130224000003.png" alt="f:id:nitro_idiot:20130224000003p:image" title="f:id:nitro_idiot:20130224000003p:image" class="hatena-fotolife"></a><br><br><a href="http://beta.quicklisp.org" target="_blank">Quicklisp</a>に登録されているCommon Lispライブラリの情報をWebで見られるサイト「<b>Quickdocs.org</b>」を作りました。<br></p>
<ul>
<li> <a href="http://quickdocs.org/" target="_blank">http://quickdocs.org/</a></li>
<br></ul>
<p>さくらVPS 1Gで動いてます。この2日で突貫で作ったみたいな感じなのであまりいじめないでください。<br></p>
<h4> 機能</h4>
<p>とてもシンプルなので、見ていただいたほうがわかりやすいかもしれません。<br><br>たとえばClackのプロジェクトページは以下です。<br></p>
<ul>
<li> <a href="http://quickdocs.org/clack" target="_blank">http://quickdocs.org/clack</a></li>
<br></ul>
<p><a href="http://f.hatena.ne.jp/nitro_idiot/20130224000145" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20130224/20130224000145.png" alt="f:id:nitro_idiot:20130224000145p:image" title="f:id:nitro_idiot:20130224000145p:image" class="hatena-fotolife"></a><br><br>右上にはWebサイトやリポジトリへのリンクを置きました<span class="footnote"><a href="/nitro_idiot/#f1" name="fn1" title="現在、Webサイト情報の表示はGitHub、BitBucketでホストされているライブラリのみ対応しています">*1</a></span>。<br><br>本文にはREADMEを表示しています。最近はGitHubにホストされているライブラリが多いので見慣れていると思いますけどね。<br><br>下のほうには.asdに記述されている情報を表示しています。Author, Maintainer と License、および依存ライブラリです。<br><br>また、一番下にはAPIリファレンスへのリンクを置きました。<br></p>
<h4> APIリファレンス</h4>
<p>Quickdocsで特筆したいところとしてはAPIリファレンスの自動生成機能があることです。<br><br>APIリファレンスというのは、プロジェクトに含まれるパッケージやその変数・関数・マクロやクラスなどを一覧で表示するものです。<br><br>例としてAlexandriaを挙げます。<br></p>
<ul>
<li> <a href="http://quickdocs.org/alexandria/api" target="_blank">http://quickdocs.org/alexandria/api</a></li>
<br></ul>
<p><a href="http://f.hatena.ne.jp/nitro_idiot/20130224001547" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20130224/20130224001547.png" alt="f:id:nitro_idiot:20130224001547p:image" title="f:id:nitro_idiot:20130224001547p:image" class="hatena-fotolife"></a><br><br>シンボル名や関数の引数の表示、およびdocstringがあればそれも表示しています。今はexportされているシンボルのみ表示しています。<br><br>気づく方もおられるかもしれませんが、その表示順序にもこだわりがあります。<br><br>Common Lispのドキュメント生成ライブラリの気に入らないところは、どれもこれも、パッケージのシンボルを全部一緒くたにしてアルファベット順に並べていることです。確かに、リファレンスとしてはアルファベットのほうが引きやすいかもしれませんが、ざっくりライブラリの概要を見るには向きません。<br><br>QuickdocsのAPIリファレンスは、実際にソースコードをパースしているので、<b>ソースコードで定義されている順番にシンボルを表示</b>しています。非常にリーズナブルです。<br><br>欠点としては、表示がちょっと遅いことでしょうか。大きめのライブラリになると返ってくるのに5秒くらいかかることもあります。じきに改善します。<br><br>Quicklisp登録のライブラリのメンテナの方はぜひご自分のライブラリのページを確認してみてください。<br></p>
<h4> おわりに</h4>
<p>Pythonを学んでアプリケーションを作ってみる過程で、Common Lispにはまだまだ足りないものが非常に多いなぁという実感があります。<br><br>特に、Pythonのコミュニティはドキュメントを書くことを推奨しており、多くのライブラリを、実際にインストールする前にWeb上で概要を知ることができます。<br><br>Common Lispで何かをやるライブラリを探したいとき、とりあえずql:system-aproposしてみて、それっぽいライブラリがなければググってみて、出て来なかったらCLiki.netで検索してみて、なかったらGitHubで検索してみて、なかったらPerlで書くみたいなことを繰り返して来ました。今後はQuickdocsでの検索で完結できることを目指します。<br><br>たとえCommon Lispのライブラリが見つかったとしても、まともな情報がWebになく、とりあえずインストールしてみたけどどう使ったらいいかわからないからソース読む、みたいなことも多々ありました。今後はQuickdocsのAPIリファレンスが助けになりそうです。<br><br>Common Lisperの方はぜひ使ってみてメールなどでフィードバックをいただけるとありがたいです。</p>
</div>
<div class="footnote">
<p class="footnote"><a href="/nitro_idiot/#fn1" name="f1">*1</a>：現在、Webサイト情報の表示はGitHub、BitBucketでホストされているライブラリのみ対応しています</p>
</div>
]]></content:encoded>
<dc:creator>nitro_idiot</dc:creator>
<dc:date>2013-02-24T00:41:29+09:00</dc:date>
</item>
<item rdf:about="http://d.hatena.ne.jp/nitro_idiot/20130215/1360931962">
<title> EmacsでKindle風の読書環境を整える</title>
<link>http://d.hatena.ne.jp/nitro_idiot/20130215/1360931962</link>
<description> プログラムを書くのに使うのはもちろんEmacsなのですが、最近はプログラムだけでなく長文ドキュメントや本なんか読むことが多いです。そういうとき、どうしてもEmacsから離れてChromiumで開いたり、長文になるとKindleに転送したりしてしまう。だけど、Kindleはカスタマイズ</description>

<content:encoded><![CDATA[
<div class="section">
<p>プログラムを書くのに使うのはもちろんEmacsなのですが、最近はプログラムだけでなく長文ドキュメントや本なんか読むことが多いです。そういうとき、どうしてもEmacsから離れてChromiumで開いたり、長文になるとKindleに転送したりしてしまう。<br><br>だけど、Kindleはカスタマイズ性が低くて、ちょっとしたことをやりたいと思ってもできない。<br><br>たとえば、調べた英単語の履歴とか出せない。こういうハッカビリティの低さには憤りを感じます。<br><br>それなら全部Emacsでやってしまえばええやん、と思ってコツコツと改良し続け、そろそろノウハウが溜まってきたのでついでに紹介します。<br></p>
<h4> Kindly Mode</h4>
<p>Kindly Modeは、EmacsのバッファをKindle風の表示にしてくれるマイナーモードです。ほとんど見た目だけのことですが、以下の機能があります。<br></p>
<ul>
<li> 見た目がかっこよくなる
<ul>
<li> 具体的には、明朝体になってフォントサイズが大きくなって行間が少し空く</li>
</ul>
</li>
<li> viライクなキーバインドが有効</li>
<li> 自動でしおりを挟んでくれる</li>
<br></ul>
<p>GitHubにpublishしたので、試したい人はAuto Installとかでインストールすると良いと思います。<br></p>
<ul>
<li> <a href="https://github.com/fukamachi/kindly-mode" target="_blank">https://github.com/fukamachi/kindly-mode</a></li>
<br></ul>
<pre>
(auto-install-from-url &#34;https://raw.github.com/fukamachi/kindly-mode/master/kindly-mode.el&#34;)
</pre>
<br>
<p>その後、.emacsに以下のコードを書きます。<br></p>
<pre>
(require &#39;kindly-mode)
</pre>
<br>
<h4> スクリーンショット</h4>
<p>下のがChromium上のKindle Cloud Reader。<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20130215210712" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20130215/20130215210712.png" alt="f:id:nitro_idiot:20130215210712p:image" title="f:id:nitro_idiot:20130215210712p:image" class="hatena-fotolife"></a><br><br>そして下のがEmacs。<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20130215210713" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20130215/20130215210713.png" alt="f:id:nitro_idiot:20130215210713p:image" title="f:id:nitro_idiot:20130215210713p:image" class="hatena-fotolife"></a><br><br>左側がKindly Modeで、右側は普通のバッファです。フォントと行間を似せました。<br></p>
<h4> 自動しおり機能</h4>
<p>手間かかった割に意外とどうでもよかった機能ですが、Emacsの操作が止まったタイミングで自動でブックマーク(しおり)を打ってくれる機能がついています。<br><br>アイドルであれば常にしおりを打つのではなく、Kindleと同じで、既にあるブックマークより進んだ位置にカーソルがあったときだけしおりをつけてくれます。<br><br>つけたしおりはファイルに保存されるので、再びファイルを開いたときに「もっと先の位置にしおりをつけた場所があるけど、移動しはりますか？」と聞いてくれるので、y と答えると移動します。<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20130215213517" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20130215/20130215213517.png" alt="f:id:nitro_idiot:20130215213517p:image" title="f:id:nitro_idiot:20130215213517p:image" class="hatena-fotolife"></a><br></p>
<h4> おわりに</h4>
<p>Emacsで読めることの利点は、Lispやシェルとの連携ができることです。<br><br>たとえば、僕はEmacsからM-x dictで英単語の意味を調べられるようにしています。辞書を引いたときはそれをログに書き出すようにしているため、あとで見直すことも可能です。<br><br>また、本の内容をコピペしたり、ツイートしたりブログに引用するのも楽かもしれないですね。あんまり活用してないけど。<br><br>これ作ったときに、「M-x follow-modeすると二画面でいい感じになるのでは！」と思ったけど、行間をいじってるといい感じに表示してくれなくて残念だった。<br><br>久しぶりにEmacs Lispを書いて思ったのは、<b>やっぱLispめっちゃ書きやすいな〜</b>、ということでした。おわり。</p>
</div>
]]></content:encoded>
<dc:creator>nitro_idiot</dc:creator>
<dc:date>2013-02-15T21:39:22+09:00</dc:date>
</item>
<item rdf:about="http://d.hatena.ne.jp/nitro_idiot/20130208/1360335474">
<title> 巳年なのでPythonでLESSの自動コンパイルしてみた</title>
<link>http://d.hatena.ne.jp/nitro_idiot/20130208/1360335474</link>
<description> これは普通の日記です。今年は巳年なのでPythonを始めました。 全てのwebエンジニアがPythonを勉強するべき2013年到来 別にこの記事に影響されるほど繊細でもないのですが、Perlは仕事でいくらでも書けるし、Rubyはあんまり好きじゃないので、Pythonやってみるかーという軽</description>

<content:encoded><![CDATA[
<div class="section">
<p>これは普通の日記です。<br><br>今年は巳年なのでPythonを始めました。<br></p>
<ul>
<li> <a href="http://anond.hatelabo.jp/20130101082333" target="_blank">全てのwebエンジニアがPythonを勉強するべき2013年到来</a></li>
<br></ul>
<p>別にこの記事に影響されるほど繊細でもないのですが、Perlは仕事でいくらでも書けるし、Rubyはあんまり好きじゃないので、Pythonやってみるかーという軽い気持ちです。<br><br>ただ、Pythonの本はなかなか良いのが見つからなくて、最初は初めてのPythonを読もうと思ったのだけど、読み終えるのに一年くらいかかりそうだったからやめました。<br><br>Pythonクックブックにも一度手をつけたけど結局読まず、最終的に読み進めたのは<a href="http://www.diveintopython.net/" target="_blank">Dive Into Python</a>だけでした。無料で読めるし、とりあえず完成したPythonコードを突き出して (Diveして)、順に説明するという方式で、他の入門書より速く学べる。実際、文法を多少知らなくてもDiveしたPythonコードは雰囲気で何をしたいのかわかるし、読みやすさを売りにしたPythonらしい本だと思います。<br><br>既にプログラム経験がある人には<a href="http://www.diveintopython.net/" target="_blank">Dive Into Python</a>おすすめです。<br><br><a href="http://www.amazon.co.jp/gp/product/B001GIOFN8/ref=as_li_ss_il?ie=UTF8&camp=247&creative=7399&creativeASIN=B001GIOFN8&linkCode=as2&tag=happatsu-22"><img src="http://ws.assoc-amazon.jp/widgets/q?_encoding=UTF8&ASIN=B001GIOFN8&Format=_SL110_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=happatsu-22" border="0"></a><img width="1" alt="" src="http://www.assoc-amazon.jp/e/ir?t=happatsu-22&l=as2&o=9&a=B001GIOFN8" style="border:none !important; margin:0px !important;" height="1" border="0"><br><br>もしくはこっちのほうがいいかな？？？<br></p>
<ul>
<li> <a href="http://www.unixuser.org/~euske/doc/python/python-lisp-j.html" target="_blank">Lisp プログラマのための Python 人門</a></li>
<br></ul>
<h4> LESSの自動コンパイル</h4>
<p>最初はFlaskでWebアプリケーションを書いていたのですが、LESSを使いたいと思って、lesscについて調べてたら、LESSファイルの変更を監視して自動でCSSにコンパイルするスクリプトを書いていました。<br><br>作ったのは下のスクリプトです。実行にNode.jsとlesscが要ります。<br></p>
<pre class="syntax-highlight">
<span class="synPreProc">import</span> os
<span class="synPreProc">import</span> re
<span class="synPreProc">import</span> shutil
<span class="synPreProc">import</span> time
<span class="synPreProc">import</span> sys

<span class="synPreProc">from</span> watchdog.observers <span class="synPreProc">import</span> Observer
<span class="synPreProc">from</span> watchdog.tricks <span class="synPreProc">import</span> Trick


<span class="synStatement">class</span> <span class="synIdentifier">FileCompileHandler</span>(Trick):

    <span class="synStatement">def</span> <span class="synIdentifier">output_path</span>(self, path):
        <span class="synStatement">return</span> re.sub(r&#34;<span class="synConstant">([^\.]+)(\..+?)?$</span>&#34;, r&#34;<span class="synConstant">\1-compiled\2</span>&#34;, path)

    <span class="synStatement">def</span> <span class="synIdentifier">compile_file</span>(self, file):
        <span class="synStatement">pass</span>

    <span class="synStatement">def</span> <span class="synIdentifier">compile_path</span>(self, path):
        <span class="synStatement">if</span> os.path.isdir(path):
            <span class="synStatement">for</span> file <span class="synStatement">in</span> os.listdir(path):
                self.compile_path(file)
        <span class="synStatement">else</span>:
            self.compile_file(path)

    <span class="synStatement">def</span> <span class="synIdentifier">on_created</span>(self, event):
        <span class="synStatement">if</span> <span class="synStatement">not</span> event.is_directory:
            self.compile_path(event.src_path)
        <span class="synStatement">else</span>:
            os.mkdir(self.output_path(event.src_path))

    <span class="synStatement">def</span> <span class="synIdentifier">on_modified</span>(self, event):
        <span class="synStatement">if</span> <span class="synStatement">not</span> event.is_directory:
            self.compile_path(event.src_path)

    <span class="synStatement">def</span> <span class="synIdentifier">on_moved</span>(self, event):
        self.on_deleted(event)
        self.compile_path(event.dest_path)

    <span class="synStatement">def</span> <span class="synIdentifier">on_deleted</span>(self, event):
        to_delete_path = self.output_path(event.src_path)
        <span class="synStatement">if</span> event.is_directory:
            shutil.rmtree(to_delete_path)
        <span class="synStatement">else</span>:
            os.remove(to_delete_path)
        <span class="synStatement">print</span> &#34;<span class="synConstant">Deleted: </span>&#34; + to_delete_path


<span class="synStatement">class</span> <span class="synIdentifier">LESSCompileHandler</span>(FileCompileHandler):

    <span class="synStatement">def</span> <span class="synIdentifier">__init__</span>(self,
                 patterns=['<span class="synConstant">*.less</span>'],
                 source_directory='<span class="synConstant">.</span>',
                 destination_directory=None,
                 lessc='<span class="synConstant">lessc</span>'):
        super(LESSCompileHandler, self).__init__(patterns=patterns)

        self.source_directory = os.path.abspath(source_directory)
        <span class="synStatement">if</span> destination_directory <span class="synStatement">is</span> <span class="synStatement">not</span> None:
            self.destination_directory = os.path.abspath(destination_directory)
        <span class="synStatement">else</span>:
            self.destination_directory = self.source_directory

        self.lessc = lessc

    <span class="synStatement">def</span> <span class="synIdentifier">output_path</span>(self, path):
        <span class="synStatement">return</span> re.sub(
            r&#34;<span class="synConstant">\.less$</span>&#34;, '<span class="synConstant">.css</span>',
            path.replace(self.source_directory, self.destination_directory))

    <span class="synStatement">def</span> <span class="synIdentifier">compile_file</span>(self, file):
        output_file = self.output_path(file)
        os.system('<span class="synConstant"> </span>'.join([self.lessc, file, output_file]))
        <span class="synStatement">print</span> '<span class="synConstant">[LESS] Compiled: %s -&#62; %s</span>' % (file, output_file)


<span class="synStatement">def</span> <span class="synIdentifier">watch_and_compile</span>(source_directory, destination_directory=None):
    event_handler = LESSCompileHandler(
        source_directory=source_directory,
        destination_directory=destination_directory)

    observer = Observer()
    observer.schedule(
        event_handler,
        path=event_handler.source_directory,
        recursive=True)
    observer.start()
    <span class="synStatement">try</span>:
        <span class="synStatement">while</span> True:
            time.sleep(1)
    <span class="synStatement">except</span> KeyboardInterrupt:
        observer.stop()
    observer.join()


<span class="synStatement">if</span> __name__ == &#34;<span class="synConstant">__main__</span>&#34;:
    watch_and_compile(*sys.argv[1:])
</pre>
<br>
<p>importしてもいいし、そのままスクリプトとして実行できます。以下のように実行すると、「static/less」以下にある".less"のファイルを監視し、コンパイル結果を「static/css」の同じ階層に出力します。<br></p>
<pre>
$ python less_compile.py static/less static/css
</pre>
<br>
<p>watchdog.tricks.Trickを継承しているので、tricks.yamlで設定を管理することもできます<br></p>
<pre class="syntax-highlight">
<span class="synComment"># tricks.yaml</span>
<span class="synIdentifier">tricks</span><span class="synSpecial">:</span>
  <span class="synStatement">-</span> less_compile.<span class="synIdentifier">LESSCompileHandler</span><span class="synSpecial">:</span>
      <span class="synIdentifier">source_directory</span><span class="synSpecial">:</span> static/less/
      <span class="synIdentifier">destination_directory</span><span class="synSpecial">:</span> static/css/
</pre>
<br>
<p>実行はwatchmedoコマンドです。<br></p>
<pre>
$ watchmedo tricks tricks.yaml
</pre>
<br>
<p>結構試行錯誤したので、2日くらいかかりました。<br></p>
<h4> Python雑感</h4>
<p>最後に、この10日くらいPythonを学んでみての雑感です。<br></p>
<h5> 読みやすい</h5>
<p>自分が書いたコードにしても、他人のコードにしても、PerlやCommon Lispより圧倒的に読みやすいです。これは言語の優劣の問題ではなく、たぶんPythonがシンタックス上の制限が多いためでしょう。書くのには自然と気を遣わせられて、きれいなコードを強制的に書かされているような感じです。大規模なプロジェクトだと活きそうですね。<br><br>前から<a href="http://clacklisp.org" target="_blank">Clack</a>や<a href="http://fukamachi.github.com/caveman/" target="_blank">Caveman</a>のコードを読んだ外人に「Pythonっぽすぎるだろ」と渋い顔で言われることがままあって気になっていたのですが、実際に学んでみて理由がわかった気がします。なんでしょうか。とりあえず褒め言葉として受け取っておきます。<br></p>
<h5> ちょっとしたことでもクラス定義しないといけないのが不便</h5>
<p>僕はPerlやCommon Lispでプログラムを書くときは、まずプロトタイプを作る目的でsubやlambdaを書いてとりあえず動かしてみる、というところから始めます。<br><br>今回のLESSの監視自動コンパイラを書こうと思ったときもそうしようと思ったのですが、Pythonではlambdaに複数の式を書くことはできないし(<a href="http://practical-scheme.net/trans/icadmore-j.html" target="_blank">確かにひどい</a>)、簡単な名前付き関数を書いていても煩雑になるばかりで、もういきなりクラス定義から始める有様でした。書きながら、いきなり泥沼に足を突っ込んだような心持ちでした。<br></p>
<h5> メソッドコンビネーションがないのが不便</h5>
<p>ファイルをコンパイルしたときのログなんかは、まとめて親クラスで定義してしまいたかったのですが、Pythonには<a href="http://ja.wikipedia.org/wiki/CLOS" target="_blank">CLOS</a>のような高級なオブジェクトシステムがないのでbeforeとかafterメソッドを定義することができなさそうです。<br><br>何か専用のモジュールがあるんでしょうか？ ちょっと不便です。<br></p>
<h4> おわり</h4>
<p>以上、日記でした。Pythonistaの方々、よろしくお願いします。</p>
</div>
]]></content:encoded>
<dc:creator>nitro_idiot</dc:creator>
<dc:date>2013-02-08T23:57:54+09:00</dc:date>
</item>
<item rdf:about="http://d.hatena.ne.jp/nitro_idiot/20121210/1355118962">
<title>20分ではてなブログを作る方法</title>
<link>http://d.hatena.ne.jp/nitro_idiot/20121210/1355118962</link>
<description> これはLisp Advent Calendarの10日目です。先日、HappyElementsさんで行われたLiveCoding #11にライブコーダとして参加させていただきました。そのときの話をしようと思います。 LiveCodingとは 一人の”LiveCoder”が、20分の制限時間で何かソフトウェアを作り、デモをします</description>

<content:encoded><![CDATA[
<div class="section">
<p><a href="http://f.hatena.ne.jp/nitro_idiot/20121210150159" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210150159.png" alt="f:id:nitro_idiot:20121210150159p:image" title="f:id:nitro_idiot:20121210150159p:image" class="hatena-fotolife"></a><br><br>これは<a href="http://qiita.com/advent-calendar/2012/lisp" target="_blank">Lisp Advent Calendar</a>の10日目です。<br><br>先日、<a href="http://www.happyelements.com/" target="_blank">HappyElements</a>さんで行われた<a href="http://ujihisa.github.com/livecoding11/" target="_blank">LiveCoding #11</a>にライブコーダとして参加させていただきました。そのときの話をしようと思います。<br></p>
<h4> LiveCodingとは</h4><br>
<blockquote>
<p>一人の"LiveCoder"が、20分の制限時間で何かソフトウェアを作り、デモをします。 LiveCoderがどのようにコーディングしているかの全てがプロジェクターで大画面に映し出され、 さらに解説役が常にLiveCoderの挙動を説明します。<br><br>A 「コンパイル中です・・・、お、おおお、なんと、Syntax error!」<br><br>B 「これは恥ずかしい…!」<br><br>LiveCoder 「(/// 」<br><br>LiveCoderが何を作るかについては、自分で決めた上でそれをLiveCoding前に告知します。 ただし、観客の要望で仕様が強制的に変更になることがあります。 もっとも、客観的に20分以内に実装できないことが明らかな場合、仕様変更要望は無視できます。<br><br>一人のLiveCoderに対して二人の解説役が付くことがうまくいくという経験則があります。<br>LiveCoderは過去に作ったものと全く同じものをLiveCodingで作ることが禁止されています。 もはやそれはLiveCodingではなく、たんなる動画の再生と見なされます。</p>
</blockquote><br>
<p>参加のきっかけはILCでお会いしたyharaさんに勧められたことだ。<br></p>
<blockquote>
<p>「Common Lispでいいですか」<br>「はい、むしろCommon Lispでお願いします。」</p>
</blockquote><br>
<p>Common Lispでいいならなんとかなるだろう。<br></p>
<h4> 作るもの</h4><br>
<p>今回、Common Lispで何かを作るということだけはあらかじめ決まっていたものの、何を作るかは未定だった。<br><br>何か風変わりなものを作ろうとも考えたが、Lispに詳しい方なんてほとんど来ないので、むしろ普通のものを作ってみては、という助言をいただいたので、普通にWebサービスを作ることにした。<br><br>Webサービスなら自分のホームグラウンドでもある。20分で現実的に作れるもので、誰もが知っているようなサービス──<b>ブログ</b>。20分でブログを作ることに決めた。<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20121210121247" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210121247.png" alt="f:id:nitro_idiot:20121210121247p:image" title="f:id:nitro_idiot:20121210121247p:image" class="hatena-fotolife"></a><br></p>
<h4> 方針</h4><br>
<p>持ち時間は20分と非常に短い。Common Lispがいかに高速な開発を可能にするとしても、20分で完璧なものを作るのは無理だ。いくらか手を抜かなければならない。<br><br>とりあえず動くものを作るのが最優先だろう。パフォーマンスは気にしない。セキュリティも、まあいいだろう。CSSなんて書いている暇はない。とにかく短いコード、短時間で動くものを作る、そのために必要なことだけに注力することにした。<br></p>
<h4> 1. プロジェクト作成</h4><br>
<p>まずはプロジェクトを作る。Common Lispだとプロジェクトを作らずにいきなりREPLで始めることもできるが、途中でEmacsがクラッシュしない保障はない。プロジェクトはファイルに書き出しておくのが賢明だ。<br></p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>ql:quickload :cl-project<span class="synSpecial">)</span>
<span class="synSpecial">(</span>cl-project:make-project #P<span class="synConstant">&#34;~/Programs/web/hatenablog/&#34;</span> :description <span class="synConstant">&#34;Write the story of your life.&#34;</span><span class="synSpecial">)</span>
</pre>
<br>
<ul>
<li> <a href="https://github.com/fukamachi/cl-project" target="_blank">CL-Project</a></li>
<br></ul>
<h4> 2. サーバ起動</h4><br>
<p>さて、ブログに最も必要な機能はなんだろう？<br><br>ブログなのでWebブラウザ上で見られないと使ってもらえない。まずはサーバを起動してみよう。<br><br>今回はインタラクティブで高速な開発が必要なため、Webフレームワークとして<a href="http://fukamachi.github.com/ningle/" target="_blank">ningle</a>を使うことにした。ningleより今回の目的に合ったフレームワークはないだろう。<br></p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>ql:quickload :ningle<span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">defvar</span> <span class="synType">*app*</span> <span class="synSpecial">(</span><span class="synStatement">make-instance</span> <span class="synSpecial">'</span><span class="synIdentifier">ningle:&#60;app&#62;</span><span class="synSpecial">))</span>
<span class="synSpecial">(</span>clack:clackup <span class="synType">*app*</span><span class="synSpecial">)</span>
<span class="synComment">;-&#62; Hunchentoot server is started.</span>
<span class="synComment">;   Listening on localhost:5000.</span>
<span class="synComment">;=&#62; #&#60;CLACK.HANDLER:&#60;HANDLER&#62; #x3020061A081D&#62;</span>
</pre>
<br>
<p>これでHunchentootをバックエンドとしてブラウザ上で見られるようになった。&#8984;-Tabを押してChromiumに移動して <a href="http://localhost:5000" target="_blank">http://localhost:5000</a> に移動する。<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20121210111622" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210111622.png" alt="f:id:nitro_idiot:20121210111622p:image" title="f:id:nitro_idiot:20121210111622p:image" class="hatena-fotolife"></a><br><br>う、何も表示されてない……。<br><br>あっ。<br><br>思い当たることあって&#8984;-&#8997;-Iを押して開発ツールを開く。<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20121210111623" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210111623.png" alt="f:id:nitro_idiot:20121210111623p:image" title="f:id:nitro_idiot:20121210111623p:image" class="hatena-fotolife"></a><br><br>404だった。何もルーティングルール書いてないんだからそりゃそうだ。とりあえず動いてはいるようなので安心した。<br></p>
<h4> 3. ルーティング設定</h4><br>
<p>トップページが404のWebサービスというのは斬新だが、早々に解決しなければならない。<br><br>ルーティングルールを書いて小粋な挨拶をさせてみよう。<br></p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span><span class="synStatement">setf</span> <span class="synSpecial">(</span>ningle:route <span class="synType">*app*</span> <span class="synConstant">&#34;/&#34;</span><span class="synSpecial">)</span>
      #<span class="synSpecial">'(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>params<span class="synSpecial">)</span>
          <span class="synConstant">&#34;Hello, I'm Hatena Blog.&#34;</span><span class="synSpecial">))</span>
</pre>
<br>
<p>ブラウザに戻ってリロード。<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20121210111934" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210111934.png" alt="f:id:nitro_idiot:20121210111934p:image" title="f:id:nitro_idiot:20121210111934p:image" class="hatena-fotolife"></a><br><br>順調。<br></p>
<h4> 4. データ定義</h4><br>
<p>このブログサービスはまだ生まれたばかりなので、ブログを書くことができない。これでは誰にも使ってもらえない。次はエントリを書けるようにしてみよう。<br><br><a href="http://ja.wikipedia.org/wiki/Common_Lisp_Object_System" target="_blank">Common Lispは完全なオブジェクト指向言語</a>なので、まずはクラス定義をする。<br></p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span><span class="synStatement">defclass</span> &#60;entry&#62; <span class="synSpecial">()</span>
    <span class="synSpecial">((</span>title <span class="synType">:type</span> <span class="synSpecial">'</span><span class="synIdentifier">string</span> :initarg :title :initform <span class="synConstant">&#34;&#34;</span><span class="synSpecial">)</span>
     <span class="synSpecial">(</span>body <span class="synType">:type</span> <span class="synSpecial">'</span><span class="synIdentifier">string</span> :initarg :body :initform <span class="synConstant">&#34;&#34;</span><span class="synSpecial">)))</span>
</pre>
<br>
<p>とりあえずタイトルと本文があれば十分だろう。エントリを作るための関数も用意しておく。<br></p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span><span class="synStatement">defvar</span> <span class="synType">*entries*</span> <span class="synStatement">nil</span><span class="synSpecial">)</span>
<span class="synSpecial">(</span><span class="synStatement">defun</span> create-entry <span class="synSpecial">(</span>&#38;key title body<span class="synSpecial">)</span>
  <span class="synSpecial">(</span><span class="synStatement">pushnew</span> <span class="synSpecial">(</span><span class="synStatement">make-instance</span> <span class="synSpecial">'</span><span class="synIdentifier">&#60;entry&#62;</span>
              :title title
              :body body<span class="synSpecial">)</span>
           <span class="synType">*entries*</span><span class="synSpecial">))</span>
</pre>
<br>
<p>とにかく僕には時間がない。既に10分が経過しようとしている。残り10分ほどで動くものにしなければならない。DBスキーマを作ってエントリをDBに入れている暇なんてない。すべてオンメモリだ！ 生まれて10分と少しのWebサービスということで甘んじることにする。<br></p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>create-entry :title <span class="synConstant">&#34;Hi&#34;</span> :body <span class="synConstant">&#34;This is my first blog entry. Hehe.&#34;</span><span class="synSpecial">)</span>
<span class="synComment">;=&#62; (#&#60;&#60;ENTRY&#62; #x3020061CC9CD&#62;)</span>
</pre>
<br>
<p>これでエントリを作れた。よし。<br></p>
<h4> 5. ブラウザに表示</h4><br>
<p>次はエントリをブラウザに表示させよう。<br><br>新しく /entries というページを作って、そこで時系列でエントリ一覧を見られるようにする。<br><br>テンプレートエンジンには<a href="http://www.common-lisp.net/project/cl-emb/" target="_blank">CL-EMB</a>を使うことにした。これはHTMLの中に独自構文で変数を展開したりループできたりするよくあるテンプレートエンジンだ。複雑なHTMLを吐くのは面倒だが、ただ表示するだけなら十分だろう。<br><br>templatesディレクトリ以下に entries.tmpl というファイルを作っておく。<br></p>
<pre class="syntax-highlight">
<span class="synIdentifier">&#60;</span><span class="synStatement">html</span><span class="synIdentifier"> xmlns=</span><span class="synConstant">&#34;http://www.w3.org/1999/xhtml&#34;</span><span class="synIdentifier"> </span><span class="synType">lang</span><span class="synIdentifier">=</span><span class="synConstant">&#34;ja&#34;</span><span class="synIdentifier"> xml:</span><span class="synType">lang</span><span class="synIdentifier">=</span><span class="synConstant">&#34;ja&#34;</span><span class="synIdentifier">&#62;</span>
<span class="synIdentifier">&#60;</span><span class="synStatement">head</span><span class="synIdentifier">&#62;</span>
<span class="synPreProc">  </span><span class="synIdentifier">&#60;</span><span class="synStatement">meta</span><span class="synIdentifier"> </span><span class="synType">http-equiv</span><span class="synIdentifier">=</span><span class="synConstant">&#34;Content-Type&#34;</span><span class="synIdentifier"> </span><span class="synType">content</span><span class="synIdentifier">=</span><span class="synConstant">&#34;text/html; charset=UTF-8&#34;</span><span class="synIdentifier"> /&#62;</span>
<span class="synPreProc">  </span><span class="synIdentifier">&#60;</span><span class="synStatement">title</span><span class="synIdentifier">&#62;</span>All your blog entries<span class="synIdentifier">&#60;/</span><span class="synStatement">title</span><span class="synIdentifier">&#62;</span>
<span class="synIdentifier">&#60;/</span><span class="synStatement">head</span><span class="synIdentifier">&#62;</span>
<span class="synIdentifier">&#60;</span><span class="synStatement">body</span><span class="synIdentifier">&#62;</span>
<span class="synIdentifier">&#60;% @loop entries %&#62;</span>
  <span class="synIdentifier">&#60;</span><span class="synStatement">h3</span><span class="synIdentifier">&#62;&#60;% @var title %&#62;&#60;/</span><span class="synStatement">h3</span><span class="synIdentifier">&#62;</span>
  <span class="synIdentifier">&#60;</span><span class="synStatement">p</span><span class="synIdentifier">&#62;&#60;% @var body %&#62;&#60;/</span><span class="synStatement">p</span><span class="synIdentifier">&#62;</span>
<span class="synIdentifier">&#60;% @endloop %&#62;</span>
<span class="synIdentifier">&#60;/</span><span class="synStatement">body</span><span class="synIdentifier">&#62;</span>
<span class="synIdentifier">&#60;/</span><span class="synStatement">html</span><span class="synIdentifier">&#62;</span>
</pre>
<br>
<p>あとは entries という名前でエントリのリストを渡してあげればいい。<br></p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>ql:quickload :cl-emb<span class="synSpecial">)</span>

<span class="synSpecial">(</span><span class="synStatement">setf</span> <span class="synSpecial">(</span>ningle:route <span class="synType">*app*</span> <span class="synConstant">&#34;/entries&#34;</span><span class="synSpecial">)</span>
      #<span class="synSpecial">'(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>params<span class="synSpecial">)</span>
          <span class="synSpecial">(</span>emb:execute-emb
           #P<span class="synConstant">&#34;entries.html&#34;</span>
           :env
           <span class="synSpecial">(</span><span class="synStatement">list</span> :entries
            <span class="synSpecial">(</span><span class="synStatement">loop</span> for entry in *entries*
                  collect
                  `<span class="synSpecial">(</span>:title ,<span class="synSpecial">(</span><span class="synStatement">slot-value</span> entry 'title<span class="synSpecial">)</span>
                    :body ,<span class="synSpecial">(</span><span class="synStatement">slot-value</span> entry 'body<span class="synSpecial">)))))))</span>
</pre>
<br>
<p>少し冗長。とはいえこれでエントリ一覧が見れるはずだ。<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20121210114009" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210114009.png" alt="f:id:nitro_idiot:20121210114009p:image" title="f:id:nitro_idiot:20121210114009p:image" class="hatena-fotolife"></a><br><br>できてる。1個だとわかりづらいからもう1個くらいエントリを作っておこう。&#8984;-TabでEmacsに戻り、REPLでcreate-entryを打ち込む。<br></p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>create-entry :title <span class="synConstant">&#34;Yippee!&#34;</span> :body <span class="synConstant">&#34;Hatena Blog is already working!&#34;</span><span class="synSpecial">)</span>
</pre>
<br>
<p>Chromiumに戻ってリロード。<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20121210114247" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210114247.png" alt="f:id:nitro_idiot:20121210114247p:image" title="f:id:nitro_idiot:20121210114247p:image" class="hatena-fotolife"></a><br><br>都合がいいことにpushnewすると新しいものが上に来るようになっている。素晴らしい。<br></p>
<h4> 6. エントリを書くフォーム</h4><br>
<p>これでREPLからエントリを書けるというLisperフレンドリなブログシステムが完成した。思いついたときに即座にREPLを立ちあげ、create-entry。クール。<br><br>しかし、ユーザ様の中にはCommon Lispをインストールしていない方もおられるかもしれない。ブラウザ上でもエントリを書けるに越したことはないだろう。<br><br>この時点で残りは5分もない。行けるか……。完成が危ういが、このまま5分間観客と談話して過ごすつもりもない。仕方ない。やってみよう。<br><br>/post というページを新しく作って投稿フォームを表示することにする。<br><br>今回もCL-EMBでやろうと思ったが、悠長にしてられない。<a href="http://github.com/arielnetworks/cl-markup" target="_blank">CL-Markup</a>で少し楽をする。<br><br>試しにREPLでmarkupマクロを呼び出して、ちゃんと求めるHTMLが返ってくるか確かめてみる。<br></p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span>ql:quickload :cl-markup<span class="synSpecial">)</span>

<span class="synSpecial">(</span>markup:markup
 <span class="synSpecial">(</span>:form :method <span class="synConstant">&#34;POST&#34;</span>
  <span class="synSpecial">(</span><span class="synType">:input</span> <span class="synType">:type</span> <span class="synConstant">&#34;text&#34;</span> <span class="synType">:name</span> <span class="synConstant">&#34;title&#34;</span><span class="synSpecial">)(</span>:br<span class="synSpecial">)</span>
  <span class="synSpecial">(</span>:textarea <span class="synType">:name</span> <span class="synConstant">&#34;body&#34;</span> <span class="synStatement">nil</span><span class="synSpecial">)))</span>
<span class="synComment">;=&#62; &#34;&#60;form method=\&#34;POST\&#34;&#62;&#60;input type=\&#34;text\&#34; name=\&#34;title\&#34; /&#62;&#60;br /&#62;&#60;textarea name=\&#34;body\&#34;&#62;&#60;/textarea&#62;&#60;/form&#62;&#34;</span>
</pre>
<br>
<p>良さそうだ。タイトルと本文を入力するだけのシンプルなHTML。<br><br>これをルーティングルールに組み込んでやれば良い。HTML全体を出力するにはmarkupマクロをxhtmlマクロに変更すればいい。<br></p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span><span class="synStatement">setf</span> <span class="synSpecial">(</span>ningle:route <span class="synType">*app*</span> <span class="synConstant">&#34;/post&#34;</span><span class="synSpecial">)</span>
      #<span class="synSpecial">'(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>params<span class="synSpecial">)</span>
          <span class="synSpecial">(</span>markup:xhtml
           <span class="synSpecial">(</span>:form :method <span class="synConstant">&#34;POST&#34;</span>
            <span class="synSpecial">(</span>:input :type <span class="synConstant">&#34;text&#34;</span> :name <span class="synConstant">&#34;title&#34;</span><span class="synSpecial">)(</span>:br<span class="synSpecial">)</span>
            <span class="synSpecial">(</span>:textarea :name <span class="synConstant">&#34;body&#34;</span> <span class="synStatement">nil</span><span class="synSpecial">)))))</span>
</pre>
<br>
<p>あまりに簡単過ぎて恐ろしくなってくるが、Chromiumに戻ってリロード<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20121210115538" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210115538.png" alt="f:id:nitro_idiot:20121210115538p:image" title="f:id:nitro_idiot:20121210115538p:image" class="hatena-fotolife"></a><br><br>しまった。submitボタンを忘れてた。これでは投稿できない。<br><br>急いでinputタグを追加して再評価する。<br></p>
<pre class="syntax-highlight">
<span class="synComment">;; 再評価</span>
<span class="synSpecial">(</span><span class="synStatement">setf</span> <span class="synSpecial">(</span>ningle:route <span class="synType">*app*</span> <span class="synConstant">&#34;/post&#34;</span><span class="synSpecial">)</span>
      #<span class="synSpecial">'(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>params<span class="synSpecial">)</span>
          <span class="synSpecial">(</span>markup:xhtml
           <span class="synSpecial">(</span>:form :method <span class="synConstant">&#34;POST&#34;</span>
            <span class="synSpecial">(</span>:input :type <span class="synConstant">&#34;text&#34;</span> :name <span class="synConstant">&#34;title&#34;</span><span class="synSpecial">)(</span>:br<span class="synSpecial">)</span>
            <span class="synSpecial">(</span>:textarea :name <span class="synConstant">&#34;body&#34;</span> <span class="synStatement">nil</span><span class="synSpecial">)(</span>:br<span class="synSpecial">)</span>
            <span class="synSpecial">(</span>:input :type <span class="synConstant">&#34;submit&#34;</span> :value <span class="synConstant">&#34;投稿！&#34;</span><span class="synSpecial">)))))</span>
</pre>
<br>
<p><a href="http://f.hatena.ne.jp/nitro_idiot/20121210115755" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210115755.png" alt="f:id:nitro_idiot:20121210115755p:image" title="f:id:nitro_idiot:20121210115755p:image" class="hatena-fotolife"></a><br><br>投稿フォームが完成。でもまだPOST先を作れてない。投稿ボタンを押すと404。残り時間はあと2分。急いだほうがいい。<br><br>POSTのときに反応するルーティングは :method :post を追加するだけでいい。<br></p>
<pre class="syntax-highlight">
<span class="synSpecial">(</span><span class="synStatement">setf</span> <span class="synSpecial">(</span>ningle:route <span class="synType">*app*</span> <span class="synConstant">&#34;/post&#34;</span> :method :post<span class="synSpecial">)</span>
      <span class="synSpecial">(</span><span class="synStatement">lambda</span> <span class="synSpecial">(</span>params<span class="synSpecial">)</span>
        <span class="synSpecial">(</span><span class="synStatement">let</span> <span class="synSpecial">((</span>title <span class="synSpecial">(</span><span class="synStatement">getf</span> params :|title|<span class="synSpecial">))</span>
              <span class="synSpecial">(</span>body <span class="synSpecial">(</span><span class="synStatement">getf</span> params :|body|<span class="synSpecial">)))</span>
          <span class="synSpecial">(</span>create-entry :title title :body body<span class="synSpecial">)</span>
          <span class="synConstant">&#34;投稿成功！&#34;</span><span class="synSpecial">)))</span>
</pre>
<br>
<p>POSTされたら単純にcreate-entryする。これでいけるはず。アドレナリンが噴出する。30秒のコールはすでに過ぎていたが、まだ時間はあるか。<br><br>ブラウザから投稿を試す。<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20121210120250" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210120250.png" alt="f:id:nitro_idiot:20121210120250p:image" title="f:id:nitro_idiot:20121210120250p:image" class="hatena-fotolife"></a><br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20121210120325" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210120325.png" alt="f:id:nitro_idiot:20121210120325p:image" title="f:id:nitro_idiot:20121210120325p:image" class="hatena-fotolife"></a><br><br>きた！ 果たしてちゃんとエントリが作られたか。<br><br>&#8984;-Lを押してロケーションバーに素早くカーソルを合わせ、/entries を開く。<br><br><a href="http://f.hatena.ne.jp/nitro_idiot/20121210120435" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210120435.png" alt="f:id:nitro_idiot:20121210120435p:image" title="f:id:nitro_idiot:20121210120435p:image" class="hatena-fotolife"></a><br><br>一番上に表示された！ 残り時間は ―― <b>4秒</b>。Common Lispの勝利である。<br></p>
<h4> おわりに</h4><br>
<p>ご覧のように、Common Lispは高速なプロトタイピングが可能な言語です。REPLで一発でサーバを起動し、インタラクティブにルーティングルールを定義、再定義、確認のサイクルが回せるのが強みです。<br><br>他の言語でもREPLと呼ばれているようなものはあるかもしれませんが、そのプロセスを環境として試しながら組み立てるみたいな使い方はされていないようですし、ライブラリ側もそういった使い方を考慮して設計されていません。インタラクティブ性はLispが持つ強みの1つです。<br><br>もちろん、今回のプロトタイピングには出て来なかった、解決すべき問題も多くあります。<br><br>たとえばDBアクセス。今回はすべてオンメモリで済ませることにしましたが、実運用までにDBに入れなければいけません。ただ、その場合も<a href="http://marijnhaverbeke.nl/postmodern/" target="_blank">Postmodern</a>や<a href="http://www.common-lisp.net/project/elephant/" target="_blank">Elephant</a>のようなObjectStoreを使えばクラス定義をそのまま使え、プロトタイピングからあまりコードを書き換えずに永続化できるはずです。<br><br>高速なプロトタイピング、そして最速なリリースを目指すならCommon Lispが最良の選択です。<br><br>ちなみにはてなブログは以下のリンクから開設することができます。どうぞご利用ください。<br></p>
<ul>
<li> <a href="http://hatenablog.com" target="_blank">http://hatenablog.com</a></li>
<br></ul>
<p><a href="http://f.hatena.ne.jp/nitro_idiot/20121210120830" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nitro_idiot/20121210/20121210120830.png" alt="f:id:nitro_idiot:20121210120830p:image" title="f:id:nitro_idiot:20121210120830p:image" class="hatena-fotolife"></a><br><br><small>※これは実話を元にしたフィクションです。はてなブログはCommon Lisp以外の言語で動いています。</small></p>
</div>
]]></content:encoded>
<dc:creator>nitro_idiot</dc:creator>
<dc:date>2012-12-10T14:56:02+09:00</dc:date>
</item>
</rdf:RDF>
