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 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.
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ąć.
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.