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

Constant Summary

Constants included from Hexp

Error, VERSION

Instance Method Summary (collapse)

Methods included from Hexp

Array, build, deep_freeze, included, parse

Constructor Details

- (Builder) initialize(tag = nil, *args, &block)

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`.

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

  • block (Proc)

    The block containing builder directives, can be with or without an argument.



19
20
21
22
23
24
25
26
# File 'lib/hexp/builder.rb', line 19

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

- (NilClass) 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:

  • (NilClass)


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/hexp/builder.rb', line 72

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

- (Hexp::Builder) <<(*args)

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:



117
118
119
120
121
122
123
124
125
126
# File 'lib/hexp/builder.rb', line 117

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

- (NilClass) _process(&block)

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:

  • (NilClass)


159
160
161
162
163
164
165
# File 'lib/hexp/builder.rb', line 159

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

- (String) inspect

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)


219
220
221
# File 'lib/hexp/builder.rb', line 219

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

- (NilClass) tag!(tag, *args, &block) 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:

  • (NilClass)


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/hexp/builder.rb', line 48

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

- (Hexp::Builder) text!(text)

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:



87
88
89
90
91
# File 'lib/hexp/builder.rb', line 87

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

- (Hexp::Node) to_hexp

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:



140
141
142
143
# File 'lib/hexp/builder.rb', line 140

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