読者です 読者をやめる 読者になる 読者になる

annictでreact-nativeの練習した

react-native の練習のために annict クライアント作りました。

コードここにあります

GitHub - gong023/annictApp: annict client by react-native

一応デモもあります。動くといいですね

デモ見てもらったらわかるんですが間違ってもストアに乗っけられる代物ではないです。annict のクライアントつくって小銭稼ごうみたいな意図はなく、単純に練習のために作りました。それも一段落したので今後機能追加等を行うこともないと思います。

何を目的にしていたかというと、一つは単純に react-native を動かしてみること、もう一つは redux に始まるフロントエンド周りアーキテクチャを咀嚼するすることです。興味が js 部分に寄っていたので、アプリとしては ios しかつくってません。今回書いたコードは android でも動かせるはずですが、deeplink の設定とか .env の読み込み設定とか結構面倒くさいのでパスしています。

中の作りに関しては、package.json を少し間引いてみるとなんとなく見えると思います

  "dependencies": {
    "axios": "^0.16.1",
    "lodash": "^4.17.4",
    "react": "16.0.0-alpha.6",
    "react-native": "^0.44.0",
    "react-native-config": "^0.4.1",
    "react-native-router-flux": "^3.38.1",
    "react-redux": "^5.0.4",
    "redux": "^3.6.0",
    "redux-logger": "^3.0.1",
    "redux-observable": "^0.14.1",
    "rxjs": "^5.3.0"
  },
  "devDependencies": {
    "eslint": "^3.13.1",
    "eslint-config-airbnb": "^13.0.0",
    "eslint-plugin-import": "^2.2.0",
    "eslint-plugin-jest": "^1.0.2",
    "eslint-plugin-jsx-a11y": "^2.2.3",
    "eslint-plugin-react": "^6.8.0",
 }

redux, redux-observable, react-native-router-flux 辺りがキモになるかと思いますが、js のシーンを真面目に追っている人間ではないので、だいたいこんな感じ?っていうのは探り探りで作りました。

とはいえ最終的に、見た目の雑さに反して裏側はそこそこ真面目に作れたような気がしてます。まあこれくらいやっとけば割と機能追加やブラッシュアップはいけるかなと。あげたコードが自分のような初学者の助けになればいいなと思います。一応 oauth 認証 -> ログイン -> いくつかの API を叩くという一連のフローは書いています。特に oauth 周りは面倒くさいので、そのサンプルとしてはいいかもしれません。

全体的にやってみた感想としては、react-native、割とドキュメントどおりにやれば動くのですごいなと思いました。この手の技術は正しくやってるはずなのになんか動かないっていうケースが多い印象だったんですが、そういうのでハマった記憶が特にありません。まあ、簡単なものしか作ってないからだと思いますが。しかし全体的に web の js よりコンフィグが100倍楽な印象があり、初学者がフロントエンドの js やるなら web より先に react-native やった方が楽なんじゃないかと思うレベルでした。

とりあえず今回の一件はロジックの組み方に焦点を当てられたのでとても体験良かったです。js 色々言われているし色々ありますが、パラダイムシフトとしてこの辺かなあというのを実際に手を動かしながら見れたのは楽しかったです。コード見てみたけどお前ここダメだよとかあったら教えてもらえるとうれしいです。

時間かかったなあと思うのは xcode へのライブラリ追加とか deeplink の設定とか appetize.io にアップロードする際の release ビルドとか、ios 側の設定です。自分のようにサーバーサイドに寄っている人はこの辺不慣れですし避けることもできないので変なストレスがあると思います。とはいえ本来フルネイティブでやるとしたら全部そんな感じになるはずなので、js の知識を使いまわしてアプリを書く部分だけでも楽できるっていうのはすごいことだなと思いました。

phpの名前空間をお手軽に変えるライブラリ書いてた

表題の通り php名前空間IDEみたいに置き換えられるコマンド書いてました。

実をいうと割と前の話で、社内の勉強会とかでは喋ってたんですが、ちゃんとパブリックで書いてなかったので書きます。あれからちょこちょこアップデートもしたし

