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. ...
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.
Việc cài đặt gem paper_trail khá đơn giản.
-
Thêm gem trong file Gemfile
gem "paper_trail", "~> 4.0.0"
-
Chạy câu lệnh bundle install.
-
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
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.
-
Với tiến trình như rake task
PaperTrail.enabled = false
-
Với config môi trường
config.paper_trail.enabled = false
-
Với class
Student.paper_trail.disable Student.paper_trail.enable
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", "--- id: 1 name: Pan Winter age: 26 created_at: !ruby/object:ActiveSupport::TimeWithZone utc: &1 2017-02-07 17:35:42.463077191 Z zone: &2 !ruby/object:ActiveSupport::TimeZone name: Etc/UTC time: *1 updated_at: !ruby/object:ActiveSupport::TimeWithZone utc: &3 2017-02-07 17:35:42.463077191 Z zone: *2 time: *3 "], ["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 !!!