« 2007年9月 | メイン | 2007年11月 »

2007年10月31日

Webデザインする上でFireworksがステキな12のポイント
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

Webデザイナとして仕事をする上で、ここ数年、個人的にFireworksが手放せないツールになっています。
twitterで某人に「書いて」と言われたのもあるのですが(笑)、Web画像を作るのによいソフトをさがしている人、これから画像編集のためのソフトを選ぼうとしている人の参考に、ということで、個人的にFireworksの気に入っている点など一通りまとめてみようと思います。

ベクター画像とビットマップ画像が等しく扱えるので作業がスムーズ

Fireworksは、ベクター画像とビットマップ画像を同時に扱えます。ベクターでちょっとしたアイコンを作るとか、ビットマップ画像を読み込んで編集するとか、すべてが一つのソフトの中で完結できます。IllustratorとPhotoshopを行き来したりするような手間は不要ですし、また複数のソフトを同時起動しなくていいのでマシンにも優しいです。
ビットマップ&ベクター
ビットマップ&ベクター posted by (C)フォト蔵

画像作成からスライス作成、画像名を指定して書き出し、までが一つのソフトで完結できる

Web制作のワークフローでいくと、

カンプというかモックアップというか、「サイトのイメージ」を制作してデザインを詰める
  ↓
クライアントからOKが出たら必要なパーツをスライスして切り出して、HTMLを組み立てていく

みたいな流れが多いかなと思うのですが、この「モックアップ作成」から「スライス切り出し」までがFireworksであれば一つのソフトで完結します。
Photoshop,Illustrator,ImageReadyを組み合わせるフローよりはシンプルでスムーズに作業できるかな、と個人的には思います。
フロー1
フロー1 posted by (C)フォト蔵
フロー2
フロー2 posted by (C)フォト蔵
out3
out3 posted by (C)フォト蔵

角丸、矢印などのちょっと複雑なグラフィックが簡単に書ける&書いた後に数値指定などして変更可能

Fireworksには「矢印」「角丸矩形」「星形」「接続線」などのグラフィックが簡単に書ける「オートシェイプ」というツールが備わっています。
書けるものもWebサイトでそれなりに使いそうなものが多いですし、Illustratorで書こうとするとそれなりに面倒だったりするので重宝しています。
また、書いたグラフィックは、たとえば角丸の半径などを「書いた後に」自由に変更できます。
Illustratorだと角丸ツールで書いたものは後から角の半径を変えようとしても難しかったりするので、このあたりの使い勝手は非常によいと思います。

この機能が便利なので、ちょっとしたプレゼン向けのグラフィックとか遷移図みたいな資料書くときにも(印刷の前提がなければ、ですが)Fireworksを使ったりします。

オートシェイプ
オートシェイプ posted by (C)フォト蔵
オートシェイプ2
オートシェイプ2 posted by (C)フォト蔵

9スライス機能が地味に素晴らしい

こちらはFireworksCS3で追加された機能で、会社で使っているFireworks8では使用不能なので機能詳細はAdobeのサイトをご覧ください…。
角丸のボックスや装飾つきのボックスなど作るときに、非常に便利です。

ショートカットが直感的でよい

これは非常に個人的な趣味の問題ですが、AdobeのWebサイトなどには出てこない部分なので…。
画像を作っているとき、細かいところを詰めたり、細かいアイコンを作ったりするときには全体をズームさせたりズームアウトさせたり、といったことをよくやります。
で、そのショートカットが、
ctrl+1で100%表示
ctrl+2で200%表示
ctrl+4で400%表示
ctrl+8で800%表示
ctrl+5で50%表示
という感じで、非常に直感的なショートカットがデフォルトで設定されています。
その他「拡大縮小ツール(Q)」や「ポイント選択ツール(A)」など、個人的によく使うショートカットがキーボードの左半分にそろっています。
右手はマウスに置いたまま、左手だけでショートカットをガシガシ使いたい自分としては非常にありがたい初期設定になっています。
(※ま、そのあたりは環境設定で自分で設定すればいいといえばいいのですけれども…)

単位が「ピクセル」

Fireworksはあくまで「Webグラフィック作成」に特化しているおかげで、基本単位が「ピクセル」です。「ポイント」だとか「cm」とかいった単位は使われません。
移動もすべてピクセル単位で動いてくれるので、ピクセル幅に合わせた配置やスライスがしやすいです。
Illustratorでも基本単位をピクセルに設定できますが、いちいち設定するのも面倒ですし、適当に動かすと12.22pxみたいな半端な数字がでてしまったりして面倒に感じることもあるのでその点FireworksはWeb向けに最適だ、と思います

デフォルトのコピペ位置が「同位置」

これが本当に細かいけど個人的に好きな部分。
Illustratorだとなぜか普通にコピペするとずれた位置にペーストされるのですが、Fireworksの場合は基本的に同じ位置にコピーしてくれます。
Webサイト作りだと、同じものを縦や横にいくつか並べるシーンは結構あると思うのですが、同じ位置にコピーしてくれるとあとは矢印キーやマウス+シフトキーで意図した位置に動かせるのでポイントが高いです。
細かいことなんですが日々の生産性に結構貢献しているように思います。

テクスチャやエフェクト付けも簡単にできる

グラフィックにちょっとした質感を与えたい時、簡単にテクスチャづけや、べベル、エンボス、ドロップシャドウといったエフェクトを追加できます。もちろんベクター画像にもビットマップ画像にも等しく効果を与えることができます。
ただ、テクスチャを多用すると、「あ、これ、Fireworksで作ったな?」というのがバレバレになるのでご注意を(特に斜線)
エフェクト・テクスチャ
エフェクト・テクスチャ posted by (C)フォト蔵
↑同じベクター画像にテクスチャやらエフェクトやら色々追加してみた例。2分くらいで作りました。

わりと安い。

PhotoshopとIllustratorを買う値段を考えたら、Fireworksはかなり安いです。それでこれだけのWebに特化されたグラフィック作成機能が手に入れば結構お得だと思います。ちなみに以前はもっと安かったような気がするんですけどね…

画像の書き出しで色々細かい最適化ができる

画像の一部、文字のある部分だけちょっと圧縮率を下げて読みやすさを保ちつつ、ほかのところは圧縮率高めてファイルサイズを下げる、みたいな、画像に合わせた細かい圧縮率の設定ができます。Webコンテンツ作りだと、ファイルサイズなど気にしなくてはならないシーンもありますので、こういった細かい設定ができるのはポイントだと思います。

シンボル機能が便利

サイトのイメージを作っていると、画面内の色んなところで使うけど同じ見た目のもの、というのが必ずあります。
これを「シンボル」として登録しておくと、たとえばそれらの見た目を一気に変えたいときに、シンボル一つを編集すれば全体に反映されます(説明がわかりにくいですねすみません…)
Illustratorにもバージョン10くらいでついた機能ですが、Fireworksなら「F8」を押せば選択しているグラフィックをすぐにシンボル化できたり、と、格段に活用しやすい気がします。

Flashとの相性がいい

今はCS3世代になって困ることも減ったと思うのですが、
以前はIllustratorからFlashに何かを持って行きたいときに、グラデーションがまったくうまく再現できない、Fireworksを経由してやるとうまくいくことがあって助けられたことがあります。もともと双方ともにMacromediaで作られていたものであるだけあって、Flashへの書き出しやコピーアンドペーストなど、Flashとの連携はスムーズにいくことが経験的に多かったです。

一通り思いつくのはこんな感じでしょうか。

ちなみによいところがあれば当然イマイチな点もあって
・文字組みのエンジンが貧弱なため、文字組みがきたない上に調整がちょっと面倒。
・単なる1px幅の直線を引きたいのになぜかアンチエイリアスがかかってぼやけた線になってしまうことがある
・エフェクトの品質がPhotoshopのものよりやや劣る。
・グラフィック品質がIllustratorで書き出した場合より若干劣ることがある気がする。
・ベクター画像いじりに若干クセがあって、細かいベクターイラストを書くときはやっぱりIllustratorのほうが使い勝手がいい。
・素材支給はやはりIllustrator形式,Photoshop形式が多いので、やっぱりPhotoshopやIllustratorは持ってたほうがいい。
とか色々とあるにはあるのですが、Fireworksは、「Webグラフィック制作ツール」として生まれ育ってきたものだけあって、主に印刷用途向けとして生まれ育ってきたIllustratorやPhotoshopなどよりは、”Webグラフィック制作ツールとして”気の利いた機能が多いです。最近、PhotoshopやIllustratorもWebグラフィック制作向けの機能を強化してきていますが、個人的にはやはりまだFireworksの生産性にはかなわない、と思います。

他にも色々機能などありますし、何より文章では伝わりきらないと思うので気になるかたはFireworksの紹介ページや、実際に体験版で試してみてください。
その他「こんなふうに便利に使っている」とか「この機能について書かないのはおかしい」とか、何かありましたらぜひ教えてください!

2007年10月30日

携帯へメールを送る際の確認事項
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

harukiです。

ケータイ宛にメールを送信するサイトにおける確認事項をまとめてみました。

サーバ側

DNS

  • IPアドレスが逆引きできるようになっているか
  • SPFレコードが正しく設定されているか

SPFは、2007年11月1日からDoCoMoも対応します。

http://www.nttdocomo.co.jp/service/mail/imode_mail/sender_id/index.html

MTA

  • EHLO/HELOコマンドでのドメイン名が正しいか
  • エンベロープFrom/Toは正しいか

その他

  • Fromフィールドのドメインが正しいか
    (Aレコード、MXレコードが存在しているドメインか)
  • Return-Pathは設定しているか
  • 端末で表示できる文字コード・形式で送っているか

あとは、OP25B(Outbound Port 25 Blocking)に該当する場合は、その確認も必要になります。

メールログを確認すれば、だいたいの原因は判明します。

端末側

「メールが届かない」といった類の問い合わせに対応するためには、届かないケースを運営側が把握しておく必要があります。

  • 容量がいっぱいになっていないか
  • 拒否設定にひっかかっていないか

    メール設定を確認

    (エラーメールを返さない設定をしていないかを確認)

    拒否設定の変更をしている場合、念のため、拒否→解除と再度行ってみると効果がある場合もあります。

各キャリアのメール設定へ行くことができるページを用意してあげることも効果的です。

 

