Hook methods trong Ruby (phần 2)

Trong bài viết trước, chúng ta đã đề cập tới 2 cách thức sử dụng hook method trong ruby là:

  • included
  • extended

Các bạn có thể xem lại ở link sau: https://viblo.asia/dieunb/posts/mrDkMOglGzL

Tiếp theo, chúng ta sẽ cùng nhau xem xét 3 cách thức khác để sử dụng hook trong ruby là:

  • prepended
  • inherited
  • method_missing

Prepended

Phần trước chúng ta nhận thấy, có thể sự dụng includeextend để sử dụng những hàm được định nghĩa trong module. Tuy nhiên, bên cạnh đó còn có một cách khác, đó là sử dụng prepend. Prepend được giới thiệu trong Ruby 2.0 và bản thân nó khác hoàn toàn so với includeextend. Những hàm được định nghĩa trong module nhưng khi sử dụng thông qua include hoặc extend đều có thể bị overide lại tại module/class. Nhưng với prepend thì hoàn toàn ngược lại. Chúng ta sử dụng để overide lại những hàm được định nghĩa trong module/class mà prepend module đó. Ví dụ:

Khi chưa prepend

module Person
  def name
    "My name belongs to Person"
  end
end

class User
  def name
    "My name belongs to User"
  end
end

puts User.new.name
=> My name belongs to User

Sau khi prepend

module Person
  def name
    "My name belongs to Person"
  end
end

class User
  prepend Person
  def name
    "My name belongs to User"
  end
end

puts User.new.name
=> My name belongs to Person

Prepend có một hàm callback tên là prepended. Hàm này sẽ được thực thi khi mà module được preprend vào một module/class khác. Ví dụ:

module Person
  def self.prepended(base)
    puts "#{self} prepended to #{base}"
  end

  def name
    "My name belongs to Person"
  end
end

Ngay sau khi thực hiện chúng ta có ngay kết quả:

Person prepended to User
My name belongs to Person

prepend được giới thiệu cũng là nhằm tránh việc sử dụng alias_method_chain trong Rails, vì thực sự sử dụng alias_method_chain không được đẹp.

Inherited

Inherit là một khái niệm quan trọng trong OOP. Ruby là một OOP vậy nên việc cung cấp inherit cũng không phải là một ngoại lệ. Cùng xem ví dụ:

class Person
  def name
     "My name is Person"
  end
end

class User < Person
end

puts User.new.name # => My name is Person

Chúng ta cũng có một hook method để nhận biết là class nào kế thừa class nào.

class Person
  def self.inherited(child_class)
    puts "#{child_class} inherits #{self}"
  end

  def name
    "My name is Person"
  end
end

class User < Person
end

puts User.new.name

Kết quả chúng ta nhận được

User inherits Person
My name is Person

Inherit trong Rails Có một class quan trọng trong các Rails app có tên là Application, class này được định nghĩa trong config/application.rb. Class này thực hiện rất nhiều tác vụ như thực hiện tất cả Railties, engines, và plugin.

method_missing

Dường như method_missing là hook method được sử dụng rộng rãi nhất vì khi tìm kiếm trong những Ruby frameworks/gems/libraries, chúng ta thấy tần xuất xuất hiện của nó rất nhiều. Hàm này sẽ được gọi khi mà chúng ta đang cố gắng thực hiện một hàm mà hàm đó không có sẵn. Cùng xem ví dụ:

class Person
  def name
    "My name is Person"
  end
end

p = Person.new

puts p.name     # => My name is Person
puts p.address  # => undefined method `address' for #<Person:0x007fb730a2b450> (NoMethodError)

Chúng ta có thể sự dụng method_missing để tránh được exception và ghi lại điều đó một cách vui vẻ.

class Person
  def method_missing(sym, *args)
     "#{sym} not defined on #{self}"
  end

  def name
    "My name is Person"
  end
end

p = Person.new

puts p.name     # => My name is Person
puts p.address  # => address not defined on #<Person:0x007fb2bb022fe0>

Như vậy, bên trên chúng ta đã đề cập xong những hàm hook quan trọng trong Ruby. Việc sử dụng linh hoạt những hook method này sẽ làm cho ứng dụng của bạn sạch sẽ và chuyên nghiệp hơn.

Nguồn + tham khảo: