CRUD em Ruby on Rails
Tags: ruby, rails, tutorial
Published: 2020-10-01 22:44:06

Olá, dev! Hoje estaremos dando continuidade ao projeto de lista de tarefas que iniciamos no [último post](https://dev.to/jackson541/criando-um-projeto-em-ruby-on-rails-3bhd), dessa vez fazendo o famoso CRUD (Create, Read, Update, Delete) da nossa aplicação.

Se você caiu aqui de paraquedas, não se preocupe! Basta já ter um projeto criado em Rails, ou ver o [primeiro post](https://dev.to/jackson541/criando-um-projeto-em-ruby-on-rails-3bhd) para criar um, que já poderá continuar.

Caso fique com dúvida sobre como fazer algo durante a explicação, pode acessar [esse repositório](https://github.com/jackson541/artigos-rails/tree/master/CRUD-parte-1) que contém todo o código desenvolvido nesse artigo.

Está pronto!? Então abra o editor, rode o projeto e prepare o café!

Para ir até a página inicial do site, execute o comando na página do projeto:
```
rails server
```

E acesse a url `http://localhost:3000` Se tudo ocorreu bem, você deve encontrar uma página como essa:
![imagem inicial rails](https://dev-to-uploads.s3.amazonaws.com/i/xr5m74t6akzemk7tg6xt.png)

Bom, agora já podemos começar a codar de verdade! Primeiro, temos que criar um Controller para a nossa aplicação, neste caso irei criar um para as listas. Lembre-se que os Controllers são responsáveis por intermediar a comunicação entre a View e a Model no model MVC e também pelas regras de negócio da aplicação.

Rode o seguinte comando para criar um controller:
```
rails generate controller Listas index
```

Repare que `rails generate controller` é o comando padrão para criação de um controller, `Listas` é seu o nome e `index` é o método que virá criado dentro dele. Esse último parâmetro, o do método, é opcional, mas ele nos dá algumas vantagens quando preenchido:
- cria um arquivo nomeado `index.html.erb`, dentro da pasta `/app/views/listas/`, que nos servirá para criar o html desse método index;
- cria a rota `/listas/index` com o método GET para a nossa aplicação, a declaração dessa rota pode ser vista dentro do arquivo `/config/routes.rb`

Que tal já começar a escrever algo na tela? Vá até o arquivo `index.html.erb` que comentei e troque o seu conteúdo para 
```html
<h1>Aqui ficarão as nossas listas</h1>
```
E basta salvar as alterações e acessar a rota `http://localhost:3000/listas/index` que irá encontrar uma página como essa:
![página inicial listas](https://dev-to-uploads.s3.amazonaws.com/i/vfrc4qoye918i7qavp8n.png)

Excelente, já conseguimos imprimir algo em nosso site! Mas antes de continuarmos, preciso falar sobre algumas dúvidas que você possa estar tendo:

#### Porque o arquivo html do index termina com `.erb`?

ERB é um sistema do próprio Ruby que nos permite utilizar códigos dessa linguagem dentro do HTML, sem essa extensão não será possível utilizar código Ruby dentro dos arquivos. Veremos mais sobre isso mais a frente.

#### Como ele sabe qual arquivo html chamar?

Como ainda não tem nenhum códido dentro do método index no listas_controller.rb, então o Rails presume que ele deve renderizar a view que comece o mesmo nome do método, nesse caso `index`.

#### Como funciona a declaração das rotas? 

Todas as rotas são declaradas dentro do arquivo routes.rb que está na pasta config. Em geral, basta colocar o nome do método seguido da sua url, como podemos ver em `get 'listas/index'`, em declarações simples como essa o Rails já presume que essa url deve chamar o método `index` do controller `listas`. No entanto, podemos definir qual método o Rails irá chamar com uma rota por meio do parâmetro `to`, exemplo: 
```
get 'listas/minhaslistas', to: 'listas#index'
```
Nesse caso, o Rails irá chamar o método index do controller listas quando a url `http://localhost:3000/listas/minhaslistas` for acessada.

#### Posso colocar esse arquivo html para a rota raiz do site `/`?
Sim, isso é realmente fácil! Vá até o arquivo `routes.rb` e digite 
```
root 'controller#metodo'
```
No nosso caso estarei trocando o controller e o método por ```
root 'listas#index'
```
Agora, se você acessar `http://localhost:3000` verá que a página do método index também está sendo renderizada na rota raiz. Lembrando que isso serve para qualquer controller que você tiver criado!

E então, ficou com mais alguma dúvida? Ficarei feliz em te responder se deixar elas lá nos comentários!

### Criando as rotas
Já sabemos como é fácil criar novos controllers, então precisamos ver bem como criar rotas para acessá-los. Anteriormente, falei que as rotas podem ser definidas utilizando o método de requisição e um comando para apontar o seu controller, porém, o Rails fornece algo que facilita muito a nossa vida quando se trata de rotas ligadas ao CRUD: podemos, com um único comando, definir todas as rotas necessárias para isso. 

Vá até o seu `routes.rb`, abaixo do comando do root, e digite o seguinte: 
```
resources :listas
```

O seu arquivo deve estar parecido com esse:
```ruby
Rails.application.routes.draw do
  root 'listas#index' 

  resources :listas

  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
```

Com essa única linha já definimos todas as rotas para criar, ver, atualizar e deletar relacionadas ao controller listas. Quer ver isso melhor? Então vá até o seu terminal e digite esse comando para listar todas rotas:
```
rails routes
```

Você verá uma tabela que lista tudo das rotas: nome, URI, Pattern e o seu controller#action.

![rotas do projeto](https://dev-to-uploads.s3.amazonaws.com/i/d75bcvuozq9u2lumkldw.png)

Essa rotas já serão suficientes para nós nesse projeto, mas, caso queira entender mais a fundo sobre esse ponto no Rails, recomendo olhar [a documentação oficial](https://guides.rubyonrails.org/routing.html).

### Criando a model
Agora precisamos trabalhar com o banco de dados(DB), onde iremos guardar as informações das nossas listas. Para isso, existem as models que nos permitem criar classes que irão representar tabelas no DB.

Para as nossas listas, pensei em colocarmos apenas dois campos simples: nome e descrição. O primeiro é do tipo `string` e o segundo do tipo `text`, caso queira adicionar outros, consulte os [tipos aceitos](https://guides.rubyonrails.org/v3.2/migrations.html#supported-types) pelo Rails. Tudo isso pode ser feito direto pelo terminal:

```
rails generate model Lista nome:string descricao:text
```
Repare que `Lista` é o nome do nosso modelo e os demais são os seus atributos acompanhados dos seus tipos.

Você deve ter recebido uma resposta como essa:

![criação model rails](https://dev-to-uploads.s3.amazonaws.com/i/igghjnhcycwtsoiz9o1q.png)

Primeiro, note que ele gerou um arquivo dentro da pasta `/db/migrate/`, esse arquivo é uma [migration](https://medium.com/@juniorb2s/migrations-o-porque-e-como-usar-12d98c6d9269). Segundo, o Rails criou um arquivo `listum.rb` dentro de `/app/models/` que é o nosso arquivo de model de fato. Porém, algo engraçado é que ele o criou com o nome Listum em vez de Lista, isso ocorre porque o Rails tenta automaticamente colocar o nome do modelo no plural, o que não acabou dando muito certo nesse caso hahaha. Isso pode acabar nos atrapalhando em algum ponto posterior, então vamos alterar o nome desse arquivo para `lista.rb` e trocar a sua primeira linha para `class Lista < ApplicationRecord`.

Analise a migration criada, ela apresenta uma estrutura que representa uma tabela com nome `lista` e que tem duas colunas: nome e descricao.
```ruby
class CreateLista < ActiveRecord::Migration[6.0]
  def change
    create_table :lista do |t|
      t.string :nome
      t.text :descricao

      t.timestamps
    end
  end
end
```

Mas criar uma model não cria a tabela automaticamente no DB, já que podemos querer fazer alguma alteração na migration após essa criação. Sendo assim, precisamos fazer essas mudanças se refletirem no banco de dados e isso é feito com o seguinte comando:
```
rails db:migrate
```

Você vai encontrar uma saída como essa informando que a tabela foi criada:
![Criação tabela rails](https://dev-to-uploads.s3.amazonaws.com/i/jsn4hosuzyjvv2o4h78t.png)

Caso fique com dúvidas sobre os comandos de db do rails, pode acessar [esse link da documentação](https://guides.rubyonrails.org/active_record_migrations.html#running-migrations).

Agora sim, caro dev, demos passos importantes no nosso projeto e daremos alguns ainda mais a partir de agora. Chegou a hora de criarmos novos métodos no nosso controller, que tal começarmos pelo de cadastrar uma nova lista?

### Criação de novos métodos
Para isso, vamos utilizar duas rotas que já criamos, a `/listas/new` e a `/listas` com o método POST. Lembre-se que você pode ver todas as rotas do app utilizando o comando `rails routes`.

Iniciaremos criando a função new no controller:
```ruby
class ListasController < ApplicationController
  def index
  end

  def new
  end
end
```

Por enquanto ele ficará vazio, pois queremos apenas que ele renderize a view padrão.

Em seguida, criamos um arquivo `new.html.erb` dentro da `/views/listas/` e podemos colocar um html qualquer:
```html
<h1>Criar uma nova lista</h1>
```

Se tudo estiver correto, você conseguirá encontrar esse conteúdo pela rota `/listas/new`.

Infelizmente apenas esse h1 não irá fazer o cadastro das listas que precisamos haha. Então, vamos complementar a página com um formulário, mas repare que ele terá uma estrutura diferente, a estrutura de tags do Rails:

```erb
<h1>Criar uma nova lista</h1>

<%= form_with scope: :lista, local: true, url: listas_path do |form| %>
    <p>
        <%= form.label :nome %>
        <%= form.text_field :nome %>
    </p>
    <p>
        <%= form.label(:descricao, "Descrição") %>
        <%= form.text_area :descricao %>
    </p>
    <p>
        <%= form.submit("Salvar") %>
    </p>
<% end %>
```

As tags do Rails sempre seguem a estrututa `<%=  %>` e quando é apenas código ruby utiliza-se o `<%  %>`, como no caso do `end`.

Bom, tenho algumas coisas importantes para te apresentar, então preste atenção! `form_with` é a tag de formulário que estamos utilizando, nesse caso ela recebe dois parâmetros: o `scope` que serve para definir um identificador para esse formulário, passamos `:lista` como identificador, e que será utilizado mais tarde para recuperar os dados preenchidos nos campos. Além desse, também tem o `local`, que serve para dizer que não queremos enviar as respostas desse form por Ajax.

Existe também um terceiro parâmetro que merece atenção especial, o `url`. Se ele não fosse passado, os dados seriam enviados para a mesma rota `/listas/new`, mas com o método POST. Porém, isso não nos serve porque já temos a rota `/listas` com o POST para receber esses dados, então utilizamos esse parâmetro para definir o novo ponto de envio. Você pode estar se perguntando o que significa `listas_path`, listas é o nome da rota desejada e quando utilizado o _path o Rails retorna a rota relativa a esse nome, no caso `/listas`. Lembre-se mais uma vez que tudo sobre as rotas do app podem ser vistas com o comando `rails routes`.

Agora repare nas demais tags utilizadas: 
- `form.label` imprime o nome de um campo para o usuário;
- `form.text_field` cria um campo de texto comum;
- `form.text_area` cria um campo de texto em área;
- `form.submit` cria um botão para enviar os dados do form.

Ao recarregar a página você irá encontrar algo assim:
![tela formulário lista](https://dev-to-uploads.s3.amazonaws.com/i/fhreewfea5zyib2wpn42.png)

O coitado está muito feio, mas isso pode ser resolvido com um pouco de CSS mais tarde.

E ae, já tentou enviar esse fomulário? Se sim, deve ter recebido um erro como esse:

![tela erro envio form](https://dev-to-uploads.s3.amazonaws.com/i/07ll5l2k7illgbar20e9.png)

Basicamente ele está nos dizendo que não conseguiu encontrar a ação create no controller listas. O que significa que ele está enviando os dados para o local correto, basta criarmos esse método.

```ruby
def create
    render plain: params.require(:lista).inspect
end
```

Aqui estamos basicamente inspecionando todos os parâmetros passados, `render` renderiza um template e `plain` diz que o template é simplesmente o texto apartir dele. `:lista` é aquele mesmo identificador que utilizamos no `scope` do form.

![renderizando textos da requisição](https://dev-to-uploads.s3.amazonaws.com/i/e7io567er64zj6btu5ic.png)

Finalmente podemos pegar esses dados e salvá-los no banco de dados. Em tese isso já deveria funcionar:

```ruby
def create
    @lista = Lista.new(params[:lista])
end
```

OBS: o Rails sabe que deve chamar o model que criamos pelo nome que utilizamos com a inicial maiúscula, o `Lista`.

No entanto, se tentarmos enviar novamente o formulário, encontraremos mais um erro:

![erro permição parâmetros](https://dev-to-uploads.s3.amazonaws.com/i/adzku5hrusqe2cccqxvm.png)

Isso ocorreu porque o Rails não nos permite simplesmente pegar todos os atributos passados na requisição do form e salvar, pois um usuário com má intenção pode enviar atributos a mais para quebrar ou roubar algo da nossa aplicação. Sendo assim, precisamos especificar exatamente quais atributos queremos pegar:

```ruby
def create
    @lista = Lista.new(params.require(:lista).permit(:nome, :descricao))

    @lista.save

    redirect_to lista_path(@lista)
end
```

Agora estamos pegando apenas o nome e descrição para criar uma nova lista. Já te adiantando, o `@lista.save` salva a lista no DB e o `redirect_to lista_path(@lista)` diz ao Rails que queremos redirecionar para o método show e exibir essa nova lista, será que isso vai funcionar?

Infelizmente não! Ainda precisamos criar esse método para que tudo funcione:

```ruby
def show
    @lista = Lista.find(params[:id])
end
```

O `find` irá buscar por uma lista utilizando o seu `id`, não se preocupe que o Rails irá passar esse id automaticamente pelo comando do `redirect_to`. Mas antes precisamos criar uma página HTML para renderizar essa lista, então crie um `show.html.erb` na pasta de listas.

```erb
<p>
    <strong>Nome: </strong>
    <%= @lista.nome %>
</p>
<p>
    <strong>Descrição: </strong>
    <%= @lista.descricao %>
</p>
```

A variável `@lista` é a mesma que estamos retornando no método show. Então, teremos algo assim:

![exibindo uma lista rails](https://dev-to-uploads.s3.amazonaws.com/i/3ure0dmb7zdi52v9u4zv.png)

Agora sim! Já conseguimos cadastrar e exibir uma lista, para fechar com chave de ouro vamos exibir todas as listas!

Volte a ação index no controller:
```ruby
def index
    @listas = Lista.all
end
```

Dessa forma, a variável listas irá guardar todas as listas registradas, basta exibir elas em seu respectivo arquivo, o `index.html.erb`:

```erb
<h1>Aqui ficarão as nossas listas</h1>

<table>
    <tr>
        <th>Nome</th>
        <th>Descrição</th>
    </tr>

    <% @listas.each do |lista| %>
        <tr>
            <td><%= lista.nome %></td>
            <td><%= lista.descricao %></td>
        </tr>
    <% end %>
</table>
```

E esse será o nosso resultado:

![exibindo todas as listas rails](https://dev-to-uploads.s3.amazonaws.com/i/axqzb2z763h6i6kwat2g.png)

Conseguimos caminhar bastante! Porém, ainda a muita estrada pela frente, pois o nosso CRUD não está finalizado. Sendo assim, peço que me acompanhe no próximo post dessa jornada!