関数型!イカ娘

(この記事は partake.in の為に書かれました。TeX マクロ初心者なので識者の皆様方のツッコミ等歓迎します。)

奥村先生まで侵略されてるじゃなイカ

フッフッフッ、むこうから侵略されに来るとは好都合でゲソ!ついでに日本の TeX 界もいっしょに侵略してやるでゲソ!

関数作らなイカ

まずは関数*1の定義でゲソ。関数を定義するには \def を使うでゲソ。

\def\incursion#1#2{#1#2 が侵略したでゲソ!}

\incursion{海の家 れもん}{イカ娘}
\end

これに適当な名前を付けて保存して、pTeX に掛ければ DVI が完成するでゲソ。これは素の TeX なので pLaTeX じゃなくて pTeX を使うでゲソ。

海の家 れもん を イカ娘 が侵略したでゲソ

と出力されていたら成功でゲソ。簡単じゃなイカ

再帰しなイカ

\newcount\cnt
\def\ika#1{
  \cnt = #1
  \advance \cnt by -1
  \ifnum#1 = 0
    イカ娘!
  \else
    侵略!\expandafter\ika\number\cnt
  \fi
}

\ika{6}

\newcount<名前> でカウンタを定義、<名前> = <数> で代入できるでゲソ。足し算は \advance <名前> by <数>、値は \number を使えば参照できるんじゃなイカ? \ifnum は if 文の仲間で \ifnum<名前1><演算子><名前2> とすれば数値を比較できるでゲソ。

ところで、ここに出てくる \expandafter ってなんでゲソ?試しに外してみるでゲソ!

\newcount\cnt
\def\ika#1{
  \cnt = #1
  \advance \cnt by -1
  \ifnum#1 = 0
    イカ娘!
  \else
    侵略!\ika{\number\cnt}
  \fi
}

\ika{6}
\end

出力結果は…

侵略!侵略!侵略!侵略!侵略!イカ娘

一回分足りないでゲソ!!

\expandafter が無いと、\ika{\number\cnt} の展開結果は

  \cnt = \number\cnt
  \advance \cnt by -1
  \ifnum\number\cnt = 0
    イカ娘!
  \else
    侵略!\ika{\number\cnt}
  \fi

になるでゲソ。#1 の部分が \number\cnt になってるじゃイカ? \ika を展開する前に \number\cnt を展開したいでゲソ……。そんな時は \expandafter を付けてみるでゲソ!

\expandafter\ika{\number\cnt}

