หากคุณเคยใช้ Ruby on Rails มาก่อน คุณอาจเคยเจอข้อกังวลใจมาก่อน เมื่อใดก็ตามที่คุณเริ่มต้นโปรเจ็กต์ Rails ใหม่ คุณจะได้รับ app/controllers/concerns
และ app/models/concerns
. แต่สิ่งที่เป็นกังวล? และทำไมบางครั้งผู้คนจากชุมชน Rails ถึงพูดถึงพวกเขาในทางที่ไม่ดี
ภาพรวมโดยย่อ
Rails Concern คือโมดูลใดๆ ที่ขยาย ActiveSupport::Concern
โมดูล. คุณอาจถามว่าข้อกังวลต่างจากโมดูลอย่างไร ข้อแตกต่างที่สำคัญคือข้อกังวลของ Rails ช่วยให้คุณใช้เวทมนตร์ได้ เช่น:
# app/models/concerns/trashable.rb
module Trashable
extend ActiveSupport::Concern
included do
scope :existing, -> { where(trashed: false) }
scope :trashed, -> { where(trashed: true) }
end
def trash
update_attribute :trashed, true
end
end
คุณเห็นคำนั้นรวมอยู่ด้วย เป็นคาร์โบไฮเดรตเล็กน้อยของ Rails ที่โรยบนโมดูล Ruby ActiveSupport::Concern
อะไร ทำเพื่อคุณคืออนุญาตให้คุณใส่โค้ดที่คุณต้องการประเมินภายในบล็อกที่รวมไว้ ตัวอย่างเช่น คุณต้องการแยกตรรกะการทิ้งขยะออกจากโมเดลของคุณ included
ให้คุณทำในสิ่งที่เราทำและต่อมารวมถึงความกังวลของโมเดลของคุณดังนี้:
class Song < ApplicationRecord
include Trashable
has_many :authors
# ...
end
ค่อนข้างสะดวกและไร้เดียงสา ณ จุดนี้ใช่ไหม โมเดลลดน้ำหนักได้เล็กน้อยและตอนนี้สามารถนำขยะกลับมาใช้ซ้ำกับรุ่นอื่นๆ ได้ ไม่ใช่แค่โมเดล Song ของเรา สิ่งต่างๆ อาจซับซ้อนขึ้นได้ เข้าไปค้นหากันเลย
ตัวอย่างคลาสสิกของมิกซ์ซิน
ก่อนที่เราจะลงมือในเชิงลึกของข้อกังวล เรามาเพิ่มคำอธิบายอื่นก่อน เมื่อคุณเห็น include SomeModule
หรือ extend AnotherModule
สิ่งเหล่านี้เรียกว่า mixins มิกซ์อินคือชุดของโค้ดที่สามารถเพิ่มไปยังคลาสอื่นได้ และอย่างที่เราทุกคนทราบจากเอกสารของ Ruby โมดูลคือชุดของเมธอดและค่าคงที่ สิ่งที่เรากำลังทำอยู่นี้คือการรวมโมดูลที่มีเมธอดและค่าคงที่ในคลาสต่างๆ เพื่อให้สามารถใช้งานได้
นั่นคือสิ่งที่เราทำกับ Trashable
กังวล. เราแยกตรรกะทั่วไปเกี่ยวกับการทำลายวัตถุแบบจำลองลงในโมดูล โมดูลนี้สามารถรวมไว้ในที่อื่นได้ในภายหลัง ดังนั้น มิกซ์อิน เป็นรูปแบบการออกแบบที่ใช้ไม่เฉพาะใน Ruby และ Rails เท่านั้น แต่ไม่ว่าจะใช้ที่ไหน ผู้คนจะชอบและคิดว่ามันดี หรือเกลียดชังและคิดว่ามันควบคุมไม่ได้
เพื่อให้เข้าใจสิ่งนี้มากขึ้น เราจะพูดถึงข้อดีและข้อเสียของการใช้สิ่งเหล่านี้ หวังว่าด้วยการทำเช่นนี้ เราจะได้รับความเข้าใจว่าเมื่อใดหรือจะใช้ข้อกังวลหรือไม่
ฉันมีทุกอย่าง
เมื่อคุณตัดสินใจที่จะดึงข้อมูลที่เป็นข้อกังวล เช่น Trashable
กังวล ตอนนี้คุณสามารถเข้าถึงฟังก์ชันการทำงานทั้งหมดได้จากทุกที่ Trashable
รวมอยู่ด้วย สิ่งนี้นำมาซึ่งพลังอันยิ่งใหญ่ แต่อย่างที่ Richard Schneeman กล่าวในบล็อกของเขาที่โพสต์ในหัวข้อว่า "พลังอันยิ่งใหญ่มาพร้อมความสามารถที่ยอดเยี่ยมในการสร้างโค้ดที่ซับซ้อน" เขาหมายถึงโค้ดที่ซับซ้อนซึ่งคุณอาจวางใจได้ ซึ่งควรจะเป็น อยู่ในความกังวลของคุณ
ถ้าเราดูที่ Trashable
อีกครั้ง:
module Trashable
extend ActiveSupport::Concern
included do
scope :existing, -> { where(trashed: false) }
scope :trashed, -> { where(trashed: true) }
end
def trash
update_attribute :trashed, true
end
end
ตรรกะของข้อกังวลนั้นขึ้นอยู่กับความจริงที่ว่า trashed
ฟิลด์มีอยู่ทุกที่ที่มีข้อกังวล ใช่ไหม ไม่เป็นไร นี่คือสิ่งที่เราต้องการ แต่สิ่งที่ผมเห็นคือผู้คนถูกล่อลวงให้ดึงสิ่งอื่นจากตัวแบบมาสู่ความกังวล เพื่อวาดภาพว่าสิ่งนี้จะเกิดขึ้นได้อย่างไร ลองนึกภาพว่า Song
model มีวิธีอื่น featured_authors
:
class Song < ApplicationRecord
include Trashable
has_many :authors
def featured_authors
authors.where(featured: true)
end
# ...
end
class Album < ApplicationRecord
include Trashable
has_many :authors
def featured_authors
authors.where(featured: true)
end
# ...
end
เพื่อให้เห็นภาพมากขึ้น ฉันได้เพิ่ม Album
โมเดลที่มี Trashable
สมมติว่าเราต้องการแจ้งผู้แต่งเพลงและอัลบั้มที่เป็นจุดเด่นเมื่อพวกเขาถูกทิ้งลงในถังขยะ ผู้คนจะถูกล่อลวงให้ใส่ตรรกะนี้ไว้ในข้อกังวลดังนี้:
module Trashable
extend ActiveSupport::Concern
included do
scope :existing, -> { where(trashed: false) }
scope :trashed, -> { where(trashed: true) }
end
def trash
update_attribute :trashed, true
notify(featured_authors)
end
def notify(authors)
# ...
end
end
ที่นี่สิ่งต่าง ๆ เริ่มซับซ้อนขึ้นเล็กน้อย เนื่องจากเรามีตรรกะในถังขยะอยู่นอกโมเดลเพลงของเรา เราจึงอาจต้องการแจ้งใน Trashable
กังวล. ในนั้นมีสิ่ง "ผิด" เกิดขึ้น featured_authors
นำมาจาก Song
แบบอย่าง. ตกลง สมมติว่าคำขอตรวจสอบ passpull และการตรวจสอบ CI
จากนั้น อีกสองสามเดือนข้างหน้า ข้อกำหนดใหม่ถูกกำหนดขึ้นโดยที่นักพัฒนาจำเป็นต้องเปลี่ยนวิธีที่เรานำเสนอ featured_authors
สำหรับเพลง Forexample ข้อกำหนดใหม่ต้องการแสดงเฉพาะผู้เขียนที่โดดเด่นจากยุโรป โดยธรรมชาติแล้ว นักพัฒนาจะพบว่ามีการกำหนดและแก้ไขผู้เขียนที่โดดเด่นในที่ใดบ้าง
class Song < ApplicationRecord
include Trashable
has_many :authors
def featured_authors
authors.where(featured: true).where(region: 'Europe')
end
# ...
end
class Album < ApplicationRecord
include Trashable
has_many :authors
# ...
end
วิธีนี้ใช้ได้ผลดีในทุกที่ที่เราแสดงให้ผู้เขียนเห็น แต่หลังจากที่เราปรับใช้ในการผลิต ผู้คนจากส่วนอื่นๆ ของโลกจะไม่ได้รับการแจ้งเตือนอีกต่อไปเกี่ยวกับเพลงของพวกเขา ข้อผิดพลาดเช่นนี้เกิดขึ้นได้ง่ายเมื่อใช้ข้อกังวล ตัวอย่างข้างต้นนั้นเรียบง่ายและประดิษฐ์ขึ้น แต่ตัวอย่างที่ "อยู่ในป่า" อาจเป็นเรื่องยากมาก
สิ่งที่มีความเสี่ยงในที่นี้คือความกังวล (มิกซ์อิน) รู้มากเกี่ยวกับโมเดลที่มันรวมอยู่ในนั้น นี่คือสิ่งที่เรียกว่าการพึ่งพาแบบวงกลม . Song
และ Album
ขึ้นอยู่กับ Trashable
สำหรับการทิ้ง Trashable
ขึ้นอยู่กับทั้งคู่สำหรับfeatured_authors
คำนิยาม. อาจกล่าวได้เช่นเดียวกันว่า Trashable
fieldneeds จะต้องมีอยู่ในทั้งสองรุ่นเพื่อให้มี Trashable
กังวลเรื่องการทำงาน
นี่คือสาเหตุที่สโมสรไม่ฝักใฝ่ฝ่ายใดอาจต่อต้าน และสโมสรมืออาชีพมีไว้สำหรับ ผมว่า ก่อน เวอร์ชันของ Trashable
เป็นสิ่งที่ฉันจะไปกับ codebase ของฉัน มาดูกันว่าเราจะทำให้เวอร์ชันที่สองดีขึ้นได้อย่างไร
คุณมาจากไหน
มองย้อนกลับไปที่ Trashable
. ของเรา ด้วยการแจ้ง เราต้องทำอะไรสักอย่างกับมัน อีกสิ่งหนึ่งที่เกิดขึ้นเมื่อใช้ความกังวลคือเรามักจะทำสิ่งที่แห้งเกินไป เรามาลองทำสิ่งนั้นเพื่อจุดประสงค์ในการสาธิตกับโมเดลที่มีอยู่ของเราโดยสร้างข้อกังวลอื่น (อดทนกับฉันใน อันนี้):
module Authorable
has_many :authors
def featured_authors
authors.where(featured: true)
end
end
จากนั้น Song
. ของเรา และ Album
จะมีลักษณะดังนี้:
class Song < ApplicationRecord
include Trashable
include Authorable
# ...
end
class Album < ApplicationRecord
include Trashable
include Authorable
# ...
end
เราทำทุกอย่างให้แห้ง แต่ตอนนี้ข้อกำหนดสำหรับผู้แต่งจากยุโรปยังไม่บรรลุผล ที่แย่ไปกว่านั้นตอนนี้คือ Trashable
ข้อกังวลและรุ่นขึ้นอยู่กับ Authorable
. อะไรนรก? ตรงคำถามของฉันเมื่อฉันจัดการกับข้อกังวลบางเวลาที่ผ่านมา เป็นการยากที่จะติดตามว่าวิธีการมาจากไหน
วิธีแก้ปัญหาทั้งหมดของฉันคือเก็บ featured_authors
ให้ใกล้เคียงกับโมเดลมากที่สุด notify
วิธีการควร ไม่ ร่วมเป็นส่วนหนึ่งของ Trashable
กังวลเลย แต่ละรุ่นควรดูแลด้วยตัวเอง โดยเฉพาะอย่างยิ่งหากพวกเขามีแนวโน้มที่จะแจ้งกลุ่มย่อยที่แตกต่างกัน มาดูวิธีทำไม่เจ็บตัวกัน:
# Concerns
module Trashable
extend ActiveSupport::Concern
included do
scope :existing, -> { where(trashed: false) }
scope :trashed, -> { where(trashed: true) }
end
def trash
update_attribute :trashed, true
end
end
module Authorable
has_many :authors
# Other useful methods that relate to authors across models.
# If there are none, ditch the concern.
end
# Models
class Song < ApplicationRecord
include Trashable
include Authorable
def featured_authors
authors.where(featured: true).where(region: 'Europe')
end
# ...
end
class Album < ApplicationRecord
include Trashable
include Authorable
def featured_authors
authors.where(featured: true)
end
# ...
end
ความกังวลเช่นนี้สามารถจัดการได้และไม่ซับซ้อนเกินไป ฉันข้าม notify
ฟังก์ชันที่ฉันอธิบายไว้ก่อนหน้านี้เนื่องจากอาจเป็นหัวข้อสำหรับวันอื่น
บอสตัวสุดท้าย
สำหรับ Basecamp ผู้สร้าง Rails ความกังวลเกี่ยวกับการอ้างอิงข้อกังวลอื่น ๆ ดูเหมือนจะสมบูรณ์แบบตามที่ DHH แสดงไว้ในทวีตเมื่อก่อน:
การดูภาพหน้าจอของรหัสแสดงว่าคุณกำลังอ้าปากค้างหรือตกใจ ฉันรู้สึกว่าไม่มีระหว่างนี้ ถ้าฉันมีโอกาสแก้ไขโค้ดนี้ ฉันคิดว่ามันเป็น "การต่อสู้ขั้นสุดท้ายกับบอส" แต่เรื่องตลกที่น่าสนใจของที่นี่คือมีความคิดเห็นที่บอกว่าข้อกังวลใดขึ้นอยู่กับข้อใด ลองดูที่:
# ...
include Subscribable # Depends on Readable
include Eventable # Depends on Recordables
# ...
การแสดงความคิดเห็นเช่นนี้อาจมีประโยชน์ แต่ก็ยังมีการตั้งค่าไว้สำหรับทำบางสิ่งที่ไม่สมบูรณ์ โดยเฉพาะอย่างยิ่งหากคุณเพิ่งเริ่มใช้ codebase การเป็นคนใหม่และการไม่รับรู้ถึง "gotchas" ทั้งหมดที่โค้ดมี สามารถส่งความกังวลลงไปด้านล่างได้อย่างแน่นอน
สิ่งนี้คือสิ่งที่ DHH แบ่งปันในความคิดเห็นในการสนทนา ทวีตตอบกลับภายในถามว่าคนที่ทำงานกับ codebase นี้ควรโต้ตอบกับข้อกังวลเช่นนี้อย่างไร DHH ตอบว่าพวกเขาไม่มีเอกสารที่เป็นลายลักษณ์อักษรมากนัก พวกเขาไม่ค่อยจ้างดังนั้นทีมของพวกเขาจึงคุ้นเคยกับเอกสารเหล่านี้เป็นอย่างดี
แต่การมีทีมงานมากประสบการณ์ที่รู้จัก codebase และข้อโต้แย้งในการใช้นั้นเป็นเรื่องแปลกและไม่แข็งแกร่ง ฉันเดาว่ามันเป็นความรู้สึกมากกว่าที่จะใช้พวกเขาหรือไม่ คุณพอใจกับการสืบทอดหลาย ๆ แบบที่ modulesprovide หรือคุณชอบองค์ประกอบหรือไม่? โทร.
บทสรุป
ดังที่เราได้เห็น ความกังวลไม่ได้มีอะไรมากไปกว่าโมดูลที่มีไวยากรณ์ที่เป็นประโยชน์ในการดึงข้อมูลและทำให้โค้ดของคุณแห้ง หากคุณมีเครื่องมือที่มีประโยชน์มากกว่าที่คาดไว้ คุณอาจไม่ควรติดต่อเพื่อแจ้งข้อกังวลทันที พฤติกรรมที่คล้ายกับการจัดการไฟล์แนบและตรรกะในการทำลายล้างที่เราแสดงให้เห็นในตัวอย่างอาจเป็นตัวเลือกที่ดีในการแยกออกเป็นโมดูล (ข้อกังวล)
หวังว่าคุณจะได้เห็นสิ่งที่ดีและไม่ดีที่เป็นไปได้เมื่อจัดการกับข้อกังวลและโมดูลโดยทั่วไป จำไว้ว่าไม่มีรหัสใดที่สมบูรณ์แบบ และสุดท้าย คุณจะเรียนรู้ได้อย่างไรว่าอะไรดีและอะไรไม่ดีสำหรับคุณ หากคุณไม่พยายามและอาจล้มเหลวหรือประสบความสำเร็จ
ไม่มีวิธีแก้ปัญหาใดที่สมบูรณ์แบบ และฉันหวังว่าคุณจะเข้าใจ Rails เกี่ยวกับวิธีการทำสิ่งต่างๆ ในบล็อกโพสต์ และเช่นเคย ใช้วิจารณญาณของคุณและตระหนักถึงข้อดีและข้อเสีย
รอตอนต่อไป เชียร์!
ป.ล. หากคุณต้องการอ่านโพสต์ Ruby Magic ทันทีที่ออกจากสื่อ สมัครรับจดหมายข่าว Ruby Magic ของเราและไม่พลาดแม้แต่โพสต์เดียว!