ホーム

へぼい日記

CodeReposにDBIx::Class::Tree::NestedSetをコミットしてみた

てなわけでRubyはやってません

がCodeReposにDBIx::Class::Tree::NestedSetをCommitしてみました。これのアルゴリズムに関しては[mysql 12071] 階層化されたデータをMySQLで扱うを参照ということで省略。ここに書いてあるのと違う点としては、親ノードの付け替え用にmvgフラグを必要としています。(インスパイア From DBIx::OO::Tree)

メソッドはDBIx-Class-Treeパクリ準拠していますのでテストもパクリ準拠してまったく同じ(あ、mvgカラムは追加してるけど)テストを通しています。

あとはDBIx::OO::Tree,DBIx::Tree::NestedSetあたりをパクリ参考にしてみました。

でもってTODOにも書いてるのですが、rootノード取得用にResultSetManagerなメソッドを生やしたいんですがどうすればいいのかが分からない…。もうちょい調べよう。

あとDBIx-Class-TreeのTODO

– DBIx::Class::Tree::NestedSet

ってかかれてるんですがこの場合やっぱ空気読んでCPANには上げないほうがいいのでしょうか。まだCPANあげたこともないのでCPANの空気が読めません><

はてなブックマーク - CodeReposにDBIx::Class::Tree::NestedSetをコミットしてみた

Rubyをやってみる

  • 投稿者: chiba
  • 2007/9/21 金曜日 11:10:42
  • learning

ブログが3か月以上あくとさすがにもう書かないことに慣れてしまってまずい。

なんかかかなきゃなぁとおもったのですが、技術ネタを書くとどうも時間がかかってしまう性質なので…。

ブログで勉強するぞ宣言をして逃げられないようにするよメソッド発動ということで。

さて、何の勉強をするかな。本棚を探ってみると買ったまま放置しているたのしいRubyRuby on Rails入門が・・・

というわけでとりあえずRubyをいまさらながらやってみようということで。
一ヶ月ぐらいでひととおりのところをできるようになりたいなと。

とりあえず今の(大いに間違っているであろう)Rubyに対する知識

  • Rubyは全部オブジェクトらしい
  • 書くのが楽しいらしい
  • Railsも楽しいらしい
  • オブジェクトならPerlでもやってるよ
  • Javaも一時期かじってたよ
  • Jpmobile便利そう!
  • でもCPANみたいなのあるの?

さて、これが1ヶ月後どうなっているか。
随時進捗もアップする予定です。

その隙間をぬってPerlの話題もまた提供できたらいいなぁ・・・。

はてなブックマーク - Rubyをやってみる

そういえば蕎麦食べなかったんじゃない?

遅ればせながら引っ越し完了ネタを。

去る7月1日 に弊社は世田谷区若林にお引越しを行いました。

こういう作業があるときに自分は参加しないというのが既にネタ化してきていますが

大方の予想通り、諸事情により参加をしませんでした。

実作業された方々本当にお疲れ様でした。

さて、 今度のオフィスは若さに満ち溢れています。

昼間なんか、本当に若い声に溢れています。

「あさがお こんなにおっきくなった~」

え?つか若すぎじゃね?

会社からの望む若林小学校

小学校近すぎだし・・・orz

はてなブックマーク - そういえば蕎麦食べなかったんじゃない?

緯度経度から住所

  • 投稿者: chiba
  • 2007/7/1 日曜日 1:52:05
  • perl

フレンジャーでは携帯3キャリアすべての簡易位置情報取得(基地局)とGPS位置情報取得に対応しています。

で、DoCoMoのオープンiエリアを除いた5つの形式では緯度経度がとれるのですが、そこから住所文字列に変更してあげています(逆ジオコーディング)。その変換にはinvgeocoderを今までは使わせていただいていました。

ところが、今日埼玉県の秩父群小鹿野にきて位置情報取得すると埼玉県秩父市田村に変換されてしまったんですねぇ。これはまずいなぁということで、いろいろほかに方法がないか調べるとCPANにて以下を発見。

Geo::Coder::YahooJapan::Inverse

Yahooの非公開apiを利用しているようですがきちんと秩父郡小鹿野に変換してくれたのでとりあえずこちらに乗り換えました。

