l'essentiel est invisible pour les yeux

Wednesday, November 29, 2006

エノルメと14年ぶりに再会

14年ぶりぐらいにこの電話と再会した。「発想する会社」中に大失敗したがとても楽しかったプロジェクトとして紹介されていたエノルメという電話である。

14年ほど前に父のオフィスでこの電話を見た。
本中の写真を見た瞬間に「あ、あれだ!」と10年以上前の記憶が鮮明によみがえったため、尋ねたところ押入れにあるという。

そんなわけで、エノルメが今朝押入れから出てきた。
写真を見るとコードが無くコードレスのようだが、実際は太くて長いらせん状のコードがついている。手に取ると重いし、留守番電話機能も無いらしい。値段も当時4万円ほどしたとか。しかし、そんなデメリットを抑えても、そのデザインは今見ても綺麗だと思える。(売れるかどうかは別問題)

調べたところ、エノルメはイタリア人デザイナーのエットーレ・ソットサスが作った電話のようだ。


優れたデザインがヨーロッパに集中するのも、ルネサンスの時代から続くデザインの歴史があるからだと聞く。ものつくりをする一人として、優れたデザインは時代を超えて愛されることに少し感動した。でもデザインとは歴史の積み重ねなのだろうな。ヨーロッパがデザインの中心地であるのも、ルネサンスの時代から続くデザインの歴史があるからなのだろう。
この電話は今見ても綺麗だ。

そして押入れから他にもいろいろと面白いプロダクトデザインを持った製品がでてきた。
ものつくりにソウルをこめている人たちの、製品を見ているとクリエイティビティを刺激された。

この時計は、倉俣史郎というデザイナの時計で昔から家に置かれていた。父が気に入っている時計のようだが、今壊れていて、新宿のActusまで持っていかないと修理できないとか。

黒色の2本の短針と長針。
赤色の1本の秒針。

文字盤はない。
正確な時間を知ることは出来ないが、この時計を部屋置く人は正確な時間を知ることを求めていないだろうな。






こちらも時計。
開封して動作させるのは面倒だったため、パッケージのみ。目を離せない展開の連続とパッケージに書かれている。

動作イメージはこんな感じ。

写真









こちらはダイヤル式の電話。飛行機のプロペラの形をしている。
昔家に置かれていた記憶はあるが、実際にこの電話を使った記憶はない。当時(80年代前半?)はNTTが家に置く電話を規制していたというから驚く。

こんな変な電話を置くことは許されていなかったらしく、NTTの電話を置くように強制していたようだ。だから、こそこそと使っていたとか。







こちらは壁掛けカレンダー。
上のパターンはいくつものパターンを重ねあわせて表現されており、日によってパターンを代えることで無限代のパターンを作ることが出来る。結構面白い。

「ニューヨーク近代美術館に展示されています」と英語で書かれている。










こちらも電話機。
Panasonic製であるがデザインが独特。昔父のオフィスにエノルメの電話機とこの電話機が置かれていた。

こちらも、値段が高くほとんど売れなかったらしい。











綺麗なデザインを持ったものが必ずしもヒットすることは無いし、むしろそのこだわりが悪く作用することもある。でも、これらは使っているだけで楽しくなるような製品だと思う。一ものつくりの人間としては、ソウルのあるものつくりをすることに魂を注ぎ込みたいし、それを感じることのできる感性を磨く必要性を凄く感じる。

PS
12月12日のデジタル・テレビの新たなる挑戦にいけることになったので、そのときにSteelecase Life/Work Center in Tokyoを見に行こう。楽しそう。

次の二冊の本が読みたかったので、大学の図書館を調べたがどちらも置いていなかった。
オライリーの本は大量にそろっているのに。上の本とかAPUにあってしかも館内利用限定。

経験デザインとタトゥー

AM5時30分 。どこかの誰かみたいに「寝るのはあの世に行ってからにします。」なんて気にはならない。

今関心を持っているテーマのひとつが「経験デザイン」。
経験デザインとは何だろう?それはスターバックスのヒットにも見られるし、一連のレクサスブランドにも見られるかもしれない。

多くのユーザは、よりいい経験デザインに惹きつけられるが、それの何がいいかをうまく語ることができない。また逆に現在使っているものが洗練されていなくても、それの不満をうまく表現することはできない。

今の経験をリデザインすることや全く新しい経験デザインを創造することが、イノベーションのヒントにつながるかもしれない。

より経験デザインを提供するには、良い経験デザインを体験し感じることだと思う。それには、リッツカールトンのスイートに泊まってサービスを受けるのがよいかもしれないし、レクサスに乗るのがいいかもしれないし、スターバックスが売り出す商品がなぜうれるのか考えてみるのもいいと思う。

そして、よい経験デザインに共通することがあるとすれば、そこに考え抜かれた物語があることだとおもう。
AppleのiPodにしてもスターバックスのヒットにしてもレクサスブランドにしても。こんなの作ったら面白いからというレベルの話でなく、真にユーザを満足させる物語があると思う。その物語は人やインターネットを通じてどんどんと広まっていく。

経験をデザインすることの出来る場所は、普段のライフスタイルの中に多く潜んでいる。
敏感にならないといけないな。


キーワードを入れてそのタトゥーに関する画像が何件ヒットするかやってみた。
体に刺繍を入れるにはそのデザインがカッコよかったり、それ自身を愛している証だろうと思ったから。

Image: harley-davidson tatoo 62件
Image: apple tatoo 74件
Image: microsoft tatoo 7件
Image: Debian tatoo 17件
Image: Redhat tatoo 0件

ノイズが多いので実際の結果は上の数値より少なくなる。Microsoftは実質0件。
ハーレーのタトゥーがカッコイイと思われていたり、Debianはあるけど、FedoraやRedhat関係は無いといったことやAppleはあるがMSはないといった結果はなかなか面白い。

Tuesday, November 28, 2006

[Cell] SPE上での実行時間計測

SPE上で実行時間を計測するには、SPU Decrementerを利用する。
SPU Decrementerは参考資料のPDFに次のように説明されている。


A register that counts down each time an event occurs. Each SPU
contains dedicated 32-bit decrementers for scheduling or performance
monitoring, by the program or by the SPU itself.


プロファイリングのために利用できる32bitレジスタで一定周期で値が減少していく。この差を取ることで実行時間の計測が出来る。

PPUのTime Base Registerと同じように周期を知るには、/proc/cpuinfoを見る。

% cat /proc/cpuinfo | grep -w timebase
timebase: 25000000
%

SPU Decrementerもこの値が周期の基準になるのか少し疑問だが、fixstartsのWikiでもこの値を使っているようなので。

SPUチャネルにSPU_WrDec, SPU_RdDecというニーモニックを指定し書き込む or 読み込むことでレジスタの値が取得できる。

uint32_t t;
spu_writech(SPU_WrDec, 0xffffffff); // 0xffffffffを書き込み
t = spu_readch(SPU_RdDec); // SPU Decrementerの値を読み込む。€‚


この低レベルAPIをラップした関数として、spu_write_decrementer(uint32_t), spu_read_decrementer(void)という関数がspu_mfcio.hで提供されている。

uint32_t t;
spu_write_decrementer(0xffffffff);
t = spu_read_decrementer();


この関数を利用したプロファイリング用のマクロを定義する。

static const int TIMEBASE = 2.5 * 1.0e7; // cat /proc/cpuinfo | grep -w timebase

// プロファイル用関数ƒ­
#define StartTimer(ts) {spu_write_decrementer(0xffffffff); ts=spu_read_decrementer();}
#define StopTimer(te) {te -= spu_read_decrementer();}
#define PrintTimer(te) {printf("timer: %f(sec)\n", te / (float)TIMEBASE * 1.0e3);}


初期値としてSPU Decrementerに大きな値を設定しておく。StopTimerでStartTimer時に取得した値との差を計算する。PrintTimerでレジスタの減少値から実行時間を算出する。
これを利用してスカラー演算とベクタ演算の実行速度を比較する。

ソースコード

#include <stdio.h>
#include <stdint.h>
#include <spu_intrinsics.h>
#include <unistd.h>
#include <spu_mfcio.h> // spu_read_decrementer and spu_write_decrementer

static const int N = 200000;
static const int TIMEBASE = 2.5 * 1.0e7; // cat /proc/cpuinfo | grep -w timebase

// プロファイリング用マクロ
#define StartTimer(ts) {spu_write_decrementer(0xffffffff); \
ts=spu_read_decrementer();}
#define StopTimer(te) {te -= spu_read_decrementer();}
#define PrintTimer(te) {printf("timer: %f(msec)\n", te / (float)TIMEBASE * 1.0e3);}

