tiny 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|