2015年はこんな年でした

  • アウトプットを増やさねばと思いつつ、はてなを始めてから一番ブログを書かなかった1年でした。Qiitaはちょっと書いてます
  • OSC2015北海道で発表してきました。多分前にOSCで登壇したのは2010年
  • PhpStorm買いました
  • 将棋ウォーズは去年4級だったけど、今年やっと3級になりました
  • 会社を辞めてフリーランスになりました

とりあえず今年のお気に入りの動画置いておきます

www.nicovideo.jp

www.nicovideo.jp

もっと前はこんな年でした

久しぶりにDockerを動かしてみた(Docker Toolbox)

前回から2年近くたってるのか。

coreos-vagrantでDockerしてみてわかったこととかハマったこととか - iakioの日記

ホストはWindows10。

インストール

boot2dockerというのを使うんだっけと思って見てみたらDeprecatedと言われたのでDocker Toolboxを使うことにした。

なんとなくKitematicはインストールしなかった。

デスクトップにできたDocker QuickStart Terminalのアイコンを起動してもエラーが出る。VirtualBoxVMはできているがストレージがマウントされていない。どうやらこの辺のバグを踏んだ模様。VirtualBoxを5.0.4にアップデートして解決。

Hello World

ドキュメントがいっぱいある。Hello Worldからみてみる。

$ docker run ubuntu:14.04 /bin/echo 'Hello world'
Error response from daemon: Cannot start container cde072590d243c292d3aee79e0af405d3d7e2ace3be670ebbd6bec9023686b0c: [8] System error: exec: "C:/Program Files/Git/usr/bin/echo": stat C:/Program Files/Git/usr/bin/echo: no such file or directory

これはスラッシュを1つ増やせばいいとどこかで見た。

$ docker run ubuntu:14.04 //bin/echo 'Hello world'
Hello world

しかしbashの方は動かなかった。

$ docker run -t -i ubuntu:14.04 //bin/bash
cannot enable tty mode on non tty input

まあdocker-machine sshすればVirtualBoxの方にはsshできるので、そこから同じことはできる。

$ docker-machine.exe ssh default
...
docker@default:~$  docker run -t -i ubuntu:14.04 /bin/bash
root@83ecba104bec:/#

network

Working with containersをやってみる。

ドキュメントに、boot2dockerを使ってる場合はlocalhostじゃなくてvirtual hostのIP使ってね。 boot2dockerコマンドで調べられるよと書いてるみたいだけど、今回の場合はdocker-machineコマンド のことだと思う。

$ docker-machine.exe ip default
192.168.99.100

取りあえずこの辺まで。

と思ったけどもうちょっと続く。

volume

VirtualBox側には、C:\Usersがそのまま/c/Usersにマウントされている。名前が一緒なのはかなり便利だ。 例えば僕はWindows上のC\Users\ishida\src\github.com\iakio\phpunit-smartrunnerというディレクトリでPHPの何かしらを開発していて、これをテストしたい場合はこのディレクトリで

$ vendor/bin/phpunit tests

と実行している(だいたいGit-Bashを使っている)。これをDockerのコンテナ内で実行したい場合は、PHPがインストールされたコンテナを用意して(phpというそのままの名前のイメージがDocker Hubにある)、同じディレクトリで、

$ docker run -v /$(pwd):/work -w //work php vendor/bin/phpunit tests

としてやればよい。php:7.0などとしてやれば簡単に複数バージョンでのテストもできそうだ。

f:id:iakio:20150923224741p:plain

シンプルなテンプレートエンジンPlatesをSilexで使ってみる

Platesという、シンプルなテンプレートエンジンを使ってみた。 小さなWebアプリケーションでTwigが必要なほどではない場合にはちょうど良いと思う。

platesphp.com

まずはSilexで最低限のものを実装しよう。

{
    "require": {
        "silex/silex": "~1.3"
    }
}
<?php
// web/index.php
require_once __DIR__ .'/../vendor/autoload.php';

$app = new Silex\Application();

$app->get('/', function () {
    return "Hello";
});
$app->run();

Getting Started

composer.jsonにPlatesを追加してcomposer updateする。

{
    "require": {
        "silex/silex": "~1.3",
        "league/plates": "3.*"
    }
}

では最低限の実装を。

<?php
// web/index.php
require_once __DIR__ .'/../vendor/autoload.php';

$app = new Silex\Application();

$app->get('/', function () {
    $templates = new League\Plates\Engine(__DIR__ . '/../templates');
    return $templates->render('home', ['title' => 'Hello']);
});
$app->run();
<!-- templates/home.php -->
<h1><?=$this->e($title)?></h1>

見ての通り、テンプレートエンジンというよりほぼPHPそのままだ(まあそもそもPHPってそういうものなんだけど)。 PHPにテンプレートエンジンとして使うための便利機能を追加したもの、と考えた方がいいかもしれない。