これなに

主に php名前空間を置き換えるためのコマンドです。IntelliJ とか使ってる人にはおなじみの機能だと思いますが、それを php スクリプトで実現した感じです。

基本的に必要なオプションは以下の3つです。

  1. composer.jsonのパス
  2. 変更前の名前
  3. 変更後の名前

変更前後の名前を入力するのは当然だと思いますが、composer.json のパスを読み込ませるのは解析対象にするファイルを知るためです。

つまり composer.json

    "autoload": {
        "psr-4": {
            "YourName\\": "src"
        },
        "files": ["src/functions.php"]
    },
    "autoload-dev": {
        "psr-4": {
            "YourName\\": "tests"
        }
    },

と書いてあればその内容に従って src,tests,functions.php を解析対象にします。

なお、解析は nikic/PHP-Parser で行います。このライブラリについては qiita に書いたので興味あれば御覧ください。

正規表現とかではなくまともな方法で静的解析するので安定性はそれなりにある、と思います。

また、各ファイルの解析はマルチプロセスで並列実行されます。namae-space を作るにあたって、100%プレーンな php だけで動くように気をつけていたので速度面でちょっと不安がありましたが、並列実行にしたことによりまあまあ大丈夫なパフォーマンスになったような気がします。遅かったらすいません。php-cs-fixer よりはマシ、かも・・・。

ついでに言うとコードの内容を書き換えるだけではなくその名前のクラスを find する機能とかも乗っけています。そのあたりはリポジトリの readme を読んでみて頂ければ幸いです。

なんでつくったの

会社のリポジトリにグローバルな名前空間に作られたクラスが結構あってちょっとアレだったので、それを一息で殺すために作りました。もう2017年だし namespace ナシでクラス作る人はさすがにいないんですが、昔のコードにそういうのが残ってて殲滅したいなと思ってました。

先述の通り IntelliJ とかには名前空間を置き換える refactoring 機能がついていて、日頃から大変お世話になっているのですが、名前がグローバルだと置き換えられないんですよね。そういうわけで自分でスクリプト書きました。

便利オプション補足

一応 readme にも書いたんですが replace コマンドののオプションを一応ここにも載せます。以下は全て optional なので必要になったら参照して下さい。

-D:–dry_run

ドライランです。コードの置き換えは行わず diff だけ出力します。

-M:–max_process

静的解析の並列実行数を指定します。デフォルト10になっててちょっと強めかも。

-A:–additional_paths, -E:exclude_paths

先述の通り解析対象は composer.json を読み込んでよしなに出すんですが、お作法が違うディレクトリがあったりしたらそれらを強制的に加えたり外したりできます。グローバルな名前を置き換えたいとかいうニーズの場合このオプションが必要になるのではないでしょうか。

-R:–replace_dir

composer.json の内容だけでは新しい名前空間のファイルの置き場所が一意に決まらない場合があります。例えば先述した composer.json の内容で、YourName\Klass.php を作りたい場合、Klass.php は src 以下におけば良いのか tests 以下におけばいいのか厳密には判断できません。そうなった場合 namae-space はインタラクティブモードで「src と tests どっち使う?」と聞いてくるのですが、それをスキップしたい場合このオプションを使って直にファイルの置き場所を指定できます。

苦労話

gong023/namae-space はとにかく手軽に使えるものにしたくて、インストールgithub から phar を落とせば終わり、という形にしようかと思ってました。

しかし、phar だと proc_open が動かないんじゃないかという疑惑があり、このため phar による提供は一旦止めてます。

phar だと proc_open が動かないんじゃないかという疑惑

再現コードはこんな感じです。

gist.github.com

php をポチポチ gdb デバッグしてみると、なんかどうも この辺php phar://myphar… みたいな文字列が渡されていてそれを実行しようとするのでそりゃ動くわけないなって感じなんですが、これこういうもんなんでしょうか。

これだれか分かる人がいたら教えていただきたいです。

php-blt2でLTした

PHP BLT #2 - connpass でLTしてきたので資料貼っておきます

