Callbacks
Os callbacks no Nerdify seguem o padrão Rails/Mongoid e permitem executar código em momentos específicos do ciclo de vida do modelo. Eles são essenciais para automatizar comportamentos como cálculos, sincronização de dados e validações complexas.
⚙️ Callbacks Disponíveis
Callbacks de Validação
| Callback | Momento de Execução |
|---|---|
before_validation | Antes de validar o modelo |
after_validation | Após validar o modelo |
Callbacks de Persistência
| Callback | Momento de Execução |
|---|---|
before_save | Antes de salvar (create ou update) |
after_save | Após salvar (create ou update) |
before_create | Antes de criar um novo registro |
after_create | Após criar um novo registro |
before_update | Antes de atualizar um registro existente |
after_update | Após atualizar um registro existente |
before_destroy | Antes de excluir um registro |
after_destroy | Após excluir um registro |
📝 Sintaxe Básica
class Customer
include Nerdify::Model
orm :mongoid
# Callback com método
before_save :normalize_phone
# Callback com condição
after_create :send_welcome_email, if: -> { email.present? }
# Callback condicional por método
before_validation :set_defaults, if: :new_record?
private
def normalize_phone
self.phone = phone.gsub(/\D/, '') if phone.present?
end
def send_welcome_email
# Lógica de envio
end
def set_defaults
self.status ||= 'active'
end
end
🎯 Padrões de Uso Comuns
1. Transformação de Dados
Use before_save para normalizar ou calcular valores antes de persistir:
before_save :calculate_signal_value
def calculate_signal_value
if type == "revenue"
self.signal_value = value
else
self.signal_value = value * -1.0
end
end
2. Criação de Registros Dependentes
Use after_create para criar registros relacionados:
after_create :generate_default_settings
def generate_default_settings
PdvSetting.create(
account_id: self.id,
store_id: stores.first.id,
default_values: true
)
end
3. Sincronização de Estado
Use múltiplos callbacks para manter dados sincronizados:
after_create :update_customer_balance
after_update :update_customer_balance
after_destroy :update_customer_balance
def update_customer_balance
customer.recalculate_balance! if customer.present?
end
4. Registro de Atividades (Audit Trail)
Combine before_update e after_update para auditoria:
before_update :capture_changes
after_update :create_activity_log
def capture_changes
@changed_fields = changes.except('updated_at', 'created_at')
end
def create_activity_log
return if @changed_fields.blank?
Activity.create(
user: current_user,
record: self,
changes: @changed_fields,
action: 'update'
)
end
5. Validações Condicionais com Estado
Use before_validation para preparar dados antes da validação:
before_validation :set_dates_based_on_status
def set_dates_based_on_status
case status
when 'progress'
self.started_at ||= Time.now
when 'done'
self.finished_at ||= Time.now
when 'pending'
self.started_at = nil
self.finished_at = nil
end
end
🔒 Condições nos Callbacks
Usando Lambda
before_save :process_nfe, if: -> { nfe_data.present? && new_record? }
Usando Método
after_create :notify_admin, if: :high_value_sale?
def high_value_sale?
total > 1000
end
Usando unless
before_destroy :prevent_deletion, unless: :can_be_deleted?
def can_be_deleted?
status == 'draft'
end
def prevent_deletion
errors.add(:base, 'Não é possível excluir este registro')
throw(:abort)
end
⚠️ Interrompendo a Execução
Para impedir que a operação continue, use throw(:abort):
before_destroy :check_dependencies
def check_dependencies
if sales.any?
errors.add(:base, 'Existem vendas vinculadas')
throw(:abort)
end
end
Ou adicione erros e retorne false:
before_save :validate_business_rules
def validate_business_rules
if discount > max_allowed_discount
errors.add(:discount, 'Desconto acima do permitido')
throw(:abort)
end
end
🔄 Callbacks com Jobs em Background
Para operações pesadas, use jobs assíncronos:
after_create :process_in_background
def process_in_background
ProcessInvoiceJob.perform_later(self.id)
# Ou usando delay do Sidekiq:
# self.delay.heavy_processing
end
📊 Ordem de Execução
A ordem de execução dos callbacks é:
1. before_validation
2. after_validation
3. before_save
4. before_create (se novo) / before_update (se existente)
5. [PERSISTÊNCIA NO BANCO]
6. after_create (se novo) / after_update (se existente)
7. after_save
Para destroy:
1. before_destroy
2. [EXCLUSÃO NO BANCO]
3. after_destroy
💡 Boas Práticas
✅ Faça
- Use callbacks para lógica que sempre deve executar
- Mantenha callbacks simples e focados
- Use condições para evitar execuções desnecessárias
- Documente callbacks complexos
❌ Evite
- Lógica de negócio complexa em callbacks (use Services)
- Callbacks que dependem de contexto externo instável
- Modificar outros registros em
before_*sem necessidade - Callbacks que podem falhar silenciosamente
🔍 Acessando Contexto do Usuário
O Nerdify disponibiliza current_user nos modelos:
before_create :set_creator
def set_creator
self.created_by = current_user if current_user.present?
end
📋 Resumo
| Caso de Uso | Callback Recomendado |
|---|---|
| Normalizar dados | before_save |
| Calcular valores | before_save |
| Criar dependências | after_create |
| Atualizar pai/irmãos | after_save, after_destroy |
| Auditoria | before_update + after_update |
| Impedir ação | before_* + throw(:abort) |
| Jobs assíncronos | after_create, after_update |
Com callbacks bem estruturados, você automatiza comportamentos críticos mantendo o código organizado e previsível. 🧠✨