html-template 0.1.0 → 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.
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
-