October 11, 2014

CentOSでcompass watchをSupervisorで永続化してOS起動時に自動起動する

以下、普通にインストールして設定する手順ですが、Vagrant環境に仕込んでチームで共有する。とかが現実的な使い方かなと思います。

Compassのインストール
$ sudo gem install compass
Supervisorのインストール
$ sudo yum install python-setuptools
$ sudo easy_install supervisor
/etc/supervisord.conf の作成(echo_supervisord_conf の内容に以下を追記)
[program:compass-watch]
command     = compass watch --poll --app-dir /path/to/app-dir/ -c /path/to/config.rb
autostart   = true
autorestart = true
/etc/rc.d/init.d/supervisord の作成
#!/bin/sh
#
# /etc/rc.d/init.d/supervisord
# sudo chkconfig --add supervisord
# sudo chkconfig --level 35 supervisord on
#
# chkconfig: - 64 36
# description: Supervisor Server
# processname: supervisord

. /etc/rc.d/init.d/functions

NAME="supervisord"
COMMAND="/usr/bin/supervisord"
PIDFILE="/var/run/$NAME.pid"
CONFIG="/etc/supervisord.conf"

start()
{
  echo -n $"Starting $NAME: "
  daemon $COMMAND -c $CONFIG --pidfile $PIDFILE
  [ -f $PIDFILE ] && success $"$NAME startup"
  echo
}

stop()
{
  echo -n $"Shutting down $NAME: "
  [ -f $PIDFILE ] && killproc $NAME || success $"$NAME shutdown"
  echo
}

case "$1" in

  start)
    start
  ;;

  stop)
    stop
  ;;

  status)
    status $NAME
  ;;

  restart)
    stop
    start
  ;;

  *)
    echo "Usage: $0 {start|stop|restart|status}"
  ;;

esac
/etc/rc.d/init.d/supervisord のサービス登録と自動起動設定
$ sudo chkconfig --add supervisord
$ sudo chkconfig --level 35 supervisord on

September 6, 2014

Laravel4.3(5.0?)のディレクトリ構成の変更メモ

4.3でなく5.0にする案も出ているみたいですが、どうなるかよくわからないので、とりあえず4.3と呼びます。

近々、4.3を使う気がするので、ディレクトリ構成の変更について調べてみました。時期的に、4.3正式リリースの少し前になると思うんですが、4.2からのアップグレードは面倒そうなので、4.3でスタートした方が良いかなぁ。と。

https://laracasts.com/series/whats-new-in-laravel-4-3 の動画が大変参考になりました。

尚、4.3はまだ開発段階なので、多少は変わる結構変わる可能性もあると思います。

まず、appディレクトリ。
app
├── Console ... app/commands に相応
├── Http
│   ├── Controllers ... app/controllers に相応
│   ├── Filters ... app/filters.php で指定していたフィルタ処理のクラス群
│   ├── Requests ... 新機能。1リクエストに対するバリデーションルールや認証ロジック等を持つクラスを実装する
│   └── routes.php ... app/routes.php に相応
├── Providers ... グローバル空間で行われていた色々な処理がプロバイダとしてまとめられた
└── User.php ... app/models/User.php に相応
appディレクトリはcomposer.jsonのPSR-4で指定されています。
"psr-4": {
    "App\\": "app/"
}
名前空間プリフィックスである "App" が嫌な場合は "app:name" で変更できます。
$ php artisan app:name Foo
動画にもあるように app/Http/Requests に実装するクラスは、コントローラメソッドにインジェクションして使うと便利そうです。コントローラメソッドへのインジェクションも、4.3からの新機能です。

次に、その他のディレクトリ。
bootstrap
├── environment.php ... 環境を決める処理がここに切り出された

config ... app/config に相応
├── filesystems.php ... 4.3で新たに組み込まれたファイルシステムに対する設定
├── namespaces.php ... appディレクトリ以下のクラスに対して使用される名前空間の設定

database app/database ... に相応

resources
├── lang app/lang ... に相応
└── views app/views ... に相応

storage app/storage ... に相応

4.2までappディレクトリにあった多くのディレクトリがプロジェクトルートに移動されています。appディレクトリに作成するファイルは、基本的には、アプリケーションを動かすためのクラスのみ。となった感じですかね。

phpunit-seleniumでテスト失敗時にスクリーンショットを撮る。スクリーンショットのファイル名をカスタマイズする。

基本的には https://phpunit.de/manual/4.2/ja/selenium.html の "例 13.3: テストに失敗したときのスクリーンショットの取得" の通りです。

以下の3つのプロパティを適切に設定すれば、テスト失敗時にスクリーンショットが作成されます。
protected $captureScreenshotOnFailure = true;
protected $screenshotPath = '/var/www/localhost/htdocs/screenshots';
protected $screenshotUrl = 'http://localhost/screenshots';
ただし、これだけだとスクリーンショット名が、自動で決まる文字列の羅列になってしまい、どのテストで失敗したのかわかりにくいです。なのでカスタマイズしてみます。

スクリーンショット名は https://github.com/giorgiosironi/phpunit-selenium/blob/master/PHPUnit/Extensions/SeleniumTestCase.php の takeScreenshot() で決まるようです。スクリーンショットディレクトリを返すメソッド "$this->getScreenshotPath()" が呼ばれていて、このメソッドはたぶんここでしか使われていないので、オーバーライドしてみました。

