Continuando a série
No post anterior cobrimos a API central da ElementComponent: criação de elementos, DSL de blocos e atributos. Agora vamos aos recursos que tornam a gem viável para uso real em produção: hooks de renderização, segurança contra XSS, cache e integração com Rails.
Hooks de renderização
A ElementComponent oferece três hooks que rodam ao redor do ciclo de render: before_render, after_render e around_render. Eles permitem alterar o elemento dinamicamente antes de virar HTML — por exemplo, adicionar uma classe condicional apenas no momento da renderização:
div = ElementComponent::Element.new("div")
div.define_singleton_method(:before_render) { add_attribute(class: "dynamic") }
div.add_content("content")
puts div.render
# => <div class="dynamic">content</div>
Esse padrão é a base de vários componentes internos da gem, que usam before_render para montar as classes CSS finais (por exemplo, combinar variant + outline + size num Button) só quando o render é de fato chamado.
Escaping automático e proteção contra XSS
Todo conteúdo string passado para add_content é escapado automaticamente. Caracteres como <, >, & e " viram suas entidades HTML:
el = ElementComponent::Element.new("p")
el.add_content("<script>alert('xss')</script>")
puts el.render
# => <p><script>alert('xss')</script></p>
Isso é importante porque, diferente de simplesmente interpolar strings, a ElementComponent trata todo conteúdo textual como não confiável por padrão — um comportamento seguro por definição.
Quando você realmente precisa injetar HTML sem escaping (por exemplo, HTML já sanitizado por outra camada), use ElementComponent.html_safe():
el.add_content(ElementComponent.html_safe("<b>bold</b>"))
puts el.render
# => <p><b>bold</b></p>
O opt-out é explícito — você precisa marcar deliberadamente o conteúdo como seguro, o que evita escapes acidentais de XSS por esquecimento.
Cache
Para elementos com conteúdo caro de gerar, a gem oferece cache embutido:
el = ElementComponent::Element.new("div")
el.add_content(expensive_operation)
el.cache # habilita cache em memória
el.render # calcula e armazena em cache
el.render # retorna o HTML já em cache
el.expire_cache! # limpa o cache
Quando Rails.cache está disponível, basta passar expires_in para usar o backend de cache do Rails em vez do cache em memória do processo:
el.cache(expires_in: 5.minutes) # usa Rails.cache quando disponível
Isso é útil para componentes reutilizados em múltiplas páginas, como um menu de navegação ou um card que muda pouco entre requisições.
Integração com Rails
A ElementComponent não depende do Rails, mas se integra bem a ele. Definindo view_context, os componentes ganham acesso a helpers como link_to, image_tag e rotas nomeadas via method_missing:
component = ElementComponent::Alert.new(variant: :success)
component.view_context = helpers
component.add_content(component.link_to("Home", root_path))
component.render
# => <div class="alert alert-success" role="alert"><a href="/">Home</a></div>
Para habilitar isso globalmente em todos os elementos da aplicação, basta incluir o módulo RailsHelpers na classe base:
ElementComponent::Element.include(ElementComponent::RailsHelpers)
Isso é especialmente útil ao construir componentes Bootstrap (que veremos nos próximos posts) que precisam gerar links internos usando as rotas da aplicação, em vez de URLs hardcoded.
Próximo post
Com a base coberta — construção de elementos, hooks, segurança e cache — a próxima parte da série entra nos 17 componentes Bootstrap 5 prontos para uso que vêm embutidos na gem, começando por Alert, Badge, Button, ButtonGroup e Card.