O que é a ElementComponent
ElementComponent é uma gem Ruby leve e flexível para construir HTML de forma orientada a objetos. Em vez de concatenar strings ou depender de um template engine, você monta a árvore de elementos usando objetos Ruby, com atributos dinâmicos, aninhamento de conteúdo, hooks de renderização e — de brinde — um conjunto completo de 17 componentes Bootstrap 5 prontos para uso.
Esta é a primeira de uma série de posts cobrindo a gem por completo. Aqui vamos focar na API central: criação de elementos, a DSL de blocos, tipos de conteúdo, gerenciamento de atributos e tags autofechadas.
Instalação
bundle add element_component
Ou, sem Bundler:
gem install element_component
Criando elementos
O bloco básico de construção é ElementComponent::Element:
p = ElementComponent::Element.new("p", class: "text-bold")
p.add_content("Hello, World!")
puts p.render
# => <p class="text-bold">Hello, World!</p>
DSL de blocos
Elementos podem receber um bloco no construtor para adicionar conteúdo de forma inline, o que deixa a árvore de HTML legível mesmo quando aninhada:
div = ElementComponent::Element.new("div", class: "container") do |e|
e.add_content("Welcome")
e.add_content(ElementComponent::Element.new("h1") { |h| h.add_content("Title") })
end
puts div.render
# => <div class="container">Welcome<h1>Title</h1></div>
O helper new_element
Dentro de um bloco, new_element é um atalho para não precisar repetir ElementComponent::Element.new toda hora:
div = ElementComponent::Element.new("div") do |e|
e.add_content(e.new_element("h1") { |h| h.add_content("Hello") })
e.add_content(e.new_element("p", class: "lead") { |p| p.add_content("World") })
end
puts div.render
# => <div><h1>Hello</h1><p class="lead">World</p></div>
Tipos de conteúdo aceitos
add_content aceita string, uma instância de Element, um bloco (avaliado no momento da renderização) ou um array combinando qualquer um desses tipos:
div = ElementComponent::Element.new("div")
div.add_content("plain text")
div.add_content(ElementComponent::Element.new("span") { |s| s.add_content("nested") })
div.add_content { |e| e.new_element("em") { |em| em.add_content("deferred") } }
div.add_content(["Hello ", ElementComponent::Element.new("strong") { |s| s.add_content("World") }, "!"])
puts div.render
# => <div>plain text<span>nested</span><em>deferred</em>Hello <strong>World</strong>!</div>
O suporte a blocos como conteúdo é interessante porque adia a avaliação para o momento do render, permitindo conteúdo dinâmico ou dependente de estado que só existe naquele momento.
Gerenciamento de atributos
A API de atributos é encadeável e dá controle fino sobre valores individuais — não só sobre o atributo inteiro:
btn = ElementComponent::Element.new("button", class: "btn", type: "button")
btn.add_attribute(class: "btn-primary") # acrescenta valor
btn.add_attribute!(id: "submit-btn", type: "submit") # reseta e define novos
btn.remove_attribute(:type) # remove o atributo inteiro
btn.remove_attribute_value(:class, "btn-primary") # remove só um valor da lista
Isso é particularmente útil para atributos multivalorados como class, onde normalmente você quer adicionar ou remover uma classe sem tocar nas demais.
Tags autofechadas (void elements)
Para elementos como <img>, <input> e <br>, basta passar closing_tag: false:
img = ElementComponent::Element.new("img", closing_tag: false, src: "image.png", alt: "Logo")
puts img.render
# => <img src="image.png" alt="Logo">
O que vem a seguir
Todos os métodos add_* retornam self, o que permite encadeamento fluente — um padrão que se repete em toda a gem, inclusive nos componentes Bootstrap. No próximo post da série, vamos ver os recursos que tornam a ElementComponent pronta para produção: hooks de renderização (before_render, after_render, around_render), proteção automática contra XSS, cache e integração com helpers do Rails.