Skip to content
← Wszystkie wpisy
4 min czytania Dawid Skłodowski

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.