« 2007年5月 | メイン | 2007年7月 »

2007年6月28日

ECサイトのユーザビリティ・ガイドライン
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんばんわ、Sashaです。

最近、ECサイトのリニューアルを計画するお手伝いをする、という仕事がありました。特にユーザビリティ的な観点から、どんなことを網羅したらこのリニューアルを成功させることができるだろうか、ということを考えながら、様々なブログを参考にしたり実際のECサイトを検証したりしていたら、以前私が紹介したユーザビリティ・ガイドラインのようなチェックリスト的なものが出来上がったので、もしかしてどこかのだれかのお役に立つこともあるかもしれない、と思い、ここに紹介させていただきます。

まず、ECサイトで実現したい基本的な目標をあげ、その目標に沿って細かく、網羅していきたい事を列挙していきました。

基本的な目標とは、次の5項目です。

  1. 見つけたい商品・情報を見つけやすくする
  2. ユーザーの労力を極力削減する
  3. 買いたい気にさせる
  4. 購入までのプロセスを簡単にする
  5. オンラインショッピングの恐怖心をなくす


この5項目の目標に近づくために、網羅したいな、と思う事項を挙げていったら、以下の表のようになりました。ECサイトの性格によって該当しない項目、足りない項目もあるかもしれませんが、必要に応じてカスタマイズしてくださいね。

チェック項目 できた?
見つけたい商品・情報を見つけやすくする
見やすく、押しやすいナビゲーション
関連情報が視覚的にまとまっている(ナビゲーションにも、商品情報にも、関連商品にも適用されます)
顧客の頭の分類にマッチするカテゴリー分け(ノートパソコン用のバッグは、バッグのカテゴリ?それともPCアクセサリのカテゴリ?という問いかけをしてみる)
ユーザーにとって見つけたい度の高いものが、上のほうにある
到着したページが自分の探していたものではなかった場合も、簡単に戻れる
到着したページが自分の探していたものではなかった場合も、すぐそばに関連商品がある
自分がどこにいるかがわかりやすい(パンくず・ページタイトルなど)
選択(検索)条件を解除できる
ユーザーニーズに合わせた並べ替え機能
絞込検索ができる
検索バーは常に表示
他の検索条件を提案する(この検索条件だと○件ありますよ、など)
ユーザーの労力を極力削減する
なるべく少ないページ遷移
なるべく少ないユーザー側の情報提供
一度カートに入れたものを覚えている
スクロールは最低限
在庫状況が前もってわかる
ページを一定商品数ごとに切るだけではなく、「このページですべて見る」もある
買いたい気にさせる
色調がきれいで見やすい
文字の大きさが重要度に比例
画像が豊富
画像がきれいで魅力的
商品のイメージを最大限掻き立てる表現
お得な商品が目に飛び込む(位置・画像・フォント・サイズ)
企画で衝動買いを誘発
関連商品で衝動買いを誘発
それを今買うことで(特典を使うことで、など)いくら得をするかがわかる
どんな風に使えるか(コンビネーション、ギフトアイデアなど)を連想させるコンテンツを用意する
購入までのプロセスを簡単にする
ユーザー側からの情報提供は最小限
購入のステップは最小限
購入のステップの中で自分の位置がわかる
なるべく少ないクリック数(ページ遷移)で、豊富な情報を提供
どれがよいか、比較・検討・判断しやすい
カートが目に入りやすい・押しやすい位置にある(商品画像や商品の基本情報のすぐ近く。)
複数個購入を考慮し、カート内で(はるいは商品ページで) 数量変更
オンラインショッピングの不安をなくす
合計でいくらになるのかが早い段階でわかる
その商品の値段が早い段階でわかる
自分がどこにいるかがわかりやすい
購入のステップが前もってわかる
購入のステップの中での自分の位置がわかる
ユーザーが提供しなければならない情報の使途が明確
個人情報保護の情報が明確
支払方法、送料、日数、配達方法、配達場所、返品方法が明確
↑上2項目の情報へのリンクが、カートボタンのそばやカートボタンを押した後のページにも配置
エラーメッセージ(フォーム入力や検索結果が条件に合わないときなど)が出た場合、次にどうしたらいいのかが明確
エラーメッセージが出た場合、次にどうしたらいいのかが明確
サイトのルックスが、会社のイメージと一致している(実店舗がある場合、実店舗のイメージとの一致も大事)
どの段階でもキャンセルができる

2007年6月26日

mod_expires と mod_rewrite を使ってウェブサーバへのアクセスを減らす方法
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

最近、雨の日が続いて自転車通勤ができていない naoya です。

今日は、先週ぐらいからフォト蔵に導入した Apache で mod_expires と mod_rewrite を使ったウェブサーバへのアクセスを減らす方法を紹介します。

通常のウェブサーバは、更新されていないリリースに対してアクセスすると、ステータスコード 304 とIf-Modified-Since ヘッダをつけて応答データを返しますが、CSS や JavaScript など比較的更新頻度の少ないファイルに対して、毎回応答を返すのはウェブサーバから見ると無駄なアクセスです。

Apache の mod_expires と mod_rewrite を使うと、この無駄なアクセスをブラウザキャッシュを有効活用にすることにより、静的なファイルに対するアクセスを減らすことができます。

まず、仕組みから説明すると、とても単純で mod_rewrite を使って CSS や JavaScript など比較的更新頻度の少ないファイルのファイル名を一意に見せかけます。同時に mod_expires を使って Expires ヘッダを設定することによりブラウザキャッシュを使って、ウェブサーバにアクセスさせないという方法です。この方法を使うと、


それでは、さっそく設定方法を紹介します。

1. Apahce に mod_expires と mod_rewrite が組み込まれていることを確認するため、Apache の設定ファイル httpd.conf を確認します。次のような設定がない場合は、追加してください。

  LoadModule expires_module modules/mod_expires.so
  LoadModule rewrite_module modules/mod_rewrite.so

2. mod_rewrite を使って、CSS と JavaScript のファイル名が一意になるので、実際の CSS と JavaScript ファイル名でアクセスできるように URL の変換ルールを次のように設定します。/css や /js は、フォト蔵の CSS と JavaScript ファイルのパスなので、環境に応じて置き換えてください。

  RewriteEngine on
  RewriteRule  ^/css/(.*?)\.v\d+\.(.*)$ /css/$1.$2 [QSA,L]
  RewriteRule  ^/js/(.*?)\.v\d+\.(.*)$ /js/$1.$2 [QSA,L]

3. mod_expires の設定を有効にして、CSS と JavaScript の Expired Date を設定します。

  ExpiresActive On
  ExpiresByType text/css "access plus 1 days"
  ExpiresByType application/x-javascript "access plus 1 days"

4. ページで読み込んでいる CSS と JavaScript のファイル名を一意に変更します。フォト蔵 では、すべて SVN で管理しているため、SVN リビジョンをつけて、常に一意になるように変更しました。

あとは、Apache を再起動して、HTTP のヘッダ情報に Expired が含まれていること、正しく CSS と JavaScript にアクセスできることを確認します。

この設定をするときの注意点は、mod_expires の設定に ExpiresDefault という項目があるのですがこの設定をすると、すべてのファイルに対して Expired ヘッダをつけてしまいます。そのため、動的なコンテンツまでブラウザキャッシュを使うようになってしまいますので注意してください。
また、Expired の期間は最初は 1 日くらいに設定して、徐々に長くていくのがいいと思います。


この設定をするにあたり、「mod_expiresとmod_rewriteを使ってサイトの帯域節約と体感速度を向上させる方法」を参考にさせていただきました。貴重な情報ありがとうございました。

2007年6月25日

railsのテストをevalを駆使して楽しよう
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは satoです。

 testコードではパフォーマンスやセキュリティをさほど気にすることはないと思うので、testコードで evalを使って楽できるケースを書いてみたいと思います。

  よく似たModel で Userと Admin があるとします。 二つの Model には confirm というメソッドが定義されていて、ユーザ登録をしてメールで送られてくる URLを をクリックすると status が 'register' から 'confirm' に変更されます。

  class UserTest < Test::Unit::TestCase
    def test_with_confirm
      user = User.find(:first, :conditions => ["status = ?", 'register'])
      user.confirm
      assert(user.status == 'confirm', 'confirm error')
    end
  end

というテストをUserTest と AdminTest に書きます。

