<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
   <title>つくるぶガイドブログ</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/" />
   <link rel="self" type="application/atom+xml" href="http://www.tkrb.jp/guide/atom.xml" />
   <id>tag:www.tkrb.jp,2008:/guide//2</id>
   <updated>2008-03-31T05:32:25Z</updated>
   <subtitle>つくるぶガイドの7人がお届けする、Web開発にチョット役立つブログです。</subtitle>
   <generator uri="http://www.sixapart.com/movabletype/">Movable Type 3.35</generator>

<entry>
   <title>Adobe AIRでAjax！(その２) AIR APIを利用してWebとLocalを繋ぐマッシュアップ サンプル</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/03/mashup_sample_which_joins_web_and_local_together_using_air_api.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.66</id>
   
   <published>2008-03-31T05:30:00Z</published>
   <updated>2008-03-31T05:32:25Z</updated>
   
   <summary> こんにちは、JavaScript担当の（株）アークウェブの竹村です。 前回はA...</summary>
   <author>
      <name>竹村 光生</name>
      
   </author>
         <category term="JavaScript" scheme="http://www.sixapart.com/ns/types#category" />
   
   <category term="118" label="AIR API" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="105" label="Adobe AIR" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="127" label="CookieManager" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="124" label="GoogleMaps" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="120" label="SQLite" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="122" label="ホットペッパー" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="121" label="マッシュアップ" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="125" label="ローカルサーチ" scheme="http://www.sixapart.com/ns/types#tag" />
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[こんにちは、JavaScript担当の<a href="http://www.ark-web.jp/" target="_blank">（株）アークウェブ</a>の竹村です。

前回はAdobe AIRの制作環境構築と、ユーザに配布するまでの流れを説明しました。

<ul class="icon-list"><li><a href="http://www.tkrb.jp/guide/2008/03/just_shift_to_adobe_air_by_html_and_javascript.html" target="_blank">Adobe AIRでAjax！HTMLとJavaScriptでそのままAIRに移行できるサンプル</a></li></ul>

今回もマッシュアップサービスを利用しつつ、AIR APIをいくつか使ったサンプルを用意しました。

<h4 class="large-tit">ホットペッパーのマッシュアップサンプル『オヒルダ！』</h4>

ホットペッパーのAPIに「ランチ」というフラグがあるのに着目し、ランチを提供しているショップリストを表示するサンプルです。ショップ検索をする際に、予め自分の住所や
指定した緯度経度などを設定できるようにしています。

オヒルダ！の構成は↓このようになります。

<img alt="01organize.jpg" src="http://www.tkrb.jp/guide/2008/03/31/01organize.jpg" width="470" height="400" />

最初にWeb版を作ります。Web版には下記の機能を盛り込みます。

w-1. ホットペッパーAPIを通して検索結果一覧を表示する
w-2. 検索で使用する地域/ジャンルを設定できるようにする
w-3. 設定をAIRアプリ用にエクスポートできるようにする

AIR版には下記の機能を盛り込みます。

a-1. ホットペッパーAPIを通して検索結果一覧を表示する
a-2. Web版の設定をインポートできるようにする

今回利用するAIR APIは『ローカルファイル選択ダイアログの表示』と『ファイルの内容の読み取り』と『SQLiteへのアクセス』の 3点です。

完成版は↓こちらからアクセスできます。
<h5 class="small-tit">オヒルダ！ (Web版)</h5>
<a href="http://okra.ark-web.jp/~takemura/public/js/adobe_air/ohiruda/" target="_blank">
<img alt="02image.jpg" src="http://www.tkrb.jp/guide/2008/03/31/02image.jpg" width="470" height="548" />
</a>
<h5 class="small-tit">オヒルダ！ AIR版 インストーラー</h5>
<iframe src="http://okra.ark-web.jp/~takemura/public/js/adobe_air/ohiruda/installer/" width="470" height="240">
<a href="http://okra.ark-web.jp/~takemura/public/js/adobe_air/ohiruda/installer/" target="_blank">iframeをご覧になれない場合はこちらをどうぞ</a></iframe>


今回のレジュメは↓このようになっています。

<ul class="common-list">
<li>w-1. ホットペッパーAPIを通して検索結果一覧を表示する</li>
<li>w-2. 検索で使用する地域/ジャンルを設定できるようにする</li>
<li>w-2-1. GoogleMapsで住所から緯度/経度を取得する</li>
<li>w-2-2. GoogleMapsで円を描く</li>
<li>w-2-3. 設定内容を保存する</li>
<li>w-3. 設定をAIRアプリ用にエクスポートできるようにする</li>
<li>a-1. ホットペッパーAPIを通して検索結果一覧を表示する</li>
<li>a-2. Web版の設定をインポートできるようにする</li>
<li>a-2-1. ローカルファイル選択ダイアログを表示して選ばせる</li>
<li>a-2-2. ファイルを開いて内容を読み込む</li>
<li>a-2-3. SQLiteに設定を保存する</li>
<li>a. SQLiteから設定を読み込むには</li>
<li>オヒルダ！AIR版のソース一式ダウンロード</li>
<li>まとめ</li>
</ul>
]]>
      <![CDATA[<style type="text/css">
.attention {color:red; font-weight:bold;}
.prompt {color: #ffffff; background: black; border:2px dotted #eeeeee; padding:10px;}
</style>
<h4 class="large-tit">w-1. ホットペッパーAPIを通して検索結果一覧を表示する (リスト表示)</h4>

まずはWeb版の実装を行います。
ホットペッパーAPIはJSONPアクセスが可能なので、これを使いましょう。

<ul class="icon-list"><li><a href="http://webservice.recruit.co.jp/hotpepper/reference.html" target="_blank">ホットペッパー グルメサーチ APIリファレンス</a></li></ul>

JSONPアクセスは、いつものように JSONscriptRequest (jsr_class.js) を利用して表示しています。

<pre><p class="code">
&lt;script type="text/javascript" src="js/<span class="attention">jsr_class.js</span>"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="js/prototype.js"&gt;&lt;/script&gt;
(...)
&lt;script type="text/javascript"&gt;
// グローバル変数
var goJsr4Listing;      // JSONscriptRequestオブジェクトのインスタンス
var gsHotpepperAPIListingURL = 'http://webservice.recruit.co.jp/hotpepper/gourmet/v1/?key=[APIキー]';
(...)
function sendListing() {
  var aArea    = getCheckedValues(document.form.area_select);
  var sAddress = $F('address');
  var iLat     = $F('lat');
  var iLng     = $F('lng');
  var iRange   = $F('range');
  var iLunch   = $F('lunch');
  var aGenre   = getCheckedValues(document.form.genre);
  
  var sUrl = gsHotpepperAPIListingURL;
  sUrl    += (aArea[0] == 'address' ? <span class="attention">'&address='+ sAddress</span> :
    <span class="attention">'&lat='+ iLat +'&lng='+ iLng) + '&range=' +iRange</span>;
  sUrl    += <span class="attention">'&format=jsonp&callback=listingCallback&count=10'</span>;
  sUrl    += (iLunch == 1 ? <span class="attention">'&lunch=1'</span> : '');
  sUrl    += (aGenre.length &gt; 0 ? <span class="attention">'&genre='+ aGenre.join(',')</span> : '');
  
  // script タグの発行
  goJsr4Listing = new JSONscriptRequest(sUrl);
  goJsr4Listing.buildScriptTag();
  goJsr4Listing.addScriptTag();
(...)
}
function listingCallback(<span class="attention">oJson</span>) {
  // script タグの削除
  goJsr4Listing.removeScriptTag();
  
  // テンプレートから内容を生成して出力する
  <span class="attention">var aItem   = oJson['results']['shop'];</span>
  if (aItem.length &gt; 0) {
    <span class="attention">makeContents</span>($('listing'), 'listing', aItem);
  } else {
    $('listing').innerHTML = '1件もありませんでした。。';
  }
}
// === 共通関数 =====
function <span class="attention">makeContents</span>(oOutput, sPath, aItem) {
  new Ajax.Request(
    getTemplate(sPath),
    {onComplete:function(oResponse){ <span class="attention">makeContentsCallback</span>(oOutput, aItem, oResponse); }}
  );
}
function getTemplate(sPath) {
  switch (sPath) {
    case 'listing':
    return 'templates/listing.html';
    default:
    alert('sPath未指定 by getTemplate');
  }
}
function <span class="attention">makeContentsCallback</span>(oOutput, aItem, oResponse) {
  var sTemplate = oResponse.responseText;
  var sContents = '';
  for (var i = 0 ; i &lt; aItem.length ; i++) {
    <span class="attention">sContents += sTemplate.interpolate(aItem[i]);</span>
  }
  <span class="attention">oOutput.innerHTML = sContents;</span>
}
(...)
&lt;div id="listing-area"&gt;
&lt;p&gt;■一覧表示&lt;/p&gt;
&lt;div id="listing"&gt;&lt;/div&gt;
&lt;/div&gt;
</p></pre>

sUrlに渡しているパラメータは直前にフォームからデータを取得してます。

JSONPで戻ってくる listingCallback関数データが1件でもあれば、内容を表示します。内容の表示方法は、前回利用したAjax.Requestを使って外部のテンプレートファイルを読み込み、内容をinterpolateメソッドで埋め込んで出力しています。

この辺は、マッシュアップの要領を得ていれば結構楽に実装できると思います。


ちなみに、一覧で出しているGoogleMapsの地図は動きません。これはGoogleMapsの『staticmap』を利用しています。

<ul class="icon-list"><li><a href="http://googlejapan.blogspot.com/2008/03/google_1433.html" target="_blank">Google Japan Blog: Google マップが携帯でも表示できるようになりました</a></li></ul>


<h4 class="large-tit">w-2. 検索で使用する地域/ジャンルを設定できるようにする</h4>

Web版の「設定フォーム」ページを作成します。
このページのポイントは 3つあります。

<ul class="common-list">
<li>w-2-1. GoogleMapsで住所から緯度/経度を取得する</li>
<li>w-2-2. GoogleMapsで円を描く</li>
<li>w-2-3. 設定内容を保存する</li>
</ul>

<h4 class="large-tit">w-2-1. GoogleMapsで住所から緯度/経度を取得する</h4>

こちらはローカルサーチという機能を利用します。早速具体的な使い方を説明します。

<pre><p class="code">
&lt;script type="text/javascript" src="http://www.google.com/jsapi?key=[APIキー]"&gt;&lt;/script&gt;
&lt;script src="<span class="attention">http://www.google.com/uds/api?file=uds.js&amp;v=0.1&amp;key=[APIキー]</span>" type="text/javascript" charset="utf-8"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
(...)
// === マップ =====
// 大まかな住所からGlobalSearchでlatlngを取得してマップを描画する
function moveTo(<span class="attention">sPlace</span>) {
  <span class="attention">gls = new GlocalSearch();</span>
  
  gls.setSearchCompleteCallback(null, <span class="attention">moveToPlaceCallback</span>);
  gls.setCenterPoint(map);
  gls.execute(<span class="attention">sPlace</span>);
}
function <span class="attention">moveToPlaceCallback</span>() {
  if ( !gls.results || gls.results.length == 0 ) {
    alert('指定された住所は見つかりませんでした');
  } else {
    <span class="attention">var oGSearchResult = gls.results[0];</span>
    drawMap(<span class="attention">new GLatLng(oGSearchResult.lat, oGSearchResult.lng)</span>);
  }
}
(...)
// マップを描画する
function drawMap(<span class="attention">oGLatLng</span>) {
  map.clearOverlays();
  var iRange = $F('range');
  map.setCenter(<span class="attention">oGLatLng</span>, goRange2data[iRange]['size']);
  $('lat').value = oGLatLng.lat();
  $('lng').value = oGLatLng.lng();
  addPolygonToMap(oGLatLng, goRange2data[iRange]['radius']);
}
</p></pre>

moveTo関数の引数である sPlace には、フォームで入力された「マップの大まかな住所」が入ります。GolobalSearchクラスのインスタンスのexecuteにそのsPlaceを渡して検索するんですが、その前にイベントハンドラの設定をしています。

イベントハンドラの moveToPlaceCallback関数ではローカルサーチで得た緯度/経度の1番目を採用してマップ描画用関数に渡しています。

マップ描画用の drawMap関数では、map.setCenter でマップの位置調整をしています。

<h4 class="large-tit">w-2-2. GoogleMapsで円を描く</h4>

GoogleMaps上に赤い円を描いている部分は、GPolygonクラスを利用しています。具体的な指定方法は下記のようにしています。

<pre><p class="code">
// マップを描画する
function <span class="attention">drawMap</span>(oGLatLng) {
  <span class="attention">map.clearOverlays();</span>
  var iRange = $F('range');
  map.setCenter(oGLatLng, goRange2data[iRange]['size']);
  $('lat').value = oGLatLng.lat();
  $('lng').value = oGLatLng.lng();
  <span class="attention">addPolygonToMap(oGLatLng, goRange2data[iRange]['radius']);</span>
}
/**
 * 下記のaddPolygonToMap関数、createCircle関数は、
 * http://www.ajaxtower.jp/googlemaps/gpolygon/index6.html を
 * 利用させていただきました.
 */
function <span class="attention">addPolygonToMap</span>(point, radius){
  var latlngs = <span class="attention">createCircle</span>(point, radius, 32);
  
  var polygon = <span class="attention">new GPolygon</span>(latlngs, "#ff0000", 5, 0.5, "#0000ff", 0.1);
  <span class="attention">map.addOverlay(polygon);</span>
}
function <span class="attention">createCircle</span>(latlng, radius, level){
  var point = map.fromLatLngToDivPixel(latlng);
  
  var latlngbounds = map.getBounds();
  var southwest = latlngbounds.getSouthWest();
  var northeast = latlngbounds.getNorthEast();
  var nelat = northeast.lat();
  var swlng = southwest.lng();
  var lefttop = map.fromLatLngToDivPixel(new GLatLng(nelat, swlng));
  
  var x = point.x - lefttop.x;
  var y = point.y - lefttop.y;
  
  var latlngs = [];
  for (var i = 0 ; i &lt; level ; i++){
    var px = x + radius * Math.cos(i * Math.PI/(level/ 2));
    var py = y + radius * Math.sin(i * Math.PI/(level/ 2));
    var latlng = map.fromContainerPixelToLatLng(new GPoint(px, py));
    latlngs.push(latlng);
  }
  latlngs.push(map.fromContainerPixelToLatLng(new GPoint(x + radius, y)));
  return latlngs;
}
</p></pre>

上記で軽く触れた drawMap関数の addPolygonToMap関数で円を描く部分を実装しています。

こちらは下記のサイトのコードを参考にさせていただきました。

<ul class="icon-list"><li><a href="http://www.ajaxtower.jp/googlemaps/gpolygon/index6.html" target="_blank">円の描画</a></li></ul>

円の計算は、createCircle関数で行っています。これは元サイトのままです。createCircleではLatLngのリストを返しています。

addPolygonToMap関数の GPolygonクラスでLatLngのリストを渡して map.addOverlayで描画します。なお、再描画する際は map.clearOverlays(); を実行しておくことで以前の円を消してから新しい円を描画できます。


<h4 class="large-tit">w-2-3. 設定内容を保存する</h4>

ページ最下部の[設定する]ボタンをクリックした時の設定内容を保存する処理は、Web版はCookieを利用します。

Cookie管理用に CookieManager というライブラリを利用しています。

<ul class="icon-list"><li><a href="http://insin.woaf.net/code/javascript/cookiemanager.html" target="_blank">Cookie Manager</a></li></ul>

<pre><p class="code">
&lt;script type="text/javascript" src="js/<span class="attention">cookiemanager.js</span>"&gt;&lt;/script&gt;
(...)
// === 保存 =====
function checkAndSave() {
(...)
  // データのセーブ
  var aSettings = makeSaveSetting();
  <span class="attention">saveSetting</span>(aSettings);
  
  alert("Cookieに保存しました。\nトップからアクセスしてみてください。");
}
function <span class="attention">saveSetting</span>(aSettings) {
  // AIRかどうか判定
  <span class="attention">if (!window.runtime)</span> {
    <span class="attention">return saveSetting4web</span>(aSettings);
  } else {
    return saveSetting4air(aSettings);
  }
}
function <span class="attention">saveSetting4web</span>(aSettings) {
  var oCookieManager = <span class="attention">new CookieManager({shelfLife:30});</span>
  <span class="attention">oCookieManager.setCookie</span>("address", encodeURI(aSettings['address']));
  oCookieManager.setCookie("lat",     aSettings['lat']);
  oCookieManager.setCookie("lng",     aSettings['lng']);
  oCookieManager.setCookie("range",   aSettings['range']);
  oCookieManager.setCookie("genre1",  aSettings['genre1']);
  oCookieManager.setCookie("genre2",  aSettings['genre2']);
  oCookieManager.setCookie("genre3",  aSettings['genre3']);
}
</p></pre>

saveSetting関数の if (!window.runtime) はAIRかどうかの判定をする際に利用できます。

肝心のCookieManagerクラスは saveSetting4web関数にあります。引数のshelfLifeは有効期限の日付指定です。Cookieの設定は setCookie、取得は getCookieです。


<h4 class="large-tit">w-3. 設定をAIRアプリ用にエクスポートできるようにする</h4>

[エクスポート]ボタンをクリックするとダウンロードダイアログが表示されます。
この部分はPHPで動作させています。

<pre><p class="code">
&lt;?php
/**
 * ohirudaのCookieの内容をダウンロードさせる.
 */
if ($_POST['lat'] == '') { echo "Not a setting data.."; exit; }
$sSetting  = sprintf('ohiruda/%s,%s/%s/%s,%s,%s/%s/', 
        $_POST['lat'],
        $_POST['lng'],
        $_POST['range'],
        $_POST['genre1'],
        $_POST['genre2'],
        $_POST['genre3'],
        $_POST['address']
      );
header("Content-type: text/plain");
header('Content-disposition: attachment; filename=export4air.txt');
header("Content-length: " . strlen($sSetting));

echo $sSetting;

?&gt;
</p></pre>


<h4 class="large-tit">a-1. ホットペッパーAPIを通して検索結果一覧を表示する</h4>

では、いよいよAIR版の作業に移ります。

まずはWeb版のHTMLをそのままコピーします。つくるぶの前回の記事に書きましたが、基本的に AIRアプリはWebと同様にHTML＋JavaScriptで動作させることが可能です。よって、オヒルダWeb版のコピーがそのままAIRで動くように思うのですが、ここに落とし穴があります。

AIRではJSONPを実行するためのJSONscriptRequestが利用できません。つまり、scriptタグを動的に挿入できないようになっているようです。対応策としては、普通にprototype.jsの Ajax.Request を使用してホットペッパーAPIにアクセスします。

<pre><p class="code">
// === リスト表示 =====
function sendListing() {
(...)
  var sUrl = gsHotpepperAPIListingURL;
  sUrl    += (aArea[0] == 'address' ? '&address='+ sAddress : '&lat='+ iLat +'&lng='+ iLng) + '&range=' +iRange;
// -&gt; modify for AIR
//  sUrl    += '&format=jsonp&callback=listingCallback&count=10';
  sUrl    += '&format=<span class="attention">json</span>&count=10';
// &lt;- modify for AIR
  sUrl    += (iLunch == 1 ? '&lunch=1' : '');
  sUrl    += (aGenre.length &gt; 0 ? '&genre='+ aGenre.join(',') : '');
  
// -&gt; modify for AIR
/*  // script タグの発行
  goJsr4Listing = new JSONscriptRequest(sUrl);
  goJsr4Listing.buildScriptTag();
  goJsr4Listing.addScriptTag();
*/
  <span class="attention">new Ajax.Request(</span>
  <span class="attention">  sUrl,</span>
  <span class="attention">  {method:'get',onComplete:listingCallback}</span>
  <span class="attention">);</span>
// &lt;- modify for AIR
  
  // Nowloading
  $('listing').innerHTML = 'Nowloading...';
}
function listingCallback(oJson) {
// -&gt; modify for AIR
/*  // script タグの削除
  goJsr4Listing.removeScriptTag();
*/
  <span class="attention">var oJson = eval('('+ oJson.responseText +')');</span>
// &lt;- modify for AIR
  (...)
</p></pre>

AIRにはクロスドメインの制約がないので、外部ドメインへのアクセスが可能です。
ホットペッパーのAPIから JSON を取得するように変えて、読み込み完了のイベントハンドラ listingCallback関数でJSONをevalしています。


<h4 class="large-tit">a-2. Web版の設定をインポートできるようにする</h4>

ついにAIR APIの登場です。
インポートには 3つのポイントがあります。

<ul class="common-list">
<li>a-2-1. ローカルファイル選択ダイアログを表示して選ばせる</li>
<li>a-2-2. ファイルを開いて内容を読み込む</li>
<li>a-2-3. SQLiteに設定を保存する</li>
</ul>


<h4 class="large-tit">a-2-1. ローカルファイル選択ダイアログを表示して選ばせる</h4>

[インポート]ボタンをクリックした時にダイアログを表示して、ユーザにファイルを指定してもらうには下記のようにします。

<pre><p class="code">
&lt;script type="text/javascript" src="js/<span class="attention">AIRAliases.js</span>"&gt;&lt;/script&gt;
(...)
// インポートファイル指定ダイアログ表示
function openImportDialog() {
  try {
    var oFile = <span class="attention">air.File.desktopDirectory;</span>
    var oFileFillter = <span class="attention">new air.FileFilter("テキスト", "*.txt");</span>
    <span class="attention">oFile.addEventListener(air.Event.SELECT, readedCallback);</span>
    <span class="attention">oFile.browseForOpen("開く", [oFileFillter]);</span>
  } catch (event) {
    alert(event.toString());
  }
}
function <span class="attention">readedCallback</span>(oEvent) {
(...)
</p></pre>

まず、HTML＋JavaScriptでAIR APIを利用するときは AIRAliases.js をインクルードしておくとかなり見やすくなります (FlexやAS3で組むコードと大差ないほどに)
例えば、File へのアクセスについていえば、↓これくらい楽です。

本来の書き方：var oFile = window.runtime.flash.filesystem.File.desctopDirectory;
AIRAliases　：var oFile = air.File.desctopDirectory;

それで、ダイアログ表示は Fileクラスの browseForOpenメソッドを実行すれば開きます。その前に air.File.desktopDirectory で開く場所を指定しています。

desktopDirectoryはユーザのデスクトップ、つまりWindowsであれば↓こちらのフォルダを指しています。

  C:\Documents and Settings\[ユーザ名]\デスクトップ

あとは、FileFilterクラスで *.txt のみダイアログ中に表示するように指定しています。また、ダイアログからファイルが決定された時の動作を addEventListenerで readedCallback関数をイベントハンドラとして登録しています。


<h4 class="large-tit">a-2-2. ファイルを開いて内容を読み込む</h4>

ローカルファイル選択ダイアログをでユーザがファイルを指定した時のイベントハンドラ readedCallback関数で、ファイルの内容を取得しています。

<pre><p class="code">
function <span class="attention">readedCallback</span>(oEvent) {
  try {
    var oFile = oEvent.target;
    var oFileStream = <span class="attention">new air.FileStream();</span>
    
    <span class="attention">oFileStream.open(oFile, air.FileMode.READ);</span>
    <span class="attention">var sReadData = oFileStream.readMultiByte(oFile.size, "utf-8");</span>
    <span class="attention">oFileStream.close();</span>
  } catch (event) {
    alert(event.toString());
  }
  
  return saveImportData(sReadData);
}
</p></pre>

そのファイルの内容にアクセスするには、FileStreamクラスを利用します。openして、readMultiByteメソッドで内容を読み込み、closeしています。


<h4 class="large-tit">a-2-3. SQLiteに設定を保存する</h4>

最後に読み込んだ内容をSQLiteに保存します。

<pre><p class="code">
var db = null;
// データの初期化
function <span class="attention">initSetting4air</span>() {
  try {
    // DBに接続
    db = <span class="attention">new air.SQLConnection();</span>
    var file = <span class="attention">air.File.applicationStorageDirectory.resolvePath("ohiruda.db");</span>
    <span class="attention">db.open(file);</span>
    
    // テーブルを用意 (なければ作る)
    var oSQLStatement = <span class="attention">new air.SQLStatement();</span>
    <span class="attention">oSQLStatement.sqlConnection = db;</span>
    <span class="attention">oSQLStatement.text</span>  = 
      "<span class="attention">CREATE TABLE IF NOT EXISTS setting</span>" +
      "(address TEXT, lat REAL, lng REAL, range INTEGER," +
      " genre1 TEXT, genre2 TEXT, genre3 TEXT);";
    <span class="attention">oSQLStatement.execute();</span>
  } catch(error) {
    alert(error.toString());
  }
}
(...)
// データ書き込み (DBを使います)
function <span class="attention">saveSetting4air</span>(aSettings) {
  try {
    // 一旦設定を削除して、登録する
    oSQLStatement.text = "DELETE FROM setting";
    oSQLStatement.execute();
    <span class="attention">oSQLStatement.text = "INSERT INTO setting </span>VALUES (".interpolate(aSettings) +
      "'#{address}','#{lat}','#{lng}','#{range}',".interpolate(aSettings) +
      "'#{genre1}','#{genre2}','#{genre3}');".interpolate(aSettings);
    oSQLStatement.execute();
  } catch(error) {
    alert(error.toString());
  }
}
</p></pre>

SQLiteはファイルにDBの内容を書き込むので、Fileクラスで書き込む先を指定しています。

applicationStorageDirectoryはWindowsであれば↓こちらのフォルダを指しています。

  C:\Documents and Settings\[ユーザ名]\Application Data\[ID値]\Local Store\
  ※ID値はアプリケーション定義ファイルに記述したidタグの値です。

FireFoxのエクステンションを利用するとSQLiteの内容を確認できます。

<ul class="icon-list"><li><a href="https://addons.mozilla.org/ja/firefox/addon/5817" target="_blank">SQLite Manager</a></li></ul>

FireFoxから、↑こちらのエクステンションをインストールして、ツールから「SQLite Manager」を開き、「Connect Database」から上記ディレクトリにあるohiruda.dbを選ぶと内容が確認できます。

<img alt="03sqlitemanager.jpg" src="http://www.tkrb.jp/guide/2008/03/31/03sqlitemanager.jpg" width="470" height="321" />

インポートすると、上記のようなデータが 1件だけ表示されるはずです。
※英語版なので日本語でインポートした部分が文字化けしています。


DBへの接続は SQLConnectionクラスの openメソッドでできます。openメソッドは同期モードですので、DBへ接続できるまで処理が止まります。この間、ユーザは何もできません。砂時計アイコンで出ます。
(実際はローカルのファイルへのアクセスなので砂時計アイコンが出る間もなく接続できます)

一方、openAsyncメソッドという非同期モードも用意されています。こちらはDB接続中でもユーザは他の操作ができます。ただ、非同期モードはイベントハンドラを設定して内容の取得を行うので、ちょっと面倒です。

openAsyncの使いどころは、DBのサイズが結構大きくなった時や、複雑な検索をかける時になると思います。

テーブルの操作は、SQLStatementクラスのexecuteメソッドで行います。
その前に oSQLStatement.text にSQL文を入れておく必要があります。また、 oSQLStatement.sqlConnectionに接続したSQLConnectionクラスのインスタンスを入れておかないと下記のエラーが表示されてしまいます。

Error: Error #3109: Operation is not permitted when the SQLStatement.sqlConnection property is not set.

初期化処理中に、DBへの接続と「CREATE TABLE IF NOT EXISTS setting」でテーブルがなかったら用意するSQLを実行しておきます。

saveSetting4air関数で、settingテーブルの内容を一旦削除して、「INSERT INTO setting」でインポートによって取得した内容を書き込んでいます。

※英語版なので日本語が文字化けします。


<h4 class="large-tit">a. SQLiteから設定を読み込むには</h4>

データを読み込む時は、initSetting4air関数を実行した後に下記のように取得します。

<pre><p class="code">
// データ読み込み (DBを使います)
function loadSetting4air() {
  try {
    // 設定を読み込む
    oSQLStatement = new air.SQLStatement();
    oSQLStatement.sqlConnection = db;
    
    oSQLStatement.text = "<span class="attention">SELECT COUNT(*) as cnt FROM setting</span>";
    oSQLStatement.execute();
    var oSQLResult = <span class="attention">oSQLStatement.getResult();</span>
    var iCount = <span class="attention">oSQLResult.data[0]['cnt'];</span>
    
    oSQLStatement.text = "<span class="attention">SELECT * FROM setting</span>";
    oSQLStatement.execute();
    
    // 読み込んだ内容の取得
    var oSQLResult = <span class="attention">oSQLStatement.getResult();</span>
  } catch(error) {
    alert(error.toString());
  }
</p></pre>

データの件数と、その内容を取得するのに2回SQL文を発行して、内容を取得しました。内容の取得は SQLStatementクラスの getResultメソッドで取得できます。


あとは、AIR版のトップも同じようにSQLiteから取得したデータをもとにホットペッパーのAPIにアクセスするパラメータとして利用しています。


<p class="url">= ちょっとTips =
トップの一覧のGoogleMaps staticmapのアクセス先がAIR版では http://okra.ark-web.jp/~takemura/... になっています。これはGoogleMapsのAPIキーにAPIを利用するドメインの指定をする必要があるため、AIRでは直接利用できないようだったので、その回避策です。
</p>


<h4 class="large-tit">オヒルダ！AIR版のソース一式ダウンロード</h4>

AIR版のソースを一式下記にまとめました。

<ul class="icon-list"><li><a href="http://okra.ark-web.jp/~takemura/public/js/adobe_air/ohiruda/download/ohiruda.zip">オヒルダ！AIR版のソース一式</a></li></ul>


<h4 class="large-tit">まとめ</h4>

今回はHTML＋JavaScriptでAIR APIを利用したサンプルを作ってみましたが、AIR API自体は大して難しいことはないと思います。

ただ、GoogleMapsがAIR上で利用できなかったり、AIR独自の挙動が見られたり、AIR APIを含むデバッグがFireBugsでできなかったりと、ちょっと開発しづらい感じはありました。

でも、ActionScript3やFlexを一から覚えるよりは、今までの知識をそのまま利用できるHTML＋JavaScriptの開発は敷居が低いと思います。

Adobe AIR 1.0が出たばかりですから、これからの開発経験が後々に生きてくるでしょうし、GoogleMaps等の外部API連携もそのうちできるようになるかもしれません。


あと、AdobeはAIRの次世代リーダーを発掘するために、下記のようなコンテストを行うようです。

<ul class="icon-list"><li><a href="http://www.adobe.com/jp/special/air/contest/" target="_blank">Adobe - Adobe AIR コンテスト (エアコン) 開催！ ～デスクトップを制するものは、未来も制す～</a></li></ul>

WebとLocalを巻き込んだAIRアプリを作って、参加されてみてはいかがでしょうか?
]]>
   </content>
</entry>
<entry>
   <title>失敗しない Rails が動かせるホスティングサービス選びと環境構築</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/03/_rails.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.65</id>
   
   <published>2008-03-19T03:35:00Z</published>
   <updated>2008-03-20T10:16:32Z</updated>
   
   <summary> Ruby（とRails）を担当している石原です。 ソーシャル「OSを入れた後に...</summary>
   <author>
      <name>石原 淳也</name>
      
   </author>
         <category term="Ruby" scheme="http://www.sixapart.com/ns/types#category" />
   
   <category term="50" label="Rails" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="109" label="ホスティング" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="110" label="環境構築" scheme="http://www.sixapart.com/ns/types#tag" />
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[Ruby（とRails）を担当している石原です。

ソーシャル「OSを入れた後にインストールする10のアプリケーション」(仮) を作る過程をレポートしてきましたが、10回目の今回をもって終わりとさせていただきます。

最後は、いよいよリリースしようという段階で必要な、ホスティングサービス選びと環境構築について書きたいと思います。

参考までにこれまでのエントリーはこちらです ↓

<ol>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/10/ruby_on_rails_1.html">つくるぶガイドブログ: Ruby on Rails を使ってひとりでサービスを作ってみよう</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/11/post_2.html">つくるぶガイドブログ: ひとりサービスの雛型をつくる(リキッドレイアウト、GetText、Acts as Authenticated)</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/11/scriptgen.html">つくるぶガイドブログ: Rails で楽々ソーシャルブックマークの仕組みを作る</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/12/rails_acts_as_taggable_redux.html">つくるぶガイドブログ: Rails プラグイン acts_as_taggable_redux でタグクラウドを作ろう</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/01/rails_ajax.html">つくるぶガイドブログ: ドラッグアンドドロップで並べ替え(Rails + Ajax)</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/01/rails_rcov.html">つくるぶガイドブログ: Rails + rcov でテストカバレッジを調べる</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/02/capistrano_rails.html">つくるぶガイドブログ: capistrano で本番環境にデプロイ</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/02/rails_yahoo_api_widgets_tabnav.html">つくるぶガイドブログ: Rails で Yahoo 検索 API + Widgets Tabnav でタブナビゲーション</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/03/14.html">つくるぶガイドブログ: ひとりサービスをリリースするまでやっておくこと10個</a></li>
</ol>
]]>
      <![CDATA[<h4 class="large-tit">いろいろあるホスティングの形態</h4>

ホスティングサービスと一口に言っても様々な形態があります。

自分のブログやホームページを持ちたいという人向けに提供される、一台のマシンを複数ユーザーで使用するいわゆる「レンタルサーバー」が最も安価で手軽なのですが、Ruby on Rails を利用できるところはまだまだ少ないのが現状です。

Ruby や Rails をシステムにインストールできる root 権限を持っていることが<span style="color:red;"<s>必要で</s>望ましく</span>、こうなると専用サーバーか、あるいは共用サーバー（一台を複数の利用者で使うのですが、仮想的に分割されていて、あたかも専用サーバーのように使えるサーバー）か、ということになります。

<div style="color:red;font-size:11px;">
<b>2008/03/20 追記</b>
&raquo; <a target="_blank" href="http://d.hatena.ne.jp/rx7/20080320/p3">Re: 失敗しない Rails が動かせるホスティングサービス選びと環境構築 - RX-7乗りの適当な日々</a>

で指摘いただいたため訂正しました。

Ruby on Rails を使用するためには、厳密には root 権限は必要ありません。root 権限なしでレンタルサーバー上で動かす方法はいくつかありますが、その場合、様々な制限が伴うことを覚悟しなくてはなりません。PHP や Perl などと比べると、Ruby on Rails をレンタルサーバー上で動かすことは難易度が高いと筆者は考えており、やはり root 権限を持てる環境にホスティングすることを推奨します。
</div>


とりあえず Ruby on Rails でサービスを構築してみたい、という方には共用サーバーをおすすめします。

レンタルサーバーよりは高いですが、専用サーバーほど高価ではありません。安いところだと、月額2000円台といったところです。

占有しているように見えるとはいえ、複数利用者で使っていることに変わりはないので、専用サーバーに比べてスペックは落ちますが、サービスがはやって利用者が増えたときに専用サーバーに乗り換えればいいと考えればいいかと思います。

英語に問題なければ、海外のホスティングサービスという選択肢もあります。

同スペックならば、海外のサービスの方が安価です。またドキュメントやコミュニティが日本のサービスよりも充実している印象を受けます。

<h4 class="large-tit">ホスティングサービス、どこを選ぶか？</h4>

具体的にどこがおすすめかという質問を受けた場合、共用サーバーならば海外の <a href="http://slicehost.com">Slicehost</a>、専用サーバーなら<a href="http://www.sakura.ne.jp/">さくらインターネット</a>、と筆者は答えます。

理由は簡単で、どちらも筆者が実際に使ってきた中で気にいっているサービスだからです。ほかのあまたあるサービスについては、ネットにたくさんの情報をみつけることができますが、実際に使ってみないと何とも言えません。

ホスティングサービスを選ぶ際に最もおすすめする方法は、誰かすでにホスティングサービスを使っている知り合いに聞いてみて、その人がそのサービスを今後も使い続ける予定ならば、同じサービスにすることです。

値段がちょっとぐらい安いからと言って、まわりでは誰も使っていないようなサービスに契約するのはそれ相応のリスクがあります。

その知人のサポートというのがかなり大きなウェイトをもって、サービス選択の際に上乗せすべき要素になると思います。

<h4 class="large-tit">OSはできるだけ新しいバージョンのものを</h4>

「知人に聞け」と書きましたが、Ruby on Rails を動かすならではの選択ポイントとうのがありますし、せっかくですから筆者の経験から参考になりそうなことを述べさせていただきます。

できるだけ新しいバージョンのOSをサポートしているホスティングサービスを選ぶのが肝要です。少し古いバージョンのOSしかサポートしていないホスティングサービスの場合は、環境構築で苦労するかもしれないことを覚悟しておく必要があります。

CentOS なら yum、ubuntu なら apt-get といったコマンド一発でパッケージをインストールできるパッケージ管理ツールがあります。古い OS だとそれでインストールできるパッケージが古いバージョンであることが多く、新しいバージョンが必要だと、ソースをダウンロード&コンパイルしてインストールしなければならなくなります。

具体例を挙げます。Ruby on Rails でサービスを運用するとき、<a target="_blank" href="http://www.tkrb.jp/guide/2008/02/capistrano_rails.html">つくるぶガイドブログ: capistrano で本番環境にデプロイ</a>でも触れましたが、Apache + Mongrel Cluster が定番の一つです。このとき使う Apache の mod_proxy_balancer は Apache 2.2 から使える機能なので、2.2 以上が yum や apt-get その他で簡単にインストールできる（あるいははじめから用意されている）バージョンのOSを選んでおくと楽だと思います。

<h4 class="large-tit">環境構築は最初はオールクリアするぐらいのつもりで</h4>

筆者が使っている <a href="http://slicehost.com">Slicehost</a> には、OS を一度まっさらに戻し、OSの種類やバージョンを用意されているものの中から簡単に選択し直すことができる機能が管理メニューに付いています。

環境構築を始めてみたはいいが、途中でぐちゃぐちゃになりわけがわからなくなってしまったり、パッケージの依存関係などの問題で別の OS あるいはバージョンに切り替えたくなることがあります。

そういうときに上記のようなオールクリアできる機能があると便利です。それもメニューから自分で操作でき、気軽に何度もできるようになっていたほうがいいでしょう。

一回で完璧にやるのではなく、最初は「間違えてもいいや」と、練習のつもりで環境構築をするのがいいと思います。一回流れをつかみ、気をつけるべきポイントを自分なりにまとめておけば、次からはもっとうまく構築できると思います。

<h4 class="large-tit">環境構築の情報源</h4>

具体的にどう環境構築するかというノウハウは OS やバージョン、やりたいことなどによって千差万別ですから他の情報源をあたってください。

OSの名前、バージョンと「インストール」というキーワードで検索すれば様々な情報を見つけることができます。ただ、こうして寄せ集めてきた情報には、パッケージの依存性や一貫性、構築のポリシーがないので適宜読み替えたり自分で補足する必要があります。

「Apache を Ubuntu にインストールする方法」という記事よりも、「Ruby + Rails + MySQL + Apache 2.2 + Mongrel を Ubuntu Gutsy にインストールする方法」という記事の方が参考になることが多いです。

ホスティングサービス自体がこうしたドキュメントを用意している場合は大いに参考になります。例えば Slicehost の場合は、

&raquo; <a target="_blank" href="http://articles.slicehost.com/">Slicehost Article Repository - VPS setup, servers, Ruby on Rails, Django, PHP, DNS, Slicemanager and more</a>

に各 OS ごとの Ruby on Rails、Django、PHP などの構築方法を説明したドキュメントが用意されています。

こうしたドキュメントを参考にしながらも、自分なりの方法というのが出てくるかと思います。オールクリアするつもりで、と書きましたが、その場合も含めもう一度環境構築するときに備えて手順を Wiki などに残しておくことをおすすめします。ブログに公開する、というのもいいでしょう。

<h4 class="large-tit">セキュリティ対策</h4>

自分のサーバーを持った場合、誰かに侵入されないかという心配がついてきます。最低限のセキュリティ対策をまずおこなっておきましょう。

iptables をファイアウォールとして使い、使用するサービスのポートだけにアクセスできるようにしておきます。ssh 接続に関しては、ポート番号を 22 から変更し、使用するユーザーだけアクセスできるようにしておいた方がいいようです。

参考：<a target="_blank" href="http://articles.slicehost.com/2007/9/4/ubuntu-lts-setup-page-1">Slicehost Articles: Ubuntu LTS setup - page 1</a>

<h4 class="large-tit">いつも同じアカウント名を</h4>

環境構築で真っ先におこなうことは、一般ユーザーの作成だと思います。このとき、いつも同じユーザー名で作成しておくと便利です。たとえば ssh 接続するときに

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

ssh username@example.com

</pre>

の代わりに

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

ssh example.com

</pre>

で済みます。

筆者の場合、ローカルのマシンや個人的に使っているサーバーはもしかしたら家族で使うかもしれないと思って名前をユーザー名にし、仕事で使うものは名字をユーザー名にしてしまったのが失敗です。

<h4 class="large-tit">まとめ</h4>

Rails が動かせるホスティングサービス選びと環境構築について、筆者の経験で参考になりそうな Tips を紹介しました。

ソーシャル「OSを入れた後にインストールする10のアプリケーション」(仮) の開発レポートは今回をもって終了です。

今現在サービスを作っている方、あるいはこれからサービスを作ろうと思っている方の参考に少しでもなれたならばうれしいです。

ソースコードを以下に置いておきます。

&raquo <a href="http://code.google.com/p/10best/">10best - Google Code
</a>

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

svn checkout http://10best.googlecode.com/svn/trunk/ 10best

</pre>

でチェックアウトするか、ブラウザでも<a href="http://10best.googlecode.com/svn/trunk/">リポジトリ</a>を閲覧することができます。 ]]>
   </content>
</entry>
<entry>
   <title>JiftyでWebアプリをつくる - JiftyからMVCを考える</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/03/jiftyweb_jiftymvc_1.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.64</id>
   
   <published>2008-03-18T01:18:28Z</published>
   <updated>2008-03-19T18:00:42Z</updated>
   
   <summary> お久しぶりです、Perl担当の西山です。 前回に続きDRY(Don&apos;t Rep...</summary>
   <author>
      <name>春田・西山</name>
      
   </author>
         <category term="Perl" scheme="http://www.sixapart.com/ns/types#category" />
   
   <category term="7" label="Perl" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="42" label="Webアプリケーション" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="24" label="Windows" scheme="http://www.sixapart.com/ns/types#tag" />
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[お久しぶりです、Perl担当の西山です。

前回に続きDRY(Don't Repeat Yourself)を追求するWebフレームワークJiftyを取り上げます。
手元の環境にPerlの実行環境やJiftyをインストールされていない方はこれまでの記事を参照してください。
今回はJiftyと一緒に配布されている"ShrinkURL"というサンプルアプリケーションを題材に、
Jiftyに従ってアプリケーションを開発する場合のモジュール構造や考え方を見ていきたいと思います。

これまでの記事：
<ol class="number-list">
<li><a href="http://www.tkrb.jp/guide/2008/01/jiftyweb_windows.html" target="_blank">JiftyでWebアプリをつくる - Windowsにインストール</a></li>
<li><a href="http://www.tkrb.jp/guide/2008/02/jiftyweb.html" target="_blank">JiftyでWebアプリをつくる - ログイン機能を作る</a></li>
</ol>

<h4 class="large-tit">Jiftyの考え方</h4>
一般的にWebアプリケーションのフレームワークなどで使用されるアプリケーション構造はMVC(Model-View-Controller)と呼ばれる形に機能は分類されますが、
Jiftyでは本家サイトの<a href="http://www.jifty.org/view/BasicConcepts" target="_blank">Basic concepts of Jifty</a>で説明されているように「Dispatcher」「Action]
「Template」「Model」という4つの要素でアプリケーションを構成しています。
それぞれの要素は主に以下のような役割で開発するように設計されています。

<ul class="common-list">
<li><b>Dispatcher: URLに対してActionやTemplateへのマッピングを定義。</b></li>
<li><b>Action: 画面内での操作やリクエストなどをトリガーに発生するビジネスロジックを定義。</b></li>
<li><b>Template: 表示系のデータ取得処理や、UIのレイアウト情報を定義。</b></li>
<li><b>Model: 操作対象となるデータモデルの構造や操作を定義。</b></li>
</ul>

通常のMVCではControllerでまとめて扱われることの多いDispatcherとActionの役割を、Jiftyではフレームワークレベルで明確に分けています。
また、表示系の処理もViewが使用するhtmlテンプレートやControllerに分散しやすいですが、JiftyではTemplateのPerlモジュール内で一括管理するように設計されているのが特徴だと思います。


<h4 class="large-tit">サンプルアプリShrinkURLを見る</h4>
新しいモジュールやフレームワークの使い方を理解するには
サンプルをてっとり早く動かしてみていろいろいじってみるのが早いと思います。
サンプルアプリケーションであるShrinkURLを見てみましょう。

<h5 class="small-tit">セットアップ</h5>
先ほどサンプルはJiftyと一緒に配布されていると書きましたが、
Windows版のPPMには含まれていないようです。
TortoiseSVNなどのSubversionクライアントをインストールしている方は下記レポジトリから、
チェックアウトしてください。
<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
http://svn.jifty.org/svn/jifty.org/jifty/trunk/examples/ShrinkURL
</pre>

ブラウザ経由でも<a href="http://search.cpan.org/CPAN/authors/id/S/SA/SARTAK/Jifty-0.71129.tar.gz" target="_blank">CPANのJiftyのページから</a>ダウンロードできます。
解凍後のフォルダのexamples\ShrinkURLが今回使用するアプリケーションフォルダです。
適当な作業フォルダにShrinkURLをコピーしてください。

また、ShrinkURLが依存するモジュールとしてNumber::RecordLocatorが必要になります。
別途Package Managerを使ってインストールしてください。

最後に、以下のようにコマンドプロンプトから
ShrinkURLフォルダ配下でコマンドを実行するとセットアップ完了です。
<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
C:\tmp\ShrinkURL>perl bin\jifty schema --setup
</pre>


<h5 class="small-tit">動作確認</h5>
まずは素の状態の動作を見てみます。
以下のようにjiftyコマンドにserverオプションを指定して実行するとアプリケーションサーバが起動し、
デフォルトでは<a href="http://localhost:8888/" target="_blank">http://localhost:8888/</a>のURLで
アクセスできるようになります。
<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
C:\tmp\ShrinkURL>perl bin\jifty server
</pre>

ShrinkURLは、入力されたURLにユニークなキーを割り当て、短縮したURLでサイトにアクセスできるようにするアプリケーションです。
登録したURLはDBに登録され、短縮したURLはいつでも利用することができます。


1.URLを入力してShrink it!ボタンをクリック

<img alt="sc0000.png" src="http://www.tkrb.jp/guide/2008/03/19/sc0000.png" width="480" height="267" />

2.短縮したURLが生成される

<img alt="sc0001.png" src="http://www.tkrb.jp/guide/2008/03/19/sc0001.png" width="480" height="267" />

生成されたURLにブラウザからアクセスすると、元の入力したURLへリダイレクトされます。
]]>
      <![CDATA[<h5 class="small-tit">ソース概要</h5>
動作を把握したところで、次はオリジナルのソースを見てみましょう。
Dispatcher、Action、Template、Modelにあたるモジュールを簡単に説明します。

<ul class="common-list">
<li><b>Dispatcher: ShrinkURL\lib\ShrinkURL\Dispatcher.pm</b><br>
ShrinkURL配下の各URLにアクセスされた場合の処理を定義しています。
アプリケーションルートにアクセスされた場合、shrinkテンプレート(ShrinkURL\lib\ShrinkURL\View.pm内に定義)を呼び出してURL入力画面を表示させます。
ルート以外にアクセスがあった場合は、短縮したURLとしてのアクセスと判断し、該当するURLを検索してリダイレクトさせます。
<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
#!/usr/bin/env perl
package ShrinkURL::Dispatcher;
use strict;
use warnings;
use Jifty::Dispatcher -base;

# visiting / will let users create new shrunken URLs
on '/' => show 'shrink';

# any other URL (that has no path separator) is potentially a shrunken URL
on '*' => run {
    my $url = $1;

    my $shrunkenurl = ShrinkURL::Model::ShrunkenURL->new;
    $shrunkenurl->load_by_shrunken($url);

    if ($shrunkenurl->id) {
        redirect($shrunkenurl->url);
    }

    # if there's no valid URL, just let the person create a new one :)
    redirect('/');
};

1;
</pre>
</li>
<li><b>Action: ShrinkURL\lib\ShrinkURL\Action\CreateShrunkenURL.pm</b><br>
URL入力画面でShrink it!ボタンがクリックされた後に実行される処理が記述されています。
ここでは<a href="http://search.cpan.org/dist/Jifty/lib/Jifty/Action/Record/Create.pm" target="_blank">Jifty::Action::Record::Create</a>を利用してModelの登録処理を行っています。
<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
#!/usr/bin/env perl
package ShrinkURL::Action::CreateShrunkenURL;
use strict;
use warnings;

use base qw/Jifty::Action::Record::Create/;
sub record_class { 'ShrinkURL::Model::ShrunkenURL' }

# have we already shrunk this URL? if so, no need to do it again!
sub take_action {
    my $self = shift;
    my $url = $self->argument_value('url');

    my $shrunkenurl = ShrinkURL::Model::ShrunkenURL->new;
    $shrunkenurl->load_by_cols(url => $url);

    if ($shrunkenurl->id) {

        # for the benefit of report_success
        $self->record($shrunkenurl);

        # for the benefit of the template that displays new shrunken URLs
        # this is called in a superclass which we bypass
        $self->result->content(id => $shrunkenurl->id);

        # this too is called in a superclass
        $self->report_success;

        # Create actions return object's ID
        return $shrunkenurl->id;
    }

    return $self->SUPER::take_action(@_);
}

# display a nice little message for the user
sub report_success {
    my $self = shift;
    $self->result->message(_("URL shrunked to %1", $self->record->shrunken));
}

1;
</pre>
</li>
<li><b>Template: ShrinkURL\lib\ShrinkURL\View.pm</b><br>
テンプレートエンジンとしてTemplate::Declareを使って、URL入力画面のレイアウト情報を定義しています。Template::Declareの記述方法については以前<a href="http://www.tkrb.jp/guide/2008/01/perl_templatedeclare.html" target="_blank">テンプレートの記述もPerlで - Template::Declareを使う</a>というエントリーを書きましたので参考にしてみてください。
また、Jiftyでは画面上の表示領域をregionという単位で分割して管理することができます。regionの詳細は<a href="http://search.cpan.org/~sartak/Jifty/lib/Jifty/Manual/PageRegions.pod" target="_blank">マニュアル</a>を参照してください。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
#!/usr/bin/env perl
package ShrinkURL::View;
use strict;
use warnings;
use Jifty::View::Declare -base;

template 'shrink' => page {

    # render the "shrink a URL" widget, which we can put on any page of the app
    render_region(
        name => 'new_shrink',
        path => '/misc/new_shrink',
    );

    # render an empty region that we push results onto
    render_region(
        name => 'new_shrinks',
    );
};

template '/misc/new_shrink' => sub {
    my $action = new_action(class => 'CreateShrunkenURL');
    form {
        Jifty->web->form->register_action($action);
        render_action($action => ['url']);

        form_submit(
            submit  => $action,
            label   => _('Shrink it!'),

            onclick => [
                { submit => $action },
                {
                    # prepend this result onto the empty region above
                    region => 'new_shrinks',
                    prepend => '/misc/shrunk_region',
                    args => {
                        id => { result_of => $action, name => 'id' },
                    },
                },
            ],
        );
    };
};

template '/misc/shrunk_region' => sub {
    my $id = get 'id';
    my $shrunken = ShrinkURL::Model::ShrunkenURL->new;
    $shrunken->load($id);

    if ($shrunken->id) {
        div {
            strong { a { attr { href => $shrunken->shrunken } $shrunken->shrunken  } };
            outs _(" is now a shortcut for %1.", $shrunken->url);
        }
    }
};

1;
</pre>
</li>
<li><b>Model: ShrinkURL\lib\ShrinkURL\Model\ShrunkenURL.pm</b><br>
URLを管理する為のDBのテーブル定義と、テーブル上のデータに対する操作が記述されています。JiftyではDBアクセスには<a href="http://search.cpan.org/dist/Jifty-DBI/" target="_blank">Jifty::DBI</a>モジュールをデフォルトで使用します。
<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
#!/usr/bin/env perl
package ShrinkURL::Model::ShrunkenURL;
use strict;
use warnings;
use Number::RecordLocator;
my $generator = Number::RecordLocator->new;

use Jifty::DBI::Schema;
use Jifty::Record schema {
    column url =>
        is distinct,
        is varchar(1000);
};

# shrunken URL is just an encoding of ID
sub shrunken {
    my $self = shift;
    Jifty->web->url(path => $generator->encode($self->id));
}

# helper function so we can easily change the internal representation of
# shrunken URLs if we desire
sub load_by_shrunken {
    my $self = shift;
    my $shrunken = shift;
    my $id = $generator->decode($shrunken);

    return $self->load($id);
}

# prepend http:// if the scheme is not already there
sub canonicalize_url {
    my $self = shift;
    my $url = shift;

    $url = "http://$url"
        unless $url =~ m{^\w+://};

    return $url;
}

1;
</pre>
</li>


<br><br>

<h4 class="large-tit">ShrinkURLをいじる</h4>
構造の概要が分かったところでソースに実際に手を加えて機能を追加してみましょう。



<h5 class="small-tit">フォーム仕様を変更</h5>
URL入力フォームの形式の変更、入力チェックの追加を行います。

JiftyではDBのスキーマ(テーブル)定義とそれに対応するフォームの項目の定義を一箇所に定義することが可能です。

ShrinkURL\lib\ShrinkURL\Model\ShrunkenURL.pmのスキーマ定義を以下のように変更してください。
<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
use Jifty::Record schema {
    column url =>
        is distinct,
        is varchar(1000),
        is mandatory,           # 必須項目に指定
        label is 'URL:',        # 入力フィールドのラベル
        render as 'textarea',   # 形式をテキストエリアに
        hints is _('Please enter URL.'), # 入力フィールドの補助的な説明文
        ajax validates,         # 入力チェックなどを非同期に実行
};
</pre>

簡単な画面仕様であればテーブル項目と画面の入力項目は一致することが多いと思うので、
同じような項目定義をViewやModelなどに分散して記述しないで一元管理できるのは便利ですね。

また、独自に入力チェックを定義したい場合は、「validate_カラム名」という命名規則でメソッドを定義すれば自動で呼び出されます。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
# URLのフォーマットの妥当性チェック
sub validate_url {
    my $self = shift;
    my $url = shift;

    # 手抜きですが'.'の有無だけチェック
    if ($url !~ m{[.]}) {
        return (0, _('Your URL is invalid.'));
    }
    return 1;
}
</pre>

ちなみにActionが実行された時にDBにアクセスしない場合などは、Actionにフォーム仕様を記述することも可能です。
その場合は、Action(ここではShrinkURL\lib\ShrinkURL\Action\CreateShrunkenURL.pm)に以下のように定義を記述してください。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
use Jifty::Param::Schema;
use Jifty::Action schema {
    param url =>
        type is 'text',
        ajax validates,
};
</pre>

その他に指定できるパラメータや詳しい仕様は<a href="http://search.cpan.org/dist/Jifty-DBI/lib/Jifty/DBI/Schema.pm" target="_blank">Jifty::DBI::Schema</a>と<a href="http://search.cpan.org/dist/Jifty/lib/Jifty/Param/Schema.pm" target="_blank">Jifty::Param::Schema</a>に書かれています。


■実行結果
<img alt="sc0002.png" src="http://www.tkrb.jp/guide/2008/03/19/sc0002.png" width="480" height="267" />


<h5 class="small-tit">登録済みリストを表示</h5>
元のアプリでは登録済みのURLが表示されないのでフォーム下部に一覧表示させてみます。
Modelに一覧データ取得用のメソッドを、Viewに一覧のレイアウトを追加します。

1. ShrinkURL\lib\ShrinkURL\Model\ShrunkenURL.pm に以下のメソッドを追加

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
sub list {
    my $self = shift;

    my $urls = ShrinkURL::Model::ShrunkenURLCollection->new;
    $urls->unlimit;

    my @shrunkens;
    while (my $url = $urls->next) {
        push @shrunkens, {
            url => $url->url,
            shrunken => Jifty->web->url(path => $generator->encode($url->id))
        };
    }
    return @shrunkens;
}
</pre>

2. ShrinkURL\lib\ShrinkURL\View.pm に以下のテンプレートを追加・修正

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
# URLリスト表示用テンプレートを追加
template '/shrink_list' => sub {
    my @urls = ShrinkURL::Model::ShrunkenURL->list;

    for my $url (@urls) {
        ul {
            li {
                strong { a { attr { href => $url->{shrunken} } $url->{shrunken}  } };
                outs _(" : shortcut for %1", $url->{url});
            };
        }
    }
};
</pre>

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
# new_shrinkテンプレートの最後に上記shrink_listテンプレートの呼び出しを追加
template '/misc/new_shrink' => sub {
    my $action = new_action(class => 'CreateShrunkenURL');
    form {
        Jifty->web->form->register_action($action);
        render_action($action => ['url']);

        form_submit(
            submit  => $action,
            label   => _('Shrink it!'),

            onclick => [
                { submit => $action },
                {
                    # prepend this result onto the empty region above
                    region => 'new_shrinks',
                    prepend => '/misc/shrunk_region',
                    args => {
                        id => { result_of => $action, name => 'id' },
                    },
                },
            ],
        );
    };
    # show registered URL;
    show '/shrink_list';
};
</pre>


■実行結果
<img alt="sc0003.png" src="http://www.tkrb.jp/guide/2008/03/19/sc0003.png" width="480" height="267" />


<h5 class="small-tit">メッセージの日本語化</h5>
最後に、英語で表示されている各種メッセージを日本語化します。
Jiftyでは<a href="http://search.cpan.org/dist/Locale-Maketext-Lexicon/" target="_blank">Locale::Maketext::Extractモジュール</a>を使用して
アプリケーションで使用されるメッセージを管理しています。
ここまでサンプルを読んでいる時に _('some message.') という関数があちこちで呼ばれていたのに気がつきましたか？
この関数で指定された文字列をキーに外部ファイルで各言語でのメッセージを設定することができます。

以下のコマンドを実行すると、日本語用のファイルの雛形が生成されます。
※ShrinkURLには生成先のフォルダが作成されていないのでShrinkURL\share\poフォルダを予め作成しておいてください。
　（雛形生成スクリプトでイチからアプリケーションを作っていればデフォルトで用意されています。）

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
C:\tmp\ShrinkURL>perl bin\jifty po --language ja
</pre>

コアプリケーション内のメッセージを抽出したファイルがShrinkURL\share\po\ja.poという名前で生成されます。
ファイル内のヘッダー部にあるContent-Typeのcharsetをutf-8に変更したあと、
メッセージ部に日本語を以下のような要領で書き加えてからサーバを起動すると、日本語のメッセージに置き換えられて実行されます。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
#: lib/ShrinkURL/.View.pm.swp:7 lib/ShrinkURL/View.pm:68
#. ($url->{url})
msgid " : shortcut for %1."
msgstr ": %1のショートカット"

#: lib/ShrinkURL/.View.pm.swp:7 lib/ShrinkURL/View.pm:56
#. ($shrunken->url)
msgid " is now a shortcut for %1"
msgstr "は%1のショートカットです。"

#: lib/ShrinkURL/Model/.ShrunkenURL.pm.swp:23 lib/ShrinkURL/Model/ShrunkenURL.pm:16
msgid "Please enter URL."
msgstr "URLを入力してください。"

#: lib/ShrinkURL/.View.pm.swp:7 lib/ShrinkURL/View.pm:29
msgid "Shrink it!"
msgstr "実 行"

#: lib/ShrinkURL/Action/.CreateShrunkenURL.pm.swp:15 lib/ShrinkURL/Action/CreateShrunkenURL.pm:34
msgid "This URL is already exists."
msgstr "そのURLは登録済みです。"

#: lib/ShrinkURL/Action/.CreateShrunkenURL.pm.swp:15 lib/ShrinkURL/Action/CreateShrunkenURL.pm:41
msgid "URL shrunked to "
msgstr "URLのショートカットが作成されました。"

#: lib/ShrinkURL/Action/.CreateShrunkenURL.pm.swp:15 lib/ShrinkURL/Action/CreateShrunkenURL.pm:50
#. ($self->record->shrunken)
msgid "URL shrunked to %1"
msgstr "URLのショートカットが作成されました。"

#: lib/ShrinkURL/Model/.ShrunkenURL.pm.swp:23 lib/ShrinkURL/Model/ShrunkenURL.pm:57
msgid "Your URL is invalid."
msgstr "正しいURLを入力してください。"
</pre>


■実行結果
<img alt="sc0004.png" src="http://www.tkrb.jp/guide/2008/03/19/sc0004.png" width="480" height="267" />


<h4 class="large-tit">まとめ</h4>
サンプルアプリケーションの実行や改造を通して、Jiftyベースで開発する場合のアプリケーション構造を駆け足で見てきました。<br>
TemplateまでPerlで記述する部分は慣れが必要かもしれませんが、DispatcherとActionがフレームワークレベルで明確に分かれていてイベントドリブンな感覚で作れるのは個人的にとても分かりやすいと思いました。<br>
あとはAction・Template・Modelに関連するフォーム定義をDRYに記述できるのも素晴らしいです。<br><br>

Webアプリケーションを開発するといっても、規模や用途によって最適なアプリケーション構造は変わってくると思います。
最小構成ではHTML+Javascript又はPHPのファイル1個で済む場合もありますし、大きなシステムではCatalystのような重厚なフレームワークを使って拡張性やカスタマイズ性を求めるケースもあると思います。
またDBのテーブルと画面仕様が1対1になるようなシンプルなアプリケーションではRailsなどでサックリ作るのが最適でしょう。

エリック・レイモンドが<a href="http://cruel.org/freeware/hacker.html" target="_blank">なるべく複数の異なるプログラミング言語を学ぶべきと語っていた</a>ように、もっと上のレイヤーのアプリケーションフレームワークについても1つに依存せずにいろいろ試して実際に開発してみることが大切なように思います。様々な設計思想に触れてみることでもしかしたら素晴らしい悟り体験に出会えるかもしれません：）

特にPerl使いの方にはフルスタックのフレームワークに接する機会は少ないと思うので、Jiftyを使ってサクサクしたWebアプリケーション開発を体験してみるときっと楽しいですよ。


]]>
   </content>
</entry>
<entry>
   <title>アプリケーションとサーバサイドマッシュアップの統合</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/03/struts.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.63</id>
   
   <published>2008-03-12T09:45:14Z</published>
   <updated>2008-03-12T12:25:48Z</updated>
   
   <summary> この連載の第2回、第3回、第4回でStrutsとAjaxを組み合わせてサーバへ...</summary>
   <author>
      <name>ATL Enterprise Architecture Unit</name>
      
   </author>
         <category term="Java" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[この連載の<a href="http://www.tkrb.jp/guide/2007/11/struts_1.html">第2回</a>、<a href="http://www.tkrb.jp/guide/2007/12/ajax.html">第3回</a>、<a href="http://www.tkrb.jp/guide/2008/01/post_5.html">第4回</a>でStrutsとAjaxを組み合わせてサーバへの通信と比較的リッチなUIを作成しました。また、<a href="http://www.tkrb.jp/guide/2008/01/stru.html">第5回</a>、<a href="http://www.tkrb.jp/guide/2008/02/java_apiweb.html">第6回</a>、<a href="http://www.tkrb.jp/guide/2008/02/restletjsonlib_web.html">第7回</a>ではマッシュアップについて説明し、実際にサーバーサイドマッシュアップの実装を行いました。

 今回は、題材のアプリケーションにサーバーサイドマッシュアップを統合します。]]>
      <![CDATA[イベント情報を検索して、一覧が表示され、一覧の「詳細」リンクをクリックすると、詳細ダイアログが開き、イベントの詳細情報が表示されるというのが今までのアプリケーションでした。今回は、詳細情報が表示される際に、イベント名でテクノラティのブログ検索を行い、さらにそのブログについてはてなブックマークでブックマークしてる人の数を、はてなAPIを使って検索し、今までの詳細画面の一番下に表示するというサンプルを作成します。
 
<p class="url">(今回作成するサンプル)
<a target="_blank" href="http://www.tkrb.jp/guide/2008/03/12/image1.JPG" target="_blank"><img alt="image1.JPG" src="http://www.tkrb.jp/guide/2008/03/12/image1.JPG" width="400" /></a>
</p>


 まずは、イベントの詳細情報が検索された場合に、Eventクラスのオブジェクトの中にブログ情報を含めるために、EventにWeblogのリストを持たせて、そのsetter/getterを持たせます。

(Event.java)
<p class="code">
public&nbsp;class&nbsp;Event&nbsp;implements&nbsp;Serializable&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;(略)
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;List<Weblog>&nbsp;weblogList;
&nbsp;&nbsp;&nbsp;&nbsp;(略)
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;List<Weblog>&nbsp;getWeblogList()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;weblogList;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;setWeblogList(List<Weblog>&nbsp;weblogList)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.weblogList&nbsp;=&nbsp;weblogList;
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</p>

 イベントの詳細情報が検索された場合に、今までのサーバサイドマッシュアップを組み合わせて、イベント名からテクノラティのblog検索をし、はてブ数の検索を行うように、EventBeanのgetEventメソッドを変更します。

(EventBean.java)
<p class="code">
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Event&nbsp;getEvent(String&nbsp;id)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Event&nbsp;event&nbsp;=&nbsp;eventDao.findByPrimaryKey(id);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List<Weblog>&nbsp;weblogs&nbsp;=&nbsp;technoratiBlogSearch.search(event.getName());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(Weblog&nbsp;weblog:weblogs)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;count&nbsp;=&nbsp;hatebuSearch.search(weblog.getPermalink());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;weblog.setHatebuCount(count);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;event.setWeblogList(weblogs);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;event;
&nbsp;&nbsp;&nbsp;&nbsp;}
</p>

 この結果、WeblogクラスのオブジェクトもDWRでやりとりをするようになるので、Serializableをimplementsします。

(Weblog.java)
<p class="code">
public&nbsp;class&nbsp;Weblog&nbsp;implements&nbsp;Serializable&nbsp;{
&nbsp;&nbsp;&nbsp;（略)
</p>

 Eventクラスのオブジェクトが持つ、WeblogクラスもDWRによってコンバートしてもらうために、dwr.xmlにコンバーターの設定を行います。

(dwr.xml)
<p class="code">
&nbsp;&nbsp;&nbsp;&nbsp;(略)
&nbsp;&nbsp;&nbsp;&nbsp;&lt;convert&nbsp;converter="bean"&nbsp;match="jp.tkrb.event.beans.Community"&nbsp;/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;convert&nbsp;converter="bean"&nbsp;match="jp.tkrb.event.beans.Event"&nbsp;/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;convert&nbsp;converter="bean"&nbsp;match="jp.tkrb.event.beans.Weblog"&nbsp;/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;(略)
</p>

 ここまでで、イベントの詳細情報をgetEven メソッドで検索したときに、Eventが持つWeblogに、Event名に関係したブログの情報とはてブ数が入ることになります。これをクライアント側のJavaScriptで表示させます。先に、表示するための領域として、top.jspのイベント表示フローティングウィンドウのテンプレートに、idがblogListというdiv要素を追加します。

(top.jsp のフローティングウィンドウのテンプレート)
<p class="code">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;id="event_detail"&nbsp;style="width:280px;height:120px;"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;table&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&gt;ID&lt;/td&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&gt;&lt;span&nbsp;id="id"&gt;&lt;/span&gt;&lt;/td&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/tr&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&gt;コミュニティ名&lt;/td&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&gt;&lt;span&nbsp;id="community.name"&gt;&lt;/span&gt;&lt;/td&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/tr&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&gt;イベント名&lt;/td&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&gt;&lt;span&nbsp;id="name"&gt;&lt;/span&gt;&lt;/td&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/tr&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&gt;開始日時&lt;/td&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&gt;&lt;span&nbsp;id="startDate"&gt;&lt;/span&gt;&lt;/td&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/tr&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&gt;終了日時&lt;/td&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&gt;&lt;span&nbsp;id="endDate"&gt;&lt;/span&gt;&lt;/td&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/tr&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/table&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;id="blogList"&nbsp;/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/ul&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/div&gt;
</p>

次に、実際に表示させるために、top.jspのopenEventDetail関数を次のように変更します。

(top.jspのopenEventDetail関数)
<p class="code">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;openEventDetail(id)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EventBean.getEvent(id,&nbsp;function(data)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dwr.util.setValue("id",&nbsp;data.id);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dwr.util.setValue("community.name",&nbsp;data.community.name);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dwr.util.setValue("name",&nbsp;data.name);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dwr.util.setValue("startDate",&nbsp;dateFormat.format(data.startDate));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dwr.util.setValue("endDate",&nbsp;dateFormat.format(data.endDate));

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(data.weblogList.length&nbsp;&gt;&nbsp;0)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;blogListStr&nbsp;=&nbsp;"&lt;ul&gt;";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(var&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;data.weblogList.length;&nbsp;i++)&nbsp;{&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;weblog&nbsp;=&nbsp;data.weblogList[i];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;blogListStr&nbsp;+=&nbsp;'&lt;li&gt;&lt;a&nbsp;href="'+weblog.permalink+'"&gt;'+weblog.title+'&lt;/a&gt;：はてブ数&nbsp;'+weblog.hatebuCount+'&lt;/li&gt;';
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;blogListStr&nbsp;+=&nbsp;"&lt;/ul&gt;";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$("blogList").innerHTML&nbsp;=&nbsp;blogListStr;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;else&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$("blogList").innerHTML&nbsp;=&nbsp;"";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;win.showCenter();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
</p>

 DWR経由で、EventBeanのgetEventを呼び出した結果、Eventをあらわすdata変数の中に、weblogListの長さが0以上であれば、ループをまわして、各Weblogオブジェクトが持っている情報を箇条書きで表示します。「$("blogList")」は、prototype.jsの関数で、idがblogListのHTML要素を取得するための関数です。

ここまでで、題材のアプリケーションとサーバーサイドマッシュアップの統合の実装は完了です。DWRでの通信やサーバサイドでの処理は以下のような構成になっています。
<p class="url">(全体の構成)
<a target="_blank" href="http://www.tkrb.jp/guide/2008/03/12/image2.jpg" target="_blank"><img alt="image2.jpg" src="http://www.tkrb.jp/guide/2008/03/12/image2.jpg" width="400" />
</a>
</p>

ここまでの記事で、Ajaxを使った開発やサーバサイドでのマッシュアップを行う際の構成など一連の流れについて、一例をご理解いただけたかと思います。]]>
   </content>
</entry>
<entry>
   <title>Adobe AIRでAjax！HTMLとJavaScriptでそのままAIRに移行できるサンプル</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/03/just_shift_to_adobe_air_by_html_and_javascript.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.62</id>
   
   <published>2008-03-12T05:00:00Z</published>
   <updated>2008-03-29T16:07:00Z</updated>
   
   <summary> こんにちは、JavaScript担当の（株）アークウェブの竹村です。 2008...</summary>
   <author>
      <name>竹村 光生</name>
      
   </author>
         <category term="JavaScript" scheme="http://www.sixapart.com/ns/types#category" />
   
   <category term="107" label="AIR+HTML+JavaScript" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="105" label="Adobe AIR" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="108" label="入力チェック" scheme="http://www.sixapart.com/ns/types#tag" />
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[こんにちは、JavaScript担当の<a href="http://www.ark-web.jp/" target="_blank">（株）アークウェブ</a>の竹村です。

2008年2月25日にAdobe AIR 1.0 (英語版)がリリースされましたね。

<ul class="icon-list"><li><a href="http://www.adobe.com/jp/products/air/" target="_blank">Adobe AIR</a></li></ul>


AIRはHTMLやJavaScriptなどWebの技術をそのまま利用してクライアントサイドのデスクトップアプリケーションの開発ができるので、今までのWebデベロッパーには親しみやすいと思います。

今回は、このAIRでHTMLとJavaScriptを使ったWebアプリをAIRに変換してユーザに配布するまでについて、書いていきます。


レジュメは↓このようになっています。

<ul class="common-list">
<li>AIRアプリケーションを制作できる環境構築</li>
<li>ユーザに配布するまでの流れ</li>
<li>アプリケーション記述ファイルの作成</li>
<li>AIRコンテンツの制作</li>
<li>AIRコンテンツの確認</li>
<li>電子署名の作成</li>
<li>インストールパッケージの作成</li>
<li>Webにアップする (インストールバッジの作成)</li>
<li>おまけ：JavaScriptライブラリを利用したフォーム入力チェックのAIR化</li>
<li>フォーム入力チェックのWeb版を制作</li>
<li>AIRアプリをWebにアップするまで一気におさらい</li>
<li>まとめ</li>
</ul>

今回作る2つのサンプルは↓こんな感じです。

<ul class="icon-list">
<li><a href="http://okra.ark-web.jp/~takemura/public/js/adobe_air/ajax_sample/ajax_sample_for_web.html" target="_blank">外部ファイルを読み込んで表示するサンプル<br />
<img alt="img_sample02.jpg" src="http://www.tkrb.jp/guide/2008/03/12/img_sample02.jpg" width="470" height="256" /></a></li>
<li><a href="http://okra.ark-web.jp/~takemura/public/js/adobe_air/form_sample/form_sample_for_web.html" target="_blank">フォームの入力チェックサンプル<br />
<img alt="img_sample01.jpg" src="http://www.tkrb.jp/guide/2008/03/12/img_sample01.jpg" width="470" height="179" /></a></li>
</ul>

まずは、Windows環境にAIRアプリケーションを構築できる環境を作って、
パブリッシュした swf をサーバにおいて動作できるようにしてみましょう。]]>
      <![CDATA[<style type="text/css">
.attention {color:red; font-weight:bold;}
.prompt {color: #ffffff; background: black; border:2px dotted #eeeeee; padding:10px;}
</style>
<h4 class="large-tit">AIRアプリケーションを制作できる環境構築</h4>

環境構築に必要となる設定は↓この４手順です。

<ol class="number-list">
<li>Adobe AIRランタイムのインストール</li>
<li>JDKのインストール</li>
<li>Adobe AIR SDKのインストール</li>
<li>Windowsの環境変数の変更</li>
</ol>


<h5 class="small-tit">Adobe AIRランタイムのインストール</h5>

AIRのランタイムは下記からダウンロードできます。

<ul class="icon-list"><li><a href="http://get.adobe.com/air/" target="_blank">Adobe AIR Download Center</a></li></ul>

ダウンロード後は、インストーラーに従ってインストールすればOKです。


<h5 class="small-tit">Java Development Kit (JDK) のインストール</h5>

JDK 5.0 か JDK 6.0 のどちらかをインストールする必要があります。ダウンロードは下記からできます。

<ul class="icon-list">
<li><a href="http://java.sun.com/j2se/1.5.0/ja/download.html" target="_blank">JDK 5.0</a></li>
<li><a href="http://java.sun.com/javase/ja/6/download.html" target="_blank">JDK 6.0</a></li>
</ul>

『JDK 5.0 Update 15』または『JDK 6.0 Update 5』のダウンロードをします。

今回、私の環境では『JDK 6.0 Update 5』の「Windows Offline Installation, Multi-language」(32bit版)をダウンロードしています。

ダウンロード後は、インストーラーに従ってインストールすればOKです。


<h5 class="small-tit">Adobe AIR SDKのインストール</h5>

※既にAIR SDKをインストールされている場合は
↓こちらのブログの手順に従ってアンインストールしてください。

<ul class="icon-list">
<li><a href="http://weblogs.macromedia.com/akamijo/archives/2008/02/adobe_air_3.cfm" target="_blank">Adobe 上条さんのブログより<br/>
http://weblogs.macromedia.com/akamijo/archives/2008/02/adobe_air_3.cfm</a></li>
</ul>

Adobe AIR SDKは下記からダウンロードできます。

<ul class="icon-list"><li><a href="http://www.adobe.com/products/air/tools/sdk/" target="_blank">Adobe AIR SDK</a></li></ul>

「Download Adobe AIR SDK for Windows」をダウンロードします。

ダウンロード後は、zipを展開してできたディレクトリを、C:\ 直下などに移動させます。私の環境では↓こちらに移動しました。

<pre><p class="url">C:\AdobeAIRSDK\</p></pre>


<h5 class="small-tit">環境変数の設定</h5>

Windowsの環境変数を設定しておくと、コマンドプロンプトからAIR用のコマンドを実行するときに楽になるので設定しておきましょう。

[Windowsロゴ]＋[Break]キーで表示される「システム」ダイアログの「詳細設定」タブの「環境変数」をクリックします。「PATH」をクリックして[編集]ボタンをクリックし、変数値に↓この値を追記して[OK]をクリックします。

  『 ;C:\AdobeAIRSDK\bin 』


<h4 class="large-tit">ユーザに配布するまでの流れ</h4>

Flashの場合、制作から配布の流れは単純でした。

<ol class="number-list">
<li>Flashコンテンツ制作</li>
<li>Flashからパブリッシュ</li>
<li>HTMLにobjectタグを定義してswfを読ませるように記述</li>
<li>Webにアップする</li>
</ol>

AIRの場合、これに少しだけ手順が増えます。

<ol class="number-list">
<li>アプリケーション記述ファイルの作成</li>
<li>AIRコンテンツの制作 (AIRコンテンツの確認)</li>
<li>電子署名の作成</li>
<li>インストールパッケージの作成</li>
<li>Webにアップする</li>
</ol>

まずは、簡単にHTML+JavaScriptのサンプルアプリを出力してみます。
そのためのプロジェクトフォルダを作りましょう。私の環境では、↓こちらに作りました。

<pre><p class="url">C:\work\air\ajax_sample\</p></pre>


<h4 class="large-tit">アプリケーション記述ファイルの作成</h4>

「アプリケーション記述ファイル」に配布するAIRに関する情報を記述します。
何を記述するかは、テンプレートとして↓こちらのディレクトリにxmlファイルがあります。

<pre><p class="url">C:\AdobeAIRSDK\templates\descriptor-template.xml</p></pre>

これを訳しているサイトがあったので、参考にしてください。

<ul class="icon-list">
<li><a href="http://gizmo.anthill.jp/fgug/modules/bwiki/index.php?AIR%20-%20%A5%A2%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3%B5%AD%BD%D2%A5%D5%A5%A1%A5%A4%A5%EB" target="_blank">アプリケーション記述ファイル</a></li>
</ul>

今回作成するアプリケーション記述ファイルは↓こちらに下記のように記述しました。

<pre><p class="url">C:\work\air\ajax_sample\ajax_sample_for_air.xml</p></pre>

<pre><p class="code">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;application xmlns="http://ns.adobe.com/air/application/1.0"&gt;
  &lt;id&gt;jp.ark-web.okra.takemura.public.js.adobeair.ajax_sample.sample&lt;/id&gt;
  &lt;version&gt;0.1&lt;/version&gt;
  &lt;filename&gt;AIR_Ajax_sample&lt;/filename&gt;
  &lt;name&gt;AIR sample: Ajax sample&lt;/name&gt;
  &lt;initialWindow&gt;
    &lt;content&gt;ajax_sample_for_air.html&lt;/content&gt;
    &lt;systemChrome&gt;standard&lt;/systemChrome&gt;
    &lt;transparent&gt;false&lt;/transparent&gt;
    &lt;visible&gt;true&lt;/visible&gt;
  &lt;/initialWindow&gt;
&lt;/application&gt;
</p></pre>


<h4 class="large-tit">AIRコンテンツの制作</h4>

簡単なHTMLとJavaScriptが連携するサンプルを作ってみました。
Web上では↓このような動作をします。

<h5 class="small-tit">Ajaxで外部ファイルを読み込むサンプル (Web版)</h5>
<iframe src="http://okra.ark-web.jp/~takemura/public/js/adobe_air/ajax_sample/ajax_sample_for_web.html" width="470" height="240">
<a href="http://okra.ark-web.jp/~takemura/public/js/adobe_air/ajax_sample/ajax_sample_for_web.html" target="_blank">iframeをご覧になれない場合はこちらをどうぞ</a></iframe>

<pre><p class="code">
&lt;script type="text/javascript" src="<span class="attention">js/prototype.js</span>"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
document.observe("dom:loaded", initialize, false);
function initialize() {
	<span class="attention">$('load-from-package-button').onclick = function(){ releaseLoadButton('request.txt'); };</span>
	<span class="attention">$('load-from-web-button').onclick     = function(){ releaseLoadButton('http://okra.ark-web.jp/~takemura/public/js/adobe_air/ajax_sample/request.txt'); };</span>
}
function <span class="attention">releaseLoadButton</span>(sFilePath) {
	new Ajax.Request(
		sFilePath,
		{onComplete: loadedText}
	);
}
function loadedText(oResponse) {
	<span class="attention">$('viewer').innerHTML = oResponse.responseText;</span>
}
&lt;/script&gt;
(...)
&lt;p&gt;[Load]ボタンクリックで、外部ファイルをAjaxで読み込んで表示します。&lt;/p&gt;
&lt;button id="load-from-package-button"&gt;packageからLoad&lt;/button&gt;
&lt;button id="load-from-web-button"&gt;webからLoad&lt;/button&gt;

&lt;p&gt;▼Loadすると、↓この部分が書き変わります。&lt;/p&gt;
&lt;div id="viewer"&gt;&lt;/div&gt;
</p></pre>

prototype.jsを利用して、load-buttonのonclickイベントのハンドラにreleaseLoadButtonを設定し、そのハンドラ内ではAjaxでrequest.txtを読み込んで、完了後は出力しています。


では、これを AIR でやってみましょう。

アプリケーション記述ファイルのcontent部分に ajax_sample_for_air.html という名前をつけましたので、上記のHTMLをこの名前で保存し直します。
できたファイルが↓こちらです。

<pre><p class="url">C:\work\air\ajax_sample\ajax_sample_for_air.html</p></pre>


<h4 class="large-tit">AIRコンテンツの確認</h4>

AIR環境のデバッグを起動するには、ADLを利用します。

[windowsロゴ]＋[R]キーで「ファイルを指定して実行」ダイアログを表示してから、『cmd』と入力してリターンを押すと、コマンドプロンプトが表示されます。

ここからプロジェクトフォルダに移動します。
このときのディレクトリ構成は↓このようになっています。

<pre><p class="prompt">
  cd C:\work\air\ajax_sample\
  dir
  (...)ajax_sample_for_air.html
  (...)ajax_sample_for_air.xml
  (...)ajax_sample_for_web.html
  (...)js
  (...)request.txt
  ※dirの時刻とサイズは割愛しました
</p></pre>

この状態から、ADLを実行します。

<pre><p class="prompt">
  adl ajax_sample_for_air.xml
</p></pre>

■注記１：
下記のエラーが出るようであれば、『環境変数』の設定に失敗しています。もう一度確認してください。

&gt; 'adl' は、内部コマンドまたは外部コマンド、
&gt; 操作可能なプログラムまたはバッチ ファイルとして認識されていません。

■注記２：
もし、↓このようなエラーが出てしまうようであれば、アプリケーション記述ファイルのapplicationタグ部分を確認してください。

  invalid application descriptor: descriptor version does not match runtime version

AIR1.0では下記のようになっています。

  &lt;application xmlns="http://ns.adobe.com/air/application/1.0"&gt;
  ※ベータ版では、http://ns.adobe.com/air/application/1.0.M4 などとなっていたはずです

■注記３：
もし、↓このようなエラーが出てしまうようであれば、

  invalid application descriptor: invalid application identifier

アプリケーション記述ファイルをよく確認してみてください。複数行になっていたり、idタグに _(アンダーバー) が入っていたりすると表示されるようです。

↓このような画面が表示されて、[Load]ボタンでrequest.txtの内容が表示されればOKです。

<img alt="img_adl01.jpg" src="http://www.tkrb.jp/guide/2008/03/12/img_adl01.jpg" width="470" height="300" />


<h4 class="large-tit">電子署名の作成</h4>

Adobe AIRでは「署名以降にインストールファイルが変更されていない」ということと「該当の ID が発行者のもの」ということを保障するために電子署名の作成を行う必要があります。

開発中の場合は『自己証明書』を利用します。この電子署名は第三者のよる検証は行われないので一般に配布する場合はVeriSignやThawteが発行する証明書を利用した方が良いそうです。

# ただし、商用証明書は結構高価なので今後の状況を見つつ、という感じでしょうか。

とりあえず、自己証明書で進めます。

コマンドプロンプトから、ADTを実行します。

<pre><p class="prompt">
  cd C:\work\air\ajax_sample\
  adt -certificate -cn SelfSigned 1024-RSA ajax_sample_for_air.pfx mypassword
</p></pre>

これで、mypassword というパスワードで自己署名した 拡張子pfx のファイルができあがります。


<h4 class="large-tit">インストールパッケージの作成</h4>

インストールパッケージは、AIRを実行するのに必要な全ファイルと上記で作成した電子証明書ファイルを指定して、パッケージファイルを作成します。

コマンドプロンプトから、ADTを実行します。

<pre><p class="prompt">
  cd C:\work\air\ajax_sample\
  adt -package -storetype pkcs12 
      -keystore ajax_sample_for_air.pfx 
      -storepass mypassword ajax_sample.air 
      ajax_sample_for_air.xml ajax_sample_for_air.html js request.txt
  ※上記は改行せずに入力してください
</p></pre>

「ajax_sample.air」が作成できたら、ダブルクリックで実行してみましょう。
(実行できないようなら、AIRランタイムを入れてください)

AIRのアプリケーション インストーラーが表示されたら、[Install]からインストールできます。

インストール後、exeファイルをダブルクリックすると、ADLでテストしたのと同様のウィンドウで実際に動作することが確認できます。


<h4 class="large-tit">Webにアップする (インストールバッジの作成)</h4>

AIR 1.0からは、ユーザがWebからパッケージをインストールするために、インストールバッジというFlashを表示できるようになっています。

インストールバッジは下記からダウンロードできます。

<ul class="icon-list"><li><a href="http://labs.adobe.com/wiki/index.php/AIR_Badge" target="_blank">AIR Badge</a></li></ul>

ダウンロード後は、zipを展開してできたファイルのうち、下記のファイルをプロジェクトフォルダにコピーします。

- swfobject.js
- AIRInstallBadge.swf
- expressinstall.swf

ただ、AIRのファイルと混ざるとあまり嬉しくないので installer というフォルダを作ってその中に入れました。

<pre><p class="url">C:\work\air\ajax_sample\installer\</p></pre>


次は、インストールバッジを表示するHTMLの作成です。

<pre><p>
&lt;script type="text/javascript" src="js/prototype.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="js/swfobject.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
document.observe("dom:loaded", initialize, false);
function initialize() {
	var so = new SWFObject("AIRInstallBadge.swf", "AIRBadge", "215", "180", "9.0.115", "#FFFFFF");
	so.useExpressInstall('expressinstall.swf');
	so.addVariable("airversion", "1.0");
	so.addVariable("appname", "<span class="attention">Ajax sample</span>");
	so.addVariable("appurl", "<span class="attention">http://okra.ark-web.jp/~takemura/public/js/adobe_air/ajax_sample/ajax_sample.air</span>");
	so.write("<span class="attention">badgePlaceHolder</span>"); //"badgePlaceHolder"はバッジのSWFを表示するdiv要素のid
}
&lt;/script&gt;
(...)
&lt;div id="<span class="attention">badgePlaceHolder</span>" style="width:215px; height:180px;"&gt;
</p></pre>

アクセスしてみると、『バッジ』が表示されます。

<h5 class="small-tit">Ajax sample のインストールバッジ</h5>
<iframe src="http://okra.ark-web.jp/~takemura/public/js/adobe_air/ajax_sample/installer/" width="470" height="240">
<a href="http://okra.ark-web.jp/~takemura/public/js/adobe_air/ajax_sample/installer/" target="_blank">iframeをご覧になれない場合はこちらをどうぞ</a></iframe>

カッコイイですね☆

Flashの指定バージョン(9.0.115)がインストールされていない場合は↓このような自動アップデートが実行されます。

<img alt="install_dialog.jpg" src="http://www.tkrb.jp/guide/2008/03/12/install_dialog.jpg" width="224" height="188" />


[Install Now]をクリックして、[Open]をクリックして、[Install]をクリックして、インストールが完了すると、アプリケーションが実行されます。

Web版で作ったのと同じく[Load]ボタンを押すと、Ajaxでrequest.txtを読み込んで出力しています。


<h4 class="large-tit">サンプル２：JavaScriptライブラリを利用したフォーム入力チェックのAIR化</h4>

AIRのアプリケーションを作る流れは理解できたと思いますが、何となくこれだけだとサンプルが寂しいので、フォーム入力チェックをするWebアプリをAIR化してみました。

今回、利用するJavaScriptのフォーム入力チェックのライブラリは↓こちらです。

<ul class="icon-list"><li><a href="http://blog.masuidrive.jp/articles/2006/01/05/validation" target="_blank">Realtime validation using Ajax</a></li></ul>

ユーザが入力したそばから入力チェックが走るライブラリです。

<h4 class="large-tit">フォーム入力チェックのWeb版を制作</h4>

<h5 class="small-tit">フォーム入力チェック サンプル (Web版)</h5>
<iframe src="http://okra.ark-web.jp/~takemura/public/js/adobe_air/form_sample/form_sample_for_web.html" width="470" height="240">
<a href="http://okra.ark-web.jp/~takemura/public/js/adobe_air/form_sample/form_sample_for_web.html" target="_blank">iframeをご覧になれない場合はこちらをどうぞ</a></iframe>

このライブラリのValidator.register関数の設定については↓こちらに試行錯誤したメモを残しましたので参考までにどうぞ。

<ul class="icon-list"><li><a href="http://www.ark-web.jp/sandbox/wiki/308.html" target="_blank">JavaScript/自動で入力チェックを行うJavaScriptライブラリ/Realtime validation using Ajaxの設定方法</a></li></ul>


<h4 class="large-tit">AIRアプリをWebにアップするまで一気におさらい</h4>

<h5 class="small-tit">アプリケーション記述ファイルの作成</h5>

ファイルを↓このように作り、内容を書き込みます。

<pre><p class="url">C:\work\air\form_sample\form_sample_for_air.xml</p></pre>

<pre><p class="code">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;application xmlns="http://ns.adobe.com/air/application/1.0"&gt;
  &lt;id&gt;jp.ark-web.okra.takemura.public.js.adobeair.form-sample.sample&lt;/id&gt;
  &lt;version&gt;0.1&lt;/version&gt;
  &lt;filename&gt;AIR_Form_sample&lt;/filename&gt;
  &lt;name&gt;AIR sample: Form sample&lt;/name&gt;
  &lt;initialWindow&gt;
    &lt;content&gt;form_sample_for_air.html&lt;/content&gt;
    &lt;systemChrome&gt;standard&lt;/systemChrome&gt;
    &lt;transparent&gt;false&lt;/transparent&gt;
    &lt;visible&gt;true&lt;/visible&gt;
  &lt;/initialWindow&gt;
&lt;/application&gt;
</p></pre>


<h5 class="small-tit">AIRコンテンツの制作</h5>

上記で制作したWeb版をコピーして名前を form_sample_for_air.html に変えて保存します。

<pre><p class="url">C:\work\air\form_sample\form_sample_for_air.html</p></pre>


<h5 class="small-tit">AIRコンテンツの確認</h5>

[windowsロゴ]＋[R]キーで「ファイルを指定して実行」ダイアログを表示してから、『cmd』と入力してリターンを押して、コマンドプロンプトを表示。

プロジェクトフォルダに移動後、ADLを実行します。

<pre><p class="prompt">
   cd C:\work\air\form_sample\
   adl form_sample_for_air.xml
</p></pre>

デバッグして問題なければ次へ進みましょう。

<h5 class="small-tit">電子署名の作成</h5>

<pre><p class="prompt">
  adt -certificate -cn SelfSigned 1024-RSA form_sample_for_air.pfx **********
  ※実パスワードは「**********」と伏せておきました、適当なパスワードを入れてください
</p></pre>


<h5 class="small-tit">インストールパッケージの作成</h5>

<pre><p class="prompt">
  adt -package -storetype pkcs12 
      -keystore form_sample_for_air.pfx 
      -storepass ****** form_sample.air 
      form_sample_for_air.xml form_sample_for_air.html js images
  ※上記は改行せずに入力してください
</p></pre>

「form_sample.air」が作成できたら、ダブルクリックで実行して、インストールして内容をチェックします。

アンインストールは、[Windowsロゴ]＋[C]キーでコントロールパネルを開いて、「プログラムの追加と削除」から『AIR sample: Form sample』を削除します。


<h5 class="small-tit">Webにアップする (インストールバッジの作成)</h5>

installer フォルダを作って、AIR Badgeの下記ファイルをコピーします。

- swfobject.js
- AIRInstallBadge.swf
- expressinstall.swf

<pre><p class="url">C:\work\air\ajax_sample\installer\</p></pre>

インストールバッジを表示するHTMLの作成。

<pre><p class="code">
&lt;script type="text/javascript" src="js/prototype.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="js/swfobject.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
document.observe("dom:loaded", initialize, false);
function initialize() {
	var so = new SWFObject("AIRInstallBadge.swf", "AIRBadge", "215", "180", "9.0.115", "#FFFFFF");
	so.useExpressInstall('expressinstall.swf');
	so.addVariable("airversion", "1.0");
	so.addVariable("appname", "Form sample");
	so.addVariable("appurl", "http://okra.ark-web.jp/~takemura/public/js/adobe_air/form_sample/form_sample.air");
	so.write("badgePlaceHolder"); //"badgePlaceHolder"はバッジのSWFを表示するdiv要素のid
}
&lt;/script&gt;
(...)
&lt;div id="badgePlaceHolder" style="width:215px; height:180px;"&gt;
</p></pre>

バッジにアクセスします。

<h5 class="small-tit">Form sample のインストールバッジ</h5>
<iframe src="http://okra.ark-web.jp/~takemura/public/js/adobe_air/form_sample/installer/" width="470" height="240">
<a href="http://okra.ark-web.jp/~takemura/public/js/adobe_air/form_sample/installer/" target="_blank">iframeをご覧になれない場合はこちらをどうぞ</a></iframe>

[Install Now]をクリックして、[Open]をクリックして、[Install]をクリックして、インストールが完了し、アプリケーションを実行して内容を確認します。


<h4 class="large-tit">まとめ</h4>

今回はAdobe AIRの制作環境を構築し、ユーザに配布するまでの流れを順を追って説明していきました。最後に超特急でしたが全工程をおさらいもしましたし、これで制作と配布はできるようになったと思います。

また、Web版のHTML+JavaScriptのコードをそのままAIRにパッケージ化することができることもお分かりいただけたと思います。

次回は、もう少し具体的にまた何かマッシュアップしつつ、AIRが提供するAPIとの連携をやりたいと思っています。では、また次回お楽しみに☆

]]>
   </content>
</entry>
<entry>
   <title>ひとりサービスをリリースするまでやっておくこと10個</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/03/14.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.61</id>
   
   <published>2008-03-05T02:20:29Z</published>
   <updated>2008-03-05T05:12:51Z</updated>
   
   <summary> Ruby（とRails）を担当している石原です。 ソーシャル「OSを入れた後に...</summary>
   <author>
      <name>石原 淳也</name>
      
   </author>
         <category term="Ruby" scheme="http://www.sixapart.com/ns/types#category" />
   
   <category term="100" label="exception notifier" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="96" label="google adsense" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="98" label="google analytics" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="94" label="montastic" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="49" label="rails" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="103" label="slow query log" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="101" label="バックアップ" scheme="http://www.sixapart.com/ns/types#tag" />
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[Ruby（とRails）を担当している石原です。

ソーシャル「OSを入れた後にインストールする10のアプリケーション」(仮) を作る過程をレポートしています。

今回は、開発はひとやすみにして、サービスをリリースする前にやっておくべき細々としたことを紹介したいと思います。

これらは筆者がひとりサービスをリリースするときに、毎回なんとなく思い出しながらやってきたことで、サービスによっては忘れてしまっているものもあります。

ですから、次からはこれをチェックリスト代わりに使おうと思っています。

Rails に限った話とWebサービス一般の話が混ざっています。ほかの言語やフレームワークを使っている方にも、Rails に限った話を自分の使っている環境に読み替えれば、いくらか参考になるかもしれません。

参考までにこれまでのエントリーはこちらです ↓

<ol>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/10/ruby_on_rails_1.html">つくるぶガイドブログ: Ruby on Rails を使ってひとりでサービスを作ってみよう</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/11/post_2.html">つくるぶガイドブログ: ひとりサービスの雛型をつくる(リキッドレイアウト、GetText、Acts as Authenticated)</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/11/scriptgen.html">つくるぶガイドブログ: Rails で楽々ソーシャルブックマークの仕組みを作る</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/12/rails_acts_as_taggable_redux.html">つくるぶガイドブログ: Rails プラグイン acts_as_taggable_redux でタグクラウドを作ろう</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/01/rails_ajax.html">つくるぶガイドブログ: ドラッグアンドドロップで並べ替え(Rails + Ajax)</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/01/rails_rcov.html">つくるぶガイドブログ: Rails + rcov でテストカバレッジを調べる</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/02/capistrano_rails.html">つくるぶガイドブログ: capistrano で本番環境にデプロイ</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/02/rails_yahoo_api_widgets_tabnav.html">つくるぶガイドブログ: Rails で Yahoo 検索 API + Widgets Tabnav でタブナビゲーション</a></li>
</ol>
]]>
      <![CDATA[<h4 class="large-tit">1. アプリケーションエラーをメールで通知する</h4>

サービスをリリースした後、ユーザーが使っていて、アプリケーションエラーが出た場合に即座にそれをメールで通知するようにしておきます。

Rails には Exception Notifier というプラグインが用意されています。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

script/plugin install exception_notification

</pre>

でインストールし、アプリケーションコントローラーに次のように一行追加します。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">

== app/controllers/application.rb ==

class ApplicationController < ActionController::Base
  include ExceptionNotifiable

</pre>

environment.rb に通知先のメールアドレスを記述して完了です。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">

== config/environment.rb ==

ExceptionNotifier.exception_recipients = %w(webmaster@10best.champierre.com)

</pre>

<h4 class="large-tit">2. エラーページをカスタマイズする</h4>

Production 環境でアプリケーションエラーが発生したとき(500エラー)、または該当するページがないとき(404エラー)には、以下のようなRailsデフォルトのエラーページが表示されます。

<img alt="500.png" src="http://www.tkrb.jp/guide/500.png" width="400" height="259" />

しかし、これではあまりに素っ気ないので、サービスのロゴやフッターを入れたり、トップページへ戻るリンクをつけておきましょう。

修正するファイルは public/404.html と public/500.html です。

<h4 class="large-tit">3. Javascript を無効にしているユーザー向け対策をおこなう</h4>

ユーザーが Javascript を有効にしていること前提で Ajax を使ったりしてこれまで開発してきました。

企業などのサイトであれば、Javascript を無効にしている場合でもちゃんとサイトの機能が動くように開発する必要があるのかもしれません。

しかし、ひとりサービスなら割り切って Javascript を無効にしているユーザーには、「ごめんなさい。Javascript を有効にしてください」といったメッセージを表示するだけで構わないと思っています。

すべてのページが共有しているような、例えば _header.rhtml に以下のように &lt;noscript> タグで囲んだメッセージを入れておきます。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">

== app/views/shared/_header.rhtml ==

  &lt;noscript>&lt;span id="noscript">このサイトではJavaScriptを使用しています。ブラウザの設定でJavaScriptを有効にしてからお使いください。&lt;/span>&lt;/noscript>

</pre>

<h4 class="large-tit">4. フッターのコピーライト表示を常に最新にしておく</h4>

Copyight ©2007-2008 champierre.com All rights reserved.

のようにフッターのコピーライト表示にサービス開始年と現在の年を記述すると思います。

年を越したときなど、この現在の年の部分をしばらくアップデートし忘れてしまうことがあります。

ずっと忘れてしまっていてこの部分の表示が古いと、サービスが継続しているにも関わらず、「このサービスってもうやってないのかな」とユーザーに誤解を与えてしまいかねません。忘れず更新しておきましょう。

面倒なら、いっそこの部分を以下のようにして、自動的に更新されるようにしてもいいかもしれません。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">

Copyright &copy;2007-#{Time.now.year} champierre.com All rights reserved.

</pre>

<h4 class="large-tit">5. slow query ログを送るようにしておく</h4>

筆者は MySQL をつかっているので、MySQL を例に挙げます。

筆者の経験だと、Rails でアプリケーションのレスポンスが遅いほとんどのケースは、処理に時間がかかるクエリーがある場合です。

&raquo; <a target="_blank" href="http://d.hatena.ne.jp/tokuhirom/20060629/1151544664">mysql のスロークエリログを集計するの術 - TokuLog 改め だまってコードを書けよハゲ</a>

を参考に、定期的に slow query を自分あてにメールしてチェックしています。

&raquo; <a target="_blank" href="http://dev.mysql.com/doc/refman/4.1/ja/slow-query-log.html">MySQL :: MySQL 4.1 リファレンスマニュアル :: 4.10.5 スロークエリログ</a>

などを参考にして、事前に MySQL の slow query log を有効にしておく必要があります。

<h4 class="large-tit">6. DBのバックアップを定期的におこなう仕組みを作っておく</h4>

筆者は開発に subversion を使っているため、ソースコードはレポジトリと本番サーバー、それに開発マシンの最低三カ所に分散しているので、ソースコードがなくなってしまう心配はしていません。

しかしデータベースの内容は別です。

あらかじめ、定期的に mysqldump をおこない、べつのサーバーに rsync する仕組みを作っておきましょう。

英語ですが、以下の記事が非常に参考になります。

&raquo; <a target="_blank" href="http://howtoforge.com/rsync_incremental_snapshot_backups">Create Incremental Snapshot-style Backups With rSync And SSH | HowtoForge - Linux Howtos and Tutorials</a>

ここに書かれていることをほぼそのまま実行しています。

<h4 class="large-tit">7. サイトのアクセス解析をおこなう</h4>

サービスをリリースして気になるのは、どのくらいのユーザーが訪れてくれていて、ページビューがどのくらいか、ということでしょう。

これを知るにはアクセス解析が必要です。

筆者は、無料で機能豊富な

&raquo; <a target="_blank" href="http://www.google.co.jp/analytics/ja-JP/">Google Analytics</a>

を使っています。

すべてのページが使うレイアウトファイルなどに以下のように、production 環境のときだけ partial ビューに切り出した Google Analytics のタグを埋め込むようにしています。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">

== app/views/layouts/application.rhtml ==

&lt;%= render(:partial => "shared/google_analytics") if RAILS_ENV == 'production' -%>
&lt;/body>
&lt;/html>

</pre>

<h4 class="large-tit">8. Google Adsense のタグをはっておく</h4>

サービスが現金を稼ぐようになれば、もっとたくさんのユーザーに訪れてもらおうと、サイトをより良くして育てる大きなモチベーションとなるでしょう。

もっとも手軽な方法が

&raquo; <a target="_blank" href="http://www.google.com/adsense">Google Adsense</a>

です。

アカウントを登録し、発行されたタグを Google Analytics でおこなった方法同様、production 環境にだけ表示するようにしておきます。

<h4 class="large-tit">9. セッションファイルを定期的に削除する仕組みをつくっておく</h4>

Rails のデフォルトの設定では、セッションのデータがアプリケーションディレクトリの下、tmp/sessions 以下に ruby_sess.fee2a644dcba97c0 といったファイルに保存されます。

放っておくと、/tmp/sessions 以下でファイルが増え続けるので定期的に削除しておきましょう。

たとえば、cron に以下のように設定して、10日以上前のセッションファイルを削除するようにしておきます。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

0 10 * * * /usr/bin/find /appfolder/tmp/sessions/ -name 'ruby_sess.*' -type f -mtime +10 | /usr/bin/xargs /bin/rm -f

</pre>

<h4 class="large-tit">10. サイトの死活監視をおこなう</h4>

サイトが知らないうちに止まっていたなんてことがないよう、筆者は

&raquo; <a target="_blank" href="http://www.montastic.com/">Montastic: the free website monitoring service</a>

という無料サービスを使っています。

定期的にサイトがアクセス可能かどうかをチェックして、もしアクセスできなくなっていたらメールで通知してくれます。

通知先を携帯メールなどにしておけばいち早くサイトダウンを知り、対応することができるでしょう。

<h4 class="large-tit">まとめ</h4>

「ひとりサービスをリリースするまでやっておくこと10個」を紹介しました。

これからサービスを作ろうと思っている方の参考になれば幸いです。

今回の内容を反映して修正したソースコードを以下にアップロードしておきます。

&raquo <a href="http://code.google.com/p/10best/">10best - Google Code
</a>

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

svn checkout http://10best.googlecode.com/svn/trunk/ 10best

</pre>

でチェックアウトするか、ブラウザでも<a href="http://10best.googlecode.com/svn/trunk/">リポジトリ</a>を閲覧することができます。 
]]>
   </content>
</entry>
<entry>
   <title>lightboxをカスタマイズしてみよう（中編）- 角丸対応ライブラリとCanvas</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/02/lightbox_canvas.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.60</id>
   
   <published>2008-02-29T13:33:32Z</published>
   <updated>2008-02-29T14:57:44Z</updated>
   
   <summary> ※このエントリは「Lightboxのデザインをカスタマイズしてみよう（前編）」...</summary>
   <author>
      <name>小森 由佳</name>
      
   </author>
         <category term="HTML/CSS" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[<img alt="image_main.jpg" src="http://www.tkrb.jp/guide/2008/02/29/image_main.jpg" width="480" height="303" />

※このエントリは「<a href="http://www.tkrb.jp/guide/2008/01/post_6.html" target="_blank">Lightboxのデザインをカスタマイズしてみよう（前編）</a>」のつづきです。

こんにちは、HTML+CSS担当の小森です。
ずいぶん間が空いてしまいましたが、<a href="http://www.tkrb.jp/guide/2008/01/post_6.html" target="_blank">前回</a>の続きです。前回は、CSSだけでlightboxを角丸にする方法をいろいろと模索したものの、サイズ可変に対応できなかった…というところで終わりました。]]>
      <![CDATA[<h4 class="large-tit">角丸でサイズ可変OKにできないの？</h4>

なんと、すでにそんなライブラリがありました。。あっさり解決！　orz

<p class="url">
Lightview
<a href="http://www.nickstakenburg.com/projects/lightview/" target="_blank">http://www.nickstakenburg.com/projects/lightview/</a>
</p>

冒頭の画面はこのライブラリを使ったものです。きれいに丸いです。
このライブラリはスライドショー機能もついていて、リサイズの動きもスムーズでなかなかかっこいいです。また角丸の丸さやエフェクトの設定といった細かいオプション設定ができるほか、画像表示だけでなくFlashや動画、iframeなどにも対応しており、見た目だけでなく機能も充実していてなかなかのライブラリです。

使用にはprototype.jsとScript.aculo.usが必要ですが、lightbox同様これらをheadで読み込み、サムネイル側のaタグにclassをつけるだけ、と導入は簡単です。

動きなど<a href="http://www.tkrb.jp/guide/2008/01/post_6.html" target="_blank">前回</a>のサンプルと比較してみてください。

<p class="url">
<a href="http://okra.ark-web.jp/~komori/tkrb/lightview/01.html" target="_blank">※Lightview版デモ</a>
</p>



<h5 class="small-tit">ソースはどうなってる？</h5>

さて、角丸を実現しているこのライブラリのソースはどのようになっているのでしょうか？

<img alt="image_stracture.jpg" src="http://www.tkrb.jp/guide/2008/02/29/image_stracture.jpg" width="480" height="120" />

やや複雑ですが、ここでは必要な要素だけ図に表してみました。

<ul class="common-list">
<li>まず角丸レイヤー全体はulリストで構成されていて、上から上角丸エリア、写真エリア、下角丸エリアと3つのliでマークアップしています。</li>
<li>一番上と一番下のliにはそれぞれにdiv（div.lv_Liquid）を追加し、幅100%を設定します。</li>
<li>さらに右寄せのulと左寄せのulをそれぞれfloatして配置し、中のliには幅50%を設定します。</li>
<li>その中にそれぞれ右隅と左隅に角丸を配置しています。隣に白背景のdiv（lv_fill）を配置してつなげることで、角丸を表現しています。</li>
</ul>


右端と左端に角丸を置く、という方法はテーブルレイアウト時代の角丸のつくりかたを彷彿とさせますね。このjsから生成したタグははテーブルを使わずdivやulで構成されているものの、かなりの入れ子になってしまっていることは否めません。

とはいえ、画像サイズ可変を満たすならば、現状ではこのようなソースを使用するのが現実解なのかもしれません。



<h4 class="large-tit">canvasタグとroundrect</h4>

さて、ちょっと横道ですが、ここで注目したいのは、このライブラリでは角丸にあたる箇所に画像ではなく<strong>canvasタグ</strong>というものが使用されていることです。

<h5 class="small-tit">canvasとは？</h5>
canvasとは、ブラウザ上に図を描くために策定された仕様で、
Flashのようなプラグインを使わずに、JavaScriptベースで図を描くことができます。
ただし、Safari 2，Firefox 1.5，Opera 9でサポートしているものの、IEは対応していません。

canvasについてはここの記述がわかりやすいです。

<p class="url">
Canvas - Canvasとは - HTML5.JP
<a href="http://www.html5.jp/canvas/what.html" target="_blank">http://www.html5.jp/canvas/what.html</a>
</p>

canvasを使うと、ブラウザだけでこんなグラフも表現できてしまう。すごい何これ。

<p class="url">
Canvasによる円グラフの例 - HTML5.JP
<a href="http://www.html5.jp/canvas/circlegraph_sample.html" target="_blank">http://www.html5.jp/canvas/circlegraph_sample.html</a>
</p>

Lightviewは、このcanvasを角丸の表現にうまく使っているわけですね。


<h5 class="small-tit">IEではどうなの？</h5>
さて上記でcanvasはIE非対応、と書きました。ではIE用にはどう対応しているかというと、このライブラリではcanvasの代わりに、roundrectが描画されています。
roundrectとは、VMLのタグです。

<blockquote>VML とは Vector Markup Language の略で、Webページ上で線や円などのベクトル図形を表示するための言語です。IE5.0 以上でサポートされています。Netscape ではサポートされていません。
（...中略....）将来的には SVG に移行する予定です。
<a href="http://www.tohoho-web.com/wwwvml.htm" target="_blank">とほほのVML入門</a>
</blockquote>

と、すでに古い技術のようなのですが、いわばIE用のcanvasといっていいのでしょうか。IE限定対応と考えると、こういう枯れた？技術を使うというのも手なんですね。

下記をIEで見ると、描画されたroundrectを見ることができます。

<p class="url">
とほほのVML入門
<a href="http://www.tohoho-web.com/wwwvml.htm#roundrect" target="_blank">http://www.tohoho-web.com/wwwvml.htm#roundrect</a>
</p>

直接HTMLからは外れてしまいましたが、ブラウザがこういった新しい技術をサポートしていけばいろいろ面白いことができそうですよね。せっかくなのでどこかで使ってみたいな、と思いました。

（上記URLで紹介されているように、IE6以上に対応したエミュレートライブラリを使えば実践的にもいろいろできそうです）

というわけで、角丸の試みは、対応ライブラリを使ってみる、という結論に？
次回、あとちょっとだけ続きます。
]]>
   </content>
</entry>
<entry>
   <title>Restletとjson-lib を使ったWebサービスへのアクセス</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/02/restletjsonlib_web.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.59</id>
   
   <published>2008-02-25T13:50:22Z</published>
   <updated>2008-03-12T10:12:12Z</updated>
   
   <summary> 前回の内容は、標準Java APIを使用を使用してテクノラティのWebサービス...</summary>
   <author>
      <name>ATL Enterprise Architecture Unit</name>
      
   </author>
         <category term="Java" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[<a href="http://www.tkrb.jp/guide/2008/02/java_apiweb.html">前回の内容</a>は、標準Java APIを使用を使用してテクノラティのWebサービスにアクセスしました。今回は、<a href="http://www.tkrb.jp/guide/2008/01/stru.html">前々回</a>で紹介した標準Java API以外のライブラリを利用して、Webサービスにアクセスしてみたいと思います。]]>
      <![CDATA[今回は、前々回も紹介したRestletとjson-libを用います。Webサービスを呼ぶのに、Restlet を使い、返ってきた JSONをパースしてJavaのオブジェクトにするのに、json-lib を用いています。
Restletは、RESTfulなサービスを作成するためのライブラリですが、クライアントとして用いて、RESTful なサービスにアクセスする機能も存在しています。

今回サンプルとして作成するものですが、前回は、「イベント検索アプリケーション」にあるイベントに関連したブログエントリも、イベント情報として提示するために、テクノラティのWebサービスを使ってブログ検索をしましたが、今回は、そのテクノラティのWebサービスを使って検索されたブログが、はてなブックマークでどのぐらいブックーマークされているのかを表示するように、前回のコードに追加したいと思います。複数のサービスを利用し、求める機能を作成するのもマッシュアップらしいコードと言えるでしょう。

はてなブックマークに関する情報を得るAPI（以降はてなブックマークエントリー情報取得APIと呼びます）は、テクノラティのサービスと違ってメンバー登録しないでも利用できます。
「http://b.hatena.ne.jp/entry/json/http://www.hatena.ne.jp/」といったように、「http://b.hatena.ne.jp/entry/json/＋ブックマーク数を知りたいURL」というように、RESTfulライクにアクセスすることで、JSONの形式で、指定したURLのはてなブックーマークされている数（以降はてブ数と呼びます）や、コメントの配列を得ることができます。

以下にはてなブックマークエントリー情報取得APIで検索した結果のJSONの例を示します（ http://www.hatena.ne.jp/ に対してブックマークされてる情報を取得した場合）

<p class="code">
({"count":"1495",
&nbsp;&nbsp;"url":"http://www.hatena.ne.jp/",
&nbsp;&nbsp;"bookmarks":[{"timestamp":"2008/02/24&nbsp;18:22:27","comment":"","user":"user1","tags":[]},
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{"timestamp":"2008/02/24&nbsp;18:09:36","comment":"","user":"user2","tags":[]}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;～&nbsp;途中略&nbsp;～
&nbsp;})&nbsp;&nbsp;
</p>

APIの仕様の詳細は、<a target="_blank" href="http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%d6%a5%c3%a5%af%a5%de%a1%bc%a5%af%a5%a8%a5%f3%a5%c8%a5%ea%a1%bc%be%f0%ca%f3%bc%e8%c6%c0API?kid=184075">はてなのサイト</a>を参照してください。

早速ですが、今回のサンプルを提示します。まずは、前回のサンプルにあった、検索結果を格納するクラスである Weblogクラスに、はてブ数を入れるための、int型のフィールドとそれに対するgetter/setterを用意します。

コード1 検索結果を格納するクラス 
<p class="code">
public&nbsp;class&nbsp;Weblog&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;name;
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;url;
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;title;
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;String&nbsp;permalink;
&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;int&nbsp;hatebuCount;
&nbsp;&nbsp;&nbsp;&nbsp;～&nbsp;途中略&nbsp;～
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;int&nbsp;getHatebuCount()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;hatebuCount;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;setHatebuCount(int&nbsp;count)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.hatebuCount&nbsp;=&nbsp;count;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
}
</p>

次に、今回作るはてブ数検索をするためのインタフェースを決定します。今回はブログのurlを渡すと、はてブ数が返ってくるようなメソッドを定義します。

コード2 はてブ数検索インタフェース
<p class="code">
package&nbsp;jp.tkrb.event.services;

public&nbsp;interface&nbsp;HatebuCountSearch&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;int&nbsp;search(String&nbsp;url);
}
</p>
次に、このインタフェースを実装して、はてなブックマークエントリー情報取得APIからはてブ数を実際に取得する実装クラスを示します。

コード3 Restlet と json-lib を使った実装コード
<p class="code">
package&nbsp;jp.tkrb.event.services;

import&nbsp;java.io.IOException;

import&nbsp;org.restlet.Client;
import&nbsp;org.restlet.data.Protocol;

import&nbsp;net.sf.json.JSON;
import&nbsp;net.sf.json.JSONNull;
import&nbsp;net.sf.json.JSONObject;
import&nbsp;net.sf.json.JSONSerializer;

public&nbsp;class&nbsp;HatebuCountSearchImpl&nbsp;implements&nbsp;HatebuCountSearch&nbsp;{
public&nbsp;int&nbsp;search(String&nbsp;url)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Client&nbsp;client&nbsp;=&nbsp;new&nbsp;Client(Protocol.HTTP);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;jsonStr&nbsp;=&nbsp;client.get("http://b.hatena.ne.jp/entry/json/"&nbsp;+&nbsp;url).getEntity().getText();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JSON&nbsp;jsonObj&nbsp;=&nbsp;JSONSerializer.toJSON(jsonStr.substring(1,&nbsp;jsonStr.length()&nbsp;-&nbsp;1));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(jsonObj&nbsp;instanceof&nbsp;JSONNull)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;((JSONObject)jsonObj).getInt("count");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(IOException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;RuntimeException(e);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</p>

今回は標準Java API以外を使っているので、各々のライブラリをダウンロードし、必要な jar ファイルをクラスパスに通す必要があります。

Restlet は、<a target="_blank" href="http://www.restlet.org/">こちら</a>の Restlet のサイトの「Download」から本稿執筆時点で最新の1.0.7のファイルをダウンロードし、ダウンロードしたファイルのlibフォルダ直下の以下のファイルをクラスパスに入れてください。
<ul class="common-list">
<li>org.restlet-1.0.7.jar</li>
<li>com.noelios.restlet-1.0.7.jar</li>
<li>com.noelios.restlet.ext.net-1.0.7.jar</li>
</ul>

json-lib は<a target="_blank" href="http://json-lib.sourceforge.net/">こちら</a>のjson-lib の SourceFourge のサイトからダウンロードしてクラスパスに入れてください。
また、json-lib は以下のライブラリに依存しているので、それらもそれぞれダウンロードしてクラスパスに入れてください。
<ul class="common-list">
<li><a target="_blank" href="http://jakarta.apache.org/commons/lang/">Jakarta Commons Lang 2.3</a></li>
<li><a target="_blank" href="http://jakarta.apache.org/commons/beanutils/">Jakarta Commons BeanUtils 1.7.0</a></li>
<li><a target="_blank" href="http://jakarta.apache.org/commons/collections/">Jakarta Commons Collections 3.2</a></li>
<li><a target="_blank" href="http://jakarta.apache.org/commons/logging/">Jakarta Commons Logging 1.1</a></li>
<li><a target="_blank" href="http://ezmorph.sourceforge.net/">EZMorph 1.0.4</a></li>
</ul>

実装コードを部分的に見ていきましょう。まずは、はてなブックマークエントリー情報取得APIにアクセスするコードです。
<p class="code">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Client&nbsp;client&nbsp;=&nbsp;new&nbsp;Client(Protocol.HTTP);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;jsonStr&nbsp;=&nbsp;client.get("http://b.hatena.ne.jp/entry/json/"&nbsp;+&nbsp;url).getEntity().getText();
</p>

RestletのClientクラスのオブジェクトをプロトコルを指定して生成し、そのオブジェクトを利用して、はてなブックマークエントリー情報取得APIにアクセスし、その結果を文字列として取得しています。

Restletでは、簡単に様々なプロトコルや、様々な返却方式を扱うことができます。たとえば、XMLを返すようなサービスを利用する場合には、

<p class="code">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DomRepresentation&nbsp;domRepresentation&nbsp;=&nbsp;client.get(&nbsp;"http://example.com/service"&nbsp;).getEntityAsDom();
</p>
といったコーディングをすることで、XMLが返却された場合に、DOM オブジェクトを生成してアクセスすることが可能になります。

今回、得られた文字列はJSON形式なので、Javaで扱うために、json-libを使います。
<p class="code">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JSON&nbsp;jsonObj&nbsp;=&nbsp;JSONSerializer.toJSON(jsonStr.substring(1,&nbsp;jsonStr.length()&nbsp;-&nbsp;1));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(jsonObj&nbsp;instanceof&nbsp;JSONNull)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;((JSONObject)jsonObj).getInt("count");
</p>
最初に、「jsonStr.substring(1, jsonStr.length() - 1)」と文字列操作してる部分について説明します。この部分は、はてなが返すJSONの形式が前述の通りに「(」と小括弧で始まり、「)」と小括弧で終っているためです。ブラウザでevalしてこのJSONを使う場合には問題ないのですが、json-libでは扱えないため最初と最後の小括弧を削除しています。

次に、JSONSerializer.toJSON に小括弧を削除したJSON文字列を渡し、パースしてもらい、json-libのJSONクラスのオブジェクトに変換してもらいます。
JSONNullとinstanceofで比較しているコードは、はてブ数が0だった場合に、はてなブックマークエントリー情報取得APIが「(null)」という文字列を返すための対応です。はてブ数が0以上の場合は、countというプロパティにはてブ数が入っているので、その値を取り出して返しています。

最後に、前回作成したテクノラティのサービスを呼び出した結果を使って、ブログのURLからはてブ数を求めるためのコードを示します。
<p class="code">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TechnoratiBlogSearch&nbsp;technoratiBlogSearch&nbsp;=&nbsp;new&nbsp;TechnoratiBlogSearchSimpleImpl();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HatebuCountSearch&nbsp;hatebuSearch&nbsp;=&nbsp;new&nbsp;HatebuCountSearchImpl();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List<Weblog>&nbsp;weblogs&nbsp;=&nbsp;technoratiBlogSearch.search("デブサミ");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(Weblog&nbsp;weblog:weblogs)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;count&nbsp;=&nbsp;hatebuSearch.search(weblog.getPermalink());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;weblog.setHatebuCount(count);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
</p>
サンプルなので、テクノラティのサービスと、はてなのサービスにアクセスしに行く実装クラスを直接 new していますが、実際にはDI管理などにする点にご注意ください。
このコードでは、前回作成したTechnoratiBlogSearchを呼び出して、「デブサミ」に関するブログを検索し、結果のリストからURLを取り出して、はてブ数を、今回作ったサービスに渡して検索して、それを格納しています。

以上で今回のJavaの標準API以外のライブラリを使って、Webサービスを呼び出してサーバサイドマッシュアップするサンプルは終了です。各種ライブラリを使うことで比較的簡単にサービスを呼び出し、その結果としてのJavaオブジェクトを取得することができるのがお分かりいただけたでしょうか。今回取り上げたもの以外にも、Webサービスを呼び出すのに適したライブラリが沢山あるので他のものについても色々と使ってみてください。
]]>
   </content>
</entry>
<entry>
   <title>楽天API＋SWFAddress2.0を利用したストアのマッシュアップ</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/02/swfaddress2_store-mushup_demo_with_rakuten-api.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.58</id>
   
   <published>2008-02-25T01:30:00Z</published>
   <updated>2008-02-25T01:55:19Z</updated>
   
   <summary> こんにちは、JavaScript担当の（株）アークウェブの竹村です。 前回の『...</summary>
   <author>
      <name>竹村 光生</name>
      
   </author>
         <category term="JavaScript" scheme="http://www.sixapart.com/ns/types#category" />
   
   <category term="93" label="RIA" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="77" label="SWFAddress" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="91" label="外部テンプレート" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="90" label="楽天API" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="78" label="状態制御" scheme="http://www.sixapart.com/ns/types#tag" />
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[こんにちは、JavaScript担当の<a href="http://www.ark-web.jp/" target="_blank">（株）アークウェブ</a>の竹村です。


前回の『<a href="http://www.tkrb.jp/guide/2008/02/state_control_that_used_swfaddress2.html" target="_blank">SWFAddress2.0を利用した「状態」による機能制御</a>』でSWFAddressのによるディープリンクの作り方と、通常のイベント駆動との違いについて理解いただけたと思います。

今回はこれを利用して、JavaScriptだけで楽天APIとマッシュアップしたストアのサイトを作ってみたいと思います。

レジュメは↓このようになっています。

<ul class="common-list">
<li>Ajaxストア つくるぶ店</li>
<li>楽天のAPI</li>
<li>状態の設計</li>
<li>初期化と状態制御部分</li>
<li>楽天APIでJSONを取得するまで</li>
<li>取得したJSONの出力は<em>テンプレート</em>を外部ファイルで</li>
<li>詳細側も同じ要領で出力</li>
<li>ディープリンクにアクセスしてみる</li>
<li>まとめ</li>
</ul>

<h4 class="large-tit">Ajaxストア つくるぶ店</h4>

どういうアプリか完成品を見てみてください。
名づけて『Ajaxストア つくるぶ店』ですっ！

<p class="url"><a href="http://okra.ark-web.jp/~takemura/public/js/swfaddress2_store_demo/#/detail/?category=100317&product=wineya:664477" target="_blank">▼Ajaxストア つくるぶ店
<img alt="ajax_store.jpg" src="http://www.tkrb.jp/guide/2008/02/24/ajax_store.jpg" width="450" height="351" />
http://okra.ark-web.jp/~takemura/public/js/swfaddress2_store_demo/#/detail/?category=100317&product=wineya:664477</a>
</p>
]]>
      <![CDATA[<style type="text/css">
.attention {color:red; font-weight:bold;}
</style>
<h4 class="large-tit">楽天のAPI</h4>

まずはマッシュアップする楽天のAPIについて紹介します。

<ul class="icon-list">
<li><a href="http://webservice.rakuten.co.jp/" target="_blank">楽天ウェブサービスセンター</a><br/>
楽天APIのトップページ</li>
<li><a href="http://webservice.rakuten.co.jp/rule/" target="_blank">楽天ウェブサービス規約</a><br/>
楽天APIの利用規約です。アフィリエイト事項や制限事項は確認しましょう</li>
<li><a href="http://webservice.rakuten.co.jp/api/itemsearch/" target="_blank">楽天商品検索API</a><br/>
商品を検索するためのAPI</li>
<li><a href="http://webservice.rakuten.co.jp/api/itemcodesearch/" target="_blank">楽天商品コード検索API</a><br/>
商品詳細を取得するためのAPI</li>
<li><a href="http://webservice.rakuten.co.jp/api/genresearch/" target="_blank">楽天ジャンル検索API </a><br/>
ジャンルを取得するためのAPI</li>
</ul>

まずは、トップページのメニューにある「ご利用方法」の「デベロッパーIDを取得」から指示された内容にしたがってデベロッパーIDを作ります。

これだけでAPIへのアクセスが可能になりました。
↓下記の[YOUR_developerID]を変更して、１行にしてブラウザのアドレスバーに入力して楽天APIへアクセスしてみてください。

<p class="url">http://api.rakuten.co.jp/rws/1.11/json?
developerId=<strong>[YOUR_developerID]</strong>
&operation=ItemSearch
&version=2007-10-25
&callBack=dummyFunction
</p>

↓このような画面が表示されたと思います。

<h5 class="small-tit">楽天API実行サンプル</h5>
<iframe src="http://api.rakuten.co.jp/rws/1.11/json?developerId=9ad39aef6903b01ca205151220b7c47e&operation=ItemSearch&version=2007-10-25&genreId=100317&callBack=dummyFunction" width="470" height="200">
<a href="http://api.rakuten.co.jp/rws/1.11/json?developerId=9ad39aef6903b01ca205151220b7c47e&operation=ItemSearch&version=2007-10-25&genreId=100317&callBack=dummyFunction" target="_blank">iframeをご覧になれない場合はこちらをどうぞ</a></iframe>

URLにcallBackを指定しているので、JSONPとして出力するように指示しています。
よって、dummyFunction({JSON}); という形式になっています。

APIの入力値と返されるJSON値については、楽天APIのサイトから確認してください。


<h4 class="large-tit">状態の設計</h4>

APIで商品情報を取得する準備ができたところで、このWebサイトの設計をしましょう。

今回の肝は「ストアをSWFAddressによって状態管理する」ということですので、まずは状態の設計をします。

イメージとしては、↓こんな感じで考えていました。

<img alt="layout.jpg" src="http://www.tkrb.jp/guide/2008/02/24/layout.jpg" width="367" height="446" />

<ul class="common-list">
<li>カテゴリ選択トップ</li>
<li>商品一覧</li>
<li>商品詳細</li>
</ul>

このそれぞれの段階でディープリンクが生成されるようにします。
※トップはディープリンクを外した状態にします


<h4 class="large-tit">初期化と状態制御部分</h4>

まずは初期化とSWFAddressによるディープリンク変更が起こった時に状態を制御するcontrollerを見ていきましょう。

<pre><p class="code">
&lt;script type="text/javascript" src="js/jsr_class.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="js/prototype.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="js/swfaddress.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
// グローバル変数
(...)
// === 初期化 =====
document.observe("dom:loaded", initialize, false);
function <span class="attention">initialize</span>() {
  // カテゴリ変更時のイベントハンドラの設定
  $('form-categoryselect').onclick = function(){ 
    <span class="attention">setSWFAddress('listing', $F('form-category'));</span>
  };
  
  // SWFAddressのイベント設定
  <span class="attention">SWFAddress.addEventListener(SWFAddressEvent.CHANGE, controller);</span>
}
function <span class="attention">controller</span>(event) {
  var iCategory = <span class="attention">SWFAddress.getParameter('category')</span>;
  giCategory    = iCategory;
  var iProduct  = <span class="attention">SWFAddress.getParameter('product')</span>;
  switch(<span class="attention">SWFAddress.getPath()</span>) {
    default:
    break;
    case 'listing':
      if (iCategory == '') { alert('カテゴリが選択されていません'); return; };
      resetGlobalJson();
      sendListing(iCategory);
    break;
    case 'detail':
      if (iCategory == '') { alert('カテゴリが選択されていません'); return; };
      if (iProduct == '') { alert('商品が選択されていません'); return; };
      sendDetail(iCategory, iProduct);
      sendListing(iCategory);
    break;
  }
}
(...)
// === 共通関数 =====
function <span class="attention">setSWFAddress</span>(sPath, iCategory, iProduct) {
  switch (sPath) {
    case 'listing':
      <span class="attention">SWFAddress.setValue('listing/?category='+iCategory);</span>
    break;
    case 'detail':
      <span class="attention">SWFAddress.setValue('detail/?category='+iCategory+'&product='+iProduct);</span>
    break;
    default:
      <span class="attention">SWFAddress.setValue('');</span>
  }
}
(...)
&lt;/script&gt;
(...)
&lt;div id="category-select"&gt;
&lt;p&gt;■カテゴリ選択&lt;/p&gt;
&lt;select <span class="attention">id="form-category"</span> name="category"&gt;
&lt;option value="100227"&gt;食品&lt;/option&gt;
&lt;option value="100317"&gt;ワイン&lt;/option&gt;
&lt;option value="551167"&gt;スイーツ&lt;/option&gt;
&lt;/select&gt;
&lt;input type="button" <span class="attention">id="form-categoryselect"</span> name="button" value="カテゴリ決定" /&gt;
</p></pre>

initialize関数のイベントハンドラの設定はフォームの値を参照して、共通関数に記述したsetSWFAddress関数を呼んでいます。setSWFAddress関数では、次に取るべく状態に応じたディープリンクを SWFAddress.setValue で作成しています。

このディープリンクを利用できるように SWFAddress.addEventListener へcontrollerを指定してあります。

controllerでは SWFAddress.getPath で状態名を取得し、SWFAddress.getParameter でパラメータを取得してから、状態に応じた処理が書かれています。


<h4 class="large-tit">楽天APIでJSONを取得するまで</h4>

つぎは、listingの状態の時に実行する sendListing関数から楽天APIでJSONを取得するまでをカンタンに見ていきます。

<pre><p class="code">
<script type="text/javascript" src="<span class="attention">js/jsr_class.js</span>"></script>
(...)
// グローバル変数
var gsRakutenAPIListingURL = 'http://api.rakuten.co.jp/rws/1.11/json?developerId=9ad39aef6903b01ca205151220b7c47e&operation=ItemSearch&version=2007-10-25&sort=random';
(...)
// === リスト表示 =====
function <span class="attention">sendListing</span>(iCategory) {
  (...)
  var sUrl = gsRakutenAPIListingURL +'&genreId='+ iCategory;
  sUrl    += '<span class="attention">&callBack=listingCallback</span>&hits=5';
  // script タグの発行
  goJsr4Listing = new JSONscriptRequest(sUrl);
  goJsr4Listing.buildScriptTag();
  goJsr4Listing.addScriptTag();
}
function <span class="attention">listingCallback(oJson)</span> {
  // script タグの削除
  goJsr4Listing.removeScriptTag();
  (...)
}
</p></pre>

JSONPを利用したいので、毎度おなじみのjsr_class.jsを利用しています。

<ul class="icon-list">
<li><a href="http://www.xml.com/pub/a/2005/12/21/json-dynamic-script-tag.html" target="_blank">jsr_class.jsライブラリ</a><br/>
scriptタグの動的挿入を支援するライブラリ</li>
</ul>

JSONPなので、データを受ける際に実行される関数は &callBack=listingCallback として楽天APIに送っています。

listingCallback関数の oJson に受信したデータが入っているので、このデータを組み立てて出力します。

次は、出力部分について追っていきましょう。


<h4 class="large-tit">取得したJSONの出力はテンプレートを外部ファイルで</h4>

楽天APIで取得したJSONを出力する箇所を見ていきます。

<pre><p class="code">
function <span class="attention">listingCallback(oJson)</span> {
  (...)
  var aItem   = oJson['Body']['ItemSearch']['Items']['Item'];
  
  // 楽天API形式から表示用に変換する
  <span class="attention">aItem = convertRakutenAPIResult(aItem);</span>
  
  // タイトルを変更する
  <span class="attention">SWFAddress.setTitle</span>(convCategoryId2Name(giCategory) +' | '+ gsTitleBase);
  
  // テンプレートから内容を生成して出力する
  <span class="attention">makeContents($('listing'), 'listing', aItem);</span>
}
(...)
// === 共通関数 =====
function <span class="attention">convertRakutenAPIResult</span>(aItem) {
  for (var i = 0 ; i < aItem.length ; i++) {
    // 商品名が大抵やたら長いので途中で切ったものを作っておく
    aItem[i]['itemName_short'] = aItem[i]['itemName'].substr(0,20);
    if (aItem[i]['itemName'].length > 20)
      aItem[i]['itemName_short'] += '...';
    // 親のカテゴリIDも取っておく
    aItem[i]['parent_genreId'] = giCategory;
    // 消費税フラグの数値化
    aItem[i]['tax'] = aItem[i]['taxFlag'] == 0? '(税込)' : '(税別)';
    // 送料フラグの数値化
    aItem[i]['postage'] = aItem[i]['postageFlag'] == 0? '送料無料' : '送料別';
    // クレジットカード利用可能フラグの数値化
    aItem[i]['creditCard'] = aItem[i]['creditCardFlag'] == 0? 'カード利用不可' : 'カード利用可';
  }
  return aItem;
}
function <span class="attention">makeContents</span>(oOutput, sPath, aItem) {
  new Ajax.Request(
    getTemplate(sPath),
    {onComplete:function(oResponse){
      <span class="attention">makeContentsCallback(oOutput, aItem, oResponse);</span>}
    }
  );
}
function getTemplate(sPath) {
  switch (sPath) {
    case 'listing':
    return 'templates/listing.html';
    case 'detail':
    return 'templates/detail.html';
    default:
    alert('sPath未指定 by getTemplate');
  }
}
function <span class="attention">makeContentsCallback</span>(oOutput, aItem, oResponse) {
  var sTemplate = oResponse.responseText;
  var sContents = '';
  for (var i = 0 ; i < aItem.length ; i++) {
    sContents += <span class="attention">sTemplate.interpolate</span>(aItem[i]);
  }
  <span class="attention">oOutput.innerHTML = sContents;</span>
}
</p></pre>

ちょっと長いですが、listingCallback関数を見ていただくと分かるようにHTMLとして出力するまでに３つのステップが記述してあります。

<h5 class="small-tit">ステップ1) 楽天API形式から表示用に変換</h5>

楽天APIは、「消費税」「送料」「クレジットカード利用可能」という部分はフラグで渡ってくるので、このデータを出力する際に文言として変換しておきます。

リスト表示では、商品名を短く表示したかったのでJSONに itemName_short というデータを独自に作って入れています。

<h5 class="small-tit">ステップ2) タイトルを変更</h5>

前回も書きましたが、カテゴリを変えたときや商品詳細で別商品を選択された状態に遷移したときは、タイトルを変更しておくと、ブラウザの「戻る」をリストさせたときにどこにもどれるのか一目瞭然となるのでユーザビリティがあがるのと、SEO的に有利になるでしょう。

<h5 class="small-tit">ステップ3) テンプレートから内容を生成して出力</h5>

