狠狠撸

狠狠撸Share a Scribd company logo
named_scopeに
ついてくわしく
(株)永和システムマネジメント ?? Rails勉強会@東京

 諸橋 恭介            (もろはし きょうすけ)



   k-morohashi@esm.co.jp (work)
  moronatural@gmail.com (private)
まとめ

? named_scopeを使うと、複雑な検索条
 件をシンプルかつ柔軟に記述できる

? 技術的にずいぶんおもしろい
 ? とてもかっこいいmethod_missingの
  よい使い方
これはなに?

? かつてRailsには with_scope という
 APIがありました

? with_scopeを使うと、検索条件を指定
 する「スコープ」を定義できます

 ? そのスコープ内での検索には、自動的
  にスコープの条件が付加されます
with_scopeの例
User.find(:all)
# SELECT * FROM users

User.find(:all, :conditions=>[“deleted=?”,false])
# SELECT * FROM users WHERE deleted = 0

User.with_scope(
  :find=>{:conditions=>[“deleted=?”,false]}) do
  User.find(:all)
end
# SELECT * FROM users WHERE deleted = 0
飞颈迟丑冲蝉肠辞辫别について

                Recipe 077 (p.220)

                検索条件をスコープ毎に
                まとめて定義する




http://www.amazon.co.jp/dp/4797336625
奥丑补迟’蝉
         named_scope?
?飞颈迟丑冲蝉肠辞辫别で使っていた「検索スコー
  プ」に名前が付けられます。

?Rails 2.1の目玉機能です
 ? RubyKaigi 2008でも松田さんが話しました
http://www.slideshare.net/a_matsuda/
  new-wave-of-database-programming-with-ruby-19-on-rails-21/
なにがすごいの?
?名前を付けたスコープ同士を掛け合わ
せられます。
?   DBアクセスは、 必要となった時点で賢く(多くは1回だ
    け)実行されます。


?「微妙に条件の異なる検索」の多くを
スコープの掛け合わせで表現できます
ORMとRDBMS
? RDBMSをOOPの世界に引っ張ってきた
 ORMはすごい!!

? しかしORMを使うと、RDBMSは単なる
 オブジェクトの永続化ストレージのように
 扱いがちです。

? named_scopeを使うと、RDBの集合演
 算が持つ力の一部をOOPの世界でも使える
 感じになります。
苍补尘别诲冲蝉肠辞辫别の
     例
有効なユーザ
      users

active users
有効なユーザ
class User < ActiveRecord::Base
  named_scope(:active,
    {:condition=>["deleted = ?", false]})
end

describe “activeなユーザ” do
 it “は全員削除されて*いない*こと” do
    User.active.should be_all{|u| not u.deleted }
  end
end
人気のあるユーザ
実行時にscopeの一部を指定

     users

             hot users
人気のあるユーザ
       実行時にscopeの一部を指定

class User < ActiveRecord::Base
  named_scope :active,
   :condition=>["deleted = ?", false]

  named_scope :hot,
      Proc.new{|arg|
        {:conditions=>[“popularity > ?”, arg],
         :order =>"popularity DESC"}
    }
end
人気のあるユーザ
       実行時にscopeの一部を指定

describe “人気度80以上のユーザ” do
 before do
   @users = User.hot(80)
 end

 it "のうちトップはdahliaであること" do
    @users.first.should == users(:dahlia)
  end
end
人気かつ有効なユーザ
 crossover 2 scopes
       users

active users users
           hot
人気かつ有効なユーザ
               crossover 2 scopes
describe "BAN! dahlia" do
  before(:each) do
    # dahliaの削除フラグを立てる つまり active usersの集合から外す
    users(:dahlia).update_attribute(:deleted, true)
  end

  it "アクティブユーザで最も人気なのはcharlesであること" do
    User.active.hot(80).first.should == users(:charles)
  end

  it "クエリ発行(select_all)は一回だけ呼ばれること" do
    User.connection.should_receive(:select_all).once.and_return([])
    User.hot(80).active.find(:all)
  end
end
他にも好きな条件を
  追加できる
      users

