O que aconteceu?
Por dentro do app
Postar um recado usa três requisições, uma depois da outra:
- Abrir o formulário. Quando a pessoa clica em Novo recado, o navegador faz uma requisição
GET /messages/new. A rota manda para a açãonew, que prepara um recado em branco, e a viewnew.html.erbmostra o formulário. - Enviar o formulário. Quando a pessoa clica em Postar recado, o navegador faz uma requisição
POST /messages, levando o que foi escrito. A rota manda para a açãocreate, que pede ao model para guardar o recado no banco de dados. - Voltar para o mural de recados. O
createnão monta nenhuma página: ele responde “vá para a página principal” (redirect_to root_path). O navegador faz então uma requisiçãoGET /, e a açãoindex, que você criou no capítulo anterior, mostra o mural de recados, já com o recado novo.
%%{init: {"sequence": {"mirrorActors": false}}}%%
sequenceDiagram
participant N as 💻 Navegador
participant C as Controller
participant M as Model
participant B as Banco de dados
Note over N,B: 1. Abrir o formulário
N->>C: GET /messages/new (ação new)
C->>M: Message.new
M-->>C: um recado em branco, só na memória
C-->>N: página Novo recado, com o formulário
Note over N,B: 2. Enviar o formulário
N->>C: POST /messages, com o recado (ação create)
C->>M: Message.create
M->>B: guarda o recado
B-->>M: guardado
M-->>C: o recado novo salvo
C-->>N: "vá para a página principal" (redirect_to)
Note over N,B: 3. Voltar para o mural de recados
N->>C: GET / (ação index)
C->>M: busca os recados
M->>B: lê os recados
B-->>M: os recados
M-->>C: a lista de recados
C-->>N: mural de recados, com o recado novo
Em cada requisição, a rota escolhe a ação do controller, como você viu no capítulo anterior.
O formulário e o recado em branco
A view new.html.erb monta o formulário a partir do recado em branco que a ação new preparou (@message = Message.new). É por isso que os campos se chamam author e content, iguais às colunas da tabela: o formulário sabe quais informações um recado tem.
O que viaja do navegador até o controller
Quando o formulário é enviado, o que a pessoa escreveu chega ao controller num pacote chamado params (de parameters, parâmetros). Você pode ver esse pacote no terminal do servidor, numa linha parecida com esta:
Parameters: {"authenticity_token" => "[FILTERED]", "message" => {"author" => "Ana", "content" => "Meu primeiro recado!"}}
Repare no "message" => {...}: a autora e a mensagem chegam juntas, dentro de um recado (message).
A lista do que entra
O message_params não pega tudo que chegou: ele diz exatamente o que o controller aceita, um recado com author e content. Se chegar qualquer outra coisa, ela é ignorada. Isso protege o app: alguém poderia mandar um formulário modificado tentando mudar uma informação que não deveria.
O resources e a convenção
O resources :messages criou as rotas pela convenção do Rails: GET /messages vai para o index, GET /messages/new vai para o new, e POST /messages vai para o create. O index e o create usam o mesmo endereço; o que muda é o tipo de requisição. Com o only, o app só tem as rotas que você usa.
A convenção também dá nome aos endereços: é daí que vem o new_message_path do link Novo recado. Por isso, o link só funcionou depois que a rota existia.
Não existem perguntas bobas
Qual é a diferença entre Message.new e Message.create?
O Message.new cria um recado só na memória do app, sem guardar: serve para o formulário saber o que preencher. O Message.create cria e já guarda no banco de dados. Se o app fosse desligado, o recado do new sumiria, e o do create continuaria lá.
Qual é a diferença entre GET e POST?
O GET pede uma página, sem mudar nada no app: é o que acontece quando você abre um endereço. O POST envia informações para o app guardar ou mudar alguma coisa: é o que acontece quando você envia um formulário. Por isso, o mesmo endereço, /messages, pode ir para duas ações diferentes.
Por que o create manda o navegador de volta, em vez de mostrar a página direto? Para ir além
Se o create mostrasse o mural de recados direto, a última requisição do navegador continuaria sendo o POST. Aí, ao recarregar a página, o navegador enviaria o formulário de novo, e o mesmo recado seria postado duas vezes. Com o redirect_to, a última requisição é um GET, e recarregar só mostra o mural de recados de novo.
O que é o authenticity_token? Para ir além
É um código secreto que o form_with coloca escondido em todo formulário. Quando o formulário chega, o Rails confere esse código para ter certeza de que o formulário veio do próprio app, e não de outro site tentando enviar recados no seu lugar. No terminal, ele aparece como [FILTERED] (escondido), justamente por ser secreto.
Por que existe o private no controller?
Tudo que vem depois do private só pode ser usado pelo próprio controller. Assim, o message_params não vira uma ação, e ninguém consegue chamar ele por um endereço. As ações, como index, new e create, ficam sempre antes do private.
Quebre de propósito Opcional
No controller, coloque um # na frente da linha @message = Message.new, para ela virar comentário. Salve e abra a página Novo recado.
Dê um palpite: o que vai acontecer?
Aparece a página de erro ArgumentError in Messages#new, com a mensagem Passed nil to the :model argument, expect an object or false. O form_with recebeu “nada” (nil) no lugar do recado em branco e não sabe montar o formulário.
Tire o #, salve e recarregue: o formulário volta.
Agora, coloque um # na frente da linha redirect_to root_path, na ação create. Salve, recarregue a página Novo recado e poste um recado.
Dê um palpite: o recado vai aparecer?
Parece que nada aconteceu: a página não muda, e o texto continua no formulário. Mas clique em Voltar: o recado está lá! Ele foi guardado, só que o create não mandou o navegador de volta para o mural de recados. Esse é outro tipo de problema sem mensagem de erro: o app funciona pela metade.
Tire o #, salve, e apague o recado de teste pelo console (Message.last.destroy).
Preciso de IA para este capítulo?
Não. O formulário, a rota e as ações seguem o mesmo caminho dos capítulos anteriores, e os erros mostram cada peça que falta.
Se quiser usar uma IA, use como tutora: peça para ela explicar, e faça você cada passo. Por exemplo:
Qual é a diferença entre uma requisição GET e uma POST? Me explique com um exemplo do dia a dia, sem me dar código.
Veja como começar a conversa em Usando IA como tutora.
E se eu pedisse o código para a IA? Para ir além
Se você pedir para uma IA “fazer um formulário para postar recados”, é comum ela sugerir o scaffold, que cria de uma vez todas as páginas e ações, inclusive as que o plano não pede. Por isso, o pedido funciona melhor com o plano:
No meu app Rails, o model
Messagetemauthorecontent. Quero uma página “Novo recado” (a açãonew), com os campos “Seu nome” e “Recado” e o botão “Postar recado”, e um link “Novo recado” na página da lista. Depois de postar, volta para a lista. Sem scaffold, e só com as rotas necessárias.
Confira o resultado contra o plano:
- Tem só o que o plano pede: a página Novo recado e o link na lista, sem páginas a mais?
- O controller usa uma lista do que aceita (como o
message_params), e nãoparamsdireto? - A rota tem
only, só com as ações que você usa?
Veja mais dicas em Como pedir código para uma IA, nos Extras.
Não esqueça
- A ação
newprepara um recado em branco, e a viewnew.html.erbmostra o formulário. - Um formulário envia o que a pessoa escreveu com uma requisição
POST. - A ação
createrecebe o formulário, pede ao model para guardar o recado e manda o navegador de volta para o mural de recados. - O controller só aceita do formulário o que está na lista do
message_params. - O
resourcescria as rotas pela convenção do Rails, e oonlyescolhe quais.
Quiz
- Quando a pessoa clica em Postar recado, que tipo de requisição o navegador faz, e para qual ação ela vai?
- Você criou a rota do
create, mas esqueceu de escrever a ação no controller. Onde o erro aparece, e o que ele diz? - Por que o link Novo recado deu erro antes de você criar a rota?
- Depois que o recado é guardado, por que a pessoa volta a ver o mural de recados?
Ver respostas
- Uma requisição
POST /messages, que vai para a açãocreate. - Na tela, nada acontece. No terminal do servidor, aparece
The action 'create' could not be found for MessagesController. - Porque o
new_message_pathé um nome que o Rails cria a partir das rotas. Sem a rota donew, esse nome não existia, e apareceu o NameError. - Porque a ação
createtermina comredirect_to root_path, que manda o navegador de volta para a página principal.
Para saber mais
Guias oficiais do Rails, em inglês:
- Action View Form Helpers: tudo sobre o
form_withe os campos de formulário. - CRUD, Verbs, and Actions: como o
resourcesliga verbos, endereços e ações. - Strong Parameters: a lista do que o controller aceita.
E agora?
Agora qualquer pessoa pode postar um recado. Mas, se ela escrever algo errado, não tem como corrigir. Próximo desafio: Errei! Como corrigir ou apagar?