tiny 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/README.md +388 -0
- data/Rakefile +1 -0
- data/lib/tiny.rb +519 -0
- data/lib/tiny/erubis.rb +63 -0
- data/lib/tiny/html.rb +177 -0
- data/lib/tiny/safe_string.rb +16 -0
- data/lib/tiny/tag.rb +47 -0
- data/lib/tiny/version.rb +3 -0
- data/spec/erubis_spec.rb +48 -0
- data/spec/fixtures/erb_list.erb +20 -0
- data/spec/fixtures/erb_list_with_helpers.erb +5 -0
- data/spec/fixtures/haml_list.haml +16 -0
- data/spec/fixtures/haml_list_with_helpers.haml +4 -0
- data/spec/helpers_spec.rb +303 -0
- data/spec/integration/erb_spec.rb +83 -0
- data/spec/integration/haml_spec.rb +59 -0
- data/spec/integration/rails_spec.rb +42 -0
- data/spec/integration/sinatra_spec.rb +42 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/support/form_helper.rb +34 -0
- data/spec/support/list_helper.rb +14 -0
- data/spec/support/rails_app.rb +44 -0
- data/spec/support/sinatra_app.rb +26 -0
- data/spec/widget_spec.rb +146 -0
- data/tiny.gemspec +31 -0
- metadata +237 -0
data/lib/tiny/erubis.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'erubis'
|
2
|
+
require 'tilt/erb'
|
3
|
+
|
4
|
+
module Tiny
|
5
|
+
# Support for emitting explicitly the result of calls to methods that
|
6
|
+
# take blocks. Based on the Rails ERB hack.
|
7
|
+
#
|
8
|
+
# <%= my_method do %>
|
9
|
+
# ...
|
10
|
+
# <% end %>
|
11
|
+
#
|
12
|
+
# It overrides Tilt default Template classes for Erubis.
|
13
|
+
#
|
14
|
+
module Erubis
|
15
|
+
# @see Erubis
|
16
|
+
class ::String
|
17
|
+
def append= obj
|
18
|
+
self << obj.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def append_escaped= obj
|
22
|
+
self << Tiny::Helpers.sanitize(obj.to_s)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @see Erubis
|
27
|
+
module ErubyExtensions
|
28
|
+
def add_expr_literal src, code
|
29
|
+
src << "_buf.append= #{code};"
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_expr_escaped src, code
|
33
|
+
src << "_buf.append_escaped= #{code};"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @see Erubis
|
38
|
+
class Eruby < ::Erubis::Eruby
|
39
|
+
include ErubyExtensions
|
40
|
+
end
|
41
|
+
|
42
|
+
# @see Erubis
|
43
|
+
class EscapedEruby < ::Erubis::EscapedEruby
|
44
|
+
include ErubyExtensions
|
45
|
+
end
|
46
|
+
|
47
|
+
# @see Erubis
|
48
|
+
class ErubisTemplate < ::Tilt::ErubisTemplate
|
49
|
+
def prepare
|
50
|
+
engine_class = options.delete(:engine_class) || Eruby
|
51
|
+
engine_class = EscapedEruby if options.delete(:escape_html)
|
52
|
+
options.merge!(:engine_class => engine_class)
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def precompiled_preamble locals
|
57
|
+
[super, "__in_erb_template=true"].join("\n")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Tilt.register ErubisTemplate, 'erb', 'rhtml', 'erubis'
|
62
|
+
end
|
63
|
+
end
|
data/lib/tiny/html.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
module Tiny
|
2
|
+
module HTML
|
3
|
+
@content_tags = []
|
4
|
+
@void_tags = []
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# Void tag names.
|
8
|
+
# Tags that should have no content.
|
9
|
+
# @return [Array]
|
10
|
+
def content_tags
|
11
|
+
@content_tags
|
12
|
+
end
|
13
|
+
|
14
|
+
# Content tag names.
|
15
|
+
# Tags that can have content.
|
16
|
+
# @return [Array]
|
17
|
+
def void_tags
|
18
|
+
@void_tags
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
# @macro tag_def
|
23
|
+
# @method $1(attrs_or_content = {}, attrs = nil, &block)
|
24
|
+
# Shortcut for {Markup#html_tag html_tag}(:$1)
|
25
|
+
#
|
26
|
+
# @param attrs_or_content [Hash, String] Tag's attributes or content.
|
27
|
+
# @param attrs [Hash] Tag's attributes if content string passed.
|
28
|
+
# @yield Content block.
|
29
|
+
# @return [String] HTML markup
|
30
|
+
#
|
31
|
+
# @see Markup#html_tag
|
32
|
+
#
|
33
|
+
def tag_def tag_name, void_tag = false
|
34
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
35
|
+
def #{tag_name} *args, &block
|
36
|
+
html_tag "#{tag_name}", *args, &block
|
37
|
+
end
|
38
|
+
RUBY
|
39
|
+
void_tag ? @void_tags.push(tag_name) : @content_tags.push(tag_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
tag_def 'area', :void
|
44
|
+
tag_def 'base', :void
|
45
|
+
tag_def 'br', :void
|
46
|
+
tag_def 'col', :void
|
47
|
+
tag_def 'hr', :void
|
48
|
+
tag_def 'img', :void
|
49
|
+
tag_def 'input', :void
|
50
|
+
tag_def 'link', :void
|
51
|
+
tag_def 'meta', :void
|
52
|
+
tag_def 'param', :void
|
53
|
+
|
54
|
+
# html 5 tags
|
55
|
+
tag_def 'embed', :void
|
56
|
+
tag_def 'article'
|
57
|
+
tag_def 'aside'
|
58
|
+
tag_def 'audio'
|
59
|
+
tag_def 'bdi'
|
60
|
+
tag_def 'canvas'
|
61
|
+
tag_def 'command'
|
62
|
+
tag_def 'datalist'
|
63
|
+
tag_def 'details'
|
64
|
+
tag_def 'figcaption'
|
65
|
+
tag_def 'figure'
|
66
|
+
tag_def 'header'
|
67
|
+
tag_def 'hgroup'
|
68
|
+
tag_def 'keygen'
|
69
|
+
tag_def 'mark'
|
70
|
+
tag_def 'meter'
|
71
|
+
tag_def 'nav'
|
72
|
+
tag_def 'output'
|
73
|
+
tag_def 'progress'
|
74
|
+
tag_def 'section'
|
75
|
+
tag_def 'source'
|
76
|
+
tag_def 'summary'
|
77
|
+
tag_def 'track'
|
78
|
+
tag_def 'video'
|
79
|
+
tag_def 'wbr'
|
80
|
+
|
81
|
+
# common tags
|
82
|
+
tag_def 'a'
|
83
|
+
tag_def 'abbr'
|
84
|
+
tag_def 'address'
|
85
|
+
|
86
|
+
tag_def 'b'
|
87
|
+
tag_def 'bdo'
|
88
|
+
tag_def 'big'
|
89
|
+
tag_def 'blockquote'
|
90
|
+
tag_def 'body'
|
91
|
+
tag_def 'button'
|
92
|
+
|
93
|
+
tag_def 'caption'
|
94
|
+
tag_def 'cite'
|
95
|
+
tag_def 'code'
|
96
|
+
tag_def 'colgroup'
|
97
|
+
|
98
|
+
tag_def 'dd'
|
99
|
+
tag_def 'del'
|
100
|
+
tag_def 'dfn'
|
101
|
+
tag_def 'div'
|
102
|
+
tag_def 'dl'
|
103
|
+
tag_def 'dt'
|
104
|
+
|
105
|
+
tag_def 'em'
|
106
|
+
|
107
|
+
tag_def 'fieldset'
|
108
|
+
tag_def 'footer'
|
109
|
+
tag_def 'form'
|
110
|
+
|
111
|
+
tag_def 'h1'
|
112
|
+
tag_def 'h2'
|
113
|
+
tag_def 'h3'
|
114
|
+
tag_def 'h4'
|
115
|
+
tag_def 'h5'
|
116
|
+
tag_def 'h6'
|
117
|
+
tag_def 'head'
|
118
|
+
tag_def 'html'
|
119
|
+
|
120
|
+
tag_def 'i'
|
121
|
+
tag_def 'iframe'
|
122
|
+
tag_def 'ins'
|
123
|
+
|
124
|
+
tag_def 'kbd'
|
125
|
+
|
126
|
+
tag_def 'label'
|
127
|
+
tag_def 'legend'
|
128
|
+
tag_def 'li'
|
129
|
+
|
130
|
+
tag_def 'map'
|
131
|
+
|
132
|
+
tag_def 'noscript'
|
133
|
+
|
134
|
+
tag_def 'object'
|
135
|
+
tag_def 'ol'
|
136
|
+
tag_def 'optgroup'
|
137
|
+
tag_def 'option'
|
138
|
+
|
139
|
+
tag_def 'p'
|
140
|
+
tag_def 'pre'
|
141
|
+
|
142
|
+
tag_def 'q'
|
143
|
+
|
144
|
+
tag_def 'rp'
|
145
|
+
tag_def 'rt'
|
146
|
+
tag_def 'ruby'
|
147
|
+
|
148
|
+
tag_def 's'
|
149
|
+
tag_def 'samp'
|
150
|
+
tag_def 'script'
|
151
|
+
tag_def 'select'
|
152
|
+
tag_def 'small'
|
153
|
+
tag_def 'span'
|
154
|
+
tag_def 'strike'
|
155
|
+
tag_def 'strong'
|
156
|
+
tag_def 'style'
|
157
|
+
tag_def 'sub'
|
158
|
+
tag_def 'sup'
|
159
|
+
|
160
|
+
tag_def 'table'
|
161
|
+
tag_def 'tbody'
|
162
|
+
tag_def 'td'
|
163
|
+
tag_def 'textarea'
|
164
|
+
tag_def 'tfoot'
|
165
|
+
tag_def 'th'
|
166
|
+
tag_def 'thead'
|
167
|
+
tag_def 'time'
|
168
|
+
tag_def 'title'
|
169
|
+
tag_def 'tr'
|
170
|
+
tag_def 'tt'
|
171
|
+
|
172
|
+
tag_def 'u'
|
173
|
+
tag_def 'ul'
|
174
|
+
|
175
|
+
tag_def 'var'
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Tiny
|
2
|
+
# A {SafeString} will not be HTML-escaped when appended to {Tag} or
|
3
|
+
# {Widget} content, whereas a String will be.
|
4
|
+
# @see Buffering#raw
|
5
|
+
# @see Buffering#concat!
|
6
|
+
# @see Buffering#concat
|
7
|
+
#
|
8
|
+
class SafeString < String
|
9
|
+
def html_safe?; true end
|
10
|
+
|
11
|
+
def concat string
|
12
|
+
return super unless String === string
|
13
|
+
super Helpers.sanitize string
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/tiny/tag.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Tiny
|
2
|
+
# @see Markup#html_tag
|
3
|
+
class Tag
|
4
|
+
attr_reader :tag_name, :attrs
|
5
|
+
def initialize tag_name, aoc = {}, attrs = nil
|
6
|
+
@attrs, @content =
|
7
|
+
Hash === aoc && attrs.nil?? [aoc] : [attrs || {}, aoc]
|
8
|
+
@content = Helpers.sanitize(@content) if @content
|
9
|
+
@tag_name = tag_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def tag_attributes
|
13
|
+
tag_attrs = attrs.map do |name, val|
|
14
|
+
next if val.nil? || val == []
|
15
|
+
next name if val == true
|
16
|
+
|
17
|
+
vals = [*val].map do |value|
|
18
|
+
EscapeUtils.escape_html value.to_s, false
|
19
|
+
end
|
20
|
+
|
21
|
+
%{#{name}="#{vals.join(' ')}"}
|
22
|
+
end.compact.join(' ')
|
23
|
+
|
24
|
+
" #{tag_attrs}" unless tag_attrs.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def render &block
|
28
|
+
if void_tag?
|
29
|
+
"<#{tag_name}#{tag_attributes} />"
|
30
|
+
else
|
31
|
+
content = @content
|
32
|
+
if block_given?
|
33
|
+
context = eval('self', block.binding)
|
34
|
+
content = context.with_buffer(&block)
|
35
|
+
content.gsub!(/^(?!\s*$)/, " ")
|
36
|
+
content.gsub!(/\A(?!$)|(?<!^|\n)\z/, "\n")
|
37
|
+
end
|
38
|
+
|
39
|
+
"<#{tag_name}#{tag_attributes}>#{content}</#{tag_name}>"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def void_tag?
|
44
|
+
HTML.void_tags.include? tag_name
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/tiny/version.rb
ADDED
data/spec/erubis_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'markup helpers' do
|
4
|
+
it "should register tiny erubis templates for '.erubis' files" do
|
5
|
+
Tilt['erubis'].should == Tiny::Erubis::ErubisTemplate
|
6
|
+
Tilt['erb'].should == Tiny::Erubis::ErubisTemplate
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should default to tiny erubis engine' do
|
10
|
+
engine = Tilt['erb'].new{}.instance_variable_get(:@engine)
|
11
|
+
engine.should be_a Tiny::Erubis::Eruby
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should use tiny escaped erubis engine for escaping html' do
|
15
|
+
engine = Tilt['erb'].new(nil, :escape_html => true){}.instance_variable_get(:@engine)
|
16
|
+
engine.should be_a Tiny::Erubis::EscapedEruby
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should escape html when passing :escape_html => true option' do
|
20
|
+
template = Tilt['erb'].new(nil, :escape_html => true) { %(<%= "<p>Hello World!</p>" %>) }
|
21
|
+
template.render.should == "<p>Hello World!</p>"
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should not escape htmle when passing :escape_html => false option' do
|
25
|
+
template = Tilt['erb'].new(nil, :escape_html => false) { %(<%= "<p>Hello World!</p>" %>) }
|
26
|
+
template.render.should == "<p>Hello World!</p>"
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should allow block with explicit output' do
|
30
|
+
template = Tilt['erb'].new do
|
31
|
+
<<-ERB
|
32
|
+
<%= [1,2].each do |i| %>
|
33
|
+
<% end %>
|
34
|
+
ERB
|
35
|
+
end
|
36
|
+
template.render.should include "[1, 2]"
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should allow block with explicit output' do
|
40
|
+
template = Tilt['erb'].new nil, :escape_html => true do
|
41
|
+
<<-ERB
|
42
|
+
<%= [1,2].each do |i| %>
|
43
|
+
<% end %>
|
44
|
+
ERB
|
45
|
+
end
|
46
|
+
template.render.should include "[1, 2]"
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<%= html_tag(:ul) do %>
|
2
|
+
<li>
|
3
|
+
<%= html_tag(:a) do %>
|
4
|
+
A
|
5
|
+
<span>1</span>
|
6
|
+
<% end %>
|
7
|
+
</li>
|
8
|
+
<%= html_tag(:li) do %>
|
9
|
+
<%= html_tag(:a) do %>
|
10
|
+
B
|
11
|
+
<span>2</span>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
<%= html_tag(:li) do %>
|
15
|
+
<%= html_tag(:a) do %>
|
16
|
+
C
|
17
|
+
<span>3</span>
|
18
|
+
<% end %>
|
19
|
+
<% end %>
|
20
|
+
<% end %>
|
@@ -0,0 +1,303 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe 'markup helpers' do
|
5
|
+
include Tiny::Helpers
|
6
|
+
|
7
|
+
let(:output) do
|
8
|
+
Capybara::Node::Simple.new(@output)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'tag' do
|
12
|
+
describe 'basic' do
|
13
|
+
describe 'attributes and content' do
|
14
|
+
before do
|
15
|
+
@output = tag(:li, :class => 'item', :id => 'hello') { text 'Hello' }
|
16
|
+
end
|
17
|
+
it { output.should have_css 'li', :count => 1 }
|
18
|
+
it { output.should have_css 'li', :text => "Hello" }
|
19
|
+
it { output.should have_css 'li.item' }
|
20
|
+
it { output.should have_css 'li#hello' }
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should not use blank attribute values' do
|
24
|
+
output = tag(:li, :class => [], :id => nil)
|
25
|
+
output.should == "<li></li>"
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should not emit value for an atribute value of true' do
|
29
|
+
output = tag(:li, 'data-something' => true)
|
30
|
+
output.should == "<li data-something></li>"
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should not allow passing text without #text' do
|
34
|
+
output = tag(:li) { 'Hello' }
|
35
|
+
output.should == '<li></li>'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should output multiple classes passing an array' do
|
39
|
+
output = tag(:li, :class => %w(item in-stock))
|
40
|
+
output.should == '<li class="item in-stock"></li>'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should allow passing content as string' do
|
44
|
+
tag(:h1, "Hello").should == "<h1>Hello</h1>"
|
45
|
+
tag(:h1, "Hello", :class => 'main').should == %{<h1 class="main">Hello</h1>}
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should escape attribute html' do
|
49
|
+
tag(:a, :href => '<script>').should == '<a href="<script>"></a>'
|
50
|
+
tag(:a, :href => 'art©').should == '<a href="art&copy"></a>'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'blocks' do
|
55
|
+
describe 'shallow blocks' do
|
56
|
+
before do
|
57
|
+
@output = tag(:div) { tag(:a) { text 'Hello' } }
|
58
|
+
end
|
59
|
+
it { output.should have_css 'div', :count => 1 }
|
60
|
+
it { output.should have_css 'a', :count => 1 }
|
61
|
+
it { output.should have_css 'div > a', :text => 'Hello' }
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'deeper blocks' do
|
65
|
+
before do
|
66
|
+
@output = tag(:div) do
|
67
|
+
tag(:a) do
|
68
|
+
text 'Hello'
|
69
|
+
tag(:img)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
it { output.should have_css 'div', :count => 1 }
|
74
|
+
it { output.should have_css 'a', :count => 1 }
|
75
|
+
it { output.should have_css 'div > a' }
|
76
|
+
it { output.should have_css 'div > a', :text => 'Hello' }
|
77
|
+
it { output.should have_css 'img', :count => 1 }
|
78
|
+
it { output.should have_css 'div > a > img' }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'buffering' do
|
83
|
+
describe 'tag concatenation' do
|
84
|
+
before do
|
85
|
+
@output = tag(:ul) do
|
86
|
+
tag(:li)
|
87
|
+
tag(:li)
|
88
|
+
tag(:li)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it { output.should have_css 'ul', :count => 1 }
|
93
|
+
it { output.should have_css 'li', :count => 3 }
|
94
|
+
it { output.should have_css 'ul > li', :count => 3 }
|
95
|
+
end
|
96
|
+
|
97
|
+
describe 'concatenation with text' do
|
98
|
+
before do
|
99
|
+
@output = tag(:ul) do
|
100
|
+
tag(:li) { text 'One' }
|
101
|
+
tag(:li) { text 'Two' }
|
102
|
+
tag(:li) { text 'Three' }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it { output.should have_css 'ul', :count => 1 }
|
107
|
+
it { output.should have_css 'li', :count => 3 }
|
108
|
+
it { output.should have_css 'ul > li', :count => 3 }
|
109
|
+
it { output.should have_css 'ul > li', :text => 'One' }
|
110
|
+
it { output.should have_css 'ul > li', :text => 'Two' }
|
111
|
+
it { output.should have_css 'ul > li', :text => 'Three' }
|
112
|
+
end
|
113
|
+
|
114
|
+
describe 'nested' do
|
115
|
+
before do
|
116
|
+
@output = tag(:ul) do
|
117
|
+
tag(:li) { tag(:a) { text 'One' } }
|
118
|
+
tag(:li) { tag(:a) { text 'Two' } }
|
119
|
+
tag(:li) { tag(:a) { text 'Three' } }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
it { output.should have_css 'ul', :count => 1 }
|
124
|
+
it { output.should have_css 'li', :count => 3 }
|
125
|
+
it { output.should have_css 'ul > li', :count => 3 }
|
126
|
+
it { output.should have_css 'a', :count => 3 }
|
127
|
+
it { output.should have_css 'ul > li > a', :text => 'One' }
|
128
|
+
it { output.should have_css 'ul > li > a', :text => 'Two' }
|
129
|
+
it { output.should have_css 'ul > li > a', :text => 'Three' }
|
130
|
+
end
|
131
|
+
|
132
|
+
describe 'outside content block' do
|
133
|
+
it 'should not buffer contiguous tags' do
|
134
|
+
tag(:span)
|
135
|
+
tag(:a).should == '<a></a>'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe 'text' do
|
141
|
+
it 'should escape text' do
|
142
|
+
@output = tag(:li){ text '&<>' }
|
143
|
+
@output.should =~ /&<>/
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should allow not scaped text' do
|
147
|
+
@output = tag(:li){ append! '&<>' }
|
148
|
+
@output.should =~ /&<>/
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe 'formatting' do
|
153
|
+
it 'should buffer with newlines and indentation' do
|
154
|
+
output = tag(:ul) do
|
155
|
+
tag :li
|
156
|
+
tag :li
|
157
|
+
end
|
158
|
+
output.should == "<ul>\n <li></li>\n <li></li>\n</ul>"
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'should buffer with newlines after text' do
|
162
|
+
output = tag(:ul) do
|
163
|
+
tag (:li) do
|
164
|
+
text 'Hi'
|
165
|
+
append! 'Hi'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
output.should == "<ul>\n <li>\n Hi\n Hi\n </li>\n</ul>"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe 'special nodes' do
|
174
|
+
describe 'comments' do
|
175
|
+
it 'should emit comment' do
|
176
|
+
comment('Hello').should == "<!-- Hello -->"
|
177
|
+
comment('Hello -- world').should == "<!-- Hello - - world -->"
|
178
|
+
comment('Hello -- -- world').should == "<!-- Hello - - - - world -->"
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should buffer comments' do
|
182
|
+
tag(:div) do
|
183
|
+
comment 'foo'
|
184
|
+
comment 'bar'
|
185
|
+
end.should == "<div>\n <!-- foo -->\n <!-- bar -->\n</div>"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe 'cdata' do
|
190
|
+
it 'should emit cdata' do
|
191
|
+
cdata('Hello').should == "<![CDATA[Hello]]>"
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'should buffer cdata' do
|
195
|
+
tag(:div) do
|
196
|
+
cdata('foo')
|
197
|
+
cdata('bar')
|
198
|
+
end.should == "<div>\n <![CDATA[foo]]>\n <![CDATA[bar]]>\n</div>"
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'should not "escape" cdata terminator' do
|
202
|
+
cdata(']]>').should == "<![CDATA[]]]]><![CDATA[>]]>"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe 'doctype' do
|
207
|
+
it 'should emit html5 doctype' do
|
208
|
+
doctype.should == '<!DOCTYPE html>'
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'should buffer doctype' do
|
212
|
+
output = with_buffer{ doctype and tag(:html) }
|
213
|
+
output.should == "<!DOCTYPE html>\n<html></html>\n"
|
214
|
+
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe 'tag closing' do
|
220
|
+
describe 'void tags' do
|
221
|
+
it 'should define void tags' do
|
222
|
+
Tiny::HTML.void_tags.should == %w(area base br col hr img input link meta param embed)
|
223
|
+
end
|
224
|
+
|
225
|
+
Tiny::HTML.void_tags.each do |tag_name|
|
226
|
+
describe tag_name do
|
227
|
+
it 'sould autoclose' do
|
228
|
+
tag(tag_name).should == "<#{tag_name} />"
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'should omit content' do
|
232
|
+
tag(tag_name){ text 'hi' }.should == "<#{tag_name} />"
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe 'content tags' do
|
239
|
+
it 'should define content tags' do
|
240
|
+
tags = %w(
|
241
|
+
article aside audio bdi canvas command datalist details
|
242
|
+
figcaption figure header hgroup keygen mark meter nav output progress
|
243
|
+
section source summary track video wbr a abbr address b bdo big
|
244
|
+
blockquote body button caption cite code colgroup dd del dfn div dl dt
|
245
|
+
em fieldset footer form h1 h2 h3 h4 h5 h6 head html i iframe ins kbd
|
246
|
+
label legend li map noscript object ol optgroup option p pre q rp rt
|
247
|
+
ruby s samp script select small span strike strong style sub sup table
|
248
|
+
tbody td textarea tfoot th thead time title tr tt u ul var
|
249
|
+
)
|
250
|
+
Tiny::HTML.content_tags.should == tags
|
251
|
+
end
|
252
|
+
|
253
|
+
Tiny::HTML.content_tags.each do |tag_name|
|
254
|
+
it "should not autoclose #{tag_name} if not empty" do
|
255
|
+
tag(tag_name).should == "<#{tag_name}></#{tag_name}>"
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe 'with_buffer' do
|
262
|
+
before do
|
263
|
+
@output = with_buffer do
|
264
|
+
tag(:head) { tag(:title, "Tiny Page!") }
|
265
|
+
tag(:body) { tag(:h1, "Hello Tiny!") }
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
it { output.should have_css 'head', :count => 1 }
|
270
|
+
it { output.should have_css 'head > title', :text => "Tiny Page!", :count => 1 }
|
271
|
+
it { output.should have_css 'body', :count => 1 }
|
272
|
+
it { output.should have_css 'body > h1', :text => "Hello Tiny!", :count => 1 }
|
273
|
+
end
|
274
|
+
|
275
|
+
describe 'dsl' do
|
276
|
+
include Tiny::HTML
|
277
|
+
|
278
|
+
describe 'void tags' do
|
279
|
+
Tiny::HTML.void_tags.each do |tag_name|
|
280
|
+
it "should render '#{tag_name}'" do
|
281
|
+
self.send(tag_name).should == "<#{tag_name} />"
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should render attributes" do
|
286
|
+
link(:href => "some.css").should == '<link href="some.css" />'
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
describe 'content tags' do
|
291
|
+
Tiny::HTML.content_tags.each do |tag_name|
|
292
|
+
it "should render '#{tag_name}'" do
|
293
|
+
self.send(tag_name).should == "<#{tag_name}></#{tag_name}>"
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
it "should render content and attributes" do
|
298
|
+
h1(:class => 'main') { text "Hello" }.should == %{<h1 class="main">\n Hello\n</h1>}
|
299
|
+
h1("Hello", :class => 'main').should == %{<h1 class="main">Hello</h1>}
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|