ホーム > アーカイブ > 2010-08

2010-08

CakePHPプラグインのまとめ – 検索とページネーション/ファイルアップロード編

cakephp_plugins.png

前回の”CakePHPプラグインのまとめ – 認証編“の続きです。
以下のページの”Searching and Pagination”と”File Uploading”の訳とメモを書いていきます。

検索とページネーション:

  • CakeDC’s Search: 少しばかり高度だが、ページネーションにフィルタリングを掛ける確実な方法だ。CakePHPのエキスパート自身によって書かれているよ。恐らくもっとテストが必要だけどね ;)
  • 面倒な検索とページネーションの処理を一括して面倒を見てくれるプラグインのようです。READMEには複合検索のスニペットが載っていますが、このプラグインを使うことでページネーション周りをすっきりと書けています。

  • Jose Gonzalez’s Filter Plugin: これには私自身すごく助けられているが、君がモデルやそれに関連するモデルのデータを通してページネーションにフィルタリングをかけたい場合にだ。私は多くのリファクタリングをしたけど、まだまだ必要だよ。しかし、私が聞いたところによると結構プロダクションで使われているみたいで、少なくとも見た目上はうまく動いているようだね :)
  • CakeDCの検索プラグインと比べるととてもシンプルにページネーションが書けそうです。機能自体もシンプルです。標準のページネーションのロジックをまとめておきたいときなどに使えそうです。検索フォームを作るヘルパーもついていますね。

  • Neil Crooke’s Filter: このクソヤローは常に私のプラグインのアイディアを盗もうとしているんだ。まだこれは使ってないけどね。けどもしこれが彼の検索プラグインだとしたら、完全にやられたよ。あーぁ。
  • これも検索フォーム表示用のヘルパーがついています。URLについた検索パラメータを自動で取得し、ページネーションのパラメータにセットしてくれるようです。複合条件に対応、多くのオペレーション(イコールや大なり小なりなど)に対応していると書いてあります。

  • Neil Crooke’s Searchable: 検索インデックスのレコードにJSONを使えるプラグインだ。これはちょっとすごくて、私はCakeのパッケージに含めていくつかのサイトで使ってる。確実に見ておくべきものだね。
  • READMEがないので詳細は不明ですが、検索インデックスを作るシェルや検索インデックスモデルのためのビヘイビアが格納されていました。ソースのクエリを見る限り、全文検索用のプラグインなのでしょうか?

  • Kalt’s Search: 私が思うに、NeilはKaltからアイディアを盗んだんだ。多分ね。
  • 複数モデルに対応したCakePHPのためのサイト検索プラグインと銘打ってあります。こちらもMySQLの全文検索用のプラグインですね。全文検索のモードもしっかり選べるようです。テーブルを1つ追加して、そこにモデル名、ID、検索インデックスを仕込み、検索に使うようです。

  • Matt Curry’s Pagination Recall: このドキュメントのないプラグインはセッションにページネーションされた現在のページを保存出来るんだ。これはコントローラのページネーション対象のアクションにリダイレクトされたときでさえ、使えるよ。
  • 51行の短いコンポーネントですね。ページネーションのオプションをセッションに保存してくれるようです。

  • Matt Curry’s Yahoo BOSS: これを使えば、君のアプリケーションの中にYahoo! Boss検索を実装出来るよ。
  • Yahoo! BOSSがわからなかったのでググってみました。

    どうやら、Yahoo!からXMLやJSON形式で検索結果を得られるサービスのようです。
    特定のサイトに限っても検索結果を得られる、1日の検索制限はなし、とあるので、なかなか便利そうなサービスですね。
    bing移行後も継続しているようです。

File Uploading:

  • Vinicius Mendes’ MeioUpload: 私もこのプラグインには貢献したよ。バージョニングはちょっとあれだけど、これは確実に便利なプラグインだ。
  • ファイルをモデルに格納するためのビヘイビアです。テーブルを使わない方式も対応しているようですね。このプラグインを入れれば、ファイルアップロード周りのコードをほとんど書かなくて良さそうです。

  • Debuggable’s TransloadIt plugin: NodeJsベースのウェブサービスにファイルをアップロードするにはこのプラグインは最高だ。君のAjaxを通してエンコーディング、プロセッシングそしてストレージを提供してくれる。これはアツい。
  • Transloaditというサービスを利用するためのプラグインのようです。Transloaditを調べてみましたが、これは便利なサービスですね。アップロード時のプログレスバー表示や動画の変換、動画や画像のメタデータの生成までやってくれます。詳細はトップページを見るとわかりますよ。

  • Michał Szajbe’s UploadPack: アップロードしたファイルを出力するためのヘルパーだ。こいつはその用途じゃ、最高のもんだ。私は今じゃ、MeioUploadさえ超えたと思って、去年一年の間に何らかの形で、この二つのコードに貢献、そしてメンテナになっているよ。
  • ファイルアップロード用のヘルパーとビヘイビアのセットですね。サムネイルの表示などもヘルパーで出来るようです。確かにMeioUploadよりも機能が充実していそうです。

  • Jose Gonzalez’s Upload: 私は私が貢献したMeioUploadとUploadPackをベースにアップロードプラグインを作ったんだ。私はまだこれを使ってないけど、だいたい44%、ユニットテストされている。一度100%になったら試してみて、また君に教えるよ ;)
  • これはファイルアップロード用のビヘイビアですね。インターフェースがしっかりしていて使いやすそうな印象を受けます。MeioUploadやUploadPackとどう違うかは使ったことがないのでわかりませんが…。

  • David Perrson’s Media: 全てのCakePHPアップロードプラグインの先祖だ。君がもし、何かこのプラグインで必要以上のことをやりたいとしても、こいつはCakePHPプラグインとしてリリースされていないんだ。このプラグインで君がやりたいことが出来ないなら、こいつはCakePHPのプラグインじゃないね。ちょっと上級者向けのプラグインだ。けどがっかりしないでくれよ。
  • ここは訳がすごく微妙です。”CakePHPプラグインとしてリリースされてない”とあるのですが、どういうことでしょうか?内部がCakePHPのコードになっていない?カスタマイズしにくい、ってことかな。MASA-Pさんとcakephperさんにご指摘頂いて、上記の訳に直しました。うーむ、英語力が追いつきませんね。
    Media Pluginに関してはECWorks BlogMASA-Pさんの記事が参考になります。

普段、ページネーションは自前で、ファイルアップロードは使わないのですが、訳したことでとても興味が出てきました。

ファイルアップロードはTransloaditというサービスが気になります。
プラグインも用意されているので試しに使ってみようかな。トラフィック次第では動画共有サイトに使えそうですね。

検索に関してはYahoo! BOSSプラグインと全文検索のプラグインが気になりますね。
Yahoo! BOSSでの日本語検索はどの程度対応しているのでしょうか?bingになってどう変わったのか…。

そのうちこの2つのサービスを取り上げみましょうか。

次は”Optimization“と”Debugging“を訳しますよ。

[連載記事一覧]

  • 認証編
    • AuthComponentの代替えになるプラグインやfacebookと連携するプラグインが紹介されています。それに加え、使いにくいACLを使いやすくするツールも。個人的には一番、興味のある記事でした。
  • 最適化/デバッグ編
    • CakePHP 2に取り込まれるであろうプラグイン、そしてContainableBehaviorをさらに高機能にしたようなプラグインが紹介されています。DebugKitの使い勝手を向上するものもありますよ!
  • ヘルパー編
    • ブログを作るなら使いたいGravatarやGoogle APIをCakePHPのために取りまとめたプラグインが紹介されています。Google APIにCakePHPを対応させるプラグインは必見です。使った人がいたら、ぜひコメントください。

GAEでPythonを始めるときに知りたい4つのまとめ

google_app_engine_for_python.png

最近、とても気になっていることがあります。
それが”Google App Engine + Python”です。

