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

ScanSnap iX500 を VirtualBox 内の Windows 8 から使う

ScanSnap iX500 と Windows 8 を買った。 サーバ的に使っている Linux マシンの VirtualBox 内に Windows 8 をインストールして、そこから iX500 を使っている。

Linux から全く使えないわけではなくて、sane を使うとスキャンはできる。 現時点で最新のリリース版である 1.0.23 は iX500 の発売より前のリリースなので、当然対応しておらず、HEAD をビルドする必要がある。

ただし、本当に画像としてスキャンができるだけで、サイズを自動的に調節したりカラーと白黒の判別を自動的にしたり OCR をかけて pdf にしたりといったことができないため、俺は勧めない。

なので素直に Windows 8 から使うようにした。ただし VirtualBox 内の。 準備として、まず Oracle Extension Pack をインストールする。これはリモートから VirtualBox のゲストマシンに VRDP で接続するのに必要なため。 また、ホストマシンに差した USB をゲストマシンから使う際にユーザが vboxusers グループに属している必要があるので、これも行っておく。

% yaourt -S virtualbox virtualbox-ext-oracle
# gpasswd -a eagletmt vboxusers

次に仮想マシンを用意する。VirtualBox では VBoxManage でコマンドラインから操作できる。

% VBoxManage createvm --name Win8 --ostype Windows8_64 --register
% VBoxManage modifyvm Win8 --memory 2048 --boot1 dvd --nic1 nat --acpi on --vrde on --usb on --usbehci on --cpus 2 --vram 64
% VBoxManage createhd --filename Win8.vdi --size 40960
% VBoxManage storagectl Win8 --name Win8sata1 --add sata --bootable on
% VBoxManage storageattach Win8 --storagectl Win8sata1 --port 1 --type hdd --medium ~/VirtualBox\ VMs/Win8/Win8.vdi
% VBoxManage storageattach Win8 --storagectl Win8sata1 --port 2 --type dvddrive --medium /tmp/win8.iso

ここでやっていることは、

  • Win8 という名前の仮想マシンを作成する
  • Win8 という仮想マシンの設定を行う
    • メモリは 2GB
    • ブートの順序の最初は dvd
    • ネットワークアダプタの割り当ては NAT
    • ACPI をサポート
    • VRDP をサポート (リモートから rdesktop-vrdp でゲストマシンに接続できる)
    • USB コントローラを有効化
    • USB 2.0 (EHCI) コントローラを有効化
    • CPU は2つ (最初1コアでやったらスキャンがだいぶ遅れることがあったので)
    • VRAM は 64MB (デフォルトでは足りなかったので適当に増やした)
  • Win8.vdi という 40GB のディスクイメージを作成
  • Win8sata1 という仮想 SATA コントローラを作成
    • これに Win8.vdi と Windows 8 のインストールディスクを繋げる

というもの。EHCI コントローラを有効にし忘れると、iX500 を接続したのになぜか認識されないということになる。

そして

% VBoxHeadless --startvm Win8

で起動できる。 クライアントからは

% rdesktop-vrdp $REMOTE_IP

で接続できるので、Windows 8 のインストール作業を行う。VirtualBox Guest Additions もインストールしておく。

USB デバイスをゲストマシンに認識させるには、さらに USB デバイスフィルタを設定する必要がある。 まず VirtualBox がちゃんと USB デバイスを認識できてるかどうかを確認する。

% VBoxManage list usbhost
Host USB Devices:

UUID:               0d4dadeb-a48e-4be1-a412-b79b28f1c917
VendorId:           0x04c5 (04C5)
ProductId:          0x132b (132B)
Revision:           1.0 (0100)
Port:               5
USB version/speed:  2/2
Manufacturer:       Fujitsu
Product:            ScanSnap iX500
Address:            sysfs:/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-1/3-1.6//device:/dev/vboxusb/003/006
Current State:      Available

これが表示されないときは、ユーザが vboxusers グループに所属していない可能性がある。 iX500 だけをパスさせるようにするため、vendor id と product id を指定してフィルタを作る。

% VBoxManage usbfilter add 0 --target Win8 --name ScanSnap --vendorid 04C5 --productid 132B

ちなみにこの vendor id と product id は lsusb でも調べられる。

そうしてから Win8 を立ち上げる。すると、usbhost の Current State が Captured に変化するはずである。

% VBoxHeadless --startvm Win8 &
[1] 16562
Oracle VM VirtualBox Headless Interface 4.2.16_OSE
(C) 2008-2013 Oracle Corporation
All rights reserved.

VRDE server is listening on port 3389.

% VBoxManage list usbhost
Host USB Devices:

UUID:               33fd08be-3bab-4280-8545-290ffffe4a8a
VendorId:           0x04c5 (04C5)
ProductId:          0x132b (132B)
Revision:           1.0 (0100)
Port:               5
USB version/speed:  2/2
Manufacturer:       Fujitsu
Product:            ScanSnap iX500
Address:            sysfs:/sys/devices/pci0000:00/0000:00:1a.0/usb3/3-1/3-1.6//device:/dev/vboxusb/003/006
Current State:      Captured

