2011年 09月 01日

エンジニアリングのフールプルーフ

2011年 09月 04日

SQL::NamedPlaceholder

もしかしたら既にあるのかもしれないのですが、見つからないのでかっとなってあげてしまいました。一部 ORM に実装されているような named placeholder を単体でやるモジュールです。

「ORM 使いたくねーし、DBI 直でやるし! あ、でも placeholder の順番間違えると嫌だな…」ってときに便利な気がします。

  use SQL::NamedPlaceholder qw(bind_named);

  my ($sql, $bind) = bind_named(q[
      SELECT *
      FROM entry
      WHERE
          user_id = :user_id
  ], {
      user_id => $user_id
  });

  $dbh->prepare_cached($sql)->execute(@$bind);
2011年 09月 06日

gerry++

2011年 09月 07日

自分が言う「成功体験」というのは、自分で考えて自分で作ったもの (行動) が正しく社会に認められることなんだと思う。このような承認欲求というのが、自分はそれなりに強いみたいだけれど、なんでそんなことになっているのか考えてみると、殆どよく解らない。実際は「成功体験」自体が全くないわけではないはずではあるものの、主観的には「失敗体験」による自信の喪失ばかりで「成功体験」は全然に少ないと感じている。

モチベーションを損う原因となるのは、行動の結果失敗することがありありと想像されることが大きくて、普段から何かにつけて、評価された結果「ダメだ」と言われることばかりが頭に浮かび、そのせいで、一度失敗した経験はすごく強い行動の抑止力になっている。それでもなお「全く何もしない」選択をせずにすんでいるときの要素は「自分で満足できればよい」ぐらい他人を信用しない心持ちと、「(仕事のように) やらないと恐怖が待っている」という危機感と、「解ってないのはあいつらののほうだ」という逆ギレのおかげかもしれない。

「承認済み」の人間が心の底から苦手でどうしようもなくて、全く関わりたくもないと思い続けている。とは言っても、どんなコミュニティでも一定数そういう人はいるものでそこはどうしようもないし、あるいな成功している (ように見える) 人には承認済みの人が多いので、これがまたうんざりさせられる事実のように感じられて、いつもすごくめげそうな気持ちになる。

「承認済み」の立場から発せられる言葉とか (特徴的な言葉遣いを)、声とか (特徴的な発声でする) というのは、あからさまに「自分は承認されています」という含みがあり「そんな言い方がアリなのか」とびっくりする。

ACE の使いかたのメモ

ACE という JS でできたブラウザ上で動くエディタがあり、すごく良くできてるし拡張性もあって使いやすいのですが、ところどころ気に入らないのをなんとかする必要があるのでメモしておきます。

デフォルトのキーバインドが糞

Cmd-L を奪って gotoline とかいう機能が動いてアホかと思うので (ロケーションバーにフォーカスできない)、以下のようにして機能を殺している… あと TAB を奪うのも場合によっては非常にうざいので機能ごと殺している。キーバインドだけ殺したいけどよくわからなかった。

editor.commands.removeCommand('gotoline'); // fucking default keybinding of ace
editor.commands.removeCommand('indent');
editor.commands.removeCommand('outdent');

JS モードなどで括弧を勝手に挿入する

基本的にはうざくない ( 連続して入力した場合は余計に入力しないので賢い) のだけれど、{ RET } と入力したりすると括弧が余計についてしまってストレスフルなので、この機能を以下のようにして殺した。

        var mode = new JavaScriptMode();
        mode.$behaviour.remove('braces');
        mode.$behaviour.remove('parens');
        mode.$behaviour.remove('string_dquotes');

HtmlMode でも script の内部は JavaScriptMode になるので、この場合以下のようにしたら殺せるっぽい。

        var mode = new HtmlMode();
        mode.$modes['js-'].$behaviour.remove('braces');
        mode.$modes['js-'].$behaviour.remove('parens');
        mode.$modes['js-'].$behaviour.remove('string_dquotes');

ついで

典型的に

        var editor = ace.edit(container);

        editor.renderer.setShowGutter(false);
        editor.renderer.setHScrollBarAlwaysVisible(false);
        editor.renderer.setPadding(5);
        editor.renderer.setShowPrintMargin(false);

        editor.setHighlightActiveLine(false);
        editor.setKeyboardHandler(MyKeyBinding);

        var session = editor.getSession();
        session.setMode(new MyMode());
        session.setUseWrapMode(true);
        session.on('change', function () {
             textarea.value = session.getValue();
        });
        session.setValue(textarea.value);

みたいのをコピペしてる


あと、StateHandler や HashHandler を使いたいとき、どうやって使っていいかわからなくて (lib/ace/keyboard/state_handler.js をそのまま読みこもうとしても define() でエラーがでる)、build/demo/kitchen-sink-uncompressed.js からコピペしてファイルを作るという、すごく頭が悪いことをやっていて困っている… lib/ace/keyboard/keybinding/emacs.js とかが state_handler に依存してるのに、build 以下には state_handler のファイルがないので、よくわからない……

2011年 09月 08日

ACE のシンタックスハイライトルールを独立で使う

function dependon (check, src) {
	return function () {
		var ret = Deferred();
		check = new Function('try { return !!(' + check + ') } catch (e) { return false }');
		if (check()) {
			Deferred.next(function () { ret.call() });
		} else {
			var script = document.createElement('script');
			script.charset = 'utf-8';
			script.src = src;
			document.body.appendChild(script);
			setTimeout(function () {
				if (!check()) {
					setTimeout(arguments.callee, 100);
				} else {
					ret.call();
				}
			});
		}
		return ret;
	};
}

var Supported = {
	'perl' : 'Perl',
	'javascript' : 'JavaScript',
	'css' : 'Css'
};

function highlight (container) {
	container.find('pre.code').each(function () {
		if (/lang-(\S+)/.test(this.className)) {
			var lang = Supported[RegExp.$1.toLowerCase()];
			if (!lang) return;
			var pre  = $(this);
			var code = pre.text();

			Deferred.chain(
				dependon('ace', '/js/editor/ace/ace-uncompressed.js'),
				dependon('require("ace/mode/' + lang.toLowerCase() + '_highlight_rules")', '/js/editor/ace/mode-' + lang.toLowerCase() + '.js')
			).
			next(function () {
				var Tokenizer = require("ace/tokenizer").Tokenizer;

				var rules = require("ace/mode/" + lang.toLowerCase() + "_highlight_rules")[lang + 'HighlightRules'];
				var tokenizer = new Tokenizer(new rules().getRules());

				var parent = document.createDocumentFragment();

				var state = 'start';
				var lines = code.split(/\n/);

				return Deferred.repeat(lines.length, function (i) {
					var line = document.createElement('span');
					line.className = 'line';

					var tokens = tokenizer.getLineTokens(lines[i], state);
					for (var j = 0, it; (it = tokens.tokens[j]); j++) {
						if (it.type == 'text') {
							line.appendChild(document.createTextNode(it.value));
						} else {
							var span = document.createElement('span');
							span.className = it.type;
							span.appendChild(document.createTextNode(it.value));
							line.appendChild(span);
						}
					}

					line.appendChild(document.createElement('br'));
					parent.appendChild(line);

					state = tokens.state;
				}).
				next(function () {
					pre.empty().append(parent);
				});
			}).
			error(function (e) {
				alert(e);
			});

		}
	});
}
2011年 09月 10日

サービス開発とか向いてないんじゃないか。

思い返してみたけど、事実として成功体験が少なく、別に過剰な思い込みによる勘違いではなかった。

2011年 09月 12日

理科系の作文技術 (中公新書 624) - 木下 是雄

木下 是雄

