2008/09/07

kunitさんと飲み

ポスト @ 0:46:05 , 修正 @ 2008/09/07 0:50:42 |     

SeasarConferenceでしたが、ほぼ午前中の一コマとLTの途中からしかでてません。すいません。
んで、夜になってから kunit さんとお話してきた。

話してきた内容ってのは、ほぼ僕が日頃気になっていることでして。
順不同で抜粋すると

  • 設計勉強会について
    • 設計勉強会いきたかったー
    • てか、設計勉強会なのに、実装っぽい話題がトップはどうよ?
    • しかし、PHPで設計勉強会というトピックがでてきたのは凄いこと
    • 2回め以降にも期待
  • もくもく会
    • もくもく会っていいっすね
    • やっぱり少しでも集中する時間をどこかにあるといい
    • わざわざもくもく会に参加するよりも、もくもくするだけなので、スタバでもいい。
  • RESTについて
    • RESTfulな設計って、クライアント側からみると、やっぱサーバは M(MVC的に考えて) だよね?んで、そこのフレームワークって煩雑すぎじゃね?
    • REST的にリソースの国際化の概念てあるんすか?(http://jp.hoge.com/ と http://hoge.com/jp/の違いってどうなる?)
    • RESTfulなフレームワークってどんなのっすか?
    • PHPはステートフルにするほうが難しいじゃん
  • symfonyについて
    • やっぱPropelてアレですよねぇ
    • sfBrowserはよく出来てる仕組みだと思うけど、やっぱ Unit なんですよね...
    • selenuimも結局ブラウザな...
    • S2Dao の Lite 実装な話題とか(これは twitter だっけ?) の続き
  • DBレイヤーとかDBアクセスとかについて
    • propel てやっぱり、なんとなく古いよーな(やっぱり torque なんだよね)
    • どう抽象化する?
    • 抽象化とパフォーマンスはホントにトレードオフか
    • drupal は、ほぼ全てが関数ベースだけど、ネーミングルールだけで上手いことモジュール分けができてる。パフォーマンスはそんなに悪くないんす
    • でも、ま、ふつーはクラスベースですよね。
    • RDB(というかTable)に国際化の概念てどうする(ja/enは分けてない?)
  • テストについて
    • テストどうしてます?
    • AcceptanceTest がしやすい仕組みのフレームワークとか
    • お客さんと意識合わせができて、テストもそのままできると便利なのは分かってるけど、ツール無いっす
    • やっぱExcelで管理できると便利よねー(国内は)
    • S2Buri とか JBoss Rule な話とか
  • ほか
    • コンテナ世代と密結合世代
    • AOPとかフレームワークを(作ったりとかして)知ると別の視点が見えてくる
    • ぶっちゃけ Maple4 はいつでるんすか?
    • 何かのニーズがあるけど、何かとニッチな事について
    • ビューとかレイヤーとかブロックみたいのとか
    • 5.3はホントに5.3ていうバージョンでいいのか(笑)
  • 色々雑談とか

ってことで、細かい部分の内容は忘れた。(しかも、上のは主がどっちかわかりずらいし...)
でも、PHP っぽい話とは少し違って、設計とかその辺の情報を少し交換できたのはよかった。
ここでゲットした情報は少しずつ展開していきたいと思います。為になります。

でも、やっぱり僕はPropelが苦手です

2008/09/04

Symfony で ActionScript(AMF) と通信する

ポスト @ 1:54:13 , 修正 @ 2008/09/04 2:00:49 | , , , ,     

PHP と ActionScript が通信するためのライブラリとして AMFPHP とかが有名ですが、名前空間とかライセンスとか色々な部分が気になるので、SabreAMF を Symfony に乗っけて動かしてみるテスト

ちなみに、既に sfAMFPlugin っていうプラギンも同様のアプローチなのですが、こいつだと README に書いてあるコードが何故か動かないです。
↓こんなの

class amfAMFAction extends sfAction
{
  public function execute($request)
  {
    $this->setLayout(false);

    $gateway = new sfAmfGateway;
    $response = sfContext::getInstance()->getResponse();
    $response->setContent($gateway->service());
    return sfView::NONE;
  }
}

上記コードだと、SabreAMF_Server の __construct 中にある

public function __construct() {

   $data = file_get_contents('php://input');
   if (!$data) throw new Exception('No valid AMF request received');
   :
   :
   :

のコードにて、if(!$data) の評価に失敗して例外となるようです。
詳しい原因は掴んでいないですが、どうも ob_start() が関係している様子(素で通信したときと、上記コードで動かしたときとだと、content-length が結構違うってのも気になりますが...)

function hoge(){
    $data = file_get_contents('php://input');
    if(!$data) {
        echo 'hello world';
    } else {
        echo 'no valid';
    }
}

// ob_start();
ob_start('');

hoge();
$result = ob_get_contents();
ob_clean();

assert($result == 'hello world'); 

タイミングなのかなんなのかわからないですが、ob_start() と ob_start('') の動きが微妙に違うらしい
(てか、似たようなのがあった - IE6でダウンロード - ekurodaの日記)

(中略)

ということで、Controller を作って、そこでやってしまうことにした。
例外処理とかも結構ハンドリングできるようになって便利。

class fmServiceController extends sfFrontWebController
{
  public function forward($moduleName, $actionName, $parameters = array())
  {
     :
     : // 何か色々書く
     :
    $actionInstance = $this->getAction($moduleName, $actionName);
     :
     : // もう少し書く
     :
    $server = new SabreAMF_CallbackServer;
    $server->onInvokeService = array($actionInstance, 'service');
    $server->exec();
  }
}

んで、Action とか

class amfAction extends fmServiceAction
{
  public function service($service, $method, $data)
  {
    :
    : // 色々やって
    :
    return $results;
  }
}

こんな感じ。

routing.ymlとかはこんな感じでいけるかな

amf_request:
  url: /:module/gateway.amf
  param: {action: amf}

json_request:
  url: /:module/:id.json
  param: {action: json}

また、ActionScript 自体はこんなもので試してみました。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="init()">
    <mx:Script>
        <![CDATA[
        import mx.controls.Alert;
        import mx.collections.ArrayCollection;
        import mx.rpc.events.ResultEvent;
        import flash.net.NetConnection;
        import flash.net.Responder;
        import flash.net.ObjectEncoding;
        import mx.utils.ObjectUtil;

        private function init():void {
            var connection:NetConnection = new NetConnection;
            connection.objectEncoding = ObjectEncoding.AMF3;
            connection.addEventListener(NetStatusEvent.NET_STATUS, function(e:NetStatusEvent):void {
               trace(ObjectUtil.toString(e));
            });
            connection.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void {
                trace(ObjectUtil.toString(e));
            });
            connection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, function(e:AsyncErrorEvent):void {
                trace(ObjectUtil.toString(e));
            });

            connection.connect('http://test.server/service_dev.php/hoge/gateway.amf');
            connection.call('HelloWorld.getData', new Responder(onResult, onFault));
            connection.close();
        }
        private function onResult(result:Object):void {
            dataGrid.dataProvider = new ArrayCollection;
            for(var i:uint = 0; i < result.length; ++i){
                dataGrid.dataProvider.addItem({
                    'AAA': result[i].AAA,
                    'BBB': result[i].BBB,
                    'CCC': result[i].CCC
                });
            }
        }
        private function onFault(fault:Object):void {
            trace(ObjectUtil.toString(fault));
        }
        ]]>
    </mx:Script>
    <mx:Panel layout="absolute">
        <mx:DataGrid id="dataGrid">
            <mx:columns>
                <mx:DataGridColumn headerText="aaa" dataField="AAA"/>
                <mx:DataGridColumn headerText="bbb" dataField="BBB"/>
                <mx:DataGridColumn headerText="ccc" dataField="CCC"/>
            </mx:columns>
        </mx:DataGrid>
    </mx:Panel>
