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 ...
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 lời khuyên 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.