3.0 / 5.0

文章作法学ぶなら (理科系に限らず) とりあえずこれを読んでおけというぐらいの本だそうで、「何か文章を書くコツが書いてある本でも欲しい」と書いたら、すすめられたり、社内の編集の方が他の人に薦めているのを見たりしたので、さっそく買って読んだ。

一度読んだ限りだと、段落をどう構成するかというのが一番参考になった。今まで段落をなんとなくで分けていたけど、トピックセンテンスを意識して必要ないことはひとつの段落に入れるなということで、(もちろん知っている人にとっては当然なんだろうけど) なるほどなあと思った。

他にも、普段気を付けていること (毎日このクソみたいな日記をつけることによって学びとった基本的なこと) も簡潔に纏めてあった。やってきたことに関して、とりあえずそんなに間違ったことを考えているわけではないと分って良かった。こういう本は場合によっては「お前は全く間違ったことをしているぞ」と言われかねないので、おそるおそる読むのだけれど、今回はそうひどいことは言われなかったように思う。

まだ十分生かしきれるほど飲み込めていないので、今後何度も読みかえす必要がある。というかもっと早く読んでいれば… と思った。

プログラミング言語については強く興味を持ち言語仕様を調べたりするのに、自然言語、例えば日本語についてはそんなに仕様を調べたりしないのは変だなとふと思った。

たぶん、間違いに気付きにくいことが大きな違いだと思った。プログラミング言語はコンピュータが解釈するから間違えているのが即座にわかるけれど、自然言語の場合、他人に読んでもらわないと間違いに気付かない。他人に読んでもらうというのはすごく敷居が高いしコストがかかる。そのうえ他人からの間違いの指摘というのは、コンピュータからの指摘と違って、大きく自尊心を傷付けられることがある。

さらに自然言語で特に母国語の場合、言語仕様について特に知らなくても運用できてしまう。なんとなくで使えて、なんとなく理解してもらえてしまうものだから、コンピュータ言語ほど厳密に書こうという動機が生まれない。

また、人間同士の場合、片方が理解しようとしている限り、コミュニケーションのコストをその片方に押し付けてしまうことができる。片方が伝える努力をしなくとも、片方が理解しようとする努力をしている限り、理解しようとしているほうに甘えてしまうことができる。コンピュータ相手であれば、コンピュータは理解しようという気がまるでないので、こちらが一方的に伝える努力をする必要がある。

人工言語であろうが自然言語であろうがコミュニケーション言語には変わりないので、もっと自然言語の仕様について敏感でもいいし、もっと伝えようと努めてもいいなと思った。

文章間の論理の繋りについての感覚がいまいちない。これは日記を書きはじめた当初からずっと感じていることで、なかなか改善しない。読みなおしてあれ?と気付ける範囲であればまだ改善ができるかもしれないけど、読みなおしてもわからないことがある。

「間違いがない」ことを証明できないと、間違いがあるかもしれないという不安からは解放されない。当然そんな証明はできっこないので、文章を書くときはいつも不安である。

Universal Block Protocol

2011年 09月 14日

自分の人生なので、自分で考えるしかない。

2011年 09月 15日

魔法少女まどか☆マギカ 5 【完全生産限定版】 [Blu-ray] - 悠木碧

悠木碧

3.0 / 5.0

買って見た。9話のコメンタリーはきゅうべぇの声の人がゲストなので面白かった。あとエンディング違うのに驚いた。

2011年 09月 14日

gerry++

2011年 09月 17日

今まで価値を感じていたものが失われたのであれば、他のものを手さぐりで探し求めるしかない。

どこに価値を感じていたか、今はどうなのかを、定期的に、最悪でも一年に一度程度は、考えなおす必要があるように思う。

仕事でやることが詰まってるときほど、さらに負荷をかけるようなことをしてしまう。ゲームや、普段は読まない本を読んだり、今までやってこなかったことをやったり、仕事自体でも細かい実装に凝ったり。

