cloned.log

2015-12-03 (Thu) Symfonyから手早くYAMLのFixtureを読み込めるAliceFixturesBundle このエントリーを含むブックマーク このエントリーのブックマークコメント

この記事は Symfony Advent Calendar 2015 の4日目の記事です。

開発環境向けのテストデータ(Fixture)作成は hautelook/AliceBundle を使えば便利だけど、テストコードからFixtureを読み込もうとすると一手間かかるので、何かないものかと探したところ h4cc/AliceFixturesBundle に出会った話。

データベースを利用したテストコードを書く場合、テストデータの準備が必要になることが多い。 「テストコードを書く労力を減らす」「テストコードの見通しを良くする」という観点から、テストコードのためのデータ準備は極力簡単にしたいところ。

Symfony + Doctrineを利用している場合に真っ先に選択肢に上がるものとして DoctrineFixturesBundle があるけれど、見ての通りFixture自体がPHPコードであり、テストデータの準備に苦労が絶えない。

そこで登場するのが nelmio/alice を利用できる hautelook/AliceBundle で、これについては去年の Symfony Advent Calendar 2014 の記事である [Symfony] AliceBundleで自動テストのfixtureをyml化しよう が詳しい。すごくわかりやすいのでご一読を。

どういうYAMLになるかというとこういう感じ。

AppBundle\Entity\User:
    user{1..10}:
        username: <username()>
        fullname: <firstName()> <lastName()>
        birthDate: <date()>
        email: <email()>
        favoriteNumber: 50%? <numberBetween(1, 200)>

これで10件のuserが作成され、それぞれにFakerを利用したランダムな名前や誕生日、メールアドレスなどが設定される。

PHPコードでテストデータを用意する苦労からは解放されたものの、まだ面倒な点があり、先に紹介した [Symfony] AliceBundleで自動テストのfixtureをyml化しよう から引用させていただくと、

<?php
$this->loadFixtures(
    array(
        new UserFixtureLoader(),
    )
);

このように一つのFixtureごとにPHPのクラス(ここではUserFixtureLoaderのこと)を作らなければならない。これは結構面倒だ。

それで色々と探していたところ h4cc/AliceFixturesBundle を発見した。DoctrineFixturesBundle に依存しないため、より簡潔なFixtureの読み込みを実現している。

具体的な実装はデモのテストコード、AliceDemo/src/h4cc/AliceDemoBundle/Tests/Controller/UserControllerTest.php を見るとわかりやすい。

抜粋すると、

<?php
public static function setUpBeforeClass()
{
    $client = static::createClient();
    $manager = $client->getContainer()->get('h4cc_alice_fixtures.manager');
    static::$fixtures = $manager->loadFiles(array(__DIR__ . '/DataFixtures/Alice/alice.yml'));
    $manager->persist(static::$fixtures);
}

こういう感じで直接AliceのYAMLを指定するだけとなっており、Loaderクラスを作成する必要がなく大変便利。

ということで、 h4cc/AliceFixturesBundle の紹介でした。なんと Symfony Advent Calendar 2015 の次の日、12/5はまだ空いているので、どなたかよろしくお願いします!

2015-01-17 (Sat) 「フラットデザインで考える 新しいUIデザインのセオリー」を読んだ このエントリーを含むブックマーク このエントリーのブックマークコメント

フラットデザインで考える 新しいUIデザインのセオリー

フラットデザインで考える 新しいUIデザインのセオリー

Modern UI 、Material Design、iOSなど、フラットデザインを冠するUIの違いを比較しながら、みんながざっくり呼んでいる「フラットデザイン」の正体を概観しつつ、それを踏まえてこれからのUIデザインを考えるための基本的な考察を提供するような感じになっている。

概観している部分の一例としては

つまり人が触るパーツを丸く、それ以外の要素をすべて 角のある矩形で構成するのです。これはGoogleが非常にうまく実現している手法です。

フラットデザインで考える 新しいUIデザインのセオリー P84

例えばiOS 7以降のiOSでは、基本のキーカラーを水色と定めて、水色になっている部分はすべて押せるパーツというルールを作り、その他の色を基本的に排除しました。

フラットデザインで考える 新しいUIデザインのセオリー P109

こういう感じで、それぞれどのようなアプローチでフラットデザインを実現しているのかを具に解説している。世の中的にはどうしてもiOSとAndroidにフォーカスしがちなところを、Windowsやフラットデザイン的なウェブサイトなど、幅広く扱っているところが良い。