新しくサービスを始める場合、機能追加の際などに、設定・確認もれのないように気をつけましょう。

2007年10月29日

symfonyのテストフレームワーク「lime」
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

yukiです。

今回はpluginその2をお休みしてsymfonyのテストフレームワークであるlimeをさわりだけご紹介します。
symfonyではlimeという独自のテストフレームワークが用意されていますが、以前採用されていたSimpleTestでの解説が多く
まだまだ日本語で説明のあるサイトは少ないのが現状です。
そんなsymfonyのlimeについて、ほんの少しですがご紹介します。

limeはsymfonyプロジェクト作成時、プロジェクトルートディレクトリにtestディレクトリが作成されます。
myproject
 |
 +--test
     |
     +--- bootstrap
     |
     +--- functional
     |      |
     |      +---- hogeActons.php
     |
     +--- unit
上記のように、init-moduleで作成したモジュール用テストがfunctionalへ、ユニットテスト用コードはunit以下へ配置されます。(bootstrapはlime読み込みなどの初期設定を行うphpがあります。)
unit以下へ○○Test.phpと書くと、以下のコマンドでテストが開始されます。
例) hogeTest.php

symfony test-unit hoge
また、
symfony test-unit
このように対象とするテストを指定しない場合、unitディレクトリ以下の**Test.phpが順に自動的に実行されます。
テストする場合、どうしても面倒なのがパラメータの指定ですが、symfonyのポリシーに従ってYAMLで設定することもできるので、とても重宝しています。

例として、暗号化を行った際のテストです。

<?php

// encryptTest.php

defined('SF_APP')         || define('SF_APP',         'myapp');
defined('SF_ENVIRONMENT') || define('SF_ENVIRONMENT', 'test');
defined('SF_DEBUG')       || define('SF_DEBUG',       true);

require_once(dirname(__FILE__).'/../bootstrap/unit.php');

// テスト数を設定
$t = new lime_test(7, new lime_output_color());
$t->diag('Hoge::encrypt()');

// 暗号化クラス
class Hoge
{
    public static function encrypt($src)
    {
        return md5((string)$src);
    }
}

// 空クラス
class a
{
    public function b()
    {
    }
}

$a = new a;

$t->can_ok(new Hoge, 'encrypt', 'Hogeオブジェクトのencryptメソッドは利用可能');
$t->is(Hoge::encrypt('a'), md5('a'), '暗号化アルゴリズムはMD5を使用');
$t->like(Hoge::encrypt('a'), '/^[a-f\d]{32}$/', 'MD5後は32文字の16進数');
$t->is(Hoge::encrypt(array(1)), md5((string)array(1)), '配列を渡した場合は型変換される');
$t->is(Hoge::encrypt($a), md5((string)$a), 'オブジェクトを渡した場合は型変換される');
$t->is(Hoge::encrypt(''), md5((string)''), '空文字列を渡しても大丈夫');
$t->is(Hoge::encrypt(false), md5((string)''), 'boolのfalseを渡しても空文字列にキャストされる');
出力は以下のようになります。
% symfony test-unit encrypt
1..7
# Hoge::encrypt()
ok 1 - Hogeオブジェクトのencryptメソッドは利用可能
ok 2 - 暗号化アルゴリズムはMD5を使用
ok 3 - MD5後は32文字の16進数
ok 4 - 配列を渡した場合は型変換される
ok 5 - オブジェクトを渡した場合は型変換される
ok 6 - 空文字列を渡しても大丈夫
ok 7 - boolのfalseを渡しても空文字列にキャストされる
 Looks like everything went fine.
次回はもう少し掘り下げてlimeをご紹介します。

auとSoftBankの絵文字一覧YAML
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

auとSoftBankの絵文字については、下記のページに公式の情報が掲載されています。しかし、情報がPDFであったり、HTMLであったりと、プログラムから使うのが難しいという問題があります。

そこでこのPDFとHTMLをYAMLにしてみました。

諸般の事情により公開を停止しました。代わりに、同様の情報の取得方法に関する記事へのリンクを紹介いたします。

2007年10月25日

symfonyでgettext
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

yamaokaです。最近、PHPのフレームワークのsymfonyをいじっています。

symfonyでメッセージの国際化を行う場合、 デフォルトではXLIFFの形式を使うようになっています。 その他、gettextやデータベースからメッセージリソースを取得できるようにもなっているのですが、 その方法があまり公開されていません。

今回はgettextを利用してメッセージの国際化を行う方法を紹介します。 gettextに関する基本的な情報については、 Wikipediaの該当項目ウノウラボの過去記事 などを参考にどうぞ。実際の動作はsymfony 1.0.8で確認しています。

  1. apps/<yourapp>/config/settings.ymlに下記を追記
    all:
      .settings:
        i18n: on                   # 国際化を有効に
        standard_helpers: [I18N]   # ヘルパーとしてI18Nを追加
        charset: UTF-8             # 文字コード設定
    
  2. apps/<yourapp>/config/i18n.ymlを編集
    all:
      default_culture: ja_JP   # デフォルト言語を日本語に
      source: gettext          # gettextを使う設定
    
  3. テンプレートファイル内にメッセージを記述しておく
    <p><?php echo __('Hello!'); ?></p>
  4. アプリケーションディレクトリに移動
  5. xgettextコマンドを使ってi18n/messages.potを作成
    $ xgettext --keyword=__ -l php -o i18n/messages.pot \
       `find modules -name \*.php -type f`
    
  6. msginitコマンドを使ってi18n/ja_JP/messages.poを作成
    $ msginit -i i18n/messages.pot -o i18n/ja_JP/messages.po -l ja
  7. messages.poの文字コード設定の部分をUTF-8に変更、リソースを追記
    "Content-Type: text/plain; charset=UTF-8\n"
    msgid "Hello!"
    msgstr "こんにちはこんにちは!"
    
  8. msgfmtコマンドを使ってi18n/ja_JP/messages.moを作成
    $ msgfmt -o i18n/ja_JP/messages.mo i18n/ja_JP/messages.po
  9. 表示させてみる
    こんにちはこんにちは!

いかがでしょうか。XML形式でわかりやすいXLIFFもいいのですが、 Pootleなどの周辺ツールも揃っているので、 使い慣れたgettextを使うことができるメリットはまだまだあると思います。

symfonyでgettextを使おうと思っている方の手助けになれば幸いです。

2007年10月24日

daemonsで簡単デーモン
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

komagataです。

mongrelを入れるときに一緒に入るdaemonsというライブラリが気になったので調べてみました。

daemonsは大まかに、サーバープログラムをstart/stop/restartなどのおなじみのやり方で操作できるようにするラッパーの機能とプログラム中で使って自分のプロセスをデーモンにする機能があるそうです。

引数を出力し続けるプログラムをデーモンにしてみたいと思います。

puts_arg.rb:

#!/usr/bin/env ruby
 
loop do
  puts ARGV.inspect
  sleep(1)
end

実行結果。

$ ruby puts_arg.rb a b c
["a", "b", "c"]
["a", "b", "c"]
["a", "b", "c"]
(略)

これにdaemonsを使ってラッパーを用意します。

puts_arg_control.rb:

#!/usr/bin/env ruby
 
require 'rubygems'
require 'daemons'
 
Daemons.run('puts_arg.rb')

これでputs_arg.rbにたいしてstart/stop/restart/runなどの操作ができるようになります。(runは非デーモンの状態で動かします)

引数は下記のように'--'の後に渡せば管理対象のプログラムへの引数になります。

$ ./puts_arg_control.rb run -- a b c
["a", "b", "c"]
["a", "b", "c"]
["a", "b", "c"]
(略)

restartやstopのためにデフォルトではプログラムのあるディレクトリにpidファイルが作成されます。(この場所もDaemons.runメソッドのオプションで細かく設定できます)

自分自身をデーモン化する方法はdemonizeを実行するだけです。

#!/usr/bin/env ruby
require 'daemonize'
include Daemonize

daemonize()

loop do
puts ARGV.inspect
sleep(1)
end

やっている内容はこちらで説明されているのとほとんど変わりません。

以前書いたdRubyのサーバーもdaemonsで簡単にデーモン化できました。Rubyでちょこっとサーバーを書くときにお勧めです。

2007年10月23日

マシン語を学ぶ子ども達
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

尾藤正人(a.k.a BTO)です

コンピュータは低レベルのシステムを抽象化することで、どんどん扱いやすいものに進化してきました。 コンピュータの歴史は抽象化の歴史だと言い換えるのは大げさかもしれませんが、近いものはあるかもしれません。 ウノウで行うプログラミングはWebプログラミングがほとんどですが、WebプログラミングはPerl, PHP, Python, RubyのようなLLを使ってプログラムを書くことが多いかと思います。 LLは高度に抽象化されて低レベルで行っている処理を意識せずに記述することができる大変優れた言語です。 LLでプログラムを記述するにあたって低レベルのシステムを理解する必要があるとは思いませんが、知っていて損はない知識です。

少し前にマシン語についての話が話題になりました。 ちょうどタイミングよく社内勉強会の当番が回ってきたので、マシン語(というかアセンブリ言語を含む低レベルなシステムの話題)について勉強会をやってみることにしました。 せっかくなので資料を公開しますので、もしよかったらご覧下さい。

ざっくりとした内容は以下になります

  • コンピュータの簡単な歴史
  • CPUアーキテクチャ
  • MIPSのアセンブリ言語
  • デモ

通称で「パタヘネ本」と呼ばれる情報科学の教科書で世界的に広く使われている著名な本があるのですが、これを題材に作成しました。 パタヘネ本はコンピュータの基本的な仕組みを幅広く、しっかりと扱った素晴らしい名著です。 ぜひ、一読をおすすめします。 邦訳で第三版まで出ていますが、第三版は大幅に内容が削られてCDに収録されています。 僕は紙媒体で読みたかったので、あえて第二版を購入しました。

デモでは実際にMIPSエミュレータであるSPIMを使って、MIPSのアセンブリ言語を実行して出力結果の確認を行いました。

SPIM

実行例となるプログラムは大学の講義資料が公開されていましたので、そちらを利用させていただきました。

マイクロプロセッサおよび演習 (最終稿)