あと、auにて測地系をtokyo指定しておきながら逆ジオコーディング時にwgs84系を利用していたため微妙にずれてたということも発覚。修正しておきました。

いままで精度が悪いなぁと思ってたかた、再度お試しください。
うん。

はてなブックマーク - 緯度経度から住所

フレンジャーはじめりです

  • 投稿者: chiba
  • 2007/6/26 火曜日 19:00:28
  • webservice

紆余曲折ありつつ

フレンジャーのベータ版をオープンしました。

今日はフレッツADSLがキレまくりで大変だったです。

はてなブックマーク - フレンジャーはじめりです

Template::Stash::FiltersでTemplate::Stash::Contextを併用する方法

  • 投稿者: chiba
  • 2007/6/20 水曜日 4:14:30
  • perl

ここの最後のほうで問題にしてた件。

BEGIN {
    $Template::Config::STASH = 'Template::Stash::Context';
};

use Template::Stash::Filters;
use Template::Stash::Filters::EscapeHTMLByCase;
use Template::Stash::Filters::ForceUTF8;

__PACKAGE__->config(
    STASH => Template::Stash::Filters->new(
        FILTERS => [
            Template::Stash::Filters::ForceUTF8->new,
            Template::Stash::Filters::EscapeHTMLByCase->new,
        ],
    ),
);

こんな風にしたらいけそうだと思ってやってみたけど
Template::Stash::Contextで

 Can't locate object method "component" via package "Template::Stash::Filters" at /usr/local/lib/perl/5.8.8/Template/Stash/Context.pm line 627.

こんなエラーが・・・。

で、いいのか悪いのかもあまり深く理解しないまま以下のような修正をTemplate::Stash::Context(VERSION 1.63)に当ててみた。

--- Context.pm.org      2007-06-20 03:02:46.000000000 +0900
+++ Context.pm  2007-06-20 04:07:23.000000000 +0900
@@ -422,7 +422,7 @@
         $rootref = 'ARRAY';
         $root = [$root];
     }