フラットデザインの特徴を踏まえつつ、これからのUIデザインを考えるための考察として、配色、余白、フォント、各種UIパーツ(ボタンやチェックボックスなど)の表現手法など、フラットデザインに触れつつ解説されている。

最近、我々がよく見るソフトウェア上のUIデザインを改めて解説されてみる、というテーマとして良書だと思う。案外気付けていなかった部分や、普段自分が使っていないOSのデザイン手法など、なるほどなーと思うところが多かった。

2014-10-29 (Wed) iPhone 6を買った このエントリーを含むブックマーク このエントリーのブックマークコメント

iPhone 6
iPhone 6 posted by (C)cloned

手元のiPhone 4sが下取りされる運命にあると知ったので、購入日からずっと貼っていたフィルムを剥がして新品同様の美しいiPhone 4sを使っていたら、iPhone 6の在庫があっさり見つかった。

iPhone 6は吹き付け仕上げな感じで、今までの金属感がなく安っぽい。Touch IDは便利だし、画面が広いのも左上が届かない以外は見やすいし良いのだけれど、良いものを手にした感は薄れたかなぁ。

2014-06-09 (Mon) UnityのWWW、WWWFormのハマりどころ このエントリーを含むブックマーク このエントリーのブックマークコメント

UnityにバンドルされているWWWクラスを使うと簡単にHTTP通信を行うことができて便利なのだけど、色々やろうとするといくつか判り難い挙動があったので、その辺りをメモ。

Unityのバージョンは4.5。

WWWのコンストラクタには下記の4つ(最後のものは非推奨)がある。ドキュメントはUnity - Scripting API: WWW.WWW

  • WWW(string url);
  • WWW(string url, WWWForm form);
  • WWW(string url, byte[] postData);
  • WWW(string url, byte[] postData, Dictionary<string, string> headers);
  • WWW(string url, byte[] postData, Hashtable headers); // deprecated

ここでまず気をつける必要があるのが、WWWFormクラスをそのまま渡す場合は、第三引数にHTTPヘッダを渡すことができない点。下記、ここから引き起こされるハマりどころ。

ファイル添付したらヘッダを追加できない?

WWWForm.AddBinaryData() を使うとファイルアップロードが可能だけど、HTTPヘッダも同時に変更したい場合に困ったことになる。

Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("Cookie", "a=b");

WWForm form = new WWWForm();
form.AddBinaryData("foo", binary, "foo.png", "image/png");

WWW www = new WWW("http://example.com/", form.data, headers);

このようにすると正しくファイルアプロードができない。ここでHTTPヘッダを変更せずに WWW(string url, WWWForm form); コンストラクタを利用すると正しく動作するようになる。

WWForm form = new WWWForm();
form.AddBinaryData("foo", binary, "foo.png", "image/png");

WWW www = new WWW("http://example.com/", form);

ということは、コンストラクタの第二引数がformそのものではなくform.dataなのが駄目で、WWWFormインスタンスだから動作するのか、と結論付けてしまうとヘッダの変更ができなくなってしまう。

実際にはそうではなくて下記の挙動が考慮できていないことが原因。

  • WWW(string url, WWWForm form); コンストラクタを利用するとヘッダは内部でWWWForm.headersの値が利用される。
  • WWWForm.AddBinaryData() を呼び出すとWWWForm.headersにContent-Typeが自動的に追加される。
    • 実際の値は multipart/form-data; boundary="xxxx"' という感じ

つまり、WWWForm.AddBinaryData() 後に追加されているContent-Typeヘッダの存在を無視して自前のヘッダのみを追加したことが原因なので、下記のようにWWWForm.headersを値を最後に設定するようにすれば動作するようになる。

Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("Cookie", "a=b");

WWForm form = new WWWForm();
form.AddBinaryData("foo", binary, "foo.png", "image/png");

foreach (DictionaryEntry entry in form.headers)
{
    headers[System.Convert.ToString(entry.Key)] = System.Convert.ToString(entry.Value);
}

WWW www = new WWW("http://example.com/", form.data, headers);

GETだとHTTPヘッダを追加できない?

WWWクラスはコンストラクタ呼び出し時にWWWForm、もしくはpostDataが存在するとPOSTと解釈するようになっている。POSTの場合は単に第三引数にヘッダのDictionaryを渡せば良いが、GETの場合は適切なコンストラクタがない、ように見えるけれど、nullを渡せばGETでもヘッダを追加できる。

