拡張可能なプログラム

Ruby のような実行時に何でもできる言語だと、ユーザが書いた Ruby のコードを実行したり、プラグインの gem をインストールするだけで使えるようになったりする仕組みを提供できる。 例えば spring は ~/.spring.rb や config/spring.rb を自動的に読み込み、また spring-commands- で始まる gem を自動的に require し、プラグイン側は Spring.register_command で独自のコマンドを定義できるようになっている。

pry も似たようなかんじで、~/.pryrc や ./pryrc を読み込んだり、pry- で始まる gem を自動的に require して、プラグイン側は Pry.commands.import 等で独自のコマンドを定義できるようになっている。

一方、Haskell のような言語だとこういうことをするのは難しい。 Haskell で書かれていて Haskell で拡張可能なプログラムというと XMonad くらいしか思い付かないんだけど、XMonad は「拡張可能なウィンドウマネージャ」というより「ウィンドウマネージャを簡単に作れるライブラリ」に近くて、実際 XMonad の設定は main 関数を持つ普通のプログラムとして書かれる。

XMonad の場合、設定をコンパイルして得たバイナリは ~/.xmonad/xmonad-$arch-$os に作られるんだけど、xmonad コマンドは ~/.xmonad/xmonad-$arch-$os を exec し、それに失敗したらデフォルトの設定で xmonad をスタートするラッパーの役割しか持っていない。

Haskell で拡張可能なプログラムとして yi を忘れてた。 yi も XMonad と同じ方式で、設定ファイルは main 関数を持つ普通のプログラムとして書かれるっぽい。XMonad 方式の設定を実現するための dyre というライブラリがあり、yi はこれを使っている。

Haskell で拡張可能なプログラムを書くとしたら、XMonad のようなアーキテクチャにするのがいいのかもしれない。 pandoc は内部では新しい Reader / Writer を追加できるようなアーキテクチャになってるけど、ユーザ側が好きな Reader / Writer を追加できるようなものではなさそう。

ttcoder

大学の ICPC の練習会で使ってる TokyoTechCoder のリポジトリGitHub で公開した https://github.com/eagletmt/ttcoder

元々オリジナルの TokyoTechCoder があってそれまではそれを使っていたけれども、 それを作っていた人が大学から離れて時間がたってしまったこともあり、メンテがされず動作が怪しいこともあった。 一昨年に半年間くらい初めて Rails アプリケーションを書いてみて、そのときの反省を活かしたい時期だったので、 ちょうど今から一年くらい前に rails new した。 今年度の練習会から新しいものを実際に使うようにして、オリジナルと同じようになるように機能を追加したり、 オリジナルから離れて独自の機能を追加したりしながら現在に至る。

自分もこの春に卒業して社会人になってしまい、このままだと以前と全く同じパターンになってしまうので、このタイミングで公開しておくことにした。 あとまぁ単純にオープンにしておきたいという気持ちもあった。 タダで Travis CI でテストを実行したり Coveralls でカバレッジを記録したりできるし、公開しておくと便利。

ArchLinux のリポジトリを akabei で楽に管理する

Arch のパッケージのリポジトリを作る 発展編 - eagletmt's blog で書いた内容を自動化するために akabei を作った。 さらに S3 の Static Website Hosting を利用する機能も実装して、実際に http://arch.wanko.cc/ を運用してみている。

基本機能

build サブコマンド

akabei build foo --repo-dir repo/x86_64 --repo-name bar --arch x86_64 で、foo/PKGBUILD をもとに repo/x86_64 以下に x86_64 向けのパッケージと、bar リポジトリABS tarball とデータベースとファイルリストが生成される。

% akabei build foo --repo-dir repo/x86_64 --repo-name bar --arch x86_64
(snip)
% ls repo/x86_64
bar.abs.tar.gz  bar.db  bar.files  foo-1.0.0-1-x86_64.pkg.tar.xz

さらに --repo-key--package-key を指定すると、指定した鍵で署名を行う。署名を行うときは gpg-agent を立ち上げておくと楽。

ビルド時の chroot はデフォルトで毎回最初から作り直す。Arch で破壊的変更があったりすると chroot 環境をメンテするのがめんどくさいので、毎回作り直してもいいと思った。--chroot-dir オプションで既にある chroot を指定して使わせることもできる。

他にも --pacman-config--makepkg-config--logdest--srcdest 等のオプションを指定できて、詳しくは akabei help build を参照。

その他のサブコマンド

また、ABS tarball やデータベースやファイルリストを扱うための abs-addabs-removerepo-addrepo-removefiles-addfiles-remove といったサブコマンドがある。 {repo,files}-{add,remove} は既存の repo-add(8) と同じ機能だけど、余計なファイル出力や symlink が無いのが特徴。

