静的サイトジェネレータMetalSmith

今回は MetalSmith という超シンプル!超タイニー!なNodeで動作する静的サイトジェネレータのお話。
なんつーかその、メタルスミスとカタカナで書きたい衝動を煽られます。

そう、名前って大切ですよね。

スタティックサイトジェネレータはAssembleをはじめ、他にいくつもあって、 なかにはhexo なんていうものもあるんですが・・・うん・・・・。名前大事。
このメタルスミスというネーミング自体はマイナス34℃の環境下で作られたという、同じく静的サイトジェネレータのWinterSmithにインスパイアされたのかなーと思ったり。

というわけで公式ドキュメントはこちら


背景

さて、このメタルスミス、segment.ioという、Webデータ解析のインテグレーションツールを提供している会社によって作られていて、彼らのgithub organizationをのぞいて見ると、赤シャツのTJやJulian Gruberがいたりします。
で、作者はこのSegment.ioのco-founderである、Ian Storm Taylor氏。イアン!ストーム!!テイラー!!!必殺技みたいなお名前でございますな。

EVERYTHING IS A PLUGIN

エヴリシン イッザ プラァギン! でかでかとTシャツにプリントされていそうなこのスローガンがもう、メタルスミスそのものを体現していると行っても過言ではありません。
中身をのぞいて見るとわかるのですが、もう謳い文句通り、ちょうシンプル!ちょうタイニー!
これだけで良いのかと不安になるくらいの軽量っぷりでございます。


ほぼ全てはプラグインなので、やることと言えば単純にuse()をチェーンしてミドルウェアを登録し、最後にbuild()を呼ぶだけ。

例えば

Metalsmith(__dirname)
  .use(markdown())
  .use(templates('handlebars'))
  .build();

簡単なサイトであれば上記のようにメタルスミス・ユーズ・ユーズ・ビルドみたいな感じでオッケー!
今までのスタティックサイトジェネレータはこの辺の、なにやってるかわからない感が酷かったのですが、メタルスミスを使えばコード内に過程をうまいこと可視化できるので組みやすいです。
処理を少し増やしてもまだ全然、怖くない感じですね。

Metalsmith(__dirname)
  .use(drafts())
  .use(markdown())
  .use(permalinks('posts/:title'))
  .use(templates('handlebars'))
  .build();


超シンプル!超タイニー!

exampleディレクトリそぞろ歩き。

では実際にメタルスミスを使ってみましょう。npmでインストールして実際に作り始めてしまうのも良いのですが、残念なことにnpmパッケージにはexampleが含まれていないので、まずはgithubからプロジェクトをcloneして、exampleを見て行きまっしょい。

$ git clone https://github.com/segmentio/metalsmith.git
$ npm install

exampleディレクトリには今のところ6つのプロジェクトがあります。とりあえずstatic-siteディレクトリを見てみましょう。

package.jsonを開いてdependencyを確認します。

  • metalsmith-template
  • metalsmith-markdown
  • metalsmith-permalinks
  • metalsmith
  • handlebars

このうち、metalsmith- から始まるものはメタルスミスのプラグインで、それら自体はパッケージに組み込まれていないので別途インストールが必要です。package.jsonにはscriptsエントリがありません。そこで Readme.md を読んでみると make build せよと。

Makefileを開いてみるとnpm installしてmetalsmithコマンドを実行するだけになっています。Windowsではmake入れるのが面倒だったりしますが、単純なのでコマンドを手入力しても良いし、バッチファイル書いてもよさそうです。


メタルスミスコマンドの利用

このメタルスミスコマンドはnodeのプログラムを書かなくても、metalsmith.jsonを作成して、実行内容を書いておくだけで処理を実行してくれるエラいやつ。

この方式だと自分でプログラムを書かなくても良いのです。

つまり、メタルスミスを利用する場合は、自分でモジュールを呼び出す方法か、JSONファイル を作成してメタルスミスコマンドを実行するかを選べるということですね。

では次にmetalsmith.jsonを確認してみます。

{                                                                                                                                      
  "metadata": {
    "title": "My Blog",
    "description": "My second, super-cool blog."
  },
  "plugins": {
    "metalsmith-markdown": {},
    "metalsmith-permalinks": {
      "pattern": ":title"
    },
    "metalsmith-templates": {
      "engine": "handlebars"
    }
  }
}

先ほどの package.json の中に記述してあった依存関係のあるプラグインが plugins に登場してますね。何やら引数を渡せる感じになっています。あとはメタデータのみとシンプル。

実際にmakeを走らせるとメタルスミスコマンドが実行され、buildディレクトリに生成されたファイルが格納されます。

と、これだけではアレなので、もうちょっと深堀りしていきましょう。

metalsmith.json のフォーマットですが、基本的なプロパティとしてsource, destination, metadata を含みます。で、source, destination は指定がない場合、それぞれsrc、buildディレクトリがデフォルトで利用されます。

pluginsのエントリですが、キーになっているプラグインモジュールがrequireされ、valueがそのオプションとして渡されます。つまり

"plugins": {
    "metalsmith-permalinks": {
        "pattern": ":title"
    }
}

とあった場合、内部的には

var plugin = require("metalsmith-permalinks");
var opts = {"pattern": ":title"};
metalsmith.use(plugin(opts));

と変換されます。

こうして各プラグインをユーズ・ユーズ・ユーズで、ガーッと積み上げて、最後に`build()` を走らせるっていうのがメタルスミスコマンドのやっていることです。

なので、さきほどの

{                                                                                                      
  "metadata": {
    "title": "My Blog",
    "description": "My second, super-cool blog."
  },
  "plugins": {
    "metalsmith-markdown": {},
    "metalsmith-permalinks": {
      "pattern": ":title"
    },
    "metalsmith-templates": {
      "engine": "handlebars"
    }
  }
}

という例は

var metalsmith = new Metalsmith(process.cwd());
metalsmith.metadata({title: "My Blog", description: "My second, super-cool blog."});
metalsmith
  .use(markdown())
  .use(permalinks({pattern:":title"}))
  .use(templates({engine:"handlebars"}))
  .build();

へと内部的に変換されて実行されます。


データの流れ

`src`ディレクトリにあるファイルを読み込み、それをプラグインに渡し、結果を`destination`ディレクトリへ出力するというのがメタルスミスの処理内容です。ちなみそれぞれのディレクトリのデフォルトは`src`、`build`となっていて、変更可能です。

ここではそのデータに着目して処理を追って行きましょう。

`example/static-site`の、処理対象となるファイルが格納されている`src`ディレクトリをのぞいて見ると、`index.md`を初め、`first-post.md`から`fourth-post.md`までフラットな階層に並んでいます。
それらの中身は

---
title: My First Post
date: 2012-08-20
template: post.html
---

An interesting post about how it's going to be different this time around. I'm going write a lot more nowadays and use this blog to improve my writing.

と、こんなだいたいこのような形式になっており、YAMLのFrontmatterにメタ情報を記述し、続いてMarkdown形式で書かれています。このへんはjekyllに影響を受けたんでしょうね。

Markdownファイルに限らず、全てのファイルは一旦このYAMLのFrontmatterの解析器にかけられ、attributeの抽出が行われます。必須というわけではないようですが、各プラグインで必要となりそうなページのメタ情報はFrontmatterにまとめておくのが良いですね。
ちなみにこのFrontmatterは`build()`で出力されるテキストからは省かれます。

さて、pluginが一つもない場合どうなるかというと、`src`ディレクトリに存在するファイルがYAML Frontmatterを除去された後に、全て`destination`ディレクトリにコピーされるという動きになります。

プラグイン

さて、このstatic-siteのexampleではmarkdown, permalinks, templatesの3つのプラグインが使われています。
それぞれその名の通り、markdownmarkdownファイルの解釈、permalinksプラグインはindexからのpermalinkの生成を、templatesはテンプレート処理を行います。
プラグインに渡すオプションは、それぞれのプラグインで定義されてるため、使いこなしにはプラグインのドキュメントも読んでおいたほうがよいですね。


プラグイン一覧


ではそれぞれを詳しく見て行きましょう。

metalsmith-markdown

markdownプラグインmarkedモジュールを利用しており、引数にmarkedへのオプションを取ります。
オプションは例えばこんな感じ。

{
  smartypants: true,
  gfm: true,
  tables: true
}

処理対象は `.md` 拡張子を持つファイルで、それらはMarkdownからHTML形式に変換され、拡張子も含めHTMLファイルとして出力されます。

metalsmith-permalinks

permalinksプラグインは入力ファイルに対し、指定されたパターンを適用したディレクトリを出力先に作成してその中に`index.html`を作成します。結果としてたとえば`about.html`が`about/index.html`として出力されたりします。

プラグインの引数にはパターンを指定するのですが、パターンに使われる要素も指定できます。例えば、