int main(unsigned long long spu_id, unsigned long long arg) {
uint32_t profile, profile_simd, i;
uint32_t
in[4] __attribute__((aligned(16))) = {1,2,3,4};
uint32_t
out[4] __attribute__((aligned(16))) = {0};
vec_int4 *v_in = (vec_int4 *) in;
vec_int4 *v_out = (vec_int4 *) out;

// スカラー値で計算
StartTimer(profile);
for(i=0;i<N;i++) {
out[0] += in[0];
out[1] += in[1];
out[2] += in[2];
out[3] += in[3];
}
StopTimer(profile);

// SIMDでベクタ演算
StartTimer(profile_simd)
for(i=0;i<N;i++) {
spu_add(*v_out, *v_in);
}
StopTimer(profile_simd);

// 出力
printf("Scalar: ");
PrintTimer(profile);

printf("Vector: ");
PrintTimer(profile_simd);

return 0;
}

コンパイルとシミュレータへの転送用に/tmpへコピー

% make
spu-gcc -Wall -I/opt/IBM/cell-sdk-1.1/src/include/spu/ -I/opt/IBM/cell-sdk-1.1/src/include/ -I/opt/IBM/cell-sdk-1.1/sysroot/usr/lib/gcc/spu/4.0.2/ 0.c -c
spu-gcc -L/opt/IBM/cell-sdk-1.1/sysroot/usr/lib/ 0.o -o spu.out
% make install
cp spu.out ../ppu-main/ppu.out /tmp/
%
SPUプログラムを動かすためのPPUプログラムを作る。
#include <stdio.h>
#include <libspe.h>

static const char SPE_FILE_NAME[] = "./spu.out";
int main() {
int status;
spe_program_handle_t *spe_handle;
speid_t spe_id;

printf("[PPE] Open SPE program.\n");
spe_handle = spe_open_image(SPE_FILE_NAME);
if(spe_handle == 0) {
printf("ERROR: Cannot open SPE program.\n");
return 1;
}

printf("[PPE] Create SPE thread.\n");
spe_id = spe_create_thread(0, spe_handle, NULL, NULL, -1, 0);
if(spe_id == 0) {
printf("ERROR: Cannot crate SPE thread.\n");
return 1;
}

printf("[PPE] Waiting SPE thread...");
spe_wait(spe_id, &status, 0);
printf("done.\n");

printf("[PPE] Release SPE program.\n");
spe_close_image(spe_handle);

return 0;
}


コンパイルと転送
% make                                                                                                   
ppu-gcc -m32 -Wall -I/opt/IBM/cell-sdk-1.1/sysroot/usr/include 1.c -c
ppu-gcc -L/opt/IBM/cell-sdk-1.1/sysroot/usr/lib 1.o -lspe -o ppu.out -m32
% make install
cp ../spu-main/spu.out ppu.out /tmp/
%


シミュレータ上に転送し実行
# callthrue source /tmp/ppu.out > ppu.out
# callthrue source /tmp/spu.out > spu.out
# chmod +x ppu.out spu.out
# ./ppu.out
[PPE] Open SPE program.
[PPE] Create SPE thread.
Scalar: timer: 2.812480(sec)
Vector: timer: 0.687480(sec)
[PPE] Waitin SPE thread...done.
[PPE] Release SPE program.
#
追記
SPUの実行形式オブジェクトファイル単体でも実行可能。

SIMDを利用したベクタ演算の方が4倍ほど早くなっています。
SIMDでは同時に4データを扱えるので妥当なところかな。
参考
Cell Broadband Engine Programming Tutorial Version 1.1 (PDF)
lesson12 - Pukiwiki

[Cell] PPE上での実行時間計測

CellはPowerPC G5互換の汎用プロセッサPPE一つと浮動小数点演算に優れたSIMDアーキテクチャのSPE八つを持つPS3にも搭載されている注目のCPU。こいつが家電に載って、ブロードバンド時代のライフスタイルのために何を作れるだろうと今から考えるだけでもテンションが上がる。

PPEもSPEもハードウェアレベルの分岐予測を持たないなど、シンプルな構造を採用し高クロックを実現している。逆に言うと、ソフトウェアレベルでプログラマ自身がアーキテクチャを理解した上でパフォーマンスを最大限に引き出すようにプログラムを書く必要がある。

さらに、スキルも要求されるとあってますます楽しい。

どれぐらい少ない時間でどれだけ多くの命令が実行できているかを知るプロファイリングのテクニックはCellプログラミングでも非常に重要。

Cellプロセッサ上では、PPEとSPU上で実行時間の計測方法が異なる。
ここでは、PPE上での実行時間の取得について説明します。

PPE上での実行時間の取得には、C言語標準のgettimeofday関数を使用する方法とPowerPC上のTime Base Register(TBR)を使用する方法がある。TBRを使用する方法の方がOS上の他のプロセスの影響を受けにくくより正確な値が取得できるというメリットがある。

Time Base Registerは、64bitのサイズを持ち一定の周期でその値が増加していくレジスタである。参考資料によるとカウンタがインクリメントされる周期はCPUの実装依存のようですが、1クロック毎に1増加すると考えてよさそうだ。

PPE(PowerPC上)で実行時間を測定するマクロとTime Base Registerの値を読み込む関数mftbを定義する。Cell実機が無いため、IBMが提供するCell Broadband Engineをx86の32bit CPU上で実行する。64bitレジスタの値を一度に読み取ることができないため、Time Base Upper(上位32bit)とTime Base Lower(下位32bit)を別々に読み込む。この時に、下位32bitの桁上がりを考慮し、TBUの値を2度読み込み以前に読み込んだ値と異なるかどうか比べる。異なる際は、もう一度上位32bitを読み込む。


// TBLの桁上がりを考慮したTBRの読み出し—
static inline unsigned long long mftb(void) {
register uint32_t tbu, tbl, tmp;

__asm__ __volatile__ (
"loop:\n\t"
"mftbu %0\n\t" /* Time Base Upper 32bitを取得*/
"mftb %1\n\t" /* Time Base Lower 32bitを取得*/
"mftbu %2\n\t" /* 再びTime Base Upper 32bitを取得*/
"cmpw %0, %2\n\t" /* TBLの桁上がりが無いかどうか確認する */
"bne loop" /* 桁上がりがあればもう一度TBUを取得しなおす */
: "=r"(tbu), "=r"(tbl), "=r"(tmp)
: /* nope */
: "cc");

return (((uint64_t) tbu) << 32) + tbl;
}


PowerPCのアセンブリ命令については参考資料の一つ目が参考になる。

実行時間を求めるにはTimbaseの周期を知る必要がある。
これは/proc/cpuinfoで確認できる。(周期は環境により異なる)

# cat /proc/cpuinfo | grep -w timebase
timebase : 25000000
#


周期とTime Base Regiterの増加量を利用すれば実行時間を求めることが出来る。

実行時間(msec) = TBR増加量 / 周期 × 1.0e3


プロファイル用マクロの全ソース
TIMEBASEの値は環境に応じて上記で調べた値に変更する必要あり。

#include <stdio.h>
#include <stdint.h>
#include <libspe.h>
#include <unistd.h>

static const int N = 3;
static const int TIMEBASE = 2.5 * 1.0e7; // cat /proc/cpuinfo | grep timebase

// TBLの桁上がりを考慮したTBRの読み出し—
static inline unsigned long long mftb(void) {
register uint32_t tbu, tbl, tmp;

__asm__ __volatile__ (
"loop:\n\t"
"mftbu %0\n\t" /* Time Base Upper 32bitを取得*/
"mftb %1\n\t" /* Time Base Lower 32bitを取得*/
"mftbu %2\n\t" /* 再びTime Base Upper 32bitを取得*/
"cmpw %0, %2\n\t" /* TBLの桁上がりが無いかどうか確認する */
"bne loop" /* 桁上がりがあればもう一度TBUを取得しなおす */
: "=r"(tbu), "=r"(tbl), "=r"(tmp)
: /* nope */
: "cc");

return (((uint64_t) tbu) << 32) + tbl;
}

#define StartTimer(ts) {ts=mftb();}
#define StopTimer(te, ts) {uint64_t t=mftb();te=t-ts;}
#define PrintTimer(te) {printf("time: %f(msec)\n", te/TIMEBASE * 1.0e3);}

int main() {
int i;
uint64_t ts, te;

StartTimer(ts);
for(i=0;i<N;i++) {
sleep(1);
puts("looping..");
}
StopTimer(te, ts);

// 実行時間の出力Š›
PrintfTimer(te);
return 0;
}


コンパイルとシュミレータにコピーするために/tmpへコピー

% make
ppu-gcc -m32 -Wall -I/opt/IBM/cell-sdk-1.1/sysroot/usr/include 0.c -c
ppu-gcc -L/opt/IBM/cell-sdk-1.1/sysroot/usr/lib 0.o -lspe -o ppu.out -m32
% make install
cp ../spu-main/spu.out ppu.out /tmp/
%


