Một số tips và tricks trong Ruby
Bài đăng này đã không được cập nhật trong 8 năm
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
All rights reserved