2008年9月13日土曜日

Inheritance on JavaScript

しばらく夏休みなので、以前から読みたかった、Pro JavaScript Design Patterns を読みながら、JavaScirpt の勉強をしてます。

これまでこのブログでも何回か JavaScript の継承について書いてきたのですが、とにかくややこしい印象でした。

JavaScript の継承は、大きく分けて
  • Classical Inheritance
  • Prototypeal Inheritance
の 2通りで実現できます。

今回の記事では、この 2通りのパターンをもう一度おさらいしてみます。

Classical Inheritance

一般的に継承というと、Super Class 作って、それを継承する、というパターンを想像します。
Classical Inheritance では、その方法を JavaScript なりに実現します。

例として、Person というクラスを作成して、それを継承する Author というクラスを作成します。
/* Class Person */
function Person(name) {
this.name = name;
}

Person.prototype.getName = function() {
return this.name;
};

/* Class Author */
function Author(name, books) {
Person.call(this, name);
this.books = books;
}

Author.prototype = new Person(); // Set up the prototype chain
Author.prototype.constructor = Author; // Set the constructor attribute to Author
Author.prototype.getBooks = function() { // Add a method to Author
return this.books;
};
以下の例で、継承が実現されているのが分かります。
var author = [];
author[0] = new Author('Larry Wall', ['Perl']);
author[1] = new Author('Yukihiro Matsumoto', ['Ruby']);

author[1].getName(); // 'Yukihiro Matsumoto'
author[1].getBooks(); // ['Ruby']
この例から、以下のような extend 関数を定義しておけば、Classical Inheritance のパターンを汎用的に利用できることが分かります。
/* Extend function. */
function extend(subClass, superClass) {
var F = function() {};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;

subClass.superClass = superClass.prototype;
if (superClass.prototype.constructor == Object.prototype.constructor) {
superClass.prototype.constructor = superClass;
}
}
この extend 関数を以下のように利用することで、継承を簡単に実現することができるようになります。
/* Class Person */
function Person(name) {
this.name = name;
}

Person.prototype.getName = function() {
return this.name;
};

/* Class Author */
function Author(name, books) {
Author.superClass.constructor.call(this, name);
this.books = books;
}

extend(Author, Person);

Author.prototype.getBooks = function() {
return this.books;
};

Prototypal Inheritance

Prototypal Inheritance とは、プロトタイプとなるオブジェクトを作成し、それを clone (複製) したものにメソッドを追加しながら、継承を実現するものです。

※ clone で生成されるのは deep copy ではないので注意してください。

Prototypal Inheritance は、JavaScript の prototype チェーンの性質を最大限に活用することによって実現します (ただし、実装自体は非常にシンプルなものです) 。

では例を見てみましょう。
/* Person Prototype Object. */
var Person = {};
Person.name = 'default name';
Person.getName = function() {
return this.name;
};

/* Author Prototype Object */
var Author = clone(Person);
Author.books = [];
Author.getBooks = function() {
return this.books;
}

var author = clone(Author);
author.name = 'Yukihiro Matsumoto';
author.books.push('Ruby');

author.getBooks();
このとき利用されている clone 関数は以下のような定義です。
/* Clone Function */
function clone(obj) {
var F = function (){};
F.prototype = obj;
return new F;
}

Classical and Prototypal Inheritance の比較

この 2 つの 継承パターンの pros & cons は以下の通りになります。

Classical Inheritance については、
  • (Pros)JavaScript やプログラマのコミュニティにて理解しやすい
  • (Cons)クラスベースの継承だが、object に prototype を結びつけるので混乱しやすい
Prototypical Inheritance の方は、
  • (Pros)メモリを効率的に利用できる
    ※ prototype chain の性質上、全ての clone オブジェクトは、プロパティやメソッドを上書きしない限り、ひとつのオブジェクト (プロトタイプとしたオブジェクト) の属性またはメソッドを参照するだけで済むからです。
  • (Cons) prototype の概念に慣れていないプログラマには理解されづらい
となります。

Mix-in

継承のおまけとして、Mix-in を実現することもできます。
ここでは Classical Inheritance パターンの場合にどのように Mix-in を実現するかを見てみます。

※ Prototype Inheritance を利用する場合、Mix-in は Prototype Object の prototype にメソッドを実装すれば良いだけなので省略します。

オブジェクトをシリアライズするクラスを、任意のオブジェクトに実装することを考えてみましょう。