シュミレータ上に転送し実行

# cellthru source /tmp/ppu.out > ppu.out
# chmod +x ppu.out
# ./ppu.out
looping..
looping..
looping..
time: 3000.000000(msec)
#


とりあえずPS3かわないとな。


参考文献
32ビット PowerPCアーキテクチャ プログラミング環境 (PDF)
The Programming Environments for 32-Bit Microprocessors(原文)
[Mac] PowerPC でもクロックカウント
tips_timebase - PukiWiki

全てのサービスの背景には人間がいる

ただいまAM4時前。 前作のイノベーションの達人が面白かったため同じ作者の前作である「発想する会社」を大学の図書館で借りてきて読んだ。

遊び心が溢れる楽しい会社だということが本からも伝わる。一度IDEOのオフィスに行ってみたい。

どちらの本も、何事も自由を尊重する会社がブレインストーミングに関してはきっちりとした取り決めをしていることや、ホットグループといわれるチームワーク・クリエイティビティの発揮できる仕事環境・ユーザエクスペリエンスの徹底した観察・アジャイルなプロトタイプ作成といった盛りだくさんの内容である。



失敗から得る経験

今年はたくさんのプロトタイプ作成を通じて色々な経験をしたが、その中で以前に作った社内用のお弁当注文システムから得たユーザエクスペリエンスは、本の内容とも重ね合わせることが出来る。

少し前、社内では、毎日のランチが悩みの種だった。
近くにおいしい定食屋が少なかったりすぐ近くの弁当屋にみんな飽き飽きしていた。「そんな時に代々木の大勝軒うまいよ!」って話が社内で出て、弁当を注文することになった。そのお弁当は単品注文と4品から好きなメニューを番号で選んで手書きの内容を毎回FAXするという面倒なフローになっていた。

そんなフローを毎日AM11時ごろになると全員に声をかけてメニューを聞いて回る、弁当大臣がうけもっていたのだが、「社内用弁当注文システム欲しいなぁ」という声が出たので、その日のうちに作ることにした。

システムはお弁当のメニューの画像が表示され日替わりなら、4項目選んでクリックするだけで注文ボタンを押すだけで注文が完了する。と、言ってもその内容を弁当大臣(のみ印刷ボタンが表示される)がプリント用ページから印刷して大勝軒にFAXすることで注文が完了する。

このシステムは1週間ほど使われたが、その後結局使われなくなった。原因は二つあると思っていて、
一つ目は、大勝軒のお弁当のボリュームが多くて毎日食べると太るしキツイ。
二つ目の理由が興味深い。
その理由とは、ワンクリックで注文できるシステムがあるのに、お弁当大臣がみんなに聞いて回ったことだ。そして、結局元通りのお昼の出前メニューがあるところにみんなが集まり、しゃべりながら注文を書き込むというスタイルに戻った。

この経験は、「お昼何たべようか?」といった話をすることの楽しみが重要であることを教えてくれた。もし次にお弁当注文システムを設計&実装するならば、注文ではなく(お昼までに)投票のような仕組みにして、エスプレッソマシーンでも置いたみんなが集まるくつろぎ場所に、数人で操作可能なタッチパネル式のモニタを用意する。そこから今日のランチの投票数や地図情報などが見れるといったものにするだろう。

「お昼何食べる?」といったコミュニケーションが楽しいからであって、ユーザはワンクリックで注文できることをたいして求めていなかったのだ。


お菓子箱

京都ドリコムにはお菓子箱があった。
会社の中央の場所に配置された、お菓子箱にはみんなが自分の好きなお菓子を次々と補給していく。「誰々はこのお菓子が好き」といった情報をみんなが知っていたりする。このお菓子箱は、そこにコミュニケーションを生みただのお菓子以上の価値をもっていたと思う。

もしここにIDEO的な発想が加われば、そのお菓子箱で人気のお菓子のランキングや、または社員が書き込む「こんなお菓子あったらなぁ・・・」という内容をブログで公開
(ブログ企業なので)しようとなるかもしれない。会社のカルチャーを表現し風通しをよくするとともに、うまくいけばそこからヒット商品のアイデアが生まれるかもしれない。

UIEJのお菓子箱は、まだまだ改善の余地がある。
会社の隅っこ(しかもトイレの隣)に置かれてしまっていてお菓子があまり減らない気がする。自分がキットカットばっかり食べているのはバレていたが、他の人が何のお菓子が好きかは知らない。


オープン・オフィス
自分の周りの環境を楽しくする。これって大事だよな。
自分のデスクやイスといった部分には改善の余地がいくらでもある。思いついたアイデアを紙に書き留めるが、その紙がバラバラになってしまいなくなるといったことがよくある。じゃあ、壁にホワイトボードフィルムを貼ろう。本は山積みになっている。カッコよく並べれないだろうか?といったことや、スワイプボードを壁に貼り付けてもよさそうだ。遊び心をくすぐるような環境を作って試すのに一番良い場所がやっぱり(長時間の時間を過ごすことになる)仕事場だろうな。


UIEJはオフィスの舞台装置設計はまだまだ改善の余地がたくさんある。
コミュニケーションが活発に行える場所はないし、席はホットグループごとに集まっているわけでもない。定期購読雑誌が一目でわかるマガジンラックのようなものもない。これからだ。

観察力
IDEOの本で一番感動したのが観察力であり、なぜ?なんで?といった経験を必ずより良いユーザエクスペリエンスとして提供できないか?と考えるところである。それが医療でありMacのマウスであり、セレブ向けの自家用ジェットの機内リモコンであり。

写真は、机の上につまれた本。LRUって感じで本棚と机の上を行き来する。
本の中の「あとでつかうてきなコメント」にはポストイットをはってるのだが、そういったデータもバーコードで管理できて(本に貼り付けるとか)、PCから本棚の検索もできるとかもっと楽しくできるだろうな。

積まれている本は、上から

  1. 発想する会社
  2. BinaryHacks
  3. UNIQLO PAPER
  4. Cell Broadband Engineのアーキテクチャに関する技術仕様書
  5. Linkers&Loaders
  6. イノベーションの達人
と続く。
最近、雑誌は立ち読みが多い。

Tuesday, November 21, 2006

UNIQLO PAPER N゜1


UNIQLOが発行するフリーペーパーUNIQLO PAPER N゜1を手に入れた。 さすがに力が入ってます。とてもフリーペーパーとは思えないし、デザインカタログとして販売していそうです。

左端は刺繍でとめられている。
中央には小学校のときに立体工作?で使った方眼紙のようなデザイン上にUNIQLO&ユニクロのロゴが配置されている。"紙"という媒体が持つ面白さを最大限利用している

紙の質だろうが写真などはそれほど鮮明でないが、それがかえって紙ってすばらしいなと思わしてくれる。

当分、全てが電子ペーパとその上で動くペーパ風リーダーに置き換わることはなさそうだ。


なんと、TOPページだけでも、UNIQLOという文字9つもある。
UNIQLO・ユニクロブランドをNYで受け入れてもらいたい。そんな日本のトップクリエイターの挑戦心あふれる気持ちがびしびしと伝わる。音楽もファッションもデザインもアートもWEBも世界に通じる。それを証明しようとしている。


中身は、

UNIQL・O

という五文字でコンテンツが区切られている。

Lのページではさまざまな動物のかわいらしいぬいぐるみがマフラーをまいているかと思えば、
OのページではMEN'S NON-NOの表紙を飾るような写真が続く。

日本発で大きなイノベーションを仕掛けたい。
そんな気持ちをところどころに強く感じる。Uのページ中には、「I Love New York」・「ODE TO TOKYO」という見開き2ページが広がるが、白地に赤丸と赤色のハートマーク。
「白に赤色の日の丸」。日本国旗をデザイン化しました。そんな印象。

現地のお店で流しているBGMも日本のクラブミュージックを流しているみたい。
日本代表のコンピュレーションアルバムを手がけたのは、FPMの田中氏。i-depやRIPSLYME×FPM, HALFBYなど普段良く聞いているアーティストの曲も入っています。
FPMといいHALFBYといい京都アツいなぁ。

これは知らなかったのだが、WEBサイトはYugopこと中村勇吾氏が手がけている。
僕は元々Yugop氏のことは知らなかったのだが、今年PMに教えてもらって色々なFlashの作品を見た。
前の記事でも少し書いたけど、UNIQLOのサイトは楽しみながら服を見ることが出来るユーザインタフェース。サイト内で遊んでいるとゆうに30分はたってしまう。

