undies 2.2.1 → 3.0.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- data/ARCH.md +116 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +20 -4
- data/LICENSE +22 -0
- data/README.md +343 -0
- data/Rakefile +25 -17
- data/bench/bench_runner.rb +132 -12
- data/bench/large.html.erb +9 -13
- data/bench/large.html.haml +11 -0
- data/bench/large.html.rb +8 -12
- data/bench/profiler +1 -1
- data/bench/profiler_runner.rb +2 -5
- data/bench/small.html.erb +9 -13
- data/bench/small.html.haml +11 -0
- data/bench/small.html.rb +8 -12
- data/bench/verylarge.html.erb +9 -13
- data/bench/verylarge.html.haml +11 -0
- data/bench/verylarge.html.rb +8 -12
- data/lib/undies/api.rb +163 -0
- data/lib/undies/element.rb +160 -80
- data/lib/undies/element_node.rb +116 -0
- data/lib/undies/io.rb +43 -0
- data/lib/undies/root_node.rb +62 -0
- data/lib/undies/source.rb +78 -2
- data/lib/undies/template.rb +17 -131
- data/lib/undies/version.rb +1 -1
- data/lib/undies.rb +3 -2
- data/test/element_closed_test.rb +69 -0
- data/test/element_node_test.rb +274 -0
- data/test/element_open_test.rb +101 -0
- data/test/element_test.rb +23 -196
- data/test/fixtures/write_thing.rb +4 -4
- data/test/helper.rb +84 -0
- data/test/io_test.rb +104 -0
- data/test/named_source_test.rb +1 -1
- data/test/raw_test.rb +25 -0
- data/test/root_node_test.rb +108 -0
- data/test/source_stack_test.rb +1 -1
- data/test/template_builder_render_test.rb +4 -9
- data/test/template_source_render_test.rb +16 -20
- data/test/template_test.rb +87 -80
- data/test/templates/content.html.rb +1 -1
- data/test/templates/test.html.rb +1 -1
- data/undies.gemspec +1 -0
- metadata +52 -23
- data/README.rdoc +0 -203
- data/lib/undies/named_source.rb +0 -54
- data/lib/undies/node.rb +0 -87
- data/lib/undies/node_stack.rb +0 -111
- data/lib/undies/output.rb +0 -31
- data/lib/undies/source_stack.rb +0 -22
- data/test/node_stack_test.rb +0 -109
- data/test/node_test.rb +0 -91
- data/test/output_test.rb +0 -69
data/ARCH.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# Undies Models
|
2
|
+
|
3
|
+
## `IO`
|
4
|
+
|
5
|
+
Used internally to handle generated output streaming. An instance of this class is required when defining Template objects.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
output = ""
|
9
|
+
templ = Undies::Template.new(Undies::IO.new(output, :pp => 2))
|
10
|
+
```
|
11
|
+
|
12
|
+
Create IO objects by passing two arguments:
|
13
|
+
|
14
|
+
* *stream*: a stream for writing generated output to. the only requirement is it must respond to `<<`.
|
15
|
+
* *options hash*: a hash of output options:
|
16
|
+
** `:pp`: (pretty print) indent size. an integer specifying how many spaces each level of indent should be. is `nil` by default and implies no pretty printed output
|
17
|
+
** `:pp_level`: the starting level for pretty printing. is zero by default.
|
18
|
+
|
19
|
+
### API
|
20
|
+
|
21
|
+
#### Attributes
|
22
|
+
|
23
|
+
* `stream`: the io steam being written to
|
24
|
+
* `newline`: the newline character for this IO. set to `"\n"` if `:pp` option is not nil, `""` otherwise
|
25
|
+
* `level`: the current pp level
|
26
|
+
* `indent`: the amount of spaces in each indent level
|
27
|
+
* `node_stack`: the stack of nodes being written on
|
28
|
+
* `current`: the current node being written (`node_stack.last`)
|
29
|
+
|
30
|
+
#### Methods
|
31
|
+
|
32
|
+
* `write`: write the given text to the io stream. aliased as `<<`.
|
33
|
+
* `push`: given a node object, pushes it onto the node stack and increments the level
|
34
|
+
* `push!`: given a node object, pushes it onto the node stack - no level changes
|
35
|
+
* `pop`: pops the current node from the node stack and decrements the level
|
36
|
+
* `options=`: pass a hash to reset (no merge) the IO options
|
37
|
+
|
38
|
+
|
39
|
+
## `Template`
|
40
|
+
|
41
|
+
Build with an IO object to generate tempalated markup. Can supply a data hash to render on. Can either supply Source templates that are evaluated in the Template scope, or you can build the template object and drive using a more builder like render approach. See the README for more details on building templates and rendering.
|
42
|
+
|
43
|
+
### API
|
44
|
+
|
45
|
+
#### Markup methods
|
46
|
+
|
47
|
+
* `_`: pass a string to insert escaped markup or text
|
48
|
+
* `__`: pass a string to insert unescaped markup or text
|
49
|
+
* `_<element>`: any method prefixed with an '_' will define an element with that name. Aliased as `tag` and `element`.
|
50
|
+
* `__attrs`: pass a Hash to modify parent element attributes within the build. Merges with current attributes.
|
51
|
+
* `__yield`: render nested content (ie from a layout) when rendering using nested Source objects.
|
52
|
+
* `__partial`: insert markup generated with its own source/data/scope into the template.
|
53
|
+
|
54
|
+
#### Flow Control methods
|
55
|
+
|
56
|
+
* `__push`: used to change the template scope to modify on the latest child element. Needed in manual render approach.
|
57
|
+
* `__pop`: used to change the template scope back to modify on the parent element.
|
58
|
+
* `__flush`: used to flush any cached markup to the IO. You must call this after rendering is complete if using the builder or manual render approaches. Can call using the singleton Template variant: `Undies::Template.flush(my_template_obj)`
|
59
|
+
|
60
|
+
|
61
|
+
## `RootNode`
|
62
|
+
|
63
|
+
Used internally to implement the markup tree nodes. Each node caches and processes nested markup and elements. At each node level in the markup tree, nodes/markup are cached until the next sibling node or raw markup is defined, or until the node is flushed. This keeps nodes from bloating memory on large documents and allows for output streaming.
|
64
|
+
|
65
|
+
### API
|
66
|
+
|
67
|
+
#### Attributes
|
68
|
+
|
69
|
+
* `__cached`: the currently cached markup/element
|
70
|
+
* `__builds`: the builds that should be run in the root scope
|
71
|
+
|
72
|
+
#### Methods
|
73
|
+
|
74
|
+
* `__attrs`: does nothing. If called on the root of a document it has no effect.
|
75
|
+
* `__flush`: flush any cached markup to the IO
|
76
|
+
* `__markup`: pass raw markup. will write any cached markup and cache this new markup.
|
77
|
+
* `__element`: pass an element node. will write any cached markup and cache the new element.
|
78
|
+
* `__partial`: insert raw markup (just calls __markup, needed by Template API)
|
79
|
+
* `__push`: will clear and push the cached node/markup to the IO handler, changing the Template API scope to that node/markup.
|
80
|
+
* `__pop`: flush. should have no effect on the IO handler if called.
|
81
|
+
|
82
|
+
|
83
|
+
## `Element`
|
84
|
+
|
85
|
+
Handles tag element markup and the scope for building and nesting markup.
|
86
|
+
|
87
|
+
### API
|
88
|
+
|
89
|
+
#### Singleton Methods
|
90
|
+
|
91
|
+
* `Element#hash_attrs`: given a nested hash, serialize to markup tag attribute string
|
92
|
+
* `Element#escape_attr_value`: given a value, escape it for attribute string use
|
93
|
+
|
94
|
+
#### Attributes
|
95
|
+
|
96
|
+
* `__cached`: the currently cached child markup/element
|
97
|
+
* `__builds`: any builds should be run against the Element
|
98
|
+
|
99
|
+
#### Methods
|
100
|
+
|
101
|
+
* `__attrs`: pass a hash. merges given hash with element attrbutes. only has an effect if called before the start tag is written (before any child elements or markup is defined).
|
102
|
+
* `__flush`: flush any cached markup to the IO
|
103
|
+
* `__markup`: pass raw markup. will write any cached markup and cache this new markup. writes the start tag appropriately if not already written.
|
104
|
+
* `__element`: pass an element node. will write any cached markup and cache the new element. writes the start tag appropriately if not already written.
|
105
|
+
* `__partial`: insert raw markup as child element markup (just calls __element, needed by Template API)
|
106
|
+
* `__push`: will clear and push the cached node/markup to the IO handler, changing the Template API scope to that node/markup.
|
107
|
+
* `__pop`: flushes any cached markup and will pop the current node/markup from the IO handler. changes the Template API scope to that parent node/markup. finally writes the end tag appropriately.
|
108
|
+
* `to_s`: pushes itself to the IO handler, changing the Template API scope to operate on itself. calls any builds for the element. calls `__pop` once builds have run.
|
109
|
+
|
110
|
+
## `Source`
|
111
|
+
|
112
|
+
TODO
|
113
|
+
|
114
|
+
## `SourceStack`
|
115
|
+
|
116
|
+
TODO
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,20 +1,32 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
undies (
|
4
|
+
undies (3.0.0.rc.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: http://rubygems.org/
|
8
8
|
specs:
|
9
|
-
ansi (1.4.
|
9
|
+
ansi (1.4.2)
|
10
10
|
assert (0.7.3)
|
11
11
|
assert-view (~> 0.5)
|
12
|
-
assert-view (0.
|
12
|
+
assert-view (0.6.0)
|
13
13
|
ansi (~> 1.3)
|
14
|
-
|
14
|
+
builder (3.0.0)
|
15
|
+
erector (0.8.3)
|
16
|
+
rake
|
17
|
+
rake
|
18
|
+
treetop
|
19
|
+
treetop (>= 1.2.3)
|
15
20
|
erubis (2.7.0)
|
21
|
+
haml (3.1.4)
|
22
|
+
markaby (0.7.2)
|
23
|
+
builder (>= 2.0.0)
|
24
|
+
polyglot (0.3.3)
|
16
25
|
rake (0.9.2)
|
17
26
|
ruby-prof (0.10.8)
|
27
|
+
treetop (1.4.10)
|
28
|
+
polyglot
|
29
|
+
polyglot (>= 0.3.1)
|
18
30
|
whysoslow (0.0.2)
|
19
31
|
ansi (~> 1.4)
|
20
32
|
|
@@ -24,8 +36,12 @@ PLATFORMS
|
|
24
36
|
DEPENDENCIES
|
25
37
|
ansi
|
26
38
|
assert (~> 0.7.3)
|
39
|
+
assert-view (~> 0.6)
|
27
40
|
bundler (~> 1.0)
|
41
|
+
erector
|
28
42
|
erubis
|
43
|
+
haml
|
44
|
+
markaby
|
29
45
|
rake (~> 0.9.2)
|
30
46
|
ruby-prof
|
31
47
|
undies!
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2011-Present Kelly Redding
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,343 @@
|
|
1
|
+
# Undies
|
2
|
+
A pure-Ruby DSL for streaming templated HTML, XML, or plain text. Named for its gratuitous use of the underscore.
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
|
6
|
+
Add this line to your application's Gemfile:
|
7
|
+
|
8
|
+
gem 'undies'
|
9
|
+
|
10
|
+
And then execute:
|
11
|
+
|
12
|
+
$ bundle
|
13
|
+
|
14
|
+
Or install it yourself as:
|
15
|
+
|
16
|
+
$ gem install undies
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
_html {
|
22
|
+
_head {
|
23
|
+
_title "Hello World"
|
24
|
+
}
|
25
|
+
body {
|
26
|
+
_h1.main!.big "Hi There!"
|
27
|
+
_p "this is an ", em("Undies"), " usage example."
|
28
|
+
}
|
29
|
+
}
|
30
|
+
```
|
31
|
+
|
32
|
+
Will stream out
|
33
|
+
|
34
|
+
```
|
35
|
+
<html>
|
36
|
+
<head>
|
37
|
+
<title>Hello World</title>
|
38
|
+
</head>
|
39
|
+
<body>
|
40
|
+
<h1 class="big" id="main">Hi There!</h1>
|
41
|
+
<p>this is an <em>Undies</em> usage example.</p>
|
42
|
+
</body>
|
43
|
+
</html>
|
44
|
+
```
|
45
|
+
|
46
|
+
## DSL
|
47
|
+
|
48
|
+
### Plain text
|
49
|
+
|
50
|
+
All text is escaped by default.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
Undies::Template.escape_html("ab&<>'\"/yz")
|
54
|
+
# => "ab&<>'"/yz"
|
55
|
+
```
|
56
|
+
|
57
|
+
Capture raw (un-escaped) text using the `raw` method
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
raw "this will <em>not</em> be escaped"
|
61
|
+
# => "this will <em>not</em> be escaped"
|
62
|
+
```
|
63
|
+
|
64
|
+
### XML
|
65
|
+
|
66
|
+
Capture an empty element:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
element(:thing) # => "<thing />"
|
70
|
+
tag('ns:thing') # => "<ns:thing />"
|
71
|
+
```
|
72
|
+
|
73
|
+
Capture an element with content:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
# basic content
|
77
|
+
|
78
|
+
element(:thing, "some content")
|
79
|
+
# => "<thing>some content</thing>"
|
80
|
+
|
81
|
+
# all content is escaped by default
|
82
|
+
|
83
|
+
element(:thing, "&<>")
|
84
|
+
# => "<thing>&<></thing>"
|
85
|
+
|
86
|
+
# you can force raw content using the `raw` method
|
87
|
+
|
88
|
+
element(:thing, raw("<raw>text</raw>"))
|
89
|
+
# => "<thing><raw>text</raw></thing>"
|
90
|
+
|
91
|
+
# you can pass in as many pieces of content as you like
|
92
|
+
|
93
|
+
element(:thing, "1 < 2", raw("<raw>text</raw>"), " & 4 > 3")
|
94
|
+
# => "<thing>1 < 2<raw>text</raw>& 4 > 3</thing>"
|
95
|
+
```
|
96
|
+
|
97
|
+
Capture an element with attributes:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
element(:thing, "some content", :one => 1, 'a' => "Aye")
|
101
|
+
# => "<thing one=\"1\" a=\"Aye\">some content</thing>"
|
102
|
+
```
|
103
|
+
|
104
|
+
Capture nested elements:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
element(:thing) {
|
108
|
+
element('Something', "Some Content")
|
109
|
+
element('AnotherThing', "more content")
|
110
|
+
} # => "<thing><Something>Some Content</Something><AnotherThing>more content</AnotherThing></thing>"
|
111
|
+
```
|
112
|
+
|
113
|
+
### HTML
|
114
|
+
|
115
|
+
In general, all the same stuff applies. However, you can call specific methods for all non-deprecated elements from the HTML 4.0.1 spec.
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
br
|
119
|
+
# => "<br />"
|
120
|
+
|
121
|
+
span "something"
|
122
|
+
# => "<span>something</span>"
|
123
|
+
|
124
|
+
div "something", :style => "color: red"
|
125
|
+
# => "<div style=\"color: red\">somthing</div"
|
126
|
+
|
127
|
+
html {
|
128
|
+
head { title "Hello World" }
|
129
|
+
body {
|
130
|
+
h1 "Hi There!"
|
131
|
+
}
|
132
|
+
} # => "<html><head><title>Hello World</title></head><body<h1>Hi There!</h1></body></html>"
|
133
|
+
```
|
134
|
+
|
135
|
+
You can't mix content and nested elements. If you specify content in the element arguments, any nested element blocks will be ignored
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
div("contents") {
|
139
|
+
span "more content"
|
140
|
+
}
|
141
|
+
# => "<div>contents</div>"
|
142
|
+
```
|
143
|
+
|
144
|
+
Use bang (!) method calls to set id attributes
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
h1.header!
|
148
|
+
# => "<h1 id=\"header\" />"
|
149
|
+
|
150
|
+
h1.header!.title!
|
151
|
+
# => "<h1 id=\"title\" />"
|
152
|
+
```
|
153
|
+
|
154
|
+
Use general method calls to add class attributes
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
_h1.header.awesome
|
158
|
+
# => "<h1 class=\"header awesome\" />"
|
159
|
+
```
|
160
|
+
|
161
|
+
Use both in combination
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
h1.header!.awesome
|
165
|
+
# => "<h1 class=\"awesome\" id=\"header\" />
|
166
|
+
```
|
167
|
+
|
168
|
+
## Streamed Output
|
169
|
+
|
170
|
+
Up to this point we've just looked at 'capture methods' - the generated output is just returned as a string. Undies, however, is designed to stream generated content to a given IO. This has a number of advantages:
|
171
|
+
|
172
|
+
* content is written out immediately
|
173
|
+
* maintain a relatively low memory profile while rendering
|
174
|
+
* can process large templates with linear performance impact
|
175
|
+
|
176
|
+
### Plain text
|
177
|
+
|
178
|
+
Stream plain text
|
179
|
+
*note*: this is only valid at the root of the view. to add plain text to an element, pass it in as an argument. it will get streamed out as the element is streamed.
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
_ "this will be escaped"
|
183
|
+
|
184
|
+
_ raw("this will not be escaped")
|
185
|
+
```
|
186
|
+
|
187
|
+
### XML
|
188
|
+
|
189
|
+
Stream xml element markup. Call the element and tag methods with two leading underscores.
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
__element(:thing)
|
193
|
+
|
194
|
+
__tag('ns:thing')
|
195
|
+
```
|
196
|
+
|
197
|
+
All other element handling is the same.
|
198
|
+
|
199
|
+
### HTML
|
200
|
+
|
201
|
+
Stream html markup. Call the html element methods with a leading underscore.
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
_br
|
205
|
+
|
206
|
+
_span "something"
|
207
|
+
|
208
|
+
_div "something", :style => "color: red"
|
209
|
+
|
210
|
+
_html {
|
211
|
+
_head { _title "Hello World" }
|
212
|
+
_body {
|
213
|
+
_h1 "Hi There!"
|
214
|
+
}
|
215
|
+
}
|
216
|
+
```
|
217
|
+
|
218
|
+
All other element handling is the same.
|
219
|
+
|
220
|
+
### Notes on streamed output
|
221
|
+
|
222
|
+
* because content is streamed then forgotten as it is being rendered, streamed elements cannot be self-referrential. No one element may refer to other previously rendered elements.
|
223
|
+
* streamed output will honor pretty printing settings - captured output is never pretty printed
|
224
|
+
* elements specified with content are printed on a single line
|
225
|
+
* elements specified with nested elements are always printed on multiple lines
|
226
|
+
* in general, define markup using streaming method calls for the main markup and add inline elements to content using the capture methods
|
227
|
+
* captured element output is always handled as raw markup and won't be escaped
|
228
|
+
|
229
|
+
## Rendering
|
230
|
+
|
231
|
+
To render using Undies, create a Template instance, providing the template source, data, and io information.
|
232
|
+
|
233
|
+
source = Undies::Source.new("/path/to/sourcefile")
|
234
|
+
data = { :two_plus_two => 4 }
|
235
|
+
io = Undies::IO.new(@some_io_stream)
|
236
|
+
|
237
|
+
Undies::Template.new(source, {}, io)
|
238
|
+
|
239
|
+
### Source
|
240
|
+
|
241
|
+
You specify Undies source using the Undies::Source object. You can create source either form a block or a file. Source content (either block or file) will be evaluated in context of the template.
|
242
|
+
|
243
|
+
### Data
|
244
|
+
|
245
|
+
Undies renders source content in the isolated scope of the Template. This means that content has access to only the data it is given or the Undies API itself. When you define a template for rendering, you provide not only the template source, but any data that source should be rendered with. Data is given in the form of a Hash. The string form of the hash keys are exposed as local instance variables assigned their corresponding values.
|
246
|
+
|
247
|
+
### IO
|
248
|
+
|
249
|
+
As said before, Undies streams to a given io stream. You specify a Template's io by creating an Undies::IO object. These objects take a stream and a hash of options:
|
250
|
+
|
251
|
+
* :pp (pretty-print) : set to a Fixnum to space-indent pretty print the streamed output.
|
252
|
+
* :level : the starting level to render pretty printed output at, default is zero
|
253
|
+
|
254
|
+
### Examples
|
255
|
+
|
256
|
+
file source, no local data, no pretty printing
|
257
|
+
|
258
|
+
```ruby
|
259
|
+
source = Undies::Source.new("/path/to/source")
|
260
|
+
Undies::Template.new(source, {}, Undies::IO.new(@io))
|
261
|
+
```
|
262
|
+
|
263
|
+
proc source, simple local data, no pretty printing
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
source = Undies::Source.new(Proc.new do
|
267
|
+
_div {
|
268
|
+
_ @content.to_s
|
269
|
+
}
|
270
|
+
end)
|
271
|
+
Undies::Template.new(source, {:content => "Some Content!!" }, Undies::IO.new(@io))
|
272
|
+
```
|
273
|
+
|
274
|
+
pretty printing (4 space tab indentation)
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
source = Undies::Source.new("/path/to/source")
|
278
|
+
Undies::Template.new(source, {}, Undies::IO.new(@io, :pp => 4))
|
279
|
+
```
|
280
|
+
|
281
|
+
### Builder approach
|
282
|
+
|
283
|
+
The above examples use the "source rendering" approach. This works great when you know your source content before render time and create a source object from it (ie rendering a view template). However, in some cases, you may not know the source until render time and/or want to use a more declarative style to specify render output. Undies content can be specified programmatically using the "builder rendering" approach.
|
284
|
+
|
285
|
+
To render using this approach, create a Template instance passing it data and io info as above. However, don't pass in any source info, only pass in any local data if you like, and save off the created template:
|
286
|
+
|
287
|
+
```ruby
|
288
|
+
# choosing not to use any local data in this example
|
289
|
+
template = Undies::Template.new(Undies::IO.new(@io))
|
290
|
+
```
|
291
|
+
|
292
|
+
Now just interact with the Undies API directly.
|
293
|
+
|
294
|
+
```ruby
|
295
|
+
# notice that it becomes less important to bind any local data to the Template using this approach
|
296
|
+
something = "Some Thing!"
|
297
|
+
template._div.something! template._ something.to_s
|
298
|
+
|
299
|
+
template._div {
|
300
|
+
template._span "hi"
|
301
|
+
}
|
302
|
+
```
|
303
|
+
|
304
|
+
*Note:* there is one extra caveat to be aware of using this approach. You need to be sure and flush the template when content processing is complete. Just pass the template to the Undies::Template#flush method:
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
# ensures all content is streamed to the template's io stream
|
308
|
+
# this is necessary when not using the source approach above
|
309
|
+
Undies::Template.flush(template)
|
310
|
+
```
|
311
|
+
|
312
|
+
### Manual approach
|
313
|
+
|
314
|
+
There is another method you can use to render output: the manual approach. Like the builder approach, this method is ideal when you don't know the source until render time. The key difference is that blocks are not used to imply nesting relationships. Using this approach, you manually 'push' and 'pop' to move up and down nesting relationship contexts. So a push on an element would move the template context to the element pushed. A pop would move back to the current context's parent element. As you would expect, pop'ing on the root of a template has no effect on the context and pushing a non-element node has no effect on the context.
|
315
|
+
|
316
|
+
To render using this approach, create a Template as you would with the Builder approach. Interact with the Undies API directly. Use the Template#__push and Template#__pop methods to change the template scope.
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
# this is the equivalent to the Builder approach example above
|
320
|
+
|
321
|
+
template = Undies::Template.new(Undies::IO.new(@io))
|
322
|
+
|
323
|
+
something = "Some Thing!"
|
324
|
+
template._div.something! something.to_s
|
325
|
+
|
326
|
+
template._div
|
327
|
+
template.__push
|
328
|
+
template._span "hi"
|
329
|
+
template.__pop
|
330
|
+
|
331
|
+
# alternate method for flushing a template
|
332
|
+
template.__flush
|
333
|
+
```
|
334
|
+
|
335
|
+
*Note:* as with the Builder approach, you must flush the template when content processing is complete.
|
336
|
+
|
337
|
+
## Contributing
|
338
|
+
|
339
|
+
1. Fork it
|
340
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
341
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
342
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
343
|
+
5. Create new Pull Request
|
data/Rakefile
CHANGED
@@ -4,26 +4,34 @@ Assert::RakeTasks.for(:test)
|
|
4
4
|
require 'bundler'
|
5
5
|
Bundler::GemHelper.install_tasks
|
6
6
|
|
7
|
-
task :default => :
|
7
|
+
task :default => :build
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
namespace :bench do
|
10
|
+
|
11
|
+
desc "Run the bench script."
|
12
|
+
task :run do
|
13
|
+
require 'bench/bench_runner'
|
14
|
+
UndiesBenchRunner.new
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the profiler on 1000 rows."
|
18
|
+
task :profiler do
|
19
|
+
require 'bench/profiler_runner'
|
20
|
+
UndiesProfilerRunner.new('verylarge').print_flat(STDOUT, :min_percent => 1)
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Run all the tests, then the profiler, then the bench."
|
24
|
+
task :all do
|
25
|
+
Rake::Task['test'].invoke
|
26
|
+
puts
|
27
|
+
Rake::Task['run_profiler'].invoke
|
28
|
+
puts
|
29
|
+
Rake::Task['run_bench'].invoke
|
30
|
+
end
|
14
31
|
|
15
|
-
desc "Run the benchmark script."
|
16
|
-
task :run_bench do
|
17
|
-
require 'bench/bench_runner'
|
18
|
-
UndiesBenchRunner.new
|
19
32
|
end
|
20
33
|
|
21
|
-
|
22
|
-
|
23
|
-
Rake::Task['test'].invoke
|
24
|
-
puts
|
25
|
-
Rake::Task['run_profiler'].invoke
|
26
|
-
puts
|
27
|
-
Rake::Task['run_bench'].invoke
|
34
|
+
task :bench do
|
35
|
+
Rake::Task['bench:run'].invoke
|
28
36
|
end
|
29
37
|
|