MIPSのようなRISC処理系は命令体系が簡単で非常に理解しやすくなっています。 x86系列は命令体系が複雑になってて、初めて学ぶには内容が難しいかもしれません。 まずはパタヘネ本を片手にMIPSを勉強する方が理解するには近道ではないでしょうか。

コンピュータの構成と設計―ハードウエアとソフトウエアのインタフェース〈上〉
ジョン・L. ヘネシー デイビッド・A. パターソン John L. Hennessy David A. Patterson 成田 光彰
日経BP社 (1999/05)
売り上げランキング: 53721
おすすめ度の平均: 5.0
5 コンピューターアーキテクチャのとても良い教科書
5 最も良い入門書
5 新人SE必読

2007年10月22日

iPhone/iPod touch対応サイトを構築するための情報
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちわ、山下です。

9月に発売された人気のiPod touchですが、私も即予約して購入してしまいました。二本の指で操作するインタフェースは、Mac OS X譲りの滑らかな動作と相まって非常に楽しいです。ベッドで寝る前にSafariでWebブラウジングしているうちに眠ってしまって、一緒に朝を迎えることもしょっちゅうです。iPhoneと違ってGoogle Mapsのアプリがない、カレンダーに予定を入力できないなど、いろいろ制限もあるのですが、今は自由で楽しいiPodライフを送っています。

さて今回は、世の中にiPhone/iPod touch対応サイトがもっと増えればいいなという願いを込めて、iPhone/iPod touch対応サイトを作るために参考になるページを紹介します。

iPhone向けWebアプリケーションとコンテンツの最適化
アップル本家によるiPhone向けWeb開発ガイドライン。まず最初に一読しておくことをお勧めします。
iPhone/iPod touchプログラミングメモ
Aptanaを利用したiPhoneプロジェクトの構築方法が日本語で解説されています。AIRによるiPhoneエミュレータは非常に便利かと。
iUI
iPhoneネイティブアプリのようなUIを実現するライブラリ。JavaScriptとCSS,画像が提供され、適切なclass/idを設定することでiPhone対応のページが簡単に作成できます。
iPhone Internet & Web Reference Library(英語)
Apple Developer Centerによる詳細な解説とサンプル。
WOiPhoneStore(英語)
WebObjects用のフレームワーク。「例のあれ(仮題)」さんのブログにて紹介されていました。
Dojo on the iPhone conference slides(英語)
JavaScriptライブラリDojoの開発者がAjaxWorld Westで明らかにしたiPhone対応について書かれています。こちらは10/31のDojo 1.0でリリースされるようです。