UNIQLOが出す安価な服にそれほど興味はなかったが、
Tシャツが天高く積み上げられるUNIQLO Soho NY店は一度言ってみたいし、Tシャツは買いたいって思った。

この"ユニクロ"ロゴだけは、どうもミスマッチな感じしてならないけど、
"ユニクロ"というブランドを世界で成功させる挑戦なのだろう。

UNIQLOのNY展開カッコイイと思った。

ページの最後にはショッピングリストのメモ用紙とユニクロ紙幣がついている。
ちなみに$15クーポン。NY店しか使えません。



Sunday, November 19, 2006

普通のやつらの上を行けと普通のやつらの下を行け

BinaryHacksの第二章・第四章を勉強しおえた。
マシンの目線でプログラムを少しだけ見れるようになった。

普通のやつらの上を行けは、ポールグラハムの言葉。
普通のやつらの下を行けは、Write Grate Codeのあとがきで鵜飼さんが書かれていた言葉。
(ネタ元はわかりません)

「普通のやつらの下を行ったうえで、普通のやつらの上を行く」

UIEでは中島さんのようなマシン語でプログラムを書かれていたバイナリアンな方から、勉強のために社内ブログに書いている内容にたくさんのツッコミをもらえる。

Wednesday, November 15, 2006

[binary] Hack #5 ELFフォーマット

via Binary Hack #5 ELF入門



バイナリの勉強。一日目は、ELF入門からやった。
はじめてのバイナリ入門なため間違いがあるかもしれません。

ELF とは実行可能バイナリやオブジェクトファイルなどのフォーマットを規定したフォーマットである。

ELFは先頭4バイトが


0x7F 0x45 0x4C 0x46

のようなマジックナンバーを持つ。

また、fileコマンドで確認できる。

[4296]% file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.0, dynamically linked (uses shared libs), for GNU/Linux 2.2.0, stripped
[4297]% file /bin/cat
/bin/cat: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.0, dynamically linked (uses shared libs), for GNU/Linux 2.2.0, stripped
[4298]%


ELFは4つの部分からなる。
ELFヘッダ
プログラムヘッダテーブル
複数のセクション
セクションヘッダテーブル

プログラムヘッダは、次のコマンドで調べる。

[4298]% readelf -h /bin/ls
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8049a50
Start of program headers: 52 (bytes into file)
Start of section headers: 74948 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 8
Size of section headers: 40 (bytes)
Number of section headers: 25
Section header string table index: 24
[4299]%


ELFヘッダからプログラムヘッダの開始位置やプログラムヘッダ数・セッションヘッダ数といった情報を知ることが出来る。

odコマンドでプログラムヘッダをダンプする。

[4301]% od -N 34 -t x1z /bin/ls
0000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 >.ELF............<
0000020 02 00 03 00 01 00 00 00 50 9a 04 08 34 00 00 00 >........P...4...<
0000040 c4 24 >.$<
0000042
[4302]%


それぞれの値は、

typedef struct {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
ElfN_Addr e_entry;
ElfN_Off e_phoff;
ElfN_Off e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
}

に対応している。

プログラムヘッダには、実行開始時にメモリにロードされるべきデータが含まれます。プログラムコードや初期化済みグローバル変数の領域などが含まれる。(参考より)
プログラムヘッダを出力するには、readelfコマンドが使用できる。

[4300]% readelf -l /bin/ls


シンボルテーブルを表示

[4307]% readelf -s /bin/ls | head -10

Symbol table '.dynsym' contains 107 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 08049484 60 FUNC GLOBAL DEFAULT UND readlink@GLIBC_2.0 (2)
2: 08049494 283 FUNC GLOBAL DEFAULT UND getgrnam@GLIBC_2.0 (2)
3: 080494a4 42 FUNC GLOBAL DEFAULT UND __fpending@GLIBC_2.2 (3)
4: 080494b4 58 FUNC GLOBAL DEFAULT UND acl_entries@ACL_1.0 (4)
5: 080494c4 83 FUNC GLOBAL DEFAULT UND sigaction@GLIBC_2.0 (2)
6: 080494d4 239 FUNC GLOBAL DEFAULT UND readdir64@GLIBC_2.2 (3)


シンボルテーブルを実際にダンプさせてみて上の表示と一致するか確かめる。

[4310]% readelf -S /bin/ls | grep DYNSYM
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 4] .dynsym DYNSYM 080484a0 0004a0 0006b0 10 A 5 1 4


先頭から0x4a0の位置から0x6b0バイト表示する。

[4313]% od -j 0x4a0 -N 0x6b0 -t x1z /bin/ls | head -10 [/home/furutani]
0002240 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................<
0002260 6c 01 00 00 84 94 04 08 3c 00 00 00 12 00 00 00 >l.......<.......<
0002300 7d 03 00 00 94 94 04 08 1b 01 00 00 12 00 00 00 >}...............<
0002320 3b 01 00 00 a4 94 04 08 2a 00 00 00 12 00 00 00 >;.......*.......<
0002340 49 00 00 00 b4 94 04 08 3a 00 00 00 12 00 00 00 >I.......:.......<
0002360 9a 02 00 00 c4 94 04 08 53 00 00 00 12 00 00 00 >........S.......<
0002400 fd 00 00 00 d4 94 04 08 ef 00 00 00 12 00 00 00 >................<
0002420 dc 03 00 00 e4 94 04 08 67 01 00 00 12 00 00 00 >........g.......<
0002440 75 01 00 00 f4 94 04 08 67 00 00 00 12 00 00 00 >u.......g.......<
0002460 62 00 00 00 84 a1 05 08 00 00 00 00 11 00 f1 ff >b...............<
[4314]%

一つ目のインデクス(0002260)は本書に取り上げられている通り。
x86はリトルエンディアンであるので、最初の32ビットは0x000037dとなりこの値がシンボル名へのオフセット値となっている。
0002260のインデクスについても同じで、0x16cがオフセットである。

ストリングテーブルを調べてストリングテーブルの開始地点と上記で求めたオフセットを合計して、ダンプする領域を特定する。

[4319]% readelf -S /bin/ls | grep STRTAB
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 5] .dynstr STRTAB 08048b50 000b50 00047b 00 A 0 0 1
[24] .shstrtab STRTAB 00000000 012400 0000c3 00 0 0 1


.dynsymのOffsetの値0xb50+0x37D=0xECD

[4320]% od --skip-bytes 0xecd --read-bytes 16 -t x1z /bin/ls
0007315 67 65 74 67 72 6e 61 6d 00 5f 73 65 74 6a 6d 70 >getgrnam._setjmp<
0007335
[4321]%


getgrnamという文字列は、シンボルテーブルの二つ目のインデクスの値と一致する。

[4326]% readelf -s /bin/ls | head -10

Symbol table '.dynsym' contains 107 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 08049484 60 FUNC GLOBAL DEFAULT UND readlink@GLIBC_2.0 (2)
2: 08049494 283 FUNC GLOBAL DEFAULT UND getgrnam@GLIBC_2.0 (2)
3: 080494a4 42 FUNC GLOBAL DEFAULT UND __fpending@GLIBC_2.2 (3)
4: 080494b4 58 FUNC GLOBAL DEFAULT UND acl_entries@ACL_1.0 (4)
5: 080494c4 83 FUNC GLOBAL DEFAULT UND sigaction@GLIBC_2.0 (2)
6: 080494d4 239 FUNC GLOBAL DEFAULT UND readdir64@GLIBC_2.2 (3)
[4327]%


参考
オブジェクトファイルについて

Tuesday, November 14, 2006

UNIQLOのNY進出

最高の人達を用意して望むといった、UNIQLOのNY進出。
総合ディレクターにN702iDのデザイナーでもある佐藤可士和氏、インテリアデザイナーに片山正道氏を向かえNY進出を果たしたUNIQLO.

UNIQLOはヨーロッパ・アジアといった海外進出ではことごとく失敗している。(香港は黒字?)
失敗している理由は何なんだろう?既に低コストで良質のウェアを提供している企業が存在するからだろうか。ニューヨーカーは新しいものに飛びつくが根付かせるには難しいとも聞く。

ロゴが日本とは異なる。
佐藤氏デザインの新デザイン。

以前柳井会長のゴルフ場付きの邸宅を会社の人に教えてもらったが、車で一周するだけで5分以上かかった。

UNIQLO USのWebサイトは完全にFlashで作られている。
画面全体がタイル上になりそれぞれのタイルが一つのアイテムの画像になっている。画像をクリックするとズームアップしそれがクローズUpされる。そして、クローズUpされた状態からもう一度クリックするとそのクローズUpされた画像が、またタイル上になって次の商品を探せる。

マウスホバーするたびに、文字が変化し動くエフェクトはどうかなという気もするが、少し遊べそうなサイトです。

参考
http://www.uniqlo.com/us/

