Class: Hexp::Builder

Inherits:
BasicObject
Includes:
Hexp
Defined in:
lib/hexp/builder.rb

Overview

Build Hexps using the builder pattern

Defined Under Namespace

Classes: NodeBuilder

Constant Summary

Constants included from Hexp

Error, ROOT, VERSION

Instance Method Summary collapse

Methods included from Hexp

Array, build, included, parse

Constructor Details

#initialize(tag = nil, *args) {|If| ... } ⇒ Builder

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Construct a new builder, and start building

The recommended way to call this is through ‘Hexp.build`. If the block takes an argument, then builder methods need to be called on that variable.

Examples:

With an explicit builder

hi = Hexp.build {|html| html.span "Hello" ; html.span " World"}

Without a builder object

hi = Hexp.build { span "Hello" ; span " World"}

Parameters:

  • tag (Symbol) (defaults to: nil)

    The tag of the outermost element (optional)

  • args (Array<Hash,String>)

    Extra arguments, a String for a text node, a Hash for attributes

Yield Parameters:

  • If (Hexp::Builder)

    the block takes an argument it will receive the builder object



28
29
30
31
32
33
34
35
# File 'lib/hexp/builder.rb', line 28

def initialize(tag = nil, *args, &block)
  @stack = []
  if tag
    tag!(tag, *args, &block)
  else
    _process(&block) if block
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missingnil

Add a tag (HTML element)

Typically this is called implicitly through method missing, but in case of name clashes or dynamically generated tags you can call this directly.

Examples:

hexp = Hexp.build :div do
  tag!(:p, "Oh the code, such sweet joy it brings")
end
hexp.to_html #=> "<div><p>Oh the code, such sweet joy it brings</p></div>"

Parameters:

  • tag (Symbol)

    The tag name, like ‘div’ or ‘head’

  • args (Array<Hash|String>)

    A hash of attributes, or a string to use inside the tag, or both. Multiple occurences of each can be specified

  • block (Proc)

    Builder directives for the contents of the tag

Returns:

  • (nil)


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/hexp/builder.rb', line 83

def tag!(tag, *args, &block)
  text, attributes = nil, {}
  args.each do |arg|
    case arg
    when ::Hash
      attributes.merge!(arg)
    when ::String
      text ||= ''
      text << arg
    end
  end
  @stack << [tag, attributes, text ? [text] : []]
  if block
    _process(&block)
  end
  if @stack.length > 1
    node = @stack.pop
    @stack.last[2] << node
    NodeBuilder.new(node, self)
  else
    NodeBuilder.new(@stack.last, self)
  end
end

Instance Method Details

#<<(*args) ⇒ Hexp::Builder

Add Hexp objects to the current tag

Any Hexp::Node or other object implementing to_hexp can be added with this operator. Multiple objects can be specified in one call.

Nokogiri and Builder allow inserting of strings containing HTML through this operator. Since this would violate the core philosophy of Hexp, and open the door for XSS vulnerabilities, we do not support that usage.

If you really want to insert HTML that is already in serialized form, consider parsing it to Hexps first

Examples:

widget = H[:button, "click me!"]
node = Hexp.build :div do |h|
  h << widget
end
node.to_html #=> <div><button>click me!</button></div>

Parameters:

  • args (Array<#to_hexp>)

    Hexpable objects to add to the current tag

Returns:



132
133
134
135
136
137
138
139
140
141
# File 'lib/hexp/builder.rb', line 132

def <<(*args)
  args.each do |arg|
    if arg.respond_to?(:to_hexp)
      @stack.last[2] << arg
      self
    else
      ::Kernel.raise ::Hexp::FormatError, "Inserting literal HTML into a builder with << is deliberately not supported by Hexp"
    end
  end
end

#_process(&block) ⇒ nil

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Call the block, with a specific value of ‘self’

If the block takes an argument, then we pass ourselves (the builder) to the block, and call it as a closure. This way ‘self’ refers to the calling object, and it can reference its own methods and ivars.

If the block does not take an argument, then we evaluate it in the context of ourselves (the builder), so unqualified method calls are seen as builder calls.

Parameters:

  • block (Proc)

Returns:

  • (nil)


175
176
177
178
179
180
181
# File 'lib/hexp/builder.rb', line 175

def _process(&block)
  if block.arity == 1
    block.call(self)
  else
    self.instance_eval(&block)
  end
end

#inspectString

Return a debugging representation

Hexp is intended for HTML, so it shouldn’t be a problem that this is an actual method. It really helps for debugging or when playing around in irb. If you really want an ‘<inspect>` tag, use `tag!(:inspect)`.

Examples:

p Hexp.build { div }

Returns:

  • (String)


238
239
240
# File 'lib/hexp/builder.rb', line 238

def inspect
  "#<Hexp::Builder #{@stack.empty? ? '[]' : to_hexp.inspect}>"
end

#tag!(tag, *args, &block) ⇒ nil Also known as: method_missing

Add a tag (HTML element)

Typically this is called implicitly through method missing, but in case of name clashes or dynamically generated tags you can call this directly.

Examples:

hexp = Hexp.build :div do
  tag!(:p, "Oh the code, such sweet joy it brings")
end
hexp.to_html #=> "<div><p>Oh the code, such sweet joy it brings</p></div>"

Parameters:

  • tag (Symbol)

    The tag name, like ‘div’ or ‘head’

  • args (Array<Hash|String>)

    A hash of attributes, or a string to use inside the tag, or both. Multiple occurences of each can be specified

  • block (Proc)

    Builder directives for the contents of the tag

Returns:

  • (nil)


59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/hexp/builder.rb', line 59

def tag!(tag, *args, &block)
  text, attributes = nil, {}
  args.each do |arg|
    case arg
    when ::Hash
      attributes.merge!(arg)
    when ::String
      text ||= ''
      text << arg
    end
  end
  @stack << [tag, attributes, text ? [text] : []]
  if block
    _process(&block)
  end
  if @stack.length > 1
    node = @stack.pop
    @stack.last[2] << node
    NodeBuilder.new(node, self)
  else
    NodeBuilder.new(@stack.last, self)
  end
end

#text!(text) ⇒ Hexp::Builder

Add a text node to the tree

Examples:

hexp = Hexp.build do
  span do
    text! 'Not all who wander are lost'
  end
end

Parameters:

  • text (String)

    the text to add

Returns:



100
101
102
103
104
# File 'lib/hexp/builder.rb', line 100

def text!(text)
  _raise_if_empty! "Hexp::Builder needs a root element to add text elements to"
  @stack.last[2] << text.to_s
  self
end

#to_hexpHexp::Node

Implement the standard Hexp coercion protocol

By implementing this a Builder is interchangeable for a regular node, so you can use it inside other nodes transparently. But you can call this method if you really, really just want the plain Node

Examples:

Hexp.build { div { text! 'hello' } }.to_hexp # => H[:div, ["hello"]]

Returns:



155
156
157
158
# File 'lib/hexp/builder.rb', line 155

def to_hexp
  _raise_if_empty!
  ::Hexp::Node[*@stack.last]
end