まず、任意のオブジェクトをシリアライズのメソッドは以下のように書けます。
/* Mix in class implemented to any other object. */
var Mixin = function() {};
Mixin.prototype = {
serialize: function() {
var output = new Array();
for (key in this) output.push(key + ': ' + this[key]);
return output.join(', ');
}
};
このクラスを Author クラスに以下のように Mix in します。
augment(Author, Mixin);
var author = new Author('Larry Wall', ['Perl']);
author.serialize();
結果、以下のようなシリアライズされた出力を得ることができます。
"name: Larry Wall, books: Perl, constructor: function Author(name, books) { Person.call(this, name); this.books = books; }, getBooks: function () { return this.books; }, serialize: function () { var output = new Array; for (key in this) { output.push(key + ": " + this[key]); } return output.join(", "); }, getName: function () { return this.name; }"
ここで利用された augment 関数は以下のようになります。
/* Augument function */
function augment(receivingClass, givingClass) {
if (arguments[2]) {
for (var i = 2, len = arguments.length; i < len; i++)
receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
}
else {
for (methodName in givingClass.prototype) {
if (!receivingClass.prototype[methodName])
receivingClass.prototype[methodName] = givingClass.prototype[methodName];
}
}
}

2008年9月6日土曜日

Cybozu2ICal.ja_JP をレンタルサーバで

僕の知り合いの多くは会社のスケジュール管理として、Cybozu Office を利用しているようです。
Cybozu Office は高機能で、社内スケジュール管理などには不足ないのですが、
  • スケジュールデータを他デバイスでサブスクライブして外出先で参照
  • 会社のスケジュールを個人カレンダーとマージ (1 日の個人スケジュールをトータルで管理できる)
などと考え出すと、途端に途方に暮れてしまいます。
これを解決する手段として、Cybozu2ICal.ja_JP (以下 Cybozu2ical と略記) という素晴しい perl モジュールを公開してくださっている方がいます。
サイボウズオフィス6以降のカレンダーアイテムを取得して、iCalendar形式に変換するコマンドラインプログラムです。このプログラムを利用することで、サイボウズのカレンダーを、iCalendar形式をサポートするアプリケーション(Microsoft Outlook, Apple iCal, Google Calendarなど)に簡単に統合できます。Cybozu2ICal.ja_JP - Ogawa::Code - Trac Description

Cybozu2ical を利用したスケジュール管理

Cybozu2ical は以下の簡単なコマンドで、Cybouzu Office を利用している会社のスケジュールを iCal 形式にして出力してくれます。
./cybozu2ical --conf config.yaml
このコマンドを会社のイントラサーバに仕掛けておいて、定期的に出力するようにすれば、様々なデバイス、アプリケーションで自分のスケジュールを管理することができるようになります。

例えば、
  • 出力フィードを Google カレンダーにサブスクライブさせ、携帯から参照
  • Mac の iCal に取り込んで iPhone に同期。

レンタルサーバで Cybozu2ical

Cybozu2ical の本質とは全く関係ないのですが、この便利ツールを利用するのに、例えば、営業さんは、自分で perl モジュールをインストールする自前サーバを持ってないことが多いでしょう。

僕の知人は、「外出先からスケジュール確認したいんだけど、レンタルサーバを借りたので、Cybozu2ical をインストールしてくれませんか?」と依頼してきました。

さくらインターネットのような格安だけれど、管理者権限を持てないようなレンタルサーバに、 Cybozu2ical のような CPAN モジュールをがんがん利用するモジュールをインストールするのは少しだけ工夫が必要でしたので、ここにインストールログを残しておきます。

ルート権限を持たないサーバでの CPAN の設定

今回利用したのはさくらインターネットのレンタルサーバで、シェルは cshell。perl は 5.8.8 を利用しています。
レンタルサーバで CPAN を利用するには、ホームディレクトリ以下に CPAN モジュールをインストールするように設定するのがポイントです。
$ cd mkdir ~/perl
$ mkdir -p ~/.cpan/CPAN/
$ touch ~/.cpan/CPAN/MyConfig.pm
MyConfig.pm を以下のように書き替えてください。
$ CPAN::Config->{cpan_home} = undef;
$ CPAN::Config->{makepl_arg} = 'PREFIX=/home/umemac/perl LIB=/home/umemac/perl/lib INSTALLMAN1DIR=/home/umemac/perl/man/m
an1 INSTALLMAN3DIR=/home/umemac/perl/man/man3';
$ CPAN::Config->{histfile} = "$ENV{HOME}/.cpan/histfile";
1;
さらに、ライブラリのパスを環境変数として設定します。
$ vi ~/.cshrc

