<?xml version="1.0" encoding="utf-8"?> 
<?xml-stylesheet href="stylesheet.xsl" type="text/xsl" media="screen"?>

<rdf:RDF 
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns="http://purl.org/rss/1.0/"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:admin="http://webns.net/mvcb/"
  xml:lang="ja">

  <channel rdf:about="http://feed.feedforce.jp/f2892/index.rdf">
    <title>FFTT</title>
    <link>http://tech.feedforce.jp/</link>
    <image rdf:resource="http://feed.feedforce.jp/f2892/logo.gif" />
    <description>社内の勉強会資料やメンバーが適当に思ったことなどを公開をしています。</description>
    <admin:generatorAgent rdf:resource="www.rsssuite.jp" />
    <items>
      <rdf:Seq>
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_1670715_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_1638902_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_1590812_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_1563962_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_1203602_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_1136619_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_1088535_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_1068064_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_968514_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_928070_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_885652_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_852138_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_832206_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_815382_2892.html" />
        <rdf:li rdf:resource="http://feed.feedforce.jp/item_30880_722434_2892.html" />
      </rdf:Seq>
    </items>
  </channel>
  <image rdf:about="http://feed.feedforce.jp/f2892/logo.gif">
    <title>FFTT</title>
    <url>http://feed.feedforce.jp/f2892/logo.gif</url>
    <link>http://tech.feedforce.jp/</link>
  </image>
  <item rdf:about="http://feed.feedforce.jp/item_30880_1670715_2892.html">
    <title>ZABBIX 1.8でサーバ監視</title>
    <link>http://feed.feedforce.jp/item_30880_1670715_2892.html</link>
    <dc:date>2010-02-10T12:13:21+09:00</dc:date>
    <description>　一時期はサーバの死活監視・リソースチェックといえばNagios + Cacti/MRTGでしたが、最近はZABBIXが割と話題に上ることも多いので、昨年末に1.8がリリースされたこともあり、筆者自宅での事例を交えつつご紹介したいと思います。
