html-template 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3e35f465439469ba0e64f58a6ece96b2b5aa8358
4
- data.tar.gz: 94054c9cf1dd94bee198b2e76e296542698b2c49
3
+ metadata.gz: 99e549ccfeeab65c5a307442d82910d2c69fa03a
4
+ data.tar.gz: c7f0bf3e17141ea27250e99ea184c61a8f85fa96
5
5
  SHA512:
6
- metadata.gz: 3ffb1e09a7d8d3ddea7bc13789ed8037813750ae8dcad89e7f0d778808ef9c1797e86545031a734efe4ac9a0b50ef1ad9b613cd15bf743d9c2276fc57e1dd4e5
7
- data.tar.gz: 3c5cf4655de62ee201567fb5650b8d8219b13fc3b6be6a534a1a8db8980f96fe9080d65c14fe04246793b1c891da972a72f235d7298c3b7cc7526db0025c9288
6
+ metadata.gz: e3a4b8328958f8934858ec3d62f906e40ce0ebc74638101bca301656319c20560ae6c748892b6ee6f0ceaa6281d063a64d256628955ab49b951ecd11a0474ddd
7
+ data.tar.gz: b225712c8e413f29d4f9e76fc35c8fa38106e32cb062c77f6695e544980c087d2f733b3ebe55873bdcf98809f22f9741e9075165ef25e2e1354679a481e63d18
data/Manifest.txt CHANGED
@@ -8,7 +8,6 @@ test/helper.rb
8
8
  test/templates/opml.xml
9
9
  test/templates/opml.xml.erb
10
10
  test/templates/opml.xml.tmpl
11
- test/templates/planet/README.md
12
11
  test/templates/planet/atom.xml.tmpl
13
12
  test/templates/planet/basic/index.html.tmpl
14
13
  test/templates/planet/fancy/index.html.tmpl
data/README.md CHANGED
@@ -23,15 +23,17 @@ the ruby version.
23
23
 
24
24
  First you make a template - this is just a normal HTML file with a few extra tags, the simplest being `<TMPL_VAR>`
25
25
 
26
- For example, `students.html.tmpl`:
26
+ For example, `test.html.tmpl`:
27
27
 
28
28
  ```
29
- <TMPL_LOOP students>
30
- <p>
31
- Name: <TMPL_VAR name><br>
32
- GPA: <TMPL_VAR gpa>
33
- </p>
34
- </TMPL_LOOP>
29
+ <html>
30
+ <head><title>Test Template</title></head>
31
+ <body>
32
+ My Home Directory is <TMPL_VAR home>
33
+ <p>
34
+ My Path is set to <TMPL_VAR path>
35
+ </body>
36
+ </html>
35
37
  ```
36
38
 
37
39
  Now you can use it in your script:
@@ -39,26 +41,23 @@ Now you can use it in your script:
39
41
  ``` ruby
40
42
  require 'html/template'
41
43
 
42
- template = HtmlTemplate.new( filename: 'students.html.tmpl' )
44
+ template = HtmlTemplate.new( filename: 'test.html.tmpl' )
43
45
 
44
- puts template.render( students: [ { name: 'Bluto Blutarsky', gpa: 0.0 },
45
- { name: 'Tracey Flick', gpa: 4.0 }
46
- ]
47
- )
46
+ puts template.render( home: ENV['HOME'],
47
+ path: ENV['PATH'] )
48
48
  ```
49
49
 
50
50
  If all is well in the universe this should output something like this:
51
51
 
52
- ```
52
+ ``` html
53
+ <html>
54
+ <head><title>Test Template</title></head>
55
+ <body>
56
+ My Home Directory is /home/alice
53
57
  <p>
54
- Name: Bluto Blutarsky<br>
55
- GPA: 0.0
56
- </p>
57
-
58
- <p>
59
- Name: Tracey Flick<br>
60
- GPA: 4.0
61
- </p>
58
+ My Path is set to /bin;/usr/bin
59
+ </body>
60
+ </html>
62
61
  ```
63
62
 
64
63
 
@@ -143,7 +142,7 @@ puts template.result( employees: [ { name: 'Sam', job: 'programmer' },
143
142
 
144
143
  The output is:
145
144
 
146
- ```
145
+ ``` html
147
146
  <p>
148
147
  Name: Sam<br>
149
148
  Job: programmer
@@ -162,7 +161,7 @@ and then iterates over the loop body producing output.
162
161
  **Loop Context Variables**
163
162
 
164
163
  Inside a loop extra variables that depend on the loop's context
165
- are made available. These are:
164
+ are made available if the `loop_vars` option is set to true (yes, true by default). These are:
166
165
 
167
166
  `__FIRST__`
168
167
 
@@ -329,7 +328,7 @@ puts HtmlTemplate.new( <<TXT ).text
329
328
  TXT
330
329
  ```