しかし以下のような moduleを継承しておくと DRYに書けます

  module UserModelTestHelper
    def get_class
      k = self.class.to_s.gsub(/Test$/, '')
      eval k
    end
    def test_with_confirm
      user = get_class.find(:first, :conditions => ["status = ?", 'register'])
      user.confirm
      assert(user.status == 'confirm', 'confirm error')
    end
  end

このようなクラスを定義しておくと module を incude するだけで 勝手にテストが生成されます。

  class TestUser < Test::Unit::TestCase
    include UserModelTestHelper 
  end
  class TestAdmin < Test::Unit::TestCase
    include UserModelTestHelper
  end

 同じような 共通のテストは UserModelTestHelperにどんどん追加するだけです。

 上記の例では クラス名を evalで Modelに変換していますが、テストファイル名やメソッド名を利用すると同じように楽できるケースがあると思います。。またUnitテストだけではなく Rspecなどにも普通に使えるのでNiceな方法を思いついたらぜひ教えてください

2007年6月20日

携帯開発向けに便利な支援ツール/サイト
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

どーも8makiです。

ウノウでは携帯サイトの開発も行っています。
携帯サイトの開発ってPC用のものと比べて制限が多かったり、面倒くさいことが目白押しです。そこで開発に便利なツール・サイトをリンク集的にまとめてみました。

~~~~~~ツール編~~~~~~

■必須ツール

  • i 絵文字

  • ドコモの絵文字が入力/表示できるようになります。
  • User Agent Switcher (Firefox 拡張)

  • User Agentを書き換えて携帯になりきることができます。
    インポート用の各種端末のUser Agentリストを作成していただいたので皆さんもご利用ください。
    useragentswitcher.xml

■あると便利なツール

~~~~~~サイト編~~~~~~

■各キャリアの技術ページ


○ドコモ
○au
○ソフトバンク/ボーダフォン

■ゲートウェイの一覧ページ

■検索エンジンにサイトを登録

■非公式だけど便利なサイト

~~~~~~~~~~~~
参考までに。。。

■クローラー判定

クローラーの判定をUserAgentで判定する場合、こんな感じの正規表現で行っています。
  • ドコモのGoogle
  • '^DoCoMo.+Googlebot-Mobile'
  • auのGoogle
  • '^.+KDDI\-Googlebot\-Mobile.+'
  • ボーダフォン
  • 'VodafoneKKSearch'
  • ソフトバンク
  • 'SoftBankMobileSearch'
    'Y\!J\-SRD'
    'Y\!J\-MBS'

■メール設定のURLリンク先

  • DoCoMo(メール設定)

  • http://docomo.ne.jp/cp/mailsetst.cgi
  • au(Eメール設定→その他の設定)

  • http://www.au.kddi.com/cgi-bin/wau/meiwaku.cgi http://imutl.ezweb.ne.jp/cgi2001/utl_menu.cgi

■ユーザIDの設定ルート

  • au(EZ番号)

  • EZトップメニュー⇒auお客様サポート⇒申し込む⇒EZ番号通知設定
  • SoftBank(ユーザID)

  • My SoftBank⇒各種変更手続き⇒ユーザID通知設定 http://pdb/cgi-ue/nph-wap_pdb_uid_update.cgi


携帯サイト開発の情報って中々共有されてない部分が多いですよね。携帯サイト開発者のの皆さんの少しでもお役に当てればと思います。

2007年6月19日

バグに効く習慣~より良いテストを実現する企業文化
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

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

プロダクトの品質を上げるには、会社ぐるみで品質管理に取り組む意識が重要です。
より良いソフトウェアテストを実現する為の企業文化として、大事だと思うことを幾つか挙げてみたいと思います。

新人にまずやってもらうことは?

新人テスターをいきなりテストに参加させるのは良くありません。製品への理解が深くないと有効なテストは出来ないからです。 まずは製品の仕様を覚えてもらったり、バグレポートの書き方を覚えてもらったりしなくてはいけないのですが、仕様書をポンと渡して、「これを見ながら製品を全部動かしてみて」といった指示を出しても現実味がなくモチベーションは揚がらないでしょう。

最初にやってもらうことは、先輩テスターの書いた障害報告の再テストか、
画面遷移図の更新など手探りで学習しながら行えることが良いと思います。

極力固定したビルドでテストする

テスト対象のビルドは極力固定されていなくてはいけません。 忙しさ故ずるずると変更を受け入れがちですが、わずかでもコードが変われば新たなバグが発生する可能性があるからです。 一番難しいのが、変更箇所として伝えられた箇所以外にも何らかの手が入っている場合です。予測出来ないバグを潰すことはとても困難です。必ずすべての変更点を明らかにしておかなくてはいけません。

本番同様の検証環境を用意する

本番と別に開発環境のあることが多いと思いますが、検証を行う環境は可能な限り本番に近い状態に保たなくてはいけません。検証の為に本番から大量のデータをコピーするなど、これは相当手間がかかります。 しかし、両環境に乖離があれば「この問題は本番だとちゃんと動くはず」というバグ修正は絶対に出てきます。この修正が正しいことをどうやって本番反映前に証明することが出来るでしょうか。 そういった綱渡りのようなスタイルの修正が成功する率はかなり低いでしょう。

バグを隠さない

コードにバグがあることは好ましくないのですが、バグ発見を忌み嫌うような空気が職場に蔓延すると、結果としてバグを隠すようになります。 そうなっては逆に問題なので、前向きに捉えるように努力しなくてはいけません。 このへんはバランス感覚が難しいところですね。

テストにかかる工数を忘れない

何故かコードを書くとなると、人はコーディングの終了イコール完成だと考える傾向にあるようです。(このことに関しては自分も人のことは言えませんが。。) 名著「人月の神話」では工数配分の例として 設計1/6 コーディング1/3 テスト1/2という比率を示しています。 趣味のプログラムではないので、検証にかかる工数を忘れないように現場の意識を保つようにしましょう。

テスターは早期にアサインさせる

仕様がなかなか決まらずに見切り発車でコーディングが進んでいる場合など、テストも泥縄式にテストケースを書いて実施することがあります。そんな調子だと十分な品質は確保出来ないでしょう。 テストは設計と表裏一体であると言えます。テストケースを記述することで設計の問題点を明らかにすることが出来ますから、テスターはなるべく早期にアサインすべきです。

Tipsを共有する:Googleの例 TotT

テスト担当者同士での情報交換を活発にするのも良いですね。 好ましい例としてGoogleにはTotTという習慣があり、トイレの壁にテストのTipsが貼ってあるそうです。#TotT = 'Testing on the Toilet'の略

google_testing_blog
google_testing_blog posted by (C)フォト蔵


Google Testing Blog
http://googletesting.blogspot.com/


Introducing "Testing on the Toilet"


思い当たるところを書いてみましたが、他にもあれば是非コメントくださいね。

今回はテスト視点でしたが、プログラマ視点で「より品質の高いコードを書く為の企業文化」という話も面白そうですね。
多分そのうちウノウプログラマー軍団の誰かがエントリを書いてくれることでしょう。乞うご期待。

2007年6月18日

ベンチャー流Webサービスの作り方(企画編)
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

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

僕はウノウが株式会社化するタイミングでウノウに参画しました。 それ以来はずっと二年半程Webサービスの開発に従事してきました。 ウノウに参画した当初はWebサービスのことは全く分かっておらず、 単なるLinux好きのエンジニアにすぎませんでした。

ウノウ株式会社の創業時に参画することにより、 サービスの企画から開発、運用まで携わることができました。

最初はエンジニアが自分一人だけだっとところから、 現在のように数多くの優秀なエンジニアを抱える企業に成長するまでの組織作りにも関わることができました。

全く経験のないところから始めたので、それこそいろんな失敗を重ねてきました。 そこで今までの経験を元にベンチャーがWebサービスを開発するにあたって気をつけておいた方がいいことをまとめてみます。

Webサービスの開発を始めるには、何はなくとも企画から。 今回はWebサービスを企画するにあたって気をつけたいことをまとめます。

・自分が思いついたことは他人も同じことを思いついている

人間の想像力はそれほど他人と変わるものではなく、 自分が思いついたアイデアは他の人も同じことを思っている可能性が高いです。 大事なのはそれを実際に形にする行動力。

・行動力