WWW www = new WWW("http://example.com/", null, headers);

POSTでpostDataがない場合はHTTPヘッダを追加できない?

応用編。POSTにも関わらず、POSTするデータはない。けれどもHTTPヘッダを追加したい。みたいな場合はどうしようもないので

form.AddField("dummy", "dummy");

とでもするしかないと思われる。postDataが空の場合は下記エラーになる。

Error when creating request. POST request with a zero-sized post buffer is not supported.

GET、POSTだけではなくPUT、DELETEも使いたい

使えない。上述のように、GETとPOSTが自動判定されるような機構で、PUT、DELETEはサポートされていない。

そういうわけでWWW用の便利なクラスを書いた

こういったハマりポイントを解決しつつ、もう少し楽にWWWとWWWFormを使いたいと思って、一つクラスを書いたのでGistに貼っておいた。

特徴としては

  • コルーチンを使わないでコールバック(Delegate)で終了後の処理を書ける(内部ではコルーチンを使っている)
  • タイムアウトをサポート(地味に必要ですよね)
  • 上述の複雑なコンストラクタのハマりどころをできるだけ解決

タイムアウトに関してはDEBUG.LOG (WWWクラスのタイムアウト処理)を参考にさせてもらいました。

使い方は下記な感じ。

using WWWKit;

public class WWWClientExample : MonoBehaviour
{
    void Start()
    {
        // 単純なGETリクエスト。終了処理はOnDoneプロパティにDelegateを代入。
        // DelegateにはおなじみのWWWクラスが渡されるのでレスポンスに関する
        // 処理は今まで通りWWWクラスのノウハウが利用可能。
        WWWClient client = new WWWClient(this, "http://example.com/");
        client.OnDone = (WWW www) => {
            Debug.Log(www.text);
        };
        client.Request();

        // POSTリクエストの場合は、WWWクラスの思想を継承して、下記のように
        // postDataを設定したらPOSTになる、という仕様。
        WWWClient http = new WWWClient(this, "http://example.com/");
        client.AddData("foo", "bar");
        client.OnDone = (WWW www) => {
            Debug.Log(www.text);
        };
        client.Request();

        // ファイル添付したい場合。下記はstringをbyte[]にしてるけど画像など
        // 何でも可能。
        byte[] binary = System.Text.Encoding.Unicode.GetBytes("bar");
        WWWClient http = new WWWClient(this, "http://example.com/");
        client.AddBinaryData("foo", binary, "test.txt", "application/octet-stream");
        client.OnDone = (WWW www) => {
            Debug.Log(www.text);
        };
        client.Request();

        // エラーハンドリングをする場合は、OnFailプロパティにDelegateを代入。
        // 正常終了のハンドリングと同じくWWWクラスが渡されるのでごにょごにょする。
        client.OnFail = (WWW www) => {
            Debug.Log(www.error);
        };

        // タイムアウトの場合はWWWクラスがDispose()された後で利用できなくなって
        // いるので、引数なしのDelegateをOnDisposedプロパティに代入する。
        client.OnDisposed = () => {
            Debug.Log("Timed out");
        };

        // タイムアウトを設定する場合はTimeoutプロパティに値を代入(float)。
        // デフォルトはタイムアウト設定なし(無制限)。
        client.Timeout = 10f;

        // ヘッダを追加する。
        client.AddHeader("Cookie", "cookiename=cookievalue");   
    }
}

OMarOMar 2015/04/01 03:40 Is there a way to make a client.Request() inside a onDone method ? Im having troubles making chained requests. Also, I have methods where I need to make a "return" inside a onDone block, but the library doesn't allow that.

clonedcloned 2015/04/01 23:49 続きはGistで!
https://gist.github.com/cloned/fe255b4f7d6feacee319#comment-1424419

トラックバック - http://d.hatena.ne.jp/cloned/20140609

2014-03-24 (Mon) WebKitは欧文フォントによって和文フォントを切り替えている このエントリーを含むブックマーク このエントリーのブックマークコメント

知らなかった。CSSで和文フォントが指定されていない場合、ブラウザのデフォルト指定フォントで表示されるものだと思っていたけど、WebKit(Safari)やWebKitからフォークしたBlink(Chrome)では、欧文フォントによって和文フォントが切り替わるようになっているみたい。Safariは7.0.2、Chromeは33.0.1750.152で確認。

f:id:cloned:20140324224036p:image

