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

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

Railsアプリの最大のボトルネックとなりうるのはSQLとのやりとりである。
(Erbのレンダラーだという話は置いておく)

なので、Railsアプリでチューニングを行う時に真っ先に手をつけるのは
AR.find系のDBアクセスな部分になる。

ちょっとしたアプリを作れば、(普通にモデリングしている限り)複数の関連モデルができあがる。
この場合、チューニングのポイントはEagerLoadingにあり、
:hoge has_many :fugas
:fuga has_many :piyos
という関連にある場合は、一発ですべて取得できるこれは非常に便利。

Hoge.find(id, :include=>{:fugas=>:piyos})
とやれば、すべての関連をインスタンス化した状態で取得できる。

これは便利なので、結構めくら滅法使ってしまう。
関連先をループで回して表示したりすることがあるときは、これだけでかなりのチューニングになる。

さて、
ここからが本題。

たいていの場合、親になるモデルをインスタンス化するのはコントローラーの責務になる。
(当たり前ですが)

で、ちょっと複雑なことになったモデルクラスで、
parent.childlen.find(:all, :conditions=>[conditions])
みたいに取得するか、find_by_sqlするか、
それともループを回して配列にコピーするのがいいのか、
判断に迷うことがある。

こんな時、すでにEagerLoadingでロードされているのか、
はたまたロードされていないのか判断ができれば処理分岐で対応できる。

loaded?

これで判定がつく

parent has_many :children

だったとき、parentクラス上で、

if self.childlen.loaded?
  # ループで回す処理
else
  # DBアクセスを伴う処理
end

という感じ。

ちなみにテスト上では反映されないので注意。

PR
ActiveRecordのfindメソッドの:conditionsオプションって、INを判ってくれない・・・

class Hoge
  :has_many :relations
  :has_many :fugas, :through=>relations

class Fuga
  :has_many :relations
  :has_many :hoges, :through=>relations

class Relation
  belongs_to :hoge
  belongs_to :fuga

とあったとして、Hogeインスタンス上から、
Fugaインスタンス配列を元にRelationインスタンス配列がほしかったとする。

ナチュラルに考えて試してみる
self.relations.find(:all, :conditions=>["fuga_id IN (?)", fugas])
# nil

じゃあID配列で
self.relations.find(:all, :conditions=>["fuga_id IN (?)", fugas.map{|f| f.id}])
# nil

それじゃあID配列をjoinしてカンマ区切りに
self.relations.find(:all, :conditions=>["fuga_id IN (?)", fugas.map{|f| f.id}.join(',')])
# 最初の一つ目だけ

find_by~ならどうだ!!
self.relations.find_by_fuga_id(fugas.map{|f| f.id})
# 最初の一つ目だけ

まあ、当然ですよね・・・

結局find_by_sqlに頼ってしまうのね・・・

Relation.find_by_sql("SELECT * FROM relations WHERE hoge_id = #{self.id} AND fuga_id IN (#{fugas.map{|f| f.id}.join(',')})")

ActiveRecordって、ある一線を越えるとSQLを直接叩く方が早くてわかりやすいよね・・・
ちゃんちゃん
AptanaRadRailsを愛用しております。
NetBeansよりもコードアシストがよくなかったりとか、いろいろアレ気なところも多いIDEですが、
まあ、手になじんでいるのもあって愛用しています。

しかし、先日アップデートしてから非常にウザいワーニングが出まくりました。

identical code structure ~

というワーニングが、コードのありとあらゆるところに出まくってます。

「似たようなコードがほかにもあるから、メソッド化してDRYを保てよ~」という意図らしいですが、

UnitTestのコードとか、
ActiveRecordのFindメソッド呼び出しとか、
ちょっとしたハッシュの定義とかでも出まくる。

確かにぱっと見似たコードですよ・・・
でもこれ以上どうしようもないじゃん。
あんたちゃんと見てないでしょ・・・という感じ。

はじめはいちいち[Igonre this warning]とかをクリックして消していたけど、
あまりにもウザ過ぎる。

で、肝心の消し方。

Window->Prefanceから
Ruby-Erorors/Warnings-Duplicate Codeで
Enable checking my code for duplicate AST structuresのチェックを外す。

環境の再構築をしているらしく、しばらく待たされるけど、
これでウザいワーニングは出なくなりました。

本家のTracにも載ってたので、世界中からウザいと思われてたんでしょうね・・・

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(関連名そのままのカラム)を探しに行くらしい。


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

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

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

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

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

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

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

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

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

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

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

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

前のページ      次のページ
ついめ~じ
ブログ内検索
フリーエリア
サニーカメラ
Powered by Ninja Blog    template by Temp* factory    icon by MiniaureType

忍者ブログ [PR]