Categories
open all | close allTags
Flash | 国際化 | 認証 | RESTful | ドキュメント | パソコン | Migration | フォーム | モデル | 名称 | rake | テスト | タグ | Aptana | CSRF | OpenID | Subversion | スキンエンジン | デュアル・コア | アクセス制御Search
そうか,クロージャって役に立つんだ
どうもいまいちクロージャの有用性が分かっていなかったのですが,階層カテゴリー部分の表示を作っていて,やっと役に立つことが分かったような気がしました。具体的に言うと,<%categorylist%>で表示する部分というのは既に作ってあったのですが,今回,アイテム投稿画面を作るときに,またカテゴリーのツリー表示が必要なことに気付いたわけです。最初はコピペして,同じようなことをやらせようとしたのですが,どうも効率が悪いし,アルゴリズムにバグがあったり,データ構造を変えたら両方直す必要があります。
そこで,Blogモデルの中に表示ルーチンを移し,そこからブロックを呼び出すような形に変えてみました。
def category_tree
tempbuffer = ""
level = 0
self.categories.inject(nil) do |last_right, cat|
# cat is under the last leaf
if !last_right || (last_right > cat.cleft)
level += 1
tempbuffer += yield(:level_up, level)
#
elsif (last_right + 2 <= cat.cleft)
tempbuffer += yield(:item_close, level)
(last_right+2..cat.cleft).each do |i|
level -= 1
tempbuffer += yield(:level_down, level)
end
else
tempbuffer += yield(:item_close, level)
end
tempbuffer += yield(:item, level, cat)
last_right = cat.cright
end
if (level > 1)
tempbuffer += yield(:level_down, level)
end
(level..1).each do |i|
tempbuffer += yield(:item_close, level)
end
tempbuffer += yield(:level_down, level)
return tempbuffer
endこれをスキン変数の実行部分では
def parse_categorylist params
blog = params[2] ? Blog.find_by_bname(params[2]) : @controller.blog
templatename = params[1]
template = Template.find_by_tdname(templatename)
bd = blog.basic_data
data = Hash.new
blog.category_tree do |type, level, cat|
data['level'] = level.to_s
case type
when :level_up
fill(template['CATLIST_HEADER'], bd.merge(data))
when :level_down
fill(template['CATLIST_FOOTER'], bd.merge(data))
when :item
data['catlink'] = url_for(:controller=>'categories', :catid=>cat.catid, :action=>'show')
fill(template['CATLIST_LISTITEM'], data.merge(cat.attrs))
when :item_close
fill(template['CATLIST_LISTITEM_END'], bd.merge(data))
end
end
endといった形でblog.category_treeにブロックを与える格好で呼び出します。かなりすっきりしたような気がします。管理画面でカテゴリーツリーが必要なときもblog.category_treeを使えばロジックは不要になります。
さらに,こういった構造に変えるメリットとして,スキン/テンプレートの実行部分とデータ構造を切り離せます。プラグインのイベント呼び出しを,これまではスキン/テンプレートの中からやる形だったのですが,モデル側に全部移せそうです。今後,スキン・エンジンを新たに作るときなどに,負担が大きく減るはずです。
Rubyforgeは「オレオレ証明書」?
そんなわけで勢い込んでRubyforgeにアカウントを作ってプロジェクトの承認も得たのですが,ログインしようとすると「安全な接続ができませんでした」というエラーが出てしまいます(Firefox3)。いわゆる「オレオレ証明書」を使っているもようです。エラー画面
無理やりログインすることはできるはずですが,ほかの人にそれを強いることはできないし…。Rubyforgeのフォーラムで数回質問が出ているようですが,改善する気があるのかどうだか。ちょっと萎えております。
Rubyforgeじゃなきゃいけないということでもないので,とりあえずSourceforge(JPじゃないほう)に申請を出しました。まあ,とりあえずSubversionを使いますのでどちらでも大差はないのですが。
Nucleus開発合宿に向けて
8月の下旬に行うNucleusの開発合宿(僕は日帰りですが)でFoodynのプレゼンをすることにしました。それに向けて,ほかの人がインストールしたり試したり準備できるようにしないといけなくなりました。まずはRubyforgeでアカウントを取り,プロジェクトの承認を得ております。しばらくは開発そのものより,ドキュメント整備などに力を入れたいと思います。
コントローラでのアクセス制御
管理画面ではすべてアクセス制御の対象になります。ビューでもアクセス制御を使って不要なリンクを出さないようにしますが,コントローラでも調べる必要があります。例えばURLを直接叩かれたときなどに対応するためです。コントローラでアクションを処理するところの先頭に,そのメソッドの呼び出しを置きます。
def new
check_blog_right :commit
@item = Item.new
unless performed?
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @item }
end
end
end
ここでは最初のcheck_blog_rightのメソッド呼び出しである。実装はこう。
def check_blog_right right
right = right.to_s
unless @member.has_right right, @blog
@errormessage = '403 error'
render :template=>'admin/admin/error.html.erb', :status => 403
end
end権限がなかった場合はその場でエラー画面を出力する。
このとき,そのままプログラムの実行を続けると本来(権限があったときのページ)を出力しようとして,既に出力が終わっているために「DoubleRenderError」が発生します。それを防ぐため,コントローラの「performed?」メソッドを使って,先に出力がある場合は,出力しないようにします
アイテム追加もできた
カテゴリーも選べないし,ただタイトルや本文入れられるだけですけど,ちゃんと生成することは確認。これで編集できるようになれば,ブラッシュアップして行くことで大分進みそう。先日書いたステージングは未来時刻を指定してそのときの表示というのを実現すればよさそう。それようの権限を入れた方がいいでしょうね。
力づくで解決
あまり,ここでばかり時間を費やしたくないので,コメントフォームを作ったときと同様,CSRF対策用のタグを手動で埋め込むことでとりあえず先に進むことにしました。ただ,さすがにフォームを作ったり,保存したりといったところは楽です。Railsのリソース・ルーティングにしたがってやっておくだけでほとんどはRailsが面倒見てくれる感じ。慣れたら,管理画面作るのはすいすいできそうです。というわけで,すごくいい加減(例えばデフォルトカテゴリはカテゴリIDを直接入れないといけない)ですが,ブログ設定をFoodynの管理画面で編集できるようになりました。
余談ですが「企業で使えるオープンソースCMS一挙12種類解説」という記事,なかなか参考になります。ステージ管理とか時限発行とか,考えていなかったなあと。時限発行は未来の日付で投稿された記事に対してpingなどを送る機能と同じような形で実現できそうな気がします。有効期限が切れた記事をドラフト扱いにするのか,遠い未来の投稿扱いにするのか,別のフラグを立てるのかといったところがちょっと悩ましいですが。
ステージ管理はどうやったらできるだろう。
またもやInvalidAuthenticcityTokenでエラー
とりあえずブログ設定のフォームなど,作ってみたりしているのですが,保存しようとするとInvalidAuthenticityTokenエラーが出てしまいます。普通にフォーム作っているだけなんだけどなあ。なぜだろう。
というわけでまたも停滞中。ちょっといらいら。
状況変わらず
Simple Localiaztionのバージョンを上げてみたのですが,状況は変わりません。動いていた部分も動かないのでちょっと困惑。バージョンが上がって大分変わっているみたいだし。とりあえずフォーラムに質問を投げてみたけどどうなることか。ここを放っておいて先に進むのは気が進まないのだけど,あまりにここで停滞しているのも何なので,そろそろ先に進むことを考えます。
言語ファイル
最近,どうも夜集中力が続かないのですが,このところ何をしているのかというと,管理画面用の言語ファイルを設定しようとして,意外に苦戦しています。設定はあっているようなのに,どうして動かないんだろう。プラグインのサイトのドキュメントを見ようとしたらApacheのエラーで落ちていたりとか。ちょっといらいら。初めての管理画面(笑)
とりあえず前言撤回で,RESTfulで用意されているもので大体なんとかできそうな感じがしてきました。deleteのユーザー・インタフェースなど,気になるところもありますが,まずは標準機能で実装していきたいと思います。で,一応今の管理画面。(管理画面)
URLは全部ではないですが,ちゃんとしたものを生成しています。
ここではメモとして,map.namespace付きのprefix付き,ネスティッド・リソースという“テンコ盛り”状態でのルーティングについて書いておきます。
map.namespace(:admin) do |admin|
admin.root :controller =>'admin'
admin.connect ':memberid', :controller =>'Admin'
admin.resources :blogs, :path_prefix=>'admin/:memberid' do |blog|
blog.resources :items
blog.resources :comments
blog.resources :categories
end
admin.resources :plugins, :path_prefix=>'memberid'
endというのがルーティング部分(まだ完全ではありません。必要なとこしか作ってないので)。ここでルートのコントローラを指定することで,アドミン用のコントローラはadminディレクトリの下から選ばれるようになります。アドミンへのアクセスは全部認証付きであり,認証されたユーザー名がmemberidになります。したがって'admin/:memberid'が管理画面のルートです。
その下のresourcesの部分が今日の骨子。ブログ周りのリソース定義をしています。RESTfulなルーティングを使うためにはresourcesが必要です。ここでちょっと注意が要るのがpath_prefixのところ,ここは本来':memberid'だけでいいはずなのですが,それだとURLを生成したときにadminが抜けてしまうバグ?があります。そこでわざわざこれを入れています。
以下でネストされたリソースを加えています。
URLを生成するときはリソースを指定する必要があるのですが,この場合,例えばアイテムの一覧を表示したかったら「admin_blog_items_url(@memberid, blog)」といったリソースをurl_forやlink_toなどのメソッドに渡します。ここで@memberidがユーザー名ですが,これをパラメータに入れるのも一つの肝。プレフィクスを指定しているからなのでしょうけれども,そのあたりの説明はドキュメントになく,試行錯誤で見つけました。なお,admin_blog_items_urlというメソッド名は「rake routes」でルート定義の一覧を出すと分かります。例えば今の部分は
admin_blog_items GET /admin/:memberid/blogs/:blog_id/items {:controller=>"admin/items", :action=>"index"}
といった形で記述されています。これで,今度はAdmin::ItemsControllerのindexメソッドを用意すればいいことが分かります。
これで後はスピードアップするかな?