それぞれ和文フォントを指定していないのに欧文フォントの空気を読んで和文フォントまで明朝体、ゴシック体、ボールドに切り替わっている。ここに表示しているのは画像で実際のHTMLサンプルはこちら

ただ、WindowsだとChromeでもこうならない。Linuxは未確認。

日本人が日本語のウェブサイトを作るときは日本語フォントを指定すると思うので気にするまでもないけど、最近Facebookのニュースフィードのシェアに明朝体が表示されるようになって、そういう趣味になったのかと思ってフォント指定を調べたら、欧文フォントしか指定されていなかったので、もしかしてと思ったらそうだった。

トラックバック - http://d.hatena.ne.jp/cloned/20140324

2014-02-13 (Thu) SLAの稼働率がどのくらいのダウンタイムなのか計算するツール作った このエントリーを含むブックマーク このエントリーのブックマークコメント

稼働率 => 月間ダウンタイム計算ツール

http://cloned.jp/sla-calc/

ついでなので、有名どころのSLAを調べて載せたんだけど、Google App Engineの95.0%未満で50%返金や、Amazon EC2の 99.0%未満で30%返金みたいな返金レートは国産だと見つけられなかった。みんな10%で統一されていて15%というのが1件見つかったくらい。

そんなにたくさん調べていないのでざっと見た感じだけど、IDCフロンティアの稼働保証が 99.999% と高かった。

2013-12-22 (Sun) topよりグラフィカルで操作性の良いhtopを使ってみた このエントリーを含むブックマーク このエントリーのブックマークコメント

htop - an interactive process-viewer for Linux

topに色を付けたいと思っていたらhtopというのがあったので使ってみた。SourceForgeに初めてコミットされたのが2006年3月4日となっていてそんなに新しいプロジェクトではないけれど、4日前にもコミットされていて、ちゃんとメンテされている模様。

Amazon Linuxだとyumリポジトリに入っているので下記だけでインストールできる。

$ sudo yum install htop

f:id:cloned:20131222214536p:image

色が付いた以上に、CPUやメモリのグラフィカルな表示が可愛い!!

f:id:cloned:20131222214913p:image

表示中のプロセスをスペースキーで選択状態にできて(画像だと黄色の行)、その場でF9を送信すると選択中のプロセスをkillできる強者。とはいえ、(プロセス番号ではなく)かなりカジュアルにkillできてしまうので本番で使うには恐いなーと思った。

f:id:cloned:20131222214912p:image

操作性が良くて、表示されていない箇所は下にも上にもスクロールできて、プロセスの選択はマウスでポチっとできるし、各操作もキーバインドを覚える事無く明記されているファンクションキーから各操作ができる。上の画像はF6 SortByを選択したときのもの。topのキーバインドは全く覚えられなくて困る(hでヘルプ出せばわかるっちゃわかるけど)。

開発環境でしばらく使ってみよう。

2013-11-27 (Wed) 馨和 KAGUA Rouge Special Edition 2013を開栓 このエントリーを含むブックマーク このエントリーのブックマークコメント

年の瀬になったら飲もうと心に決めていた馨和 KAGUA Rouge Special Edition 2013を開栓した。

「馨和 KAGUA」Rouge Special Edition 2013
「馨和 KAGUA」Rouge Special Edition 2013 posted by (C)cloned

そもそも馨和がどういうビールかについては、最近の記事だと横浜タカシマヤに日本クラフトビール「馨和 KAGUA」−限定試飲販売も - ヨコハマ経済新聞が適確に説明されていると思う。

7月に販売開始間もなく購入したので約4ヶ月寝かせてからの開栓。元々の馨和 Rougeがアルコール度数が9%なのに対してこれは11%なので、力強い味なのだろうという思い込みがあったためか、第一印象は「あっさり」だった。もちろん「薄い」という意味ではなく、Special Editionじゃない方と同じくらいの「あっさり」という意味である。

しかし、二口、三口と飲み進めるうちに「あぁ、丸いな」と感じるようになった。数ヶ月という若さにも関わらず「丸い」と感じられるのは非常に好印象だった。とはいえ、「ビールとして丸いか?」と問われれば「まだまだ生き生きとしている」と答えると思う。あくまでも元々の馨和 Rougeに比べて丸さを感じられるということであるし、繊細な炭酸が生き生きと感じされるのかもしれない。この辺はビールの状態(コンディション)に依るので、僕自身が飲んだ状態に依るのだけれど、こういう「ビールの状態」が存在するのも面白味の一つだろうか。