ルートのアクション内でEngineをnewするのはあんまりなので改善しよう。行儀よくやるならServiceProviderを使うのかもしれないけどここはより単純に、単にコンテナに追加してしまおう。

<?php
// web/index.php
...
$app['view'] = $app->share(function () {
    return new League\Plates\Engine(__DIR__ . '/../templates');
});

$app->get('/', function () use ($app) {
    return $app['view']->render('home', ['title' => 'Hello']);
});

Silexのバージョンは1.3.0で、Pimpleが1.1.1なので$app->share()を使っている。

layoutとinsert

もう少し色々試してみたいんだけど、ネタがないと難しいのでRails Tutorialの静的ページ部分を借りてくることにした。詳細は省くがnpmでbootstrap-sassをインストールし、gruntでビルドできるようにした。

さてレイアウトを導入しよう。

<!-- templates/home.php -->
<?php $this->layout('layouts/application', ['title' => 'Home']) ?>

<div class="center hero-unit">
    <h1>Welcome to the Sample App</h1>

    <h2>
        This is the home page for the
        <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
        sample application.
    </h2>
</div>
<!DOCTYPE html>
<!-- templates/layouts/application.php -->
<html>
<head>
    <title><?=$title?></title>
    <link rel="stylesheet" type="text/css" href="app.css">
    <?=$this->fetch('layouts/shim')?>
</head>
<body>
<?=$this->fetch('layouts/header')?>
<div class="container">
    <?=$this->section('content')?>
    <?=$this->fetch('layouts/footer')?>
</div>
</body>
</html>

$this->layout()でレイアウトを指定し、$this->section()で呼び出している。$this->section()の引数はセクション名で、contentは全ての出力を表す唯一の予約済みのセクション名だ。 明示的にセクションを作る場合は $this->start('name') ... $this->stop() を使う。

$this->layout()でレイアウトを指定すると同時にtitleも渡しているので、index.php側からはtitleを渡す必要は無くなった。

$app->get('/', function () use ($app) {
    return $app['view']->render('home');
});

<?=$this->fetch('layouts/shim')?><?php $this->insert('layouts/shim')?>と書くこともできる。

full_titleヘルパー関数

手順は前後するけど、Rails Tutorialの中にfull_titleというヘルパー関数が出てくるのでこれを実装してみよう。

<?php
// web/index.php
...
$app['view'] = $app->share(function () {
    $engine = new League\Plates\Engine(__DIR__ . '/../templates');
    $engine->registerFunction('full_title', function ($title) {
        $base_title = 'Ruby on Rails Tutorial';
        if (empty($title)) {
            return $base_title;
        }
        return "$base_title | $title";
    });
    return $engine;
});
<!-- templates/layouts/application.php -->
<html>
<head>
    <title><?=$this->full_title($title)?></title>
    <link rel="stylesheet" type="text/css" href="app.css">
    <?=$this->fetch('layouts/shim')?>
</head>
...

以下のようにtitleに空文字以外を渡すと、Ruby on Rails Tutorial | Home と表示され、

<!-- templates/home.php -->
<?php $this->layout('layouts/application', ['title' => 'Home']) ?>

titleに空文字を渡すと、Ruby on Rails Tutorial と表示される。

<!-- templates/home.php -->
<?php $this->layout('layouts/application', ['title' => '']) ?>

ところで、titleはエスケープして出力したほうが良いだろう。つまりfull_titleを通してからescapeすることになる。 これにはいくつかやり方がある。

<?=$this->e($this->full_title($title))?>
<?=$this->batch($title, 'full_title|e')?>
<?=$this->e($title, 'full_title')?>

1つ目は説明するまでもないだろう。2つ目の $this->batch() は、第二引数に複数のヘルパー関数名を | で繋いで記述することができ、 これらが左から順番に実行されるというもの。3つ目も同じような意味で、$this->e() 関数のみ、$this->chan() と同様に 第二引数に複数の関数名を| で繋いで記述することができ、そして最後にエスケープが行われる。

titleのデフォルト値

またちょっと戻るのだけれど、titleを表示したくないときに、

<!-- templates/home.php -->
<?php $this->layout('layouts/application', ['title' => '']) ?>

とやるのはちょっとカッコ悪い。しかしなにも渡さないと Undefined variable になってしまう。 レイアウト側でデフォルトを指定する方法も無くはなさそうだけど不格好なので、 Engine 側でデフォルト値を指定してしまおう。

