Categories
open all | close allTags
モデル | 国際化 | CSRF | フォーム | Flash | rake | Aptana | タグ | 認証 | デュアル・コア | テスト | アクセス制御 | スキンエンジン | Migration | Subversion | RESTful | パソコン | OpenID | ドキュメント | 名称Search
なかなかうまくいかないなあ
なんとかうまくRailsをだまして特定の場合だけフィルターを通そうとしているのですが,before_filterはアクションに対してしか効かないため,あとからトラップするのはなかなか難しいです。うーむ。余田話
最近ブログの参入障壁なんていう記事もいろいろ出ているけど,どうなんでしょうね。独自ドメインだと不利だとか。このブログなんて,ほとんど知っている人しか見ていないと思われるくらいのものですけど,まあそれで困りもしないし。
ブログサービスも2回ほど使ったことがあり(いや実は今も残っているから過去形じゃないんだけどどちらも半年以上更新していない)ますが,どうもしっくりこないんですよね。なにか自分でいじれないところが嫌でねえ。お仕着せになじまないというか。そういう人がNucleus使っているんだと思いますが。
それにしても技術系のブログの人ってはてな率が異常に高いですよね。どうしてなんだろう。独自に立てたいと思わないのかな。それともそれほどはてながいいってこと?
はてなのアカウントは持っているけどダイアリーは使ってないからなあ。だれか知ってたら教えてください。あ,過疎ブログでつぶやいてもだめか(笑)。
初心者用言語って…
PHPの話から派生して,初心者向け言語の話になっているようです。いろいろな人のブログを見てみても,なんだか意見が発散しているというか,そもそも初心者向けって何かという議論がなし崩しになっている感じがします。
Matzさんは抽象化を取り上げているのですが,僕はこれは行きすぎだろうと思います。自分がプログラミングを始めたときのことを考えると,最初の関門というのは数値の27と文字列の"27"の違いが分からなかったことだと思います。
PHPなどいわゆるLLは,この二つを「区別しなくていいよ」というのが基本的な態度のように思えます(Rubyはそうでもない部分が多いですが)。Webプログラミングという「入出力はテキストで行い」ながら,内部的には都合がいいときだけ数値として解釈するという世界では,これはとても便利です。一方で,こういった特徴が型に対して無自覚なプログラマを増やし,SQLインジェクションなどの温床になってしまっているようにも思えます。
そういった「自覚をうながす」ためには型付けの強い言語,例えばPascal(Delphi含む)のようなものが一番向いていると思います。
Matzさんのいう抽象化の機能というのは,一回このように型の違いなどを理解した上で,初めて身に付くものです。いきなりそこへ飛んでしまっては初心者は理解できないでしょう。
世間との折り合いを付ける意味ではC++とかって選択肢もあるでしょうけど「個人的に嫌い」なので。オブジェクト指向言語だったら,Smalltalk,Java,Rubyみたいに最初からオブジェクト指向として作られているものでないと気持ち悪いです。
フォームと言語の処理
フォームは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を使って名前を指定する方法があるのかと,探しているのですが,見つかりません。