今回、テンプレートは外部ファイル化するようにしました。これによって、デザイナとの連携がしやすくなりますし、なによりJavaScriptの '(シンクグクォート)などで括らなくていいのが楽チンです。


makeContents関数で外部ファイルを読み込むようにして、Ajaxで受信完了したらmakeContentsCallback関数を実行するようにしています。

makeContentsCallback関数では、テンプレートに prototype.js のinterpolateでデータを置換しながら投入し、出力オブジェクトのinnerHTMLへ突っ込んでいます。


<h4 class="large-tit">詳細側も同じ要領で出力</h4>

詳細側も同じ要領で出力できます。

<pre><p class="code">
// グローバル変数
var gsRakutenAPIDetailURL  = 'http://api.rakuten.co.jp/rws/1.11/json?developerId=9ad39aef6903b01ca205151220b7c47e&operation=ItemCodeSearch&version=2007-04-11';
(...)
// === 詳細表示 =====
function <span class="attention">sendDetail</span>(iCategory, iProduct) {
  var sUrl = gsRakutenAPIDetailURL +'&itemCode='+ iProduct;
  sUrl    += '<span class="attention">&callBack=detailCallback</span>';
  // script タグの発行
  goJsr4Detail = new JSONscriptRequest(sUrl);
  goJsr4Detail.buildScriptTag();
  goJsr4Detail.addScriptTag();
}
function <span class="attention">detailCallback(oJson)</span> {
  // script タグの削除
  goJsr4Detail.removeScriptTag();
  
  var aItem   = oJson['Body']['ItemCodeSearch']['Items']['Item'];
  
  // 楽天API形式から表示用に変換する
  <span class="attention">aItem = convertRakutenAPIResult(aItem);</span>
  
  // タイトルを変更する
  <span class="attention">SWFAddress.setTitle</span>(aItem[0]['itemName_short'] +' | '+ gsTitleBase);
  
  // テンプレートから内容を生成して出力する
  <span class="attention">makeContents($('detail'), 'detail', aItem);</span>
}
</p></pre>

