のらねこの気まま暮らし

技術系だけど、Qiita向きではないポエムとかを書くめったに更新されないヤツ

YAPC::Asia 2014に参加してきました #yapcasia

YAPC::Asiaとは

8/29, 8/30に開催された、世界最大規模のプログラミングカンファレンスです。 Perl界隈のエンジニアからPerlは関係ないけど、プログラミングが好きな人が集まる年に一度のエンジニアの祭典!

YAPC::Asia 2014

この記事は

わたしく@mizuki_rの個人的な見解をだらだら述べる記事でございます。 なお、藤沢にある某メイドバーで執筆しているなう。何してんだ、ほんとなにしてるんだ僕。

全体の振り返り

最後のkeynoteのtypesterさんのトークは正直胸に刺さった。 なんだろう、まだうまい言葉に落とせていないような感動具合。 本当にいいトークだった。

通して、自分の立ち回りや今後のキャリアプランとかを改めて考えさせられるスピリチュアルなトークが多かった印象。 同期が二人発表しているのを見て、やっぱりトークしたかったなぁと思う反面 参加してよかったYAPC! 大好きYAPC! スタッフの皆さんお疲れ様でした!

@yusukebe さん大変だとおっしゃっていたけどぜひとも来年も開催を、そして次こそスピーカーとして参加したいw

あ、あと落選したトーク、どこかで話せる機会を作りたいな。

8/29日の振り返り

僕が参加したトークは以下。 真面目なまとめや評価に関しては、技評さんや他のすばらしいエンジニアのブログを参考にしてくれといいな。

あと、量が多いので個人的に興味のある記事だけピックアップしちゃう。

インフラエンジニア(狭義)は死んだ

WEBのインフラ系は、物理面を意識することも減ったし、便利なツールも増えてきている。 アプリが動いているのもソフトウェアだし、便利なツールもプログラミングできないと使いこなせない場合も増えている。 この先、プログラミングできないインフラエンジニアは死にそう。

そんな状況で、プログラミングから逃げていた(逃げていたとはもう今は思えない)studio3104氏がどうプログラミングと向き合ってきたのかっていうお話。

「インフラエンジニア」っていう言葉は「プログラミングできない」「プログラミングができなくてもいい」みたいな認識があって、 「インフラエンジニア」ってなのることでそのことを許容しているのではないか? 自称は自意識に影響を与えるというメッセージはすごく僕に刺さった。

僕自身フロントエンドエンジニアという自称の中で、いくつかの責務から目をそらしていた節がある。

与えられた役割、動ける範囲を改めて見直して自称ではなく、自分として何ができるかを改めて考えてみようと思った。

すごい意識の高いトークで一発目からしびれました。

お待たせしました。PerlでBDDを簡単に以下略

tokuhiromさんが書いてきたTest系Moduleの振り返りと、perl6並に夢の詰まったTest::Builder2の開発停止に伴う、じゃあぼくらはどうすればいいんだに対する回答。

それがTest::Kantan 最近のテストライブラリから、言うまでもなく後方互換を保ちつつテストの集計を刷新したunicode文字使ったり色つければウケがいいというフレームワーク

subtestはテストのコンテキストがわかりにくく、JavaScriptでBDDを主体としたSpecによるテストを行っていた最近の僕としては、 より可用性のあるライブラリっぽい気配がしてもう胸がたかなる。

なお、後輩が試した話によるとサンプルそのままだと動かなくて、describeバグってるぽい? 余裕見て検証したいところである。

Perl::Lint

バグとなりそうな危ういコードを指摘してメンテナビリティの高いコードを推奨しつつレビューの手間を省くツール、それがソースコードリンター。 PBPのポリシーを実装したperlcriticは遅いので、もっと早いのを作りました、というトーク。

Perlの静的解析の手法と、Perl::Lintの使い方の説明をしつつ、いやぁモダンな名前空間だなぁと思っていた。 perlcriticnのカスタマイズはなかなか職人芸な気がしていて手が出しにくい。しかも時間かかるから常にテストと一緒に走らせるには重い。

そんな状況の打開策を提案してくれるモジュールのよう。

独自ポリシーの定義をサポートしつつ、perlcriticのまんまのポリシーを定義している。

独自ポリシーの定義をサポートしつつ、perlcriticのまんまのポリシーを定義している。 もちろん、簡単なポリシーなら簡単にかけるが、難しいポリシーをかくのは難しいというまあそうだよねってところだけど、今後に期待。

