I'm a Ruby on Rails / jQuery web developer. Follow me at @sikachu

เจออะไรใน Rails 3: เพิ่มพลัง ActiveRecord#find (ตอนที่ 2)

February 12th, 2010 Posted in Ruby, Ruby on Rails

เจออะไรใน Rails 3 เป็นสกู๊ปพิเศษสำหรับนำเสนอสิ่งใหม่ๆ ที่จะมีเพิ่มขึ้นมาใน Ruby on Rails 3.0

หลังจากที่ผมนำเสนอเรื่องของการ deprecate options ทั้งหมดของ ActiveRecord#find แล้วเปลี่ยนเป็นเมธอดที่ทำหน้าที่คล้ายๆ กับ named_scope กันไปแล้ว ตอนนี้ผมไปเจอข้อมูลจาก @lifo ซึ่งพูดเพิ่มเติมในเรื่องของเมธอดที่จะถูก deprecated และเมธอดที่ให้ใช้แทนครับ

สิ่งที่จะถูกถอดออกไป

การเรียกใช้ #find โดยมี option hash นั้นจะ deprecated ออกไป

User.find(:first, :where => {:status => "suspended"})

แต่อย่างไรก็ตาม #find method จะยังคงอยู่ โดยคุณสามารถใช้มันเพื่อหา record ตาม ID ได้แทน เช่น

User.find(1)
User.find(3,4,5) # ให้ argument เป็น array ได้แล้ว

การใช้ #find(:first), #find(:all) จะถูกแทนที่ด้วยเมธอด #first, #all

User.find(:first) # => deprecated
User.first # use this

และการใช้เมธอดสำหรับการคำนวณทุกอันนั้น ก็จะไม่รับ argument ที่เป็น hash แล้วเช่นกัน

User.count(:id, :conditions => {:status => "suspended"}) # => deprecated
User.where({:status => "suspended"}).count # use this

แต่ยังคงยกเว้น #count ที่ยังคงรับตัวเลือก :distinct อยู่

นอกจากนั้นการกำหนดค่า named_scope โดยใช้ option hash และเมธอด #scoped_by_xxx และ #default_scope ก็ถูกถอดออกเช่นกัน

# all lines here are deprecated
named_scope :suspended, :conditions => { :status => "suspended" }
default_scope :conditions => "status != 'deleted'"
User.scoped_by_status("suspended")

เมธอดใหม่ทั้งหมด

ActiveRecord ใน Rails 3 นั้น จะมีการเพิ่มเมธอดเหล่านี้

  • where
  • having
  • select
  • group
  • order
  • limit
  • offset
  • joins
  • includes
  • lock
  • readonly
  • from

เมธอดทุกอย่างนั้นสามารถ chain เข้าหากันได้ เพราะฉะนั้นคุณก็จะสามารถทำอย่างนี้ได้

User.where(:status => "suspended").includes(:event_log).limit(15).order(:name)
User.where(:name => "john_doe").joins(:profile)

และเนื่องจาก result ของมันเป็น lazy-loading หมด ทำให้คุณสามารถนำมันไปเก็บในตัวแปร หรือว่าเพิ่ม condition เข้าไปทีหลังได้ โดย SQL จะถูกส่งไปยัง server เมื่อคุณเรียกใช้ #each, #first, #all เท่านั้น

suspended_users = User.where(:status => "suspended")
latest_suspended_users = suspended_users.order("id DESC").limit(5)
 
User.where(:status => "suspended") # ยังไม่ query
User.where(:status => "suspended").all # query แล้ว!

เพราะฉะนั้นถ้าคุณไม่สนใจในเรื่องของการ lazy loading และต้องการจะโหลดข้อมูลลงมาเก็บไว้ใน array ก่อน คุณก็เพียงแค่ใช้ #all ตามหลัง query ของคุณเท่านั้นเอง

suspended_users = User.where(:status => "suspended").all # ข้อมูลถูกเก็บใน suspended_users

named_scope ยังเปลี่ยนไปเป็น scope อีก

นอกจากที่กล่าวมาข้างต้นนั้น ยังมีการเปลี่ยนชื่อเมธอดของ named_scope ให้เป็น scope และรองรับการใช้เมธอดใหม่ของ finder เพราะฉะนั้นจากโค้ด

named_scope :suspended, :conditions => { :status => "suspended" }

ก็ต้องเปลี่ยนไปเป็น

scope :suspended, where({ :status => "suspended"})

นอกจากนั้นยังมีการเพิ่มเติม with_scope และ with_exclusive_scope ซึ่งโค้ดในบล็อกที่ให้มานั้นจะมี scope ตามที่ระบุไว้แทน

with_scope(where( :status => 'suspended')) do
  ...
end
with_exclusive_scope(User.online) do
  ...
end

แล้วเมื่อไรจะต้องเปลี่ยนละเนี่ย …

คุณไม่จำเป็นต้องรีบเปลี่ยนโค้ดในส่วนของการใช้ finder เพราะใน Rails 3.0 นั้น ทุกอย่างยังคงจะใช้ได้ตามปกติ โดยที่มีกำหนดการว่าเมธอดเก่าๆ นั้นจะถูกนำออกไปใน Rails 3.2 ครับ เพราะฉะนั้นคุณก็ยังคงสามารถใช้โค้ดเก่าๆ ในการ query ข้อมูลมาได้ แต่ผมแนะนำว่าค่อยๆ ไล่เปลี่ยนไป ก็จะทำให้โค้ดของคุณพร้อมสำหรับการ upgrade ไปยัง version ใหม่ๆ ครับ ;)

  • http://onedd.net wiennat

    อ่านแล้วมันยิ่งคล้าย LINQ แฮะ

    แต่เหมือนว่าจะคิดไปเอง