speakerdeck.com

普段ほとんどエンジニアの勉強会行かないしLTするのとか多分2年ぶりとかだったと思う。

自分の場合、ついこの前まで卓球ハウスにいたので技術の話する相手や機会には困らなかったけれど、卓球ハウスは解散したので今後もしかしたらこういう機会増えるのかなあとか思った。

内容について特に補足することはないけど、PHP拡張のマクロ情報少なくて面倒だなあという感じです。References に載せてあるリンクからの抜粋だけど、一応ここわかって助かったみたいなの書いときます。

  • PHP_CHECK_LIBRARY で必要な引数について
  1. The name of the library. In our case score will be transformed into -lscore when compiling. Example: cc -o conftest -g -O0 -Wl,-rpath,/usr/local/lib -L/usr/local/lib -lscore conftest.c
  2. The name of the function to try and find within our score library.
  3. The set of actions to take if the function is found. In our case, we are adding to the Makefile code to compile against our library and defining HAVE_SCORE which is used by the during compilation.
  4. The set of actions to take if the function is not found. In our case, we are throwing an error with a human readable error message.
  5. The set of extra library definitions. In our case, we are making sure the compiler knows where to find our shared object.
  • PHP_ADD_LIBRARY_WTH_PATH で必要な引数について
  1. The name of the library.
  2. The path to the library.
  3. The name of a variable to store library information. We will use this with PHP_SUBST.

詳しくはこちらで

rust-carbonっていうライブラリ作った

先月ようやく rust 1.0 が出たということで、いじってみるがてらライブラリをひとつ作った。

rust-carbon という名前で、rust で時間の扱いをやりやすくするものだ。

インターフェイスphpcarbon という有名なライブラリを真似たので、それが名前の由来となっている。以下使い方とか書いていく。

インストール

Cargo.toml にこれ書いて

[dependencies]
carbon = "0.1.*"

こう

extern crate carbon;

使い方

こんな感じ。現在時刻が 2015-01-15 01:30:30.500000000 だとして考えて欲しい。

// インポート
use carbon::*;

// 現在時刻は 2015-01-15 01:30:30.500000000

DateTime::now().start_of().second();
// => carbon::DateTime に 2015-01-15 01:30:30.000000000 が詰まって返る
DateTime::now().start_of().minute();
// => carbon::DateTime に 2015-01-15 01:30:00.000000000 が詰まって返る
DateTime::now().start_of().hour();
// => carbon::DateTime に 2015-01-15 01:00:00.000000000 が詰まって返る
DateTime::now().start_of().day();
// => carbon::DateTime に 2015-01-15 00:00:00.000000000 が詰まって返る
DateTime::now().start_of().month();
// => carbon::DateTime に 2015-01-01 00:00:00.000000000 が詰まって返る

DateTime::now().end_of().second();
// => carbon::DateTime に 2015-01-15 01:30:30.999999999 が詰まって返る
DateTime::now().end_of().minute();
// => carbon::DateTime に 2015-01-15 01:30:59.999999999 が詰まって返る
DateTime::now().end_of().hour();
// => carbon::DateTime に 2015-01-15 01:59:59.999999999 が詰まって返る
DateTime::now().end_of().day();
// => carbon::DateTime に 2015-01-15 23:59:59.999999999 が詰まって返る
DateTime::now().end_of().month();
// => carbon::DateTime に 2015-01-31 23:59:59.999999999 が詰まって返る

当然メソッドチェーンできる

DateTime::now().start_of().day().end_of().hour();
// => carbon::DateTime に 2015-01-15 00:59:59.999999999 が詰まって返る

単体テストなどで now の返り値を指定したい場合があると思う。rust-carbon では以下のように指定できる。

// 例えば現在時刻を 2015-01-01 00:00:00 にしたいとする
let tm = time::Tm {
  tm_sec: 0, tm_min: 0, tm_hour: 0, tm_mday: 1, tm_mon: 0, tm_year: 115, tm_wday: 4, tm_yday: 0, tm_isdst: 0, tm_utcoff: 0, tm_nsec: 0 };
};