思いついたらまず行動しましょう。 すぐに行動しないと判断がブレます。 行動しないと何も生まれません。 「それ同じこと考えてたんだよね」と後でいうのは完全に言い訳です。

・スピード重視

とにかくスピードを重視しましょう。 遅延はコスト増、成功確率低につながり、いいところはほとんどありません。

・少人数体制

企画は少人数で行いましょう。 人数が多いと「船頭多くして船山に登る」になってしまい、 決定事項も全員の意見をうまくまとめた最小公倍数的なものになってしまいます。 これでは、思い切ったことや、大きなことができません。 相談するのはいいですが、最終的な決断は一人、多くても二人で行うのが理想です。

・ブレストをしよう

単なるアイデア出しは人数が多い方がいいです。 いろんな人と話をしたり、ブレストしたりしていろんなアイデアを出しましょう。

・機能追加は慎重に

Webサービスは気軽に機能拡張することができます。 そのためいろいろ機能を追加しがちですが、 一回追加した機能を外すのは機能を追加するよりも難しいです。 新機能の追加は慎重に行いましょう。 「システムを複雑にするのは簡単だが、簡単にするのは難しい」を肝に銘じておきましょう。

・ユーザの反応は予想と違うと考えよう

ユーザの反応、使い方は企画者側が想像したのと違うと考えておくべきです。 予想通りだったら問題ないですが、 予想通りいかなかった場合にうまく舵取りできるような体制にしておかないといけません。

・ユーザ視点で考えよう

Webサービスはユーザに使ってもらわないと意味がありません。 「ユーザの反応 >>>> 自分のやりたいこと」の図式を頭に叩き込んでおきましょう。 自分のやりたいことを優先させるのは単なる自己満足にすぎません。

・コアユーザは少ないことを認識しよう

熱狂的に使ってくれるようなコアユーザの割合は基本的には少なくて、 せいぜい一割か多くて二割ぐらいです。 アクセスの多くを占めるのは傍観者で、多くの傍観者を生み出すのがコアユーザです。 コアユーザに喜んでもらえるようなサービス作りを心がけましょう。 コアユーザに気に入ってもらわなければサービスは伸びません。

・聞いていい意見といけない意見をしっかり区別しよう

ユーザからの意見はしっかり聞きましょう。 そして聞いていい意見といけない意見をしっかり区別しましょう。 聞いていいかどうかの判断は非常に難しいですが、 コアユーザに喜んでもらえそうな意見を採用しましょう。

サービスを大きくするには広がりが大事です。 他のユーザに影響の与えないような意見は開発コストがかかるだけで、 サービスが大きくなる原動力にはなりえません。

・自分のリテラシーを徹底的に下げる

ユーザは自分達が想像するよりも無知であることを認識しましょう。 「これぐらい分かるだろう」という考えは危険です。 自分のリテラシーを徹底的に下げて物事を考えるようにしましょう。

・方向転換する勇気

サービスを立ち上げた人は自分の考えにこだわりのある人が多いです。 ユーザの反応を見て、想像と違った場合にそれを認めるのは勇気のいることです。 ユーザの反応がよくない場合はそれを素直に認めて方向転換しましょう。

Flickrが最初はMMORPGを開発する会社だったのがうまくいかず、 写真共有サービスとして方向転換して成功したのが顕著な例です。 ここまで大幅な方向転換は稀ですが、小さな方向転換はいろんな場面ででてきます。

・シンプルに

とにかくシンプルにしましょう。 基本的にWebサービスの開発は「企画->開発->ユーザの反応を見る->企画」の繰り返しです。 先に述べたように、追加した機能を外すのはそんなに簡単ではありませんし、 予想と違って方向転換を行う必要がいくつも出てきます。

最初に作り込んでしまうと、 ユーザの反応がよくなかったときに方向転換するのが難しくなります。 できるだけシンプルにしておいて、まずはユーザの反応を見ましょう。 ユーザの反応が良ければ拡張・継続、悪ければやめればいいだけです。

・こだわりを持とう

ユーザの反応を見て素直に認めるのも大事ですが、時にはこだわりも大事です。 周りから大反対されても自分の信念に従って突き進むことも必要です。 iPod登場時のAppleユーザの反応は冷ややかでした。 何がうまく行くかなんてのは誰にも分からないし、 最初からうまくいくことも多くはありません。

・あきらめない

たとえ自分が考えた通りにうまくいかなくても粘りましょう。 どん底の状態でも起死回生は可能です。 あきらめた時点で終わりです。とことんやりましょう。

・愛だよ、愛 by Keita

最終的に大事なのは、そのサービスに対する愛です。 どんなに優秀な人でも、あなたがそのサービスにかける情熱には勝てません。 この熱意がサービスを成長させるためには必要です。

・まとめ

Webサービスを開発するにあたって企画の部分で気をつけないといけないことをまとめてみました。 人それぞれ考え方は違うとは思いますが、僕なりに経験を重ねいろいろ考えた上で現在の結論に至っています。 今後自分でWebサービスを開発したい方の参考になれば幸いです。

2007年6月15日

ActionScript2.0のデバッグを支援する KLog を公開しました!
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

KLog について

ウノウでFlash開発する時に密かに利用している ログツール「KLog」を一般公開します。

どうして作ったのか?

こんにちは、クジラ飛行机(酒徳2号)です。 私は、ウノウで、Flashの開発や、 Windowsのクライアントアプリの開発を行っています。 今回、私がFlashの開発時に使っているログ出力システム KLog を紹介します。

ActionScript3.0(Flex2)になって、ようやく、Flashにも まともなプログラミングの開発環境がつきましたが、 Flash MX2004/8/MTASCの開発は、デバッグがやり辛くて困っていました。

特に、MTASCというフリーのFlashコンパイラを利用して開発をしていると、 trace()命令も使えないので、デバッグが非常に困難でした。

そこで、作ったのが、KLog です。

klog - ActionScript2.0 デバッグシステム
klog - ActionScript2.0 デバッグシステム posted by (C)クジラ飛行机(峰酒徳)

KLogを使って嬉しいことは、以下の点です。

  • Flash の trace() 命令よりも使いやすいログ出力
    • メッセージの出力はもちろん、オブジェクトのプロパティ一覧を出力可能
    • ログの出力先を簡単に変更できる
      • KLogServer に出力
      • Flashの画面内に出力
      • Firefoxのアドイン Firebug のコンソールに出力
  • ログ専用アプリ KLogServer が便利
    • ログ内の検索が簡単
    • 実行途中に変数の値を調べたり値を書き換えることができる
    • 常に最前面に表示させておくことが可能(意外と便利)

KLogの使い方

基本的に、KLogは、ActionScript2.0クラスの KLog.as と、 ログを受信するWindows用ツールの KLogServer.exe の2つで構成されます。 KLog.as は、ActionScript2.0 のクラスです。 ログを出力したいプロジェクトに組み込んで使います。

手順1:KLogServerの起動

アーカイブに同梱されている KLogServer.exe を実行します。 初めての実行では、ファイアウォールの設定画面が出るので、 サーバーの起動を許可してください。

手順2:プロジェクトへの組み込み

プロジェクトと同じフォルダに、KLog.as をコピーします。

そして、プロジェクト内で、始めに実行されるコードに、 KLogの設定を記述します。(この手順は省略できます。) 始めに、どこにログを出力するのかを指定します。

// ログを始める
KLog.isTest        = true;  // KLog をはじめる
KLog.use_textfield = false; // Flash内の画面に出力するか?
KLog.use_server    = true;  // KLogServer に出力するか?
KLog.use_firebug   = false; // FireBugに出力するか?

デフォルトでは、WindowsアプリのKLogServerに出力されます。

そして、ログを出力したいところで、以下のように記述します。

KLog.write("ログに出力する内容");

これだけです。もし、オブジェクトのプロパティ一覧を表示したい場合は、 以下のように書きます。

KLog.print_r( object );

そして、デバッグが終わって、ログ出力機能をオフにしたい場合は、 以下のように KLog.isTest = false と書くと、 ログ出力機能を行わないようになります。

KLog.isTest = false;

備考

Flash Player 7以降に対応していますので、Wii 対応アプリや、 FlashLite2.0アプリのデバッグにも最適です。

KLog のダウンロード

KLogは以下よりダウンロードできます。

http://aoi-project.com/download/KLog/

SmartyでRailsライクなレイアウトテンプレートを使う
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは。先日入社しましたmiyakeと申します。新入りです。