負荷の大部分は不安で構成されている。この不安を短期間に最大限まで呼びこむのは、短期的な不安に上限があるために理にかなっているようには思う。

詰めこんでいるときは行動力が強く発揮できるのは良いと思う。けど、波があるのはよくない。

2011年 09月 18日

2011年 09月 27日

全然更新してないが普通に生きている。この日記書いてない期間というのは、自分の場合

  • 何も考えてない
  • 考えているけど公開で書けない
  • 考えているふりしかしていない

のうちのどれかで、今回のこの期間については「考えているふりしかしていない」が当てはまると考える。

いろいろ不安やら考えごとはあるものの、日記に落とす前に寝てしまっている。しかし文章に起こせなかった考えはなかったも同然だ。「昨日はたくさん考えたな」と自分で思ってはいても、実際のところ頭の中は整理されておらず、ループする思考の波に身をまかせているだけになってしまっている。

文章に限らないけれど、何もアウトプットせずに「考えている」だけというのは「考えているふりしかしていない」ことと同じだ。

南禅寺 大寧軒

久しぶりに南禅寺に行ったら、いつもは拝観を受け付けていない大寧軒という庭園が見られるようになっていた。三柱鳥居があるということで、それに期待を持って入ってみた。

三柱鳥居は木嶋坐天照御魂神社にあるのを何度か見にいっていて、形がかっこいいので気に入ってる。京都だとその神社の元糺の森にしかないと思い込んでいたのだけれど、今回行った大寧軒にも確かに小さいものの一基あった。元糺の森は水が沸きでるところに立っているけど、こっちもそうらしい。

高校生か、大学生ぐらいのときは、プログラムを書くときの長期的な目標として「作りたいものを作れるようになる」というのがあったことを思い出した。

今それはある程度できるようになったけれど、ただできるようになったと言っても、自分のフィールドに強引にひっぱってくる力がついたり、他人の成果 (ライブラリなど) を探しだす能力が多少ついただけで、プログラミングそのもののスキルはそんなに向上していないように思える。

「プログラミングそのもののスキル」というのは「アルゴリズム」「設計」「実装」にわけられると思うけど (もっとあるかもしれない)、特にアルゴリズム関係の力はほとんど成長していない。設計力と実装力は今の会社に入ってから特に重点的に考えていることだから、多少できるようになっていると思うけれど (できるようになっていないと困る)、それもまだまだやっぱり経験の不足は否めない。

それはともかく「作りたいものを作れるようになる」は今も良い目標だと思っていて、これはちょうど、馬が目の前のにんじんを食べたくて走るように、自分がすすめばすすむほど「作りたいもの」は遠くにいって、ずっと成長の指標になる気がしている。目標を立てた当初、自分が「作りたいもの」はブログシステムだったけれど、これは思うように作れるようになってきている。今作りたいものは、たくさんの人がストレスなく楽しく使えるサービスであるので、また遠くに餌がいってしまっている。

「ちょっと思いついたから実装書いてみるわ」みたいなノリでさっさと作りあげてしまう、というのに憧れている。

2011年 09月 26日

OSX のマウスの加速度がムカつく

// #!gcc -framework Cocoa -framework IOKit mouse.c
// code from http://lists.apple.com/archives/usb/2004/aug/msg00057.html

#include <stdio.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/hidsystem/IOHIDLib.h>
#include <IOKit/hidsystem/IOHIDParameter.h>
#include <IOKit/hidsystem/event_status_driver.h>

int main () {
	NXEventHandle h = NXOpenEventStatus();
	if (!h) return 1;
	IOHIDSetAccelerationWithKey( h, CFSTR(kIOHIDMouseAccelerationType), -1.0);
	return 0;
}

を実行すれば一応加速はきれる。けど速度が固定になってしまう (遅い…)