let test_now = DateTime::create_from_tm(tm);
DateTime::set_test_now(test_now);
DateTime::now();
// => carbon::DateTime に 2015-01-01 00:00:00 が詰まって返る

また、rust-carbon は rust の準標準ライブラリである rust-time の薄いラッパーである。

そのため、rust-time の構造体及びそこに生えているメソッドに簡単にアクセスできる。to_string() とか細かい時間の調節とかはそっちのメソッドを使ってもらうことになる。

// 現在時刻は 2015-01-15 01:30:30.500000000

DateTime::now().tm.strftime("%Y-%m-%d %H:%M:%S").ok().unwrap().to_string();
// => "2015-01-15 01:30:30" という string が返る

use std::ops::{Add, Sub};

let hour = time::Duration::hours(1);
DateTime::now().tm.add(hour)
// rust-time の time::Tm 構造体に 2015-01-15 02:30:30.500000000 が詰まって返る

他詳細なことについては README を見てほしい。

補足

rust-carbon は習作で、冒頭にも書いたとおり、とりあえず rust 1.0 を触ってみたかったという動機から作られている。実用を目的としたわけではないので、実際に使おうとすると困る点がいくつかある。具体的には、まず UTC 以外の時間が扱えない(日本人だと絶望的)。あと time::Tm.add()/sub() を使った時に time::Tm が返ってしまうので、細かい時間調節をしようとすると小回りが効かない。

この辺りは気が向いた時とか実際に使い始めたとかされたら直していきたい。

また、rust で時間を扱うライブラリとしては、rust-chrono もある。rust-carbon はシンプルなのに対して、rust-chrono は複雑なことができる。 まぁ個人的には rust-chrono はインターフェイスがゴチャゴチャしていて気にくわないし、元々 rust-time にかなり任せられそうなので、あんな大仰で面倒くさそうなラッパーいるか?というのが正直な気持ちだが、もし rust で便利な時間操作系のライブラリを探しているとかいうのであれば、検討すると良いと思う。

rust 1.0 の雑感

rust は地味に nightly の頃にいじってみていたのだが 当時作ったライブラリは何にもいじってなくてもデイリーでビルドがぶっ壊れていてなかなか愉快だった。 公式ドキュメントの説明も難しかったし、まともな rust のコード読もうとすると rust 本体のコードしかないみたいな感じだった。

正直 1.0 も見送って 1.1 になるまで触るのやめようかなと思っていたのだが、今回の 1.0 でも触っていて先に挙げていたようなストレスはほとんどなくなっていた。ビルドもぶっ壊れなくなったし、今回の公式ドキュメント もだいぶ読みやすくなったと思う。

rust はモダンな言語仕様をふんだんに取り入れていて、本来持っているポテンシャルは高いと思う。細かいところはいちいち説明しきれないのでぜひ自分で見てくれという感じだが、結構堅牢でカッコイイ実装ができる。その上 ownership,borrow,lifetime の概念により省メモリに抑えることができる。結果として堅牢さと軽さが両立するのでかなりいい言語なのではという気がする。触っていてなかなか楽しかった。

ただ、go みたいな手軽さはほとんどない。サッと書いて動かして感動を得るというよりは、堅牢に設計した時の俺スゲー感とか、メモリチューニングを楽しんでいく言語だと思う。(go との比較ついでに書くと、並列処理書くのがまだ面倒なのが痛いが)

ちなみに、rust を今現在の仕事の現場で使いたいかというとそれはNOになる。あんまりぶっ壊れなくなったとはいえヘビーに触ったわけではないのでまだ怖いし、そもそも書く人が多いとはいえない言語なので現場で導入というのは考えられない。

言語本来のポテンシャルとしては他人と一緒に開発するのに向いているし、これから伸びていったら面白そう。

ctrl-aでDJした

一応備忘録として書いておこうと思う。割と前の話だけど、去る5/28に、秋葉原mograでctrl-aっていうイベントがあって、そこでDJした。