なお現在ラスボスのようなポリシーを実装してくれるコントリビューターを募集しているそう。 われこそわという方はぜひ。

僕はjshintを読んで挫折しました。

8/30の振り返り

オープンソースの開発現場

perl 5.20で入ったsignaturesにまつわる議論についてのお話。 個人的にはベストトークショーになってほしいくらいいい話だった。

signaturesの実装を提案し議論を醸したMartini氏の奮戦とその立ち回り、p5pという民主主義のメリット・デメリットはとても興味深いものだった。 perl5は多様な人が居て、多様な意見がある。

そのなかで自分の目指すべきところを正確に見据え、その優先順位を守って、時には受け止め、時には受け流しうまいこと立ちまわることがOSSにおいて大事なのだと感じた。 いろんな人達の日々の努力よって僕らは支えられ、いまこうしてコードを書いているのだと改めて感謝したトークだった。

ちなみにはやくsingaturesを使うの楽しみにしてるぜ!!!

半端なPHPDisで以下略

皆まで言うな、今回のベストトークショーである。 perlのカンファレンスでPHPのトークがベストトークショーともう伝説だよねこれwwww

語るまでもない、PHPのもつ闇としかしその闇と戦うPHPerの強いメンタルを垣間見た。 元PHPerとしては、PHPの変化にちょっと興味をそそられ、たまには弄りたいなと思った。

でもやっぱりPHPはいいかなwwww

Perlあるある

豪華メンツを集めた対談みたいな、Perl初学者向けのトーク。 名を轟かすPerlMongersの赤裸々な素顔に迫るトークだった。

いろいろ名言いただきましたけど、ちょっと個々では割愛。 多くて・・・。

ただ、 「コードを書くのに許可はいらない」 「コードも作文も同じ。読まれて意味の分からないコードは書かない」 「基礎が大事」 「Done is better than perfect」 心に刻みたい。

追記

  • APC::AsiaになってたのでYAPC::Asiaに直した。
  • Papixはフリー素材
  • インフラエンジニア(狭義)は死んだを

Amon2::Plugin::Web::Auth::PathというPluginを書いた

ソースはこちら。トルネで録画したJOJOの映像が乱れまくって辛いです。
https://github.com/rymizuki/p5-Amon2-Web-Auth-Path

頑張って短くした前置き

Amon2にはWeb::Authという認証をサポートするプラグインがあります。が、それらは外部の認証自身のアプリケーションで使うためのツールであり、アプリケーションのページ単位での認証をおこなってくれません。
例えば、トップページは認証をかけない、マイページはかける。お問い合わせページはかけない、という細かい制御が難しい。
BEFORE_DISPATCHでPATH毎、あるいはController毎に管理するとか、Routerのany使うとかやりようはありますが、アプリの規模が膨らめば膨らむ程コードも膨らむ。可読性も下がるしあまりよろしくない。


Amon2::Plugin::Web::Auth::PathはPATH単位でBEFORE_DISPATCHを走らせて、認証の処理をContextから分離したものです。Pluginのinit時にpathsをArrayRefで渡すか、moduleを渡すかの選択ができます。
少ない認証だけでしたら、pathsでも十分なのですが、僕の要求ではWebContextの行数が倍に膨れ上がる複雑な処理だったので、出来ればそこは切り出したい、と思って外部Moduleを呼べるようにしました。ついでにDSLなんかをつくってみました。

そんなわけで、
Router::Simpleでの実装は、@takuji31さんのAmon2::Web::Authorizerを参考にさせて頂きました。

https://github.com/takuji31/Amon2-Web-Authorizer


処理の中で、認証が通ったか通ってないかの判断を明示的にするために$auth->success,$auth->failedのメソッドを呼ぶといいんじゃない?とid:karupaneruraにアドバイスいただいてまんま実装しました。

つかいかた

package YourApp::Web;
use Amon2::Lite;

get '/' => sub {
    my $c = shift;
};

get '/mypage' => sub {
    my $c = shift;
};

__PACKAGE__->load_plugins(
    'Web::Auth' => +{
        module => 'Twitter',
        ...
    },
    'Web::Auth::Path' => sub {
        paths => [
            '/'          => sub { $_[1]->success },
            qr{^/auth}   => sub { $_[1]->success },
            qr{^/mypage} => sub {
                my ($c, $auth,) = @_;
                if ($c->session->get('is_login')) {
                    $auth->success;
                } else {
                    $auth->failed;
                    # redirect to Auth::Site::Twitter
                    return $c->redirect('/auth/twitter/authenticate');
                },
            },
        ],
    },
);