331
330
 
332
- will print if debugging is turned on:
331
+ will print if debugging is turned on (e.g. `HtmlTemplate.config.debug = true`):
333
332
 
334
333
  ```
335
334
  line 4 - match <TMPL_VAR name ESCAPE="HTML"> replacing with: <%=h name %>
@@ -3,7 +3,7 @@
3
3
 
4
4
  class HtmlTemplate
5
5
  MAJOR = 0
6
- MINOR = 1
6
+ MINOR = 2
7
7
  PATCH = 0
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
data/lib/html/template.rb CHANGED
@@ -16,13 +16,13 @@ module Enumerable
16
16
  end
17
17
  def index=(value) @index=value; end
18
18
  end
19
-
19
+
20
20
  def each_with_loop( &blk )
21
21
  loop_meta = LoopMeta.new( size )
22
22
  each_with_index do |item, index|
23
23
  loop_meta.index = index
24
24
  blk.call( item, loop_meta )
25
- end
25
+ end
26
26
  end
27
27
  end
28
28
 
@@ -35,55 +35,100 @@ require 'html/template/version' # note: let version always get first
35
35
 
36
36
  class HtmlTemplate
37
37
 
38
+ class Configuration
39
+ def debug=(value) @debug = value; end
40
+ def debug?() @debug || false; end
41
+
42
+ def strict=(value) @strict = value; end
43
+ def strict?() @strict || true; end
44
+
45
+ def loop_vars=(value) @loop_vars = value; end
46
+ def loop_vars?() @loop_vars || true; end
47
+ end # class Configuration
48
+
49
+ ## lets you use
50
+ ## HtmlTemplate.configure do |config|
51
+ ## config.debug = true
52
+ ## config.strict = true
53
+ ## end
54
+
55
+ def self.configure
56
+ yield( config )
57
+ end
58
+
59
+ def self.config
60
+ @config ||= Configuration.new
61
+ end
62
+
63
+
38
64
  attr_reader :text ## returns converted template text (with "breaking" comments!!!)
39
- attr_reader :template ## return "inner" (erb) template object
65
+ attr_reader :template ## return "inner" (erb) template object
40
66
  attr_reader :errors
67
+ attr_reader :names ## for debugging - returns all referenced / used names in VAR/IF/UNLESS/LOOP/etc.
68
+
69
+ ## config convenience (shortcut) helpers
70
+ def config() self.class.config; end
71
+ def debug?() config.debug?; end
72
+
73
+ def strict?() @strict; end
74
+ def loop_vars?() @loop_vars; end
41
75
 
42
- def initialize( text=nil, filename: nil )
76
+ def initialize( text=nil, filename: nil, strict: config.strict?, loop_vars: config.loop_vars? )
43
77
  if text.nil? ## try to read file (by filename)
44
78
  text = File.open( filename, 'r:utf-8' ) { |f| f.read }
45
79
  end
46
80
 
81
+ ## options
82
+ @strict = strict
83
+ @loop_vars = loop_vars
84
+
47
85
  ## todo/fix: add filename to ERB too (for better error reporting)
48
- @text, @errors = convert( text ) ## note: keep a copy of the converted template text
49
-
86
+ @text, @errors, @names = convert( text ) ## note: keep a copy of the converted template text
87
+
50
88
  if @errors.size > 0
51
89
  puts "!! ERROR - #{@errors.size} conversion / syntax error(s):"
52
90
  pp @errors
53
- raise ## todo - find a good Error - StandardError - why? why not?
91
+
92
+ raise if strict? ## todo - find a good Error - StandardError - why? why not?
54
93
  end
55
94
 
56
95
  @template = ERB.new( @text, nil, '%<>' )
57
96
  end
58
97
 
59
98
 
99
+
100
+ IDENT = '[a-zA-Z_][a-zA-Z0-9_]*'
101
+ ESCAPES = 'HTML|NONE'
102
+
60
103
  VAR_RE = %r{<TMPL_(?<tag>VAR)
61
- \s
62
- (?<ident>[a-zA-Z_][a-zA-Z0-9_]*)
63
- (\s
104
+ \s+
105
+ (?<ident>#{IDENT})
106
+ (\s+
64
107
  (?<escape>ESCAPE)
65
- =
66
- "(?<format>HTML|NONE)"
108
+ =(
109
+ (?<q>['"])(?<format>#{ESCAPES})\k<q> # note: allow single or double enclosing quote (but MUST match)
110
+ | (?<format>#{ESCAPES}) # note: support without quotes too
111
+ )
67
112
  )?
68
113
  >}x
69
114
 
70
115
  IF_OPEN_RE = %r{(?<open><)TMPL_(?<tag>IF|UNLESS)
71
- \s
72
- (?<ident>[a-zA-Z_][a-zA-Z0-9_]*)
116
+ \s+
117
+ (?<ident>#{IDENT})
73
118
  >}x
74
119
 
75
120
  IF_CLOSE_RE = %r{(?<close></)TMPL_(?<tag>IF|UNLESS)
76
- (\s
77
- (?<ident>[a-zA-Z_][a-zA-Z0-9_]*)
78
- )? # note: allow optional identifier
121
+ (\s+
122
+ (?<ident>#{IDENT})
123
+ )? # note: allow optional identifier
79
124
  >}x
80
-
125
+
81
126
  ELSE_RE = %r{<TMPL_(?<tag>ELSE)
82
127
  >}x
83
128
 
84
129
  LOOP_OPEN_RE = %r{(?<open><)TMPL_(?<tag>LOOP)
85
- \s
86
- (?<ident>[a-zA-Z_][a-zA-Z0-9_]*)
130
+ \s+
131
+ (?<ident>#{IDENT})
87
132
  >}x
88
133
 
89
134
  LOOP_CLOSE_RE = %r{(?<close></)TMPL_(?<tag>LOOP)
@@ -91,7 +136,7 @@ class HtmlTemplate
91
136
 
92
137
  CATCH_OPEN_RE = %r{(?<open><)TMPL_(?<unknown>[^>]+?)
93
138
  >}x
94
-
139
+
95
140
  CATCH_CLOSE_RE = %r{(?<close></)TMPL_(?<unknown>[^>]+?)
96
141
  >}x
97
142
 
@@ -106,7 +151,7 @@ class HtmlTemplate
106
151
  CATCH_CLOSE_RE )
107
152
 
108
153
 
109
- def to_recursive_ostruct( o )
154
+ def to_recursive_ostruct( o )
110
155
  if o.is_a?( Array )
111
156
  o.reduce( [] ) do |ary, item|
112
157
  ary << to_recursive_ostruct( item )
@@ -125,8 +170,47 @@ class HtmlTemplate
125
170
  end
126
171
 
127
172
 
173
+ class Names
174
+ def initialize
175
+ @names = Hash.new
176
+ end
177
+
178
+ def to_h() @names; end
179
+
180
+ def add( scope, ident, tag )
181
+ ## e.g. scope e.g. ['feeds'] or ['feeds','items'] - is a stack / array
182
+ ## ident e.g. title - is a string
183
+ ## tag e.g. $VAR/$LOOP/$IF/$UNLESS - is a string
184
+ h = fetch( scope, ident )
185
+ h[ tag ] ||= 0
186
+ h[ tag ] += 1
187
+ end
188
+
189
+ private
190
+ def fetch( scope, ident )
191
+ ## fetch name in scoped hierarchy
192
+ ## if first time than setup new empty hash
193
+ h = @names
194
+ scope.each do |name|
195
+ h = h[name] ||= {}
196
+ end
197
+ h[ ident ] ||= {}
198
+ end
199
+ end ## class Names
200
+
201
+
202
+ def ident_to_loop_it( ident ) # make loop iterator (e.g. Channels => channel and so on)
203
+ ## assume plural ident e.g. channels
204
+ ## cut-off last char, that is,
205
+ ## the plural s channels => channel
206
+ ## note: ALWAYS downcase (auto-generated) loop iterator/pass name
207
+ ident[0..-2].downcase
208
+ end
209
+
210
+
128
211
  def convert( text )
129
- errors = [] # note: reset global errros list
212
+ errors = [] # note: reset global errros list
213
+ names = Names.new ## keep track of all referenced / used names in VAR/IF/UNLESS/LOOP/etc.
130
214
 
131
215
  stack = []
132
216
 
@@ -138,7 +222,7 @@ class HtmlTemplate
138
222
  lineno += 1
139
223
 
140
224
  if line.lstrip.start_with?( '#' ) ## or make it tripple ### - why? why not?
141
- buf << "%#{line.lstrip}"
225
+ buf << "%#{line.lstrip}"
142
226
  elsif line.strip.empty?
143
227
  buf << line
144
228
  else
@@ -159,9 +243,19 @@ class HtmlTemplate
159
243
  ## note: peek; get top stack item
160
244
  ## if top level (stack empty) => nothing
161
245
  ## otherwise => channel. or item. etc. (with trailing dot included!)
162
- ctx = stack.empty? ? '' : "#{stack[-1]}."
246
+ ctx = if stack.empty?
247
+ ''
248
+ else
249
+ ## assume plural ident e.g. channels
250
+ ## cut-off last char, that is,
251
+ ## the plural s channels => channel
252
+ ## note: ALWAYS downcase (auto-generated) loop iterator/pass name
253
+ "#{ident_to_loop_it( stack[-1] )}."
254
+ end
163
255
 
164
256
  code = if tag == 'VAR'
257
+ names.add( stack, ident, '$VAR' )
258
+
165
259
  if escape && format == 'HTML'
166
260
  ## check or use long form e.g. CGI.escapeHTML - why? why not?
167
261
  "<%=h #{ctx}#{ident} %>"
@@ -169,20 +263,28 @@ class HtmlTemplate
169
263
  "<%= #{ctx}#{ident} %>"
170
264
  end
171
265
  elsif tag == 'LOOP' && tag_open
266
+ names.add( stack, ident, '$LOOP' )
267
+
172
268
  ## assume plural ident e.g. channels
173
269
  ## cut-off last char, that is, the plural s channels => channel
174
270
  ## note: ALWAYS downcase (auto-generated) loop iterator/pass name
175
- it = ident[0..-2].downcase
176
- stack.push( it )
177
- "<% #{ctx}#{ident}.each_with_loop do |#{it}, #{it}_loop| %>"
271
+ it = ident_to_loop_it( ident )
272
+ stack.push( ident )
273
+ if loop_vars?
274
+ "<% #{ctx}#{ident}.each_with_loop do |#{it}, #{it}_loop| %>"
275
+ else
276
+ "<% #{ctx}#{ident}.each do |#{it}| %>"
277
+ end
178
278
  elsif tag == 'LOOP' && tag_close
179
279
  stack.pop
180
280
  "<% end %>"
181
281
  elsif tag == 'IF' && tag_open
282
+ names.add( stack, ident, '$IF' )
182
283
  "<% if #{ctx}#{ident} %>"
183
- elsif tag == 'UNLESS' && tag_open
184
- "<% unless #{ctx}#{ident} %>"
185
- elsif (tag == 'IF' || tag == 'UNLESS') && tag_close
284
+ elsif tag == 'UNLESS' && tag_open
285
+ names.add( stack, ident, '$UNLESS' )
286
+ "<% unless #{ctx}#{ident} %>"
287
+ elsif (tag == 'IF' || tag == 'UNLESS') && tag_close
186
288
  "<% end %>"
187
289
  elsif tag == 'ELSE'
188
290
  "<% else %>"
@@ -200,13 +302,13 @@ class HtmlTemplate
200
302
  raise ArgumentError ## unknown tag #{tag}
201
303
  end
202
304
 
203
- puts " line #{lineno} - match #{m[0]} replacing with: #{code}"
305
+ puts " line #{lineno} - match #{m[0]} replacing with: #{code}" if debug?
204
306
  code
205
307
 
206
308
  end
207
309
  end
208
310
  end # each_line
209
- [buf, errors]
311
+ [buf, errors, names.to_h]
210
312
  end # method convert
211
313
 
212
314
 
@@ -229,9 +331,9 @@ class HtmlTemplate
229
331
  hash[key] = to_recursive_ostruct( val )
230
332
  hash
231
333
  end
232
-
334
+
233
335
  ## (auto-)convert array and hash values to ostruct
234
- ## for easy dot (.) access
336
+ ## for easy dot (.) access
235
337
  ## e.g. student.name instead of student[:name]
236
338
 
237
339
  @template.result( Context.new( **kwargs ).get_binding )
data/test/helper.rb CHANGED
@@ -4,6 +4,7 @@ require 'minitest/autorun'
4
4
  ## our own code
5
5
  require 'html/template'
6
6
 
7
+ HtmlTemplate.config.debug = true
7
8
 
8
9
 
9
10
  ########
@@ -1,4 +1,4 @@
1
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
1
+ <!DOCTYPE HTML>
2
2
  <html>
3
3
 
4
4
  ### Planet HTML template.
@@ -22,7 +22,7 @@
22
22
 
23
23
  <head>
24
24
  <title><TMPL_VAR name></title>
25
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
25
+ <meta charset="utf-8">
26
26
  <meta name="generator" content="<TMPL_VAR generator ESCAPE="HTML">">
27
27
  </head>
28
28
 
@@ -85,4 +85,4 @@
85
85
  </p>
86
86
  </body>
87
87
 
88
- </html>
88
+ </html>
@@ -1,4 +1,4 @@
1
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
1
+ <!DOCTYPE HTML>
2
2
  <html>
3
3
 
4
4
  ### Fancy Planet HTML template.
@@ -13,7 +13,7 @@
13
13
 
14
14
  <head>
15
15
  <title><TMPL_VAR name></title>
16
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
16
+ <meta charset="utf-8">
17
17
  <meta name="generator" content="<TMPL_VAR generator ESCAPE="HTML">">
18
18
  <link rel="stylesheet" href="planet.css" type="text/css">
19
19
  <TMPL_IF feedtype>
data/test/test_merge.rb CHANGED
@@ -78,24 +78,63 @@ print $template->output;
78
78
  =end
79
79
 
80
80
  def test_students_example
81
-
82
- tmpl =<<TXT
83
- <TMPL_LOOP students>
84
- <p>
85
- Name: <TMPL_VAR name><br/>
86
- GPA: <TMPL_VAR gpa>
87
- </p>
88
- </TMPL_LOOP>
81
+ t = HtmlTemplate.new( <<TXT )
82
+ <TMPL_LOOP students>
83
+ <p>
84
+ Name: <TMPL_VAR name><br/>
85
+ GPA: <TMPL_VAR gpa>
86
+ </p>
87
+ </TMPL_LOOP>
89
88
  TXT
90
89
 
91
- t = HtmlTemplate.new( tmpl )
92
90
  puts t.text
93
91
  puts "---"
94
- puts t.render( students: [ { name: 'Bluto Blutarsky', gpa: 0.0 },
95
- { name: 'Tracey Flick', gpa: 4.0 } ])
92
+ result = t.render( students: [ { name: 'Bluto Blutarsky', gpa: 0.0 },
93
+ { name: 'Tracey Flick', gpa: 4.0 } ])
94
+ puts result
95
+
96
+ exp =<<TXT
97
+ <p>
98
+ Name: Bluto Blutarsky<br/>
99
+ GPA: 0.0
100
+ </p>
101
+ <p>
102
+ Name: Tracey Flick<br/>
103
+ GPA: 4.0
104
+ </p>
105
+ TXT
106
+
107
+ assert_equal exp, result
108
+ end
96
109
 
97
110
 
98
- assert true # assume it's alright if we get here
99
- end
111
+ def test_escape
112
+ t = HtmlTemplate.new( <<TXT )
113
+ <TMPL_LOOP pubs>
114
+ Name: <TMPL_VAR name>
115
+ Name: <TMPL_VAR name ESCAPE=HTML>
116
+ Name: <TMPL_VAR name ESCAPE="HTML">
117
+ Name: <TMPL_VAR name ESCAPE='HTML'>
118
+
119
+ </TMPL_LOOP>
120
+ TXT
121
+
122
+ exp =<<TXT
123
+ Name: Fuller's Ale & Pie House
124
+ Name: Fuller&#39;s Ale &amp; Pie House
125
+ Name: Fuller&#39;s Ale &amp; Pie House
126
+ Name: Fuller&#39;s Ale &amp; Pie House
127
+
128
+ Name: Mel's Craft Beers & Diner
129
+ Name: Mel&#39;s Craft Beers &amp; Diner
130
+ Name: Mel&#39;s Craft Beers &amp; Diner
131
+ Name: Mel&#39;s Craft Beers &amp; Diner
132
+
133
+ TXT
134
+
135
+ assert_equal exp, t.render( pubs: [{ name: "Fuller's Ale & Pie House" },
136
+ { name: "Mel's Craft Beers & Diner" }])
100
137
  end
101
138
 
139
+ end # class TestMerge
140
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: html-template
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-10 00:00:00.000000000 Z
11
+ date: 2020-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc
@@ -58,7 +58,6 @@ files:
58
58
  - test/templates/opml.xml
59
59
  - test/templates/opml.xml.erb
60
60
  - test/templates/opml.xml.tmpl
61
- - test/templates/planet/README.md
62
61
  - test/templates/planet/atom.xml.tmpl
63
62
  - test/templates/planet/basic/index.html.tmpl
64
63
  - test/templates/planet/fancy/index.html.tmpl
@@ -1,7 +0,0 @@
1
- # Notes on (Original) Planet HTML Templates
2
-
3
- see <https://people.gnome.org/~jdub/bzr/planet/devel/trunk/examples/>
4
-
5
-
6
-
7
-