Last week came out a
preview of Ruby 2.3.0 . What is interesting from the changes:
- safe navigation operator in objects (safe navigation operator), # 11537
- The
dig
method has been added to Hash and Array to access the depth of nested hashes or arrays. # 11688 - movement towards default (frozen) string literals ( justification , discussion # 11473
Safe navigation operator
In Ruby, a safeguarding operator appeared, similar to the operator '?.' in Groovy and some other languages. The operator is used to shorten expressions, where you check the existence of an object and then refer to the object's method only in the case of a positive test:
obj.nil? && obj.some_method
If a chain of objects and methods is used, then the check may look cumbersome and several times useless to perform intermediate methods. For example, a typical case for Ruby on Rails to securely retrieve a large profile picture:
')
image = user && user.profile && user.profile.thumbnails && user.profile.thumbnails.large
Here the
profile
method will be executed three times and the
thumbnails
method will be executed twice. The optimized version will look even harder:
image = user && (profile = user.profile) && (thumbnails = profile.thumbnails) && thumbnails.large
At the same time, the result may not be entirely correct, if one of the objects in motion will be absent - then the image will be
false
, not
nil
. And if you check for
nil?
then the code will look even more confusing.
There is also a way to move deeper into the chain without checking, catching an exception, for example, in the usual way:
image = begin user.profile.thumbnails.large rescue NoMethodError nil end
or causing general disapproval of postfix rescue:
image = user.profile.thumbnails.large rescue nil
ActiveSupport to solve this problem delivers
try
and
try!
methods
try!
:
image = user.try(:profile).try(:thumbnails).try(:large)
These methods are added to the
Object
class and when they are called, they first check for the existence of the called method and, if it is absent, return
nil
. If the
user
has the
#profile
method, it will be executed and on its result further along the chain will be called
try(:thumbnails)
. If
user
is
nil
, then
#try
will return
nil
and so on the chain
#try
will be called from
nil
two more times. Slow? But short.
What does the newly appeared safe navigation operator in Ruby
According to task
# 11537 for safe navigation in Ruby 2.3.0, the '.?' Operator was added, changed later to '&.'. In the new syntax, the expression from the examples can be written as:
image = user&.profile&.thumbnails&.large
Together with a concise look, such an implementation gives a quick check for
nil
, since the changes are implemented at the parser level and the ruby ​​code is not involved in the checks. After
nil
encountered, further execution of the chain is interrupted. The check is performed precisely on
nil
, and not on a logical condition, so if the result is
false
, then the execution will continue successfully along the chain.
If arguments are passed to the method, then, in contrast to
try
, they are calculated only if the object exists and the method is actually called. For example, for ActiveSupport, the
obj.try(:foo, bar())
expression will always execute
bar()
, even if
obj
does not exist. But in the
obj&.foo(bar())
expression, the
obj&.foo(bar())
argument will be evaluated only when
ojb
not
nil
.
Safe navigation can also be used when assigning a value to an attribute:
obj&.attr += 1
From the moment of the appearance of the feature request to its confirmation and commit with the first implementation variant, only slightly more than one month passed.
Navigation into embedded hashes and arrays with #dig.
Feature # 11688 adds the
dig
method to
Hash
and
Array
, which is used to safely derive values ​​from nested hashes and arrays. This method replaces the expression:
value = hash[:a].nil? ? nil : hash[:a][:b].nil? ? nil : hash[:a][:b][:c]
or:
value = hash[:a][:b][:c] rescue nil
on:
value = hash.dig(:a, :b, :c)
Access to nested hashes and arrays is often used when processing the parameters of HTTP requests received by Rails applications, or when working with YAML or JSON structures. Adding
dig
to the depth not only provides a convenient access method, but also
accelerates it several dozen times.
The
dig
method was also recently added to
Struct
, but did not have time to get into the first preview 2.3.0.
String immutability
In Ruby 3, all string literals will be unchanged. Regarding the immutability of the disputes are long and now the movement in this direction has become more specific. It is expressed in the appearance of the “magic” commentary
frozen-string-literal
, whose presence includes by default immutability for all string literals, and in the addition -
--enable/--disable=frozen-string-literal
to control this behavior.
The main argument in favor of immutable strings is an increase in the speed of work due to internal optimizations. In most cases, strings remain unchanged throughout the life cycle, and fixing this behavior improves performance without changing the code.
And we have a small survey
We have prepared a hosting of Ruby applications in
Jet9 containers. Both on
cloud hosting and
failover cluster .
One of the reasons why Ruby is the first to appear on Jet9 is to unify our own internal services and our customers' internal services (websites, billing, workflow, bugtracker, tickets, etc.). To simplify our life and reduce the cost of updating and maintaining the zoo from different distributions, HA-clusters with different configurations and individual physical servers, we transfer everything to a couple of typical HA-clusters on the Jet9 platform. Most of it is written in Ruby — its own applications and third-party applications (Redmine, Gitlab) are used. Thus, we have done support for Ruby on Jet9, including for ourselves, we check it for ourselves.
Applications run under Nginx + Apache + mod_passenger (5.0.21), this is the most convenient way. But you can use standalone Passenger or other application servers (Unicorn, Puma). Versions 2.2.2 and 2.2.3 are now available, and others are being prepared. In this regard, the survey: