- アウトプットを増やさねばと思いつつ、はてなを始めてから一番ブログを書かなかった1年でした。Qiitaはちょっと書いてます
- OSC2015北海道で発表してきました。多分前にOSCで登壇したのは2010年
- PhpStorm買いました
- 将棋ウォーズは去年4級だったけど、今年やっと3級になりました
- 会社を辞めてフリーランスになりました
とりあえず今年のお気に入りの動画置いておきます
とりあえず今年のお気に入りの動画置いておきます
前回から2年近くたってるのか。
coreos-vagrantでDockerしてみてわかったこととかハマったこととか - iakioの日記
ホストはWindows10。
boot2dockerというのを使うんだっけと思って見てみたらDeprecatedと言われたのでDocker Toolboxを使うことにした。
なんとなくKitematicはインストールしなかった。
デスクトップにできたDocker QuickStart Terminalのアイコンを起動してもエラーが出る。VirtualBoxのVMはできているがストレージがマウントされていない。どうやらこの辺のバグを踏んだ模様。VirtualBoxを5.0.4にアップデートして解決。
ドキュメントがいっぱいある。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:/#
Working with containersをやってみる。
ドキュメントに、boot2dockerを使ってる場合はlocalhostじゃなくてvirtual hostのIP使ってね。 boot2dockerコマンドで調べられるよと書いてるみたいだけど、今回の場合はdocker-machineコマンド のことだと思う。
$ docker-machine.exe ip default 192.168.99.100
取りあえずこの辺まで。
と思ったけどもうちょっと続く。
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などとしてやれば簡単に複数バージョンでのテストもできそうだ。
Platesという、シンプルなテンプレートエンジンを使ってみた。 小さなWebアプリケーションでTwigが必要なほどではない場合にはちょうど良いと思う。
まずは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();
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()
を使っている。
もう少し色々試してみたいんだけど、ネタがないと難しいので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')?>
と書くこともできる。
手順は前後するけど、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を表示したくないときに、
<!-- 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') ?>
見た目を重視してこんなタイトルにしてみましたが、基本的には私が「実践テスト駆動開発」と「phpspec」をどう解釈したか、というような内容になっています。
45分あってもなかなか伝えるのが難しいテーマだったのですが、とりあえず時間厳守はできてよかったです(一部デモを飛ばしてしまいましたが)。
www.slideshare.net
いくつか補足を。
Specの書き方が特殊なため、Spec内ではIDEの補完がほとんど効きません。調べてみたところ、部分的には@mixin
アノテーションを使って補完させることができるようです。
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がダメだというのではなく、PHPでRailsの真似をするのは無理があるという意味です)。
このような方向性に、phpspecはマッチしているのではないかなという気がしています。
久しぶりにこういうのやってみました。良い問題。
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あたりより
電王戦出場にあたっては、棋士の先生には申し訳ないですが、 そこまで勝負自体にはこだわりはなくて、 コンピューターを使っていかにプロ棋士が強くなって、 プロ将棋のレベルが上がっていくことに貢献できるかということに 私の場合はモチベーションがあるというか、
コンピューターと人間両方強くなっていくというのが、 今後の将棋界のありかたとしては一番良いのではないかと思っております。
将棋のプロを目指し、挫折し、コンピューター将棋の面白さに出会い、電王戦を迎えた巨瀬さんの心境はどのようなものだったか、ちょっと本人以外には想像できないものではないかという気がする。
電王戦がエンターテイメントとして盛り上がっていることは良いことだけれど、すこし煽り過ぎなのではないかと思う。棋士が負ければ「引退しろ」といわれたり、ソフトが負ければ「もう二度と将棋界に関わるな」といわれなければいけないほどのものなのだろうか。
一方で、コンピューターから見た将棋と人間から見た将棋の違いから、改めて将棋というのはどういうゲームなのかということに気づかされることもあって、そこが電王戦の面白さだ。
もう少し冷静にコンピューター将棋と人間との関係を楽しめるようになればいいなと思う。