(おまけ) iPhone/iPod touchのUser-Agent

Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+
(KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3

Mozilla/5.0 (iPod; U; CPU like Mac OS X; ja-jp) AppleWebKit/420.1
(KHTML, like Gecko) Version/3.0 Mobile/3A110a Safari/419.3

それでは、皆さん良い脱獄ライフを!


2007/10/25追記:
iUIを利用した「映画生活 for iPod」を公開しました。映画のランキング情報などの他に、iPhone/iPod touch上で予告編動画の閲覧も可能ですので、ぜひご利用ください。

2007年10月19日

MagickWand For PHPで、PDFを画像にしてみる
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

MagickWand for PHPと、Imagick 2.0系はもともと同じ、ImageMagickののAPI群を利用しているため、片方でできることとは基本的にもう片方でもできるようになっています。

たとえば、PECL/Imagickのメンテナである、Mikko氏のblogのMikko's blogのCreating a PDF previewという記事の内容で、pdfの画像にしてプレビューするというのがありますが、これも、あっさり以下のような感じで移植ができます。


※ 多少コードを変えて、複数ファイルがあるときのために、ファイルに保存するようにしてみました。
    $image =  NewMagickWand();
    MagickReadImage($image, 'test.pdf');
    MagickSetFormat($image, 'png');
    MagickWriteImages($image, 'test.png');
プレビューを用意するときとかに使えそうです。

以前の、角丸画像を作るエントリーのコードも、Imagickのソースコードを読んで、ほぼそのままの移植で実装することができましたのでぜひ、皆さんも挑戦して、公開してみてください。

Mikko's blogは非常によさげなので、今後も参考にさせていただこうとおもいます。
ご参考になれば幸いです。

2007年10月18日

C/C++でFastCGIを作る
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは satoです。

現在 Ruby on Rails で書かれた アプリケーションの 一部のURIを高速化するために、lighttpd + FastCGI で 書き直しています。FastCGI は あらかじめ プロセスを常駐させておき、リクエストが来た際に、常駐しているプロセスに Unix domain socket あるいは TCP/IP で通信を行い プロセス起動時のオーバーヘッドを無くすことにより、処理を高速化します。今回は lighttpd + FastCGI で Hello word を作る 解説します。

まず lighttpd と FastCGI を 用意します 環境はCentOS5です。

lighttpd:
yum install lighttpd
FastCGI:
wget http://www.fastcgi.com/dist/fcgi.tar.gz
tar xzvf fcgi-2.4.0.tar.gz
cd fcgi-2.4.0
make
sudo make install

lighttpd の設定ファイル ligttpd.conf を編集します

vi /etc/lighttpd/lighttpd.conf 
server.modules              = (
  "mod_access",
  "mod_fastcgi",
  ""mod_accesslog")
(中略)
fastcgi.server = (
    "/" => (
        ("socket" => "/dev/shm/test.fcgi.socket.1",
         "check-local" => "disable")
   )
)

簡単に説明しますと、mod_fastcgiモジュールを有効にして、 / に来たリクエストを /dev/shm/test.fcgi.socket.1 に送ります

fastcgi の test.cpp のソースは以下です。

#include "fcgi_config.h"
#include 
#ifdef HAVE_UNISTD_H
#include 
#endif
#ifdef _WIN32
#include 
#else
extern char **environ;
#endif
#include "fcgi_stdio.h"
int main () {
    int count = 0;
    while (FCGI_Accept() >= 0) {
        printf("Content-type: text/html\r\n"
            "\r\n"
            "FastCGI HelloWorld"
            "

FastCGI HelloWorld

\n" "Request number %d, Process ID: %d

\n", ++count, getpid()); } return 0; }

簡単に解説しますと FCGI_Accept() は システムコール の accept と似たような感じでリクエストが発生すると 待ち状態から ループに入ります。次にprintfですが これは #include "fcgi_stdio.h" の中で

#define printf FCGI_printf

となっていて 実際には FCGI_printfが 呼ばれます。 FCGI_printf は 最終的には システムコールの write を呼んで 通信などを行います(windowsでは send関数)。あとは content-type を出力して おしまいです。fcgi はそのまま終了しないで FCGI_Accept に戻り、リクエストがあるのを待ちます

g++ -o test.fcgi test.c -lfcgi 

として test.fcgi の バイナリができたら、lighttpd に付属している spawn-fcgi というツールで fcgiを立ち上げます

sudo spawn-fcgi -f ./test.fcgi -s /dev/shm/test.fcgi.socket.1 2>&1 > /dev/null

これでアクセスできるはずです。何度もアクセスすると プロセスは終了しないので、プロセスIDは変わっていないのに、count は増えているかと思います。mysqlのconnectや設定ファイルの読み出しは ループの外で行いましょう。

勉強会の時にも出た質問なんですが、Apache module で作らない理由は、Apache にしか使用できないということと、そのサーバを他用途で使用するときに mod_test が読まれていると 無駄に connectionを張ってしまったりするためです。

2007年10月17日

PostfixでSMTP EHLOコマンドで送るドメイン名を変更する方法
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは、naoyaです。

今日は、Postfix 2系で有効なSMTP EHLOコマンドで送るドメイン名を変更する方法を紹介したいと思います。

最近、メールサーバのスパムチェック強化によって、SMTP EHLOコマンドで送るドメイン名が正引きできないとスパムメールと判定して受信を拒否するメールサーバが増えてきました。このようなメールサーバの場合、通常の設定で内部のメールサーバからメールを送信すると受信を拒否されてしまうことがあります。

Postfix 2系では、このEHLOコマンドで送るドメイン名を変更することができるようになっているので、さきほどのようなメールサーバでも対応することができます。

設定方法は、とても簡単でPostfixの設定ファイルmain.cf(Fedora Coreの場合は、/etc/postfix/main.cf)に、次の行を追加するだけです。

smtp_helo_name = ドメイン名

この設定を保存後、Postfixを再起動することで有効になります。


また、内部のメールサーバからメールを送信すると内部のドメイン名がメールヘッダに入り気になる人のために、この情報を破棄する方法もあわせて紹介します。

設定方法は、Postfixのheader_checks(Fedora Coreの場合は、/etc/postfix/header_checks)というファイルに設定を追加する必要がありますが、例えば次のように追加します。

/^Received: by s1.server.unoh.net.*/ IGNORE

この例は、Recevied by: s1.server.unoh.netで始めるメールヘッダを破棄するという設定例になります。もちろん見ての通り、破棄する対象のメールヘッダの設定は正規表現でも記述することができます。

あとは、main.cfにheader_checksを読み込むように追加するだけです。

header_checks = regexp:/etc/postfix/header_checks

Postfixはこのような柔軟な設定に加えて、パフォーマンスも非常によいのでウノウでは頻繁に利用しています。

2007年10月16日

色覚異常を考慮してWebColor216色を選択できる便利なサービス
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは! やまもと@テスト番長です。

このところ段々と秋らしくなってきて、ウノウでも風邪をひくスタッフが目立つようになってきました。皆さんもお体にはお気をつけ下さい。
さて、テストネタからちょっと離れますが、便利なWEBサービスがありましたのでご紹介します。

Color Laboratory -- AWARE Center -- HTML Writers Guild

Color Laboratory
Color Laboratory posted by (C)フォト蔵


ここでは色覚異常の場合の色の見え方を考慮しつつ、WebColor216色のカラーパレットから色の組み合わせを試してみることが出来ます。

右上のフレームでBaseline Gamma(OSの違いによるガンマ補正設定)を選択すると、色覚異常のタイプ設定が出ます。タイプを選択すると、操作にあわせて左上のフレームにあるカラーパレットが変化し、どんな風に見えているかをシミュレートしてくれます。更にカラーパレットの色をクリックすると下のフレームに選択した色が並び、組み合わせを試しやすくなっています。

ウェブページのスクリーンショットに補正をかけて表示してくれるサービスはちらほらありますが、色を選ぶ為のサービスは珍しいですね。
色覚異常は男性の20人に1人が先天的に持っているそうなので、規模の大きいサービスでは考慮しなくてはいけない問題のひとつです。もしアクセシビリティのテストをする機会があって、デザイナーさんの選んだ色に文句をつけなくてはいけなくなった時、このサイトを教えてあげると喜ばれそうです。

2007年10月12日

JavaScriptだけでWebサイトが構築できるHelma
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

初めまして。今月、ウノウに入社しましたNAKAMURAです。今後ともよろしくお願いします。

最近のWebサイトでは、DHTMLの操作やAjaxなど、JavaScriptが欠かせない存在となってきていますが、そんなJavaScriptでサーバーサイドも構築できるHelmaについて取り上げてみたいと思います。

Helmaとは?

HelmaはJetty上で動くJavaで実装されたフレームワークです。サイト構築にはJavaScriptを利用します。Apacheで例えるならmod_javascriptといったところでしょうか(Apacheモジュールとして動くJavaの実装にはmod_gcjというものもあります)。JavaScriptの実装には、Java6に組み込まれているRhinoが利用されています。

サーバーサイドでJavaScriptを利用するという特徴以外にも、一般的に必要とされるようなテンプレートやマクロなどの機能も兼ね備えています。

インストール

さっそくインストールをして動かしてみます。HelmaはWindowsやMacOSXでも動作しますが、ここではOSにLinux(Fedora6)を利用しています。

$ cd ~/
$ wget http://adele.helma.org/download/helma/1.6.0/helma-1.6.0.tar.gz
$ tar zxvf ./helma-1.6.0.tar.gz

適当な場所にダウンロードをして解凍します。インストール作業自体はこれで終了となります(ただし、Javaの1.4以上がインストールされている必要があります)。

それでは、Helmaを起動してみます。

$ cd ./helma-1.6.0
$ ./start.sh

start.shの中でjarを実行しています。デフォルトではポートが8080になっていますので、これで「http://localhost:8080/」にブラウザでアクセスすることができます。

Helma Welcome Page
Helma Welcome Page posted by (C)フォト蔵

アクセスすると、このようなWelcomeページが開きます。このWelcomeページ自体がサンプルアプリケーションのようになっていますので、構成を理解するには、apps/welcome配下を確認すると良さそうです。

Helma入門

ここでは、新規にページを作成して、スキンファイルの読み出しまでをやってみます。

まず、apps.propertiesに以下のように記述します(manageやwelcomeの設定は先頭に#を付けてコメントアウトしています)。

hello
hello.mountpoint = /
hello.repository.0 = apps/hello/code/

次にルートディレクトリを作成します。

$ mkdir -p ./apps/hello/code/Root

呼び出されるコードを配置します。「./apps/hello/code/Root/sample.hac」というファイルを作成して、以下のように記述します。

res.write("Hello World");

これで、「http://localhost:8080/sample」にアクセスして「Hello World」と表示されれば成功です。.hacを取り除いた名前がURIと対応しています。

.hacファイルはJavaScriptで記述します。JavaScriptからの出力ができたところで、テンプレートを呼び出してみます。

「./apps/hello/code/Root/sample.hac」を以下のように変更します。

function plus(a, b) {
  return a + b;
}
this.result = plus(10, 20);
this.renderSkin("sample");

次に、スキン(テンプレート)ファイルを作成します。「./apps/hello/code/Root/sample.skin」というファイルを作成して、以下のように記述します。

<html>
<body>
  <p>Result: <% this.result %></p>
</body>
</html>

「http://localhost:8080/sample」にアクセスして「Result: 30」と表示されれば成功です。このようにスキンファイルを利用することで、JavaScriptのロジックとHTMLの出力を分離することができます。また、ここでは触れていませんが、JavaScript側でリクエストやレスポンスのオブジェクトを参照することもできます。

まとめ

サーバーサイドにJavaScriptが利用できるフレームワーク、Halmaについて紹介しました。設定ファイルによるセッティングやマクロの定義など、一通りは実装されているようです。

国内で採用された例は今のところ聞いたことがないのですが、ご興味の湧いた方は一度触ってみてはいかがでしょうか。

開発に関するドキュメントについては、本家サイトにまとめられています。

また、、日本語の情報としては、以下のエントリが良くまとまっていましたので、参考にさせて頂きました。感謝いたします。

2007年10月11日

procmailを使ったメール処理
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

shimookaです。 今回はメールの自動振り分けによく使われるprocmailについて書いてみました。

procmailとは?

 procmailは古くからあるメールフィルタソフトです。主にメールの振り分けを行うために利用されます。  procmailは2001/09/10にリリースされたver.3.22が最新版となっていて、最も広く普及しているMDAの一つです。多くのLinuxディストリビューションでもrpm・devパッケージが用意されています。また、Postfixのパッケージなどでは、あらかじめ設定が用意されていたりします。

procmailの基本的な使い方

 procmailを使ってメールを振り分ける場合、$HOMEディレクトリ直下に「.procmailrc」というファイルを作成し、振り分け条件や条件にマッチした場合の処理を記述します。  .procmailrcの書式は以下の通りで、procmailではレシピ(recipe)と呼んでいるようです。
:0 [フラグ] [:[ローカルロックファイル名]]
[条件]
[条件]
  :
[処理内容]
 フラグには条件の対象の制限(メールヘッダ、ボディ)やレシピの実行可否などを指定します。条件にはマッチさせる正規表現を記述します。なお、条件は1行に1つ、行頭は「*」である必要があります。処理内容には、メール転送やパイプを使って外部コマンドにメール内容を引き渡すことも可能です。以下は、.procmailの簡単なサンプルです。
SHELL=/bin/sh

# サービス用サーバからのメール、かつ、Subjectの先頭が文字列"[ALERT]"
# である場合、何はともあれ、携帯に転送する。。。 orz
# (ただし、日本語メールは考慮していない)
:0H:
* ^From: user\@example\.com
* ^Subject: \[ALERT\]
!example@xxxxxx.ne.jp

# デフォルトの転送先。
# 書かなければ、$MAILDIRへ(通常はローカルのメールボックス)
:0
!myaddress@example.com
 .procmailrcファイルの具体例ですが、一番知られている例はspamassassinとの組み合わせでしょうか。以下のような感じです。

SHELL=/bin/sh
LOGFILE=$HOME/.procmailrc.log
SPAM=$HOME/imap/SPAM_Trash
DOUBT=$HOME/imap/doubt

:0fw: spamassassin.lock
| spamassassin

:0H:
* ^X-Spam-Level: \*\*\*\*\*\*\*\*\*\*+
* ^X-Spam-Flag: YES
/dev/null

:0H:
* ^X-Spam-Flag: YES
$SPAM

:0H:
* ^X-Spam-Level: \*\*\*\*\*\*\*\*
$SPAM

:0
* ^Subject:.*\[SPAM\]
$SPAM
 なお、.procmailrcの詳しい書式については、procmailrc(5)もしくはJM Projectのページを参照してください。

procmailでエラーメール処理を考えてみる

 いきなり話が飛びますが、多くのサービスで「メールを使ってユーザーに通知する」ということを行っていると思います。メルマガもそうですね。しかし、メールアドレスが変わったとか登録時の入力ミスなど、様々な理由で配信できないメールの処理に頭を悩ませることになりがちです。  ここで、procmailを使ったエラーメール処理を考えてみると、エラーとなった送信先アドレスを抽出することはできそうです。ただし、メールヘッダだけエラーメールかどうかの判断が難しいので、メールBodyで判断することになります。

SHELL=/bin/sh
ADDRESSES_FILE=$HOME/error_addresses.log

:0 Bi
* ^.*The Postfix program
* ^.* said: 5[0-5][0-9]
* ^.*\.\.\. Unknown user
* Final-Recipient: rfc822; \/.*
| echo "$MATCH" >> $ADDRESSES_FILE

:0 Bi
* ^\/<[^>]+>:
* ^User Not Found
| echo "$MATCH" | sed -e "s/.*<\([^>]\{1,\}\)>.*/\\1/g" >> $ADDRESSES_FILE
 かなりベタベタなレシピですが、これをエラーメール受信専用のアカウントの.procmailrcとして用意しておけば、送信先アドレスをうまく抽出することができます。その後は、出力したファイルをバッチでDBに登録するなどが可能になります。また、上の例はパイプでファイルに出力していますが、そのままDB登録用のコマンドに渡してしまうことも可能と思います。  ただし、送信先のMTAによってメッセージが変わったりしますので、残念ながら、実際には上記だけで全て対応できません。もし、もっとスマートな方法をご存じの方は、コメントやトラックバックなどで教えて下さい ;-)

まとめ

 自宅サーバなどでimapを使っている方は、メール振り分けなどですでにprocmailを使っているかも知れませんね。ただ、メールの振り分けだけではなく、パイプで外部コマンドに渡してやると様々な処理ができます。その組み合わせ次第で、もっと簡単に面白いことができるんじゃないかと思っています。  是非、一度お試しを:-)

参考URL

 以下のサイトを参考にさせていただきました。改めて感謝いたします。

2007年10月10日

Wikiのプラグイン記法を実装する
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

bokkoです。

普段からWikiを使っていると、「○○する記法がほしいなあ」と思うことがあると思います。
しかし、単純に記法を追加しようとすると、ほかの記法とぶつかってしまったり、思わぬ副作用を引き起こす可能性があるため、大抵のWikiクローンには独自でプラグインを作るための仕組みがあります。
例えば、どこかにplugin_nameというスクリプトファイルを用意し、{{plugin_name 引数}}と書かれた部分をそのスクリプトファイルを実行した結果に置き換える、という風に。

というわけで、今日は私がローカルで使っているオレオレWikiクローンでのプラグイン機構の実装例を紹介しようと思います。実現するためのコードは以下のようになっています。(ほかの機能のためのコードが混ざってしまっていてちょっと見づらいですが)

private function createPlugin(){
    $dir = Config::GetPluginDir().'/';
    $hd = null;
    $file = null;
    $str = 'if($hd = opendir($dir)){
                  while(false !== $file = readdir($hd)){
                      if($file === \\\'\\2.php\\\'){
                          require_once($dir.$file);
                      }
                  }
              }
              if(function_exists(plugin_\\2)) return \\\'\\1\\\'.plugin_\\2(\\\'\\3\\\', $this->plugined_file).\\\'\\4\\\';
              else return \\\'Not Found!\\4\\\';';
    // 引数なし 例:{{lastupdate}}
    $this->text = preg_replace("/([^<])\{\{([^\{ ]*)()\}\}([^>])/e",
                                          "eval('$str')",
                                          $this->text);
     // 引数あり 例:{{strlen abc}}
     $this->text = preg_replace("/([^<])\{\{([^\{]*)\s([^\{]*)\}\}([^>])/e",
                                           "eval('$str')",
                                           $this->text);
} 

まず、テキストの中{{plugin_name}}、または{{plugin_name 引数}}というのがあれば、その中身をevalに放り込みます。その後、plugin_nameの部分に該当するファイル名と関数名があれば、その関数を実行し、結果を返します。ファイルや関数が存在しない場合は、「Not Found!」という文字列を返します。(実際に実用的なプラグインの仕組みを作る場合はもっと厳密な決まりが必要だと思います)

時刻を取得する場合は、以下のようなコードになります。

function plugin_lastUpdate($arg, $file){
    return date("Y/m/d H:i:s");
}

Ext.js入門: Grid編
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

Extは、ウェブアプリケーションを構築するための、クライアントサイドのJavaScriptフレームワーク公式サイトのFAQより)です。日本ではまだ実際の利用例はあまり多くないようですが、たぶんに日本語のドキュメントがまだそれほど多くはないからではないかと思われ、今後利用事例は増えていくだろうと思われます。

Extの真骨頂は高機能でデザインの洗練された、(直ちに利用可能な)レディメイドのユーザインタフェースウィジェット群です。Extは「アダプタ」として、prototype.jsやjQuery、Yahoo! UIといったJavaScript拡張ライブラリを利用する設計ですが、現在は独自のアダプタも用意され、サードパーティのライブラリに依存しない実装も可能です。

現時点での安定版はバージョン1.1ですが、既に、大幅な機能強化が図られたバージョン2.0のアルファ版が公開されており、以下のページでそのサンプルを見ることができます。

以下ではExtの数あるウィジェットの中でももっともポピュラーなもののひとつであると思われる「グリッド」(表組み)のサンプルコードを紹介します。

Extのグリッドは、列見出しのクリックによる並べ替えやその境界をドラッグすることでの列幅の変更、そして列見出しの右クリックで表示される、こうした機能をまとめたコンテキストメニューなど、表計算ソフトを思わせる豊富な機能があらかじめ用意されています。

なお、以下のサンプルコードは、SCRIPT要素とLINK要素の「path/to/ext/」となっている箇所を実際にExtを配置したディレクトリ名に変更して利用してください。

シンプルなグリッド

以下の例は、あらかじめ定義済みの配列をグリッドで表示します(これは公式サイトのチュートリアル入門編にある例の、ほぼそのままです)。

Extではデータを、データストア(Ext.data.Store)というオブジェクトに保持します。配列をデータストアに読み込むには、以下のようにExt.data.MemoryProxyとExt.data.ArrayReaderを使用します。データがグリッドでどのように表示されるかは、カラムモデル(Ext.grid.ColumnModel)で定義します。

なお以下の画像では、列見出しを右クリックしてコンテキストメニューを表示していますが、メニュー項目が日本語で表示されているのが確認できると思います。Extの構成ファイルには各国語のリソースファイルも含まれており、日本語のリソースファイル(source/locale/ext-lang-ja.js)を読み込むようにSCRIPT要素を追加すれば、表示の多くが日本語化されるようになっています。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta http-equiv="Content-Style-Type" content="text/css">
    <meta http-equiv="Content-Script-Type" content="text/javascript">
    <title>TEST</title>
    <script type="text/javascript" src="path/to/ext/adapter/ext/ext-base.js"></script>
    <script type="text/javascript" src="path/to/ext/ext-all.js"></script>
    <script type="text/javascript" src="path/to/ext/source/locale/ext-lang-ja.js"></script>
    <link rel="stylesheet" type="text/css" href="path/to/ext/resources/css/ext-all.css">
    <script type="text/javascript">
    <!--
    /**
     * サンプルデータ
     */
    var myData = [
        ['Apple',       29.89,  0.24,   0.81,   '9/1 12:00am'],
        ['Ext',         83.81,  0.28,   0.34,   '9/12 12:00am'],
        ['Google',      71.72,  0.02,   0.03,   '10/1 12:00am'],
        ['Microsoft',   52.55,  0.01,   0.02,   '7/4 12:00am'],
        ['Yahoo!',      29.01,  0.42,   1.47,   '5/22 12:00am']
    ];

    /**
     * DOMの読み込みが完了した時点で実行される
     */
    Ext.onReady(function() {

        /**
         * データストア
         */
        var ds = new Ext.data.Store({
            /**
             * データストアに配列を読み込む
             */
            proxy:  new Ext.data.MemoryProxy(myData),
            reader: new Ext.data.ArrayReader(
                {id: 0},
                [
                    {name: 'company'},
                    {name: 'price',         type: 'float'},
                    {name: 'change',        type: 'float'},
                    {name: 'percentChange', type: 'float'},
                    {name: 'lastChange',    type: 'date', dateFormat: 'n/j h:ia'}
                ]
            )
        });

        /**
         * カラムモデル
         */
        var cm = new Ext.grid.ColumnModel(
            [
                {header: 'Company',      width: 320, dataIndex: 'company'},
                {header: 'Price',        width: 140, dataIndex: 'price'},
                {header: 'Change',       width: 80,  dataIndex: 'change'},
                {header: '% Change',     width: 80,  dataIndex: 'percentChange'},
                {header: 'Last Updated', width: 180, dataIndex: 'lastChange', renderer: Ext.util.Format.dateRenderer('Y/m/d')}
            ]
        );
        cm.defaultSortable = true;

        /**
         * グリッド
         */
        var grid = new Ext.grid.Grid(
            'grid-example',
            {ds: ds, cm: cm}
        );

        /**
         * グリッドの描画
         */
        grid.render();
        ds.load();
    });
    //-->
    </script>
  </head>
  <body>
    <div style="margin:10px;padding:10px;border:1px solid #000">
      <h1>Ext.grid Example #1</h1>
      <p>A very simple grid.</p>
      <div id="grid-example" style="overflow:hidden;width:800px">
        The example grid will be placed here.
      </div>
    </div>
  </body>
</html>

Ajaxなグリッド

以下の例では、あらかじめ用意してある配列に代えて、Ajaxで読み込んだXMLのデータを表示します。これにはデータストアへのデータの読み込みに、以下のようにExt.data.HttpProxyとExt.data.XmlReaderを使用します。ちなみに、データがJSON形式の場合はExt.data.JsonReaderを使用します。また、Ext.data.ScriptTagProxyを使用してクロスドメインのアクセスも行えます。

以下の例は、Amazon Webサービスで取得したXMLデータが「amazon.xml」というファイル名でこのHTMLと同じディレクトリにあるという構成が前提になっています。

また、以下の例ではカラムモデルにコールバック関数を設定し、アイテムの題名をそのまま表示するのではなく、そのアイテムのAmazon.co.jpのページへのハイパーリンク付きで表示しています。さらに、グリッドに「rowdblclick」イベントを設定し、列をダブルクリックすることでもそのアイテムのページを表示するようにしています。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta http-equiv="Content-Style-Type" content="text/css">
    <meta http-equiv="Content-Script-Type" content="text/javascript">
    <title>TEST</title>
    <script type="text/javascript" src="path/to/ext/adapter/ext/ext-base.js"></script>
    <script type="text/javascript" src="path/to/ext/ext-all.js"></script>
    <script type="text/javascript" src="path/to/ext/source/locale/ext-lang-ja.js"></script>
    <link rel="stylesheet" type="text/css" href="path/to/ext/resources/css/ext-all.css">
    <script type="text/javascript">
    <!--
    /**
     * DOMの読み込みが完了した時点で実行される
     */
    Ext.onReady(function() {

        /**
         * データストア
         */
        var ds = new Ext.data.Store({
            /**
             * データストアにAjaxでXMLを読み込む
             */
            proxy:  new Ext.data.HttpProxy(
                {url: 'amazon.xml'}
            ),
            reader: new Ext.data.XmlReader(
                {id: 'ASIN', record: 'Item', totalRecords: '@total'},
                [
                    'DetailPageURL',
                    {name: 'Title',        mapping: 'ItemAttributes > Title'},
                    {name: 'Author',       mapping: 'ItemAttributes > Author'},
                    {name: 'Manufacturer', mapping: 'ItemAttributes > Manufacturer'}
                ]
            )
        });

        /**
         * カラムモデル
         */
        var cm = new Ext.grid.ColumnModel(
            [
                {header: '題名',   width: 480, dataIndex: 'Title', renderer: formatTitle},
                {header: '著者',   width: 140, dataIndex: 'Author'},
                {header: '出版社', width: 180, dataIndex: 'Manufacturer'}
            ]
        );
        cm.defaultSortable = true;

        /**
         * グリッド
         */
        var grid = new Ext.grid.Grid(
            'grid-example',
            {ds: ds, cm: cm}
        );
        grid.on('rowdblclick', openWindow, this);

        /**
         * グリッドの描画
         */
        grid.render();
        ds.load();
    });

    /**
     * カラムモデル用コールバック関数
     */
    function formatTitle(val, metadata, record) {
        return '<a href="' + record.data['DetailPageURL'] + '">' + val + '<\/a>';
    }
    function openWindow(grid, rowIndex) {
        window.open(grid.getDataSource().getAt(rowIndex).data.DetailPageURL, 'foo');
    }
    //-->
    </script>
  </head>
  <body>
    <div style="margin:10px;padding:10px;border:1px solid #000">
      <h1>Ext.grid Example #2</h1>
      <p>Loading XML into a grid w/ Ajax.</p>
      <div id="grid-example" style="overflow:hidden;width:800px">
        The example grid will be placed here.
      </div>
    </div>
  </body>
</html>

ドラッグ&ドロップなグリッド

以下の例は、ふたつのグリッドを並べて表示し、ドラッグ&ドロップ操作で、グリッド間で各行のデータを移動できるようにしたものです。同じ構造のグリッドをふたつ表示するので、データストアとグリッドをふたつずつ定義し、各グリッドのパラメータには「enableDragDrop」と「ddGroup」を設定しています。そしてExt.dd.DropTargetで、各グリッドのドラッグ&ドロップ時の動作を定義しています。

なお、上のふたつの例ではグリッドを単体でそのまま(DIV要素内に)表示していましたが、以下の例では表示にレイアウトマネージャ(Ext.BorderLayout)を使用しています(レイアウトマネージャの詳細は今回は端折ります)。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="ja">
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta http-equiv="Content-Style-Type" content="text/css">
    <meta http-equiv="Content-Script-Type" content="text/javascript">
    <title>TEST</title>
    <script type="text/javascript" src="path/to/ext/adapter/ext/ext-base.js"></script>
    <script type="text/javascript" src="path/to/ext/ext-all.js"></script>
    <script type="text/javascript" src="path/to/ext/source/locale/ext-lang-ja.js"></script>
    <link rel="stylesheet" type="text/css" href="path/to/ext/resources/css/ext-all.css">
    <script type="text/javascript">
    <!--
    /**
     * DOMの読み込みが完了した時点で実行される
     */
    Ext.onReady(function() {

        /**
         * データストア
         */
        var ds1 = new Ext.data.Store({
            /**
             * データストアにAjaxでXMLを読み込む
             */
            proxy:  new Ext.data.HttpProxy(
                {url: 'amazon-small.xml'}
            ),
            reader: new Ext.data.XmlReader(
                {id: 'ASIN', record: 'Item', totalRecords: '@total'},
                [
                    'DetailPageURL',
                    {name: 'Title',        mapping: 'ItemAttributes > Title'},
                    {name: 'Author',       mapping: 'ItemAttributes > Author'},
                    {name: 'Manufacturer', mapping: 'ItemAttributes > Manufacturer'}
                ]
            )
        });
        ds2 = new Ext.data.Store({
            reader: new Ext.data.ArrayReader({}, [
                'DetailPageURL',
                'Title',
                'Author',
                'Manufacturer'
            ])
        });

        /**
         * カラムモデル
         */
        var cm = new Ext.grid.ColumnModel(
            [
                {header: '題名',   dataIndex: 'Title', id: 'Title', renderer: formatTitle},
                {header: '著者',   dataIndex: 'Author'},
                {header: '出版社', dataIndex: 'Manufacturer'}
            ]
        );
        cm.defaultWidth = 150;
        cm.defaultSortable = true;

        /**
         * グリッド
         */
        var grid1 = new Ext.grid.Grid(
            'grid-example',
            {ds: ds1, cm: cm, enableDragDrop: true, ddGroup: 'ddGroup1', autoExpandColumn: 'Title', loadMask: {msg: 'しばらくお待ちください'}}
        );
        grid1.on('rowdblclick', openWindow, this);
        var grid2 = new Ext.grid.Grid(
            'grid-example2',
            {ds: ds2, cm: cm, enableDragDrop: true, ddGroup: 'ddGroup2', autoExpandColumn: 'Title'}
        );
        grid2.on('rowdblclick', openWindow, this);

        /**
         * ドラッグ&ドロップ
         */
        var grid1dd = new Ext.dd.DropTarget(grid1.container, {
            ddGroup: 'ddGroup2',
            notifyDrop: function(dd, e, data) {
                for (var i = 0; i < data.selections.length; i++) {
                    if (!ds1.getById(data.selections[i].id)) {
                        ds1.add(data.selections[i]);
                        ds2.remove(data.selections[i]);
                    }
                }
                grid2.getSelectionModel().clearSelections();
                return true;
            }
        });
        var grid2dd = new Ext.dd.DropTarget(grid2.container, {
            ddGroup: 'ddGroup1',
            notifyDrop: function(dd, e, data) {
                for (var i = 0; i < data.selections.length; i++) {
                    if (!ds2.getById(data.selections[i].id)) {
                        ds2.add(data.selections[i]);
                        ds1.remove(data.selections[i]);
                    }
                }
                grid1.getSelectionModel().clearSelections();
                return true;
            }
        });

        /**
         * レイアウト
         */
        layout = new Ext.BorderLayout(document.body, {
            north: {
                split:false,
                initialSize: 60,
                titlebar: false
            },
            center: {
                titlebar:    true,
                resizeTabs:  true
            },
            east: {
                split:true,
                initialSize: 400,
                titlebar:    true,
                collapsible: true,
                collapsed:   true,
                animate:     true
            },
            south: {
                split:false,
                initialSize: 30,
                titlebar: false
            }
        });
        layout.add('north',  new Ext.ContentPanel('header'));
        layout.add('center', new Ext.GridPanel(grid1, {title: '検索結果'}));
        layout.add('east',   new Ext.GridPanel(grid2, {title: 'ドラッグ &amp; ドロップ'}));
        layout.add('south',  new Ext.ContentPanel('footer'));

        /**
         * グリッドの描画
         */
        grid1.render();
        grid2.render();
        ds1.load();
    });

    /**
     * カラムモデル用コールバック関数
     */
    function formatTitle(val, metadata, record) {
        return '<a href="' + record.data['DetailPageURL'] + '">' + val + '<\/a>';
    }
    function openWindow(grid, rowIndex) {
        window.open(grid.getDataSource().getAt(rowIndex).data.DetailPageURL, 'foo');
    }
    //-->
    </script>
  </head>
  <body>
    <div id="header" style="padding:5px">
      <h1>Ext.grid Example #3</h1>
      <p>Drag &amp; drop between two grids in the layout panel</p>
    </div>
    <div id="grid-example" style="overflow:hidden;width:400px">
      The example grid #1 will be placed here.
    </div>
    <div id="grid-example2" style="overflow:hidden;width:400px">
      The example grid #2 will be placed here.
    </div>
    <div id="footer" style="padding:5px">
      <address style="font-size:12px;font-style:oblique;text-align:right">
        2007 Unoh, Inc. All rights reserved.
      </address>
    </div>
  </body>
</html>

なおFirebugがインストールされたFirefoxでは、Extのデバッグコンソールが利用できます。HTMLのSCRIPT要素で「ext-all.js」を読み込んでいるところを「ext-all-debug.js」を読み込むように変更しておくと、Control+Shift+Homeキーをタイプするとデバッグコンソールが表示されます。ただしExtを使用したページは、FirebugがインストールされたFirefoxではかなり表示が遅くなるので、その点は注意が必要です。

2007年10月 5日

ブラウザでER図が描ける「WWW SQL Designer」紹介
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

miyakeです。皆さんドキュメントはしっかり書いていますか? 近頃のWeb界隈では、詳細仕様書的なものは作らずにスピーディーに開発を進めるケースも多いかも知れません。ドキュメント作成のコストは小さくありませんし、内容を最新に保つのも一苦労です。とは言え、複数の人間が関わるプロジェクトにはER図はあると嬉しいなと個人的には思います。

今日はそんなER図をブラウザ上で作成するアプリケーション「WWW SQL Designer」をご紹介します。

以下のスクリーンショットは、このアプリを実際に触って試せるlive DEMOのものです。

WWW SQL Designer
WWW SQL Designer posted by (C)フォト蔵

上部のメニューからテーブルやカラムを作ったり消したりして、テーブルをドラッグアンドドロップで配置、リレーションを同じくドラッグアンドドロップで設定、というのがおおまかな作業の流れになります。まずは実際にデモを触ってみるのが一番分かりやすいかと思います。

■動作環境

live DEMO上でも作業は可能ですが、実際に使う場合はダウンロードして自分のサーバ上で動作させるのが一般的でしょう。

WWW SQL Designerはphpで書かれているので、phpが動く環境が必要です。php5でしか試していないのですが、恐らく4でも動くのではないかと思います。

また、データベース内にER図の情報を保存する場合はMySQLが必要となります。

■インストール&初期設定

配布されているzipファイルをダウンロードして、しかるべき場所に展開すればとりあえず動きます。

ER図のデータをDBに保存する場合は、io.phpの初めの方にある

/* mysql: */
    define('SERVER','localhost');
    define('USER','');
    define('PASSWORD','');
    define('DB','');
    define('TABLE','wwwsqldesigner');
    $conn = mysql_connect(SERVER,USER,PASSWORD);
    mysql_select_db(DB);
/**/

という部分のUSER, PASSWORD, DBにそれぞれユーザ名、パスワード、DB名を書き込みます。

また、僕は試していないのですが、ODBC経由でDBの情報をインポートする機能があるようで、その設定はimport.phpの初めの方に、

$conn = odbc_connect('','','');

として用意されています。

■基本的な操作

直感的な操作が出来るようになっているのですが、最初は分かりづらいかも知れないので(特にリレーションの設定)まとめておきます。

テーブル作成
New tableをクリックした後で、作業スペースをクリックするとその場所にテーブルが出来る
テーブル削除
テーブルをクリックで選択して、Delete tableをクリック
カラムの作成
テーブルを選択して、Add rowをクリック
カラムの削除
カラムを選択して、Delete rowをクリック
カラムの位置変更
カラムを選択して、Up, Downをクリック
リレーションの設定
PrimaryKeyに設定したカラムを選択して、そのままドラッグして結合対象のテーブル上でボタンを放す

■データの書き出しと読み込み

WWW SQL Designerは、ER図のデータをXMLとして扱います。XMLでデータを管理する場合は、IMPORT/EXPORTメニューから Export XML あるいは Import XML を選択して GO! をクリックします。するとサブウィンドウが表示されるので、書き出しの場合はコピーして保存、読み込みの場合は貼り付けてLoadをクリックします。

DBに保存する場合は、Save to database, Save As... to database, Load from databaseでそれぞれ操作します。途中でKeywordの入力を求められますが、これがそのER図の名前になります。

DBから読み込む場合はURLパラメータのkeywordで指定してやると、ブラウザで開いた時にそのデータが読み込まれます。

DBに保存した場合も、XMLをそのまま格納しているだけなので、どちらを使ってもいいでしょう。

csvの読み書きには対応していないので、必要であればメニューを改造して実装するか、XMLと相互変換するスクリプトを組むことになります。

■使い方の例

あくまで個人的な使い方ですが、参考までにご紹介します。

まず、ER図はプロジェクトの初期段階で描き起こします。データは日付毎に保存したり、確認・修正が容易なのでXMLファイルで運用します。

既存のプロジェクトで利用する場合は、XMLにテーブルとカラムの一覧を書き起こして、それをImport from XMLで読み込ませました。場合によっては、ちゃんと設定した上でODBC経由でimportした方が便利かも知れません(やってないので何とも言えません)。

WWW SQL Designerでは各カラムの型や文字数も設定可能ですが、操作が面倒なのと、ブラウザ上ではそのカラムを選択しないと内容が見えないので、そこはあえて無視することにしました。ただ、数値型・文字型・日付型・その他でカラムの色が変わるので、そこは選択しておきます。

ある程度まとまった時点でGenerate MySQL scriptでSQLとして書き出します。各カラムの設定をちゃんとしていないので、後はテキストエディタで適宜設定やコメントを書いていきます。

開発を進めていく上でDBを変更する場合は、ER図とSQLの二重メンテが発生しますが、そこは割り切って両方手を入れます。ER図の側でカラム設定を手抜きするので、それほど手間ではありません。

残念ながらER図を画像として出力する機能はありませんので、最終的なER図はスクリーンショットから作ることにします。

IMPORT/EXPORTメニューからOpen print viewを実行すると、上部のメニューと右下の全体図が消えます。この状態でスクリーンショットを取ります。消えたメニューを元に戻す方法は無さそうなので、ブラウザで再読込するしかないようです。データの保存は前もって!

図が1画面に収まらないケースがほとんどだと思いますが、そこはFirefoxのアドオンなどでクリアします。ちなみに、僕はScreengrab!を使っています。

そのままだと大きすぎるので、Fireworksでリサイズして、必要ならば注釈などを加えた上で画像として保存します。これでER図の完成です。ここの処理はいくらでもやりようがあるので、お好きな方法でどうぞ。

■リレーションの制限を解除する

2007年10月5日現在の最新版であるVersion 1.3.4では、外部キーとして設定出来るのはPrimaryKeyに指定したカラムだけとなっているようです。

「テーブルには全てintでauto_incrementなidをPKとして設定し、結合はそのidで行う」というルールでDBを設計している場合はあまり困ることはないと思いますが、この制限を解除したい場合はJavaScriptを書き換えることで対応出来ます。

現在、PrimaryKeyに指定していないカラムではドラッグ系のイベントが働かないようになっているらしく、その判定をしないようにしてやればOKです。

具体的には、js/main.jsの153~155行目、


if (table_array[number].rows[rownumber].pk) {
    drag_start = src;
}

となっている部分のifを外して、

drag_start = src;

だけにします。ただし、この状態だと同じテーブル内で間違えて、少しでもドラッグアンドドロップをしてしまうと、同一テーブル内での参照が作れてしまうので少し注意が必要です。

■まとめ

あまり複雑なER図には対応出来なかったり、Webアプリであるが故の制約もありますが、これだけのソフトがフリーで公開されているというのは嬉しいことです。

また、WWW SQL Designerは現在も積極的に開発が進められており、今後の機能増強も期待出来ます。

個人的には非常にありがたく使わせてもらっていますので、ER図を描くツールに困っている方は一度試してみてはいかがでしょうか?

2007年10月 4日

Adobe MAX で明らかになった次世代Flashの世界
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは! yamazakiです。
毎度このブログでは日ごろの仕事にちょっと役立つかもしれないデザイン話をあれこれとやらせていただいているのですが、今回はちょっと趣を変えて。

日ごろの仕事はどちらかというとデザイン画像を作ったり、HTMLやCSSのコードを書いたりといったことが多いのですが、個人的にFlashが好きで、ActionScriptやFlex関係の勉強会などにもお邪魔させていただいたり、Flash関係の情報を集めていたりしています。
9/30~10/3にアメリカのシカゴで開催されたAdobe MAXというイベントで、Flashを中心としたAdobe製品についての発表がありまして、様々な新しい情報が出てきました。
このブログをご覧いただいている方の中にも、Flex/FlashやSilverlightなどといった技術の活用やRIA方面に関心のある方もいらっしゃるかと思いますし、趣味と実益を兼ねて、捕捉している限りの発表内容をざっくりまとめてみました。

ちなみにより詳しい内容はメディアテクノロジーラボ さんなどに、実際にMAXに参加された方による記事・映像へのリンクなどもありますのでぜひご覧くださいませ。



まずは以前からアナウンス済みのものから

FlashPlayer9 update

こちらはすでにアナウンス済み。H.264ビデオ、HD対応、AAC対応などメディアプレーヤとしての機能がぐんと拡充された感じです。
Adobe Labsにてベータ版公開中。

Adobe Media Player

こちらも以前にアナウンス済みですね。Adobe謹製のメディアプレイヤーです。映像配信の際のブランディングができるなど、Adobeらしい特徴を持ったメディアプレイヤーに仕上がっている模様です。プレリリース版がAdobe Labsで公開中 です

Flex 3 SDK/Flex Builder 3

こちらは以前からベータ版がAdobe Labsに公開されてましたが、MAXにあわせてbeta2が公開になりました 。Flex Builderのロードマップなどについてはflex.orgwiki に色々とまとまってますのでご参考までに。

Photoshop Express

Photoshopの簡易オンライン版。簡易版といいながら写真の補正等の目的では使えそうな内容みたいでした。

Flash lite3.0

携帯電話向けFlashの新版。flv再生できるというのが一番のポイントですね。
国内だとドコモが対応を発表しているとのことですが、いったいいつ乗ってくるのやら。


そして今回のMAXで初めて明かされたもの

FlashPlayer10(Astro)

今回のMAXではじめて明らかになったFlashPlayerの次期バージョンです。
  • 画像などを3次元的に扱うためのAPI
  • 文字組み機能が強化
など、デザイナとしては表現力の強化に期待できそうな機能追加。他に注目ポイントとして、Flash内部で使用する画像処理のフィルタを自作できるようになるそうです。そのための開発ツールとして、AIF(Adobe Image Foundation) Toolkit というものが公開されまして、こちらでswf内でも利用可能な高速画像処理フィルタを作ることができるそうです。

Flash

こちらはFlashのIDEのほうの話。
  • Flashの開発画面上で映像再生できる
といった便利な機能拡張や
  • タイムラインにキーフレームをいくつも打って、みたいな面倒なことをせずにアニメーション作れる
  • 書いた絵にボーンを仕込んでアニメーションさせる、みたいなことができる
といった、アニメーション作りに便利な機能拡張がされるようです。

Flash on C/C++

どうやらC/C++の資産がFlashで活用できるようになるらしい、という話のようで。C/C++で書いたものがASに取り込めて、たとえばXSLTの機能を追加、とか色々拡張できる、という感じの説明っぽかったんですがイマイチ英語が聞き取れず。とりあえずFlashの常識からすると恐ろしい内容のデモが展開されてまして会場大フィーバー。

Thermo

こちらは今回のMAXで初公開になったプロダクト。
性格としては「Flex Builder for Designer」といったもので、RIAインターフェースを、デザイナーが制作するのを助けるツール、といった感じ。
IllustratorやPhotoshop、Fireworksなどで作ったグラフィックをThermoに取り込んで、各パーツにインタラクションなどを設定。それをmxmlに吐き出して、エンジニアに渡す、といったようなワークフローが想定されているようです。リリース時期は未定。映像はこちら(aralbalkan.com)

Flex Builder linux

FlexのEclipse pluginがlinuxでも動くようになる模様。Public Alphaが公開されている ので興味のある方は自己責任でお試しあれ。

Flash home

携帯電話のOSそのものをFlashに?という技術、なのか何なのかよくわからず。とりあえずブート段階から各種操作などのUIが全てFlashになっていた、というのはわかったんですが…。

その他にもAdobe CS4製品やサーバ製品、Adobe社で運営するサービスなどについてもあれこれと話があったようなのですが追いきれてないのでその辺りは割愛。
以上、大雑把なまとめですが、次世代のWebを作るツールとして、Flashの可能性がまた広がった、といった感じでしょうか。
どんどん進化していくので本当についていくの大変です…。


以下参考にさせていただいたサイト:
Adobe Labs
flex.org
メディアテクノロジーラボ ブログ
Flex Coder
http://journal.mycom.co.jp/articles/2007/10/04/max2/index.html
http://aralbalkan.com/
http://www.peterelst.com/blog/

DjangoのURL逆引き機能
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

Chihiroです。

今回は、Django 0.96以降から改善が進んでいるDjangoのURL逆引き機能に関して紹介したいと思います。この機能を上手く使うと、Djangoを使ったWeb開発において、URLの設計と維持にかかる手間を大幅に削減できます。

公式ドキュメントの日本語訳にもこの機能の説明はありますが、説明が分散していて全体像が見えにくいと思いますので、この機能を使わない場合の問題点を指摘し、次に使った場合、ルーティング、モデル、ビュー、テンプレートがそれぞれどのように書けるかを説明したいと思います。

なお、動作確認はSubversionから取得した開発版Django(rev.6447)を使用して行っています。

Django0.96以前のURL定義の問題点

Djangoの大きな特徴として、「自由なURL設計」がありますが、一方でモデル、テンプレート、ビュー、ルーティングにおいて冗長にURLの定義しなくてはいけないという問題点がありました。

例えば、

http://example.com/entries/1978/

というURLで指示されるリソース(例えばブログのエントリ)を考えた場合、まずurls.pyに記述するルーティング定義(URLConf)に、次のような設定を行う必要があります。

patterns += ('myaproject.myapp.views',
             (r'^entries/(?P<entry_id>\d+)/', 'edit_entry'),
             )

次に、Adminツールなどの恩恵を受けるために、Djangoの流儀に従って、次のような感じでモデルにget_abosolute_urlメソッドを実装していました。

from django.db import models

class Entry(models.Model):
    title = models.CharField(max_length=250)
    content = models.TextField()

    def get_absolute_url(self):
      return '/entries/%s/' % self.id

    def __unicode__(self):
        return self.title

URLConfで書いたr'^entries/(?P<entry_id>\d+)/'は正規表現で、モデルに書いたのは'/entries/%s/'というフォーマット文字列という違いはありますが、似たような定義を繰り返しています。

さらに、リソースを参照したり、リソースに対して何らかの操作を行ったりする場合、そのためのURLのフォーマットをテンプレート内に直接書く必要があります。

例えば、あるブログのエントリの編集画面のURLを"/entries/{entry_id}/edit/"のように設計した場合、テンプレートの中に次のように書くことになるでしょう。

{% if entries %}
<ul>
  {% for entry in entries %}
  <li>{{ entry.title|escape }}<a href="/entries/{{ entry.id }}/edit/">{% trans "Edit" %}</a></li>

  {% endfor %}
</ul>
{% endif %}

ここまでの例で分かるように、モデル、ルーティング、テンプレートにおいて、"^entries/(?P<entry_id>\d+)/"、"/entries/%s/"、 "/entries/{{ entry.id }}/edit/"のように、似たようなURLの定義を繰り返す必要がありました。

URL定義のDRY化

開発版Djangoでは、URLConfでのURL定義を元に、ビュー関数名からリソースのURLの解決を行えるようになりました。分かりやすく言うと、urls.pyにURL設計をまとめることができるようになったのです。

基本的なCRUDを例にとって説明します。

まず、URLConfは次のように定義できます。

# -*- coding: utf-8 -*-
# myproject/urls.py
from django.conf.urls.defaults import *
from myapp.models import Entry

urlpatterns = patterns(
    'myproject.myapp.views',

    # エントリの新規作成
    url(r'^entries/new/$',
        'create_entry',
        name='create_entry'),

    # エントリの編集
    url(r'^entries/(?P<entry_id>\d+)/edit/$',
        'edit_entry',
        name='edit_entry'),

    # エントリの削除
    url(r'^entries/(?P<entry_id>\d+)/delete/$',
        'delete_entry',
        name='delete_entry'),
)

urlpatterns += patterns(
    'django.views.generic.list_detail',

    # エントリ一覧
    # GenericViewを使います
    url(r'^entries/$',
        'object_list',
        dict(queryset=Entry.objects.all(),
             allow_empty=True),
        name='show_entries'

        ),

    # エントリ詳細
    # GenericViewを使います
    url(r'^entries/(?P<object_id>\d+)/$',
        'object_detail',
        dict(queryset=Entry.objects.all()),
        name='show_entry_detail'
        ),
    )

urlpatterns += patterns(
    'django.views.generic.simple',

    # 上のどれでもない場合は/entries/にリダイレクト
    url(r'',
        'redirect_to',
        dict(url='/entries/')
        ),

    )

上のコード例にあるように、開発版Djangoでは、今までのタプル(シーケンス)でのURL定義の他に、新たにurlという関数で定義を行えるようになりました。この関数を使うと、名前つきのURLパターンを定義するのが多少楽になります。

名前つきをURLパターンを使った方が、URL定義の逆引きをするときにタイプ量が減るという単純なメリットもありますが、名前付きURLを使わないと解決できないケースもあります。詳しくは公式のドキュメントを参照してください。

開発版Djangoの機能を活用すると、URLの設計(URLのフォーマットを決める作業)はこのURLConfの中で完結し、モデル・ビュー・テンプレートでは、URLパターンの名称とモデルのキーなどのパラメータからURLを決定することができるようになります。

まず、モデルでは、permalinkデコレータ関数を使うと、URLConfに基づいてモデルのURLを構成することができます。(公式ドキュメントによる解説)

from django.db import models
from django.db.models import permalink

class Entry(models.Model):
    title = models.CharField(max_length=250)
    content = models.TextField()

    @permalink
    def get_absolute_url(self):
        return ('show_entry_detail', [str(self.id)])

    def __unicode__(self):
        return self.title

ビュー関数からURLの解決を行う場合には、reverse関数を使います。(公式ドキュメントによる解説)

# -*- coding: utf-8 -*-
from django.shortcuts import *
from django.http import *
from django.core.urlresolvers import reverse
import django.newforms as forms

from models import Entry

EntryForm = forms.models.form_for_model(Entry)

def create_entry(request):
    """
    新しいエントリを作成する。
    作成後は、エントリ一覧のページにリダイレクトする。
    """
    form = EntryForm(request.POST or None)
    if form.is_valid():
        entry = form.save()
        return HttpResponseRedirect(reverse('show_entries'))

    return render_to_response('myapp/entry_form.html', {'form': form})

テンプレート内からURLを解決する場合は、{% url %}タグを使います。(公式ドキュメントによる解説)

このタグは、{% url URLパターンの名前 パラメータ %}のように使います。

{% load i18n %}
{% extends "base.html" %}
{% block content %}
<div>
  <a href="{% url create_entry %}">{% trans "Post a new entry" %}</a>
</div>

{% if object_list %}
  <ul>
  {% for entry in object_list %}
    <li>{{ entry.title|escape }}
        (<a href="{% url edit_entry entry_id=entry.id %}">{% trans "Edit" %}</a> |
         <a href="{% url delete_entry entry_id=entry.id %}">{% trans "Delete" %}</a>)
    </li>
  {% endfor %}
  </li>
{% else %}
  <p>{% trans "No entries" %}</p>
{% endif %}
{% endblock %}

まとめ

permalink, reverse, {% url %}を使うと、URL設計を変更したい場合などに、変更にかかるコストを大幅に削減できます。またURL設計をURLConfに集約できるので、コードやテンプレートの可読性も高まります。

例えば、「当初、"/entries/{entry_id}/"のようなURLをブログ・エントリのパーマリンクとしていたが、開発段階でパーマリンクを"{entry_id}/"に変更することになった」というケースを考えてみてください。この場合でも、permalink, reverse, {% url %}を使っていれば、URLConfを書き換えるだけで済みます。

今回のコード例は下記のリンクからダウンロードできますので、どうぞご利用ください。

django071003.tgz

2007年10月 3日

Symfonyプラグインまとめ ~その1~
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

yukiです。

PHPのフレームワークは数多くありますが、みなさん選ぶポイントはどのような点でしょうか。
セキュリティの問題など多々あると思いますが、共通しているのは「楽をすること」だと思います。
最近は自宅で開発する時はsymfonyを使おうかと思い調べてみたところ、優秀なプラグインが多くありました。
今回はその一部をご紹介をしたいと思います。

  • Dynamic Generators
sfCssTabsplugin
Word Press の管理画面風なタブレイアウト
sfSavvyPlugin
symfonyの追加ヘルパー
sfSiteMapPlugin
簡易サイトマップ作成
sfSiteMapPlugin
簡易サイトマップ作成
  • JavaScript
ddJQueryCalendar
jQuery calenderの追加
sfDojoIntegration
DoJo Toolkitの追加
sfWidgetsPlugin
gWidgets libraryの追加
sfJSONRPCPlugin
JSON-RPCの利用
sfLightboxPlugin
LightBox2の利用
sfLightWindowPlugin
LightWindow v2.0の利用
sfMilonicPlugin
Milonicを利用したドロップダウンリスト
sfModalBoxPlugin
ModalBoxの利用
sfNiftyPlugin
Nifty corner cubeの利用
sfPJSPlugin
actionからjsファイルを動的に生成
sfPrototypePlugin
デフォルトのprototype.jsを置き換えて使えるようにする
sfTaconitePlugin
JQuery Taconite Pluginの利用
sfUJSPlugin
actionからjsファイルを動的に生成
sfUnobstrusiveDojoPlugin
Dojoの利用ヘルパー
sfUrchinPlugin
Google Analiticsのコードを埋め込む
sfYUIPlugin
YUI(Yahoo UI Library)の利用

http://trac.symfony-project.com/wiki/SymfonyPluginsご紹介したプラグイン以外にもここに数多くのプラグインが詳しく載っていますのでご参考下さい。
次回はもう少しコード内部に影響するプラグインをご紹介しようと思います。

2007年10月 2日

Mash up Award 3rd の受賞作品が発表されました
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

matsudaです。こんにちは。

昨日9/30にありました Mash up Award 3rd の表彰式に参加してきました。
応募総数193作品の中から、部門賞・各賞・最優秀賞で33作品が選ばれました

各受賞作品/応募作品は Mash up Award 3rd のページでご覧いただけますので、今回はそのなかでもフォト蔵APIを使った作品のページをご紹介します。

また、今回はウノウ賞も用意させていただきました。受賞作品はこちら!

フォト蔵 for Skype
http://d.hatena.ne.jp/nodoguro/20070902

検索のキーワードに引っ掛けて写真を表示する作品が多い中、面白い使い方を提案していただけたのが今回、ウノウ賞として決めた理由です。

それでは、以下フォト蔵APIも利用した作品です。面白いインターフェースもあったりと力作ぞろいでした。

横断検索 クワシク
http://kwsk.unw.nu/

StartCommand(スタートコマンド)
http://www.startcommand.com/

ベビー服のコーディネート - toddlr.jp
http://www.toddlr.jp/

バスでお出かけMAP
http://calta.jp/bybus/

モバイルショット
http://www.ngi644.net/net/atelier/mobileshot

リボルバースタイル サーチ
http://stoap.net/revolver/

カーライフナビ!
http://sweetwater.la.coocan.jp/carlifenavi/

Wish Place
http://wishplace.sa57.jp/

地図DOGA
http://www.map-movie.org/

Enqupedia アンクペディア
http://enquete.s15.coreserver.jp/

Firefly Navigator
http://firefly.maddy.jp/

ColorBar Generator(カラーバージェネレーター)
http://style4.info/color/colorbar.html

酒-Go!
http://syu-go.nsstella.net

JAPAN !
http://apps.facebook.com/japan_app/

リッチ辞書
http://www.wasabi-tk.com/richdic/index.html

ねこねこ倶楽部
http://neco.tv/

ふれっく
http://frec.hbbox.net/

Coogr(クーガー)
http://www.got2do.com/lab/coogr/

folksony β版
http://www.folksony.com/

Tretter <<いま話題の言葉に一言物申す。What is a buzz word?>>
http://mashup3rd.heteml.jp/TopicSearch/TopicSearch.html

NAVIEW-OBJECT for PHOTOZOU
http://naview.com/mashup/

やはり全体的にキーワードに引っ掛けて写真を表示させるマッシュアップが多かったようですね。

全体の応募作品では位置情報+αの作品がまだまだ数が多い状況でしたが、音声解析やカーナビと連携した特徴的な作品が増えてきたといものもありました。

今後のマッシュアップの参考になりますね。


About 2007年10月

2007年10月にブログ「ウノウラボ by Zynga Japan」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2007年9月です。

次のアーカイブは2007年11月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Zynga Japan