出るきっかけは本当にたまたまで、プライベートで仲良いkazzoneに飲み屋で出る?って言われたからだった気がする。酔っぱらってて出る出るって返事したら本当に出してもらえた。自分のような人見知りでもこういうことがあるのだから、人の縁というのは恐ろしい。

DJ自体は本当に初心者で、去年会社辞めて有給消化してるときにいじりはじめてそれからなんとなく練習してた程度だった。mograといえばアキバ文化の一端を担う聖地だし、自分じゃなくたってあそこにイベントで立ちたい人はいっぱいいると思う。出演前は嬉しいよりも畏れ多い気持ちで一杯だった。

いざイベントが始まると恐ろしいぐらいに人がたくさん来て盛り上がった。正直何が起きてるのかよくわからなかった。実際平日であれだけ人が集まったのは異常だと複数の人から聞いた。

自分は出番がオープニングだったので人は多くなかったのだけど、kazzoneがDJしてる時にVJとして前に立っていたので、場の盛り上がりを肌で感じることができた。好きな曲が流れた時に起こる歓声というか怒声みたいなものと、限界までボリュームが引き上げられたキック音とが混ざって起きる異常な振動は、日常生活ではなかなか感じることはできない。お客さんはちゃんと訓練されたクズで、色々なところで助けられた。本当にありがたかった。

ちなみにVJは本番の2日前にやってくれって言われて、一夜漬けで準備したとかそういう思い出もある。しかも準備してる時に仕事のほうでデカい障害出してしまって頭おかしくなりそうなぐらい脳汁出てた。キツかった。

DJの方は身内には良かったみたいなことは言ってもらえたけど、まぁ身内だし、初めてにしてはという枕詞が省略されていたので、客観的にみてお世辞にも上手くいってはいないと思う。一応ネットのどこかには当日のプレイを再現して上げたものもあるけど、とてもソーシャルアカウントとかで宣伝する気にはならない。選曲は本当に難しい。イベントの文脈、客層、自分の順番、他のDJの趣向、BPM、キー、その他諸々考えなければならないことがたくさんある。みんな軽々と回してるけどその難しさが身にしみた。

ctrl-aはおかげさまで次回もあるし、また出させてもらえそうなので次はもっとうまくやれるように頑張りたい。

goでテスト並列実行させたら楽な気がする

チームにある程度テストがかさばってくると、テストの実行時間が問題になる。せっかくテストを頑張って作っていても、実行時間が10分とか20分になってくるとどうしてもテストを動かすのが億劫になる。 せっかっく開発がノッてきたのに、テストのフィードバックがすぐ得られないと集中が途切れてしまう。手元なら実行範囲を狭めればいいだけだが、 CI 環境ではそうもいかないし、 そこでこけている原因を探るのに一回のトライエラーが10分間隔とかになると辛い。

まぁ、そういう建前は色々あるけど、テストのフィードバックが早く得られると困る人はいないと思う。

そういうわけでテストを並列実行しようという話が出てきて、php 界隈で並列実行を試みようとすると例えば以下のライブラリに行き当たる。

paratest は一番メジャーっぽいんだけど、php で書かれていて心配。php でマルチプロセスを行うのはかなりの黒魔術が必要なはずで、正直地雷踏みそうで近寄りたくない。 あとで見てみるとそんなことなさそう。昔読んだ php でマルチプロセスしてるコードが並列関係なく単純に出来が悪くて悪い印象持っていただけっぽい。

一方で parallel-phpunit は shell でなんか安心感がある。ただやっぱりそこは shell なのでテストの実行単位を分けていったりするのにもう少し自由度があったほうがいいのではという気がする。また少し離れたところでいうと、RRRSpec とかもある。これは確かにすごいのだが、こういう大艦巨砲が必要なわけではない。とにかく楽に学習コストとか下げてやりたい。

そんな感じの文脈で、ある程度費用対効果が良さそうな線を考えると、go で phpunit の並列実行するスクリプト書くのがいいんじゃないかなあという結論に至ってえいやで書いてみた。

package main

