Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

Scopes vs Class Methods ใน Rails: วิธีไหนดีกว่า?

บทความนี้มีภาษาเกาหลีด้วย ขอบคุณ Soonsang Hong!

Scopes เป็นวิธีดีในการดึง objects ที่ต้องการจากฐานข้อมูล:

app/models/review.rb
class Review < ActiveRecord::Base scope :most_recent, -> (limit) { order("created_at desc").limit(limit) } end

ใช้ scope แบบนี้:

app/models/homepage_controller.rb
@recent_reviews = Review.most_recent(5)

การเรียก scope ดู เหมือนหัวๆ กับการเรียก class method บน Review และสร้างเป็น class method ได้ง่าย:

app/models/review.rb
def self.most_recent(limit) order("created_at desc").limit(limit) end
app/controllers/homepage_controller.rb
@recent_reviews = Review.most_recent(5)

ทำไมต้อง用 scope ตอนที่ class method ทำได้เหมือนกัน? คุ้มค่าที่จะแยก concept ที่เหมือนกันแต่ต่างกันนี้ไว้ในหัว? ถ้าเจอ bug แปลกๆ ไป? อะไรเพิ่มเติมแบบนี้ไม่ใช่สิ่งที่ทำให้ Rails เรียนยากขึ้น?

เมื่อไหร่ควร用 scope แทน class method?

ทำไมต้อง用 Scope ตอนที่มี Class Methods อยู่แล้ว?

ถ้าอยากดึง reviews ทั้งหมดหลังวันที่กำหนด? แต่ถ้าไม่ระบุวันที่ อยากได้ ทั้งหมด แทน?

เป็น scope:

app/models/review.rb
scope :created_since, ->(time) { where("reviews.created_at > ?", time) if time.present? }

ง่ายใช่ไหม? class method ครับ?

app/models/review.rb
def self.created_since(time) if time.present? where("reviews.created_at > ?", time) else all end end

ใช้ความพยายามเพิ่มนิดหน่อย Scopes ชอบ return scopes เพื่อ chain ได้ง่าย:

Review.positive.created_since(5.days.ago)

แตเพื่อให้ class method ทำงานเหมือนกัน ต้องจัดการกรณี time เป็น nil เอง ไม่งั้น ผู้เรียก ต้องดูเองว่ามี scope ที่ chain ได้กลับมาหรือไม่

เมธอดที่ return object ชนิดเดียวกันเสมอมีประโยชน์มาก ไม่ต้องกังวล edge cases หรือ errors สามารถสมมติได้ว่าจะได้ object ที่ใช้งานได้เสมอ

ที่นี่หมายถึง chain scopes ได้โดยไม่กังวล nil กลับมา

ยังมีวิธีทำลายสมมติฐานว่าได้ scope กลับมาเสมอ:

app/models/review.rb
scope :broken, -> { "Hello!!!" }
irb(main):001:0> Review.broken.most_recent(5) NoMethodError: undefined method `most_recent' for "Hello!!!":String

แตในโค้ดจริงผมไม่เคยเจอ

สิ่งที่ผมชอบที่สุดใน scopes คือการแสดง เจตนา บอกคนอ่านโค้ดต่อไปว่า "เมธอดนี้ chain ได้ จะกลายเป็น list of objects ช่วย select ชุด object ที่ถูกต้อง" นั่นมากกว่า class method ทั่วไปมาก

เมื่อไหร่ควร用 Class Method แทน Scope?

เพราะ scopes แสดงเจตนา ผม用เมื่อ chain built-in scopes 簡單 (如 where 和 limit) 成更複雜的 scopes การหาชุด objects ที่ถูกต้องคือสิ่งที่ scopes ออกแบบมาเพื่อทำ

มี 2 ข้อยกเว้น:

  1. ตอนต้องการ preload scopes แปลงเป็น associations แทน
  2. ตอนทำมากกว่า chain built-in scopes 用 class methods

ตอน logic ของ scope ซับซ้อน class method รู้สึกเป็นที่วางที่ถูกต้อง

ใน class method ผสม Ruby code กับ database code ได้ง่าย ถ้ามี sorting logic เขียนใน Ruby ง่ายกว่า ดึง objects มาใน default order แล้ว sort_by เรียงใหม่

หรือถ้าอยากล่ะลอง class method อาจดึงข้อมูลจากหลายที่: database, Redis, external API แล้วรวมเป็น collection ที่ รู้สึก เหมือน scope ที่ to_a แล้ว

แม้นั้น ยังควรวาง selecting, sorting, joining, filtering code ใน scopes แล้วค่อย用 scopes ใน class method จะได้ class method ชัดเจน และ scopes ใช้ได้ทั่วแอป

Scopes เป็นฟีเจอร์โปรดของผมใน Rails ทำอะไรได้ powerful - อ่านบทความผมเรื่อง sorting และ filtering Rails models เพื่อดู scope example ที่มีประโยชน์มาก

และมีวิธีง่ายมาสถัก scopes: เล่นกับมันใน tiny, focused apps บทตัวอย่างฟรีของ Practicing Rails จะสอนวิธี ลองดูสิ!