Tìm hiểu gem PaperTrail

Hiện nay, trong mỗi dự án công nghệ thông tin việc lưu log khi có sự thay đổi dữ liệu đã trở thành một chức năng cơ bản và bắt buộc phải có. Việc lưu log có ý nghĩa quan trọng và khá tiện dụng trong những trường hợp như:

  • Điều tra khi có sự nghi ngờ về dữ liệu.
  • Rollback lại dữ liệu khi bị mất.
  • ...

Lập trình viên có rất nhiều cách để xây dựng chức năng này trong dự án Ruby on Rails, có thể xây dựng bằng tay hay sử dụng gem tuy vậy, đơn giản và tiện dụng nhất mình xin được giới thiệu gem parper trail. PaperTrail không chỉ đơn giản trong cách cài đặt, cách sử dụng mà cái cách nó lưu log cũng khiến người dùng không mất quá nhiều thời gian để hiểu.

Hiện tại versions mới nhất của PaperTrail là 6.0.2.

CÀI ĐẶT

Việc cài đặt gem paper_trail khá đơn giản.

  1. Thêm gem trong file Gemfile

        gem "paper_trail", "~> 4.0.0"
    
  2. Chạy câu lệnh bundle install.

  3. Thêm bảng versions vào databases.

    Chạy lệnh: Ruby bundle exec rails generate paper_trail:install

    Câu lệnh trên sẽ tạo ra một file migrations với nội dung:

      def change
         create_table :versions do |t|
           t.string   :item_type, :null => false
           t.integer  :item_id,   :null => false
           t.string   :event,     :null => false
           t.string   :whodunnit
           t.text     :object,    :limit => TEXT_BYTES
           t.datetime :created_at
         end
         add_index :versions, [:item_type, :item_id]
       end
    

    Trong đó: item_type: Lưu tên bảng dữ liệu thay đổi. item_id: Lưu id của bản ghi có sự thay đổi. event: Sự kiện xảy ra với bản ghi thay đổi. whodunnit: Lưu thông tin về người thay đổi bản ghi. datetime: Lưu thời gian bản ghi versions được tạo. add_index: Đánh index cho bản ghi versions

CÁCH SỬ DỤNG

Một cách đơn giản để sử dụng paper_trail với tất cả thiết lập mặc định của gem: Thêm dòng sau vào model cần lưu log:

class Student < ApplicationRecord

  has_paper_trail
end

PaperTrail cung cấp khá nhiều phương thức để customers chính thiết lập mặc định của mình. Ví dụ:

  • Thay đổi sự kiện tác động vào bản ghi

    has_paper_trail :on => [:update, :destroy]
    

    Chỉ lưu log với hàm update và destroy bỏ qua hàm create

      has_paper_trail :on => []
    
      paper_trail.on_destroy   
      paper_trail.on_update     
      paper_trail.on_create
    

    Thêm sự kiện lưu log vào callbacks bằng từng lệnh riêng biệt

  • Thay đổi điều kiện khi tạo mới versions.

        has_paper_trail :if     => Proc.new { |t| t.language_code == 'US' },
                        :unless => Proc.new { |t| t.type == 'DRAFT'       }
    
  • Customers attributes

        has_paper_trail :ignore => [:name, :age]
    

    Bỏ qua việc lưu log khi một trong hai attributes name hoặc age thay đổi. Khi cả hai cùng thay đổi thỳ việc lưu log vẫn diễn ra bình thường

    Tương tự với các phương thức khác: only: Chỉ lưu log với attributes được khai báo. skip: Bỏ qua log với attributes được khai báo.

BẬT TẮT PAPER TRAIL

  1. Với tiến trình như rake task

    PaperTrail.enabled = false
    
  2. Với config môi trường

    config.paper_trail.enabled = false
    
  3. Với class

    Student.paper_trail.disable
    Student.paper_trail.enable
    

Demo và một số method cơ bản của PaperTrail

