你好,我是富士大顆 Aiko
本篇會談到:
要講 callback 得先提到生命週期是什麼。
在 Rails 的 Model 中,生命週期是指一個資料記錄(Record)從被創造(Create)到被更新(Update)和最終被刪除(Destroy)的整個過程。在這個過程中,可以使用 Callback 來在特定的時機點執行某些操作。
假設我們有一個 Article
Model ,生命週期可能如下:
Create: 當 user 正在進行 create 一篇新文章。
before_validation
: 驗證前,可以用來確保所有必填欄位都有值。
after_validation
: 驗證後,通常是用來記錄 log;如果有錯誤,則記錄錯誤。
before_create
: 在儲存新文章到資料庫之前,設定預設的狀態,這裡是先預設為草稿。
after_create
: 在新文章被成功儲存後,可以發送通知給作者。
class Article < ApplicationRecord
before_validation :set_defaults
after_validation :log_errors, if: -> { errors.present? }
before_create :set_status
after_create :notify_author
private
def set_defaults
self.title ||= "Untitled"
end
def log_errors
Rails.logger.debug "Validation failed: #{errors.full_messages.join(", ")}"
end
def set_status
self.status = "draft"
end
def notify_author
AuthorMailer.article_created(self).deliver_now
end
end
Rails.logger.debug
Rails.logger.debug
是 Rails 內建的日誌(Logging)機制的一部分,用於將訊息寫入到日誌中。對於追蹤問題、監控應用程式狀態或記錄特定事件非常有用。
Rails.logger
提供了不同等級的日誌方法,包括:
debug
: 用於記錄除錯訊息。info
: 用於記錄一般資訊。warn
: 用於記錄警告。error
: 用於記錄錯誤。fatal
: 用於記錄致命錯誤。Article
model,並且你想在文章驗證失敗時記錄錯誤訊息。class Article < ApplicationRecord
after_validation :log_errors, if: -> { errors.present? }
private
def log_errors
Rails.logger.debug "Validation failed: #{errors.full_messages.join(", ")}"
end
end
在這個例子中,after_validation
Callback 會在驗證後執行。如果有錯誤,Rails.logger.debug
會將錯誤訊息寫入到日誌中。
可以在開發環境下的 log/development.log
文件或生產環境下的 log/production.log
文件中查看這些日誌。
更新(Update): 當文章被編輯或更新時。
before_update
: 在更新文章之前,記錄更新的時間。after_update
: 在文章更新後,清除快取。class Article < ApplicationRecord
before_update :set_updated_at
after_update :clear_cache
private
def set_updated_at
self.updated_at = Time.now
end
def clear_cache
Rails.cache.delete([self.class.name, id])
end
end
刪除(Destroy): 當文章被刪除時。
before_destroy
: 確保文章不是最後一篇。after_destroy
: 從所有(其他人)的收藏列表中移除該文章。class Article < ApplicationRecord
before_destroy :ensure_not_last_article
after_destroy :remove_from_favorites
private
def ensure_not_last_article
if Article.count <= 1
errors.add(:base, "Cannot delete the last article")
throw :abort #Rails 會立即停止後續的 Callback 並中止目前的操作。
end
end
def remove_from_favorites
Favorite.where(article_id: id).destroy_all
end
end
throw :abort
在 Rails 的 Callback 中,throw :abort
是一種用來中止目前操作(例如儲存、更新或刪除)的方式。
這通常用於在某些條件不滿足時阻止記錄的儲存或刪除。
class User < ApplicationRecord
before_destroy :ensure_not_admin
private
def ensure_not_admin
if admin?
errors.add(:base, "Cannot delete admin")
throw :abort
end
end
end
before_destroy
Callback 會在試圖刪除一個使用者記錄之前執行。如果該使用者是管理員,throw :abort
會被觸發,中止刪除,並新增一個錯誤訊息。
你可能會好奇,admin?
這個會指向誰,刪除者還是被刪除者?
在這個例子中確實沒有明確使用 self
。然而,在 Ruby 的實體方法(Instance Method)中,即使沒有明確寫出 self
,它仍然是有用 self。所以當你在這個方法中使用 admin?
時,實際上是在使用 self.admin?
,其中 self
指的是當前的 User 實體,也就是被操作的這個資源。
在 Ruby(以及 Rails)中,實體方法是定義在類別(Class)內部,但是需要一個該類別的實體(Instance)來使用它的方法。換句話說,實體方法是屬於實體的方法,而不是類別本身。
以下是一個簡單的 Ruby 類別,其中包含一個實體方法 say_hello
。
class Person
def say_hello
puts "Hello!"
end
end
要使用這個 say_hello
方法,你需要先 .new
一個 Person
類別的實體,然後使用這個實體來使用方法。
Aiko = Person.new
# Aiko 是 person class 新的實體
# 使用這個實體來使用在 person class 裡 的 say_hello 方法
Aiko.say_hello
# Hello!
在這個例子中,say_hello
就是一個實體方法,因為需要一個 Person
的實體 Aiko 來使用它。
類別方法是直接屬於類別的,不需要實體就能使用。
class Person
def self.say_goodbye
puts "Goodbye!"
end
end
# 直接使用類別名稱來調用類別方法
Person.say_goodbye # 輸出:Goodbye!
在這個例子中,say_goodbye
是一個類別方法,因為你可以直接使用 Person
類別來使用它,而不需要一個實體。
總而言之,如果你不是 person, 而想使用 person 這個 class 有的方法,那除非你是 person 直接用,要不你就要 .new
一個實體來使用 person class 有的方法!
callback = call then back 出去了記得回來
...被主函式呼叫運算後會返回主函式),是指通過參數將函式傳遞到其它代碼的,某一塊可執行代碼的參照。
(看維基完全危機感...到底在講什麼)
在 Rails 中,Callback 是一種在 Model 的生命週期中的特定時機點自動執行的方法。
這些 Callback 方法讓你在特定的時機(例如:儲存、更新或刪除記錄)執行邏輯。
before_validation
after_validation
before_save
after_save
before_create
after_create
before_update
after_update
before_destroy
after_destroy
before_save
Callback在儲存之前自動將 email 轉換為小寫。
class User < ApplicationRecord
before_save :downcase_email
private
def downcase_email
self.email = email.downcase
end
end
after_create
Callback在建立新的使用者後,自動發送歡迎信。
class User < ApplicationRecord
after_create :send_welcome_email
private
def send_welcome_email
UserMailer.welcome_email(self).deliver_now
end
end
before_destroy
Callback在刪除資料之前,確保被刪的身份不是管理員。
class User < ApplicationRecord
before_destroy :ensure_not_admin
private
def ensure_not_admin
if admin?
errors.add(:base, "Cannot delete admin")
throw :abort
end
end
end
after_validation
Callback驗證後,記錄錯誤訊息。
class Article < ApplicationRecord
after_validation :log_errors, if: -> { errors.present? }
private
def log_errors
Rails.logger.debug "Validation failed: #{errors.full_messages.join(", ")}"
end
end
今天就先到這邊!
下篇想來談測試!