sendDetail関数で、楽天APIにアクセスしてJSONPで detailCallback関数が実行されるように仕込んでおき、detailCallback関数で、リスト出力と同じ３つのステップで出力しているだけになっています。


<h4 class="large-tit">ディープリンクにアクセスしてみる</h4>

前回同様にディープリンクを使ってアクセスしてみます。

<ul class="icon-list">
<li><a href="http://okra.ark-web.jp/~takemura/public/js/swfaddress2_store_demo/index.html" target="_blank">トップ</a><br/>
普通のトップ。これは何も変わらずです。</li>
<li><a href="http://okra.ark-web.jp/~takemura/public/js/swfaddress2_store_demo/index.html#/listing/?category=551167" target="_blank">リスト表示</a><br/>
スイーツカテゴリのリスト表示</li>
<li><a href="http://okra.ark-web.jp/~takemura/public/js/swfaddress2_store_demo/index.html#/detail/?category=551167&product=kashizairyo:10002647" target="_blank">詳細表示</a><br/>
『コーティングチョコレート(ミルク） 100g』という商品の詳細表示</li>
</ul>

一覧表示、詳細表示ともにアクセスすると楽天APIで取ってきた内容が表示されたと思います。

ローディングを端折っているので「○○を決めてください」的な文言が出てしまっていてちょっと見苦しいですが、とりあえずサンプルということでシンプルに書いてあります。


<h4 class="large-tit">まとめ</h4>

２回に渡って紹介してきました『SWFAddress2.0』というライブラリについて、使い方と事例をご理解いただけたでしょうか？

AjaxやFlashを含めた RIA(リッチインターネットアプリケーション)を構築する上ではURLによる画面遷移が無いため、１画面を長く使うようなアプリケーションを構築する必要性が多くなってくると思います。その時に、このようなディープリンクを作るライブラリを思い出していただければと思います。

また、状態管理を使った設計は通常のイベント駆動とは異なり、イベントハンドラに直接動作を記述すのではなく、一旦イベントのコントローラに制御を渡してから、コントローラから動作するようにすることで、任意の状態を復元できるように設計する必要がある。ということを気をつけていただければと思います。]]>
   </content>
