entries_in_this_pageプラグイン
現在のページに表示されているエントリのタイトルが一目で分かる様にしたいな、ってことで、 entries_in_this_pageプラグインというものを作ってみました。左メニューの「entries in this page」の様に表示されます。
プラグインの中身自体は非常に単純で、以下の様なデータのリファレンスを返してるだけです。
$entries = [
{ title => 'title1',
path => 'path1',
fn => 'fn1',
},
{ title => 'title2',
path => 'path2',
fn => 'fn2',
},
];
ttプラグインを利用して、このリファレンスをフレーバの中で以下の様に処理してます。
<ul> [% FOREACH entry = entries_in_this_page.entries %] <li><a href="[% entry.path %]/[% entry.fn %]">[% entry.title %]</a><li> [% END %] </ul>
こんな感じでプラグインからHTMLを分離したかったというのが、ttプラグインを作った理由だったりします。やっぱり、プログラムの中にHTMLが混じるのは気色悪いので。
blosxomでtagging #7
taggingプラグインを修正しました。タグでエントリを絞り込む部分のアルゴリズムを見直し。大量にエントリがある場合にはパフォーマンスが良くなるはず。
Catalystでajax - 補足
今回はCatalystはあんまり関係なくて、prototype.jsの使い方に関する補足、といった感じです。
前回のエントリの最後で、「xmlHttpRequestで取得したデータを、単純にそのままHTMLにはめ込むだけなので、サーバ側で生成したデータを、Javascript側で何らかの処理をしてからHTMLにはめ込むという用途には使えませんが、単純なajaxアプリであれば十分使えるかと。」ということを書いたのですが、これはHTML::Prototypeが吐き出すJavascriptが、基本的にAjax.Updaterという関数を使っているからですね。なので、prototype.js自体が、「Javascript側で何らかの処理をしてからHTMLにはめ込む」という処理ができない、というわけではないです。
この様な処理をするには、具体的には以下の様なコードになります。ただしこの方法では、HTML::PrototypeやCatalyst::Plugin::Prototypeが使えないので、若干手間が増えます。
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript">
function completed(request){
var data = request.responseText;
// 何か処理をする...
}
</script>
<form onsubmit="new Ajax.Request( '/url', { parameters: Form.serialize(this),onComplete: function(request){ completed(request) } } ) ; return false;">
<input type="text" name="text" />
<input type="submit" />
</form>
肝になるのはAjax.Requestという関数を使っていることと、「onComplete」の部分ですね。この部分で、xmlHttpRequestでサーバからデータを取得完了した時点で実行する関数を指定できます。requestはxmlHttpRequestオブジェクトなので、呼び出した関数内で、request.responseTextでサーバからのデータを取り出すことができます。その上で取得したデータに対して何か処理してやれば良い、ってわけですね。
HTML::PrototypeやCatalyst::Plugin::Prototypeが使えないのはちょっと痛いですが、Ajax.Request関数の書式に慣れてしまえば、自分でxmlHttpRequest周りのコードを書かなくて良いのですから、メリットの方が大きいと思います。
で、実際に上の方法を用いて、ajaxでdel.icio.usを使いやすく #0のdel.icio.usサイドバーのJavascript部分を書き直してみると、どんな感じになるのか見てみることにします。
まず、prototype.jsを使わずに何も考えずに書いたもの。(xmlHttpRequestを使っているfunctionのみ抜き出しています。)JSON形式のデータを受け取って、evalでオブジェクトに変換する、という処理です。
function init() {
tagsArea.innerHTML = '<p class="loading">loading tags ...</p>';
var req1 = new XMLHttpRequest();
if(req1){
req1.onreadystatechange = function() {
if (req1.readyState == 4 && req1.status == 200) {
eval('tagsListObj = ' + req1.responseText);
displayTags();
}
};
req1.open('GET', '/delside/index.cgi');
req1.send(null);
}
var req2 = new XMLHttpRequest();
if(req2){
req2.onreadystatechange = function() {
if (req2.readyState == 4 && req2.status == 200) {
eval('tagToLinkObj = ' + req2.responseText);
}
};
req2.open('GET', '/delside/index.cgi?rm=tagToLink');
req2.send(null);
}
}
次にprototype.jsのAjax.Request関数を使って書き直したもの。
function init() {
tagsArea.innerHTML = '<p class="loading">loading tags ...</p>';
new Ajax.Request( '/deislde/index.cgi',
{
method: 'GET',
onComplete: function(request){
eval('tagsListObj = ' + request.responseText);
displayTags();
}
});
new Ajax.Request( '/delside/index.cgi?rm=tagToLink',
{
method: 'GET',
onComplete: function(request){
eval('tagToLinkObj = ' + request.responseText);
}
});
}
太字部分が変更された箇所です。若干ですが、コードがすっきりしましたよね?
以上、prototype.jsがこんな風にも使える、という例でした。おしまい。
Catalystでajax - 実践編
概要
前回のエントリに関して、MM/Memoで、「うーん、便利なのかなー。perl(とかのサーバサイド)コード中にjavascriptが混ざるのって嫌な気がするけど。」と書いている方がいらっしゃったのですが、ちょっと勘違いをされているような気がします。自分でJavascriptのコードを一切書かずに済ませるための、HTML::PrototypeならびにCatalyst::Plugin::Prototypeですから。
なので、今回は実践編っつーことで、Javascriptを一切書かずにajaxなウェブアプリ(ってほどのものではないけど)をCatalystで作ってみたいと思います。といっても、けんたろさんのCatalyst で作る簡単 Web アプリケーション: Feed2JS 解説で、既にCatalystでajaxで自分ではJavascriptを書いていないという、素晴らしいサンプルがあるのですが、ここではよりシンプルな(しょぼい)サンプルで、「こんなに簡単にajaxできるぜ」ってことを示したいと思います。
プロジェクト生成
$ catalyst.pl Ajax
ビューの生成
$ cd Ajax $ ./script/ajax_create.pl view TT TT
perlコード作成
lib/Ajax.pmを修正します。修正した部分だけ以下に示します。
use Catalyst qw/Prototype/;
sub default : Private {
my ( $self, $c ) = @_;
$c->stash->{template} = 'default';
$c->forward('Ajax::V::TT');
}
sub get : Global {
my ( $self, $c ) = @_;
$c->res->output('success.');
}
テンプレート作成
以下の内容のroot/default というファイルを作成します。
[% c.prototype.define_javascript_functions %]
<p>
This is a sample of ajax web app using Catalyst.
</p>
<p>
[% c.prototype.link_to_remote('click here and you will see the string "success."',
{
update => 'container',
url => '/get',
}) %]
</p>
<div id="container">
</div>
アクセスしてみる
cgiで起動する場合には、以下の様なスクリプトを作成します。
#!/usr/local/bin/perl -w use strict; use lib '/path/to/Ajax/lib'; use Ajax; Ajax->run;
実際にアクセスしてみるとこうなります。リンクをクリックすると、画面遷移せずに「success.」という文字を表示します。
以上で完了です。Javascriptを一切書かずにajaxなウェブアプリが作れる、ということがお分かり頂けたと思います。
xmlHttpRequestで取得したデータを、単純にそのままHTMLにはめ込むだけなので、サーバ側で生成したデータを、Javascript側で何らかの処理をしてからHTMLにはめ込むという用途には使えませんが、単純なajaxアプリであれば十分使えるかと。
blosxom + Template Toolkit
interpolate_fancyプラグインは便利なのですが、HTML::TemplateやTemplate Toolkitに慣れているとなんか物足りないし、やはり慣れた記法で記述したいですよね。
で、探してみるとblosxomでHTML::Templateを使うためのプラグインを見つけましたが、Template Toolkitに対応したものはないようです。
個人的にはHTML::Templateもシンプルで好きなのですが、Template Toolkitの柔軟性も捨てがたいので、Template Toolkit対応プラグインを作ってみました。ttプラグイン
通常であれば、
$plugin::variable
と記述するところを、
[% plugin.variable %]
と記述します。package blosxomの変数であれば、
[% blosxom.variable %]
でも、
[% variable %]
でもOKです。
blosxom.cgi中のinterpolateサブルーチンの内容もこのプラグインに含まれてますので、本来の
$plugin::variable
という記述でもちゃんと処理します。
IF, UNLESSはちゃんと動作してるようですが、他のディレクティブはどうなるか試していません。
HTML::Templateであれば、paramメソッドを利用して、テンプレートで使われているパラメータリストを取得できるのですが、Template Toolkitでは同様のことができるメソッドがないようで、Template::Parserのtokenise_directiveメソッドで分解したtokenを正規表現で処理してパラメータを取得しています。IF, UNLESS以外のディレクティブがちゃんと動作するかどうかは、ここの処理にかかってるわけですが、たぶん、複雑なデータ構造のものをFOREACHでまわしたりするのは、正常に動作しないと思う。
このサイトについて
特にコンセプトとか目的とかはないです。なんとく「俺」が滲み出てる、そんなサイトであればいいな、と。
blosxomつかってます。カスタマイズしやすいのが魅力的。まだカスタマイズ不十分なところがあるので、ちょくちょくいじることになるかと。そのせいで、エラーで見られなくことがあるかもしれませんが、そんな時は「ああ、あいつカスタマイズ失敗してやがんの。」と笑ってやってください。
インデックスページではajax使って、ページ遷移なしにコメントとトラックバックを表示したり、コメントの投稿ができるようになっています。(asynchronousじゃないけど。)Javascriptオフの場合や、xmlHttpRequestに対応していないブラウザでは、ページ遷移する様にしています。
Win IE6, Win Mozilla Firefox, Mac Camino, Mac Safariで表示確認してます。Mac IEお断り。表示が崩れます。
ajaxでdel.icio.usを使いやすく #0
話的には前回の続きなんだけど、もはやRSSは関係ないのでタイトル変えます。
del.icio.usはソーシャルブックマークとしてはともかく、自分用ブックマークとしてはいまいち使いづらいんですよね。なので、最近はSpurlばかり使っています。Spurlには自分用ブックマークとして使うのにとても便利な、Spurl Barというものがあるので。
じゃあ、del.icio.usにも似たようなサイドバーがあれば、もっと使いやすくなるのでは、ということで、del.icio.us barを作ってみました。以下がサンプルとソースです。(Firefoxでしか動作確認してません。IEではJavascriptエラーになります。)
Spurl Barの劣化コピーです。アイコンも拝借してしまいました。(ほんとは良くないんだろうけど…)tagフォームに文字を入れると、tagリストが絞り込まれていきます。タグをクリックすると、そのタグを持つリンクを表示します。サンプルのブックマークは俺がdel.icio.usにポストしたものを、API経由で取得しています。
技術的には、API経由で取得したデータをperl CGIでjson形式に整形し、それをJavascriptのxmlHttpRequestで取得してevalでオブジェクトに変換、ってことをやってます。
現在は単一のタグだけしか対応していません。複数タグでの絞込みができるといいのですが、それは次の機会に。
相変わらず適当に作ってるので、色々問題はありますが、改良していけば結構いいものになりそうな気が。
ソーシャルブックマークのRSSをブラウザのブックマークに取り込む #1
前回のエントリで、RSSをXBELに変換するゲートウェイについて触れましたが、Firefoxを使う限りにおいてはLive Bookmarks機能を使えばそれで済むので、せっかっく作ったゲートウェイがあまり意味がないことに気づきました。
それもなんだか悔しいので、今度はdel.icio.usのAPIを利用して、自分がpostしたブックマークをすべて、XBELに変換するためのゲートウェイを作りました。
前回と違って、タグ毎にフォルダを分けるようにしています。FirefoxのBookmarks Synchronizer extensionで取り込んで、サイドバーに表示すると、こんな感じでタグ別にフォルダ分けして表示されます。
これはLive Bookmarksではできないことなので、ちょっと満足しました。まあ、色々と足りないところはあるのですが、自分はSpurlサイドバーで満足してるので、これを使うことはないですね。なので、要望があれば改良していきますが、なければこのままほっときます。
ソーシャルブックマークのRSSをブラウザのブックマークに取り込む #0
はてなのnaoyaさんがnaoyaのブックマークにて、「del.icio.us の RSS をブックマークのお気に入りに取り込めるようにしたいなあ。」と呟いていたのを見て、こんなものを作ってみた。(naoyaさんが考えているものと違うかもしれませんが。もしかしてはてなブックマークのお気に入りのこと?)
使い方は簡単で、フォームにソーシャルブックマークのRSSフィードURLを入力してsubmitすると、XBELに変換されたものが表示されます。
で、これをXBELに対応したインポートツールで取り込めばOK、って感じです。Mozilla Firefoxであれば、Bookmarks Synchronizerというextensionがあります。(FTPと書いてますが、HTTP/HTTPSも対応してます。)このextensionでsubmit後のURLを設定しておけば、ブラウザを起動するたびに、指定したRSSフィードをブックマークにインポートしてくれます。試しにdel.icio.us/popularをインポートしみると、以下の様になりました。
antipop2.0のけんたろさんが作成されたAlpha Geek Trackerも試しにインポートしてみたのですが、途中までは取り込めたものの、データ量が多すぎるためか、Bookmarks Synchronizerが途中でハングしてしまいました…
試しに作ってみただけなので、現段階では単一のRSSフィードしか扱えませんが、次は複数のRSSフィードをフォルダ分けしてXBELに変換する、という機能の追加を考えてます。
でも、FirefoxだとLive BookmarksでRSSを直接ブックマークに取り込めるから、XBELに変換する意味がないと今気づいた…