mustache 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/README.md +2 -0
  2. data/Rakefile +9 -8
  3. data/bin/mustache +36 -10
  4. data/lib/mustache.rb +26 -0
  5. data/lib/mustache/context.rb +23 -0
  6. data/lib/mustache/template.rb +6 -5
  7. data/lib/mustache/version.rb +1 -1
  8. data/man/mustache.1 +18 -27
  9. data/man/mustache.1.html +3 -1
  10. data/man/mustache.5 +38 -52
  11. data/man/mustache.5.html +9 -4
  12. data/man/mustache.5.ron +3 -0
  13. data/test/autoloading_test.rb +1 -1
  14. data/test/fixtures/comments.mustache +1 -0
  15. data/test/fixtures/comments.rb +14 -0
  16. data/test/fixtures/complex_view.mustache +16 -0
  17. data/test/fixtures/complex_view.rb +34 -0
  18. data/test/fixtures/crazy_recursive.mustache +9 -0
  19. data/test/fixtures/crazy_recursive.rb +31 -0
  20. data/test/fixtures/delimiters.mustache +6 -0
  21. data/test/fixtures/delimiters.rb +22 -0
  22. data/test/fixtures/double_section.mustache +7 -0
  23. data/test/fixtures/double_section.rb +14 -0
  24. data/test/fixtures/escaped.mustache +1 -0
  25. data/test/fixtures/escaped.rb +14 -0
  26. data/test/fixtures/inner_partial.mustache +1 -0
  27. data/test/fixtures/inner_partial.txt +1 -0
  28. data/test/fixtures/namespaced.mustache +1 -0
  29. data/test/fixtures/namespaced.rb +25 -0
  30. data/test/fixtures/nested_objects.mustache +16 -0
  31. data/test/fixtures/nested_objects.rb +35 -0
  32. data/test/fixtures/node.mustache +8 -0
  33. data/test/fixtures/partial_with_module.mustache +3 -0
  34. data/test/fixtures/partial_with_module.rb +37 -0
  35. data/test/fixtures/passenger.conf +5 -0
  36. data/test/fixtures/passenger.rb +27 -0
  37. data/test/fixtures/recursive.mustache +4 -0
  38. data/test/fixtures/recursive.rb +14 -0
  39. data/test/fixtures/simple.mustache +5 -0
  40. data/test/fixtures/simple.rb +26 -0
  41. data/test/fixtures/template_partial.mustache +2 -0
  42. data/test/fixtures/template_partial.rb +18 -0
  43. data/test/fixtures/template_partial.txt +4 -0
  44. data/test/fixtures/unescaped.mustache +1 -0
  45. data/test/fixtures/unescaped.rb +14 -0
  46. data/test/helper.rb +5 -12
  47. data/test/mustache_test.rb +14 -2
  48. data/test/partial_test.rb +50 -3
  49. metadata +33 -1
data/README.md CHANGED
@@ -358,6 +358,8 @@ Meta
358
358
  * Test: <http://runcoderun.com/defunkt/mustache>
359
359
  * Gems: <http://rubygems.org/gems/mustache>
360
360
 
361
+ You can also find us in `#{` on irc.freenode.net.
362
+
361
363
  [1]: http://code.google.com/p/google-ctemplate/
362
364
  [2]: http://www.ivan.fomichev.name/2008/05/erlang-template-engine-prototype.html
363
365
  [3]: http://google-ctemplate.googlecode.com/svn/trunk/doc/howto.html
data/Rakefile CHANGED
@@ -14,7 +14,8 @@ task :default => :test
14
14
  if command? :turn
15
15
  desc "Run tests"
16
16
  task :test do
17
- exec "turn"
17
+ suffix = "-n #{ENV['TEST']}" if ENV['TEST']
18
+ sh "turn test/*.rb #{suffix}"
18
19
  end
19
20
  else
20
21
  Rake::TestTask.new do |t|
@@ -28,7 +29,7 @@ end
28
29
  # Ron
29
30
  #
30
31
 
31
- if command? :ron
32
+ if command? :ronn
32
33
  desc "Show the manual"
33
34
  task :man => "man:build" do
34
35
  exec "man man/mustache.1"
@@ -36,7 +37,7 @@ if command? :ron
36
37
 
37
38
  desc "Build the manual"
38
39
  task "man:build" do
39
- sh "ron -br5 --organization=DEFUNKT --manual='Mustache Manual' man/*.ron"
40
+ sh "ronn -br5 --organization=DEFUNKT --manual='Mustache Manual' man/*.ron"
40
41
  end
41
42
  end
42
43
 
@@ -64,11 +65,11 @@ begin
64
65
 
65
66
  desc "Push a new version to Gemcutter and publish docs."
66
67
  task :publish => :gemcutter do
68
+ require File.dirname(__FILE__) + '/lib/mustache/version'
69
+
67
70
  system "git tag v#{Mustache::Version}"
68
- system "git push origin v#{Mustache::Version}"
69
- system "git push origin master"
70
- system "gem push pkg/mustache-#{Mustache::Version}.gem"
71
- system "git clean -fd"
71
+ sh "git push origin master --tags"
72
+ sh "git clean -fd"
72
73
  exec "rake pages"
73
74
  end
74
75
  rescue LoadError
@@ -87,7 +88,7 @@ end
87
88
  # end
88
89
 
89
90
  desc "Publish to GitHub Pages"
90
- task :pages => [ "build:man" ] do
91
+ task :pages => [ "man:build" ] do
91
92
  Dir['man/*.html'].each do |f|
92
93
  cp f, File.basename(f).sub('.html', '.newhtml')
93
94
  end
@@ -3,9 +3,41 @@
3
3
  require 'mustache'
4
4
  require 'yaml'
5
5
 
6
- if !$stdin.tty?
7
- doc = $stdin.read
8
- if doc =~ /^(\s*---(.*)---\s*)/m
6
+ def help
7
+ puts <<-usage
8
+ Usage:
9
+ $ cat data.yml template.mustache | mustache
10
+ $ mustache data.yml template.mustache
11
+ $ cat <<data | druby mustache - template.mustache
12
+ ---
13
+ name: Bob
14
+ age: 30
15
+ ---
16
+ data
17
+
18
+ See compiled Ruby string:
19
+ $ mustache -c FILE
20
+
21
+ Help:
22
+ $ mustache -h
23
+
24
+ See mustache(1) or http://defunkt.github.com/mustache/mustache.1.html
25
+ for an overview.
26
+ usage
27
+ end
28
+
29
+ if (ARGV.delete('-c') || ARGV.delete('--compile')) && (file = ARGV[0])
30
+ puts Mustache.compile(File.read(file))
31
+
32
+ elsif ($stdin.tty? && ARGV.empty?) || ARGV.delete('-h') || ARGV.delete('--help')
33
+ help
34
+
35
+ else
36
+ # Not at a terminal, read from STDIN and print rendered templates to
37
+ # STDOUT.
38
+ doc = ARGF.read
39
+
40
+ if doc =~ /^(\s*---(.+)---\s*)/m
9
41
  yaml = $2.strip
10
42
  template = doc.sub($1, '')
11
43
 
@@ -15,11 +47,5 @@ if !$stdin.tty?
15
47
  else
16
48
  puts doc
17
49
  end
18
- else
19
- puts <<-usage
20
- Usage: cat data.yml template.mustache | mustache
21
-
22
- See mustache(1) or http://defunkt.github.com/mustache/mustache.1.html
23
- for an overview.
24
- usage
50
+ exit
25
51
  end
@@ -85,6 +85,21 @@ class Mustache
85
85
  render(*args)
86
86
  end
87
87
 
88
+ # Compiles a string template and returns it as a string for use as
89
+ # an interpolated Ruby string (not fully rendered HTML), e.g.
90
+ # >> Mustache.compile("Hi, {{person}}!")
91
+ # => "Hi, #{CGI.escapeHTML(ctx[:person].to_s)}!"
92
+ def self.compile(template)
93
+ templateify(template).to_s
94
+ end
95
+
96
+ # Given a file name and an optional context, attempts to load and
97
+ # render the file as a template.
98
+ def self.render_file(name, context = {})
99
+ data = File.read("#{template_path}/#{name}.#{template_extension}")
100
+ render(data, context)
101
+ end
102
+
88
103
  # The template path informs your Mustache subclass where to look for its
89
104
  # corresponding template. By default it's the current directory (".")
90
105
  def self.template_path
