All(most all) you need to know about Ruby 2.7

All(most all) you need to know about Ruby 2.7

It's Christmas day and as per the tradition the Ruby core team will release has released Ruby 2.7 today. Ruby 2.7 is last release before the big major version upgrade to Ruby 3.0 coming up in 2020. Ruby 2.7 has lot of interesting features, some changes that you may not like, some changes that you will definitely like. It also builds the foundation for Ruby 3 by introducing warnings, deprecations and experimental features. In this post, you will learn everything that you need to know about Ruby 2.7

Improved IRB

The IRB now supports syntax highlighting, inline editing of methods. It also supports auto completion. It auto indents the code as we type.

Numbered parameters

We can access the arguments passed to the block by using _1 _2 etc.

[1, 2, 10].map { _1.to_s(16) } #=> ["1", "2", "a"]

I found out that this feature seems to be really useful when there is only argument being passed to the block.

For eg.

[1,2,3].map { _1 * 3 }
=> [3, 6, 9]

You can also use it with blocks which get multiple arguments.

[1,2].each_with_index { p _1 }
1
2
=> [1, 2]
[1,2].each_with_index { p _2 }
0
1
=> [1, 2]

Discussion -

https://bugs.ruby-lang.org/issues/4475

https://bugs.ruby-lang.org/issues/15723

Pattern matching

Pattern matching is added to Ruby 2.7 as experimental feature. We can use it in the case statements.

case [0, [1,2,3]]
  in [a, [b, *c]]
    p a
    p b
    p c
end
(irb):6: warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!
0
1
[2, 3]

It also supports hash destructuring.

params = { name: "john", email: "[email protected]" }
case params
  in { name: a, email: b }
    p a
    p b
end
(irb):18: warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!
"john"
"[email protected]"
=> "[email protected]"

We get the warning that this feature is experimental :) Also Matz mentioned in his 2019 Rubyconf keynote that pattern matching is slow. But we can still give it a try!

Discussion -

https://bugs.ruby-lang.org/issues/14912

Argument forwarding

This is really cool feature. If you want to just pass all the arguments of the current method to a new method, Ruby 2.7 makes it dead easy with the help (...).

def hello(...)
  Greeter.new.hello(...)
end
  
The paranthesis around ... are mandatory.

Discussion -

https://bugs.ruby-lang.org/issues/16253

Manual compaction of Ruby's GC

A new method GC.compact is introduced to compact Ruby's GC so as to reduce the heap fragmentation. This operation runs a full GC, compacts the heap and then runs full GC again. The GC.compact method can be called by library authors to compact the GC. Application developers mostly will not need to call it as part of the application code. For eg. Puma now compacts GC just before the fork.

Aaron's talk linked below on this topic is worth checking out to understand how this feature works and what problems it tries to solve.

Discussion -

https://bugs.ruby-lang.org/issues/15626

Benchmark -

https://gist.github.com/tenderlove/99112e9fcc85d9c6c7d9d0ea40063fc6

Aaron's talk from Rubyconf 2019 -

Introducing FrozenError#receiver

FrozenError is raised when we try to modify a frozen object. The receiver method will return the object on which modification was attempted. This will help in debugging.

In the docs it is mentioned that when you are raising FrozenError, the second argument can be passed as the receiver object but I am getting an error if I pass second argument to FrozenError on Ruby 2.7.0-rc2.

FrozenError.new("error message", "receiver")

Traceback (most recent call last):
        5: from (irb):68
        4: from (irb):69:in `rescue in irb_binding'
        3: from (irb):69:in `new'
        2: from (irb):69:in `initialize'
        1: from (irb):69:in `initialize'
ArgumentError (wrong number of arguments (given 2, expected 0..1))

Introducing Module#const_source_location

Method#source_location is my favourite debugging method. It returns the location where a method is defined. Similarly, Module#const_source_location will return location where the constant is defined.

Repo.const_source_location(:CLASS_FOR_DOC_LANGUAGE)
=> ["/Users/prathamesh/Projects/sources/codetriage/app/models/repo.rb", 23]

Discussion -

https://bugs.ruby-lang.org/issues/10771

Improvements to JIT

There have been improvements made to the JIT compiler which introduced in Ruby 2.6. You can run your Rails application with JIT by adding a flag like this:

https://github.com/codetriage/codetriage/commit/b0c4f5b1f91e3960f26ca92ecf9a1d3cb3bfb926#diff-5bd6b85c2d6fc987875f4bf82de2a15a

Real keyword arguments

Biggest change in Ruby 2.7 which has maximum impact in all of our existing code is with the way keyword arguments will be handled in Ruby going forward.

Before Ruby 2.7, the keyword argument is a normal argument that is a Hash object and is passed as the last argument. In Ruby 3, a keyword argument will be completely separated from normal arguments like a block parameter that is also completely separated from normal arguments. In Ruby 2.7 a warning is displayed when a normal argument is used instead of keyword argument. If you try to run a Rails app on Ruby 2.7, your terminal will be filled with warnings such as:

/Users/prathamesh/.rbenv/versions/2.7.0-rc2/lib/ruby/gems/2.7.0/gems/activerecord-6.0.2/lib/active_record/type.rb:27: warning: The last argument is used as keyword parameters; maybe ** should be added to the call
/Users/prathamesh/.rbenv/versions/2.7.0-rc2/lib/ruby/gems/2.7.0/gems/mustermann-1.0.3/lib/mustermann/ast/compiler.rb:43: warning: The last argument is used as keyword parameters; maybe ** should be added to the call

This change is causing the logs flooded with these warnings. This change is explained here with possible solutions. If you are using Rails, there are lot of warnings related to this change. But they are being fixed one by one. We will have to wait for a Ruby 2.7 compatible Rails release to get rid of all of the warnings.

But if you want, you can get rid of most of these warnings at this very moment as well. Check out how to do it - https://prathamesh.tech/2019/12/26/managing-warnings-emitted-by-ruby-2-7/

And much more..

There are so many changes that I have not covered in this blog post. Honourable mentions are Enumerable#tally, Enumerable#filter_map, begin and endless ranges. For full list of changes, checkout this.

Bonus

Here is a list of gems which have fixed the warnings from Ruby 2.7.

If you are on Heroku, you can give Ruby 2.7 try at this very moment because it is already available 🥳 https://blog.heroku.com/ruby-2-7-0-holiday-release

If you are using chruby or rbenv or rvm, Ruby 2.7.0 is available for installation. Ruby core team has provided a docker image for Ruby 2.7.0 as well. You can use it as follows:

docker run -it --rm rubylang/ruby irb

Ruby 2.7.0 is also available on Travis CI and Circle CI.

Happy holidays and happy hacking with Ruby 2.7!

Show Comments
>