忍者ブログ
ブログツール、2007/11/06作成
[1] [2] [3] [4] [5] [6] [7
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

FactoryGirlで、
関連を表現したいときは
fuga belongs_to :hogeだったとすると

Factory.define :hoge_name, :class=>Hoge do |h|
  h.name 'name'
end

Factory.define :fuga_name, :class=>Fuga do |f|
  f.name 'fuga'
  f.hoge Factory(:hoge)
end

と書けるんだと理解していた。ブレース({})で囲む必要があるのは、遅延ロードしたいときだけ、
という認識だったんだが、どうも違うみたい。

きちんとブレースで囲んで、
  f.hoge { Factory(:hoge) }
とやってやらないと、rakeでエラーが帰ってくる。

どうやらFactoryGirlがDBのテーブルにfuga.hoge(関連名そのままのカラム)を探しに行くらしい。


PR
新しいプロジェクトでテストをきちんと書くのに、
どうもリレーションが多いモデルなので、
まじめにFixtureを書こうとすると気が狂いそうになる。

特に、Railsのバージョンを2.3.5に変えたので、
IDが自動的に生成されちゃうので、なおのこと関連どうかけってよ・・・orz
なことに。

とりあえずFixtureのヘルプをみるも、omapを使え、とか書いてあるし。
調べてみるとかなりめんどくさそう。
ということで見つけたのがここ

なるほどねえ・・・
ということで導入決定。

親切に書いてあるけど、一応本家を参照しつつやってみる。

しかしまあ、どこも今はRSpecが標準らしく、
使い方はどこもRSpec向けの設定ばかり。

その上なんかテストの形式も変わってるし・・・
test "hoge" do て何?
という感じですっかり時代に取り残された気分。

まあ、とにかくfactoriesディレクトリを作成して*.rbファイルを作り、
中にFactory.defuineなものを書いて設定するとよし。

ただ、Fixtureと違って自動的にロードしてくれないので、
各テストのsetupメソッドでファクトリを呼び出してインスタンス化とかしておかないとだめ。

逆に、細かくセーブ前、セーブ済み、属性のみ、スタブのみとかコントロールできるので
それはそれでいいかも。

というわけで、ちょっと使い込んでみます。

# 調べている時に、YAMLの中に<%= %>でRuby記述できることを今回初めて知ったのは内緒

属性値に何らかの手を加えてから格納したいときがある。

たとえば、名前関係のフィールドで「フリガナ」フィールドに全角カタカナだけを入力させたい。
正規表現でそれ以外をキックするのはいいけど、
ひらがなや半角カタカナは自動変換できるから自動変換したいと。

方法はいくつかある。
一般的にはフィールド格納をオーバーライドする方法
もう一つはvalidateをオーバーライドしてそこで何とかする方法

今回はまったのは属性代入をオーバーライドする方法。

ActiveRecordを継承したNameクラスにkanaフィールドがあったとする。

普通のRubyクラスなら

class Name
  def kana=(str)
     @kana = NKF.nkf('--katakana', str)
  end
end

しかし、ARの場合@でクラス変数にアクセスできない。
エイリアスで kana=を別名定義して代入させようとするとこれはだめ。
kana=は定義されていなくて、MethodMissingで代入されるからだ。
self.kana=で代入すると普通に無限ループに陥っちゃう。

どうするかというと、別のアクセス方法を試す。

class Name < ActiveRecord::Base
  def kana=(str)
    self[:kana] = NKF.nkf('--katakana', str)
  end
end

とまあこんな感じ。

と考えるとシンボルでアクセスされると自動変換されないか・・・
素直に考えれば、validateをオーバーライドするのがいいのかもね。
どっちにしても検証はしないといけないしね。

class Name < ActiveRecord::Base
  def validate
    self.kana = NKF.nkf('--katakana', self.kana)
     unless self.kana =~ /カタカナかどうかを判別する/
        self.errors.add :kana, "全角カタカナで入力してください"
     end
  end
end

普通にモデルを作っていて、一意性を確保したい属性には、validates_uniqueness_ofでチェックするのがRails流。
スマートにかけていいよね~とか思っていると、ここに落とし穴が。

まず、MySQLをRDBMSに利用している場合のみらしいが、
オプションの :case_sencitive が効かない

大文字小文字を認識してくれないのであるよ。
同じ理由で、ActiveRecord.countもActiveRecord.findも正しくない結果が帰ってくる。

で、ActiveRecord.find/countの時は、:conditionsオプションを指定するときに、
BINARY句を追加する。

こんな感じ
Hoge.find(:conditions=>["BINARY name = ?", "hoge"])

まあ、これはいいとして、validates_uniqueness_ofではどうするか、ということになる。
手っ取り早いが根本的でない解決法は、validates_uniqueness_ofを使わない、という選択肢。
代わりにvalidateメソッドを定義して、
そこでcountを発行して存在するかをチェックする。

このとき、自身がnew_record?かどうかで、自分を除外するコードを足すかどうかを判別する必要がある。

根本的な解決は、ActiveRecord/validate.rbのvalidates_uniqueness_ofをオーバーライドする事になる。
属性の型を認識して、BINARY句を追加するか判別しないといけない。

しかし、更に問題なのはvalidate_uniquness_ofではレコードの一意性が保証されないこと。
いわゆる、トランザクションとかロックの問題なんだけど、これはDBMSに丸投げするのが正解。

マイグレーションを使っているなら最後にユニークインデックスを張ってやる

add_index :tabel_name, :colun_name, :unique=>true

これは、validates_uniqueness_ofを使っている以上はどの場合もそうらしい。

やれやれ。
エラーページを自前のものにしたいとき、これまではごく簡単に、public/500.htmlとかを書き換えてました。

ただ、これだと、imgタグとかを使ったときにRoutingErrorが発生したり、
動的になにかを表示できないと。

で、Render系メソッド呼び出してエラーページをレンダリングしたくなるわけです。

で、ググってみた。
すると、ActionController::Rescue::rescue_actionをオーバーライドするといいよ~
等の情報が。

でも、rescue_actionをオーバーライドすると、
ログなんかも自前で呼び出さないといけなくなるとか。

あ~、結構めんどくさいなあ・・・と思いつつドキュメントとか、ソースを読んでみると、
どうやらrescue_action_in_publicをオーバーライドすればいいらしい。

エラー発生から画面表示までの流れは、

エラー発生->
rescue_action->
エラーハンドラ呼び出し/アクセスもと判別->
(ローカルアクセスの時)rescue_action_locally(開発中のいつものエラー画面)
(パブリックアクセスの時)rescue_action_in_public->
エラーコード取得->エラー画面表示(render :file)

という感じ。

rescue_actionを単純にオーバーライドすると、
・ハンドラ呼び出しがされない
- ロガー呼び出しがない
・アクセスもと判別がない
 - ローカル開発中のエラートラッカーが見られない
等々の問題が。
(もちろん、オーバーライド先で実装すればいい話だけど)

それよりは、もっと末端の
rescue_action_in_publicをオーバーライドすれば、
実際にサービスアクセスされたときだけオリジナルのエラー画面を表示できるし、
ロガー関連もデフォルトのものが自動的に呼び出されるので、
これまでにロガー関係に手を入れていた場合もそのままいける。

ちゅう訳で、
自前エラー画面を実装するときはActionController::Rescue::rescue_action_in_publicをオーバーライドしよう。

ちなみに自分でやったのは、ActionControllerでオーバーライド。

  def rescue_action_in_public(exception)
    code = response_code_for_rescue(exception).to_i
    render_page = 'normal_error'  # 500番台を基本とする
    if code >= 400 && code < 500  # 400番台のエラー
      render_page = 'no_route'
    end
    puts render_page
    puts "xhr request is #{request.xhr?}"
    if request.xhr?
      render :partial=>"#{render_page}"
    else
      render :action=>"#{render_page}", :layout=>'error'
    end
  end

と、こんな感じ。

よく見ると、ドキュメントにも書いてある。

rescue_action_in_public(exception)

Overwrite to implement public exception handling (for requests answering false to local_request?). By default will call render_optional_error_file. Override this method to provide more user friendly error messages.

※Rails1.2系です。2.0系はもっと便利になってるらしい。
ついめ~じ
ブログ内検索
フリーエリア
サニーカメラ
Powered by Ninja Blog    template by Temp* factory    icon by MiniaureType

忍者ブログ [PR]