defaults write .GlobalPreferences com.apple.mouse.scaling -1

ってやればいいだけかもしれないけど、こっちは再起動がいる。(上のコードだと即時で反映される)

2011年 09月 27日

gerry++

DBI で dsn パースしてオプションを一部奪いとる

dsn ごとに接続オプションを独自で追加したいみたいなとき、別途設定を増やしたりするのが嫌なので、dsn の中の仕組みを利用したい。

Plack::Session::Store::MySQL

Plack::Session::Store::DBI というのがあって、最初とりあえずそれを使っていたのだけれど、もうちょいクエリの数を減らしておきたいと思い、以下のようなものを書いた。実際のところセッションのストレージに MySQL を使うべきかはよくわからないのですが…

変更点として

  • セッションの内容が不変だったら UPDATE を発行しない
    • ただしシリアライザがハッシュに関しても常に同じシリアライズ結果を返す必要がある (Data::MessagePack や JSON の場合 canonical オプションがあるので有効にする)
  • セッションキーの存在を確認せず、ON DUPLICATE KEY UPDATE する (アトミックになる + クエリの数が減る)
package Plack::Session::Store::MySQL;
use utf8;
use strict;
use warnings;
use parent qw(Plack::Session::Store::DBI);

sub fetch {
    my ($self, $session_id) = @_;
    my $table_name = $self->{table_name};

    my $sth = $self->_dbh->prepare_cached("SELECT session_data FROM $table_name WHERE id = ?");
    $sth->execute( $session_id );

    my ($data) = $sth->fetchrow_array;

    $self->{$session_id}->{_prev_data} = $data;

    $sth->finish;

    $data ? $self->deserializer->( $data ) : ();
}

sub store {
    my ($self, $session_id, $session) = @_;
    my $table_name = $self->{table_name};

    my $data = $self->serializer->($session);

    if ($self->{$session_id}->{_prev_data} ne $data) {
        my $sth = $self->_dbh->prepare_cached("INSERT INTO $table_name SET id = ?, session_data = ? ON DUPLICATE KEY UPDATE session_data=VALUES(session_data)");
        $sth->execute($session_id, $data);
    }
}

1;
__END__

