Một số tips và tricks trong Ruby

Trong bài viết này, mình xin giới thiệu một số tips và tricks trong ngôn ngữ Ruby. Mong rằng những trick dưới đây sẽ giúp cho những người mới cảm thấy fun và thích thú hơn khi tìm hiểu về ngôn ngữ này. 😄

1.Sử dụng & để tạo procs

Tôi có một mảng String và tôi muốn viết hoa hết các phần tử trong mảng này như sau:

arr = ['we', 'love', 'ruby']
caps = arr.map {|str| str.upcase}

Rất quen thuộc phải không? Tuy nhiên tôi có thể viết ngắn gọn lại câu lệnh trên sử dụng toán tử &

caps = arr.map(&:upcase)

Toán tử & nhận tham số là đối tượng và gọi hàm to_proc trên đối tượng đó. Đối với đối tượng là một symbol, hàm to_proc sẽ tạo ra một proc và truyền vào đó đối tượng, đối tượng này sẽ thực thi hàm có tên ứng với symbol thông qua lệnh send. Vì vậy thực chất câu lệnh trên tương đương:

caps = arr.map {|str| str.send(:upcase)}

Bây giờ, thay vì gọi đến instance method của đối tượng, ta có thể truyền đối tượng đó như một tham số của một hàm nào đó khác

def demo_method(word)
  word + ' - ' + 'demo'
end
#Thay vi phải gọi
res = arr.map{|str| demo_method(str)}

#Ta có thể làm như sau
res = arr.map(&method(:demo_method))

2.Xử lý như nhau với một đối tượng hay một mảng các đối tượng

Đôi khi trong lúc viết hàm, ta cần cho lựa chọn truyền tham số vào là một đối tượng duy nhất hoặc truyền vào một mảng các đối tượng, thay vì phải kiểm tra kiểu của tham số truyền vào là gì, ta có thể sử dụng toán tử splat(*) [*something] hoặc class Array Array(something) như sau:

#Thay vì phải kiểm tra kiểu dữ liệu
def print_digits(items)
  unless items.is_a? Array
    puts items
  else
    items.each {|i| puts i}
  end
end

#Ta có thể làm như sau với kết quả tương tự
def print_digits(items)
  [*items].each {|i| puts i}
  # hoặc: Array(items).each {i} puts i
end

3.Tạo một hash từ một mảng các giá trị

Bạn có thể tạo ra một hash từ mảng các giá trị sử dụng Hash[...] như sau

Hash['key1', 'value1', 'key2', 'value2']

# => {"key1"=>"value1", "key2"=>"value2"}

4. Double star (**)

Tôi có đoạn code sau:

def my_method(a, *b, **c)
  return a, b, c
end

trong đó a là một tham số bình thường, *b sẽ nhận tất cả các tham số sau a và đưa chúng vào một mảng. Còn **c sẽ lấy bất cứ tham số nào ở dạng key: value

my_method(1)
# => [1, [], {}]

my_method(1,2,3,4)
# => [1, [2,3,4], {}]

my_method(1,2,3,4,a: 1, b: 2)
# => [1, [2,3,4], {:a=>1, :b=>2}]

5.Tap

Tap là một phương thức làm cho code của bạn dễ đọc hơn. Hãy cùng xem xét class sau:

class User
  attr_accessor :a, :b, :c
end

Giờ ta cần khởi tạo một user và gán giá trị cho mỗi thuộc tính a, c, c. Thay vì làm như sau:

def my_method
  o = User.new
  o.a = 1
  o.b = 2
  o.c = 3
  o
end

ta có thể sử dụng phương thức tap:

def my_method
  User.new.tap do |o|
    o.a = 1
    o.b = 2
    o.c = 3
  end
end

Về cơ bản, phương thức tap truyền cho block đối tượng được gọi và trả lại đối tượng đó.

6.Heredocs

Heredocs là một tính năng của Ruby cho phép bạn tạo ra một chuỗi String dài nằm trên nhiều dòng mà không lo phải sử dụng ký tự escape (\). Tuy nhiên chuỗi trong heredocs lại không tự ignore các khoảng trắng, dẫn đến việc ta phải để nội dung của heredocs sát vào lề trái. Điều này làm mất tính thẩm mỹ của những dòng code:

def my_method
  <<-EOT
Some
Very
Interesting
Stuff
  EOT
end

Tuy nhiên có một trick nhỏ để tránh trường hợp này đó là sử dụng gsub cùng biểu thức chính quy (regex) để tự động loại bỏ các khoảng trắng không cần thiết:

def my_method
  <<-EOT.gsub(/^\s+/, '')
    Some
    Very
    Interesting
    Stuff
  EOT
end

7.Nested Ternary Operator

Toán tử 3 ngôi là tính năng mà nhiều ngôn ngữ hỗ trợ và được sử dụng rất thường xuyên:

puts x == 10 ? "x is ten" : "x is not ten"

Tuy nhiên ternary operator còn có thể viết lồng vào với nhau như sau:

qty = 1
qty == 0 ? 'none' : qty == 1 ? 'one' : 'many'
# => 'one'

8.Instance Method Caching

Khi làm việc với instance của một class nào đó, đôi khi ta sẽ gặp trường hợp một instance method a nào đó thực hiện các câu lệnh query rất phức tạp chỉ để trả về cùng một kết quả mỗi khi method đó được gọi. Điều này gây lãng phí tài nguyên bộ nhớ cùng như mất thời gian chờ câu lệnh được thực thi xong và phải có cách nào đó để caching lại kết quả của method trả về. Đó là lúc ta nên sử dụng toán tử ||=. Toán tử này sẽ trả về giá trị của biến hoặc set giá trị cho biến nếu nó nil (hoặc false):

class Person
  def tweets
    @tweets ||= Twitter.get_tweets(self.username)
  end
end

person = Person.new
person.tweets   # => Mất vài giây thực hiện và trả về một mảng các tweets
person.tweets   # => Trả về mảng các tweets mà ta đã cache lại được sau lần gọi đầu tiên

Tổng kết

Trên đây là một số những trick rất nhỏ mà mình đã tổng hợp lại được. Hy vọng bài viết sẽ hữu dụng một phần nào đó với các bạn đã, đang và sẽ chuẩn bị bước chân vào thế giới đầy màu sắc Ruby! (yeah2)

Nguồn tham khảo

  1. https://blog.atechmedia.com/ruby-tips-part-1/
  2. https://samurails.com/ruby/ruby-tricks-improve-code/

All Rights Reserved