Omakase mode

普通にリポジトリを運用するときに行う操作を自動化した Omakase mode がある。

最初に、akabei omakase init で設定ファイルを生成する。

% akabei omakase init foo --repo-key $GPGKEY --package-key $GPGKEY
      create  .akabei.yml
      create  foo
      create  sources
      create  logs
      create  PKGBUILDs
      create  etc
      create  etc/makepkg.i686.conf
      create  etc/pacman.i686.conf
      create  etc/makepkg.x86_64.conf
      create  etc/pacman.x86_64.conf
Edit etc/makepkg.*.conf and set PACKAGER first!
% echo 'PACKAGER="John Doe <john@doe.com>"' >> etc/makepkg.i686.conf
% echo 'PACKAGER="John Doe <john@doe.com>"' >> etc/makepkg.x86_64.conf

パッケージを追加するときは、PKGBUILDs/$pkgname というディレクトリを作ってその中に PKGBUILD を置き、akabei omakase build $pkgname とする。 するといいかんじに i686/x86_64 用のパッケージが作られ、ABS tarball、データベース、ファイルリストが更新される。 ビルド時にダウンロードしたソースやビルドのログも、それぞれ source ディレクトリと logs ディレクトリに作られる。

% akabei omakase build bar
(snip)
% tree foo
foo
`-- os
    |-- i686
    |   |-- bar-1.0.0-1-i686.pkg.tar.xz
    |   |-- bar-1.0.0-1-i686.pkg.tar.xz.sig
    |   |-- foo.abs.tar.gz
    |   |-- foo.db
    |   |-- foo.db.sig
    |   `-- foo.files
    `-- x86_64
        |-- bar-1.0.0-1-x86_64.pkg.tar.xz
        |-- bar-1.0.0-1-x86_64.pkg.tar.xz.sig
        |-- foo.abs.tar.gz
        |-- foo.db
        |-- foo.db.sig
        `-- foo.files

そして、この foo ディレクトリを Apache なり nginx なりで公開する。

パッケージを更新するときも、PKGBUILD を更新してから同じように akabei omakase build $pkgname とすればいい。 このとき、古いパッケージファイルは消されずに残る。 これは意図的にそうしている (けど、古いパッケージを消すオプションもあってもいいかもしれない)。

パッケージを削除するときは akabei omakase remove $pkgname とする。 ただし、これによって消されるのは ABS tarball、データベース、ファイルリスト内のエントリであり、パッケージファイル自体は消されずに残る。

Omakase mode with S3

これは完全に自分の都合で追加した。 S3 上にリポジトリを作るのは便利で楽だと思っていて、実際に http://arch.wanko.cc/ ではそうしてる。

akabei omakase init --s3 とすると S3 の設定を含む .akabei.yml が生成されて、.akabei.yml に S3 用のアクセスキーを設定すれば、あとは同じように akabei omakase build $pkgname でビルドして公開できる。

Arch のパッケージのリポジトリを作る 発展編

基本編は http://eagletmt.hateblo.jp/entry/2012/12/21/214507 。 基本編で紹介した方法だけでもリポジトリとしての機能を果たせるディレクトリ構成を作れるが、 公式リポジトリと比較して足りないものが3つある。

  1. 署名
  2. pkgfile 用のデータベース
  3. abs 用の tarball

実際にこれらを追加したリポジトリhttp://arch.wanko.cc/vim-latest/ にある。

署名をつける

pacman 4 にメジャーバージョンアップしてから、パッケージとデータベースに署名をつけられるようになった。 2012-06-04 の時点ですべての公式リポジトリのパッケージに署名がつけられ、デフォルトで署名のチェックを強制するようになっている (Arch Linux - News: Having pacman verify packages)。 独自リポジトリでも署名のチェックを強制するには /etc/pacman.conf で SigLevel = Required に設定すればいい。

署名するにはもちろん GPG の鍵が必要なので、最初に用意しておく。自分は以下のサイトを参考にした。

以下の説明では、署名する鍵として実際の自分の鍵である C48DBD97 を用いる。

パッケージに署名をつける

単純に makepkg で作る場合は、/etc/makepkg.conf で GPGKEY を設定して BUILDENV!signsign に変更すればいい。

BUILDENV=(fakeroot !distcc color !ccache check sign)

GPGKEY="C48DBD97"

ただし、基本編に書いたようにきちんとパッケージを作るには makechrootpkg を利用して chroot 環境で作成したほうがいい。 chroot 環境から自分の GPG 鍵は見えないので、makechrootpkg でパッケージを作った後に gpg コマンドで普通に署名を追加するしかない (たぶん)。

例えば vim-latest というパッケージを作る場合、

