« 2006年03月 | メイン | 2006年05月 »

2006年04月29日

[Femo]公開ページでGoogle AnalyticsとFeedBurner

公開ページでGoogle AnalyticsとFeedBurnerなどのサービスが使えるようになりました。

Google Analyticsは「UA-XXXXXX-XX」のアカウントIDを入力する形で導入できます。
また、公開ページのFeedのURL(http://femo.jp/ID/rss)から指定されたURLへ301 Redirectする機能をつけました。これによってスムーズにFeedBurnerやRSS広告社などのサービスを利用できるようになると思います。

アクセス統計やFeedのカスタマイズなどアプリケーション側での対応がなかなか追いつかないところだと思います。
こういったサービスを積極的に利用できるようにして、アプリケーション本来の機能に特化していくのもいいんじゃないかと思う。

2006年04月28日

Catalystでユーザページ

Femoでのユーザページのコード。
Femo::Controller::Userというコントローラで実装している。
同じ正規表現が繰り返しているのでもうすこしなんとかならないかと

package Femo::Controller::User;

use strict;
use warnings;
use base 'Catalyst::Controller';

sub auto : Private {
    my ( $self, $c ) = @_;
    $c->stash->{view} = $c->model('Users')->retrieve(publish_id=>$c->req->snippets->[0]);
    if( !$c->stash->{view} ) {
        $c->res->status('404');
        $c->res->output('Not Found');
        $c->req->action(undef);
        return;
    }
    $c->stash->{template} = 'user.tt';
    $c->req->params->{page} ||= 1;
}

#index
sub user : Regex('^([a-z0-9_]{3,20})$') {
    my ( $self, $c ) = @_;
    $c->stash->{view} = $c->model('Entries')->...
}

sub rss : Regex('^([a-z0-9_]{3,20})/rss$') {
    my ( $self, $c ) = @_;
    $c->forward('user');
    #rss
}

sub tag : Regex('^([a-z0-9_]{3,20})/tag/(.+)$') {
    my ( $self, $c ) = @_;
}

#あと省略


ユーザIDの正規表現「([a-z0-9_]{3,20})」が繰り返すのがあれだなぁ。

__PACKAGE__->config->{namespace} = qr/^([a-z0-9_]{3,20})/;

とか設定して、PathとかLocalRegexが使えたらいいかも。ただ、IDの抽出を一度autoで書けば動いたのはよかった。

2006年04月26日

[Femo]公開メモ機能追加しました。

書いたメモを公開できる機能をFemoに追加しました。
TypekeyはURLとして使いにくいので、新たに公開用のIDを設定のところで決める必要があります。

URLは、

http://femo.jp/demo/

のようなURLになります。僕のメモはここ

femo-publish.png

公開ページには、タグ毎、投稿日付での一覧機能の他、tagcloudの表示、RSS2.0のFeedなどがあります。ただ、Ajaxとかは(いまのところ)全くないです。
ページ構成や画面のデザインにid:typester氏のCLONやid:nekokak氏のNeko::Memoを参考にしました。thxです。

Blogに書くところまでもないちょっとしたことを記しておくのに、CLONやNeko::Memoみたいなアプリケーションが自分でも欲しいって言うのはあって、何か新しく作ろうかと思っていたんだけど、ユーザの方からのご意見などもあり、Femoに追加実装してみた。
こうすると自分だけのメモと公開してもいいようなメモを同時に管理できるのもいいところっすな。
これに「出社しました」と書くのが次の目標w

今後の予定しては、Text::HatenaのInline対応(問題はCache)、公開ページにadsenseとかfeedburnerとかが使えるようにしたり、コメント機能とかつけたり、細かい使い勝手をあげていく予定。

FormValidator::Simple::Plugin::DateTime::Format

MTのエントリーフォームのようにinput="text"に日付・時間を入力する場合のチェックに。
DateTime::Format::*とDateTime::Format::Strptimeを利用するメソッドの2つ

use FormValidator::Simple qw/DateTime::Format/;
use CGI;

my $q = CGI->new;
$q->param( datetime1 => '2006-04-26T19:09:21+0900' );
$q->param( datetime2 => '2006-04-26 19:09:21' );

my $result = FormValidator::Simple->check( $q => [
    datetime1 => [ [ 'DATETIME_STRPTIME', '%Y-%m-%dT%T%z' ] ],
    datetime2 => [ [ qw/DATETIME_FORMAT MySQL/ ] ],
] );


DateTime::Format::Strptimeはver. 1.0700にしないとtime_zoneがUTCになるバグがあります。

ソースは追記に

package FormValidator::Simple::Plugin::DateTime::Format;

use strict;
use warnings;
use FormValidator::Simple::Exception;
use FormValidator::Simple::Constants;
use DateTime::Format::Strptime;
use UNIVERSAL::require;

sub DATETIME_FORMAT {
    my ( $self, $params, $args ) = @_;
    my $date   = $params->[0];
    my $format = $args->[0];
    FormValidator::Simple::Exception->throw(
        qq/Validation DATETIME_FORMAT needs a format argument./)
      unless $format;

    my $module;
    if ( ref $format ) {
        $module = $format;
    }
    else {
        $module = "DateTime::Format::$format";
        $module->require
          or FormValidator::Simple::Exception->throw(
            qq/Validation DATETIME_FORMAT: failed to require $module. "$@"/ );
    }
    my $dt;
    eval {
        $dt = $module->parse_datetime($date);
    };
    my $result = $dt ? TRUE : FALSE;

    if ( $dt && $self->options->{time_zone} ) {
        $dt->set_time_zone( $self->options->{time_zone} );
    }
    return ($result, $dt);
}

sub DATETIME_STRPTIME {
    my ( $self, $params, $args ) = @_;
    my $date   = $params->[0];
    my $format = $args->[0];
    FormValidator::Simple::Exception->throw(
        qq/Validation DATETIME_STRPTIME needs a format argument./)
      unless $format;

    my $dt;
    eval{
        my $strp = DateTime::Format::Strptime->new(
            pattern => $format,
            on_error => 'croak'
        );
        $dt = $strp->parse_datetime($date);
    };

    my $result = $dt ? TRUE : FALSE;

    if ( $dt && $self->options->{time_zone} ) {
        $dt->set_time_zone( $self->options->{time_zone} );
    }
    return ($result, $dt);
}

1;

2006年04月25日

ASCII24、RBBTODAY、ImpressのEntryFullText

CEEK.JP NEWSのRSSの補完につかえる、PlaggerのEntryFullTextファイル。

ascii24_com.yaml

author: kazeburo
custom_feed_handle: http://ascii24\.com/news/$
custom_feed_follow_link: /news/i/\w+/article/\d{4}/\d\d/\d\d/\d{6|-\d{3}\.html
handle: http://ascii24\.com/news/i/\w+/article/\d{4}/\d\d/\d\d/\d{6|-\d{3}\.html
extract: .*?<H1>(.*?)</H1>.*?.*?<SMALL>.*?(\d{4}年\d+月\d+日).*?</SMALL>.*?(.*?)
extract_capture: title date body
extract_date_format: %Y年%m月%d日


rbbtoday_com.yaml

# upgrade http://www.rbbtoday.com/rss/rbb.rdf
author: kazeburo
handle: http://www\.rbbtoday\.com/news
extract: <h1 class="ttl02">(.*?)</h1>.*?<tr><td><img src="/shared/img/spacer.gif" width="1" height="\d\d"></td></tr>.*?<tr>.*?<td class="f14120" valign="top">(.*?)</tr>.*?<tr><td><img src="/shared/img/spacer.gif" width="1" height="10"></td></tr>
extract_capture: title body


impress.yaml Impress全般いけます。

author: kazeburo
handle: http://\w+\.watch\.impress\.co\.jp/
extract: (.*)
extract_capture: body


あと、itmedia.yamlは

handle: http://(?:www|plusd)\.itmedia\.co\.jp/\w+/articles

の方が幸せになれる。

2006年04月20日

FormValidator::Simple::Plugin::InArray

PHPにはあるらしいin_arrayFormValidator::Simple

use FormValidator::Simple qw/InArray/;
my $result = FormValidator::Simple->check( $q => [
    param => [qw/IN_ARRAY foo bar baz/]
]);

paramの値がlistの中にあれば真。
NOT_IN_ARRAYも使える。

実装はList::Utilのfirstを使っているだけ。listの要素数が多い場合は実装を変えた方がいいだろうな。

package FormValidator::Simple::Plugin::InArray;

use strict;
use warnings;
use FormValidator::Simple::Exception;
use FormValidator::Simple::Constants;
use List::Util qw/first/;

our $VERSION = '0.01';

sub IN_ARRAY {
    my ( $class, $params, $args ) = @_;
    my $data = $params->[0];

    return ( first { defined $_ && $_ eq $data } @$args ) ? TRUE : FALSE;
}

1;

2006年04月19日

W41Sにしました

携帯の機種変更をしました。
W21SからW41Sになりました。

色はホワイト、微妙にピンクっぽい。
W21Sに比べると微妙に小さくなってますね。

ジョグダイアルがなくなりましたが、やはり少し使いにくい感じがします。
メニューの移動時にジョグをまわしてクリックするという指を離さなくてもいい作業が、
キーの真ん中をクリックするために指を離す必要がでてくる。これは頂けない。

まぁ、電池の持ちが悪くなっていたので仕方ない。
しばらく使えばなれるでしょう。

FormValidator::SimpleでBETWEENやEQUAL_TOに0を使いたい

FormValidator::Simple(v0.14)でBETWEENやEQUAL_TOに0が使えない

my $result = FormValidator::Simple->check( $q => [
    age => [ ['BETWEEN', 0, 3] ],
] );

はerrorになります

FormValidator::Simple::Validatorで

    unless ( defined $start && $start =~ /^\d+$/ && defined $end && $end =~ /^\d+$/ ) {
        FormValidator::Simple::Exception->throw(
        qq/Validation BETWEEN needs two numeric arguments./
        );
    }

と、definedを入れてみてはどうだろう。

2006年04月17日

TypeKeyのIDに使える文字

Femoでメモを

http://femo.jp/ID/

のURLで公開できるようにと考えているのですが、

TypeKeyのIDでひとつ気づいたことが
TypeKeyの登録ページでIDの説明に

Can contain up to 25 characters and must not contain spaces.

と書いてあるのですが、マジに制約がこれだけの模様。
「/」「@」とか、全角文字もIDとして使えるし、http://~~やメールアドレスのようなIDも登録ができるみたいです。

う〜ん。ちょっと困ったぞ。
回避策としては、公開用IDを別に作成することかなぁ。

__追記__
id:vkgtaroさんが詳しく調べています
Profile Pageが上書き可能ってちょっとなぁ。

2006年04月14日

URI::Fetch::Decode

Plaggerのcodeを参考(ほとんどコピー)にした。
URI::FetchでとってきたコンテンツをUTF8 flaggedにする。

use URI::Fetch::Decode;
my $fetch = URI::Fetch::Decode->fetch("http://www.yahoo.co.jp/");
my $utf8 = $fetch->decode_content;


あんまりテストしてないので注意。

package URI::Fetch::Decode;

use strict;
use warnings;
use base qw(URI::Fetch);

our $VERSION = '0.01';

sub fetch{
    my $class = shift;
    my $fetch = $class->SUPER::fetch(@_);
    return unless $fetch;
    bless $fetch, "URI::Fetch::Decode::Response";
}

1;

package URI::Fetch::Decode::Response;

use strict;
use warnings;
use base qw(URI::Fetch::Response);
use Encode ();

our $Detector;

BEGIN {
    if ( eval { require Encode::Detect::Detector; 1 } ) {
        $Detector = sub { Encode::Detect::Detector::detect($_[0]) };
    } else {
        require Encode::Guess;
        $Detector = sub {
            my @guess = qw(utf-8 euc-jp shift_jis); # xxx japanese only?
            eval { Encode::Guess::guess_encoding($_[0], @guess)->name };
        };
    }
}


sub decode_content{
    my $res = shift;
    my $content = $res->content;

    return unless $content;

    # 1) get charset from HTTP Content-Type header
    my $charset = ($res->http_response->content_type =~ /charset=([\w\-]+)/)[0];

    # 2) if there's not, try META tag
    $charset ||= ( $content =~ m!<meta http-equiv="Content-Type" content=".*charset=([\w\-]+)"!i )[0];

    # 3) if there's not still, try Detector/Guess
    $charset ||= $Detector->($content);

    # 4) falls back to UTF-8
    $charset ||= 'utf-8';

    my $decoded = eval { Encode::decode($charset, $content) };

    if ($@ && $@ =~ /Unknown encoding/) {
        $charset = $Detector->($content) || 'utf-8';
        $decoded = Encode::decode($charset, $content);
    }

    $decoded;
}

1;

近況

2週間ぐらいたって、そろそろrebootしなければと思い始めた。

ここんところ何をやっていたのかと言えば、FF12と家事。
あとはかなりのんびり。

FF12はまだクリアしてない。
リドルアナ大瀑布前。

レベルは主力組が60強、控え組が30弱
モブとかお金稼ぎとか移動に時間がかかっています。
そろそろクリアに向かわないと。

明日はShibuya.jsのTechtalkに参加で東京です。

2006年04月13日

CentOS 4.2 -> 4.3 でトラブル

CentOS 4.2 -> 4.3 とアップデートしたら、bindが起動しないトラブルと、
ターミナルでTABキーを押すとlogoutするという問題発生。シェルが落ちるのかな。

両方とも原因がよくわからないのだが、
bindはrpm -eで関連するpackageを削除、設定ファイルなどを退避させて再インストール
ターミナルの問題は、bashをrpm -Uvh --forceで再インストール
でなおりました。

なんか前回のアップデート時はMySQLが起動しなくなったりとちょっと鬼門です。

2006年04月07日

Femo 終了タグ付きメモを隠すことができるようになりました

Femoに終了タグ付きメモを隠すことができる機能を追加しました。

femo_hide_finish.png

終了タグが含まれる場合にそのメモを一覧から隠すことができるようになりました。
現在のところ終了タグは「終了」「完了」「finish」「done」の4つです。
設定ページやメモ一覧のソート条件のところから表示・非表示を切り替えできます。

なお、この機能はタグや日付で絞り込んでいる時のみ有効です。


タグの検索にはMySQLの全文検索を使いました。Sennaは使ってないです。
何かタグで検索している時でないとこの機能が使えないのは、

-完了 -終了 -finish -done

という条件だけでは検索ができないからです。
何か一つでもプラスの条件がないと。

全部に共通した適当な文字列を追加するかなぁ。

2006年04月05日

FemoでDB更新ミスしました。

大変申し訳ございません。
Femoでデータベースの更新時にミスをしてしまいました。

すべてのメモの更新時間が、「2006-04-05 23:44:09」前後になってしまっております。
他のデータには影響はありません。

完全な僕の不注意によるものです。
ユーザのみなさま、大変申し訳ございません。
以後ないように気をつけます。

Template::Plugin::FillInForm

sticky formの方法としては、Framework側で行う方法と、TT側で行う方法があると思うのですが、
後者の方ってあまり使っている人いないんですかね。

Template::Plugin::FillInFormを使うときは、Templateに

[% USE FillInForm %]
[% FILTER fillinform fobject => c.req %]
<form method="post" action="/foo">
<input ...>
</form>
[% END %]

と書ける。

まぁ、問題はFillInFormのタイミングで
Catalyst::Plugin::FillinFormのfinalizeは条件的に使えないし。
endに入れてしまって毎度実行という手もあるけど、HTML::Parserが不必要な時にも動くのは微妙。

この点、TTでの処理はポイントを絞れているので無駄がないと思っているのだが、どうだろう。

2006年04月02日

SEN_INDEX_DELIMITED

id:tasukuchanさんにSennaのSEN_INDEX_DELIMITEDを実装していただいたので、試す。

MySQLを用意するのが面倒なので、Senna.pmで試すことにする。
XSというかCをほとんど知らないので間違っていたら指摘してくださいです。

まずpatch

  • Senna.xsにSEN_INDEX_DELIMITEDとmysql風queryメソッド追加
  • SEN_INDEX_DELIMITEDとqueryの簡易テストのt/04-delim.tを追加
  • NGRAM onlyのcreateはエラーになるようなので修正

ファイルは以下
http://nomadscafe.jp/tmp/Senna-0.11.patch

queryのテストとして、前回と同じデータを入れて以下を作って実行
スクリプトはエレガントじゃないけど気にしない。

Senna.pmをいちいちインストールは面倒なので、このファイルをSenna.pmのdistディレクトリにいれて実行します。

#!/usr/bin/perl

use strict;
use warnings;
use FindBin;
use lib ($FindBin::Bin,"$FindBin::Bin/lib","$FindBin::Bin/blib/lib","$FindBin::Bin/blib/arch");
use Senna::Index qw(:all);
use Text::Tags::Parser;
use Encode;
use File::Spec;

my @tags = (
q/task femo/, #1
q/femo mail task/, #2
q/task femo/, #3
q/task plagger/, #4
q/task 2006-03-27 "perl monger"/, #5
q/task 終了/, #6
q/feedback femo task dev/, #7
q/task feeder dev/, #7
q/femo task dev/ #8
);

my $index_name = 'test-delimited.db';
my $path       = File::Spec->catfile($index_name);
my $index      = Senna::Index->create($path, SEN_VARCHAR_KEY, SEN_INDEX_DELIMITED);

my $i = 1;
for my $tags (@tags){
    my @unpacks;
    $tags = Encode::decode_utf8($tags);
    for my $tag (Text::Tags::Parser->new->parse_tags($tags)) {
        push @unpacks, &normalize($tag);
    }
    $index->put($i, join(' ', @unpacks));
    $i++;
}

for my $q (qw/task femo 終了/,"perl monger",{
    plus => [qw/task femo/],
    minus => [qw/終了/]
}){
    print &querize($q),",\n";
    my $cur = $index->query(&querize($q));
    while (my $r = $cur->next) {
        printf("%s\n", $r->key);
    }
}

sub querize{
    my $obj = shift;
    return "*E-1 ".&normalize($obj) unless ref $obj;
    $obj->{plus} ||= [];
    $obj->{minus} ||= [];
    my $plus = join " " , map { "+" . &normalize($_)  } @{$obj->{plus}};
    my $minus = join " " , map { "-" . &normalize($_)  } @{$obj->{minus}};
    return join " " , "*E-1", $plus, $minus;
}

sub normalize{
    my $str = shift;
    $str = Encode::decode_utf8($str) unless utf8::is_utf8 $str;
    return join 'x', map { sprintf("%02x",$_) } 
            unpack("U*", $str);
}


*E-1は完全一致検索のみを行うというプラグマ。詳しくはここ
結局unpackしているのは、"perl monger"の様に間にスペースを含むものもあるからです。

実行すると、

+74x61x73x6b +66x65x6dx6f -7d42x4e86
1
2
3
7
9

っていうのが最後にでます。
これは、「task」と「femo」を含んで「終了」を含まない、という条件で検索した結果で、前回と同じになってます。

ってことで、SEN_INDEX_DELIMITEDはイケテそうです。
問題はMySQLの移行かぁ。

2006年04月01日

YAPC && Hackathon && return $HOME

今日は寝すぎた。。

3日間すばらしい体験でした。

いままでNet Onlyだったg:subtechやid:nekokak、id:vkgtaro、id:ziguzagu、id:ikasam_aなどの
方々ともお話する機会がたくさんありました。
これからもよろしくお願いします。

細かい感想はここここ(写真)を見ればわかる。
便利な時代になったもんだ。

個人的な感想としては

  • Best Practice & perltidy
  • Perl 6 - どこからmuch upはじめるか
  • mixiのバックエンドはすごいことに
  • 英語 - 勉強しなくちゃ
  • takahashi(v)
  • Indyの(ry


あたり。

来週MF勉強会(Sledge カンファレンス)で、Shibuya.jsにも行きますのでまたよろしくお願いします。