元々、Pythonには興味がありました。

オライリーのPython入門も読破しました。
10日でおぼえる Python 入門教室も5日まではやりました。

しかし、そこで終わりました…。
CakePHPという慣れ親しんだ言語とフレームワークがあったので、そちらに走ってしまいました。

ですが、私はPythonistaに憧れるPHPerです。
ここいらで一つ、重い腰を挙げてPythonの勉強を始めることにしました。

Pythonを勉強する一つの強い理由付けとして、”Google App Engine“の存在があります。
お知らせメールの運営を始めてから、サーバの管理には悩まされっぱなしです。
サーバ管理には多くの時間と手間が掛かります。
管理と運営は基本的に”事前準備”が大切です。完全に裏方の仕事です。
正直、サーバを触るのは好きだけど、管理はしたくない!んです。

では、サーバ管理を放棄するために何をすれば良いか?
その答えの一つが”Google App Engine”でした。
ここでは、@tfmagicianがGoogle App Engine上でPythonを勉強するための記事をまとめます。

皆さんがGAE上でPythonを勉強するときの参考にしてください。

Pythonを勉強する

個人的には最もノウハウが蓄積されているであろうDjangoをフレームワークとして使いたい。
そこで、Djangoのチュートリアルを絡め、Pythonを勉強するためのリンクと書籍を集めてみました。

新人プログラマのためのGoogle App Engineクラウド・アプリケーション開発講座―JAVA PYTHON対応 Google App Engine クラウドアプリケーション開発講座

  • 著者: 掌田 津耶乃
  • 出版社: ラトルズ
  • 発売日: 2009/08
オープンソース徹底活用 Slim3 on Google App Engine for Java オープンソース徹底活用 Slim3 on Google App Engine for Java

  • 著者: ひが やすを (著, 監修), 小川 信一 (著)
  • 出版社: 秀和システム
  • 発売日: 2010/7/30

この本だけJavaのフレームワークSlim3に関するものです。ただ、この本が最もGoogle App EngineのBig Tableについて詳しく述べられているようです。

Programming Google App Engine Programming Google App Engine

  • 著者: Dan Sanderson (著)
  • 出版社: Oreilly & Associates Inc
  • 発売日: 2009/11/15
Google App Engine 実践リファレンス Google App Engine 実践リファレンス

  • 著者: 清野 克行 (著)
  • 出版社: 技術評論社
  • 発売日: 2010/1/5

こう見ると、大々的にGoogle App Engine + Pythonについて触れられている書籍がありませんね。
特に日本ではPythonよりもJavaのほうが知名度も高く、使っている人が多いために、出版社の意向でこういった傾向になっていくのでしょうか。

Google App Engine上で動かすフレームワークを選ぶ

  • 【特集】Google App Engineで開発するためのフレームワーク × 16 + α
  • Google App Engine上で動くJavaとPythonのフレームワークが特集されています。

  • webapp
  • Googleが用意したGoogle App Engine用のフレームワークです。公式サイトにチュートリアルがあります。

  • Django
  • Pythonで最も使われているであろうフレームワークです。日本語で書かれた書籍が数冊出ています。

  • Kay
  • Google App Engine向けの日本製フレームワークです。これも検索するといくつか、記事を見かけました。日本語のドキュメントがあるので始めやすいのではないでしょうか。またGoogle App Engineに最適化されているのも魅力です。

  • CherryPy
  • “Pythonらしく”を目標に作られたフレームワークです。

  • Pylons
  • 比較的新しいフレームワークのようです。Ruby on Railsに強い影響を受けています。CakePHPもRoRに強い影響を受けているので、Bakerには取っ付きやすいかもしれませんね。余談ですがDropBoxのサイトもこのフレームワークで作られているそうです。

  • Flask
  • Pythonのマイクロフレームワークです。Pythonのマイクロフレームワーク「Flask」ならApp EngineのTwitter Botが15行で書けるといった記事があがっていました。

まだまだフレームワークは存在します。どれを使うか、迷いますね。
Djangoで始めようと思ったのですが、Pylonsも気になっています。

Google App Engineの運用を学ぶ

Amazon EC2との違いやGoogle App Engineの事例、Cronの使用方法などをまとめたサイトです。

Google App Engine上の料金体系を知る

Google App Engine上でアプリケーションを作るときに気になるのが、料金体系。
わかりやすく比較しているサイトをピックアップ。

ざっと、個人的にGoogle App Engine for Pythonを勉強するために必要な情報をまとめてみました。
これからGoogle App EngineでPythonを勉強する、という方はこれらの記事を見て、一緒に頑張りましょう。

あ、最後に注意ですが、自分でGoogle App Engineの情報をググるときは、”Google App Engine + キーワード“で検索したほうがたくさん見つかりますよ。
GAE + キーワード“で始め、検索していて「なんだ、GAEって情報少ないんだなぁ。」と勘違いしていましたから(苦笑)

CakePHPでフィクスチャに惑わされずにテストを書く方法 – モック編

cakephp_testing.png

前回挙げたチュートリアルはやってみましたか?
快適なテストライフを送ってますか?

テストケースをたくさん書いていると気づくのは、フィクスチャがメンテナンスの邪魔をするということ。
フィクスチャに初期データを定義すると、それを気にしながらテストケースを作ることになります。
これがとても面倒くさいんです。

これを解消すべく、今日はモックを使ったテストケースの書き方を紹介します。

モックとは

SimpleTestのモックで参考になるのは、以下の書籍です。

Webアプリケーションテスト手法 Webアプリケーションテスト手法

  • 著者: 水野 貴明 (著), 石井 勇一 (著), 新藤 愛大 (著), 岸田 健一郎 (著), 荻野 淳也 (著), 安井 力 (著), 田中 慎司 (著)
  • 出版社: 毎日コミュニケーションズ
  • 発売日: 2008/7/25

この書籍のp154にモックについて以下のように書いてあります。

モックを使うとデータファイルからではなく擬似的に値を返せるので、OrderReaderがどのような動作をするのか可視的に図ることができます。もしCSVから違う入力形式をサポートするように仕様が変わった場合にも、このテストコードを見れば、修正が用意となります。

さてモックというと何だか難しく聞こえますが、テストコードの記述手順を箇条書きにしてみれば、それほどでもないと思うでしょう。モックを使わない場合のテストは以下の手順です。

1. テストするメソッドを呼び出す
2. テスト結果を評価する

一方でモックを使う場合は、以下の手順です。

1. モックを生成する
2. モックで戻り値を設定する
3. テストするメソッドを呼び出す
4. テスト結果を評価する
5. モックが使われたか確認する

“Webアプリケーションテスト手法”より

またモックを英英辞書で引くと以下のようにあります。

You use mock to describe something which is not real or genuine, but which is intended to be very similar to the real thing.

“Collins Cobuild English Dictionary”より

“very similar to real thing”がポイントですね。
つまり、一言でモックを言うならば”見せかけのクラス”です。

ランキングモデルのテストケースの修正

今回の記事は前回の”cakephpを使ったテスト駆動開発“の続きです。
使用したコードやチュートリアルの流れなどは全て前回の記事を参考にして下さい。

以下が前回、作成したランキングモデルのテストケースです。

/* Ranking Test cases generated on: 2010-07-13 18:07:16 : 1279014616*/
App::import('Model', 'Ranking');

// テストケース用のクラスはTestCaseで終わる名前にし、CakeTestCaseを継承する。TestCaseより前はファイル名と一致させる。必ずしもテストするクラス名と一致させる必要はない。
class RankingTestCase extends CakeTestCase {
  var $fixtures = array('app.ranking');

  function startTest() {
    $this->Ranking =& ClassRegistry::init('Ranking');
  }

  function endTest() {
    unset($this->Ranking);
    ClassRegistry::flush();
  }

