ActiveRecord::Extensions
去年Twitterで教えてもらったActiveRecord::Extensions。すごく良いです。
日本語で書かれた記事が見つからなかったので、書いてみます。
ActiveRecord::Extensionsは文字通り、ActiveRecordの拡張です。わりと便利な機能がたくさんあるのでmysql使っている人は入れても損は無いと思う。postgresqlは未対応です。
インストール
gem install ar-extensions
もしくは
script/plugin install http://arext.rubyforge.org/svn/tags/ar-extensions-0.7.0
gemとpluginどちらも用意されてるのが嬉しい。
あとはenvironment.rbとかにrequire 'ar-extensions'しとく。
(注:ar-extensions-0.7.0はActiveRecordの1.14.1以上が必要です。)
主だったところの使い方
独断と偏見で、便利そうなところを抽出しました。詳しい使い方はrdoc読むかソースをここから読むと良いです。
Hashをサポートしたfindが可能
Postっていうモデルがあったとして
class Post < ActiveRecord::Base ; end Post.find( :all, :conditions=>{ :title => "Title", # title='Title' :author_contains => "Zach", # author like '%Zach%' :author_starts_with => "Zach", # author like 'Zach%' :author_ends_with => "Dennis", # author like '%Zach' :published_at => (Date.now-30 .. Date.now), # published_at BETWEEN xxx AND xxx :rating => [ 4, 5, 6 ], # rating IN ( 4, 5, 6 ) :rating_not_in => [ 7, 8, 9 ] # rating NOT IN( 4, 5, 6 ) :rating_ne => 4, # rating != 4 :rating_gt => 4, # rating > 4 :rating_lt => 4, # rating < 4 :content => /(a|b|c)/ # REGEXP '(a|b|c)' )
to_sqlをメソッドのように使う
class InsuranceClaim < ActiveRecord::Base ; end class InsuranceClaimAgeAndTypeQuery def to_sql "age_in_days BETWEEN 1 AND 60 AND claim_type IN( 'typea', 'typeb' )" end end
↑のように定義しておけば
claims = InsuranceClaim.find( :all, InsuranceClaimAgeAndTypeQuery.new ) claims = InsuranceClaim.find( :all, :conditions=>{ :claim_amount_gt => 30000, :age_and_type => InsuranceClaimAgeAndTypeQuery.new } )
こんな風に、複雑な条件のfindがすっきり書ける。
たくさんのデータのインポート
'create'や'save'メソッドはシングルINSERTなので、たくさんのデータをインポートするときはクエリが大量に発生する。データが数千件とかになると明らかにボトルネックなので、普通はマルチプルINSERTするメソッドを独自に作るわけだけど、ActiveRecord::ExtensionsはマルチプルINSERTするクエリを発行するメソッドを定義してくれている。
columns = [ :author_name, :title ] values = [ [ 'yoda', 'test post' ] ] BlogPost.import columns, values
素晴らしい。ちなみに、importにはActiveRecord::Base#reloadに似たsynchronizeっていうメソッドがあって、こいつを使うとインポートした内容が指定したオブジェクトに入る。
post = BlogPost.find_by_author_name( 'zdennis' ) columns = [ :author_name, :title ] values = [ [ 'yoda', 'test post' ] ] BlogPost.import columns, values, :synchronize=>[ post ] post.author_name # => 'yoda'
to_csvの拡張
to_csvを様々に拡張している。標準ライブラリよりも速いCSVライブラリのfaster_csv使っているからスピードも安心。
指定したカラムだけcsv吐き出し。
book = Book.find( 1 ) book.to_csv( :only=>%W( title isbn ) )
belong_toのassociationを使っているモデルのcsv吐き出し。
class Book < ActiveRecord::Base belongs_to :author end book = Book.find( 1 ) book.to_csv( :include=>:author )
has_manyのassociationを使っているモデルのcsv吐き出し。
class Book < ActiveRecord::Base has_many :tags end book = Book.find( 1 ) book.to_csv( :include=>:tags )
ネストしたassociationを使ってcsv吐き出し。
class Book < ActiveRecord::Base belongs_to :author has_many :tags end book = Book.find( 1 ) book.to_csv( :includes=>{ :author => { :only=>%W( name ) }, :tags => { :only=>%W( tagname ) } )
おわりに
例にあげた機能はほんの一部です。
詳しくは公式サイト
http://www.continuousthinking.com/tags/arext
を見てください。
みんなもっとActiveRecord::Extensions使えば良いと思うよ。
追記
NoMethodError: undefined method `to_csv'
と出てto_csvができないときは
class YourModel < ActiveRecord::Base include ActiveRecord::Extensions::FindToCSV end
includeしてあげると出来るよ。