この状態ならゲストマシンから iX500 を認識できているはずなので、iX500 のインストールディスクを VBoxManage storageattach で繋げてインストール作業をすれば使えるようになる。

スキャンしたデータは共有したいので、VirtualBox の共有フォルダ機能を使った。

% VBoxManage sharedfolder add Win8 --name scansnap --hostpath ~/scansnap

ゲストマシンでは \\VBOXSVR\scansnap にスキャンデータを置くようにする。 俺の場合はさらに ~/scansnap をホスト側で NFS で export するようにしている。

こんなかんじで iX500 を使ってますが、それなりに快適です。

参考

neco-ghc で補完時に型情報を表示できるようにした

ghc-mod のほうに補完に使えそうな機能追加があったので久しぶりに neco-ghc を更新した https://github.com/ujihisa/neco-ghc

vimrc で let g:necoghc_enable_detailed_browse = 1 とすると補完時に型情報が表示されるようになる。 ghc-mod 1.11.5 で追加された機能を使っているので、有効化するにはそれ以上のバージョンが必要。

変数だけではなく型クラスや型シノニム等にも表示される。

欠点としては、無効化したときと比べてやや初期化に時間が掛かることが挙げられる。 キャッシュされるので最初さえ我慢できれば従来通り高速に補完候補が表示される。

YARD で拡張ライブラリのドキュメントを書くときに気をつけること

Ruby の拡張ライブラリを書いていていて、YARD でドキュメントを生成しようと思ったらうまく生成されなくてしばらくはまっていた。

YARD も RDoc も、pure Ruby なライブラリだけでなく C で書く拡張ライブラリのドキュメンテーションにも対応している。 そのはずなんだけど、RDoc ではちゃんとメソッドのドキュメントが生成されているのに、YARD だと生成されない、という現象に悩まされた。 いろいろ調べた結果、YARD は簡単に言うと

  1. Ruby のメソッドの本体として使われていそうな関数定義を見つけたら、関数名からそのコメントや定義へのマッピングを追加 (symbol_handler.rb)
  2. 関数内で rb_define_method しているのを見つけたら、マッピングからコメントや定義を取得して、メソッド名と関連付ける (method_handler.rb)

みたいなアルゴリズムになっていて、つまり rb_define_method している行よりソースコード的に上でコメント付きのメソッド本体の関数の定義 (!= 宣言) する必要がある。

なので、

#include <ruby.h>

VALUE rb_cFoo;
static VALUE foo(VALUE self, VALUE x);

void Init_foo(void)
{
  rb_cFoo = rb_define_class("Foo", rb_cObject);
  rb_define_method(rb_cFoo, "foo", RUBY_METHOD_FUNC(foo), 1);
}

/* call-seq: foo(x)
 *
 * Foo method
 *
 * @param [Fixnum] x the argument
 * @return [Fixnum] the return value
 */
VALUE foo(VALUE self, VALUE x)
{
  return x;
}

みたいに書くと YARD のドキュメンテーションに失敗する。

#include <ruby.h>

VALUE rb_cFoo;

/* call-seq: foo(x)
 *
 * Foo method
 *
 * @param [Fixnum] x the argument
 * @return [Fixnum] the return value
 */
static VALUE foo(VALUE self, VALUE x)
{
  return x;
}

void Init_foo(void)
{
  rb_cFoo = rb_define_class("Foo", rb_cObject);
  rb_define_method(rb_cFoo, "foo", RUBY_METHOD_FUNC(foo), 1);
}

こう書くと成功する。

一方 RDoc では、どっちで書いてもちゃんとメソッドに対するドキュメントが生成される。

NFS をマウントしたままシャットダウンすると異常に時間がかかる問題

NFS をマウントしたままシャットダウンすると、電源が切れるまでに1分以上かかった。 しかし手動で NFS を umount し (これはすぐ終わる)、シャットダウンすると普通にスムーズにシャットダウンする。

ちょうど同じような症状で悩んでいる人が arch-general にいて、そこで解決しているのを見つけた https://mailman.archlinux.org/pipermail/arch-general/2013-January/032794.html 。 自分の環境でもこの方法で解決した。

原因は NFS を umount する前にネットワーク接続が切れていることだった。 これを解決するには、systemd に NFS にはネットワーク接続が必要であることを伝えればいい。 systemd はブート時に、/usr/lib/systemd/system-generators/systemd-fstab-generator で /etc/fstab から mount unit を生成する。 これは systemctl list-units で見ることができる。 systemctl list-units すると

mnt-nfs.mount       loaded active mounted /mnt/nfs

みたいな行があるので、ここからマウントポイントに対応する mount unit の名前がわかる。 あとは、これがネットワーク接続を必要としていることを設定すればいい。 自分も arch-general で回答している人のように dhcpcd@.service を使っていたので、

# mkdir /etc/systemd/system/mnt-nfs.mount.wants
# ln -s /usr/lib/systemd/system/dhcpcd@.service /etc/systemd/system/mnt-nfs.mount.wants/dhcpcd@eth0.service

とした。