これじゃダメでゲソ。\expandafter は \ika を読み飛ばして次のトークンを評価するでゲソ。\ika の次のトークンは { なので \ika を飛ばして { を評価して \ika に戻ってくるだけなので最初と同じでゲソ。
正しくはこうやるでゲソ!

\expandafter\ika\number\cnt

これなら、 \ika を飛ばして \number を評価、\number は引数を要求して \number\cnt が評価された後 \ika{評価済みの\number\cnt} になるでゲソ*2

…… \ika をムシするとは良い度胸じゃなイカ!?

高階関数にしてみなイカ

さっきの \ika{6} の代わりに高階関数を使ってみるでゲソ!

\def\hoge#1{%
  #1{6}
}

\hoge{\ika}

簡単じゃなイカ

*1:本当の名前はマクロでゲソ……。私の天敵のマグロみたいで恐ろしいので名前を変えてやったでゲソ!

*2: http://oku.edu.mie-u.ac.jp/tex/mod/forum/discuss.php?d=58&parent=317

Google Calendar API を Ruby から操作したい

Rubygems の gcalapi を使えば Google Calendar API を簡単に叩けます。使い方は他にもいろいろ載ってるのでここでは触れません。

使ってみて、ちょっと困ったのが、gcalapi は イベント UID *1が読めない事。

Google Calendar から送られてくるデータに、ちゃんと UID は入っているけど、gcalapi gem 側でそれを認識していないのが原因だということが分かりました。とりあえず、てっとっりばやく修正するには、

module GoogleCalendar
  class Event
    attr_accessor :uid
  end
  Event::ATTRIBUTES_MAP["uid"] = { "element" => "gCal:uid", "attribute" => "value" }
end

等とすれば認識されるようになります。オープンクラス便利ですね。他のパラメータも同様に Event::ATTRIBUTES_MAP を書き換えて、アクセサを定義すれば認識されるようになります。*2

*1:iCalendar 形式 [RFC 2445] における、ユニークな識別子

*2:ちなみに、gcalapi は オリジナルの XML を保持しておいて、認識しているパラメータのみを REXML で書き換えるので、認識されていないパラメータがあるからといって、Event.save! した際に情報が欠損したりはしないようです。

レンタルサーバ等で gitosis を使う

gitosis の使い方の記事はいろいろある*1ようですが、gitosis 用のユーザを作らず一般ユーザ権限のみで運用する方法について書いた記事が無いようなので、その場合の方法、注意点等を軽く書いておきます。

gitosis は ~/.ssh/authorized_keys を管理用リポジトリの post-update hook を使って自動的に書き換えることでユーザ管理を実現しています。したがって、gitosis を使うには OpenSSH 形式の ~/.ssh/authorized_keys が使える事が大前提です。また、公開鍵認証以外でのログイン手段が無い場合、authorized_keys が壊れた場合に対処ができなくなるので危険です*2。諦めましょう。

インストール

Git と Python あたりのインストールは済んでいるものとします。

リポジトリから gitosis を取ってきてインストールします:

$ git clone git://eagain.net/gitosis.git
$ cd gitosis
$ python setup.py install --user

インストール先は ~/.local/ になるので、~/.local/bin に PATH を通しておいてください。

gitosis 初期設定

gitosis 管理用の鍵ペアを用意し gitosis を初期化します:

$ gitosis-init < id_rsa.pub

gitosis の仕組み上、authorized_keys へ通常のログイン用に登録された鍵を併用するとちょっと面倒かもしれません。
管理用のリポジトリ ~/repositories/gitosis-admin.git の post-update hook に実行権限を付けておきます:

chmod 755 ~/repositories/gitosis-admin.git/hooks/post-update

後は gitosis-admin.git を clone してきて設定をするのですが……、
えーと、力尽きたので、後の作業は http://progit.org/book/ja/ch4-7.html を見てください。

gitosis の仕組み

gitosis の設定後の authorized_keys の中身は

### autogenerated by gitosis, DO NOT EDIT
command="gitosis-serve user1",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3(略) user1
command="gitosis-serve user2",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3(略) user2

のようになっています。command オプションが指定されているため、この鍵で認証された場合はユーザがクライアント側で指定したコマンドを無視し、その代わりに command オプションで指定された gitosis-serve user1 が実行されます。
クライアントが指定したコマンドは環境変数 SSH_ORIGINAL_COMMAND に格納されているので、gitosis-serve はその環境変数を解釈してアクセス制御をしています*3

ありがちな問題?

鍵を流用した場合

gitosis 管理用に鍵ペアを用意せず、通常のログインに使う鍵を流用した場合

$ git clone hoge@example.org:gitosis-admin.git
Initialized empty Git repository in /home/fuga/gitosis-admin/.git/
fatal: 'gitosis-admin.git' does not appear to be a git repository
fatal: The remote end hung up unexpectedly

のようなエラーに見舞われることがあります。これは、gitosis 経由でリポジトリにアクセスしていないため発生します*4
この場合は、単に、

git clone hoge@example.org:repositories/gitosis-admin.git

のように、直接リポジトリにアクセスしてしまえば良いでしょう。

PATHが通せない場合

大抵 ~/.bashrc か ~/.ssh/environment あたりに書いておけば良いと思いますが、
PATH が通せない場合は gitosis をフルパスで指定する必要があるので、スクリプトを手で弄る必要があります。

gitosis/ssh.py 中に

def generateAuthorizedKeys(keys):
    TEMPLATE=('command="gitosis-serve %(user)s",no-port-forwarding,'
              +'no-X11-forwarding,no-agent-forwarding,no-pty %(key)s')
    yield COMMENT
    for (user, key) in keys:
        yield TEMPLATE % dict(user=user, key=key)

という部分があるので、gitosis-serve をフルパスに修正します。例えば /home/hoge/.local/bin/gitosis-serve の場合、

def generateAuthorizedKeys(keys):
    TEMPLATE=('command="/home/hoge/.local/bin/gitosis-serve %(user)s",no-port-forwarding,'
              +'no-X11-forwarding,no-agent-forwarding,no-pty %(key)s')
    yield COMMENT
    for (user, key) in keys:
        yield TEMPLATE % dict(user=user, key=key)

とします。もう一箇所は、gitosis/templates/admin/hooks/post-update 中の gitosis-run-hook を、やはりフルパスに修正します。

前:

#!/bin/sh
set -e
gitosis-run-hook post-update
git-update-server-info

後:

#!/bin/sh
set -e
/home/hoge/.local/bin/gitosis-run-hook post-update
git-update-server-info

*1:http://progit.org/book/ja/ch4-7.html

*2:gitosis 用ユーザを作る理由の一つでしょう

*3:git がリポジトリへアクセスする方法については http://progit.org/book/ja/ch9-6.html を参照してください

*4:同じ鍵に対して、gitosis 経由でアクセスするものと、通常のログイン用の両方の設定があるので