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 CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- undies (2.0.0)
4
+ undies (2.1.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/README.rdoc CHANGED
@@ -1,74 +1,164 @@
1
1
  = Undies
2
- A pure-Ruby DSL for writing HTML or plain text templates. Named for its gratuitous use of the underscore.
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
- == Usage
6
- Empty tag:
7
- _br # => "<br />"
8
-
9
- Tag with content:
10
- _h1 {
11
- _ "Some Header"
12
- } # => "<h1>Some Header</h1>"
13
- Nested tags:
14
- _body {
15
- _div {
16
- _ "Some Content"
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 &amp; 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 &amp; 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
- Tag with attributes
26
- _h1(:class => 'title', :title => "A Header")
27
- # => "<h1 class=\"title\" title=\"A Header\" />"
28
- Tag with id attribute
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
- Tag with class attributes
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
- == Rendering Templates
78
+ use in combo:
79
+ _h1.header!.awesome
80
+ # => "<h1 class=\"awesome\" id=\"header\" />
36
81
 
37
- * basic render:
38
-
39
- # render a block of markup
40
- Undies::Template.new do
41
- _div {
42
- _ "Some Content"
43
- }
44
- end.to_s
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
- * streaming render
110
+ === Output
47
111
 
48
- # render the template and push it to the output stream as it is being constructed
49
- # just pass an IO to the template constructor
50
- Undies::Template.new(output_io).new do
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
- _ "Some Content"
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
- * render passing local vars in and pretty printing:
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
- # render a file with markup
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
- === Pretty Printing
67
- If you want to pretty print the output, pass an integer specifying the number of spaces to indent to the `to_s` method.
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
@@ -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.html_attrs(attrs="", ns=nil)
11
- if attrs.kind_of? ::Hash
12
- attrs.empty? ? '' : (attrs.keys.map(&:to_s).sort.map(&:to_sym).inject('') do |html, key|
13
- ak = ns ? "#{ns}_#{key}" : key.to_s
14
- av = attrs[key]
15
- html + (av.kind_of?(::Hash) ? html_attrs(av, ak) : " #{ak}=\"#{av}\"")
16
- end)
17
- else
18
- attrs.to_s
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('"', '&quot;').gsub('<', '&lt;')}\""
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.html_attrs(@attrs)}" + (@content_blocks.size > 0 ? ">" : " />")
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(data="")
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(Element.new(name, attrs, &block))
46
+ self.node_buffer.push(obj)
57
47
  end
58
48
 
59
49
  def flush
@@ -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 to the class level as possilbe to keep from
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 initialize(source, data, output)
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
- @_undies_output.flush
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.element(*args, &block); end
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>'') ========================
@@ -1,3 +1,3 @@
1
1
  module Undies
2
- VERSION = "2.0.0"
2
+ VERSION = "2.1.0"
3
3
  end
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 :html_attrs, :content, :flush
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 HtmlAttrsTest < BasicTests
33
- desc "the element class html_attrs util"
32
+ class HashAttrsTest < BasicTests
33
+ desc "the element class hash_attrs util"
34
34
 
35
- should "convert an empty hash to html attrs" do
36
- assert_equal '', Undies::Element.html_attrs({})
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 html attrs" do
40
- attrs = Undies::Element.html_attrs(:class => "test", :id => "test_1")
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
- assert attrs.include?('class="test"')
43
- assert attrs.include?('id="test_1"')
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="&quot;this&quot; 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 &lt; escaped"', attrs
44
57
  end
45
58
 
46
- should "convert a nested hash to html attrs" do
47
- attrs = Undies::Element.html_attrs({
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="&quot;this&quot; 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, :element, :flush
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.element("span"); subject.flush
93
+ subject.node(Undies::Element.new("span")); subject.flush
80
94
  assert_equal "\n<span />", @out
81
95
  end
82
96
 
83
- should "pretty print elements with content" do
84
- subject.element("div") {}; subject.flush
85
- assert_equal "\n<div></div>", @out
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
- assert_equal @hey, subject.node("hey!")
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
- elem = Undies::Element.new(:div)
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
 
@@ -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 '_' method" do
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
- _ data
74
- end, {:data => @data}, @output)
75
- assert_equal subject.send(:escape_html, @data), @out
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 and plain text templating DSL.}
13
- s.description = %q{A pure-Ruby HTML and plain text templating DSL.}
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: 15
4
+ hash: 11
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
+ - 1
8
9
  - 0
9
- - 0
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: 2011-12-05 00:00:00 Z
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 and plain text templating DSL.
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 and plain text templating DSL.
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