git submodule を使わないで vim プラグインを管理する

dot-files を git で管理してるとついつい vim プラグインも submodule 使って管理してしまいたくなるんですが、 submodule を使わないほうが楽なんじゃないかなと思って dot-files の整理と一緒にこんなシェルスクリプトを書いてみた。

整理した dot-files はこちら。 GitHub - punytan/dot-files: dot-files

使い方など

  • .gitignore に vim/buldle/* を追加して、bundle以下は管理しないようにする
  • 追加したいプラグインリポジトリを install 部分に追加する
  • bundleディレクトリは各自の環境に合わせて書き換えて
  • プラグインのアップデートは upgrade を引数に実行するとまとめて git pull してくれる
  • pathogen.vim 使ってください(って書けって言われた。常識ですけど!)
build-env/05_vimbundle.sh
#!/usr/bin/env bash

if [ ! -d "$HOME/dot-files/vim/bundle" ]; then
    mkdir -p "$HOME/dot-files/vim/bundle";
fi

cd "$HOME/dot-files/vim/bundle";

case $1 in
    'install')
        git clone git://github.com/thinca/vim-guicolorscheme.git;
        git clone git://github.com/Shougo/neocomplcache.git;
        git clone git://github.com/thinca/vim-quickrun.git;
        git clone git://github.com/Shougo/unite.vim.git;
        git clone git://github.com/thinca/vim-ref.git;
        ;;
    'upgrade')
        dirlista=`find . -maxdepth 1 -type d -print`;
        for d in $dirlista; do
            if [ "$d" != "." ]; then
                cd "$d";
                echo $d;
                git pull;
                cd '..';
            fi
        done;
        ;;
    *)
        cat << EOM
Usage:
    $0 install

    $0 upgrade

EOM
        ;;
esac

daemontools メモ

daemontools はめったに設定しないので毎回忘れる!とくにmultilogとか編集中にはダウンさせておくとか。

一連の流れをまとめたメモ。

インストール

$ sudo apt-get install daemontools-run
$ cd
$ mkdir daemontools

foo ディレクトリをつくり、スティッキービットをたてる

$ cd ~/daemontools
$ mkdir foo
$ mkdir foo/log
$ chmod +t foo

シンボリックリンクを /etc/service 以下につくり、自動で supervise が起動するのでダウンさせておく

$ cd /etc/service
$ sudo ln -s /home/hoge/daemontools/foo foo
$ sudo svc -d /etc/service/foo
$ sudo svc -d /etc/service/foo/log

runを作成

$ cd ~/daemontools/foo
$ vim run
export PATH=/path/to/library
cd /home/hoge/app/foo && exec setuidgid hoge executable-file

log/runを作成

$ vim log/run
exec setuidgid hoge multilog t ./main

作成したrunファイルに実行パーミッションを付与、ログを書き込むためにlogディレクトリの所有ユーザを指定する

$ chmod +x run log/run
$ chown hoge log

先ほどダウンさせたサービスを開始する

$ sudo svc -u /etc/service/foo/log
$ sudo svc -u /etc/service/foo
$ sudo svstat /etc/service/foo
$ sudo svstat /etc/service/foo/log

を実行して up の秒数が増えていれば正常に起動している。
10 seconds 以上にならないときは異常終了しているので環境変数やパスが間違っていないか確認する。

SSHだけでリモートサーバからローカルMacにGrowl通知したい!

Growlしたい!というときは次のようにやってます。

概要

  1. リモートでgrowl専用のログファイルを準備する。
  2. ローカルからリモートにsshで接続し、tail -fでログファイルの監視する。
  3. リモート側では、そのログファイルに対してGrowlさせたい内容をリダイレクトを使って追記する
  4. 追記された内容が出力されるので、その出力をローカルのLLで行ごとに読み込む。
  5. 読み込んだ行を growlnotify を起動して通知する

これだけ。

growlnotify が必要なので、 GrowlのディスクイメージのExtrasフォルダから事前にインストールしておくこと。

手順

ステップ1 - リモートで
$ echo 'foo' > $HOME/growler.log 

このように、ログファイル(通知させたい内容を追記するファイル)を作成しておく。

ステップ2 - ローカルで
$ ssh example@example.com tail -n 1 -f '$HOME/growler.log' | perl -e 'system "growlnotify", "-m",  $_ while (<STDIN>);'

この例では、example.comsshでログインし、ログファイルをtail -f で監視する。
標準出力がpipeによってperlに流れるので、それを受信してgrowlnotifyを起動する。
これによってファイルの最終行がgrowlされ、接続できているか確認できる。

ステップ3 - リモートで
$ echo 'hoge' >> $HOME/growler.log
$ ls >> $HOME/growler.log

このように標準出力をログファイルに追記するだけでgrowl通知ができる!

応用

とても時間のかかる処理をさせている間に別の処理をしたい場合
cpan-outdated -p | cpanm; echo 'upgraded' >> $HOME/growler.log

としておくと、処理が終わったときに通知してくれます。

ログファイルを吐き出すIRCクライアント(この場合はweechat)をリモートのscreen上で実行している場合
ssh example@example.com tail -n 1 -f '$HOME/.weechat/logs/*/*/*.weechatlog' | perl irc_growl.pl &

としてサーバにつなげ、irc_growl.plで処理を加えた上でgrowlさせることもできる。irc_growl.plでは、 --appIcon にLimeChatを指定することでGrowl時のアイコンを変更している。

sshのコマンドが長いと感じたら、.bashrcにaliasを設定しておくと良い。
参考にirc_growl.plを貼りつけておく。

irc_growl.pl
while (<STDIN>) {
    chomp;
    next unless $_;
    next if /^==>/; # ignore tail's output

    my $attr = parse($_);
    if ($attr->{type} =~ /(:?NOTICE|PRIVMSG)/) {
        system("growlnotify", "-m", $attr->{content}, "-t", $attr->{user}, "--appIcon", "LimeChat");
    }
}

sub parse {
    my ($line) = @_;

    my ($time, $cmd, @contents) = split /\t/, $line;
    my $content = join "\t", @contents;

    my $user;
    if (not $cmd) {           # critical error
        $cmd = 'ERROR';
    } elsif ($cmd eq '-->') { # join
        $cmd = 'JOIN';
    } elsif ($cmd eq '<--') { # part
        $cmd = 'PART';
    } elsif ($cmd eq '--') {  # messages from server
        $cmd = 'SERVER';
    } elsif ($cmd eq '*') {   # notice
        $user = $cmd;
        $cmd = 'NOTICE';
    } else {                  # privmsg
        $user = $cmd;
        $cmd = 'PRIVMSG';
    }

    return +{
        type    => $cmd,
        time    => $time,
        content => $content,
        user    => $user,
    };
}

やっぱりTCPでネットワーク通知したい場合

私の場合はこれで困ってないのでしらべてない。

sugyanさんが週末を犠牲にしてまで調べているのでしばしお待ちを!!

pjax こそが pushState + Ajax の本命

pjaxの前にpushStateとは

AjaxjQueryの説明は不要として、pushStateとはなんぞや。

pushStateを使ってブラウザの履歴に対する操作をし、HTMLの一部のみを書き換える動作でもブラウザの戻る/進む機能を実現できる方法のひとつ。Ajaxなページを再現し、かつURLを見慣れた方法で自然にpermalinkを表現できる。

有名なところではGitHubで使われてるアレ。

hash fragment (/#!/)

ブラウザの履歴を機能させるため、URL の fragment (#) を使ってAjaxなページを実現する方法。一時期もてはやされた感があるが、さらなる「#!」URL批判 - karasuyamatenguの日記 など合理的な反論があり、これから導入するのはためらわれるところ。

有名なところではTwitterで使われているあの厄介者。

pjaxとは

pjax とは pushState + ajax を合わせた語で、その名のとおり pushState を使いつつ Ajax な処理を行う為の jQuery ライブラリ。GitHubのdefunktが開発していることもあり、今後pushStateを使ったものでは導入が進んでいく可能性がかなり高いのではないかと読んでいる。

また、最近ではページの見た目の面では非クロスブラウザを許容する風潮があるように見受けられ、この流れともpjaxは親和性が高い。

「高機能なWebブラウザでは見栄えよく、そうでないWebブラウザで“も”それなりに」
Webページの見栄えにどこまでこだわるのか | 日経 xTECH(クロステック)

pjaxの振る舞いは、ChromeのようなブラウザではpushStateを使い、IEのようなブラウザでは通常のアクセスと同じように、全く同じpermalinkでアクセスできるようにしてくれる。

「コンテンツがcurlでロードできなければそのサイトは壊れている勢力」の救世主

私もこの勢力のうちのひとりだと内心思っているのでやや傾倒している感はある。

しかし hash fragment を使用したページは curl では取得できない。これはサーバ側にfragment以降は送信されないためであるが、publicなものにcurlやLWP::UserAgentやGoogleクローラーといったクライアントでアクセスさせるためにURLに細工することは解せない。かと言って自分でゴリゴリとpushStateの実装を書くのも骨が折れる。そういった問題を解決してくれるものになると思われる。

使い方

ここではざっくりとした使い方を書いておく。

細かい使い方はいろいろあるようなので、GitHubにあるREADMEを参照すると良い。GitHub - defunkt/jquery-pjax: pushState + ajax = pjax

クライアントサイド

"js-pjax" クラスのアンカーに対してのみ機能させる場合はこのようにセレクタを書く。

<script src="/jquery.min.js"></script>
<script src="/jquery.pjax.js"></script>
<script type="text/javascript">
  $(function () {
    $('a.js-pjax').pjax('#main');
  })
</script>
サーバサイド

pjaxを使ったリクエストの場合に、HTTPヘッダに X-PJAX: true が付くのでサーバサイドではヘッダを見て返すbodyを変更する。

もし、 X-PJAX がない場合はすべてをレンダリングしたHTMLを返し、X-PJAX が true の場合には対象のコンテナにロードさせるのに適切なresponse bodyを返すよう処理を書く。X-PJAX のリクエストの場合でタイトルを変更したい場合は、<title>タグも含めて応答する。

実装

サーバをPerlで実装したので

$ cd /tmp
$ curl https://gist.github.com/raw/901139/c13279a29cfcca8cc75e63fb9eeb65b3ca2785c7/app.psgi -LO 
$ plackup

で起動し、リクエスト/レスポンスをチェックできる。

app.psgi
use strict;
use warnings;
use feature qw/say switch/;
use Data::Section::Simple;
use Text::Xslate;
use Plack::Request;

my $tx = Text::Xslate->new(
    path => [ Data::Section::Simple->new->get_data_section ],
);

my $app = sub {
    my $req = Plack::Request->new(shift);
    my %data = ( %ENV,
        TIME => scalar localtime,
        PJAX => ($req->header('X-PJAX') ? 1 : 0)
    );

    say "----- X-PJAX is " . ($data{PJAX} ? 'TRUE' : 'FALSE');
    my $type =  $data{PJAX} ? 'pjax' : 'default';

    my $res = $req->new_response(200);
    $res->content_type('text/html; charset=utf-8');

    given ($req->path_info) {
        when ('/') {
            $data{title} = "root";
            $res->body( $tx->render("root-$type.tx", {data => \%data}) );
        }
        when ('/home') {
            $data{title} = "/home";
            $res->body( $tx->render("home-$type.tx", {data => \%data}) );
        }
        when ('/help') {
            $data{title} = "/help";
            $res->body( $tx->render("help-$type.tx", {data => \%data}) );
        }
        when ('/favicon.ico') {
            $res->redirect("http://www.google.com/favicon.ico", 301);
        }

        default {
            $res->status(404);
            $res->body('Not Found');
        }
    }

    return $res->finalize;
};

$app;

__DATA__

@@ home-pjax.tx
    <: if $data.PJAX { :> <title><: $data.title :></title> <: } :>
    <p> Hello, <: $data.USER :> </p>
@@ home-default.tx
    : cascade base;
    : override main -> { include "home-pjax.tx" }
    : override title -> { "/home" }

@@ help-pjax.tx
    <: if $data.PJAX { :> <title><: $data.title :></title> <: } :>
    <pre> <: $data | dump :> </pre>
@@ help-default.tx
    : cascade base;
    : override main -> { include "help-pjax.tx" }
    : override title -> { "/help" }

@@ root-pjax.tx
    <: if $data.PJAX { :> <title><: $data.title :></title> <: } :>
    <p> pjax!! pjax!! pjax!!</p>
@@ root-default.tx
    : cascade base;
    : override main -> { include "root-pjax.tx" }
    : override title -> { "root" }

@@ base.tx
    <!DOCTYPE html>
    <html>
        <head>
            <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
            <script src="http://pjax.heroku.com/jquery.pjax.js"></script>
            <meta charset='utf-8'> 
            <title> <: block title -> { :> hello pjax <: } :> </title>
            <script type="text/javascript">
                $(function () {
                    $('a.js-pjax').pjax('#main');
                })
            </script>
        </head>
        <body>
            : include "nav.tx"
            <div>
            : $data.TIME
            </div>
            <div id="main">
                : block main -> { }
            </div>
        </body>
    </html>

@@ nav.tx
    <ul id="nav">
        <li><a href="/" class="js-pjax">Index</a></li>
        <li><a href="/home" class="js-pjax">Home</a></li>
        <li><a href="/help" class="js-pjax">Help</a></li>
    </ul>
サンプルについて

pjaxリクエストを飛ばす方法、レスポンスをどう返せば意図したとおりに動くか、レスポンスを受けてタイトルを変更する方法など、手っ取り早く知りたいであろう事柄を埋めこんであるので、多少は参考になると思う。

curlでgistから取ってくる方法が一番楽だと思う。

結論

簡単にpushStateによる履歴操作とhash fragment (裏側のURLは汚い)に勝る綺麗なURLを実現できるので流行るといいな。

Perl CPANモジュールガイド

CPAN本の出版おめでとうございます&献本頂きありがとうございます。私がリリースしたもの*1も収録されています。

Perl CPANモジュールガイド

紹介と感想

CPANモジュールを32のジャンル(ユーティリティ/データ/クラス/日付/アルゴリズム等々)に分け、目的のモジュールと使い方を説明する形式で、まさに書名の通り「CPAN モジュールガイド」と呼ぶにふさわしい本といえるのではないでしょうか。また、400ページ超のこの分量を書くのは相当な苦労だったのだろうと想像できるほどの力作となっています。


多くのPerl入門書ではCPANの記述は最終章近くで存在に触れている程度でしかなく、どれを使えば良いのかわからない状態で先に進めない人が多いのではないかと思いますが、本書はそういった問題を解決するひとつの手段になりうるのではないかと思います。


また、自分の知らないモジュールがたくさんあることを思い知らされました。さらに、モジュール紹介の中にあるtips*2Perlを使う上で知っておいたほうが良いものが多いです。


モジュールガイドなのにAcme::のnamespaceがないのはうわなにをするやめくぁwせdrftgyふじkl;

ということで、Perl CPANモジュールガイドおすすめです。

*1:AnyEvent::Twitter 0.52 と AnyEvent::Twitter::Strem のパッチ紹介

*2:例えば、use constant した定数にコンパイルタイムで最適化が入ること等々

TEPCO 電気需要 JSON API 作った

http://linknode.net/tepco.html

Access-Control-Allow-Origin: * 

をヘッダーに含めているので、Same Origin Policyを気にせず、サイトをまたがってリクエストを飛ばしてJSONを取ってくることができます。

使用例

次のようなもので簡単に表を出力することができます。

    <script> 
        $(function () {
            $.ajax({
                url : 'http://api.linknode.net/tepco/v2/demand.json',
                dataType : 'json',
                success  : function (data) {
                    //console.log(data.raw);
                    //console.log(data);
                    $('#raw').append(data.raw);
 
                    $('#last_updated').append(data.last_updated.date + ' ' + data.last_updated.time);
 
                    for (var i in data.demand) {
                        $('#target > tbody').append(
                            $('<tr>').append(
                                $('<td>').append(data.demand[i].date + ' ' + data.demand[i].time),
                                $('<td>').append(data.demand[i].today),
                                $('<td>').append(data.demand[i].yesterday)));
                    }
                }
            });
        });
    </script> 

search.cpan.org で syntax highlighting

最近 search.cpan.org で POD 中のコードに syntax highlight できるようになりました。

使い方

ページ最下部右側にこのようなcolor schemeを選ぶオプションがあるので、リストの中から自分好みのものを選ぶと良いでしょう。
おすすめは "cpan" らしいです。


参考

Syntax highlighting for search.cpan.org | Mark A. Stratman [blogs.perl.org]