Bài viết này chia sẻ về một cách hạn chế việc lặp code hay DRY, nếu bạn chưa biết về DRY có thể đọc thêm tại đây Rails AntiPattern: Duplicate Code Duplication (p1)
Khi chúng ta viết một phần mở rộng cho Ruby on Rails model, ActiveSupport::Concern có thể là một lựa chọn tốt. chúng hoạt động giống như mixin modules có thể được bao gồm vào trong một lớp, nhưng có thêm một số lợi ích cho Rails model, chẳng hạn như khả năng thêm Callbacks.
Bài viết này sẽ chỉ cho bạn cách DRY đoạn code của bạn, concern không chỉ thêm các custom methods cho các bản ghi, mà còn thêm Callbacks kích hoạt khi create, update và delete.
ActiveRecord Callbacks
Callbacks là các phương thức được kích hoạt khi một số sự kiện nhất định xảy ra trong vòng đời của ActiveRecord model. Bạn có thể xem danh sách ActiveRecord Callbacks trên Rails Guide. Một số Callbacks trong đó:
- before/after validations
- before/after save/update/destroy
- around save/create/update/destroy
- after_commit/after_rollback (được liên kết với các bản ghi trong database/rollbacks)
- after_initialize (khi new được gọi trên một bản ghi)
- after_find (bất cứ khi nào Active Record lấy một bản ghi từ cơ sở dữ liệu)
- after_touch
Bạn có thể thêm vào code của bạn để chạy khi các Callbacks này được kích hoạt. Một cách để làm điều này là sử dụng một ActiveSupport::Concern, mà sau đó có thể chia sẻ hay sử dụng trên nhiều model.
Using a Callback in a Concern
Bạn có thể khai báo một Concern bằng cách sử dụng module được extended ActiveSupport::Concern. Sau đó thêm các phương thức khác mà bạn muốn bao gồm.
module Publishable extend ActiveSupport::Concern def email UserMailer.weekly_summary(self).deliver_now end end
Để thêm Concern vào model, bạn include vào như bạn muốn với module vậy. Sau đó, bạn có thể gọi concern đó trong model callbacks để chúng được chạy khi callback được kích hoạt.
class User < ApplicationRecord include Publishable after_save -> { email } end
Điều này có nghĩa là bạn có thể tạo các bản ghi và gọi phương thức email của Concern.
Callback after_save được kích hoạt khi create hoặc update, vì vậy phương thức email sẽ được gọi cho các phương thức sau:
- User.create
- User.new.save
- User.update
Extracting the Callback into the Concern
Bạn cũng có thể khai báo Concern để gọi các callbacks khi nó được include vào model. Để làm được điều này chúng ta sẽ sử dụng included trong Concern.
module Publishable extend ActiveSupport::Concern included do after_create -> { email } end def email UserMailer.weekly_summary(self).deliver_now end end
Bây giờ model không còn chứa các đoạn code logic nữa mà Concern sẽ làm điều đó.
class User < ApplicationRecord include Publishable end
Testing Callbacks with RSpec
Ví dụ, bạn có một Callback chẳng hạn như after_save :email sử dụng Rails ActionMailer để gửi xác nhận email sau khi user đã được lưu.
class User < ApplicationRecord after_save -> { email } def email UserMailer.confirmation(self).deliver_now end end
Một email sẽ được gửi đi khi một bản ghi được lưu trong test của bạn.
Để ngăn chặn điều này, bạn cần stub phương thức này. Tùy vào codebase của bạn, bạn có thể stub cho các file riêng lẻ hoặc toàn bộ test của bạn.
Trong một tệp riêng lẻ, bạn sẽ thêm các phần sau để có thể chạy Rspec:
RSpec.describe User do subject { FactoryBot.create(:user) } before do allow(subject).to receive(:email) end ... your tests
Hoặc với toàn bộ các file bằng cách thêm phần sau vào file spec_helper.rb
RSpec.configure do |config| config.before(:each) do allow_any_instance_of(User).to receive(:email) end end
Chú ý: nó có thể gây chậm khi chạy test
Sau đó khi bạn muốn test cho Callbacks, bạn cần phải tạo bản ghi, đặt expect cho nó, sau đó gọi một phương thức để kích hoạt Callback.
RSpec.describe User do describe "#create" do it "calls the Callback #email" do record = User.new(params) expect(record).to receive(:email) record.save end end end
Thanks for reading!
Nguồn: https://medium.freecodecamp.org/add-callbacks-to-a-concern-in-ruby-on-rails-ef1a8d26e7ab