Translate

2013年10月24日木曜日

Controller_Restを継承したabstractクラスのbefore()メソッド内部でレスポンスを返す

FuelPHPにおいてRestfulな処理を実装する時には、まずController_Restを継承したController_Hogeなんかを作って、そこにメソッドをグリグリと記述していくのだけれども、開発が進むと「ユーザの認証処理もろもろの初期化とか全部親クラスのbefore()とかafter()でやらせたいなー」となる事が多々あると思います。というか絶対そうなります。ところが、プロジェクトによってはCoreクラスの編集が禁止されていたりするわけで、そんな場合にはConroller_Restに直接の変更を加えることは当然できません。で、回避方法としてはController_Restを継承したabstractクラスController_Myrestクラスなどを作り、そこにbefore()やらafter()を実装した上で、実際にリクエストで呼び出されるクラスではこのクラスを継承する感じになると思います。

abstract class Controller_Myrest extends \Controller_Rest
{
    protected $user;
    publc function before()
    {
        parent::before();
        $this->user = Model_User::find($this->param('user_id'));
    }
}

で、このController_Myrest::before()の中で、例えば上記のようにUser_Modelを取得して$this->userに格納するという事を毎回行うとして、仮にuser_idが違っていたり不正だったりした時には下記のようなエラーハンドリングが必要となるわけです。

    publc function before()
    {
        parent::before();
        $this->user = Model_User::find($this->param('user_id'));
        if(!($this->user instanceof Model_User))
        {
            //エラー処理
        }
    }

※エラーハンドリングは適当なのでそのまま使っちゃダメです。

処理を中断させるにはExceptionを発生させたりexit()したりと色々ありますが、これらを使うとステータスコードに問答無用で500がセットされてしまいます。せっかくのRestコントローラなので、ちゃんとレスポンスコードを返してあげたいところです。が、before()内部でreturnしてもその先に進んでしまい、実際に呼び出された子クラスのメソッドが実行されてしまうので、ここでは別の内部URLにリダイレクトさせ、そちらで正式なレスポンスを返すことにしようと思います。ここでは仮に'/default/unauthorized'というURLにリダイレクトさせ、そこで強制的に404を返す、という形をとります。

まず、abstractクラスのbefore()。

    publc function before()
    {
        parent::before();
        $this->user = Model_User::find($this->param('user_id'));
        if(!($this->user instanceof Model_User))
        {
            //エラー処理
            \Response::redirect('/default/unauthorized');
        }
    }

それから、/default/unauthorizedの実装。

class Controller_Default extends Controller_Rest
{
    public function action_unauthorized()
    {
        return \Response::forge(NULL, 404);
    }
}

これで無事に404が返されるようになります。キモはController_DefaultではController_Myrestを継承しないこと。でないとリダイレクトがループします。本当は直接に処理を中断させて、かつステータスコードも返せるようないい方法があるのかも知れないけど、とりあえずこれで動いたのでよしとします。

2013/11/06 追記

before()内で以下のように書いて、例外を返せばいいことに気がついた。とりあえず404を返すが、必要があれば独自の*Exceptionクラスを実装して、そっちを呼べばよし。

throw new HttpNotFoundException;

2013年10月18日金曜日

FuelPHPのModel::query()でちょっとだけ複雑なクエリを組み立てる

こんな内容のSQLを組み立てたい時、
select * from table \
where (column1 = 'val11' and column2 = 'val12') \
or (column1 = 'val21' and column2 = 'val22')
こんな感じで記述。
$result = Model_Table::query()
    ->and_where_open()
       ->where('column1', '=', 'var11')
       ->where('column2', '=', 'val12')
    ->and_where_close()
    ->or_where_open()
        ->where('column1', '=', 'var21')
        ->where('column02', '=', 'val22')
    ->or_where_close();

2013年10月10日木曜日

プロンプトに現在のgitローカルリポジトリ/ブランチを表示する

作業中に現在見ているgitローカルリポジトリの場所とブランチ、それからコミット状況がテキスト色で分かると便利だなー、と思い、その辺りをスクリプトにまとめてみた。

cat ~/bin/gitbranch.bash
#!/bin/bash
# echo git branch for color PS1

name=`git symbolic-ref HEAD 2> /dev/null`
if [[ -z $name ]]
then
        exit
fi

name=`basename $name`
st=`git status`
repos=`git remote -v | \
grep -o '\/.*' | \
sed -e "s/[ \s\/]//g" | \
sed -e "s/(fetch)//g" | \
sed -e "s/\.git//g" | \
awk "NR==1{print}"`
if [[ -n `echo $st | grep "nothing to commit"` ]]
then
    color="\[\e[32;1m\]"
else
    color="\[\e[31;1m\]"
fi
reset_color="\[\e[0m\]"
echo -E $color[$repos $name]$reset_color
これを.bashrcあたりで読み込んでPS1にセットしておくと、プロンプトが常に更新される感じになる。
PS1="\$(~/bin/gitbranch.bash)\n[\u@\h: \W]> "
プロンプトそのものはこんな感じ。
[Repo-Name branch]
[username@localhost: dir] > # commit前 [Repo-Name branch]
[username@localhost: dir] > # commit後

2013年10月7日月曜日

perlのオブジェクト同士を比較する時の、演算子による挙動の違いを実験

perlにおいてオブジェクト同士を比較する時、==を使うべきかeqを使うべきか若干悩んで、検証してみる事にした。 まずはソース。
# cat /tmp/test.pl
use strict;
use warnings;
use utf8;
use 5.010;
use Data::Dumper;
{
        package Hoge;
        sub new()
        {
                my $class = shift;

                my $hash = {
                        id   => int(rand 10),
                        name => int(rand 10)
                };
                bless $hash,$class;
        }
}

my $obj1 = new Hoge;
my $obj2 = $obj1;
say (($obj1 == $obj2) ? 1 : 0);
say (($obj1 eq $obj2) ? 1 : 0);

say (($obj1 == $obj3) ? 1 : 0);
say (($obj1 eq $obj3) ? 1 : 0);

1;
で、出力結果がこれ。
# perl /tmp/test.pl
0
0
1
1
2つ目までの出力結果が異なるのは当然として。3つ目がtrueになるのも、下記を読んで納得。
Perlのリファレンス比較は==演算子
問題は最後、4つ目。文字列比較した時に何故一致するんだろうかと。 で、以下のコードを追加。
say qq($obj1 $obj3);
おもむろに実行。該当部分の出力結果を抜粋。
Hoge=HASH(0x8d74968) Hoge=HASH(0x8d74968)
なるほど、blessされたリファレンスを文字列として評価すると、"クラス名=型(アドレス)"と解釈されるのか。

2013年10月3日木曜日

ログアウト時に自分が最後の一人だったらtmux kill-server

ログアウト時にtmuxのセッションを強制的に終了する。
自分以外の人が作業してるかどうかを一応チェックして、他に人がいたら何もしない。自分が別プロセスでログイン中の時も何もしない。
もちろん、tmuxが動いてない時も何もしない。
cat /path/to/killtmux.bash
#!/bin/bash

count=`who | grep $USER | wc -l`
if [ 1 -ge $count ]; then
expect -c "
set timeout -1
spawn /usr/local/bin/tmux ls
expect {
    \"failed\" {
        exit 1
    }
    \"eof\" {
        spawn /usr/local/bin/tmux kill-server
        exit 0
    }

    interact
}
"
fi

~/.bash_logoutあたりでこのスクリプトを叩くように追加。
echo /path/to/killtmux.bash >> ~/.bash_logout