抱負なうシステム紹介
Bloged at : 2011-01-03 18:19:53
あけましておめでとうございます。
抱負なう というサービスを2011年新年早々作ってみました。ソースはgithubで公開しています。
フレームワーク等は利用せず、CPANモジュールを組み合わせてスクラッチから作ってみました。
せっかくですので、実装のちょっとしたポイントを一つ紹介します。
コントローラではWebに関することのみの処理を行い、それ以外の処理はすべてAPIモジュールに投げる仕組みを導入しています。
利用例) ソース このページ例
上記はコントローラの関数なのですが、処理自体は User APIになげて処理を行い、取得した API Responseオブジェクトの状態をみてブラウザへの表示を切り替えています。
APIソース
profilesで検証ルールを記載しています。
うけとったデータを検証し、それに基づいてDBにアクセスしデータを取得し、API Resultオブジェクトを返しています。
この実装の利点は、複数のクライアント(PC/スマートフォン/モバイル/テストスクリプト等)対応により柔軟に対応できるということです。特に重要な点は、APIはWebの依存から切り離されており、どこからでも簡単に利用できるということです。
今後はこのAPI部分の実装を標準化し、たとえばtwitter OAuthの認証部分とかを複数のサービスで使い回せるようなミドルウェアー的なのを用意していければ良いなぁと思います。
抱負なう というサービスを2011年新年早々作ってみました。ソースはgithubで公開しています。
システムについて
フレームワーク等は利用せず、CPANモジュールを組み合わせてスクラッチから作ってみました。
システムポイント紹介
せっかくですので、実装のちょっとしたポイントを一つ紹介します。
コントローラとAPIの分離
コントローラではWebに関することのみの処理を行い、それ以外の処理はすべてAPIモジュールに投げる仕組みを導入しています。
利用例) ソース このページ例
sub houfu {
my ( $self, $c ,$args ) = @_;
$c->template('user/houfu.tx');
my $api_res = $c->api('User')->lookup_houfu( $args );
if($api_res->has_error){
return $c->handle_not_found();
}
else {
$c->append_stash( $api_res->stash );
}
}
上記はコントローラの関数なのですが、処理自体は User APIになげて処理を行い、取得した API Responseオブジェクトの状態をみてブラウザへの表示を切り替えています。
APIソース
sub profiles {
+{
lookup => {
required => [qw/screen_name/],
},
lookup_houfu => {
required => [qw/houfu_code screen_name/],
}
}
}
profilesで検証ルールを記載しています。
sub lookup_houfu {
my $self = shift;
my $args = shift;
my $v_res = $self->validate( $args );
my $stash = {};
if(!$v_res->has_error){
my $v = $v_res->valid;
my $user_hash = $self->db->lookup_member( $v->{screen_name} )
or return $self->create_error_set($v_res,'user_not_found');
my $houfu_hash = $self->db->lookup_houfu( $v->{screen_name},$v->{houfu_code})
or return $self->create_error_set($v_res,'houfu_not_found');
$stash->{user_hash} = $user_hash;
$stash->{houfu_hash} = $houfu_hash;
}
return $self->create_result_set( { v_res => $v_res , stash => $stash } );
}
うけとったデータを検証し、それに基づいてDBにアクセスしデータを取得し、API Resultオブジェクトを返しています。
この実装の利点は、複数のクライアント(PC/スマートフォン/モバイル/テストスクリプト等)対応により柔軟に対応できるということです。特に重要な点は、APIはWebの依存から切り離されており、どこからでも簡単に利用できるということです。
今後はこのAPI部分の実装を標準化し、たとえばtwitter OAuthの認証部分とかを複数のサービスで使い回せるようなミドルウェアー的なのを用意していければ良いなぁと思います。
まとめ
XslateのMacroをXslateを利用して作る
Bloged at : 2010-11-11 18:03:07
XslateはMacroに対応しているのですが、INCLUDE先では利用できないという制約があります。
その対応が以下実際のコード。 INCLUDE先でも利用できるようになった際の移行を考慮して実装しています。
kazeburo++
こんな感じで利用。
実際には、MACRO じゃなくて moduleとして擬似的に実装なんだけどね
その対応が以下実際のコード。 INCLUDE先でも利用できるようになった際の移行を考慮して実装しています。
kazeburo++
package Golgo::Admin::View::MacroSection;
use strict;
use warnings;
use base qw/Exporter/;
use Data::Section::Simple;
use Text::Xslate;
our @EXPORT = qw(
paginate
);
my $XSLATE;
__PACKAGE__->create_xslate();
sub paginate {
my $args = {};
( $args->{pager} , $args->{my_uri} ) = @_;
render('paginate', $args );
}
sub create_xslate {
my $reader = Data::Section::Simple->new(__PACKAGE__);
my $templates = $reader->get_data_section;
my $tx = Text::Xslate->new(
module => [ 'Text::Xslate::Bridge::TT2Like'],
syntax => 'TTerse',
path => [ $templates ]
);
$XSLATE = $tx;
}
sub render {
my ($name , $args ) =@_;
Text::Xslate::Util::mark_raw($XSLATE->render($name,$args));
}
1;
__DATA__
@@ paginate
[% IF pager %]
[% CALL pager.uri(my_uri) %]
[% IF pager.last_page != pager.first_page %]
<div class="pager">
<span>全件数 [% pager.total_entries %]</span>
[%- IF pager.previous_page %]<a href="[% pager.build_uri(pager.previous_page) %]" class="prev">前</a>[% END %]
[%- FOR p IN pager.pages_in_navigation(10) %]
[%- IF p == pager.current_page %]<span>[% p %]</span>[% ELSE %]<a href="[% pager.build_uri(p) %]" class="number">[% p %]</a>[% END -%]
[%- END %]
[%- IF pager.next_page %]<a href="[% pager.build_uri(pager.next_page) %]" class="next">次</a>[% END %]
</div><!-- /pager -->
[% END # over 1 page %]
[% END # has pager %]
こんな感じで利用。
my $tx = Text::Xslate->new(
module => [ 'Text::Xslate::Bridge::TT2Like','Golgo::Admin::View::MacroSection'],
syntax => 'TTerse',
);
[% paginate(pager, c.req.uri ) %]
実際には、MACRO じゃなくて moduleとして擬似的に実装なんだけどね
KEY更新によるキャッシュ破棄実装
Bloged at : 2010-09-15 15:23:49
KEY更新によるキャッシュ破棄とは
一般的にCacheを破棄する際には以下のように明示的に$keyを指定して削除処理を行う。
my $key = 'user_id:3'; $cahce->delete($key);それに対しKEY更新によるキャッシュ破棄では
my $key = "user_id:$cache_no:3";
のようにcacheのkeyに$cache_noというのを埋め込み、$cache_noを変更することによりキャッシュの疑似破棄をおこなう。 $cache_no の値は、データの更新があった際にインクリメントするようにする。
長所
- キャッシュの破棄を明示的に記載する必要がない(奇麗なソースが書ける)
- 複数のキャッシュをまとめて破棄することができる(Cache::Memcached::ManagedのGroup的なこと)
- キャッシュKEYの一元管理がしやすい
短所
とくに短所は無いのだがあえて記載すると
- 値を更新時に$cache_noをインクリメントする必要がある(timestampを使うとかもアリかもしれない)
- cache_noをどこかに保持する必要がある
サンプルコード
sub cacheable() {
my($code, $key, $exp) = @_;
$exp ||= 60 * 5;
my $cache = OreSama::Cache->instance;
if (defined(my $data = $cache->get($key))) {
return $data;
}
my $data = $code->();
$cache->set($key, $data, $exp);
$data;
}
# SETするコード例 (フレンド一覧取得)
my $key = join(":",'hoge',$user_cache_id,$user_id);
my $res = cachable( $key , sub {
my @friend_objs= DB::Friend->search( {user_id => $user_id} );
return \@friend_objs;
}
);
#破棄するコード例(ユーザ情報更新)
$user_obj->user_cache_id( $user_obj->user_cache_id + 1);
$user_obj->save;
まとめ
サービスにもよるとおもいますが、ありだと思います。