今日はSmartyでRuby on Rails的なレイアウトテンプレート機能を実現するTipsをご紹介します。

まず、そもそもRailsのレイアウトテンプレートとはどんな物なのか、簡単に解説します。

Smartyに限らず、何らかのテンプレートエンジンでページを出力する場合、ヘッダやフッタといった共通部分を別ファイルに切り分けるというのはよくあるアプローチです。

この場合、そのページのテンプレートファイルに加えて、ヘッダとフッタのテンプレートを別途用意します。

これに対して、レイアウトテンプレートはそのページのテンプレートと「土台になるテンプレート(=レイアウトテンプレート)」でページを出力します。

実際のHTMLに照らし合わせて図にすると、こんなイメージです。

レイアウトテンプレートのイメージ
レイアウトテンプレートのイメージ posted by (C)フォト蔵

ヘッダ・フッタを分割した場合、各テンプレートはHTMLの一部分を切り出した内容になります。

一方レイアウトを使用すると、レイアウトテンプレートはHTMLとして成立する形で記述できます。デザイナーさんにテンプレートを作ってもらう時も、コーディング後にファイルを分割する作業がなくて済むので効率的です。

後から修正が入った場合も、レイアウトは1枚のHTMLとして成立しているので作業が楽ですし、レイアウトテンプレートをバリデータにかけることもできるのでトラブルを回避しやすくなります。

実際のテンプレートは以下のように記述します。

layout.tpl

<html>
 <head>
  <title>{$TITLE}</title>
 </head>
 <body>{$CONTENT}</body>
</html>

content.tpl

{assign var="TITLE" value="レイアウトテンプレートのサンプル"}
<h1>レイアウトテンプレート</h1>
<p>こいつが各ページのテンプレートになります。</p>

ページのテンプレート(僕はコンテンツテンプレートと呼んでいます)はHTMLの一部分だけになってしまいますが、レイアウトテンプレートは1枚のHTMLとして成立します(サンプルは必要な要素も大幅に省略していますが)。

ページを出力する時は、layout.tplの{$CONTENT}にcontent.tplの内容が展開されるようにします。

また、コンテンツテンプレート側で宣言したテンプレート変数が、レイアウトから参照できるようにします。ページのタイトルや個別に読み込むCSS、JavaScriptなどを変数とすることで柔軟性のあるレイアウトが作れます。

php側の処理は非常に簡単です。ここでは、レイアウト処理の部分だけ掲載します。

$content = $smarty->fetch('content.tpl');
$smarty->assign('CONTENT', $content);
$smarty->display('layout.tpl');

通常、Smartyではテンプレートを上の行から順に処理するので、変数は宣言した行以降でしか有効になりません。が、このようにcontent.tplを先にfetchしてやると、fetchした時点でcontent.tplの変数定義が全て処理されるため、layout.tplではcontent.tpl内で定義した全ての変数が有効になります。

分かってしまえば非常に単純な仕組みですが、なかなか使えるんじゃないでしょうか。

手元のライブラリでは、このアプローチをベースに

  • 表示するページのディレクトリからlayout.tplを探す
  • 同じディレクトリにない場合は親ディレクトリを再帰的にたどって探す
  • コンテンツ側でテンプレート変数{$LAYOUT}が指定されたらそのファイルをレイアウトとする
  • {$LAYOUT}が予約語「plain」だったらレイアウトは適用しない

といった機能を追加しています。皆さんも、自分なりの機能を追加したSmartyで快適なテンプレートライフを模索してみてはいかがでしょうか?

最後に。レイアウトテンプレートにSmartyキャッシュを使用する場合の注意点を。

コンテンツのキャッシュが更新された時には、レイアウトのキャッシュを手動でクリアしてください。レイアウトは自分自身に変更がない限りは有効時間までキャッシュを更新してくまれせんので。

2007年6月14日

「名刺作り」で学ぶレイアウト(+α)基礎講座
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは。yamazakiです。
4月に入社してはや3ヶ月目。同じように4月に入った方などは、仕事にも少しずつ慣れてきて、そろそろ少しずつ外に目を向ける余裕も出てくる頃でしょうか。
たとえば社外のイベントや勉強会などに参加する、となれば必携の「名刺」。
会社から支給される場合がほどんどだとは思いますが、中には自分でオリジナルの名刺を作って配る、なんていう人もいるかもしれませんね。
今回は「名刺のデザイン」をネタに、「レイアウト」の基本的なあれこれを解説してみたいと思います。

名刺といえば大抵の場合、以下のような内容を載せますね。

  • 名前
  • 肩書き
  • メールアドレス
  • 携帯電話番号
  • 会社の電話番号、Fax番号
  • 会社名
  • 会社の住所

これ以外にも会社によって色々あるかと思いますが、ひとまず今回はこれだけの要素で作ってみることにします。
また、使うフォントは、(MS ゴシックは印刷向けにはちょっと品質がよくないので)Officeが入っているPCには大抵入っているであろう「HGP創英角ゴシックUB」「HGPゴシックE」の2つのみ使う、という制限を課して進めてみます。(ので、ちょっと作例の品質はよくないですがその点はあしからず)

じゃあ、早速ですがとりあえず名刺のフォーマット(55mm×91mm)に流し込んでみましょう。

meishi1
meishi1 posted by (C)フォト蔵
さて、できました。必要な要素はすべて入っています。
でもこれではなんというか、いくらなんでも微妙ですね…。
何がいけないのでしょうか。

グループ化する


まず、ひとつの問題点は、「要素がグループ化されていない」ということです。
「名前」「部署」などの各要素はそれぞれ「関連の深いもの」と「関連の薄いもの」があります。たとえば「会社名」と「会社の住所」は関連の深い情報です。逆に「個人名」と「会社の住所」はやや関連が薄いでしょう。

それぞれ関連のあるものはまとめて、グループ化してやると、情報は閲覧しやすくなります。たとえば「会社についての情報が見たい」と思ったら右下のほうにまとまってるからそこを見ればいい、というように、迷わず色々な情報にアクセスする、ということがしやすくなるからです。

グループ化の「方法」


視覚的に「グループわけ」をする方法としては、こんな方法があります。

  • 離す
  • 大きさを変える
  • フォントのウエイト(太さ)、種類を変える(文字の場合)
  • 色を変える
  • 囲う

grouping
grouping posted by (C)フォト蔵
では、上の名刺もちょっとグループを分けて、関係のありそうなものをまとめてみましょう。
とりあえず「会社関連の情報」と「個人に関する情報」の二つに分けてみます。グループ化についてはとりあえず無難に「離す」方向で…。

meishi2
meishi2 posted by (C)フォト蔵

主役は何か


だいぶわかりやすくなりましたが、でもまだなんだか微妙です。
その理由のひとつは「主役が何か分からない」ということです。

Webや新聞を見るとき、大抵は見出しを流し読みして気になったものの中身を見ます。見出しがなかったら、それぞれの記事を少しずついちいち読んで…というげんなりするような手間がかかります。
「まず、どれを見たらいいのか」、それをちゃんと見る人に教えてあげること。それがWeb/情報デザインでも名刺などのデザインでも大事なポイントの一つです。

というわけで、名刺における「主役」を考えてみます。
たとえばもしも「自分の名前を覚えてもらいたい」という目的で使う名刺なら、主役は何よりも「名前」でしょうし、逆に「会社の名前」を覚えてほしい場合は「会社の名前」を主役に定めます。そのあたりはその名刺を渡す「目的」から判断できると思います。

主役は主役らしく


主役が決まったら、主役は主役らしい見た目にしましょう。たとえばこんな方法があります。

  • 大きくする
  • 太くする
  • 色を他と変える
  • 他のものよりも空間を広くとる