1;


callbackにはWebContext, Plack::Util::initline_objectのインスタンス, Router::Simpleのmatchが渡されます。
callbackの中では$auth->succes/$auth->failedを呼びましょう。呼ばないと死にます。Appが。
callbackの返り値があればそのままreturnするので(Plack::Responseだけに固定したほうが良いのだろうか)$c->redirect, $c->render等ご自由に。after_dispatchにも影響します。controllerが走らないだけなの。

Router::Simpleつかってるのでパスの指定はRouter::Simpleのままつかえます。

モジュールを使う場合

こんなモジュールを書いて

package MyApp::Auth::Path::App;
use Amon2::Auth::Path::Declare;

path '/' => sub { $_[1]->success };

1;

WebContextではこんな感じでパラメータ渡してやる。

__PACKAGE__->load_plugins(
    'Web::Auth::Path', {
        module => 'MyApp::Auth::Path::App',
    },
);

以下はpathsと同様。

デバッグでしか使ってないけどとりあえず残した

initのoptionにon_success,on_errorのcallbackが渡せます。
$auth->success/failedの結果を受け取って、responseの返却をするまえに走らせるHookです。




とまあ、こんなかんじで。
とりあえず簡単なテストは追加したので、ひとまずはこれでよいかなーと。

さて、怠惰な休日を謳歌しましょう。

Amon2::Plugin::Web::ValidatorというPluginを書いた

ソースは以下。まだ全然書き途中
https://github.com/rymizuki/p5-Amon2-Plugin-Web-Validator

長めの前置き

以前からポツポツとリクエストのValidation系のModuleがどうにも肌に合わず、去年の秋ごろにData::Validatorを弄る感じで自作Moduleを使っていた。

別にあれがダメこれがだめで、というよりは社内で使うのにいろいろ制約があったことと、どうも冗長に感じること、Data::Validatorをメソッドのバリデーションに使っているのにリクエストはRuleの書式が違うことなど、どうにもまとまりがなかった。

受け付けるデータのチェックを行うのであれば、ModelもContollerも同じフォーマットであるにこしたことはないし、Mouse::Typesとか型チェック系のModuleも使っているのだから、ある程度ルールも共通化できる。
nameパラメータは何文字以上何文字以内、とか。Webインターフェースだけが受け付けるわけじゃなく、CUIインターフェースでも利用する可能性があるいじょう、どちらかに依存してしまうのはよくないし、かといってModelでValidationすれば他でしなくてもいい、ということではないと僕は考えている。


Data::ValidatorはRuleごとインスタンスをキャッシュできたり、Roleで挙動を変えられたり、TypeConstraintsで型を定義できたりしていて使い勝手が良い。
メソッドなんかで引数のバリデーションに使うのであれば、メソッドの先頭に持ってきて、明示的にどういう引数を期待しているのかを記述できる。これはメンテナンスのときに非常にありがたい。


そんなこんなもあって、最初はData::Validatorでリクエストをバリデーションしていたんだけれど、だんだんその冗長性に嫌気がさしてきた。余談だけれど、僕は一月半で一つのサービス作るくらいの勢いでコードを書いていたから、そりゃもうValidationしまくりなのです。
要求としては以下が追加された。

  • なるべく短くValidationを行いたい
  • バリデーションを行ったパラメータだけを受け取りたい
  • エラーメッセージとパラメーターを紐付けて受け取りたい
  • Fileのアップロードもバリデーションしたい(サイズとかContentTypeとか)


そこまで高機能なValidatorに思い当たらず(探せばあったかもだけど、入れるのがかなり手間の予感)、僕だけの要求が強そうなので、勉強もかねてとりあえず書いてみた。
ついでにそこからAmon2::Pluginにしてみた。


だいたいそんなところが、書いたよって話のコンテキスト。いや長い。

READMEの練習みたいなの

SYNOPSIS

package Project::Web::App;
use Amon2::Lite;

get '/list' => sub {
    my $c = shift;
    my $data = $c->validator(rule => +{
        page => {isa => 'Int', default => 1},
    })->valid_data;
    my $result = $c->model('any')->run(%$data);
    $c->render('list.tx', {result => $result});
};

