undies 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.rdoc +140 -50
- data/lib/undies/element.rb +12 -10
- data/lib/undies/node.rb +7 -1
- data/lib/undies/output.rb +4 -14
- data/lib/undies/template.rb +28 -9
- data/lib/undies/version.rb +1 -1
- data/test/element_test.rb +32 -11
- data/test/node_test.rb +8 -0
- data/test/output_test.rb +32 -19
- data/test/template_test.rb +60 -9
- data/undies.gemspec +2 -2
- metadata +6 -6
data/Gemfile.lock
CHANGED
data/README.rdoc
CHANGED
@@ -1,74 +1,164 @@
|
|
1
1
|
= Undies
|
2
|
-
A pure-Ruby DSL for
|
2
|
+
A pure-Ruby DSL for streaming templated HTML, XML, or plain text. Named for its gratuitous use of the underscore.
|
3
|
+
|
3
4
|
== Installation
|
4
5
|
$ gem install undies
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
} # => "<body><div>Some Content</div></body>"
|
19
|
-
Buffer escaped output:
|
20
|
-
_ "this will be escaped & and added to the buffer"
|
21
|
-
# => "this will be escaped & added to the buffer"
|
22
|
-
Buffer un-escaped output:
|
6
|
+
|
7
|
+
== DSL
|
8
|
+
|
9
|
+
Undies uses an underscore-based DSL for rendering content. It can render plain text, html-escaped text, xml, and html.
|
10
|
+
|
11
|
+
=== Plain text
|
12
|
+
|
13
|
+
Escaped (xml / html) text:
|
14
|
+
|
15
|
+
_ "this will be escaped & streamed"
|
16
|
+
# => "this will be escaped & streamed"
|
17
|
+
|
18
|
+
Raw (un-escaped) text:
|
23
19
|
__ "this will <em>not</em> be escaped"
|
24
20
|
# => "this will <em>not</em> be escaped"
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
|
22
|
+
=== XML
|
23
|
+
|
24
|
+
Empty node:
|
25
|
+
_thing # => "<thing />"
|
26
|
+
|
27
|
+
Node with content:
|
28
|
+
_thing {
|
29
|
+
_ "Some Content"
|
30
|
+
_ "More Content "
|
31
|
+
} # => "<thing>Some ContentMore Content</thing>"
|
32
|
+
|
33
|
+
Node with attributes:
|
34
|
+
_thing(:one => 1, 'a' => "Aye")
|
35
|
+
# => "<thing one=\"1\" a=\"Aye\" />"
|
36
|
+
|
37
|
+
Nested nodes:
|
38
|
+
_thing {
|
39
|
+
_Something {
|
40
|
+
_ "Some Content"
|
41
|
+
}
|
42
|
+
} # => "<thing><Something>Some Content</Something></thing>"
|
43
|
+
|
44
|
+
Namespaces / Doctype stuff:
|
45
|
+
__ "<?xml version="1.0" encoding="UTF-8"?>"
|
46
|
+
|
47
|
+
_thing('xmlns:ss="urn:some-cool-namespace"') {
|
48
|
+
send("_ss:Value", {'ss:some_attr' => "some attr value"}) {
|
49
|
+
_ "Some Content"
|
50
|
+
}
|
51
|
+
} # => "<thing xmlns:ss=\"urn:some-cool-namespace\"><ss:Value ss:some_attr=\"some attr value\">Some Content</ss:Value>"
|
52
|
+
|
53
|
+
Comments:
|
54
|
+
__ "<!-- some comment -->"
|
55
|
+
# => "<!-- some comment -->"
|
56
|
+
|
57
|
+
=== HTML
|
58
|
+
|
59
|
+
Same stuff applies:
|
60
|
+
__ "<!DOCTYPE html>"
|
61
|
+
|
62
|
+
_div(:style => "border-top: 1px") {
|
63
|
+
_h1 { _ "Hello World" }
|
64
|
+
_p { _ "blah blah" }
|
65
|
+
} # => "<div style=\"border-top: 1px\"><h1>Hello World</h1><p>blah blah</p></div>"
|
66
|
+
|
67
|
+
id attribute helper:
|
29
68
|
_h1.header!
|
30
69
|
# => "<h1 id=\"header\" />"
|
31
|
-
|
70
|
+
|
71
|
+
_h1.header!.title!
|
72
|
+
# => "<h1 id=\"title\" />"
|
73
|
+
|
74
|
+
class attributes helper:
|
32
75
|
_h1.header.awesome
|
33
76
|
# => "<h1 class=\"header awesome\" />"
|
34
77
|
|
35
|
-
|
78
|
+
use in combo:
|
79
|
+
_h1.header!.awesome
|
80
|
+
# => "<h1 class=\"awesome\" id=\"header\" />
|
36
81
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
82
|
+
== Streamed Output
|
83
|
+
|
84
|
+
Undies works by streaming content defined by using its DSL to a given output stream. Any call to Template#_, Template#__, or Tempate#_<node> will stream its output (or you can write helpers you mix in that use these base api methods). This has a number of advantages:
|
85
|
+
|
86
|
+
* content is written out immediately
|
87
|
+
* maintain a relatively low memory profile while rendering
|
88
|
+
* can process large templates with linear performance impact
|
89
|
+
|
90
|
+
However, because content is streamed then forgotten as it is being rendered, Undies templates cannot be self-referrential. No one element may refer to other previously rendered elements.
|
91
|
+
|
92
|
+
== Rendering
|
93
|
+
|
94
|
+
To render using Undies, create a Template instance, providing the template source, data, and output information.
|
95
|
+
|
96
|
+
source = Undies::Source.new("/path/to/sourcefile")
|
97
|
+
data = { :two_plus_two => 4 }
|
98
|
+
output = Undies::Output.new(@some_io_stream)
|
99
|
+
|
100
|
+
Undies::Template.new(source, {}, output)
|
101
|
+
|
102
|
+
=== Source
|
103
|
+
|
104
|
+
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.
|
105
|
+
|
106
|
+
=== Data
|
107
|
+
|
108
|
+
Undies renders source content in isolated scope (the context 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 methods that return their corresponding values.
|
45
109
|
|
46
|
-
|
110
|
+
=== Output
|
47
111
|
|
48
|
-
|
49
|
-
|
50
|
-
|
112
|
+
As said before, Undies streams to a given output stream. You specify a Template's output by creating an Undies::Output object. These objects take an io stream and a hash of options:
|
113
|
+
|
114
|
+
* :pp (pretty-print) : set to a Fixnum to tab-space indent pretty print the output.
|
115
|
+
|
116
|
+
=== Examples
|
117
|
+
|
118
|
+
# file source, no local data, no pretty printing
|
119
|
+
source = Undies::Source.new("/path/to/source")
|
120
|
+
Undies::Template.new(source, {}, Undies::Output.new(@io))
|
121
|
+
|
122
|
+
|
123
|
+
# proc source, simple local data, no pretty printing
|
124
|
+
source = Undies::Source.new(Proc.new do
|
51
125
|
_div {
|
52
|
-
_
|
126
|
+
_ content.to_s
|
53
127
|
}
|
54
|
-
end
|
128
|
+
end)
|
129
|
+
Undies::Template.new(source, {:content => "Some Content!!" }, Undies::Output.new(@io))
|
130
|
+
|
131
|
+
|
132
|
+
# pretty printing (4 space tab indentation)
|
133
|
+
source = Undies::Source.new("/path/to/source")
|
134
|
+
Undies::Template.new(source, {}, Undies::Output.new(@io, :pp => 4))
|
135
|
+
|
136
|
+
=== Builder approach
|
137
|
+
|
138
|
+
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.
|
139
|
+
|
140
|
+
To render using this approach, create a Template instance passing it data and output 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:
|
141
|
+
|
142
|
+
# choosing not to use any local data in this example
|
143
|
+
template = Undies::Template.new(Undies::Output.new(@io))
|
144
|
+
|
145
|
+
Now just interact with the Undies API directly.
|
55
146
|
|
56
|
-
|
147
|
+
# notice that it becomes less important to bind any local data to the Template using this approach
|
148
|
+
something = "Some Thing!"
|
149
|
+
template._div.something! {
|
150
|
+
template._ something.to_s
|
151
|
+
}
|
57
152
|
|
58
|
-
|
59
|
-
# make the local vars 'four' and 'name' available the the template markup
|
60
|
-
# pretty print the output with 2-space indentation
|
61
|
-
Undies::Template.new("path/to/file", {
|
62
|
-
:four => 2 + 2,
|
63
|
-
:name => "Awesome"
|
64
|
-
}).to_s(2)
|
153
|
+
*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:
|
65
154
|
|
66
|
-
|
67
|
-
|
155
|
+
# ensures all content is streamed to the template's output stream
|
156
|
+
# this is only necessary using the "builder approach"
|
157
|
+
Undies::Template.flush(template)
|
68
158
|
|
69
159
|
== License
|
70
160
|
|
71
|
-
Copyright (c) 2011 Kelly Redding
|
161
|
+
Copyright (c) 2011-Present Kelly Redding
|
72
162
|
|
73
163
|
Permission is hereby granted, free of charge, to any person
|
74
164
|
obtaining a copy of this software and associated documentation
|
data/lib/undies/element.rb
CHANGED
@@ -7,15 +7,17 @@ module Undies
|
|
7
7
|
# polluting the public instance methods and to maximize the effectiveness
|
8
8
|
# of the Element#method_missing logic
|
9
9
|
|
10
|
-
def self.
|
11
|
-
if attrs.kind_of?
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
10
|
+
def self.hash_attrs(attrs="", ns=nil)
|
11
|
+
return attrs.to_s if !attrs.kind_of?(::Hash)
|
12
|
+
|
13
|
+
{}.tap do |a|
|
14
|
+
attrs.each { |k, v| a[ns ? "#{ns}_#{k}" : k.to_s] = v }
|
15
|
+
end.sort.inject('') do |html, k_v|
|
16
|
+
html + if k_v.last.kind_of?(::Hash)
|
17
|
+
hash_attrs(k_v.last, k_v.first)
|
18
|
+
else
|
19
|
+
" #{k_v.first}=\"#{k_v.last.gsub('"', '"').gsub('<', '<')}\""
|
20
|
+
end
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
@@ -103,7 +105,7 @@ module Undies
|
|
103
105
|
end
|
104
106
|
|
105
107
|
def start_tag
|
106
|
-
"<#{@name}#{self.class.
|
108
|
+
"<#{@name}#{self.class.hash_attrs(@attrs)}" + (@content_blocks.size > 0 ? ">" : " />")
|
107
109
|
end
|
108
110
|
|
109
111
|
def end_tag
|
data/lib/undies/node.rb
CHANGED
@@ -11,15 +11,21 @@ module Undies
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def self.flush(output, node)
|
14
|
+
output.pp_use_indent = true if node.force_pp?
|
14
15
|
output << self.content(node)
|
15
16
|
end
|
16
17
|
|
17
|
-
def initialize(content)
|
18
|
+
def initialize(content, opts={})
|
18
19
|
@start_tag = nil
|
19
20
|
@end_tag = nil
|
21
|
+
@force_pp = opts[:force_pp]
|
20
22
|
@content = content
|
21
23
|
end
|
22
24
|
|
25
|
+
def force_pp?
|
26
|
+
!!@force_pp
|
27
|
+
end
|
28
|
+
|
23
29
|
def ==(other_node)
|
24
30
|
self.class.content(self) == other_node.class.content(other_node) &&
|
25
31
|
self.instance_variable_get("@start_tag") == other_node.instance_variable_get("@start_tag") &&
|
data/lib/undies/output.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
require 'undies/node'
|
2
|
-
require 'undies/element'
|
3
1
|
require 'undies/node_buffer'
|
4
2
|
|
5
3
|
module Undies
|
6
4
|
class Output
|
7
5
|
|
8
6
|
attr_reader :io, :options, :pp, :node_buffer
|
7
|
+
attr_accessor :pp_use_indent
|
9
8
|
|
10
9
|
# the output class wraps an IO stream, gathers pretty printing options,
|
11
10
|
# and handles buffering nodes and pretty printing to the stream
|
@@ -23,7 +22,7 @@ module Undies
|
|
23
22
|
|
24
23
|
# setup any pretty printing
|
25
24
|
@pp = opts[:pp]
|
26
|
-
self.pp_level = 0
|
25
|
+
self.pp_level = opts[:pp_level] || 0
|
27
26
|
self.pp_use_indent = true
|
28
27
|
|
29
28
|
@options = opts
|
@@ -38,22 +37,13 @@ module Undies
|
|
38
37
|
@pp_level = value
|
39
38
|
end
|
40
39
|
|
41
|
-
def pp_use_indent=(value)
|
42
|
-
@pp_use_indent = value
|
43
|
-
end
|
44
|
-
|
45
40
|
def <<(data)
|
46
41
|
@io << (@pp_use_indent ? "#{@pp_indent}#{data}" : data.to_s)
|
47
42
|
end
|
48
43
|
|
49
|
-
def node(
|
50
|
-
self.node_buffer.pull(self)
|
51
|
-
self.node_buffer.push(Node.new(data))
|
52
|
-
end
|
53
|
-
|
54
|
-
def element(name, attrs={}, &block)
|
44
|
+
def node(obj)
|
55
45
|
self.node_buffer.pull(self)
|
56
|
-
self.node_buffer.push(
|
46
|
+
self.node_buffer.push(obj)
|
57
47
|
end
|
58
48
|
|
59
49
|
def flush
|
data/lib/undies/template.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'undies/source_stack'
|
2
2
|
require 'undies/output'
|
3
|
+
require 'undies/node'
|
4
|
+
require 'undies/element'
|
5
|
+
|
3
6
|
|
4
7
|
module Undies
|
5
8
|
class Template
|
6
9
|
|
7
|
-
# have as many methods
|
10
|
+
# have as many methods on the class level as possible to keep from
|
8
11
|
# polluting the public instance methods, the instance scope, and to
|
9
12
|
# maximize the effectiveness of the Template#method_missing logic
|
10
13
|
|
@@ -12,26 +15,37 @@ module Undies
|
|
12
15
|
template.instance_variable_get("@_undies_output")
|
13
16
|
end
|
14
17
|
|
15
|
-
def
|
18
|
+
def self.flush(template)
|
19
|
+
template.instance_variable_get("@_undies_output").flush
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(*args)
|
23
|
+
output = if args.last.kind_of?(Output)
|
24
|
+
args.pop
|
25
|
+
else
|
26
|
+
raise ArgumentError, "please provide an Output object"
|
27
|
+
end
|
28
|
+
data = args.last.kind_of?(::Hash) ? args.pop : {}
|
29
|
+
source = args.last.kind_of?(Source) ? args.pop : Source.new(Proc.new {})
|
30
|
+
|
16
31
|
# setup the source stack and output objects
|
17
|
-
raise ArgumentError, "please provide a Source object" if !source.kind_of?(Source)
|
18
32
|
@_undies_source_stack = SourceStack.new(source)
|
19
|
-
raise ArgumentError, "please provide an Output object" if !output.kind_of?(Output)
|
20
|
-
@_undies_output = output
|
21
33
|
|
22
34
|
# apply data to template scope
|
23
|
-
raise ArgumentError if !data.kind_of?(::Hash)
|
24
35
|
if (data.keys.map(&:to_s) & self.public_methods.map(&:to_s)).size > 0
|
25
36
|
raise ArgumentError, "data conflicts with template public methods."
|
26
37
|
end
|
27
38
|
metaclass = class << self; self; end
|
28
39
|
data.each {|key, value| metaclass.class_eval { define_method(key){value} }}
|
29
40
|
|
41
|
+
# save off the output obj for streaming
|
42
|
+
@_undies_output = output
|
43
|
+
|
30
44
|
# yield to recursivley render the source stack
|
31
45
|
self.__yield
|
32
46
|
|
33
47
|
# flush any remaining output to the stream
|
34
|
-
|
48
|
+
self.class.flush(self)
|
35
49
|
end
|
36
50
|
|
37
51
|
# call this to render template source
|
@@ -56,10 +70,15 @@ module Undies
|
|
56
70
|
def _(data=""); self.__ self.escape_html(data.to_s); end
|
57
71
|
|
58
72
|
# Add a text node with the data un-escaped
|
59
|
-
def __(data=""); @_undies_output.node(data.to_s); end
|
73
|
+
def __(data=""); @_undies_output.node(Node.new(data.to_s)); end
|
74
|
+
|
75
|
+
# Add a text node with the data un-escaped, forcing pp output
|
76
|
+
def ___(data="")
|
77
|
+
@_undies_output.node(Node.new(data.to_s, :force_pp => true))
|
78
|
+
end
|
60
79
|
|
61
80
|
# Add an element to the nodes of the current node
|
62
|
-
def element(*args, &block); @_undies_output.
|
81
|
+
def element(*args, &block); @_undies_output.node(Element.new(*args, &block)); end
|
63
82
|
alias_method :tag, :element
|
64
83
|
|
65
84
|
# Element proxy methods ('_<element>'') ========================
|
data/lib/undies/version.rb
CHANGED
data/test/element_test.rb
CHANGED
@@ -12,7 +12,7 @@ class Undies::Element
|
|
12
12
|
end
|
13
13
|
subject { @e }
|
14
14
|
|
15
|
-
should have_class_methods :
|
15
|
+
should have_class_methods :hash_attrs, :content, :flush
|
16
16
|
should have_instance_method :to_str
|
17
17
|
|
18
18
|
should "be a Node" do
|
@@ -29,22 +29,35 @@ class Undies::Element
|
|
29
29
|
|
30
30
|
end
|
31
31
|
|
32
|
-
class
|
33
|
-
desc "the element class
|
32
|
+
class HashAttrsTest < BasicTests
|
33
|
+
desc "the element class hash_attrs util"
|
34
34
|
|
35
|
-
should "convert an empty hash to
|
36
|
-
assert_equal '', Undies::Element.
|
35
|
+
should "convert an empty hash to element attrs" do
|
36
|
+
assert_equal '', Undies::Element.hash_attrs({})
|
37
37
|
end
|
38
38
|
|
39
|
-
should "convert a basic hash to
|
40
|
-
attrs = Undies::Element.
|
39
|
+
should "convert a basic hash to element attrs" do
|
40
|
+
attrs = Undies::Element.hash_attrs(:class => "test", :id => "test_1")
|
41
41
|
assert_match /^\s{1}/, attrs
|
42
|
-
|
43
|
-
|
42
|
+
assert_includes 'class="test"', attrs
|
43
|
+
assert_includes 'id="test_1"', attrs
|
44
|
+
|
45
|
+
attrs = Undies::Element.hash_attrs('key' => "string")
|
46
|
+
assert_includes 'key="string"', attrs
|
47
|
+
end
|
48
|
+
|
49
|
+
should "escape double-quotes in attr values" do
|
50
|
+
attrs = Undies::Element.hash_attrs('escaped' => '"this" is double-quoted')
|
51
|
+
assert_includes 'escaped=""this" is double-quoted"', attrs
|
52
|
+
end
|
53
|
+
|
54
|
+
should "escape '<' in attr values" do
|
55
|
+
attrs = Undies::Element.hash_attrs('escaped' => 'not < escaped')
|
56
|
+
assert_includes 'escaped="not < escaped"', attrs
|
44
57
|
end
|
45
58
|
|
46
|
-
should "convert a nested hash to
|
47
|
-
attrs = Undies::Element.
|
59
|
+
should "convert a nested hash to element attrs" do
|
60
|
+
attrs = Undies::Element.hash_attrs({
|
48
61
|
:class => "testing", :id => "test_2",
|
49
62
|
:nested => {:something => 'is_awesome'}
|
50
63
|
})
|
@@ -151,6 +164,14 @@ class Undies::Element
|
|
151
164
|
assert_equal '<br class="big" />', @out
|
152
165
|
end
|
153
166
|
|
167
|
+
should "serialize with attrs that have double-quotes" do
|
168
|
+
src = Undies::Source.new do
|
169
|
+
element(:br, :class => '"this" is double-quoted')
|
170
|
+
end
|
171
|
+
templ = Undies::Template.new(src, {}, @output)
|
172
|
+
assert_equal '<br class=""this" is double-quoted" />', @out
|
173
|
+
end
|
174
|
+
|
154
175
|
should "serialize with attrs and content" do
|
155
176
|
src = Undies::Source.new do
|
156
177
|
element(:strong, {:class => 'big'}) { __ "Loud Noises!" }
|
data/test/node_test.rb
CHANGED
@@ -28,6 +28,14 @@ class Undies::Node
|
|
28
28
|
assert_equal "a text node here", out
|
29
29
|
end
|
30
30
|
|
31
|
+
should "force pp when flushed if directed" do
|
32
|
+
output = Undies::Output.new(StringIO.new(out = ""), :pp => 2, :pp_level => 1)
|
33
|
+
output.pp_use_indent = false
|
34
|
+
Undies::Node.flush(output, Undies::Node.new("should be pp", {:force_pp => true}))
|
35
|
+
|
36
|
+
assert_equal "\n should be pp", out
|
37
|
+
end
|
38
|
+
|
31
39
|
end
|
32
40
|
|
33
41
|
end
|
data/test/output_test.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require "assert"
|
2
2
|
|
3
3
|
require 'stringio'
|
4
|
+
require 'undies/node'
|
5
|
+
require 'undies/element'
|
6
|
+
|
4
7
|
require "undies/output"
|
5
8
|
|
6
9
|
class Undies::Output
|
@@ -14,8 +17,9 @@ class Undies::Output
|
|
14
17
|
subject { @output }
|
15
18
|
|
16
19
|
should have_readers :io, :options, :pp, :node_buffer
|
20
|
+
should have_accessor :pp_use_indent
|
17
21
|
should have_instance_methods :options=, :<<, :pp_level
|
18
|
-
should have_instance_methods :node, :
|
22
|
+
should have_instance_methods :node, :flush
|
19
23
|
|
20
24
|
should "know its stream" do
|
21
25
|
assert_same @io, subject.io
|
@@ -57,6 +61,10 @@ class Undies::Output
|
|
57
61
|
assert_equal 2, subject.pp
|
58
62
|
end
|
59
63
|
|
64
|
+
should "start pp at level 0 by default" do
|
65
|
+
assert_equal 0, subject.pp_level
|
66
|
+
end
|
67
|
+
|
60
68
|
should "pretty print stream data" do
|
61
69
|
subject << "some data"
|
62
70
|
assert_equal "\nsome data", @out
|
@@ -71,43 +79,48 @@ class Undies::Output
|
|
71
79
|
end
|
72
80
|
|
73
81
|
should "pretty print nodes" do
|
74
|
-
subject.node("lala"); subject.flush
|
82
|
+
subject.node(Undies::Node.new("lala")); subject.flush
|
75
83
|
assert_equal "\nlala", @out
|
76
84
|
end
|
77
85
|
|
86
|
+
should "pretty print at a given level if directed to" do
|
87
|
+
subject.options = {:pp => 2, :pp_level => 2}
|
88
|
+
subject.node(Undies::Node.new("lala")); subject.flush
|
89
|
+
assert_equal "\n lala", @out
|
90
|
+
end
|
91
|
+
|
78
92
|
should "pretty print elements with no content" do
|
79
|
-
subject.
|
93
|
+
subject.node(Undies::Element.new("span")); subject.flush
|
80
94
|
assert_equal "\n<span />", @out
|
81
95
|
end
|
82
96
|
|
83
|
-
should "
|
84
|
-
subject.
|
85
|
-
|
97
|
+
should "keep node content on the same line" do
|
98
|
+
subject.node(Undies::Element.new("div") {
|
99
|
+
subject.node Undies::Node.new("hi")
|
100
|
+
subject.node Undies::Node.new("there")
|
101
|
+
})
|
102
|
+
subject.node(Undies::Element.new("div") {
|
103
|
+
subject.node Undies::Node.new("")
|
104
|
+
subject.node Undies::Node.new("hi")
|
105
|
+
subject.node Undies::Node.new("again")
|
106
|
+
})
|
107
|
+
subject.flush
|
108
|
+
|
109
|
+
assert_equal "\n<div>hithere</div>\n<div>hiagain</div>", @out
|
86
110
|
end
|
87
111
|
|
88
112
|
end
|
89
113
|
|
90
114
|
|
91
115
|
class NodeHandlingTests < BasicTests
|
92
|
-
before do
|
93
|
-
@hey = Undies::Node.new "hey!"
|
94
|
-
|
95
|
-
src = Undies::Source.new do
|
96
|
-
_div.good.thing!(:type => "something") {
|
97
|
-
__ "action"
|
98
|
-
}
|
99
|
-
end
|
100
|
-
@expected_output = "hey!"
|
101
|
-
end
|
102
116
|
|
103
117
|
should "create and append nodes" do
|
104
|
-
|
118
|
+
subject.node Undies::Node.new "hey!"
|
105
119
|
assert_equal 1, subject.node_buffer.size
|
106
120
|
end
|
107
121
|
|
108
122
|
should "create and append elements" do
|
109
|
-
|
110
|
-
assert_equal elem, subject.element(:div)
|
123
|
+
subject.node Undies::Element.new(:div)
|
111
124
|
assert_equal 1, subject.node_buffer.size
|
112
125
|
end
|
113
126
|
|
data/test/template_test.rb
CHANGED
@@ -8,18 +8,18 @@ class Undies::Template
|
|
8
8
|
class BasicTests < Assert::Context
|
9
9
|
desc 'a template'
|
10
10
|
before do
|
11
|
-
src = Undies::Source.new(Proc.new {})
|
11
|
+
@src = Undies::Source.new(Proc.new {})
|
12
12
|
@outstream = StringIO.new(@out = "")
|
13
13
|
@output = Undies::Output.new(@outstream)
|
14
14
|
|
15
|
-
@t = Undies::Template.new(src, {}, @output)
|
15
|
+
@t = Undies::Template.new(@src, {}, @output)
|
16
16
|
end
|
17
17
|
subject { @t }
|
18
18
|
|
19
|
-
should have_class_method :output
|
19
|
+
should have_class_method :output, :flush
|
20
20
|
should have_instance_method :to_s
|
21
21
|
should have_instance_methods :element, :tag, :escape_html
|
22
|
-
should have_instance_methods :_, :__
|
22
|
+
should have_instance_methods :_, :__, :___
|
23
23
|
should have_instance_methods :__yield, :__partial
|
24
24
|
|
25
25
|
should "provide access to its output object via a class method" do
|
@@ -55,12 +55,42 @@ class Undies::Template
|
|
55
55
|
|
56
56
|
end
|
57
57
|
|
58
|
+
class TemplateCreationTests < BasicTests
|
59
|
+
|
60
|
+
should "complain if creating a template with no Output obj" do
|
61
|
+
assert_raises ArgumentError do
|
62
|
+
Undies::Template.new(@src, {})
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
should "default the data to an empty hash if none provided" do
|
67
|
+
assert_nothing_raised do
|
68
|
+
Undies::Template.new(@src, @output)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
should "default the source to an empty Proc source if none provided" do
|
73
|
+
assert_nothing_raised do
|
74
|
+
Undies::Template.new(@output)
|
75
|
+
end
|
76
|
+
assert_equal "", @out
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
58
81
|
class NodeTests < BasicTests
|
59
82
|
desc "with text data"
|
60
83
|
before do
|
61
84
|
@data = "stuff & <em>more stuff</em>"
|
62
85
|
end
|
63
86
|
|
87
|
+
should "add the text escaped using the '_' method" do
|
88
|
+
Undies::Template.new(Undies::Source.new do
|
89
|
+
_ data
|
90
|
+
end, {:data => @data}, @output)
|
91
|
+
assert_equal subject.send(:escape_html, @data), @out
|
92
|
+
end
|
93
|
+
|
64
94
|
should "add the text un-escaped using the '__' method" do
|
65
95
|
Undies::Template.new(Undies::Source.new do
|
66
96
|
__ data
|
@@ -68,11 +98,17 @@ class Undies::Template
|
|
68
98
|
assert_equal @data, @out
|
69
99
|
end
|
70
100
|
|
71
|
-
should "add the text escaped using the '
|
101
|
+
should "add the text un-escaped with force pretty printing using the '___' method" do
|
102
|
+
output = Undies::Output.new(@outstream, :pp => 2)
|
72
103
|
Undies::Template.new(Undies::Source.new do
|
73
|
-
|
74
|
-
|
75
|
-
|
104
|
+
_div {
|
105
|
+
__ "not-pp"
|
106
|
+
___ "pp"
|
107
|
+
__ "not-pp-but-anyway"
|
108
|
+
}
|
109
|
+
end, {}, output)
|
110
|
+
|
111
|
+
assert_equal "\n<div>not-pp\n pp\n not-pp-but-anyway\n</div>", @out
|
76
112
|
end
|
77
113
|
|
78
114
|
should "add empty string nodes using '__' and '_' methods with no args" do
|
@@ -240,7 +276,7 @@ class Undies::Template
|
|
240
276
|
end
|
241
277
|
@expected_output = "<div class=\"good\" id=\"thing\" type=\"something\">action</div>"
|
242
278
|
|
243
|
-
Undies::Template.new(src, {}, Undies::Output.new(outstream))
|
279
|
+
@template = Undies::Template.new(src, {}, Undies::Output.new(outstream))
|
244
280
|
end
|
245
281
|
|
246
282
|
should "should write to the stream as its being constructed" do
|
@@ -249,5 +285,20 @@ class Undies::Template
|
|
249
285
|
|
250
286
|
end
|
251
287
|
|
288
|
+
class FlushTests < StreamTests
|
289
|
+
desc "and adds content post-init"
|
290
|
+
before do
|
291
|
+
@expected_post_output = "<div>Added post-init</div>"
|
292
|
+
@template._div { @template._ "Added post-init" }
|
293
|
+
end
|
294
|
+
|
295
|
+
should "not stream full content until Undies#flush called on the template" do
|
296
|
+
assert_equal @expected_output, @output
|
297
|
+
Undies::Template.flush(@template)
|
298
|
+
assert_equal @expected_output + @expected_post_output, @output
|
299
|
+
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
252
303
|
end
|
253
304
|
|
data/undies.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ["Kelly Redding"]
|
10
10
|
s.email = ["kelly@kelredd.com"]
|
11
11
|
s.homepage = "http://github.com/kelredd/undies"
|
12
|
-
s.summary = %q{A pure-Ruby HTML
|
13
|
-
s.description = %q{A pure-Ruby HTML
|
12
|
+
s.summary = %q{A pure-Ruby DSL for streaming templated HTML, XML, or plain text. Named for its gratuitous use of the underscore.}
|
13
|
+
s.description = %q{A pure-Ruby DSL for streaming templated HTML, XML, or plain text. Named for its gratuitous use of the underscore.}
|
14
14
|
|
15
15
|
s.files = `git ls-files`.split("\n")
|
16
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: undies
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 2.0.0
|
10
|
+
version: 2.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kelly Redding
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2012-01-10 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
type: :development
|
@@ -48,7 +48,7 @@ dependencies:
|
|
48
48
|
version: 0.7.3
|
49
49
|
version_requirements: *id002
|
50
50
|
name: assert
|
51
|
-
description: A pure-Ruby HTML
|
51
|
+
description: A pure-Ruby DSL for streaming templated HTML, XML, or plain text. Named for its gratuitous use of the underscore.
|
52
52
|
email:
|
53
53
|
- kelly@kelredd.com
|
54
54
|
executables: []
|
@@ -125,7 +125,7 @@ rubyforge_project:
|
|
125
125
|
rubygems_version: 1.8.11
|
126
126
|
signing_key:
|
127
127
|
specification_version: 3
|
128
|
-
summary: A pure-Ruby HTML
|
128
|
+
summary: A pure-Ruby DSL for streaming templated HTML, XML, or plain text. Named for its gratuitous use of the underscore.
|
129
129
|
test_files:
|
130
130
|
- test/element_test.rb
|
131
131
|
- test/helper.rb
|