Ruby 2.5: rescue w blokach i szybszy boot
Ruby 2.5.0 ląduje w Boże Narodzenie z garścią praktycznej ergonomii — rescue/ensure wprost w blokach do/end, yield_self, Hash#slice — plus szybszy start i przekazywanie bloków. Praktyczny przegląd.
Ruby 2.5.0 przybyło w Boże Narodzenie, kontynuując wzorzec ostatnich wydań: garść małych, praktycznych usprawnień ergonomii, których użyjesz od razu, plus cicha praca nad wydajnością pod maską. Nic tutaj nie przekształca języka, ale kilka zmian usuwa codzienne tarcie, a zyski wydajności są darmowe. Oto przegląd.
rescue i ensure wprost w blokach
Najbardziej mile widziana zmiana sprząta coś, co irytowało rubistów latami. Wcześniej, by złapać
wyjątek wewnątrz bloku do...end, potrzebowałeś jawnego wewnętrznego begin:
# przed 2.5 — niezgrabny wewnętrzny begin
[1, 2, 3].each do |n|
begin
risky(n)
rescue => e
log(e)
end
end
2.5 pozwala umieścić rescue, else i ensure wprost w bloku, bez potrzeby begin —
dokładnie jak w ciele metody:
# Ruby 2.5 — rescue wprost w bloku
[1, 2, 3].each do |n|
risky(n)
rescue => e
log(e)
end
To drobiazg, ale bloki są wszędzie w Ruby, a obsługa błędów w nich jest częsta, więc to usuwa powracającą małą brodawkę i dodatkowy poziom wcięcia. Czyni też blok czytanym jak metoda, co jest właściwym modelem myślowym.
yield_self: rura dla dowolnego obiektu
Po cichu potężnym dodatkiem jest Object#yield_self, który podaje odbiorcę do bloku i zwraca
wynik bloku. Pozwala łączyć transformacje na wartości bez tymczasowych zmiennych, w potoku od
lewej do prawej:
# bez tego — zmienna tymczasowa, albo zagnieżdżone wywołania czytane od środka
url = build_url(params)
response = fetch(url)
data = parse(response)
# z yield_self — czytelny potok
data = params.yield_self { |p| build_url(p) }
.yield_self { |u| fetch(u) }
.yield_self { |r| parse(r) }
To wersja operatora pipe w Ruby i błyszczy, gdy chcesz wyrazić „weź tę wartość, potem zrób to,
potem to” jako płynący łańcuch, a nie stos tymczasowych albo zagnieżdżenie od środka. (Nazwa jest
trochę toporna — krótszy alias, then, przybywa w późniejszej wersji — ale zdolność jest tu
teraz i naprawdę użyteczna.)
Hash#slice i spółka
Trio małych metod usuwających boilerplate, który pisałeś sto razy. Hash#slice zwraca hash z
tylko danymi kluczami — idealne do białolistowania:
params.slice(:name, :email) # tylko te klucze, reszta porzucona
Obok niego String#delete_prefix i String#delete_suffix robią dokładnie to, co mówią,
zastępując kapryśne regexy sub(/\A.../, ""):
"image.png".delete_suffix(".png") # => "image"
"/api/users".delete_prefix("/api") # => "/users"
Żadne z nich nie jest głębokie, ale każde zastępuje mały, lekko podatny na błędy idiom jasną metodą — rodzaj poloru, który czyni codzienny kod lepiej czytelnym.
Wydajność: szybszy boot i szybsze bloki
Pod maską 2.5 wnosi dwie wygrane wydajności warte odnotowania. Start jest szybszy — require
i ładowanie biblioteki standardowej są zoptymalizowane, więc twoja aplikacja bootuje zauważalnie
szybciej, co ma znaczenie dla zestawu testów, konsoli i wszystkiego, co często startuje procesy
(CI, deploye, obciążenia w stylu serverless). I przekazywanie bloku do metody jest znacząco
szybsze — wywołanie bloku to gorąca ścieżka w idiomatycznym Ruby (każdy each, map,
reduce), więc zoptymalizowanie go to szeroka, darmowa poprawa w niemal całym kodzie Ruby. Jak w
poprzednich wydaniach, aktualizujesz się, a twój istniejący kod działa trochę szybciej za nic.
Mniejsze dodatki
Dopełniając: Struct.new zyskuje keyword_init: true, byś mógł zbudować struct argumentami
nazwanymi (Point.new(x: 1, y: 2)) zamiast polegać na kolejności pozycyjnej; Dir.children i
Dir.each_child listują zawartość katalogu bez wpisów . i .., które zawsze musiałeś
filtrować; pokrycie gałęzi jest dodane do narzędzi pokrycia, byś mógł mierzyć nie tylko które
linie, ale które gałęzie ćwiczą twoje testy; i jest zwykła runda deprecjacji oraz porządków
biblioteki standardowej. Wyszukiwanie stałej najwyższego poziomu (::SomeConstant spadające do
najwyższego poziomu z wnętrza przestrzeni nazw) zostaje usunięte jako deprecjonowane zachowanie,
co może zaskoczyć starą bazę kodu przy aktualizacji — warte grepa.
Czy aktualizować?
Tak, jak zwykle — to niskoryzykowne i szeroko korzystne. rescue na poziomie bloku to zmiana,
którą poczujesz codziennie; yield_self, Hash#slice i metody string/dir to małe wygody, które
przyjmiesz naturalnie; a szybszy boot i przekazywanie bloków to darmowe wygrane wydajności dla
dowolnej aplikacji, z poprawą bootu szczególnie mile widzianą dla cykli testów i CI. Jedynym
zastrzeżeniem aktualizacji jest usunięte wyszukiwanie stałej najwyższego poziomu, łatwe do
sprawdzenia.
Myśl przewodnia ostatniego Ruby jest stabilna i uspokajająca: każdego roku garść ostrych małych usprawnień ergonomii czyniących język przyjemniejszym do pisania, plus cicha praca nad wydajnością czyniąca kod, który już masz, szybszym. 2.5 to podręcznikowy przykład — bez dramatu, po prostu język, który robi się przyjemniejszy i trochę szybszy każdego Bożego Narodzenia.