Một số thay đổi ở Ruby 2.3.0 qua các ví dụ
Phiên bản thử nghiệm Ruby 2.3.0 preview 1 vừa được phát hành giới thiệu một số cú pháp và functions mới cho các core classes. Bài viết này sẽ điểm qua một số thay đổi đáng chú ý kèm theo các ví dụ minh họa.
frozen-string-literal pragma
Đóng băng (freeze) string được giới thiệu trong Ruby 2.1 với mục đích tối ưu bộ nhớ cho việc sử dụng string. Và việc mặc định đóng băng các string đang được thảo luận cho Ruby 3.0. Trong hoàn cảnh đó Ruby 2.3.0 giới thiệu một magic comment cho phép mặc định đóng băng các string trong một Ruby file.
Giả sử chúng ta có 1 file frozen.rb
như sau:
# frozen_string_literal: true
puts "abc".reverse!
Thực thi frozen.rb
trên terminal:
ruby frozen.rb
#=> frozen.rb:2:in `reverse!': can't modify frozen String (RuntimeError)
Chúng ta có thể bỏ dòng comment # frozen_string_literal: true
hoặc thay bằng # frozen_string_literal: false
thì sẽ không bị lỗi khi thay đổi string nữa:
# frozen_string_literal: false
puts "abc".reverse!
ruby frozen.rb
#=> cba
Cũng có thể thử nghiệm frozen_string_literal
comment trong irb:
# frozen_string_literal: true
"abc".reverse!
#=> RuntimeError: can't modify frozen String
# frozen_string_literal: false
"abc".reverse!
#=> "cba"
Ngoài ra, chúng ta có thể sử dụng command line option --enable=frozen-string-literal
/ --disable=frozen-string-literal
safe navigation operator
Xét một câu điều kiện quen thuộc như sau
current_user && current_user.is_admin?
Ruby 2.3.0 cho phép rút gọn cú pháp như trên thành như sau:
current_user&.is_admin?
Tính năng này không những giảm bớt số kí tự cần viết, đồng thời cũng bảo đảm trả về giá trị nil
một cách an toàn thay vì raise NoMethodError
cho nil
object, một lỗi thường gặp do chúng ta không kiểm tra đầy đủ các trường hợp (VD: gọi current_user.is_admin?
mà chưa kiểm tra current_user
có tồn tại hay không).
Hash
Hàm Hash#dig
cho phép một hash truy xuất dữ liệu một cách an toàn (trả về nil
trong trường hợp không truy xuất được thay vì NoMethodError
exception)
hash = {a: {b: {c: 1}}}
hash[:a][:b][:c] # => 1
hash.dig :a, :b, :c # => 1
hash[:a][:d][:c] # NoMethodError: undefined method `[]' for nil:NilClass
hash.dig :a, :d, :c # => nil
Các hàm Hash#<=
, Hash#<
, Hash#>=
, Hash#>
dành cho việc so sánh các hash:
{ a: 1, b: 2 } > { a: 1 } # => true
{ a: 1 } > { a: 1 } # => false
{ b: 1 } > { a: 1 } # => false
{ a: 1, b: 2 } < { a: 1, b: 2, c: 3 } # => true
Hàm Hash#fetch_values
cho phép raise exception khi key không tồn tại:
values = {
foo: 1,
bar: 2,
baz: 3,
qux: 4
}
values.values_at(:foo, :bar) # => [1, 2]
values.fetch_values(:foo, :bar) # => [1, 2]
values.values_at(:foo, :bar, :invalid) # => [1, 2, nil]
values.fetch_values(:foo, :bar, :invalid) # => KeyError: key not found: :invalid
Hàm Hash#to_proc
biến một hash thành proc object
hash = { a: 1, b: 2, c: 3 }
keys = %i[a c d]
keys.map(&hash) # => [1, 3, nil]
Array
Giống như Hash#dig
, hàm Array#dig
cho phép một array truy xuất dữ liệu một cách an toàn (trả về nil
trong trường hợp không truy xuất được thay vì NoMethodError
exception)
arr = [[1, 2, 3], [4, 5, 6]]
arr[1][2] # => 6
arr.dig(1, 2) # => 6
arr[2][2] # NoMethodError: undefined method `[]' for nil:NilClass
arr.dig(2, 2) # => nil
Hàm Array#bsearch_index
trả về vị trí thay vì giá trị của kết quả
[1, 2, 5, 3, 4].bsearch { |x| x > 2 }
# => 5
[1, 2, 5, 3, 4].bsearch_index { |x| x > 2 }
# => 2
[1, 2, 5, 3, 4].bsearch_index { |x| x > 5 }
# => nil
Enumerable
Hàm Enumerable#grep_v
cho phép chọn lọc các phần tử KHÔNG phù hợp với điều kiện đặt ra (ngược với hàm Enumerable#grep
)
[1, "abc", nil, :abc, 5.0].grep(Numeric) # => [1, 5]
[1, "abc", nil, :abc, 5.0].grep_v(Numeric) # => ["abc", nil, :abc]
Numeric
Thêm hai hàm Numeric#positive?
và Numeric#negative?
để kiểm tra giá trị của một số là âm hay dương.
3.positive? # => true
3.negative? # => false
-7.33.negative? # => true
[1, -3, 5, -7, 9].select(&:negative?) # => [-3, -7]
cài đặt sẵn gem did_you_mean
Gem did_you_mean đưa ra gợi ý khi gặp phải các exceptions NoMethodError
hay NameError
. Gem này được cài đặt sẵn trong Ruby 2.3.0
3.positive
# NoMethodError: undefined method `positive' for 3:Fixnum
# Did you mean? positive?
class User; end
Usser
# NameError: uninitialized constant Usser
# Did you mean? User
Tham khảo
- Thông tin về các visible changes của Ruby 2.3.0 được xuất bản tại NEWS for Ruby 2.3.0
- Một số ví dụ được lấy từ bài viết New features in ruby 2.3 của John Backus.