Những điều có thể bạn chưa biết trong Ruby
Bài đăng này đã không được cập nhật trong 6 năm
Bài viết tổng hợp một số kĩ thuật, khái niệm mình tìm hiểu được về Ruby, bài viết yêu cầu bạn đã có kiến thức cơ bản về Ruby như: method lookup, instance method, class method, ...
1. Open Class
Xem xét ví dụ sau:
    class A
       def say_hi
          puts "Say Hi to everybody"
       end
    end
    class A
       def say_goodbye
          puts "Say Goodbye to everybody"
       end
end
Trong ví dụ trên, khi lần đầu tiên viết "class A", thì chưa tồn tại "class A" nào, vì thế Ruby sẽ định nghĩa ra class này và "say_hi" method. Khi lần thứ 2, sử dụng "class A" thì "class A" đã tồn tại, do đó, Ruby không cần định nghĩa class này nữa, thay vì thế, Ruby "mở lại" "class A" và định nghĩa phương thức "say_goodbye".
Bạn có thể làm điều này với bất cứ class nào trong Ruby, tức là cả với các class có sẵn của Ruby như String, Array => cho phép bạn điều chỉnh class có sẵn tùy ý.
Thử ví dụ sau:
Giả sử bạn muốn thêm một hàm chuyển một số sang tiền VND.
    class Numeric
       def to_money
          puts self.to_s + " VND" // giả sử ở đây mình trả về chuỗi ghi ra giá tiền kèm đơn vị tiền tệ
       end
    end
Lúc này, nếu bạn gõ
   100.to_money
sẽ được kết quả sau:
   100 VND
Một vấn đề của kĩ thuật này đó là rất có thể, có tình huống, bạn sẽ thay đổi hành vi hiện tại của instance method. (Monkeypatch)
Ví dụ:
    "hello".reverse  // sẽ in ra: "olleh"
    class String
       def reverse
          puts "override"
       end
    end
    "hello".reverse  // sẽ in ra: "override"
2. Defining Methods Dynamically
Hẳn các bạn đã từng nghe qua define_method nếu đọc về metaprogramming trong ruby. Đây là một kĩ thuật giúp chúng ta định nghĩa phương thức tại runtime. Bạn chỉ cần truyền define_method tên phương thức và một block - block này sẽ trở thành nội dung của method. Mình sẽ lấy một ví dụ về việc sử dụng define_method bằng việc viết mô phỏng cách ActiveRecord hoạt động.
    class ActiveRecord
      def self.has_one obj
         define_method obj do
            puts "#{self.class.to_s.downcase} has one #{obj}"
         end
      end
      def self.has_many objs
         define_method objs do
            puts "#{self.class.to_s.downcase} has many #{objs}"
         end
      end
      def self.belongs_to obj
         define_method obj do
            puts "#{self.class.to_s.downcase} belongs_to #{obj}"
         end
      end
    end
    class Moive < ActiveRecord
       has_many :actors
    end
    moive = Movie.new
    moive.actors // sẽ in ra: "movie has many actors"
3. Scope gates & Self
    v1 = 1
    class A            // bắt đầu scope của class, lúc này, self chính là class A
       v2 = 2
       def my_method   // bắt đầu scope của method, lúc này, self chính là instance
          v3 = 3
       end             // kết thúc scope của method
    end                // kết thúc scope của class
Khi một phương thức được gọi mà không xác định receiver thì thực chất là phương thức được gọi trên receiver ngầm định (self). Xem ví dụ:
    class Parent
       def self.say_hi
          "Say Hi to the world"
       end
    end
    class Child < Parent
      say_hi
    end
    // sẽ in ra "Say Hi to the world"
Ở đoạn code trên, phương thức "say_hi" được gọi mà không xác định receiver => thực chất sẽ là "self.say_hi", lúc này, trong scope của class thì self chính là class, đó là lý do in ra dòng chữ "Say Hi to the world". Một ví dụ khác:
   class A
      def say_hi
         introduce
      end
      
      def introduce
         "Hello everybody, My name is Ahi"
      end
   end
   
   a = A.new
   a.say_hi    // sẽ in ra "Hello everybody, My name is Ahi"
Ở đoạn code trên, phương thức introduce được gọi mà cũng không xác định receiver => self.introduce, lúc này, trong scope của method thì self là instance, => sẽ gọi phương thức "introduce" => in ra dòng chữ như trên.
4. Module
Trong Ruby, Module không quá khác biệt so với class, điều khác nhau duy nhất đó là chúng ta không thể tạo đối tượng từ Module (thông qua từ khóa new). Vậy, tại sao lại có module, lợi ích của nó là gì? Theo ý kiến của mình thì, module một cách bạn gom nhóm các phương thức, để sau đó có thể sử dụng ở nhiều nơi. Bạn có thể đem vào class này, class kia. Không như class, khi định nghĩa trong class thì phương thức sẽ bị gắn chặt với class đó và các class kế thừa nó. Module trong Ruby thường được sử dụng với 2 từ khóa:
- include: Trong trường hợp này, phương thức của module sẽ trở thành instance method của class.
    module M
      def say_hi
         puts "Say Hi to the world"
      end
    end
    class A
      include M
    end
    a = A.new
    a.say_hi        // sẽ in ra "Say Hi to the world"
Thực tế khi bạn include một Module vào một class thì biểu đồ kế thừa của class sẽ như sau
 Đó là lý do method của module trở thành instance method của class.
Đó là lý do method của module trở thành instance method của class.
- extend: Trong trường hợp này, phương thức của module sẽ trở thành class method của class.
   module M2
      def sayonara
         puts "Sayonara"
      end
   end
   
   class A
      extend M2
   end
   
   A.sayonara      // sẽ in ra "Sayonara"
Biểu đồ class sẽ như sau:

Đó là lý do tại sao method của module trở thành class method của class.
Nếu bạn đã hiểu cách Ruby làm việc với từ khóa include, extend, thì giờ giả sử, chỉ sử dụng include, làm sao chúng ta có thể biến phương thức của module thành class method???
Xem xét cách làm sau
    class C; end
    module M
      def say_hello
         "Say hello to the world"
      end
    end
    class << C
     include M
    end
    C.say_hello   // sẽ ỉn ra "Say hello to the world"
Bản chất của cách làm trên, chính là việc include module trong Singleton Class của class.
5. Kết luận
Trên đây là một vài khái niệm, kĩ thuật mình tìm hiểu được, giúp mình hiểu hơn về Ruby - một ngôn ngữ khá đẹp (cảm giác lúc code khá là sướng). Nếu các bạn có những hiểu biết gì khác về Ruby thì hãy chia sẻ thêm. Cảm ơn các bạn đã đọc bài.
Tài liệu tham khảo:
Metaprogramming Ruby - Paolo Perrotta
https://pragmaticstudio.com/tutorials/ruby-macros
All rights reserved
 
  
 