  /**
   * addGoodメソッドのテスト
   */
  // testで始まるメソッドがテストとして実行される。テストメソッドは必ずtestでメソッド名を始めること。
  function testAddGood() {
    debug('addGoodメソッドのテスト');

    // 正常: id=1のデータに対してaddGoodメソッドを実行する。
    // 確認: 返り値がtrueであること、goodがプラス1されていること。
    $ret = $this->Ranking->addGood(1);
    $this->assertTrue($ret);
    $params = array(
      'conditions' => array('id = ' => 1),
      'fields' => array('good'),
      'recursive' => -1
    );
    $data = $this->Ranking->find('list', $params);
    $expected = array(1 => 2);
    // この部分がテスト: 期待する結果とメソッドの戻り値を比較して等しいならテストが通る。等しくないならテストが失敗する。
    $this->assertEqual($expected, $data);

    // 異常: id=2のデータに対してaddGoodメソッドを実行する。
    // 確認: 返り値がfalseであること。
    $ret = $this->Ranking->addGood(1);
    // この部分がテスト: メソッドの戻り値がfalseならテストが通る。false以外ならテストが失敗する。
    $this->assertFalse($ret);

  }

  /**
   * getGoodTitlesメソッドのテスト
   */
  // testで始まるメソッドがテストとして実行される。テストメソッドは必ずtestでメソッド名を始めること。
  function testGetGoodTitles() {

    // 正常: 取得件数を5件に設定し、getGoogTitlesメソッドを実行する。
    // 確認: 上位から5件のデータが取得出来ること。
    $ret = $this->Ranking->getGoogTitles(5);
    $expected = array(
      array(
        'Ranking' => array(
          'id' => 1,
          'title' => 'title1',
          'good' => 10,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 2,
          'title' => 'title1',
          'good' => 9,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 3,
          'title' => 'title1',
          'good' => 8,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 4,
          'title' => 'title1',
          'good' => 7,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 5,
          'title' => 'title1',
          'good' => 6,
        ),
      ),
    );
    // この部分がテスト: 期待する結果とメソッドの戻り値を比較して等しいならテストが通る。等しくないならテストが失敗する。
    $this->assertEqual($expected, $ret);

  }

}
?>

このうち、テスト対象のRankingモデルをモック化してしまいます。
こうすることで、特定のメソッドを実行したときに呼び出されるfindメソッドやdeleteメソッドのコール回数や引数を確認出来るのです。

テスト用のクラスRankingTestCaseを書く前に以下の記述を追加してください。

/**
 * テーブルを使用させないためにテスト対象のRankingモデルをオーバライド
 */
class TestRanking extends Ranking {
  var $useTable = false;
}

Mock::generatePartial(
  'TestRanking', 'MockRanking',
  array('exists', 'updateAll', 'find')
);

これでRankingモデルがモック化されたMockRankingモデルが生成されます。
モックの考え方は初め、意味がわからないと思います。
そのため、今回は説明の前にテストケースを書いてしまいます。

<?php
/* Ranking Test cases generated on: 2010-07-13 18:07:16 : 1279014616*/
App::import('Model', 'Ranking');

/**
 * テーブルを使用させないためにテスト対象のRankingモデルをオーバライド
 */
class TestRanking extends Ranking {
  var $useTable = false;
}

Mock::generatePartial(
  'TestRanking', 'MockRanking',
  array('exists', 'updateAll', 'find')
);

class RankingTestCase extends CakeTestCase {
    // TestRankingクラスを作ったこと、モッククラスを作ったことでテーブルが不要に。フィクスチャをコメントアウトする。
	// var $fixtures = array('app.ranking');

	function startTest() {
		$this->Ranking =& ClassRegistry::init('MockRanking');
	}

	function endTest() {
		unset($this->Ranking);
		ClassRegistry::flush();
	}

  /**
   * addGoodメソッドのテスト
   */
  function testAddGood() {
    debug('addGoodメソッドのテスト');

    // 正常: id=1のデータに対してaddGoodメソッドを実行する。
    // 確認: exists, updateAllメソッドが呼ばれていること。返り値がtrueであること。

    // existsメソッドが引数なしで呼ばれていること。
    $this->Ranking->expectOnce('exists', array());
    // existsメソッドの返り値をセット
    $this->Ranking->setReturnValue('exists', true);

    // updateAllメソッドがgoodをインクリメントする内容の引数で呼ばれていること。
    $fields = array('good' => 'good + 1');
    $conditions = array('id = ' => 1);
    $this->Ranking->expectOnce('updateAll', array($fields, $conditions));
    // updateAllメソッドの返り値をセット
    $this->Ranking->setReturnValue('updateAll', true);

    // addGoodメソッドの呼び出しと結果の確認
    $ret = $this->Ranking->addGood(1);
    $this->assertTrue($ret);

  }

  function testAddGoodNothing() {

    // 異常: 存在しないデータ(existsメソッドがfalseを返すデータ)に対してaddGoodメソッドを実行する。
    // 確認: existsメソッドが呼ばれていること。updateAllメソッドが呼ばれていないこと。 

    // existsメソッドが引数なしで呼ばれていること。
    $this->Ranking->expectOnce('exists', array());
    // existsメソッドの返り値をセット
    $this->Ranking->setReturnValue('exists', false);

    // updateAllメソッドが呼ばれていないこと。
    $this->Ranking->expectNever('updateAll');

    $ret = $this->Ranking->addGood(10);
    $this->assertFalse($ret);

  }

  /**
   * getGoodTitlesメソッドのテスト
   */
  function testGetGoodTitles() {
    debug('getGoodTitlesメソッドのテスト');

    // 正常: 取得件数を5件に設定し、getGoodTitlesメソッドを実行する。
    // 確認: findメソッドが呼ばれていること。

    // findメソッドが上位5件を取得する条件で呼ばれていること。
    $params = array(
      'limit' => 5,
      'page' => 1,
      'order' => 'good DESC',
      'recursive' => -1,
    );
    $this->Ranking->expectOnce('find', array('all', $params));
    $return = array(
      array(
        'Ranking' => array(
          'id' => 6,
          'title' => 'title6',
          'good' => 10,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 5,
          'title' => 'title5',
          'good' => 9,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 4,
          'title' => 'title4',
          'good' => 8,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 3,
          'title' => 'title3',
          'good' => 7,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 2,
          'title' => 'title2',
          'good' => 6,
        ),
      ),
    );
    $this->Ranking->setReturnValue('find', $return);

    $ret = $this->Ranking->getGoodTitles(5);
    $expected = array(
      array(
        'Ranking' => array(
          'id' => 6,
          'title' => 'title6',
          'good' => 10,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 5,
          'title' => 'title5',
          'good' => 9,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 4,
          'title' => 'title4',
          'good' => 8,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 3,
          'title' => 'title3',
          'good' => 7,
        ),
      ),
      array(
        'Ranking' => array(
          'id' => 2,
          'title' => 'title2',
          'good' => 6,
        ),
      ),
    );
    $this->assertEqual($expected, $ret); 

  }

}
?>

コメントに入れた観点でテストを実施しています。
前回のテストと今回のテストの観点の比較を以下に書いておきます。

前回のテスト 今回のテスト
1. テスト対象のメソッドを呼び、テーブルに実際にデータを挿入する。 1. テスト対象のメソッドを呼び、モック化されたクラスのメソッドを呼ぶ。
2. このデータをテストケースで読み込み、想定したデータと一致するか、確認する。 2. こうして呼ばれたメソッドの呼び出し回数や引数が一致するか、確認する。

まずはモックを使うと、”このようなテストが出来る“ということが理解出来ましたか?
これがわかれば後は書き方を理解するだけです。

モックの作り方

先ほどのコーディングで何をしているのか、モックはどう書くのかを順を追って説明して行きます。

まずはモックのイメージです。
始めに書いた以下のコードは、次のようなクラスを作成することを意味しています。

/**
 * テーブルを使用させないためにテスト対象のRankingモデルをオーバライド
 */
class TestRanking extends Ranking {
  var $useTable = false;
}

Mock::generatePartial(
  'TestRanking', 'MockRanking',
  array('exists', 'updateAll', 'find')
);

mock_object.png
モックのイメージ

このことは、Mock::generatePartialをprintすると簡単にわかります。

<?php
class MockRanking extends TestRanking {
  var $_mock;
  var $_mocked_methods = array('exists', 'updateall', 'find');

  function MockRanking() { $this->_mock = &new SimpleMock();
    $this->_mock->disableExpectationNameChecks();
  }

  function setReturnValue($method, $value, $args = false) {
    if (! in_array(strtolower($method), $this->_mocked_methods)) {
      trigger_error("Method [$method] is not mocked");
      $null = null;
      return $null;
    }
    $this->_mock->setReturnValue($method, $value, $args);
  }

  /** ----- 省略 ----- **/

  function exists() {
    $args = func_get_args();
    $result = &$this->_mock->_invoke("exists", $args);
    return $result;
  }  

  function updateAll() {
    $args = func_get_args();
    $result = &$this->_mock->_invoke("updateAll", $args);
    return $result;
  }  

  function find() {
    $args = func_get_args();
    $result = &$this->_mock->_invoke("find", $args);
    return $result;
  }

}
?>

わかりますか?つまり、Mockクラスがgenerateメソッドの呼び出しを受けて、自動的にコードを吐き出しているんです。
PHPはスクリプト言語なので、こうして吐き出されたコードも簡単に自身のコードの一部として扱うことが出来ます。
こういった面白いハックを見ると、自分でも作ってみたくなりますね。

話が少し逸れました。モックのクラスの生成について理解出来ましたか?
これが理解出来れば後は簡単、テストに使用する基本的なメソッドの動きがわかってくると思います。

後はもう自由にテストを書けますよね?

モックでよく使用するメソッド

最後にモックでよく使用するメソッドをまとめておきます。
もちろん、モック化したクラスに対してメソッドを実行してください。

このメソッド一覧があれば、どんなテストも簡単に書けるようになるでしょう。

メソッド名 動作
expectAt
($n, $method, $arguments)
n回目の特定のメソッドの呼び出しが指定した引数であることを確認する。
expectCallCount
($method, $n)
特定のメソッドがn回呼び出されることを確認する。
expectNever
($method)
特定のメソッドが呼び出されないことを確認する。
expectOnce
($method, $arguments)
呼び出し回数が1回で、かつ指定した引数であることを確認する。引数指定を省略した場合は、呼び出し回数のみ確認する。
setReturnValue
($method, $value)
特定のメソッドの返り値をセットする。
setReturnValueAt
($n, $method, $value)
特定のメソッドのn回目の返り値をセットする。

モック、なかなか便利でしょう。
私はよく、フィクスチャを書くのが面倒なときやデータベースの仕様が固まっていないときに、モデルのモックを作りテストします。

特にデータベースの仕様が固まっていないときに便利です。
テストケースを書きながら、テーブルのフィールドや型を考えることが出来ます。

これは以下の4ステップが、

  1. データベース仕様を決める。
  2. メソッドを作成する。
  3. データベース仕様を変更する。
  4. メソッドを変更する。

次の2ステップに変わることを意味します。

  1. メソッドを作成する。
  2. データベース仕様を決める。

なかなか、便利なので皆さんもやってみてください。

モックを使ったテストケースの書き方がわかりましたか?
もし、わからない点や間違った点があれば気軽にコメントをください。

[2010/09/08 追記]

Pythonでのテストの記事ですが、テストやメソッドを設計する上で参考になる点が多いと思います。
ご一緒にどうぞ。

空気のように開発をサポートするLaunchBar

application_launch_bar.png

システムを開発するときは、長時間お気に入りのMacの前で色々な操作をしていきます。
例えば、”PHPの関数を忘れたから検索する。”、”アルゴリズムがわからないからググる。”、あるいは”仕様書を保存したディレクトリがわからないからSpotlightで検索する。”などなど。

皆さんはどういう手段でこれらの操作を始めますか?Dock?それともブラウザのブックマーク?
これらの操作の始まりをまとめてくれるアプリケーションがあったら素敵だと思いませんか?

それを実現してくれるのがLaunchBarです。

LaunchBarとは

LaunchBarはカテゴリに分けるとすれば”ランチャー“に当てはまります。
しかし、他のランチャーとは違い、

  1. LaunchBarを立ち上げる。
  2. 文字列を入力する。
  3. アプリケーションや操作を選ぶ。
  4. 選択したアプリケーションや操作を実行する。

という流れで使用します。

他のランチャーと違うのは、アップルスクリプトやシェルスクリプトを使うことで操作を選択出来ることです。
単なるショートカットではなく、ランチャー対象に対してコマンドを渡せるのです。
これがLaunchBarの強みであり、LaunchBarが開発を空気のようにサポートしてくれる理由です。

24ドルの有料アプリケーションですが、それだけの価値があります。
円高のうちにぜひ買ってください(笑)
(ちなみにトライアル版は30日間無料で使えます)

LaunchBarのインストール

インストールは通常のMacと同様、以下のURLからファイルをダウンロードし、ドラッグ&ドロップで完了です。

launch_bar_installation.png
LaunchBarのインストール

LaunchBarを使う

早速、使ってみましょう。
LaunchBarは起動方法さえわかれば、その世界を味わうことが出来ます。

まず、LaunchBarを立ち上げてください。ドックにアイコンが出ればLaunchBarは立ち上がっています。

launch_bar_on_dock.png
ドック上のLaunchBar

次にショートカットでLaunchBarを呼び出します。
初期設定では”Command” + “Space”が設定されています。”Command”キーを押した後に”Space”キーを押してください。

launch_bar_window.png
LaunchBarのウィンドウ

この小さなウィンドウの先には大きな世界が広がっています。

練習として、良く使う”システム環境設定”をLaunchBarから呼び出します。

  1. “Command” + “Space”を押す。
  2. “system”とタイプする。
  3. 十字キーで”System Preferences”を選択する。
  4. “Enter”キーをタイプする。

launch_bar_input_system.png
該当するアプリケーション一覧

launch_bar_select.png
十字キーで選択

これで”システム環境設定”を呼び出せました。
これがLaunchBarの基本操作です。

次に、”LaunchBar”をググってみます。

  1. “Command” + “Space”を押す。
  2. “google”とタイプする。
  3. 十字キーで”google”を選択する。
  4. “space”キーをタイプする。
  5. “LaunchBar”とタイプする。
  6. “Enter”キーをタイプする。

launch_bar_google_search.png
LaunchBarに検索ワードを入力

そうするとブラウザが立ち上がり、Googleの検索結果が出るはずです。
どうですか?LaunchBarの公式ページが表示されましたか?

ここでは十字キーを使ってアプリケーションを選択しましたが、何度か同じ操作を繰り返すとLaunchBarが入力を記憶してくれ、よく使うアプリケーションが上位に来るようになります。
そのため、ほとんどの場合、十字キーに触れずに操作が完了します。
これもLaunchBarが空気のような存在である理由ですね。

LaunchBarのカスタマイズ

LaunchBarは初期設定でしばらくは満足できるはずです。
例えば、以下のことが初期設定で実行できます。

LaunchBarで実行出来るデフォルトの機能

出来ること 打ち込むコマンド
アプリケーションを実行する アプリケーション名
Wikipediaを検索する wikipedia
PHPドキュメントを検索する php
TinyURLでURLを短縮する make tinyurl
コピーの履歴を表示する clipboard
検索履歴を開く ページのタイトルの一部
電卓で計算する 数値と式(プラスやマイナスなどの記号)
sshでサーバにログインする ssh + space + オプション指定

これだけでも十分ことが足りそうですね。
私自身も初期設定でしばらく使っていました。

しかし、LaunchBarはカスタマイズすることでどんどん自分にフィットしていきます。
私が追加しているカスタマイズを紹介します。

“Shift”キーx2で起動

私は起動方法を変更しています。
“Command” + “Space”だと、ことえりの文字種選択画面が開いてしまうため、使いにくいのです。

この設定は簡単に出来ます。

launch_bar_preferences.png
LaunchBar >> Preferences

launch_bar_double_shift.png
Shortcuts

これで”Shift”キーを連続2回押すことでLaunchBarが起動するようになります。

sshのショートカット

私はシェルスクリプトにsshコマンドを書き込み、ショートカットを作っています。
これをLaunchBarのインデックスに含めることで、一発で特定サーバにログインできます。

launch_bar_show_index.png
Index >> Show Index

launch_bar_unix_executables.png
Unix Executables

launch_bar_unix_executables_add.png
追加したいファイルを選択/Add

これでLaunchBarのインデックスに追加されました。
Unix Executablesのルールに追加することで、シェルがターミナルで実行されるようになります。

[注意]

  • シェルに実行権限を与えておくこと。
  • 拡張子を与えないこと。

拡張子には特に気をつけてください。拡張子をつけてしまうと、エディタが開いてしまい、思った通りの動作になりません。
シェルスクリプトの中身は普通に書けばOKです。わからない人はググってください。

CakePHPのAPI検索

CakePHPに限らず、検索クエリがURLに含まれているものは全てLaunchBarに対応できます。
ここではCakePHPのAPI検索をLaunchBarに追加します。
LaunchBarのコマンド追加は全て先ほどの”Index >> Show Index”から行います。

launch_bar_search_templates.png
Search Templates (UTF-8)

launch_bar_search_templates_add.png
インデックス名とURLを入力

URLを入力するときに検索ワードに置き換えたい文字列を”*(アスタリスク)”とします。
この”*(アスタリスク)”が、”Space”キーを押した後に入力した検索ワードに置き換わります。
CakePHPのAPI検索は以下のように入力します。

Name URL
CakePHP http://api13.cakephp.org/search/*

Amazon.co.jpの商品検索

AmazonもCakePHPのAPI検索と同様の方法で実装できます。

Name URL
Amazon Japan http://www.amazon.co.jp/s?ie=UTF8&index=blended&keywords=*

英英辞書の登録

英英辞書もCakePHPのAPI検索やAmazonの商品検索と同様の方法で実装できます。

私はLongmanとCollins Cobuildを登録してあります。

Name URL
Longman http://www.ldoceonline.com/search/?q=*
Collins Cobuild http://dictionary.reverso.net/english-cobuild/*

ここでは関係ないですが、この2つの辞書はオススメです。特にCollins Cobuildは例文が多くとてもわかりやすい!

Omnifocusとの連携

Omnifocusにタスクを追加するにはスクリプトを利用します。
私はこちらのスクリプトを利用させて頂いてます。

on handle_string(taskName)
   if taskName is not "" then
      tell application "OmniFocus" to tell default document
         make new inbox task with properties {name:taskName}
      end tell
      open location "x-launchbar:hide"
   end if
end handle_string

これをomnifocus.scptという名前で好きなディレクトリに保存し、インデックスにこのディレクトリを追加します。

launch_bar_directory_add.png
“Add Folder…”からディレクトリごとインデックスに追加

これで以下の流れでInboxにタスクを追加できます。

  1. “omnifocus”とタイプする。
  2. “omnifocus.scpt”を選択する。
  3. “space”キーをタイプする。
  4. タスクを入力する。

私はこの操作か、Google検索を一番使っていると思います。

カスタマイズする上で参考になるページ

最後にカスタマイズの参考になるページを挙げておきます。
私よりも使いこなしている人がたくさんいるので、これらのページを見てLaunchBarマスターになってください。

CakePHPプラグインのまとめ – 認証編

cakephp_plugins.png

CakePHPのフォーラムで知ったのですが、Jose Diaz-Gonzalezさんがプラグインのまとめを書いてくれています。

CakePHPに限らずフレームワークの良いところ、それはやはりソースコードを再利用できることです。
そしてそのフレームワークの利用者が多ければ多いほど、再利用できるソースコードが増えていきます。

CakePHPにはビヘイビアやコンポートネント、ヘルパーから始まり、そして様々なものを統合して活用できるプラグインといったコードを再利用する仕組みが採用されています。
今回は先ほど紹介したブログの記事を日本語訳し、自分なりにまとめて、紹介します。

[注意] 初めてまともに訳すので、かなり意訳してます。ここはおかしい、変だ、という点があればご指摘ください。

認証と承認

  • Debuggable’s Authsome: AuthComponentを置き換えるのは悪いことじゃない。このコンポーネントは君のアプリ側でリダイレクトを操作できないからだ。その一方で、こいつはAuthsome::get('fieldName')というメソッドを持っていて、どこでも簡単にユーザデータを扱うことが出来るんだ。
  • “Auth for people who hate the Auth component”(AuthComponentが嫌いな人のための認証)というタイトルでgithubに公開していますね。
    AuthComponentはその構造上、カスタマイズが非常にしにくいです。
    AuthComponentとは違い、認証に関連する様々な設定を独自に定義出来る認証コンポーネントのようです。

  • Jose Gonzalez’s Sanction: 何か私が作るとき、AuthSomeとシンプルな設定ファイルでアプリケーションのパーミションを制御する。これはHtmlHelperの置き換えとして使うことができ、さらに君の設定ファイルをハックできるよ。
  • これを使うと、コンポーネントのオプションにアクセス許可するメソッドを指定出来るようです(AuthComponentはまだオプション指定出来ませんよね?)。
    さらに、ヘルパーが付属していて、これを使うと”認証が必要なページへのリンク“を自動的に非表示にする機能も備えているようです。
    かなり便利そうですね。

  • Nick Baker’s Facebook: Facebookをと連携するプラグインだ。AuthComponentと連携して使うことが出来る – 恐らく、Authsomeとも。Facebook認証部分のフル機能を作れるね。いつかはこれを使ってみたい。デモサイトはここ
  • AuthComponentや独自の認証システムと連携してfacebookの認証システムを構築出来る、とありますね。
    次のような機能を実装出来るようです。
    – Share (サイトをシェアする機能)
    – Like (良いね!機能)
    – Login/Logout (登録不要でfacebookユーザを認証する機能)
    – Activity (あなたのアプリケーションと友達のアクティビティを公開する機能)
    – Friend Pile (あなたのアプリケーションの友達を表示する機能)
    – Recommendations (現在のページのレコメンドを表示する機能)
    – Fan Boxes (あなたのアプリケーションのファンにする機能)
    – Profile Pictures (ユーザのプロフィール画像を表示する機能)
    – Live Streams (facebookを通して動的なライブストリーミングイベントを作り、あなたのサイト通してアクセスする機能)
    – Comments (facebookのコメントをあなたのサイトの好きな部分で利用する機能)
    – Status (ユーザのステータスを表示する機能)
    facebookの機能がわからないので微妙ですが、なんだか色々なことが出来そうです。
    これも使ってみたいものリスト入り。

  • Nick Baker’s Gigya: これはカスタマイズしたソーシャルネットワークプラグインのようだね。これを使うと君のアプリで1つのAPIを他のAPIと統合できるみたいだ。まだ成熟していないけど、確かに良いアイディアだ。
  • 何が出来るかはここを見るとわかりやすいですね。

    Gigyaというサービスを介してSNSをまとめてしまおうという試みのようです。
    Gigyaに対して”シェア”すれば、取りまとめたSNS全てに反映される、ということでしょうか?
    逆に自分で作ったサイトからGigyaに対して認証すれば、ユーザが持っているどれかのSNSで自動的に認証してくれると?
    これは、そのGigyaと連携するためのプラグインのようです。
    これも面白そうなサービスですね。

  • Jedt’s Spark Plug: AuthsomeとシンプルなACLで実装したとても便利なユーザ管理と管理者機能。私はまだ試してないけど、恐らくこれはパーミションのフィルタリングのために私の作ったコンポーネントを使ってるね。すぐにスクリーンショットを欲しいな。
  • READMEに詳しく載っていないので詳細は不明ですが、サンプルのconfig.phpを覗くと、かなりわかりやすくACLの設定が出来るようです。
    私はACL自体、あまり使ったことがないので微妙ですが、これも使いやすそうなプラグインです。

  • Valerij Bancer’s PoundCake Control Panel: プラグインの説明から: “動的にデータベースからユーザとグループにパーミションを割り当てるためのACLメニューを自動生成する、ユーザとグループ管理のためのパネル”、良いね。
  • これもREADMEが詳しく載っていないので、詳細は不明ですが、こんなスクリーンショットが置いてありました。
    screenshot.jpg
    これを自動で作れるならかなり素敵ですね。

  • Travis Rowland’s SuperAuth: もし君のサイトでローレベルなACLを実装したいなら、これはお勧めしない。こいつは本当に高機能で – テストは不足しているものの、本当に機能が充実している。テストが充実してたなら、本当にお勧めするよ。
  • 自動ログイン“の機能も備えているようです。
    パーミションのキャッシュ機能、クエリと一緒にパーミションを取得する機能、マルチグループ対応等々、本当に色々な機能を備えているようです。
    その一方、CakePHP 1.2でのテストやマルチグループのテストを行っていないと書いてあり、確かにテスト不足のようです。

  • Mark Story’s ACL Extras: とても扱いにくいACL。いつもACLを使えって?このシェルを使ったなら、君のACLライフを簡単にしてくれるよ。
  • 先ほど述べた通りACLを使ったことがないので、コメント出来ないのですが…ACLの面倒な設定をCLIで出来るのかな?

  • Mark Story’s Menu Component: ACLベースのメニューを作成してくれる。とてもクールじゃない?
  • ACLに基づいたメニューをコントローラをスキャンすることで自動で作ってくれるようです。
    メニューは配列としてビューにセットされるので、開発者はそれを表示するだけ、という訳です。
    きっちり構造化してあればかなり便利ですね。

  • Matt Curry’s Static User: このコードAuthsome::get('fieldName')を使うには、君はAuthComponentのコードを使いすぎてるよね?AuthComponentと同じことをするための簡単な方法だよ。ただし、User::get('fieldName')のような形だけど。とてもシンプルに実装できるから試してみて。
  • 認証系の処理をオーバーライドしてくれるイメージのようです。
    しかも、認証したユーザの情報をどこでも(ModelだろうがViewだろうが)取り出せるようにしてくれるんですね。
    うまく使わないと、コードがややこしくなりそうですが、これも便利そうです。

なかなか独特な表現を使っていて訳すのは難しいですね。
私の英語のレベルが低いからだとは思いますが…。

次はSearching and Paginationを訳しますね。

[連載記事一覧]

  • 検索とページネーション/ファイルアップロード編
    • CakePHPの使いにくいページネーションを使いやすくするプラグイン、Yahoo! BOSSのプラグインが紹介されています。ファイルアップロードはTransloaditというサービスに対応させるプラグインから、メジャーなメディアプラグインまで。
  • 最適化/デバッグ編
    • CakePHP 2に取り込まれるであろうプラグイン、そしてContainableBehaviorをさらに高機能にしたようなプラグインが紹介されています。DebugKitの使い勝手を向上するものもありますよ!
  • ヘルパー編
    • ブログを作るなら使いたいGravatarやGoogle APIをCakePHPのために取りまとめたプラグインが紹介されています。Google APIにCakePHPを対応させるプラグインは必見です。使った人がいたら、ぜひコメントください。

私がUbuntuを使うのをやめた理由

ubuntu.png

私は1年前までUbuntuユーザでした。
ノートPCを買ったは良いのですが、入っていたのはあの有名なWindows Vista
使いこなそうと努力はしたのですが、結局Windowsを捨てる理由となったOSとなりました。

そしてそのノートPCにUbuntuを入れて2年ほど使用していました。
しかし、今はMacに乗り換えています。
なぜ、Ubuntuをやめたのか、それなりに理由があります。

開発の補助ツールの欠如

UbuntuはDebianベースのOSです。
そのため、開発環境の構築は容易に出来ます。
aptコマンドを実行するだけで、テスト環境ならすぐに構築できます。

しかし、開発をするために必要なのはApacheやMySQLだけではありません。
それを補助するアプリケーションが必要なのです。
私が今、Macで使っているアプリケーションです。

これらはどれも私にとって重要なアプリケーションばかりです。
仕事の7つ道具といっても過言ではありません。

Ubuntuにはこいうったアプリケーションが欠如している、あるいは使い勝手が悪いものばかりでした。

例えば、EvernoteはLinuxのネイティブ版がありません。
Webアプリケーションでは動作が重く、またネットがない環境で使用することが出来ません。

もちろん、SafariもLinuxネイティブ版はありません。
Firefoxが標準のブラウザなのですが、重たいため日常的に使っていると不満が出てきます。

MindManagerの代替えとしてFreeMindがあります。
ただ、これもマインドマッパーには物足りないものです。
グラフィックが悪く、使っているうちにこの点がとても気になります。

全体的にWindowsよりも動作が快適で、Ubuntu自体のグラフィックも素晴らしいものでした。
しかし、こういった補助ツールの欠如が最終的にUbuntuからMacに乗り換えるきっかけになったのです。

さて、ここまではUbuntuの悪口でした。
ここからは上記で紹介したアプリケーションの代替えを探してみることにします。

Safari

Safariの良いところはMacとの親和性と、その動作速度です。
Macでは親和性からSafariを使っています。

Ubuntuではこういった親和性の部分でブラウザを選ぶことが出来ないので、速度を重視して選びます。
そうなると、去年の12月にLinux β版が公開されたChromeが一番でしょう。

chrome.png

ChromeならLinuxでもFirefoxの重い動作から解放される気がします。

Firefox

Firefoxの魅力はそのアドオンの多さ
Webアプリケーションを作る上でFirefoxのアドオンは必須です。

LinuxのFirefoxでもアドオンは使えるので、そのまま使いましょう。

firefox.jpg

Evernote

これが一番問題です。Evernoteにはネイティブ版がありません。
WindowsのアプリケーションをシミュレートするWineというアプリケーションがありますが、これは動作が重たかったり、不安定だったりと日々の運用が大変になるのでお勧めしません。

そのため、選択肢は一つでしょう。

Chrome + Evernote Web版です。

chrome.png + evernote.jpg

ChromeのJSの動作速度は驚くべきものです。
これと合わせれば日常的な用途でのEvernoteの使用は耐えうるものになるでしょう。

ただ、ネットがない環境でEvernoteが使えない、という弱点が残ります。
たまに、私はオフラインの環境でコーディングするのでこれは痛い。

LaunchBar

ランチャーはLaunchBarと同じような機能を持ったGNOME Doというアプリケーションが出ていました。
私がUbuntuを使っていたころはLaunchyを使っていたのですが、こちらのほうが使い勝手が良さそうです。
参考ページを載せておくのでぜひ使いこなしてください。

gnome_do.png

GNOME Doって何?という方はこちらの紹介記事をどうぞ。

MindManager

正直、UbuntuにMindManagerの代わりになるアプリケーションはないでしょう。
商用のアプリケーションなだけあって、MindManagerは機能が豊富です。

実際にFreeMindを使ってみて、微妙だったので、ここでは別のアプリケーションを紹介しておきます。

VYM.png

MOONGIFTさんでも紹介されていますね。

Omnifocus

OmnifocusはGTDを実践するためのタスク管理ソフトです。
これも商用のアプリケーションなだけあって、機能は豊富で、安定感は抜群です。
またGTDの考えにしっかりと沿って作り込まれています。

これも完璧に代替えできるアプリケーションはないでしょう。
ただ、LinuxでGTDを実践できるアプリケーションを見つけたので、紹介しておきます。

このアプリ、興味があるので時間があれば詳しくレビューしたいですね。

gtg.png

VMware Fusion

VMware Fusionは開発用のLinuxを走らせるために使用しています。

LinuxでVMware Fusionの代替えといえば、VirtualBoxでしょう。

virtualbox.png

Ubuntuなのでそのまま開発環境に使ってもいいのですが、ぜひ仮想化することをお勧めします。
気軽に再インストールやバックアップの復帰ができるので、パッケージの依存関係を壊してしまったときなんかに便利ですよ。

UbuntuからMacに乗り換えて1年。
こうやって冷静に振り返ってみると、なかなか良いアプリケーションが揃っていますね。
私自信の知識がなかったのもUbuntuからMacに乗り換えた要因の一つな気がしてきました…。

商用と違い、全てオープンソース。無料で環境を整えることが出来るのも魅力です。

そして、Ubuntu 10.10からはマルチタッチ対応になります。
これで、Macのような操作性を手に入れる準備は整ったことになります。

私がMacからUbuntuに乗り換える日がまた来るかもしれませんね。

[2010/08/30 追記]
ここ数日、Ubuntuのディスクトップ使用について良い記事があがっていました。
1つ目はTwitterクライアントの紹介です。

Twitterクライアントにも満足出来ていなかったので、これは注目のアプリケーションです。

2つ目はUbuntu環境をノートPCに1から構築する記事です。
とてもたくさんのアプリケーションが紹介されています。そして、どれも使い勝手が良さそうです。

こういった記事を見ると、改めて私がUbuntuを使っていたころに調べが足りなかったんだな、と思い知らされます。
皆さんもUbuntuをやめる前に読んでみてください。
きっと、Ubuntuを使いたくなりますよ。

CakePHP 1.2.8 released

bakery.png

CakePHP1.2.8がリリースされました。
The Bakeryの記事の翻訳を載せておきます。

CakePHP 1.2.8 released

マイナーバージョンアップなので細かな修正が主ですね。

CakePHP 1.2.8 リリース

By Mark Story (mark_story)

CakePHP開発チームはCakePHP 1.2.8のリリースをアナウンスできてとてもハッピーです。1.2.8は1.2ブランチのメンテナンスリリースで1.2で、バグフィックスと最適化が含まれています。

4月に1.2.7をリリースしてから、30のコミットと20のチケットを処理しました。あなたのアプリケーションに少しだけ影響があるでしょう。

- l10nにおいてウェールズ語をサポートしました。
- Controller::validateErrorsが独立したモデルオブジェクトを許可するようになりました。詳しくはドキュメントに載せてあります。
- bindModelとunbindModelを複数回呼んだ場合に正しくアソシエーションをリセットするようになりました。
- TextHelper::autoLink()がURLに対してstrtolower関数を呼ばなくなりました。これは短縮URLに対応するためのものです。
- HttpSocketが同じオブジェクトに対してリクエストを発行した場合に認証を破棄しなくなりました。
- String::insert()は正しく、同じサブパターンで始まる2つのキーを扱えるようになりました。

もし全ての変更が知りたいならchangelog[2]とそれに関連するチケットをチェックすると良いでしょう。
1.3の開発とサポートが続けられています。2.0のブランチもまたいくつかのブランチと共に開発中です。
もし2.0の開発の手助けに興味があるなら、wikiのpages[3]を見て、チケットを投げてください。
私たちはリリースのためにチケット、パッチ、そしてドキュメントに貢献してくた皆さんに感謝したいです。
Cakefest 2010[4]もまた準備が着々と進んでいます。もしチケットをまだ買っていないなら、買えるうちに買ってください。

Download a packaged release [1]
View the changelog[2]

[1] http://github.com/cakephp/cakephp/downloads
[2] http://cakephp.lighthouseapp.com/projects/42648/changelog-1-2-8
[3] http://cakephp.lighthouseapp.com/projects/42648/milestones/71894-200
[4] http://cakefest.org

Controller::validateErrorsの修正

どのような修正か、わからなかったので少し調べました。

http://github.com/cakephp/cakephp/commit/418b8e5

どうやら、今まではコントローラにアタッチされていたモデルだけがvalidateErrorsの対象として選べたようです。
これたアタッチされていないモデルもvalidateErrorsの対象として選べるようになったようです。

訳が怪しいですが、頑張ってみました。皆さんのお役に立てれば幸いです。

集中してコーディングするための5つのtips

coffe-red.jpg

個人的に実践している”集中してコーディングするための5つのtips“をまとめます。

行程を分ける

rules.png

“コーディングの中”でも行程を分けましょう。

  1. 書く
    • コードを書くだけです。関数名を忘れてしまってもそこは適当に埋めて、飛ばします。また、この段階で実行しないように。
  2. 穴を埋める
    • 1.でわからなかった点を埋めます。ドキュメントやググって見た目上、正しいコードを書きます。
  3. 構文エラーを修正する
    • ここで初めて実行します。まず、1回では動かないのでテストと修正を繰り返し、構文エラーをなくします。

コーディングの中でさらに行程を分けることで、それぞれに集中しやすくなります。
特に“書く”段階の効果は絶大です。
何も気にせずにひたすらコードを書けるので、ゾーンに入りやすくなります。

時計をなくす

clock.png

時間を気にすると、それが焦りを、焦りは集中を奪います。
かといって無制限に仕事をする訳にはいきません。
そこで時計なくして、カウントダウンタイマーに切り替えます。

私はMacで時計を非表示に、カウントダウンタイマーとしてConcentrateを使っています。

インターネットを切る

noaccess.jpg

集中力を持続させるために、気を散らしてはいけません。
特にゾーンに入る前は注意力が散乱しがち。
ゾーンに入ればインターネットに繋がっていようが何も関係ありませんが、そこに持って行くためにインターネットを切りましょう。

私はコーディングするときは(ハンバーガーの)マックによく行きます。
そのときはモバイル端末を持たないように。

不要なプリケーションを閉じる

close.png

不要なウィンドウとアプリケーションは閉じましょう。
必要になったら開けば良いだけです。

私はターミナルとOmnifocusだけしか開いていません。
あとは必要に応じて立ち上げては消します。

また、Concentrateも活用しています。

logo.png
Concentrate

このアプリケーションは集中に不要なアプリケーション(例えばTweetie)を強制的に落としてくれます。
立ち上げそうになっても、Concentrateがブロックしてくれます。
同様にウェブサイトもブロックできるので、SNS全般へのアクセスを禁止し、集中力を高めることが出来ます。

慣れている言語で書く

programs.png

ある意味これが一番重要かもしれません。
不慣れな言語で書くと、構文や関数がわからず、コーディングの段階でゾーンに入ることが出来ません。
一番慣れている言語を使って、集中してコーディングしましょう。

私がコーディングするときに気をつけていることをまとめました。
ゾーンに入ることが出来れば、気分爽快です。
コードがいつの間にか出来上がってるのですから!

lifehackerさんの記事やこちらの書籍も参考になりますね。

最強の集中術 最強の集中術

  • 著者: ルーシー・ジョー・パラディーノ
  • 出版社: エクスナレッジ
  • 発売日: 2008/03/26

皆さんはコードを集中して書くときに工夫してることはありますか?

Macのカーソル移動をvi風にするハック

vi_keyboard.png

viに慣れてくるとGUIでも”j”キーや”k”キーを打ってしまいませんか?
あるいは”i”キー、”d”と”$”キー…。

こうしてブログを書いてる間も、それらのキーを打ち込んでいまいそうです(苦笑)

そこで、Macでvi風の操作が出来ないか、調べてみました。
そうすると、そういったキーマップをしてくれるソフトウェアがあるんですね。

KeyMap4MackBook

viのコマンドモードと入力モードを再現したり、特定のキーとviのコマンドキーをタイプしたときだけviの動作を再現することが出来るようです。
もちろん、Snow Leopardにも対応しています。

こちらのスライドがわかりやすいですね。

Keyremap4macbook進化したvi mode

まだ私は導入していませんが、うまくキー割当が出来るなら入れてみたいな、と思っています。
使っている方、いらっしゃいますか?

PHPでテキストの類似度を求める

levenshtein.png

テキストを扱うサービスでは表現の揺れが問題になることが多いですね。
Web上にあるテキストコンテンツは全て人間が入力したものと言っても過言ではありません。

人間が入力する、ということは必ず表現の揺れが発生します。
単純な入力ミスや、複数存在する表現などによるものです。

お知らせメールでもこの例に漏れず、表現の揺れが問題になりました。
ユーザが登録したアーティストや著者に関する情報は、漏れなくユーザに届ける必要があります。
届かなければサイトの信頼に関わりますからね。

内部の処理として文字列の類似度を計算している部分があります。
類似度をプログラムで計算し、登録したアーティストや著者に関連する情報か否か、をフィルタリングしています。

ここで言う文字列の類似度とは、”田中太郎”と”田中次郎”の文字列としての近さを言います。
“田中太郎”と”田中次郎”では”太”と”次”が違うだけなので、かなり近い文字列だと言えます。

今回は、このような文字列の近さをプログラムで求めます。
実例として、検索エンジンの”もしかして”検索に使われているようです。

レーベンシュタイン距離

冒頭で説明したような文字列の類似度の1つがレーベンシュタイン距離です。

レーベンシュタイン距離とは文字操作をコストとして変換、そしてそれの合計を文字列の近さとして数値化するものです。
Wikipediaには以下のようにあります。

レーベンシュタイン距離(レーベンシュタインきょり)あるいは編集距離(へんしゅうきょり)は、情報理論において、二つの文字列がどの程度異なっているかを示す数値である。具体的には、文字の挿入や削除、置換によって、一つの文字列を別の文字列に変形するのに必要な手順の最小回数として与えられる。
この用語は、1965年にこの距離の概念を考案したロシアの学者ウラジミール・レーベンシュタインにちなんで命名された。スペルチェッカー等において、二つの文字列がどの程度に類似しているかの決定が求められる場合、レーベンシュタイン距離の応用は有用である。

このレーベンシュタイン距離を求めるアルゴリズムが存在します(当たり前か)。
そして、なんとPHPにはデフォルトでこれを求める関数(levenshtein)も存在します。
しかし、調べたところ、これがマルチバイトに対応していないことがわかりました。残念。

また、PHPにはsimilar_textという関数もあるのですが、こちらもマルチバイトだとうまく動作しないようです。
理由はASTRODEOさんの記事を見てください。

そこで今回は自作することにしました。
ただ、一から考える時間もなかったので、参考になる記事を探しました。

特にnaoyaさんの記事はレーベンシュタイン距離について詳しく書いてあり、参考になります。

この2つの記事から書いたコードがこれです。

function levenshtein($string1, $string2, $insert = 1, $delete = 1, $replace = 1) {
  preg_match_all('/./u', $string1, $string1, PREG_PATTERN_ORDER);
  $string1 = $string1[0];
  $stringLength1 = count($string1);
  preg_match_all('/./u', $string2, $string2, PREG_PATTERN_ORDER);
  $string2 = $string2[0];
  $stringLength2 = count($string2);

  $d = array();
  for ($i = 0; $i <= $stringLength1; $i++) {
    $d[$i] = array($i);
  }
  for ($j = 0; $j <= $stringLength2; $j++) {
    $d[0][$j] = $j;
  }   

  for ($i = 1; $i <= $stringLength1; $i ++) {
    for ($j = 1; $j <= $stringLength2; $j ++) {
      $cost = ($string1[$i - 1] == $string2[$j - 1]) ? 0 : 1;
      $d[$i][$j] = min(
        $d[$i - 1][$j] + $insert,
        $d[$i][$j - 1] + $delete,
        $d[$i - 1][$j - 1] + ($replace * $cost));
    }
  }   

  return $d[$stringLength1][$stringLength2];
}

コードの工夫

S1100さんのコードでは文字列を配列化するために独自の関数を作っているのですが、私はpreg_match_allを利用して分割しました。
文字コードもUTF-8に対応するだけで良かったため、文字コードを指定する部分も省いています。

正規表現マッチを使っているので速度的にはどうなの?と思ったのですが、結論から言うとpreg_match_allを使った方が速かったです。
速度の計測に使ったソースと、その結果が次です。

独自の関数で分割した場合

<?php
function mbStringToArray($string, $encoding = 'UTF-8') {
  $results = array();
  $len = mb_strlen($string, $encoding);
  for($i = 0; $i < $len; $i ++) {
    $results[] = mb_substr($string, $i, 1, $encoding);
  }
  return $results;
}

$diffSum = 0;
for($i = 0; $i < 100; $i ++) {
  $string = 'こんにちは。世界。/Hello World!/Hello PHP World!/こんにちは。マルチバイトの世界。';

  $start = microtime(true);
  $array = mbStringToArray($string);
  $end = microtime(true);

  $diffSum += ($end - $start);
}

printf('average : %s[sec]'."\n", ($diffSum / 100) * 1000);
?>

preg_match_allで分割した場合

<?php
$diffSum = 0;
for($i = 0; $i < 100; $i ++) {
  $string = 'こんにちは。世界。/Hello World!/Hello PHP World!/こんにちは。マルチバイトの世界。';

  $start = microtime(true);
  preg_match_all('/./u', $string, $array);
  $array = $array[0];
  $end = microtime(true);

  $diffSum += ($end - $start);
}

printf('average : %s[sec]'."\n", ($diffSum / 100) * 1000);
?>

mbStringToArrayとpreg_match_allの速度比較

プログラム実行回数 100回の平均実行時間[s]
mbStringToArray preg_match_all
1 0.06064 0.02315
2 0.05609 0.02305
3 0.06398 0.02679
4 0.05628 0.02199
5 0.05911 0.02295
平均 0.05922 0.02358

さすが組み込み関数ですね。2倍ほど速度が違います。
ただ、UTF-8以外のコードでレーベンシュタイン距離を求めたい、といったときにはこれは使えませんね。

それとS1100さんのコードと比べ、同じ文字をカウントする処理も取り除いてあります。
似ている文字列か否かのしきい値にこの値を使おうと思ったのですが、考えた末、不要と判断しました。
お知らせメールでは”置換”、”削除”、”挿入”の全てのコストを1に設定し、どれだけの文字数が違うかが関数から返るようにしています。
そうして、全体の文字数と文字数の差異から文字数が違う割合を計算し、これをしきい値としています。

“もしAとBが80%同じ文字列なら、同じ文字列として取り扱う”
1 – “レーベンシュタインのコスト” / “Aの文字数” * 100 ≒ “文字が同じ割合”

“レーベンシュタインのコスト”が”Aの文字数”よりも大きくなることがあるので、厳密には”文字が同じ割合”とは違いますね。
ですが、その場合はマイナスの値となります。したがって、しきい値チェックのための値としてはほとんど問題ありません。

もし、”もしかして”検索を実装しようと思っている人は参考にしてみてください。
また、このコードをつかった”もしかして”検索の実装方法も書いてみたいですね。

ホーム > アーカイブ > 2010-08

スポンサードリンク
書いている人
つぶやき
RSS 気になるニュース
過去の記事

ページの上部に戻る