Categories
open all | close allTags
名称 | 国際化 | OpenID | ドキュメント | Subversion | アクセス制御 | Flash | テスト | Migration | スキンエンジン | RESTful | Aptana | rake | パソコン | モデル | フォーム | デュアル・コア | タグ | 認証 | CSRFSearch
フォームと言語の処理
フォームはRailsのビューの機能を使ってパーシャル・レンダリングで実装します。具体的にはcommentform-notloggedin.templateだったら,これをviewsの下のformsディレクトリに置き,「_commentform_notloggedin.rhtml」と,①最初に_を付ける,②ハイフンを_にする,③拡張子をrhtmlにするといったファイル名の変更をします。中身もビューの仕様に合わせて修正します。
<a id="nucleus_cf"></a>
<form method="post" action="#nucleus_cf">
<div class="commentform">
<input type="hidden" name="action" value="addcomment" />
<input type="hidden" name="url" value="<%= formdata(:destinationurl) %>" />
<input type="hidden" name="itemid" value="<%= itemid %>" />
<%= errordiv %>
<label for="nucleus_cf_body"><%= text("_COMMENTFORM_COMMENT") %></label>
<textarea name="body" class="formfield" cols="40" rows="10" id="nucleus_cf_body"><%= formdata(:body) %></textarea>
<label for="nucleus_cf_name"><%= text("_COMMENTFORM_NAME") %></label>
<input name="user" size="40" maxlength="40" value="<%= formdata(:user) %>" class="formfield" id="nucleus_cf_name" />
<label for="nucleus_cf_mail"><%= text("_COMMENTFORM_MAIL") %></label>
<input name="userid" size="40" maxlength="60" value="<%= formdata(:userid) %>" class="formfield" id="nucleus_cf_mail" />
<label for="nucleus_cf_email"><%= text("_COMMENTFORM_EMAIL") %></label>
<input name="email" size="40" maxlength="100" value="<%= formdata(:email) %>" class="formfield" id="nucleus_cf_email" />
<%= callback(:FormExtra,:commentform_notloggedin) %>
<input type="checkbox" value="1" name="remember" id="nucleus_cf_remember" <%= formdata(:rememberchecked) %> />
<label for="nucleus_cf_remember"><%= text("_COMMENTFORM_REMEMBER") %></label>
<input type="submit" alt="<%= text("_COMMENTFORM_SUBMIT") %>" value="<%= text("_COMMENTFORM_SUBMIT") %>" class="formbutton" />
</div>
</form>
で,formdataやtextをアプリケーションのヘルパで実装します。ここではtextのところだけ示します。
def text sym
l(sym.to_sym)
end
上の例ではtextの引数は文字列にしていますが,シンボルでも構いません。
次に多言語化の処理です。
Simple Localizationは,YAML形式の言語ファイルを作ることで多国語化を行うので,現在のNucleusのスタイルと非常によく似ています。さらにYAMLの階層構造を利用できるので,ネームスペースをあまり気にしなくていいというメリットがあります。今回の実装では,Nucleusの言語ファイルからなるべく簡単に移植できるように,次のようにしています。
libの下にlanguagesディレクトリを置き,そこに言語ファイルを置いていきます。今のところja.ymlだけが存在しています。中身はこんな感じ。
app:
_ERROR_ITEMCLOSED: このアイテムは閉じました
_COMMENTFORM_COMMENT: コメント
_COMMENTFORM_NAME: お名前
_COMMENTFORM_MAIL: ウェブサイト
_COMMENTFORM_EMAIL: メール
_COMMENTFORM_REMEMBER: 情報を記憶しておく
_COMMENTFORM_SUBMIT: コメントを追加
本当は階層構造にしていった方がきれいですが,移植のしやすさを考えてフラットにしてしまいました。
そして,environment.rbに次のように書きます。
simple_localization :language => [:ja, :en]
ArkanisDevelopment::SimpleLocalization::Language.lang_file_dirs << "#{File.dirname(__FILE__)}/../lib/languages"
最初の行は言語ファイルの種類を示しています。1番目が優先的に使われます。2行目がこのアプリ用の言語ファイルを読ませるための設定です。これで,先ほどのYAMLを読み込んで表示するようになりました。
国際化プラグイン
Simple Localizationは,プラグインにも利用できることがわかったので,これを採用することにしました。あまり進んでいませんが
render :partialでフォームを貼り込めることは確認できました。国際化について補足
プラグインを国際化するのにも使えるかどうか。メールはどうするかっていうのもあるけど,そこまで国際化ツールに期待すると機能が肥大化してしまいそう。
さて,フォームをどうしようか
前に,認証,更新系の順に作業する予定と書きましたが,先に更新系をやろうかと思っています。その前段階としてフォームをどうするかで悩み中。
まず,現状のNucleusはNucleusのパーサーを使ってフォームを生成しています。これをどうするか。
今のRails版のアプローチでは,そのまま移植するのが一番楽ですが,フォーム部分はスキンと独立しているので,Nucleusの<%,%>を使う必然性はありません。なので,この部分はRailsのビュー機能を使い,Partial Renderingで入れていくのがいいのかなと思っています(それで動くかどうかは未テスト)。
それともう1つ,国際化をどうするかという問題があります。RailsのWikiの国際化方法の比較ページを見ていたのですが,決めかねています。世間的にはgettextの評価が高いのかと思っていますが,gemでインストールしなければいけないのがちょっと面倒なのと,「gettextでなくちゃ」というのがどこなのか,よく分かっていないというのがあります。
Railsのプラグインとして使える中では,DBに言語データを入れていくGlobalizeは面倒なので却下。なんとなくよさげかなあと思うのがGloc。ただ,Simple Localizationというのも導入のしきいが低そうなところは好感が持てます。とりあえずSimple Localizationを入れておいて,後から変えることを検討するというのが妥当なところかもしれません。
テンプレートのコメントの処理がよく分からない
Nucleusのテンプレートにおけるコメントの処理がよく理解できなくて,先に進めないでいます。なんだかややこしいことをやっているのは分かるのですが。えーと,これだけでは何なので,今まで書いていなかった,基本的な処理の流れを紹介(といっても大したことはありませんが)。
まず,入ってきたURLを振り分けるのはroutes.rbの役割。ここはこんな感じです(デフォルトブログの場合)。
# rules for the index page
map.blog '', :controller => "application", :action => 'index'
map.blogcategory 'catid/:catid', :controller => "application", :action => 'index'
# rules for the item page
map.item 'item/:itemid', :controller => 'application', :action => 'item'
map.itemcategory 'item/:itemid/catid/:catid', :controller => 'application', :action => 'item'
# rules for the archivelist page
map.archivelist 'archivelist', :controller => 'application', :action => 'archivelist'
# rules for the member page
map.member 'member/:memberid', :controller => 'application', :action => 'member'
# rules for the archive page
map.archive 'archive/:daymonth', :controller=> 'application', :action => 'archive', \
:requirements=>{:daymonth => /\d{4}-d{1,2}(-d{1,2})?/}
map.archivecategory 'archive/:daymonth/catid/:catid', :controller=> 'application', \
:action => 'archive', \
:requirements=>{:daymonth => /\d{4}-d{1,2}(-d{1,2})?/}
名前付きのルーティングもできるように,map.connectではなくて,各ルートに名前を付けています。
これを見て分かるように,このあたりは全部applicationというメインのコントローラが処理します。actionのところでスキンタイプごとに振り分ける形。
このほか,ポスト系を処理するactionのコントローラと管理用のadminコントローラ,rss用のコントローラを別途作っていく予定です。
これを受けるapplication.rbは今のところこんな感じ。グローバル変数を多用しているところがちょっと格好悪いのですが。
class ApplicationController < ActionController::Base
private
def set_charset
headers['Content-Type'] = "text/html; charset=utf-8"
end
# Pick a unique cookie name to distinguish our session data from others'
session :session_key => '_nonror_session_id'
private
def setblog
if !params['blogname']
$NCGlobals['blog'] = Blog.find(CONF['DefaultBlog'])
else
$NCGlobals['blog'] = Blog.find_by_bname(params['blogname']) || Blog.find(CONF['DefaultBlog'])
end
end
def setcatid
if params['catid']
$NCGlobals['catid'] = params['catid'].to_i
end
end
public
def index
setblog
setcatid
$NCGlobals['skin'] = $NCGlobals['blog'].skin
$NCGlobals['skintype'] = 'index'
$NCGlobals['item'] = Array.new
$NCGlobals['controller'] = self
@contents = $NCGlobals['skin'].part($NCGlobals['skintype'])
@controller = NCParser::DefaultSkinIndex.new $NCGlobals['controller']
end
def item
setblog
setcatid
$NCGlobals['skin'] = $NCGlobals['blog'].skin
$NCGlobals['skintype'] = 'item'
$NCGlobals['item'] = [Item.find(params['itemid'].to_i)]
$NCGlobals['controller'] = self
@contents = $NCGlobals['skin'].part($NCGlobals['skintype'])
@controller = NCParser::DefaultSkinItem.new $NCGlobals['controller']
render :action=>'index'
end
end今,actionはindexとitemしかありませんが,itemの最後には「render :action=>'index'」というのが入っています。これは表示用のビュー・ファイルはindex.rhtmlを使うということ。全スキンで共通にしています。といってもビュー・ファイルの中身は「<%= parse %>」だけ。これを処理するのはapplication_helper.rbで,この中身もいたってシンプル。
# Methods added to this helper will be available to all templates in the application.
module ApplicationHelper
def parse
@controller.parse(@contents)
end
end
ここで@controllerというのがパーサのオブジェクトで,それがスキンの中身をパースするという仕組みです。これでパーサが実行した結果が出力されます。これが基本的な流れです。どうもきたない
文字化け問題,Railsのコンソールでは化けているものの,Webの出力は化けていないことが分かり,ますますよく分かりませんが,とりあえず今のままでも困らないので,このまま進めます。スキン変数とテンプレート変数をちょこちょこと実装しています。相変わらずRubyのnilの扱いにはちょっととまどいがちですが,間違えるパターンが分かってきたので,バグ取りの時間は短くなってきたようです。
URL生成もできているのですが,気になるのはRails開発ブログでDeprecated Controller Methodsの中にurl_for(:#{options}) - use url_for with a named route directlyと書かれていること。
しかし,名前付きの呼び出し方 ...._url はprotectedなので直接コントローラから呼べないのと,_send_を使って無理やり送ってもちょっと動作が違うという問題があります。url_forを使って名前を指定する方法があるのかと,探しているのですが,見つかりません。
文字化けわけ分からん
DBをmysqldumpして,エディタで見てみたらちゃんとUTF-8でデータが入っています。それなのにRailsでは文字化け。
$KCODEもdatabase.ymlのencodingも指定しているのになあ…
ほかに何かあったっけ?
そんなわけで
RadRailsのSubversion対応はできました(Thanks to hsurさん)。で,結局夕べは眠くて,コントローラをローカル変数で渡したときに動かないというバグだけ取って寝ました。昨日は睡眠時間4時間オーバー。全体の進捗を書いておくと,パーサの基本部分はできていて,後はスキンやテンプレートでparse_●●というのを書いていけばいい状態。次に与えられたURLに対して動作を振り分けるところを作っていて,それに対応するURL生成をテストしているところ。これがうまく行ってcreateLink系の部分ができると基本的にブラウズはできる形になります(検索除く)。
その後は,認証系を作り,コメントなどの更新系の作業に移る計画です。
もろもろ
●パーサをクラスでなくてモジュールにするかという件。パーサが複数になって連動したりすると名前のコンフリクトとか絶対に起きるだろうなと思い,やっぱり却下。今はコントローラをグローバル変数に入れているのが,事実上は問題なくてもちょっと気持ち悪いところ。そこをパラメータで渡してインスタンス変数にしようかと。でも,なぜかエラーが出ます。グローバル変数はあまり使いたくないのだけど,プラグインで様々な機能を入れていくときに結局必要になるのではないかという気もします。●プログラムを書くのは夜中12時くらいから2時くらいまでなのですが,デバグなどしているとついつい時間オーバーしてしまいがち。朝7時に起きることを考えると3時までには寝るようにしないときついのですが。さらに,夜1回1時間くらい寝てしまってから,起きて作業をすると,目が冴えてしまって,今度は眠りに着くのが大変。夕べも結局睡眠時間が3時間くらいに。こうなると昼間の仕事に影響が出てしまうので,一回寝てしまった日はプログラム書かないとか,デバッグしないとか,ルールを決めないと体壊しそうです。
●Subversionの使い方が,相変わらずよく分かっていません。今はローカルにtrunkだのbranchだののツリーを作って,サーバーのリポジトリと同期させ,trunkの下だけをInstantRailsの下にコピーして作業しています。作業内容をツリーに入れるときに,どこを更新したかちゃんと覚えていないので,更新忘れのファイルなどが出現しがちです。それから,他の場所で更新したものを反映するときも,ローカルのツリーでどこが変わったのかを忘れてしまい,作業ディレクトリに反映し忘れてしまうことがあります。RadRails(Eclipse)にはSubversionの機能があるというのですが,使い方が分かっていません。作業ディレクトリから直接リポジトリと同期させるのでしょうか。
●このブログ,カテゴリーもちゃんと作っていないいい加減な構成。基本的に作業メモだからいいんだけど,実際には動作チェックもここのデータを使っているので,もうちょっとまじめに作っておかないとちょっと大変。MCやTagEXも入れようかなあ。あ,既に入っているのだけどつかってないだけでした。