Categories
open all | close allTags
rubygems | 名称 | Migration | スキンエンジン | カテゴリ | デュアル・コア | Flash | テスト | 認証 | CSRF | ドキュメント | フォーム | タグ | rake | パソコン | JustPosted | RESTful | Aptana | 国際化 | SubversionSearch
スキン・エンジンの切り替え部分
今ここに手を入れるのが適切だったのかどうか分かりませんが,いつかはやらないといけないところですから,やってよかったのでしょう。まだ完全ではありませんが,思ったほどトラブルなしで動きそうです。最大の変更は,従来のスキンクラスとは別に様々なスキンを一括管理するモデルを加えたこと。SkinManagerとしています。登録内容はスキンの名前とそのクラスです。クラスが決まればそれに対してリクエストを投げることで,実際の描画などを行います。同時にBlogモデルとのリレーションは従来のSkinクラスに代わって,SkinManagerになります。互換性のために,Skinクラスの当初登録内容とSkinManagerのIDは同じにします。
class SkinManager < ActiveRecord::Base
set_base_name :skin_manager
has_many :blogs, :class_name => 'Blog', :foreign_key => 'bdefskin'
delegate :file, :fileurl, :to=>'@skin'
def parse controller
@skin = self.skinclass.constantize.findname(self.skinname)
@skin.parse controller
end
end
内容はこれだけ。実際のスキンのクラス側ではクラスメソッドとしてfindname(名前からスキンのオブジェクトを返す),インスタンスメソッドとしてparse(描画実行),file(ファイル名から内容を返す),fileurl(ファイル名からURLを生成する),part(スキンタイプからそのスキンの内容を返す)といったメソッドを用意しておく必要がありますが,クラスのヒエラルキーは問いません。
説明を補足するとparseメソッドの1行目にあるconstantizeというのがここのキモです。これはRailsがStringクラスを拡張して加えたメソッドの一つで文字列からそのクラスのオブジェクトを作ります。つまりskinclassが"Skin"だったら"Skin".constantizeはSkinクラスになります。これにfindnameというメソッドを呼び出すことでオブジェクトを返してくれます。同じようなことはevalを使ってもできますが,evalよりはこの方がきれいですし,セキュリティ上もベターです。
補遺:実は今回初めて新しくテーブルを作るためにMigrationの機能を使いました。予想はしていたけどテーブルのプレフィクスを見てくれません。どうしたものだか。いっそプレフィクスはあきらめて,nucleus_~~というテーブル名を標準にしてしまうという妥協案を取ってしまうという手もあるかも。
というわけでパースの構造を見直しています
今日はパースしないところまでできました(意味なし,笑)。今まではデフォルト・パーサーのクラスにパースさせていたのですが,今回はスキンにパースさせる形に変えてみました。スキンのテーブルから,パーサーの名前を取ってきて,パーサーにパースさせる方が汎用性が高いかも。まあ,その違いはそんなに大きくないはずなので,簡単に切り替えられるでしょう。
今回の改造ができると,例えばスキンやテンプレートをDBではなくファイルにするスキンあたりは簡単に作れます。parsedincludeのところだけどうごまかすかちょっと考えどころではありますが。
スキン・エンジンの仕様・実装メモ
現在のスキン・モデルはNucleusのDBベースのものしか取り扱えない。これをファイルベースのスキンなどにも利用できるようにする必要がある。ユーザーからするとスキンは名前で指定するだけだから,今のスキン・クラスとは別に統合的にスキンを管理するためのモデル・クラスが必要になる。そのクラスではスキン名とエンジン名のマッチングだけを行う。実際にスキンを読み込むところなどはスキン・エンジンの責任になる。今の実装ではスキン・データを読み込むところがコア側にあるので,責任範囲を変えないといけない。つまり,スキン・エンジンはスキン名を与えられたら,描画内容を返すこと,である。またスキン・エンジンは実際の描画を他のエンジンに任せることができる。すなわち,高級言語からアセンブラに変換するようなもので,結果を他のエンジンに引き渡して描画できる。
このようにするには,スキン・エンジンをモデルとして登録する際に,下位のエンジンが何かというフィールドを用意しておけばよい。
スキン・エンジン自体もRailsのプラグインとして登録可能であり,プラグインと同様にスキン・エンジン用のスタブ・クラスを用意する。後の実装はほとんど自由にできるはずだ。
管理画面を作り始める
まずはルーティングと認証から始めます。ルーティングはRails2の新機能であるmap.namespaceを使って表示系とは違うコントローラにします。具体的にはこんな感じ。
map.namespace(:admin) do |admin|
admin.root :controller =>'Admin'
admin.connect ':memberid', :controller =>'Admin'
endこれでadminディレクトリのadmin_controller.rbからAdmin::AdminControllerクラスをロードしようとします。
そちら側はこんな感じ
class Admin::AdminController < ActionController::Base
helper :all # include all helpers, all the time
before_filter :setmember
htpasswd :class=>"Member", :user=>"mname", :pass=>"mpassword" , :nofilter=>true
# See ActionController::RequestForgeryProtection for details
# Uncomment the :secret if you're not using the cookie session store
protect_from_forgery # :secret => '501bb263f43979b7993430648b7066d0'
def setmember
result = htpasswd_authorize
if @htpasswd_authorized_username
if !params['memberid']
redirect_to(:memberid=>@htpasswd_authorized_username)
else
if @htpasswd_authorized_username != params['memberid']
redirect_to(:memberid=>nil)
end
end
@member = Member.find_by_mname(@htpasswd_authorized_username)
end
return result
end
def index
render
end
end これで認証を通ってindexを実行するところまで動きます。
PS.TinyMCEを使ってみたのですが,こういったコードを入れるものとかとは相性よくなさそうです。Rucleus(仮)で採用しようかと考えていたのですが,別のエディタを使う方がよさそう。
認証の部分
アイテムでのprevlink,nextlinkもできました。ということで,スキン変数,テンプレート変数で実装していないのはまだまだたくさんありますが,次のフェーズに移ることを優先したいと思います。最近,コードを全く紹介していなかったので,現状の認証用コードをあげておきます。
def setmember
if (params['memberid'])
result = htpasswd_authorize
if @htpasswd_authorized_username
if params['memberid'] == ''
redirect_to(:controller=>'members', :memberid=>@htpasswd_authorized_username, \
:action=>'show')
else
if @htpasswd_authorized_username != params['memberid']
redirect_to(:memberid=>'', :action=>'show')
end
end
@member = Member.find_by_mname(@htpasswd_authorized_username)
end
return result
end
return true
end
これをbefore_filterで動かしています。つまりURLでmemberidが設定されたとき(/member/だけでも空文字列を設定するようにしています)だけ認証を行います。後はhtpasswdプラグインをちょっといじって,そちらでのbefore_filterを止めているのと,Nucleusのmemberテーブルに入っているMD5で暗号化されたパスワードとマッチするようにしています。
認証に成功した場合はコントローラの@member変数にメンバーのオブジェクトが入ります。
ページング処理を追加
といってもShowBlogsみたいな完全なページリンクではなく,従来からのprevlink,nextlinkに対応しただけです。link_toがヘルパーでしか使えないことに気付かず,思ったより時間がかかってしまいましたが,それ以外は特に問題なく。もともとモデルからアイテムを引っ張ってくるメソッドにオフセットを入れていたので,そこのパラメータを変えるだけで簡単にできました。というわけでpaginating_findを一応インストールしてみたものの,今のところは出番なく。このまま出番なしでいくのか,ページングの高機能化をするときに使うのかは保留状態です。
ところでprevlinkやnextlinkはアイテムページでも使えるわけですが,ここのサポートって結構難しいかも。そのときのコンテキストに依存するため,ちゃんと条件を与えて前や後のアイテムを調べないといけません。まあしょうがないからついでに考えておきましょう。
それから,ページングを高機能化する場合,カスタマイズ性をどうやって担保するのかって問題がでてきます。スキン変数を新たに作って好きなようにしてもらうのか,あるいは決めうちで作ってCSSでなんとかしてもらうのか。設計からすると前者の方法でやりたいところです。
02 Mar, 2008 | Foodyn ( 実装メモ , つぶやき , 作業メモ ) | ページネーション / prevlink / nextlink | Andy | Leave comment - 2 -
進捗
認証のところは一応動いています。一応というのは今のところBASIC認証しかしていないから。ダイジェストを使うにはテーブルを書き換えないといけないため,第2ステップで対応していきます。これで,ログインしているときとしていないときでコメントフォームを切り替える(URLも違います)ことができるようになり,コメントも動いています。
ついでに,これまでグローバル変数で対応していた部分,例えば現在のブログ・オブジェクトなどをコントローラのインスタンス変数としてアクセスするように変えました。ここで意外にはまってしまって,ちょっと苦労しました。原因はインスタンス変数の名前とパブリックなメソッドの名前が同じだったこと。Rubyはメソッド呼び出しにカッコが不要なため文法的にはメソッドと変数を区別できません。その部分でした。こういったところはRailsで警告を出してくれるなど,もうちょっと親切だと助かるのにと思いました。
とにもかくにも基本機能は大分そろってきたので,次にプラグインの構成を考えるか管理画面に移るかといったところを考えています。あ,もしかしたらページスイッチを先にするかも。
なんとかうまくいきそう
特定の条件のときだけ認証を行うには,フィルターを使わず,直接認証のファンクションを呼び出すほうが簡単にできそうです。最初はうまくいかなかったのですが,これは認証に失敗したときに(例えば最初のアクセスでは必ず失敗します),ブラウザにヘッダを送って認証用のユーザー名やパスワードなどを再送信してもらわないといけませんのをやっていなかったからでした。認証に失敗したときにすぐに終了するようにしたら,ちゃんと動くようになりました。まだDBとの連動などはやっていないのですが,ここの仕組みさえ動いていれば,そんなに大変ではないはずです。NucleusでAccessControlを作ったときにちょっとでもHTTP認証の仕組みを理解しておいてよかったです。
なかなかうまくいかないなあ
なんとかうまくRailsをだまして特定の場合だけフィルターを通そうとしているのですが,before_filterはアクションに対してしか効かないため,あとからトラップするのはなかなか難しいです。うーむ。認証をもうちょっと考える
昨日の方法はやはりあまりいいと思えないので,プラグインに手を入れて特定のメソッドだけを読んだときだけ認証をするようにできないか考えています。認証はbefore_filterでやっているので,ここのonlyの指定を入れたらいいだけのように思うのですが,案外思ったように動いてくれません。まだまだ精進が必要だなあ。orz