ホテル ボラボラ

4年後には、ここの水上バンガローに泊まって、トローリングでブルーフィッシュ釣りをして遊べるぐらいになっていたい。25歳・・・4年は厳しいか。5年、26歳。

HOTEL BORA BORA
http://www.amanresorts.com/bora/res.htm
http://www.amanresorts.com/bora/gallery.htm
http://www.tahiti-weddingbell.com/hotel/htb.html

参考
ttp://ameblo.jp/yukino-sakura-hana/theme-10002626343.html#4081

Binary Hacksが届いた

先日発売されたBinary Hacksが 届いた。

抽象化された世界で美しいコードを書いて感動できるのも、全て低レイヤの世界がしっかりとした土台を作ってくれているからである。土台が崩れたときには、その上のレイヤに気づかれた理論全てが崩壊してしまう。

先日読んだ、フェルマーの定理にも似たような話があった。
数学の世界というのは、証明というのが圧倒的な力を持つ。証明にいたらないものは"予測"と呼ばれる。証明は完全無欠であり、よく知られているピタゴラスの定理なども紀元前に証明されたわけだが、現在でもそれは定理であり覆ることは無い。

数学では予想の上に理論を展開することは非常にリスキーである。もし、何十年後にその予想が成り立たないと証明されてしまえばその上に築かれた数学会の何十年にも及ぶ知識の結晶が一瞬にしてくずれてしまうからである。

低レイヤを理解した上で、その上のレイヤ上で美しいソースコードを書きたいと常々思うのだが、なかなか実践できずサボっていた。OS30日本は1日目で挫折した。本重すぎ。

こう書くと、あまりにも技術LoveなT型人間に聞こえるが、T型が全く別の畑を持つT型人間を歓迎し、コラボレートしていくことで世の中を楽しくするようなモノを作っていけるんだろう。
横幅を興味の範囲・縦軸を知識の深さとして、TをⅡ、そしてⅢにしていくような感じ。

今日からBinary Hacksを一つずつ実践していき、より高レイヤで美しく効率的なプログラムを展開できるように努力を続けたい。

こういうすばらしい本を出版してくださった方には本当に感謝です。

Monday, November 13, 2006

[javascript] JavascriptでMap by Method

[ruby] RubyでMap by MethodをJavascriptに移植してみた。

SpiderMonkey限定になるのがとてもイタイのですが、IEでも同じような方法が試せないかあとで考える。(定義されていないメソッドを呼び出した際に呼び出すフックメソッドを定義するこの方法は何て名前がついてるんだろう?誰か教えてください。)


String.prototype.singularize = function() { // 手抜き実装
return this.replace(/s$/, '');
}
Array.prototype.map = function(lambda) {
var ret = [];
for(var i=0,l=this.length;i<l;i++) ret[i] = lambda(this[i]);
return ret;
}
Array.prototype.collect = Array.prototype.map;

Array.prototype.__noSuchMethod__ = function(name, args) {
if(match = name.match(/(map|collect)_([\w_]+)/)) {
iterator = match[1], methods = match[2].split('_and_');
return this[iterator](function(item) {
return methods.map(function(method){
return item[method];
});
});
} else {
return this.map(function(item){
return item[name.singularize()];
});
}
}

var data = [{name: 'Matz', lang: 'ja', loves: 'ruby'},
{name: 'DHH', lang: 'en', loves: 'ruby'},
{name: 'Takahashi', lang: 'ja', loves: 'ruby'},
{name: 'Moriq', lang: 'ja', loves: 'ruby'}];

console.log(data.names()); // => ["Matz","DHH","Takahashi","Moriq"]
console.log(data.map_name_and_lang()); // => [["Matz","ja"],["DHH","en"],["Takahashi","ja"],["Moriq","ja"]]
console.log(data.map_name_and_lang_and_loves()); // => [["Matz","ja","ruby"],["DHH","en","ruby"],["Takahashi","ja","ruby"],["Moriq","ja","ruby"]]


どう見てもString#singularizeが手抜き実装です。

[ruby] RubyでMap by Method

2年前PHPでガリガリと作って工夫をこらし考えていたときには、試行錯誤を繰り返していたが、Railsを使い出してからついついRails内の機能で満足してしまう。

よほど気をつけていないと、そこには改善すべき面白い機能がたくさんあるのにそれを見逃してしまう。多少面倒でもRuby&Railsの十分な恩恵にあずかっていて、それで十分簡単だと思い込んでしまう。

例えばSymbol#to_procを使用した次のようなコードをよく書く。


Person.find(:all).map(&:username)
Person.find(:all).map {|obj| [obj.username, obj.age]}

Symbol#to_procは、とても美しいく便利でよく利用する分だけ、このコーディングが冗長に思えてくる。

method_missingを利用してメソッドをmapメソッドと関連付けDynamic Map?(ARのDynamic Finderにちなんで勝手に命名)として作用させるというアイデアに出会った。

require 'rubygems'
require 'active_support'

class Rubyist
attr_accessor :name
attr_accessor :lang

def initialize(name, lang)
self.name = name
self.lang = lang
end
end

module MapByMethod
def self.included(base)
super

base.module_eval <<-EOS
def method_missing(method, *args, &block)
begin
super
rescue NoMethodError => e
if match = method.to_s.match(/(map|select|collect|each|reject)_([\\w\\_]+)/)
iterator, methods = match[1], match[2].split('_and_')
return self.send(iterator) {|item| methods.map {|method| item.send method}}
else
return self.map {|item| item.send method.to_s.singularize}
end
raise e
end
end
EOS
end
end
Array.send :include, MapByMethod

rubyists = [Rubyist.new("Matz", "ja"), Rubyist.new("DHH", "en"), Rubyist.new("takahashi", "ja"), Rubyist.new("moriq", "ja")]
p rubyists.names
p rubyists.map_name
p rubyists.map_name_and_lang


やっていることは単純で、method_missingを利用して高階関数をDynamicに適用するメソッドを利用可能にしている。

  1. 定義されていないメソッド(ある英単語の複数系)が与えられた際には、メソッド名を単数形にしたメソッドがmapへの高階関数として渡される。
  2. _and_でメソッド名をつなぐことにより、それぞれのメソッドを呼び出した結果を配列にして返す。

実行結果は次の通り。

p rubyists.names # => ["Matz", "DHH", "takahashi", "moriq"]
p rubyists.map_name # => [["Matz"], ["DHH"], ["takahashi"], ["moriq"]]
p rubyists.map_name_and_lang # => [["Matz", "ja"], ["DHH", "en"], ["takahashi", "ja"], ["moriq", "ja"]]
はい、美しいです。

参考
New magical version of Symbol.to_proc

Saturday, November 11, 2006

イノベーションを生むということ - 人類学者と実験者

Apple.comはイノベーションを生んでいる会社だ。
Googleもイノベーションを生んでいる。
Microsoftもイノベーションを生んでいる?た?



イノベーションを生むというのは何?
Amazonでぶらぶらしているときに、おすすめされた「イノベーションの達人」を先日購入した。

この本はデザイン・ファームIDEOを支える人材を10のカテゴリに分けて、イノベーションを起こす会社を作るには、どういったキャラクタ(を演じる。またはもつ。)の人たちが必要か?という疑問に答えてくれている本である。

この本に10個登場する人間のタイプは、向き不向きは誰でもあるにせよ意識することでそのキャラクタになれるということがすばらしい。そして、一人の人間は一つのキャラクタだけではなく、複数のキャラクタを兼ね持つになることになる。つまりイノベーションを起こすチームに求められるキャラクタや人物ではなく、誰でもイノベーションを起こすチームを作ることができるということだ。

人間がすごいところは、誰でも情熱を持ち努力することで世の中に大きな影響を与えるエグゼクティブにもなれるし、次々とイノベーションを起こすこともできることだとあらためて思う。

第一章は人類学者という人達について書かれているが、彼らは人をよく観察しうまくコミュニケーションをとり問題を明らかにする。技術の世界にいると、「無知は悪とみなされたり」・「あるツールを使いこなせないのはその人がバカだから」と見下すような場面によく遭遇する。

でも、本当はそういうところに大きなチャンスが隠れているのに探そうともせず独りよがりになりがちである。この項では、フィールドワークの重要性を再認識した。

第二章の実験者について書かれている。実験者は、多くのプロトタイプを生み出し多くの失敗を経験する。失敗した分だけ実験者としての能力の幅を広げる。好奇心旺盛で情熱を持ち努力を欠かさないのが実験者に求められる能力である。