今回はとりあえず名前を覚えてもらいたい、ということで、名前を目立たせます。(※ちなみに画像の名前は本名です。せっかくですので覚えてやってください(笑)
「会社情報」の部分では会社名が大事だと判断して、会社名を太く、少し大きくして、さらに住所などから少し離しました。離すことで、グループ内で少しだけ特別な感じが出ています。

meishi3
meishi3 posted by (C)フォト蔵
前のものと位置はほとんど変わってないのに、だいぶ見た目の印象が変わりました。

さらに味付けして印象をコントロールする


さてここからはさらに一歩突っ込んで、「印象」のコントロールをしてみましょう。

文字の大小、文字の太さ

文字は大きく太くなると勢いや活気、元気さが出てきます。ただしこれは同時に「下品さ」「荒っぽさ」のようなマイナスの印象を与えることがありますので注意が必要です。逆に小さくすると「落ち着き」「繊細さ」「上品さ」などの印象が出てきます。これも裏を返せば「たよりなさ」「気の弱さ」のような印象にもつながります。

meishi5
meishi5 posted by (C)フォト蔵

meishi4
meishi4 posted by (C)フォト蔵
また、名刺の中にある文字の大小の差、というのも大事です。文字の大小の差が大きいと、元気さ、活気が出てきます。逆に大小の差が小さいと落ち着きが出ます。

文字間・余白

meishi6
meishi6 posted by (C)フォト蔵
単なるゴシック体でも、こうして文字と文字の間を広めにしてやると、それだけでなんとなく上品な、洗練された感じに見えてきませんか?
逆にもっと狭くしてみると、暑苦しさというか熱気というか、そんなものが出てきます。

meishi7
meishi7 posted by (C)フォト蔵
余白については、非常に大雑把に言うと、余白が多いと上品な印象になります。
逆に、文字の間を狭くして敷き詰めて、余白をなくしてやると元気さ・勢いが出ます。


以上、非常にざっくりした内容ではありますが、いかがでしたでしょうか。
今回は「レイアウト」というテーマでしたので、色はあくまでモノクロのみ、文字のみでやってみましたが、とりあえず文字だけでもこれだけのことができます。
他にも「位置、配置による印象」「バランス」「グリッドレイアウト」など今回書ききれなかった内容もあるのでその辺りはまたいずれ。
いずれにしてもここまでの内容は基本のき。
写真やイラストを入れたり、色を変えたり、ちょっと特殊な紙を使ってみたり…と、名刺ひとつをとっても作り方は無限大です。
クリエイティブにいきましょう!

2007年6月13日

Pythonでメールを送信したい人のためのサンプル集
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

chihiroです。

最近Pythonでのメールを送受信に試行錯誤することがあり、ようやく分かってきたので、ここにまとめておきたいと思います。

Pythonでメールを送信する

Python標準ライブラリでメールを送信する場合、

  1. emailパッケージを使ってMIME文書を作成
  2. smtplibを使って送信

という手順を踏みます。

emailパッケージははじめはとっつきにくいのですが、 各クラスのインターフェイスは統一感があり、よく練られているので、一度分かってしまえば明快です。 国際化されたヘッダーやテキスト以外のコンテンツの扱いに関しても問題ないので、 "battery inside"なPythonのありがたみを実感できるパッケージだと思います。

基本的な例

テキスト形式のメッセージをlocalhost:25から送信する例です。

# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
from email.Utils import formatdate

def create_message(from_addr, to_addr, subject, body):
    msg = MIMEText(body)
    msg['Subject'] = subject
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Date'] = formatdate()
    return msg

def send(from_addr, to_addr, msg):
    # SMTPの引数を省略した場合はlocalhost:25
    s = smtplib.SMTP()
    s.sendmail(from_addr, [to_addr], msg.as_string())
    s.close()

if __name__ == '__main__':
    from_addr = 'spam@example.com'
    to_addr = 'egg@example.com'
    msg = create_message(from_addr, to_addr, 'test subject', 'test body')
    send(from_addr, to_addr, msg)

MIMETextクラスのインスタンスを作って、text/plainなMIME文書を作ります。 smtplibによる接続・送信は、認証がない場合は単純明快です。

gmailを使って送信してみる

gmailはTLSを使って接続しなくてはならないので、接続と認証の手順が上の例とは異なります。

# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText

def create_message(from_addr, to_addr, subject, body):
    # 上と同じ
    pass

def send_via_gmail(from_addr, to_addr, msg):
    s = smtplib.SMTP('smtp.gmail.com', 587)
    s.ehlo()
    s.starttls()
    s.ehlo()
    s.login('yourname@gmail.com', 'password')
    s.sendmail(from_addr, [to_addr], msg.as_string())
    s.close()

if __name__ == '__main__':
    from_addr = 'yourname@gmail.com'
    to_addr = 'egg@example.com'
    msg = create_message(from_addr, to_addr, 'test subject', 'test body')
    send_via_gmail(from_addr, to_addr, msg)

日本語を含んだメール

emailパッケージのデフォルトの文字セットは'us-ascii'なので、 日本語を含んだメールを送信する場合は、明示的に指定する必要があります。

plain/textのMIME文書の場合は、MIMETextオブジェクトのコンストラクタで文字セットを指定するのが一番簡単です。 それに加えて、メールのヘッダーは、email.HeaderのHeaderオブジェクトを使って国際化しなくてはなりません。

# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
from email.Header import Header
from email.Utils import formatdate

def send(from_addr, to_addr, msg):
    # 上に同じ
    pass

def create_message2(from_addr, to_addr, subject, body, encoding):
    # 'text/plain; charset="encoding"'というMIME文書を作ります
    msg = MIMEText(body, 'plain', encoding)
    msg['Subject'] = Header(subject, encoding)
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Date'] = formatdate()
    return msg

if __name__ == '__main__':
    from_addr = 'spam@example.com'
    to_addr = 'egg@example.com'
    msg = create_message2(from_addr, to_addr, u'テスト', u'本文', 'ISO-2022-JP')
    send(from_addr, to_addr, msg)

DoCoMoに絵文字入りのメールを送る

文字セットにShift_JISを使用すると、DoCoMo端末に対して絵文字を含んだメールを送信することができます。

ただし、Pythonのemailパッケージは、入力文字コードとしてShift_JIS, EUC-JPを選択した場合、 自動的に出力文字コードをISO-2022-JPに変換しようとするので、強制的にShift_JISで送信するには追加の処理が必要です。

以下は、"NO Erlang[絵文字:ひらめき] NO Life[絵文字:ハート(複数)]"というメールを送信するサンプルです。 DoCoMoでは、"0xf90xa0"が「ひらめき」の絵文字、"0xf90x94"が「ハート(複数)」の絵文字です。

# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
from email.Header import Header
from email.Utils import formatdate
from email import Charset

# ヘッダーをQuoted-Printable, ボディーをbase64でエンコードします
Charset.add_charset('shift_jis', Charset.QP, Charset.BASE64, 'shift_jis')

# 文字コードの変換には、"shift_jis"ではなく"cp932"を使っています
Charset.add_codec('shift_jis', 'cp932')

def send(from_addr, to_addr, msg):
    # 上に同じ
    pass

def create_message_for_docomo(from_addr, to_addr, subject, body):
    msg = MIMEText(body, 'plain', 'shift_jis')
    msg['Subject'] = Header(subject, 'shift_jis')
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Date'] = formatdate()
    return msg

if __name__ == '__main__':
    from_addr = 'spam@example.com'
    to_addr = 'your_email_address@docomo.ne.jp'
    body = 'NO Erlang\xf9\xa0No Life\xf9\x94'
    subject = 'test'
    msg = create_message_for_docomo(from_addr, to_addr, subject, body)
    send(from_addr, to_addr, msg)

ポイントは、Charset.add_charsetでshift_jisの設定を上書きしていることと、 Charset.add_codecで明示的にコーデックを指定することで、shift_jisのエンコード、 デコードにcp932を使用するようにしていることです。

SoftBankに絵文字入りのメールを送る

一方、SoftBankには、UTF-8を使うと絵文字入りメールを送信できるようです。 SoftBankの技術資料「絵文字一覧」に、各絵文字のユニコードが公開されているので、これを参考に、ユニコード文字列の本文をUTF-8にエンコードしてMIME文書を作ります。 これさえ分かってしまえば、難しいことはありません。

例えば、蛇の絵文字はユニコードだとU+E52Dなので、"アイ ラブ Python[絵文字:蛇]"という文書ならば、次のようにします。

# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
from email.Header import Header
from email.Utils import formatdate

def send(from_addr, to_addr, msg):
    # 上に同じ
    pass

def create_message_for_softbank(from_addr, to_addr, subject, body):
    msg = MIMEText(body, 'plain', 'utf-8')
    msg['Subject'] = Header(subject, 'utf-8')
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Date'] = formatdate()
    return msg

if __name__ == '__main__':
    from_addr = 'spam@example.com'
    to_addr = 'your_email_address@softbank.ne.jp'
    body = u'アイラブ Python\ue52d'.encode('utf-8')
    subject = 'test'
    msg = create_message_for_softbank(from_addr, to_addr, subject, body)
    send(from_addr, to_addr, msg)

DoCoMoの例と違い、Charset.add_charsetを行っていませんが、これは、UTF-8を使用した場合、 初めから「ヘッダーにはQuoted-Printableかbase64, 本文にはbase64」でエンコードするように定義されているためです。

添付付きメール・デコメール

添付ファイル付きのメールを送るには、MIMEMultipartクラスのインスタンスをつくり、 MIMEText, MIMEAudio, MIMEImage等のMIMEオブジェクトをattachしていきます。

添付付きメールを送るだけでは捻りがないので、HTMLメール(DoCoMoのデコメール)の送信例を紹介します。

# -*- coding: utf-8 -*-
import smtplib
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from email.Header import Header
from email.Utils import formatdate

def send(from_addr, to_addr, msg):
    # 上に同じ
    pass

HTML_BODY = """<html>
<body>
<img src="cid:a"><br>
Hello World<br>
<img src="cid:c"><br>
Welcome to the world of Python<br>
<img src="cid:b"><br>
<body>
</html>"""

def create_deco(from_addr, to_addr):
    msg = MIMEMultipart()
    msg['Subject'] = Header(u"デコメ", 'ISO-2022-JP')
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Date'] = formatdate()

    related = MIMEMultipart('related')
    alt = MIMEMultipart('alternative')
    related.attach(alt)

    content = MIMEText(HTML_BODY, 'html', 'ISO-2022-JP')
    alt.attach(content)

    for filename in ['a', 'b', 'c']:
        fp = file('%s.gif' % filename, 'rb')
        
        # Content-Type: image/gif; name="a.gif"
        img = MIMEImage(fp.read(), 'gif', name=filename)
        
        # HTMLからイメージを参照するにはContent-IDが必要です
        img['Content-ID'] = '<%s>' % filename
        related.attach(img)

    msg.attach(related)
    return msg

if __name__ == '__main__':
    from_addr = 'spam@example.com'
    to_addr = 'your_email_address@docomo.ne.jp'
    msg = create_deco(from_addr, to_addr)
    send(from_addr, to_addr, msg)

送信結果は次のようになります。

デコメールに関しては以前にharukiさん詳しい資料を公開していますので、興味のある方はこちらもご覧下さい。

おまけ

泥臭いサンプル集になってしまいましたが、それなりに動くWebアプリケーション、 Webサイトを作ろうとするならば、もう少し洗練された解決策が必要です。

  • Twistedtwisted.mail.smtpを使えば、非同期IOを利用して効率よくメールの送信を行えそうです。
  • 開発版Djangodjango.core.mailモジュールは大幅にリファクタリングされて、メッセージの作成と送信を分離できるようになりました。Djangoをインストールしておけば、もちろんDjangoのテンプレートエンジンを利用できるので、「テンプレートからメッセージの作成」「MIME文書作成」「送信」という処理をきれいに行うことができそうです。email, smtplibパッケージの使用例としても参考になります。

僕自身まだ研究中で、いろいろ試行錯誤している段階なのですが、 もう少し整理できたならば、またこのブログで紹介したいと思います。

また、自分自身いろいろ調査していて、 Python標準の「ライブラリリファレンス」 がやはり一番参考になると実感しています。こちらも合わせてどうぞ。

2007年6月12日

ニンテンドーDSブラウザーにあわせてサイトを作るときの軽い覚書
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは。matsudaです。

以前、こちらでも紹介させていただきました犬・猫写真でカワイイか投票する「Cute or Not」ですが、これはもともとNintendoDSで遊べるように作っていました。DSブラウザはいまさらな感が否めないですが、上画面に写真・下画面に投票ボタンにして、タッチパネルでポチポチできて、暇つぶしにはもってこいな気がしたからです。

簡単にデザインできるだろう…とはじめは思っていましたが、実はそうでもなく、ちょっと苦戦したところがあったので、メモ程度に残しておきます。

※以下、DSブラウザー特有の“縦長モード”での場合です。

■画面のデザインは1画面:縦176px×横247px

NintendoDS の液晶は1画面192px×256pxになっています。そのうち上画面は上16pxがURL表示、下画面は下16pxが機能表示に利用されています。横幅はスクロールバー用に9px使用されているので、結局1画面の有効範囲は176px×247pxになります。

■CSSはhandheld

当初、CSSが反映されず悪戦苦闘しました。 <link rel="stylesheet" href="style.css" media="handheld" type="text/css"> とmedia="handheld"を入れるとうまくいきます。

■CSSのfont-sizeは3段階

x-small、small、mediumのみでした。あの小さな画面ではこれくらいでちょうどいいのでしょうね。


などど悪戦苦闘している時に見つけた、とても便利なサイトがありましたので最後にご紹介します。(もっと早くに見つけておくべきでした…)

●ニンテンドーDSブラウザーWiki - 開発/html仕様
http://hammerspace.squares.net/DS/wiki.cgi?page=%B3%AB%C8%AF%2Fhtml%BB%C5%CD%CD

●DSブラウザ実験ページ
http://ds.wakufactory.jp/dev/
こちらの“縦画面モード用テンプレ”を利用すると便利です!

そのほかTipsをご存知であればぜひ教えてください。

viを使おう
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

ウノウでは指定のエディタやフレームワークというのはなく、サクラエディタ やvi、emacsなど個々に使いやすいエディタを使用しています。
vi派、emacs派に分かれて、いかに優れているか度々論争になりますが、ウノウではどうもvi派が優勢なようです。
さて、その多数派のviですが、初心者向けとは言いがたくとっつきにくい面があります。
かく言う私もずっとサクラエディタ派でしたが、今ではviの移動コマンドに慣れてしまいブラウザでもjやkをタイプしたりCtrl+bしたりしてます。
またカスタマイズすることでどんどん使いやすくなり、もはや手放せないモノになってきます。
そこで、私が使っているのはvimですが、使っているvimの設定を一部ご紹介しようと思います。

.vimrc
set autoindent #オートインデント
set number #行番号表示
set incsearch #インクリメンタルサーチ
set ignorecase #検索時に大文字小文字を無視
set nohlsearch #検索時にハイライト無効
set showmatch #対括弧の表示
set showmode #モード表示
set backspace=2 #上行末尾の文字を1文字消去
set title #編集中のファイル名を表示
set ruler #ルーラーの表示
set tabstop=4 #タブ文字数を4に
set shiftwidth=4 #シフト移動幅
set expandtab #タブの代わりに空白文字挿入

ほかにも設定は色々あるので試してみてはいかがでしょうか。

ほかによく使うコマンドは、

:e ++enc=UTF-8 #開いているファイルの文字コード(この場合はUTF-8に)を変換したりなど。
:vs [ファイル名] 縦に分割
:sp [ファイル名] 横に分割

今日からあなたも viva vi!

2007年6月 6日

Feedcreatorのご紹介
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

yamaokaです。

PHPでRSSフィードを出力する場合、 皆さんはどうされているでしょうか。 Smartyなどの テンプレートエンジンを利用して自前で組み立てるのもいいですが、 RSSフィード生成用のライブラリを使うと便利です。

Feedcreator

Feedcreatorは、 SourceForge.netで 開発されているPHPのRSSフィード生成ライブラリです。 FeedCreator.class.phpを ベースにして開発されています。

RSS 0.91/1.0/2.0、Atom 0.3/1.0のフォーマットにそれぞれ対応しています。 また、PIE 1.0やOPML 1.0などにも対応。ライセンスはLGPLです。

ライブラリをダウンロードして展開すると、 「official_demo.php」というファイルがあるので目を通しておきましょう。 おおよその処理の流れがつかめるかと思います。

利用例

ライブラリは「feedcreator.class.php」という1つのファイルにまとめられています。 例えば、Atom 1.0のフォーマットで「feed.xml」に出力するには以下のように記述します。

define('TIME_ZONE', '+09:00');

require_once '/path/to/feedcreator.class.php';

$creator = new UniversalFeedCreator();
$creator->useCached();
$creator->title = 'タイトル';
$creator->description = '説明文';
$creator->link = 'http://example.com/';
$creator->syndicationURL = 'http://example.com/feed.xml';

// 記事を1つ追加
$item = new FeedItem();
$item->title = '記事のタイトル';
$item->link = 'http://example.com/articles/sample';
$item->description = '記事の短い説明';
$item->date = '2007-06-06T12:00:00+09:00';
$item->author = '記事の著者名';

$creator->addItem($item);

// ファイルとして保存する場合
$creator->saveFeed('ATOM1.0', 'feed.xml');

// ファイルに保存せずに出力する場合
// $creator->outputFeed('ATOM1.0');

利用上の注意

タイムゾーンを定義すること

必要に応じて「TIME_ZONE」という定数を定義する必要があります。 指定された値がRSSフィード内の日付のタイムゾーンとして使用されます。 設定しない場合のデフォルト値は「+01:00」です。 日本で使う場合は必ず定義する必要がありますね。

エンコーディングに注意

ソースを覗いてみたところ、 各フォーマットでデフォルトのエンコーディングを内部的に持ってしまっているようです。 例えば、Atom 1.0は「utf-8」、RSS 2.0は「ISO-8859-1」となっています。 RSS 2.0の場合、そのままでは日本語が使えません。

任意のエンコーディングに変更できるような仕組みが用意されていないので、 日本語を利用するためにはライブラリ自体に手を入れるか、 UTF-8で利用できるAtomを利用するなどの工夫が必要になります。

最後に

PHPでRSSフィードをパースするためのライブラリは MagpieRSSなど いろいろあるのですが、RSSフィードを生成するためのライブラリはあまりありません。

そうした中、Feedcreatorは 手軽にRSSフィードの生成を行うことができる貴重なライブラリです。 自前で組み立てる場合はいろいろと調べる必要がありますが(RSSフィードの仕様など)、 ライブラリを利用すればそういう面倒はありません。

ただ、日本語を扱う上でいろいろと問題があるのも確かです。 その問題点を認識した上でハックして利用するか、 自前でRSSフィードを組み立てる方法を選択するか。 あるいは、もっと柔軟に扱えるようなライブラリを作って 公開するという選択肢もあるかもしれませんね。

2007年6月 5日

MySQL5からのインデックス結合で1テーブル複数インデックスを使う
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

komagataです。

Webアプリケーションのパフォーマンスの大半はデータベース、特にインデックスの使われ方にかかっている気がします。

仕事でもMySQLをよく使いますが、MySQLでは1テーブルに付き1インデックスしか使われません。PostgreSQLなどと比べてそのことが気になってMySQLでのパフォーマンスチューニングに全く自信が持てませんでした。

オライリーの実践ハイパフォーマンスMySQLには下記のように書かれています。

実際、UNIONを除き、MySQLでは、1つのクエリを実行するとき、1つのテーブルに付き1つのインデックスしか使用できない。この事実は、繰り返し述べるに値するほど重要である。「MySQLでは、1つのクエリを実行するとき、1つのテーブルにつき1つのインデックスしか使用できないのである。」

また、その制約を考えたクエリの書き方として下記の様に書いてあります。

mysql> EXPLAIN SELECT * FROM Headline
    -> WHERER ExpireTime >= 1012201600 OR Id <= 5000000
    -> ORDER BY ExpireTime ASC LIMIT 10\G

何と、MySQLは完全なテーブルスキャンを実行すべきだと判断してしまった。(中略)このようなクエリを、UNIONを使用して書き換えることができる。そのためには、クエリを2つのクエリに分解し、それぞれのクエリで1つのインデックスを使用する。その後で、結果を結合してソートする。つまり、以下のようなクエリを実行すればよい。

(SELECT * FROM Headline WHERE ExpireTime >= 1081020749
ORDER BY ExpireTime ASC LIMIT 10)
 
UNION
 
(SELECT * FROM Headline WHERE Id <= 50000
ORDER BY ExpireTime ASC LIMIT 10)
 
ORDER BY ExpireTime ASC LIMIT 10

こんな簡単なクエリでわざわざこんなことしなくちゃいけないのかよと思いました・・・。

しかし本書はMySQL 4.0.14をベースにかかれていています。MySQLユーザー的には常識なのかもしれませんがMySQL 5.0からはインデックス結合という機能があるそうです。1テーブル1インデックス問題(勝手にそう呼んでいる)が解決するかもしれないと思って調べてみました。

インデックス結合とはその名の通り複数のインデックスを結合して結果を返してくれるそうです。EXPLAINのtypeフィールドではindex_mergeというメソッド名で表されます。

マニュアルに拠ればインデックス結合には3つのアルゴリズムがありそれぞれEXPLAINのExrtaフィールドで確認できます。(Using~というやつ)

そしてそれぞれのアルゴリズムは下記のような時に選ばれるそうです。

  • 共通集合アルゴリズム --- Using intersect(...)
    WHERE節で異なるインデックスをANDでつないだ時。
  • ユニオンアクセスアルゴリズム --- Using union(...)
    WHERE節で異なるインデックスをORでつないだ時。
  • ソートユニオンアクセスアルゴリズム --- Using sort_union(...)
    WHERE節で異なるインデックスをORでつないだ時でソートが必要な時。(前述のUNIONしなければならない例はこれに該当するんじゃないでしょうか)

結合方法自体OPTIMIZERが選択するので必ず使われるわけではありません。
実際の効果の程を試してみました。

試した環境:

Debian etch
MySQL 4.1.22, MySQL 5.0.32

テーブル:

CREATE TABLE `employees` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(32) default NULL,
  `age` int(11) default NULL,
  PRIMARY KEY  (`id`),
  KEY `index_name` (`name`),
  KEY `index_age` (`age`)
)