</mx:Application>

とりあえず、ob_start 中(?)の php://input 値が取得できるようになるまで、これで進めることにする。

2008/08/28

syslog にログを叩き込む

ポスト @ 23:02:47 , 修正 @ 2008/08/28 23:06:01 | ,     

phpのsyslog関数だと、リモートのsyslogサーバに書き込めない(?)ので、こんな感じで逃げるテスト

class LoggerSyslog {

    private $ident;
    private $opt;
    private $facility;

    private $ip = '127.0.0.1';
    private $port = 514;

    public function __construct($ident, $opt, $facility){
        $this->ident = $ident;
        $this->opt = $opt;
        $this->facility = $facility;
    }

    public function setIp($ip){
        $this->ip = $ip;
    }

    public function setPort($port){
        $this->port = $port;
    }

    public function write($message, $priority){
        $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
        socket_set_nonblock($socket);
        if(socket_connect($socket, $this->ip, $this->port)){
            //$data = '<' . ($this->facility | $priority) . '>' . $this->ident . ': ' . $message;
            $data = '<' . self::makePriority($this->facility, $priority) . '>' . $this->ident . ': ' . $message;
            socket_write($socket, $data, strlen($data));
        }
        socket_close($socket);
    }

    private static function makePriority($facility, $priority){
        return ($facility & SyslogFacility::LOG_FACMASK) | $priority;
    }

}