プロトタイプを数多く作ることの重要性はよく聞く話だが、「プロトタイプの基準を下げる」という言葉にはっとさせられた。プロトタイプを作る際に、みんなが素敵!すげぇ!というのを作りたいがためについつい本来のコンセプトから脱線してこってしまうことがよくある。脱線して本来の線路に戻ってこれればいいのだが、作りこんでいる途中にコンセプトを見失ってしまうことのほうが多い。

プロトタイプに求められるものは、見た目の美しさや使いやすさなどではなく、荒削りでもよいがキラリと光るアイデアである。見る目がある人たちは、そこを見る。ということを常に頭の中に入れておこう。

また失敗を恐れないカルチャーつくりの重要性。あるアイデアに対して、一つではなく複数のプロトタイプがあることがプロトタイプの長所と短所について有意義な議論の話につながる。

たとえ話としてこんな話が書かれていた。
彼女の服を選ぶ際に、もう買ってきた服をきて「この服どう思う?」と聞かれたときには彼の答えは決まっていて、いい or 悪いの二択しか存在しない。ショッピング中に一緒に選んでいて、7つの中から選ぶときに初めて、この服のここがかわいいといったよりよい選択への議論へとつながる。

ビデオによるプロトタイプの有効性ではBMWの話が出てくる。BMW者では世界屈指の映像監督を集めて8分間のショートドラマを作成し、自社のHP上でのみ公開したがあっという間に口コミで広まり頼んでもいないのに新聞にまでのって、リンクURLが書かれたメールが飛び交う自体となったという話である。

Ruby on RailsがJavaの10倍の生産性というデモムービーで世界中のプログラマが飛びついたり、Scrybeのデモビデオが世界中の人達の間で話題になったり、AppleのWinを皮肉るCMについて多くのブロガが言及したりと映像によるプロモーションは大きな力を持つ。Youtubeのようなサービスが出るにつれますますこの流れは加速していく。

映像は、短い時間でサービスのコンセプトを万国共通に伝えられるすばらしいプロモーションである。ケチって出来の悪い物を作ってはいけない。世界中でとりあげられることを考えると安い先行投資である。

と、中田英と一泊23万のバカンスセレブデートが報じられた白雪って子がカワイイとうらやましく思いながらも、第二章まで読んで思った事を忘れないように書き留めた。

[RSpec] Mock API

Mock Object

Mock Objectの作成


my_mock = mock(<name>)
my_mock = mock(<name>, <options>)
person = mock('person', :null_object => true)


Mockは名前を引数に取る。仕様の検証が終わった際に全てのMockが検証される。
option引数をハッシュで与えることでMockの振る舞いを調整できる。現在、:null_objectのみがサポートされている。:null_object => trueを引数に渡すとMockに対する全てのメソッドがMock自身を返すようになる。

Mockに対してスタブメソッドを定義する

person.should_receive(:name) # person.name => nil
person.should_not_receive(:name) # person.name => raise Spec::Mocks::MockExpectationError


Mockに対して引数を固定してスタブメソッドを定義する

person.should_receive(:say).with(:hello)
person.say(:hello) # => nil
person.say # => raise Spec::Mocks::MockExpectationError


初回の呼び出しのみ引数を固定

person.should_receive(:say).once.with(:hello)
person.say(:hello) # => nil
person.say(:hello) # => raise Spec::Mocks::MockExpectationError


引数を渡さないことを明示的に宣言

person.should_receive(:say).with(:no_args)
person.say(:hellow) # => raise Spec::Mocks::MockExpectationError


引数を取りうる(なしもOK)ことを明示的に宣言
これはwith()のデフォルトの定義である。

person.should_receive(:say).with(:any_args)


スタブメソッドの引数の型のみ指定する。

person.should_receive(:say).with(:string)
person.say('hello') # => nil
person.say(false) # => raise Spec::Mocks::MockExpectationError

:string, :numeric, :boolean, :anythingが指定可能。

should_receiveで定義したメソッドが呼び出される回数を定義する。
デフォルトでは、should_receiveで定義するメッセージは一度だけ呼び出し可能である。

person.should_receive(:say)
person.say(:hello) # => nil
person.say(:hello) # => raise Spec::Mocks::MockExpectationError



person.should_receive(:say).twice
person.say(:hello) # => nil
person.say(:hello) # => nil


メソッドが呼び出される任意の回数を指定する

person.should_receive(:say).exactly(1)
person.say(:hello) # => nil
person.say(:hello) # => raise Spec::Mocks::MockExpectationError


メソッドが呼び出される最小(下限) or 最大(上限)回数を指定する

person.sayを最低2回呼び出すように定義。
person.should_receive(:say).at_least(2)
person.say(:world)
# => raise Spec::Mocks::MockExpectationError


最低n回呼び出すように指定

person.should_receive(:say).at_least(n).times


呼び出される最大回数(上限)を定義

person.should_receive(:say).at_most(:once)
person.should_receive(:say).at_most(:twice)
person.should_receive(:say).at_most(n).times


何度でも呼び出し可能にする

person.should_receive(:say).any_number_of_times


返り値を明示的に定義する

person.should_receive(:say).and_return('Hi!')
person.say # => "Hi!"



person.should_receive(:say).and_return(['Hello', 'world'])
person.say # => ["Hello", "world"]


and_returnにはブロックを渡すことが出来る。ブロックの実行結果が返される。

calc.should_receive(:add).with(:numeric, :numeric).and_return {|a,b| a+b}
calc.add(1,2) # => 3


メソッド呼び出し時に例外を発生させる

person.should_receive(:say).and_raise('error')
person.should_receive(:say).and_throw('error')
person.say # => "error":String


ブロック引数を定義する。メソッド呼び出し時にはブロックを渡す。

person.should_receive(:say).and_yield(1,2)
person.say {|a,b| a+b} # => 3


メソッドが呼び出される順番を明示的に定義する。
should_receiveをordered付きで定義した順番に呼び出しているので検証は成功する。

person.should_receive(:one).ordered
person.should_receive(:two).ordered
person.should_receive(:three).ordered
person.one
person.two
person.three


次の例は、定義した順番で呼び出していないので失敗する。

person.should_receive(:one).ordered
person.should_receive(:three).ordered
person.should_receive(:two).ordered
person.one
person.two
person.three


should_recieveにはブロックを渡すことも出来る。呼び出された際にブロックが実行され検証される。

person.should_receive(:say) do |a,b|
a.should_eql 'hello'
b.should_eql 'world'
end
person.say('hello', 'world')


次の例は第一引数が'hellow'出ないため検証に失敗する。

person.should_receive(:say) do |a,b|
a.should_eql 'hello'
b.should_eql 'world'
end
person.say('hello', 'world')


参考
Mock API

[rails] 実践RSpec on Rails - コントローラとモデルのBehaviourを書く

先日RSpec on Rails0.7(0.7.1も)が出ました。
まだまだ枯れていないので実際のプロジェクトで採用するのは難しい面もありますが、isoration from Databaseを支えるmock/stub frameworkや、isoration from viewsは強力なので、それほど影響の無い作成済みの社内アプリにRSpec on Railsを適用してみました。

maihaさんがやっておられるように、自作で作るのもすごく楽しそうなのですがとりあえずは使ってみます。mock/stub frameworkの実装の詳細をあとでみる。

BBDでは、Behaviourを書いてからコードの実装を行うのですが、今回は以前にうみがめで作った社内用情報共有ツールBasecamp(某signalsのパクリ)にBehaviourを書いていくことになります。

BasecampのSpec

  • 社内でブログをベースに情報共有を行うグループウェア
  • 認証は、Ajaxによるワンタイムパスワード認証
  • 3日以内で作るために、BBDもTDDをしていなかった。
  • はてな記法が使える。はてな日記のインポートが可能
rake stats

[4141]% rake stats [/var/www/sankhon]
(in /var/www/sankhon)
+----------------------+-------+-------+---------+---------+-----+-------+
| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Helpers | 37 | 33 | 0 | 5 | 0 | 4 |
| Controllers | 620 | 480 | 9 | 54 | 6 | 6 |
| Components | 0 | 0 | 0 | 0 | 0 | 0 |
| Models | 82 | 75 | 12 | 3 | 0 | 23 |
| Libraries | 574 | 472 | 6 | 15 | 2 | 29 |
| Model specs | 16 | 13 | 0 | 0 | 0 | 0 |
| View specs | 0 | 0 | 0 | 0 | 0 | 0 |
| Controller specs | 54 | 42 | 0 | 0 | 0 | 0 |
| Helper specs | 0 | 0 | 0 | 0 | 0 | 0 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total | 1383 | 1115 | 27 | 77 | 2 | 12 |
+----------------------+-------+-------+---------+---------+-----+-------+
Code LOC: 1060 Test LOC: 55 Code to Test Ratio: 1:0.1

You have new mail.
[4146]% [/var/www/sankhon]


セットアップ

