FormValidator::Lite::Upload を Mojolicious に対応させる
多分こんな感じで行けるんじゃないでしょうか。
FormValidator::Lite::Upload を Mojo::Message::Request に対応して、
--- perl5/lib/perl5/FormValidator/Lite/Upload.pm 2012-06-06 00:40:55.000000000 +0900 +++ lib/FormValidator/Lite/Upload.pm 2012-06-06 00:43:22.000000000 +0900 @@ -29,6 +29,8 @@ 'HTTPEngine'; } elsif ($q->isa('Plack::Request')) { 'PlackRequest'; + } elsif ($q->isa('Mojo::Message::Request')) { + 'MojoUpload'; } else { if ($q->can('upload')){ # duck typing # this feature is needed by HTML::Shakan or other form validation libraries
FormValidator::Lite::Upload::MojoUpload を作成。
package FormValidator::Lite::Upload::MojoUpload; use strict; use warnings; use base qw/FormValidator::Lite::Upload/; sub new { my ($class, $q, $name) = @_; my $upload = $q->upload($name); return unless $upload; bless { q => $q, name => $name, upload => $upload, }, $class; } sub size { shift->{upload}->size } sub type { shift->{upload}->headers->content_type } sub fh { shift->{upload}->asset->handle } 1;
FormValidator::Lite の新しいバージョンをチェックしたら色々と機能が追加されてて良い感じに便利でした。
[追記 06/15]
TODO: asset が Mojo::Asset::Memory の場合に fh() が多分コケるので、それに対応する。
他のライブラリとの競合を避けたブロックスコープの書き方
色々なライブラリを使用している既存のページなどで、ちょっとした処理を挟みたい場合、Prototype などと、$() が現状・将来的にも競合しないようにするには、
jQuery.noConflict()
を一般的に使いますが、もっと影響範囲を狭く、シンプルに実現する方法が以下のようにあります。
(function($) { // 2. 引数を $ で受けると // 3. このスコープでは、$ を jQuery オブジェクトとして参照できる。 $('#blah').show(); })(jQuery); // 1. 引数に jQuery オブジェクトを渡し
この方法であれば、
j$ = jQuery;
と、定義したときのように、名前空間を汚染することもありません。
無名関数とその引数で、スコープ内の値をコントロールする手法は他にも応用が利きます。
(function(window) { })(this);
など。
Mojolicious 始めました
最近ようやく Mojolicious に手を出し始めました。プロダクション環境に導入するかは、まだ未定ですが、ソースコードを読みながら試用しています。
Catalyst の原作者が DRY でなく DIY で作っているとのこと。Moose 依存などはなく、コアモジュールにしか依存していません。
ただ、perl-5.10.1 以降を必要とします。(Mojolicious 2.46)
1st リリースされてから、かなり時間が経っている割には、日本語の情報が id:perlcodesample 氏が翻訳されているドキュメント以外は少ないようなので、これからちょっとずつ、ここに得た知識を残して行ければと思います。
↓Mojolicious (2.46) の依存モジュールと、そのモジュールがコア入りした perl リリースバージョン。
B: 5.005 Carp: 5 Config: 5.00307 Cwd: 5 Data::Dumper: 5.005 Digest::MD5: 5.007003 Digest::SHA: 5.009003 Encode: 5.007003 Errno: 5.00504 Exporter: 5 ExtUtils::MakeMaker: 5 Fcntl: 5 File::Basename: 5 File::Copy: 5.002 File::Find: 5 File::Path: 5.001 File::Spec: 5.00405 File::Temp: 5.006001 FindBin: 5.00307 Getopt::Long: 5 I18N::LangTags: 5.007003 I18N::LangTags::Detect: 5.008005 IO::File: 5.00307 IO::Poll: 5.006 IO::Socket: 5.00307 IO::Socket::INET: 5.006 List::Util: 5.007003 Locale::Maketext: 5.007003 MIME::Base64: 5.007003 MIME::QuotedPrint: 5.007003 Pod::Simple::HTML: 5.009003 Pod::Simple::Search: 5.009003 POSIX: 5 re: 5.00405 Scalar::Util: 5.007003 Socket: 5 Sys::Hostname: 5 Test::Harness: 5 Test::More: 5.006002 Time::HiRes: 5.007003
環境に左右されない PERL5LIB を設定する .profile の書き方
プロジェクト用のモジュールを extlib とか専用のディレクトリを掘っていると、それを @INC に突っ込まなくてはいけないので、ブートストラップスクリプトで調整したり、開発用に .bashrc や .profile に書くと思いますが、モジュールがインストールされるディレクトリ名は、
./extlib/lib/perl5/i486-linux-gnu-thread-multi (32bit Ubuntu) ./extlib/lib/perl5/darwin-thread-multi-2level (OSX)
のように、アーキテクチャ名が入ったりするので、環境によってディレクトリ名がまちまちで書き換えないといけません。
なので、下記のように .profile を書くと環境に左右されないので便利です。
ARCHNAME=$(perl -MConfig -e 'print $Config{archname}') EXTLIB=./extlib/lib/perl5:./extlib/lib/perl5/$ARCHNAME PERL5LIB=./lib:$EXTLIB export PERL5LIB=$PERL5LIB
[追記]
lestrrat 氏がコメントで PERL5OPT を利用する方法を紹介して下さいました。ありがとうございます!
export PERL5OPT=-Mlib=extlib/lib/perl5
lib.pm も Config.pm を使って archname 以外も見て上記の方法よりもよしなにしてくれます。より、.profile がスッキリしますね。
AWS Route53 でホスト名にワイルドカードを設定する方法
下記のように
<?xml version="1.0" encoding="UTF-8"?> <ChangeResourceRecordSetsRequest xmlns="https://route53.amazonaws.com/doc/2011-05-05/"> <ChangeBatch> <Comment>This change batch creates a A record for *.example.com.</Comment> <Changes> <Change> <Action>CREATE</Action> <ResourceRecordSet> <Name>*.example.com.</Name> <Type>A</Type> <TTL>3600</TTL> <ResourceRecords> <ResourceRecord> <Value>192.168.0.1</Value> </ResourceRecord> </ResourceRecords> </ResourceRecordSet> </Change> </Changes> </ChangeBatch> </ChangeResourceRecordSetsRequest>
Plack ベースで作った Web サービスとそのシステムアーキテクチャ
本日、TVTalk という Web サービスをリリースしました。
Twitter 上で、テレビ局のハッシュタグが付いているツイートを拾って、放送中の番組情報と紐付けるという、アグリゲーターサービスです。リアルタイムにタイムラインを追うにも、放送済み番組の内容をチェックするのにも使えますので、みなさんぜひ使ってみてください。ブックマークやいいね!も良かったらお願いします!
今日は、この Web サービスの裏側のシステム構成を紹介したいと思います。
構成
Web サーバー
Web はフロントに nginx をおいて、静的ファイルは nginx にサーブさせ、拡張子がついていない URL のみバックエンドの Starman に渡しています。nginx の設定ファイルは以下のようになっています。
location / { root /home/tt/repos/tvtalk/static; } location = / { access_log /var/log/tvtalk/nginx_service/access.log; proxy_pass http://localhost:8081; proxy_set_header Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; } location ~ /stream/ { access_log /var/log/tvtalk/nginx_stream/access.log; proxy_pass http://localhost:8082; } location ~ /[^/.]*(\?|$) { access_log /var/log/tvtalk/nginx_service/access.log; proxy_pass http://localhost:8081; proxy_set_header Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; }
上記設定から分かるように、ページを生成するのは Starman プロセス、ツイートのストリームを吐き出すのは Twiggy と分けています。
放送中のタイムラインは、jquery.ev.js から long-poll で Twiggy + Tatsumaki に繋いで実現しています。
Streaming API との自動再接続処理
Twitter の Streaming API は、何時間か毎に接続が切れるので、自動再接続する処理を施しています。ツイートをアーカイブするクロウラープロセスは、普通に while ループで回し、ストリームを吐き出すプロセスは、AE::timer で接続状態をチェックしつつ、自動再接続処理をし、Twiggy::Server->register_service でハンドラ登録して、AE::cv->recv でブロックしています。
Streaming API の接続制限
Streming API を利用するにあたって注意する点は、Streaming API に複数のコネクションを張ると切られまくるので、複数のトラックワードを指定したい場合、複数のコネクションを張るのではなく、単一のコネクションを張って、on_tweet 時に、どのワードをトラックしたかアプリケーション側で判別する必要があるということです。この辺の注意事項は公式の API ドキュメントに書いてあります。
コネクションを張る前に、Tatsumaki::MessageQueue->instance でトラックワード数ぶんのキューを用意してあります。
現時点では、タイムラインは jquery.ev + long-poll で実装していますが、そのうち、DUI.js + multipart/mixed に置き換えたいと思っています。
my と state のパフォーマンス比較
最近の Linux ディストリビューションの perl は 5.10 以上になってきた感じなので、最近は state など新しい機能を使うようになってきました。
そこでちょっと気になったので state のパフォーマンスを計測してみました。ベンチマークコードは以下。動作が全く同じ訳ではないですが。
use 5.010; use strict; use warnings; use Benchmark ':all'; cmpthese(timethese(-1, { 'my' => \&_my, 'state' => \&_state, })); { my $v = 1; sub _my { $v } } sub _state { state $v = 1; }
結果は以下のように、ベンチマークを実行する毎に違う結果が出ました。実行環境は Ubuntu 10.04 の perl 5.10.1 です。
Benchmark: running my, state for at least 1 CPU seconds... my: 1 wallclock secs ( 1.08 usr + 0.00 sys = 1.08 CPU) @ 8972488.89/s (n=9690288) state: 4 wallclock secs ( 2.14 usr + 0.00 sys = 2.14 CPU) @ 10718488.79/s (n=22937566) Rate my state my 8972489/s -- -16% state 10718489/s 19% -- Benchmark: running my, state for at least 1 CPU seconds... my: -2 wallclock secs ( 1.15 usr + 0.00 sys = 1.15 CPU) @ 9459979.13/s (n=10878976) state: 2 wallclock secs ( 1.02 usr + 0.00 sys = 1.02 CPU) @ 7710116.67/s (n=7864319) Rate state my state 7710117/s -- -18% my 9459979/s 23% -- Benchmark: running my, state for at least 1 CPU seconds... my: 2 wallclock secs ( 1.22 usr + 0.00 sys = 1.22 CPU) @ 9024628.69/s (n=11010047) state: 2 wallclock secs ( 1.04 usr + 0.00 sys = 1.04 CPU) @ 9830399.04/s (n=10223615) Rate my state my 9024629/s -- -8% state 9830399/s 9% --