んで、facility は何かってーと、これ

interface SyslogFacility {
    // kernel messages
    const LOG_KERN = 0;
    // random user-level messages(1 << 3)
    const LOG_USER = 8;
    // mail system(2<<3)
    const LOG_MAIL = 16; 
    // system daemons(3<<3)
    const LOG_DAEMON = 24;
    // security/authorization(4<<3)
    const LOG_AUTH = 32;
    // internal syslogd use(5<<3)
    const LOG_SYSLOG = 40;
    // line printer subsystem(6<<3)
    const LOG_LPR = 48;
    // network news subsystem(7<<3)

    const LOG_NEWS = 56;
    // UUCP subsystem(8<<3)
    const LOG_UUCP = 64;
    // clock daemon(15<<3)
    const LOG_CRON = 120;
    
    // reserved for local use(16<<3)
    const LOG_LOCAL0 = 128;
    // reserved for local use(17<<3)
    const LOG_LOCAL1 = 136;
    // reserved for local use(18<<3)
    const LOG_LOCAL2 = 144;
    // reserved for local use(19<<3)
    const LOG_LOCAL3 = 152;
    // reserved for local use(20<<3)
    const LOG_LOCAL4 = 160;
    // reserved for local use(21<<3)
    const LOG_LOCAL5 = 168;
    // reserved for local use(22<<3)
    const LOG_LOCAL6 = 176;
    // reserved for local use(23<<3)
    const LOG_LOCAL7 = 184;
    
    // mask to extract facility
    const LOG_FACMASK = 0x03F8;
}

同様に、priority はこんなの

interface SyslogPriority {
    // system is unusable
    const LOG_EMERG = 0;
    // action must be taken immediately
    const LOG_ALERT = 1;
    // critical conditions
    const LOG_CRIT = 2;
    // error conditions
    const LOG_ERR = 3;
    // warning conditions
    const LOG_WARNING = 4;
    // normal but significant condition
    const LOG_NOTICE = 5;
    // informational
    const LOG_INFO = 6;
    // debug-level messages
    const LOG_DEBUG = 7;
    // mask to extract priority
    const LOG_PRIMASK = 0x0007;
}

これだけで使うのなら、こうなる

$syslog = new LoggerSyslog('syslogtest', null, SyslogFacility::LOG_USER);
$syslog->write('hoge foo bar', SyslogPriority::LOG_INFO);

もし、Appenderが必要なLoggerの場合、こんなAppenderを用意すればいいかと

class SyslogAppender implements Appender {

    private $ident;
    private $opt;
    private $facility;

    private $ip = '127.0.0.1';
    private $port = 514;

    public function __construct($ident, $opt, $facility){
        $this->ident = $ident;
        $this->opt = $opt;
        $this->facility = $facility;
    }

    public function setIp($ip){
        $this->ip = $ip;
    }

    public function setPort($port){
        $this->port = $port;
    }

    public function append(LoggingEvent $event){
        $level = $event->getLevel();
        $priority = SyslogPriority::LOG_PRIMASK;
        switch($level->toInt()){
        case LogLevel::ALL_INT:
            $priority = SyslogPriority::LOG_EMERG;
            break;
        case LogLevel::DEBUG_INT:
            $priority = SyslogPriority::LOG_DEBUG;
            break;
        case LogLevel::INFO_INT:
            $priority = SyslogPriority::LOG_DEBUG;
            break;
        case LogLevel::WARN_INT:
            $priority = SyslogPriority::LOG_WARNING;
            break;
        case LogLevel::ERROR_INT:
            $priority = SyslogPriority::LOG_ERR;
            break;
        case LogLevel::FATAL_INT:
            $priority = SyslogPriority::LOG_CRIT;
            break;
        case LogLevel::OFF_INT:
        default:
            return;
        }
        $this->write($event->getMessage(), $priority);
    }

    public function write($message, $priority){
        $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
        socket_set_nonblock($socket);
        if(socket_connect($socket, $this->ip, $this->port)){
            $data = '';
            $data .= '<' . self::makePriority($this->facility, $priority) . '>';
            $data .=  $this->ident . ': ' . $message;
            socket_write($socket, $data, strlen($data));
        }
        socket_close($socket);
    }

