Class: Philiprehberger::HtmlBuilder::Builder
- Inherits:
-
Object
- Object
- Philiprehberger::HtmlBuilder::Builder
- Defined in:
- lib/philiprehberger/html_builder/builder.rb
Overview
DSL-based HTML builder that creates a tree of nodes
Constant Summary collapse
- STANDARD_TAGS =
%i[ a abbr address article aside audio b bdi bdo blockquote body button canvas caption cite code colgroup data datalist dd del details dfn dialog div dl dt em fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup html i iframe ins kbd label legend li main map mark menu meter nav noscript object ol optgroup option output p picture pre progress q rp rt ruby s samp script section select slot small span strong style sub summary sup table tbody td template textarea tfoot th thead time title tr u ul var video ].freeze
- VOID_TAGS =
%i[area base br col embed hr img input link meta param source track wbr].freeze
- ALL_TAGS =
(STANDARD_TAGS + VOID_TAGS).freeze
Instance Method Summary collapse
-
#cache(key) { ... } ⇒ void
Cache a rendered block result by key.
-
#class_names(*args) ⇒ String
Build a space-joined CSS class string from mixed arguments.
-
#define_component(name) { ... } ⇒ void
Define a reusable named component.
-
#doctype ⇒ void
Emit an HTML5 doctype declaration (‘<!DOCTYPE html>`).
-
#field(name, label_text: nil, type: 'text', **attrs) ⇒ void
Form builder helper: builds a label + input pair.
-
#form_for(action, method_type: 'post', **attrs) { ... } ⇒ Node
Form builder helper: builds a form tag with common defaults.
-
#hidden_field(name, value) ⇒ Node
Form builder helper: generate a hidden input field.
-
#initialize ⇒ Builder
constructor
A new instance of Builder.
-
#list(items, ordered: false, **attrs) {|item| ... } ⇒ Node
Build a list (ul or ol) from an array of items.
-
#raw(html) ⇒ void
Add raw HTML content without escaping.
-
#render_if(condition) { ... } ⇒ void
Conditionally render a block if the condition is truthy.
-
#render_unless(condition) { ... } ⇒ void
Conditionally render a block if the condition is falsy.
-
#select_field(name, options_list, label_text: nil, selected: nil, **attrs) ⇒ void
Form builder helper: builds a label + select with options.
-
#submit(text = 'Submit', **attrs) ⇒ Node
Form builder helper: generate a submit button.
-
#text(content) ⇒ void
Add raw text content to the current context.
-
#textarea_field(name, content = nil, label_text: nil, **attrs) ⇒ void
Form builder helper: builds a label + textarea.
-
#to_html ⇒ String
Render all root-level nodes to HTML (minified).
-
#to_pretty_html(indent_size: 2) ⇒ String
Render all root-level nodes to pretty-printed HTML with indentation.
-
#use_component(name, **locals) ⇒ void
Render a previously defined component.
Constructor Details
#initialize ⇒ Builder
Returns a new instance of Builder.
22 23 24 25 26 27 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 22 def initialize @root_children = [] @stack = [] @components = {} @cache_store = {} end |
Instance Method Details
#cache(key) { ... } ⇒ void
This method returns an undefined value.
Cache a rendered block result by key
On the first call with a given key, the block is executed, its rendered HTML is stored, and a raw node is appended. On subsequent calls with the same key, the cached HTML is appended without re-executing the block.
277 278 279 280 281 282 283 284 285 286 287 288 289 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 277 def cache(key, &block) raise Error, 'a block is required for cache' unless block if @cache_store.key?(key) raw(@cache_store[key]) else nested = Builder.new nested.instance_eval(&block) html = nested.to_html @cache_store[key] = html raw(html) end end |
#class_names(*args) ⇒ String
Build a space-joined CSS class string from mixed arguments
Strings are included as-is. Hash keys are included when their value is truthy.
255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 255 def class_names(*args) result = [] args.each do |arg| case arg when Hash arg.each { |key, val| result << key.to_s if val } else result << arg.to_s end end result.join(' ') end |
#define_component(name) { ... } ⇒ void
This method returns an undefined value.
Define a reusable named component
122 123 124 125 126 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 122 def define_component(name, &block) raise Error, 'a block is required for define_component' unless block @components[name.to_sym] = block end |
#doctype ⇒ void
This method returns an undefined value.
Emit an HTML5 doctype declaration (‘<!DOCTYPE html>`)
Has no children and no attributes. In pretty mode the declaration is rendered on its own line at the current indentation.
89 90 91 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 89 def doctype current_children << DoctypeNode.new end |
#field(name, label_text: nil, type: 'text', **attrs) ⇒ void
This method returns an undefined value.
Form builder helper: builds a label + input pair
162 163 164 165 166 167 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 162 def field(name, label_text: nil, type: 'text', **attrs) field_id = attrs.delete(:id) || name.to_s.tr('_', '-') label_str = label_text || name.to_s.gsub('_', ' ').split.map(&:capitalize).join(' ') label label_str, for: field_id input(type: type, name: name.to_s, id: field_id, **attrs) end |
#form_for(action, method_type: 'post', **attrs) { ... } ⇒ Node
Form builder helper: builds a form tag with common defaults
151 152 153 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 151 def form_for(action, method_type: 'post', **attrs, &block) form(action: action, method: method_type, **attrs, &block) end |
#hidden_field(name, value) ⇒ Node
Form builder helper: generate a hidden input field
216 217 218 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 216 def hidden_field(name, value) input(type: 'hidden', name: name.to_s, value: value.to_s) end |
#list(items, ordered: false, **attrs) {|item| ... } ⇒ Node
Build a list (ul or ol) from an array of items
236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 236 def list(items, ordered: false, **attrs, &block) tag_name = ordered ? :ol : :ul send(tag_name, **attrs) do items.each do |item| if block li { block.call(item) } else li item.to_s end end end end |
#raw(html) ⇒ void
This method returns an undefined value.
Add raw HTML content without escaping
78 79 80 81 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 78 def raw(html) node = RawNode.new(html) current_children << node end |
#render_if(condition) { ... } ⇒ void
This method returns an undefined value.
Conditionally render a block if the condition is truthy
98 99 100 101 102 103 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 98 def render_if(condition, &block) return unless condition raise Error, 'a block is required for render_if' unless block instance_eval(&block) end |
#render_unless(condition) { ... } ⇒ void
This method returns an undefined value.
Conditionally render a block if the condition is falsy
110 111 112 113 114 115 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 110 def render_unless(condition, &block) return if condition raise Error, 'a block is required for render_unless' unless block instance_eval(&block) end |
#select_field(name, options_list, label_text: nil, selected: nil, **attrs) ⇒ void
This method returns an undefined value.
Form builder helper: builds a label + select with options
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 177 def select_field(name, , label_text: nil, selected: nil, **attrs) field_id = attrs.delete(:id) || name.to_s.tr('_', '-') label_str = label_text || name.to_s.gsub('_', ' ').split.map(&:capitalize).join(' ') label label_str, for: field_id select(name: name.to_s, id: field_id, **attrs) do .each do |opt| if opt.is_a?(Array) opt_text, opt_value = opt option_attrs = { value: opt_value.to_s } option_attrs[:selected] = true if opt_value.to_s == selected.to_s option opt_text, **option_attrs else option_attrs = { value: opt.to_s } option_attrs[:selected] = true if opt.to_s == selected.to_s option opt.to_s, **option_attrs end end end end |
#submit(text = 'Submit', **attrs) ⇒ Node
Form builder helper: generate a submit button
225 226 227 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 225 def submit(text = 'Submit', **attrs) (text, type: 'submit', **attrs) end |
#text(content) ⇒ void
This method returns an undefined value.
Add raw text content to the current context
70 71 72 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 70 def text(content) current_children << content.to_s end |
#textarea_field(name, content = nil, label_text: nil, **attrs) ⇒ void
This method returns an undefined value.
Form builder helper: builds a label + textarea
204 205 206 207 208 209 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 204 def textarea_field(name, content = nil, label_text: nil, **attrs) field_id = attrs.delete(:id) || name.to_s.tr('_', '-') label_str = label_text || name.to_s.gsub('_', ' ').split.map(&:capitalize).join(' ') label label_str, for: field_id textarea(content, name: name.to_s, id: field_id, **attrs) end |
#to_html ⇒ String
Render all root-level nodes to HTML (minified)
32 33 34 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 32 def to_html @root_children.map { |c| c.respond_to?(:to_html) ? c.to_html : Escape.html(c.to_s) }.join end |
#to_pretty_html(indent_size: 2) ⇒ String
Render all root-level nodes to pretty-printed HTML with indentation
40 41 42 43 44 45 46 47 48 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 40 def to_pretty_html(indent_size: 2) @root_children.map do |c| if c.respond_to?(:to_html) c.to_html(indent: 0, indent_size: indent_size) else Escape.html(c.to_s) end end.join("\n") end |
#use_component(name, **locals) ⇒ void
This method returns an undefined value.
Render a previously defined component
133 134 135 136 137 138 139 140 141 142 |
# File 'lib/philiprehberger/html_builder/builder.rb', line 133 def use_component(name, **locals) block = @components[name.to_sym] raise Error, "undefined component: #{name}" unless block if block.arity.zero? || (block.arity.negative? && locals.empty?) instance_eval(&block) else instance_exec(locals, &block) end end |