Class: Philiprehberger::HtmlBuilder::Builder

Inherits:
Object
  • Object
show all
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

Constructor Details

#initializeBuilder

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.

Parameters:

  • key (Object)

    the cache key

Yields:

  • the block to render and cache

Raises:



247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/philiprehberger/html_builder/builder.rb', line 247

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.

Parameters:

  • args (Array<String, Hash>)

    class names and conditional hashes

Returns:

  • (String)

    space-joined class string



225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/philiprehberger/html_builder/builder.rb', line 225

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

Parameters:

  • name (Symbol, String)

    the component name

Yields:

  • the block that defines the component’s HTML

Raises:



112
113
114
115
116
# File 'lib/philiprehberger/html_builder/builder.rb', line 112

def define_component(name, &block)
  raise Error, 'a block is required for define_component' unless block

  @components[name.to_sym] = block
end

#field(name, label_text: nil, type: 'text', **attrs) ⇒ void

This method returns an undefined value.

Form builder helper: builds a label + input pair

Parameters:

  • name (String, Symbol)

    the field name

  • label_text (String) (defaults to: nil)

    the label text

  • type (String) (defaults to: 'text')

    the input type (default “text”)

  • attrs (Hash)

    additional input attributes



152
153
154
155
156
157
# File 'lib/philiprehberger/html_builder/builder.rb', line 152

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

Parameters:

  • action (String)

    the form action URL

  • method_type (String) (defaults to: 'post')

    the HTTP method (default “post”)

  • attrs (Hash)

    additional attributes

Yields:

  • the form contents

Returns:



141
142
143
# File 'lib/philiprehberger/html_builder/builder.rb', line 141

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

Parameters:

  • name (String, Symbol)

    the input name attribute

  • value (String, Symbol)

    the input value attribute

Returns:



206
207
208
# File 'lib/philiprehberger/html_builder/builder.rb', line 206

def hidden_field(name, value)
  input(type: 'hidden', name: name.to_s, value: value.to_s)
end

#raw(html) ⇒ void

This method returns an undefined value.

Add raw HTML content without escaping

Parameters:

  • html (String)

    the raw HTML string



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

Parameters:

  • condition (Object)

    the condition to evaluate

Yields:

  • the block to render if condition is truthy

Raises:



88
89
90
91
92
93
# File 'lib/philiprehberger/html_builder/builder.rb', line 88

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

Parameters:

  • condition (Object)

    the condition to evaluate

Yields:

  • the block to render if condition is falsy

Raises:



100
101
102
103
104
105
# File 'lib/philiprehberger/html_builder/builder.rb', line 100

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

Parameters:

  • name (String, Symbol)

    the field name

  • options_list (Array<Array, String>)

    list of [text, value] pairs or plain strings

  • label_text (String) (defaults to: nil)

    the label text

  • selected (String, nil) (defaults to: nil)

    the selected value

  • attrs (Hash)

    additional select attributes



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/philiprehberger/html_builder/builder.rb', line 167

def select_field(name, options_list, 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
    options_list.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

Parameters:

  • text (String) (defaults to: 'Submit')

    the button text (default “Submit”)

  • attrs (Hash)

    additional button attributes

Returns:



215
216
217
# File 'lib/philiprehberger/html_builder/builder.rb', line 215

def submit(text = 'Submit', **attrs)
  button(text, type: 'submit', **attrs)
end

#text(content) ⇒ void

This method returns an undefined value.

Add raw text content to the current context

Parameters:

  • content (String)

    the text content (will be escaped)



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

Parameters:

  • name (String, Symbol)

    the field name

  • content (String, nil) (defaults to: nil)

    the textarea content

  • label_text (String) (defaults to: nil)

    the label text

  • attrs (Hash)

    additional textarea attributes



194
195
196
197
198
199
# File 'lib/philiprehberger/html_builder/builder.rb', line 194

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_htmlString

Render all root-level nodes to HTML (minified)

Returns:

  • (String)

    the rendered HTML



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

Parameters:

  • indent_size (Integer) (defaults to: 2)

    number of spaces per indent level (default 2)

Returns:

  • (String)

    the pretty-printed HTML



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

Parameters:

  • name (Symbol, String)

    the component name

  • locals (Hash)

    local variables passed to the component block

Raises:



123
124
125
126
127
128
129
130
131
132
# File 'lib/philiprehberger/html_builder/builder.rb', line 123

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