Những thứ bạn có thể lưu được với PaperTrail

Khi sử dụng PaperTrail để lưu log, ngoài việc muốn biết bản record được update những attributes nào thì bạn lại càng muốn biết ai là người đã update/create/destroy hay chỉ đơn giản là bạn muốn thêm các thuộc tính khác có thể lưu lại của 1 record. Hãy tìm cách để thực hiện chúng.

Trước tiên phải add gem PaperTrail vào project đã https://github.com/paper-trail-gem/paper_trail

1. Ai đã thay đổi record?

Mặc định PaperTrail sẽ có thuộc tính whodunnit để lưu id của người thay đổi, và cũng cung cấp cho bạn 1 callback. Nếu trong controller có method current_user thì callback sẽ thực hiện assign current_user.id vào trường whodunnit.

# callback
class ApplicationController
  before_action :set_paper_trail_whodunnit
end
# Tells PaperTrail who is responsible for any changes that occur.
    def set_paper_trail_whodunnit
        if ::PaperTrail.request.enabled?
          ::PaperTrail.request.whodunnit = user_for_paper_trail
        end
    end
# Returns the user who is responsible for any changes that occur.
    def user_for_paper_trail
        return unless defined?(current_user)
        ActiveSupport::VERSION::MAJOR >= 4 ? current_user.try!(:id) : current_user.try(:id)
    rescue NoMethodError
        current_user
    end

Ơ.... thế nếu controller của bạn không có method current_user mà là 1 method khác lưu user hiện tại đang đăng nhập thì sao? Override method user_for_paper_trail lại là được, ví dụ:

    def user_for_paper_trail
        return unless defined?(current_member)
        ActiveSupport::VERSION::MAJOR >= 4 ? current_member.try!(:id) : current_member.try(:id)
    rescue NoMethodError
        current_member
    end

2. Associations

Muốn theo dõi hay thống nhất các association (tạm dịch các mối quan hệ) ta sử dụng thêm gem này

Cái này mình chỉ hiểu được một phần đó là "Nếu record cha và record con đều được update cùng 1 lần thì với việc sử dụng PaperTrail-AssociationTracking(PT-AT) thì ta có thể thống nhất lại các bản ghi như trước transaction"

class Location < ActiveRecord::Base
  belongs_to :user
  has_paper_trail
end

class User < ActiveRecord::Base
  has_one :location
  has_paper_trail
end
user.name                  # dinvvan
user.location.address      # Số 1 Lương

User.transaction do
    user.location.update_attributes address: "BacNinh"
    user.update_attributes name: "DangVan"
end

t = user.versions.last.reify(has_one: true)
t.name                         # dinvvan
t.location.address              # "Số 1 Lương", instead of "BacNinh"

Vậy là nó sẽ restore lại bản ghi trước khi thực hiện transaction

Ở đây ta chỉ cần lưu ý là

  • Has-One association thì option của method reifyhas_one:true
  • Has-Many-Through association thì là has_many: true
  • Belongs-To association là belongs_to: true

3. Storing Metadata

Bằng cách sử dụng Model Metadata hay Controller MetaData chúng ta có thể thêm các cột cho bảng versions example:

Model Metadata

Class Song < ActiveRecord::Base
    has_many :playlists

    has_paper_trail(
        meta: {
            singer: :singer_id,
            views: :total_views,
        }
    )
    def total_views
        200
    end
end

Sử dụng ở controller Metadata from Controllers

class ApplicationController
  def info_for_paper_trail
    { ip: request.remote_ip, user_agent: request.user_agent }
  end
end

Nâng cao hơn chút nữa, ta có một ví dụ nhỏ

singer_id là một thuộc tính của Song để biết là người hát bài hát đó và PaperTrail nó đã mặc định sẽ lưu nó rồi. Nhưng mà vấn đề đặt ra là nếu bạn muốn lấy ra versions của 1 ca sĩ cụ thể (singer); nếu không dùng metadata bạn sẽ phải thống nhất lại bảng version để tìm ra những versions thuộc về ca sĩ đó => bất khả thi. Vậy thì dùng metadata thì có gì gọi là khả thi?

Đơn giản là metadata sẽ tìm ra những version bạn muốn nếu bạn sử dụng nó 😃)

PaperTrail::Version.where(singer_id: singer_id) => #<...

Cảm ơn bạn đã đọc và chúc bạn có thêm được một vài thông tin bổ ích.

All Rights Reserved