@@ -116,6 +131,17 @@ class Mustache
116
131
  @template = nil
117
132
  end
118
133
 
134
+ # The template name is the Mustache template file without any
135
+ # extension or other information. Defaults to `class_name`.
136
+ def self.template_name
137
+ @template_name || underscore
138
+ end
139
+
140
+ def self.template_name=(template_name)
141
+ @template_name = template_name
142
+ @template = nil
143
+ end
144
+
119
145
  # The template file is the absolute path of the file Mustache will
120
146
  # use as its template. By default it's ./class_name.mustache
121
147
  def self.template_file
@@ -18,6 +18,21 @@ class Mustache
18
18
  @stack = [@mustache]
19
19
  end
20
20
 
21
+ # A {{>partial}} tag translates into a call to the context's
22
+ # `partial` method, which would be this sucker right here.
23
+ #
24
+ # If the Mustache view handling the rendering (e.g. the view
25
+ # representing your profile page or some other template) responds
26
+ # to `partial`, we call it and use the result. Otherwise we render
27
+ # and compile the partial as its own view and return the result.
28
+ def partial(name)
29
+ if @mustache.respond_to? :partial
30
+ @mustache.partial(name)
31
+ else
32
+ @mustache.class.render_file(name, self)
33
+ end
34
+ end
35
+
21
36
  # Adds a new object to the context's internal stack.
22
37
  #
23
38
  # Returns the Context.
@@ -48,6 +63,14 @@ class Mustache
48
63
  fetch(name, nil)
49
64
  end
50
65
 
66
+ # Do we know about a particular key? In other words, will calling
67
+ # `context[key]` give us a result that was set. Basically.
68
+ def has_key?(key)
69
+ !!fetch(key)
70
+ rescue ContextMiss
71
+ false
72
+ end
73
+
51
74
  # Similar to Hash#fetch, finds a value by `name` in the context's
52
75
  # stack. You may specify the default return value by passing a
53
76
  # second parameter.
@@ -59,6 +59,7 @@ class Mustache
59
59
  def compile(src = @source)
60
60
  "\"#{compile_sections(src)}\""
61
61
  end
62
+ alias_method :to_s, :compile
62
63
 
63
64
  # {{#sections}}okay{{/sections}}
64
65
  #
@@ -73,7 +74,6 @@ class Mustache
73
74
  res << compile_tags($`)
74
75
  name = $1.strip.to_sym.inspect
75
76
  code = compile($2)
76
- ctxtmp = "ctx#{tmpid}"
77
77
  res << ev(<<-compiled)
78
78
  if v = ctx[#{name}]
79
79
  v = [v] unless v.is_a?(Array) # shortcut when passed non-array
@@ -94,7 +94,7 @@ class Mustache
94
94
  # 4. Partial tags - {{> partial_name }}
95
95
  def compile_tags(src)
96
96
  res = ""
97
- while src =~ /#{otag}(#|=|!|<|>|\{)?(.+?)\1?#{ctag}+/m
97
+ while src =~ /#{otag}(#|=|!|<|>|&|\{)?(.+?)\1?#{ctag}+/m
98
98
  res << str($`)
99
99
  case $1
100
100
  when '#'
@@ -107,7 +107,7 @@ class Mustache
107
107
  self.otag, self.ctag = $2.strip.split(' ', 2)
108
108
  when '>', '<'
109
109
  res << compile_partial($2.strip)
110
- when '{'
110
+ when '{', '&'
111
111
  res << utag($2.strip)
112
112
  else
113
113
  res << etag($2.strip)
@@ -119,8 +119,8 @@ class Mustache
119
119
 
120
120
  # Partials are basically a way to render views from inside other views.
121
121
  def compile_partial(name)
122
- src = File.read("#{@template_path}/#{name}.#{@template_extension}")
123
- compile(src)[1..-2]
122
+ name = name.to_s.to_sym.inspect
123
+ ev("ctx.partial(#{name})")
124
124
  end
125
125
 
126
126
  # Generate a temporary id, used when compiling code.
@@ -157,6 +157,7 @@ class Mustache
157
157
  end
158
158
 
159
159
  # {{{}}} - an unescaped tag
160
+ # Aliased as & - {{&name}}
160
161
  def utag(s)
161
162
  ev("ctx[#{s.strip.to_sym.inspect}]")
162
163
  end
@@ -1,3 +1,3 @@
1
1
  class Mustache
2
- Version = '0.6.0'
2
+ Version = '0.7.0'
3
3
  end
@@ -1,5 +1,5 @@
1
- .\" generated with Ron/v0.3
2
- .\" http://github.com/rtomayko/ron/
1
+ .\" generated with Ronn/v0.4.1
2
+ .\" http://github.com/rtomayko/ronn/
3
3
  .
4
4
  .TH "MUSTACHE" "1" "March 2010" "DEFUNKT" "Mustache Manual"
5
5
  .
@@ -19,16 +19,15 @@ frontmatter from standard input and prints one or more documents to
19
19
  standard output.
20
20
  .
21
21
  .P
22
- YAML frontmatter beings with \fB---\fR on a single line, followed by YAML,
23
- ending with another \fB---\fR on a single line, e.g.
22
+ YAML frontmatter beings with \fB\-\-\-\fR on a single line, followed by YAML,
23
+ ending with another \fB\-\-\-\fR on a single line, e.g.
24
24
  .
25
25
  .IP "" 4
26
26
  .
27
27
  .nf
28
-
29
- \fB---
28
+ \-\-\-
30
29
  names: [ {name: chris}, {name: mark}, {name: scott} ]
31
- --- \fR
30
+ \-\-\-
32
31
  .
33
32
  .fi
34
33
  .
@@ -48,10 +47,9 @@ For example:
48
47
  .IP "" 4
49
48
  .
50
49
  .nf
51
-
52
- \fB{{#names}}
50
+ {{#names}}
53
51
  Hi {{name}}!
54
- {{/names}} \fR
52
+ {{/names}}
55
53
  .
56
54
  .fi
57
55
  .
@@ -63,28 +61,25 @@ Now let's combine them.
63
61
  .IP "" 4
64
62
  .
65
63
  .nf
66
-
67
- \fB$ cat data.yml
68
- ---
64
+ $ cat data.yml
65
+ \-\-\-
69
66
  names: [ {name: chris}, {name: mark}, {name: scott} ]
70
- ---
67
+ \-\-\-
71
68
  $ cat template.mustache
72
69
  {{#names}}
73
70
  Hi {{name}}!
74
71
  {{/names}}
75
-
76
72
  $ cat data.yml template.mustache | mustache
77
73
  Hi chris!
78
74
  Hi mark!
79
75
  Hi scott!
80
- \fR
81
76
  .
82
77
  .fi
83
78
  .
84
79
  .IP "" 0
85
80
  .
86
81
  .P
87
- If you provide multiple YAML documents (as delimited by \fB---\fR), your
82
+ If you provide multiple YAML documents (as delimited by \fB\-\-\-\fR), your
88
83
  template will be rendered multiple times. Like a mail merge.
89
84
  .
90
85
  .P
@@ -93,23 +88,20 @@ For example:
93
88
  .IP "" 4
94
89
  .
95
90
  .nf
96
-
97
- \fB$ cat data.yml
98
- ---
91
+ $ cat data.yml
92
+ \-\-\-
99
93
  name: chris
100
- ---
94
+ \-\-\-
101
95
  name: mark
102
- ---
96
+ \-\-\-
103
97
  name: scott
104
- ---
98
+ \-\-\-
105
99
  $ cat template.mustache
106
100
  Hi {{name}}!
107
-
108
101
  $ cat data.yml template.mustache | mustache
109
102
  Hi chris!
110
103
  Hi mark!
111
104
  Hi scott!
112
- \fR
113
105
  .
114
106
  .fi
115
107
  .
@@ -121,8 +113,7 @@ If you have RubyGems installed:
121
113
  .IP "" 4
122
114
  .
123
115
  .nf
124
-
125
- \fBgem install mustache \fR
116
+ gem install mustache
126
117
  .
127
118
  .fi
128
119
  .
@@ -2,7 +2,7 @@
2
2
  <html>
3
3
  <head>
4
4
  <meta http-equiv='content-type' value='text/html;charset=utf8'>
5
- <meta name='generator' value='Ron/v0.3'>
5
+ <meta name='generator' value='Ronn/v0.4.1'>
6
6
  <title>mustache(1) -- Mustache processor</title>
7
7
  <style type='text/css'>
8
8
  body {margin:0}
@@ -64,6 +64,7 @@
64
64
 
65
65
  <h2 id='NAME'>NAME</h2>
66
66
  <p><code>mustache</code> -- Mustache processor</p>
67
+
67
68
  <h2>SYNOPSIS</h2>
68
69
 
69
70
  <p><code>cat data.yml template.mustache | mustache</code></p>
@@ -157,6 +158,7 @@ Hi scott!
157
158
  <p>mustache(5), mustache(7), gem(1),
158
159
  <a href="http://defunkt.github.com/mustache/">http://defunkt.github.com/mustache/</a></p>
159
160
 
161
+
160
162
  <ol class='foot man'>
161
163
  <li class='tl'>DEFUNKT</li>
162
164
  <li class='tc'>March 2010</li>
@@ -1,5 +1,5 @@
1
- .\" generated with Ron/v0.3
2
- .\" http://github.com/rtomayko/ron/
1
+ .\" generated with Ronn/v0.4.1
2
+ .\" http://github.com/rtomayko/ronn/
3
3
  .
4
4
  .TH "MUSTACHE" "5" "March 2010" "DEFUNKT" "Mustache Manual"
5
5
  .
@@ -12,12 +12,11 @@ A typical Mustache template:
12
12
  .IP "" 4
13
13
  .
14
14
  .nf
15
-
16
- \fBHello {{name}}
15
+ Hello {{name}}
17
16
  You have just won ${{value}}!
18
17
  {{#in_ca}}
19
18
  Well, ${{taxed_value}}, after taxes.
20
- {{/in_ca}} \fR
19
+ {{/in_ca}}
21
20
  .
22
21
  .fi
23
22
  .
@@ -29,13 +28,12 @@ Given the following hash:
29
28
  .IP "" 4
30
29
  .
31
30
  .nf
32
-
33
- \fB{
31
+ {
34
32
  "name": "Chris",
35
33
  "value": 10000,
36
- "taxed_value": 10000 - (10000 * 0.4),
34
+ "taxed_value": 10000 \- (10000 * 0.4),
37
35
  "in_ca": true
38
- } \fR
36
+ }
39
37
  .
40
38
  .fi
41
39
  .
@@ -47,10 +45,9 @@ Will produce the following:
47
45
  .IP "" 4
48
46
  .
49
47
  .nf
50
-
51
- \fBHello Chris
48
+ Hello Chris
52
49
  You have just won $10000!
53
- Well, $6000.0, after taxes. \fR
50
+ Well, $6000.0, after taxes.
54
51
  .
55
52
  .fi
56
53
  .
@@ -81,6 +78,10 @@ All variables are HTML escaped by default. If you want to return
81
78
  unescaped HTML, use the triple mustache: \fB{{{name}}}\fR.
82
79
  .
83
80
  .P
81
+ You can also use \fB&\fR to unescape a variable: \fB{{& name}}\fR. This may be
82
+ useful when changing delimiters (see "Set Delimter" below).
83
+ .
84
+ .P
84
85
  By default a variable "miss" returns an empty string. This can usually
85
86
  be configured in your Mustache library.
86
87
  .
@@ -90,11 +91,10 @@ Template:
90
91
  .IP "" 4
91
92
  .
92
93
  .nf
93
-
94
- \fB* {{name}}
94
+ * {{name}}
95
95
  * {{age}}
96
96
  * {{company}}
97
- * {{{company}}} \fR
97
+ * {{{company}}}
98
98
  .
99
99
  .fi
100
100
  .
@@ -106,11 +106,10 @@ Hash:
106
106
  .IP "" 4
107
107
  .
108
108
  .nf
109
-
110
- \fB{
109
+ {
111
110
  "name": "Chris",
112
111
  "company": "<b>GitHub</b>"
113
- } \fR
112
+ }
114
113
  .
115
114
  .fi
116
115
  .
@@ -122,11 +121,10 @@ Output:
122
121
  .IP "" 4
123
122
  .
124
123
  .nf
125
-
126
- \fB* Chris
124
+ * Chris
127
125
  *
128
126
  * &lt;b&gt;GitHub&lt;/b&gt;
129
- * <b>GitHub</b> \fR
127
+ * <b>GitHub</b>
130
128
  .
131
129
  .fi
132
130
  .
@@ -149,13 +147,12 @@ Template:
149
147
  .IP "" 4
150
148
  .
151
149
  .nf
152
-
153
- \fB{{#person}}
150
+ {{#person}}
154
151
  Shown!
155
152
  {{/person}}
156
153
  {{#anything_else}}
157
154
  Never shown!
158
- {{/anything_else}} \fR
155
+ {{/anything_else}}
159
156
  .
160
157
  .fi
161
158
  .
@@ -167,10 +164,9 @@ Hash:
167
164
  .IP "" 4
168
165
  .
169
166
  .nf
170
-
171
- \fB{
167
+ {
172
168
  "person": true
173
- } \fR
169
+ }
174
170
  .
175
171
  .fi
176
172
  .
@@ -182,8 +178,7 @@ Output:
182
178
  .IP "" 4
183
179
  .
184
180
  .nf
185
-
186
- \fBShown! \fR
181
+ Shown!
187
182
  .
188
183
  .fi
189
184
  .
@@ -206,10 +201,9 @@ Template:
206
201
  .IP "" 4
207
202
  .
208
203
  .nf
209
-
210
- \fB{{#repo}}
204
+ {{#repo}}
211
205
  <b>{{name}}</b>
212
- {{/repo}} \fR
206
+ {{/repo}}
213
207
  .
214
208
  .fi
215
209
  .
@@ -221,14 +215,13 @@ Hash:
221
215
  .IP "" 4
222
216
  .
223
217
  .nf
224
-
225
- \fB{
218
+ {
226
219
  "repo": [
227
220
  { "name": "resque" },
228
221
  { "name": "hub" },
229
222
  { "name": "rip" },
230
223
  ]
231
- } \fR
224
+ }
232
225
  .
233
226
  .fi
234
227
  .
@@ -240,10 +233,9 @@ Output:
240
233
  .IP "" 4
241
234
  .
242
235
  .nf
243
-
244
- \fB<b>resque</b>
236
+ <b>resque</b>
245
237
  <b>hub</b>
246
- <b>rip</b> \fR
238
+ <b>rip</b>
247
239
  .
248
240
  .fi
249
241
  .
@@ -255,8 +247,7 @@ Comments begin with a bang and are ignored. The following template:
255
247
  .IP "" 4
256
248
  .
257
249
  .nf
258
-
259
- \fB<h1>Today{{! ignore me }}.</h1> \fR
250
+ <h1>Today{{! ignore me }}.</h1>
260
251
  .
261
252
  .fi
262
253
  .
@@ -268,8 +259,7 @@ Will render as follows:
268
259
  .IP "" 4
269
260
  .
270
261
  .nf
271
-
272
- \fB<h1>Today.</h1> \fR
262
+ <h1>Today.</h1>
273
263
  .
274
264
  .fi
275
265
  .
@@ -289,15 +279,13 @@ For example, this template and partial:
289
279
  .IP "" 4
290
280
  .
291
281
  .nf
292
-
293
- \fBbase.mustache:
282
+ base.mustache:
294
283
  <h2>Names</h2>
295
284
  {{# names }}
296
285
  {{> user }}
297
- {{/ names }}
286
+ {{/ names }}
298
287
  user.mustache:
299
288
  <strong>{{ name }}</strong>
300
- \fR
301
289
  .
302
290
  .fi
303
291
  .
@@ -309,11 +297,10 @@ Can be thought of as a single, expanded template:
309
297
  .IP "" 4
310
298
  .
311
299
  .nf
312
-
313
- \fB<h2>Names</h2>
300
+ <h2>Names</h2>
314
301
  {{# names }}
315
302
  <strong>{{ name }}</strong>
316
- {{/ names }} \fR
303
+ {{/ names }}
317
304
  .
318
305
  .fi
319
306
  .
@@ -329,12 +316,11 @@ Consider the following contrived example:
329
316
  .IP "" 4
330
317
  .
331
318
  .nf
332
-
333
- \fB* {{ default_tags }}
319
+ * {{ default_tags }}
334
320
  {{=<% %>=}}
335
321
  * <% erb_style_tags %>
336
322
  <%={{ }}=%>
337
- * {{ default_tags_again }} \fR
323
+ * {{ default_tags_again }}
338
324
  .
339
325
  .fi
340
326
  .