12/08/2018, 15:22

Nghệ thuật Callback trong Ruby

Callbacks là một phương thức của Active Record, nó sẽ được gọi tới vào một thời điểm nào đó trong vòng đời của một đối tượng. Callback thường được dùng để thực thi các phương thức logic trước hoặc sau khi đối tượng có một sự thay đổi nào đó, ví dụ như create, update, delete,...Chúng thường được sử ...

Callbacks là một phương thức của Active Record, nó sẽ được gọi tới vào một thời điểm nào đó trong vòng đời của một đối tượng. Callback thường được dùng để thực thi các phương thức logic trước hoặc sau khi đối tượng có một sự thay đổi nào đó, ví dụ như create, update, delete,...Chúng thường được sử dụng song song với việc validate dữ liệu để đảm bảo rằng việc vào ra dữ liệu database là hoàn toàn chính xác. Tuy nhiên nếu sử dụng Callback không hợp lí thì sẽ tạo ra một số trường hợp xấu gây ảnh hưởng đến quá trình test và debug.

Callbacks được sử dụng trực tiếp cùng với những phương thức ActiveRecord như là create, save, update, destroy của các bản ghi trong database. Chúng phải được khai báo trên cùng của mỗi model. Ví dụ như sau:

class Person < ActiveRecord::Base
after_initialize do |person|
  puts "Waaaahh! I'm a baby! Waaaahh!"
end
after_save do |person|
  puts "You saved me! I owe you my life!!"
end
end

>> Person.new
#=> Waaaahh! I'm a baby! Waaaahh!
>> Person.save
#=> You saved me! I owe you my life!!

Trong ví dụ trên có một lỗi khá là ngớ ngẩn, chúng ta đã truyền một block trong vào phương thức Callback, block này sẽ in ra dòng message khi đối tượng được khởi tạo hoặc lưu vào database. Các block này chỉ thật sự được truyền vào phương thức Callback nếu chúng không quá dài (chỉ trên một dòng). Thông thường, những callback này sẽ được kiên kết với các phương thức đã được khai báo cùng với các class tương ứng. Ví dụ, trước khi lưu một record ExcitedUser vào database, chúng ta cần phải chắc chắn răng tên của ExcitedUser phải trong ALL CAPS. Chúng ta có thể sử dụng như sau:

class ExcitedUser < ActiveRecord::Base
  before_save :upcase_name
  private
  def upcase_name
    self.name == self.name.upcase
  end
end

Các Callback cho phép thêm vào các điều kiện lọc và kiểm tra hệ thống, thỉnh thoảng sẽ dừng hệ thống lại khi bất cứ một test nào đó không được thoả mãn. Những method này nên được khai báo bằng private để mà chúng bị gọi bên ngoài model. Điều này rất quan trọng khi bạn muốn thêm một callback vào ứng dụng của bạn, và điều này sẽ được thảo luận sau.

Callback có tất cả 19 callback mà chúng ta có thể sử dụng với model ActiveReocrd. Phần lớn tất cả các Callback này giải quyết các vấn đề về CRUD:

- before_create         - before_update            - before_destroy
- around_create         - around_update            - around_destroy
- after_create          - after_update             - after_destroy

Luồng đi của CRUD trước và sau khi gọi Callback rất là đơn giản. Chúng sẽ thêm các logic vào đối tượng trước hoặc sau khi được created, updated, hoặc destroyed. Để hiểu rõ về vấn đề này, hãy cùng xem ví dụ dưới đây:

class Party < ActiveRecord::Base
  before_create :before_party
  around_create :around_party
  after_create :after_party
private
  def before_party
    puts "We gotta get ready for this party!"
  end
  def around_party
    puts "This party is about to start!"
    yield # party is created
    puts "Party Time!!"
  end
  def after_party
    puts "What a nice party that was."
  end
end
>> Party.create
#=> "We gotta get ready for this party!"
#=> "This party is about to start!"
#=> "Party Time!!"
#=> "What a nice party that was."

Trước khi phương thức Party#create được gọi thì các Callback này sẽ được thực thi trước. Sau đó khi bạn thật sự muốn tạo ra đối tượng Party bạn cần gọi yield bên trong callback around_create. Như bạn đã thấy ở ví dụ trên khi Party#create được gọi, thì các callback sẽ được gọi với thứ tự before, around, after.

Các Callback thường được sử dụng cùng với validate để sắp xếp hoặc xóa bỏ các dữ liệu mà người nhập dùng không hợp lệ. Trong ví dụ dưới đây, chúng ta sẽ sử dụng callback before_validation để loại bỏ các định dạng nhập vào không đúng.

class CreditCard < ActiveRecord::Base
before_validation :normalize_credit_card, on: :create
private
  def normalize_credit_card
     self.number = number.gsub(/[^0-9]/, "")
   end
end

Chú ý ở ví dụ trên, chúng ta sử dụng :on, cái này sẽ cho phép bạn xác định callback đó sẽ áp dụng cho phương thức nào, ví dụ trên là đối với create, hoặc bạn có thể thay thế bằng destroy, update... Một lưu ý nữa trong Callback, là việc validate và thực thi database tất cả xâu chuỗi với nhau theo kiểu hàng đợi, nếu chỉ cần một callback nào bị lỗi, thì tất cả các callback sau nó sẽ không thể được thực thi, đây là một trong những lỗi thường xuyên gặp phải đối với lập trình viên

  1. https://viblo.asia/nguyen.my.huyen/posts/DbmemLYwGAg
0