</entry>
<entry>
   <title>Rails で Yahoo 検索 API + Widgets Tabnav でタブナビゲーション</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/02/rails_yahoo_api_widgets_tabnav.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.57</id>
   
   <published>2008-02-20T01:00:00Z</published>
   <updated>2008-02-20T10:56:44Z</updated>
   
   <summary> Ruby（とRails）を担当している石原です。 ソーシャル「OSを入れた後に...</summary>
   <author>
      <name>石原 淳也</name>
      
   </author>
         <category term="Ruby" scheme="http://www.sixapart.com/ns/types#category" />
   
   <category term="82" label="API" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="50" label="Rails" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="86" label="Tabnav" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="88" label="Widgets" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="80" label="Yahoo" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="84" label="タブ" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="56" label="プラグイン" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="83" label="検索" scheme="http://www.sixapart.com/ns/types#tag" />
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[Ruby（とRails）を担当している石原です。

ソーシャル「OSを入れた後にインストールする10のアプリケーション」(仮) を作る過程をレポートしています。

これまでのエントリーはこちら ↓

<ol>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/10/ruby_on_rails_1.html">つくるぶガイドブログ: Ruby on Rails を使ってひとりでサービスを作ってみよう</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/11/post_2.html">つくるぶガイドブログ: ひとりサービスの雛型をつくる(リキッドレイアウト、GetText、Acts as Authenticated)</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/11/scriptgen.html">つくるぶガイドブログ: Rails で楽々ソーシャルブックマークの仕組みを作る</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/12/rails_acts_as_taggable_redux.html">つくるぶガイドブログ: Rails プラグイン acts_as_taggable_redux でタグクラウドを作ろう</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/01/rails_ajax.html">つくるぶガイドブログ: ドラッグアンドドロップで並べ替え(Rails + Ajax)</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/01/rails_rcov.html">つくるぶガイドブログ: Rails + rcov でテストカバレッジを調べる</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/02/capistrano_rails.html">つくるぶガイドブログ: capistrano で本番環境にデプロイ</a></li>
</ol>

今回は API との連携、マッシュアップをおこなってみたいと思います。

手前味噌ではありますが、筆者が以前作成した

&raquo; <a target="_blank" href="http://blog.champierre.com/archives/504">Rails で Yahoo 検索 API を使うためのプラグイン YahooSearchApi : 僕は発展途上技術者</a>

を使います。

ソーシャル「OSを入れた後にインストールする10のアプリケーション」(仮) は、みんながOSを入れた直後にどんなアプリケーションをインストールするのかを、そのアプリケーションの Web ページをブックマークすることで共有するサイトです。

アプリケーション名をキーワードに、API を使って Yahoo 検索の結果を表示することで、そのアプリケーションの関連情報を表示してみます。

後半では Widgets というプラグインの Tabnav 機能を使って、タブナビゲーションを実現します。
]]>
      <![CDATA[<h4 class="large-tit">YahooSearchApi プラグインのインストールとアプリケーションIDの登録</h4>

Rails で Yahoo 検索 API を使うためのプラグイン <a href="http://rails-workshop-tokyo.googlecode.com/svn/trunk/plugins/yahoo_search_api/README">YahooSearchApi</a> をインストールします。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

script/plugin install http://rails-workshop-tokyo.googlecode.com/svn/trunk/plugins/yahoo_search_api

</pre>

Yahoo API を使うためにはアプリケーションIDの登録が必要です。

<a target="_blank" href="http://e.developer.yahoo.co.jp/webservices/register_application">Yahoo!デベロッパーネットワーク - アプリケーションIDの登録</a>

より登録をおこない、アプリケーションIDを取得します。

<h4 class="large-tit">コントローラーとビューの編集</h4>

コントローラーを編集して、アプリケーションをキーワードとして Yahoo の検索結果を10件取得する処理を追加します。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">

== app/controllers/softwares_controller.rb ==

class SoftwaresController < ApplicationController

  def show
    @software = Software.find(params[:id])

    # 検索パラメーターを指定
    # http://developer.yahoo.co.jp/search/web/V1/webSearch.html 参照
    search_params = {
      :type => 'all',  <font color="red"># 全クエリー文字を含む検索結果を取得</font>
      :results => 10,  <font color="red"># 10件の検索結果を取得</font>
      :start => 1,  <font color="red"># 検索結果の先頭位置</font>
      :language => 'ja'  <font color="red"># 言語</font>
    }

    web_search_service = YahooSearchApi::WebSearchService.new(
      'アプリケーションID', <font color="red"># 取得したアプリケーションID</font>
      @software.title,  <font color="red"># キーワード</font>
      search_params  <font color="red"># 検索パラメーター</font> 
    )

    @results = web_search_service.results
  end

end

</pre>

ビューは以下の通りです。アプリケーションのタイトルとURLの後に、Yahoo の検索結果を表示します。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">

== app/views/softwares/show.rhtml ==

&lt;p>&lt;b>Title:&lt;/b> &lt;%= h @software.title %>&lt;/p>
&lt;p>&lt;b>Url:&lt;/b> &lt;%= link_to h(@software.url), @software.url %>&lt;/p>
&lt;p>&lt;b>「&lt;%= h @software.title %>」の Yahoo 検索結果:&lt;/b>&lt;/p>
&lt;ol>
&lt;% @results.each do |result| %>
&lt;li>&lt;%= link_to result.Title, result.ClickUrl %>&lt;/li>
&lt;%= result.Summary %>
&lt;div style="color:green">&lt;%= result.Url %>&lt;/div>
&lt;% end %>
&lt;/ol>

</pre>

以下のように Yahoo 検索結果が表示されるようになりました。(クリックすると大きな画像が見られます)

<a href="http://www.tkrb.jp/guide/yahoo.png"><img alt="yahoo_small.png" src="http://www.tkrb.jp/guide/yahoo_small.png" width="400" height="350" /></a>

<h4 class="large-tit">Widgets Tabnav でタブナビゲーション</h4>

これだけだと少しもの足りないので、複数の検索結果を表示してみます。

たとえば、Firefox のページなら、「Firefox」をキーワードにした検索結果に加えて、「Firefox インストール」「Firefox 使い方」「Firefox ダウンロード」の検索結果も表示するようにします。

それぞれの検索結果はタブナビゲーションで切り替えられるようにしましょう。

&raquo; <a target="_blank" href="http://www.seesaw.it/en/toolbox/widgets">SeeSaw | Rails Widgets</a>

というプラグインがあり、Tabnav という機能で簡単にタブナビゲーションを追加できます。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

ruby script/plugin install svn://svn.seesaw.it/widgets/trunk

</pre>

でインストールしておきます。

まず、複数の検索結果を表示できるようにコントローラーを改造します。

http://localhost:3000/softwares/show/1 というURLで通常の検索結果を表示するところを、keyword というパラメーターを追加し、例えば、http://localhost:3000/softwares/show/1?keyword=install で「Firefox インストール」の検索結果を表示するようにします。

keyword=usage なら「Firefox 使い方」、keyword=download なら「Firefox ダウンロード」です。

追加、変更した部分を赤字で示しました。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">

== app/controllers/softwares_controller.rb ==

class SoftwaresController < ApplicationController

  def show
    @software = Software.find(params[:id])

    <font color="red">keyword = @software.title    
    case params[:keyword]
    when 'install'
      keyword += ' インストール'
    when 'usage'
      keyword += ' 使い方'
    when 'download'
      keyword += ' ダウンロード'
    end</font>

    search_params = {
      :type => 'all',
      :results => 10,
      :start => 1,
      :language => 'ja'
    }

    web_search_service = YahooSearchApi::WebSearchService.new(
      'アプリケーションID',
      <font color="red">keyword</font>,
      search_params
    )

    @results = web_search_service.results
  end

end

</pre>

ビューの作成に移ります。

yahoo_search という名前でタブナビゲーションのひな形となる partial ビューファイルを生成します。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

ruby script/generate tabnav yahoo_search

</pre>

生成したビューファイルのコメントを参考に、以下の通り全面的に書き換えます。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">

== app/views/widgets/_yahoo_search_tabnav.rhtml ==

&lt;%
render_tabnav :yahoo_search, 
              :generate_css => true do 
         
  add_tab do |t|
    t.named "#{h(@software.title)}"
    t.titled "「#{h(@software.title)}」の検索結果"
    t.links_to :controller => 'softwares', :action => 'show', :id => @software.id
  end

  add_tab do |t|
    t.named "#{h(@software.title)} インストール"
    t.titled "「#{h(@software.title)} インストール」の検索結果"
    t.links_to :controller => 'softwares', :action => 'show', :id => @software.id, :keyword => 'install'
  end

  add_tab do |t|
    t.named "#{h(@software.title)} 使い方"
    t.titled "「#{h(@software.title)} 使い方」の検索結果"
    t.links_to :controller => 'softwares', :action => 'show', :id => @software.id, :keyword => 'usage'
  end

  add_tab do |t|
    t.named "#{h(@software.title)} ダウンロード"
    t.titled "「#{h(@software.title)} ダウンロード」の検索結果"
    t.links_to :controller => 'softwares', :action => 'show', :id => @software.id, :keyword => 'download'
  end
end 
%>

</pre>

add_tab で始まる各ブロックがひとつのタブにあたり、named にタイトル、titled にツールチップの内容、links_to にリンク先を記述します。

リンク先には、さきほどのコントローラーの改造で追加した keyword パラメーターをそれぞれ指定しています。

上記 partial ファイルはビューから、tabnav :yahoo_search で呼び出します。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">

== app/views/softwares/show.rhtml ==

&lt;p>&lt;b>Title:&lt;/b> &lt;%= h @software.title %>&lt;/p>
&lt;p>&lt;b>Url:&lt;/b> &lt;%= link_to h(@software.url), @software.url %>&lt;/p>

&lt;p>&lt;b>Yahoo 検索結果:&lt;/b>&lt;/p>
<font color="red">&lt;% tabnav :yahoo_search do %></font>
&lt;ol>
&lt;% @results.each do |result| %>
&lt;li>&lt;%= link_to result.Title, result.ClickUrl %>&lt;/li>
&lt;%= result.Summary %>
&lt;div style="color:green">&lt;%= result.Url %>&lt;/div>
&lt;% end %>
&lt;/ol>
&lt;% end %>

</pre>

以下のようなタブナビゲーションの完成です。(クリックすると大きな画像が見られます)

<a href="http://www.tkrb.jp/guide/tab.png"><img alt="yahoo_small.png" src="http://www.tkrb.jp/guide/tab_small.png" width="400" height="257" /></a>

<h4 class="large-tit">まとめ</h4>

Yahoo 検索 API を使い、検索結果という形で関連情報を表示してみました。

このように API を使うと、簡単に自分のサービスに新たな情報を追加することができます。

他の API と組み合わせたり、見せ方を工夫することで他にはないユニークな情報を提供できるかもしれません。

今回までのソースコードを以下にアップロードしておきます。

&raquo <a href="http://code.google.com/p/10best/">10best - Google Code
</a>

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

svn checkout http://10best.googlecode.com/svn/trunk/ 10best

</pre>

でチェックアウトするか、ブラウザでも<a href="http://10best.googlecode.com/svn/trunk/">リポジトリ</a>を閲覧することができます。 
]]>
   </content>
</entry>
<entry>
   <title>標準Java APIを使用したWebサービスへのアクセス</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/02/java_apiweb.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.56</id>
   
   <published>2008-02-12T11:03:12Z</published>
   <updated>2008-02-12T11:27:55Z</updated>
   
   <summary> 昨今のJavaは、大規模なWebアプリケーションを構築するための技術として、す...</summary>
   <author>
      <name>ATL Enterprise Architecture Unit</name>
      
   </author>
         <category term="Java" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[昨今のJavaは、大規模なWebアプリケーションを構築するための技術として、すっかり定着しました。サーバサイドでの処理としては、データベースへのアクセスが大半を占めることでしょう。しかし最近では、一般公開されている多くのWebサービスを利用する機会が増えてきています。それは一部のマッシュアップサイトにとどまらず、企業システムの中でも採用されることが多くなってきています。Javaエンジニアにとって、サーバサイドマッシュアップというキーワードは、今後どんどん身近なものになります。

<a href="" target="_blank">前回の内容</a>は、典型的なマッシュアップの形態や、SOAPやRESTなどのWebサービスの種類についても取り上げました。今回は、実際にWebサービスにアクセスして情報を得るためのコードを紹介しようと思います。何らかのライブラリをいきなり使っても良いのですが、まずは堅く、標準Java APIのみで試してみたいと思います。といっても、とっても面倒な手順というわけではありませんので、ご安心ください。]]>
      <![CDATA[さて、「イベント検索アプリケーション」を考えた場合、例えば、あるイベントに関連したブログエントリもイベント情報として提示できると、アプリケーションの付加価値があがりますね。そのイベントの開催情報、参加した人の感想、さらにはイベントのスピーカーの方の話なども見つけることができるかもしれません。まさにマッシュアップですね。ブログ検索のWebサービスで代表的なものといえば、株式会社テクノラティジャパンが提供しているブログ検索APIがあげられます。今回はこのサービスを実際にアクセスしてみます。

そのためには、最初にAPIキーを取得する必要があります。多くのWebサービスでは、最初に利用登録を行い、その結果発行されたキーを使ってサービスを利用するという形態がほとんどです。テクノラティ社のブログ検索APIのAPIキーを得るためには、まずメンバー登録を行います。

メンバー登録
<ul><li><a href="https://www.technorati.jp/signup/" target="_blank">https://www.technorati.jp/signup/</a></il></ul>

メンバー登録が済めば、以下のURLでAPIキーを得ることができます。ページ上部の「あなたのテクノラティAPIキーはこちらです:」の後が、APIキー文字列となります。

テクノラティジャパンのAPIについて
<ul><li><a href="http://www.technorati.jp/developers/apikey.html" target="_blank">http://www.technorati.jp/developers/apikey.html</a></li></ul>

APIキーを入手できれば、ブログ検索APIをすぐに利用することができます。APIの説明は、以下のURLに解説されています。今回使用するのは、キーワード検索APIです。

テクノラティジャパン APIの使用に関して
<ul><li><a href="http://www.technorati.jp/developers/api/" target="_blank">http://www.technorati.jp/developers/api/</a></li></ul>

キーワード検索API
<ul><li><a href="http://www.technorati.jp/developers/api/search.html" target="_blank">http://www.technorati.jp/developers/api/search.html</a></li></ul>

このブログ検索APIは、RESTfulライクな形式のWebサービスですので、試しにアクセスするのは非常に簡単です。単にWebブラウザでURLを打ち込んでアクセスする、それだけです。例えば「デブサミ」というキーワードでブログを検索してみるには、以下のURLにアクセスするだけです。APIキーの部分には、先ほど入手したAPIキーをそのまま記載します。

<blockquote>
http://api.technorati.jp/search?key=[APIキー]&query=デブサミ
</blockquote>

以下のように、検索結果がXML形式で得られることがわかると思います。REST準拠と謳っているWebサービスであれば、このように手軽にアクセスできます。逆に、SOAP対応と謳っているWebサービスの場合は、比較的複雑な手順を踏む必要が出てきます。

<img alt="technorati.jpg" src="http://www.tkrb.jp/guide/2008/02/12/technorati.jpg" width="412" height="210" />

さて、早速このサービスにアクセスするためのコードを説明していきましょう。今回は標準Java APIを使用しますが、次回以降より便利な実装に入れ替える予定ですので、ブログ検索のインタフェースをきっちりと決めておきましょう。コード1にインタフェースを、コード2に検索結果を格納するためのクラスを紹介します。

コード1 ブログ検索インタフェース
<p class="code">
package jp.tkrb.event.services;
import java.util.List;

public interface TechnoratiBlogSearch {
&nbsp;&nbsp;&nbsp;&nbsp;List&lt;Weblog&gt; search(String keyword);
}
</p>

コード2 検索結果を格納するクラス
<p class="code">
package jp.tkrb.event.services;

public class Weblog {
&nbsp;&nbsp;&nbsp;&nbsp;private String name;
&nbsp;&nbsp;&nbsp;&nbsp;private String url;
&nbsp;&nbsp;&nbsp;&nbsp;private String title;
&nbsp;&nbsp;&nbsp;&nbsp;private String permalink;
    
&nbsp;&nbsp;&nbsp;&nbsp;public Weblog(String name, String url, String title, String permalink) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.name = name;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.url = url;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.title = title;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.permalink = permalink;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public String getName() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return name;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public String getPermalink() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return permalink;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public String getTitle() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return title;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public String getUrl() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return url;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;public String toString() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return name + " : " + url + " : " + title + " : " + permalink;
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</p>

キーワードを渡すと、関連するブログエントリがWeblogクラスのインスタンスのコレクションとして得られる、というAPIとしました。Weblogクラスには、toString()メソッドを定義して、内容を目で見てわかりやすいようにしてあります。

いよいよWebサービスにアクセスするコードです。標準Java APIのみの使用となるので、少し長めのコードとなっています。ちなみに、import文は省略しています。

コード3 標準Java APIを使用した実装クラス
<p class="code">
package jp.tkrb.event.services;

public class TechnoratiBlogSearchSimpleImpl implements TechnoratiBlogSearch {
&nbsp;&nbsp;&nbsp;&nbsp;private static final String TECHNORATI_URL = "http://api.technorati.jp/search?key=";
&nbsp;&nbsp;&nbsp;&nbsp;private static final String API_KEY = "72e0ca796d918081695af3014eb3b751";
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;public List&lt;Weblog&gt; search(String keyword) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InputStream in = null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&lt;Weblog&gt; weblogs = new ArrayList&lt;Weblog&gt;();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String encoded = URLEncoder.encode(keyword, "UTF-8");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;URL technorati = new URL(TECHNORATI_URL + API_KEY + "&query=" + encoded);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;URLConnection connection = technorati.openConnection();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DocumentBuilder builder = factory.newDocumentBuilder();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in = connection.getInputStream();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Document doc = builder.parse(in);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Element root = doc.getDocumentElement();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Element document = child(root, "document");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NodeList items = document.getElementsByTagName("item");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; items.getLength(); i++) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Element item = (Element)items.item(i);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Element weblog = child(item, "weblog");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String name = child(weblog, "name").getFirstChild().getNodeValue();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String url = child(weblog, "url").getFirstChild().getNodeValue();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String title = child(item, "title").getFirstChild().getNodeValue();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String permalink = child(item, "permalink").getFirstChild().getNodeValue();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;weblogs.add(new Weblog(name, url, title, permalink));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return weblogs;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (ParserConfigurationException e) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException(e);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (MalformedURLException e) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException(e);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (UnsupportedEncodingException e) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException(e);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (IOException e) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException(e);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (SAXException e) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException(e);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} finally {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (in != null) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;in.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (IOException e) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException(e);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;private Element child(Element parent, String tagName) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NodeList children = parent.getElementsByTagName(tagName);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (Element)children.item(0);
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</p>

部分的に見ていきましょう。まずは、ブログ検索APIにアクセスするコードです。

<p class="code">
String encoded = URLEncoder.encode(keyword, "UTF-8");
URL technorati = new URL(TECHNORATI_URL + API_KEY + "&query=" + encoded);
URLConnection connection = technorati.openConnection();
</p>

まず、キーワード文字列に対してUTF-8でURLエンコードをかけます。例えば「デブサミ」というキーワード文字列であれば、「%E3%83%87%E3%83%96%E3%82%B5%E3%83%9F」という変換となります。その後、APIキーを含むURL全体を構築して、それを元にURLオブジェクトを生成します。それだけですと単なる識別子なので、openConnection()メソッドを呼び出して、WebサービスにアクセスするためのURLConnectionオブジェクトを得ます。

ブログ検索の結果はXML形式となりますので、その内容を解析するためにXMLパーサーを利用することになります。

<p class="code">
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
in = connection.getInputStream();
Document doc = builder.parse(in);
</p>

上記のコードは、JavaSEに標準搭載されているJAXP APIを利用しています。具体的には、DocumentBuilderオブジェクトを取得し、URLConnectionオブジェクトから入力ストリームを得て、parse()メソッドに渡しています。この時点でWebサービスとの通信が行われ、結果のXMLがパースされてDOMが構築されます。

あとは結果の内容を解析するだけなのですが、残念ながらこの解析処理が面倒なコーディングとなります。DOMはオブジェクトのツリー構造となるため、このツリーを辿って必要な情報を探し出します。

ここで、ちょっとした便利メソッドを紹介しましょう。DOM内のある要素の子要素をタグ名を指定して取得する、というchild()メソッドが今回活躍してくれます。

<p class="code">
private Element child(Element parent, String tagName) {
&nbsp;&nbsp;&nbsp;&nbsp;NodeList children = parent.getElementsByTagName(tagName);
&nbsp;&nbsp;&nbsp;&nbsp;return (Element)children.item(0);
}
</p>

指定された要素の子要素をgetElementsByTagName()メソッドで探します。このメソッドの結果は複数存在することが想定されているのですが、このメソッドの前提として「子要素は1つのみ」と見なし、固定的に1つ目の要素を返しています。

このchild()メソッドを使って、必要となる情報を抜き出してWeblogオブジェクトを作り出している処理が、以下のコードです。

<p class="code">
Element root = doc.getDocumentElement();
Element document = child(root, "document");
NodeList items = document.getElementsByTagName("item");
for (int i = 0; i &lt; items.getLength(); i++) {
&nbsp;&nbsp;&nbsp;&nbsp;Element item = (Element)items.item(i);
&nbsp;&nbsp;&nbsp;&nbsp;Element weblog = child(item, "weblog");
&nbsp;&nbsp;&nbsp;&nbsp;String name = child(weblog, "name").getFirstChild().getNodeValue();
&nbsp;&nbsp;&nbsp;&nbsp;String url = child(weblog, "url").getFirstChild().getNodeValue();
&nbsp;&nbsp;&nbsp;&nbsp;String title = child(item, "title").getFirstChild().getNodeValue();
&nbsp;&nbsp;&nbsp;&nbsp;String permalink = child(item, "permalink").getFirstChild().getNodeValue();
&nbsp;&nbsp;&nbsp;&nbsp;weblogs.add(new Weblog(name, url, title, permalink));
}
</p>

検索結果は、document要素の子要素である個々のitem要素が相当しますので、item要素をfor文でぐるぐる回しています。item要素内にある「weblog/name、weblog/url、title、permalink」要素が、それぞれ「ブログ名、ブログのURL、ブログエントリのタイトル、ブログエントリのURL」となります。これらを先ほどのchild()メソッドを利用して取得し、Weblogオブジェクトを生成してコレクションに格納しています。あとはこのコレクションをブラウザに返却してイベント情報として表示してあげれば、マッシュアップが完成するということになります。

以上で、標準Java APIだけを使ったサーバサイドマッシュアップの準備体操は終わりです。今回のブログ検索では、扱いたい情報が4つしかないため、それほど行数があるわけではありません。しかし、量が多くなるに従って、このような手動マッピングは非常に苦痛になってくることでしょう。Webサービス側の仕様変更に弱いのも気になりますね。次回はそういった弱点を克服し、さらに手軽にサーバサイドマッシュアップを行うための施策を考えてみたいと思います。]]>
   </content>
</entry>
<entry>
   <title>SWFAddress2.0を利用した「状態」による機能制御</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/02/state_control_that_used_swfaddress2.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.55</id>
   
   <published>2008-02-08T06:00:00Z</published>
   <updated>2008-02-08T06:00:04Z</updated>
   
   <summary> こんにちは、JavaScript担当の（株）アークウェブの竹村です。 以前、「...</summary>
   <author>
      <name>竹村 光生</name>
      
   </author>
         <category term="JavaScript" scheme="http://www.sixapart.com/ns/types#category" />
   
   <category term="35" label="PopBox" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="77" label="SWFAddress" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="78" label="状態制御" scheme="http://www.sixapart.com/ns/types#tag" />
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[こんにちは、JavaScript担当の<a href="http://www.ark-web.jp/" target="_blank">（株）アークウェブ</a>の竹村です。

以前、「<a href="http://www.tkrb.jp/modules/feature0/index.php?id=12" target="_blank">Flash|Ajaxベストサイトセレクション２０</a>」でご紹介していた
「Gucci」のサイトですが、今もなお、Ajaxによる画面遷移を行っています。

<ul class="icon-list"><li><a href="http://www.gucci.com/us/index2.html" target="_blank">Gucci</a></li></ul>

さて、この「Gucci」のサイトを語る上で外せないのが、オンラインショッピング ページのURLです。

例えば、↓こちらのバッグのURLへアクセスすると、バッグの詳細画面が表示されます。
<a href="http://www.gucci.com/us/us-english/us/spring-summer-08/web-exclusives/#0-179859-197020BCB1G7007" target="_blank">http://www.gucci.com/us/us-english/us/spring-summer-08/web-exclusives/#0-179859-197020BCB1G7007</a>

# 以下に商品を表すIDが振ってあります。

これを取ってアクセスすると、↓このカテゴリトップが表示されます。
<a href="http://www.gucci.com/us/us-english/us/spring-summer-08/web-exclusives/" target="_blank">http://www.gucci.com/us/us-english/us/spring-summer-08/web-exclusives/</a>

このように商品詳細へ直接リンクを張ることを「ディープリンク」と呼ぶようです。
今回は、このディープリンクを実装する概念とライブラリをご紹介します。

レジュメは↓このようになっています。

<ul class="common-list">
<li>SWFAddress2.0</li>
<li>タブ切り替えのサンプル</li>
<li>初期化処理</li>
<li>イベントハンドラの処理</li>
<li>イベント駆動との違い</li>
<li>状態の中のパラメータ</li>
<li>ディープリンクにアクセスしてみる</li>
<li>次回予告</li>
</ul>


まず、SWFAddress2.0というディープリンクをお助けするライブラリがありますので、
このライブラリを使いながら、概念を理解していきましょう。
]]>
      <![CDATA[<style type="text/css">
.attention {color:red; font-weight:bold;}
</style>
<h4 class="large-tit">SWFAddress2.0</h4>

元々は、Flashにディープリンクをつけるためのライブラリとして登場しました。
Flashは 1画面上で画面遷移するので、ディープリンクが付けられなかったためです。

<ul class="icon-list">
<li><a href="http://www.asual.com/swfaddress/" target="_blank">SWFAddressの公式サイト (英語)</a></li>
<li><a href="http://www.asual.com/swfaddress/docs/en/" target="_blank">SWFAddressのAPIガイド (英語)</a></li>
</ul>

ただし、このライブラリは DHTML や Ajax でも利用可能なように作られています。

以下で、タブ切り替えを題材にサンプルを見ながら説明していきます。


<h4 class="large-tit">タブ切り替えのサンプル</h4>

まず、普通にタブ切り替えを自作で作った場合は↓このようになるかと思います。

<h5 class="small-tit">普通のタブ切り替え</h5>
<iframe src="http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_tab_old.html" width="470" height="120">
<a href="http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_tab_old.html" target="_blank">iframeをご覧になれない場合はこちらをどうぞ</a></iframe>

タブをクリックしたら内容が切り替わるだけのサンプルです。

<pre><p class="code">
&lt;script type="text/javascript" src="js/prototype.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
document.observe("dom:loaded", initialize, false);
function initialize() {
	// タブクリック時のイベントハンドラの設定
	<span class="attention">$('tab_a').onclick = function(){ changeTab('tab_a'); };</span>
	$('tab_b').onclick = function(){ changeTab('tab_b'); };
	$('tab_c').onclick = function(){ changeTab('tab_c'); };
	
	// 初期状態
	changeTab();
}
function <span class="attention">changeTab</span>(sTarget) {
	switch(sTarget) {
		<span class="attention">case 'tab_a':</span>
		default:
			<span class="attention">$('tab_a').className = 'active';</span>
			<span class="attention">$('contents_a').show();</span>
			$('tab_b').className = 'unactive';
			$('contents_b').hide();
			$('tab_c').className = 'unactive';
			$('contents_c').hide();
		break;
		case 'tab_b':
			$('tab_a').className = 'unactive';
			$('contents_a').hide();
			$('tab_b').className = 'active';
			$('contents_b').show();
			$('tab_c').className = 'unactive';
			$('contents_c').hide();
		break;
		case 'tab_c':
			$('tab_a').className = 'unactive';
			$('contents_a').hide();
			$('tab_b').className = 'unactive';
			$('contents_b').hide();
			$('tab_c').className = 'active';
			$('contents_c').show();
		break;
	}
}
&lt;/script&gt;
(...)
<span class="attention">&lt;span id="tab_a"&gt;TabA&lt;/span&gt;</span>
&lt;span id="tab_b"&gt;TabB&lt;/span&gt;
&lt;span id="tab_c"&gt;TabC&lt;/span&gt;
<span class="attention">&lt;div class="contents" id="contents_a"&gt;これは TabA のコンテンツです。&lt;/div&gt;</span>
&lt;div class="contents" id="contents_b"&gt;This is contents of TabB.&lt;/div&gt;
&lt;div class="contents" id="contents_c"&gt;TabC ノ コンテンツ デアル。&lt;/div&gt;
</p></pre>

※tag_aに関する部分のみ赤文字にしています。

initializeで、タブクリックのイベントハンドラを設定して、
changeTab関数で指定したタブを開くように組んであります。


では、SWFAddressを利用してタブ切り替えを作ってみます。

<h5 class="small-tit">SWFAddress2を利用したタブ切り替え</h5>
<iframe src="http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_tab.html" width="470" height="120">
<a href="http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_tab.html" target="_blank">iframeをご覧になれない場合はこちらをどうぞ</a></iframe>

iframeだとうまくいきませんね。。
お手数ですが、↓こちらにアクセスしてください。

<p class="url"><a href="http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_tab.html" target="_blank">▼SWFAddress2を利用したタブ切り替え
http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_tab.html</a>
</p>

<pre><p class="code">
&lt;script type="text/javascript" src="js/prototype.js"&gt;&lt;/script&gt;
<span class="attention">&lt;script type="text/javascript" src="js/swfaddress.js"&gt;&lt;/script&gt;</span>
&lt;script type="text/javascript"&gt;
document.observe("dom:loaded", initialize, false);
function initialize() {
	// タブクリック時のイベントハンドラの設定
	<span class="attention">$('tab_a').onclick = function(){ SWFAddress.setValue('tab_a'); };</span>
	$('tab_b').onclick = function(){ SWFAddress.setValue('tab_b'); };
	$('tab_c').onclick = function(){ SWFAddress.setValue('tab_c'); };
	
	// SWFAddressのイベント設定
	<span class="attention">SWFAddress.addEventListener(SWFAddressEvent.CHANGE, changeTab);</span>
}
function <span class="attention">changeTab</span>(event) {
//	alert("■value:"+event.value+" ■path:"+event.path+" ■id:"+event.parameters["id"]);
	switch(event.path) {
		<span class="attention">case '/tab_a/':</span>
		default:
			<span class="attention">$('tab_a').className = 'active';</span>
			<span class="attention">$('contents_a').show();</span>
			$('tab_b').className = 'unactive';
			$('contents_b').hide();
			$('tab_c').className = 'unactive';
			$('contents_c').hide();
		break;
		case '/tab_b/':
			$('tab_a').className = 'unactive';
			$('contents_a').hide();
			$('tab_b').className = 'active';
			$('contents_b').show();
			$('tab_c').className = 'unactive';
			$('contents_c').hide();
		break;
		case '/tab_c/':
			$('tab_a').className = 'unactive';
			$('contents_a').hide();
			$('tab_b').className = 'unactive';
			$('contents_b').hide();
			$('tab_c').className = 'active';
			$('contents_c').show();
		break;
	}
}
&lt;/script&gt;
(...)
<span class="attention">&lt;span id="tab_a"&gt;TabA&lt;/span&gt;</span>
&lt;span id="tab_b"&gt;TabB&lt;/span&gt;
&lt;span id="tab_c"&gt;TabC&lt;/span&gt;
<span class="attention">&lt;div class="contents" id="contents_a"&gt;これは TabA のコンテンツです。&lt;/div&gt;</span>
&lt;div class="contents" id="contents_b"&gt;This is contents of TabB.&lt;/div&gt;
&lt;div class="contents" id="contents_c"&gt;TabC ノ コンテンツ デアル。&lt;/div&gt;
</p></pre>

違っているのは、initializeの部分と、changeTabの引数が event になっています。

まずは initialize から見ていきましょう。

<h4 class="large-tit">初期化処理</h4>

<pre><p class="code">
function initialize() {
	// タブクリック時のイベントハンドラの設定
	$('tab_a').onclick = function(){ <span class="attention">SWFAddress.setValue('tab_a');</span> };
	$('tab_b').onclick = function(){ SWFAddress.setValue('tab_b'); };
	$('tab_c').onclick = function(){ SWFAddress.setValue('tab_c'); };
	
	// SWFAddressのイベント設定
	<span class="attention">SWFAddress.addEventListener(SWFAddressEvent.CHANGE, changeTab);</span>
}
</p></pre>

SWFAddress.setValue('tab_a'); が実行されると、URLの末尾が #tab_a のように表示されます。

これが SWFAddress の現在の「状態」を表しています。
つまり「tab_a」の状態ということですね。

その下の SWFAddress.addEventListener の第1引数の SWFAddressEvent.CHANGE は
「状態」が変わった時を意味します。

第2引数の changeTab はイベントハンドラです。

では、イベントハンドラを見てみます。

<h4 class="large-tit">イベントハンドラの処理</h4>

<pre><p class="code">
function changeTab(<span class="attention">event</span>) {
//	<span class="attention">alert("■value:"+event.value+" ■path:"+event.path+" ■id:"+event.parameters["id"]);</span>
	switch(<span class="attention">event.path</span>) {
		case '/tab_a/':
		default:
			$('tab_a').className = 'active';
			$('contents_a').show();
			$('tab_b').className = 'unactive';
			$('contents_b').hide();
			$('tab_c').className = 'unactive';
			$('contents_c').hide();
		break;
</p></pre>

※長いので、tab_aとデフォルトの場合だけ表示しています。

引数に取る event は、SWFAddress から渡されます。

alert 部分のコメントアウトを外し、TabBをクリックすると

■value:/tab_b/ ■path:/tab_b/ ■id:undefined

と表示されます。

この event.path を使って「状態」に応じたタブ状況を作っていたわけです。

<h4 class="large-tit">イベント駆動との違い</h4>

上記で作った 2つのプログラムですが、前者はイベントハンドラに「動作」を
設定しておく、いわゆる『イベント駆動』でした。

これに対して、後者はイベントハンドラに「状態」を設定しておき、状態が
変わったらそれを通知するようにセットしています。


どちらも、イベントに対して動作する点は変わりませんが、イベントを動作として
捕らえるか、状態として捕らえるかの違いがあります。

つまり、「状態をtab_bに変更せよ」という命令を受けたら、単にtab_bの状態を
作ればよいだけなのです。

今イベントハンドラが実行されたのか、後からディープリンクでアクセスされたのかを
意識することなく、単純に命令された状態を復元するだけでよい。ということです。


<h4 class="large-tit">状態の中のパラメータ</h4>

上記の alert を見たときに、idってなんだ? と思われた方もいると思います。
これは、QueryStringに含まれるパラメータです。

次の例は、PopBoxの画像をクリックした時に、このパラメータを使って画像拡大を
実行するようにしています。

<h5 class="small-tit">PopBoxによるパラメータの例</h5>
<iframe src="http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_popbox.html" width="470" height="240">
<a href="http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_popbox.html" target="_blank">iframeをご覧になれない場合はこちらをどうぞ</a></iframe>

やっぱりiframeではダメなので、リンク先を示しておきます。

<p class="url"><a href="http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_popbox.html" target="_blank">▼PopBoxによるパラメータの例
http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_popbox.html</a>
</p>

先ほど作ったタブの中にPopBoxを仕込んだ画像を入れてあります。
PopBoxについては↓こちらで学習できます。

<ul class="icon-list">
<li><a href="http://www.tkrb.jp/guide/2007/11/flickr_image_search_mashup_vol2.html" target="_blank">Flickrとのマッシュアップ！ (PopBoxとdhtmlxGrid)　その２</a><br/>
PopBoxとdhtmlxGridの説明です。iframeでそれぞれのサンプルを確認できます。</li>
<li><a href="http://www.ark-web.jp/sandbox/wiki/265.html" target="_blank">PopBoxの使い方</a><br/>
PopBoxのサンプルと一部のAPIについて解説しています。</li>
</ul>

PopBoxなので画像の部分は↓このようになっています。

<pre><p class="code">
&lt;img id="pop_a" class="PopBoxImageSmall" alt="アークウェブ忘年会の画像"
	src   = "samples/thm01.jpg"
	pbsrc = "samples/img01.jpg"
	pbCaption = "アークウェブ忘年会の画像"
	pbRevertText = "クリックで画像を閉じます" /&gt;
</p></pre>

普通のPopBoxでは、このimgタグの中に onclick イベントハンドラで Pop という
関数を呼んで画像拡大をおこなっていましたが、今回は画像拡大を1つの「状態」として
扱いたいので、外しています。

では、画像がクリックされた時の状態を保持するためにイベントハンドラの設定を
追加している箇所を見てみましょう。

<pre><p class="code">
document.observe("dom:loaded", initialize, false);
function initialize() {
	(...)
	// 画像クリック時のイベントハンドラの設定
	<span class="attention">$('pop_a')</span>.onclick  = function(){ <span class="attention">SWFAddress.setValue</span>('<span class="attention">tab_a/?pop=pop_a</span>'); };
	$('pop_b').onclick  = function(){ SWFAddress.setValue('tab_b/?pop=pop_b'); };
	$('pop_c1').onclick = function(){ SWFAddress.setValue('tab_c/?pop=pop_c1'); };
	$('pop_c2').onclick = function(){ SWFAddress.setValue('tab_c/?pop=pop_c2'); };
	
	// SWFAddressのイベント設定 (こっちは変わってません)
	SWFAddress.addEventListener(SWFAddressEvent.CHANGE, changeTab);
}
</p></pre>

各タブの中に、画像が全部で4つあるのでそれぞれSWFAddress.setValueで状態を
登録しています。

?以降がパラメータになります。普通にリクエストパラメータのGETと同じですね。

次に、このイベントを受ける changeTab を見ていきます。

<pre><p class="code">
function changeTab(event) {
//	<span class="attention">alert("■value:"+SWFAddress.getValue()+" ■path:"+SWFAddress.getPath()+" ■pop:"+SWFAddress.getParameter("pop"));</span>
	switch(SWFAddress.getPath()) {
		case '/tab_a/':
		default:
			$('tab_a').className = 'active';
			$('contents_a').show();
			$('tab_b').className = 'unactive';
			$('contents_b').hide();
			$('tab_c').className = 'unactive';
			$('contents_c').hide();
			<span class="attention">popImage(SWFAddress.getParameter("pop"));</span>
		break;
		(...)
	}
}
function <span class="attention">popImage</span>(pop) {
	if ($(pop)) {
		// タイトルを変更して画像をPop!
		<span class="attention">SWFAddress.setTitle</span>($(pop).alt+' | '+gsBaseTitle);
		<span class="attention">Pop($(pop), 100, 'PopBoxImageLarge');</span>
	}
}
</p></pre>

今回は、上のタブ切り替えのサンプルとは違う方法で、値を取得するようにしました。

以前：event.value, event.path, event.parameters["pop"]
今回：SWFAddress.getValue(), SWFAddress.getPath(), SWFAddress.getParameter("pop")

さて、この場合の alert もコメントを外して表示したら、どのように
表示されるかというと

■value:/tab_b/?pop=pop_b ■path:/tab_b/ ■pop:pop_b

と表示されます。

path は変わらないので今まで通りのタブ表示/非表示の処理が走ります。

popImage という関数を追加しています。changeTab 関数は、
パラメータで受け取った pop を渡して、画像拡大のトリガーである
Pop 関数を実行するようにしています。

Pop 関数の手前の SWFAddress.setTitle は titleタグを変更することができます。
これによって、ブラウザの「戻る」をリストさせたときに
どこにもどれるのか一目瞭然となります。


<h4 class="large-tit">ディープリンクにアクセスしてみる</h4>

上記のPopBoxのサンプルから、ディープリンクを使ってアクセスしてみます。

<p class="url"><a href="http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_popbox.html#/tab_c/?pop=pop_c1" target="_blank">▼TabCの画像1(pop_c1)を表示した状態にアクセス！
http://okra.ark-web.jp/~takemura/public/js/swfaddress2/sample_popbox.html#/tab_c/?pop=pop_c1</a>
</p>

遷移先画面で「TabC」に切り替わって、自動的に画像1が拡大表示されたと思います。


<h4 class="large-tit">次回予告</h4>

次回は、マッシュアップアプリを作りながら、このSWFAddressの実演をしてみようと
思っています。

お楽しみに～☆
]]>
   </content>
</entry>
<entry>
   <title>capistrano で本番環境にデプロイ</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/02/capistrano_rails.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.54</id>
   
   <published>2008-02-07T13:30:01Z</published>
   <updated>2008-02-07T15:16:41Z</updated>
   
   <summary> Ruby（とRails）を担当している石原です。 ソーシャル「OSを入れた後に...</summary>
   <author>
      <name>石原 淳也</name>
      
   </author>
         <category term="Ruby" scheme="http://www.sixapart.com/ns/types#category" />
   
   <category term="74" label="capistrano" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="49" label="rails" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="73" label="デプロイ" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="75" label="本番環境" scheme="http://www.sixapart.com/ns/types#tag" />
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[Ruby（とRails）を担当している石原です。

ソーシャル「OSを入れた後にインストールする10のアプリケーション」(仮) を作る過程をレポートしています。

これまでのエントリーはこちら ↓

<ol>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/10/ruby_on_rails_1.html">つくるぶガイドブログ: Ruby on Rails を使ってひとりでサービスを作ってみよう</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/11/post_2.html">つくるぶガイドブログ: ひとりサービスの雛型をつくる(リキッドレイアウト、GetText、Acts as Authenticated)</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/11/scriptgen.html">つくるぶガイドブログ: Rails で楽々ソーシャルブックマークの仕組みを作る</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2007/12/rails_acts_as_taggable_redux.html">つくるぶガイドブログ: Rails プラグイン acts_as_taggable_redux でタグクラウドを作ろう</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/01/rails_ajax.html">つくるぶガイドブログ: ドラッグアンドドロップで並べ替え(Rails + Ajax)</a></li>
<li><a target="_blank" href="http://www.tkrb.jp/guide/2008/01/rails_rcov.html">つくるぶガイドブログ: Rails + rcov でテストカバレッジを調べる</a></li>
</ol>

まだ開発途中ではありますが、今回はアプリケーションを本番環境にデプロイしてしまおうと思います。

開発環境で開発したアプリケーションを本番環境に移すデプロイの作業は、それぞれの環境によって千差万別なやりかたがあるため、ときにはかなりてこずることがあります。

リリース間際ではなく、なるべく早い段階で本番環境でも開発環境と同じように動くことを確認しておくと安心です。

capistrano はデプロイの作業を楽にしてくれるおすすめのツールです。

&raquo; <a href="http://www.capify.org/">Capistrano: Home</a>

本番環境には筆者が契約している VPS(Cent OS 5) を例に、capistrano のデプロイ作業の流れを紹介します。

アプリケーションサーバーには Mongrel Cluster、Web サーバーには Apache という定番の構成をとることにします。]]>
      <![CDATA[<h4 class="large-tit">capistrano の前に。前提条件</h4>

capistrano を使ってデプロイする前に、必要なソフトウェアやミドルウェアを本番環境となるサーバーにインストールしておく必要があります。

筆者が実際にインストールしたものを列挙します。それぞれのインストール方法は割愛させていただきます。

<ul>
<li>ruby 1.8.6</li>
<li>rubygems 1.0.1</li>
</ul>

まずは ruby と rubygems です。これがないと始まりません。

<ul>
<li>Apache 2.2.6</li>
<li>MySQL 5.0.27</li>
</ul>

Apache や MySQL はホスティングサーバーに始めから用意されているかもしれません。ただ Apache に関しては、後述する mongrel cluster との連携のためには、ある程度新しいバージョンのものが必要です。

以上が用意できたら必要な gem ライブラリをインストールしていきます。

<ul>
<li>rails 1.2.6</li>
<li>gettext 1.10.0</li>
<li>mongrel 1.1.3</li>
<li>mongrel_cluster 1.0.5</li>
</ul>

これらは以下のコマンド一発でインストール可能なので、次々インストールしていきましょう。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
sudo gem install [ライブラリ名]
</pre>

<h4 class="large-tit">開発環境に capistrano をインストール</h4>

capistrano は開発環境からコマンド一発で、最新のソースコードを本番環境に配置したり、データベースのマイグレーションやサーバープロセスの再起動をおこなってくれるツールです。

これらのタスクはそれぞれ、本番環境にログインして手動でおこなえる些細なタスクではありますが、ちょっとでもミスをすると実際に動いているサービスに影響がでてしまいます。

ぜひとも自動化してしまうことをおすすめします。

capistrano は開発環境で使用するので、ローカルのマシンに

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
sudo gem install capistrano
</pre>

でインストールします。

Rails アプリケーションのトップフォルダにて

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
capify .
</pre>

を実行すると、config フォルダの下に deploy.rb という capistrano 用の設定ファイルが出来ます。

この deploy.rb を以下のように編集します。

それぞれの説明を赤字でコメント文として追加しました。

<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">

require 'mongrel_cluster/recipes'
<font color="red"># Mongrel に関連した capistrano コマンドが使えるようになります</font>
set :application, "10best" <font color="red"># アプリケーションの名前</font>
set :repository,  "http://10best.googlecode.com/svn/trunk" <font color="red"># subversion のレポジトリ</font>
set :user, "app" <font color="red"># 本番環境に ssh でアクセスするときのログイン名</font>
set :mongrel_conf, "#{current_path}/config/mongrel_cluster.yml" <font color="red"># mongrel_cluster 設定ファイル</font>
set :mongrel_clean, true <font color="red"># mongrel を再起動するとき、残ってしまったプロセスをクリアします</font>

# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
# set :deploy_to, "/var/www/#{application}" 
<font color="red"># 本番環境のデプロイ先のパス。何も指定しない場合 /u/apps/[アプリ名] にデプロイされます</font>

# If you aren't using Subversion to manage your source code, specify
# your SCM below:
# set :scm, :subversion

role :app, "10best.champierre.com" <font color="red"># 本番環境ホスト名(アプリケーションサーバー)</font>
role :web, "10best.champierre.com" <font color="red"># 本番環境ホスト名(Webサーバー)</font>
role :db,  "10best.champierre.com", :primary => true <font color="red"># 本番環境ホスト名(DBサーバー)</font>

# http://www.hostingrails.com/forums/deployment_troubleshooting_thread/665
default_run_options[:pty] = true <font color="red">
# capistrano コマンド実行中、パスワード入力の部分でうまく動作しない場合、これを入れると直る場合があります</font>

</pre>

mongrel cluster の設定ファイルを作成するためにローカルマシンにも mongrel をインストールしておきます。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
sudo gem install mongrel
</pre>

以下のコマンドを実行すれば、config フォルダに mongrel cluster の設定が書かれた mongrel_cluster.yml が作成されます。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
mongrel_rails cluster::configure -e production -p 8000 -a 127.0.0.1 -N 3 -c /u/apps/10best/current
</pre>

<h4 class="large-tit">cap コマンドで簡単デプロイ</h4>

いよいよデプロイです。

capistrano のコマンドは cap で始まります。rake のように、

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
cap -T
</pre>

で、使用できるコマンドの一覧を表示できます。

まずは

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
cap deploy:setup
</pre>

を実行してみましょう。

これで本番環境のデプロイ先に必要なディレクトリが作成されます。今回の例では、/u/apps/ の下に 10best というフォルダが作成されます。

作成されないときは、ssh ログインユーザーに指定されているユーザーが u や apps に対して書き込み権限を持っているかどうか確認しましょう。

ちゃんと作成されているようであれば、

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
cap deploy:cold
</pre>

を実行します。ソースコードをレポジトリよりチェックアウトし、データベースのマイグレーションをおこない、mongrel を起動します。

なかなか一発ではうまく完了しないと思います。その場合には、capistrano が何をおこなおうとして失敗したかがエラーとして表示されますので、本番環境にログインしてみて、同じコマンドを実行してみると問題と解決方法を発見しやすいかと思います。

すべてのコマンドがうまく実行できたら、本番環境でアプリケーションが動作しているはずです。

といってもまだ外からアクセスできないので、本番環境上でたとえば

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
lynx http://localhost:8000
</pre>

とテキストブラウザでアクセスしてみて動作していることを確認します。

<h4 class="large-tit">Apache と Mongrel を連携させる</h4>

最後に Apache と Mongrel を接続して、外からもアプリケーションにアクセスできるようにします。

Apache の設定ファイルに

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
</pre>

があって、mod_proxy と mod_proxy_balancer が使えることを確認してください。

httpd.conf を以下の通り編集します。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

ProxyRequests Off

DocumentRoot /u/apps/10best/current/public

&lt;Directory "/u/apps/10best/current/public">
  Options FollowSymLinks
  AllowOverride None
  Order allow,deny
  Allow from all
&lt;/Directory>

ProxyPass /images !
ProxyPass /stylesheets !
ProxyPass /javascripts !

ProxyPass / balancer://railsapp/
ProxyPassReverse / balancer://railsapp/
&lt;Proxy balancer://railsapp>
  BalancerMember http://127.0.0.1:8000 loadfactor=20
  BalancerMember http://127.0.0.1:8001 loadfactor=20
  BalancerMember http://127.0.0.1:8002 loadfactor=20
&lt;/Proxy>

</pre>

Apache を再起動後、外部からもアプリケーションにアクセスできることを確認します。

<h4 class="large-tit">まとめ</h4>

capistrano を使って本番環境にデプロイしました。

今回ソースコードにほとんど更新はありませんが、ここまでのソースコードをアップロードしておきます。

&raquo <a href="http://code.google.com/p/10best/">10best - Google Code
</a>

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">

svn checkout http://10best.googlecode.com/svn/trunk/ 10best

</pre>

でチェックアウトするか、ブラウザでも<a href="http://10best.googlecode.com/svn/trunk/">リポジトリ</a>を閲覧することができます。 
]]>
   </content>
</entry>
<entry>
   <title>JiftyでWebアプリをつくる - ログイン機能を作る</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/02/jiftyweb.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.53</id>
   
   <published>2008-02-05T10:34:15Z</published>
   <updated>2008-02-09T15:28:15Z</updated>
   
   <summary> ※2008/02/09  Jifty::Plugin::Authenticat...</summary>
   <author>
      <name>春田・西山</name>
      
   </author>
         <category term="Perl" scheme="http://www.sixapart.com/ns/types#category" />
   
   <category term="7" label="Perl" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="42" label="Webアプリケーション" scheme="http://www.sixapart.com/ns/types#tag" />
   <category term="24" label="Windows" scheme="http://www.sixapart.com/ns/types#tag" />
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
<b>
※2008/02/09 
Jifty::Plugin::Authentication::Passwordについて追記しました。
</b>
</pre>

こんにちは、Perl担当の西山です。

前回に引き続き、DRY(Don't Repeat Yourself)をとことん追求する
WebフレームワークJiftyの話題です。
今回からJiftyを使ったWebアプリケーションの作り方について書いていこうと思います。
手元の環境にPerlの実行環境やJiftyをインストールしていない方は
<a href="http://www.tkrb.jp/guide/2008/01/jiftyweb_windows.html" target="_blank">前回のエントリー</a>を参照してください。

<h4 class="large-tit">ログイン機能を付ける</h4>
さてサンプルアプリのお題は・・・と考えてみましたが、<br>
Jifty自体がRailsを少なからず意識していることもありますし、<br>
本ガイドブログのRubyのエントリーに勝手に(汗;)連動して<br>
パスワード認証によるログイン機能をまずは実装してみようと思います。<br>
<a href="http://www.tkrb.jp/guide/2007/10/ruby_on_rails_1.html" target="_blank">Ruby on Railsを使った作り方はこちら</a> を参照して比較してみるのも面白いのでは？<br><br>
なお、ここでは説明の簡単の為にDBはMySQLではなく標準添付のSQLiteを使用します。
]]>
      <![CDATA[<h5 class="small-tit">方針：Loginプラグインを利用する</h5>
Jiftyの機能拡張として開発されているJifty::Plugin::Loginというプラグインを使用します。<br>
Loginプラグインを追加すると、以下の機能が追加されブラウザから利用できるようになります。

<ul class="common-list">
<li>アカウント作成 (/signup)</li>
<li>ログイン (/login)</li>
<li>ログアウト (/logout)</li>
<li>パスワード変更 (/chgpasswd)</li>
<li>パスワード確認 (/passwordreminder)</li>
</ul>

ログイン/ログアウト機能だけでなく、アカウント自体の作成やパスワード確認など
アカウント管理全般の機能がセットで用意されます。

Loginプラグインは前回の手順でインストールしたJiftyには入っていない為、別途インストールする必要があります。
前回の記事を参考にしてJifty-Plugin-Loginパッケージを検索してインストールしてください。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
※2008/02/05現在PPMのリポジトリに登録されているJifty-Plugin-Loginは、
CPANにアップされているものと比べてバージョンが古い？ようで、
スクリプト起動時に以下のようなエラーメッセージが表示されました。

Undefined subroutine &UNIVERSAL::is called at C:/Perl/site/lib/Jifty/Plugin/Logi
n/Model/User.pm line 4.
Compilation failed in require at C:/Perl/site/lib/Jifty/Util.pm line 261.
BEGIN failed--compilation aborted at C:/Perl/site/lib/Jifty/Plugin/Login/Action/
Signup.pm line 11.
Compilation failed in require at C:/Perl/site/lib/Jifty/Util.pm line 261.

少々荒っぽい方法ですが、<a href="http://search.cpan.org/CPAN/authors/id/S/SA/SARTAK/Jifty-0.71129.tar.gz" target="_blank">CPANからモジュールを直接ダウンロード</a>し、
tarボールを展開して、デフォルト設定ではC\Perl\site\lib\Jifty\Plugin配下に
インストールされているLoginプラグインを上書きすることで今回はしのぎました。

</pre>


<h5 class="small-tit">1.アプリケーションの作成</h5>
では、ここからイチからログインプラグインを利用するまでの流れを説明します。<br>
まずはヘルパースクリプトを実行してアプリケーションの雛形を作成します。<br>
コマンドプロンプトを開いて、以下のようにjiftyコマンドのappオプションを実行してください。<br>
ここでは"LoginTest"というアプリケーション名で作成します。
<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
C:\tmp>jifty app --name LoginTest
Creating new application LoginTest
Creating directory LoginTest/lib
Creating directory LoginTest/lib/LoginTest
Creating directory LoginTest/bin
Creating directory LoginTest/etc
Creating directory LoginTest/doc
Creating directory LoginTest/log
Creating directory LoginTest/var
Creating directory LoginTest/var/mason
Creating directory LoginTest/share
Creating directory LoginTest/share/po
Creating directory LoginTest/share/web
Creating directory LoginTest/share/web/templates
Creating directory LoginTest/share/web/static
Creating directory LoginTest/lib/LoginTest/Model
Creating directory LoginTest/lib/LoginTest/Action
Creating directory LoginTest/t
Creating configuration file LoginTest/etc/config.yml
</pre>

<h5 class="small-tit">2.設定ファイルの編集</h5>
生成された雛形の中で、LoginTest\etc\config.ymlがアプリケーション全体の設定ファイルになります。
必要な変更は2点です。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
①使用するプラグインを設定
  Plugins: 
    - Login: {}
</pre>

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
①メール送信設定
  # 確認メールなどの送信者アドレスに使用
  AdminEmail: robot@tkrb.jp
  # メール送信方法を指定。標準のSendmailはWindows環境では利用できない為、
  # SMTP(Net::SMTPモジュール)を指定する
  Mailer: SMTP
  # Net::SMTPモジュールへの引数を配列のリファレンスで設定
  MailerArgs: ['Host', 'SMTPサーバ名']
</pre>


<h5 class="small-tit">3.ユーザ情報のModelクラスを作成</h5>
ヘルパースクリプトを実行してユーザ情報のModelクラスの雛形を生成します。<br>
以下のようにLoginTest\bin\jiftyコマンドのmodelオプションに<br>
Modelクラス名を指定して実行してください。
<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
C:\tmp\LoginTest>perl bin\jifty model --name User
Writing file C:\tmp\LoginTest/lib/LoginTest/Model/User.pm
Writing file C:\tmp\LoginTest/t/00-model-User.t
</pre>

生成されたUserクラス(LoginTest\lib\LoginTest\Model\User.pm)
<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
use strict;
use warnings;

package LoginTest::Model::User;
use Jifty::DBI::Schema;

use LoginTest::Record schema {

};

# Your model-specific methods go here.

1;
</pre>
生成されたUserクラスを編集して、Loginプラグインが用意するUserモデルを継承するよう記述します。

編集後のUserクラス(LoginTest\lib\LoginTest\Model\User.pm)
<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
use strict;
use warnings;

package LoginTest::Model::User;
use base qw/Jifty::Plugin::Login::Model::User/;

1;
</pre>


<h5 class="small-tit">4.DBをセットアップ</h5>
以上の手順でアプリケーションの準備は整いました。
以下のコマンドを実行して、アプリケーションが必要とするデータベースとスキーマを作成します。
このコマンドはスキーマ情報などに変更が無ければ以降は実行する必要はありません。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
C:\tmp\LoginTest>perl bin\jifty schema --setup
INFO - Generating SQL for application LoginTest...
INFO - Using LoginTest::Model::User, as it appears to be new.
INFO - Using Jifty::Model::Session, as it appears to be new.
INFO - Using Jifty::Model::Metadata, as it appears to be new.
INFO - Set up version 0.0.1, jifty version 0.71129
</pre>

<h5 class="small-tit">5.動作確認</h5>
実際にLoginTestアプリケーションを起動してブラウザで動作を確認してみましょう。
LoginTest\bin\jiftyコマンドにserverオプションを渡して実行するとアプリケーションが起動して、
http://localhost:8888/ のURLで確認できます。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
C:\tmp\LoginTest>perl bin\jifty server
DEBUG - Plugin Jifty::Plugin::Login::View added as a Template::Declare root
DEBUG - Plugin Jifty::Plugin::Login mason component root added: (C:\Perl\site\li
b\auto\Jifty\Plugin\Login/web/templates)
INFO - You can connect to your server at http://localhost:8888/
</pre>

<ul class="common-list">
<li>アカウント作成 http://localhost:8888/signup </li>
<img alt="sc0001_thumb.png" src="http://www.tkrb.jp/guide/2008/02/05/sc0001_thumb.png" width="480" height="293" />

<li>ログイン http://localhost:8888/login </li>
<img alt="sc0002_thumb.png" src="http://www.tkrb.jp/guide/2008/02/05/sc0002_thumb.png" width="480" height="279" />

<li>パスワード変更 http://localhost:8888/chgpasswd </li>
<img alt="sc0003_thumb.png" src="http://www.tkrb.jp/guide/2008/02/05/sc0003_thumb.png" width="480" height="231" />


</ul>

各入力フォームでは入力チェックもしっかり用意されていて、Ajaxでもチェックされます。
また、アカウント作成時に入力されたメールアドレスに確認メールを送信して
メールを受信したユーザに承諾された時に初めてアカウントが作成する処理を作るのも
いざ作ろうと思うとかなり面倒なので、プラグインとして用意されているのは便利ですね。

<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
※Loginプラグインはデフォルトではエラーメッセージやメール文章などが
英語で表示されますが、別途設定ファイルを用意することで
日本語を割り当てることが可能です。具体的な方法は次回以降に扱います。
</pre>

<h4 class="large-tit">Jifty::Plugin::Authentication::Passwordを使う場合</h4>
<b>※2008/02/09追記</b><br>
現在はJifty::Plugin::LoginよりもJifty::Plugin::Authentication::Passwordを<br>
使用する方が望ましいという情報をいただきました。<br>
ご指摘ありがとうございます。<br>
<a href ="http://d.hatena.ne.jp/charsbar/20080206" target="_blank">http://d.hatena.ne.jp/charsbar/20080206</a>


Jifty::Plugin::Authentication::Passwordを使う場合の差異は2点です。

1. config.ymlにAuthentication::Passwordプラグインを設定
<pre style="line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:0px 10px 0px 10px;">
  Plugins: 
    - Authentication::Password: {}
</pre>

2.ヘルパースクリプトで生成されたUserクラス(LoginTest\lib\LoginTest\Model\User.pm)に
　プラグインの宣言を追記。
<pre style="background:transparent url(http://fukapyon.tkrb.jp/mt/mt-static/themes/theme-guide/tit_code.gif) no-repeat left top; line-height:1.5em; overflow:auto; border:1px solid #269000; font-size:10px; padding:20px 10px 0px 10px;">
use strict;
use warnings;

package LoginTest::Model::User;
use Jifty::DBI::Schema;

use LoginTest::Record schema {};

use Jifty::Plugin::User::Mixin::Model::User;
use Jifty::Plugin::Authentication::Password::Mixin::Model::User;


1;
</pre>

以降のDBのセットアップやサーバの起動手順は上記Loginプラグインと同様です。
Authentication::Passwordプラグインを使うと、
各種文言やメール文章などもデフォルトで日本語表示されます。


<h4 class="large-tit">まとめ</h4>
Jiftyを使って、アプリケーションの雛形の作成からログイン機能を追加するところまでの流れを見てきました。<br>
ファイルやモジュールの構成などの詳細には立ち入らずにJiftyでのアプリケーション作成の概要を説明しましたが、それでもプラグインを追加・設定するだけで
アカウント作成やログインなどのアカウント管理機能を手軽に導入できることが分かりました。<br>
JiftyのPluginは「機能」だけでなくURLや画面も含めてセットで提供することも可能な為、
「サブアプリケーション」を追加するイメージに近いかもと思いました。

次回は具体的なアプリケーション作成方法を見てみたいと思います。

<h5 class="small-tit">参考URL</h5>
<ol class="number-list">
<li><a href="http://search.cpan.org/dist/Jifty/" target="_blank">Shawn M Moore / Jifty - search.cpan.org</a></li>
<li><a href="http://d.hatena.ne.jp/charsbar/20070831/1188529679" target="_blank">CommitBitの使い方 - Charsbar::Note</a></li>
<li><a href="http://www.crium.univ-metz.fr/docs/devel/jifty/" target="_blank">CRIUM : Framework Jifty</a></li>
</ol>
]]>
   </content>
</entry>
<entry>
   <title>Javaによるサーバサイドマッシュアップ</title>
   <link rel="alternate" type="text/html" href="http://www.tkrb.jp/guide/2008/01/stru.html" />
   <id>tag:www.tkrb.jp,2008:/guide//2.52</id>
   
   <published>2008-01-28T10:55:35Z</published>
   <updated>2008-01-28T11:25:16Z</updated>
   
   <summary> 前回までに、Strutsアプリケーションの一部機能がAjax対応になりました。...</summary>
   <author>
      <name>ATL Enterprise Architecture Unit</name>
      
   </author>
         <category term="Java" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.tkrb.jp/guide/">
      <![CDATA[<a target="_blank" href="http://www.tkrb.jp/guide/2008/01/post_5.html">前回</a>までに、Strutsアプリケーションの一部機能がAjax対応になりました。ビジネスロジックがJavaBeansになっていることで、それが持つ処理の再利用が実現でき、さらにDWRを使うことによって、Webブラウザ上からJavaScriptでのサーバサイドの処理を比較的簡単に利用することができました。多くのStrutsで作られたシステムは、このようにAjax対応を進めていくことができますので、積極的にAjaxに対応して、よりリッチな操作感を利用者に提供していきましょう。

さて今回からは、Javaでのマッシュアップに関する話をしていきたいと思います。元々Javaは「ネットワークに強い」という特徴がありますので、インターネット上に存在するどんなWebサービスでも、マッシュアップを行って自分のサービスに統合することが可能です。他の言語と違い、Javaの世界は、無数のOSSが次々と登場しています。それらをうまく活用することで、よりマッシュアップが手軽なものになります。]]>
      <![CDATA[題材としている「イベント検索アプリケーション」も、マッシュアップを行って、扱う情報を充実させていきたいと思います。単にイベント情報だけが提供されるのでは、面白くありません。例えば、

<ul class="common-list">
<li>開催場所の近くにどんな居酒屋があるか</li>
<li>イベントの内容に関連した書籍を探したい</li>
<li>参加した人のブログを見てみたい</li>
</ul>

など、イベントを取り巻く情報は数知れず考えられます。これらも提供してくれるイベント検索アプリケーションであれば、とても魅力的なサービスに見えることでしょう。

<h4 class="large-tit">マッシュアップの形態</h4>

Javascriptからサーバを呼び出すには、図1にあるようにJavascriptからXMLHttpRequestを使ってサーバの処理を呼び出します。他のWebサービスをマッシュアップをする場合、XMLHttpRequestでは、図1の×印で示されているように、ドメインを超えてアクセスすることを禁止する「クロスドメインの制約」が存在しているため、直接JavascriptからWebサービスのサービスを呼ぶことが出来ません。

<br />

<img alt="figure1.gif" src="http://www.tkrb.jp/guide/2008/01/28/figure1.gif" width="480" height="200" />

<center>図1 通常のサーバ呼び出し</center>

<br />

そこで、クロスドメイン制約を超えてJavascriptからWebサービスのサービスを呼び出すために、いくつかの方法が考えられます。

ひとつは、自サーバを他のサービスのプロキシとして利用する方法です(図2)。Javascriptからは、自サーバにXMLHttpRequestでリクエストを行い、自サーバからマッシュアップ先のWebサービスのサービスを呼び出し、その結果をそのままWebブラウザに返してマッシュアップを行います。この方法では、サーバ側では通信を行うだけで済みますが、Webサービスのサービスから返されるXMLやJSONなどをJavascriptで解釈する必要があります。Javascriptが得意でない開発者にとっては、難しいパターンと言えるでしょう。

<br />

<img alt="figure2.gif" src="http://www.tkrb.jp/guide/2008/01/28/figure2.gif" width="480" height="240" />

<center>図2 自サーバをプロキシとする</center>

<br />

次の方法は、JSONPと呼ばれる方法です(図3)。マッシュアップをしたいWebサービスがJSONPに対応している場合に利用することができます。HTMLのscriptタグは、XMLHttpRequestと違ってクロスドメインの制約を受けません。この利点を利用して、scriptタグのsrc属性にWebサービスを呼び出すリクエストを記述し、その際に指定されたコールバック関数の中で結果を処理する、というマッシュアップを行うことができるようになります。ただし、このパターンに関してもJavascriptに関する知識がかなり必要となるでしょう。

<br />

<img alt="figure3.gif" src="http://www.tkrb.jp/guide/2008/01/28/figure3.gif" width="480" height="210" />

<center>図3 JSONP</center>

<br />

最後の方法は、自サーバをプロキシとして利用する方法と似ていますが、サーバ側でWebサービスを呼び出した後に、得られたXMLやJSONの中から欲しい情報を抜き出してマッシュアップする方法です(図4)。サーバ側での処理は増えますが、Javascriptをシンプルに記述することができるので、Java側のコーディングが得意な開発者が多い場合に適しています。

<br />

<img alt="figure4.gif" src="http://www.tkrb.jp/guide/2008/01/28/figure4.gif" width="480" height="260" />

<center>図4 サーバサイドマッシュアップ</center>

<br />

<h4 class="large-tit">Webサービスの種類</h4>

Webサービスは、人間がWebブラウザを使ってアクセスするものではなく、情報を得るためにプログラムがアクセスするものです。そのために、Webサービスを利用するためのプログラム(クライアント)と、情報を提供するWebサービス(サーバ)との間で、通信のための共通的な取り決めが必要になってきます。この取り決めをプロトコルと言いますが、Webサービスでは、大きく分けて3つのプロトコルが採用されています。

<ul class="common-list">
<li>SOAP</li>
<li>RESTful</li>
<li>REST準拠</li>
</ul>

<h5 class="small-tit">SOAP</h5>

<a target="_blank" href="http://ja.wikipedia.org/wiki/SOAP_(%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB)">SOAP</a>とは、Webサービスが提供する情報や処理を、XMLをベースにして呼び出す手順を取り決めたプロトコルです。扱われる情報は、SOAPによって取り決められたXMLの封筒に格納されて配送されます。SOAPの仕様は非常に複雑で重厚ですが、多くの場合その仕様を開発者が理解する必要はありません。

下記のような老舗的なWebサービスのほとんどが、SOAPをサポートしていました。しかし、最近では減少傾向にあるようです。

<ul class="common-list">
<li><a target="_blank" href="http://code.google.com/">Google SOAP Search API</a>(廃止が決定)</li>
<li><a target="_blank" href="http://www.amazon.com/gp/browse.html?node=3435361">Amazon Web Service</a></li>
<li><a target="_blank" href="http://developer.yahoo.co.jp/">Yahoo Webサービス</a></li>
<li><a target="_blank" href="http://webservice.rakuten.co.jp/">楽天検索API</a></li>
</ul>

SOAPに対応したWebサービスにアクセスする場合、開発者は下記のどちらかの手法を選択することになります。

<ul class="common-list">
<li>Webサービス側から提供されるクライアントライブラリを利用してWebサービスにアクセスする。</li>
<li>Webサービス側から提供されるWSDLからスタブコードを生成して、それを使ってWebサービスにアクセスする。</li>
</ul>

どちらの場合も、JavaのAPIを使ってWebサービスにアクセスすることが可能になるため、低レベルな通信を意識せずにWebサービスを利用できるという利点があります。ただし、Webサービスへのアクセスに何らかの認証が必要とされる場合に、複雑なAPIを使わなければならないといったことも考慮しなければなりません。

JavaにおいてSOAPに対応したWebサービスを扱う場合、下記のライブラリがよく使われます。

<ul class="common-list">
<li><a target="_blank" href="http://ws.apache.org/axis2/">Apache Axis</a></li>
<li><a target="_blank" href="http://sdc.sun.co.jp/java/techarticles/jax/Rev3/jax_rpc_03.html">Java API for XML-based RPC</a></li>
</ul>

<h5 class="small-tit">RESTful</h5>

次のRESTfulとは、HTTPのメソッドでよく使われるGETやPOSTに加えて、PUTやDELETEといったメソッドも使用してサーバ内の情報を取得あるいは操作を行うためのプロトコルです。サーバにある情報(リソースと呼ばれます)には、それを特定するためのURI(WebではURLに相当します)が決定されます。そのURIに対してDELETEメソッドを発行することによって情報の削除を依頼する、またはPUTメソッドを発行することにより情報を修正する、といった手順がRESTfulです。

RESTfulが盛んに言われるようになったのは最近ですので、RESTfulに対応したすぐにマッシュアップできるWebサービスはまだまだ数少ないです。しかし、今後増えてくることは確実です。

JavaでのRESTful対応Webサービスにアクセスするための手法についても、次々と登場することが予想されます。しかし、現時点での選択肢は残念ながら少なく、<a target="_blank" href="http://hc.apache.org/httpclient-3.x/">Jakarta Commons HttpClient</a>などを頼って自作するケースがほとんどでしょう。

<ul class="common-list">
<li><a target="_blank" href="http://www.restlet.org/">Restlet</a></li>
</ul>

<h5 class="small-tit">REST準拠</h5>

RESTfulは、SOAPに比べるとかなり軽量な手順ですが、HTTPのPUTやDELETEといったメソッドを利用しなくてはいけない、URIの規約が堅い、などの点で取っ付きにくさがあります。そこで、単に情報を提供するだけのWebサービスでは、RESTfulに完全対応するのではなく、RESTfulの一部分のみを採用しているケースがほとんどだと言えるでしょう。情報の操作が必要なければ、基本的にGETメソッドのみで済みますので、情報を特定するためのURIのみREST風に決めてあげれば、RESTful準拠のWebサービスということになります。

REST準拠なWebサービスは、上記のSOAP対応のWebサービス全てが同時にRESTにも準拠しています。その他にも、下記のWebサービスがREST準拠です。

<ul class="common-list">
<li><a target="_blank" href="http://api.hotpepper.jp/">ホットペッパー Webサービス</a></li>
<li><a target="_blank" href="http://api.gnavi.co.jp/api/service.htm">ぐるなびWebサービス</a></li>
<li><a target="_blank" href="http://jp.youtube.com/dev">YouTube</a></li>
</ul>

JavaでのREST準拠なWebサービスへのアクセスは、上記のJakarta Commons HttpClientを使用したり、java.net.URLConnectionによるアクセスなど、比較的低レベルなものを使用することで十分なケースがほとんどです。もちろん、Restletも使用することもできるでしょう。

<h4 class="large-tit">Webサービスが返す情報の形式</h4>

Webサービスが返す情報の形式としては、一般的にXMLとJSONの2種類があります。利用者がどちらかを選べるパターンもあれば、どちらかに固定されているパターンもあります。最近では、選べるWebサービスが多くなってきました。

受け取ったXMLをJava内部で扱うには、そのままDOMやSAXパーサを利用して扱う方法もありますが、単調なコードの繰り返しとなり不具合が起こり易くなります。そこで、最近ではXMLとJavaオブジェクトをマッピングするためのライブラリが数多く出てきており、それらを使うことが一般的になってきています。主なライブラリとして、以下があげられます。

<ul class="common-list">
<li><a target="_blank" href="http://xstream.codehaus.org/">XStream</a></li>
<li><a target="_blank" href="http://commons.apache.org/digester/">CommonsDigester</a></li>
<li><a target="_blank" href="http://xmlbeans.apache.org/">XMLBeans</a></li>
<li><a target="_blank" href="http://jibx.sourceforge.net/">JiBX</a></li>
<li><a target="_blank" href="https://jaxb.dev.java.net/">JAXB</a></li>
</ul>

JSON(JavaScript Object Notation)とはもともとはJavaScriptにおけるデータ構造を記述するするための記法でした。それが、Ajaxで使用されるJavaScriptとサーバとのデータのやり取りに使われるようになり、2006年にはRFCで仕様が策定され現在では広く使われるようになりました。JSONで表現できるデータ型は、数値、文字列、真偽値、配列、ハッシュなどが表現でき、比較的簡単な記述でデータ構造をあらわすことができます。

Javaにおいても、もちろんJSONを扱うことが多くなってきています。JSONの記述とJavaオブジェクトをマッピングするライブラリとして、下記のライブラリが使われています。

<ul class="common-list">
<li><a target="_blank" href="http://www.json.org/java/index.html">org.json</a></li>
<li><a target="_blank" href="http://json-lib.sourceforge.net/">JSON-lib</a></li>
<li><a target="_blank" href="http://jsonmarshaller.sourceforge.net/">JsonMarshaller</a></li>
</ul>

<h4 class="large-tit">技術を組み合わせる</h4>

ここまでで、Javaでのサーバサイドマッシュアップで使われる技術を見てきました。これらは、どれか一つを習得すれば良い、というものではなく、利用したいWebサービスの都合に応じて適用するライブラリや情報の形式を選択する、ということが求められます。もちろん、利用するライブラリは好みのものをチョイスして、使い倒します。この選択肢の広さが、Javaの醍醐味と言えるかもしれません。

今後の傾向としては、WebサービスはRESTfulを採用する流れが強くなっていくと予想されます。JavaEEの仕様にもRESTfulに対応するためのAPIが盛り込まれる予定ですので、今からウォッチしておくと良いでしょう。

<h4 class="large-tit">次回は</h4>

今回はWebサービスで使われるプロトコルや情報の形式の紹介と、JavaでWebサービスにアクセスする際に使用する各種ライブラリを紹介しました。次回は、「イベント検索アプリケーション」に機能追加して、Javaでのサーバサイドマッシュアップを体験することにしましょう。]]>
   </content>
</entry>

</feed>