テストデータ(ランダムなデータ10万件):

#!/usr/bin/env ruby
 
100000.times do |i|
  puts "INSERT INTO employees (name, age) VALUES ('name_#{rand(100)}', #{rand(100)});"
end


テストデータを投入し、ANALYZE TABLEした後のインデックスは下記の通りです。

mysql> SHOW INDEX FROM employees\G
*************************** 1. row ***************************
       Table: employees
  Non_unique: 0
    Key_name: PRIMARY
Seq_in_index: 1
 Column_name: id
   Collation: A
 Cardinality: 160673
    Sub_part: NULL
      Packed: NULL
        Null:
  Index_type: BTREE
     Comment:
*************************** 2. row ***************************
       Table: employees
  Non_unique: 1
    Key_name: index_name
Seq_in_index: 1
 Column_name: name
   Collation: A
 Cardinality: 53557
    Sub_part: NULL
      Packed: NULL
        Null: YES
  Index_type: BTREE
     Comment:
*************************** 3. row ***************************
       Table: employees
  Non_unique: 1
    Key_name: index_age
Seq_in_index: 1
 Column_name: age
   Collation: A
 Cardinality: 99
    Sub_part: NULL
      Packed: NULL
        Null: YES
  Index_type: BTREE
     Comment:
3 rows in set (0.00 sec)