1. Demo

  • Thêm mới model Student với nội dung như sau:

        class CreateStudents < ActiveRecord::Migration[5.0]
          def change
            create_table :students do |t|
    
              t.string   :name
              t.integer  :age
              t.timestamps
              end
          end
         end
    
    
  • Tạo mới bản ghi Student

        irb(main):006:0> s = Student.new name: "Pan Winter", age: 26
    => #<Student id: nil, name: "Pan Winter", age: 26, created_at: nil, updated_at: nil>
    irb(main):007:0> s.save
       (0.2ms)  begin transaction
      SQL (0.6ms)  INSERT INTO "students" ("name", "age", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Pan Winter"], ["age", 26], ["created_at", 2017-02-07 17:35:42 UTC], ["updated_at", 2017-02-07 17:35:42 UTC]]
      Student Load (0.2ms)  SELECT  "students".* FROM "students" WHERE "students"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
      SQL (0.2ms)  INSERT INTO "versions" ("item_type", "item_id", "event", "created_at") VALUES (?, ?, ?, ?)  [["item_type", "Student"], ["item_id", 1], ["event", "create"], ["created_at", 2017-02-07 17:35:42 UTC]]
       (69.8ms)  commit transaction
    => true
    
  • Update bản ghi vừa tạo ra.

        irb(main):025:0> s.update_attributes age: 27
       (0.1ms)  begin transaction
      SQL (1.5ms)  UPDATE "students" SET "age" = ?, "updated_at" = ? WHERE "students"."id" = ?  [["age", 27], ["updated_at", 2017-02-07 19:06:20 UTC], ["id", 1]]
      Student Load (0.1ms)  SELECT  "students".* FROM "students" WHERE "students"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
      SQL (0.2ms)  INSERT INTO "versions" ("item_type", "item_id", "event", "object", "created_at") VALUES (?, ?, ?, ?, ?)  [["item_type", "Student"], ["item_id", 1], ["event", "update"], ["object", "---\nid: 1\nname: Pan Winter\nage: 26\ncreated_at: !ruby/object:ActiveSupport::TimeWithZone\n  utc: &1 2017-02-07 17:35:42.463077191 Z\n  zone: &2 !ruby/object:ActiveSupport::TimeZone\n    name: Etc/UTC\n  time: *1\nupdated_at: !ruby/object:ActiveSupport::TimeWithZone\n  utc: &3 2017-02-07 17:35:42.463077191 Z\n  zone: *2\n  time: *3\n"], ["created_at", 2017-02-07 19:06:20 UTC]]
       (83.7ms)  commit transaction
    => true
    

    Có thể thấy PaperTrail đã hoạt động khá ổn, mỗi khi có sự kiện thay đổi dữ liệu thỳ một bản ghi Versions được tạo ra log lại nội dung tương ứng

2. Một số method cơ bản.

  • Lấy ra tất cả bản ghi log của một record

    student.versions
    
    # Dữ liệu lấy ra có dạng
    # [<PaperTrail::Version>, <PaperTrail::Version>, ...]
    
  • Lấy sự kiện thay đổi của record

    version = student.versions.last
    version.event
    
    # "update"
    
  • Trả về object là bản ghi được log với dữ liệu trước khi thay đổi.

    irb(main):038:0> version.reify
    => #<Student id: 1, name: "Pan Winter", age: 26, created_at: "2017-02-07 17:35:42", updated_at: "2017-02-07 17:35:42">
    

    Tương tự với: previous_version: Lấy ra bản ghi trước khi thay đổi next_version: Lấy ra bản ghi tiếp đến so với bản hiện tại.

Trên đây mình chỉ giới thiệu một số method cơ bản và cách sử dụng của chúng. Còn rất nhiều method tiện dụng khác mà PaperTrail cung cấp các bạn có thể tham khảo Tại đây

Nhìn chung gem PaperTrail khá đầy đủ, minh bạch và dễ sử dụng, giúp lập trình viên tiết kiệm nhiều thời gian và công sức khi làm việc. Chỉ với một vài thao tác đơn giản chúng ta đã có một chức năng lưu log dữ liệu hoàn hảo và ổn định.

Thanks for reading !!!