「馨和 KAGUA」Rouge Special Edition 2013
「馨和 KAGUA」Rouge Special Edition 2013 posted by (C)cloned

さて、話は変わってシリアルは3495だった。4000本の限定醸造で自分の瓶が何番なのか記載されている。特に3495という数字に意味はないけれども、4000本の中の1つをいただいている、というのも一つの楽しみになる。

この「馨和 KAGUA」Rouge Special Edition 2013はなんとまだ在庫があるらしい。750mlで3,045円。酒屋でシャンパンをカゴに入れる人にとってはそんなものかという値段だろうし、ビール1缶(350ml)200円と思う人にとっては高いと思う。このビールをどういうポジションと見るかによって高い安いの感覚は変わると思う。

馨和はとてもおすすめのビールだけど、Special Editionから入るよりは普通のバージョンを買ってみて好きかどうか試してみるのが良いと思う。

トラックバック - http://d.hatena.ne.jp/cloned/20131127

2013-11-23 (Sat) 関西ソーシャルゲーム勉強会でVPCとChefに関する発表をしてきました このエントリーを含むブックマーク このエントリーのブックマークコメント

第5回 関西ソーシャルゲーム勉強会でAmazon VPCとChefに関する発表をしてきました。発表資料は以下のリンクからどうぞ。

小さく始めて後で困らないためのVPCとChefを使ったAWS運用

トラックバック - http://d.hatena.ne.jp/cloned/20131123

2013-10-20 (Sun) ガチャみたいに設定した確率で抽選を行うPHPライブラリを作った このエントリーを含むブックマーク このエントリーのブックマークコメント

くじ引きやガチャガチャのように、あるものはよく出る、あるものはあまり出ない、というように確率に差がある上で一つ抽出する、というプログラムをたまに書くことがあるので、その部分だけを行うPHPライブラリを作った。

インストール

Composerを使う場合は、composer.jsonのrequireに

"cloned/luckybox": "$VERSION"

を追加。$VERSIONに入れられるバージョンはPackagistを参照。今のところは 0.9.* としておくと良いでしょう。

Composer推奨だけど、Composerを使わない場合はGitHubからソースコードをダウンロードしてrequire_onceしてもOK。

使い方

コインは60%、キノコは35%、スターは5%、という設定で1回くじを引いてみる例はこんな感じ。

<?php
use LuckyBox\LuckyBox;
use LuckyBox\Card\IdCard;

// Items
$items = array(
    1 => array('name' => 'Coin',     'rate' => 60), // 60%
    2 => array('name' => 'Mushroom', 'rate' => 35), // 35%
    3 => array('name' => 'Star',     'rate' => 5),  //  5%
);

// Setup
$luckyBox = new LuckyBox();

foreach ($items as $id => $item) {
    $card = new IdCard();
    $card->setId($id)
         ->setRate($item['rate']);

    $luckyBox->add($card);
}

// Draw
$card = $luckyBox->draw();
$item = $items[$card->getId()];

echo "You got {$item['name']}" . PHP_EOL;

LuckyBoxに確率(rate)を設定したCardを詰めて、LuckyBox#draw()するとその確率に応じた割合で一つ取得できる。

バンドルされているIdCardクラスは、rate以外にidのみ保持できるクラスで、より複雑なCardを作りたい場合は、Cardインタフェースを実装したものであれば自作しても利用可能。

デフォルトでは無限に LuckyBox#draw() できるけれど、LuckyBox#setConsumable(true) するとadd()したCardの分しか引けなくなる。こんな感じで利用できる。

<?php
$luckyBox = new LuckyBox();

// Add some cards.

$luckyBox->setConsumable(true);

while (!$luckyBox->isEmpty()) {
    $card = $luckyBox->draw();

    // Do something.
}

0.5% みたいなより精度の高い確率を設定したい場合は、より大きな数をrateに設定すると可能。

<?php
$card1 = new IdCard();
$card2 = new IdCard();
$card1->setRate(1023); // 10.23%
$card2->setRate(8977); // 89.77%

ここでは説明しやすいように合計を100とか10000にしているけれど、実際には全てのrateの合計に対する比率になっているので、「1:1:3」みたいな指定も可能。「1:1:3」とすると「20%:20%:60%」という確率になる。

同じような要件があって、まだコーディングしていなければ、是非使ってみてください。

トラックバック - http://d.hatena.ne.jp/cloned/20131020