The 2.5 series of Ruby first saw the light on October 10th, as part of the first preview release toward Ruby 2.5.0. Just a couple of months later, on Christmas day (a popular release date on the Ruby releases calendar), the first stable version of the series was born.
Reflected on the release notes, we can find a whole bunch of features, changes and performance improvements which found their way to the final release. On this post, I’d like to do a quick rundown of the features that I’ve found most interesting.
Hash#slice
For a long time, Ruby has had built-in support for slicing strings and arrays. The Rails library, ActiveSupport, took this one step further, and extended slicing to hashes.
On that same note, Ruby 2.5.0 is now providing a way for the
programmers to choose the keys of a hash they’re interested in (in
fact, the proposed name for the method was choice
to start with).
Given a hash, slice
will generate a new hash
containing only the keys which were specified in the arguments section.
On Ruby 2.4.3:
irb(main):> "this is a string".slice("is a")
=> "is a"
irb(main):> ["this", "is", "an", "array"].slice(3)
=> "array"
irb(main):> { a: "this", b: "is", c: "a", d: "hash" }.slice(:c, :d)
NoMethodError: undefined method `slice' for {:a=>"this", :b=>"is", :c=>"a", :d=>"hash"}:Hash
from (irb):3
from /Users/me/.rubies/ruby-2.4.3/bin/irb:11:in `<main>''
On Ruby 2.5.0:
irb(main):> { a: "this", b: "is", c: "a", d: "hash" }.slice(:c, :d)
=> {:c=>"a", :d=>"hash"}
Hash#transform_keys
As part of Ruby 2.4, Hash#transform_values
was introduced.
This useful method allows us to transform the values in our hash with the
results of running a block
once for every value.
On Ruby 2.4.3:
irb(main):> { a: "hello", b: "world" }.transform_values {|v| v.upcase }
=> {:a=>"HELLO", :b=>"WORLD"}
irb(main):> { a: "hello", b: "world" }.transform_values(&:upcase)
=> {:a=>"HELLO", :b=>"WORLD"}
In a similar fashion to Hash#slice
, the presence of a transform_keys
alternative on Rails seems to have triggered this
new proposal.
Hash#transform_keys
works the same way, only that it affects hash keys
instead of values. A new hash will be returned with the results of
running the provided block
once for every key.
On Ruby 2.5.0:
irb(main):> { a: "hello", b: "world" }.transform_keys {|k| k.to_s }
=> {"a"=>"hello", "b"=>"world"}
irb(main):> { a: "hello", b: "world" }.transform_keys(&:upcase)
=> {:A=>"hello", :B=>"world"}
Pattern argument on Enumerable methods
An interesting addition in Ruby 2.5.0, is that the Enumerable
module
methods we all know and love, such as any?
, all?
, none?
and one?
, now accept a pattern argument.
This behavior was already present
on the grep
function, so it made sense to extend it to other Enumerable
methods as well.
Let’s take a look at an example to see how it works.
irb(main):> words = ["hello", "fellow", "bye"]
=> ["hello", "fellow", "bye"]
irb(main):> words.any?(/ello/)
=> true
irb(main):> words.all?(/ello/)
=> false
irb(main):> words.one?(/ello/)
=> false
irb(main):> words.one?(/ye/)
=> true
irb(main):> words.none?(/ye/)
=> false
irb(main):> words.all?(/e/)
=> true
If a single pattern argument is provided, the method runs pattern === element
for every collection member. The result will of course depend on the method being used.
On the previous examples, we are matching different regular expression
patterns against each of the strings in our words
array. But, what
else can we match?
Here are some examples:
irb(main):> [1, 2, 3].all?(Numeric)
=> true
irb(main):> [1, 3.14, 42].none?(Float)
=> false
irb(main):> ["Hi", "there", 2].any?(String)
=> true
Given that ===
is implemented on a per class basis, we can get
different behavior when using it with, for instance, Classes or Regexps.
Here, you’ll find more information on the case equality operator ===.
Reverse backtrace
This feature, labeled as experimental on the release notes, has the potential to save you tons of scrolling at the time of inspecting logs or reading error backtraces.
As a Rails developer, I’m used to long backtraces. Given that we tend to read these from bottom to top, it sometimes means that we end up scrolling a lot just to reach the error message we were looking for.
Of course, this wouldn’t be as helpful in the case of short backtraces, but I guess it shouldn’t be a negative change if that’s your normal scenario.
I’m stating it’s experimental status, given that it currently works only for TTY, and it’s actual user experience benefits are widely discussed.
As a way to illustrate the new format, here are a couple of examples:
On Ruby 2.4.3:
irb(main):> raise "Something went wrong!"
RuntimeError: Something went wrong!
from (irb):1
from /Users/me/.rubies/ruby-2.4.3/bin/irb:11:in `<main>'
On Ruby 2.5.0:
irb(main):> raise "Something went wrong!"
Traceback (most recent call last):
2: from /Users/me/.rubies/ruby-2.5.0/bin/irb:11:in `<main>'
1: from (irb):3
RuntimeError (Something went wrong!)
PP by default
The PP
Ruby module, is a very popular pretty-printer for Ruby
objects. Up until now, using it to print nicely formatted objects
enforced you to require the module: require 'pp'
It seems that people got a bit tired of requiring pp all over the place, and ended up proposing its inclusion on the Kernel module.
On Ruby 2.4.3:
irb(main):> cart = [{ items: ["tomato", "banana", "mango"], total: 200 }, { shipping_options: ["two_day", "one_day"]}, { payment_method
s: ["Cash", "Credit Card"]}]
=> [{:items=>["tomato", "banana", "mango"], :total=>200}, {:shipping_options=>["two_day", "one_day"]}, {:payment_methods=>["Cash", "Credit Card"]}]
irb(main):> pp cart
NoMethodError: undefined method `pp' for main:Object
Did you mean? p
from (irb):11
from /Users/me/.rubies/ruby-2.4.3/bin/irb:11:in `<main>'
irb(main):> require 'pp'
irb(main):> pp cart
[{:items=>["tomato", "banana", "mango"], :total=>200},
{:shipping_options=>["two_day", "one_day"]},
{:payment_methods=>["Cash", "Credit Card"]}]
=> [{:items=>["tomato", "banana", "mango"], :total=>200}, {:shipping_options=>["two_day", "one_day"]}, {:payment_methods=>["Cash", "Credit Card"]}]
On Ruby 2.5.0:
irb(main):> cart = [{ items: ["tomato", "banana", "mango"], total: 200 }, { shipping_options: ["two_day", "one_day"]}, { payment_method
s: ["Cash", "Credit Card"]}]
=> [{:items=>["tomato", "banana", "mango"], :total=>200}, {:shipping_options=>["two_day", "one_day"]}, {:payment_methods=>["Cash", "Credit Card"]}]
irb(main):> pp cart
[{:items=>["tomato", "banana", "mango"], :total=>200},
{:shipping_options=>["two_day", "one_day"]},
{:payment_methods=>["Cash", "Credit Card"]}]
=> [{:items=>["tomato", "banana", "mango"], :total=>200}, {:shipping_options=>["two_day", "one_day"]}, {:payment_methods=>["Cash", "Credit Card"]}]
Bottom line
In this rundown, we did a quick tour of some of the new features in Ruby 2.5.0.
In particular, I’m a fan of the little method additions
and enhancements, such as slice
and pattern matching on Enumerable
sequence predicates. Also, the reverse backtrace printing looks promising. I hope
it doesn’t end up being just an experimental and temporary thing.
What are the features or improvements which caught your attention the most?
Please let me know in the comments section!