sudo makechrootpkg -cur ~/chroot-x86_64
gpg --detach-sign -u C48DBD97 vim-latest-7.4.141-1-x86_64.pkg.tar.xz

とする。 あとはパッケージと同じディレクトリに .sig ファイルも置いて公開するだけでいい。

データベースに署名をつける

ついでにデータベースにも署名する。 これは --sign --key C48DBD97 というオプションをつけて repo-add すればいい。 さらに --verify というオプションをつけると、現在のデータベースの署名をチェックしてから新たな署名を生成する。 独自リポジトリを更新していくときは常に --verify をつけておいたほうがいいだろう。

例えば vim-latest というリポジトリの場合、

repo-add --sign --key C48DBD97 --verify vim-latest.db.tar.gz vim-latest-7.4.141-1-x86_64.pkg.tar.xz

とする。 すると vim-latest.db.tar.gz.sig も生成されるので、それぞれ vim-latest.db と vim-latest.db.sig という名前で公開する。

pkgfile 用のデータベースを作る

pkgfile を使うと、あるコマンドやファイルがどのパッケージについているか調べることができる。

% pkgfile -b vim
extra/gvim
extra/vim
vim-latest/vim-latest

この機能は pkgfile -u したときにリポジトリから "#{repo}.files" というデータベースをダウンロードしてくることで達成されている。 この .files というデータベースを作るには repo-add を使う。

repo-add --sign --key C48DBD97 --verify --files vim-latest.files.tar.gz vim-latest-7.4.141-1-x86_64.pkg.tar.xz

これによって生成された vim-latest.files.tar.gz を、 普通のデータベースと同様に vim-latest.files という名前で公開する。 .sig を一緒に公開してもいいけど、pkgfile は特に署名のチェックはしない。 結局、.files はリポジトリのデータベースにファイル一覧をつけただけのファイルなので、リポジトリのデータベースと同様に新たにパッケージを追加する度に repo-add --files して更新する。

abs 用の tarball を作る

これを作っておくと、abs コマンドでソースツリーを保ったり、yaourt -G でソースパッケージを展開したりすることができる。

ただし、これを作るための便利なコマンドというのは用意されていない様子。 なので適当に自分で tarball を作るしかない。

vim-latest/
vim-latest/vim-latest/
vim-latest/vim-latest/PKGBUILD
vim-latest/vim-latest/vim-7.4.035-breakindent.patch

というような構成の tarball を vim-latest.abs.tar.gz という名前でデータベースと同じディレクトリで公開すればいい。

まとめ

最終的に公開するディレクトリの構成はこのようになる。

i686/vim-latest-7.4.141-1-i686.pkg.tar.xz
i686/vim-latest-7.4.141-1-i686.pkg.tar.xz.sig
i686/vim-latest.abs.tar.gz
i686/vim-latest.db
i686/vim-latest.db.sig
i686/vim-latest.files
x86_64/vim-latest-7.4.141-1-x86_64.pkg.tar.xz
x86_64/vim-latest-7.4.141-1-x86_64.pkg.tar.xz.sig
x86_64/vim-latest.abs.tar.gz
x86_64/vim-latest.db
x86_64/vim-latest.db.sig
x86_64/vim-latest.files

2013年の思い出

わりと色んな出来事や変化のある年だったと思う。

ICPC

1月の頭にコーチからメールが転送されてきて、世界大会へ行けることを知った。嬉しさより驚きのほうが上だった。 コンテストの結果は1問も通せなかったという非常に残念なものだったけど、世界大会は楽しかった。

バイト

1月から初めてのバイトを始めた。 大学でイベントのちょっとした手伝いや TA はやったことあったけど、普通(?)のアルバイトは初めてだった。 TA のときも思ったけど、時間を捧げてるとはいえ、これだけでこれくらいのお金が貰えるのかーというのが最初の感想だった。 技術的な新鮮味はあんまり感じなくて、労働とかお金とかについて考えることが多かった。

自分が自由に使えるお金が増えたことで、金銭感覚も多少変わったと思う。 バイト始めて少し経ってから 2TB の WD Red を一気に4個買ったりしてた。 趣味の品について「これ n 時間の労働と同じ価値か」と換算してしまうとだいたい不当に安く感じてしまってよくないので、この考え方はすぐやめるようにした。

電子書籍

最初にラノベを一冊買ってみて iPhone で読んでみたところ普通に読めることがわかったので、そこから一気に Kindle Store でラノベを買うようになった。 バイトへの行き帰りの電車内で読む習慣がついて、高校生のときと同じかそれ以上によく読むようになった。今数えたら去年一年間で45冊くらいだった。 漫画も電子書籍で買うようになった。漫画は iPhone で読むのは正直辛いけど読めないことはなく、漫画による部屋の圧迫と比較して電子書籍を選ぶようになった。

