2010-12-21

Inflectorのやっていることがイマイチわからなかったのでまとめてみました

どだい日本人には複数形なんて...って話の前に、その他のメソッドの動作も理解できていなかったのでコードを追ってみました。以下メモ。読んだのはCakePHP 1.3.6のInfratorです。

pluralize
singularize
1. 最後の単語が例外単語帳にマッチしたらそれを返す
2. 単語全体が変更なしグループにマッチしたら何もせずに返す
3. ルールを一つずつ当てはめていき、マッチしたら実行して返す

まあ、この二つは想像通りというか、パターンは複雑だけどやっていることは単純明快。

humanize
1. 全ての"_"をスペースに
2. 各単語の最初の文字を大文字、それ以外を小文字に変換

camelize
1. humanize
2. スペースを取り除く

underscore
1. 先頭文字を除き、文字列中の大文字の前に"_"を挿入する
2. 全部小文字に

これがくせ者。大文字が続く場合に、各文字の前に"_"が入ります。
例)PostgreSQL => postgre_s_q_l

またこの正規表現が、戻り読み言明を使っていて最初読めなかったです。
/(?<=\w)([A-Z])/ まだまだ勉強不足ですな。

tableize
1. underscore
2. pluralize

classify
1. singularize
2. camelize

variable
1. underscore
2. camelize
3. 先頭の文字を小文字に

slug
1. アルファベットレターと数字以外の文字を" "に変換
2. 連続する" "を"_"に変換
3. 先頭と最後に"_"が一つ以上あった場合は取り除く

これも正規表現を読むのに苦労しました
/[^\s\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu

Unicode 文字プロパティ参照

おそらく結局
/[^\s\p{L}\p{Nd}]/mu
と一緒なのではと思うんですが、どうなんでしょう。

2010-12-16

PHPで簡単にコンソールにカラライズした出力をする方法



Lithiumの表示が綺麗だったので、その部分だけ簡単に使えるように抜き出してみました。PHPの変態的ともいえる配列の動作(辞書的+数字のミックス)を取り入れたんで、比較的使いやすくできました。以下のような感じで、緑色の"world"が表示されます。

console_out(array('Hello', 'green' => '"world"', '!'));
echo "\n";

array() が余計かなぁ。ここがもっと簡単にかけるとうれしいんだけど。こんな風にかけたらうれしいな。

console_out(['Hello', 'green'=>'"world"', '!']);
echo "\n";

2010-10-16

Lithium FAQの一部を翻訳してみたよ

LithiumのFAQが素晴らしかったので翻訳してみました。口調が適当だなぁ。ですますがいりまじってます。翻訳は自分の語り口とはまた変わってくるので面倒なり。


さて、内容は静的なクラスやメソッドとテストのしやすさを例に、プログラミングにおける重要な概念を説明しています。Lithiumのかかげる「consistency」を垣間見れる内容。


分らスタティックをあちこちで使ってるね。テストするのがすごく大変なんじゃないの?

厳密に言って、スタティックなものは実に簡単にテストできるよ。 フレームワークだけでなく、その上のアプリケーションも含めて、全体をテスト可能なコードにしておけるように、Lithiumは関数プログラミングからいくつかのコンセプトを借りてきているんだ。 これらのコンセプトを理解するために、まずいくつかの用語を定義しておこう:

状態 :ソフトウェアを書くうえで必要不可欠なもの。ウィキペディアは状態を "システムの様々な条件を計測した値のスナップショット"と定義しているPHPアプリケーションの開発においては、文脈、もしくはスコープにによって異なるが、Webサーバーがindex.phpをロードしたときには、状態はスクリプトのリクエスト(GETやPOSTデータなど含む)、$_SERVERや$_ENVによって得られるシステムの情報、そのほかのスーパーグローバル変数によって定義されることになる。明白でないが、状態は現在の日付や時間なども含む。メソッド内で状態と言えば、渡されたパラメータ、メソッドがバインドされているオブジェクト(i.e. $this)に納められたもの、 そしてグローバルにアクセス可能なそのほかすべてのものが含まれる。

副作用 :この概念は状態と密接に関連する 副作用 とは、メソッドやそのほかのルーチン実行中に自分のスコープの外で引き起こす変化のことを指す。副作用には、グローバル変数の値、オブジェクトのプロパティ(i.e. $this)などが変更されることや、データベース、ファイ縷々システムに対する変更、参照渡しによるパラメータの値の変更、さらにはechoによる出力なども含む。

可変性 :可変性とは、ものが変化できる性質を持つことを指す。PHPにおける可変性の典型的な例は、変数やオブジェクトのプロパティにみられる。対照的に、不変性とは変更できないことを指す。たとえば、すなわちグローバルまたはクラスレベルの定数がそうだ。したがって、 可変性な状態 とは、変更可能なアプリケーションの状態(上記参照)の1要素と言うことができる。これらの変更が、いわゆる副作用を引き起こすもの、である。これは、明らかに見えるかもしれないが、後でもっと重要になってくる。

参照透明性 :渡されたパラメータのみによって返値の値が決まり、また自分のスコープの外の状態に対して何の副作用も発生しないという、メソッドや関数のもつ特質のこと。参照透過性を持つメソッドは、外部の状態を準備する必要がないので、テストするのが非常に簡単である。同じ値を渡せば、必ず同じ出力が得られるからである。また参照透過性を持つ関数は、キャッシュするのも簡単だ。同じパラメータであれば何度呼び出しても同じ結果が返るからである。このプロセスは、 メモ化 として知られている。


さて話を元に戻すと、よく言われている静的なもののテストが抱える問題点とは、変更可能なグローバルな状態と関わらなくてはいけない、ということですね。例として、いつ変更されるかわからない外部の(通常はグローバルな)情報に依存しているスタティックメソッドなんかをテストする場合のこと。シングルトンは、このアンチパターンの代表例だね。シングルトンの背後にある考え方は、ある時点で使用可能なインスタンスが一つ(たった一つ)だけあるということだ。

この考え方の本質的な問題は、シングルトンの属性がどうなっているのか知るすべがないこと。だってアプリケーションのどこからでも、それらを変更することができるわけだから。これは、ほぼすべてのソフトウェアがもつロジック的なバグの原因となっていることを証明しているように思う。唯一の解決策は、シングルトンの設計を、一度生成されたら属性を変更することができないように(つまり不変に)することだろう。 もしくは、シングルトンが必要ないような構造に設計しなおすか。

幸いなことに、Lithiumのすべての静的クラスは参照透明性なメソッドか、利用パターンとして不変を要求させるようなメソッドによって構成されている。 いくつか例を見てみよう。参照透明性のメソッドとしては、lithium\util\String::insert()はテンプレート文字列に値を挿入した文字列を返すし、lithium\util\Inflector::camelize()は単語や文章の別バージョンを生成します。これらの関数は外部に副作用を発生させないし引数だけによって決まる値を生成します。

不変な静的クラスの例としては、Adaptableを継承するクラス、例えばCacheやConnectionなどがあります。これらのクラスは、システムレベルのリソースへのアクセスを形成し提供してます。例えば、データベースへの接続、ユーザーのセッション、キャッシュの構成などです。これらのクラスは、どうやってアクセスするか、操作するかという情報を config()メソッドで得られる情報によって設定します。これは、アプリケーションのブートプロセスの間に、一度だけしか起こりません。そのクラスに対する以後のアクセスは(たとえばadapter()メソッドを通して)、いつも同じ属性を持った同じアダプターインスタンスが返されます。これにより、我々の水面下でアプリケーションの状態が変わってしまうことによって起こる問題を回避できます。

別の問題としてよく言われるのは、静的なもののテストはmockを用意するのが難しい、もっといえば依存性を取り替えるのが難しいことでしょう。以下の例を考えてみます。
class A {
   public static function foo() {
       // return some calculated value
   }
}

class B {
   public static function bar() {
       $result = A::foo();
       // perform some calculation on $result
       return $result;
   }
}




この例では、
B::bar()呼び出しはA::foo()を呼び出しています。Aへの呼び出しをしないでBを単独でテストすることは不可能です。PHP 5.2までは、解決策はありませんでした。しかし、PHP 5.3で"動的な"静的メソッド呼び出しを使うことで、次のようなことが可能になります:


class B {
   public static function bar($dependency) {
       $result = $dependency::foo();
       // perform some calculation on $result
       return $result;
   }
}




これで、
Aをほかのクラスに交換することができるようになり、Bをテストするのがとても簡単になりました。$dependencyはテストの時には別のモッククラスの名前に置き換えることができるからです。これはまた、Bの設計をもっと柔軟にすることができるという好ましい副作用ももたらします。

Lithiumはこの依存性の問題に対応するため、プロテクテッドな$_classes属性を一貫して使っています。次のコードはlithium\action\Dispatcherの一部です。


class Dispatcher extends \lithium\core\StaticObject {

   // ...
   protected static $_classes = array(
       'router' => 'lithium\net\http\Router'
   );
   // ...
}

Dispatcherの依存関係は動的に変更することができるように設計されています。別のルーティングクラスに変更することも可能です。これにはconfig()メソッドを使います。内部的には、Routerの呼び出しは以下のようになっています。


$router = static::$_classes['router'];
$result = $router::process($request);

すべての依存関係がこのように構成されているわけではありませんが、フレームワーク全体で使われている大事な規約です。例外として、変更できない依存関係はユーティリティメソッドへの依存であり、これらは参照透明性が保証されている場合がほとんどです。なので、ハードコーディングされた依存関係があるからと言って、テストが難しくなったり複雑になることはありません。



2010-09-01

プロパティにブロックを設定したい場合にいつも忘れるので記録

@property (nonatomic, copy) void (^callEventHandler)(CTCall*);

括弧の中に ^ と宣言したい名前を書くと。はい、覚えましたね。

copy じゃなくて retain でもいいんだろうか?

2010-08-10

ブロックを使うとiPhone OS 3での動作は不可能ということを再度確認

個人的にはiOS 4以前のコードは書かないことに決めていますが、そうはいってもお仕事です。未だにiPhone OS 3向けのビルドも作ってます。でも一度ブロックの使いやすさを味わってしまうと、もうなんか使いたくって仕方のない状態。なんならiOS 4専用の部分だけでも条件分岐を入れてブロック使わせてもらっていいかな?という検証。

結論:ブロックを使ったコードがバイナリに含まれているとdyldのリンクエラーで起動すらしません。


Tue Aug 10 12:48:26 unknown UIKitApplication:xxx[0xd506][2007] : dyld: Symbol not found: __NSConcreteStackBlock
Tue Aug 10 12:48:26 unknown UIKitApplication:xxx[0xd506][2007] :   Referenced from: /var/mobile/Applications/5DBA5BE0-0517-4ABB-AF15-C4006334BF74/Xxx.app/xxx
Tue Aug 10 12:48:26 unknown UIKitApplication:xxx[0xd506][2007] :   Expected in: /usr/lib/libSystem.B.dylib

まぁ、自分のアプリケーションだったらコードもコントロールできるけど、使っているライブラリなんかがブロック使い出したら、iPhone OS 3向けのビルドはもう作れないと言うことですな。

2010-07-28

駅ベル - 1.1.0 バージョンアップのポイントをまとめました


  • 駅までの距離に従い、精度を上げるようにした
    • アラームが正しく動作する率がアップ
    • 目的地まで遠い場合にはバッテリーの持ちアップ
  • iPodで音楽再生中にも、ちゃんとアラームが鳴るように
  • バックグラウンドでも、正しく音が鳴るように
  • バックグラウンドでも、バイブレーションが動作するように
  • 設定画面を追加
    • アラームの音を変更可能に(4種類内蔵)
    • バイブレーションを選択可能に(iPhoneのみ)
    • アラーム開始距離を変えられるように(右図)
  • iPhone 4 Retina Display対応(持ってませんが)

実は今回の一番の見所は、内部のロケーションマネージャの使い方を1から見直して、完全に新しい実装で書き直しました。本当に最小限の必要としている部分でしかロケーションマネージャーを使わないように細かいチューニングを行ったので、バッテリーの持ちは前のバージョンよりもかなりよくなっています。使っているときはステータスバーのロケーションインジケータが出るので、画面ごとに出たり消えたるすることで確認できると思います。

ロケーションマネージャについては、iOS4のマルチタスキングの売りの一つだけに、バッテリーの持ちとの新たな戦いの部分、別エントリーで書きます。

駅ベルのバージョンアップでリジェクト食らいました。その顛末。

今回の駅ベルのバージョンアップは、バックグラウンドで音が鳴らないという部分の対応。UILocalNotificationに任せておけばいいかなと思ってたんですが、ちゃんと音が鳴って欲しいという要望が強く、対応することに。

長いWaiting for reviewの期間を過ごして、なんといきなりリジェクト。その理由がこれ。
We've completed the review of your application; however, because no audible content is played when the application is in the background, we cannot post this version to the App Store. We have included additional details below to help explain the issue. We hope you’ll consider revising and resubmitting your application.
iOS4向けのマルチタスキング対応アプリケーションの一つの機能、バックグラウンドでの音声再生機能を使いたいためにフラグを付けたんだけど、それが裏目になった模様。おまえのアプリケーションは音を出して内じゃないか、と。ふーむ。

そりゃぁ、カリフォルニアではまだ鳴らないっつーの(笑)。どうもアプリケーションの動作を正しく理解してもらえてない模様。普段なら慌てるところですが、どうせ自分のアプリケーション、落ち着いて対応してみました。一度Rejectedになっちゃっているので、バイナリを再度アップロードしないでも再審査してくれるか不明だったので、ちゃんと行くかどうかのテストケースになります。

いろいろ騒ぐ前に、まずはちゃんとレビューチームとコミュニケーションを開始することが一番です、と言うアドバイスももらったので、まずはメール。

Dear review team,
My application uses audio to notify user as alarm sound. My application has to play sound in background mode, and I have no idea without adding "audio" flag. 
The Apple engineer I met at the WWDC Lab said, only way to play sound is to add "audio" flag in UIBackgroundModes. If still you reject my app, please tell another way to play alarm sound in background. I think that's not fair.
- My app plays audio when the device close to the station.
- Playing audio in background needs "audio" flag in UIBackgroundModes.
Other audio application can be paused. My application is also paused until it reaches to destination, isn't it?
Please re-think about this.
Yosuke Suzuki
で、その晩はMOSA entranceがあったので、その場でリジェクトくらったーとネタにしてたりしていたんですが、裏ではことが進んでおり、今朝になってメールが届いてました。

Thank you for you email and clarification.  We are proceeding with the review and will update you with further status as soon as we are able.
Thank you for your continued patience.
短い。あまりに短いが、そしてアメリカ人、決してわびの言葉はないが、ちゃんとスムーズに対応してくれました。
無事にリリース。

教訓。リジェクトされても慌てず騒がず、ちゃんとレビューチームとお話しはしましょう。返事もくれます。他の「Contact us」系のメールと違ってスムーズです。