What's new in Ruby 2.4?
It’s Christmas day, and following the tradition of the last few years, the Ruby core team have released a new Ruby version today. I’ll summarize some of the interesting new features in Ruby 2.4 here.
Previous: Ruby 2.3.
Numbers
Fixnum and Bignum have been unified into Integer class.
So far, we’ve had two classes for storing integers -
Fixnum for small integers, and Bignum for numbers outside this range.
However, these are implementation details
that programmers don’t need to worry about while writing code.
These two classes have been replaced by a single Integer class.
Previously, Integer was a superclass of these two classes,
but now both Fixnum and Bignum are aliases to Integer.
# 2.3
42.class #=> Fixnum
(2**62).class #=> Bignum
# 2.4
42.class #=> Integer
(2**62).class #=> Integer
Fixnum == Integer #=> true
Bignum == Integer #=> true- Feature #12005: Unify Fixnum and Bignum into Integer
- Slides by Tanaka Akira
- Ruby 2.4 unifies Fixnum and Bignum into Integer (BigBinary blog)
New Integer#digits method
42.digits #=> [2, 4]Precision for float modifiers
Float methods like #ceil, #floor, #truncate and #round
take an optional argument to set precision.
1.567.round #=> 2
1.567.round(2) #=> 1.57
123.456.round(-1) #=> 120Float#round default behavior remains unchanged
This one isn’t really a change, but this change in default behavior initially made it to one of the preview releases, and was reverted later on.
By default, #round uses round-half-up behavior, ie. 1.5 would be rounded to 2.
The new behavior was to use banker’s rounding, which rounds half to nearest even number.
This might cause bugs in many existing applications which rely on half-up rounding,
so the original default has been retained.
# suggested behavior
1.5.round #=> 2
2.5.round #=> 2
# actual behavior
1.5.round #=> 2
2.5.round #=> 3Float#round options
Even though the round-to-nearest-even change was reverted,
new options were introduced in Float#round
that allow you to explicitly set what kind of rounding to use.
2.5.round #=> 3
2.5.round(half: :even) #=> 2
2.5.round(half: :down) #=> 2
2.5.round(half: :up) #=> 3binding.irb
I’m a big fan of the pry gem for the binding.pry method
that opens a REPL while running your code.
IRB has now introduced this feature,
and ruby now opens a REPL when it encounters the binding.irb method.
Hash
Hash#compact
This method, and the bang version, #compact!,
remove keys with nil values from the hash.
{ a: "foo", b: false, c: nil }.compact
#=> { a: "foo", b: false }Hash#transform_values
Applies the block for each value in the hash.
Also provides a #transform_values! method that modifies the existing hash.
Examples from the docs:
h = { a: 1, b: 2, c: 3 }
h.transform_values {|v| v * v + 1 } #=> { a: 2, b: 5, c: 10 }
h.transform_values(&:to_s) #=> { a: "1", b: "2", c: "3" }Strings, Symbols and IO
String supports unicode case mappings
Until now, Ruby only performed case conversion on ASCII characters.
The upcase, downcase, swapcase, capitalize methods on String and Symbol
have now been extended to work with unicode characters.
# 2.3
"Türkiye".upcase #=> "TüRKIYE"
"TÜRKİYE".downcase #=> "tÜrkİye"
# 2.4
"Türkiye".upcase #=> "TÜRKIYE"
"TÜRKİYE".downcase #=> "türki̇ye"Specify string buffer size
String.new now allows a capacity argument
to specify the size of the buffer.
This will have performance benefits
when the string will be concatenated many times.
String.new('foo', capacity: 1_000)Symbol#match now works like String#match
Symbol#match used to return the match position,
while String#match returned a MatchData object.
This has been fixed in 2.4 and now both return a MatchData.
# 2.3
:hello_ruby.match(/ruby/) #=> 6
# 2.4
:hello_ruby.match(/ruby/) #=> #<MatchData "ruby">IO#gets and other methods get a chomp flag
You can now add an optional chomp: true flag to
#gets, #readline, #each_line, #readlines and IO.foreach.
# In 2.3, you did this
foo = gets.chomp
# 2.4
foo = gets(chomp: true)Regexp
Regexp#match?
This new method returns true or false without updating the $~ global variable.
Since it doesn’t create a MatchData object or update $~,
it performs better than #match.
/foo/.match?('foo') #=> true
$~ #=> nilRegexp#named_captures
Returns a hash representing information about the named captures.
/(?<fname>.+) (?<lname>.+)/.match('Ned Stark').named_captures
#=> {"fname"=>"Ned", "lname"=>"Stark"}Enumerable
Enumerable#sum
(1..5).sum #=> 15
%w(a b c).sum('') #=> "abc"Files and directories
The #empty? method was added to Dir, File and Pathname.
Dir.empty?('path/to/some/dir') #=> true
File.empty?('path/to/some/file') #=> true
require 'pathname' # Needed to use Pathname class
Pathname.new('file-or-dir').empty? #=> trueLanguage features
Multiple assignment in conditionals
In Ruby 2.3, you would get a syntax error if you tried multiple assignment in a conditional. This has been changed to a warning instead.
# 2.3
if (a,b = [1,2]) then 'yes' else 'no' end
#=> SyntaxError: (irb):9: multiple assignment in conditional
# 2.4
if (a,b = [1,2]) then 'yes' else 'no' end
#=> warning: found = in conditional, should be ==
#=> 'yes'
if (a,b = nil) then 'yes' else 'no' end
#=> warning: found = in conditional, should be ==
#=> 'no'Wrapping up
I haven’t mentioned all the new features in Ruby 2.4 here, but if you’re interested in the complete list of changes, take a look at the Ruby 2.4.0 NEWS file.
Here are some more articles covering the new release:
- Ruby 2.4 Released: Faster Hashes, Unified Integers and Better Rounding on the Heroku blog
- What’s new in Ruby 2.4 on the Jobandtalent Engineering team blog
- Behavior changes in Ruby 2.4 on the Wyeworks blog
- 9 New Features in Ruby 2.4 on the Black Bytes blog