    private static function makePriority($facility, $priority){
        return ($facility & SyslogFacility::LOG_FACMASK) | $priority;
    }

}

このケースだとこんな風になる($identはlogger::nameでもいいのかも)

$logger = new Logger;

$logger->addAppender(new SyslogAppender('logsample', 0, SyslogFacility::LOG_USER));
$logger->info('%s %s %d', 'hello', 'world', 777);

やっぱログは、ログの集計とか面倒だからどこかのリモートログサーバに書き込んで、そこで一元管理したいよね。っていうお話し。
syslog-ngなんかを立てて、そこに叩き込めばなんとかならないかな?(TCPもあることだし)

2008/08/14

Testing_FIT を試す

ポスト @ 22:36:38 , 修正 @ 2008/08/14 22:39:02 | , ,     

Acceptance Testの為にTesting_FITを使ってみた。

UnitTestのツールは沢山あるけど、AcceptanceTestという形で探すと、なかなか少ないもんです。
少し前の記事だと、GANCHIKU.com » Testing_FITを使ってみる。があったけど、少し状況が変わっているみたいなので、それまでのログ

インストール

まだ、stable リリースが行われていないので、beta 版のインストールを行うことにする

yusuke.hata.local: ~> sudo pear install channel://pear.php.net/Testing_FIT-0.2.2
downloading Testing_FIT-0.2.2.tgz ...
Starting to download Testing_FIT-0.2.2.tgz (29,584 bytes)
.........done: 29,584 bytes

install ok: channel://pear.php.net/Testing_FIT-0.2.2

ディレクトリ構成

fit/
 + fixture/
     + CalculateDiscount.php
 + spec/
     + CalculateDiscount.html
 + impl/
     + Discount.php
     + SampleDiscount.php

HTML用意

テストをこなす上で、その仕様が書かれたHTMLを用意します。これは僕等開発者が用意するんではなく、お客さんとかに用意してもらうらしい。

<!DOCTYPE html public "-//w3c//dtd html 4.0 transitional//en">
<html>
    <head>
        <title>TestCase1</title>
    </head>
    <body>
        <table>
            <tr>
                <td colspan="2">CalculateDiscount</td>
            </tr>
            <tr>
                <td>amount</td>
                <td>discount()</td>
            </tr>
            <tr>
                <td>0.00</td>
                <td>0.00</td>
            </tr>
            <tr>
                <td>100.00</td>
                <td>0.00</td>
            </tr>
            <tr>
                <td>999.00</td>
                <td>0.00</td>
            </tr>
            <tr>
                <td>1000.00</td>
                <td>0.00</td>
            </tr>
            <tr>
                <td>1010.00</td>
                <td>50.50</td>
            </tr>
            <tr>
                <td>1100.00</td>
                <td>55.00</td>
            </tr>
            <tr>
                <td>1200.00</td>
                <td>60.00</td>
            </tr>
            <tr>
                <td>2000.00</td>
                <td>100.00</td>
            </tr>
        </table>
    </body>
</html>

<table> タグの一行めに書かれた(上の例だとCalculateDiscount) Fixture が、実行される Fixture というネーミングルールになる
つまり、CalculateDiscount という Fixture クラスを作成しなければならない

また、データ項目の宣言は Fixture の次の行に行う(ここでは、amount と discount)
<th> タグで宣言せずに、<td> タグとして宣言すること

Fixture 作成

実際のテストを行うFIT。public でプロパティを設定しているけど、ここには、HTML上のデータがマッピングされる

<?php
 
class CalculateDiscount extends Testing_FIT_Fixture_Column {
    /**
     * @var string
     */
    public $amount;

    private $discount;

    public function __construct(){
        parent::__construct();
        $this->discount = new SampleDiscount;
    }

    /**
     * @return string
     */
    public function discount(){
        return $this->discount->getDiscount($this->amount);
    }
}

今回は、SampleDiscount の実装クラスを利用してテストを行う
html 上 <td> で書かれた項目名と同じプロパティを Fixtrue クラス内に宣言し、
プロパティドキュメントに @var 宣言を行って、型のタイプを記述する

また、項目名の最後に ( ) と記述されているものは、処理メソッドになるので、public のメソッドとして宣言し、値を返却する
メソッドドキュメントとして、@return 宣言を行い、型のタイプを記述する