Sample schema:

    CREATE TABLE session (
        `id`           CHAR(72) NOT NULL,
        `session_data` BLOB,
        `modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id)
    ) ENGINE=InnoDB;
use Plack::Builder;
use Data::MessagePack;
use Plack::Session::State::Cookie;
use Plack::Session::Store::MySQL;

my $MessagePack = Data::MessagePack->new();
$MessagePack->canonical;

builder {
    enable
        "Plack::Middleware::Session",
        state => Plack::Session::State::Cookie->new(),
        store => Plack::Session::Store::MySQL->new(
            get_dbh      => sub { DBI->connect_cached(config->param('dsn_session'), 'foo', 'bar') },
            table_name   => 'session',
            serializer   => sub { $MessagePack->pack(+shift) },
            deserializer => sub { eval { $MessagePack->unpack(+shift) } || +{} },
        );
        
        $app
};

シリアライザ

Storable

  • 常に同じシリアライズ結果にできる ($Storable::canonical)
  • 可読性なし
  • データサイズ大
  • データ形式が Perl 限定 (Ruby でもパースできるという噂が)
  • bless 状態を保持できる

Data::Dumper

  • 常に同じシリアライズ結果にできる ($Data::Dumper::Sortkeys = 1)
  • 可読性あり
  • データ形式が Perl 限定
  • bless 状態を保持できる

JSON::XS

  • 常に同じシリアライズ結果にできる ($json->canonical(1))
  • 可読性あり
  • バイト列を保存できない
    • JSON の仕様上文字列しかないのでバイト列を保存したければ Base64 したりしなくてはいけなくて使いにくい
  • Storable よりは早い

Data::MessagePack

  • 常に同じシリアライズ結果にできる ($mp->canonical(1))
  • データサイズ小
  • バイナリ
  • 可読性ほぼなし (Storable と違って文字列は読める)
  • 早い
2011年 09月 28日

VPS に移設するものなどのメモ

自宅サーバをやめたい。

  • irssi
  • tiarra
  • irssw
    • perl + Plack のセットが必要
  • irc gateway 系
    • ruby1.8 と ruby1.9 系のいろいろが必要

使っているけど移設しない/できないの

  • OpenFL → やめて LDR に戻った。あんまり困らない
  • daap → 移設できないので無視する。必要な音楽ファイルは別途持ち歩く

時代を遡ってる気がする……

2011年 09月 29日

2011年 09月 28日

さくら VPS セットアップメモ

  • カスタムOSインストールで Ubuntu 10.04 amd64 をインストール
    • Chrome だと Java が起動しないので Safari で
    • 手順に従って情報を入力していく
    • Ubuntu の場合 root ログインはデフォルトで無効で安心
    • インストール中にユーザをつくるのでつくる
  • DNS を設定しておき、コンソールから逆引き設定をかえておく。
$ ssh hostname
$ sudo vi /etc/hostname
# かえたホスト名に
$ mkdir .ssh
$ chmod 0700 .ssh
$ ssh-keygen -t dsa
$ exit
# ログインしたいマシンで
$ cat ~/.ssh/id_dsa.pub | ssh hostname "cat >> ~/.ssh/authorized_keys"
$ ssh hostname # パスワードを訊かれないことを確認 (公開鍵認証でログインしたことを確認)
$ sudo vi /etc/ssh/sshd_config
PasswordAuthentication no
PermitRootLogin no
UsePAM no
$ sudo restart ssh
# 別のターミナルを開いて ssh できることを再度確認
$ sudo reboot # ホスト名反映のため一旦リブート
$ sudo apt-get install language-pack-ja # warning: setlocale: LC_ALL: cannot change locale (ja_JP.UTF-8) とかでるので……
$ sudo ufw default deny
$ sudo ufw allow 22
$ sudo ufw allow 80
$ sudo ufw allow 60000:61000/udp 
$ sudo ufw enable
$ sudo ufw status

すぐやるべきことはやったので作業環境をつくる

$ sudo apt-get install zsh
$ chsh -s /bin/zsh
$ sudo apt-get install screen
$ sudo apt-get install ruby
$ sudo apt-get install git-core
$ git clone git@github.com:cho45/dotfiles.git # github につくった id_dsa.pub を登録しておく
$ cd dotfiles
$ ruby setup.rb
$ exec zsh
$ screen -S main

$ sudo apt-get install gcc
$ sudo apt-get install libncurses5-dev

$ mkdir tmp
$ cd tmp
$ wget ftp://ftp.vim.org/pub/vim/unix/vim-7.3.tar.bz2
$ tar xjvf vim-7.3.tar.bz2
$ cd vim73
$ ./configure --prefix=/usr/local/vim7 --enable-multibyte --enable-gpm --enable-cscope --with-features=huge --enable-fontset --disable-gui --without-x --disable-xim --disable-perlinterp
$ make -j 2
$ sudo make install

tiarra を設定する

$ sudo apt-get install daemontools
$ sudo apt-get install daemontools-run
$ sudo ln -s /etc/service /service
$ cd ~/bin
$ cd
$ mkdir app
$ cd app
$ wget http://clovery.jp/tiarra/archive/2010/02/tiarra-20100212.tar.gz
$ tar xzvf tiarra-20100212.tar.gz
$ ln -s ~/app/tiarra-20100212/tiarra ~/bin
$ sudo mkdir -p /var/log/tiarra/{cho45,chokan}
$ sudo chown cho45 /var/log/tiarra/{cho45,chokan}   
$ sudo ln -s ~/dotfiles/services/tiarra /service/
2011年 09月 30日