import (
    "fmt"
    "os/exec"
    "path/filepath"
    "runtime"
    "sync"
)

var (
    runner  = "./vendor/bin/phpunit"
    target  = "./tests/*"
    exclude = []string{"tests/fixture"}
    outputs = []byte{}
)

func main() {
    files, err := filepath.Glob(target)
    if err != nil {
        panic(err)
    }

    var wg sync.WaitGroup
    runtime.GOMAXPROCS(runtime.NumCPU())
    for _, file := range files {
        wg.Add(1)
        if contains(file, exclude) {
            wg.Done()
            continue
        }
        go func(file string) {
            out, err := exec.Command(runner, file).Output()
            if err != nil {
                wg.Done()
                fmt.Println("failed to execute: " + runner + " " + file)
                panic(err)
            }
            outputs = append(out)
            wg.Done()
        }(file)
    }
    wg.Wait()

    for _, output := range outputs {
        fmt.Printf("%c", output)
    }
}

func contains(value string, slice []string) bool {
    for _, s := range slice {
        if value == s {
            return true
        }
    }

    return false
}

このスクリプトでは、tests 以下のディレクトリごとに phpunit を並列実行している。今回はテストランナーを phpunit にしているけれど、var 以下を書き換えれば rspec でも karma でも testem でも何でも動かせる。

本当にとりあえずで書いたのでテストの実行単位が雑すぎるというのはあるけど、まぁそれに関しては拡張は難しくないのであまり気にしていない。 それよりも一番弱点になるのは CI 環境で go が必要になることだろうけど、別に致命的ではないと思う。go の書き方がなってない、とかそういう話は素直にすいませんといいたい。

とにかく楽に、費用対効果が高い形でテストを並列実行して高速化したいという話であれば go でスクリプト書くのはよい気がしてる。

PHPUnitを便利にするライブラリ幾つかつくった

年末年始+αの時間で PHPUnit をちょっと便利にするライブラリを幾つか作ったのでそれぞれ書いておく。

assert_chain

https://github.com/gong023/assert_chain

PHPUnit で一つの変数について詳しくアサーションをかけたい場合、以下のように書くことがあると思う。

<?php
$arr = [
    'intKey'    => 1,
    'stringKey' => 'foo',
    'boolKey'   => true,
];

$this->assertNotEmpty($arr);
$this->assertArrayHasKey('intKey', $arr);
$this->assertSame(1, $arr['intKey']);
$this->assertArrayHasKey('stringKey', $arr);
$this->assertSame('foo', $arr['stringKey']);
$this->assertArrayHasKey('boolKey', $arr);
$this->assertTrue($arr['boolKey']);

一体何度 $this->assert を何回書けばいいんだという話になるので、assert_chain を使って以下のように書けるようにした。以下は上記のコードと等価だ。

<?php

$this->assert()
  ->notEmpty($arr)
  ->arrayHasKey('intKey', $arr)
  ->same(1, $arr['intKey'])
  ->arrayHasKey('stringKey', $arr)
  ->same('foo', $arr['stringKey'])
  ->arrayHasKey('boolKey', $arr)
  ->true($arr['boolKey']);

また、PHPUnit の assert はほぼ全て $actual の値(つまりテスト対象の変数)を渡す部分があるが、centralizedAssert を使うことでテスト対象の変数が自動的にアサーションに渡るようになっている。

<?php

$arr = ['key' => 'value'];

$this->centralizedAssert($arr)
  ->notNull()
  ->notEmpty()
  ->notCount(0)
  ->count(1)
  ->arrayNotHasKey('no existing key')
  ->arrayHasKey('key')
  ->notContains('no existing value')
  ->contains('value')
  ->equals(['key' => 'value']);

アサーションPHPUnit_Framework_Assert にプロキシしてるだけなので、普段 PHPUnit を使っている人は違和感なく使えるはず。当然ロジックも変わらない。

詳しいことは README を見て欲しい。

assertEqualsassertSame を使って一度に雑にテストするのもいいが、このように複数アサーションを重ねて段階的にテストしていくとテストが失敗した時に原因がわかりやすい。