まとめると、以下のようになります。$screenshotPath は、とりあえず "__DIR__" にしています。$screenshotUrlは、とりあえずドキュメントの通りです。(これどんな意味があるのだろうか。)
protected $captureScreenshotOnFailure = true;
protected $screenshotPath = __DIR__;
protected $screenshotUrl = 'http://localhost/screenshots';

/**
 * {@inheritdoc}
 */
protected function getScreenshotPath()
{
    $className = end(explode('\\', get_class()));
    $methodName = \PHPUnit_Framework_Testcase::getName(false);

    return parent::getScreenshotPath().sprintf('%s_%s_', $className, $methodName);
}
テストを失敗させて "{クラス名}_{メソッド名}_{文字列の羅列}.png"が作成されればOKです。

takeScreenshot()のオーバーライドだと丸ごと書かないといけないので、この方法を選びました。getScreenshotPath()というメソッド名の意味合いは変わってしまいますが、まあ、テストケースなので良いかなと。

Macでphpunit-seleniumを動かすメモ

Mac OS X Mavericksで確認しています。

この記事の手順では brew, composer, phpunit 等が予め必要です。

Selenium Serverをインストールします。
$ brew install selenium-server-standalone
Selenium Serverを起動します。
$ selenium-server
適当な場所にディレクトリを作って https://github.com/giorgiosironi/phpunit-selenium をインストールします。
$ composer require phpunit/phpunit-selenium:1.*
適当なテストケースを書きます。
$ more ExampleTest.php
<?php
require_once 'vendor/autoload.php';
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';

class ExampleTest extends PHPUnit_Extensions_SeleniumTestCase
{
    public static $browsers = [
        ['name' => 'Firefox', 'browser' => '*firefox'],
        ['name' => 'Chrome',  'browser' => '*googlechrome'],
    ];

    protected function setUp() {
        $this->setBrowserUrl('http://www.example.com/');
    }

    public function testTitle() {
        $this->open('http://www.example.com/');
        $this->assertTitle('Example Domain');
    }
}
ブラウザ名の先頭につけるAsteriskについて http://stackoverflow.com/questions/7405498/in-selenium-1-why-are-all-the-browser-commands-prefixed-with-an-asterix に記載がありました。まだよく理解していませんが。。。

実行してみます。
$ phpunit ExampleTest.php
PHPUnit 4.2.4 by Sebastian Bergmann.

..

Time: 7.01 seconds, Memory: 5.25Mb

OK (2 tests, 2 assertions)
FirefoxとChromeが起動してテストが実行されました。

テストの書き方はPHPUnitのマニュアル https://phpunit.de/manual/4.2/ja/selenium.html に記載されています。

思っていたより簡単で良かった。

August 16, 2014

Knockout.jsのforeachで少しハマったのでメモ

最近、ぶっつけ本番でKnockout.jsを使っていて、foreachの動きで少しハマったのでメモ。(慣れている人にとっては大したこと無い内容なんだろうけど。。。)

サンプルソースは以下です。
<html>
<head>
    <script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
</head>
<body>
<!-- foreach をネストする際、内側の foreach で使用する配列に "$root" をつけないとエラーになる  -->
<div data-bind="foreach: {data: array1, as: '$value1' }">
    <p data-bind="text: $value1"></p>
    <div data-bind="foreach: {data: $root.array2, as: '$value2' }">
        <p data-bind="text: $value2"></p>
    </div>
</div>

<!-- "$index" は単独だと出力できるが、文字列連結時等は "$index()" としないとエラーになる  -->
<!-- "$parentContext.$index" も同様  -->
<div data-bind="foreach: {data: array1, as: '$value1' }">
    <p data-bind="text: $index"></p>
    <p data-bind="text: $index() + ':' + $value1"></p>
    <div data-bind="foreach: {data: $root.array2, as: '$value2' }">
        <p data-bind="text: $parentContext.$index"></p>
        <p data-bind="text: $parentContext.$index() + ':' + $value2"></p>
    </div>
</div>

<script>
    function ViewModel() {
        var self = this;
        
        self.array1 = ko.observableArray(['foo', 'bar', 'baz']);
        self.array2 = ko.observableArray(['FOO', 'BAR', 'BAZ']);
    };

    ko.applyBindings(new ViewModel());
</script>
</body>
</html>

コメントに書いてある通りですが

1.
foreach をネストする際、内側の foreach で使用する配列に "$root" をつけないとエラーになる

2.
"$index" は単独だと出力できるが、文字列連結時等は "$index()" としないとエラーになる("$parentContext.$index" も同様)

で、ハマった。。。


P.S.
まだほんの少し使った程度ですが、JSはjQueryがメイン。という開発の中に、双方向バインディングだけ付け足したい時とかは便利かなと思いました。jQueryの処理も好きに書いて良い(という思想な)はずなので、例えばチームのメンバ的に、AngularJSだと(主にディレクティブ周りで)敷居が高くなってしまい導入に躊躇するような状況でも、Knockout.jsなら比較的楽に導入出来る気がします。(制約が緩いほど秩序は保ちにくくなってしまうはずですが。)