Pracując od kilku miesięcy jako Junior RoR developer poznałem kilka wartych uwagi gemów, z których ciężko byłoby mi już zrezygnować. Postaram się po krótce opisać każdy z nich i przedstawić główne zalety, jakie w nich widzę.

Rubocop

Numer jeden, obowiązkowy gem! Działanie jest proste - po odpaleniu rubocopa, gem sprawdza wszystkie nasze pliki pod kątem tego jakiej jakości piszemy kod. Oczywiście nie jest w stanie sprawdzić tego czy spełniamy SOLID, czy pracujemy w TDD, ale kontroluje to, w jaki sposób piszemy. Zwraca uwagę na bezsensowne warunki if, na źle nazwane namespace’y, na złe praktyki w pisaniu metod (nie mogą być zbyt długie), na zbędne returny na końcu funkcji, i wiele, wiele innych. Przyznam, że wiele razy, dzięki niemu kod, który pisałem, stawał się o wiele czytelniejszy i zrozumiały.
Bonusowo możemy użyć rubocopa jako pluginu do VScode, czy innego edytora, który po każdym zapisaniu pliku, sprawdzi jego poprawność. Osobiście korzystam z tego rozwiązania w połączeniu z Solargraphem, co sprawia, że pisanie kodu Rubiego w VScode jest bardzo wydajne i przyjemne.

DecentExposure

DecentExposure jest świetnym, upraszczającym pracę narzędziem. Umożliwia ‘eksponować’ (nie bardzo tłumaczenie tego słowa ma tu sens) zmienne, lub zasoby z kontrolerów, których potrzebujemy w widokach. A więc zamiast pisać:

class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end

Możemy wykorzystać następujący kod.

class PostsController < ApplicationController
  expose :posts, -> { Post.all }
end

Przy okazji tego przykładu ujawnia się kolejna zaleta DecentExposure - jeśli posiadamy plik widoku o takiej samej nazwie jak metoda, która zostaje wywołana pod określoną przez router ścieżką, możemy całkowicie pominąć jej definiowanie. Mówiąc prościej - jesli w folderze app/views/ mamy plik index.html.haml/.erb, nie musimy pisać def index; end w klasie PostsController. Natomiast w widoku, aby skorzystać z :posts, używamy jej jak zwykłej zmiennej:

- posts.each do |post|
%h2= post.title
%p= post.body

W taki sam sposób możemy korzystać z ‘eksponowanych’ zasobów w widokach Mailerów.

Cocoon oraz SimpleForm

Prędzej czy później w Twojej aplikacji pojawią się formularze z dynamicznie dodawanymi polami, z modelami z zagnieżdżonymi w nich innymi modelami, itd… W tej sytuacji pomoże Cocoon. Korzystając z accepts_nested_attributes_for możemy w jednym formularzu wysyłać ‘zagnieżdżone’ dane do modeli, które są w jakiejś relacji z rekordem, który właśnie wyświetlamy. Widać to na przykładzie z dokumentacji RoR - model Book pozwala na przesłanie ‘przez niego’ wartości dla modeli Author oraz Pages

class Book < ActiveRecord::Base
  has_one :author
  has_many :pages

  accepts_nested_attributes_for :author, :pages
end

A więc w tej sytuacji, by umożliwić odbieranie tych danych przez kontroller napisalibyśmy mniej więcej taki kod w funkcji book_params

class BooksController < ApplicationController
[...]
  private
  
  def book_params
    params.require(:book).permit(:title, :isbn, :author_id, author_attributes: [:id, :name, :_destroy], pages_attributes: [:id, :_destroy])
  end

Czymże jest :_destroy wyjaśnie za chwilę, warto najpierw zwrócić uwagę jak zbudować formularz, by wysyłał porządane przez nas parametry. W tym miejscu skorzystamy z SimpleForma, ponieważ sprawia, że budowanie formularzy staje się bajecznie proste.

= simple_form_for book do |f|
  = f.input :title
  = f.input :isbn
  = f.association :author, { collection: authors }
  = simple_fields_for author do |af|
    = af.input :name
  = simple_fields_for pages do |pf|
    ...

Tak przygotowany formularz wyśle wypełnione pola w następującej strukturze:

:book => { :title => 'Tytuł', :isbn => '21421-312321-241', :author_id => 12,
           :author_attributes => { :name => 'Franz Kafka' }, :pages_attributes => [{}, {}, {} }

Dzięki poprzednio przygotowanemu modelowi Book za jednym zamachem zapiszemy w bazie danych wszystkie dane, dla różnych modeli.

Wróćmy zatem do Cocoona i pola :_destroy. Cocoon wyposaża nas w gotowe komponenty (a dokładniej dwie funkcje generujące gotowe przyciski) do dodawania nowych pól oraz usuwania już istniejących. A więc jeśli mamy książkę (Book), a do niej przypisane kilka stron (pages), możemy wyświetlić to w postaci takich właśnie pól z buttonami do usuwania relacji.
Klikając w button ‘usuń’, dzięki gemowi oznaczamy ukryte pole :_destroy, dzięki któremu Railsy wiedzą, że chcemy tą relacje usunąć.

I wiele, wiele więcej

Jest jeszcze wiele gemów, które bardzo się przydają w codziennej pracy, jednak nie sposób wymienić wszystkie i odpowiednio opisać. Będzie to dobry materiał na kolejnego posta w przyszłości.