9 New Features in Ruby 2.4

It has become a tradition to release new Ruby versions on Christmas.

And in this post I want to cover some of the most interesting changes in Ruby 2.4 so you can keep up with the news ๐Ÿ™‚

Float#round with keyword

If you are using floats in your app I hope you use floor or ceil for rounding, because the Float#round method is changing its default behavior in Ruby 2.4.

Example:

# Ruby 2.3
(2.5).round
3

# Ruby 2.4
(2.5).round
2

The default behavior is now to “round-to-nearest-even”.

UPDATE: The default behavior for Float#round is back to “rounding up” in the final version of Ruby 2.4. This was a decision taken by Matz after this post was originally published.

In addition, Float#round now takes an argument which you can use to define the type of rounding you want.

The options are:

  • :even
  • :up
  • :down

Example:

(4.5).round(half: :up)
5

Also Float#floor, Float#ceil & Float#truncate now take an optional argument that lets you set a precision.

Chomp flag for IO methods

If you ever used a method like gets or each_line I’m sure you remember having to deal with those pesky new line characters.

The ones that looks like this: \n.

This is something beginners always have trouble with, but this new feature might be able to help!

Here’s an example:

input = gets.chomp
# "abc\n"

Now in Ruby 2.4 you will be able to set the chomp keyword argument & gets will remove the newline character for you.

Example:

input = gets(chomp: true)
# "abc"

This will work with any value that’s not false or nil (the only “falsy” values in Ruby).

So this also works (but not recommended because it can be confusing):

input = gets(chomp: 1234)
# "abc"

It’s not a super big deal, but it can save you a method call ๐Ÿ™‚

Pathname#empty?

Ruby 2.4 implements Dir#empty? & File#empty?, these methods let you check if a directory or a file is empty (that was pretty obvious, right?).

But Pathname#empty? was also added recently.

In case you are not familiar with Pathname, it’s a class that merges together functionality from both the Dir class & the File class.

In addition, it’s also more “OO” (Object Oriented) in the sense that it returns Pathname objects, instead of strings.

Example:

Pathname.empty?("file or directory name")

Commit: https://github.com/ruby/ruby/commit/9373c5efb993dd8cae0526118805449b19af2c22

Set compare_by_identity

Sets are a data structure that is available as part of the standard library. It helps you keep a collection of unique items.

By default the objects are compared based on their value (or to be more accurate, based on their hash value).

But in Ruby 2.4 it’s possible to have a set of unique objects based on their object id.

Example:

require 'set'

# Normal set

set = Set.new

set << 123 << 123 << "abc" << "abc"
# [123, "abc"]

# Identity set

set = Set.new().compare_by_identity

set << 123 << 123 << "abc" << "abc"
# [123, "abc", "abc"]

If anyone knows of any interesting use for this feature leave a comment ๐Ÿ™‚

Commit: https://github.com/ruby/ruby/commit/76977611dd68e384fdce8c546efda5e1931e67a6

Refinements with Kernel#send, BasicObject#send, Symbol#to_proc

I'm not going to cover refinements in detail here, but the basic idea is to be able to add methods to a class like String, while keeping this change "localized" to one file or class.

Since Ruby 2.4, methods defined via refinements will be available when called via methods like Kernel#send & Symbol#to_proc.

Example:

module TenTimes
  refine String do
    def ten_times
      puts self * 10
    end
  end
end

class Thing
  using TenTimes

  "abc".send(:ten_times)
end

If you try this in 2.3 or below you will get an 'undefined method' error.

Commit: https://github.com/ruby/ruby/commit/35a29390197750abf97ef16fa0740e377764daef

Hash#transform_values

Here's another method extracted from Rails & coming directly to Ruby. I'm talking about Hash#transform_values, which works in a similar way to Array#map.

Example:

h = {a: 1, b: 2, c: 3}

h.transform_values { |v| v * 10 }
# {a: 10, b: 20, c: 30}

There is also Hash#transform_values! if you need in-place mutation.

Kernel#clone now takes an optional keyword argument

As you may know, it's possible to make a copy of a Ruby object. This is useful because most Ruby objects are mutable & you may want to avoid making changes to the original object.

We have two methods for making an object copy:

  • clone
  • dup

There are a few small differences between clone & dup, but for this post let's just say that clone keeps the "frozen" status of the original object while dup does not.

New in 2.4 is the ability to call clone with a "freeze" flag.

Example:

foo = "test".freeze
boo = foo.clone(freeze: false)

boo.frozen?
# false

I'm not sure to what extend this is useful, but who doesn't want more options ๐Ÿ™‚

Commit: https://github.com/ruby/ruby/commit/320ae01c5fb091eab0926c186f304a9caeda1ace

Thread.report_on_exception

Another feature coming with 2.4 is Thread.report_on_exception. This was proposed because Thread exceptions are silent by default, and this can hide problems with your code.

The default value is false, but if your app is using Threads you should try enabling this when upgrading to Ruby 2.4.

Example:

Thread.report_on_exception = true

t1 = Thread.new do
  puts  "In new thread"
  raise "Exception from thread"
end

sleep(1)
puts "In the main thread"

This will show the exception, but it will not crash your program. The alternative is Thread.abort_on_exception, which has always been available.

Notice that there is also an instance version of this method, so you can set this option per-thread instead of all threads.

Binding#irb

Are you a fan of using binding.pry for debugging? Well now we also have binding.irb which works in a similar way.

But since it's irb you still don't get all the nice things that pry gives you, like the syntax highlighting.

Better than nothing if for some reason you don't have access to pry ๐Ÿ™‚

Commit: https://github.com/ruby/ruby/commit/493e48897421d176a8faf0f0820323d79ecdf94a

Conclusion

This new version of Ruby brings in many interesting features, so make sure to check it out when it's available.

Don't forget to share this post so more people can learn about these new features in Ruby 2.4!

14 thoughts on “9 New Features in Ruby 2.4”

  1. In “Chomp flag for IO methods”

    input = gets.chomp

    “abc\n”

    The chomp would remove the “\n”. It’s probably better to show first the example without the chomp and then with the chomp removing the newline. ๐Ÿ™‚

    https://repl.it/Eit4/0

  2. You made a mistake with (2.5).round – it will still return 3 in 2.4.0-preview2. I can’t imagine Ruby team deciding to change the default behaviour for such an important method. It would lead to massive amount of bugs.

  3. Wow, โ€œround-to-nearest-evenโ€ sounds really strange to me. What’s the reasoning?

    In my view unifying Fixnum and Bignum into Integer is an important thing as well ๐Ÿ™‚

  4. It avoids “sign bias” that can make some calculations on rounded numbers head significantly away from zero compared to the same calculations on unrounded numbers or rounded versions of numbers that are very close. For example looking at the list [1.5, 2.5, 3.5, 4.5]:

    The average of those numbers is 3.
    The average of those numbers after rounding each towards even is 3.
    If you subtract 0.00001 from each number and then round them away from zero, the average is 3.

    However:
    * The average of those numbers after rounding each away from zero is 3.5.

Comments are closed.