Ruby 2.7 Notes
Posted Updated Verified
Here are some of the biggest changes coming out Ruby 2.7
Pattern Matching
Pattern matching uses a case statement combined with the in keyword (instead
of when) to match arrays and hashes. You can also augment with an if or
unless guard.
ruby 2.7 pattern matching pattern_matching.rb
def parse_specific_age json
case JSON.parse(json, symbolize_names: true)
in {name: 'Alice', children: [{name: 'Bob', age: age}]} if age < 5
p "Alice's kid, Bob, is only #{age} years old"
in {name: 'Alice', children: [{name: 'Bob', age: age}]}
p "Alice's kid, Bob, is #{age} years old"
else
p 'No Alice with a child named Bob'
end
end
parse_specific_age '{ "name": "Alice", "children": [{ "name": "Brenda", "age": 20 }] }'
# => No Alice with a child named Bob
parse_specific_age '{ "name": "Alan", "children": [{ "name": "Bob", "age": 20 }] }'
# => No Alice with a child named Bob
parse_specific_age '{ "name": "Alice", "children": [{ "name": "Bob", "age": 6 }] }'
# => Alice's kid, Bob, is 6 years old
parse_specific_age '{ "name": "Alice", "children": [{ "name": "Bob", "age": 3 }] }'
# => Alice's kid, Bob, is only 3 years old
Ruby will try to match the variable with the case statement, and assign any
supplied variables their corresponding values. So, in the first in clause
above, the provided hash must have a :name key with a value of Alice . It
must also have a :children key consisting of an array of hashes. That hash
must have a key of :name with a value of Bob and a key of :age , the value
of which will be assigned to the variable age , if age is less than 5.
Note that the case statement halts after the first match, so the order of the conditions is important. You need to start most specific -> least specific.
In the example above, what if you wanted to match against a value in a variable?
So, instead of looking for the name Bob , we want to match to whatever is
passed in the variable child_name
variable pattern matching variable_pattern_matching.rb
in {name: 'Alice', children: [{name: child_name, age: age}]} if age < 5
Well, the syntax we’ve just described above says that this will match any name,
and assign that name to the variable child_name, just like it assigns the
variable age. That’s not what we want.
So, we pin this variable
in {name: 'Alice', children: [{name: ^child_name, age: age}]} if age < 5
So, now we can do:
def parse_some_age json, child_name
case JSON.parse(json, symbolize_names: true)
in {name: 'Alice', children: [{name: ^child_name, age: age}]} if age < 5
p "Alice's kid, #{child_name}, is only #{age} years old"
in {name: 'Alice', children: [{name: ^child_name, age: age}]}
p "Alice's kid, #{child_name}, is #{age} years old"
else
p "No Alice with a child named #{child_name}"
end
end
parse_some_age '{ "name": "Alice", "children": [{ "name": "Brenda", "age": 20 }] }', 'Brenda'
# => Alice's kid, Brenda, is 20 years old
parse_some_age '{ "name": "Alan", "children": [{ "name": "Bob", "age": 20 }] }', 'Blake'
# => No Alice with a child named Blake
parse_some_age '{ "name": "Alice", "children": [{ "name": "Bob", "age": 6 }] }', 'Bob'
# => Alice's kid, Bob, is 6 years old
parse_some_age '{ "name": "Alice", "children": [{ "name": "Bob", "age": 3 }] }', 'Bob'
# => Alice's kid, Bob, is only 3 years old
This is interesting, as this isn’t just a syntactic sugar change, this is a nice whole new language feature that adds a method of programming that is quite new. Elixir has a very powerful pattern matching syntax a bit like this, and it is used all over the place.
This is entirely new to ruby, though, and I’m interested to see how it is adopted and whether it will change common ruby idioms.
Keyword Arguments
From 2.7, keywords arguments are changing. The old style will be deprecated. This is a SemVer breaking change, and thus, the new style will be mandatory in ruby 3.0.
Old Style
As a recap, the old style keyword arguments was a first-class version of a hash passed as an argument in a method call. If the last argument in a method call was a hash, it was converted into keyword arguments.
ruby 2.6 keyword arguments keyword_arguments_2_6.rb
def old_style_method(regular_param, optional_param: 'value', required_param:)
p "#{regular_param}, #{optional_param} and #{required_param}"
end
old_style_method 'a', required_param: 'c'
# => "a, value and c"
old_style_method 'a', optional_param: 'b', required_param: 'c'
# => "a, b and c"
old_style_method 'a', optional_param: 'b'
# => ArgumentError
These let you pass in named arguments (which is handy for self-documenting your code), without worrying about argument position.
New Style
In Ruby 3, hashes will no longer be converted into keyword arguments. From 2.7 you will get deprecation warnings for all sorts of scenarios where this can happen. You will need to explicitly handle the conversion to keyword arguments.
You should:
Use the splat operators for array (*) and hash (**) to explicitly
deconstruct an array or hash into keyword arguments.
Use {} braces to explicitly pass a hash, so the parser is sure you are not
passing keyword arguments.
ruby 2.7 keyword arguments keyword_arguments_2_7.rb
def keyword_argument_method(foo: 'default')
p foo
end
def hash_argument_method(h={foo: 'default'})
p h[:foo]
end
def long_keyword_argument_method(_, foo: 'default')
p foo
end
def long_hash_argument_method(_, h={foo: 'default'})
p h[:foo]
end
example = { foo: 'hello' }
keyword_argument_method
# Ruby 2.7 => "default"
# Ruby 3.0 => "default"
hash_argument_method
# Ruby 2.7 => "default"
# Ruby 3.0 => "default"
keyword_argument_method example
# Ruby 2.7 =>"hello"
# Ruby 3.0 => ArgumentError
hash_argument_method example
# Ruby 2.7 => "hello"
# Ruby 3.0 => "hello"
keyword_argument_method(example)
# Ruby 2.7 => "hello"
# Ruby 3.0 => ArgumentError
keyword_argument_method(**example)
# Ruby 2.7 => "hello"
# Ruby 3.0 => "hello"
hash_argument_method(foo: "goodbye")
# Ruby 2.7 => goodbye
# Ruby 3.0 => goodbye
hash_argument_method({foo: "goodbye"})
# Ruby 2.7 => goodbye
# Ruby 3.0 => goodbye
long_hash_argument_method('dummy', foo: "goodbye")
# Ruby 2.7 => goodbye
# Ruby 3.0 => goodbye
long_hash_argument_method('dummy', {foo: "goodbye"})
# Ruby 2.7 => goodbye
# Ruby 3.0 => goodbye
long_keyword_argument_method('dummy', foo: "goodbye")
# Ruby 2.7 => goodbye
# Ruby 3.0 => goodbye
long_keyword_argument_method('dummy', {foo: "goodbye"})
# Ruby 2.7 => goodbye
# Ruby 3.0 => ArgumentError
long_keyword_argument_method('dummy', **{foo: "goodbye"})
# Ruby 2.7 => goodbye
# Ruby 3.0 => goodbye
Argument Forwarding
Also, argument forwarding (...)
is a thing that exists.
Numbered Parameters
You can now access arguments passed to a block using implicit numbered arguments, because apparently, that’s more readable.
[1,2,3].map { _1 * 2 }
irb
The irb REPL is also improved: It has syntax highlighting.

See Also
A comprehensive description of all the changes is here.