Depois de meses desenvolvendo em Typescript com NestJS por demanda do meu atual trabalho, consegui um tempinho para meu tão amado Ruby on Rails. Melhor ainda agora, como não trabalho oficialmente com Rails, posso me dar o luxo de me aventurar na versão 7, recém lançada em modo alfa, em meus projetos pessoais.
OBS: Pretendo escrever alguns artigos sobre minhas cabeçadas com NestJS e Typescript. Alguns temas que pretendo abordar é TypeOrm com migrations e aplicação Multitenant usando PostgreSQL RLS.
Neste tutorial eu pretendo desenvolver (e aprender) utilizando Rails 7, esbuild, Tailwind e Hotwire (Turbo e Stimulus), mas meu foco será mais sobre o pacote Hotwire e como ele pode nos ajudar. Conforme avanço nos estudos e na implementação, vou complementando este tutorial. Por enquanto temos:
O pano de fundo é uma aplicação estilo Kanban, com um quadro em que podemos incluir, ver, editar e excluir os cards/tarefas e isso ser persistido simultaneamente via websockets para todas as sessões abertas da aplicação. Todo código esta disponível neste repositório. Note que incluí algumas branches que representam as partes abordadas aqui.
Nesta parte inicial, explico como configurar o Rails 7, com suas novas opções, e como “dockerizar” os bancos de dados PostgreSQL e Redis. O resultado final desta etapa é o disponível na branch blog-part-0.
Utilizei as seguintes versões para este projeto:
$ ruby -v
# ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [x86_64-linux]
$ rails -v
# Rails 7.0.0.rc1
$ yarn -v
# 1.22.17
Criei um novo projeto com o seguinte comando:
$ rails new LearningHotwire \
-d=postgresql \
--skip-test \
-j esbuild \
--css tailwind
Nada de novo nas duas primeiras flags: será uma aplicação com banco de dados PostgreSQL
(-d=postgresql
) e sem o pacote padrão de testes com Minitest (--skip-test
).
Nas duas últimas temos algumas novidades: escolho o esbuild
como JavaScript bundler (-j esbuild
) e o tailwind
como CSS processor/framework (--css tailwind
). Estas novas flags correspondem as gemas
jsbundling-rails e
cssbundling-rails, incluídas
automaticamente no Gemfile.
No Gemfile eu removi os comentários e inclui algumas gemas ficando assim:
# Gemfile
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby "3.0.3"
gem "rails", "~> 7.0.0.rc1"
gem "cssbundling-rails", ">= 0.1.0"
gem "jbuilder", "~> 2.7"
gem "jsbundling-rails", "~> 0.1.0"
gem "pg", "~> 1.1"
gem "puma", "~> 5.0"
gem "redis", "~> 4.0"
gem "sprockets-rails", ">= 3.4.1"
gem "stimulus-rails", ">= 0.7.3"
gem "turbo-rails", ">= 0.9.0"
gem "bootsnap", ">= 1.4.4", require: false
group :development, :test do
gem "byebug"
gem "rspec-rails", "~> 4.0.0"
end
group :development do
gem "web-console", ">= 4.1.0"
gem "foreman", require: false
gem "rubocop", require: false
gem "rubocop-packaging", require: false
gem "rubocop-performance", require: false
gem "rubocop-rails", require: false
gem "rubocop-rspec", require: false
gem "rubycritic", require: false
end
O Redis vem comentado automaticamente, mas vamos utilizá-lo mais a frente para o ActionCable com o turbo-rails.
Nos blocos de :development
e :test
inclui algumas gemas para teste e lint. Não sei ainda se vou
ou não desenvolver testes, mas na dúvida instalei o RSpec com o comando padrão
rails generate rspec:install
e inclui arquivos de configuração como
.reek.yml,
.rubocop.yml,
.rubycritic.yml,
entre outros.
Para facilitar o desenvolvimento eu “dockerizei” o PostgreSQL e Redis com o seguinte docker-compose.yml:
# docker-compose.yml
version: '3.4'
services:
db:
image: postgres:14-alpine
container_name: learhot-db-ctr
mem_limit: 256m
command: -c fsync=off --client-min-messages=warning
volumes:
- db:/var/lib/postgresql/data
ports:
- "127.0.0.1:5432:5432"
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_INITDB_ARGS: '--encoding=UTF-8 --lc-collate=C --lc-ctype=C'
restart: on-failure
logging:
driver: none
redis:
image: redis:6-alpine
container_name: learhot-redis-ctr
mem_limit: 256m
volumes:
- redis-data:/var/lib/redis/data
ports:
- "127.0.0.1:6379:6379"
restart: on-failure
logging:
driver: none
volumes:
db:
redis-data:
Para se comunicar com esses bancos alterei o config/database.yml e o config/cable.yml:
# config/database.yml
default: &default
adapter: postgresql
encoding: UTF8
host: localhost
user: postgres
password: postgres
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: LearningHotwire_development
port: 5432
test:
<<: *default
database: LearningHotwire_development
port: 5432
# config/cable.yml
development:
adapter: redis
url: <%= "#{ENV.fetch("REDIS_URL") { "redis://localhost:6379" }}/1" %>
channel_prefix: LearningHotwire_development
test:
adapter: async
production:
adapter: redis
url: <%= "#{ENV.fetch("REDIS_URL") { "redis://localhost:6379" }}/1" %>
channel_prefix: LearningHotwire_production
Com as novas inclusões de JavaScript bundler e CSS processor/framework, o Rails 7 utiliza para subir todo o ambiente de desenvolvimento a gema foreman que chama os processos listados no arquivo Procfile.dev. Isso tudo porque não mais apenas o servidor do Rails precisa ser executado em modo watch, ou seja, em modo de reload automático (em inglês chamamos de watch process), mas agora também essas duas novas inclusões.
Como incluímos o PostgreSQL e Redis no docker, podemos incluir também a chamada docker-compose up
no
Procfile.dev,
ficando assim:
docker: docker-compose up
web: bin/rails server -p 3000
js: yarn build --watch
css: yarn build:css --watch
É isso. Basta agora executar o script disponível bin/dev
que chama o foreman para o Procfile
que atualizamos.
# esse script já vem com permissão de execução
$ bin/dev
Você deve obter algo assim como saída no seu terminal:
Não esqueça de criar o banco de dados. Você pode fazer isso agora na página web:
Ou, em um outro terminal já que precisamos do docker em execução, utilize:
$ rails db:create
E acesse http://localhost:3000/ e veja a clássica telinha do Rails.
Por agora, é isso.