Ayaml

https://github.com/gong023/Ayaml

yml による fixture ライブラリ。yml ファイルから php array を生成する。

# user.yaml
valid_user:
  id: 1
  name: Taro
  created: 2014-01

上記ファイルから以下のように array を作る。

<?php
Ayaml::file('user')->schema('valid_user')->dump();
=> ['id' => 1, 'name' => 'Taro', 'created' => '2014-01'];

with メソッドを使って値の上書きもできる。

<?php
Ayaml::file('user')->schema('valid_user')->with(['id' => 2, 'name' => 'John'])->dump();
=> ['id' => 2, 'name' => 'John', 'created' => '2014-01'];

以下のようにシーケンシャルな配列を作ることもできる。

<?php
$validUser = Ayaml::file('user')->schema('valid_user');

Ayaml::seq($validUser)->range('id', 10, 12)->byOne()->dump();
=>
[
  ['id' => 10, 'name' => 'Taro', 'created' => '2014-01'],
  ['id' => 11, 'name' => 'Taro', 'created' => '2014-01'],
  ['id' => 12, 'name' => 'Taro', 'created' => '2014-01'],
];

Ayaml::seq($validUser)->between('created', '2014-01', '2014-03')->byMonth()->dump();
=>
[
  ['id' => 1, 'name' => 'Taro', 'created' => '2014-01'],
  ['id' => 1, 'name' => 'Taro', 'created' => '2014-02'],
  ['id' => 1, 'name' => 'Taro', 'created' => '2014-03'],
];

詳しいことは README を見て欲しい。

Ayaml の特徴は、テストデータそのものを DB データとして作るのではなく、単純に array を生成する点。

php の fixture ライブラリとして有名なものとして、DBUnit, alice, php-factory-girl がある。これらのライブラリは、Ayaml とは違い DB にデータを入れるところまで責任を持ってくれる。

もちろんそれはそれで良いのだが、一方で DB 周りを扱うライブラリは馴染みのあるものを使いたいという気持ちはないだろうか。少なくとも自分にはある。上記のライブラリは doctrine を使うことになるので、テストのためにわざわざその DSL を覚える羽目になる(DBUnit はそうとも限らないが、あれはあれでよく我慢して使えるなと思ってる)。正直それは馬鹿げているし、あーシャーディングされてる場合どうすんだっけとか考えだすとテストデータひとつ作るのに膨大な時間を費やす羽目になる。

Ayaml は DB にデータを入れるという責任を放棄し、手前で好きに array 作って手前のライブラリに流し込めというスタンスにすることで自由度を上げている。php には active_record のような支配的なライブラリがないので、こういうアプローチもありだと考えた。

ただ一方で、Ayaml を実装する際自分に課した制約として「doctrine の DSL を覚えるより短い時間で実装すること」というのがあり(それ以上かかるなら既存ライブラリ使ったほうが早いから)、実際 ver0.1.0 までは 1h 弱ぐらいの時間で実装したので、特にシーケンシャル array を作る所なんかはパワー不足な面が否めない。 Ayaml 以上に仕事してくれる DSL が欲しいとか、プロダクトで doctrine を使っているとかいう事情があれば、alice を検討するのがよいと思う。

PHPUnityo

https://github.com/gong023/phpunityo

PHPUnit の成功時 or 失敗時に Yo してくれる。travis でも使える。

まぁ自分は CI の結果は普通にメールで見てるのでぶっちゃけそんなに必要ないのだが、Yo で送られてくるとめでたい。それだけ。

設定は Yo の API token を環境変数に突っ込んだ上で phpunit.xml.dist に以下の情報を足せばいい。

<listeners>
  <listener class="PHPUnit_Yo" file="vendor/gong023/src/PHPUnit_Yo.php">
    <arguments>
      <string name="sendUser">Your Name</string>
      <string name="onSuccess">false</string>
      <string name="onFailure">true</string>
    </arguments>
  </listener>
</listeners>

詳しくは README を見て欲しい。