Sponsored Content
The Wayback Machine - https://web.archive.org/web/20200901104001/https://medium.com/rubyinside/whats-new-in-ruby-2-7-79c98b265502

What’s new in Ruby 2.7?

Guy Maliar
Dec 2, 2019 · 6 min read
Image for post
Image for post

Pattern Matching

The best one line definition would be case/when + multiple assignments.

case [0, [1, 2, 3]]
in [a, [b, *c]]
p a #=> 0
p b #=> 1
p c #=> [2, 3]
end
case [0, [1, 2, 3]]
in [0, [a, 2, b]]
p a #=> 1
p b #=> 3
end

Numbered Parameters

Numbered parameter as the default block parameter is introduced as an experimental feature, it allows us to drop the pipe definition in blocks, (|value|) and create shorter blocks with numbered parameters _1, _2, _3 etc.

[1, 2, 10].map { _1.to_s(16) } #=> ["1", "2", "a"]
_1 = 0
[1].each { p _1 } # prints 0 instead of 1, but also warns.
# bad
names.map { |name| name.upcase }
# good
names.map(&:upcase)
# also good?
names.map { _1.upcase }
# Shorter one-liners?hash = {
key_1: "1",
key_2: "2",
}
hash.map { [_1.to_s, _2.to_i] }.to_h
# => { "key_1" => 1, "key_2" => 2 }
# Simpler simple-one-lines that can't be otherwise written?[1, 2, 3].collect { _1 ** 2 }# Shorter definitions of less important variables?h = Hash.new { _1[_2] = [] }
h[:a].push(1)
# Cleaner method chains?HTTP.get(url).body.then { JSON.parse(_1, symbolize_names: true) }# Weird each with objects?[:method_1, :method_2, :method_3].each_with_object(Post.find(1)) { _2.send(_1) }

Argument Forwarding

A nice quality of life feature when we want to pass all the arguments as is,

def foo(...)
bar(...)
end

Beginless Ranges

An experimental feature with still with some concerns, beginless ranges are looked more as a feature for DSL completeness rather than usability. This feature is used mostly for comparison where we’d like to define a condition for “everything below the specified value”. Dates and numbers make the perfect example and even for version strings.

case release_date
when ..1.year.ago
puts "ancient"
when 1.year.ago..3.months.ago
puts "old"
when 3.months.ago..Date.today
puts "recent"
when Date.today..
puts "upcoming"
end

ruby_version_is ..."2.6" do
# ...
end

Flip flop operator is back

A lesser known feature in Ruby is the flip flop operator. It has it’s root coming from Perl but isn’t used as much anymore.

(1..10).each { |i| puts i if (i == 5) .. (i == 8) }
5
6
7
8
=> 1..10

Array#intersection

The equivalent to:

[ 1, 1, 3, 5 ] & [ 3, 2, 1 ]                 #=> [ 1, 3 ]
[ 'a', 'b', 'b', 'z' ] & [ 'a', 'b', 'c' ] #=> [ 'a', 'b' ]
[ 1, 1, 3, 5 ].intersection([ 3, 2, 1 ])                #=> [ 1, 3 ]
[ 'a', 'b', 'b', 'z' ].intersection([ 'a', 'b', 'c' ]) #=> [ 'a', 'b' ]

Enumerable#filter_map

select and map are often used to transform an array while filtering some items out completely.

[1, 2, 3].select { |x| x.odd? }.map { |x| x.to_s }
[1, 2, 3].map { |x| x.to_s if x.odd? }.compact
[1, 2, 3].each_with_object([]) { |x, m| m.push(x.to_s) if x.odd? }
[1, 2, 3].filter_map {|x| x.odd? ? x.to_s : nil } #=> ["1", "3"]

Enumerable#tally

The way to count the amount of repetitions of a specific value in an Enumerable would have been one of the followings:

array
.group_by { |v| v }
.map { |k, v| [k, v.size] }
.to_h
array
.group_by { |v| v }
.transform_values(&:size)

array.each_with_object(Hash.new(0)) { |v, h| h[v] += 1 }
["A", "B", "C", "B", "A"].tally #=> {"A"=>2, "B"=>2, "C"=>1}

Enumerator.produce

Enumerator.produce(initial, &block), will produce an infinite sequence where each next element is calculated by applying block to previous.

require 'date'dates = Enumerator.produce(Date.today, &:next) #=> infinite sequence of dates
dates.detect(&:tuesday?) #=> next tuesday
# easy Fibonacci
Enumerator.produce([0, 1]) { |f0, f1| [f1, f0 + f1] }.take(10).map(&:first)
require 'octokit'Octokit.stargazers('rails/rails')p Enumerator.generate(Octokit.last_response) { |response| response.rels[:next].get }
.first(3)
.flat_map(&:data)
.map { |h| h[:login] }
# => ["wycats", "brynary", "macournoyer", "topfunky", "tomtt", "jamesgolick", ...

Symbol#start_with? / Symbol#end_with?

Just some convenience methods that behave like String#start_with? and String#end_with?

Keyword Arguments spec changes

Keyword arguments assignments spec is changing a little bit with additional warnings and deprecation warnings that will probably be hapenning next year with Ruby 3.

def foo(key: 42); end; foo({key: 42})   # warned
def foo(**kw); end; foo({key: 42}) # warned
def foo(key: 42); end; foo(**{key: 42}) # OK
def foo(**kw); end; foo(**{key: 42}) # OK
def foo(h, **kw); end; foo(key: 42)      # warned
def foo(h, key: 42); end; foo(key: 42) # warned
def foo(h, **kw); end; foo({key: 42}) # OK
def foo(h, key: 42); end; foo({key: 42}) # OK
def foo(h={}, key: 42); end; foo("key" => 43, key: 42)   # warned
def foo(h={}, key: 42); end; foo({"key" => 43, key: 42}) # warned
def foo(h={}, key: 42); end; foo({"key" => 43}, key: 42) # OK
def foo(opt={});  end; foo( key: 42 )   # OK
def foo(**kw); p kw; end; foo("str" => 1) #=> {"str"=>1}

Ruby Inside

Ruby articles and posts

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store