หลังจากที่คุณอ่านบทแนะนำ Rails เสร็จแล้ว และเริ่มใช้งานแอปของคุณเอง สิ่งต่างๆ จะเกิดความสับสน เช่นเดียวกับที่ไม่ใช่ CRUD ตรรกะทั่วไปของคุณไปที่ไหน? การดึงดูดผู้ติดตามจาก Twitter เข้ากับ MVC ได้อย่างไร ถามสองคน คุณจะได้สี่คำตอบ หรือกระทู้ของคุณกลายเป็นกลุ่มคนฉลาดที่ดูถูกกันเป็นชั่วโมงๆ ในขณะที่คุณเอาหัวโขกโต๊ะ ไม่ว่าจะด้วยวิธีใด คุณก็ต้องปวดหัวอยู่ดี
คุณไม่สามารถสร้างแอปที่คุณใฝ่ฝันถึงได้หากไม่มี บางส่วน ตรรกะทั่วไปที่ไม่ใช่ Rails แล้วคุณจะวางโค้ดของคุณไว้ที่ใด และยังคงความเรียบง่าย
จุดเริ่มต้นที่ง่าย
เมื่อฉันมีตรรกะที่รู้สึกว่าเกี่ยวข้องกับโมเดล ActiveRecord ที่มีอยู่ ฉันจะเริ่มด้วยการใส่ลงในโมเดลนั้น ตัวอย่างเช่น ถ้าฉันมี Game
model และฉันต้องการนำเข้าเกมจำนวนมากจากไฟล์ CSV ฉันจะใส่วิธีการนั้นลงใน Game
คลาส:
class Game < ActiveRecord::Base
def self.parse_from_csv(csv_string)
games = []
CSV.parse(csv_string, quote_char: "'") do |row|
games << Game.from_csv_row(row) if (row[0] == 'G')
end
games
end
def self.from_csv_row(row)
Game.new({
dgs_game_id: row[1],
opponent_name: row[2],
created_at: row[4],
updated_at: row[4],
})
end
end
คุณมีข้อมูลทั้งหมดที่วิธีการของคุณต้องการอยู่ในมือ สามารถทดสอบได้ง่าย และอาจเป็นที่ที่ผู้มีส่วนร่วมรายใหม่จะมองหาตรรกะนั้นก่อน
แต่ถ้าคุณเพิ่มและเปลี่ยนโมเดลนั้นไปเรื่อยๆ มันจะใหญ่และซับซ้อน ส่วนต่างๆ ของโมเดลจะโต้ตอบกันในลักษณะแปลกๆ ยิ่งเปลี่ยน ยิ่งยาก
ในกรณีดังกล่าว คุณอาจต้องการจัดโครงสร้างโค้ดใหม่ให้เป็นโมเดลที่ไม่ใช่ ActiveRecord
โมเดล Non-ActiveRecord
เพียงเพราะมันอยู่ในแอป Rails ไม่ได้หมายความว่าจะต้องรับช่วงจาก Active/Action แต่อย่างใด
คุณสามารถเขียนโค้ด Ruby ของคุณเองในวัตถุ Ruby ธรรมดา และใช้โค้ดดังกล่าวในแอป Rails ของคุณ วัตถุเหล่านี้ยังสามารถเรียกว่าแบบจำลองได้ เนื่องจากวัตถุเหล่านี้สร้างแบบจำลองส่วนหนึ่งของปัญหาของคุณ พวกเขาไม่มีฐานข้อมูล ActiveRecord ที่จัดเก็บข้อมูล
ครั้งต่อไปที่ฉันทำงานกับตัวแยกวิเคราะห์ CSV ของเกมนั้น Game
ชั้นเรียนใหญ่เกินไปเล็กน้อย ดังนั้นฉันจึงย้ายตรรกะ parser ไปเป็น GameCSVParser
ชั้นเรียน
คอมมิตทั้งหมดอยู่ที่นี่ แต่นี่คือสิ่งที่คลาสที่ไม่ใช่ ActiveRecord ดูเหมือน:
class GameCSVParser
def initialize(csv_string)
@rows = CSV.parse(csv_string, quote_char: "'")
end
def games
game_rows.map { |row| Game.new(game_attributes(row)) }
end
private
def game_rows
@rows.select { |row| is_game_row?(row) }
end
def game_attributes(row)
{
dgs_game_id: row[1],
opponent_name: row[2],
created_at: row[4],
updated_at: row[4],
}
end
def is_game_row?(row)
row[0] == 'G'
end
end
ฉันจะไปทางขวาเพื่อสร้างวัตถุ Ruby ธรรมดาใหม่หากตรรกะที่ฉันกำลังเพิ่มไม่รู้สึกว่าเกี่ยวข้องกับรุ่น ActiveRecord ใด ๆ หรือหากโค้ดดูเหมือนว่าควรเป็นส่วนหนึ่งของสิ่งที่ยังไม่มีอยู่ในแอป มิฉะนั้น ส่วนใหญ่จะปรากฏขึ้นผ่านการปรับโครงสร้างใหม่
ด้วยวัตถุ Ruby ธรรมดา คุณสามารถเขียนอะไรก็ได้ แต่รู้ว่าคุณสามารถเขียน อะไรก็ได้ ไม่ได้ช่วยให้คุณมีทิศทาง คุณต้องการวิธีการอะไร? วัตถุใหม่ทั้งหมดของคุณจะโต้ตอบกันอย่างไร
แอป Rails จำนวนมากใช้หมวดหมู่เหมือนกัน ของวัตถุทับทิมธรรมดา หมวดหมู่เหล่านี้เป็นรูปแบบที่คุณสามารถติดตามเพื่อเขียนโค้ดที่นักพัฒนารายอื่นรู้จัก คุณอาจเคยได้ยินมาบ้างแล้ว
วัตถุบริการ ผู้นำเสนอ และงาน
ไม่มีอะไรพิเศษเกี่ยวกับวัตถุที่ให้บริการ ผู้นำเสนอ และงาน พวกมันเป็นเพียงวัตถุ Ruby ธรรมดาที่ทำงานในลักษณะที่จดจำได้โดยเฉพาะ
ตัวอย่างเช่น งานที่ต้องการ เป็นคลาส Ruby ธรรมดาที่มี perform
เมธอดและ @queue
:
class FetchGamesForPlayer
@queue = :default
def self.perform(player_id)
player = Player.scoped_by_id(player_id).ready_for_fetching.first
player && player.fetch_new_games!
end
end
perform
จะถูกเรียกเมื่อมีการเรียกใช้งาน
พรีเซ็นเตอร์ เป็นวัตถุ Ruby ธรรมดาที่มีรหัสที่เหมาะสมในมุมมองเท่านั้น:
class UserPresenter
def show_related_users?
@user.related.count > 3
end
end
นอกจากนี้ยังอาจรวมถึงตัวช่วยดูของ Rails หรือนำวัตถุที่แตกต่างกันสองสามรายการและถือว่าเป็นวัตถุที่รวมเป็นหนึ่งเดียวเพื่อความสะดวกของมุมมอง
วัตถุบริการ เป็นวัตถุ Ruby ธรรมดาที่แสดงกระบวนการที่คุณต้องการดำเนินการ ตัวอย่างเช่น การเขียนความคิดเห็นในโพสต์อาจ:
- แสดงความคิดเห็น
- ส่งอีเมลแจ้งเตือนไปยังผู้เขียนโพสต์
ออบเจ็กต์บริการสามารถทำได้ทั้งสองอย่าง และป้องกันไม่ให้ตรรกะนั้นอยู่ในคอนโทรลเลอร์ของคุณ
มีวัตถุบริการที่ยอดเยี่ยมที่นี่ เต็มไปด้วยตัวอย่าง
สำหรับกระบวนการง่ายๆ ฉันไม่กังวลกับออบเจ็กต์บริการ แต่ถ้าตัวควบคุมเริ่มหนักเกินไป ก็ควรวางตรรกะพิเศษนั้นไว้
คุณสามารถใช้รูปแบบเหล่านี้เพื่อจัดระเบียบตรรกะทางธุรกิจของคุณเองได้ พวกมันเป็นเพียงออบเจ็กต์ Ruby ธรรมดา แต่เป็นออบเจ็กต์ Ruby ที่มีรสชาติเหมือนกัน มีชื่อ และคุณสามารถพูดคุยกับนักพัฒนาคนอื่นๆ ได้
คุณเริ่มที่ไหน
มีหลายๆ ที่ที่ตรรกะทางธุรกิจที่ไม่ใช่ Rails ของคุณสามารถนำไปใช้ได้ อาจจะเลือกยาก นี่คือสิ่งที่ฉันทำ:
- หากตรรกะส่วนใหญ่เกี่ยวข้องกับคลาสที่มีอยู่ แม้ว่าจะเป็นโมเดล ActiveRecord ฉันก็ใส่มันไว้ในคลาสนั้น
- หากไม่เข้ากับคลาสที่มีอยู่ ฉันจะสร้างคลาส Ruby ธรรมดาใหม่เพื่อใช้ตรรกะ
- หากรู้สึกว่าตรรกะควรเป็นส่วนหนึ่งของสิ่งที่ยังไม่มี ฉันจะสร้างคลาส Ruby ธรรมดาขึ้นมาใหม่
- หากฉันกลับมาที่โค้ดในภายหลัง และโมเดลเริ่มซับซ้อนเกินไป หรือโค้ดไม่สมเหตุสมผลในโมเดลนั้นอีกต่อไป ฉันจะปรับโครงสร้างให้เป็นคลาส Ruby แบบธรรมดา
- หากโค้ดดูสมเหตุสมผลในมุมมองหนึ่ง ฉันจะเพิ่มโค้ดนั้นในผู้ช่วยหรือสร้างผู้นำเสนอ
- หากโค้ดไม่ต้องการรันระหว่างการร้องขอ HTTP หรือต้องรันในเบื้องหลัง โค้ดจะทำงาน
- หากฉันกำลังเล่นกลแบบจำลองหรือขั้นตอนต่างๆ ของกระบวนการ และทำให้ตัวควบคุมเข้าใจยากเกินไป ฉันจะใส่มันลงในวัตถุที่ให้บริการ
แล้วคุณล่ะ รหัสของคุณไปไหน และคุณมีรูปแบบใดนอกเหนือจากนี้ที่คุณพบว่ามีประโยชน์หรือไม่ แสดงความคิดเห็นและแจ้งให้เราทราบ
และถ้าคุณยังไม่มีกระบวนการ ลองใช้ของฉันดู ดูว่ามันเหมาะกับคุณอย่างไร ไม่มีวิธีใดที่สมบูรณ์แบบในการเขียนโค้ด แต่เมื่อคุณติดขัด ขั้นตอนเช่นนี้จะช่วยคุณในการเริ่มต้น