-    if ($rootref eq __PACKAGE__ || $rootref eq 'HASH') {
+    if (eval { $rootref->isa(__PACKAGE__) } || $rootref eq 'HASH') {

        # if $root is a regular HASH or a Template::Stash kinda HASH (the
        # *real* root of everything).  We first lookup the named key
@@ -607,7 +607,7 @@
     return undef                                               ## RETURN
        unless $root and defined $item and $item !~ /^[._]/;

-    if ($rootref eq 'HASH' || $rootref eq __PACKAGE__) {
+    if ($rootref eq 'HASH' || eval { $rootref->isa( __PACKAGE__)}) {
 #      if ($item eq 'IMPORT' && UNIVERSAL::isa($value, 'HASH')) {
 #          # import hash entries into root hash
 #          @$root{ keys %$value } = values %$value;

なんかこれで不具合おきたりするんだろーか。
http://search.cpan.org/src/ABW/Template-Toolkit-2.19/t/stashc.t
このテストはとりあえずパスしたけど。。。

はてなブックマーク - Template::Stash::FiltersでTemplate::Stash::Contextを併用する方法

それTemplate::Stash::Contextで

  • 投稿者: chiba
  • 4:00:21
  • perl

ちと古い話題だけど、
TTでDBICのhas_manyなメソッドを使う
TTでDBICのmany_to_manyなメソッドを使う
[% WHILE %] の中のこと その後
ここら辺で問題になっていた

何を言いたいかというと、TTでhas_manyで定義した子オブジェクトの結果一覧を取得するメソッド(ここではcomments)にアクセスするとイテレーターではなく配列のリファレンスとして扱われるので大変面倒。

それ、Template::Stash::Contextでもいけますですよ。

(Catalystの場合)MyAPP::View::TT

__PACKAGE__->config(
STASH => Template::Stash::Context->new(),
);

こうして、Viewのほうでは

<html><body>
<ul>
[% WHILE (topic = topics.next) %]
<li>
[% topic.title %]
: comments([% topic.comments.scalar.count %])
</li>
[% END %]
</ul>
</body>
</html>

こんな感じで。これだとmany-to-manyでもたぶんいける。(試してないけど)

ただ、自分はTemplate::Stash::Filtersを使ってるので結局使えなかったり。
うーんなんとかしたい。というか、Stashをauto-filter的な使い方してるのが間違っているのかもしれないけど。。。
追記
なんとかしてみた->Template::Stash::FiltersでTemplate::Stash::Contextを併用する方法

はてなブックマーク - それTemplate::Stash::Contextで

さて、そろそろ反撃してもいいですか?(携帯サイトでCSSを3キャリア共通にする)

  • 投稿者: chiba
  • 2007/6/19 火曜日 4:07:43
  • perl

DoCoMoはCSSの外部参照に対応してくれていない。
via モバイル勉強会が開催されました!
んなわけで、作ってみました。
Catalyst::Plugin::CSS::Docomo

package Catalyst::Plugin::CSS::Docomo;
use strict;

use CSS::Tiny;
use CSS::Tiny::Style;
use HTML::TreeBuilder::Select;
use NEXT;

our $VERSION = '0.01';

sub finalize {
    my $c = shift;
    unless ( $c->response->body and not ref $c->response->body ) {
        return $c->NEXT::finalize;
    }
    unless ( $c->response->content_type =~ /^text|xml$|javascript$/ ) {
        return $c->NEXT::finalize;
    }

    unless ($c->req->mobile_agent->is_docomo) {
        return $c->NEXT::finalize;
    }

    $c->res->content_type('application/xhtml+xml; charset=Shift_JIS');

    my $tree = HTML::TreeBuilder::Select->new()->parse($c->res->output)->eof;

    my @stylesheets = $tree->select('link[rel="stylesheet"]');
    unless (@stylesheets) {
        return $c->NEXT::finalize;
    }

    foreach my $stylesheet_el (@stylesheets) {
        my $css = CSS::Tiny->read( $c->path_to('root' . $stylesheet_el->attr('href')) );
        if ($css) {
                for my $st ($css->styles) {
                    foreach my $el ($tree->select($st->selector)) {
                        $st->add_to($el)
                    }
                }
                {
                    no warnings 'redefine';
                    local *HTML::Element::_xml_escape = &_xml_escape;
                    $c->res->output($tree->as_XML);
                }
        }
    }

    $c->NEXT::finalize;
}

sub _xml_escape {
    foreach my $x (@_) {
        $x =~ s!([<&>])!'&#'.(ord($1)).';'!seg;
    }
    return;
}

1;

まぁかなりやっつけ仕事なのはいなめないですが。一応動きます。
キャリア判別のためにCatalyst::Plugin::MobileAgentに依存してます。
他にはCSS::Tiny::Style,HTML::TreeBuilder::Selectあたりを使ってます。それぞれ依存モジュールがどばっとあるので一緒にいれまくってください。

仕組み的には、外部参照していてrootの下にStatic::Simpleとかで読み込ませてるCSSファイルをCSS::Tinyで読み込んでセレクタごとにスタイルをstyle属性に追加しまくるというかんじです。
優先順位なんかはチェックしてないのでどうなるか分りません。
で、これは実はこういうことをやりたくてCPANをあさってたらCSS::Tiny::StyleのSYNOPSISに

#——————————————————
# more interesting example
#——————————————————
# inline a stylesheet onto the single HTML elements
#——————————————————

use HTML::TreeBuilder;

my $tree = HTML::TreeBuilder->new();
$tree->parse_file(‘filename.html’);

$css = CSS::Tiny->read( ‘stylesheet.css’ );

for my $el ($tree->descendants) {
for my $st ($css->styles) {
if ($st->match($el)) {
$st->add_style($el);
}
}
}
print $tree->as_HTML;

こんなのがかいてあったのでこれがほぼ元ネタです。
$st->match($el)がいまいちな動きだったので
HTML::TreeBuilder::Selectを使うことにしましたけどね。

また、動的にクラスを付与しないとかであれば最初にTTを変換してキャッシュしておくというアプローチのほうが賢いでしょう。
あとは、root配下じゃないcssに対応するためbase_pathなんかが設定できたり、LWPなんかと連動して外部サーバのCSSなんかにも
対応するとなかなか汎用的になるかなぁ。
Catalyst以外でも使えるように別の名前空間でモジュール化してもいいだろうし・・・。
まぁ今日はのところは疲れたのでこれぐらい。必要に駆られないとやらなそうだけどね。

以上、反撃終わり。

はてなブックマーク - さて、そろそろ反撃してもいいですか?(携帯サイトでCSSを3キャリア共通にする)

Template::Stash::Filters(TTのStashで複数のfilterを実行)

  • 投稿者: chiba
  • 2007/6/18 月曜日 2:23:32
  • perl

前回のエントリーでTemplate::Stash::EscapeHTMLByCaseというのを作ったわけですが、こうやっていろいろStashで対応しようとすると、Template::Stash::ForceUTF8も使ってるから使えない~とかが発生する。

というのも、TTのSTASHは一つのObjectしか渡せないからだ。これには多分いろいろ理由があって、Template::Stash::Contextのようにgetのアルゴリズム自体に手を入れてる場合どうすんの?ってことだからだと思う。

ただ、EscapeHTMLByCaseやForceUTF8なんかのように単にfilterを掛けたいだけであれば繋げたい。

というわけでTemplate::Stash::Filters作ってみました。

package Template::Stash::Filters;

use strict;
use Template::Config;
use base ($Template::Config::STASH);
our $VERSION = '0.01';

sub get {
    my ($self, @args) = @_;

  PRE_PROCESS:
    foreach my $filter (@{$self->{FILTERS}}) {
        if (!$filter->can('pre_process')) {
            next PRE_PROCESS;
        }
        $filter->pre_process(@args);
    }

    my $var = $self->SUPER::get(@args);

  FILTER:
    foreach my $filter (@{$self->{FILTERS}}) {
        if (!$filter->can('filter')) {
            next GET;
        }
        $var = $filter->filter($var);
    }

    return $var;
}

1;

__END__

Template::Stash::XXX系のモジュールを再利用できたらいいんだろうけど、$self->SUPER::getをhackする方法が分らなかったのでFilter実装モジュールも車輪の再発明。

Template::Stash::Filters::EscapeHTMLByCase

package Template::Stash::Filters::EscapeHTMLByCase;

use strict;
our $VERSION = '0.01';

use base qw(Class::Accessor);
__PACKAGE__->mk_accessors(qw(is_raw));

sub pre_process {
    my ($self, $args) = @_;

    if (is_this_raw(@{$args})) {
        $self->is_raw(1);
        strip_raw_specifier($args);
    }
    else {
        $self->is_raw(0);
    }

}

sub filter {
    my ($self, $var) = @_;

    if ($self->is_raw || ref $var) {
        return $var;
    }

    return html_filter($var);
}

sub html_filter {
    my $text = shift;
    for ($text) {
        s/&/&amp;/g;
        s/</&;t;/g;
        s/>/&gt;/g;
        s/"/&quot;/g;
    }
    return $text;
}

sub is_this_raw {
    my @args = @_;

    if (ref $args[0] ne 'ARRAY') {
        return 0;
    }

    if ($args[0]->[0] ne 'RAW') {
        return 0;
    }

    return 1;
}

sub strip_raw_specifier {
    my $args = shift;

    splice @{$args->[0]}, 0, 2;
}

1;

pre_processを活用してます。

Template::Stash::Filters::ForceUTF8

package Template::Stash::Filters::ForceUTF8;

use strict;
our $VERSION = '0.01';

use base qw(Class::Accessor);
use Encode;

sub filter {
    my ($self, $var) = @_;

    return $var if ref $var;

    Encode::_utf8_on($var) unless Encode::is_utf8($var);
    return $var;
}

1;
__END__

で、使い方はこんな感じ


use Template;
use Template::Stash::Filters;
use Template::Stash::Filters::EscapeHTMLByCase;
use Template::Stash::Filters::ForceUTF8;

my $template = Template->new({
    STASH => Template::Stash::Filters->new(
        FILTERS => [
            Template::Stash::Filters::ForceUTF8->new,
            Template::Stash::Filters::EscapeHTMLByCase->new,
        ],
    ),
});

うーん、newへの引数が無いモジュールばっかなのでTemplate::Stash::Filters::がうっとうしいなぁ。useするのも面倒だし勝手にuseしちゃう方式つくってみるかなぁ。

まぁまた時間があったときにでも。

はてなブックマーク - Template::Stash::Filters(TTのStashで複数のfilterを実行)

Template::Stash::EscapeHTMLByCase(TTでXSS対策)

  • 投稿者: chiba
  • 2007/6/15 金曜日 2:41:55
  • perl

TTでのXSS対策としては下記の2つの方法がまずは考えられます。

1. [% data | html %]のようにFILTERをすべての変数出力部分に使う
2. Template::Stash::EscapeHTMLを使って自動的に全ての変数出力をEscapeする

1は毎回”| html”と書くことになるので、間違いが置きやすいので2を使うことが多いのですが
システム側からescapeしないで出力したいとか、INCLUDEで変数を渡したいとかいうときに問題がでてしまう。
INCLUDEの話はどういうことかというと

たとえば、ヘッダーをheader.ttというファイルにまとめて共有で使っており
渡された変数を用いてタイトルを構成するときなんかがあったとする。
(ちなみにMETAだと変数は渡せない)

content.tt
[% INCLUDE header.tt
title = title_html
%]

header.tt

<html>
<head>
<title>[% title %]</title>
</head>
<body>

まぁこんな構成で下記のように呼び出したとする
#!/usr/bin/perl
use strict;

use Template;
use Template::Stash::EscapeHTML;

my %var = (
title_html => ‘<html>’,
);

my $template = Template->new({
STASH => Template::Stash::EscapeHTML->new(),
});

$template->process(‘content.tt’, \%var);
そうするとこれの実行結果は

<html>
<head>
<title>&amp;lt;html&amp;gt;</title>
</head>
<body>

となってしまう。
これは意図した結果ではなく正しくは

<html>
<head>
<title>&lt;html&gt;</title>
</head>
<body>

となってほしいわけですね。
二重にEscapeされてしまう。
これは、STASHのgetは変数の代入の時にも呼ばれている影響です。
代入する度にEscapeされるわけでINCLUDEしなくても単純に

[% title = title_html %]
[% title %]

このようにしていてもやはり同じ問題は起きてしまう。

というわけで、escapeしないことを明示的に指示することをしたい。
こうなってくると最初の1の方法の”| html”でも同じじゃないかと思われるかもしれないが
Order allow, deny
より
Order deny, allow
(Apache用語)
のほうがセキュリティ上は好ましいし、何よりコード量も減る。

で、Template::Stash::EscapeHTMLByCaseってのを作ってみた。
命名はまずいかもしれない。

package Template::Stash::EscapeHTMLByCase;

use strict;
use Template::Config;
use base ($Template::Config::STASH);
our $VERSION = '0.01';

sub get {
    my ($self, @args) = @_;

    if (is_raw(@args)) {
        @args = strip_raw_specifier(@args);
        return $self->SUPER::get(@args);
    }

    my ($var) = $self->SUPER::get(@args);

    unless (ref($var)) {
        return html_filter($var);
    }
    return $var;
}

sub html_filter {
    my $text = shift;

    for ($text) {
        s/&/&amp;/g;
        s/</&lt;/g;
        s/>/&gt;/g;
        s/"/&quot;/g;
    }
    return $text;
}

sub is_raw {
    my @args = @_;

    if (ref $args[0] ne 'ARRAY') {
        return 0;
    }

    if ($args[0]->[0] ne 'RAW') {
        return 0;
    }

    return 1;
}

sub strip_raw_specifier {
    my @args = @_;

    splice @{$args[0]}, 0, 2;

    return @args;
}

1;

使い方はこんな感じにRAWを最初につけてあげる。

content_kai.tt
[% INCLUDE header.tt
title = RAW.title_html
%]

そうすると期待通りに下記の文字列が出力される。

<html>
<head>
<title>&lt;html&gt;</title>
</head>
<body>

と、こんなことをやってみましたが皆さんはこんな状況をどうしてるもんなんでしょ
それ、TemplateToolkitの標準機能で解決できるよ。とかありそうだけど・・・。
なかったらCPANに上げてみたいなぁ(まだCPAN Authorじゃない・・・)とか。

はてなブックマーク - Template::Stash::EscapeHTMLByCase(TTでXSS対策)

1 3 4 5 6 7 8 9

ホーム

検索
フィード
メタ情報

ページの上部に戻る