post '/add' => sub {
    my $c = shift;
    my $validator = $c->validator(rule +{
        name  => 'Str',
        profile => 'Str',
    });
    if ($validator->is_success) {
      ... things ...
    } else {
        return $c->render('error.tx', {errors => $validator->get_errors)};
    }
};

__PACKAGE__->load_plugins(
    'Web::Validator' => {
        module     => 'Data::Validator',
        message  => {str => 'input type is strings!'},
);

1;

がいよー

初期化時に"module"を読み込んでいるのは、Data::Validator以外も使えるようにしたいとか思いそうと思ったから。

messageはdecamelizeしたRuleのISAをキーにしたHashRef。なんでdecamelizeしたのかは忘れたけどたぶんそういう仕様なんだ。

$c->validatorでruleをHashRefで読み込んでいるけれど、外部ファイルにまとめて初期化時に渡すことも可能。

__PACKAGE__->load_plugin('Web::Validator' => \%rule);

もともとは、こっちで使ってたんだけど、@kfly8 氏に突っ込まれて$c->validatorからRuleを呼べるようにした。

Data::ValidatorはNoThrow,AllowExtraのRoleを使っており、Validationしない余計なパラメータがあっても大丈夫。失敗した時に死んだりしない。まだ頑張れる。

メソッドチェイン厨としては、いまのインターフェースは結構きにいってて、最短一行3メソッド呼び出しでバリデーションデータを受け取れるのは嬉しい。

一応テストもちょっとずつ追加して、週末にはMakefile.PLあたりをもうちょいちゃんとしたいとおもっているのと、Podもちゃんと書こうと思ってる。

is_successとかvalid_dataとかget_errorsとか以外にもいろいろメソッドは追加しているので、その辺もちゃんとやりたいのです。



2年間くらいPerl書いてきたけど、低機能なValidatorでもみんな気にせず使ってて、僕だけなのかこんなことに悩むのは、と長らく思っていた。
単純に慣れの問題とか、優先順位の問題とか、そもそもそんなにValidator拘るようなコード書いてへんってのもあるのかなーとは思う。管理画面クリエイターだもの、僕は。


ともあれ、2年目にしてようやく実用的な(?)モジュールを公開できそうなのはちょっとした成長だと思う。
ちなみに最近週一で新しいPlugin生やしてるので、使えそうなのあったら社内外かかわらず出して行きたいと思ってる。とりあえず次は一応Githubには上がってる。ニヤリ

先輩のコードを大きくパクったという、ね・・・
ま、週末にでも書きましょう。

翌朝追記

早速コメントを頂いた。

id:kfly8

opts でon_error と on_success 受け取れるようにして欲しいなーw

わがままさんめっ

こんなふうにすると、Validation成功時・失敗時にコールバックを実行できる。

__PACKAGE__->load_plugin('Web::Validator', {
    module => 'Data::Validator',
    message => \%message,
    on_success => sub {
        my ($c, $validator) = @_;
        debugf 'yappie! %s', ddf $validator->valid_data;
    },
    on_error => sub {
        my ($c, $validator) = @_;
        return $c->render('error.tx', {errors => $validator->get_errors});
    },
});

mojoを使った話

すでに二日くらい経過しているので、何を書こうと思っていたか忘れてしまった。

ちょっと待ってくれ。今思い出す。

・・・

そう、Mojolicious::LiteはperlのWAFなんだけれど、日本語のドキュメントが整っていたり、Google先生に聞けばたいていの問題は解決する。なので、特に書くことが無いんだ。

とはいえ、念のため自分メモを作る。
こういう振り返りは今後のためになると信じている。

Mojolicious::Liteで作る

morbo
開発サーバ
log
デバグログ置き場
public
静的ファイル置き場
templates
テンプレートを分けた場合の置き場

開発サーバ morbo

なんて読むんだろう?
ファイルが変更されると自動でリスタートをしてくれる。

    • verbose を設定すると、ファイル変更のリスタートやsyntax errorなどを吐いてくれる。
    • watch を設定すると初期値に加えて指定したディレクトリも監視し、変更したら更新してくれるようになる。試してない。

$ morbo ./app.pl daemon --listen http://127.0.0.1:8080 --verbose

log

ちょっとログの出し方にはまった。
Mojoliciousはmode=developmentの時にデバグログを吐く。デバグなんだからそうですよね。

mojo梱包のサーバを使うなら↓のようにすればよい。

$ ./app.pl -l *:8080 --mode=development

開発サーバとして梱包されているmorboにはmodeの指定の機能が無いようで、どうすればいいかわからない。
とりあえず、↓のようにした

app->log->level('debug');
app->start

これで、./log 以下にdevelopment.logが吐かれるようになる。

public

cssや画像ファイルといった静的ファイルを配置するばしょ。
public/css/style.cssと指定しておけば、/css/style.cssのパスで取得できるのだよ。

templates

特になにもしなければ、mojo::liteは__DATE__セクションからテンプレートの情報を引いてきてくれる。
けれども、ファイルが大きくなりすぎると管理しにくいので、分けたい、そんなときにこのディレクトリを使う。

例えば、"/"にindexのファイルを配置したければ、次のように。
app.pl

use Mojolicious::Lite;

get '/' => sub {
    my $self = shift;
} => 'index';

app->start;

directory

templates
- index.html.ep
app.pl

getメソッドで'/'にアクセスが来たら、index(という名前の付いた).html(ファイル形式の).ep(テンプレート)を探しに行く。__DATA__以下になければ、templates/を見に行く。


気を付けなければならないのは、読み込むべきテンプレートの指定と、拡張子に.html.epを付けなければならないこと。

.htmlはHTMLコンテンツであることを示す。
.epはmojoが扱うテンプレートハンドラーの形式。

Router

mojoのdispatcher。

リクエストメソッドとパスの指定により、ルートを決定する。

get '/' => sub { ... } => 'index';
get 'register' => sub { ... } => 'register';
post 'register' => sub { ... return $self->render_json); };