<?php
// web/index.php
...
$app['view'] = $app->share(function () {
    $engine = new League\Plates\Engine(__DIR__ . '/../templates');
    $engine->addData(['title' => '']);
...

こうすると、全てのテンプレートで title に空文字列がセットされるので、home.php側からはtitleを渡す必要がなくなった。

<!-- templates/home.php -->
<?php $this->layout('layouts/application') ?>

OSC2015北海道で「phpspecで学ぶLondon School TDD」という発表をしてきました

見た目を重視してこんなタイトルにしてみましたが、基本的には私が「実践テスト駆動開発」と「phpspec」をどう解釈したか、というような内容になっています。

45分あってもなかなか伝えるのが難しいテーマだったのですが、とりあえず時間厳守はできてよかったです(一部デモを飛ばしてしまいましたが)。

www.slideshare.net

いくつか補足を。

伝えたかったこと(あるいは上手く伝えられなかったかもしれないこと)

  • 外から中へ、あるいは依存関係の上から下へ実装することで、上位が下位に要求するものが明確になる。
  • setFoo()のような、プライベート変数をセットするだけのメソッドであっても、それは外から観測できる何かに影響を及ぼすはず(影響を及ぼさないのであればそのメソッド自体不要)。その外から観測できるものをモックを使ってテストできるれば、テストのためだけにgetFoo()を追加する必要は無くなる。

QAより

補完について

Specの書き方が特殊なため、Spec内ではIDEの補完がほとんど効きません。調べてみたところ、部分的には@mixinアノテーションを使って補完させることができるようです。

f:id:iakio:20150614155113p:plain

PhpStorm & phpspec | Dimitrios Savvopoulos

しかし、この先のshouldReturn()などは補完することができません。

タイプヒントについて

身内でリハをやったときに、「仮実装を作るときに何故タイプヒントを付けてくれないのか」という質問がありました。

    // このような仮実装を生成するが
    public function toHtmlFromReader($argument1)
    {
        return '<p>Hi, there</p>';
    }

    // なぜこのようにしてくれないのか
    public function toHtmlFromReader(Reader $argument1)
    {
        return '<p>Hi, there</p>';
    }

これについては議論があったようですが結果的にphpspec自体ではRejectされ、extentionで実装されています。

雑感

私は主にLaravel方面をWatchしていますが、ここ最近のPHPフレームワークは、Composer、PSR、PimpleライクなDIコンテナの発明を経て、HTTPからのアプリケーションの分離みたいな方向に向かっているように思います。Laravel方面ではDDD、CommandBus、CQRS、Event Sourcingといったキーワードが注目されているようです。

そしてこれは、Rails likeなアーキテクチャからの決別でもあります(Railsがダメだというのではなく、PHPRailsの真似をするのは無理があるという意味です)。

このような方向性に、phpspecはマッチしているのではないかなという気がしています。

PostgreSQLで「1時間以内に解けなければプログラマ失格となってしまう5つの問題」の5問目を解いてみた

久しぶりにこういうのやってみました。良い問題。

1時間以内に解けなければプログラマ失格となってしまう5つの問題が話題に

create or replace function eval(expr text, out ret integer) as
$$begin execute 'select ' || expr into ret; end;$$ language plpgsql;

with
    op (c) as (
       select unnest(ARRAY[ ' + ', ' - ', '' ])
    ),
    a (c) as (
        select 1 || op1.c
            || 2 || op2.c
            || 3 || op3.c
            || 4 || op4.c
            || 5 || op5.c
            || 6 || op6.c
            || 7 || op7.c
            || 8 || op8.c
            || 9
          from op as op1
             , op as op2
             , op as op3
             , op as op4
             , op as op5
             , op as op6
             , op as op7
             , op as op8
    )
select c
  from a
 where eval(c) = 100 
 order by 1
;

巨瀬亮一さんの「勝ち自体にはそんなにこだわっていなかった」の意味について

巨瀬さんは将棋電王戦トーナメント(電王戦に出場するソフトを決める予選)の優勝者インタビューでも同様の発言をしている。

第2回 将棋電王トーナメント 決勝トーナメント最終日 決勝・準決勝 - 2014/11/03 11:00開始 - ニコニコ生放送

7:17あたりより

電王戦出場にあたっては、棋士の先生には申し訳ないですが、 そこまで勝負自体にはこだわりはなくて、 コンピューターを使っていかにプロ棋士が強くなって、 プロ将棋のレベルが上がっていくことに貢献できるかということに 私の場合はモチベーションがあるというか、

コンピューターと人間両方強くなっていくというのが、 今後の将棋界のありかたとしては一番良いのではないかと思っております。

将棋のプロを目指し、挫折し、コンピューター将棋の面白さに出会い、電王戦を迎えた巨瀬さんの心境はどのようなものだったか、ちょっと本人以外には想像できないものではないかという気がする。

電王戦がエンターテイメントとして盛り上がっていることは良いことだけれど、すこし煽り過ぎなのではないかと思う。棋士が負ければ「引退しろ」といわれたり、ソフトが負ければ「もう二度と将棋界に関わるな」といわれなければいけないほどのものなのだろうか。

一方で、コンピューターから見た将棋と人間から見た将棋の違いから、改めて将棋というのはどういうゲームなのかということに気づかされることもあって、そこが電王戦の面白さだ。

もう少し冷静にコンピューター将棋と人間との関係を楽しめるようになればいいなと思う。

www.asahi.com

nlab.itmedia.co.jp