この条件でMySQL 4.1.22, 5.0.32それぞれで上記のアルゴリズムが使われた場合(5.0.32)と使われない場合(4.1.22)でEXPLAIN結果と速度を計りました。

・共通集合アルゴリズム

MySQL 4.1.22の場合:

mysql> EXPLAIN SELECT * FROM employees WHERE name = "name_1" AND age = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
         type: ref
possible_keys: index_name,index_age
          key: index_name
      key_len: 33
          ref: const
         rows: 571
        Extra: Using where
1 row in set (0.00 sec)

普通にインデックスが一つだけ使われました。

MySQL 5.0.32の場合:

mysql> EXPLAIN SELECT * FROM employees WHERE `name` = 'name_1' AND `age` = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
         type: index_merge
possible_keys: index_name,index_age
          key: index_age,index_name
      key_len: 5,99
          ref: NULL
         rows: 5
        Extra: Using intersect(index_age,index_name); Using where
1 row in set (0.01 sec)

typeにindex_merge、ExtraにUsing intersect(index_age,index_name)と出ました。共通集合アルゴリズムを使って別々のインデックスがマージされて使われているようです。

実際の速度:

mysql> SELECT SQL_NO_CACHE * FROM employees WHERE `name` = 'name_1' AND `age` = 1;

インデックス結合無し(MySQL 4.1.22):0.01 sec
インデックス結合有り(MySQL 5.0.32):0.01 sec

・ユニオンアクセスアルゴリズム

