Ruby Version Changes

Arun Mathew Kurian
Thoughts on Tech
Published in
6 min readJan 8, 2019

Each version of Ruby includes a set of new features and methods. This documentation list out the new changes that were introduced from Ruby version 2.5 onwards.

New Features and Functions added to Ruby 2.5

Features

#1 Reverse Stack Trace:

One of the major feature that was introduced in Ruby 2.5.0 is Reverse Stack Trace.

Before Ruby 2.5, the printed backtrace contained the exception class and the error message at the top. Next lines will contain the location of the various cascaded method calls. This made it difficult to read the error message as it is difficult to display the whole backtrace in the visible viewport of the terminal.

So from Ruby 2.5 onwards, the backtrace was printed in reverse order. That is an error message and exception class is printed last at the backtrace. Also, a frame number was also added with each line.

Example

irb(main):001:0>1/0
Traceback (most recent call last):
5: from /home/arun/.rbenv/versions/2.5.3/bin/irb:23:in `<main>’
4: from /home/arun/.rbenv/versions/2.5.3/bin/irb:23:in `load’
3: from /home/arun/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.3/gems/irb-1.0.0/exe/irb:11:in `<top (required)>’
2: from (irb):1
1: from (irb):1:in `/’
ZeroDivisionError (divided by 0)

#2 Allows rescue/else/ensure inside do/end blocks:

Before Ruby 2.5.0 it was not possible to add rescue/else/ensure blocks inside do/end blocks. If it was added an error will be thrown.

SyntaxError: (irb):4: syntax error, unexpected keyword_rescue,
expecting keyword_end

This was fixed in 2.5.0 and the rescue/else/ensure can be written in a do/end block.

Example Code

array.each do |number|
p 10 / number
rescue ZeroDivisionError => exception
p exception
next
end
end

#3. Significant Performance Improvement

According to docs Ruby 2.5,0 brought about 5–10% performance improvement. Some observations related are

  • String interpolation will be around 72% faster when a large string is created
  • String#prepend will be around 42% faster if only one argument is given
  • Enumerable#sort_by, Enumerable#min_by & Enumerable#max_by will be about 50% faster
  • Mutex is rewritten to be smaller and faster
  • ERB now generates code from a template twice as fast as Ruby 2.4

One of the optimizations done is to use OPTIMIZED_CMP() to compare the objects instead of <=> method.

Functions

#1 Kernel#yield_self

Kernel#yield_self works similarly to the method Object#tap, but they return different values.

Object#tap yields self to the block and then returns self.

Example

(1..10).tap { |x| puts “original: #{x}” }
.to_a.tap { |x| puts “array: #{x}” }

Returns
# => original: 1..10
# => array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Kernel#yield_self yields self to the block and then returns the result of the block execution.

(1..10).yield_self{|x| puts “original: #{x}”

Returns

original: 1..10
=> nil

#2 Hash#transform_keys/Hash#transform_keys!

Hash#transform_keys is added for transforming keys of the hash., which is similar to the transform_values function present in Ruby 2.4.

Example

h = { name: “arun”, email: “arun@example.com” }
=> {:name=>”arun”, :email=>”arun@example.com”}
h.transform_keys { |k| k.to_s }
=> {“name”=>”arun”, “email”=>”arun@example.com”}

Hash#transform_keys! is also added which changes the hash in place.

#3 Hash#slice

Hash#slice is a simple method to select key-value pairs from a hash with specified keys.

Example

blog = { id: 1, name: ‘Ruby 2.5’, description: ‘Blog’ }

To get name and description from this hash.

Before 2.5

blog.select { |key, value| [:name, :description].include?(key) }

=> {:name=>”Ruby 2.5", :description=>”Blog”}

After

blog.slice(:name, :description)

=> {:name=>”Ruby 2.5", :description=>”Blog”}

#4 String#delete_prefix / String#delete_suffix

Earlier in Ruby inorder to delete suffix part we were using the method chomp. However there was no method to delete prefix part this was fixed by adding two methods String#delete_prefix and String#delete_suffix

Usage

Projects::CategoriesController”.delete_prefix(“Projects::”) => “CategoriesController”Projects::CategoriesController”.delete_suffix(“Controller”)

=> “Projects::Categories”

#5 Enumerable#any?, all?, none?, and one? accept a pattern argument

Ruby has sequence predicates such as all?, none?, one? and any? which take a block and evaluate that by passing every element of the sequence to it.

The usage before 2.5 was

queries.any? { |sql| /LEFT OUTER JOIN/i =~ sql }

Ruby 2.5 introduced a shorthand for this, which is as follows.

queries.any?(/LEFT OUTER JOIN/i)

Internally case equality operator(===) is used against every element of the sequence and the pattern argument.

So this translates to

queries.any? { |sql| /LEFT OUTER JOIN/i === sql }

This feature is applicable to all?, none?, one? and any? Methods.

#6 Struct.new can create classes that accept keyword arguments

Before Ruby 2.5 it was not possible to declare Ruby Structs using keyword params. With Ruby 2.5 this can be done using the keyword_init option

Usage:

Struct.new(:fname, :lname, keyword_init: true)my_struct = MyStruct.new(fname: ‘first’, lname: ‘last’)

New Features and Functions added to Ruby 2.6

Features

#1 Just In Time Compiler

Ruby 2.6 introduces an initial implementation of a JIT (Just-In-Time) compiler.

The JIT compiler aims to improve the performance of Ruby programs. Unlike traditional JIT compilers which operate in-process, Ruby’s JIT compiler writes out C code to disk and spawns a common C compiler to generate native code

In order to enable the JIT compiler, specify — jit on the command line or in the $RUBYOPT environment variable. Specifying — jit-verbose=1 will cause the JIT compiler to print additional information

#2 Performance Improvement

Transient Heap support added for ruby reduces the footprint of short-living memory objects which reduced the memory consumption of short living Hash objects by about 7%.

Also, it has been noted that Proc#call is now around 1.4x faster. This is done by removing the temporary allocation for $SAFE. $SAFE is now a process global state and it can be set back to 0

Combined with improvements around block handling introduced in Ruby 2.5, block evaluation now performs 2.6x faster in a micro-benchmark in Ruby 2.6.

#3 Range#=== now uses cover? rather than include?

This makes it possible for case statements like

case DateTime.now 
when Date.today..Date.today + 1
‘win!’
else
‘fail’
End

#4 Bundler is now installed as a default gem.

#5 Syntax Error when else without rescue

In exception handling blocks, else without rescue now causes a syntax error

#6 Constant names may start with a non-ASCII capital letter

Functions

#1 Endless Ranges

Ruby introduces the (0..) range.

ary[1..] # identical to ary[1..-1]
(1..).each {|index| … } # infinite loop from index 1

#2 Array#union and Array#difference

An easier way to find the difference and union multiple arrays.

[1, 1, 2, 2, 3, 3, 4, 5 ].difference([1, 2, 4])

#=> [ 3, 3, 5 ]
[“a”, “b”, “c”].union([“c”, “d”, “a”])

#=> [ “a”, “b”, “c”, “d” ]

#3 Array#filter is a new alias for Array#select

[:foo, :bar].filter { |x| x == :foo } # => [:foo]

#4 Enumerable#to_h now accepts a block that maps keys to values

Before 2.6 in order to convert an array into hash, an intermediate array was required.

(1..5).each_with_object({}) { |x, h| h[x] = x ** 2 }(1..5).map { |x| [x, x ** 2] }.to_h

From 2.6 onwards the to_h allow to use a block which eliminates the intermediate array,

(1..5).to_h { |x| [x, x ** 2] } #=> {1=>1, 2=>4, 3=>9, 4=>16, 5=>25}

#5 Hash#merge, merge! now accept multiple arguments

A variable amount of arguments when merging hashes

hash1.merge(hash2, hash3)

#6 The #then method

The then method is an alias to yield_self. This was introduced to improve readability.

Usage

events = Event.upcoming
events = events.limit(params[:limit]) if params[:limit]
events = events.where(status: params[:status]) if params[:status]
Events

becomes

Event.upcoming
.then { |events| params[:limit] ? events.limit(params[:limit]) : events }
.then { |events| params[:status] ? events.where(status: status) : events }
# or
Event.upcoming
.then(&method(:with_limit))
.then(&method(:with_status))

#7 Random.bytes

This function can be used to generate random bytes

Usage

Random.bytes(8) # => “\xAA\xC4\x97u\xA6\x16\xB7\xC0\xCC”

#8 Binding#source_location

This method returns the source location of the binding, a 2-element array of __FILE__ and __LINE__. It is recommended to use Binding#source_location instead of Kernel#eval

References

https://www.ruby-lang.org/en/news/2018/12/25/ruby-2-6-0-released/

https://www.ruby-lang.org/en/news/2017/12/25/ruby-2-5-0-released/

https://ruby-doc.org/core-2.6.0.preview2/

https://www.reddit.com/r/ruby/comments/9wmpqk/whats_new_in_ruby_26/e9oe3q3/

https://www.rubyguides.com/ruby-version-changes/

https://blog.bigbinary.com/2018/01/02/ruby-2-5-enumerable-predicates-accept-pattern-argument.html

https://blog.bigbinary.com/2017/11/28/ruby-2-5-added-delete_prefix-and-delete_suffix-methods.html

https://bogdanvlviv.com/posts/ruby/new-method-kernel-yield_self-since-ruby-2_5_0.html

https://medium.com/@k0kubun/the-method-jit-compiler-for-ruby-2-6-388ee0989c13

https://www.youtube.com/watch?v=_a4Hpu590AI

--

--