zrb 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3d95c6b276edcdeb944acbb480dc0104d5cf0bd0
4
+ data.tar.gz: 8583aaf2793e75e1f4f8b4e2bb37a769243178b3
5
+ SHA512:
6
+ metadata.gz: 80a4758f359a27d984fd2b52d9120e132adccb17ebcea23c6c183d5ad36f21c73e2e82214d648c20f270fc21094b6f59eaa7c051666acbd5cd9c0af3803bc496
7
+ data.tar.gz: d3ecbb267cd5add08f970ae5659065d0114ecb58afe47534f0ea22cb2a2d2c68ff3e49c220a8c988cd7063eec1ba29e8518cf2c1e9fb7db7f7edf0b0ab1e8f7a
@@ -0,0 +1,54 @@
1
+ # ZRB, simple Ruby template engine
2
+
3
+ ZRB is a lightweight template engine with the following features:
4
+
5
+ * Automatic HTML escaping of expressions
6
+ * Ruby-like syntax for expressions: `#{person.name}`
7
+ * Helper functions can capture the result of yielding a block
8
+ * Block helpers are supported
9
+
10
+ Example:
11
+
12
+ ```zrb
13
+ <h1>Welcome #{user.name}</h1>
14
+
15
+ <ul>
16
+ <? messages.each do |msg| ?>
17
+ <li>#{msg.text}</li>
18
+ <? end ?>
19
+ </ul>
20
+
21
+ <?= form_for messages_path do |f| ?>
22
+ <textarea name="content"></textarea>
23
+ <? end ?>
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ZRB uses [Tilt](https://github.com/rtomayko/tilt) for rendering.
29
+ `ZRB::Template` is a Tilt template and you can use all of Tilt's
30
+ features:
31
+
32
+ ```ruby
33
+ require 'zrb'
34
+ tmpl = ZRB::Template.new('index.zrb')
35
+ tmpl.render(scope, :user => user)
36
+ ```
37
+
38
+ Note however that ZRB has one strict requirement on the scope: **The
39
+ scope must implement the method `build_zrb_buffer`**. This method should
40
+ return an instance of `ZRB::Buffer` (or a subclass like
41
+ `ZRB::HTMLBuffer`). This is required in order to support block helpers:
42
+
43
+ ```ruby
44
+ class RenderScope
45
+ def build_zrb_buffer
46
+ @_buffer = ZRB::HTMLBuffer.new
47
+ end
48
+
49
+ def form_for(path, &blk)
50
+ "<form>#{@_buffer.capture(blk)}</form>"
51
+ end
52
+ end
53
+ ```
54
+
@@ -0,0 +1,193 @@
1
+ module ZRB
2
+ class Parser
3
+ attr_reader :data, :source, :filename, :lineno
4
+
5
+ def initialize(data, filename, lineno)
6
+ @data = data
7
+ @filename = filename
8
+ @lineno = lineno
9
+ @source = ""
10
+ @state = :ruby
11
+ generate
12
+ end
13
+
14
+ SPACE = /[\t ]/
15
+
16
+ TOKEN = /
17
+ (.*?) # Regular text
18
+ (?:
19
+ ( # Expression start
20
+ \#\{
21
+ )
22
+
23
+ | (?: # Statement
24
+ (^#{SPACE}*)?
25
+ \<\? (.*?) \?>
26
+ (#{SPACE}*\n)? # Remove trailing newline
27
+ )
28
+ | \z
29
+ )
30
+ /mx
31
+
32
+ EXPR_CLOSE = '}'
33
+
34
+ def parse
35
+ pos = 0
36
+ while pos < data.size
37
+ match = data.match(TOKEN, pos)
38
+ raise SyntaxError, "#{filename}:#{lineno}: parse error" unless match
39
+ all, text, expr, statement_space, statement, _ = *match
40
+ pos += all.size
41
+
42
+ yield :text, text unless text.empty?
43
+
44
+ if expr
45
+ # Start looking for a closing brace
46
+ closing_pos = pos
47
+ begin
48
+ closing_pos = data.index(EXPR_CLOSE, closing_pos + 1)
49
+ break unless closing_pos
50
+ expr = data[pos...closing_pos]
51
+ end until valid_ruby?(expr)
52
+
53
+ if !closing_pos
54
+ raise SyntaxError, "#{filename}:#{lineno}: unclosed brace"
55
+ end
56
+
57
+ yield :expression, expr
58
+
59
+ pos = closing_pos + 1
60
+ end
61
+
62
+ yield :newline if statement_space
63
+
64
+ if statement
65
+ case statement[0]
66
+ when ?=
67
+ # block expression
68
+ yield :block_expression, statement[1..-1]
69
+ when ?#
70
+ # comment
71
+ yield :comment, statement[1..-1]
72
+ else
73
+ yield :statement, statement
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def valid_ruby?(text)
80
+ catch(:halt) do
81
+ eval("BEGIN{throw :halt};#{text}")
82
+ end
83
+ rescue SyntaxError
84
+ false
85
+ else
86
+ true
87
+ end
88
+
89
+ def ensure_string
90
+ if @state != :string
91
+ source << 'buffer.append_string("'
92
+ @state = :string
93
+ end
94
+ end
95
+
96
+ def append_text(text)
97
+ escaped = text.split("\n", -1).map { |part| part.inspect[1..-2] }.join("\n")
98
+
99
+ ensure_string
100
+ source << escaped
101
+ end
102
+
103
+ def append_expr(ruby)
104
+ ensure_string
105
+ source << "\#{buffer.escape((#{ruby}))}"
106
+ end
107
+
108
+ def append_ruby(ruby)
109
+ source << '");' if @state == :string
110
+ source << ruby
111
+ @state = :ruby
112
+ end
113
+
114
+ def append_newline
115
+ if @state == :string
116
+ source << "\"\\\n\""
117
+ else
118
+ source << "\n"
119
+ end
120
+ end
121
+
122
+ def generate
123
+ append_ruby('buffer = build_zrb_buffer;')
124
+
125
+ parse do |type, value|
126
+ case type
127
+ when :text
128
+ append_text(value)
129
+ when :expression
130
+ append_expr(value)
131
+ when :block_expression
132
+ append_ruby("buffer.append=#{value};")
133
+ when :statement
134
+ append_ruby("#{value};")
135
+ when :newline
136
+ append_newline
137
+ end
138
+ end
139
+
140
+ append_ruby('buffer')
141
+ end
142
+ end
143
+
144
+ require 'tilt'
145
+
146
+ class Template < Tilt::Template
147
+ def prepare
148
+ @src = Parser.new(data, eval_file, line).source
149
+ end
150
+
151
+ def precompiled_template(locals)
152
+ @src
153
+ end
154
+
155
+ Tilt.register Template, 'zrb'
156
+ end
157
+
158
+ class Buffer < String
159
+ def escape(other)
160
+ other.to_s
161
+ end
162
+
163
+ def append=(other)
164
+ self << escape(other)
165
+ end
166
+
167
+ def append_string(other)
168
+ self << other
169
+ end
170
+
171
+ def capture(blk)
172
+ start = self.size
173
+ blk.call
174
+ ensure
175
+ return self.slice!(start..-1)
176
+ end
177
+ end
178
+
179
+ require 'cgi'
180
+
181
+ class HTMLBuffer < Buffer
182
+ def to_html; self end
183
+
184
+ def escape(other)
185
+ if other.respond_to?(:to_html)
186
+ other.to_html
187
+ else
188
+ CGI.escape_html(other.to_s)
189
+ end
190
+ end
191
+ end
192
+ end
193
+
@@ -0,0 +1,4 @@
1
+ module ZRB
2
+ VERSION = '1.0.0'
3
+ end
4
+
@@ -0,0 +1,97 @@
1
+ require 'minitest/autorun'
2
+ require 'zrb'
3
+
4
+ class TestZRB < Minitest::Test
5
+ include ZRB
6
+
7
+ def build_zrb_buffer
8
+ flunk "no buffer setup" unless @buffer
9
+ return @buffer
10
+ ensure
11
+ @buffer = nil
12
+ end
13
+
14
+ def test_text
15
+ @buffer = Buffer.new
16
+ t = Template.new { "Hello world" }
17
+ assert_equal "Hello world", t.render(self)
18
+
19
+ @buffer = Buffer.new
20
+ t = Template.new { "Hello\nworld" }
21
+ assert_equal "Hello\nworld", t.render(self)
22
+ end
23
+
24
+ def test_capture
25
+ @buffer = Buffer.new
26
+ t = Template.new { <<-EOF }
27
+ <? @res = buffer.capture(proc do ?>
28
+ Hello world!
29
+ <? end) ?>
30
+ EOF
31
+
32
+ assert_equal "", t.render(self).strip
33
+ assert_instance_of Buffer, @res
34
+ assert_equal "Hello world!", @res.strip
35
+ end
36
+
37
+ def test_stmt_spacing
38
+ t = Template.new { <<-EOF }
39
+ <? if check ?>
40
+ hello
41
+ <? else ?>
42
+ world
43
+ <? end ?>
44
+ EOF
45
+
46
+ @buffer = Buffer.new
47
+ assert_equal "hello\n", t.render(self, :check => true)
48
+ @buffer = Buffer.new
49
+ assert_equal "world\n", t.render(self, :check => false)
50
+ end
51
+
52
+ def test_nested_capture
53
+ @buffer = Buffer.new
54
+ t = Template.new { <<-EOF }
55
+ <? @res1 = buffer.capture(proc do ?>
56
+ Hello world!
57
+ <? @res2 = buffer.capture(proc do ?>
58
+ Hello second!
59
+ <? end) ?>
60
+ <? end) ?>
61
+ EOF
62
+
63
+ assert_equal "", t.render(self).strip
64
+ assert_instance_of Buffer, @res1
65
+ assert_instance_of Buffer, @res2
66
+ assert_equal "Hello world!", @res1.strip
67
+ assert_equal "Hello second!", @res2.strip
68
+ end
69
+
70
+ def test_expr
71
+ @buffer = Buffer.new
72
+ t = Template.new { 'Hello #{1 + 1}' }
73
+ assert_equal "Hello 2", t.render(self)
74
+ end
75
+
76
+ def form_for(&blk)
77
+ "<form>#{@form_buffer.capture(blk).strip}</form>"
78
+ end
79
+
80
+ def test_block_expr
81
+ @form_buffer = @buffer = Buffer.new
82
+ t = Template.new { <<-EOF }
83
+ <?= form_for do ?>
84
+ Hello world!
85
+ <? end ?>
86
+ EOF
87
+
88
+ assert_equal "<form>Hello world!</form>", t.render(self).strip
89
+ end
90
+
91
+ def test_html
92
+ @buffer = HTMLBuffer.new
93
+ t = Template.new { 'Hello #{"&"}' }
94
+ assert_equal "Hello &amp;", t.render(self)
95
+ end
96
+ end
97
+
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zrb
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Magnus Holm
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: tilt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email:
29
+ - judofyr@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README.md
35
+ - lib/zrb.rb
36
+ - lib/zrb/version.rb
37
+ - test/test_zrb.rb
38
+ homepage: https://github.com/judofyr/zrb
39
+ licenses:
40
+ - MIT
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 1.9.3
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 2.2.2
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Simple template engine
62
+ test_files: []