MySQL 4.1.22の場合:

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM employees WHERE `name` = 'name_1' OR `age` = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
         type: ALL
possible_keys: index_name,index_age
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 100000
        Extra: Using where
1 row in set (0.01 sec)

対象レコードが多いのでALLになりました。

MySQL 5.0.32の場合:

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM employees WHERE `name` = 'name_1' OR `age` = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
         type: index_merge
possible_keys: index_name,index_age
          key: index_name,index_age
      key_len: 99,5
          ref: NULL
         rows: 2749
        Extra: Using union(index_name,index_age); Using where
1 row in set (0.00 sec)

typeにindex_merge、ExtraにUsing union(index_name,index_age)と出ました。ユニオンアクセスアルゴリズムを使って別々のインデックスがマージされて使われているようです。

実際の速度:

mysql> SELECT SQL_NO_CACHE * FROM employees WHERE `name` = 'name_1' OR `age` = 1;

インデックス結合無し(MySQL 4.1.22):0.13 sec
インデックス結合有り(MySQL 5.0.32):0.02 sec

・ソートユニオンアクセスアルゴリズム

MySQL 4.1.22の場合:

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM employees WHERE `name` = 'name_1' AND `age` < 5\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
         type: ref
possible_keys: index_name,index_age
          key: index_name
      key_len: 33
          ref: const
         rows: 571
        Extra: Using where
1 row in set (0.01 sec)

普通にインデックスが一つだけ使われました。

MySQL 5.0.32の場合:

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM employees WHERE `name` = 'name_1' OR `age` < 5\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
         type: index_merge
possible_keys: index_name,index_age
          key: index_name,index_age
      key_len: 99,5
          ref: NULL
         rows: 11537
        Extra: Using sort_union(index_name,index_age); Using where
1 row in set (0.00 sec)

typeにindex_merge、ExtraにUsing sort_union(index_name,index_age)と出ました。ソートユニオンアクセスアルゴリズムを使って別々のインデックスがマージされて使われているようです。

実際の速度:

mysql> EXPLAIN SELECT SQL_NO_CACHE * FROM employees WHERE `name` = 'name_1' OR `age` < 5;

インデックス結合無し(MySQL 4.1.22):0.15 sec
インデックス結合有り(MySQL 5.0.32):0.07 sec

大抵の場面で複数のインデックスを結合してクエリが速くなることがわかりました。これらの条件はとりたてて特殊なものではないので速くなる場面は多そうです。MySQL 5.0以上に乗り換えた再に無自覚に効果が出ているかもしれませんが、MySQL 4.1系以前とは違ってこのインデックス結合を意識したインデックス設計は大事だと思います。

個人的にこの1テーブル1インデックス問題はずっと気になっていて、他のRDBに乗り換えようかな、などと考えていたんですが、このインデックス結合をみてMySQLで頑張ってみようという気になりました。

※追記
「複合インデックスと名前が紛らわしい」、「例題が悪い」との指摘を受けました。確かにその通りだと思いました。それとPostgreSQLのEXPLAINで出てくる様なMerge自体のコストも気になるところです。追って調べていきたいと思います。

2007年6月 4日

携帯におけるメールアドレスの制限について調べてみました
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

harukiです。
携帯サイトの開発は、いわば端末の制限との戦いとも言えます。
今回は、メールアドレスだけに絞って、端末での制限について調べてみました。

(会社にある端末で調べたので、すべての端末で当てはまるかどうかは保障できません)

    まず、携帯のメーラでは宛先に入れられるByte数が決まっています。
  • DoCoMo: 50Byte
  • au: 64Byte
  • SoftBank: 128Byte
  • 上記のByte数より長いメールアドレスへは端末からは絶対に送信できません。

    次に、local-partで使える文字を以下の機能が動くかどうかで調べてみました。
  • メールの送信
  • web(aタグのmailto)
  • メール(本文でのmailto機能)
文字RFC2822DoCoMoauSoftBank
送信aタグメール本文送信aタグメール本文送信aタグメール本文
!
"×××××××××
#
$
%
&×
'
(×××××××××
)×××××××××
*
+
,××××××××××
-
.
/
:××××××××
;××××××××
<×××××××××
=×
>×××××××××
?×
@×××××××××
[××××××××
\××××××××
]××××××××
^
_
`
{
|
}
~
"localpart"@example.com××××
"localpart@example.com"×××××
<localpart@example.com>××××
[localpart]@example.com×××××××
.localpart@example.com×

よほど特殊な事情がない限りは気にしなくていいことではありますが、何かの時に一から調べるのが面倒なのでまとめてみました。

2007年6月 2日

Delphi For PHPを簡単に触ってみました
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

jQueryは、ajaxSubmitプラグインが好きなKeitaです。

僕はもともと、Delphiの技術者でそこからPHP技術者になりました。
もはや、Web系のアプリケーションの開発に夢中なので、Delphiをいじることは少なくなりましたが、ちょっと前に、Delphi For PHPという統合開発環境が発売されたようなので、Object Pascalじゃなくて、Delphiって言語の名前って誰か中の人いってたよなーとおもいつつ、軽く触ってみました。

まず、Delphi For PHPは、Delphiと同じようにその言語の開発環境というだけではなく、フレームワーク的なものを内包しています。

Delphiでは、WindowsアプリケーションをIDEでぺたぺた貼り付けて開発するためにVCL(Visual Component Library)というライブラリがありますが、Delphi For PHPにもVCL For PHPというものが準備されているようです。

こういうのが、準備されているおかげで、Delphi for PHPの概要(英語)のビデオにあるような、本当にPHPで動いているのかわからないような開発方法で開発ができるようになるようです。

「なようです」、とかいってるのもアレなので、とりあえず、使ってみたいと思います。

WS000001
WS000001 posted by (C)フォト蔵

とりあえず、Hello PHPとかいて実行してみました。


WS000002
WS000002 posted by (C)フォト蔵

とりあえず、なんか動きました。

で、まず最初に気になったこういうIDEでいわれる出力されるHTMLソースがどうかというあたりですが、こんな感じでした。


<!-- Unit1 begin -->

<html >
<head>
<script type=&text/javascript& src=&/vcl-bin/js/common.js&></script>
<title>Unit1</title>
<meta http-equiv=&Content-Type& content=&text/html; charset=euc-jp& />
</head>

<body style=& margin-left: 0px; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; & >
<form style=&margin-bottom: 0& id=&Unit1& name=&Unit1& method=&post& action=&/sample1.php&>
<table width=&800& style=&height:600px& border=&0& cellpadding=&0& cellspacing=&0& ><tr><td valign=&top&>
<div id=&Label1_outer& style=&Z-INDEX: 0; LEFT: 104px; WIDTH: 75px; POSITION: absolute; TOP: 49px; HEIGHT: 13px&>
<div id=&Label1& style=& font-family: Verdana; font-size: 10px; height:13px;width:75px;& >Hello Delphi</div>

</div>
</td></tr></table>
</form></body>
</html>
<!-- Unit1 end -->

ちなみに、ここまでの作業は、Delphi For PHPをインストールするだけで、Webサーバの立ち上げまでやってくれるので、他に別途作業する必要はありませんでした。

もちろん、Delphiの名を冠しているだけあって、ステップ実行も可能です。これには、DBGというPHPのエクステンションを利用しているようです。

ちなみに、出来上がったPHPファイルを見てみると。
保存したファイル名のものとなにやら、PHPのふりをしたフォームの情報が入ったXMLファイルが出来上がっており、これを、Webサーバに配置して、VCL For PHPにファイルにパスを通してやれば、既存のApache上でも動きそうな気がします。

さて、本来は、Delphi For PHPの売りであると予測される、DB接続なども紹介したいのですが(ビデオ見た限りはそこらへんがとても変態的でよかった)、とりあえず、触ってみたといいたかっただけで、それ以上の情熱は今の所わかなかったので、何か思い立ったら詳しくつっこみたいとおもいます。

個人的な結論として、ちゃんと使えば、Accessなどと同じようなレベルで社内Webサーバに配置して配布せずに、社内アプリケーションとして用意するのには使えるかもしれないとおもいました。
が、すでにPHPをやっていて、ばりばりフレームワークを使って開発しているような人たちに必要かといわれると、住み分けが大切だと思いました。

もし、ご興味が湧きましたら、フル機能あるトライアル版がありますので、そちらを試してみてはいかがでしょうか。