setenv PERL5LIB /home/USERNAME/perl/lib:/home/USERNAME/perl/lib/perl5/site_perl/5.8.8:/home/USERNAME/perl/lib/perl5/5.8.8
※ USERNAME は適宜変更してください。
次に cpan を起動し、初期設定開始。
$ perl -MCPAN -e shell
質問に対して Enter キーの連打で OK ですが、CPANミラーサイトの選択だけは、お住まいの地域に合わせ、設定してください。
(1) ...
(2) Asia
(3) ...
(4) ...
Select your continent (or several nearby continents) [] 2

(1) ...
(2) ...
(7) Japan
(8) ...
Select your country (or several nearby countries) [] 7

(1) ftp://...
(2) ftp://...
(3) ftp://...
Select as many URLs as you like (by number),
put them on one line, separated by blanks, e.g. '1 4 5'
(or just hit RETURN to keep your previous picks) [] 1 4 5
これで準備完了です。Cybozu2ical に必要なモジュールをインストールしていきましょう。
cpan> install Text::CSV_XS
cpan> install DateTime
cpan> install LWP::UserAgent
cpan> install Class::Accessor::Fast
cpan> install Data::ICal
cpan> install YAML
※一部上手くいかないときは cpan> force install <モジュール名> を試みてください。

Cybozu2ical の設定

次に、Cybozu2ical をダウンロードします。
$ mkdir ~/src
$ cd ~/src
$ /usr/local/bin/wget "http://code.as-is.net/public/attachment/wiki/DownloadableFiles/cybozu2ical-0.31.zip?format=raw" -O cybozu2ical-0.31.zip
$ unzip cybozu2ical-0.31.zip
$ cd cybozu2ical-0.31
自分のサイボウズの設定をモジュールに教えるために、 config.yaml を用意します。
$ cp -p config.yaml.sample config.yaml
$ chmod 0600 config.yaml
$ vi config.yaml
以下の、デフォルトの設定を、自分の設定に書き替えて保存してください。
cybozu_url: http://www.example.com/cbag/ag.cgi
calname: Your Calendar Name
username: user
#userid: XX
password: pass
time_zone: Asia/Tokyo
tzname: JST
input_encoding: shiftjis
output_encoding: utf8
#calendar_driver: SyncCalendar
calendar_driver: ApiCalendar
date_range: 30
ここまできたら、Cybozu2ical モジュールがきちんと動くか試してみましょう。
$ ./cybozu2ical --conf config.yaml
BEGIN:VCALENDAR
CALSCALE:GREGORIAN
METHOD:PUBLISH
.....
iCal 形式のスケジュールが表示されたら成功です。

Crontab の設定

ここまできたらもう一息。Crontab で、上記のコマンドを定期的に実行するように設定し、出力結果を www (公開) ディレクトリ以下に置いておけば、いつでも、iCal 形式のほぼ最新のスケジュールが 様々なアプリケーションから参照できるようになります。

Cybozu2ical の定期実行用に、以下のような簡単な shell スクリプトを用意します。
$ touch cron.sh
$ vi cron.sh

#!/bin/csh

# Crontab 用に環境変数を設定
setenv PERL5LIB /home/USERNAME/perl/lib:/home/USERNAME/perl/lib/perl5/site_perl/5.8.8:/home/USERNAME/perl/lib/perl5/5.8.8

cd /home/USERNAME/cybozu2ical-0.31/

./cybozu2ical --conf config.yaml>/home/USERNAME/www/s5gI6e2kp3XQ6t9N73Fk2
※USERNAME や 出力ファイル名 s5gI6e2kp3XQ6t9N73Fk2 は適宜変更してください。

出力ファイル名は、複雑にしておくことをお勧めします。Strong Password Generator とかで生成すると良いんじゃないかな。必要に応じて、公開ディレクトリの HTTP プロトコルを変更したり、認証方式を工夫したりして、スケジュールの安全性を確保してください。

最後に Crontab に登録して完了!
*/3 * * * * csh /home/umemac/cybozu2ical-0.31/cron.sh
こうして一度、Cybozu Office に入ったスケジュールを、様々なアプリケーションからサブスクライブできるようになると、もう手放せなくなりますよ。