Một số tính năng hữu ích trong ruby
Bài đăng này đã không được cập nhật trong 7 năm
Enumerator.new
Có thể nhiều người biết đến Enumerator.new nhưng số người thực sự dùng nó là không nhiều Dưới đây là những lí do nên dùng nó
- Đưa vào scope mới
- Producer-Consumer pattern. Làm rõ ràng 2 phase khởi tạo giá trị và sử dụng các giá trị đó
- Việc đưa toàn bộ giá trị vào 1 mảng tốn memory. Việc dùng enumerable giúp giảm bớt memory
- Có thể thực hiện nested
Ví dụ
Producer phase
user_ids = Enumerator.new do |y|
open("candidates.txt") do |f|
f.each_line do |line|
next if line.start_with?("#")
user_id = line.chomp
y << user_id
end
end
end
Consumer phase
user_ids.with_index 1 do |user_id, index|
puts "#{index} #{user_id}"
end
Trong ví dụ trên consumer phase là khá đơn giản nên có thể viết trực tiếp, khi nó trở nên phức tạp hơn thì bạn sẽ thấy được lợi ích của việc có thể nested khi sử dụng Enumerator.new
Object#tap
Object#tap có lẽ cũng được nhiều người biết đến nhưng có lẽ chủ yếu dùng để debug trong method chain.
Ví dụ
(1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
tap { |x| puts "array: #{x.inspect}" }.
select { |x| x%2 == 0 }.
tap { |x| puts "evens: #{x.inspect}" }.
map { |x| x*x }.
tap { |x| puts "squares: #{x.inspect}" }
Kết quả
original: 1..10
array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens: [2, 4, 6, 8, 10]
squares: [4, 16, 36, 64, 100]
=> [4, 16, 36, 64, 100]
Object# tap có thể sử dụng trong nhiều tình huống khác nhau như dưới đây
- Làm rõ ràng phase khởi tạo biến số và phase sử dụng
- Khi gía trị trả về là hash hay object và cảm thấy nghi hoặc
alphabet_nums = {}.tap do |hash|
("a".."z").each.with_index 1 do |alphabet, index|
hash[alphabet] = index
end
end
alphabet_nums = {}
("a".."z").each.with_index 1 do |alphabet, index|
alphabet_nums[alphabet] = index
end
Khi so sánh 2 cách viết trên, cách viết bọc đoạn code trong tap sẽ làm rõ hơn phase khởi tạo giá trị chính là đoạn code nằm trong tap. Trong trường hợp alphabet_nums không có thay đổi gì sau khi khởi tạo thì có nghĩa là mọi thay đổi của alphabet_nums chỉ nằm trong khối block khởi tạo ban đầu
Float::INFINITY
Trong Ruby có giá trị Float::INFINITY dùng để biểu diễn giá trị vô hạn
Xem xét trường hợp dưới đây
# Nếu to là nil thì giá trị của x lớn bao nhiêu cũng được ngược lại chỉ lấy các giá trị x < to
if to.nil? || x < to
do_something(x)
end
Bằng cách chuyển giá trị mặc định của to thành Float::INFINITY chúng ta có thể bỏ được đoạn check nil
to ||= Float::INFINITY
if x < to
do_something(x)
end
Nếu chỉ có 1 đoạn code đơn lẻ như trên thì cũng không thấy được sự tiện lợi, nhưng nếu có nhiều đoạn xử lí với cùng đoạn check điều kiện như trên thì bạn sẽ tránh được việc phải viết đi viết lại đoạn check nil nhiều lần
Array#shelljoin
ruby có cung cấp standard library tên là shellwords
Sau khi require shellwords, các kí tự cần escape trong lệnh bash sẽ tự động được escape và liên kết lại
require 'shellwords'
["vlc", "Ruby on Rails.mp4"].shelljoin #=> "vlc Ruby\\ on\\ Rails.mp4"
Điều này rất thuận tiện khi thực hiện call các lệnh bash tại code ruby
SecureRandom.hex
Khi cần tạo chuỗi kí tự ngẫu nhiên (session key chẳng hạn), thực hiện require securerandom sau đó gọi SecureRandom.hex là rất tiện lợi
require 'securerandom'
SecureRandom.hex # "396bc8f76cffce4d3bfea3d07f58b09b"
Ngoài ra còn có thể dùng khi tạo mail ngẫu nhiên, đặt tên cho các file tạm thời
Toán tử Flip Flop
Bằng cách sử dụng cú pháp ..
trong câu điều kiện, toán tử này cho phép xác định điều kiện bắt đầu và điều kiện kết thúc
(1..5).each do |x|
puts x if (x == 2) .. (x == 4)
end
Kết quả là
2
3
4
Một ví dụ khác: thực hiện xử lí chỉ với phần code ruby nằm trong file Markdown. Đoạn code sẽ nằm trong khối bắt đầu và kết thúc với ```
ARGF.each_line do |line|
if (line.start_with?("```rb"))..(line.chomp == "```")
do_something(line)
end
end
Tài liệu tham khảo
http://qiita.com/cuzic/items/a265f140fdff289d5c07 http://ruby-doc.org/core-2.2.0
All rights reserved