O ciclo de vida dos callbacks, em ordem
Onde o curso de validações parou
Validações ActiveRecord avançadas terminou com uma pergunta: este registro pode ser salvo? O que acontece antes, durante e depois do save ficou para este curso. Então este curso abre com uma pergunta direta: quando .save roda, em que ordem o Rails faz tudo o que fica em volta dele?
Os models deste app já dependem da resposta. Identity::User declara before_save :downcase_email e after_create :create_default_preference. Enrollment::UserBadge, Enrollment::LessonCompletion e Partners::Partner declaram cada um um before_validation, e Catalog::Lesson declara um after_save. Nenhuma aula explicou essas linhas ainda, e este curso começa descobrindo a ordem em vez de supor qual é.
Um callback em cada etapa
class ScratchPost < ApplicationRecord
before_validation { log("before_validation") }
after_validation { log("after_validation") }
before_save { log("before_save") }
before_create { log("before_create") }
after_create { log("after_create") }
after_save { log("after_save") }
before_update { log("before_update") }
after_update { log("after_update") }
before_destroy { log("before_destroy") }
after_destroy { log("after_destroy") }
around_save :around_save_wrap
around_create :around_create_wrap
around_update :around_update_wrap
def log(name) = ($order << name)
def around_save_wrap
log("around_save (before yield)"); yield; log("around_save (after yield)")
end
def around_create_wrap
log("around_create (before yield)"); yield; log("around_create (after yield)")
end
def around_update_wrap
log("around_update (before yield)"); yield; log("around_update (after yield)")
end
end
Numa tabela de rascunho, com cada callback acrescentando seu nome em $order, ScratchPost.create!(title: "Hello") produziu isto:
1. before_validation
2. after_validation
3. before_save
4. around_save (before yield)
5. before_create
6. around_create (before yield)
7. around_create (after yield)
8. after_create
9. around_save (after yield)
10. after_save
Lendo o formato
A validação roda primeiro e é separada do save. before_validation e after_validation disparam antes de qualquer escrita. Quando a validação passa, before_save dispara, e daí em diante as etapas se aninham. around_save abre, e as etapas próprias do create rodam dentro dele: before_create, depois around_create abrindo, o INSERT, around_create fechando, e então after_create. around_save só fecha depois de tudo isso, e after_save roda por último.
before_save e after_save disparam em todo save, seja create ou update. before_create e after_create disparam uma vez só, quando a linha é nova. Dentro de uma mesma etapa, before_create e around_create rodam na ordem em que o model os declara; esta classe declara before_create primeiro.
O mesmo registro, atualizado
Chamar .update!(title: "Hello Updated") no mesmo post, já salvo, produziu o mesmo formato. O par de update ocupa o lugar do par de create:
1. before_validation
2. after_validation
3. before_save
4. around_save (before yield)
5. before_update
6. around_update (before yield)
7. around_update (after yield)
8. after_update
9. around_save (after yield)
10. after_save
destroy pula a validação
1. before_destroy
2. after_destroy
Chamar .destroy no post rodou duas etapas e nada mais. Não há before_validation nem before_save. Apagar uma linha não depende de os atributos dela serem válidos, então o Rails não roda essa parte.
Pontos-chave
- Numa tabela de rascunho, criar um registro rodou dez etapas de callback numa ordem fixa, de
before_validationaafter_save, combefore_create/after_createaninhados dentro debefore_save/after_save. - Atualizar um registro salvo roda o mesmo formato, com
before_update/after_updateno lugar do par de create.before_save/after_savedisparam nos dois casos. .destroypula a validação: sóbefore_destroye depoisafter_destroyrodam.Identity::User(before_save,after_create),UserBadge/LessonCompletion(before_validation) e outros models deste projeto já usam essas etapas. O resto do curso trata de cada uma.
Continue com a aula 2
before_save vs. after_create neste app
Explica por que Identity::User normaliza o email em before_save e cria as preferências em after_create.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.