# app/services/notificacao/resolvedor_variaveis.rb # # Completa o contexto de um disparo com as variáveis que o template REALMENTE # usa. É o que permite escrever {{entregas_mes}} ou {{telefone_suporte}} numa # mensagem sem que o gatilho precise saber dessas variáveis. # # SOB DEMANDA, de propósito: `{{entregas_mes}}` é uma query no espelho de # rastreio. Resolver o catálogo inteiro em todo disparo faria dezenas de # consultas para preencher variáveis que ninguém escreveu. # # Precedência (a primeira que responde vence): # 1. o que o gatilho já mandou (contexto real do acontecimento); # 2. variável de sistema (empresa, data, hora…); # 3. variável de dados (consulta ao banco); # 4. variável personalizada do ADM. # # ⚠️ NADA aqui levanta. Uma variável que falha vira string vazia — a mesma regra # do renderizador, e pelo mesmo motivo: mensagem com buraco é ruim, disparo # derrubado por causa de um texto é pior. module Notificacao module ResolvedorVariaveis MARCADOR = /\{\{\s*(\w+)\s*\}\}/ module_function # textos: tudo que será interpolado (blocos do template, assunto, corpo). # dados: o contexto que o gatilho produziu. def completar(textos, dados = {}) contexto = (dados || {}).transform_keys(&:to_s) faltantes = usadas(textos) - contexto.keys faltantes.each_with_object(contexto.dup) do |chave, resultado| valor = resolver(chave) resultado[chave] = valor unless valor.nil? end end # Chaves {{assim}} encontradas em qualquer texto (String, Array ou Hash de # blocos — o template é jsonb aninhado). def usadas(textos) varrer(textos).scan(MARCADOR).flatten.uniq end def resolver(chave) chave = chave.to_s valor = valor_de_sistema(chave) || valor_de_dados(chave) || valor_personalizado(chave) valor&.to_s end # ── privados de módulo ────────────────────────────────────── def varrer(objeto) case objeto when String then objeto when Hash then objeto.values.map { |v| varrer(v) }.join(' ') when Array then objeto.map { |v| varrer(v) }.join(' ') else '' end end def valor_de_sistema(chave) meta = CatalogoVariaveis::SISTEMA[chave] meta && executar(meta[:resolver], chave) end def valor_de_dados(chave) meta = CatalogoVariaveis::DADOS[chave] meta && executar(meta[:resolver], chave) end def valor_personalizado(chave) return nil unless VariavelPersonalizada.table_exists? VariavelPersonalizada.ativas.find_by(chave: chave)&.valor rescue StandardError => e Rails.logger.error("[Variaveis] personalizada #{chave}: #{e.class}: #{e.message}") nil end def executar(bloco, chave) return nil if bloco.nil? resultado = bloco.call # Número cru numa mensagem fica sem separador de milhar ("4977"). resultado.is_a?(Numeric) ? Formatador.numero(resultado) : resultado.to_s rescue StandardError => e Rails.logger.error("[Variaveis] #{chave}: #{e.class}: #{e.message}") nil end end end