html-template 0.0.1 → 0.1.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: d7ee8a92f5791e4ac9a898027d3815cb0f4c3619
4
- data.tar.gz: f20c60078147da549da1495b13abb55f7aef81f8
3
+ metadata.gz: 3e35f465439469ba0e64f58a6ece96b2b5aa8358
4
+ data.tar.gz: 94054c9cf1dd94bee198b2e76e296542698b2c49
5
5
  SHA512:
6
- metadata.gz: 49601653c67d7e9eb88813fe399e6aa738a8bb232d4048b98aaaa918335e9d22261d24f084243d545c5f6f9ae4849809ac49a30d3b210396e1ca138a47ec8455
7
- data.tar.gz: cdbc81e61aabbac50bfd499d3ffa9efa3417d3652fe2610a32c66b39d597a17bc604431b78f1684031a055527f3618de78770e7f0394b84e5f3669972bb73bed
6
+ metadata.gz: 3ffb1e09a7d8d3ddea7bc13789ed8037813750ae8dcad89e7f0d778808ef9c1797e86545031a734efe4ac9a0b50ef1ad9b613cd15bf743d9c2276fc57e1dd4e5
7
+ data.tar.gz: 3c5cf4655de62ee201567fb5650b8d8219b13fc3b6be6a534a1a8db8980f96fe9080d65c14fe04246793b1c891da972a72f235d7298c3b7cc7526db0025c9288
data/README.md CHANGED
@@ -9,9 +9,364 @@
9
9
 
10
10
 
11
11
 
12
+
13
+ ## Intro
14
+
15
+ Note: The original [`HTML::Template`](https://metacpan.org/pod/HTML::Template) package was written by Sam Tregar et al
16
+ (in Perl with a first 1.0 release in 1999!)
17
+ and the documentation here
18
+ is mostly a copy from the original
19
+ with some changes / adoptions for
20
+ the ruby version.
21
+
22
+
23
+
24
+ First you make a template - this is just a normal HTML file with a few extra tags, the simplest being `<TMPL_VAR>`
25
+
26
+ For example, `students.html.tmpl`:
27
+
28
+ ```
29
+ <TMPL_LOOP students>
30
+ <p>
31
+ Name: <TMPL_VAR name><br>
32
+ GPA: <TMPL_VAR gpa>
33
+ </p>
34
+ </TMPL_LOOP>
35
+ ```
36
+
37
+ Now you can use it in your script:
38
+
39
+ ``` ruby
40
+ require 'html/template'
41
+
42
+ template = HtmlTemplate.new( filename: 'students.html.tmpl' )
43
+
44
+ puts template.render( students: [ { name: 'Bluto Blutarsky', gpa: 0.0 },
45
+ { name: 'Tracey Flick', gpa: 4.0 }
46
+ ]
47
+ )
48
+ ```
49
+
50
+ If all is well in the universe this should output something like this:
51
+
52
+ ```
53
+ <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>
62
+ ```
63
+
64
+
12
65
  ## Usage
13
66
 
14
- To be done
67
+
68
+ This library attempts to make using HTML templates simple and natural.
69
+ It extends standard HTML with a few new HTML-esque tags - `<TMPL_VAR>` `<TMPL_LOOP>`, `<TMPL_IF>`, `<TMPL_ELSE>` and `<TMPL_UNLESS>`. The file written with HTML and these new tags is called a template. It is usually saved separate from your script - possibly even created by someone else! Using this module you fill in the values for the variables, loops and branches declared in the template. This allows you to separate design - the HTML - from the data, which you generate in the ~~Perl~~ Ruby script.
70
+
71
+ ### The Tags
72
+
73
+ `TMPL_VAR` • `TMPL_LOOP` • `TMPL_IF` • `TMPL_ELSE` • `TMPL_UNLESS`
74
+
75
+
76
+ #### `TMPL_VAR`
77
+
78
+ <TMPL_VAR name>
79
+
80
+ The `<TMPL_VAR>` tag is very simple.
81
+ When the template is output - for each `<TMPL_VAR>` tag you
82
+ use in the template the `<TMPL_VAR>` is replaced with the value text you specified.
83
+ If you don't set a value it just gets skipped in the output.
84
+
85
+ **Attributes**
86
+
87
+ The following "attributes" can also be specified in template var tags:
88
+
89
+ `ESCAPE`
90
+
91
+ This allows you to escape the value before it's put into the output.
92
+
93
+ This is useful when you want to use a `TMPL_VAR` in a context where those characters would cause trouble. For example:
94
+
95
+ <input name=param type=text value="<TMPL_VAR name>">
96
+
97
+ If you use a value like `sam"my` you'll get in trouble with HTML's
98
+ idea of a double-quote. On the other hand,
99
+ if you use `ESCAPE=HTML`, like this:
100
+
101
+ <input name=param type=text value="<TMPL_VAR name ESCAPE=HTML>">
102
+
103
+ You'll get what you wanted no matter what value happens
104
+ to be passed in.
105
+
106
+ The following escape values are supported:
107
+
108
+ `HTML`
109
+
110
+ Replaces the following characters with their
111
+ HTML entity equivalent: `&`, `"`, `'`, `<`, `>`
112
+
113
+ `NONE`
114
+
115
+ Performs no escaping. This is the default.
116
+
117
+
118
+
119
+
120
+ #### `TMPL_LOOP`
121
+
122
+ <TMPL_LOOP name> ... </TMPL_LOOP>
123
+
124
+ The `<TMPL_LOOP>` tag is a bit more complicated than `<TMPL_VAR>`. The `<TMPL_LOOP>` tag allows you to delimit a section of text and give it a name. Inside this named loop you place `<TMPL_VAR>`s. Now you pass in a list (an array) of values for this loop. The loop iterates over the list and produces output from the text block for each pass. Here's an example:
125
+
126
+ In the template:
127
+
128
+ <TMPL_LOOP employees>
129
+ <p>
130
+ Name: <TMPL_VAR name><br>
131
+ Job: <TMPL_VAR job>
132
+ </p>
133
+ </TMPL_LOOP>
134
+
135
+ In your Ruby code:
136
+
137
+ ``` ruby
138
+ puts template.result( employees: [ { name: 'Sam', job: 'programmer' },
139
+ { name: 'Steve', job: 'soda jerk' }
140
+ ]
141
+ )
142
+ ```
143
+
144
+ The output is:
145
+
146
+ ```
147
+ <p>
148
+ Name: Sam<br>
149
+ Job: programmer
150
+ </p>
151
+
152
+ <p>
153
+ Name: Steve<br>
154
+ Job: soda jerk
155
+ </p>
156
+ ```
157
+
158
+ As you can see above the `<TMPL_LOOP>` takes a list of values
159
+ and then iterates over the loop body producing output.
160
+
161
+
162
+ **Loop Context Variables**
163
+
164
+ Inside a loop extra variables that depend on the loop's context
165
+ are made available. These are:
166
+
167
+ `__FIRST__`
168
+
169
+ Value that is true for the first iteration of the loop
170
+ and false every other time.
171
+
172
+ `__LAST__`
173
+
174
+ Value that is true for the last iteration of the loop and false every other time.
175
+
176
+ `__INNER__`
177
+
178
+ Value that is true for the every iteration of the loop except for the first and last.
179
+
180
+ `__OUTER__`
181
+
182
+ Value that is true for the first and last iterations of the loop.
183
+
184
+ `__ODD__`
185
+
186
+ Value that is true for the every odd iteration of the loop.
187
+
188
+ `__EVEN__`
189
+
190
+ Value that is true for the every even iteration of the loop.
191
+
192
+ `__COUNTER__`
193
+
194
+ An integer (starting from 1) whose value increments for each iteration of the loop.
195
+
196
+ `__INDEX__`
197
+
198
+ An integer (starting from 0) whose value increments
199
+ for each iteration of the loop.
200
+
201
+
202
+ Just like any other `TMPL_VAR`s these variables can be used in `<TMPL_IF>`, `<TMPL_UNLESS>` and `<TMPL_ELSE>` to control how a loop is output.
203
+
204
+ Example:
205
+
206
+ ```
207
+ <TMPL_LOOP foos>
208
+ <TMPL_IF __FIRST__>
209
+ This only outputs on the first pass.
210
+ </TMPL_IF>
211
+
212
+ <TMPL_IF __ODD__>
213
+ This outputs every other pass, on the odd passes.
214
+ </TMPL_IF>
215
+
216
+ <TMPL_UNLESS __ODD__>
217
+ This outputs every other pass, on the even passes.
218
+ </TMPL_UNLESS>
219
+
220
+ <TMPL_IF __INNER__>
221
+ This outputs on passes that are neither first nor last.
222
+ </TMPL_IF>
223
+
224
+ This is pass number <TMPL_VAR __COUNTER__>.
225
+
226
+ <TMPL_IF __LAST__>
227
+ This only outputs on the last pass.
228
+ </TMPL_IF>
229
+ </TMPL_LOOP>
230
+ ```
231
+
232
+ One use of this feature is to provide a "separator" similar in effect to the ruby method `join()`. Example:
233
+
234
+ ```
235
+ <TMPL_LOOP fruits>
236
+ <TMPL_IF __LAST__> and </TMPL_IF>
237
+ <TMPL_VAR name><TMPL_UNLESS __LAST__>, <TMPL_ELSE>.</TMPL_UNLESS>
238
+ </TMPL_LOOP>
239
+ ```
240
+
241
+ Would output something like:
242
+
243
+ Apples, Oranges, Brains, Toes, and Kiwi.
244
+
245
+
246
+ NOTE: A loop with only a single pass will get both `__FIRST__` and `__LAST__`
247
+ set to true, but not `__INNER__`.
248
+
249
+
250
+
251
+ #### `TMPL_IF`
252
+
253
+ <TMPL_IF name> ... </TMPL_IF>
254
+
255
+ The `<TMPL_IF>` tag allows you to include or not include a block of the template based on the value of a given name. If the name is given a value that is true for Ruby - like `true` - then the block is included in the output. If it is not defined, or given a false value - like `false` or `nil` - then it is skipped. The names are specified the same way as with `<TMPL_VAR>`.
256
+
257
+ Example Template:
258
+
259
+ <TMPL_IF bool>
260
+ Some text that is ouptut only if bool is true!
261
+ </TMPL_IF>
262
+
263
+ Now if you call `template.result( bool: true )` then the above block will be included by output.
264
+
265
+ `<TMPL_IF> </TMPL_IF>` blocks can include any valid HTML template construct - `VAR`s and `LOOP`s and other `IF/ELSE` blocks.
266
+
267
+
268
+
269
+ #### `TMPL_ELSE`
270
+
271
+ <TMPL_IF name> ... <TMPL_ELSE> ... </TMPL_IF>
272
+
273
+ You can include an alternate block in your `<TMPL_IF>` block by using `<TMPL_ELSE>`. NOTE: You still end the block with `</TMPL_IF>`, not `</TMPL_ELSE>`!
274
+
275
+ Example:
276
+
277
+ <TMPL_IF bool>
278
+ Some text that is output only if bool is true.
279
+ <TMPL_ELSE>
280
+ Some text that is output only if bool is false.
281
+ </TMPL_IF>
282
+
283
+
284
+ #### `TMPL_UNLESS`
285
+
286
+ <TMPL_UNLESS name> ... </TMPL_UNLESS>
287
+
288
+ This tag is the opposite of `<TMPL_IF>`. The block is output if the name is set false or not defined. You can use `<TMPL_ELSE>` with `<TMPL_UNLESS>` just as you can with `<TMPL_IF>`.
289
+
290
+ Example:
291
+
292
+ <TMPL_UNLESS bool>
293
+ Some text that is output only if bool is false.
294
+ <TMPL_ELSE>
295
+ Some text that is output only if bool is true.
296
+ </TMPL_UNLESS>
297
+
298
+
299
+
300
+
301
+
302
+ ## Back to the Future - Convert HTML Templates to Embedded Ruby (ERB)
303
+
304
+ The HTML library always converts classic
305
+ HTML Template to Embedded Ruby (ERB) style.
306
+ Use the `text` attribute to get the converted template source.
307
+
308
+ Example:
309
+
310
+ ``` ruby
311
+ puts HtmlTemplate.new( <<TXT ).text
312
+ <opml version="1.1">
313
+ <head>
314
+ <title><TMPL_VAR name ESCAPE="HTML"></title>
315
+ <dateModified><TMPL_VAR date_822></dateModified>
316
+ <ownerName><TMPL_VAR owner_name></ownerName>
317
+ <ownerEmail><TMPL_VAR owner_email></ownerEmail>
318
+ </head>
319
+
320
+ <body>
321
+ <TMPL_LOOP Channels>
322
+ <outline type="rss"
323
+ text="<TMPL_VAR name ESCAPE="HTML">"
324
+ xmlUrl="<TMPL_VAR url ESCAPE="HTML">"
325
+ <TMPL_IF channel_link> htmlUrl="<TMPL_VAR channel_link ESCAPE="HTML">"</TMPL_IF> />
326
+ </TMPL_LOOP>
327
+ </body>
328
+ </opml>
329
+ TXT
330
+ ```
331
+
332
+ will print if debugging is turned on:
333
+
334
+ ```
335
+ line 4 - match <TMPL_VAR name ESCAPE="HTML"> replacing with: <%=h name %>
336
+ line 5 - match <TMPL_VAR date_822> replacing with: <%= date_822 %>
337
+ line 6 - match <TMPL_VAR owner_name> replacing with: <%= owner_name %>
338
+ line 7 - match <TMPL_VAR owner_email> replacing with: <%= owner_email %>
339
+ line 11 - match <TMPL_LOOP Channels> replacing with: <% Channels.each_with_loop do |channel, channel_loop| %>
340
+ line 13 - match <TMPL_VAR name ESCAPE="HTML"> replacing with: <%=h channel.name %>
341
+ line 14 - match <TMPL_VAR url ESCAPE="HTML"> replacing with: <%=h channel.url %>
342
+ line 15 - match <TMPL_IF channel_link> replacing with: <% if channel.channel_link %>
343
+ line 15 - match <TMPL_VAR channel_link ESCAPE="HTML"> replacing with: <%=h channel.channel_link %>
344
+ line 15 - match </TMPL_IF> replacing with: <% end %>
345
+ line 16 - match </TMPL_LOOP> replacing with: <% end %>
346
+ ```
347
+
348
+ and result in:
349
+
350
+ ``` erb
351
+ <opml version="1.1">
352
+ <head>
353
+ <title><%=h name %></title>
354
+ <dateModified><%= date_822 %></dateModified>
355
+ <ownerName><%= owner_name %></ownerName>
356
+ </head>
357
+
358
+ <body>
359
+ <% Channels.each_with_loop do |channel, channel_loop| %>
360
+ <outline type="rss"
361
+ text="<%=h channel.name %>"
362
+ xmlUrl="<%=h channel.url %>"
363
+ <% if channel.channel_link %> htmlUrl="<%=h channel.channel_link %>"<% end %> />
364
+ <% end %>
365
+ </body>
366
+ </opml>
367
+ ```
368
+
369
+
15
370
 
16
371
  ## License
17
372
 
@@ -3,8 +3,8 @@
3
3
 
4
4
  class HtmlTemplate
5
5
  MAJOR = 0
6
- MINOR = 0
7
- PATCH = 1
6
+ MINOR = 1
7
+ PATCH = 0
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
10
10
  def self.version
data/lib/html/template.rb CHANGED
@@ -7,6 +7,27 @@ require 'ostruct'
7
7
  require 'fileutils'
8
8
 
9
9
 
10
+
11
+ module Enumerable
12
+ class LoopMeta
13
+ def initialize( total )
14
+ @total = total
15
+ @index = 0
16
+ end
17
+ def index=(value) @index=value; end
18
+ end
19
+
20
+ def each_with_loop( &blk )
21
+ loop_meta = LoopMeta.new( size )
22
+ each_with_index do |item, index|
23
+ loop_meta.index = index
24
+ blk.call( item, loop_meta )
25
+ end
26
+ end
27
+ end
28
+
29
+
30
+
10
31
  # our own code
11
32
  require 'html/template/version' # note: let version always get first
12
33
 
@@ -15,29 +36,54 @@ require 'html/template/version' # note: let version always get first
15
36
  class HtmlTemplate
16
37
 
17
38
  attr_reader :text ## returns converted template text (with "breaking" comments!!!)
18
-
19
- def initialize( text )
20
- @text = convert( text ) ## note: keep a copy of the converted template text
21
- @template = ERB.new( strip_comments( @text ) )
39
+ attr_reader :template ## return "inner" (erb) template object
40
+ attr_reader :errors
41
+
42
+ def initialize( text=nil, filename: nil )
43
+ if text.nil? ## try to read file (by filename)
44
+ text = File.open( filename, 'r:utf-8' ) { |f| f.read }
45
+ end
46
+
47
+ ## 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
+
50
+ if @errors.size > 0
51
+ puts "!! ERROR - #{@errors.size} conversion / syntax error(s):"
52
+ pp @errors
53
+ raise ## todo - find a good Error - StandardError - why? why not?
54
+ end
55
+
56
+ @template = ERB.new( @text, nil, '%<>' )
22
57
  end
23
58
 
24
59
 
25
60
  VAR_RE = %r{<TMPL_(?<tag>VAR)
26
61
  \s
27
- (?<ident>[a-zA-Z_0-9]+)
62
+ (?<ident>[a-zA-Z_][a-zA-Z0-9_]*)
63
+ (\s
64
+ (?<escape>ESCAPE)
65
+ =
66
+ "(?<format>HTML|NONE)"
67
+ )?
28
68
  >}x
29
69
 
30
- IF_OPEN_RE = %r{(?<open><)TMPL_(?<tag>IF)
70
+ IF_OPEN_RE = %r{(?<open><)TMPL_(?<tag>IF|UNLESS)
31
71
  \s
32
- (?<ident>[a-zA-Z_0-9]+)
72
+ (?<ident>[a-zA-Z_][a-zA-Z0-9_]*)
33
73
  >}x
34
74
 
35
- IF_CLOSE_RE = %r{(?<close></)TMPL_(?<tag>IF)
75
+ 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
36
79
  >}x
80
+
81
+ ELSE_RE = %r{<TMPL_(?<tag>ELSE)
82
+ >}x
37
83
 
38
84
  LOOP_OPEN_RE = %r{(?<open><)TMPL_(?<tag>LOOP)
39
85
  \s
40
- (?<ident>[a-zA-Z_0-9]+)
86
+ (?<ident>[a-zA-Z_][a-zA-Z0-9_]*)
41
87
  >}x
42
88
 
43
89
  LOOP_CLOSE_RE = %r{(?<close></)TMPL_(?<tag>LOOP)
@@ -45,27 +91,43 @@ class HtmlTemplate
45
91
 
46
92
  CATCH_OPEN_RE = %r{(?<open><)TMPL_(?<unknown>[^>]+?)
47
93
  >}x
94
+
95
+ CATCH_CLOSE_RE = %r{(?<close></)TMPL_(?<unknown>[^>]+?)
96
+ >}x
48
97
 
49
98
 
50
99
  ALL_RE = Regexp.union( VAR_RE,
51
100
  IF_OPEN_RE,
52
101
  IF_CLOSE_RE,
102
+ ELSE_RE,
53
103
  LOOP_OPEN_RE,
54
104
  LOOP_CLOSE_RE,
55
- CATCH_OPEN_RE )
56
-
57
-
58
- def strip_comments( text )
59
- ## strip/remove comments lines starting with #
60
- buf = String.new('') ## note: '' required for getting source encoding AND not ASCII-8BIT!!!
61
- text.each_line do |line|
62
- next if line.lstrip.start_with?( '#' )
63
- buf << line
64
- end
65
- buf
105
+ CATCH_OPEN_RE,
106
+ CATCH_CLOSE_RE )
107
+
108
+
109
+ def to_recursive_ostruct( o )
110
+ if o.is_a?( Array )
111
+ o.reduce( [] ) do |ary, item|
112
+ ary << to_recursive_ostruct( item )
113
+ ary
114
+ end
115
+ elsif o.is_a?( Hash )
116
+ ## puts 'to_recursive_ostruct (hash):'
117
+ OpenStruct.new( o.reduce( {} ) do |hash, (key, val)|
118
+ ## puts "#{key} => #{val}:#{val.class.name}"
119
+ hash[key] = to_recursive_ostruct( val )
120
+ hash
121
+ end )
122
+ else ## assume regular "primitive" value - pass along as is
123
+ o
124
+ end
66
125
  end
67
126
 
127
+
68
128
  def convert( text )
129
+ errors = [] # note: reset global errros list
130
+
69
131
  stack = []
70
132
 
71
133
  ## note: convert line-by-line
@@ -76,7 +138,7 @@ class HtmlTemplate
76
138
  lineno += 1
77
139
 
78
140
  if line.lstrip.start_with?( '#' ) ## or make it tripple ### - why? why not?
79
- buf << line ## pass along as is for now!!
141
+ buf << "%#{line.lstrip}"
80
142
  elsif line.strip.empty?
81
143
  buf << line
82
144
  else
@@ -90,6 +152,9 @@ class HtmlTemplate
90
152
  ident = m[:ident]
91
153
  unknown = m[:unknown] # catch all for unknown / unmatched tags
92
154
 
155
+ escape = m[:escape]
156
+ format = m[:format]
157
+
93
158
  ## todo/fix: rename ctx to scope or __ - why? why not?
94
159
  ## note: peek; get top stack item
95
160
  ## if top level (stack empty) => nothing
@@ -97,24 +162,40 @@ class HtmlTemplate
97
162
  ctx = stack.empty? ? '' : "#{stack[-1]}."
98
163
 
99
164
  code = if tag == 'VAR'
100
- "<%= #{ctx}#{ident} %>"
165
+ if escape && format == 'HTML'
166
+ ## check or use long form e.g. CGI.escapeHTML - why? why not?
167
+ "<%=h #{ctx}#{ident} %>"
168
+ else
169
+ "<%= #{ctx}#{ident} %>"
170
+ end
101
171
  elsif tag == 'LOOP' && tag_open
102
172
  ## assume plural ident e.g. channels
103
173
  ## cut-off last char, that is, the plural s channels => channel
104
174
  ## note: ALWAYS downcase (auto-generated) loop iterator/pass name
105
175
  it = ident[0..-2].downcase
106
176
  stack.push( it )
107
- "<% #{ctx}#{ident}.each do |#{it}| %>"
177
+ "<% #{ctx}#{ident}.each_with_loop do |#{it}, #{it}_loop| %>"
108
178
  elsif tag == 'LOOP' && tag_close
109
179
  stack.pop
110
180
  "<% end %>"
111
181
  elsif tag == 'IF' && tag_open
112
182
  "<% if #{ctx}#{ident} %>"
113
- elsif tag == 'IF' && tag_close
183
+ elsif tag == 'UNLESS' && tag_open
184
+ "<% unless #{ctx}#{ident} %>"
185
+ elsif (tag == 'IF' || tag == 'UNLESS') && tag_close
114
186
  "<% end %>"
115
- elsif unknown && tag_open
116
- puts "!! ERROR"
117
- "<%# !!error - unknown open tag: #{unknown} %>"
187
+ elsif tag == 'ELSE'
188
+ "<% else %>"
189
+ elsif unknown
190
+ errors << if tag_open
191
+ "line #{lineno} - unknown open tag: #{unknown}"
192
+ else ## assume tag_close
193
+ "line #{lineno} - unknown close tag: #{unknown}"
194
+ end
195
+
196
+ puts "!! ERROR in line #{lineno} - #{errors[-1]}:"
197
+ puts line
198
+ "<%# !!error - #{errors[-1]} %>"
118
199
  else
119
200
  raise ArgumentError ## unknown tag #{tag}
120
201
  end
@@ -125,7 +206,7 @@ class HtmlTemplate
125
206
  end
126
207
  end
127
208
  end # each_line
128
- buf
209
+ [buf, errors]
129
210
  end # method convert
130
211
 
131
212
 
@@ -142,6 +223,17 @@ class HtmlTemplate
142
223
  ## todo: use locals / assigns or something instead of **kwargs - why? why not?
143
224
  ## allow/support (extra) locals / assigns - why? why not?
144
225
  ## note: Ruby >= 2.5 has ERB#result_with_hash - use later - why? why not?
226
+
227
+ kwargs = kwargs.reduce( {} ) do |hash, (key, val)|
228
+ ## puts "#{key} => #{val}:#{val.class.name}"
229
+ hash[key] = to_recursive_ostruct( val )
230
+ hash
231
+ end
232
+
233
+ ## (auto-)convert array and hash values to ostruct
234
+ ## for easy dot (.) access
235
+ ## e.g. student.name instead of student[:name]
236
+
145
237
  @template.result( Context.new( **kwargs ).get_binding )
146
238
  end
147
239
  end
@@ -1,17 +1,19 @@
1
+ %### Planet OPML template.
2
+ %###
1
3
  <?xml version="1.0"?>
2
4
  <opml version="1.1">
3
5
  <head>
4
- <title><%= name %></title>
6
+ <title><%=h name %></title>
5
7
  <dateModified><%= date_822 %></dateModified>
6
8
  <ownerName><%= owner_name %></ownerName>
7
9
  </head>
8
10
 
9
11
  <body>
10
- <% channels.each do |channel| %>
12
+ <% channels.each_with_loop do |channel, channel_loop| %>
11
13
  <outline type="rss"
12
- text="<%= channel.name %>"
13
- xmlUrl="<%= channel.url %>"
14
- <% if channel.channel_link %> htmlUrl="<%= channel.channel_link %>"<% end %> />
14
+ text="<%=h channel.name %>"
15
+ xmlUrl="<%=h channel.url %>"
16
+ <% if channel.channel_link %> htmlUrl="<%=h channel.channel_link %>"<% end %> />
15
17
  <% end %>
16
18
  </body>
17
19
  </opml>
@@ -1,7 +1,9 @@
1
+ ### Planet OPML template.
2
+ ###
1
3
  <?xml version="1.0"?>
2
4
  <opml version="1.1">
3
5
  <head>
4
- <title><TMPL_VAR name></title>
6
+ <title><TMPL_VAR name ESCAPE="HTML"></title>
5
7
  <dateModified><TMPL_VAR date_822></dateModified>
6
8
  <ownerName><TMPL_VAR owner_name></ownerName>
7
9
  </head>
@@ -9,9 +11,9 @@
9
11
  <body>
10
12
  <TMPL_LOOP channels>
11
13
  <outline type="rss"
12
- text="<TMPL_VAR name>"
13
- xmlUrl="<TMPL_VAR url>"
14
- <TMPL_IF channel_link> htmlUrl="<TMPL_VAR channel_link>"</TMPL_IF> />
14
+ text="<TMPL_VAR name ESCAPE="HTML">"
15
+ xmlUrl="<TMPL_VAR url ESCAPE="HTML">"
16
+ <TMPL_IF channel_link> htmlUrl="<TMPL_VAR channel_link ESCAPE="HTML">"</TMPL_IF> />
15
17
  </TMPL_LOOP>
16
18
  </body>
17
19
  </opml>
@@ -1,61 +1,61 @@
1
1
  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
2
2
  <feed xmlns="http://www.w3.org/2005/Atom">
3
3
 
4
- <title><TMPL_VAR name></title>
5
- <link rel="self" href="<TMPL_VAR feed ESCAPE="HTML">"/>
6
- <link href="<TMPL_VAR link ESCAPE="HTML">"/>
7
- <id><TMPL_VAR feed ESCAPE="HTML"></id>
8
- <updated><TMPL_VAR date_iso></updated>
9
- <generator uri="http://www.planetplanet.org/"><TMPL_VAR generator ESCAPE="HTML"></generator>
4
+ <title><TMPL_VAR name></title>
5
+ <link rel="self" href="<TMPL_VAR feed ESCAPE="HTML">"/>
6
+ <link href="<TMPL_VAR link ESCAPE="HTML">"/>
7
+ <id><TMPL_VAR feed ESCAPE="HTML"></id>
8
+ <updated><TMPL_VAR date_iso></updated>
9
+ <generator uri="http://www.planetplanet.org/"><TMPL_VAR generator ESCAPE="HTML"></generator>
10
10
 
11
11
  <TMPL_LOOP Items>
12
- <entry<TMPL_IF channel_language> xml:lang="<TMPL_VAR channel_language>"</TMPL_IF>>
13
- <title type="html"<TMPL_IF title_language> xml:lang="<TMPL_VAR title_language>"</TMPL_IF>><TMPL_VAR title ESCAPE="HTML"></title>
14
- <link href="<TMPL_VAR link ESCAPE="HTML">"/>
15
- <id><TMPL_VAR id ESCAPE="HTML"></id>
16
- <updated><TMPL_VAR date_iso></updated>
17
- <content type="html"<TMPL_IF content_language> xml:lang="<TMPL_VAR content_language>"</TMPL_IF>><TMPL_VAR content ESCAPE="HTML"></content>
18
- <author>
12
+ <entry<TMPL_IF channel_language> xml:lang="<TMPL_VAR channel_language>"</TMPL_IF>>
13
+ <title type="html"<TMPL_IF title_language> xml:lang="<TMPL_VAR title_language>"</TMPL_IF>><TMPL_VAR title ESCAPE="HTML"></title>
14
+ <link href="<TMPL_VAR link ESCAPE="HTML">"/>
15
+ <id><TMPL_VAR id ESCAPE="HTML"></id>
16
+ <updated><TMPL_VAR date_iso></updated>
17
+ <content type="html"<TMPL_IF content_language> xml:lang="<TMPL_VAR content_language>"</TMPL_IF>><TMPL_VAR content ESCAPE="HTML"></content>
18
+ <author>
19
19
  <TMPL_IF author_name>
20
- <name><TMPL_VAR author_name ESCAPE="HTML"></name>
20
+ <name><TMPL_VAR author_name ESCAPE="HTML"></name>
21
21
  <TMPL_IF author_email>
22
- <email><TMPL_VAR author_email ESCAPE="HTML"></email>
22
+ <email><TMPL_VAR author_email ESCAPE="HTML"></email>
23
23
  </TMPL_IF author_email>
24
24
  <TMPL_ELSE>
25
25
  <TMPL_IF channel_author_name>
26
- <name><TMPL_VAR channel_author_name ESCAPE="HTML"></name>
26
+ <name><TMPL_VAR channel_author_name ESCAPE="HTML"></name>
27
27
  <TMPL_IF channel_author_email>
28
- <email><TMPL_VAR channel_author_email ESCAPE="HTML"></email>
28
+ <email><TMPL_VAR channel_author_email ESCAPE="HTML"></email>
29
29
  </TMPL_IF channel_author_email>
30
30
  <TMPL_ELSE>
31
- <name><TMPL_VAR channel_name ESCAPE="HTML"></name>
31
+ <name><TMPL_VAR channel_name ESCAPE="HTML"></name>
32
32
  </TMPL_IF>
33
33
  </TMPL_IF>
34
- <uri><TMPL_VAR channel_link ESCAPE="HTML"></uri>
35
- </author>
36
- <source>
34
+ <uri><TMPL_VAR channel_link ESCAPE="HTML"></uri>
35
+ </author>
36
+ <source>
37
37
  <TMPL_IF channel_title>
38
- <title type="html"><TMPL_VAR channel_title ESCAPE="HTML"></title>
38
+ <title type="html"><TMPL_VAR channel_title ESCAPE="HTML"></title>
39
39
  <TMPL_ELSE>
40
- <title type="html"><TMPL_VAR channel_name ESCAPE="HTML"></title>
40
+ <title type="html"><TMPL_VAR channel_name ESCAPE="HTML"></title>
41
41
  </TMPL_IF>
42
42
  <TMPL_IF channel_subtitle>
43
- <subtitle type="html"><TMPL_VAR channel_subtitle ESCAPE="HTML"></subtitle>
43
+ <subtitle type="html"><TMPL_VAR channel_subtitle ESCAPE="HTML"></subtitle>
44
44
  </TMPL_IF>
45
- <link rel="self" href="<TMPL_VAR channel_url ESCAPE="HTML">"/>
45
+ <link rel="self" href="<TMPL_VAR channel_url ESCAPE="HTML">"/>
46
46
  <TMPL_IF channel_id>
47
- <id><TMPL_VAR channel_id ESCAPE="HTML"></id>
47
+ <id><TMPL_VAR channel_id ESCAPE="HTML"></id>
48
48
  <TMPL_ELSE>
49
- <id><TMPL_VAR channel_url ESCAPE="HTML"></id>
49
+ <id><TMPL_VAR channel_url ESCAPE="HTML"></id>
50
50
  </TMPL_IF>
51
51
  <TMPL_IF channel_updated_iso>
52
- <updated><TMPL_VAR channel_updated_iso></updated>
52
+ <updated><TMPL_VAR channel_updated_iso></updated>
53
53
  </TMPL_IF>
54
54
  <TMPL_IF channel_rights>
55
- <rights type="html"><TMPL_VAR channel_rights ESCAPE="HTML"></rights>
55
+ <rights type="html"><TMPL_VAR channel_rights ESCAPE="HTML"></rights>
56
56
  </TMPL_IF>
57
- </source>
58
- </entry>
57
+ </source>
58
+ </entry>
59
59
 
60
60
  </TMPL_LOOP>
61
61
  </feed>
@@ -1,30 +1,30 @@
1
1
  <?xml version="1.0"?>
2
2
  <rdf:RDF
3
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
4
- xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
5
- xmlns:foaf="http://xmlns.com/foaf/0.1/"
6
- xmlns:rss="http://purl.org/rss/1.0/"
7
- xmlns:dc="http://purl.org/dc/elements/1.1/"
3
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
4
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
5
+ xmlns:foaf="http://xmlns.com/foaf/0.1/"
6
+ xmlns:rss="http://purl.org/rss/1.0/"
7
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
8
8
  >
9
9
  <foaf:Group>
10
- <foaf:name><TMPL_VAR name ESCAPE="HTML"></foaf:name>
11
- <foaf:homepage><TMPL_VAR link ESCAPE="HTML"></foaf:homepage>
12
- <rdfs:seeAlso rdf:resource="<TMPL_VAR url ESCAPE="HTML">" />
10
+ <foaf:name><TMPL_VAR name ESCAPE="HTML"></foaf:name>
11
+ <foaf:homepage><TMPL_VAR link ESCAPE="HTML"></foaf:homepage>
12
+ <rdfs:seeAlso rdf:resource="<TMPL_VAR url ESCAPE="HTML">" />
13
13
 
14
14
  <TMPL_LOOP Channels>
15
- <foaf:member>
16
- <foaf:Agent>
17
- <foaf:name><TMPL_VAR name ESCAPE="HTML"></foaf:name>
18
- <foaf:weblog>
19
- <foaf:Document rdf:about="<TMPL_VAR link ESCAPE="HTML">">
20
- <dc:title><TMPL_VAR title_plain ESCAPE="HTML"></dc:title>
21
- <rdfs:seeAlso>
22
- <rss:channel rdf:about="<TMPL_VAR url ESCAPE="HTML">" />
23
- </rdfs:seeAlso>
24
- </foaf:Document>
25
- </foaf:weblog>
26
- </foaf:Agent>
27
- </foaf:member>
15
+ <foaf:member>
16
+ <foaf:Agent>
17
+ <foaf:name><TMPL_VAR name ESCAPE="HTML"></foaf:name>
18
+ <foaf:weblog>
19
+ <foaf:Document rdf:about="<TMPL_VAR link ESCAPE="HTML">">
20
+ <dc:title><TMPL_VAR title_plain ESCAPE="HTML"></dc:title>
21
+ <rdfs:seeAlso>
22
+ <rss:channel rdf:about="<TMPL_VAR url ESCAPE="HTML">" />
23
+ </rdfs:seeAlso>
24
+ </foaf:Document>
25
+ </foaf:weblog>
26
+ </foaf:Agent>
27
+ </foaf:member>
28
28
  </TMPL_LOOP>
29
29
 
30
30
  </foaf:Group>
@@ -1,15 +1,15 @@
1
1
  <?xml version="1.0"?>
2
2
  <opml version="1.1">
3
- <head>
4
- <title><TMPL_VAR name ESCAPE="HTML"></title>
5
- <dateModified><TMPL_VAR date_822></dateModified>
6
- <ownerName><TMPL_VAR owner_name></ownerName>
7
- <ownerEmail><TMPL_VAR owner_email></ownerEmail>
8
- </head>
9
-
10
- <body>
11
- <TMPL_LOOP Channels>
12
- <outline type="rss" text="<TMPL_VAR name ESCAPE="HTML">" xmlUrl="<TMPL_VAR url ESCAPE="HTML">" title="<TMPL_IF title><TMPL_VAR title ESCAPE="HTML"></TMPL_IF><TMPL_UNLESS title><TMPL_VAR name ESCAPE="HTML"></TMPL_UNLESS>"<TMPL_IF channel_link> htmlUrl="<TMPL_VAR channel_link ESCAPE="HTML">"</TMPL_IF> />
13
- </TMPL_LOOP>
14
- </body>
3
+ <head>
4
+ <title><TMPL_VAR name ESCAPE="HTML"></title>
5
+ <dateModified><TMPL_VAR date_822></dateModified>
6
+ <ownerName><TMPL_VAR owner_name></ownerName>
7
+ <ownerEmail><TMPL_VAR owner_email></ownerEmail>
8
+ </head>
9
+
10
+ <body>
11
+ <TMPL_LOOP Channels>
12
+ <outline type="rss" text="<TMPL_VAR name ESCAPE="HTML">" xmlUrl="<TMPL_VAR url ESCAPE="HTML">" title="<TMPL_IF title><TMPL_VAR title ESCAPE="HTML"></TMPL_IF><TMPL_UNLESS title><TMPL_VAR name ESCAPE="HTML"></TMPL_UNLESS>"<TMPL_IF channel_link> htmlUrl="<TMPL_VAR channel_link ESCAPE="HTML">"</TMPL_IF> />
13
+ </TMPL_LOOP>
14
+ </body>
15
15
  </opml>
@@ -1,36 +1,36 @@
1
1
  <?xml version="1.0"?>
2
2
  <rdf:RDF
3
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
4
- xmlns:dc="http://purl.org/dc/elements/1.1/"
5
- xmlns:foaf="http://xmlns.com/foaf/0.1/"
6
- xmlns:content="http://purl.org/rss/1.0/modules/content/"
7
- xmlns="http://purl.org/rss/1.0/"
3
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
4
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+ xmlns:foaf="http://xmlns.com/foaf/0.1/"
6
+ xmlns:content="http://purl.org/rss/1.0/modules/content/"
7
+ xmlns="http://purl.org/rss/1.0/"
8
8
  >
9
9
  <channel rdf:about="<TMPL_VAR link ESCAPE="HTML">">
10
- <title><TMPL_VAR name ESCAPE="HTML"></title>
11
- <link><TMPL_VAR link ESCAPE="HTML"></link>
12
- <description><TMPL_VAR name ESCAPE="HTML"> - <TMPL_VAR link ESCAPE="HTML"></description>
10
+ <title><TMPL_VAR name ESCAPE="HTML"></title>
11
+ <link><TMPL_VAR link ESCAPE="HTML"></link>
12
+ <description><TMPL_VAR name ESCAPE="HTML"> - <TMPL_VAR link ESCAPE="HTML"></description>
13
13
 
14
- <items>
15
- <rdf:Seq>
14
+ <items>
15
+ <rdf:Seq>
16
16
  <TMPL_LOOP Items>
17
- <rdf:li rdf:resource="<TMPL_VAR id ESCAPE="HTML">" />
17
+ <rdf:li rdf:resource="<TMPL_VAR id ESCAPE="HTML">" />
18
18
  </TMPL_LOOP>
19
- </rdf:Seq>
20
- </items>
19
+ </rdf:Seq>
20
+ </items>
21
21
  </channel>
22
22
 
23
23
  <TMPL_LOOP Items>
24
24
  <item rdf:about="<TMPL_VAR id ESCAPE="HTML">">
25
- <title><TMPL_VAR channel_name ESCAPE="HTML"><TMPL_IF title>: <TMPL_VAR title_plain ESCAPE="HTML"></TMPL_IF></title>
26
- <link><TMPL_VAR link ESCAPE="HTML"></link>
27
- <TMPL_IF content>
28
- <content:encoded><TMPL_VAR content ESCAPE="HTML"></content:encoded>
29
- </TMPL_IF>
30
- <dc:date><TMPL_VAR date_iso></dc:date>
31
- <TMPL_IF author_name>
32
- <dc:creator><TMPL_VAR author_name></dc:creator>
33
- </TMPL_IF>
25
+ <title><TMPL_VAR channel_name ESCAPE="HTML"><TMPL_IF title>: <TMPL_VAR title_plain ESCAPE="HTML"></TMPL_IF></title>
26
+ <link><TMPL_VAR link ESCAPE="HTML"></link>
27
+ <TMPL_IF content>
28
+ <content:encoded><TMPL_VAR content ESCAPE="HTML"></content:encoded>
29
+ </TMPL_IF>
30
+ <dc:date><TMPL_VAR date_iso></dc:date>
31
+ <TMPL_IF author_name>
32
+ <dc:creator><TMPL_VAR author_name></dc:creator>
33
+ </TMPL_IF>
34
34
  </item>
35
35
  </TMPL_LOOP>
36
36
 
@@ -2,27 +2,27 @@
2
2
  <rss version="2.0">
3
3
 
4
4
  <channel>
5
- <title><TMPL_VAR name></title>
6
- <link><TMPL_VAR link ESCAPE="HTML"></link>
7
- <language>en</language>
8
- <description><TMPL_VAR name ESCAPE="HTML"> - <TMPL_VAR link ESCAPE="HTML"></description>
5
+ <title><TMPL_VAR name></title>
6
+ <link><TMPL_VAR link ESCAPE="HTML"></link>
7
+ <language>en</language>
8
+ <description><TMPL_VAR name ESCAPE="HTML"> - <TMPL_VAR link ESCAPE="HTML"></description>
9
9
 
10
10
  <TMPL_LOOP Items>
11
11
  <item>
12
- <title><TMPL_VAR channel_name ESCAPE="HTML"><TMPL_IF title>: <TMPL_VAR title_plain ESCAPE="HTML"></TMPL_IF></title>
13
- <guid><TMPL_VAR id ESCAPE="HTML"></guid>
14
- <link><TMPL_VAR link ESCAPE="HTML"></link>
15
- <TMPL_IF content>
16
- <description><TMPL_VAR content ESCAPE="HTML"></description>
17
- </TMPL_IF>
18
- <pubDate><TMPL_VAR date_822></pubDate>
19
- <TMPL_IF author_email>
20
- <TMPL_IF author_name>
21
- <author><TMPL_VAR author_email> (<TMPL_VAR author_name>)</author>
22
- <TMPL_ELSE>
23
- <author><TMPL_VAR author_email></author>
24
- </TMPL_IF>
25
- </TMPL_IF>
12
+ <title><TMPL_VAR channel_name ESCAPE="HTML"><TMPL_IF title>: <TMPL_VAR title_plain ESCAPE="HTML"></TMPL_IF></title>
13
+ <guid><TMPL_VAR id ESCAPE="HTML"></guid>
14
+ <link><TMPL_VAR link ESCAPE="HTML"></link>
15
+ <TMPL_IF content>
16
+ <description><TMPL_VAR content ESCAPE="HTML"></description>
17
+ </TMPL_IF>
18
+ <pubDate><TMPL_VAR date_822></pubDate>
19
+ <TMPL_IF author_email>
20
+ <TMPL_IF author_name>
21
+ <author><TMPL_VAR author_email> (<TMPL_VAR author_name>)</author>
22
+ <TMPL_ELSE>
23
+ <author><TMPL_VAR author_email></author>
24
+ </TMPL_IF>
25
+ </TMPL_IF>
26
26
  </item>
27
27
  </TMPL_LOOP>
28
28
 
data/test/test_merge.rb CHANGED
@@ -77,8 +77,6 @@ $template->param(
77
77
  print $template->output;
78
78
  =end
79
79
 
80
- Student = Struct.new( :name, :gpa )
81
-
82
80
  def test_students_example
83
81
 
84
82
  tmpl =<<TXT
@@ -93,8 +91,8 @@ TXT
93
91
  t = HtmlTemplate.new( tmpl )
94
92
  puts t.text
95
93
  puts "---"
96
- puts t.render( students: [ Student.new( 'Bluto Blutarsky', 0.0 ),
97
- Student.new( 'Tracey Flick', 4.0 ) ])
94
+ puts t.render( students: [ { name: 'Bluto Blutarsky', gpa: 0.0 },
95
+ { name: 'Tracey Flick', gpa: 4.0 } ])
98
96
 
99
97
 
100
98
  assert true # assume it's alright if we get here
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.0.1
4
+ version: 0.1.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-09 00:00:00.000000000 Z
11
+ date: 2020-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc