Brackets 0.38リリース - マルチカーソルサポート

Adobe Brackets 0.38がリリースされたようです。Sprint 38って呼ぶのやめたんですかね。

f:id:iakio:20140416230652g:plain

画像はAlt+Shift+Downを使ってます。その他Alt+マウスドラッグとか、1つ選択した後にCtrl+Bで同じ文字列を選択したりもできます。

オライリーの本をGoogle Playで400円前後で購入

O'Reillyの一部の書籍はGoogle Playでアプリとしても販売されています。

O'Reilly Media - Google Play の Android アプリ

f:id:iakio:20140413164256p:plain

JavaScript: The Definitive Guildeが399円。安い。

f:id:iakio:20140413165842p:plain

Part IIIの Core JavaScript Referenceもついてきます。

アプリはAldikoというeBookリーダーにバンドルされた形でインストールされます。ただ、このリーダーがスワイプしようとするとテキストを選択してしまったりと使い勝手がイマイチです(単体でインストールしたAldikoはそんなことないので、バンドル版のバージョンの問題かもしれません)。

しかしありがたいことに、メニューからePubをエクスポートすることができます。

f:id:iakio:20140413165425p:plain

f:id:iakio:20140413165426p:plain

ただこのePubに少々問題があるようで、例えばGoogle Play Booksにアップロードするとエラーとなってしまいました。EPUB Validator (beta)で調べてみたところ、フォントが同梱されていないようです。

ePubをunzipして、OEBPS/content.opfを開いて、

    <item id="epub.embedded.font.1" href="LiberationMono-Bold.otf" media-type="font/opentype"/>
    <item id="epub.embedded.font.2" href="LiberationMono-BoldItalic.otf" media-type="font/opentype"/>
    <item id="epub.embedded.font.3" href="LiberationMono-Italic.otf" media-type="font/opentype"/>
    <item id="epub.embedded.font.4" href="LiberationMono.otf" media-type="font/opentype"/>
    <item id="epub.embedded.font.5" href="LiberationSerif.otf" media-type="font/opentype"/>

この辺をざっくり消してしまうか、あるいは他のフォントに置き換えてしまうとよさそうです。置き換える場合は、フォントファイルをOEBPSフォルダに追加し、OEBPS/core.cssファイルを適当に直します。

蛇足ですがこの本、Google Play Booksで買うと4110円です。

JavaScript: The Definitive Guide - Google Play の書籍

Brackets Sprint 36 & 37

Adobe BracketsのSprint 36について書こうと思っている間にSprint 37も出ました。

注目はPreferences。プロジェクトのルートフォルダの.brackets.jsonというファイルで、jslintのオプションやらインデントの幅やらを設定できるようになりました。まーテキストエディタならインデント幅くらい設定できて当たり前だろという感じもしますが。まだ改行コードは設定できないし。

How to Use Brackets · adobe/brackets Wiki

また、マルチカーソルモードの開発が始まったようです。 BracketsチームはTrelloを使っているので、のぞいてみると夢が広がります。

Brackets | Trello

"Minimal .editorconfig support"ほしいなあ。

1つのSubjectに集中せよ - phpspecのコンセプト

1つ前のエントリで「知らないうちに」と書きましたが、実際は2012年からphpspec2という名前で存在していて、途中でphpspecのリポジトリに統合されたようです。

commit f6b8f26c563cfd07af28a7d4bdcf41a1aee33556
Author: Marcello Duarte <marcello.duarte@gmail.com>
Date:   Mon Apr 29 16:49:28 2013 +0100

    Initial commit

commit 5e277377ae015f9fc226864edfbbb7dc47efea27
Author: Marcello Duarte <marcello.duarte@gmail.com>
Date:   Mon Apr 29 16:37:12 2013 +0100

    Wipe phpspec 1.x

phpspec2と呼ばれていたころのブログ記事で、コンセプトがわかりやすく紹介されていました。サンプルコードは今のAPIとはちょっと違います。

everzet's blog • phpspec2: SUS and collaborators

翻訳する英語力は無いので雰囲気だけ。

  • SUSとは、Subject Under Specificationのこと。1つのSpecificationでは1つのSUSの振る舞いについて記述することが超重要。複数に集中しようとするのは自殺行為だ
  • Specificationとは、これから実装しようとしているオブジェクトがどのように振る舞うべきかの例(example)のリスト
  • phpspec2は、1つのSpecificationに1つのSUTというスタイルを強制する。$thisはSUTをProphetという特殊なオブジェクトでラップしたもの。SUTのProxyをしつつ、Matcherで期待する振る舞いについて記述できたり、未実装のメソッドを呼び出すとfatal errorの代わりに色々(メソッドを生成するか聞いてきたりとか)してくれる。
  • なのでSpecification内で$thisの振る舞いの例を書いていけばよい
  • とはいえ、普通はSUSと協調する他のオブジェクト(collaborators)があるよね。そいつらは全部Mockして、実際の振る舞いについて考えるのは後回しにしよう。それがMockists approachだ
  • SpecBDDは言っている — 1つのオブジェクトに集中せよ。その唯一の方法がMockである — と

Mockists approachのことをLondonTDDとかLondon-styleとかいうらしいですよ。使ってみたいですね。TDD Bootcampとかで隣の人に「キミってLondon-style?」って。

Mockの書き方をPHPUnitと比較してみましょう。コードはPHPUnitのドキュメントのものです

<?php
class SubjectTest extends PHPUnit_Framework_TestCase
{
    public function testObserversAreUpdated()
    {
        $observer = $this->getMock('Observer', array('update'));
        $observer->expects($this->once())
                 ->method('update')
                 ->with($this->equalTo('something'));
        $subject = new Subject('My subject');
        $subject->attach($observer);
        $subject->doSomething();
    }
}

phpspecだとこうなります(説明面倒なのでコンストラクタの引数は省略してますが)

<?php
namespace spec;
use PhpSpec\ObjectBehavior;

class SubjectSpec extends ObjectBehavior
{
    function it_updates_observers(\Observer $observer) {
        $observer->update('something')->shouldBeCalledTimes(1);
        $this->attach($observer);
        $this->doSomething();
    }
}

シンプルですね。SubjectSpecの中では$thisはSubjectのインスタンスのラッパーになるという決まりになっています。また引数があればphpspecが自動的にMockを生成してくれます。

但しPASSするにはObserver#update()の空の実装かインターフェースが必要です。逆に言えば、PASSしたときには、Collaboratorsに何が要求されるかはわかっているので、次はCollaboratorsのどれかをSUSに選んでSpecificationを書きながら空の実装を埋めていけばいいわけです。

最後に注意事項

  • 正直phpspecのドキュメントはイマイチです。phpspec/prophecyは見ておいた方がいいです。他にもいじってみて色々わかったことがあるのでまた書くかも. まー上のコードみたいに常に簡単にかけるわけでもないです
  • 1つのやり方を強制するので、0から何かを作るのにはいいかもしれませんが、既存のレガシーコードと戦うのに向いているかどうかは疑問です

知らないうちにphpspecがすごいことになっていた件

昔あったphpspecが進化したものなのかそれとも別のものなのかは知りませんが。

すごい。軽くキモい。17分の動画です。

まず、いきなり「MovieCollectionにMovieをadd()するとcountが1になること」というのスペックを書きます。MovieCollectionクラスもMovieクラスもまだ作ってません。

f:id:iakio:20140306183705p:plain

phpspec runを実行すると当然失敗して、「MovieCollectionクラスが無いけど作ってほしい?」と尋ねてきます。Yなら空のMovieCollectionクラスが作られます。

f:id:iakio:20140306185158p:plain

関係ないけどこういうスクリーンキャストを作ったりライブコーディングする人は、ターミナルの色の設定したほうがいいですね。GitHubにいろいろカラーテーマがあるので。

その後いろいろ説明を省きますが、今度は「MovieCollection::add()メソッドが無いけど作る?」と言ってきたりします。ローカライズすると色々楽しめそうです(謎)。

f:id:iakio:20140306185434p:plain

また、引数で使っているMovieクラスの方は、空の実装だけあればphpspecが勝手にスタブを生成してくれる(newする必要すらない)というのも面白いところです。type hintを本来とは全然違う目的で使うというナウいPHPerに人気の手法ですね。

さらに進んで、MovieCollection#markAllAsWatched()メソッドのスペックを書くところ。

「ここではMovieのテストをしてるんじゃなく、MovieCollectionのテストをしているんだ」と、markAllAsWatched()がMovieの状態を変更することではなく、Movie#watch()メソッドが呼び出されることをモックを使ってテストしています。

<?php
class MovieCollectionSpec extends ObjectBehavior
{
...
    function it_can_mark_all_movies_as_watched(Movie $movie1, Movie $movie2)
    {
        $movie1->watch()->shouldBeCalled();
        $movie2->watch()->shouldBeCalled();

        $this->add([$movie1, $movie2]);

        $this->markAllAsWatched();
    }

簡潔ですね。Movieクラスに空のwatch()メソッドを作り、MovieCollection#markAllAsWatched()を実装することでこのテストは成功します。

スペックを書く対象は1つだけ、コラボレーターは全部Fake Object、というスタイルで書きやすさをつきつめてみたという感じです。

スタブやモックの使い方については、prophecyを見るのがよさそうです。

あと、PhpStormのリファクタリング機能の強力さもこの動画の見所です。

Pimple 2.0がリリースされたのでPimpleについて復習してみる

ぶっちゃけ出るなんて思ってませんでしたが、Pimple 2.0がリリースされたので、1.xの復習と2.0での変更について調べてみます。

Pimple - A simple PHP Dependency Injection Container

Pimple(1.x)の基本

DIコンテナとは何か、という説明をはぶいてPimpleの動作を単に説明すれば、