実装クラス定義

こんなのを用意した

<?php

interface Discount {
    public function getDiscount($amount = '0.00');
}
<?php

class SampleDiscount implements Discount {
    public function getDiscount($amount = '0.00'){
        return null;
    }
}

runner作成

ちょっと汚いが、こんな感じで runner を用意

<?php
ini_set('display_errors', 0);
require_once 'Testing/FIT/Loader.php';

Testing_FIT_Loader::loadClass('Testing_FIT_Fixture', 'Testing_FIT_Runner');
Testing_FIT_Loader::loadClass('Testing_FIT_Fixture_Column');

Testing_FIT_Loader::addClassDir(dirname(__FILE__) . '/impl');
Testing_FIT_Loader::addClassDir(dirname(__FILE__) . '/fixture');
Testing_FIT_Loader::loadClass('CalculateDiscount', 'Discount', 'SampleDiscount');

$runner = new Testing_FIT_Runner;
$iter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(dirname(__FILE__) . '/spec'));
foreach($iter as $file){
    if($file->isDir()){
        continue;
    }
    $path = $file->getPathName();
    $runner->run($path, '-');
}

runメソッドの第二引数に '-' と指定しているのは、output の出力を行わない(?) という指定になる。

実行

このままでは、実行しても失敗となる

Sampleクラスの修正

本来なら、これを修正する時点でおかしい気がするが、まぁ、サンプルなのでクラスの修正を行って動きを確認

<?php

class SampleDiscount implements Discount {
    public function getDiscount($amount = '0.00'){
        return '0.00';
    }
}

これを実行すると、以下のように色付けが変更される

最終的に以下のように修正し、すべて期待通りの動きと確認されたらOK(サンプルなので、実際はちゃんとレビューとかしないといけないけど)

<?php

class SampleDiscount implements Discount {
    private $results = array(
        '1010.00' => '50.50',
        '1100.00' => '55.00',
        '1200.00' => '60.00',
        '2000.00' => '100.00'
    );
    public function getDiscount($amount = '0.00'){
        if(is_null($amount) || !is_string($amount) || !is_numeric($amount)){
            return '0.00';
        }
        if(!isset($this->results[$amount])){
            return '0.00';
        }
        return $this->results[$amount];
    }   
}

まとめ

FITについては、沢山の資料があるんで省略。