他にも、pullやheadも使えるかもしれない。

プレースホルダ

/list?page=1 というURLを /list/1 にしたい場合、以下の様にすれば可能。

get '/list/:page' => sub {
    my $self = shift;
    my $page = $self->param('page');
};


また、プレースホルダに初期値を指定したい場合、

get '/list/:page' => {page => 1} => sub {...};

で、/list にアクセスした時に ?page=1 が設定される。
べんりー。

under

認証など各ページ間で共通した処理を記述したい場合に使うもの。

underメソッドを呼び出した移行のルート記述すべてにその設定が適応される。

例えば、
"/"は認証を行わず、"/user"には認証をかけたい場合

# 認証なし
get '/' => sub {...} => 'index';

# これ以降のページは認証
under sub {
   my $self = shift;
   if ($self->is_login) {
      $self->redirect_to(...);
      return;
   }
   return 1;
}

get '/user' => sub {...} => 'index';


また、"/some/add", "/some/list", "/some/edit" などとディレクトリが重なっているのもunderで書ける。

under '/some';
get '/add' => {...};
get '/list' => {...};
get '/edit' => {...};

Controller

Mojolicious::Liteでのルーター(get, post等)で受け取るインスタンスは、Mojolicious::Controller。
$self->renderとかやっているけど、それはControllrメソッドなのです。

僕が主に使いそうなのは以下。

  • param
  • session
  • render

param

Mojolicious::Message::Requestのparamメソッドへのアリアス。だと思っている。
もちろん、$self->req->param も利用可能。取得内容は同じ。

GET/POSTのクエリパラメータへアクセスする。

また、Rooterで指定したパラメータにもこちらでアクセス可能。

session

Cookieによるセッション管理メソッド。
詳しいことは読んでいないが、Plack::Request::Sessionや、HTTP::Sessionなどとはいろいろ違う。
暗号化したセッションデータをクライアントのcookieに格納する。

session_idをcookieに格納し、データ自体はサーバで持つ他のセッション系モジュールとは若干挙動がことなる。

詳しく調べていないので、今後 flashや暗号キーの設定についても調べるべし。

# session set
$self->session(user_id => $id);

# session get
my $user_id = $self->session('user_id');

# session remove
$self->session(user_id => undef);

# session expire
$self->session(expires => 1);

renderer

テンプレートをレンダリングするメソッド

Mojolicious::Rendererが基底クラス。

MIMEタイプを元に、レンダリングを行うらしい。
初期値で指定されているのは、たしか、[json, text, xml]とHTMLだった気がする。

# index.html.epをレンダリング
$self->render('index'); 

#   with param
$self->render(index => %values);

# hashをjsonで
$self->render_json(%hash);