  1. コンテナ(Pimpleのインスタンス)は連想配列のように見えて、そこに値や無名関数をセットできる
  2. 値を取り出す時、セットされたものが値であれば、単にその値が返却される
  3. 値を取り出す時、セットされたものが無名関数であれば、その無名関数を呼び出した戻り値が返却される。この時、無名関数の第一引数にはコンテナそのものが渡される

というこれだけのものです。

Inversion of Control コンテナと Dependency Injection パターンの例でいえば

<?php
$c = new \Pimple();
$c['filename'] = 'movies1.txt';
$c['movie_finder'] = function($c) {
    return new ColonMovieFinder($c['filename']);
};
$c['movie_lister'] = function($c) {
    return new MovieLister($c['movie_finder']);
};

こんな感じで依存関係を定義します。どこからみてもPHPのコードに見えますがこれは設定ファイルみたいなものだと考えてください。ここまでの段階ではColonMovieFinderのインスタンスもMovieLiserのインスタンスも作成されていません。

使う側は、

<?php
$lister = $c['movie_lister'];

としてやれば、無名関数が2つ呼ばれてガラガラポンとMovieListerのインスタンスを取得できるという寸法です。

shareメソッド(1.x)

上の例では、$c['movie_lister']が参照されるたびに無名関数が呼ばれ、新しいMovieListerのインスタンスが返却されていました。そうではなくSingleton風に毎回同じインスタンスを返却してほしい場合は、

$c['movie_lister'] = $c->share(function($c) {
    return new MovieLister($c['movie_finder']);
});

と書く必要があります。

extendメソッド(1.x)

サービスを定義したところを直接変更できない。でもサービスに何かしら手を加えたい。でもまだインスタンスは作成したくない、みたいなときにextendメソッドを使います。実際どういうことなのか最初はピンとこなかったのですが、GitlistというGitリポジトリビュワーにfabpot(Pimple、Silexの作者)が投げたPullRequestをみてなるほどなと思いました。

https://github.com/klaussilveira/gitlist/commit/65e0bd402b3ca0ac47e361a87d5d86327960a1a9#diff-828e0013b8f3bc1bb22b4f57172b019d

元のコードは、

 // Register Git and Twig service providersclass_path
 $app->register(new Silex\Provider\TwigServiceProvider(), array(
     'twig.path'       => __DIR__.'/views',
     'twig.options'    => array('cache' => __DIR__.'/cache'),
 ));

この辺はSilexの決まり事なのですが、ここで$app['twig']を参照するとTwig_Environmentインスタンスを作成して色々設定してくれることになってます。で、このインスタンスに対して機能を追加したいので 、

// Add the md5() function to Twig scope
$app['twig']->addFilter('md5', new Twig_Filter_Function('md5'));

と書いていたのですが、これだと使うかどうかにかかわらずTwig_Environmentインスタンスが作成されてしまいます。もしTwig_Environmentインスタンスを作成する部分を自分で書いていたのであれば、

$app['twig'] = $app->share(function () {
    $twig = new Twig_Environment();
    $twig->addFilter('md5', new Twig_Filter_Function('md5'));
    return $twig;
});

と書けていたわけです。PRではこれを、

$app['twig'] = $app->share($app->extend('twig', function($twig, $app) {
    // Add the md5() function to Twig scope
    $twig->addFilter('md5', new Twig_Filter_Function('md5'));

    return $twig;
}));

と変更していました。SilexのServiceProvierを使う場合は覚えておきたいパターンですね。

2.0での変更点

shareがデフォルトに

2.0では、以前のshare()の動作がデフォルトになりました。すなわちセットされたものが無名関数であれば、常にキャッシュされた値を返します。またshareというメソッドは無くなりました。

別のインスタンスを返してほしい場合は、factoryメソッドを使います。

$c['movie_lister'] = $c->factory(function($c) {
    return new MovieLister($c['movie_finder']);
});

extendの変更

1.xでは、extendメソッドの戻り値をコンテナにアサインしなおしていましたが、その必要がなくなりました

// 1.x
$c['movie_finder'] = $c->extend('movie_finder', function($movie_finder, $c) {
    $movie_finder->setFilename($c['filename']);
    return $movie_finder;
});

// 2.0
$c->extend('movie_finder', function($movie_finder, $c) {
    $movie_finder->setFilename($c['filename']);
    return $movie_finder;
});

「電車の中で泣く赤ちゃんについての当たり前のお話」を読んで思ったこと

電車の中で泣く赤ちゃんについての当たり前のお話: 不倒城

こういうことだな。

f:id:iakio:20140107230038p:plain

(納得)

図解力がある人が書けばきっとわかりやすいはず。