ちなみに、Acceptance Test Driven Developmentなんて言葉もあるようで、AcceptanceTestについてはもう少し知ることが多そう。
ってか、こういったことを便利(簡単)に行えるツールを誰か作って(とかいう

TODO

  • wiki 形式とか csv 形式、excel 形式などで使えるようにすると便利かと
  • Fixture クラスの再利用ができないので、もう少し利用方法を考える
  • 日本語でテストケースを書いてもいいような方法を考える
  • コンテナとかと連携できるように考える

wiki形式とかで書けるようになれば、あっち側の学習コストが下がって良いかも。
暇な時に挑戦しよう

Fit For Developing Software: Framework For Integrated Tests (Robert C. Martin)
  • 発売元: Prentice Hall
  • レーベル: Prentice Hall
  • スタジオ: Prentice Hall
  • メーカー: Prentice Hall
  • 価格: ¥ 6,659
  • 発売日: 2005/07/08
  • 売上ランキング: 34095
  • おすすめ度 3.0

2008/08/10

php.ini をもう少し活用する

ポスト @ 23:29:40 , 修正 @ 2008/08/10 23:37:25 |     

phpのコンパイルオプションに--disable-allとかして、extensionで管理している人向け。自分の整理メモ

php の configure オプションで、--with-config-file-scan-dir=/etc/php.dと指定していると、/etc/php.d/xml.iniとかの ini ファイルとかを読み込んでくれるので、これつかって少し php.ini を整理します。

例えば、php.ini-recommended には沢山の設定値が記述されていて、正直読みづらいです。
なので、必要なモジュール単位に分けて管理。

mbstringだけでいくとこんな感じ(/etc/php.d/mbstring.ini)

extension=mbstring.so

[mbstring]
; language for internal character representation.
mbstring.language = Japanese

; internal/script encoding.
; Some encoding cannot work as internal encoding.
; (e.g. SJIS, BIG5, ISO-2022-*)
mbstring.internal_encoding = UTF-8

他にも、項目の多い session とか、xdebug なんかもこの方法で管理すると、少し楽ができるかと思います。
(php.iniには、error_reportingとかextension_dirの設定だけにして読むのを少なくする)

ちなみに、--with-config-file-scan-dir=/etc/php.activeとかって指定して、/etc/php.d自体には、各モジュールの ini ファイルを沢山配置し、/etc/php.activeには必要なファイルだけ ln -s で管理するとモジュールの読み込みとかの管理が少し楽になるかも。

というメモ

2008/08/01

就職しました。

ポスト @ 23:36:21 |     

2008年8月1日に、都内のベンチャーに就職しました。
長いこと(2年くらい ?) PHP から遠ざかっていましたが、また PHP 界隈に復帰します。
よろしくお願いします。

2008/07/31

ニートになりました。

ポスト @ 18:34:34 |     

報告(誰に?)が送れましたが
2008年6月30日付けでサラリーマンを辞め、ニートの仲間入りしました。
一人前のニートになるべく頑張ります。よろしくお願いします。

2008/07/26

Log4Io を作ってみた。

ポスト @ 20:52:56 |     

Log4Io を作ってみた。
ref http://coderepos.org/share/browser/lang/io/Log4Io/io

使い方は、Log4** とほぼ同じで、こんな感じ

Log4Io

logger := Log4Io getLogger("sample")
logger addAppender(Log4Io ConsoleAppender with)
logger setLevel(Log4Io Level INFO)
if(logger isDebugEnabled) then (
    logger debug("debug")
)
if(logger isInfoEnabled) then (
    logger info("info")
)
if(logger isWarnEnabled) then (
    logger warn("warn")
)

これを実行するとこんな感じ。

==> 2008-07-26 20:48:50 JST INFO sample - info
==> 2008-07-26 20:48:50 JST WARN sample - warn

また、Lobby に log4ioLogger を設置しているので、こちらはそのまま使えます。

Log4Io

// lobby log4ioLogger
log4ioLogger error("hoge")
log4ioLogger info("foo")
log4ioLogger warn("bar")

こっちは、こんな感じに出力される

==> 2008-07-26 20:48:50 JST ERROR Log4Io - hoge
==> 2008-07-26 20:48:50 JST INFO Log4Io - foo
==> 2008-07-26 20:48:50 JST WARN Log4Io - bar

ほとんど log4js からパクったけど、Log4J 並みの細かいことも作れそう。
ただ、パッケージとかが無い分だけ色々と面倒なのだけど。。。

Io の clone でハマる

ポスト @ 20:42:29 | ,     

clone したときは、init メソッドが呼ばれて、そこで必ず初期化しないとダメだということ
以下のコードだと、オブジェクトの値が clone されるんだね

Hoge := Object clone do(
    values := List clone
    with := method(name,
        c := self clone
        c values append(name)
        c
    )
)

a := Hoge with("a")
b := Hoge with("b")
a values println
==> list(a, b)

これってつまり js だと、以下のコードと同じってことか。

var Hoge = function (){};
Hoge.prototype = {
    values: [],
    with: function(name){
        var c = new Hoge
            c.values.push(name)
        return c
    }
};

var a = Hoge.prototype.with("a")
var b = Hoge.prototype.with("b")
print(a.values)
==> (a, b)

ということで、Io のコード及び、js のコードを直すなら、こんな感じになる。
# って基礎中の基礎か

Io

Hoge := Object clone do(
    init := method(
        self values := List clone
    )
    with := method(name,
        c := self clone
        c values append(name)
        c
    )
)

a := Hoge with("a")
b := Hoge with("b")
a values println
==> a
b values println
==> b

js

var Hoge = function (){return this.init.apply(this)};
Hoge.prototype = {
    init: function (){
        this.values = []
    },
    with: function(name){
        var c = new Hoge
            c.values.push(name)
        return c
    }
};

var a = Hoge.prototype.with("a")
var b = Hoge.prototype.with("b")
alert(a.values)
==> a
alert(b.values)
==> b

慣れは怖い

2008/07/25

メモリを4GBに増設した

ポスト @ 20:16:15 | ,     

増設したといっても、結構前に増設してたんだけど、メモリが 1GB から 4GB になると結構快適に過ごせてます。
購入は上海問屋 → 【楽天市場】[送料\210〜]【相性保証付】[個数限定特価] ノートパソコン用メモリ DDR2 PC2-5300 2GB:上海問屋セレクト SODIMM DDR2 PC2-5300 2GB [メ1]:上海問屋
安いっすね。もっと早くに買っておけば良かった。

以前のログ