Class: Undies::Template

Inherits:
Object
  • Object
show all
Defined in:
lib/undies/template.rb

Constant Summary collapse

ESCAPE_HTML =

Ripped from Rack v1.3.0 ======================================

> ripped b/c I don’t want a dependency on Rack for just this

{
  "&" => "&",
  "<" => "&lt;",
  ">" => "&gt;",
  "'" => "&#x27;",
  '"' => "&quot;",
  "/" => "&#x2F;"
}
ESCAPE_HTML_PATTERN =
Regexp.union(*ESCAPE_HTML.keys)
ELEM_METH_REGEX =

Element proxy methods (‘_<element>”) ========================

/^_(.+)$/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Template

end Rip from Rack v1.3.0 =====================================



44
45
46
47
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/undies/template.rb', line 44

def initialize(*args)
  # setup a node stack with the given output obj
  output = if args.last.kind_of?(NodeStack) || args.last.kind_of?(Output)
    args.pop
  else
    raise ArgumentError, "please provide an Output object"
  end
  @_undies_node_stack = NodeStack.create(output)

  # apply any given data to template scope
  data = args.last.kind_of?(::Hash) ? args.pop : {}
  if (data.keys.map(&:to_s) & self.public_methods.map(&:to_s)).size > 0
    raise ArgumentError, "data conflicts with template public methods."
  end
  metaclass = class << self; self; end
  data.each {|key, value| metaclass.class_eval { define_method(key){value} }}

  # setup a source stack with the given source
  source = args.last.kind_of?(Source) ? args.pop : Source.new(Proc.new {})
  @_undies_source_stack = SourceStack.new(source)

  # yield to recursivley render the source stack
  self.__yield

  # flush any elements that need to be built
  self.class.flush(self)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object



161
162
163
164
165
166
167
# File 'lib/undies/template.rb', line 161

def method_missing(meth, *args, &block)
  if meth.to_s =~ ELEM_METH_REGEX
    element($1, *args, &block)
  else
    super
  end
end

Class Method Details

.escape_html(string) ⇒ Object

Escape ampersands, brackets and quotes to their HTML/XML entities.



39
40
41
# File 'lib/undies/template.rb', line 39

def self.escape_html(string)
  string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
end

.flush(template) ⇒ Object



23
24
25
# File 'lib/undies/template.rb', line 23

def self.flush(template)
  node_stack(template).flush
end

.node_stack(template) ⇒ Object



19
20
21
# File 'lib/undies/template.rb', line 19

def self.node_stack(template)
  template.instance_variable_get("@_undies_node_stack")
end

.source_stack(template) ⇒ Object

have as many methods on the class level as possible to keep from polluting the public instance methods, the instance scope, and to maximize the effectiveness of the Template#method_missing logic



15
16
17
# File 'lib/undies/template.rb', line 15

def self.source_stack(template)
  template.instance_variable_get("@_undies_source_stack")
end

Instance Method Details

#_(data = "", mode = :inline) ⇒ Object

Add a text node (data escaped) to the nodes of the current node



140
141
142
# File 'lib/undies/template.rb', line 140

def _(data="", mode=:inline)
  self.__ self.class.escape_html(data.to_s), mode
end

#__(data = "", mode = :inline) ⇒ Object

Add a text node with the data un-escaped



145
146
147
148
149
# File 'lib/undies/template.rb', line 145

def __(data="", mode=:inline)
  Node.new(data.to_s, mode).tap do |node|
    self.class.node_stack(self).node(node)
  end
end

#__attrs(attrs_hash = {}) ⇒ Object

call this to modify element attrs inside a build block. Once content or child elements have been added, any ‘__attr’ directives will be ignored b/c the elements start_tag has already been flushed to the output



76
77
78
79
80
81
82
83
# File 'lib/undies/template.rb', line 76

def __attrs(attrs_hash={})
  self.class.node_stack(self).current.tap do |node|
    if node
      node.class.merge_attrs(node, attrs_hash)
      node.class.set_start_tag(node)
    end
  end
end

#__flushObject

call this to manually flush a template



113
114
115
# File 'lib/undies/template.rb', line 113

def __flush
  self.class.flush(self)
end

#__partial(source, data = {}) ⇒ Object

call this to render partial source embedded in a template partial source is rendered with its own scope/data but shares its parent template’s output object



131
132
133
134
135
136
137
# File 'lib/undies/template.rb', line 131

def __partial(source, data={})
  if source.kind_of?(Source)
    Undies::Template.new(source, data, self.class.node_stack(self))
  else
    self.__ source.to_s, :partial
  end
end

#__popObject

call this method to manually pop the current scoped node from the node stack

  • flushes the cache

  • changes the context of template method calls to operate on the parent node



106
107
108
109
110
# File 'lib/undies/template.rb', line 106

def __pop
  ns = self.class.node_stack(self)
  ns.clear_cached
  ns.pop
end

#__pushObject

call this method to manually push the currently cached node onto the node stack

  • implicitly flushes the cache

  • changes the context of template method calls to operate on that node



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/undies/template.rb', line 89

def __push
  ns = self.class.node_stack(self)
  node, ns.cached_node = ns.cached_node, nil
  if node
    # add an empty build block to generate a non-closing start tag
    # and a closing end tag
    node.class.add_build(node, Proc.new {})
    node.class.set_start_tag(node)
    node.class.set_end_tag(node)

    ns.push(node)
  end
end

#__yieldObject

call this to render template source use this method in layouts to insert a layout’s content source



119
120
121
122
123
124
125
126
# File 'lib/undies/template.rb', line 119

def __yield
  return if self.class.node_stack(self).nil? || (source = self.class.source_stack(self).pop).nil?
  if source.file?
    instance_eval(source.data, source.source, 1)
  else
    instance_eval(&source.data)
  end
end

#element(*args, &build) ⇒ Object Also known as: tag

Add an element to the node stack



152
153
154
155
156
# File 'lib/undies/template.rb', line 152

def element(*args, &build)
  Element.new(*args, &build).tap do |element|
    self.class.node_stack(self).node(element)
  end
end

#respond_to?(*args) ⇒ Boolean

Returns:

  • (Boolean)


168
169
170
171
172
173
174
# File 'lib/undies/template.rb', line 168

def respond_to?(*args)
  if args.first.to_s =~ ELEM_METH_REGEX
    true
  else
    super
  end
end