active users n users
          hot

         Other..
named_scope
    実行時の動き
User.active.hot(80).each do |u|
  do_something
end
User.active.hot(80)
        - generate Scope instance -

User#active
#=> <#Scope:active, @proxy_scope=>User>


<#Scope:active>#hot(3)
---> <#Scope:active>#method_missing(:hot)

# scopes.include?(:hot)であるため
#=> <#Scope:hot, @proxy_scope=> <# Scope:active>>
Scope#method_missing


?User#activeはUserクラスを親ス
コープとしたScopeを作る

?Scope#method_missingは呼ばれた
メソッドに対応する子Scopeを作る
?   子笔谤辞肠には亲スコープとして蝉别濒蹿を渡す
Scope#method_missing


def method_missing(method, *args, &block)
  if scopes.include?(method)
    scopes[method].call(self, *args)
  else
    with_scope :find => proxy_options do
       proxy_scope.send(method, *args, &block)
    end
  end
end
User.active.hot(80)
      - execute and respond to method -

<#Scope:hot, @proxy_scope=> <#Scope:active>>#each
---> <#Scope:hot>#proxy_found
---> <#Scope:hot>#find(:all)
---> <#Scope:hot>method_missing(:find)
---> <#Scope:hot>#with_scope{ <#Scope:active>#find }
---> <#Scope:active>#method_missing(:find)
---> <#Scope:active>#with_scope{   User.find(:all)   }
# => [<#User:..>, .... ]

[<#User:..>, ...].each{ do_something }
Scope#method_missing



?呼ばれたメソッドに対応するScope
が定義されていない場合
? 自身の?ndパラメータでwith_scopeし、親ス
 コープの当該メソッドを呼び出す
Scope#method_missing


def method_missing(method, *args, &block)
  if scopes.include?(method)
    scopes[method].call(self, *args)
  else
    with_scope :find => proxy_options do
       proxy_scope.send(method, *args, &block)
    end
  end
end
飞颈迟丑冲蝉肠辞辫别で
             書き直したイメージ
User.with_scope(
  :find=>{:conditions=>[”popularity>?”, 80],
          :order=>”popularity” }) do

  User.with_scope(
   :find=>{:conditons=>[“deleted=?”, false]}) do

   User.find(:all).each{ ... }

  end
end                    Rails2.xではこのままでは動きません
まとめ

? named_scopeを使うと、複雑な検索条
 件をシンプルかつ柔軟に記述できる

? 技術的にずいぶんおもしろい
 ? とてもかっこいいmethod_missingの
  よい使い方
you.any?{|u|
  u.question?
}
FAQ: スコープはどんなふ
  うに結合されるの?
? 後ろから順にwith_scopeを使います。
 ? WHERE句相当(:conditionsパラメータ)はANDで
  結合されます。

 ? ORDERやLIMITは先に指定したscopeで指定して
  いるものがそのまま使われるっぽいです。

  ?   この辺りのロジックは activerecord-2.1.0/lib/
      active_record/base.rb : 1807 あたりを参照
FAQ: Ruby 1.9だとより
カッコ良いという噂があるが?
 ? Procオブジェクト生成に->記法が使えます
 旧
        named_scope :hot,
           Proc.new{|arg|
             {:order =>"popularity DESC",
 これが          :limit => arg}
         }

 こうなる   named_scope :hot, ->{
            {:order =>"popularity DESC",

 新       }
             :limit => arg}
FAQ: テストがよりカッコ良く
  書けるという噂があるが?
 ? mockするのが楽にキレイになりそうな感じ。
 旧      User.should_receive(:find).
          with(:all,:condition=>[...]).
 これが      and_return( [alice] )


 こうなる
        User.shold_receive(:active)

 新        and_return( [alice] )
飞颈迟丑冲蝉肠辞辫别について

                Recipe 077 (p.220)

                検索条件をスコープ毎に
                まとめて定義する




http://www.amazon.co.jp/dp/4797336625
ご清聴
 ありがとう
ございました

More Related Content

named_scope more detail - WebCareer