Class: Tina4::HtmlElement

Inherits:
Object
  • Object
show all
Defined in:
lib/tina4/html_element.rb

Overview

Programmatic HTML builder — avoids string concatenation.

Usage:

el = Tina4::HtmlElement.new("div", { class: "card" }, ["Hello"])
el.to_s  # => '<div class="card">Hello</div>'

# Builder pattern (via call)
el = Tina4::HtmlElement.new("div").call(Tina4::HtmlElement.new("p").call("Text"))

# Helper functions
include Tina4::HtmlHelpers
html = _div({ class: "card" }, _p("Hello"))

String/scalar children are HTML-escaped by default to defeat XSS; nested HtmlElement children render themselves (already escaped, no double-escape); a Raw / SafeString child renders verbatim (explicit opt-in for trusted markup).

Constant Summary collapse

VOID_TAGS =
%w[
  area base br col embed hr img input
  link meta param source track wbr
].freeze
HTML_TAGS =
%w[
  a abbr address area article aside audio
  b base bdi bdo blockquote body br button
  canvas caption cite code col colgroup
  data datalist dd del details dfn dialog div dl dt
  em embed
  fieldset figcaption figure footer form
  h1 h2 h3 h4 h5 h6 head header hgroup hr html
  i iframe img input ins
  kbd
  label legend li link
  main map mark menu meta meter
  nav noscript
  object ol optgroup option output
  p param picture pre progress
  q
  rp rt ruby
  s samp script section select slot small source span
  strong style sub summary sup
  table tbody td template textarea tfoot th thead time
  title tr track
  u ul
  var video
  wbr
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tag, attrs = {}, children = []) ⇒ HtmlElement

Returns a new instance of HtmlElement.

Parameters:

  • tag (String)

    HTML tag name

  • attrs (Hash) (defaults to: {})

    attribute => value

  • children (Array) (defaults to: [])

    child elements (strings or HtmlElement instances)



81
82
83
84
85
# File 'lib/tina4/html_element.rb', line 81

def initialize(tag, attrs = {}, children = [])
  @tag = tag.to_s.downcase
  @attrs = attrs
  @children = children
end

Instance Attribute Details

#attrsObject (readonly)

Returns the value of attribute attrs.



76
77
78
# File 'lib/tina4/html_element.rb', line 76

def attrs
  @attrs
end

#childrenObject (readonly)

Returns the value of attribute children.



76
77
78
# File 'lib/tina4/html_element.rb', line 76

def children
  @children
end

#tagObject (readonly)

Returns the value of attribute tag.



76
77
78
# File 'lib/tina4/html_element.rb', line 76

def tag
  @tag
end

Instance Method Details

#call(*args) ⇒ HtmlElement

Builder pattern — appends children and/or merges attributes.

Parameters:

  • args (Array)

    Strings, HtmlElements, Hashes (treated as attrs), or Arrays

Returns:

  • (HtmlElement)

    a new HtmlElement with the appended children



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/tina4/html_element.rb', line 91

def call(*args)
  new_attrs = @attrs.dup
  new_children = @children.dup

  args.each do |arg|
    case arg
    when Hash
      new_attrs = new_attrs.merge(arg)
    when Array
      new_children.concat(arg)
    else
      new_children << arg
    end
  end

  self.class.new(@tag, new_attrs, new_children)
end

#to_sObject

Render to HTML string.



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/tina4/html_element.rb', line 110

def to_s
  html = "<#{@tag}"

  @attrs.each do |key, value|
    case value
    when true
      html << " #{key}"
    when false, nil
      next
    else
      html << " #{key}=\"#{escape_text(value.to_s)}\""
    end
  end

  if VOID_TAGS.include?(@tag)
    html << ">"
    return html
  end

  html << ">"

  @children.each do |child|
    case child
    when HtmlElement
      # Nested elements render themselves (they already escape their own
      # children) — emit as-is to avoid double-escaping.
      html << child.to_s
    when Raw
      # Explicitly trusted markup — emit unescaped. Raw subclasses String,
      # so this branch MUST come before the generic scalar escape below.
      html << child.to_s
    else
      # Plain string/scalar child — escape to defeat stored/reflected XSS.
      html << escape_text(child.to_s)
    end
  end

  html << "</#{@tag}>"
  html
end