{
    pattern: ':date/:title',
    date: 'YYYY'

と指定すると`年/タイトル`といった形式でディレクトリが作成され、その下にindex.htmlが作成されます。dateの変換に関してはこのプラグインの中でmomentモジュールを利用して変換されており、momentが理解できるフォーマットを指定します。

---
title: My First Post
date: 2012-08-20
---

このようなFrontmatterがあり、先ほどのパターンを適用すると`2012/my-first-post/index.html`として出力されます。

そのほか、`relative` オプションというものがデフォルトで`true`なのですが、これが`true`の場合は`src`ディレクトリにあるファイルが出力先ディレクトリに全てコピーされます。例えば

    src/
      css/
        style.css
      post.html

このようなディレクトリ構成は

    build/
      post/
        index.html
        css/
          style.css
      css/
        style.css

として出力されます。この挙動をオフにしたい場合は`relative`オプションを`false`に指定する必要があります。

metalsmith-templates

さて、最後のtemplatesプラグインですが、内部でConsolidate.jsを利用しており、引数にはそれで利用可能なものを指定できます。
exampleではテンプレートエンジンにhandlebarsを指定していますね。

プラグインの引数に指定できるのは

engine
テンプレートエンジンの指定
directory
テンプレートが置かれているディレクトリ指定、デフォルトは`templates`
inPlace
本文中のタグをFrontmatterのattributeで置換するか否かのフラグ。デフォルトOFF。
pattern
テンプレート適用のマッチャーに使うパターンを記述します。記述はmultimatchを使っているのでpythonphpのglob記法のような、`*.md`や`file??.txt`といった表現で書くことが出来ます。


となっており、

metalsmith.use(templates({
    engine: 'handlebars',
    directory: 'templates_dir',
    pattern: 'publish*.md'
}));

このように指定できます。

プラグインではFrontMatterの`template`がテンプレートファイル名を指定しており、このファイルの実体は`templates`ディレクトリに置かれることを想定しています。
中身は

<html>
<head>
  <title>My Blog</title>
</head>
<body>
  <h1>{{ title }}</h1>
  <time>{{ date }}</time>
  {{{ contents }}}
</body>
</html>

こんな感じで、handlebarsによって title や date、contentsがそれぞれ置き換えられて出力されます。titleやdateはYAML frontmatterから、それ以外がcontentsと置き換えられます。

API

メタルスミスで重要なAPIは`use()`と`build()`くらいです。useはプラグインを登録し、buildで実行するという簡単な流れです。その他、`source()`や`destination()`、`metadata()`というAPIでデフォルトの振る舞いを変更したり、処理全体で必要となるメタデータを登録することができます。

使い方

本来はスタティックサイトジェネレータなのですが、もうちょっといろんなことが出来ます。
exampleではScaffoldに使ったり、ビルドツールとしても使えることが窺えます。処理はプラグインが受け持ち、メタルスミスはそのプラグインの登録、指定ディレクトリ内のファイル読み込み、書き出しを行うだけなので、いろいろ応用が利く仕組みになっています。まだイニシャルコミットから二ヶ月程度と若いプロジェクトなのでこれからが楽しみなモジュールです。ぼくもちょこちょこプラギン書き始めました。

HackerNewsのスクレーパーを書いてみた

今話題のKoaについて書こうかとも思ったのですが・・・。
なんとなく最近、HNを中心に見るようになったので、HNスクレーパーを作ってみました。

皆さんは日頃、どのような手段で情報を収集しているのでしょうか?今年はGoogle Readerの終了というこの世の終わりか審判の日かといわんばかりの破壊的なイベントが発生して阿鼻叫喚の地獄を味わった方も多いかもしれません。代替手段はなきにしもあらずなので、「気のせいだった。」とおっしゃる諸兄も多い事でしょう。しかし僕にとってはもう「!!!!!!!」と、声にもならなかった出来事であり、頼みの綱のReederは開発を終了、(その後バージョンアップ)してしまい、自分はただ涙を流して冷たいコンクリートの壁を叩き、泣き崩れる毎日だったのです。そんななか、タイミングよく現れていたGunosy、これぞ救世主とばかりに登録したものの、そもそもの情報ソースが自分の求めていたものとは違ってああお前もかとやはり鉄格子を握りしめて頭を打ち付ける日々を送ることと相成ってしまいました。(今はかなり使い勝手が良いのかも)

こういったことを回避するためには、やはり終了してしまう可能性のある、他人が運営するサービスを利用するよりは自分でサービスそのものを作る必要性がある。このことを Google Reader終了からしばらくして松屋のトマトカレーが教えてくれたのです。トマトカレーよ、お前までいなくなるとは。

はい、要するに自分で情報収集系のツールを作りましょうと。メンテしましょう育てましょうということで、僕が言いたいのはここまでです。

お読みいただき誠に、誠にありがとうございました。


あとは蛇足です!

Google Readerがなくなった背景にはやはりRSSリーダとしての単一機能がやはりよくなかったのだろうと思っています。
今の時代、我々にはTwitterがあり、FBがあり、それほど人気はないようですがGoogle+があるのです。そしてHNがあり、Redditがある。情報ソースは多岐にわたるようになったんですね。

そういったマルチナソース(マルチなソース)を集約し、内容を垂れ流してしまう、そんなアプリこそ皆それぞれが持つべき武器なのです。我らに自由を!

今回はHNだけですけど、様々な情報ソースを集約していくとよいのでしょうね。

決意的ななにか(蛇足の蛇足)

気がつくとアクセスして知らぬうちに時間を浪費してしまう。はてブ恐ろしい子
ということで、自分のための情報ソースを確立するまでもう、はてブは見ない!との強い意志をもって、はてブ封印をしてました。

ちなみにですね、はてブの未来はパーソナライズサービスなんかにあると思ってます。個人的に。ホッテントリって、いわゆるベストセラーのコーナーみたいな、注意を引くけれども別に自分が読まなくてもよいものばかりですし・・・。

ということで僕のマシンではアドレスバーに "b" を打つだけで真っ先に顔を見せるはてブ、悲しいけれどお別れだ!

はてブ見ていたと思ったらいつのまにかHackerNewsを見ていた!何を言ってい(ry

そんな状態を作り出すため、ちょろっと考えたのが b.hatena.ne.jp から news.ycombinator.com へのリダイレクト。
hostsファイルで行けるかなーと思ったのですがやっぱそんなことはなく、proxy通すのもなーと考えて、安直にブラウザの拡張で逃げる!
探してみたけど「うーん」というレベルのものしかなかったのでchrome上にオレオレブラウザ拡張を作ってしのぎましょう。適当にディレクトリを掘って配置するのはファイル二つです。

マニフェストファイル( `manifest.json` )がこっちで、

{
  "name": "Hatebikkuri! Redirector",
  "description": "All your request are belong to us",
  "version": "0.1",
  "manifest_version": 2,
  "background": {
    "scripts": [ "redirect.js" ]
  },
  "permissions":["http://*/*", "https://*/*", "webRequest","webRequestBlocking"]
}

バックグラウンドスクリプト( `redirect.js` )がこっち。

var _redirectUrl = "https://news.ycombinator.com";
var cb = function (details) {
  return { redirectUrl: _redirectUrl};
};
var filter = { urls: ["*://b.hatena.ne.jp/*"] };
var opt_extraInfoSpec = ["blocking"];

chrome.webRequest.onBeforeRequest.addListener(cb, filter, opt_extraInfoSpec);

`chrome://extensions` のページで開発者機能を有効にして、さっきのディレクトリを指定して読み込み、エクステンションを有効にするとあら不思議、はてブ見てるハズなのにいつのまにか HackerNews 見てるじゃないですかー!

こうして都知事が辞職することを発表の三日後くらいに知る事ができる環境が整いました。

と、Chrome 拡張くらい作れるようになっておくといろいろと便利ですというお話でした。
マニフェストを記述したファイルとHTML、JSファイルさえあればよいので結構いろいろ使えますよ。

ちなみに移り気な方はfilterのurls配列についつい見てしまうサイトを追加しておくと捗ります。UIくっつけてきちんとしたものをchromeのwebstoreに登録するのもありかも。
はい、あんまり関係なかったですね。本題に戻ります。

設計

設計と言ってもまぁ、そんな難しくないですね。
やることは以下です。

  • ソースURL取得
  • クローラ
  • 解析
  • HTML生成

と、この四段階に収束します。

ソースURL取得

どこにアクセスして情報ソースのURLを持ってくるかという部分ですね。
RSSであればそれすなわち情報ソースなのであまり複雑ではありません、が、困るのがTwitterやFB、といったソーシャルメディアですね。タイムラインにガガっと。
そこからURLを抜き出します。短縮URLだとカブりがひどいので展開後のを保存。

URLの取得先は、だいたいこんな感じかな?

下処理が必要なのはNews Siteで、URLがたくさん含まれているのでそれらを取得しておきます。
今回はHackerNewsのみを扱ってますが、ほかのは *TODO* に突っ込んでおいてそのうち挑戦しようかと。
HNから取り出したURLは後で使うHTML取得候補のコレクションに入れておきます。あ、ちなみにMongoDB使ってます。

{
    url: String,
    failCount: Int,
    date: Date // fetch した日時
}

適当にこんな感じで。

ちなみにHNのサイトはパースが必要になります。jsdomやcheerioを使うと良いでしょう。ここではjsdomを。
どのようにリンクを抜き出すかはじっくりとHTMLを読み解く事が必要になります。
あ、あとHNをクローリングする時は頻繁にしないようにしましょうね。1時間や2時間に1回くらいでよいかと。

var hnc = require('./lib/hn.js');
var MongoClient = require('mongodb').MongoClient;

function insertUrls(arr, cb) {
  MongoClient.connect('mongodb://127.0.0.1:27017/mynewssite', function(err, db) {
    if(err) throw err;
    var collection = db.collection('news_urls');
    (function insertOne() {
      var record = arr.splice(0, 1)[0];
      console.log('record: '+record);
      var storeObj = {
        "url" : record,
        failCount: 0,
        date: new Date()
      };
      try {
        collection.insert(storeObj, function(err) {
          if (err) {
            cb(err);
            return;
          }
          if (arr.length === 0) {
            db.close();
            cb();
          } else {
            insertOne();
          }
        });
      } catch (e) {
        db.close();
        cb(e);
      }
    })();
  });
}

hnc(function(err, res) {
  if(err) throw err;
  insertUrls(res, function(err){
    if(err) {
      console.log(err);
      return;
    }
    console.log('done');
  });
});

と、このようにして取得しておきます。

おっと、./lib/hn.js はこんな感じ。

var jsdom = require('jsdom'),
    request = require('request'),
    url = require('url'),
    util = require('util');

var HNURL = 'http://news.ycombinator.com';

/**
 * retrieve URL on hn top page
 * Note: please don't retrieve so much, or you'll be blamed.
 *
 */
var hnc = function(cb){
  request(HNURL, function(err, res, body) {
    var self = this;
    self.items = [];
    
    // response filter
    if (body && /^Unknown/.test(body)) {
      return cb(new Error('You are blamed'));
    }
    if ((err && res.statusCode !== 200) || err) {
      return cb(new Error(err));
    }

    // analize the page
    var urls = [];
    var window = jsdom.jsdom(body).createWindow();
    jsdom.jQueryify(window, function(window) {
      var $ = window.jQuery;
      var i = 0;
      $('.title').each(function(){
        if(i%2 === 1) {
          urls.push($("a", this).attr('href'));
        }
        i++;
      });
      cb(null, urls);
    });
  });
};

module.exports = hnc;

短縮URLの展開

ソーシャルメディアは扱わないといっておきながら、そこで流れる短縮URLの展開についてメモ。

これは [request](https://github.com/mikeal/request) モジュールを使うと一発でできます。
`followAllRedirects` というオプションを `true` に設定してリクエストを投げると `response.request.href` に最終的なLocationが入りますのでそれを利用します。

var request = require('request');
var opt =  { method: "HEAD", url: shortUrl, followAllRedirects: true };
function expandUrl(shortUrl, cb) {
  request(opt, function (error, response) {
    if(error) return cb(error);
    cb(null, response.request.href);
  });
}

こんな感じになります。

クローラと解析

node-readabilityが一気にこの作業をしてくれます。
これだけ。なんと簡単なのでしょうか。

var read = require('node-readability');
var fetchUrls = require('./lib/fetchUrls.js');
var MongoClient = require('mongodb').MongoClient;

function summarize(arr, cb){
  MongoClient.connect('mongodb://127.0.0.1:27017/mynewssite', function(err, db) {
    var collection = db.collection('news');
    (function readOne() {
      var record = arr.splice(0,1)[0];
      read(record.url, function(err, article) {
        if(err) {
          console.error('ERR: '+err);
          readOne();
          return;
        }
        var storeObj = {
          url: record.url,
          title: article.title,
          body: article.content,
          date: new Date()
        };
        collection.insert(storeObj, function(err) {
          if(err) throw err;
          console.log('length: '+arr.length);
          if(arr.length === 0) {
            db.close();
            cb();
          } else {
            readOne();
          }
        });
      });
    })();
  });
}

fetchUrls(function(err, res){
  if(err) throw err;
  summarize(res, function(err){
    if(err) throw err;
    console.log('done');
  });
});

あ、fetchUrls.jsのソースも

var MongoClient = require('mongodb').MongoClient;
var util = require('util');

function fetchUrls(cb) {
  MongoClient.connect('mongodb://127.0.0.1:27017/mynewssite', function(err, db) {
    if(err) throw err;
    var collection = db.collection('news_urls');
    collection.find( { failCount: {$gte : 0} }).toArray(function(err, res){
      if(err) {
        db.close();
        cb(err);
        return;
      }
      db.close();
      cb(null, res);
    });
  });
}
module.exports = fetchUrls;

MEMO:
RSS取得について少しメモ。

ニュースサイトの記事とRSSでは性格が異なります。
で、RSSはフィードのコンテントタイプがhtmlの場合があるので、残念ながらそのまま出力というわけにはいきません。

RSSの取得には [feedparser](https://github.com/danmactough/node-feedparser) という便利なモジュールがあるのでそれを使います。

保存項目はタイトル、URL、description、取得日時、ダミーのステータスコード
で、タイトルとdescriptionですが、htmlに一度埋めて、先ほどのものと揃えます。
これはもう、あとの分析でニュースサイトの記事と一緒に解析するためです。省力化。
といいつつRSSは後回し!です。

<html>
<head><title>タイトル</title></head>
<body>
description
</body>
</html>

という形で入れておきます。今回、解析モジュールにnode-readabilityを使って一気に省力化を図っているのですが、今の段階だとURL->サマリーという流れなのでそこを変更する必要があります。

boilerpipeやgooseなんかを使えるようにしても面白いかもしれませんね。

解析部分については巷のWebデータ解析屋さんは何を使っているのでしょうかねー。K-means、EM、Farthest Firstやクラスター分析などなど面白そうなトピックがあるので、解析好きな人は深堀りしてみると良いと思います。

UIとAPI

UIは自由にしてもらうことにして、今回はAPI叩くとJSONを返すようにしておきます。

JSONで最新20件を返しましょうか。

{
    "articles" : [
        {
            "title": "title",
            "url" : "http://link",
            "body" : "some words here",
            "date" : ....
        }, ...
    ]
}

で、expressの簡単ソースがこっちです。

var express = require('express');
var app = express();
var MongoClient = require('mongodb').MongoClient;

app.get('/', function(req, res){
  MongoClient.connect('mongodb://127.0.0.1:27017/mynewssite', function(err, db){
    if(err) {res.send(500, err);}
    var collection = db.collection('news');
    collection.find({}, {_id:0}).sort({date:-1}).limit(20).toArray(function(err, result){
      if(err) {res.send(500, err);}
      var jsonObj = {"articles": result};
      res.json(jsonObj);
      db.close();
    });
  });
});
app.listen(9090);

と、ざっくりとした概要はこんな感じでしょうか。
どの言語でもさらりとかけるような便利なモジュールが揃っていると思いますが、今回はNode.jsで挑戦してみました。
皆さんも自分で情報収集のためのコードを書いてみると良いと思います。

あとがき

最後に `chrome://extensions` を開いて、いまいましい自作リダイレクトプラグインの横にあるゴミ箱ボタンをクリック。これでようやくはてブが見れます!!!

海外カンファレンスの歩き方 - NodeDublin編

はじめに

技術ネタじゃないです!もし期待されてた方がいたらごめんなさい。今回は、海外のカンファレンスに行ったことがなく、これから行くことを考えている人、もしくは行ってみても良いかもという人向けにカンファレンスに行く際のTIPSをまとめてみました。で、もちろん自分はNode大好き、かつNode学園祭のアドカレなので実例としてはNodeのカンファレンス、です。ちなみに世界中でJS/Nodeに関するカンファレンスが数多く開催されています。ホント、世界レベルで見れば月二回以上は開催されてる?ってくらい。
今回出席したNodeDublinの翌週にはEmpire.jsRealtimeConf(これJSに限らないかもだけど)なんかが開催されています。JS/Node界隈で大きなカンファレンスといえばJSConfとNodeConfですね。JSConfはアメリカとヨーロッパで開催されていましたが、新しくオーストラリアでも開催されるみたい。NodeConfは今度のNode学園祭でも来日するMikealが主催するもの。

目的

自分にとっては技術セッションを聞くこと以外にも、いかに自分が取るに足らないかを自覚する旅、ひいてはモチベーションを高めるため、また海外に友達を作る旅、でもあります。基本一人です。というか、一人の方がいろいろ経験値を稼げるのでむしろぼっち推奨。

選定

やっぱりアメリカ・ヨーロッパで開催されるもの。その方面で行われるものであればだいたい英語が通じるので。出席者がその他ロシア、南米、中近東、アジアからの人でもそこに来る人はみんな英語話します。今回話した人たちも英語が母国語じゃない人ばかりでした。ということで今回はNodeDublinに照準を定め、いざ自腹でカンファレンスGo!自転車買ったり、Adobe製品アップグレードしたりするための貯金を取り崩してGo!

費用

海外のカンファレンスのチケットってたいていの場合高いですね。だいたい安くて5万円くらいからかな。Nodeだとまだこの金額だけど、DB系やエンプラ系のミドルウェアJava方面になると10万以上のも結構あります。価値をそこにちゃんと見いだせるか、です。ご覚悟!
飛行機はだいたいヨーロッパでもアメリカでも9万円くらいから12万程度とヨーロッパのほうが高いってことはないです。どっちもおんなじくらい。ホテルは都市によってこれでもかってくらい全然金額が違う。他の都市に比べて、サンフランシスコやニューヨーク、パリみたいな観光地だとめっさ高いです。うまく都心を外せばちょっとは安くはなりますがそれでも高い!幸いにしてダブリンは安い方。円高もあって、今回泊まったところで6,000円ちょいくらいだったかな。総額でいうと20万円ちょいくらい。Retina MBP買えるって?モノより思い出!! いや、カンファレンスは将来へ続く人間関係を構築できるというところで単なる思い出よりもさらに価値ありですからね!ただ、高いのは認めます!

準備

カンファレンスチケット

早割のチケットを用意しているところが多いです。Earlybirdって呼ばれてるもの。金額が結構違ってくるので、行くことを決心したなら早割チケットの購入をオススメ。ただ、限定数の発売であることが多く、人気のカンファレンスでは争奪戦になります。今年のGoogle I/Oのチケット売り出し時には6,250クエリ/秒ものリクエストを記録したとか。しかも売り出しが現地時間で都合のよい時間、つまりこっちだと真夜中だったりするので大変です。人気のカンファレンスはチケット代金が値上がりすることもあります。例えば先ほどのGoogle I/O、2011年は$450だったのが2012年はなんと倍の$900。来年でいいやと思うよりは「今年のうちに!」と気合いで行ってしまうのが良いかもしれませんね。
無事カンファレンスのチケットがとれたら次に飛行機とホテルの予約。SkyScannerExpediaなんかのオンライン予約サイトが一番。H.I.S.なんかもあるけど、見積もり比較すると旅行会社のは結局オンライン予約サイトの安さに太刀打ちできません。差額が万単位になるともうこっちでいいやってなります。早めに予約しないととれなくなったり高くなったりするので気をつけて!ちなみにSkyScannerはandroid/iOS向けにアプリがあるんですが、なかでもiPad向けのはヤバいです。検索しているうちに海外に吸い込まれます。

飛行機の選び方




non-stop or 1stop
直行便で行くか、それとも中継便で行くか。です。中継地があると荷物ちゃんと載せ換えてくれるかなーとかいろいろ心配なんだけど、中継地到着と中継地発の時間をうまくずらせれば中継地でしばらく楽しめることも考慮に入れておこう。ちなみに乗り継ぎは中継地到着から乗り継ぎ便出発までが24時間以内のことを言いまっす。今回は中東経由だと安かったんだけど結構な迂回コースになるので成田発パリ経由ダブリン、ダブリン発アムス経由成田というコース。
stop-over, open-joe
乗り継ぎと違い、24時間以上、滞在できるのがストップオーバー。目的にたどり着く前にちょっと他の都市も観光したい!ってときにお役立ちです。また、到着した空港と違う空港から出発して帰ってくるのがオープンジョー。例えば今回の場合で言えばダブリンに到着したけどイギリスのヒースローから帰ってくる。なんて方法。どうせ行くならちょっとゆったり回ってこようかって人向けですね。
通路側?窓側?
足のばせる非常ドアのところ、あとは基本、通路側オススメです。窓側であれば最後方に近い席もおすすめ。後ろの方だとデカい飛行機でも窓側ブロックの座席の並びが3つから2つになって通路に出やすく、テーブル使われているとき以外はトイレに行きやすいので。あとはSeatGuruなんかで良い席がないか探してみましょう。
到着時間
自分の場合は目的地に朝着くパターンが一番あってます。目的地着いて、歩きまくって、疲れまくってから夜寝るとジェットラグ少なくてすんでる感じ。逆に夕方・夜に目的地に着くパターンだと寝れなくて、結局何日か時差ボケに苦しんじゃいます。これは個人差ありだと思いますが。
ラッキーアップグレード
ビジネスクラスに空きのある場合、エコノミーから無料で席をアップグレードしてくれる場合があります。でもこの仕組みも市場経済に支配されているのです。座席にはクラスというものがあって、高い金額払った順にそのアップグレード権が回ってきます。もしくはマイレージの上級会員のクラス順。しかもビジネスクラスが空いてる保証もありません。なので普通に格安求めてオンラインサイトでチケット買ってる僕らにはほぼ関係ないの。無念!さらにはエコノミークラスの中でさえもマイレージのクラスによって差がつきます。エコノミー内の良い席はマイレージの中級以上クラスの人が優先的に指定できる仕組みになっているのです。なんという格差。
フライト日
フライト日を前日にしたり翌日にして再確認すると「うわっ・・・私のチケット代金高すぎ!」みたいなこともままあるのでいちおう確認して悩んでみるべき。
旅行費用の支払いはクレジットカードで
カードの中には旅行代金をそのカードで払うことで保険カバー率が高くなる、もしくはそのカードで支払うことによって保険が初めて適用されるものがあります。自分の持っているカードの約款を確認してみましょう。ちなみにカードの記名がパスポートと同一でないとややこしいことになるので気をつけて!
ESTA

アメリカに行く場合、電子渡航認証というものをしておく必要ありです。空港でも出来ないことはないですが、たまにサイトが落ちてたり、認証が降りるまで時間がかかる場合もあるので早めに行っておきましょう。

現地通貨/クレジットカード/キャッシュカード

現地通貨をどうするか。前もって両替しておくかあとで現地で引き出すかになると思いますが、クレジットのキャッシングを使うのが一番便利。限度額を確認しておきます。アメリカならそこいら中にATMがあるのですが、日本見たく、屋内に設置されておらず、無造作に剥き出しな感じ。あとJCBはDiscoverカードが利用可能なところであれば使えます。Discover使えるお店で店員さんがこんなカード見たことないと突き返してきたら、使えるからリーダに通してみてよとお願いしましょう。VISA/Masterなら無問題。AMEXだとヨーロッパでちょっぴり苦戦するかも。
キャッシュカードは結構面倒で、新生銀行のだとキャッシュカードがそのまま使えるんだけど他の銀行だとインターナショナルカードの申し込みが必要だったりします。で、最近、手数料が割に合わないのかインターナショナルカードの発行受付を停止する銀行がどんどん増えているご様子。ただ、銀行のうちいくつかはVISAのデビットカードを作ることができます。これがなんなのかというと要するにキャッシュカードみたいなもので、スキミングなんかの記事を読んでクレジットカードを使うことに疑心暗鬼になってしまっている人はこちらもオススメ。

服装

夏なんかだとうっかりね、Tシャツとショートパンツなんかで行っちゃいそうだけど、目的地の気候はちゃんとおさえておこう。特にサンフランシスコの場合、7月8月なんかはちょー涼しいです。というか寒い!!ま、服は現地調達するから大丈夫ですという人はそれでいいです。あっちのブランドものなんかはもちろん現地で買った方が安いかもですが、こっちのイオン価格の格安レンジ服は土地鑑ない限り探せないと思うので安くあげたい人はあらかじめ持って行くべき。ちなみにサンフランシスコにはこの夏、ユニクロがユニオンスクエアの近くにオープンしました。

海外旅行保険
なぜ海外旅行保険
日本のは例えば、入院日額5,000円など、契約した保険金額が定額で支払われる「定額払い」方式、対して海外旅行障害保険のは実損払い。で、日本の定額払いの金額じゃぜーんぜん足りない場合がほとんどなので入っておくべき。ちなみにアメリカだと救急車呼ぶだけで10万(もちろん救急車の中で手当すればそれも加算)、入院10万円代半ば、手術費用数百万円。いろいろ合算して1,000万円以上ってのもあります。なので救急車呼ぶときや病院の受付など各所で支払い能力がちゃんとあるのか聞かれます。アメリカの医療ドラマ見てて、ちょっと難しそうな病気の人が出てくるとおそらくウン千万単位の治療費なんだろうなーといろんな意味でgkbrです。
カバー期間
自宅出発から自宅帰着まで。ネットでやっとくと良いです、空港でも入れるけど、それだと自宅から空港までカバーされないのでちょっとだけ勿体ない、かつネットで申し込むと安くなったりするので。
プラン
保障内容を自分でカスタマイズできるサイトでやると吉。傷害死亡はそんなに必要ないと思う。携行品のも特に必要ないかも。そんな高いもの持ち歩かないよね?査定されるとMacもちょー安くなっちゃうし。それよりは夜中に危険なとこうろつかないとか予防策を講じるべき。と、この辺は人によって必要と感じる保障がバラバラでしょうね。
クレジットカードにも保険ついてるものがある
自動付帯と利用付帯というのがあって、旅行代金をカードで払った時に保険適用されるのが利用付帯。で、クレジットカードの保険だけじゃカバー範囲、金額ともに不足することが予想されるのでやっぱり海外旅行保険入っておいた方がいいです。ちなみにクレジットカードの保険だと疾病死亡ついてないので気をつけて。あと、カード複数毎持ってたら加算可能みたい。ただし、傷害死亡/後遺障害については加算されません。また同一ブランドカードは加算されないこともあります。
持ち物

パスポート・スリッパ・クレジットカード・薬飲んでる人はもちろんその薬。あと、飛行機のチケット、海外旅行保険かけた人は契約書を印刷したもの。expediaみたいなオンラインのサイトで買った人は旅程を印刷したものがチケット代わり。航空会社の中にはケータイでチェックインできてペーパーレスで行けちゃうところもあります。あと、スリッパですが、たいていのホテルにはスリッパないし、売ってるところはあっても探すのは結構至難の業。なのでスリッパ持ってくといいです。あとクレジットカードないと相当不便です。なんせスタバでも普通にカード使えるくらい。できれば買い物なんかもカードの方がいいような気がします。スキミングされるかもっていうリスクはもちろんありますが、非がなければそこは保険ききます。
あとは服、PC・ケーブル・アダプタ類、持って行く人はカメラ、あと洗面用具くらい。ホテルのアメニティは全く期待できないのでこっちから持ってくのおすすめ。シャンプーとか。体洗うスポンジも持ってくのおすすめ。液体はやっかいです。ヘアジェルとかも含めて。100ml以上の容器に入ってるものは機内持ち込みが制限されていて、それにダメだし食らうのは機内持ち込みの荷物の検査時。ということは大きめの荷物を預けてしまっているとにっちもさっちも行かなくなってアウト、その場で手放さなければいけません。
スーツケースやバッグなどはなるべく地味目、服装もなるべく目立たないものを。強盗目線で魅力を感じないものを選択します。着替えはTシャツ何枚かと下着、靴下くらい。寒い時期はパーカもしくはセーター類と上着も。荷物は自分の場合、機内持ち込みできるサイズの小さなスーツケースと、Macが入るサイズのバックパックだけ。
ほか、目的地がアメリカの場合にはカップラーメンだのカレーだのは持って行かないこと。税関で引っかかります。肉類ダメ。肉エキスもダメ!絶対!

出発


都内に住んでいて、成田から飛行機飛ぶなら成田エクスプレスで行くのがやっぱりおすすめ。往復割引なら片道2400円。座席間隔広いのでゆったり。あとでエコノミー席という監獄にぎゅーぎゅーに詰め込まれるからせめて列車くらいはゆったりと。
まず空港着いたら自分の乗る便に遅れがないかまずはチェック。
空港で荷物を預けるのに時間結構かかります。国内線みたくすぐにパパッと終わるものではないので余裕は見ておくこと。もし荷物を絞って機内持ち込みオッケであればチェックイン機でパスポートと予約の情報を入力するだけで大丈夫。あ、液体(お酒とか)が荷物にある場合は前述したとおり機内持ち込みできないので預けるしかないです。あと中継地で降りてちょっとの間その街の雰囲気を味わってくるのなら極力預けた方が楽。最終目的地で荷物届いてるかどうかドキドキだけどね。
ユニクロで服を買って行きたいとかは国内の各空港にありますので荷物検査や出国手続きの前に買っておきましょう。ちなみにユニクロのTシャツはおみやげに便利なんだけど(今なら攻殻機動隊のとか、まぁギーク向けおみやげですな)店舗が限定されていて、空港のには売ってません・・・。
いろいろゲートをくぐったら免税店がたくさん。もしスリッパ買い忘れたらここでも買えないことはない・・・かも。自分は足おっきいので前もって買わないとならんのですが。

搭乗

アメリカはどっちかというと仕事で出かける人が多いみたいですが、ヨーロッパはツアー客満載な印象ですね。今回は教会の神父さんが横に座っていて、いろいろカトリックについて教えてくれました。飛行機に乗ってる時間ちょー長いからね、いろんなこと聞きました。カトリックプロテスタントって仲悪いの?とか教会って普通の工務店にお願いして作るの?とかバチカンって国家じゃないとダメなの?とか修道院の話とかいろいろ。興味本位でズバズバ聞いてしまい申し訳ありません。

機内食日本食?洋食?

日本の空港と接続する便だと日本食か洋食選べたりします。日本食のほうが失敗度、つまりちょーまずくて食えないよこんなのっていうのは少ない気がします。とはいえ機内食なのでおいしさも期待できません。さらに日本食もない場合があります。基本、航空会社によってうまいかまずいかがだいたいわかるみたいですが、機内食でフライト決めたりしないよね。まずくても我慢の子、コンビニ弁当の方が断然おいしいんじゃって時さえある。それでもエールフランスなんかでシャンパンが食前に出てくると多少盛り上がっちゃうよね。



ということで今回、ダブリンへの直行便はないので、到着した中継地はパリ。午前4時半。ダブリンへの飛行機出発までは間があるのでパリ市内をちょろっと回ります。まずはATMでお金を幾ばくか引き出す。で空港から市内へ電車移動。着いたのまだ5時半、ちょー寒い、暗い、トイレない。
ヨーロッパはもうちょっとで冬時間に変わるってところで、8時にならないと明るくならない。7時にようやくパン屋さんがオープンするんだけど、まだまだ真っ暗。歩きまくった末、カタコトのフランス語駆使してなんとかクロワッサンの中サイズを二つゲットして、ようやく夜が明けてからはショパンのお墓参りして、地下鉄でエッフェル塔行ってサンジェルマンのカフェでコーヒー飲んだらすぐさま電車飛び乗って空港戻って、自動小銃背負った警備中の兵隊さんにビビりつつダブリンへ向けて出発。なんだか苦行めいてた。

現地到着

まずはホテルへ向かおう。それがたとえ朝であっても。その目的は荷物を預けるため。チェックインの時間になっていなくても荷物は預けられますからね。さらに大抵は自分の泊まるホテルじゃなくても預けられるっぽい。で、空港から市内へはバスか鉄道が安いですが、どでかい荷物の場合はシャトル(乗り合いタクシーみたいなもの?)かタクシーがやっぱり便利っす。今回はもちろんバス移動。サンフランシスコに行く場合でSFOからならBARTという地下鉄みたいなのに乗って行くと、マーケットストリート周辺のホテルであればすぐ近くまで行けます。でもBARTの駅ってちょー暗くて駅名が見にくいので最初のうちは全身全霊で聞き耳を立てるべし。あと、自動改札機がよく壊れてるので注意。

SIM調達

SIMフリーのケータイがあれば超便利(iPhone5だと合うSIMがまだなかったりするかも&地図が残念)。現地でプリペイドSIM買ってしまえばそれでオッケ。あらかじめキャリアのショップでいくらか払ってSIMロック解除してもらうとよいですね。自分の使っている端末の対応周波数はあらかじめ調べておきましょう。せっかくロック解除しても渡航先のキャリアで使えないと切ないので。
もし期間長ければあっちでケータイごと手に入れるっていうのもあり。ローエンドならスマフォも安いので海外パケホーダイを使うより安い場合がままあります。で、端末がテザリング対応ならそれだけで外出時のWi-Fi環境も整う(SIMロック解除した端末でテザリングできるかはやってないのでわかりません)。
あと、WiMAXは実はアメリカでも使えます。ただし、ルータタイプのは使えません。PC内蔵タイプで通信モジュールにインテルチップ使っているヤツのみ。切ない。
http://www.uqwimax.jp/service/price/voyage.html
Wi-Fiルータが使えればいいのに!って思っている人は結構な率でいるんじゃないかと。アメリカであればLTE利用のルータがプリペイドSIM合わせて1万円数千円程度くらいかな。これからも何度も行くぜっていう人はこういうの買っちゃってもいいかも。日本から借りてっていうのもありだけど、なくしたりとられたり期間が予期せず変わったりというときにちょっとめんどくさいのでその辺気にならなければ。

WiFi

フリーのWiFiがカフェなんかで使えまっす。ま、セキュリティはアレなのでそういうの気にせず、というか気をつけて使える人向け。パスワードかかってる場合がほとんどなので店員さんに聞いてみよう。あと、ホテルではロビーでフリーのWiFiが使える場合が多いです。ネット使うのに部屋だと1日$15取られたりするところもままありますので予約時に確認しておきましょう。ほか、これは日本も一緒ですが、有線のケーブルがひゅるっとおかれているだけのとこも多いのでWebで調べて必要であれば(MBAの人とか)USB-LANアダプタや小型無線ルータなんかを持って行くのが吉。

お役立ちサイト
GoogleMap
いわずもがな、ですね。
Yelp
食べログみたいだけど食べるとこ探す他にもいろいろ。例えばコインランドリーやガソリンスタンド、ドラッグストアを探したり。iOS/Android向けのアプリがあってそれ使うとちょーらくちん。(ただしヨーロッパではYelp内の地図があまり信用ならん場合もあるのでGoogleMapでダブルチェック)
TripAdvisor
ホント観光したいならこういうサイトもアリかと。あとはローカルマガジンのWebサイトでいろいろ情報収集すると良いです。
Crimemap
サンフランシスコ周辺だとcrimespottingとかね。一応覚悟のため。

カンファレンス

会場到着






たいてい朝8時くらいからレジストレーション開始。自分の氏名告げてネームプレート受け取るだけ。ネームプレートはなくさないようにね!で、簡単なごはん用意されてます。座席は決まってません。ただ、スピーカー用にリザーブされている席もあるのでそこだけ気をつけよう。あと、たいてい無限コーヒーもあります。今回の会場はギネスストアハウス。ギネス本拠地。ちなみにカンファレンスではノベルティでTシャツを何枚かもらえることが多いので、それを想定して日本から持っていくTシャツの枚数減らせます。
周りでは馬も走ってました。

着席。セッション開始!

WiFi接続はパンフに書いてあるか、もしくは会場でアナウンスされます。わかんなかったら周りの人にきいてみよう。ノートを取りながらセッションをきこう。その場で質問できるものはして、わからなければ後できこう。
ホントいろんな人が話してた。
ただし中にはとんだハプニングも。

フォグマシーンが発狂。「何か質問ある?あ、ゴメン。そもそも手見えない。」

で、ためになるセッションの数々。ストリーム、モジュラーアプローチなどなど。セッションの内容を僕がだらりと書き連ねるよりはKarolinaがうまくまとめてくれているのでそちらを参照あれ。
今回びっくりしたのはSwedish Pirates Partyを立ち上げた@falkvinge、つまり政治家もセッションでトークしていたところ。
日本だと政治家は所詮ぼくらエンジニアの代弁者じゃないし、こんな場には出てこないので印象深かったです。ちょーパワフルトークで、なるほどなーと。

お昼、休憩時間

ランチ時は会場近くに場所が用意されます。サンドイッチなど簡単なものが用意されることが多いですね。あとはケータリング屋さんのものとか。ケータラーが会場に来ていろいろ準備します。待ち行列が長めになることが多いので早めに早めに。
「いただきます!」の時間ですが、ま、ぼっちなわけですね。だけど勇気を振り絞り、いろんな人と会話してみよう!
ベジタリアン用の食事もむろん用意されます。"for vegan"と書かれておかれているものがそうです。ベジタリアンじゃないのに取っちゃダメですからね。今回、2日目からはお昼ごはんのタイミングで無限ギネススタートしてました。

名刺
日本だと初めての人にあったらまず名刺交換からってなるけど、海外だと会話した後に「じゃ、またね。なんかあったら連絡してよ。」って時に渡します。いきなり名刺渡すと怪訝に思われるので注意。もちろん名刺を持ち歩いているのは日本人くらいなので相手が名刺持ってるのは確率低いです。基本こちらからの一方通行、渡しっぱ。
セッション終了&晩ご飯

カンファレンスにはたいてい、パーティがセットされているんだけど、晩飯込みじゃなくて、どちらかというと晩ご飯食べ終えたくらいの時間にパーティが開催される場合が多いです。なので試練はまず晩ご飯。あえなくボッチ飯が確定すると切ない。そこかしこにマックやサブウェイあるからそれでもいいんだけどね。やっぱり現地のもの食べてみたいよね。日本だとぼっちでレストランといえども気合い振り絞れば行けるんだけど、あっちでは一人だとやんわり断られるレストランも多いからね!

敷居の低さランキング

  • スーパーでお惣菜チックなやつ
  • ファーストフード、カフェ
  • 中華
  • カウンターのあるパブっぽいとこで料理もだすとこ
  • レストラン

ファーストフードやカフェの類いなら世界共通。楽勝っす。マックやSUBWAYは世界中どこにでもある感じ。その土地特有の、というか日本にはあまりないファーストフードもあります。アメリカだとタコス屋(Taqueria)とか。やむを得ずファーストフードを食べるならせめていつもはなかなか食べれないものを選びたいですね。ちなみにアメリカ行くとスタバで名前聞かれます。別にホントに名前聞きたいわけじゃなくてあとで飲み物できたときの呼び名が欲しいだけなので難しい名前の人は和名(?)じゃなくともダニエルやマイケルなど適当に応えておk。短縮形のダンやマイクでもオッケー。日本だと名前を聞かれて自分の愛称を答えちゃうのってかなりアレな感じですが、向こうでは短縮形もれっきとした名前として通る。ビル・クリントンのビルも短縮形。

で、それよりはもうちょっと現地のものを食べれるよう、パブっぽい雰囲気でカウンターに座れる店を探す訳です。Yelpなんかで評価がそこそこで店内写真あるのから丹念に探して行く。あまり混んでなさそうな時間を狙ってお店へGo! メニューがわからなければとりあえずおすすめが何かきいてみよう。ちなみにメニューのなかでスペシャリテっていうのがその店ならではの料理。個人的にはスペシャリテでアタリを引いたことないので普通に気にせず選んでます。

中華料理はなぜか結構オープンで一人でも気兼ねなく入れる店が多いような気がする。でもね、料理サイズは一人客を想定している訳もなく、ちょーデカめなのでご注意あれ。ハーフサイズで作ってくれってお願いしてもいいかも。残したら包んでくれるよ。昔は日本でも結構それやってくれるとこあったけど、今だとなかなかない。残したの包んでっていうと「!!!!!」って顔されたりするよね。もし持ち帰ったものを食べるまでに時間が経ってしまい、それ食べて食中毒にでもなられたら大変だっていう日本的予防策の一つなのかもだけど。それはさておき、もらったのはホテルに持って帰って食べてもいいし、帰る途中でホームレスの人にあげてしまってもいい。だからそんなのいらないなんて言わないように。

支払い時、チェックしてというと伝票ホルダに伝票を挟んで持ってきます。カードならカード挟んで、現金なら現金挟んでお店の人に渡します。で、カードを出すともちろんカードの伝票を持ってくるんだけど、それにチップを上乗せして支払金額を自分で書く。なので伝票には合計金額とチップの金額欄、これらを合計した支払金額欄というのが設けられています。チップの金額は省略して支払金額だけ書いてもいいみたい。ファーストフードやカフェなど、レジで会計済ませられるところはレジ横にチップ入れるビンがあったりするので入れるか入れないか含め、小銭なりドル札なり、ご自由に。

もう一つ大事なこと。一人で食事していてトイレ行く時に貴重品など取られて困るものは必ず一緒に持って行くこと!!!盗まれると警察行かなきゃならないとかでホント時間無駄にするし、中にパスポートが入ってましたなんてもう目もあてられません。なんでそんな大事なものを置きっぱなしにしたのかと大抵は同情してもくれないですからね!

パーティ

パーティこそがセッションに参加する理由そのもの。
今だとセッションってオンラインで共有されることが多いし、家にいながらでも知識は得られる。でもね、友達作りはなかなかオンラインじゃできない。今度また一緒に飲もうぜ!なんて言えないじゃんね。なのでパーティを欠席するのはめちゃめちゃもったいない。自分の大好きなテーマについて掘り下げていろんな人と話す機会なんて他にそうそうないでしょ。あのちょー高いチケット代はまさにこのパーティ出席の為と心得ること。

とりあえず、ここでもまずはボッチから始まる訳だよね。
勇気を振り絞って他の人に声をかけよう。じゃないと見ず知らずの僕らに声かけてくれる人なんていないからね!日常会話でとりあえずはおk「どっから来たの?」とか「何やってるの?」って。で、好きな言語の話をしてあれいいよねこれいいよねーっていう話。相手の話に耳を傾け、興味を持って積極的に話していこう。とりあえずお酒が入るから、話しかけるこっち側、話しかけられるあっち側にもある程度の余裕が出来ていて思ったよりも楽に進むはず。
とりあえず陽気に行こう。緊張した顔や無表情っていうのは相手とのコミュニケーションをあらかじめ拒否しているようなものだから元気に!表情をきっちり作れるっていうのはとても大事なこと。もちろん会話中も表情豊かに!
パーティで気をつけるべき点は飲み過ぎでへべれけになってしまう点。あくまでも自分は基本、ぼっちであり、誰の手も頼れないということを認識すべし。記憶を飛ばすと厄介なことになります。たとえ無事ホテルに着けていても、昨日楽しかったな!ってメールが翌日に来てももはや誰だか、何の話をしたのかも覚えてないのは辛過ぎるのです。記憶を保持できる量に留めましょう。





会期が2日であればプレパーティ含めて3回くらいあります。今回は到着時の体調最悪でプレパーティは出席しなかったので2回出席。1日目のパーティはThe Sugar Clubという、由緒正しそうな劇場型のトコ。日本でもこういうとこあるのかもしれないけど、テーブル付きのちょー余裕のある映画館みたいなとこ。もちろんFreeDrinkで無限ギネス。

まず始まったのはアイリッシュウイスキーテイスティングイベント。各アイリッシュウイスキーの歴史やテイスティングのポイントなどを順々に説明していきます、で、係の皆さんの手により、少量のウイスキーがビックリドッキリメカ並みに運ばれてきます。前列に座っていたのでまず配り始めに一つ、で配り終わってからおかわりどうぞってさらにもう一つのダブルカウント。
maxとnexxyが後ろで次の日のセッション向けのハードウェアを半田ごて使いながら組み上げているのを見ている間、となりのおじいちゃんとウイスキーのお話を。このおじいちゃん、カンファレンスで見たっけかなあ。ちなみにmaxはぬこが大大大好き。日本に来ることあったら猫カフェに連れてってあげたい!テイスティングが終わると次にイーリアスパイプの演奏。ケルティックの香り漂うなんとも郷愁を感じる演奏。って弾いてるのさっき話してたお爺ちゃんじゃんね!

これらが終わるとどこからともなく楽器が出てきてその場でのセッションが繰り広げられる。みんななんでそんな楽器弾けるの??ってくらいヨーロッパって確実に楽器を演奏できる率が高い。この日は早々に切り上げます。この日ラストまで飲んでた人に聞いたらなんと朝5時半まで飲んでいたとか。
2日目のパーティ。アイリッシュパブ


この日ももりもり無限ギネス。でも後でもうビール飲めないよって頃に聞いたのがいやいやアイルランドでもギネスだけじゃなくてマイクロブリュワリー文化っていうのがあってねって話。ギネスは日本でも飲めるんだから、もうちょっとその地でしか飲めない、いろんな地ビール飲んどきゃ良かった!しばらく飲んでると地下もオープンしたとのお知らせ。アイリッシュパブの地下って何か恐そうなイメージだけど、行ってみたらそこはクラブでしたという・・・なんかNodeのカンファレンスって少なくとも会期中1回はクラブでパーティっていうのが通例になってる気が・・・というかオープンソース系のカンファレンスってクラブでパーティやるの多い気がしまっす。今回はPlaidがDJで来ててちょーびっくり。で、ユニコーンさんに会いました。実在とは何かについてDJに教授中。ちなみにこのユニコーンさんは@dshaw、いつも陽気なエンターテイナーです。

ぐでんぐでんになりながらもJoyentのDTraceの権威に話を聞いたりGoogleの人とV8のお話したり、いろいろ話したなぁ。というわけでつつがなくパーティ終了であります。

その他会期中の日常

街を巡る
交通機関
バスやら地下鉄、鉄道。日本のNavitimeや乗り換え案内は絶大なアプリだなって感じる瞬間です。そういうアプリあんまないみたいなので行動は余裕を持って!ヨーロッパだとでかでかと駅の表示があったりするんだけど、アメリカだと駅の表示がちっちゃかったり、車掌さんのアナウンスも日本ばりによく聞き取れなかったりとドキドキです。ま、でもダァシェリエスは海外から来た旅行者にとっては宇宙語だよね。たとえちょっと日本語かじった人にとっても。移動にタクシー乗るのもありですが、アメリカだとカードで払った場合、結構な額のチップ組み込まれる場合もあるので現金払い推奨。ちなみにダブリンでは日本でもよく見かける欧州車や日本車に混じってSkodaというチェコ製の車もタクシーではメジャーでした。運転手さん曰く、いろいろトータルで超エコだよ!とのこと。乗ってみた感じ全然悪くなくって欧州車ならではの固さもちゃんと健在。あ、バスなんかは前のドアから乗ります。ここは東京と一緒。
歩く
自分の場合は基本、歩きです。歩き回ります。体力使いまくって夜ぐっすり眠って時差ボケ防止。だいたい一日20kmくらいは普通に歩き回ってるかも。
街にとけ込む

カメラ下げてガイドブック持ってさらに携帯みてる、という典型的観光客の行動は日中は許されても夜中は控えた方が良いです。強盗、詐欺のいいエサです。周囲への注意がそがれるので携帯やタブレット眺めながら歩かないこと、持っている携帯を強奪されたりもしますし。ぱっと見地元の人間と思われることを心がけよう。もちろんそれでも無防備になった瞬間つけ込まれる可能性はあります。たまに100メートル先からでも日本から来たことがバレバレな女の子グループがいたりしますが、危なっかしいことこのうえないですね。

心のよりどころ

カフェ探し
基本はぼっち。でもちょっとそれだと寂しい。だからせめて期間中に気軽に寄れるカフェぐらい用意しておきたいところ。あまり混んでいない、でもWiFiのある店があればベスト。日本からちょっとしたおみやげ持ってって、ちょっぴり顔なじみになったタイミングで渡せると旅の期間中、くつろげる場所を確保できます。必要ないかもだけど。カフェ相手なのでおいしいコーヒー豆をよく自分は持って行くかな。いろいろよくしてくれたらチップも忘れずにね。で、今回のカフェ。アイリッシュブレックファストはソーセージ、たまご、ブラック/ホワイトプディングとパン、あとは豆料理とかトマトなどちょっとした野菜が付くのが標準みたい。
おみやげ

おみやげ渡す人がいるかもって場合には、ベタなところでオリエントバザーなんかで「ザ・ジャパン」的なものがリーズナブルに手に入るので結構おすすめ。あと、扇子なんかも人気の様子。でも目的はカンファレンス、そんなにそんなにおみやげが必要になる場面はないと思いまっす。

洗濯


あらかじめコインランドリーが近くにあるホテル選ぶとトータルパッケージが減ってちょー楽。(ホテルのランドリーサービス高いし・・・ま、風呂場で洗濯するのもいいんだけど)洗剤持って行かなくても大抵のコインランドリーでは洗剤の自動販売機があります。アメリカのドラッグストアで買おうとするとばかでっかいのばかりなので注意。え?ちっちゃいの見つけた?多分それ柔軟剤!自分も買って失敗したもんね。

チップ


アメリカに行くのであれば出発時に1ドル札に両替していくといいです。$100なら帯封付き!チップだけでそんなに使うわけではないですが、交通費だのなんだのに使うし、帰る時に余ってたらドラッグストアなんかでiTunesストアカードを買ってくるとよいかも。なぜこちらで両替していくのか、ですがあっちはもうクシャックシャなドル札ばっかなのでチップで新しいお札渡すとみんな喜ぶのですね。反対に両替の窓口の人にはいやがられちゃいますが。
ちなみにヨーロッパではチップはたいてい硬貨。ユーロは国毎に硬貨の模様が違ってます。アイルランドのはアイリッシュハープ。ギネスなんかにも使われているアレです。

その他持ちものについて

カメラ。SONYのRX100がお気に入り。

SONY デジタルカメラ DSC-RX100 1.0型センサー F1.8レンズ搭載 ブラック Cyber-shot DSC-RX100

SONY デジタルカメラ DSC-RX100 1.0型センサー F1.8レンズ搭載 ブラック Cyber-shot DSC-RX100

良いところ
軽い・小さい
ポケットに入るサイズ。ちょっと分厚いけど入ります。首から下げなくてもいいのはウレシイところ。
暗いとこ強い(それでもシャッタースピードぎりぎり)
基本的にカンファレンス会場はプロジェクタを利用して資料を投影する都合もあり、結構暗いです。なので高感度が使い物にならなければシャッタースピードが遅くなり、被写体ブレを防ぐことができません。RX100は高感度強いので助かります。テレ側でのF値がもうちょっと明るければ良いのですがなんとか高ISOでカバーできる感じ。
USB充電
充電アダプタ要らない!バッテリー切れそうになったらMacで充電!
デジタルズームで7.2x
デジタルズームというか超解像ズーム?デジタルズームなんか!って言う人結構いるけど別に引き延ばして使う訳じゃないので結構使ってます。
マニュアルフォーカス
そこそこ使いものになります。

あと、コンパクトなので一脚持ってくと会場の俯瞰が撮れます。よくテレビの音声さんがブームの先にマイク付けてますが、あの代わりにカメラ付けちゃう感じ。写真の場合はレリーズ付けられないのでセルフタイマーをうまく使いましょう。動画撮っても面白い。

良くないところ
オートだとマクロ時にF1.8がデフォルトになるのはちょっと辛い。
F1.8だと被写界深度が浅過ぎて使いづらいのですよ・・・。マクロで料理撮ろうとするとほんと5mmくらいしかピントあってないんじゃないのってくらいになって大変。カバーするために設定保存してそれ呼び出して使うといいです。
操作体系がチグハグ
いろいろとちぐはぐです。修正ファームウェアを是非公開して欲しいところ。それまでは気合いでカバー。
動画フォーマット
MTSという方式で、Macだとめんどくさいです。が、取り込めないこともないです。

ちなみにRAWはあんま使ってません。売り物の写真を撮る訳じゃないし、自分にはJPEGでじゅうぶん。ということでカメラ屋さんに行って実際手に取って、是非心揺さぶられてみてください。
一眼レフはカンファレンス用途だと倍率高めで明るいレンズが必要になって荷物が重たくなるのと、あと派手なシャッター音がどうしても気になっちゃうのであまりおすすめできません・・・。でもやっぱりキレイに撮れるんですよね。判断はお任せします。

旅を終えて

ほんと良かった!みんなに会えたし、自分がいかに小さい存在かまたもや強く自覚したし、これからも頑張っていこうって気になれた!
と、世界中に知己を作ってどんどん進んで行きたいなと思ったところで今回のカンファレンス旅は終了。オーガナイザのCianにはホントホント、深く感謝したい。随所に見られるホスピタリティがハンパないカンファレンスでした。

旅をするのホントオススメ。断捨離なんて言葉があるけど旅をすることで自分にとって必要のあるもの、ないものを結構見極められます。やりすぎるとバックパック背負ってどっかにいったまま帰って来れなくなる可能性もあるけど!


あ、実はNodeDublin終了した翌日にnodecopterのミートアップがあってそれにも顔出してきました。
@Engine Yardダブリンオフィス。

Droneがね、ちょーブンブン飛んでた。

と、期せずしてちょー長くなりました。ごめんなさいごめんなさい。で、来たる11月18日には日本でもNodeのカンファレンスが開催されますね。東京Node学園2012。海外ゲスト陣をはじめ、Node好きな人がこれだけ集まるイベントはそうそうないので是非、後夜祭にも参加してNode話で盛り上がってみるといいですよ!

Sapporo.jsでJavaScriptの成り立ちについてLTしてきました。


当初はNodeのことを5分で話すつもりでしたが、id:tricknotesの「時間はどのくらいあればいいですか?」という有難い申し出を受けて設定したのが20分。

さすがに手元の資料では足りないのでJavaScriptの歴史についても話してきました。

そちらについては資料すら作ってなかったのでホワイトボード使いつつ記憶を頼りに延々しゃべっていくという・・・さらに字が汚くて見えづらかったと思います。すみません。

で、帰ってきてからざざざっと資料を作りました。
まずはECMAのトコまで。

JSってサイドストーリーがとっても多い言語なので突っ込んで調べるといろいろ新しい発見があり、ネタに事欠かない言語でもあります。そういうのを調べて行くとかなりJavaScriptに親近感が湧くようになるのでみなさんも是非。

と、話がずれてきましたがLTの後半に話していたサーバサイドJavaScriptについてはまた別途。


追記: id:koyhoge さんからご指摘頂いたJim ClarkはMosaicに関わっていないというのはその通りですね。
スライドを修正しておきました :) ありがとうございます。

TOEICの点数が上がらない英語勉強法

これは最近よく感じる疑問。普通にきれいな英文を書けているような人でもなぜみんな一様に「もっと英語勉強しなきゃ」って言うのだろうか。もう勉強する必要ないじゃん。

TOEICの勉強している人は今600点だから、700点取らないとと言う。で、700点取ってる人は800点取らないとという。最終的にみんな900点以上を目指す勢い。もちろん何かを勉強することに異論は全然ない。むしろ大賛成だよ。でも「英語学」を勉強している訳じゃなく、その目的は英語を実際に使うことではなかろうか?いや、プロの翻訳家目指していたり比喩や暗喩バリバリの難しい文章を理解できるようにっていうのなら話はまた別だけどね。

英語を学ぶっていうのはつまり、プロトコルを学ぶこと。エンジニア向けに言い換えると「英語を学ぶということはTCP/IPを学ぶようなこと」と言ってもいいかもしれない。そう考えてみると英語を勉強している人っていうのは伝送プロトコルそのものを学びたいのだろうか?自分が思うにそうじゃなくってその先から送り出されてくるコンテンツにアクセスしたいんだよね?

それなら実際にもっとガンガン英語を使ってコンテンツにアクセスしていこう。
漠然と使うっていうのもなんなので手始めに「使う」を4領域に分解してみる。「読む」「書く」「聞く」「話す」の4つ。

「読む」に関しては全然問題ない。インターネットの普及でこれでもかとばかりに読むものがある。エンジニアであれば技術資料なんて英語てんこ盛りなんだからどんどん興味のある分野のものを読んでいけばいい。わからない単語は何度も出てくるようなものは辞書で調べるとしても、一度も見たことのないようなものはそれがキーワードとなっていない限りすっ飛ばしたってかまわない。TIPSが一つあるとすれば「あ、これ見たことあるけどなんだっけ」という単語は辞書を引き直そう。それなりの頻度で使われていてこれからも目にするはずだから。

さて、残る3領域の「書く、聞く、話す」だ。
「書く」っていうのもそれほどレベルが高いわけじゃない。得意とする分野のことについてブログ書いたりもできる。日本の技術ブログ文化って世界的に見てもスゴいんだ。良記事を書くことでそこから生まれるコミュニケーションも期待できるので言語の垣根を越えて興味を持てるものを題材に書くこと。

残るのが「聞く、話す」。
「聞く」と「話す」を切り分けて「聞く」から始めるのもありだろう。でも「話す」シチュエーションには「聞く」ことも含まれるだろうからこの際効率を上げて一緒にしてみよう。

まずは英語が下手な自分と英語で会話してくれる誰かを探さなきゃいけないんだけど、これが超絶難しい。相手にとって片言でしか英語を話せない人と会話するメリットを考えてみれば自明だね。その辺のソリューションが英会話教室なんだろうけど、教室に行く時間を空けなきゃならないし、さらには高額だ。

英語を話せる友達さえ作れればいいんだけどなー。と誰もが思っているんじゃない?

なんだか小学校に入学したときと同じ難題がパワーアップして今再び!って感じだ。
今から考えれば学校ってシステムはすごいって思わないかい?友達作りに理想的な条件が設定されているんだ。同年代の人、もしくは学びたいことが一緒の人たちが一カ所に集まっているんだなんてそうそうないだろう?

そういうインフラを今、僕らはどこに求めればいいんだろうか。

一番手頃だと思われるのはやっぱりfacebook
でもFBで知り合ったからってそれがリアル会話にまで発展するかというとそんなことは滅多にない。たいていはチャットで済むんだから。「オレ、お前とリアルに話したい」って言ったらむしろ相手は引きまくるだろう。

自分の考えた中で最良の選択肢はやっぱり日本語版の出ていない、海外のMMORPGかなと。

MMORPGにはさ、まず目的があって(ボス倒すとか)、その目的を達成するためにレベルを上げるなんていう行動もさることながらマルチプレイヤー同士で「協力」することで心理的な連帯感が生まれるよね。で、その連帯感から友情って生まれやすいのは理解できることと思う。MMORPGを選んだのには理由があって、ゲームがリアルタイムで進行するってところ。テキストチャットで悠長にやりとりしている時間がなくなるとボイスチャットが登場するからね。

ここで引いちゃう人もいるだろう。
まさかヘッドセット付けてゲーム?オタクっぽくない?みたいな。

アメリカってその広大な国土に人がばらけているせいで都市部以外は実は娯楽が少ない。だから映画やテレビの場面を題材にしたジョークが多いのかも、同じ映画やテレビ番組をホント何回も見てるから。ま、そういった背景があればMMORPGが根付きやすいのは想像できるんじゃないかな。結果として相当な数の人たちがMMORPGを楽しんでいる。もちろん、女性も含めてね。親子で遊んでいる人だって珍しくない。

話を元に戻すと、ゲーム中のボイスチャットには「聞く」「話す」のトレーニングとしても最強レベルの効果がある。例えば10人や20人の努力がさ、君が英語の指示を的確に理解できなかったために無為に終わるかもしれないんだ。そりゃもう聞いている側としては最高度の集中力で相手の話す声を、内容を聞き取ろうと努力するハメになる。中にはマイクレベルが高すぎて声が割れていたり、もしくはノイズまじりだったり、もともと声が小さかったり、同時に複数が喋ったり、やけに話の長いやつがいたりとコンディションが良くないのは日常茶飯事。そんな悪条件下だとしても何とか相手の意志をくみ取っていかなきゃならない。だから普通に会話するよりも何倍もの集中力で相手の声に集中するんだ。わからない単語が聞こえたらその単語の重要度、聞き返すべきかなどを瞬時に判断していく。
うまく聞き取れないうちは意味を取れなくて失敗しちゃいけないという責任感から、いろんなサイトを巡って「読む」ことで予習し「聞く」ことを補完することになるだろう。そんなことを続けているうちに君が地道に調べ上げた知識が仲間にとって価値のあるものになる、その段階までくると今度は知識を「書き出し」たり、「声で相手に伝え」たりといった方法でその価値を共有することになる。
そう、いよいよ「話す」時だ。
残念なことにやっぱり最初はうまくしゃべれるわけがない。だから、まず「話す内容」を「書き出す」ことで練習することになる。「書く」ことが前提となっている場面ではもう日本語よりも英語そのままで情報収集したほうが効率が良くなるので「読む」のも同時進行だ。で、ちょこっとした文章を書いては仲間に「これでわかる?」なんて聞いたり。さて、いざ内容が決まり実際に話し始めても「おい今のなんつった?」「一番いい英語で頼む」「$%&*G*Y$@{)F???」などとはじめはホント散々だ。でも何度も赤っ恥書きながら次第に言い回しを覚えて慣れていく。恥をかくっていうのは日本人が極度に恐れることなんだけれども、どんどん恥をかいて凝り固まった自分のくだらないプライドをまずは粉々にぶち壊す。そして学習の螺旋階段をダッシュで上っていこうよ。

そのうちに音声を使ってチャットするのが苦痛じゃなくなって仲間たちとゲームを楽しめるようになるだろう。励まし励まされ喜びを分かち合ったり、喧嘩したりできる仲間とのコミュニケーションの中で文化の違いを学んでいけるようにもなるだろう。例えばさ、アメリカではあんなに犠牲者が出ているのにも関わらずなぜ軍人になる人がたくさんいるんだろうね?家族は反対しないのかな?などとまぁ仲間たちと話をしながら「へぇー」と思うことはそれこそいくらでもあるんだ。

と、MMORPGを英語を使いまくる素敵な手段として紹介してみたわけだけど問題点ももちろんあるから注意だ。

  • ゲーム自体を楽しめないと飽きる
  • そんじょそこらの英語教材よりもよっぽど時間食う。
  • こっちの夜はあっちの朝だ!
  • ゲーム上で使われる語彙に汎用性はない
  • ゲーム自体にハマッて抜け出せなくなる可能性
  • ゲームスキルもある程度必要
  • 人間関係を築けるだけのソーシャルスキルが必要

・・・いろいろあるなぁw
それでもトライしてみる価値はあると思ってる。初期投資もさほど必要ない。

さて、そろそろまとめに入ろう。こういったことを書いてきたのには理由がある。

学習曲線っていうとだいたいこんなS字カーブを描いていて、TOEICの点数が上がれば上がるほど単位時間あたりのスコア上昇率が低くなっていってしまう。だからTOEIC900点取ったぜなんていう人って凄まじいまでの努力をしていてすごいなって素直に思うんだけど、同時にもったいないなとも思うんだ。
TOEICがこれほどちやほやされるのは英語が使える基準をほかにどうやって設定していいかわからないから。実務でどういった局面で英語を使っていたかを履歴書に載せられないからその代わりにTOEICの点数を書いているだけじゃないか?例えばさ、英語を使ってlinuxカーネル開発メーリングリストを束ねていましたなんていう経験があろうものならTOEICの点数なんて霞みまくって書く意味すらない。もしそういった人が履歴書にTOEICの点数がないために足切りされましたなんていったらそれはもう採用側のEpic Fail。だからできることならこのTOEIC一辺倒な世界を打ち破るためにも何か英語で実務的なことをこなしましたっていう経験を積もう。アメリカ人やイギリス人を採用するときにはTOEIC何点ですかなんて間の抜けた質問はせず、実務の話をするだろう?何か光るものを磨こう。ユニークな存在になろう。不安を点数で紛らわすな。「日本人だから英語下手だけど」っていうエクスキューズだけはナシ!そう最初に言っとくことで甘く見てもらおうだなんてそんな言い訳はナシだ!ビシッとさ、どこが悪いか指摘してもらえないと改善は望めない。

と、ちょう上から目線で書いてしまってごめんね!
まだMMORPGやってるのかって?実はもうやってないんだ。仲間たちとゲーム以外の手段で緊密に連絡を取れるようになったらもういいかなと思ってね。
あと参考までに書いておくと自分のTOEICの点数は700点(そんな点数で大丈夫かとか言うなーw)。5, 6年前に受けたっきりなんだけど会社から受けさせられない限り、自分からはもう受けないつもり。ただその代わりに英語を使って何かを始めようって思ってる。
最後に、こんなヒクツなタイトル付けてごめん!でもTOEIC捨てちゃえば点数もあがらないよ!

Yet Another 白熱授業

みなさま、明けましておめでとうございます!

blog の更新が滞ってしまっていて大変申し訳ないのですが、今はちょうど node.js の本を執筆しているところでなかなか node ネタを書けないというかここでネタ書くヒマがあったら本を書けという心の縛りがありまして・・・。


でも新年明けましてということで今日は書いちゃうのだ。

さて、もう半年以上 CS のカリキュラム紹介をしていなかったんだけど、今日は Harvard 大学の CS50 という授業を紹介。折しも元日ということで学びに関連する記事をあげるのにはいい日だと思って。というか今日明日の夜はサンデル教授の白熱教室、再放送もやってることだし!

さて、CS50 はいわゆる CS の入門コース。
どこが白熱なのかというと、まず、やっぱり先生がイイんだよね。彼の名は David J. Malan。彼は Harvard での BA -> CS 専攻型の経歴を持ってて 1996 年にはかの Brian Kernighan に CS50 を教わっている(なんという贅沢!)。で、なおかつイケメン(って言ってもいいよね?)。彼の持ち味はまずはそのマシンガントークだ。勢いに乗ってガンガン飛ばして聴衆を圧倒しちゃうから聞いているほうは退屈って言葉を思いつく暇さえないくらいだ。このCS50 で扱われているのは初級のアルゴリズムC言語。ま、どこの大学でもあるようなカリキュラムの一つだよね。

さて、ではほかの学校で行われる授業とどこが違うのだろうか?

例えば Youtube。この授業の中で彼はよく Youtube の動画を紹介するんだけど、これがもうね、うまいこと見つけてくるんだよね!例えばソートの授業での1コマでは Google の面接をオバマ大統領(このときは上院議員だったのかな)が受ける動画が紹介されていて、その中で Googleの中の人が「100万ある 32bit int をソートするのにもっとも効率のいい方法は?」なんて門外漢のオバマ氏にビシッと聞いちゃってる動画。
それにどう答えるかは
http://www.youtube.com/watch?v=HAY4TKIvSZE
こちらを参照のこと。

ほかにも電話帳を二つに引き裂く動画(バイナリサーチの授業、これはジョークだけど)や
http://www.youtube.com/watch?v=P4fcOLN9heU

ポインタの解説ではスタンフォードの学生(?)が作ったと思しきクレイアニメも紹介しちゃう。
http://cslibrary.stanford.edu/104/

こんな突飛な動画を見つけてきて紹介しては学生の関心をぐっと惹き付けながら授業を進めていくんだ。教科書をただ読んでいてもやっぱりつまらないからさ、Youtube にアップされた動画を使ってサブジェクトを常に身近なものとして捉えてもらい、集中力のレベルを引き上げ、そして記憶として授業の時間内に固定させる効果もあるんじゃないかと思う。僕は心理学者でもなんでもないからこんなことを言うのは憚られるんだけれども。

セメスターが終わりに近づくと最終提出プロジェクトの仕上げをするのにオールナイトHackasonも行われる。豪華なことにその日はピザや中華料理が振る舞われる上、朝まで残った人には朝食付きだ!こんなことが授業の一環として企画されちゃうなんてすごいよね。

そんなこんなでプロジェクトを仕上げた後にはお祭り(CS50 Fair)だ。家族や友達も連れておいでよってさ!
http://photos.cs50.net/Events/CS50-Fair-2010/

さらに CS50 のサイトには通販サイトまであって、そこではなんと「CS50グッズ」が販売されているんだ。CS50のロゴが入ったTシャツやスウェットのほかにクマのぬいぐるみまで!w 日本だと「計算機科学1」とかっていうロゴの入ったTシャツが売られちゃう感じ?

ということで、Harvard の CS 入門のちょっと変わっていたり楽しそうなことを列挙してみたけど、その中身は濃く、プログラムを組んだことのない人が1セメスターが終わってできるようになっていることはかなりハイレベルなんじゃないかな。(僕はCSの授業を受けたことがないのでこの程度はフツーだったらごめんなさい、でももし僕が全くプログラミングを知らない状態から12週でこのレベルをこなせるようになってるって考えたらすごいと思うんだ。はや7週目で Valgrind 触るとか!)
授業で扱われているスライド等々はすべてWeb上からダウンロード可能な上、授業のビデオ自体も全部見れちゃうので、文系卒できちんとした CS の授業を取ったことのないプログラマや中高生には是非オススメしたい教材。しかも2010年度のビデオからは transcript が完備されているので英語のリスニングは苦手だけどリーディングならという人でも全然大丈夫だからね。そのうち transcript 読むのがかったるくなってリスニング力があがるなんていうプログラマ的怠惰による英語力の進化も期待できたりして。

すべては
http://www.cs50.net
もしくは
http://www.cs50.tv
を参照のこと。

と、紹介はここまで。ここからはその授業の成り立ちの話ね。

さて、以前は Sever Hallの 200 人弱しか入れなかった教室で細々とやっていたこの授業はその人気の上昇に伴い 2008 年度からは Sanders Theatre という 1166 名まで入れる教室(というかコンサートでも使われるようなホール)で行われるようになった。件のサンデル教授の授業もこの Sanders Theatre だね。この通り、CS50 は昔から大人気な授業じゃなくって、2002 年には授業を取っている生徒数がわずか 100 人(!)を切る程度にまで落ち込んでしまっていたんだ。でもそれを David J. Malan は見事なまでに立て直した。この辺の経緯については彼自身が「Reinventing CS50」というタイトルで論文にしているので授業改革に携わる教員の方には是非参照してほしい。
http://www.cs.harvard.edu/malan/publications/fp310-malan.pdf
ん、いやむしろすべての人に参照してほしいところ。なぜならこの資料の中に綴られていることは教育界のみならず多くの領域に通じるはずだからね。

せっかくなのでこの論文を独りよがりにちょっぴり説明してみよう。この授業の再開発の主眼は

「学生をふるいにかけるのではなく、CSという分野に目を開かせる」こと。

そのために様々な努力が行われるんだけど、教える側ができるのは学生たちを後押しすることだけ。うん、そっちの側からできることってそれだけなんだよ!授業以外に平均すると15時間の workload が必要で、それだけでもキッツい授業なんだけど、それを教える側が惜しみないリソースを提供することで後押しするんだ。教材を全部自宅から参照可能にしたり、知ってて当たり前の、「ググれカス」とか言われかねないちょっと聞きにくいことなんかもあらためて質問できるように匿名で質問できる掲示板を用意したりね。
そう、学生にとっての親しみやすさを第一に考えるんだ。決してこれはカリキュラム内容を今までより易しくするとかそういうのじゃなくって、例えばコンピューターサイエンスのみならずギークカルチャーや internet meme を紹介したりという演出をしてみたり、Youtube の動画を紹介するのもこの怒濤の90分を学生に乗り切らせるためのちょっとした息抜き用にとして使ったりとかね。
このCS50の授業はほかの授業と比べたらそりゃもう最大の負荷を強いるんだけど、こういった取り組みの結果として Harvard では5番目(今年はもうちょっと上に行ってるかも)にデカい授業になることができたんだ。

と、こんな感じのことが書かれている。むしろこれは資料というより David J. Malan の冒険譚って印象だね。

最後に数字をあげておこう。2010度の CS50 の enrollment は前年比 56% 増の 525人になった。この 56% という数字がどれほどすごいかわかる?就職したらわかると思うけど、たいてい企業内での数字っていうと20%以内、つまり両手両足の指の数の和以上の数字っていうのはほとんど出てこないんだよ?しかもそれをダブルスコア以上だ!!
http://www.thecrimson.com/article/2010/9/13/students-class-cs50-more/
(もちろん facebook創立者たる Mark Zuckerberg の影響もあるんだろうけどね!)

こんな、愛さえ感じられる授業、一度は受けてみたいな。

node.jsとは何か(4)

さて、前回まで基礎部分をいろいろと説明したので今回からは実装について。現在のソースやその成り立ちを説明するのもいいんだろうけど、今日からはちょっぴりハンズオン形式に趣向を変えてみよう。ってことで


node.js を作っていくよ!



実装編その一はJSエンジンであるV8にJavaScriptのソースを食わせて実行する、つまりはオレオレJS環境を作るまでを扱うのだ。
V8はもともと他のソフトウェアに組み込まれて使用されることを想定(例えばChromeとかね)されているのでこういう作業が必要になる。


手順は大きくわけて二つ
1. まずはV8のソースを落としてきてV8のビルド
2. V8のソースディレクトリに自作のC++のソースを作ってコンパイル&実行

C++が出てきた時点で引いちゃったかもしれないけど、C++を使えるようになるのが今回の目的ではないのでまずはリラックス。C++っていったってそんなに難しく構える必要ないから!

例えば

int main(int argc, char* argv[]) {
  return 0;
}

っていうコード。

  • C++で実行プログラムを作るのにはmain関数が必要。
  • 関数には戻り値の型と引数の型を指定する。
  • 戻り値は指定されているとおりint型で返す。

ってことを実装しただけだ。いやそれC言語でしょって思うかもしれないけど気分は++。ちゃんとコンパイルすれば動くからね。簡単でしょ?もちろん Hello World さえ表示しないけどそれはそれで置いておこう。

あと、おそらくmain関数の引数が気になる、もしくはトラウマ的に嫌なイメージを抱く人もいるんだろうけど、引数の数と引数の値を渡されるんだというくらいの認識にとどめておいてぜーんぜん構わない。

もちろん興味を持ってC++をやろうって思った人はこんなでたらめな説明で満足せずにどんどん突っ込んで勉強していってほしい。


じゃあまずV8のビルドから。手順は
http://code.google.com/intl/ja/apis/v8/build.html
に載っている。

$ svn checkout http://v8.googlecode.com/svn/trunk/ v8

としてソースをsubversionリポジトリから引っ張り出して、そのディレクトリで

$ scons

とするだけ、ここで注意したいのがsconsでビルドする際にx86_64環境の人は

$ scons arch=x64

としてCPUアーキテクチャを指定する必要があるってところかな。

さて、これをもとに本日のお題となるのがこちらのページ
http://code.google.com/intl/ja/apis/v8/get_started.html
V8の「さあ始めよう」だね。

このページを見てみると中ほどに Hello World 出力プログラムのコードが載っている。
まずはこのプログラムを実際に動かしてみよう。

このソースをコピペし、コンパイルして実行する。つまり

#include <v8.h>

using namespace v8;

int main(int argc, char* argv[]) {

  // Create a stack-allocated handle scope.
  HandleScope handle_scope;

  // Create a new context.
  Persistent<Context> context = Context::New();
  
  // Enter the created context for compiling and
  // running the hello world script. 
  Context::Scope context_scope(context);

  // Create a string containing the JavaScript source code.
  Handle<String> source = String::New("'Hello' + ', World!'");

  // Compile the source code.
  Handle<Script> script = Script::Compile(source);
  
  // Run the script to get the result.
  Handle<Value> result = script->Run();
  
  // Dispose the persistent context.
  context.Dispose();

  // Convert the result to an ASCII string and print it.
  String::AsciiValue ascii(result);
  printf("%s\n", *ascii);
  return 0;
}

これをさっきビルドしたV8のディレクトリにhello_world.cppとして保存し、次に

g++ -Iinclude hello_world.cpp -o hello_world libv8.a -lpthread

としてコンパイルしたら

$ ./hello_world

で実行だ。

成功すると Hello, World! とプロンプトに表示される。

おめでとう!


と、ここで終わらせるのはあんまりだ。なので解説を試みるね。


main関数が始まってすぐの

HandleScope handle_scope;

という行はHandleScope型のhandle_scopeっていう変数を作り出しているだけ。
これがないと下の方でHandle<...>と始まる行があるんだけど、そこでコケちゃう。イメージとしてはHandle型の変数達が棲息する場所を作るっていう感じかな。V8ではこの行はおまじないとしてほぼ確実に使うので気にしないったら気にしない。位置的にはHandle型の変数が現れる前に書いておけばオッケーだ。

Persistent<Context> context = Context::New();

この行では実行コンテキストを作っている。必ず一つはないとどこ実行していいのかわからないので落ちちゃうんだ。

Context::Scope context_scope(context);

作られたコンテキストに対し、この行で実際に実行コンテキストを設定する。で、その後に下の方で

context.Dispose();

として破棄しているね。
ちなみにこのコンテキストはいくつも作ることができて、Context::Scopeで設定することでそれぞれ実行可能。ブラウザの中でiframeの中に書いたjsをメインの画面のjsと一緒に実行するときなんかはこの仕組みを使って複数のコンテキストが切り替わっているってわけ。

コンテキストとはそもそも何かというと、例えば2つのプロセス、AとBっていうのがあってそれをマルチタスク環境上で同時に実行しているとしよう。1CPUだとこの2つのプロセスのうちどちらか一方しか瞬間的には処理できなくって、これをOSの中にあるタスクスケジューラってヤツがあたかもこの2つのプロセスを同時に実行しているように見せかけるためA->B->A->Bみたいに高速にスイッチングしているんだ。で、このうちCPUが現在こなしている処理っていうのをコンテキストと呼んでいる。

で、次の

Handle<String> source = String::New("'Hello' + ', World!'");

では"'Hello' + ', World!'"って文字列をV8用のJSソースとして設定している。

Handle<Script> script = Script::Compile(source);

ここがV8の結構特徴的なところだと思うんだけど、V8では読み込んだソースを初回実行時にコンパイルするんだ。バイトコードみたいな中間コードじゃなくってきちんとマシン語にまでね。ここで内部的に使われているアセンブラはSun謹製のアセンブラをさらにGoogleでチューンしたもの。

そうしてコンパイルしたものを

Handle<Value> result = script->Run();

で実行して今度はresultの中に結果を格納している。

後はその結果を文字列にしてプリント。

String::AsciiValue ascii(result);
printf("%s\n", *ascii);

どうだろうか、C++の知識よりもV8を使うためのお約束ばかりだ。
このようにものすんごく簡単にオレオレJSを作れちゃう。


さあ、このサンプルをもとにもうちょっとだけ踏み込んでみようか。次はJavaScriptファイル名を指定して、このプログラムに実行させたい。
先ほどのプログラムに対して加える変更内容は

1. 引数として指定されたファイルを読み込む
2. 先ほどの Handle source に読み込んだ内容を食わせる。

と、こんだけ。


でだ。こっからはなるべく楽をしたい。なのでサンプルからパクれるものを探すのだ。V8のディレクトリの
sample/shell.cc
をみたらちょうどいい感じに見つかった。持ってきたのはファイルを読み込むその名もReadFile関数。

#include <v8.h>

using namespace v8;


v8::Handle<v8::String> ReadFile(const char* name) {
  FILE* file = fopen(name, "rb");
  if (file == NULL) return v8::Handle<v8::String>();

  fseek(file, 0, SEEK_END);
  int size = ftell(file);
  rewind(file);

  char* chars = new char[size + 1];
  chars[size] = '\0';
  for (int i = 0; i < size;) {
    int read = fread(&chars[i], 1, size - i, file);
    i += read;
  }
  fclose(file);
  v8::Handle<v8::String> result = v8::String::New(chars, size);
  delete[] chars;
  return result;
}


int main(int argc, char* argv[]) {

  Persistent<Context> context = Context::New();
  Context::Scope context_scope(context);

  // 引数が渡されなかったらエラーで終了
  if(argc < 2)  {
    fprintf(stderr, "script was not specified.\n");
    return 1;
  }
  HandleScope hs;
  // 引数に指定されたファイルを読み込む
  Handle<String> source = ReadFile(argv[1]);

  Handle<Script> script = Script::Compile(source);
  Handle<Value> result = script->Run();

  String::AsciiValue ascii(result);
  printf("%s\n", *ascii);

  context.Dispose();
  return 0;
}

これをolele.cppという名前で保存して、

g++ -Iinclude oleole.cpp -o oleole libv8.a -lpthread

としてコンパイルして実行してみる。

$ ./oleole

するとまずは

script was not specified.

と冷たくあしらわれて終了する。じゃ次に何か適当なJSのファイルをコイツに食わせてみよう。

var i = 1;
var j = 2;
i + j;

という内容のファイルをsample.jsとして保存し、今度は

$ ./oleole sample.js

として実行だ。

「3」という結果が返ってきて無事JSファイルをV8で実行することができただろうか。
エラー処理してないからちょっとしたミスでプログラムが落ちちゃうのはご愛嬌。

ということで実装編第一回のオレオレJS作成編は終了なのだ。
次回はlibevを使ったイベントループ実装編の予定。



ここからは補足説明。さっきのソースの中のReadFile関数で「v8::」っていうのがそこらじゅうに書かれているけどそれ取っちゃっても動くからね。

using namespace v8;

っていう行がそれ取っちゃっても動くようにするおまじないだ。

あとargcとargvっていうのはそれぞれコマンドラインからの引数の数と内容を含む。分かり辛いから例を挙げてみよう。

$ ./oleole sample.js

こうやって実行すると./oleoleとsample.jsの二つがmain関数に渡されるのでargcは2になり、argv[0]に./oleole、argv[1]にはsample.jsが入るって寸法。
ちなみにargcはargument count、argvはargument vectorの略だ。
UNIX系のOSやWindowsではmain関数に渡されるのはargcとargvだけではなくさらにもう一つenvpというものがあり、環境変数を受け取ることができる。

int main ( int argc, char* argv[], char* envp[] )

さらにMacOS Xでは4つ目の、その名もappleというのがある。

int main ( int argc, char* argv[], char* envp[], char* apple[] )

手元にMacがないので確認できないけどapple[0]にはパス情報が入っているハズ。
あ、ついでにもう一つ。mainの戻り値を「0」に設定しているけど「0」っていうのは正常終了を表す。それ以外はエラーってことね。