Rails 使用 concern 幫 model 減肥
當我們一個專案開發久了,一定會遇到單一 model / controller 肥大的問題,某些情境才使用的 class / instance methods 如果全都放在單一檔案內看起來又有點礙眼,很想把相似的功能放在一起管理。這個時候我們就能使用 concern 來幫我們的 model / controller 減肥
當我們一個專案開發久了,一定會遇到單一 model / controller 肥大的問題,某些情境才使用的 class / instance methods 如果全都放在單一檔案內看起來又有點礙眼,很想把相似的功能放在一起管理。這個時候我們就能使用 concern 來幫我們的 model / controller 減肥,這樣也可以針對該 concern 來撰寫測試。
範例
如果是 Rails6 的專案,新建立起來就會有預設好資料夾 app/models/concerns
或是 app/controllers/concerns
。可是我們團隊比較習慣在 model 上使用。
在命名上沒有太硬性規定,只是 Rails 作者 DHH 會使用形容詞結尾。
source: DHH tweet
class Transaction < ApplicationRecord
include Transactions::Refundable
# 一堆 methods..
def expire_date
end
def form_expire_date
end
def check_expire!
end
def self.expire_notification_schedule(trade_no)
end
end
從上述的 code 來看,可以發現許多方法都是跟 expire
有相關,此時肯定會有一些程式碼潔癖的心裡發作,很想要把他們通通給丟到一個地方統一管理。這個時候我們就可用 concern 來整理程式碼。
module Transactions
module ExpireMethods
extend ActiveSupport::Concern
# class methods 可以移到 class_methods block 省去 self.
class_methods do
def expire_notification_schedule(trade_no)
end
end
def expire_date
end
def form_expire_date
end
def check_expire!
end
end
end
這樣就能幫我們的 Transaction 減肥了,這個檔案就會看起來非常的舒服。
class Transaction < ApplicationRecord
include Transactions::Refundable
include Transactions::ExpireMethods
end
另外 concern 不只可以整理 method 也可以整理 validations, associations, scopes,我們只要寫在 included block 內即可,例如:
module Transactions
module FooBar
extend ActiveSupport::Concern
included do
after_commit :update_transaction_money, on: :create
enum country_restricted: { free: false, restricted: true }
has_many: foobar
scope :in_stock, lambda { # do something }
end
end
end
參考資料
https://signalvnoise.com/posts/3372-put-chubby-models-on-a-diet-with-concerns
https://blog.appsignal.com/2020/09/16/rails-concers-to-concern-or-not-to-concern.html
https://codeclimate.com/blog/7-ways-to-decompose-fat-activerecord-models/