Yacafiの動作を読み解く 2
Yacafiの動作を読みとく より引き続く。
79 〜 95行目
_pack
79 my $yacafi = _read_file( $INC{'Yacafi.pm'} );
_read_fileについては、143〜147に実装が書かれてる。
143 sub _read_file { 144 my $file = shift; 145 open my $fh, '<', $file or die $file . ': ' . $!; 146 do { local $/; <$fh> }; 147 }
%INCには、読み込まれたファイル名とフルパスがkeyとvalueの組み合わせでつっこまれてる。Yacafi.pmのファイルパスを持ってきて、Yacafi.pmの中身を丸ごと取得する。$/はslurping(丸呑み)なモードのこと。79行目の$yacafiには、Yacafi.pm内のソースコードが丸々収まっていることになる。
次に、Yacafiのソースコードから EXTEND 〜 EXTEND .. END の部分を対象にして正規表現のmatchを行う。
81 # extends 82 $yacafi =~ s!### EXTEND (\w+)\n(.+?)### EXTEND \1 END\n! 83 $args{extends}->{$1} ? $2 : '' 84 !esg;
実態は、
$yacafi =~ s!〜!〜!esg;
84行目のesgは、
- e:式の右側の評価を行なう
- s:ワイルドカードのドット( . )が改行にもマッチするようにする
- g:繰り返し置換
特にYacafi.pmを弄っていない場合、importで呼び出された時に渡されるハッシュのextendsがtemplateをkeyの一つとして保有していれば、2番目にマッチした値が 382 〜 607行目の ###EXTEND 〜 ### EXTEND template END と置き換わる。
一見して分かりにくいこと間違いないけれど、
$yacafi =~ /### EXTEND (\w+)\n(.+?)### EXTEND \1 END\n/; $yacafi = $args{extends}->{$1} ? $2 : '';
と同義。
とりあえずここまで。82〜84行目の把握にかなり時間食った気がする。
Yacafiの動作を読み解く
軽量フレームワークの話題が殆ど上がらなくなった感もあり、とってつけたような記事に思われない丁度いいタイミングかなーとか思ったのでやってみることにした。
wget http://svn.coderepos.org/share/lang/perl/Yacafi/trunk/lib/Yacafi.pm
以前Yacafiを使った掲示板を書いてた事があったけど、当時は細かい挙動とか全然分かってなかったから、方々からのコピペで更に理解不可能になり気味だった。理解を正確に行って、使えるような代物にするのが目的。
42 〜 75行目
- import
45 my $caller = caller; 46 $CURRENT_CLASS = $args{current_class} || $caller;
引数にcurrent_classをkeyとした値を渡すと、それが基本classとして扱われる。そうでなければmainが基本classになる。
実際に書くとすると、
use Yacafi current_class => 'hogehoge';
という具合。
- pre config
49 if ( exists $args{extends} 50 && exists $args{extends}->{template} 51 && ref( $args{extends}->{template}->{params} ) eq 'HASH' ) 52 { 53 while ( my ( $key, $val ) 54 = each %{ $args{extends}->{template}->{params} } ) 55 { 56 $TEMPLATE_PARAMS->{$key} = $val; 57 } 58 }
指定のハッシュが渡されていれば、templateのパラメータを指定。
実際に書くと、
use Yacafi current_class => 'hogehoge', extends => { template => { params => { tag_start => '<tmt', tag_end => 'tmt>', }, }, };
こんな具合。params内の指定は24〜35行目のkey。
60 〜 61行目
pack
60 # create a pack file$ 61 _pack(%args) if @ARGV && $ARGV[0] eq '--pack';$
perl hogehoge.cgi --pack > hogehoge.cgi
な感じで、Yacafi.pm + index.cgiなファイルを作るコマンドのための行。_pack自体は77〜141行に。
63 〜 72行目
function export
63 # functions export 64 no strict 'refs'; 65 for my $name (qw/ dispatch query controller model view redirect filter / ) 66 { 67 *{ $caller . '::' . $name } = \&{$name}; 68 } 69 if ( exists $args{extends} && exists $args{extends}->{template} ) { 70 *{ $caller . '::view_template' } = \&view_template_nocompile; 71 } 72
呼び出し元のスクリプトへ、メソッドを定義する。
- dispatch
- query
- controller
- model
- view
- redirect
- filter
が使えるという具合に。
なお、
use Yacafi extends => { template => { params => .... } };
のようにtemplateを定義しておくと、view_templateメソッドで 407 〜 414行目のview_template_no_compileが呼び出されるようになる。
次は_pack辺りでも触る。
moxyがエラーで転けてた
最新のmoxyを実行すると転けるのでソース辿ってみたら、最後のcommitが原因だった。
diff --git a/lib/Moxy.pm b/lib/Moxy.pm index e788ffa..adc1fb6 100644 --- a/lib/Moxy.pm +++ b/lib/Moxy.pm @@ -276,6 +276,7 @@ sub _make_response { (my $url = $req->uri->path_query) =~ s!^/!!; - $url = uf_uristr($url); + $url = uf_uristr( uri_unescape $url); if ($url) { # do proxy my $res = $self->_do_request(
URIはuri_escapeされた文字列を使うと、正規表現に当てはまらずURIオブジェクトがうまく作成されないらしい。
今回の$req->uri->path_queryに含まれるurlはuri_escapeされていたため、escapeされた文字列をベースにuf_uristrが実行されてescape状態でのURLとなり、結果正規表現から外れたためにURIオブジェクトがうまく作成されず、hostが取得できていなかったためエラーとなっていた模様。
WassrにiTunesで再生してるタイトルを送信する
AppleScriptで何気なしに書いてみた。再生が始まったとたんに送信する形のほうが楽な気もするけど、それも微妙だとおもったので手動に。
リファレンスの類いがあまり落ちてないから複雑なのは書きづらいけど、ちょっとしたやつなら割と書きやすい感じ。Macでしか動かないから応用しようがないのも事実。
AirStationの設定をValueDomainに定期的につっこむ
AirStation WHR-HP-GNという前提での、ValueDomain定期更新スクリプト。各AirStationの型番毎にqueryが判明すれば汎用化できるんだろうけど、そこまでやる意味があるか分からないので。
AirStationにもAPI備わってればいいのになぁとはよくおもう。
#!/usr/local/bin/perl use strict; use warnings; use Regexp::Common qw/net/; use Encode qw(decode encode); use WWW::Mechanize; use URI; use Net::DNS::ValueDomain::DDNS; my $user = <id>; my $pass = <pass>; my $url = "http://192.168.11.1/cgi-bin/cgi?"; my $ip = ''; my $airstation = URI->new($url); my $mech = WWW::Mechanize->new( autocheck => 1 ); $mech->credentials($user,$pass); $airstation->query_form( req => 'twz' ); $mech->get( $airstation->as_string ); $airstation->query_form( req => 'tfr', id => '41' ); $mech->get( $airstation->as_string ); $airstation->query_form( req => 'frm', frm => 'info.html' ); $mech->get( $airstation->as_string ); my $content = $mech->content; my @content = split(/\n/, $content); foreach my $line ( @content ) { $line =~ s/\s//g; if ( $ip eq '' && $line =~ /$RE{net}{IPv4}/o ) { $ip = encode('utf-8', $line); } } $ip =~ s/<td>//; $airstation->query_form( req => 'twz', frm => 'logout.html' ); $mech->get( $airstation->as_string ); my $ddns = Net::DNS::ValueDomain::DDNS->new; $ddns->update( domain => 'haoyayoi.net', password => $ddns_pass, host => '*', ip => $ip, );
簡単な掲示板というかフレームワークの設計をはじめてみた
色々な事情で機能縮小した掲示板を、縮小時の手間と変わらないままで、かつての機能を維持した上で、ユーザ情報をメッセージから分離させて管理できるようにする、という、ちょっと無茶っぽい条件を成立させるスクリプトの作成を勉強がてら行ってる。…使うかどうかは微妙だけど。
現状
- 元々の掲示板
- 現在の掲示板
- ソースについては、現在編集ができる立場にいるためか伺い知る事はできる。とりあえず、テーブル設計や諸処のロジック設計から、拡張した後の手間が大きく増えるのだけは分かっている。
掲示板を一から設計したことがないのもあって、とりあえずアプリのベースを作り、そこに掲示板として必要な機能を突っ込んでいくことにした。PSYRENなる同類アプリも書いてたけど、本質的ではない部分で色々悩んだ挙句一旦停止した。
cgiとして処理をするしかない環境のため、コストパフォーマンスのいいモジュールということで cgi-extlib-perl のモジュールに依存する形。
かつsql片を直使うためにDBIx::Skinnyをいれてみた。DBI使うほうが速度早いのは確かなんだけど、あえて設計面を採ってみた。
archnameのこと
私製アプリを作ってる時に、cpanmを使ってモジュールを非root権限にて入れておく作業をたまに行うのだけれど、lib以下に収まる際の
などのサブディレクトリについて余り考えたことがなかったので、確認してみた。
これはオペレーションシステムのアーキテクチャ依存のモジュールが収まる場所であり、OSXのシステムに依存するものであれば、darwin-2level以下にモジュールがインストールされるようになっている。
現在使用しているアーキテクチャ名が何かを確認したい場合は、
#!/usr/local/bin/perl use strict; use warnings; use Config; print "$Config{archname}\n";
とかで。
アーキテクチャ依存モジュールの呼び出しを抽象化したい場合は、
BEGIN { unshift @INC, "/path/to/perl5/$Config{archname}"; }
ということになるけれど、各アーキテクチャ別にインストール作業しない場合は余り意味がないので、Configを使わずに直接パスを埋め込んだ方が早い。