?</description>
    <content:encoded><![CDATA[<p><span class="date"></span></p>
<p class="body">　一時期はサーバの死活監視・リソースチェックといえばNagios + Cacti/MRTGでしたが、最近はZABBIXが割と話題に上ることも多いので、昨年末に1.8がリリースされたこともあり、筆者自宅での事例を交えつつご紹介したいと思います。</p>
<p>? <a href="http://tech.feedforce.jp/zabbix-18.html#more-98" class="more-link">(more&#8230;)</a></p>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_1638902_2892.html">
    <title>Twitter の Streaming API について</title>
    <link>http://feed.feedforce.jp/item_30880_1638902_2892.html</link>
    <dc:date>2010-01-21T11:42:26+09:00</dc:date>
    <description>先日社内でおこなった勉強会で Twitter の Streaming API について紹介しましたので、以下にそのスライドを貼っておきます。
Streaming API はつい最近正式版となりました。ただ、全公開ステータスを取得できる firehose や Retweet に限定したステータスを取得できる retweet などのメソッドは、まだ特定のアプリケーションアカウントでしか利用できません。
しかし、Streaming API はコネクションをひとつ張れば切断されるまでリアルタイムにデー</description>
    <content:encoded><![CDATA[<p>先日社内でおこなった勉強会で Twitter の <a href="http://apiwiki.twitter.com/Streaming-API-Documentation" target="_blank">Streaming API</a> について紹介しましたので、以下にそのスライドを貼っておきます。</p>
<p><iframe src="http://docs.google.com/present/embed?id=dgbcz6cm_114gdx2mgf7" width="410" frameborder="0" height="342"></iframe></p>
<p>Streaming API はつい最近正式版となりました。ただ、全公開ステータスを取得できる firehose や Retweet に限定したステータスを取得できる retweet などのメソッドは、まだ特定のアプリケーションアカウントでしか利用できません。</p>
<p>しかし、Streaming API はコネクションをひとつ張れば切断されるまでリアルタイムにデータを取得することができます。従来の REST API では定期的にリクエストを投げてデータを取得する必要がありましたので、その分のコストが削減できます。</p>
<p>ですので、これから Twitter のデータを取得して新しいアプリケーションを構築しようとする場合は、Streaming API の利用を考慮に入れておくとよいかなと思います。</p>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_1590812_2892.html">
    <title>rvmの紹介</title>
    <link>http://feed.feedforce.jp/item_30880_1590812_2892.html</link>
    <dc:date>2009-12-11T17:39:28+09:00</dc:date>
    <description>複数のRubyをインストールし、切り替えて使用できるツール rvm(Ruby Version Manager) の紹介を行いました。</description>
    <content:encoded><![CDATA[<h2>rvmとは</h2>
<ul>
<li>Ruby Version Manager の略</li>
<li>複数のRubyをインストールして管理し、切り替えて使う仕組み。
<ul>
<li>MRI(Matz&#8217;s Ruby Implementation), JRuby, Rubinius, REE(Ruby Enterprise Edition), MacRubyに対応しています。</li>
</ul>
</li>
<li>2009年夏に登場しました。</li>
<li>ほとんどシェル関数で実装されています。</li>
</ul>
<h3>ご注意</h3>
<p>この記事の内容は2009年12月4日時点のものです。執筆時点で、rvmは活発に開発が進められているため、最新の状態とは挙動が異なる可能性があります。</p>
<h2>用途(例)</h2>
<ul>
<li>広く使われるライブラリを、異なるruby実装でテストしたい。</li>
<li>使用しているRubyが異なる古いプロダクトを保守したい。</li>
</ul>
<h2>情報源</h2>
<ul>
<li>本サイト: <a href="http://rvm.beginrescueend.com/" class="external">http://rvm.beginrescueend.com/</a></li>
<li>GitHub: <a href="http://github.com/wayneeseguin/rvm/" class="external">http://github.com/wayneeseguin/rvm/</a>
<ul>
<li>Clone URL: git://github.com/wayneeseguin/rvm.git</li>
</ul>
</li>
</ul>
<h2>インストール</h2>
<h3>gemで</h3>
<pre>
$ sudo gem install rvm
$ rvm-install
</pre>
<p>インストールが済んだらgemは削除して構いません。</p>
<pre>
$ sudo gem uninstall rvm
</pre>
<h3>gitで</h3>
<pre>
$ git clone git://github.com/wayneeseguin/rvm.git
$ cd rvm
$ ./install
</pre>
<p>どちらの場合も ~/.rvm 以下にファイルがコピーされ、rvmコマンドが使えるようになります。<br />
このとき ~/.bashrc・~/.bash_profile・~/.zshrc のすべてが書き換えられます。存在しない場合、作成されます。不要なら消しましょう。</p>
<h2>主なコマンド</h2>
<pre>
$ rvm info         - 現在の実行環境を表示
$ rvm list         - 管理しているrubyの一覧を表示
$ rvm install 名前 - 指定されたrubyをインストール
$ rvm use 名前     - 現在のシェルで使用するrubyを切り替える
$ rvm ruby ...     - rubyを一括実行
$ rvm gem ...      - gemを一括実行
$ rvm rake ...     - rakeを一括実行
$ rvm tests ...    - rake testを一括実行
$ rvm specs ...    - rake specを一括実行
</pre>
<h2>rubyのインストール</h2>
<p>インストールしていないものも含め、使える名前のリストを表示します。</p>
<pre>
$ rvm list --all
(ruby-)1.8.0-tv1_8_0
:
(ruby-)1.8.7(-p174)
:
macruby-head # Build from the macruby git repository
</pre>
<p>リストから名前を指定してインストールします。リスト内の名前の括弧内は省略できます。</p>
<pre>
$ rvm install 1.8.7
</pre>
<p>~/.rvm の下にインストールしたRubyの関連ファイル一式が置かれます。</p>
<h2>rubyの切り替え</h2>
<p>基本的な仕組みは、</p>
<ul>
<li>PATHの先頭部に .rvmの管轄下のものを前置。あれば入れ替え。</li>
<li>ラッパースクリプトでの環境変数(GEM_HOMEなど)の切り替え。</li>
</ul>
<pre>
で実現されています。
$ echo $PATH
$ rvm use 1.8.7
$ echo $PATH (先頭部分が変化する)
</pre>
<p>irb, rake, gemなども対応するものが使えるようになる。</p>
<h2>ディレクトリごとの自動切り替え</h2>
<p>カレントディレクトリに .rvmrc ファイルがあり、そこに</p>
<pre>
rvm ruby-1.8.7-p174
</pre>
<p>のように記述しておくと、このディレクトリおよびサブディレクトリに対するrubyを変更することが出来ます。</p>
<pre>
$ cd ~/project1; ruby -v
ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin9]
$ cd ~/project2; ruby -v
ruby 1.8.6 (2009-08-04 patchlevel 383) [i386-darwin9.8.0]
$ cd ~/project3; ruby -v
ruby 1.9.2dev (2009-12-02 trunk 25977) [i386-darwin9.8.0]
</pre>
<p>cdコマンドがシェル関数で再実装されており、ホームディレクトリや / に到達するまでディレクトリを遡って .rvmrc を探し、見つかったものを評価することで切り替えを実現しています。</p>
<h2>一括実行</h2>
<pre>
$ rvm ruby -v
$ rvm gem install rails
$ rvm specs
</pre>
<p>インストールされているrubyで該当処理を順次実行していきます。</p>
<p>複数のRubyに対して一括でgemをインストールしたり、テストを実行したりできます。</p>
<h2>gemセット</h2>
<p>使用するrubyの名前の後ろに%と任意のラベルを付けてgemセットを指定することができます。この仕組みにより、同じrubyを使用しつつ、異なるgem構成を使うことができます。</p>
<pre>
$ cd ~/project1; gem list
$ cd ~/project2; gem list
$ cd ~/project3; gem list
</pre>
<h2>質疑応答</h2>
<h3>Q: インストールにRubyは必要?</h3>
<p>A: gitで入れるなら不要。</p>
<h3>Q: Passengerで使うには?</h3>
<p>A: PassengerRubyには、passenger-install-apache2-module が表示するパスではなく、rvmのラッパースクリプトを指定する。ラッパーの中で必要な環境変数の設定を行ってから対象のRubyをexecしている。<br />
[<a href="http://rvm.beginrescueend.com/passenger/" class="external">http://rvm.beginrescueend.com/passenger/</a>]</p>
<p>ただし、発表者は未検証。</p>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_1563962_2892.html">
    <title>Resque 概要</title>
    <link>http://feed.feedforce.jp/item_30880_1563962_2892.html</link>
    <dc:date>2009-11-24T19:09:21+09:00</dc:date>
    <description>GitHub で使われているジョブキューの Resque の概要について、社内勉強会で発表しました。以下が発表に利用したスライドです。

    Introduction to Resque</description>
    <content:encoded><![CDATA[<div>
  GitHub で使われているジョブキューの Resque の概要について、社内勉強会で発表しました。以下が発表に利用したスライドです。</p>
<ul>
<li><a href="http://www.slideshare.net/koshigoe/introduction-to-resque">Introduction to Resque</a></li>
</ul>
</div>
<div>
  スライドの内容は大分省略しているので、以下に補足資料へのリンクを追記します。</p>
<ul>
<li><a href="http://w.koshigoe.jp/study/?%5BBackgroundJob%5D%5BRuby%5D+Resque+%A4%C8%A4%CF">KOSHIGOE学習帳 - [BackgroundJob][Ruby] Resque とは</a></li>
<li><a href="http://w.koshigoe.jp/study/?%5BBackgroundJob%5D%5BRuby%5D+Resque+%A4%CE+README+%A4%E8%A4%EA">KOSHIGOE学習帳 - [BackgroundJob][Ruby] Resque の README より</a></li>
<li><a href="http://w.koshigoe.jp/study/?%5BKVS%5D%5BRedis%5D+Redis+%B3%B5%CD%D7">KOSHIGOE学習帳 - [KVS][Redis] Redis 概要</a></li>
<li><a href="http://w.koshigoe.jp/study/?%5BKVS%5D%5BRedis%5D+Redis+%A5%D7%A5%ED%A5%C8%A5%B3%A5%EB%BB%C5%CD%CD">KOSHIGOE学習帳 - [KVS][Redis] Redis プロトコル仕様</a></li>
<li><a href="http://w.koshigoe.jp/study/?%5BKVS%5D%5BRedis%5D+Redis+%A5%B3%A5%DE%A5%F3%A5%C9%B0%EC%CD%F7">KOSHIGOE学習帳 - [KVS][Redis] Redis コマンド一覧</a></li>
</ul>
</div>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_1203602_2892.html">
    <title>「えもにゅ」がAmazon EC2で動くまで</title>
    <link>http://feed.feedforce.jp/item_30880_1203602_2892.html</link>
    <dc:date>2009-02-17T19:26:04+09:00</dc:date>
    <description>先月末、「えもにゅ」というサービスをリリースしました。さらに12日には携帯から「えもにゅ」に投稿できる「えもにゅモバイル」もリリースしました。</description>
    <content:encoded><![CDATA[<p>先月末、「<a href="http://emonyu.jp/">えもにゅ</a>」というサービスをリリースしました。さらに12日には携帯から「えもにゅ」に投稿できる「えもにゅモバイル」もリリースしました。</p>
<ul>
<li><a href="http://www.feedforce.jp/entries/000854.html">感情を記録できるサービス 『えもにゅ』 をリリース！</a></li>
<li><a href="http://www.feedforce.jp/entries/000860.html">『えもにゅ』 にモバイル版登場！</a></li>
</ul>
<h3>えもにゅとは</h3>
<p>「<a href="http://emonyu.jp/">えもにゅ</a>」は自分の今の「気持ち」とその「度合い」を選んで投稿するサービスです。投稿した気持ちは「えもにゅくん」というキャラクターの表情によって表現され、ブログパーツを通してブログ上で公開することができます。</p>
<p>感情とブログパーツに特化している点が、これまでのライフログ系のサービスとは異なる点かと思います。</p>
<p>また、「人」と「時間」と「感情」のデータを結びつけて蓄積し、そのデータをAPI経由で公開することで、新たな相乗効果が生まれることを期待しています。(APIは今後公開予定です)</p>
<h3>えもにゅをEC2で動かす</h3>
<p>さて、その「えもにゅ」ですが、サーバーは <a href="http://aws.amazon.com/ec2/">Amazon EC2</a> を利用しています。「なぜ日本のサービスで EC2 なのか？」と思われるかもしれませんが、</p>
<ul>
<li>個人的に EC2 を使ってみたかった</li>
<li>日本で EC2 を利用してサービスを展開している事例が少ない</li>
<li>円高で料金が安くなっている</li>
<li>日本での利用が増えればEC2のサーバーが日本にもできるかもという淡い期待</li>
</ul>
<p>といった点が EC2 を利用しようと考えた理由です。</p>
<h3>えもにゅの構成</h3>
<p>えもにゅの構成は以下の図のようになっています。</p>
<p><img src="http://tech.feedforce.jp/wp-content/uploads/2009/02/emonyu-amazon-ec2.gif" alt="えもにゅの構成" /></p>
<p>EC2上にCentOSのインスタンスを起動、その上にPHP, Apache, MySQLなどをインストールしています。今のところインスタンスは1つです。画像やCSSといったスタティックなファイルは Amazon S3 に配備し、 static.emonyu.jp というサブドメインをCNAMEに割り当てています。</p>
<p>また、EC2とS3のみでは画像等のローディングが遅くて使い物にならなかったため、 Amazon CloudFront にキャッシュさせることで、日本からアクセスされても耐えられるようにしています。</p>
<h3>RightScale社のCentOSイメージを利用</h3>
<p>EC2のインスタンスで起動させているAMI(イメージ)は、<a href="http://www.rightscale.com/">RightScale</a>社が提供しているCentOSのイメージを利用しました。</p>
<p>このイメージを利用する際の注意点は、EC2のSmall Instanceで利用可能な160GBのうちの10GBしかルートにマウントされていない点です。</p>
<p>残りの150GBは /mnt に割り当てられています。MySQL を yum でインストールすると /var/lib/mysql にデータディレクトリが作成されますが、上記の通り /var 以下では 10GB までしか利用できないため、 /mnt の下に mysql用のディレクトリを作成して、シンボリックリンクを貼るといった対処が必要になります。</p>
<pre>$ cd /var/lib
$ rm mysql
$ ln -s /mnt/var/lib/mysql mysql
$ lllrwxrwxrwx 1 root   root      18  1月  8 22:30 mysql -&gt; /mnt/var/lib/mysql</pre>
<p>my.cnf の内容もそれにあわせて書き換えました。</p>
<pre>[mysqld]
datadir=/mnt/var/lib/mysql
socket=/mnt/var/lib/mysql/mysql.sock
[mysql.server]
basedir=/mnt/var/lib</pre>
<p>また、タイムゾーンが米国時間となっているため、最初に日本時間に変更しておく必要があります。</p>
<pre>lrwxrwxrwx 1 root root 30? 1月 16 11:16 /etc/localtime -&gt; /usr/share/zoneinfo/Asia/Tokyo</pre>
<h3>CloudFrontでスタティックファイルをホスティングする際の注意点</h3>
<p>CloudFrontのサーバは日本に置かれているためアクセスは速いのですが、キャッシュの有効時間を24時間未満に設定することができないため、キャッシュのコントロールがうまくいかないという問題があります。</p>
<p>ファイルの内容が更新されてもキャッシュは無効にならないため、キャッシュを無効にするにはファイル名を変更する必要があります。リリースのたびにファイル名を変更するのは面倒です。</p>
<p>そこで、スタティックファイルのディレクトリにバージョン番号を付与し、リリースのたびにそのバージョン番号を変更していくという、少々めんどくさい方法で対応することにしました。</p>
<p>イメージとしては、</p>
<pre>&lt;img src="/img/title.gif" alt="..." /&gt;</pre>
<p>という img タグがあったら、</p>
<pre>&lt;img src="http://static.emonyu.jp/24/img/title.gif" alt="..." /&gt;</pre>
<p>のようにテンプレートを書き換え、画像ファイル等はその番号のディレクトリに配備する、というものです。バージョン番号はデータベースで管理しています。</p>
<h3>S3にファイルを転送する際の注意点</h3>
<p>EC2からS3へファイルを転送するのに、<a href="http://undesigned.org.za/2007/10/22/amazon-s3-php-class">Amazon S3 PHP Class</a>というライブラリを利用しています。このライブラリは以下のようにして利用できるのですが、</p>
<pre>$s3-&gt;putObjectFile(
                   '/path/to/style.css',
                   'bucket-name',
                   's3/file/path/css/style.css',
                   S3::ACL_PUBLIC_READ);</pre>
<p>putObjectFile() の6番目の引数に適切に Content-Type を与えてあげる必要があります。たとえば上記のように Content-Type なしで転送してしまうと、ライブラリがデフォルトの Content-Type として text/plain を適用してしまうために、ブラウザでページを表示したときにCSSが適用されなくなります。そのため、</p>
<pre>$s3-&gt;putObjectFile(
                   '/path/to/style.css',
                   'bucket-name',
                   's3/file/path/css/style.css',
                   S3::ACL_PUBLIC_READ,
                   array(),
                   'text/css');</pre>
<p>のように、最後の引数で Content-Type を与える必要があります。もしくはファイルの拡張子から Content-Type を自動的に判別できるように、ライブラリ自身を修正してもよいと思います。</p>
<h3>まとめ</h3>
<p>以上のように少し手間と工夫が必要ですが、Amazon EC2, S3, CloudFrontを利用して日本向けのサービスを提供することができます。</p>
<p>今後は既存ユーザの利便性向上やAPIの公開、クラウドを生かした新機能の開発などをしていきたいと考えています。</p>
<p>というわけで、ぜひぜひ「<a href="http://emonyu.jp/" target="_blank">えもにゅ</a>」を使ってみてください。</p>
<p><a href="http://emonyu.jp/" target="_blank">http://emonyu.jp/ </a></p>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_1136619_2892.html">
    <title>Easy-Mmodeを使ってEmacsのマイナーモードを作る</title>
    <link>http://feed.feedforce.jp/item_30880_1136619_2892.html</link>
    <dc:date>2008-12-12T19:01:15+09:00</dc:date>
    <description>今回はEmacsのマイナーモードを作成する手順を紹介します。
説明では弊社が提供しているプロダクトのひとつ RSS Suite で利用している独自フレームワーク中で、対応するファイル間を移動できるようにするマイナーモードを作成していきます。</description>
    <content:encoded><![CDATA[<p align="justify">今回はEmacsのマイナーモードを作成する手順を紹介します。</p>
<p>説明では弊社が提供しているプロダクトのひとつ <a href="http://www.rsssuite.jp" title="RSS Suite" target="_blank">RSS Suite</a> で利用している独自フレームワーク中で、対応するファイル間を移動できるようにするマイナーモードを作成していきます。</p>
<h3>メジャーモードとマイナーモード</h3>
<p>Emacsにはカスタマイズする定義の集まりであるモードというものがあり、メジャーモードとマイナーモードの2種類があります。</p>
<h4>メジャーモード</h4>
<p>特定のテキスト編集向けに特化している。各バッファには、ある時点で1つのメジャーモードしかない。</p>
<h4>マイナーモード</h4>
<p>メジャーモードの選択とは独立にオン/オフできる機能を提供する。<br />
ここではマイナーモードを作成します。</p>
<h3>バッファとウィンドウ</h3>
<p>Emacs内でファイル間を移動するには、ウィンドウとバッファの操作が必要になります。</p>
<h4 align="justify">バッファ</h4>
<p>編集領域。Emacsの編集操作はすべてバッファを対象に行われます。</p>
<h4 align="justify">ウィンドウ</h4>
<p align="justify">?バッファの表示領域のこと。</p>
<h3>Easy-Mmode</h3>
<p>単純なマイナーモードであればEasy-Mmodeパッケージを利用すると便利です。<br />
以下のように書くと、test-modeというマイナーモードを定義できます。</p>
<pre> ;; マイナーモードの定義
 (easy-mmode-define-minor-mode test-mode
 ;; モード名は、-mode
 ;; ドキュメント
?  "This is Test Mode."
 ;; 初期値
 nil
 ;; on の時のモード行への表示
 " TestMode"
 ;; マイナーモード用キーマップの初期値
 '(("C-cf" . test-function))</pre>
<p>「M-x test-mode」でモードのオン/オフを切り替えることができ、test-modeがオンの時はキーバインド「C-c f」で関数test-functionを呼び出すことができるようになります。<br />
この設定の場合、test-modeがオンのときは&#8221;TestMode&#8221;がモード行に表示されます。</p>
<p><a href="http://tech.feedforce.jp/wp-content/uploads/2008/12/2008-12-09_1807.png" title="2008-12-09_1807.png"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/12/2008-12-09_1807.png" alt="2008-12-09_1807.png" /></a></p>
<h3 align="justify">実際にマイナーモードを作ってみる</h3>
<p>では、実際にマイナーモードを作成していきます。<br />
ここでは<a href="http://www.rsssuite.jp" title="RSS Suite" target="_blank">RSS Suite</a>の管理画面で利用するファイル間を移動できるようにすることを目標とします。</p>
<p align="justify">RSS Suiteの管理画面で必要となるファイルの構成は以下です。</p>
<ul>
<li>lib/FormProcess/FormProcessXxxYyyZzz.php(フォーム処理)</li>
<li>lib/PageContents/PageContentsXxxYyyZzz.php(コンテンツ表示)</li>
<li>lib/qfd/xxx.yyy.zzz.qfd(フォームの定義)</li>
<li>template/rss_admin/xxx.yyy.zzz.html(htmlテンプレート)</li>
</ul>
<p align="justify">命名規則からファイル名をうまく変換して、現在開いているファイルから対応するファイルへと移動できるようにします。</p>
<p> まず、Easy-Mmodeを利用して、RSS Suiteモードを定義します。</p>
<pre>?(easy-mmode-define-minor-mode rsssuite-mode
;; ドキュメント
 "This is RSS Suite Mode."
 ;; 初期値
 nil;; モード行への表示
 "RSSSuite"
 ;; マイナーモード用キーマップの初期値
 '(("C-cf" . suite-open-formprocess)
?  ("C-cp" . suite-open-pagecontents)
?  ("C-cq" . suite-open-qfd)
?  ("C-ct" . suite-open-template)))</pre>
<p>ここではそれぞれ4つの対応ファイルを開くためのキーマップを定義しています。<br />
次に、切り替えるファイルへのパスをそれぞれ定義しておきます。</p>
<pre> (defvar suite-qfd-path "lib/qfd/")
 (defvar suite-formprocess-path "lib/FormProcess/")
 (defvar suite-pagecontents-path "lib/PageContents/")
 (defvar suite-template-path "template/rss_admin/")
 (defvar suite-lib-path "lib/")</pre>
<p>関数defvarで変数に値を定義しています。<br />
次に関連ファイルを開いた時にrsssuite-modeをオンにするためhookを定義します。<br />
この設定は必須ではありませんが、rss_suiteディレクトリ以下のファイルを開いた時に自動でrsssuite-modeがオンになる方がラクなのでそのようにします。</p>
<pre>;; ファイルを開いた時のhook
(add-hook 'find-file-hooks
?         (function (lambda ()
?                     (suite-on-rsssuite-mode))))</pre>
<p>find-file-hooksはファイルを訪問後に呼び出されます。</p>
<p>ここで実行しているsuite-on-rsssuite-mode関数は以下のように定義してあります。</p>
<pre>;; RSS Suiteのディレクトリ配下であればrsssuite-modeにする
(defun suite-on-rsssuite-mode ()
? (if (string-match "rss_suite[^/]*/" default-directory)
?     (unless rsssuite-mode (rsssuite-mode))))</pre>
<p>変数rsssuite-modeはモードがオンになっていてばt, オフであればnilとなります。<br />
関数rsssuite-modeはオン・オフを切り替える関数で、ここではオフの時のみオンにする処理にしています。</p>
<p>次に、マイナーモードのキーマップで定義した関数suite-open-formprocessを見ていきます。</p>
<pre>;; 対応するFormProcessファイルを開く
(defun suite-open-formprocess ()
? (interactive)
? (setq suite-formprocess-file-path (concat (suite-get-top-dir)
?                                           suite-formprocess-path
?                                           "FormProcess"
?                                           (suite-get-capitalized-name)
?                                           ".php"))
? (set-window-buffer (selected-window) (find-file-noselect suite-formprocess-file-path))
? (suite-on-rsssuite-mode))</pre>
<p align="justify">defunで関数を定義しています。interactiveが含まれていると、 「M-x 関数名」で実行できるコマンドになります。<br />
変数suite-formprocess-file-pathに、対応するFormProcessファイルの絶対パスを格納しています。<br />
関数set-window-bufferは指定したウィンドウにバッファの内容を表示させます。ここでは選択されているウィンドウ(selected-windnow)に、確保したファイルsuite-formprocess-file-pathに対応するバッファを表示させています。</p>
<p>suite-formprocess-file-pathを求める部分では、関数concatで以下の文字列を連結しています。</p>
<ul>
<li>(suite-get-top-dir)</li>
<li>suite-formprocess-path</li>
<li>&#8220;FormProcess&#8221;</li>
<li>(suite-get-capitalized-name)</li>
<li>&#8220;.php&#8221;</li>
</ul>
<p>関数suite-get-top-dirの定義は以下です。</p>
<pre>?;; RSS Suiteのトップディレクトリの絶対パスを取得
 (defun suite-get-top-dir ()
   (string-match "rss_suite[^/]*/" default-directory)
   (substring default-directory 0 (match-end 0)))</pre>
<p>match-endには、string-matchの結果である終端のポイント位置が入ります。</p>
<p>関数suite-get-capitalized-nameの定義は以下です。</p>
<pre>?;; キャメルケースのファイル名を取得
(defun suite-get-capitalized-name ()
  (cond
   ((suite-formprocessp) (string-match "FormProcess(.+).php" (suite-get-file-name)) (suite-get-main-name))
   ((suite-pagecontentsp) (string-match "PageContents(.+).php" (suite-get-file-name)) (suite-get-main-name))
   ((suite-qfdp)  (string-match "(.+).qfd" (suite-get-file-name)) (suite-dot-to-capitalize-string (suite-get-main-name)))
   ((suite-templatep) (string-match "(.+).html" (suite-get-file-name)) (suite-dot-to-capitalize-string (suite-get-main-name)))
   (t nil)))</pre>
<p>現在開いているファイルがqfdファイル(xxx.yyy.zzz.qfd)やtemplateファイル(xxx.yyy.zzz.html)の場合、キャメルケースに変換して(XxxYyyZzz)返します。<br />
変換しているのは、関数suite-dot-to-capitalize-stringの部分です。</p>
<pre>?;; ファイル名からメインとなる部分の名前を切り出す
(defun suite-get-main-name ()
  (substring (suite-get-file-name) (match-beginning 1) (match-end 1))
  )</pre>
<p>match-beginningやmatch-endには直前に評価しているstring-matchの結果が入ります。</p>
<pre>;; ドットで連結されたファイル名をリストに変換
;; foo.bar.baz =&gt; (foo bar baz)
(defun suite-dot-to-capitalize-string (str)
  (suite-concat-capitalize-string-list (split-string str "."))
  )</pre>
<p>split-stringはドットで区切ってリストにしています。</p>
<pre>;; 文字列のリストをキャピタライズして連結する
;; (foo bar baz) -&gt; FooBarBaz
(defun suite-concat-capitalize-string-list (list)
  (cond
   ((= (length list) 1) (capitalize (car list)))
   (t (concat (capitalize (car list)) (suite-concat-capitalize-string-list (cdr list))))))</pre>
<p>listの長さが1であれば、carでlistの先頭要素をキャピタライズして返します。<br />
listの要素が2つ以上の場合は先頭要素をキャピタライズした文字列と、cdrで先頭要素を取り除いたlistを自身の関数suite-concat-capitalize-string-listに渡した結果の文字列と連結します。</p>
<p>逆に、ドットで区切られた名前から、キャピタライズした名前を取得する処理は以下です。</p>
<pre>;; ドットで連結されたファイル名を取得
(defun suite-get-concatenated-name ()
  (cond
   ((suite-formprocessp) (string-match "FormProcess(.+).php" (suite-get-file-name)) (suite-split-capitalize-string (suite-get-main-name )))
   ((suite-pagecontentsp) (string-match "PageContents(.+).php" (suite-get-file-name)) (suite-split-capitalize-string (suite-get-main-name )))
   ((suite-qfdp) (string-match "(.+).qfd" (suite-get-file-name)) (suite-get-main-name))
   ((suite-templatep)  (string-match "(.+).html" (suite-get-file-name)) (suite-get-main-name))
   (t nil))
  )

;; キャメルケースの文字をドット区切りに分解
;; FooBarBaz -&gt; foo.bar.baz
(defun suite-split-capitalize-string (str)
  (setq case-fold-search nil)
  (string-match "^[A-Z][a-z]+" str)
  (cond
   ((= (match-end 0) (length str)) (downcase str))
   (t  (concat
	(concat (downcase (substring str (match-beginning 0) (match-end 0))) ".")
	(suite-split-capitalize-string (substring str (match-end 0) (length str)))
	))))</pre>
<p>関数suite-split-capitalize-stringでは、受け取った文字列を先頭が大文字でそれ以降小文字が続く正規表現でマッチさせます。<br />
もしstring-matchの結果の終端ポイント位置とstrの長さが同じであれば、downcaseして返します。<br />
長さが異る場合は、downcaseした最初の単語に&#8217;.'を連結したものと、マッチ以降の文字を自身の関数suite-split-capitalize-stringに渡して返される文字列を連結します。</p>
<p align="justify">以上で対応したファイル間を移動できるマイナーモードができました。<br />
最後にソース全体を載せておきます。</p>
<pre>
 ;; RSS Suite モード
(easy-mmode-define-minor-mode rsssuite-mode
                              ;; ドキュメント
                              "This is RSS Suite Mode."
                              ;; 初期値
                              nil
                              ;; on の時のモード行への表示
                              " RSSSuite"
                              ;; マイナーモード用キーマップの初期値
                              '(("C-cf" . suite-open-formprocess)
                                ("C-cp" . suite-open-pagecontents)
                                ("C-cq" . suite-open-qfd)
                                ("C-ct" . suite-open-template)
                                ))

;; Suiteのトップからそれぞれのファイルへのパス
(defvar suite-qfd-path "lib/qfd/")
(defvar suite-formprocess-path "lib/FormProcess/")
(defvar suite-pagecontents-path "lib/PageContents/")
(defvar suite-template-path "template/rss_admin/")
(defvar suite-lib-path "lib/")

;; ファイルを開いた時のhook
(add-hook 'find-file-hooks
          (function (lambda ()
                      (suite-on-rsssuite-mode))))

;; Suiteのディレクトリ配下であればRSSSuite modeにする
(defun suite-on-rsssuite-mode ()
  (if (string-match "rss_suite[^/]*/" default-directory)
      (unless rsssuite-mode (rsssuite-mode))))

;; suiteのトップディレクトリの絶対パスを取得
(defun suite-get-top-dir ()
  (string-match "rss_suite[^/]*/" default-directory)
  (substring default-directory 0 (match-end 0)))

;; 対応するFormProcessファイルを開く
(defun suite-open-formprocess ()
  (interactive)
  (setq suite-formprocess-file-path (concat (suite-get-top-dir)
                                            suite-formprocess-path
                                            "FormProcess"
                                            (suite-get-capitalized-name)
                                            ".php"))
  (set-window-buffer (selected-window)
		     (find-file-noselect suite-formprocess-file-path))
  (suite-on-rsssuite-mode))

;; 対応するPageContentsファイルを開く
(defun suite-open-pagecontents ()
  (interactive)
  (setq suite-pagecontents-file-path (concat (suite-get-top-dir)
                                             suite-pagecontents-path
                                             "PageContents"
                                             (suite-get-capitalized-name)
                                             ".php"))
  (set-window-buffer (selected-window)
		     (find-file-noselect suite-pagecontents-file-path))
  (suite-on-rsssuite-mode))

;; 対応するqfdファイルを開く
(defun suite-open-qfd ()
  (interactive)
  (setq suite-qfd-file-path (concat (suite-get-top-dir)
                                    suite-qfd-path
                                    (suite-get-concatenated-name)
                                    ".qfd"))
  (set-window-buffer (selected-window)
		     (find-file-noselect suite-qfd-file-path))
  (suite-on-rsssuite-mode))

;; 対応するテンプレートファイルを開く
(defun suite-open-template ()
  (interactive)
  (setq suite-template-file-path
	(concat (suite-get-top-dir)
		suite-template-path
		(suite-get-concatenated-name)
		".html"))
  (set-window-buffer (selected-window)
		     (find-file-noselect suite-template-file-path))
  (suite-on-rsssuite-mode))

;; ファイル名からメインとなる部分の名前を切り出す
(defun suite-get-main-name ()
  (substring (suite-get-file-name) (match-beginning 1) (match-end 1)))

;; キャメルケースのファイル名を取得
(defun suite-get-capitalized-name ()
  (cond
   ((suite-formprocessp)
    (string-match "FormProcess(.+).php" (suite-get-file-name))
    (suite-get-main-name ))
   ((suite-pagecontentsp)
    (string-match "PageContents(.+).php"
		  (suite-get-file-name)) (suite-get-main-name ))
   ((suite-qfdp)
    (string-match "(.+).qfd" (suite-get-file-name))
    (suite-dot-to-capitalize-string (suite-get-main-name )))
   ((suite-templatep)
    (string-match "(.+).html" (suite-get-file-name))
    (suite-dot-to-capitalize-string (suite-get-main-name )))
   (t nil)))

;; ドットで連結されたファイル名を取得
(defun suite-get-concatenated-name ()
  (cond
   ((suite-formprocessp)
    (string-match "FormProcess(.+).php" (suite-get-file-name))
    (suite-split-capitalize-string (suite-get-main-name )))
   ((suite-pagecontentsp)
    (string-match "PageContents(.+).php" (suite-get-file-name))
    (suite-split-capitalize-string (suite-get-main-name )))
   ((suite-qfdp)
    (string-match "(.+).qfd" (suite-get-file-name))
    (suite-get-main-name))
   ((suite-templatep)
    (string-match "(.+).html" (suite-get-file-name))
    (suite-get-main-name))
   (t nil)))

;; 文字列のリストをキャピタライズして連結する
;; (foo bar baz) -&gt; FooBarBaz
(defun suite-concat-capitalize-string-list (list)
  (cond
   ((= (length list) 1) (capitalize (car list)))
   (t (concat (capitalize (car list))
	      (suite-concat-capitalize-string-list (cdr list))))))

;; ドットで連結されたファイル名をリストに変換
;; foo.bar.baz =&gt; (foo bar baz)
(defun suite-dot-to-capitalize-string (str)
  (suite-concat-capitalize-string-list (split-string str "."))
  )

;; キャメルケースの文字をドット区切りに分解
;; FooBarBaz -&gt; foo.bar.baz
(defun suite-split-capitalize-string (str)
  (setq case-fold-search nil)
  (string-match "^[A-Z][a-z]+" str)
  (cond
   ((= (match-end 0) (length str)) (downcase str))
   (t  (concat
	(concat (downcase
		 (substring str (match-beginning 0) (match-end 0))) ".")
	(suite-split-capitalize-string (substring str
						  (match-end 0)
						  (length str)))))))

;; FormProcessかどうか
(defun suite-formprocessp ()
  (string-match "FormProcess" (suite-get-file-name)))

;; PageContentsかどうか
(defun suite-pagecontentsp ()
  (string-match "PageContents" (suite-get-file-name)))

;; qfdかどうか
(defun suite-qfdp ()
  (string-match ".*.qfd" (suite-get-file-name)))

;; テンプレートかどうか
(defun suite-templatep ()
  (string-match ".*.html" (suite-get-file-name)))

;; ファイル名取得
(defun suite-get-file-name ()
  (if buffer-file-name
      (file-name-nondirectory buffer-file-name)
    (error "there are not respond file")))</pre>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_1088535_2892.html">
    <title>ShindigとPartuzaでOpenSocialガジェットのテスト環境を構築(その2)</title>
    <link>http://feed.feedforce.jp/item_30880_1088535_2892.html</link>
    <dc:date>2008-10-27T11:07:36+09:00</dc:date>
    <description>前回はOpenSocialガジェットのホスティング環境を構築できる Shindig について説明しました。
&amp;#187; FFTT : ShindigとPartuzaでOpenSocialガジェットのテスト環境を構築(その1)
今回は、その Shindig を利用して OpenSocial 機能を実装している Partuza を紹介します。</description>
    <content:encoded><![CDATA[<p>前回はOpenSocialガジェットのホスティング環境を構築できる <a href="http://incubator.apache.org/shindig/">Shindig</a> について説明しました。</p>
<p>? <a href="http://tech.feedforce.jp/shindig-partuza-opensocial-1.html">FFTT : ShindigとPartuzaでOpenSocialガジェットのテスト環境を構築(その1)</a></p>
<p>今回は、その Shindig を利用して OpenSocial 機能を実装している <a href="http://www.partuza.nl/">Partuza</a> を紹介します。</p>
<h2>Partuza</h2>
<p><a href="http://www.partuza.nl/">Partuza</a> はオープンソースのSNSです。PHPで書かれています。読み方はよくわかりません。ソースをダウンロードして個人の環境に設置することで使えるようになります。ライセンスは Apache License 2.0 です。</p>
<h3>こんなとき参考になります</h3>
<ul>
<li>Apache Shindigを利用してOpen Social対応SNSを作りたい</li>
<li>すでにあるSNSにApache Shindigを利用したOpen Social機能を実装したい</li>
</ul>
<p>というわけで、早速 Partuza をインストールしてみます。ソースコードは <a href="http://code.google.com/p/partuza/">partuza - Google Code</a> から入手できます。</p>
<h3>Partuza のインストール</h3>
<h4>checkout</h4>
<pre>$ mkdir /var/www/partuza
$ cd /var/www/partuza
$ svn co http://partuza.googlecode.com/svn/trunk .</pre>
<h4>バーチャルホストを設定</h4>
<pre>&lt;VirtualHost 192.168.1.142:80&gt;
    DocumentRoot /var/www/partuza/html
    ServerName dev.partuza.jp
    ErrorLog /var/log/httpd/partuza_error_log
    CustomLog /var/log/httpd/partuza_access_log combined

    AddDefaultCharset UTF-8

    &lt;Directory /var/www/partuza/html&gt;
        AllowOverride All
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;</pre>
<h4>必要であれば</h4>
<p>/etc/php.ini に以下の記述があるか。</p>
<pre>short_open_tag = On</pre>
<p><a href="http://www.libgd.org/Main_Page">gd</a>をインストールしていなければインストール。</p>
<h4>パーミッション変更</h4>
<pre>$ chmod 777 /var/www/partuza/images/people</pre>
<h4>SQL実行(MySQL)</h4>
<pre>$ cd /var/www/partuza
$ mysqladmin -u root -p create partuza
$ mysql -u root -p -D partuza &lt; partuza.sql</pre>
<p>ここまで設定すれば、Partuza が動きます。</p>
<p>しかし、まだ Shindig が組み込まれていませんので、Shindig を Partuza に組み込む設定をします。</p>
<h4>Shindig に local.php を作成</h4>
<pre>$ vi /var/www/shindig/config/local.php</pre>
<p>local.phpファイルの中身は次のようにする。</p>
<pre>&lt;?php

$shindigConfig = array(
                       'people_service' =&gt; 'PartuzaPeopleService',
                       'activity_service' =&gt; 'PartuzaActivitiesService',
                       'app_data_service' =&gt; 'PartuzaAppDataService',
                       'extension_class_paths' =&gt; '/var/www/partuza/Shindig',);</pre>
<h4>Partuza の config.php を編集</h4>
<p>Shindig が動く環境のホスト名を設定します。</p>
<pre>$ vi /var/www/partuza/html/config.php</pre>
<pre>- 'gadget_server'    =&gt; 'http://shindig',
+ 'gadget_server'    =&gt; 'http://dev.shindig.jp',</pre>
<p>ここまで設定できれば、PartuzaでOpenSocialガジェットが動くようになります。</p>
<p><img src="http://tech.feedforce.jp/wp-content/uploads/2008/10/partuza-home.gif" alt="Partuza" /></p>
<p>↑ テストで<a href="http://www.google.co.jp/ig/directory?hl=ja&amp;url=map.fkoji.com/kusayakyu/search.xml">このガジェット</a>を追加してみました。(※ このガジェットにOpenSocialの機能はありません。)</p>
<h2>まとめ</h2>
<p>このように、 Shindig と Partuza を利用すると、OpenSocial対応のガジェットおよびSNSの開発環境を簡単に構築することができます。興味のある方はぜひ利用してみてください。</p>
<p>? <a href="http://tech.feedforce.jp/shindig-partuza-opensocial-1.html">FFTT : ShindigとPartuzaでOpenSocialガジェットのテスト環境を構築(その1)</a></p>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_1068064_2892.html">
    <title>ShindigとPartuzaでOpenSocialガジェットのテスト環境を構築(その1)</title>
    <link>http://feed.feedforce.jp/item_30880_1068064_2892.html</link>
    <dc:date>2008-10-06T10:59:23+09:00</dc:date>
    <description>OpenSocial対応のガジェットを構築するうえでの悩みどころは、「テスト環境どうするの？」というところではないでしょうか。
まだ公開していないガジェットアプリケーションをOrkutのsandboxでテストするのはちょっとなぁ・・・と思ったことがある人は、ここで紹介するShindigとPartuzaを使ってみるといいかもしれません。
今回はまず Shindig について説明します。</description>
    <content:encoded><![CDATA[<p><a href="http://code.google.com/apis/opensocial/">OpenSocial</a>対応のガジェットを構築するうえでの悩みどころは、「テスト環境どうするの？」というところではないでしょうか。</p>
<p>まだ公開していないガジェットアプリケーションを<a href="http://www.orkut.com/">Orkut</a>のsandboxでテストするのはちょっとなぁ・・・と思ったことがある人は、ここで紹介するShindigとPartuzaを使ってみるといいかもしれません。</p>
<p>今回はまず Shindig について説明します。</p>
<h2>Shindig</h2>
<p><a href="http://incubator.apache.org/shindig/">Shindig</a>は<a href="http://code.google.com/apis/opensocial/docs/0.8/spec.html">OpenSocial API Specification</a>と<a href="http://code.google.com/apis/gadgets/docs/spec.html">Gadgets Specification</a>を実装したオープンソースのプロジェクトです。<a href="http://incubator.apache.org/">Apache incubator</a>にて開発が進められています。</p>
<p>Shindigを使うと誰でも自由にOpenSocialガジェットをホストするサイトを構築することができます。ローカル環境に立てればテスト環境が作れるというわけです。</p>
<p>※ iGoogleのガジェットをいくつか試してみましたが、すべてのガジェットが実行できるわけではありませんでした。iGoogle独自のメソッドの多くは実装されていないようです。</p>
<h3>Shindigの構成要素</h3>
<ul>
<li>Gadget Container JavaScript</li>
<li>Gadget Server</li>
<li>OpenSocial Container JavaScript</li>
<li>OpenSocial Data Server</li>
</ul>
<h3>導入</h3>
<p><a href="http://incubator.apache.org/shindig/#php" class="external">http://incubator.apache.org/shindig/#php</a></p>
<p>Java版もありますが、ここではPHP版で。</p>
<h4>必要環境</h4>
<ul>
<li>Subversion</li>
<li>mod_rewrite</li>
<li>PHP 5.2.x (json, simplexml, mcrypt, curl)</li>
</ul>
<h4>ソースをチェックアウト</h4>
<pre>$ mkdir /var/www/shindig
$ cd /var/www/shindig
$ svn co http://svn.apache.org/repos/asf/incubator/shindig/trunk/ .</pre>
<h4>バーチャルホストを立てる</h4>
<p>ここでは dev.shindig.jp というドメインとします。</p>
<pre>&lt;VirtualHost 192.168.1.142:80&gt;
    DocumentRoot /var/www/shindig/php
    DirectoryIndex index.html index.php
    ServerName dev.shindig.jp
    AddDefaultCharset UTF-8 

    &lt;Directory /var/www/shindig/php&gt;
        AllowOverride All
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;</pre>
<h4>PECL json インストール</h4>
<p>個人の環境にPECL jsonが入っていなかったのでインストールしました。</p>
<pre>$ sudo pecl install json</pre>
<h3>ガジェットを実行してみる</h3>
<p>ここまでできれば、すぐにガジェットを実行することができます。エンドポイントは、</p>
<pre>http://dev.shindig.jp/gadgets/ifr?</pre>
<p>です。urlパラメータを与えてガジェットを実行します。</p>
<pre>http://dev.shindig.jp/gadgets/ifr?url=[ガジェットのXMLファイルのURL]</pre>
<p><img src='http://tech.feedforce.jp/wp-content/uploads/2008/09/shindig-example.gif' alt='shindig-example.gif' /></p>
<p>↑ ドキュメントに<a href="http://www.labpixies.com/campaigns/todo/todo.xml">サンプルとして記載されているガジェット</a>を実行したところ。</p>
<p>このように、Shindigを使うと簡単にOpenSocial対応ガジェットをホスティングする環境を作ることができます。</p>
<p>というわけで、次回はPartuzaについて。</p>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_968514_2892.html">
    <title>RubyCocoaを1杯</title>
    <link>http://feed.feedforce.jp/item_30880_968514_2892.html</link>
    <dc:date>2008-07-02T18:43:33+09:00</dc:date>
    <description>今回はRubyCocoaを使ってMacOSXの簡単なアプリケーションを作ってみようと思います。</description>
    <content:encoded><![CDATA[<p><span class="Apple-style-span" style="font-family: Times; line-height: normal"> </span></p>
<p style="padding: 0.5em; background-color: #ffffff; font-family: Georgia,'Times New Roman',Times,serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 1em; line-height: 1.3em; font-size-adjust: none; font-stretch: normal" align="justify">今回はRubyCocoaを使ってMacOSXの簡単なアプリケーションを作ってみようと思います。<span id="more-75"></span></p>
<h2><span class="Apple-style-span" style="font-size: 16px; font-weight: normal"><span class="Apple-style-span" style="font-size: 24px; font-weight: bold">RubyCocoaとは</span></span></h2>
<ul>
<li>Rubyを使ってMacOSXのアプリケーションフレームワークであるCocoaを操作できる</li>
<li>対話的にCocoaを開発できる</li>
<li>Leopardから標準搭載</li>
<li>バージョン 0.13.2(2008/2/16)</li>
</ul>
<h2><span class="Apple-style-span" style="font-size: 16px; font-weight: normal"><span class="Apple-style-span" style="font-size: 24px; font-weight: bold">代表的なアプリケーション</span></span></h2>
<p align="justify">RubyCocoaを使って開発されたアプリケーションには以下のようなものがあります。</p>
<ul>
<li><a href="http://limechat.sourceforge.net/index_ja.html">LimeChat for OSX</a></li>
<li><a href="http://coderepos.org/share/wiki/Chemr">Chemr</a></li>
<li><a href="http://blog.deadbeaf.org/2008/03/01/qstwitter-14/">QSTwitter<br />
</a></li>
</ul>
<p align="justify">特にQuickSilverからTwitterに投げれるQSTwitterはとても便利に使わせてもらっています。</p>
<h2>作ってみよう</h2>
<p align="justify">Twitterにポストするだけの簡単なアプリケーションを作ってみます。</p>
<h2><font class="Apple-style-span" size="6"><span class="Apple-style-span" style="font-size: 19px">準備</span></font></h2>
<p align="justify">Xcodeを使うので、あらかじめインストールしておく必要があります。MacOSXのインストールDVDに含まれているのでそこからインストールするか、もしくは<a href="http://developer.apple.com/">ADC</a>(Apple Developer Connection)のサイトからダウンロードすることもできます。また、RubyからTwitterにポストする部分でRuby Twitter Gemを使いましたので、それもインストールしておきます。 ターミナルを開いて、</p>
<pre style="font-family: 'Courier New',fixed; font-size: 11px; line-height: 13px">$ sudo gem install twitter</pre>
<p>でインストールします。</p>
<h2><font class="Apple-style-span" size="6"><span class="Apple-style-span" style="font-size: 19px">プロジェクト作成</span></font></h2>
<p align="justify">まず、プロジェクトを作成します。/Developer/Applications/Xcode.appを開きます。 File-&gt;New Projectを選択し、 Cocoa-Ruby Applicationを選択します(1)。</p>
<p align="justify"><a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/2008-06-26_0029.png" title="new project"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/2008-06-26_0029.thumbnail.png" alt="new project" /></a> (1)</p>
<p>プロジェクト名を入力して次に進むと、プロジェクトウィンドウが立ちあがります(2)。</p>
<p align="justify"><a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/2008-06-26_0031.png" title="プロジェクトウィンドウ"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/2008-06-26_0031.thumbnail.png" alt="プロジェクトウィンドウ" /></a> (2)</p>
<p>次にコントローラを作成します。 Classes-&gt;Add-&gt;Ruby NSObject subclass を選択します(3)。</p>
<p align="justify"> <a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/2008-06-26_0032.png" title="Ruby NSObject subclass"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/2008-06-26_0032.thumbnail.png" alt="Ruby NSObject subclass" /></a> (3)<br />
ここではAppController.rbというファイル名で保存します。 今回は次のようなコードにしました。</p>
<pre>require 'osx/cocoa'
require 'rubygems'
require 'twitter'

class AppController &lt; OSX::NSObject
  include OSX
  ib_outlet :window, :name, :password, :message
  ib_action :post
  def post
    twit = Twitter::Base.new(@name.stringValue.to_s, @password.stringValue.to_s)
    twit.update(@message.stringValue.to_s)
    @message.setStringValue ''
  end
end</pre>
<p align="justify"> ib_outletで、コントローラからウィンドウへの参照を定義しています。 ib_actionで、アクションを定義します。</p>
<p>ボタンがクリックされたときにここで定義したアクシ ョンが呼ばれるように後で設定します。<br />
def post … end にアクションpostの処理内容を書きます。 Twitterクラスのインスタンスを生成し、ウィンドウに入力してあるメッセージをポストします。@nameで参照しているオブジェクトを取得し、stringValueメソッドでそのオブジェクトの文字列を取得します。 stringValueで取得した文字列はCocoaの文字列クラスのオブジェクトなので、to_sメソッドで Rubyの文字列に変換しています。</p>
<p align="justify"> setStringValueメソッドで、参照しているオブジェクトの文字列に文字を設定しています。</p>
<p align="justify">このコードを保存し、次にInterfaceBuilderを使ってUIとコントローラを結びつけていきます。 プロジェクトウィンドウのMainMenu.nibをダブルクリックする(4)とInterfaceBuilderが立ち上がります。</p>
<p> <a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/mainmenunib.png" title="mainmenunib.png"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/mainmenunib.thumbnail.png" alt="mainmenunib.png" /></a> (4)<br />
LibraryパレットからNSObjectをドラッグし、メインウィンドウにドロップします(5)。</p>
<p align="justify"><a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/movensobject.png" title="movensobject.png"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/movensobject.thumbnail.png" alt="movensobject.png" /></a> (5)<br />
このオブジェクトを選択した状態で、クラスパレットのClassに先程作成したAppControllerを選択します(6)。</p>
<p align="justify"> <a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/selectappcontroller.png" title="selectappcontroller.png"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/selectappcontroller.thumbnail.png" alt="selectappcontroller.png" /></a> (6)<br />
次にWindowとコントローラを結びつけていきます。今回は元々あるWindowは使わないので、メインウィンドウにあるWindowを削除します。<br />
LibraryパレットからHUD Windowをドラッグ&amp;ドロップでメインウィンドウに移動します(7)。</p>
<p><a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/hudwindow.png" title="hudwindow.png"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/hudwindow.thumbnail.png" alt="hudwindow.png" /></a> (7)</p>
<p>このウィンドウとコントローラを結びつけるために、メインウィンドウのAppControllerをCtrlキーを押したままマウスを押し、Windowにドラッグして離します。</p>
<p align="justify"><a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/picture-5.png" title="picture-5.png"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/picture-5.thumbnail.png" alt="picture-5.png" /></a> (8)</p>
<p align="justify"> Outletsというウィンドウが表示されるので、AppController.rbのib_outletに定義した windowを選択します(8)。次に、Twitterにポストするために必要なボタンやテキストフィールドを追加していきます。<br />
メインウィンドウのPanel(Window)をダブルクリックするとそのウィンドウが表示されるので、そこにパネルウィンドウから必要なオブジェクトをドラッグ&amp;ドロップで配置していきます(9)。今回は、PushButtonとTextField,Secure TextField, Labelを使いました。</p>
<p align="justify"><a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/windowbottun.png" title="windowbottun.png"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/windowbottun.thumbnail.png" alt="windowbottun.png" /></a> (9)<br />
配置したテキストフィールドやボタンとコントローラを結びつけていきます。<br />
メインウィンドウのAppControllerを選択し、Ctrlキーを押したままマウスを押してウィンドウのオブジェクトにドラッグして離します。すると、選択肢にAppController.rbで定義したアウトレットが表示されるので、対応するものを選択します(10)。</p>
<p align="justify"><a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/picture-6.png" title="picture-6.png"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/picture-6.thumbnail.png" alt="picture-6.png" /></a> (10)<br />
messageやname,passwordの部分をこのように設定していきます。<br />
次はTwitterにポストする時に押すボタンとコントローラを結びつけます。<br />
アウトレットの時とは逆に、ボタンを選択しCtrlキーを押したままマウスを押してメインウィンドウのAppControllerにドラッグして離します(11)。<br />
このとき、AppController.rbのib_actionに定義した「post」が表示されるので、選択します。</p>
<p align="justify"><a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/picture-7.png" title="picture-7.png"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/picture-7.thumbnail.png" alt="picture-7.png" /></a> (11)<br />
ウィンドウのオブジェクトをすべてコントローラと結びつけたら、実行してみます。<br />
Xcodeのメニューにある、Build -&gt; Build and Go で実行するとアプリケーションが立ち上がります(12)。</p>
<p align="justify"> <a href="http://tech.feedforce.jp/wp-content/uploads/2008/06/rctwitter.png" title="rctwitter.png"><img src="http://tech.feedforce.jp/wp-content/uploads/2008/06/rctwitter.thumbnail.png" alt="rctwitter.png" /></a> (12)</p>
<p align="justify"> Twitterのアカウント情報を入力し、Postボタンを押してMessageを投げることができれば成功です。</p>
<h2>おわりに</h2>
<p align="justify"> 細かな処理は省略しましたが、RubyCocoaを使ってアプリケーションを作る大まかな流れはつかめたと思います。<br />
アカウント情報が間違っていた場合の例外処理や、そもそも設定部分は環境設定画面に置くなど、まだまだやることはたくさんありそうです。</p>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_928070_2892.html">
    <title>Mercurial はじめの一歩</title>
    <link>http://feed.feedforce.jp/item_30880_928070_2892.html</link>
    <dc:date>2008-05-30T16:11:35+09:00</dc:date>
    <description>弊社ではソースコードの管理にSubversionを使用していますが、個人的に興味があるので、分散バージョン管理システムの一つであるMercurialを触ってみました。</description>
    <content:encoded><![CDATA[<p>弊社ではソースコードの管理に<a href="http://subversion.tigris.org/">Subversion</a>を使用していますが、個人的に興味があるので、分散バージョン管理システムの一つである<a href="http://www.selenic.com/mercurial/wiki/">Mercurial</a>を触ってみました。</p>
<p> <a href="http://tech.feedforce.jp/mercurial.html#more-59" class="more-link">(more&#8230;)</a></p>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_885652_2892.html">
    <title>OpenVZ - OSレベルの仮想化について</title>
    <link>http://feed.feedforce.jp/item_30880_885652_2892.html</link>
    <dc:date>2008-04-25T18:18:11+09:00</dc:date>
    <description>OpenVZは、Linuxで動作するコンテナベースの仮想化ソフトです。</description>
    <content:encoded><![CDATA[<p>OpenVZは、Linuxで動作するコンテナベースの仮想化ソフトです。</p>
<p>本家URL<br />
<a href="http://openvz.org/">http://openvz.org/</a><br />
wiki<br />
<a href="http://wiki.openvz.org/">http://wiki.openvz.org/</a></p>
<p> <a href="http://tech.feedforce.jp/open-vz.html#more-58" class="more-link">(more&#8230;)</a></p>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_852138_2892.html">
    <title>フィードとユーザーエージェントに関する基礎知識</title>
    <link>http://feed.feedforce.jp/item_30880_852138_2892.html</link>
    <dc:date>2008-03-28T19:36:30+09:00</dc:date>
    <description>はじめに
弊社が提供しているプロダクトのひとつに RSS Suite というフィードの作成/配信/効果測定機能を提供しているASPサービスがあります。
このRSS Suiteが備えている効果測定機能では、毎日のフィードの購読者数と各記事のクリック数を集計してレポートとして提供しています。
今回はこの「フィードの購読者数」について、少しお話をしたいと思います。</description>
    <content:encoded><![CDATA[<h2>はじめに</h2>
<p>弊社が提供しているプロダクトのひとつに <a href="http://www.rsssuite.jp/">RSS Suite</a> というフィードの作成/配信/効果測定機能を提供しているASPサービスがあります。</p>
<p>このRSS Suiteが備えている効果測定機能では、毎日のフィードの購読者数と各記事のクリック数を集計してレポートとして提供しています。</p>
<p><img src='http://tech.feedforce.jp/wp-content/uploads/2008/03/rsssuite-graph.gif' alt='rsssuite-graph.gif' /></p>
<p>↑ このような時系列のグラフで購読者数とクリック数を表示しています。</p>
<p><img src='http://tech.feedforce.jp/wp-content/uploads/2008/03/rsssuite-ua-graph.gif' alt='rsssuite-ua-graph.gif' /></p>
<p>↑ 購読者数はユーザーエージェント別にもレポートされます。</p>
<p>今回はこの「フィードの購読者数」について、少しお話をしたいと思います。</p>
<h2>購読者をどう定義する？</h2>
<p>まず「購読者」とは何か？ということについてですが、フィードをRSSリーダーなどに登録している人や、フィードを読み込んでいるガジェットを利用している人などが「購読者」にあたると考えられます。</p>
<p>フィードには毎日多彩なユーザーエージェントからのアクセスがあります。フィードを収集するためだけのボットや、Internet Explorerのような普通のブラウザなど、購読者とは見なせないものも多数含まれます。</p>
<p>そのため、それらの中から「購読者」を判別するためには、アプリケーションのユーザーエージェント文字列を見て、そのアプリケーションがRSSリーダー機能を備えているかどうかを地道に調査する必要があります。</p>
<h2>RSSリーダーの種類と購読者数</h2>
<p>一口にRSSリーダーといっても、いくつかの種類があります。</p>
<ol>
<li><a href="http://reader.goo.ne.jp/">goo RSSリーダー</a>や<a href="http://www.newsgator.com/Individuals/FeedDemon/Default.aspx">FeedDemon</a>のようなクライアント型のRSSリーダー</li>
<li><a herf="http://reader.livedoor.com/">livedoor Reader</a>や<a href="http://reader.google.com/">Google Reader</a>のようなウェブ型のRSSリーダー</li>
<li>FirefoxやInternet Explorer 7.0のようなRSSリーダー機能つきブラウザ</li>
</ol>
<h3>クライアント型のRSSリーダー</h3>
<p>このタイプのRSSリーダーの場合、一つのRSSリーダーにつき利用者は一人であろうという前提で購読者数を集計しています。</p>
<h3>ウェブ型のRSSリーダー</h3>
<p>このタイプのRSSリーダーでは、クローラーが一つであっても利用者は多数存在します。それらの多くはユーザーエージェントに購読者数を付与してフィードを収集しているので、その情報を元に購読者数を集計しています。</p>
<p>このタイプで代表的なRSSリーダーのユーザーエージェントは次のようなものです。</p>
<pre>livedoor Reader
livedoor FeedFetcher/0.01 (http://reader.livedoor.com/; 9 subscribers)

Google Reader
Feedfetcher-Google; (+http://www.google.com/feedfetcher.html; 1 subscribers; feed-id=1633438417581487553)

Bloglines
Bloglines/3.1 (http://www.bloglines.com; 2 subscribers)

はてなRSS
Hatena RSS/0.3 (http://r.hatena.ne.jp; 21 subscribers)
</pre>
<p>このようにユーザーエージェントの情報を元にすれば購読者数を把握できるのですが、逆にいうとその情報を信用する以外に購読者数を知る術がないというのが現状です。</p>
<h3>RSSリーダー機能つきのブラウザ</h3>
<p>RSSリーダー機能つきのブラウザの場合、ポイントとなるのは以下の2つです。</p>
<ul>
<li>ブラウザの種類を適切に判別すること</li>
<li>ブラウザのバージョンを適切に判別すること</li>
</ul>
<p>これらはフィードの効果測定において重要なことであり、かつ、非常に手間のかかるところでもあります。ですので、このブラウザの判別について、もう少し掘り下げてみたいと思います。</p>
<h2>ブラウザの判別</h2>
<h3>ブラウザの種類を適切に判別すること</h3>
<p>IEのレンダリングエンジンを利用しているブラウザの多くは、ユーザーエージェントの文字列がIEのものと酷似しています。たとえば<a href="http://www.lunascape.jp/">Lunascape</a>は次のようなユーザーエージェントを持っています。</p>
<pre>Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04324.17; Lunascape 4.1.3)</pre>
<p>上記の場合、IE7.0ではなく Lunascape 4と判別しなければなりません。</p>
<p>また、IEのユーザーエージェントには見たことのない文字列が多数入っていることが多く、その中に特定のブラウザを示す文字列があるかどうかを見つける必要があります。</p>
<pre>Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; FunWebProducts-AskJeevesJapan; .NET CLR 1.1.4322; SpamBlockerUtility 4.8.4)</pre>
<p>上記の場合、FunWebProductsやSpamBlockerUtilityという文字列がありますが、これらはブラウザを示すものではないため、上記のユーザーエージェントはIE7.0と判別される必要があります。</p>
<h3>ブラウザのバージョンを適切に判別すること</h3>
<p>RSSリーダー機能が搭載されていれば、そのブラウザからのアクセスは「購読」と見なせる可能性がありますが、RSSリーダー機能が搭載されていないブラウザの場合は「購読」と見なすことはできません。</p>
<p>IEではバージョン7以降にRSSリーダー機能が搭載されています。しかし、バージョン6以前には搭載されていませんでした。</p>
<p>同様に、Safariもバージョン2.0以降でRSSリーダーが搭載されていて、それ以前のバージョンには搭載されていませんし、OperaやNetscapeでも同じくバージョンによってRSSリーダー機能が搭載されているものと搭載されていないものがあります。</p>
<p>世の中には数万種類のブラウザがあり、ユーザーエージェント文字列もそれと同じ数だけ存在します。そのため、どの文字列がブラウザを示すものであるか、どのブラウザにRSSリーダー機能が搭載されているかを常に調べておく必要があります。</p>
<h2>まとめ</h2>
<p>今回は、フィードの購読者数をユーザーエージェント文字列を元にカウントする際のポイントをお話しました。ポイントは以下の通りです。</p>
<ul>
<li>購読者の定義</li>
<li>購読者数つきのユーザーエージェント</li>
<li>ブラウザの種類の判別</li>
<li>ブラウザのバージョンの判別</li>
<li>日々の調査</li>
</ul>
<p>ユーザーエージェントの判別はルールさえあれば自動的に実行できますが、新種のユーザーエージェントが登場したときはその都度ルールの追加をしていく必要があります。</p>
<p>そのためフィードフォースの技術チームでは、ユーザーエージェントの調査を毎日持ち回りでおこなっていて、新しいユーザーエージェントが出現した際にも迅速にレポートに反映できる仕組み作りに取り組んでいます。</p>
<p>今回お話した内容以外にもユーザーエージェントに関していくつか面白い話があるのですが、それはまた機会があればお話したいと思います。</p>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_832206_2892.html">
    <title>ホワイトボードを使ったタスク管理</title>
    <link>http://feed.feedforce.jp/item_30880_832206_2892.html</link>
    <dc:date>2008-03-11T18:18:56+09:00</dc:date>
    <description>はじめに
弊社では、技術チームのタスク管理に、ホワイトボードを利用しています。毎朝、ホワイトボードの前にチームメンバーを集めて、スタンドアップミーティングを行っています。
今回のコラムでは、このホワイトボードとタスク管理についてお話しします。</description>
    <content:encoded><![CDATA[<p class="day">
<p class="body">
<p class="section">
<ul>
<li><a href="#l0">はじめに</a>
<ul>
<li><a href="#l1">前知識</a></li>
</ul>
</li>
<li><a href="#l2">目的および効果</a>
<ul>
<li><a href="#l3">見える化</a></li>
<li><a href="#l4">意識合わせ</a></li>
<li><a href="#l5">振り返り</a></li>
</ul>
</li>
<li><a href="#l6">ホワイトボードの使い方</a>
<ul>
<li><a href="#l7">(1) Q内の月別予定</a></li>
<li><a href="#l8">(2) KPT</a></li>
<li><a href="#l9">(3) 直近のタスクプール</a></li>
<li><a href="#l10">(4) 先延ばしタスクプール</a></li>
<li><a href="#l11">(5) 定期タスクプール</a></li>
<li><a href="#l12">(6) メンバーのタスク管理表</a>
<ul>
<li><a href="#l13">(6a) 実行中タスク</a></li>
<li><a href="#l14">(6b) 各曜日別の実行済みタスク</a></li>
</ul>
</li>
<li><a href="#l15">(7) メンバー用の汎用タスクプール</a></li>
</ul>
</li>
<li><a href="#l16">タスクをホワイトボードで管理するメリット</a>
<ul>
<li><a href="#l17">朝ミーティングの効率</a></li>
<li><a href="#l18">一目瞭然</a></li>
<li><a href="#l19">振り返りの効率</a></li>
<li><a href="#l20">公開性</a></li>
</ul>
</li>
<li><a href="#l21">まとめ</a></li>
<li><a href="#l22">最後に</a></li>
</ul>
<p class="day">
<h2><span class="date"><a title="l0" name="l0"></a> </span><span class="title">はじめに</span></h2>
<p class="body">
<p class="section">弊社では、技術チームのタスク管理に、ホワイトボードを利用しています。毎朝、ホワイトボードの前にチームメンバーを集めて、スタンドアップミーティングを行っています。</p>
<p>今回のコラムでは、このホワイトボードとタスク管理についてお話しします。</p>
<p><img src="http://resource.feedforce.jp/study/whiteboard/white-board-practice.jpg" width="460" height="345" alt="ホワイトボードの写真" border="0" /></p>
<h3><a title="l1" name="l1"></a><span class="sanchor"> </span>前知識</h3>
<p>これ以降の文章の中で、「タスクカード」という言葉が登場します。これは、タスクを書き留める付箋の事です。この付箋をホワイトボードに貼り付けて、タスク管理を行っています。</p>
<p><img src="http://resource.feedforce.jp/study/whiteboard/task_card.png" alt="タスクカード例" /></p>
<ul>
<li>色でおおまかに分類
<ul>
<li>プロダクトの色、バグは赤、汎用の色、など</li>
</ul>
</li>
<li>(1) BTS管理されているならそのID</li>
<li>(2) タスクの簡潔な見出し</li>
<li>(3) 実施期間</li>
<li>(4) 見積り(時間単位)</li>
<li>(5) 実働時間(時間単位)</li>
</ul>
<p>ちなみに、付箋は強粘着がおすすめです。</p>
<p class="day">
<h2><span class="date"><a title="l2" name="l2"></a> </span><span class="title">目的および効果</span></h2>
<p class="body">
<p class="section">
<ul>
<li>見える化</li>
<li>意識合わせ</li>
<li>振り返り</li>
</ul>
<h3><a title="l3" name="l3"></a><span class="sanchor"> </span>見える化</h3>
<p>チームで仕事をするにあたって、誰が何をどの程度行っているのかを、目に見える形で管理したいという要望から、ホワイトボードによるタスク管理は始まりました。</p>
<h3><a title="l4" name="l4"></a><span class="sanchor"> </span>意識合わせ</h3>
<p>また、毎朝ホワイトボードの前で、数分〜十数分間のスタンドアップミーティングを行い、意識合わせおよびメンバーのタスク状況の把握などが効率よく行えます。</p>
<h3><a title="l5" name="l5"></a><span class="sanchor"> </span>振り返り</h3>
<p>さらに、週末には振り返りを実施しており、その際にも、仕事量や見積もり誤差の把握に役立っています。振り返りの際には、その週のKPTも報告しあい、それをホワイトボードに書き留めており、反省等を忘れずに意識し続ける事にも役立っています。</p>
<p class="day">
<h2><span class="date"><a title="l6" name="l6"></a> </span><span class="title">ホワイトボードの使い方</span></h2>
<p class="body">
<p class="section">ホワイトボードは、いくつかの領域に分けて使用しています。</p>
<p><img src="http://resource.feedforce.jp/study/whiteboard/whiteboard.png" alt="ホワイトボード例" /></p>
<ul>
<li>(1) Q内の月別予定</li>
<li>(2) KPT</li>
<li>(3) 直近のタスクプール</li>
<li>(4) 先延ばしタスクプール</li>
<li>(5) 定期タスクプール</li>
<li>(6) メンバーのタスク管理表
<ul>
<li>(6a) 実行中タスク</li>
<li>(6b) 各曜日別の実行済みタスク</li>
</ul>
</li>
<li>(7) メンバー用の汎用タスクプール</li>
</ul>
<h3><a title="l7" name="l7"></a><span class="sanchor"> </span>(1) Q内の月別予定</h3>
<p>Q(3ヶ月)内の予定を、各月に分けて掲示する領域です。大きめなマイルストーンや、イベント事などが主に掲示されています。</p>
<p>チームによっては、各月でやりきる予定のタスクカードを貼り付けていたりします。</p>
<h3><a title="l8" name="l8"></a><span class="sanchor"> </span>(2) KPT</h3>
<p>振り返りミーティングで報告された内容を書き留めておく領域です。重要な事は延々と残しておき、習慣づいたり、改善されたような事は消していきます。</p>
<h3><a title="l9" name="l9"></a><span class="sanchor"> </span>(3) 直近のタスクプール</h3>
<p>だいたい、1週間以内で実行する予定のタスクカードを貼り付ける領域です。お知らせを書いたりもします。</p>
<h3><a title="l10" name="l10"></a><span class="sanchor"> </span>(4) 先延ばしタスクプール</h3>
<p>諸事情から、優先度が下がって先延ばしになったタスクカードを、念のため忘れ去られない様に貼付けておく領域です。だいたい、Q末に整理する様にしています。</p>
<h3><a title="l11" name="l11"></a><span class="sanchor"> </span>(5) 定期タスクプール</h3>
<p>定期的に実行しているタスクカードを貼り付ける領域です。担当者が休んでいる様な場合にも、忘れられない様に注意しています。</p>
<h3><a title="l12" name="l12"></a><span class="sanchor"> </span>(6) メンバーのタスク管理表</h3>
<p>各メンバーごとに担当している(完了した)タスクカードを貼り付ける領域です。</p>
<h4><a title="l13" name="l13"></a> (6a) 実行中タスク</h4>
<p>現在実行中のタスクカードを貼り付けるブロックです。</p>
<h4><a title="l14" name="l14"></a> (6b) 各曜日別の実行済みタスク</h4>
<p>実行済みタスクカードを貼り付けるブロックです。月曜から金曜までに分かれています。</p>
<h3><a title="l15" name="l15"></a><span class="sanchor"> </span>(7) メンバー用の汎用タスクプール</h3>
<p>担当が決まっていつつ、諸事情から優先度が下がったり、ペンディング中となったタスクカードを貼付けておく領域です。</p>
<p class="day">
<h2><span class="date"><a title="l16" name="l16"></a> </span><span class="title">タスクをホワイトボードで管理するメリット</span></h2>
<p class="body">
<p class="section">弊社では、原則すべてのタスクをBTSで管理しています。すでに管理されているタスクを、さらにホワイトボードでも管理する意味はいったいなんなのでしょうか。冗長な事をして無駄ではないでしょうか。</p>
<h3><a title="l17" name="l17"></a><span class="sanchor"> </span>朝ミーティングの効率</h3>
<p>大前提として、朝ミーティングは欠かせません。</p>
<p>BTSでタスクを管理している為、誰が何をしているのか、どういう状況なのかなどは、注意を払えば大体の事は分かります。しかし、注意を払っても大体の事しか分からず、開発メンバーにそれほどの余裕はありません。</p>
<p>他のメンバーの状況等を知りたいという潜在的な欲求はあるはずですが、期日(翌週のリリース日)に向けて集中している中で、他の事に目を向ける余裕はなかなか生まれません。</p>
<p>毎朝、スタンドアップミーディングを行う事で、短時間で集中して、チーム内での情報共有や意識合わせを行えるわけです。その際に、タスク状況を目に見えて分かりやすく管理している、<strong>アナログな</strong>ホワイトボードは非常に役に立ちます。</p>
<h3><a title="l18" name="l18"></a><span class="sanchor"> </span>一目瞭然</h3>
<p>ふと顔を上げ、ホワイトボードを見れば、誰がどの様な状況かを知る事が出来ます。</p>
<p>例えば、何かに煮詰まって助けを求めたい場合に、比較的手が空いていそうな人を探すのに役立ちます。</p>
<p>デジタルな世界に関するプロフェッショナルとはいえ、一般的な人生を歩んでいる限りは、大抵の場合でアナログなツールの方が分かりやすかったり親しみやすかったりします。</p>
<p><a href="http://tech.feedforce.jp/bts-2.html" class="external">FFTT : BTS - バグトラッキングシステムの利用（２）</a>でお話しした、BTS人形もそうですが、時にはアナログな方法を採用した方が効率的な場合があります。</p>
<h3><a title="l19" name="l19"></a><span class="sanchor"> </span>振り返りの効率</h3>
<p>週で実施したタスクがホワイトボードに貼付けられているので、その週の振り返りが手ぶらで行えます。</p>
<p>振り返りといっても、深く突っ込んだ内容ではありません。その週に何を行い、その見積もり誤差がどの程度で何が原因なのか、次に活かすべき事は何かなど、簡潔な内容ですませています。</p>
<p>深く突っ込んだ振り返りは、Q末に開発プラクティス等全般的な見直しミーティングや、技術チーム内での面談等で実施しています。これは、今回のお話とは趣旨がずれますので、割愛します。</p>
<p>つまり、言いたい事は、ささっと意味のある簡潔な振り返りが手間いらずで出来ます、という事です。</p>
<p>また、KTPをホワイトボードに記録しているので、これも効果的な振り返りを助けてくれています。</p>
<h3><a title="l20" name="l20"></a><span class="sanchor"> </span>公開性</h3>
<p>これについては、技術チームの人の意見を聞いたわけではないので自信がありませんが、公開されている事にも意味がある様に思います。</p>
<p>例えば、管理的な立場の人が、ぱっと見て進捗具合を把握できるツールがあるというのは、重要ではないでしょうか。</p>
<p>朝ミーティングにはプロダクトを統括する人にも参加してもらっていますが、後でふとした時にも簡単に確認できるので、非常に彼らの助けになっているだろうと期待しています。</p>
<p class="day">
<h2><span class="date"><a title="l21" name="l21"></a> </span><span class="title">まとめ</span></h2>
<p class="body">
<p class="section">タスクをアナログな方法でみんなに見える様に、簡潔に管理する事は、チーム作業において大きな助けとなってくれます。</p>
<p class="day">
<h2><span class="date"><a title="l22" name="l22"></a> </span><span class="title">最後に</span></h2>
<p class="body">
<p class="section">今回ご紹介した利用例や目的は、弊社で最適化したものになっているため、皆様の環境にはそぐわないかもしれません。しかしながら、私の実感で、非常に役に立ってくれる無くてはならないツールであるというのは、まぎれも無い事実です。</p>
<p>このような事例を参考に、もしよろしければ、皆様の環境でも似た様な取り組みを導入・ご検討されてみてはいかがでしょうか。</p>
<p>余談ですが、先日、弊社の企画・デザイン混合チームでも、ホワイトボードでのタスク管理を始めたようです。どうやら、開発側とは少し違った使い方をしている様で、興味深く見守っていきたいと思います。</p>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_815382_2892.html">
    <title>OpenID認証2.0〜概論</title>
    <link>http://feed.feedforce.jp/item_30880_815382_2892.html</link>
    <dc:date>2008-02-25T11:09:31+09:00</dc:date>
    <description>OpenID認証2.0の&quot;概論&quot;についての発表資料です
仕様の詳細部分については説明を省略しています
XRI周辺についての説明も省略しています(力量不足につき)
仕様を把握しきれてはいないため、誤りが多く含まれている可能性があります
誤りの訂正や、内容へのご批判等、コメントやメール等でご指摘お願いします
suzuki at feedforce.jp

以下は、実際の発表で使用したスライドのPDFです。
発表資料(PDF) -- 1.1MB</description>
    <content:encoded><![CDATA[<div class="section">
<ul>
<li><a href="#l0">はじめに</a></li>
<li><a href="#l1">OpenID認証とは</a></li>
<li><a href="#l2">特徴</a></li>
<ul>
<li><a href="#l3">「オープン」</a></li>
<li><a href="#l4">「秘密情報の保護」</a></li>
<li><a href="#l5">「分散的」</a></li>
<li><a href="#l6">「HTTP」</a></li>
<li><a href="#l7">「拡張」</a></li>
</ul>
<li><a href="#l8">用語</a></li>
<li><a href="#l9">プロトコル概観</a></li>
<ul>
<li><a href="#l10">開始(Initiation)</a></li>
<li><a href="#l11">正規化(Normarization)</a></li>
<li><a href="#l12">発見(Discovery)</a></li>
<li><a href="#l13">関連づけ(Association)</a></li>
<li><a href="#l14">認証要求</a></li>
<li><a href="#l15">エンドユーザーの認可</a></li>
<li><a href="#l16">承認/却下</a></li>
<li><a href="#l17">照合</a></li>
</ul>
<li><a href="#l18">実例</a></li>
<ul>
<li><a href="#l19">OP-Local Identifierで始める</a></li>
<li><a href="#l20">OP Identifierで始める</a></li>
<li><a href="#l21">HTMLのURLで始める</a></li>
<li><a href="#l22">自前のYadis IDで始める</a></li>
</ul>
<li><a href="#l23">セキュリティ</a></li>
<ul>
<li><a href="#l24">攻撃の予防</a></li>
<ul>
<li><a href="#l25">盗聴による攻撃</a></li>
<li><a href="#l26">中間者による攻撃</a></li>
<li><a href="#l27">悪意のRPによるProxy攻撃</a></li>
</ul>
<li><a href="#l28">User-Agent</a></li>
<li><a href="#l29">UI</a></li>
<li><a href="#l30">DoS攻撃</a></li>
<li><a href="#l31">信頼性と評価</a></li>
</ul>
<li><a href="#l32">おまけ</a></li>
<ul>
<li><a href="#l33">参照先</a></li>
<li><a href="#l34">OPとRPの一例</a></li>
<ul>
<li><a href="#l35">OP</a></li>
<li><a href="#l36">RP</a></li>
</ul>
<li><a href="#l37">日本での活動</a></li>
<li><a href="#l38">手前味噌</a></li>
</ul>
<li><a href="#l39">最後に</a></li>
</ul>
</div>
<div class="day">
<h2><span class="date"><a name="l0"> </a></span><span class="title">はじめに</span></h2>
<div class="body">
<div class="section">
<ul>
<li>OpenID認証2.0の&#8221;概論&#8221;についての発表資料です</li>
<li>仕様の詳細部分については説明を省略しています</li>
<li>XRI周辺についての説明も省略しています(力量不足につき)</li>
<li>仕様を把握しきれてはいないため、誤りが多く含まれている可能性があります</li>
<li>誤りの訂正や、内容へのご批判等、コメントやメール等でご指摘お願いします
<ul>
<li>suzuki at feedforce.jp</li>
</ul>
</li>
</ul>
<p>以下は、実際の発表で使用したスライドのPDFです。</p>
<ul>
<li><a href="http://resource.feedforce.jp/study/20080222/openid_authentication_2_0-introduction.pdf" class="external">発表資料(PDF) &#8212; 1.1MB</a></li>
</ul></div>
</p></div>
</div>
<div class="day">
<h2><span class="date"><a name="l1"> </a></span><span class="title">OpenID認証とは</span></h2>
<div class="body">
<div class="section">
<blockquote>
<p>OpenID認証はエンドユーザーが管理するIdentifierを証明する方法を提供します。</p>
</blockquote>
<dl>
<dt>注目</dt>
</dl>
<ul>
<li>Identifierはエンドユーザーが管理している</li>
</ul></div>
</p></div>
</div>
<div class="day">
<h2><span class="date"><a name="l2"> </a></span><span class="title">特徴</span></h2>
<div class="body">
<div class="section">
<h3><a name="l3"><span class="sanchor"> </span></a>「オープン」</h3>
<p>公開規格である為、誰にでも自由に無償で利用可能です。当然ながら、アイデンティティを特定ドメインに限定せず、Web全体での利用を可能にします。</p>
<p>また、公開規格である事から、ドメイン間を統一的な認証インターフェースで繋ぐ効果が期待できます。例えば、(擬似的な)SSOが考えられるでしょう。</p>
<p>注) 実際に安全なSSOの実現が簡単であるかは別問題とします。</p>
<h3><a name="l4"><span class="sanchor"> </span></a>「秘密情報の保護」</h3>
<p>OpenID認証では、認証を要求する側に対して、エンドユーザーのパスワードやメールアドレスなどを教える事無く、認証を完了させる事が可能です。</p>
<h3><a name="l5"><span class="sanchor"> </span></a>「分散的」</h3>
<p>OpenID認証では、中央の権威機関などが存在しない為、分散的であると言えます。つまり、特別な機関の承認や登録を必要としません。</p>
<p>また、エンドユーザーは利用する認証のサーバーを自由に選択する事が出来ます。認証のサーバーを切り替えたとしても、利用しているアイデンティティを保持する事が可能です。</p>
<h3><a name="l6"><span class="sanchor"> </span></a>「HTTP」</h3>
<p>OpenID認証では、標準的なHTTPのリクエスト/レスポンスのみを使います。このため、User-Agentの特別な能力や、その他の特別なクライアントソフトウェアが要求される事はありません。例えば、認証の手続きの間に、Cookieの利用が強制される様な事もありません。</p>
<p>HTTPベースであるため、AJAXスタイルでもうまくいきますが、この場合は、モダンブラウザやスクリプト対応などに依存する事になります。</p>
<h3><a name="l7"><span class="sanchor"> </span></a>「拡張」</h3>
<p>OpenID認証では、認証以上の事を拡張仕様で補う様になっています。例えば、プロフィール情報の交換や、その他のアイデンティティに基づく情報の交換等が、拡張しようを利用して行われます。</p>
<p>また、認証のサーバーのポリシーを取得する為の拡張しようなども提案されているようです。</p>
</p></div>
</p></div>
</div>
<div class="day">
<h2><span class="date"><a name="l8"> </a></span><span class="title">用語</span></h2>
<div class="body">
<div class="section">
<p>沢山のIdentifierが登場する為、ここで全てを理解するのは難しいと思います。後述するプロトコル概観の中で、つじつまを合わせるのが良いでしょう。</p>
<dl>
<dt>Identifier</dt>
</dl>
<p>いわゆるアイデンティティです。個人を特定する記号であると考えればよいでしょう。HTTPかHTTPSをスキームとするURIか、XRIで表現されます。</p>
<p>OpenID認証のドキュメント中には、文脈によった様々なIdentifierが登場するので、注意してください。</p>
<dl>
<dt>User-Agent</dt>
</dl>
<p>HTTP/1.1を実装しているエンドユーザーのウェブブラウザです。ウェブブラウザに限定するわけでもありませんが、対話的な認証を行う場合は、一般的にウェブブラウザが使われるでしょう。</p>
<dl>
<dt>Relying Party</dt>
</dl>
<p>RPと呼びます。エンドユーザーが管理するIdentifierの証明を望むウェブアプリケーションの事です。</p>
<dl>
<dt>OpenID Provider</dt>
</dl>
<p>OPと呼びます。RPがエンドユーザーの管理するIdentifierを主張するために信頼するOpenID認証サーバーです。</p>
<dl>
<dt>OP Endpoint URL</dt>
</dl>
<p>User-Supplied Identifierに対して発見を行った結果に得られた、OpenID認証プトロコルのメッセージを受け取るURLです。これは、HTTPかHTTPSの絶対URLです。</p>
<dl>
<dt>OP Identifier</dt>
</dl>
<p>OPを表すIdentifierです。例えば、Yahoo! JAPANの場合、&#8221;http://yahoo.co.jp/&#8221;であると考えてよいでしょう。</p>
<dl>
<dt>User-Supplied Identifier</dt>
</dl>
<p>エンドユーザーによってRPに与えられるIdentifierか、OPでエンドユーザーに選択されるIdentifierの事です。</p>
<dl>
<dt>Claimed Identifier</dt>
</dl>
<p>エンドユーザーが自分のものであると主張するIdentifierです。OpenID認証は、この主張が真実である事を証明する事を目的としています。</p>
<p>User-Supplied IdentifierがURLだった場合はそれを正規化して得られたIdentifierで、XRIだった場合はCanonicalIDです。</p>
<dl>
<dt>OP-Local Identifier</dt>
</dl>
<p>各OPで局所的な、エンドユーザーの為の代替Identifierです。これは、エンドユーザーの管理下にある必要はありません。</p>
</p></div>
</p></div>
</div>
<div class="day">
<h2><span class="date"><a name="l9"> </a></span><span class="title">プロトコル概観</span></h2>
<div class="body">
<div class="section">
<p>概観について、一部、実例を交えながら説明します。</p>
<ul>
<li>OP: Yahoo! JAPAN</li>
<li>RP: Fastladder</li>
</ul>
<h3><a name="l10"><span class="sanchor"> </span></a>開始(Initiation)</h3>
<p>エンドユーザーが、User-Agent経由でRPにUser-Supplied Identifierを与えます。</p>
<p><img src="http://resource.feedforce.jp/study/20080222/openid_initiation.png" alt="開始"></p>
<h3><a name="l11"><span class="sanchor"> </span></a>正規化(Normarization)</h3>
<p>RPは、受け取ったUser-Supplied Identifierを正規化します。</p>
<pre>
yahoo.co.jp -&gt; http://yahoo.co.jp/
</pre>
<h3><a name="l12"><span class="sanchor"> </span></a>発見(Discovery)</h3>
<p>正規化したUser-Supplied Identifierに対して、発見を行います。</p>
<p>発見の結果、エンドユーザーの認証に使う&#8221;OP Endpoint URL&#8221;が確立されます。</p>
<pre>
$ curl -I http://yahoo.co.jp/
HTTP/1.1 302 Found
Date: Mon, 18 Feb 2008 14:08:03 GMT
Location: http://www.yahoo.co.jp/index.html
Connection: close
Content-Type: text/html; charset=euc-jp
</pre>
<ul>
<li>リダイレクトがある場合は追いかけます</li>
</ul>
<pre>
$ curl -I http://www.yahoo.co.jp/index.html
HTTP/1.1 200 OK
Date: Mon, 18 Feb 2008 14:08:23 GMT
P3P: policyref="http://privacy.yahoo.co.jp/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE GOV"
Expires: -1
Pragma: no-cache
Cache-Control: no-cache
X-XRDS-Location: http://open.login.yahoo.co.jp/openid20/www.yahoo.co.jp/xrds
Connection: close
Content-Type: text/html; charset=utf-8
</pre>
<ul>
<li>詳細は省略しますが、&#8221;X-XRDS-Location&#8221;のURLからドキュメントを取得します</li>
</ul>
<pre>
$ curl -i http://open.login.yahoo.co.jp/openid20/www.yahoo.co.jp/xrds
HTTP/1.1 200 OK
Date: Mon, 18 Feb 2008 14:10:51 GMT
P3P: policyref="http://privacy.yahoo.co.jp/w3c/p3p.xml", CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE GOV"Last-Modified: Thu, 17 Jan 2008 01:25:25 GMT
Accept-Ranges: bytes
Content-Length: 354
Connection: close
Content-Type: application/xrds+xml

&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;xrds:XRDS
    xmlns:xrds="xri://$xrds"
    xmlns:openid="http://openid.net/xmlns/1.0"
    xmlns="xri://$xrd*($v*2.0)"&gt;
  &lt;XRD&gt;
    &lt;Service priority="0"&gt;
      &lt;Type&gt;http://specs.openid.net/auth/2.0/server&lt;/Type&gt;

      &lt;URI&gt;https://open.login.yahooapis.jp/openid/op/auth&lt;/URI&gt;
    &lt;/Service&gt;
  &lt;/XRD&gt;
&lt;/xrds:XRDS&gt;
</pre>
<ul>
<li>URI要素でOP Endpoint URLが示されています。</li>
</ul>
<h3><a name="l13"><span class="sanchor"> </span></a>関連づけ(Association)</h3>
<p>このプロセスは任意です。</p>
<p>OpenID認証の手続きの中で、RPとOPはリクエスト/レスポンスの署名を確認する事で、それぞれの正当性を証明します(照合(Verification))。</p>
<p>関連づけは、この照合のための余計なリクエスト/レスポンスを節約する為に役立ちます。</p>
<p>具体的には、Diffie-Hellman鍵共有を利用して、RPとOPの間に関連付けを構築します。</p>
<ul>
<li><a href="http://ja.wikipedia.org/wiki/Diffie-Hellman%E9%8D%B5%E5%85%B1%E6%9C%89" class="external">Diffie-Hellman鍵共有 - Wikipedia</a></li>
</ul>
<h3><a name="l14"><span class="sanchor"> </span></a>認証要求</h3>
<p>RPがエンドユーザーのUser-Agentを、認証要求パラメータをつけた&#8221;OP Endpoint URL&#8221;にリダイレクトさせます。</p>
<dl>
<dt>認証要求のパラメータ例</dt>
</dl>
<ul>
<li>openid.ns</li>
<li>openid.mode</li>
<li>openid.claimed_id</li>
<li>openid.identity</li>
<li>openid.assoc_handle</li>
<li>openid.return_to</li>
<li>openid.realm</li>
</ul>
<p>パラメータの詳細には触れません。</p>
<h3><a name="l15"><span class="sanchor"> </span></a>エンドユーザーの認可</h3>
<p>OPが、エンドユーザーが本当にOpenID認証を行うために正当であると認められているのかを確認します。</p>
<p>一般に、これはエンドユーザーをログインさせる事で確かめられます。</p>
<p>この方法や方針については、OpenID認証の仕様の範囲外となっています。</p>
<p><img src="http://resource.feedforce.jp/study/20080222/openid_user_auth.png" alt="エンドユーザー認可"></p>
<h3><a name="l16"><span class="sanchor"> </span></a>承認/却下</h3>
<p>RPからの認証要求に対して、OPは承認か却下を示すメッセージを付けて、エンドユーザーのUser-AgentをRPにリダイレクトします。</p>
<dl>
<dt>メッセージのパラメータ例</dt>
</dl>
<ul>
<li>openid.ns</li>
<li>openid.mode</li>
<li>openid.op_endpoint</li>
<li>openid.claimed_id</li>
<li>openid.identity</li>
<li>openid.return_to</li>
<li>openid.response_nonce</li>
<li>openid.invalidate_handle</li>
<li>openid.assoc_handle</li>
<li>openid.signed</li>
<li>openid.sig</li>
</ul>
<p>パラメータの詳細には触れません。</p>
<h3><a name="l17"><span class="sanchor"> </span></a>照合</h3>
<p>OPから受け取った情報について、RPが照合を行います。</p>
<ul>
<li>戻りURL</li>
<li>発見した情報</li>
<li>nonce</li>
<li>署名</li>
</ul>
<p>この照合が全て妥当であったときに、OpenID認証による認証は成功となります。</p>
<p><img src="http://resource.feedforce.jp/study/20080222/openid_successful.png" alt="成功"></p>
</p></div>
</p></div>
</div>
<div class="day">
<h2><span class="date"><a name="l18"> </a></span><span class="title">実例</span></h2>
<div class="body">
<div class="section">
<p>幾つかの実例を見てみましょう。概観に引き続き、RPはFastladderで、OPはYahoo! JAPANです。</p>
<p>以下の点に注目してみてください。</p>
<ul>
<li>最終的にRPに認識されるアイデンティティ(Identifier)</li>
<li>〜Identifier</li>
</ul>
<p>この実例を通して特に注意してほしい事は、OPを変更した場合でもIdentifierを保持する方法がOpenID認証の中で提供されている、という事です。</p>
<p>また、この実例の中で利用しているOP-Local Identifierは、各自で発行されたものに読み替えてください。</p>
<h3><a name="l19"><span class="sanchor"> </span></a>OP-Local Identifierで始める</h3>
<p>User-Supplied IdentifierにOP-Local Identifierを使ってみましょう。</p>
<ol>
<li>まず、フォームの入力欄にYahoo! JAPANで発行されたIDを入力してボタンをクリックします
<ol>
<li>https://me.yahoo.co.jp/a/Ya_JyXB8ZPujNdvm8OOHJ837_LRI7UJll__x</li>
</ol>
</li>
<li>Yahoo! JAPANにリダイレクトされるので、そのままログインします</li>
<li>ログイン後、OpenID認証を継続するか聞かれるので「続ける」ボタンをクリックします
<ol>
<li>OPによっては、ここでIdentifierを選択する事ができます</li>
</ol>
</li>
<li>Fastladderにリダイレクトされ、認証が成功して完了です</li>
</ol>
<p>このケースでは、最終的にYahoo! JAPANで発行されたIDが使われます。</p>
<pre>
$ curl "https://me.yahoo.co.jp/a/Ya_JyXB8ZPujNdvm8OOHJ837_LRI7UJll__x"
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;
OpenID Provider 2.0 -- User's page
&lt;/title&gt;&lt;link rel="openid2.provider" href="https://open.login.yahooapis.jp/openid/op/auth"&gt;
&lt;link rel="openid.server" href="https://open.login.yahooapis.jp/openid/op/1.1/auth"&gt;
&lt;/head&gt;
&lt;script&gt;window.location.href='http://openid.yahoo.co.jp';&lt;/script&gt;

&lt;body&gt;
&lt;p&gt;
The user's OpenID provider is
&lt;a href="https://open.login.yahooapis.jp/openid/op/auth"&gt;
https://open.login.yahooapis.jp/openid/op/auth&lt;/a&gt;
&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<ul>
<li>rel=&#8221;openid2.provider&#8221;があるlink要素で(OpenID認証2.0の)OP Endpoint URLが示されています</li>
</ul>
<h3><a name="l20"><span class="sanchor"> </span></a>OP Identifierで始める</h3>
<p>User-Supplied IdentifierにOP Identifierを使います。</p>
<ol>
<li>まず、フォームの入力欄に&#8221;yahoo.co.jp&#8221;と入力してボタンをクリックします</li>
<li>Yahoo! JAPANにリダイレクトされるので、そのままログインします</li>
<li>ログイン後、OpenID認証を継続するか聞かれるので「続ける」ボタンをクリックします</li>
<li>Fastladderにリダイレクトされ、認証が成功して完了です</li>
</ol>
<p>このケースでは、最終的にYahoo! JAPANで発行されたIDが使われます。</p>
<p>注) &#8220;yahoo.co.jp&#8221;からOpenID認証の手続きを始めるために、Yadisプロトコルが使われています</p>
<pre>
$ curl -LI http://yahoo.co.jp
HTTP/1.1 302 Found
...
Location: http://www.yahoo.co.jp/index.html
...

HTTP/1.1 200 OK
...
X-XRDS-Location: http://open.login.yahoo.co.jp/openid20/www.yahoo.co.jp/xrds
...
</pre>
<ul>
<li>認証を要求するために必要な情報が書かれたドキュメントが<em>X-XRDS-Location</em>ヘッダで示されています</li>
</ul>
<pre>
$ curl -i http://open.login.yahoo.co.jp/openid20/www.yahoo.co.jp/xrds
HTTP/1.1 200 OK
...
Content-Type: application/xrds+xml

&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;xrds:XRDS
    xmlns:xrds="xri://$xrds"
    xmlns:openid="http://openid.net/xmlns/1.0"
    xmlns="xri://$xrd*($v*2.0)"&gt;
  &lt;XRD&gt;
    &lt;Service priority="0"&gt;
      &lt;Type&gt;http://specs.openid.net/auth/2.0/server&lt;/Type&gt;
      &lt;URI&gt;https://open.login.yahooapis.jp/openid/op/auth&lt;/URI&gt;

    &lt;/Service&gt;
  &lt;/XRD&gt;
&lt;/xrds:XRDS&gt;
</pre>
<ul>
<li>&#8221;&lt;URI&gt;https://open.login.yahooapis.jp/openid/op/auth&lt;/URI&gt;&#8221;がOP Endpoint URLです</li>
</ul>
<h3><a name="l21"><span class="sanchor"> </span></a>HTMLのURLで始める</h3>
<ol>
<li>まず、フォームの入力欄に自分で管理するIDを入力してボタンをクリックします
<ol>
<li>http://id.koshigoe.jp/openid2.html</li>
</ol>
</li>
<li>Yahoo! JAPANにリダイレクトされるので、そのままログインします</li>
<li>ログイン後、OpenID認証を継続するか聞かれるので「続ける」ボタンをクリックします</li>
<li>Fastladderにリダイレクトされ、認証が成功して完了です</li>
</ol>
<p>このケースでは、最終的に&#8221;http://id.koshigoe.jp/openid2.html&#8221;がIDとして認識されます。</p>
<p>:http://id.koshigoe.jp/openid2.html:</p>
<pre>
&lt;html&gt;
&lt;head&gt;
  &lt;link rel="openid2.provider" href="https://open.login.yahooapis.jp/openid/op/auth" /&gt;
  &lt;link rel="openid2.local_id" href="https://me.yahoo.co.jp/a/Ya_JyXB8ZPujNdvm8OOHJ837_LRI7UJll__x" /&gt;

&lt;/head&gt;
&lt;body&gt;&lt;/body&gt;&lt;/html&gt;
</pre>
<ul>
<li>rel=&#8221;openid2.provider&#8221;のlink要素でOP Endpoint URLが示されています</li>
<li>rel=&#8221;openid2.local_id&#8221;のlink要素でOP-Local Identifierが示されています</li>
</ul>
<h3><a name="l22"><span class="sanchor"> </span></a>自前のYadis IDで始める</h3>
<p>Yadis IDは<a href="http://yadis.org/wiki/Yadis_1.0_(HTML)" class="external">Yadisプロトコル</a>で定義されます。Yadisプロトコルは、Identifierとそれを扱うサービスおよび用途を提供するプロトコルです。</p>
<p>OpenID認証の場合、<em>OpenID認証</em>で使う<em>Identifier</em>と、それを証明する<em>OP</em>の<em>OP Endpoint URL</em>を提供して貰います。<br />
詳細については省略します。</p>
<ol>
<li>まず、フォームの入力欄に自分で管理するYadis IDを入力してボタンをクリックします
<ol>
<li>http://id.koshigoe.jp/</li>
</ol>
</li>
<li>Yahoo! JAPANにリダイレクトされるので、そのままログインします</li>
<li>ログイン後、OpenID認証を継続するか聞かれるので「続ける」ボタンをクリックします</li>
<li>Fastladderにリダイレクトされ、認証が成功して完了です</li>
</ol>
<p>このケースでは、最終的に&#8221;http://id.koshigoe.jp/&#8221;がIDとして認識されます。</p>
<p>:http://id.koshigoe.jp/:</p>
<ul>
<li>Yadis ID</li>
</ul>
<pre>
$ curl -I http://id.koshigoe.jp/
HTTP/1.1 200 OK
...
X-XRDS-Location: http://id.koshigoe.jp/yadis_claimed-identifier.php
...
</pre>
<p>:http://id.koshigoe.jp/yadis_claimed-identifier.php:</p>
<ul>
<li>Yadis document (XRDS)</li>
</ul>
<pre>
$ curl -i http://id.koshigoe.jp/yadis_claimed-identifier.php
HTTP/1.1 200 OK
...
Content-Type: application/xrds+xml

&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)"&gt;

  &lt;XRD&gt;
    &lt;Service&gt;
      &lt;Type&gt;http://specs.openid.net/auth/2.0/signon&lt;/Type&gt;
      &lt;URI&gt;https://open.login.yahooapis.jp/openid/op/auth&lt;/URI&gt;
      &lt;LocalID&gt;https://me.yahoo.co.jp/a/Ya_JyXB8ZPujNdvm8OOHJ837_LRI7UJll__x&lt;/LocalID&gt;

    &lt;/Service&gt;
  &lt;/XRD&gt;
&lt;/xrds:XRDS&gt;
</pre>
<ul>
<li>&#8221;&lt;Type&gt;http://specs.openid.net/auth/2.0/signon&lt;/Type&gt;&#8221;は、Claimed Identfierを最終的なIdentifierとして使うという様な意味です</li>
<li>&#8221;&lt;LocalID&gt;https://me.yahoo.co.jp/a/Ya_JyXB8ZPujNdvm8OOHJ837_LRI7UJll__x&lt;/LocalID&gt;&#8221;はOP-Local Identifierです</li>
</ul></div>
</p></div>
</div>
<div class="day">
<h2><span class="date"><a name="l23"> </a></span><span class="title">セキュリティ</span></h2>
<div class="body">
<div class="section">
<h3><a name="l24"><span class="sanchor"> </span></a>攻撃の予防</h3>
<h4><a name="l25"> </a>盗聴による攻撃</h4>
<p>OpenID認証は、盗聴について脆弱である可能性があります。特にnonceを確認しない場合、盗聴者は認証に成功した主張を奪いそれを利用する事が出来ます。TLSやSSLなどのトランスポート層での暗号化や、nonceを確認する様にする事で、盗聴による攻撃を防ぐ事が出来ます。</p>
<h4><a name="l26"> </a>中間者による攻撃</h4>
<p>中間者によって、署名されたフィールドが改ざんされる可能性があります。この改ざんは、関連づけを使う事で防ぐ事が出来ます。関連づけによってRPとOPは鍵を共有します。この鍵なしで署名されたフィールドを変更した場合、MACが壊れるので、その改ざんを防ぐ事が出来ると考えられます。MACに関する扱いやすい攻撃方法は知られていないようです。ただし、MACによる保護の質は、そのMACの無作為性に依存します。</p>
<p>また、DNSやトランスポート層が信頼できない場合、メッセージも信頼できません。この場合、攻撃者がOPを装う事が可能な為、関連付けやステートレスモードによって、攻撃者がメッセージを不正に処理する事が出来ます。</p>
<p>そして、発見に攻撃者が介入できる場合、攻撃者がOPを自由に指定できるためOPを装う必要がなくなります。XRDSドキュメントに関しては、XMLDSIGというデジタル署名を利用する事で改ざんを防ぐ事が出来ます。</p>
<p>SSL通信における中間者攻撃に関しては、信頼される機関が発行する証明書を利用する事で、証明書の偽造が非常に困難になります。</p>
<h4><a name="l27"> </a>悪意のRPによるProxy攻撃</h4>
<p>悪意あるRPによって、OPとUser-Agentとの間にProxyが挟まれる可能性があります。この際、エンドユーザーがOPに対して送信するクレデンシャルを、悪意あるRPがキャプチャする事が可能です。Proxyを排除する複数の方法は存在しますが、ここでは省略します。</p>
<h3><a name="l28"><span class="sanchor"> </span></a>User-Agent</h3>
<p>OpenID認証とは別に、User-Agentやそのホストコンピュータが、マルウェアやスパイウェアに汚染される可能性があります。これは、OpenID認証のプロトコル内で対応できる範囲を超えています。</p>
<h3><a name="l29"><span class="sanchor"> </span></a>UI</h3>
<p>OPを模したフィッシングサイトから、エンドユーザーを保護できる作りにすべきです。また、フィッシング攻撃の可能性についてエンドユーザーを教育したり、攻撃を無効化するプラグインの様なものを使わせる様にすべきです。</p>
<h3><a name="l30"><span class="sanchor"> </span></a>DoS攻撃</h3>
<p>OpenID認証におけるリクエストが本物であるかどうかを、素早く確認する様な仕組みは用意されていない為、DoS攻撃の可能性があります。例えば、関連づけや認証の署名を照合する為のメッセージを、RPが繰り返しリクエストする事で行われるでしょう。</p>
<p>最も危険性が高いものは、関連づけにおけるDiffie-Hellman鍵共有の計算です。RPがパラメータを指定できる為、負荷が高い計算をOPに対して強制する事が可能です。</p>
<p>対抗措置としては、IPベースのレート制限やリクエスト拒否を使う事が出来ます。また、&#8221;openid.realm&#8221;と&#8221;openid.return_to&#8221;を基にしたリクエスト拒否を考慮してもよいでしょう。</p>
<h3><a name="l31"><span class="sanchor"> </span></a>信頼性と評価</h3>
<p>OpenID認証は特別な中央機関を必要としない為、それぞれのRPやOPに関する信頼性が未知数です。現状では、一般に有名な信頼できるであろう企業が提供しているOPのみを許可するRPが多いようです。この問題について、色々と議論がされているようです。</p>
</p></div>
</p></div>
</div>
<div class="day">
<h2><span class="date"><a name="l32"> </a></span><span class="title">おまけ</span></h2>
<div class="body">
<div class="section">
<h3><a name="l33"><span class="sanchor"> </span></a>参照先</h3>
<ul>
<li><a href="http://openid.net/developers/specs/" class="external">OpenID &gt;&gt; Read the Specifications</a></li>
<li><a href="http://wiki.openid.net/Libraries" class="external">Libraries - OpenID Wiki</a></li>
<li><a href="http://yadis.org/wiki/Yadis_1.0_(HTML)" class="external">Yadis 1.0 (HTML) - Yadis</a></li>
<li><a href="http://www.atmarkit.co.jp/fsecurity/index/index_openid.html" class="external">OpenIDの仕様と技術 連載インデックス - ＠IT -</a></li>
</ul>
<h3><a name="l34"><span class="sanchor"> </span></a>OPとRPの一例</h3>
<h4><a name="l35"> </a>OP</h4>
<ul>
<li>Yahoo! JAPAN</li>
<li>はてな</li>
<li>Livedoor</li>
<li>Blogger</li>
</ul>
<h4><a name="l36"> </a>RP</h4>
<ul>
<li>Fastladder</li>
<li>はてな</li>
<li>LiveJournal</li>
</ul>
<h3><a name="l37"><span class="sanchor"> </span></a>日本での活動</h3>
<ul>
<li>アイデンティティ飲み会</li>
<li>Liberty Alliance 技術セミナー</li>
<li>openid-ja</li>
<li>ブログ記事</li>
</ul>
<h3><a name="l38"><span class="sanchor"> </span></a>手前味噌</h3>
<ul>
<li><a href="http://lab.koshigoe.jp/en2ja/" class="external">OpenID関連の和訳文書</a>
<ul>
<li>注) 訳を鵜呑みにしないでください</li>
</ul>
</li>
</ul></div>
</p></div>
</div>
<div class="day">
<h2><span class="date"><a name="l39"> </a></span><span class="title">最後に</span></h2>
<div class="body">
<div class="section">
<p>今回の発表では、概論にとどめる為に多くを省略しています。</p>
<ul>
<li>XRI</li>
<li>メッセージのフォーマットなど</li>
<li>手続きの詳細部分
<ul>
<li>通信種類(間接通信、直接通信)</li>
<li>署名</li>
<li>照合</li>
<li>ほか</li>
</ul>
</li>
<li>OpenID認証1.1との互換性</li>
<li>拡張</li>
<li>など</li>
</ul>
<p>今回の発表は、OpenIDの概要をつかんでもらいつつ、周辺の話題に触れてもらう事を目的としています。実際に、OpenID認証を導入しようとしたりする場合には、他の参考資料や仕様書原文をあたる必要があるでしょう。</p>
<p>繰り返しになりますが、個人的に以下の資料がおすすめです。</p>
<ul>
<li><a href="http://www.atmarkit.co.jp/fsecurity/index/index_openid.html" class="external">OpenIDの仕様と技術 連載インデックス - ＠IT -</a></li>
<li><a href="http://openid.net/specs/openid-authentication-2_0.html" class="external">Final: OpenID Authentication 2.0 - Final</a></li>
</ul>
<p>それでは、最後までおつきあいいただき、ありがとうございました。</p>
</p></div>
</p></div>
</div>]]></content:encoded>
  </item>
  <item rdf:about="http://feed.feedforce.jp/item_30880_722434_2892.html">
    <title>開発合宿レポート＠伊東温泉</title>
    <link>http://feed.feedforce.jp/item_30880_722434_2892.html</link>
    <dc:date>2007-11-01T19:44:51+09:00</dc:date>
    <description>10月28日から30日にかけて開発合宿に行ってきたfukunagaです。月・火お休みをもらって個人的に行ったわけですが、なぜかここにレポート書いてます。
場所は静岡の伊東温泉。開発合宿の旅館としてよく利用されている「山喜旅館」が舞台となりました。</description>
    <content:encoded><![CDATA[<p><img src="http://tech.feedforce.jp/wp-content/uploads/2007/11/itoh-onsen-2.jpg" alt="伊東温泉" /></p>
<p>10月28日から30日にかけて開発合宿に行ってきた<a href="http://blog.fkoji.com/" target="_blank">fukunaga</a>です。月・火お休みをもらって個人的に行ったわけですが、なぜかここにレポート書いてます。</p>
<p>場所は静岡の伊東温泉。開発合宿の旅館としてよく利用されている「<a href="http://www.ito-yamaki.co.jp/" target="_blank">山喜旅館</a>」が舞台となりました。</p>
<p>メンバーは8月末まで弊社で技術チームのリーダーをしていたakahige氏とその知り合いN氏と私の3人。季節外れの直射日光にやられつつも、N氏の愛車で渋滞にはまることなく旅館へ到着しました。</p>
<p><img src="http://tech.feedforce.jp/wp-content/uploads/2007/11/itoh-onsen-1.jpg" alt="伊東温泉" />
<p>↑ 10月末とは思えない天候とこの青空。<b>絶好の開発日和</b>ですねｗ</p>
<p>開発合宿のポイントに、「<b>行きの道中でアイデアを練る</b>」というのがあります。私は作るものを決めかねていたのですが、行きの昼食時の会話にふと出た“とある話題”から面白いアイデアがわきあがってきて、結局そのアイデアに乗っかることにしました。</p>
<p>そんな感じで旅館に着くなり開発開始。</p>
<p><img src="http://tech.feedforce.jp/wp-content/uploads/2007/11/itoh-onsen-3.jpg" alt="開発合宿" /></p>
<p>↑当然ながら全員デュアル・ディスプレイ。私はN氏から借りました。</p>
<p>ざっくりとしたスケジュール(by akahige)は以下のような感じ。</p>
<p>
初日<br />
17:00 進捗報告<br />
18:00 夕食</p>
<p>2日目<br />
08:00 朝食<br />
09:00 進捗報告<br />
12:00 昼食<br />
17:00 進捗報告</p>
<p>3日目<br />
08:00 朝食<br />
09:00 最終報告＆振り返り<br />
10:00 チェックアウト
</p>
<p>これ以外の時間は開発、休憩、睡眠、風呂を適当に。</p>
<p><img src="http://tech.feedforce.jp/wp-content/uploads/2007/11/itoh-onsen-4.jpg" alt="山喜旅館の食事" /></p>
<p>ということで、合宿の良かったことをいくつか。</p>
<ul>
<li><b>定期的に進捗報告</b>をおこなったことで、他の人の反応や意見をその都度聞けるのはよい。</li>
<li>日・月・火という日程だったので、他の客がほとんどおらず集中できた。</li>
<li>そういうわけで<b>風呂を独り占め</b>できた。</li>
<li>他の人が黙々と作業しているのはよい刺激。</li>
<li>疑問点が即解決。</li>
<li><b>座椅子作業</b>が快適。</li>
</ul>
<p>それとは逆に、次は気をつけたいこと、ちょっと不満だったことをいくつか。</p>
<ul>
<li>寝不足等で睡眠時間が長くなった。でも睡眠は大切かと。</li>
<li>10月なのに蚊がｗ</li>
<li>思ったより風呂がぬるかった。</li>
</ul>
<p>それ以外に、個人的にプライベートで開発をするときはあまりメモをとらないのですが、他の二人がアイデアや仕様をノートに書いていたので真似してみたら考えがまとまりやすかったです。</p>
<p>行きも帰りも渋滞にはまることなく、スケジュール通りに移動ができました。なによりこれはバイカーであるakahige氏が道を熟知していたおかげ。</p>
<p>日曜日の朝の富士山がとても素晴らしかったことが印象に残った開発合宿でした。写真撮り忘れましたけどね。</p>
<h4>お世話になりました</h4>
<p><a href="http://www.ito-yamaki.co.jp/" target="_blank">伊豆伊東温泉の格安旅館、新鮮な海鮮料理と24時間入浴の自家源泉、温泉旅館　山喜旅館</a></p>
<p>【関連エントリー】</p>
<ul>
<li><a href="http://tech.feedforce.jp/dev_camp.html">FFTT : はじめての開発合宿</a></li>
</ul>]]></content:encoded>
  </item>
</rdf:RDF>