9月から同人誌を中心に本を裁断してスキャンして電子化し始めた。 最初は裁断に対して抵抗あると思っていたけど、いざ始めてみると極一部の同人誌を除いてガンガン裁断してた。 このおかげでだいぶ部屋の有効スペースを取り戻せた。

インターン

5月からの一ヶ月間インターンしてた。 ここでやったことの1つとして bundle install の並列化があって、先日その変更も含む bundler 1.5.1 がリリースされた。 OSS プロジェクトに適当に貢献することは前から個人でやってたけど、給料をもらってその時間で OSS 活動するのは初めてで気持ち良かった。 ここでもメンターの方からは技術的な話より仕事の進め方等について指摘されることが多かった。 計画とか目標の立て方とか、成果のアピールとか。このへんは今も悩んでる。

アニメ

GJ部アイカツちはやふる2期、あたりが特に最高だった。 あとアニメから入ってプリズマイリヤの漫画を読んだのもよかった。魔法少女いい。

ただ、バイトを始めたせいかアニメを見る時間はだいぶ削られた。 相変わらず録画はしてるけど、消化率は減り続けてると思う。

クレジットカード決済の流れと限度額

急にクレジットカードが一時的に使えなくなったことがこれまでで二度あって、カード会社に問い合わせたところどちらも理由は限度額によるものだった。 しかし、Web 上で確認できる速報の利用明細や自分でつけていた利用記録を確認してみても、限度額に到達しているとは思えなかった。 問い合わせの際に「最終的には利用明細には載らず請求もされないけれど、現時点では限度額の計算に使われているものがある」ということを示唆され、 一体どういうことなのかを知りたくてクレジットカード決済の流れと限度額について適当に調べた。

クレジットカードで決済するときは

  1. 与信枠の確保
  2. 売上の確定

という2つのステップを踏む。 1 のステップでカード情報や金額をカード会社に送信して、カードの有効性等を確認する。 このとき請求金額が限度額内かどうかも確認し、大丈夫ならその決済用の与信枠を確保する。 限度額の確認は、これまでに確保した与信枠と今回確保する与信枠の合計が限度額内かどうかで判定する。 その後、2 のステップで売上が確定する。 利用明細に載る日付は 2 を行った日である。

実店舗でカードを使ってその場で物を買う場合やダウンロードコンテンツへの課金の場合、1 と 2 がほぼ同時に行われるので気になることは無いけれども、 例えば通販や予約販売で注文と発送のタイミングが異なる場合、注文のタイミングで 1 を行い発送のタイミングで 2 を行うことが多い(?)ので 1 と 2 の間に時間的なズレが生じる。 すると 1 と 2 の間では「与信枠は確保されているけど請求は確定していない」という状態になり、速報の利用明細には載らないけど限度額の計算には使われている、という状態になる。 注文をキャンセルした場合、通常は確保した与信枠のキャンセルも行われるらしいものの、 カード会社によってはキャンセルが反映されるまでに時間がかかるらしい。 また毎月固定の金額を請求するような継続課金のサービスの場合、売上を確定するのは月末が多いものの、取りっぱぐれるリスクを抑えるために月初のほうに与信枠を確保することが多いらしい [要出典]。

したがって、予約 (キャンセル済みであっても) や継続課金によって利用明細と確保された与信枠の間に差が出てくるようだった。 発売日が遠い商品の予約等は請求金額には影響が無いものの、限度額の低い学生にとっては気をつけるべきことだと思った。

RubyGems 2.2 から拡張ライブラリのインストール先が変わったっぽい

ruby 2.1.0-preview2 に同梱されている RubyGems は 2.2 であり、このバージョンから拡張ライブラリのインストール先が変わったっぽい。まだ preview なので正式版でもこのままなのかはわからないけど…

今までは lib/ruby/gems/2.1.0/gems/foo-0.1.0/lib みたいなディレクトリが基点になっていて普通の Ruby のコードと同じ場所に置かれていたけど、RubyGems 2.2 から新たに lib/ruby/gems/2.1.0/extensions というディレクトリができ、lib/ruby/gems/2.1.0/extensions/x86_64-linux/2.1.0-static/foo-0.1.0 のようなディレクトリが基点になる。この変更に関係する pull-request はたぶんこれ https://github.com/rubygems/rubygems/pull/596

extensions 以下のディレクトリも自動的に $LOAD_PATH に追加されるので普通は影響がないはずだけど、lib 以下に拡張ライブラリも存在すると仮定した処理を行っている場合は動かなくなってしまう。 実際に msgpack-ruby がこれに該当していて、先週直されてた https://github.com/msgpack/msgpack-ruby/pull/22