% sudo gem install rspec
% sudo gem install zentest -v 3.4.1
% sudo gem install diff-lcs
% ./script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_0_7_0/vendor/rspec_on_rails/vendor/plugins/rspec


AccountController#loginがログインに関する実装を提供するコントローラである。
GETの場合は、ワンタイムパスワードのためのトークンを埋め込んだログイン画面を表示し、POSTの際は認証を行い成功すると200を返し失敗すると400を返す。
ログインはXMLHttpRequestで行う。

BBDではBehaviourを書いてから実装するのがセオリーだが、ここでは既にあるコードにBehaviourを書いて必要に応じてリファクタリングを施していく。

旧AccountsController#login

def login
case request.method
when :get
@challenge_code = [rand(64), rand(64)].pack("C*").tr("\x00-\x3f", "A-Za-z0-9./").crypt([rand(64), rand(64)].pack("C*").tr("\x00-\x3f", "A-Za-z0-9./"))
session[:challenge_code] = @challenge_code
when :post
if user = User.find_by_username(params[:username])
hashed_password = Digest::MD5.new(user.password+session[:challenge_code]).to_s
if params[:hashed_passwd] == hashed_password
session[:challenge_code] = nil
session[:user] = user
render :text => "true"
else
render :text => "パスワードが間違っています", :status => 401 # Unauthroized
end
else
render :text => "入力されたユーザは存在しません", :status => 401 # Unauthroized
end
end
end

当初実装していたコードでは、認証の成功/失敗に関するBehaviourを書きづらいので、認証部分のロジックをモデルに移して再実装する。始めに、モデルに実装する認証メソッドのBehaviourを定義する。

[4138]% ./script/generate spec_model user

UserモデルのBehaviourを定義する。
spec/model/user_spec.rb
require File.dirname(__FILE__) + '/../spec_helper'
require 'digest/md5'

context "User class with fixtures loaded" do
fixtures :users

specify "should count one Users" do
User.should_have(1).records
end

specify "生のパスワード+トークンをMD5ハッシュ化した文字列をワンタイムパスワードとする" do
user = users(:junkonno)
challenge_code = [rand(64), rand(64)].pack("C*").tr("\x00-\x3f", "A-Za-z0-9./").crypt([rand(64), rand(64)].pack("C*").tr("\x00-\x3f", "A-Za-z0-9./"))
user.certify(challenge_code, Digest::MD5.new(user.password+challenge_code).to_s).should_eql true
end
end

Behaviourに基づきUser#certifyを実装する。(関連部分のみ)
app/model/user.rb

require 'digest/md5'

class User < ActiveRecord::Base
def certify(code, hashed_passwd)
hashed_passwd == Digest::MD5.new(self.password+code).to_s
end
end

fixturesを定義し検証を行う。
spec/controller/users.yml

# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
junkonno:
id: 1
username: 'junkonno'
password: 'junkonno'

User#certifyを検証する。

[4138]% ./script/rails_spec spec/models/user_spec.rb [/var/www/sankhon]

..

Finished in 0.102308 seconds

2 specifications, 0 failures
[4141]% [/var/www/sankhon]


検証クリア。
User#certifyを使用して認証を行うようにAccountsController#loginを書き直す。
app/controller/accounts_controller.rb

when :post
user = nil
if (user = User.find_by_username(params[:username])) && user.certify(session[:challenge_code], params[:hashed_password])
session[:user] = user.dup
session[:challenge_code] = nil
render :text => "true"
else
render :text => "ユーザが存在しないかパスワードが間違っています", :status => 401 # Unauthroized
end
end

書き直したAccountsControllerに関するBeahviourを定義する。

spec/controller/accounts_controller_spec.rb

require File.dirname(__FILE__) + '/../spec_helper'

context "The AccountsController" do
# fixtures :accounts
controller_name :accounts

setup do
@user = mock('user')
@user.stub!(:new_record?).and_return(false)
User.stub!(:new).and_return(@user)
end

specify "should be a AccountsController" do
controller.should_be_an_instance_of AccountsController
end

specify "/accounts/loginへGETした際は、チャレンジコードを設定する" do
get 'login'
assigns[:challenge_code].should_match /[\w\d]+/
end

specify "/accounts/logoutへPOSTした際は、認証を行い成功したらtrueを出力し200を返す" do
User.should_receive(:find_by_username).with('junkonno').and_return(@user)
@user.should_receive(:certify).and_return(true)
controller.should_render :text => "true"
post 'login', :username => 'junkonno'
end

specify "/accounts/logoutへPOSTした際は、認証を行い失敗したら401を返す" do
User.should_receive(:find_by_username).with('junkonno').and_return(@user)
@user.should_receive(:certify).and_return(false)
controller.should_render :status => 401, :text => "ユーザが存在しないかパスワードが間違っています"
post 'login', :username => 'junkonno'
end
end
RSpec on Rails0.7の強力な機能である、mock/stub frameworkを使用する。
Mock/stubを使用することで、モデルとDBを切り離しBehaviourを書くことが出来る。

次のコードは、User#newでインスタンスが作成される際に、実際のインスタンスではなくSpec::Mocks::Mockクラスのインスタンスを返すようにスタブを定義する。コントローラ内でUser.newが実行された場合は、Mockオブジェクトのインスタンスである@userが返される。

setup do
@user = mock('user')
@user.stub!(:new_record?).and_return(false)
User.stub!(:new).and_return(@user)
end
setupのブロック内でスタブを定義し、specifyのブロック内でshould_receiveによってスタブメソッドを再定義するのが推奨されているやり方です。二つのメソッドは期待される結果は同じだが、stub!が検証(例外とか)されないのに対して、should_receiveは検証が行われる。
Behaviourは、プログラマでなくとも普通の英文として読むことができます。美しい。

assingsオブジェクトでは、コントローラ内で設定されたクラス変数を確認することが出来る。

specify "/accounts/loginへGETした際は、チャレンジコードを設定する" do
get 'login'
assigns[:challenge_code].should_match /[\w\d]+/
end
'junkonno'が引数に与えられて、User.find_by_usernameが呼び出されたときには、@userを返すようにする。@user.certifyが呼びださされた際には、trueを返し認証が成功した際のBehaviourを検証する。

User.should_receive(:find_by_username).with('junkonno').and_return(@user)
@user.should_receive(:certify).and_return(true)

検証を行う。
[ERROR:1]% ./script/rails_spec spec/controllers/accounts_controller_spec.rb                                                           [/var/www/sankhon]

....

Finished in 0.233141 seconds

4 specifications, 0 failures
[4148]% [/var/www/sankhon]


まとめ
今回は、ログインの実装に関するBehaviour(コントローラとモデル)を定義してリファクタリングを行った。
BBDを進める際は、ロジックを可能な限りモデルに定義しBehaviourを書いていくほうがよい。既存のソースに適用することでソースコードが綺麗になる。
次回は、controllerとviewsのBehaviourの定義。

Rspecのサイトにも書かれているように、RSpec0.7以上ではControllerとViewsのBehaviourを完全に分離して定義できます。これによりViewによりControllerのBahaviourが失敗するといったことがなくなります。そして、Views単体のBBDはselenium等のほかのフレームワークを利用して進める方法が推奨されています。

では、ふるたにでした。

参考
Mock Objects
Mock API
RSpec on Rails – Specifying Controllers
RSpec on Rails – Specifying Models

Wednesday, November 08, 2006

