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 ofRuby 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!
In “Chomp flag for IO methods”
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
Thanks for your comment ๐
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.I tried this on
2.4.0-preview3
. It also says in the NEWS file that the default behaviour is changing, so I don’t know what to tell you ๐Sorry, didn’t notice there was preview3 already. That’s bonkers!
They’re not changing it now, according to Matz: https://bugs.ruby-lang.org/issues/12958#note-18
Thankfully!
Thanks for letting us know. I will add a note to this post ๐
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 ๐
Not sure about the reasoning, maybe digging into the feature request could bring some insight into that. And yes the unification is important, but didn’t mention it because other blogs have covered that already ๐
I wasn’t familiar with this rule either, until the calculator app PCalc temporarily removed it a few months ago: http://leancrew.com/all-this/2016/10/well-rounded/.
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.
Thanks for your explanation ๐
the reason is to prevent biasing numbers on average upward or downward.
For example if you have a list of numbers that include a lot of x.5 and you were to round all these upward, the average magnitude of the list would also shift upward. Likewise if you round downward, downward.
By rounding to the nearest even number these would balance out, given enough samples.
https://mathematica.stackexchange.com/a/2120/45178
Thank you ๐