10 new features in Ruby 2.5

Posted on by Artem Sarkisov

This is a guest post by Junichi Ito (@jnchito). Junichi is a Ruby programmer at SonicGarden.jp, translator of Everyday Rails Testing with RSpec, and one of the most popular Ruby writers/bloggers in Japan

Ruby 2.5.0-preview1 was released on October 10 2017. It introduces lots of new features and performance improvements. I have handpicked 10 new features to go over. I hope this article will help you understand some of the coming changes available in Ruby 2.5!

rescue/else/ensure are allowed inside do/end blocks without begin/end

You can write rescue/else/ensure clauses without begin/end keyword within do/end blocks:

[1].each do |n|
  n / 0
rescue
  # rescue
else
  # else
ensure
  # ensure
end

But you’ll get an error if you use {} blocks:
[1].each { |n|
  n / 0
rescue
  # rescue
else
  # else
ensure
  # ensure
}
#=> SyntaxError: (irb):3: syntax error, unexpected keyword_rescue, expecting '}'
#     rescue
#     ^~~~~~

Top-level constant lookup is removed

In Ruby 2.4, the following code works with a warning:

class Staff; end
class ItemsController; end

Staff::ItemsController
#=> warning: toplevel constant ItemsController referenced by Staff::ItemsController
#=> ItemsController

This is because the top-level constants are defined under Object class, and Ruby tries to look up the superclass of Staff class, and eventually finds ItemsController under the Object class which is a superclass of Staff class. The details were discussed here:

But in Ruby 2.5, Ruby won’t look up superclass. So the previous code fails with error:

Staff::ItemsController
#=> NameError: uninitialized constant Staff::ItemsController
#   Did you mean?  ItemsController

Bundler gem is now bundled

This change is postponed due to a big issue. Please see also:
https://github.com/ruby/ruby/commit/7825e8363d4b2ccad8e2d3f5eeba9e26f6656911

Bundler is bundled as a standard library. So you can use it without gem install bundler!

Print backtrace and error message in reverse order (experimental)

In Ruby 2.5, backtrace and error message are printed in reverse order. For example, in Ruby 2.4:

$ ruby ./test/error_example.rb
./test/error_example.rb:7:in {{EJS1}}method_2'
    from ./test/error_example.rb:2:in {{EJS2}}'

In Ruby 2.5:
$ ruby ./test/error_example.rb
Traceback (most recent call last):
        3: from ./test/error_example.rb:10:in {{EJS3}}method_1'
        1: from ./test/error_example.rb:7:in {{EJS4}}/': divided by 0 (ZeroDivisionError)

As of preview1, this is an experimental feature and applied only when STDERR is unchanged and a tty.

Kernel#yield_self

Kernel#yield_self is introduced. This method takes a receiver as a block argument and returns a block value as it is:

2.yield_self { |n| n * 10 } #=> 20

names = ['Alice', 'Bob']
names.join(', ').yield_self { |s| "(#{s})" } #=> "(Alice, Bob)"

String#delete_prefix/delete_suffix

String#delete_prefix/delete_suffix can remove prefix and suffix from a string:

'invisible'.delete_prefix('in') #=> "visible"
'pink'.delete_prefix('in') #=> "pink"

'worked'.delete_suffix('ed') #=> "work"
'medical'.delete_suffix('ed') #=> "medical"

Array#prepend/append as aliases of unshift/push

Array#prepend/append are added as aliases of unshift/push methods:

array = [3, 4]
array.prepend(1, 2) #=> [1, 2, 3, 4]
array               #=> [1, 2, 3, 4]

array = [1, 2]
array.append(3, 4)  #=> [1, 2, 3, 4]
array               #=> [1, 2, 3, 4]

Hash#transform_keys/transform_keys!

Hash#transform_keys can change keys according to the return value of a block:

hash = { a: 1, b: 2 }
hash.transform_keys { |k| k.to_s }
#=> { 'a' => 1, 'b' => 2 }

transform_keys! is a destructive version:
hash = { a: 1, b: 2 }
hash.transform_keys! { |k| k.to_s }
#=> { 'a' => 1, 'b' => 2 }

hash
#=> { 'a' => 1, 'b' => 2 }

Dir.children/each_child

You might have used Dir.entries method:

Dir.entries('./test/dir_a')
#=> [".", "..", "code_a.rb", "text_a.txt"]

If you want to remove “.” and “..” from the returned values, you can Dir.children instead:
Dir.children('./test/dir_a')
#=> ['code_a.rb', 'text_a.txt']

Dir.each_child returns not an array but an Enumerator object:
Dir.each_child('./test/dir_a')
#=> #<Enumerator: Dir:each_child(\"./test/dir_a\")>"

Dir.each_child('./test/dir_a').to_a
#=> ['code_a.rb', 'text_a.txt']

ERB#result_with_hash

The following code shows how to assign local variables to ERB template in Ruby 2.4:

require 'erb'
require 'ostruct'

namespace = OpenStruct.new(a: 2, b: 3)
template = 'Result: <%= a * b %>'
ERB.new(template).result(namespace.instance_eval { binding }) #=> "Result: 6"

But in Ruby 2.5, you can rewrite with ERB#result_with_hash like this:
require 'erb'

template = 'Result: <%= a * b %>'
ERB.new(template).result_with_hash(a: 2, b: 3) #=> "Result: 6"

Other changes

You can find other changes on the Ruby 2.5 NEWS page: NEWS for Ruby 2.5.0

Love Ruby 2.5!

Comments below can no longer be edited.

2 Responses to 10 new features in Ruby 2.5

  1. Philippe Creux says:

    October 27, 2017

    Thanks for taking the time to sharing that!

  2. Junichi Ito says:

    December 24, 2017

    NOTE: Bundling bundler is now postponed. So as of Ruby 2.5.0, you need to run gem install bundler. Please see also:

    https://github.com/ruby/ruby/commit/7825e8363d4b2ccad8e2d3f5eeba9e26f6656911

Subscribe

Subscribe for updates