My Stiq(という表現は(ry)をブログに表示してみた

Be Stiqというサービスで好きなページにStiqをはって緩いコミュニケーションができるのだが、自分が張ったStiqをブログに表示してみた。右のバー。

UIE Japanはよく英語がとびかうのだが、英語に精通している人にとって"MyStiq"という表現はアレらしいが、他の表現も見つからなかったのでそうした。

データは全てAPIで取得できる。callbackパラメータを渡す事でJSONPで取得できるので、scriptタグを埋め込めればどこでも表示できる。


function showMyStiqs(json) {
//var data = eval(json);
for(var i=0,l=data.length;i<l;++i) {
try {
document.write('<li><a href="'+data[i].url+'">'+data[i].title+' ('+data[i].replies.length+')</a></li>');
}catch(e){}
}
}


<script type="text/javascript" src="http://be.stiq.net/apis/stiq?username=rakuto&callback=showMyStiqs" charset="UTF-8"></script>

毎回、ソースコードの色付けが面倒だけど、ハイライトのないソースコードは見るのも苦手なので頑張ってつけている。よしソースコードのハイライトをつけるGreaseMonkey script、VimColor at Bloggerつくろう。

Tuesday, November 07, 2006

[Javascript] iTunes7のように画像を水面に反射させたように表示する

賛否両論ある、iTunes7のアートワークの3D表示
でも、画像を水面反射したような見せ方をするだけでCool!と思わせれるかも?しれない。
意外と簡単に作れるので自作のフレームワークに組み込むのもよさそうです。

で、一日一つ新しい事を習得する(と決めた)ための、今日のネタはCanvasタグ。
ネタ自体は既出ですがCanvasタグの練習がてら作ってみました。

デモ

画像のURLを直接入力するか、URLをリストから選んでCreateボタンを押すとアルバムのジャケットのアートワークが水面反射付きで表示される。

現在

  • Saffariでは動作未確認。(動作未確認。Canvasタグの実装にバグがある様子)

ソースはシンプル。

var UA = navigator.userAgent.toLowerCase();
var isIE = (UA.indexOf('msie') != -1) &&amp;amp; !window.opera;

function addImageAndReflect(url, opacity) {
var opacity = opacity || 0.7;

/* image and reflection wrap element */
var imgWrapElm = document.createElement('div');
imgWrapElm.className = 'imgWrap';
var image = new Image;
image.src = url;
image.style.display = 'block';

if(isIE) {
var reflectImgElm = document.createElement('img');
reflectImgElm.src = url;
reflectImgElm.style.filter = "flipv progid:DXImageTransform.Microsoft.Alpha(opacity="+(1-opacity)*100+",finishopacity=0,style=1,startx=0,starty=0,finishx=0,finishy="+image.height+")"; // ⑤
imgWrapElm.appendChild(image);
imgWrapElm.appendChild(reflectImgElm);
document.body.appendChild(imgWrapElm);
} else {
/* canvas element */
var canvas = document.createElement('canvas');
image.onload = function() {
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext('2d');
with(ctx) {
save();
translate(0, image.height-1);
scale(1,-1);
drawImage(image, 0, 0);
restore();
var gradient = createLinearGradient(0, 0, 0, image.height);
gradient.addColorStop(1, "rgba(0, 0, 0, 1)");
gradient.addColorStop(0, "rgba(0, 0, 0, "+opacity+")");
globalCompositeOperation = 'destination-out';
fillStyle = gradient;
fillRect(0, 0, image.width, image.height);
}
imgWrapElm.appendChild(image);
imgWrapElm.appendChild(canvas);
document.body.appendChild(imgWrapElm);
}
}
}


①でcanvasタグとImageオブジェクトを作成する。
IEなどcanvas.getContextに対応していないブラウザのために処理を条件分岐。

②の解説。
Canvasへの画像の描画は、drawImageを使用する。drawImageをコールする前に画像の読み込みを完了している必要があるため、onloadイベントハンドラとしてCanvasタグの描画を行う。読み込みが完了していないのに、drawImageを呼び出すとExceptionを吐いてスクリプトが停止する。

③の解説。
原点を移動して、グリッドの座標を変換することで画像をY軸に対して反転させる。原点移動の際に-1をしているのは、オリジナルの画像との隙間を無くすため。

④の解説。
グラデーションをかけるための矩形図形を作成する。
画像のサイズと同じ長方形を作成し塗りつぶしを作成したグラデーションに指定する。

rgbaは、RGB値に加えて不透明度(0~1)を指定する。
上記のグラデーションは、黒色の完全に不透明の状態から、不透明度0.7の黒色までのグラデーションを作成している。
var gradient = createLinearGradient(0, 0, 0, image.height);  // ④
gradient.addColorStop(1, "rgba(0, 0, 0, 1)");
gradient.addColorStop(0, "rgba(0, 0, 0, 0.7)");

グラデーションを塗りつぶしに指定した矩形図形を画像の上に表示するように指定している。
参考: Canvas tutorial:Compositing

globalCompositeOperation = 'destination-out';

塗りつぶしを作成したグラデーションに指定してから、画像と同じサイズの図形を描画する。

fillStyle = gradient;
fillRect(0, 0, image.width, image.height);

と、意外と簡単に水面反射を作成することが出来る。

⑤はIE対応。
IEにはCanvasが実装されていないため、フィルタを用いて同様の効果を実現する。
flipvは画像を反転させるフィルタ。

参考
WHATWGのCanvas仕様
Canvas tutorial:Drawing shapes
Canvas tutorial:Compositing
Canvas tutorial:Transformations
Canvas tutorial:Applying styles and colors
Reflection.js

Monday, November 06, 2006

Canvas.getContext.drawWindow

Firefox 1.5以上 only.

現在のウインドを描画する 削除




var canvas = document.getElementById('c1');
var ctx = canvas.getContext('2d');
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
with(ctx) {
translate(canvas.width/2, 0);
scale(0.6, 0.6);
rotate(Math.PI/4, Math.PI/4);
drawWindow(window, 0, 0, 600, 600, "#000000");
lineWidth = 2.0;
strokeRect(0, 0, 600, 600);
}

[Rails] RSpec on Railsを始めてみた - インストールと実行

RSpec on Railsのさわりだけ。

おそばせながら今頃、BDD(Behaviour Driven Development)を知りました。。。
これからはきちんとBDD or TDDで開発を進めます。

BDDはテスト駆動開発と言葉の言い回しが大きく異なっている。
「ベヒイビアを書いて仕様を設計する。」これが大きなポリシー。
BDDでは必ず仕様コード(spec)を書いてから実際のコーディングを行う。


Rubyには、RSpecというツールがありこれを利用する。
gemパッケージが用意されているので簡単。


$ gem install rspec


次にRSpec on Railsプラグインをインストール。
REL_X_Y_Zの部分をrspecのバージョンとあわせる必要がある。

$ cd RAILS_ROOT
$ ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_0_6_4/vendor/rspec_on_rails/vendor/plugins/rspec


RSpecでは、spec/ディレクトリ以下にspec, fixturesファイルなどが展開される。
始めにこのディレクトリを作成する。


$ ./script/generate rspec


インストール終わり。

テストの実行方法がいくつか用意されている。

rakeを使用する場合
  1. 全てのビヘイビアを実行
  2. コントローラのベヘイビアを実行
  3. モデルのビヘイビアを実行
それぞれ、

$ rake spec
$ rake spec:controllers
$ rake spec:models

で実行可能。

単体でも仕様を検証可能
単体で仕様(ビヘイビア)を満たすか検証することも可能。
先にscript/rails_spec_serverというプログラムを起動しておく。(Railsの環境設定が読み込まれるため実行が早くなる)

次の例では、personモデルの仕様を検証している。

$ ./script/rails_spec_server
$ ./script/rails_spec spec/models/person_spec.rb


コントローラー・モデルの作成
RSpecには、Specも同時に生成してくれるジェネレータが定義されている。
それぞれ、

$ ./script/generate rspec_controller index
$ ./script/generate rspec_model person

として利用でき、


create spec/controllers/
create spec/views/index
create spec/controllers/index_controller_spec.rb
create spec/models/
create spec/fixtures/
create spec/models/person_spec.rb
create spec/fixtures/people.yml

といった感じで通常のジェネレータに加えて、(モデル名 | コントローラ名)_spec.rbというファイルなどを生成してくれる。

仕様(べヘイビア)を書く
Personモデルを作成して、spec/models/person_spec.rbを編集して仕様を定義した。

require File.dirname(__FILE__) + '/../spec_helper'

context "Person class with fixtures loaded" do
fixtures :people

specify "should count two People" do
Person.should_have(2).records
end

specify "should full name is 'first_name last_name'" do
person = Person.find(1)
person.full_name.should_equal "#{person.first_name} #{person.last_name}"
end
end


app/model/person_spec.rbにメソッドを定義
class Person < ActiveRecord::Base
def full_name
"#{self.first_name} #{self.last_name}"
end
end

テスト実行
別ターミナルでscript/rails_spec_serverを実行しておく。

[4246]% ./script/rails_spec spec/models/person_spec.rb [/var/www/test]

..

Finished in 0.114999 seconds

2 specifications, 0 failures
[4248]%

この二つの仕様は正しいのでピリオドが二つ出力された。
仕様(spec)の数だけピリオド(またはF)が出力される。

should_equalをshould_not_equalに書き換えて、意図的に失敗させた場合は、次のように出力される。

[4237]% ./script/rails_spec spec/models/person_spec.rb [/var/www/test]

.F

1)
Spec::Expectations::ExpectationNotMetError in 'Person class with fixtures loaded should full name is 'first_name last_name''
"rakuto furutani" should not equal "rakuto furutani"
./spec/models/person_spec.rb:12:in `should full name is 'first_name last_name''

Finished in 0.103591 seconds

2 specifications, 1 failure
You have new mail.
[4250]%

二つ目のSpecが正しくないことがわかる。

簡単だが今日はここまで。
次回、RSpec on RailsとRSpecの中身を詳しくみていきたい。


参考
RSpec on Rails
RSpec
RSpec Documentation