Pular para o conteúdo principal

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

CallbackMomento de Execução
before_validationAntes de validar o modelo
after_validationApós validar o modelo

Callbacks de Persistência

CallbackMomento de Execução
before_saveAntes de salvar (create ou update)
after_saveApós salvar (create ou update)
before_createAntes de criar um novo registro
after_createApós criar um novo registro
before_updateAntes de atualizar um registro existente
after_updateApós atualizar um registro existente
before_destroyAntes de excluir um registro
after_destroyApó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 UsoCallback Recomendado
Normalizar dadosbefore_save
Calcular valoresbefore_save
Criar dependênciasafter_create
Atualizar pai/irmãosafter_save, after_destroy
Auditoriabefore_update + after_update
Impedir açãobefore_* + throw(:abort)
Jobs assíncronosafter_create, after_update

Com callbacks bem estruturados, você automatiza comportamentos críticos mantendo o código organizado e previsível. 🧠✨