Vào ngày 25/12/2018, phiên bản ruby 2.6.0 chính thức được phát hành, kèm theo đó là những tính năng mới được bổ sung và
cải tiền về hiệu suất. Một trình biên dịch mới JIT(Just-In-Time)
được ra đời, một module mới RubyVM::AbstractSyntaxTree
được đưa vào, hiệu suất được cải thiện 1.7 lần
so với Ruby 2.5. Chúng ta hãy cùng điểm qua một số tính năng mới đáng chú ý ở phiên bản Ruby 2.6.0
#then
cho Kernel#yield_self
Ruby 2.5 đã giới thiệu phương thức #yield_self
nó sẽ trả về một block với dữ liệu nhận vào chính là đối tượng gọi method,
kết quả trả về của block đó là một đối tượng mới, từ Ruby 2.6 chúng ta có thể gọi #then
tương tự như #yield_self
1
2
3
4
2.5.0 :1 > "Hello".yield_self { |str| str + " World" }.yield_self { |str| str + " , I am saiury92" }
#=> "Hello World , I am saiury92"
2.6.0 :1 > "Hello".then { |str| str + " World" }.then { |str| str + " , I am saiury92" }
#=> "Hello World , I am saiury92"
Trước ruby 2.6, tên constant cần bắt đầu bởi ký tự in hoa ASCII, nó đồng nghĩa với việc tên các class
và module
không thể
bắt đầu bằng chữ cái in hoa không phải ASCII, đoạn code dưới đây sẽ bắn ra exception class/module name must be CONSTANT
1
2
3
2.5.0 :1 > class Большойдвоичный
2.5.0 :1 > end
#=> SyntaxError: (eval):2: class/module name must be CONSTANT
Nhưng Ruby 2.6 cho phép đặt tên các class
và module
bắt đầu bằng chữ in hoa không phải ASCII.
1
2
3
# Không có lỗi, không sinh exception
2.6.0 :1 > class Большойдвоичный
2.6.0 :2 > end
Range#%
Với những phiên bản Ruby trước, nếu muốn tạo vòng lặp vô hạn cùng với chỉ sổ thì ta sử dụng Float::INFINITY
cùng với upto
hoặc Range
, hoặc sử dụng Numeric#step
, sang ruby 2.6 ta có thể dùng (1..)
hoặc (1..nil)
1
2
3
4
5
2.5.0 :1 > (1..Float::INFINITY).each { |n| puts n }
2.5.0 :2 > 1.step.each { |n| puts n }
2.6.0 :1 > (1..).each { |n| puts n }
2.6.0 :2 > (1..nil).each { |n| puts n }
Ngoài ra class Range
trong ruby 2.6.0 có thêm phương thức mới %
trả về đối tượng Enumerator::ArithmeticSequence
,
như ví dụ dưới đây sẽ lấy ra array có bước nhảy là 3
.
1
2
2.6.0 :1 > ((0..) % 3).take(10)
#=> [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
Enumerator
: Enumerator::Chain
và Enumerator::ArithmeticSequence
Như chúng ta đã biết class Range
được include module Enumerable
, với phiên bản ruby 2.6.0 module Enumerable
được thêm
phương thức Enumerable#chain
sẽ khởi tạo 1 object từ Enumerator::Chain
. Dựa vào đây chúng ta có thể cộng
một Range cho một Array như sau:
1
2
3
4
2.6.0 :1 > e = (1..3).chain([4, 5])
#=> #<Enumerator::Chain: [1..3, [4, 5]]>
2.6.0 :2 > e.to_a
#=> [1, 2, 3, 4, 5]
Với những bản ruby trước 2.6.0 thì gọi phương Range#step
sẽ trả về 1 object của Enumerator
sẽ không lấy được phần tử cuối cùng
của range đó theo phương thức #last
, nhưng với ruby 2.6.0 thì Range#step
trả về Enumerator::ArithmeticSequence
và sẽ có
thể dùng Enumerator::ArithmeticSequence#last
để lấy được phần tử cuối cùng.
1
2
3
4
5
6
7
8
9
10
11
12
13
2.5.0 :1 > (1..10).step(2).class
#=> Enumerator
2.5.0 :2 > (1..10).step(2).last
#=> undefined method `last' for #<Enumerator: 1..10:step(2)>
2.5.0 :3 > (1..10).step(2) == (1..10).step(2)
#=> false
2.6.0 :1 > (1..10).step(2).class
#=> Enumerator::ArithmeticSequence
2.6.0 :2 > (1..10).step(2).last
#=> 9
2.6.0 :3 > (1..10).step(2) == (1..10).step(2)
#=> true
Ở các phiên bản cũ hơn phương thức Hash#merge
chỉ có 1 đối số truyền vào nhưng ở bản 2.6.0 sẽ không giới hạn đối
số truyền vào.
1
2
3
4
5
6
7
8
9
10
2.5.0 :1 > a = { a: 1 }
b = { b: 2 }
c = { c: 3 }
2.5.0 :2 > a.merge(b).merge(c)
#=> {:a=>1, :b=>2, :c=>3}
2.5.0 :3 > a.merge(b, c)
#=> ArgumentError (wrong number of arguments (given 2, expected 1))
2.6.0 :1 > a.merge(b, c)
#=> {:a=>1, :b=>2, :c=>3}
exception
vào Integer()
và Float()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2.5.0 :1 > Integer('foo')
#=> ArgumentError (invalid value for Integer(): "foo")
2.5.0 :2 > Integer('foo') rescue nil
#=> nil
2.6.0 :1 > Integer('foo', exception: false)
#=> nil
2.6.0 :2 > Float('foo', exception: false)
#=> nil
2.6.0 :1 > Benchmark.ips do |x|
2.6.0 :2 > x.report("rescue") {
2.6.0 :3 > Integer('foo') rescue nil
2.6.0 :4 > }
2.6.0 :5 > x.report("kwarg") {
2.6.0 :6 > Integer('foo', exception: false)
2.6.0 :7 > }
2.6.0 :8 > x.compare!
2.6.0 :9 > end
Warming up --------------------------------------
rescue 57.120k i/100ms
kwarg 235.373k i/100ms
Calculating -------------------------------------
rescue 666.623k (± 1.9%) i/s - 3.370M in 5.057321s
kwarg 3.615M (± 0.8%) i/s - 18.124M in 5.013689s
Comparison:
kwarg: 3615095.7 i/s
rescue: 666623.0 i/s - 5.42x slower
Với lựa chọn exception
chúng ta có thể kiểm soát được hành vi của phương thức thay vì sử dụng rescue
như ở các phiên bản
trước và điều này cũng giúp phần tăng tốc độ xử lý (nhanh hơn 5.42x so với sử dụng rescue ở ví dụ trên).
Để sinh ra các byte ngẫu nhiên chúng ta đã quá quen với phương thức SecureRandom#random_bytes
, ở phiên bản 2.6.0 có
thêm phương thức Random#bytes
với tốc độ xử lý nhanh hơn nhiều (gấp 8.27x).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2.6.0 :1 > Benchmark.ips do |x|
2.6.0 :2 > x.report("random bytes") {
2.6.0 :3 > Random.bytes(10)
2.6.0 :4 > }
2.6.0 :5 > x.report("secure random bytes") {
2.6.0 :6 > SecureRandom.random_bytes(10)
2.6.0 :7 > }
2.6.0 :8 > x.compare!
2.6.0 :9 > end
Warming up --------------------------------------
random bytes 449.938k i/100ms
secure random bytes 116.499k i/100ms
Calculating -------------------------------------
random bytes 11.778M (± 5.2%) i/s - 58.942M in 5.020230s
secure random bytes 1.424M (± 3.2%) i/s - 7.223M in 5.078557s
Comparison:
random bytes: 11778264.6 i/s
secure random bytes: 1423807.3 i/s - 8.27x slower
Hai phương thức mới #union
và #difference
được thêm vào lớp Array
trong Ruby 2.6, để phân biệt và kết hợp giữa các
mảng.
1
2
3
4
2.6.0 :1 > [1,2,3,4,5].difference([3])
#=> [1, 2, 4, 5]
2.6.0 :2 > [1,2,3,4,5].union([5,6,7])
#=> [1, 2, 3, 4, 5, 6, 7]
<<
và >>
vào Proc
1
2
3
4
5
6
7
8
2.6.0 :1 > f = proc{|x| x + 2}
#=> #<Proc:0x000056240c75d290@(irb):1>
2.6.0 :2 > g = proc{|x| x * 3}
#=> #<Proc:0x000056240c74fa78@(irb):2>
2.6.0 :3 > (f << g).call(3) # Gống như f.call(g.call(3))
#=> 11
2.6.0 :4 > (f >> g).call(3) # Gống như g.call(f.call(3))
#=> 15
FileUtils#cp_lr
Phương thức FileUtils#ln_s
hay FileUtils#ln_sf
(tự động force
ghi đè khi liên kết tồn tại) sẽ tạo liên kết tượng trưng
(symbolic link), trong phiên bản mới ruby 2.6 sẽ thêm phương thức FileUtils#cp_lr
tạo liên kết cứng (hard link) từ thư mục
hoặc file nguồn đến thư mục đích, để hiểu rõ về 2 loại liên kết này hãy tham khảo bải viết
hard link và symbolic link.
1
2
3
# Cài đặt thư viện 'lib' tới 'mylib' tới thư mục app.
FileUtils.rm_r 'app/mylib', :force => true
FileUtils.cp_lr 'lib/', 'app/mylib'
Comments