trenni 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/trenni/builder.rb +93 -41
- data/lib/trenni/strings.rb +32 -0
- data/lib/trenni/version.rb +1 -1
- data/test/test_builder.rb +55 -30
- data/test/test_strings.rb +70 -0
- data/test/test_template.rb +1 -1
- metadata +6 -3
data/lib/trenni/builder.rb
CHANGED
@@ -18,6 +18,8 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
+
require 'trenni/strings'
|
22
|
+
|
21
23
|
module Trenni
|
22
24
|
|
23
25
|
INSTRUCT_ATTRIBUTES = [
|
@@ -26,22 +28,41 @@ module Trenni
|
|
26
28
|
].freeze
|
27
29
|
|
28
30
|
class Builder
|
31
|
+
INDENT = "\t"
|
32
|
+
|
33
|
+
# A helper to generate fragments of markup.
|
34
|
+
def self.fragment(builder = nil, &block)
|
35
|
+
if builder
|
36
|
+
yield builder
|
37
|
+
|
38
|
+
return nil
|
39
|
+
else
|
40
|
+
builder = Builder.new
|
41
|
+
|
42
|
+
yield builder
|
43
|
+
|
44
|
+
return builder.output.string
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
29
48
|
def initialize(options = {})
|
49
|
+
@strict = options[:strict]
|
50
|
+
|
30
51
|
@output = options[:output] || StringIO.new
|
52
|
+
@indentation = options[:indentation] || INDENT
|
53
|
+
@indent = options.fetch(:indent, true)
|
31
54
|
|
32
|
-
@
|
55
|
+
@escape = options[:escape]
|
33
56
|
|
34
57
|
@level = [0]
|
35
58
|
@children = [0]
|
36
59
|
end
|
37
60
|
|
38
|
-
|
39
|
-
@options[:indent] != nil
|
40
|
-
end
|
61
|
+
attr :output
|
41
62
|
|
42
|
-
def
|
43
|
-
if indent
|
44
|
-
@
|
63
|
+
def indentation
|
64
|
+
if @indent
|
65
|
+
@indentation * (@level.size - 1)
|
45
66
|
else
|
46
67
|
''
|
47
68
|
end
|
@@ -70,58 +91,52 @@ module Trenni
|
|
70
91
|
@output.puts "<!DOCTYPE#{text}>"
|
71
92
|
end
|
72
93
|
|
94
|
+
# Begin a block tag.
|
73
95
|
def tag(name, attributes = {}, &block)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
@output.puts if indent?
|
86
|
-
@output.write indent + "</#{name}>"
|
87
|
-
else
|
88
|
-
@output.write indent + "<#{name}#{tag_attributes(attributes)}/>"
|
96
|
+
full_tag(name, attributes, @indent, @indent, &block)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Begin an inline tag.
|
100
|
+
def inline(name, attributes = {}, &block)
|
101
|
+
indent = @indent
|
102
|
+
|
103
|
+
full_tag(name, attributes, @indent, false) do
|
104
|
+
@indent = false
|
105
|
+
yield
|
106
|
+
@indent = indent
|
89
107
|
end
|
90
108
|
end
|
91
109
|
|
92
110
|
def text(data)
|
93
|
-
|
94
|
-
|
111
|
+
append to_html(data)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Append pre-existing markup:
|
115
|
+
def append(data)
|
116
|
+
# The parent has one more child:
|
117
|
+
@level[-1] += 1
|
118
|
+
|
119
|
+
if @indent
|
120
|
+
lines = data.strip.split(/\n/)
|
121
|
+
|
122
|
+
lines.each_with_index do |line, i|
|
95
123
|
@output.puts if i > 0
|
96
|
-
@output.write
|
124
|
+
@output.write indentation + line
|
97
125
|
end
|
98
126
|
else
|
99
127
|
@output.write data
|
100
128
|
end
|
101
129
|
end
|
102
130
|
|
103
|
-
def options(options)
|
104
|
-
saved_options = @options
|
105
|
-
@options = options
|
106
|
-
|
107
|
-
yield
|
108
|
-
|
109
|
-
@options = saved_options
|
110
|
-
end
|
111
|
-
|
112
|
-
def tag_attributes(attributes)
|
113
|
-
self.class.tag_attributes(attributes, @options[:strict])
|
114
|
-
end
|
115
|
-
|
116
131
|
# Convert a set of attributes into a string suitable for use within a <tag>.
|
117
|
-
def
|
132
|
+
def tag_attributes(attributes)
|
118
133
|
buffer = []
|
119
134
|
|
120
135
|
attributes.each do |key, value|
|
121
136
|
if value == true
|
122
|
-
buffer << (
|
137
|
+
buffer << Strings::to_simple_attribute(key, @strict)
|
123
138
|
elsif value
|
124
|
-
buffer <<
|
139
|
+
buffer << Strings::to_attribute(key, to_html(value))
|
125
140
|
end
|
126
141
|
end
|
127
142
|
|
@@ -131,6 +146,43 @@ module Trenni
|
|
131
146
|
return ''
|
132
147
|
end
|
133
148
|
end
|
149
|
+
|
150
|
+
protected
|
151
|
+
|
152
|
+
def to_html(data)
|
153
|
+
@escape ? Strings::to_html(data) : data
|
154
|
+
end
|
155
|
+
|
156
|
+
# A normal block level/container tag.
|
157
|
+
def full_tag(name, attributes, indent_outer, indent_inner, &block)
|
158
|
+
if block_given?
|
159
|
+
if indent_outer
|
160
|
+
@output.puts if @level.last > 0
|
161
|
+
@output.write indentation
|
162
|
+
end
|
163
|
+
|
164
|
+
@output.write "<#{name}#{tag_attributes(attributes)}>"
|
165
|
+
@output.puts if indent_inner
|
166
|
+
|
167
|
+
# The parent has one more child:
|
168
|
+
@level[-1] += 1
|
169
|
+
|
170
|
+
@level << 0
|
171
|
+
|
172
|
+
yield
|
173
|
+
|
174
|
+
children = @level.pop
|
175
|
+
|
176
|
+
if indent_inner
|
177
|
+
@output.puts if children > 0
|
178
|
+
@output.write indentation
|
179
|
+
end
|
180
|
+
|
181
|
+
@output.write "</#{name}>"
|
182
|
+
else
|
183
|
+
@output.write indentation + "<#{name}#{tag_attributes(attributes)}/>"
|
184
|
+
end
|
185
|
+
end
|
134
186
|
end
|
135
187
|
|
136
188
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
module Trenni
|
3
|
+
module Strings
|
4
|
+
HTML_ESCAPE = {"&" => "&", "<" => "<", ">" => ">", "\"" => """}
|
5
|
+
HTML_ESCAPE_PATTERN = Regexp.new("[" + Regexp.quote(HTML_ESCAPE.keys.join) + "]")
|
6
|
+
|
7
|
+
def self.to_html(string)
|
8
|
+
string.gsub(HTML_ESCAPE_PATTERN){|c| HTML_ESCAPE[c]}
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.to_quoted_string(string)
|
12
|
+
'"' + string.gsub('"', '\\"').gsub(/\r/, "\\r").gsub(/\n/, "\\n") + '"'
|
13
|
+
end
|
14
|
+
|
15
|
+
# `value` must already be escaped.
|
16
|
+
def self.to_attribute(key, value)
|
17
|
+
%Q{#{key}="#{value}"}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.to_simple_attribute(key, strict)
|
21
|
+
strict ? %Q{#{key}="#{key}"} : key.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.to_title(string)
|
25
|
+
string.gsub(/(^|[ \-_])(.)/){" " + $2.upcase}.strip
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.to_snake(string)
|
29
|
+
string.gsub("::", "").gsub(/([A-Z]+)/){"_" + $1.downcase}.sub(/^_+/, "")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/trenni/version.rb
CHANGED
data/test/test_builder.rb
CHANGED
@@ -27,54 +27,79 @@ require 'digest/md5'
|
|
27
27
|
|
28
28
|
require 'trenni'
|
29
29
|
|
30
|
-
class
|
30
|
+
class TestBuilder < Test::Unit::TestCase
|
31
31
|
def test_tags
|
32
|
-
|
33
|
-
|
34
|
-
builder = Trenni::Builder.new(:output => output)
|
32
|
+
builder = Trenni::Builder.new(:indent => false)
|
35
33
|
|
36
34
|
builder.instruct
|
37
35
|
builder.tag('foo', 'bar' => 'baz') do
|
38
36
|
builder.text("apples and oranges")
|
39
37
|
end
|
40
38
|
|
41
|
-
assert_equal "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<foo bar=\"baz\">apples and oranges</foo>", output.string
|
39
|
+
assert_equal "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<foo bar=\"baz\">apples and oranges</foo>", builder.output.string
|
42
40
|
end
|
43
41
|
|
44
|
-
def
|
45
|
-
|
42
|
+
def test_full_html
|
43
|
+
builder = Trenni::Builder.new(:indent => true)
|
46
44
|
|
47
|
-
builder
|
48
|
-
builder.
|
49
|
-
builder.
|
50
|
-
|
51
|
-
|
52
|
-
builder.tag('title') do
|
53
|
-
builder.text('Hello World')
|
54
|
-
end
|
55
|
-
end
|
56
|
-
builder.tag('body') do
|
45
|
+
builder.doctype
|
46
|
+
builder.tag('html') do
|
47
|
+
builder.tag('head') do
|
48
|
+
builder.inline('title') do
|
49
|
+
builder.text('Hello World')
|
57
50
|
end
|
58
51
|
end
|
52
|
+
builder.tag('body') do
|
53
|
+
end
|
59
54
|
end
|
60
55
|
|
61
|
-
assert_equal "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<title
|
56
|
+
assert_equal "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<title>Hello World</title>\n\t</head>\n\t<body>\n\t</body>\n</html>", builder.output.string
|
62
57
|
end
|
63
58
|
|
64
|
-
def
|
65
|
-
|
66
|
-
text = Trenni::Builder.tag_attributes({:required => true})
|
67
|
-
assert_equal " required", text
|
59
|
+
def test_inline_html
|
60
|
+
builder = Trenni::Builder.new(:indent => true)
|
68
61
|
|
69
|
-
|
70
|
-
|
71
|
-
|
62
|
+
builder.inline("div") do
|
63
|
+
builder.tag("strong") do
|
64
|
+
builder.text("Hello")
|
65
|
+
end
|
66
|
+
|
67
|
+
builder.text "World!"
|
68
|
+
end
|
72
69
|
|
73
|
-
|
74
|
-
|
70
|
+
assert_equal "<div><strong>Hello</strong>World!</div>", builder.output.string
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_indentation
|
74
|
+
builder = Trenni::Builder.new(:indent => "\t")
|
75
75
|
|
76
|
-
|
77
|
-
|
78
|
-
|
76
|
+
puts builder.output.string
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_escaping
|
80
|
+
builder = Trenni::Builder.new(:escape => true)
|
81
|
+
builder.inline :foo, :bar => %Q{"Hello World"} do
|
82
|
+
builder.text %Q{if x < 10}
|
83
|
+
end
|
84
|
+
|
85
|
+
assert_equal %Q{<foo bar=""Hello World"">if x < 10</foo>}, builder.output.string
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_attributes_strict
|
89
|
+
builder = Trenni::Builder.new(:strict => true)
|
90
|
+
builder.tag :option, :required => true
|
91
|
+
assert_equal %Q{<option required="required"/>}, builder.output.string
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_attributes_compact
|
95
|
+
builder = Trenni::Builder.new(:strict => false)
|
96
|
+
builder.tag :option, :required => true
|
97
|
+
assert_equal %Q{<option required/>}, builder.output.string
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_attributes_order
|
101
|
+
builder = Trenni::Builder.new(:strict => true)
|
102
|
+
builder.tag :t, [[:a, 10], [:b, 20]]
|
103
|
+
assert_equal %Q{<t a="10" b="20"/>}, builder.output.string
|
79
104
|
end
|
80
105
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright (c) 2007, 2012 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'pathname'
|
24
|
+
require 'test/unit'
|
25
|
+
require 'stringio'
|
26
|
+
|
27
|
+
require 'trenni/strings'
|
28
|
+
|
29
|
+
class TestStrings < Test::Unit::TestCase
|
30
|
+
def test_to_html
|
31
|
+
text = Trenni::Strings.to_html("<foobar>")
|
32
|
+
assert_equal "<foobar>", text
|
33
|
+
|
34
|
+
text = Trenni::Strings.to_html(%q{"I'd like to do this & that :p", she said.})
|
35
|
+
assert_equal %q{"I'd like to do this & that :p", she said.}, text
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_to_quoted_string
|
39
|
+
text = Trenni::Strings.to_quoted_string(%Q{"Hello World"})
|
40
|
+
assert_equal %q{"\"Hello World\""}, text
|
41
|
+
|
42
|
+
text = Trenni::Strings.to_quoted_string(%Q{"Hello\r\nWorld"})
|
43
|
+
assert_equal %q{"\"Hello\r\nWorld\""}, text
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_to_attribute
|
47
|
+
text = Trenni::Strings.to_attribute(:foo, 'bar')
|
48
|
+
assert_equal %Q{foo="bar"}, text
|
49
|
+
|
50
|
+
text = Trenni::Strings.to_simple_attribute(:foo, false)
|
51
|
+
assert_equal %Q{foo}, text
|
52
|
+
|
53
|
+
text = Trenni::Strings.to_simple_attribute(:foo, true)
|
54
|
+
assert_equal %Q{foo="foo"}, text
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.to_simple_attribute
|
58
|
+
strict ? %Q{#{key}="#{key}"} : key.to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_to_title
|
62
|
+
text = Trenni::Strings.to_title("foo bar")
|
63
|
+
assert_equal "Foo Bar", text
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_to_snake
|
67
|
+
text = Trenni::Strings.to_snake("Happy::Go::Lucky")
|
68
|
+
assert_equal "happy_go_lucky", text
|
69
|
+
end
|
70
|
+
end
|
data/test/test_template.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trenni
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -28,9 +28,11 @@ files:
|
|
28
28
|
- Rakefile
|
29
29
|
- lib/trenni.rb
|
30
30
|
- lib/trenni/builder.rb
|
31
|
+
- lib/trenni/strings.rb
|
31
32
|
- lib/trenni/template.rb
|
32
33
|
- lib/trenni/version.rb
|
33
34
|
- test/test_builder.rb
|
35
|
+
- test/test_strings.rb
|
34
36
|
- test/test_template.rb
|
35
37
|
- trenni.gemspec
|
36
38
|
homepage: https://github.com/ioquatix/trenni
|
@@ -47,7 +49,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
47
49
|
version: '0'
|
48
50
|
segments:
|
49
51
|
- 0
|
50
|
-
hash: -
|
52
|
+
hash: -2578082460541813883
|
51
53
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
54
|
none: false
|
53
55
|
requirements:
|
@@ -56,7 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
58
|
version: '0'
|
57
59
|
segments:
|
58
60
|
- 0
|
59
|
-
hash: -
|
61
|
+
hash: -2578082460541813883
|
60
62
|
requirements: []
|
61
63
|
rubyforge_project:
|
62
64
|
rubygems_version: 1.8.24
|
@@ -65,4 +67,5 @@ specification_version: 3
|
|
65
67
|
summary: A fast native templating system that compiles directly to Ruby code.
|
66
68
|
test_files:
|
67
69
|
- test/test_builder.rb
|
70
|
+
- test/test_strings.rb
|
68
71
|
- test/test_template.rb
|