erubis 2.3.1 → 2.4.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.
Files changed (97) hide show
  1. data/CHANGES +69 -2
  2. data/README.txt +1 -1
  3. data/benchmark/bench.rb +1 -1
  4. data/bin/erubis +1 -1
  5. data/contrib/erubis +3295 -2
  6. data/contrib/inline-require +1 -1
  7. data/doc-api/classes/Erubis.html +1 -1
  8. data/doc-api/classes/Erubis/ArrayEnhancer.html +12 -12
  9. data/doc-api/classes/Erubis/Basic/Converter.html +4 -4
  10. data/doc-api/classes/Erubis/BiPatternEnhancer.html +12 -12
  11. data/doc-api/classes/Erubis/CGenerator.html +60 -60
  12. data/doc-api/classes/Erubis/Context.html +42 -42
  13. data/doc-api/classes/Erubis/Converter.html +3 -2
  14. data/doc-api/classes/Erubis/DeleteIndentEnhancer.html +6 -6
  15. data/doc-api/classes/Erubis/Engine.html +30 -30
  16. data/doc-api/classes/Erubis/ErboutEnhancer.html +12 -12
  17. data/doc-api/classes/Erubis/EscapeEnhancer.html +6 -6
  18. data/doc-api/classes/Erubis/HeaderFooterEnhancer.html +12 -12
  19. data/doc-api/classes/Erubis/Helpers/RailsHelper.html +62 -7
  20. data/doc-api/classes/Erubis/Helpers/RailsHelper/PreprocessingEruby.html +177 -0
  21. data/doc-api/classes/Erubis/JavaGenerator.html +54 -54
  22. data/doc-api/classes/Erubis/JavascriptGenerator.html +60 -60
  23. data/doc-api/classes/Erubis/Main.html +20 -20
  24. data/doc-api/classes/Erubis/NoTextEnhancer.html +6 -6
  25. data/doc-api/classes/Erubis/OptimizedEruby.html +6 -6
  26. data/doc-api/classes/Erubis/OptimizedGenerator.html +72 -72
  27. data/doc-api/classes/Erubis/OptimizedXmlEruby.html +6 -6
  28. data/doc-api/classes/Erubis/PI/Converter.html +3 -3
  29. data/doc-api/classes/Erubis/PercentLineEnhancer.html +6 -6
  30. data/doc-api/classes/Erubis/PhpGenerator.html +54 -54
  31. data/doc-api/classes/Erubis/PrintEnabledEnhancer.html +20 -20
  32. data/doc-api/classes/Erubis/PrintOutEnhancer.html +30 -30
  33. data/doc-api/classes/Erubis/RubyEvaluator.html +47 -15
  34. data/doc-api/classes/Erubis/StringBufferEnhancer.html +12 -12
  35. data/doc-api/classes/Erubis/TinyEruby.html +24 -24
  36. data/doc-api/classes/Erubis/XmlHelper.html +79 -14
  37. data/doc-api/created.rid +1 -1
  38. data/doc-api/files/README_txt.html +2 -2
  39. data/doc-api/files/erubis/context_rb.html +1 -1
  40. data/doc-api/files/erubis/converter_rb.html +2 -2
  41. data/doc-api/files/erubis/engine/ec_rb.html +1 -1
  42. data/doc-api/files/erubis/engine/ejava_rb.html +1 -1
  43. data/doc-api/files/erubis/engine/ejavascript_rb.html +1 -1
  44. data/doc-api/files/erubis/engine/enhanced_rb.html +1 -1
  45. data/doc-api/files/erubis/engine/eperl_rb.html +1 -1
  46. data/doc-api/files/erubis/engine/ephp_rb.html +1 -1
  47. data/doc-api/files/erubis/engine/eruby_rb.html +1 -1
  48. data/doc-api/files/erubis/engine/escheme_rb.html +1 -1
  49. data/doc-api/files/erubis/engine/optimized_rb.html +1 -1
  50. data/doc-api/files/erubis/engine_rb.html +1 -1
  51. data/doc-api/files/erubis/enhancer_rb.html +1 -1
  52. data/doc-api/files/erubis/error_rb.html +1 -1
  53. data/doc-api/files/erubis/evaluator_rb.html +2 -2
  54. data/doc-api/files/erubis/generator_rb.html +1 -1
  55. data/doc-api/files/erubis/helper_rb.html +2 -2
  56. data/doc-api/files/erubis/helpers/rails_helper_rb.html +3 -2
  57. data/doc-api/files/erubis/local-setting_rb.html +1 -1
  58. data/doc-api/files/erubis/main_rb.html +2 -2
  59. data/doc-api/files/erubis/tiny_rb.html +1 -1
  60. data/doc-api/files/erubis_rb.html +1 -1
  61. data/doc-api/fr_class_index.html +1 -0
  62. data/doc-api/fr_method_index.html +128 -120
  63. data/doc/users-guide.html +248 -88
  64. data/lib/erubis.rb +2 -2
  65. data/lib/erubis/context.rb +1 -1
  66. data/lib/erubis/converter.rb +3 -2
  67. data/lib/erubis/engine.rb +1 -1
  68. data/lib/erubis/engine/ec.rb +1 -1
  69. data/lib/erubis/engine/ejava.rb +1 -1
  70. data/lib/erubis/engine/ejavascript.rb +1 -1
  71. data/lib/erubis/engine/enhanced.rb +1 -1
  72. data/lib/erubis/engine/eperl.rb +1 -1
  73. data/lib/erubis/engine/ephp.rb +1 -1
  74. data/lib/erubis/engine/eruby.rb +1 -1
  75. data/lib/erubis/engine/escheme.rb +1 -1
  76. data/lib/erubis/engine/optimized.rb +1 -1
  77. data/lib/erubis/enhancer.rb +1 -1
  78. data/lib/erubis/error.rb +1 -1
  79. data/lib/erubis/evaluator.rb +15 -4
  80. data/lib/erubis/generator.rb +1 -1
  81. data/lib/erubis/helper.rb +14 -2
  82. data/lib/erubis/helpers/rails_helper.rb +48 -3
  83. data/lib/erubis/local-setting.rb +1 -1
  84. data/lib/erubis/main.rb +7 -8
  85. data/lib/erubis/tiny.rb +1 -1
  86. data/test/assert-text-equal.rb +1 -1
  87. data/test/data/users-guide/def_method.rb +14 -0
  88. data/test/data/users-guide/def_method.result +3 -0
  89. data/test/test-engines.rb +1 -1
  90. data/test/test-enhancers.rb +1 -1
  91. data/test/test-erubis.rb +40 -2
  92. data/test/test-main.rb +17 -4
  93. data/test/test-users-guide.rb +1 -1
  94. data/test/test.rb +1 -1
  95. data/test/testutil.rb +1 -1
  96. metadata +15 -11
  97. data/test/data/users-guide/stderr.log +0 -3
data/CHANGES CHANGED
@@ -1,8 +1,75 @@
1
- # $Rev: 83 $
2
- # $Release: 2.3.1 $
1
+ # $Rev: 91 $
2
+ # $Release: 2.4.0 $
3
3
  # copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
4
4
 
5
5
 
6
+ #
7
+ - release: 2.4.0
8
+ date: 2007-07-19
9
+ enhancements:
10
+
11
+ - |
12
+ Preprocessing is supported by Ruby on Rails helper.
13
+ Preprocessing makes Ruby on Rails application about 20-40 percent faster.
14
+
15
+ For example,
16
+
17
+ [%= link_to 'Show', :action=>'show', :id=>_?('@user.id') %]
18
+
19
+ is evaluate by preprocessor and expanded into the following
20
+ when template file is loaded.
21
+
22
+ <a href="/users/show/<%=@user.id%>">Show</a>
23
+
24
+ It means that link_to() is not called when template is rendered
25
+ and rendering speed will be much faster in the result.
26
+
27
+ See User's Guide for details.
28
+
29
+ - |
30
+ Erubis::Eruby#evaluate() (or Erubis::RubyEvaluator#evaluate()) now
31
+ creates Proc object from @src and eval it.
32
+
33
+ def evaluate(context=Context.new)
34
+ context = Context.new(context) if context.is_a?(Hash)
35
+ @_proc ||= eval("proc { #{@src} }", TOPLEVEL_BINDING, @filename || '(erubis)')
36
+ return context.instance_eval(&@_proc)
37
+ end
38
+
39
+ This makes evaluate() much faster when eruby object is reused.
40
+
41
+ - |
42
+ Erubis::Eruby#def_method() is supported.
43
+ This method defines ruby code as instance method or singleton metod.
44
+
45
+ require 'erubis'
46
+ s = "hello <%= name %>"
47
+ eruby = Erubis::Eruby.new(s)
48
+ filename = 'hello.rhtml'
49
+
50
+ ## define instance method to Dummy class (or module)
51
+ class Dummy; end
52
+ eruby.def_method(Dummy, 'render(name)', filename) # filename is optional
53
+ p Dummy.new.render('world') #=> "hello world"
54
+
55
+ ## define singleton method to an object
56
+ obj = Object.new
57
+ eruby.def_method(obj, 'render(name)', filename) # filename is optional
58
+ p obj.render('world') #=> "hello world"
59
+
60
+ This is equivarent to ERB#def_method().
61
+
62
+ - |
63
+ Erubis::XmlHelper.url_escape() and u() which is alias of url_escape()
64
+ are added.
65
+ This is equivarent to ERB#Util.url_escape().
66
+
67
+
68
+ bugfix:
69
+ - Help message was not shown when '-h' is specified. Fixed.
70
+ - 'def method()' was not availabe in template file. Fixed.
71
+
72
+
6
73
  #
7
74
  - release: 2.3.1
8
75
  date: 2007-05-26
data/README.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  = README
2
2
 
3
- release:: 2.3.1
3
+ release:: 2.4.0
4
4
  copyright:: copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
5
5
 
6
6
 
data/benchmark/bench.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ###
4
4
  ### $Rev: 77 $
5
- ### $Release: 2.3.1 $
5
+ ### $Release: 2.4.0 $
6
6
  ### copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
7
7
  ###
8
8
 
data/bin/erubis CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ###
4
4
  ### $Rev: 77 $
5
- ### $Release: 2.3.1 $
5
+ ### $Release: 2.4.0 $
6
6
  ### copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
7
7
  ###
8
8
 
data/contrib/erubis CHANGED
@@ -2,10 +2,3303 @@
2
2
 
3
3
  ###
4
4
  ### $Rev: 77 $
5
- ### $Release: 2.3.1 $
5
+ ### $Release: 2.4.0 $
6
6
  ### copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
7
7
  ###
8
8
 
9
- require 'erubis/main'
9
+ #--begin of require 'erubis/main'
10
+ ###
11
+ ### $Rev: 91 $
12
+ ### $Release: 2.4.0 $
13
+ ### copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
14
+ ###
15
+
16
+ require 'yaml'
17
+ #--begin of require 'erubis'
18
+ ##
19
+ ## $Rev: 59 $
20
+ ## 2.4.0
21
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
22
+ ##
23
+
24
+ ##
25
+ ## an implementation of eRuby
26
+ ##
27
+ ## ex.
28
+ ## input = <<'END'
29
+ ## <ul>
30
+ ## <% for item in @list %>
31
+ ## <li><%= item %>
32
+ ## <%== item %></li>
33
+ ## <% end %>
34
+ ## </ul>
35
+ ## END
36
+ ## list = ['<aaa>', 'b&b', '"ccc"']
37
+ ## eruby = Erubis::Eruby.new(input)
38
+ ## puts "--- code ---"
39
+ ## puts eruby.src
40
+ ## puts "--- result ---"
41
+ ## context = Erubis::Context.new() # or new(:list=>list)
42
+ ## context[:list] = list
43
+ ## puts eruby.evaluate(context)
44
+ ##
45
+ ## result:
46
+ ## --- source ---
47
+ ## _buf = ''; _buf << '<ul>
48
+ ## '; for item in @list
49
+ ## _buf << ' <li>'; _buf << ( item ).to_s; _buf << '
50
+ ## '; _buf << ' '; _buf << Erubis::XmlHelper.escape_xml( item ); _buf << '</li>
51
+ ## '; end
52
+ ## _buf << '</ul>
53
+ ## ';
54
+ ## _buf.to_s
55
+ ## --- result ---
56
+ ## <ul>
57
+ ## <li><aaa>
58
+ ## &lt;aaa&gt;</li>
59
+ ## <li>b&b
60
+ ## b&amp;b</li>
61
+ ## <li>"ccc"
62
+ ## &quot;ccc&quot;</li>
63
+ ## </ul>
64
+ ##
65
+
66
+
67
+ module Erubis
68
+ VERSION = ('$Release: 2.4.0 $' =~ /([.\d]+)/) && $1
69
+ end
70
+
71
+ #--begin of require 'erubis/engine'
72
+ ##
73
+ ## $Rev: 77 $
74
+ ## $Release: 2.4.0 $
75
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
76
+ ##
77
+
78
+
79
+ #--begin of require 'erubis/generator'
80
+ ##
81
+ ## $Rev: 77 $
82
+ ## $Release: 2.4.0 $
83
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
84
+ ##
85
+
86
+ #--begin of require 'abstract'
87
+ ##
88
+ ## $Rev: 1 $
89
+ ## $Release: 0.1.0 $
90
+ ## copyright(c) 2006 kuwata-lab.com all rights reserved.
91
+ ##
92
+ ##
93
+ ## helper to define abstract method in Ruby.
94
+ ##
95
+ ##
96
+ ## example1. (shorter notation)
97
+ ##
98
+ ## require 'abstract'
99
+ ## class Foo
100
+ ## abstract_method 'arg1, arg2=""', :method1, :method2, :method3
101
+ ## end
102
+ ##
103
+ ##
104
+ ## example2. (RDoc friendly notation)
105
+ ##
106
+ ## require 'abstract'
107
+ ## class Bar
108
+ ## # ... method1 description ...
109
+ ## def method1(arg1, arg2="")
110
+ ## not_implemented
111
+ ## end
112
+ ##
113
+ ## # ... method2 description ...
114
+ ## def method2(arg1, arg2="")
115
+ ## not_implemented
116
+ ## end
117
+ ## end
118
+ ##
119
+
120
+
121
+ ##
122
+ class Module
123
+
124
+ ##
125
+ ## define abstract methods
126
+ ##
127
+ def abstract_method args_str, *method_names
128
+ method_names.each do |name|
129
+ module_eval <<-END
130
+ def #{name}(#{args_str})
131
+ mesg = "class \#{self.class.name} must implement abstract method `#{self.name}##{name}()'."
132
+ #mesg = "\#{self.class.name}##{name}() is not implemented."
133
+ err = NotImplementedError.new mesg
134
+ err.set_backtrace caller()
135
+ raise err
136
+ end
137
+ END
138
+ end
139
+ end
140
+
141
+ end
142
+
143
+
144
+ ##
145
+ module Kernel
146
+
147
+ ##
148
+ ## raise NotImplementedError
149
+ ##
150
+ def not_implemented #:doc:
151
+ backtrace = caller()
152
+ method_name = (backtrace.shift =~ /`(\w+)'$/) && $1
153
+ mesg = "class #{self.class.name} must implement abstract method '#{method_name}()'."
154
+ #mesg = "#{self.class.name}##{method_name}() is not implemented."
155
+ err = NotImplementedError.new mesg
156
+ err.set_backtrace backtrace
157
+ raise err
158
+ end
159
+ private :not_implemented
160
+
161
+ end
162
+ #--end of require 'abstract'
163
+
164
+ module Erubis
165
+
166
+
167
+ ##
168
+ ## code generator, called by Converter module
169
+ ##
170
+ module Generator
171
+
172
+ def self.supported_properties() # :nodoc:
173
+ return [
174
+ [:escapefunc, nil, "escape function name"],
175
+ ]
176
+ end
177
+
178
+ attr_accessor :escapefunc
179
+
180
+ def init_generator(properties={})
181
+ @escapefunc = properties[:escapefunc]
182
+ end
183
+
184
+
185
+ ## (abstract) escape text string
186
+ ##
187
+ ## ex.
188
+ ## def escape_text(text)
189
+ ## return text.dump
190
+ ## # or return "'" + text.gsub(/['\\]/, '\\\\\&') + "'"
191
+ ## end
192
+ def escape_text(text)
193
+ not_implemented
194
+ end
195
+
196
+ ## return escaped expression code (ex. 'h(...)' or 'htmlspecialchars(...)')
197
+ def escaped_expr(code)
198
+ code.strip!
199
+ return "#{@escapefunc}(#{code})"
200
+ end
201
+
202
+ ## (abstract) add @preamble to src
203
+ def add_preamble(src)
204
+ not_implemented
205
+ end
206
+
207
+ ## (abstract) add text string to src
208
+ def add_text(src, text)
209
+ not_implemented
210
+ end
211
+
212
+ ## (abstract) add statement code to src
213
+ def add_stmt(src, code)
214
+ not_implemented
215
+ end
216
+
217
+ ## (abstract) add expression literal code to src. this is called by add_expr().
218
+ def add_expr_literal(src, code)
219
+ not_implemented
220
+ end
221
+
222
+ ## (abstract) add escaped expression code to src. this is called by add_expr().
223
+ def add_expr_escaped(src, code)
224
+ not_implemented
225
+ end
226
+
227
+ ## (abstract) add expression code to src for debug. this is called by add_expr().
228
+ def add_expr_debug(src, code)
229
+ not_implemented
230
+ end
231
+
232
+ ## (abstract) add @postamble to src
233
+ def add_postamble(src)
234
+ not_implemented
235
+ end
236
+
237
+
238
+ end
239
+
240
+
241
+ end
242
+ #--end of require 'erubis/generator'
243
+ #--begin of require 'erubis/converter'
244
+ ##
245
+ ## $Rev: 89 $
246
+ ## $Release: 2.4.0 $
247
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
248
+ ##
249
+
250
+ #--already included require 'abstract'
251
+
252
+ module Erubis
253
+
254
+
255
+ ##
256
+ ## convert
257
+ ##
258
+ module Converter
259
+
260
+ attr_accessor :preamble, :postamble, :escape
261
+
262
+ def self.supported_properties # :nodoc:
263
+ return [
264
+ [:preamble, nil, "preamble (no preamble when false)"],
265
+ [:postamble, nil, "postamble (no postamble when false)"],
266
+ [:escape, nil, "escape expression or not in default"],
267
+ ]
268
+ end
269
+
270
+ def init_converter(properties={})
271
+ @preamble = properties[:preamble]
272
+ @postamble = properties[:postamble]
273
+ @escape = properties[:escape]
274
+ end
275
+
276
+ ## convert input string into target language
277
+ def convert(input)
278
+ codebuf = "" # or []
279
+ @preamble.nil? ? add_preamble(codebuf) : (@preamble && (codebuf << @preamble))
280
+ convert_input(codebuf, input)
281
+ @postamble.nil? ? add_postamble(codebuf) : (@postamble && (codebuf << @postamble))
282
+ @_proc = nil # clear cached proc object
283
+ return codebuf # or codebuf.join()
284
+ end
285
+
286
+ protected
287
+
288
+ ##
289
+ ## detect spaces at beginning of line
290
+ ##
291
+ def detect_spaces_at_bol(text, is_bol)
292
+ lspace = nil
293
+ if text.empty?
294
+ lspace = "" if is_bol
295
+ elsif text[-1] == ?\n
296
+ lspace = ""
297
+ else
298
+ rindex = text.rindex(?\n)
299
+ if rindex
300
+ s = text[rindex+1..-1]
301
+ if s =~ /\A[ \t]*\z/
302
+ lspace = s
303
+ #text = text[0..rindex]
304
+ text[rindex+1..-1] = ''
305
+ end
306
+ else
307
+ if is_bol && text =~ /\A[ \t]*\z/
308
+ #lspace = text
309
+ #text = nil
310
+ lspace = text.dup
311
+ text[0..-1] = ''
312
+ end
313
+ end
314
+ end
315
+ return lspace
316
+ end
317
+
318
+ ##
319
+ ## (abstract) convert input to code
320
+ ##
321
+ def convert_input(codebuf, input)
322
+ not_implemented
323
+ end
324
+
325
+ end
326
+
327
+
328
+ module Basic
329
+ end
330
+
331
+
332
+ ##
333
+ ## basic converter which supports '<% ... %>' notation.
334
+ ##
335
+ module Basic::Converter
336
+ include Erubis::Converter
337
+
338
+ def self.supported_properties # :nodoc:
339
+ return [
340
+ [:pattern, '<% %>', "embed pattern"],
341
+ [:trim, true, "trim spaces around <% ... %>"],
342
+ ]
343
+ end
344
+
345
+ attr_accessor :pattern, :trim
346
+
347
+ def init_converter(properties={})
348
+ super(properties)
349
+ @pattern = properties[:pattern]
350
+ @trim = properties[:trim] != false
351
+ end
352
+
353
+ protected
354
+
355
+ ## return regexp of pattern to parse eRuby script
356
+ def pattern_regexp(pattern)
357
+ prefix, postfix = pattern.split() # '<% %>' => '<%', '%>'
358
+ #return /(.*?)(^[ \t]*)?#{prefix}(=+|\#)?(.*?)-?#{postfix}([ \t]*\r?\n)?/m
359
+ #return /(^[ \t]*)?#{prefix}(=+|\#)?(.*?)-?#{postfix}([ \t]*\r?\n)?/m
360
+ return /#{prefix}(=+|-|\#)?(.*?)-?#{postfix}([ \t]*\r?\n)?/m
361
+ end
362
+ module_function :pattern_regexp
363
+
364
+ #DEFAULT_REGEXP = /(.*?)(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
365
+ #DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
366
+ #DEFAULT_REGEXP = /<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
367
+ DEFAULT_REGEXP = pattern_regexp('<% %>')
368
+
369
+ public
370
+
371
+ def convert_input(src, input)
372
+ pat = @pattern
373
+ regexp = pat.nil? || pat == '<% %>' ? DEFAULT_REGEXP : pattern_regexp(pat)
374
+ pos = 0
375
+ is_bol = true # is beginning of line
376
+ input.scan(regexp) do |indicator, code, rspace|
377
+ match = Regexp.last_match()
378
+ len = match.begin(0) - pos
379
+ text = input[pos, len]
380
+ pos = match.end(0)
381
+ ch = indicator ? indicator[0] : nil
382
+ lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
383
+ is_bol = rspace ? true : false
384
+ add_text(src, text) if text && !text.empty?
385
+ ## * when '<%= %>', do nothing
386
+ ## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
387
+ if ch == ?= # <%= %>
388
+ add_text(src, lspace) if lspace
389
+ add_expr(src, code, indicator)
390
+ add_text(src, rspace) if rspace
391
+ elsif ch == ?\# # <%# %>
392
+ n = code.count("\n") + (rspace ? 1 : 0)
393
+ if @trim && lspace && rspace
394
+ add_stmt(src, "\n" * n)
395
+ else
396
+ add_text(src, lspace) if lspace
397
+ add_stmt(src, "\n" * n)
398
+ add_text(src, rspace) if rspace
399
+ end
400
+ else # <% %>
401
+ if @trim && lspace && rspace
402
+ add_stmt(src, "#{lspace}#{code}#{rspace}")
403
+ else
404
+ add_text(src, lspace) if lspace
405
+ add_stmt(src, code)
406
+ add_text(src, rspace) if rspace
407
+ end
408
+ end
409
+ end
410
+ rest = $' || input # add input when no matched
411
+ add_text(src, rest)
412
+ end
413
+
414
+ ## add expression code to src
415
+ def add_expr(src, code, indicator)
416
+ case indicator
417
+ when '='
418
+ @escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
419
+ when '=='
420
+ @escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
421
+ when '==='
422
+ add_expr_debug(src, code)
423
+ end
424
+ end
425
+
426
+ end
427
+
428
+
429
+ module PI
430
+ end
431
+
432
+ ##
433
+ ## Processing Instructions (PI) converter for XML.
434
+ ## this class converts '<?rb ... ?>' and '${...}' notation.
435
+ ##
436
+ module PI::Converter
437
+ include Erubis::Converter
438
+
439
+ def self.desc # :nodoc:
440
+ "use processing instructions (PI) instead of '<% %>'"
441
+ end
442
+
443
+ def self.supported_properties # :nodoc:
444
+ return [
445
+ [:trim, true, "trim spaces around <% ... %>"],
446
+ [:pi, 'rb', "PI (Processing Instrunctions) name"],
447
+ [:embchar, '@', "char for embedded expression pattern('@{...}@')"],
448
+ [:pattern, '<% %>', "embed pattern"],
449
+ ]
450
+ end
451
+
452
+ attr_accessor :pi, :prefix
453
+
454
+ def init_converter(properties={})
455
+ super(properties)
456
+ @trim = properties.fetch(:trim, true)
457
+ @pi = properties[:pi] if properties[:pi]
458
+ @embchar = properties[:embchar] || '@'
459
+ @pattern = properties[:pattern]
460
+ @pattern = '<% %>' if @pattern.nil? #|| @pattern == true
461
+ end
462
+
463
+ def convert(input)
464
+ code = super(input)
465
+ return @header || @footer ? "#{@header}#{code}#{@footer}" : code
466
+ end
467
+
468
+ protected
469
+
470
+ def convert_input(codebuf, input)
471
+ unless @regexp
472
+ @pi ||= 'e'
473
+ ch = Regexp.escape(@embchar)
474
+ if @pattern
475
+ left, right = @pattern.split(' ')
476
+ @regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/m
477
+ else
478
+ @regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}/m
479
+ end
480
+ end
481
+ #
482
+ is_bol = true
483
+ pos = 0
484
+ input.scan(@regexp) do |pi_arg, stmt, rspace,
485
+ indicator1, expr1, indicator2, expr2|
486
+ match = Regexp.last_match
487
+ len = match.begin(0) - pos
488
+ text = input[pos, len]
489
+ pos = match.end(0)
490
+ lspace = stmt ? detect_spaces_at_bol(text, is_bol) : nil
491
+ is_bol = stmt && rspace ? true : false
492
+ add_text(codebuf, text) # unless text.empty?
493
+ #
494
+ if stmt
495
+ if @trim && lspace && rspace
496
+ add_pi_stmt(codebuf, "#{lspace}#{stmt}#{rspace}", pi_arg)
497
+ else
498
+ add_text(codebuf, lspace) if lspace
499
+ add_pi_stmt(codebuf, stmt, pi_arg)
500
+ add_text(codebuf, rspace) if rspace
501
+ end
502
+ else
503
+ add_pi_expr(codebuf, expr1 || expr2, indicator1 || indicator2)
504
+ end
505
+ end
506
+ rest = $' || input
507
+ add_text(codebuf, rest)
508
+ end
509
+
510
+ #--
511
+ #def convert_input(codebuf, input)
512
+ # parse_stmts(codebuf, input)
513
+ # #parse_stmts2(codebuf, input)
514
+ #end
515
+ #
516
+ #def parse_stmts(codebuf, input)
517
+ # #regexp = pattern_regexp(@pattern)
518
+ # @pi ||= 'e'
519
+ # @stmt_pattern ||= /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?/m
520
+ # is_bol = true
521
+ # pos = 0
522
+ # input.scan(@stmt_pattern) do |pi_arg, code, rspace|
523
+ # match = Regexp.last_match
524
+ # len = match.begin(0) - pos
525
+ # text = input[pos, len]
526
+ # pos = match.end(0)
527
+ # lspace = detect_spaces_at_bol(text, is_bol)
528
+ # is_bol = rspace ? true : false
529
+ # parse_exprs(codebuf, text) # unless text.empty?
530
+ # if @trim && lspace && rspace
531
+ # add_pi_stmt(codebuf, "#{lspace}#{code}#{rspace}", pi_arg)
532
+ # else
533
+ # add_text(codebuf, lspace)
534
+ # add_pi_stmt(codebuf, code, pi_arg)
535
+ # add_text(codebuf, rspace)
536
+ # end
537
+ # end
538
+ # rest = $' || input
539
+ # parse_exprs(codebuf, rest)
540
+ #end
541
+ #
542
+ #def parse_exprs(codebuf, input)
543
+ # unless @expr_pattern
544
+ # ch = Regexp.escape(@embchar)
545
+ # if @pattern
546
+ # left, right = @pattern.split(' ')
547
+ # @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/
548
+ # else
549
+ # @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}/
550
+ # end
551
+ # end
552
+ # pos = 0
553
+ # input.scan(@expr_pattern) do |indicator1, code1, indicator2, code2|
554
+ # indicator = indicator1 || indicator2
555
+ # code = code1 || code2
556
+ # match = Regexp.last_match
557
+ # len = match.begin(0) - pos
558
+ # text = input[pos, len]
559
+ # pos = match.end(0)
560
+ # add_text(codebuf, text) # unless text.empty?
561
+ # add_pi_expr(codebuf, code, indicator)
562
+ # end
563
+ # rest = $' || input
564
+ # add_text(codebuf, rest)
565
+ #end
566
+ #++
567
+
568
+ def add_pi_stmt(codebuf, code, pi_arg) # :nodoc:
569
+ case pi_arg
570
+ when nil ; add_stmt(codebuf, code)
571
+ when 'header' ; @header = code
572
+ when 'footer' ; @footer = code
573
+ when 'comment'; add_stmt(codebuf, "\n" * code.count("\n"))
574
+ when 'value' ; add_expr_literal(codebuf, code)
575
+ else ; add_stmt(codebuf, code)
576
+ end
577
+ end
578
+
579
+ def add_pi_expr(codebuf, code, indicator) # :nodoc:
580
+ case indicator
581
+ when nil, '', '==' # @{...}@ or <%== ... %>
582
+ @escape == false ? add_expr_literal(codebuf, code) : add_expr_escaped(codebuf, code)
583
+ when '!', '=' # @!{...}@ or <%= ... %>
584
+ @escape == false ? add_expr_escaped(codebuf, code) : add_expr_literal(codebuf, code)
585
+ when '!!', '===' # @!!{...}@ or <%=== ... %>
586
+ add_expr_debug(codebuf, code)
587
+ else
588
+ # ignore
589
+ end
590
+ end
591
+
592
+ end
593
+
594
+
595
+ end
596
+ #--end of require 'erubis/converter'
597
+ #--begin of require 'erubis/evaluator'
598
+ ##
599
+ ## $Rev: 91 $
600
+ ## $Release: 2.4.0 $
601
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
602
+ ##
603
+
604
+ #--begin of require 'erubis/error'
605
+ ##
606
+ ## $Rev: 77 $
607
+ ## $Release: 2.4.0 $
608
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
609
+ ##
610
+
611
+ module Erubis
612
+
613
+
614
+ ##
615
+ ## base error class
616
+ ##
617
+ class ErubisError < StandardError
618
+ end
619
+
620
+
621
+ ##
622
+ ## raised when method or function is not supported
623
+ ##
624
+ class NotSupportedError < ErubisError
625
+ end
626
+
627
+
628
+ end
629
+ #--end of require 'erubis/error'
630
+ #--begin of require 'erubis/context'
631
+ ##
632
+ ## $Rev: 77 $
633
+ ## $Release: 2.4.0 $
634
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
635
+ ##
636
+
637
+
638
+ module Erubis
639
+
640
+
641
+ ##
642
+ ## context object for Engine#evaluate
643
+ ##
644
+ ## ex.
645
+ ## template = <<'END'
646
+ ## Hello <%= @user %>!
647
+ ## <% for item in @list %>
648
+ ## - <%= item %>
649
+ ## <% end %>
650
+ ## END
651
+ ##
652
+ ## context = Erubis::Context.new(:user=>'World', :list=>['a','b','c'])
653
+ ## # or
654
+ ## # context = Erubis::Context.new
655
+ ## # context[:user] = 'World'
656
+ ## # context[:list] = ['a', 'b', 'c']
657
+ ##
658
+ ## eruby = Erubis::Eruby.new(template)
659
+ ## print eruby.evaluate(context)
660
+ ##
661
+ class Context
662
+ include Enumerable
663
+
664
+ def initialize(hash=nil)
665
+ hash.each do |name, value|
666
+ self[name] = value
667
+ end if hash
668
+ end
669
+
670
+ def [](key)
671
+ return instance_variable_get("@#{key}")
672
+ end
673
+
674
+ def []=(key, value)
675
+ return instance_variable_set("@#{key}", value)
676
+ end
677
+
678
+ def keys
679
+ return instance_variables.collect { |name| name[1..-1] }
680
+ end
681
+
682
+ def each
683
+ instance_variables.each do |name|
684
+ key = name[1..-1]
685
+ value = instance_variable_get(name)
686
+ yield(key, value)
687
+ end
688
+ end
689
+
690
+ def to_hash
691
+ hash = {}
692
+ self.keys.each { |key| hash[key] = self[key] }
693
+ return hash
694
+ end
695
+
696
+ def update(context_or_hash)
697
+ arg = context_or_hash
698
+ if arg.is_a?(Hash)
699
+ arg.each do |key, val|
700
+ self[key] = val
701
+ end
702
+ else
703
+ arg.instance_variables.each do |varname|
704
+ key = varname[1..-1]
705
+ val = arg.instance_variable_get(varname)
706
+ self[key] = val
707
+ end
708
+ end
709
+ end
710
+
711
+ end
712
+
713
+
714
+ end
715
+ #--end of require 'erubis/context'
716
+
717
+
718
+ module Erubis
719
+
720
+
721
+ ##
722
+ ## evaluate code
723
+ ##
724
+ module Evaluator
725
+
726
+ def self.supported_properties # :nodoc:
727
+ return []
728
+ end
729
+
730
+ attr_accessor :src, :filename
731
+
732
+ def init_evaluator(properties)
733
+ @filename = properties[:filename]
734
+ end
735
+
736
+ def result(*args)
737
+ raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
738
+ end
739
+
740
+ def evaluate(*args)
741
+ raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
742
+ end
743
+
744
+ end
745
+
746
+
747
+ ##
748
+ ## evaluator for Ruby
749
+ ##
750
+ module RubyEvaluator
751
+ include Evaluator
752
+
753
+ def self.supported_properties # :nodoc:
754
+ list = Evaluator.supported_properties
755
+ return list
756
+ end
757
+
758
+ ## eval(@src) with binding object
759
+ def result(_binding_or_hash=TOPLEVEL_BINDING)
760
+ _arg = _binding_or_hash
761
+ if _arg.is_a?(Hash)
762
+ ## load _context data as local variables by eval
763
+ #eval _arg.keys.inject("") { |s, k| s << "#{k.to_s} = _arg[#{k.inspect}];" }
764
+ eval _arg.collect{|k,v| "#{k} = _arg[#{k.inspect}]; "}.join
765
+ _arg = binding()
766
+ end
767
+ return eval(@src, _arg, (@filename || '(erubis)'))
768
+ end
769
+
770
+ ## invoke context.instance_eval(@src)
771
+ def evaluate(context=Context.new)
772
+ context = Context.new(context) if context.is_a?(Hash)
773
+ #return context.instance_eval(@src, @filename || '(erubis)')
774
+ @_proc ||= eval("proc { #{@src} }", TOPLEVEL_BINDING, @filename || '(erubis)')
775
+ return context.instance_eval(&@_proc)
776
+ end
777
+
778
+ ## if object is an Class or Module then define instance method to it,
779
+ ## else define singleton method to it.
780
+ def def_method(object, method_name, filename=nil)
781
+ m = object.is_a?(Module) ? :module_eval : :instance_eval
782
+ object.__send__(m, "def #{method_name}; #{@src}; end", filename || @filename || '(erubis)')
783
+ end
784
+
785
+
786
+ end
787
+
788
+
789
+ end
790
+ #--end of require 'erubis/evaluator'
791
+ #--already included require 'erubis/context'
792
+
793
+
794
+ module Erubis
795
+
796
+
797
+ ##
798
+ ## (abstract) abstract engine class.
799
+ ## subclass must include evaluator and converter module.
800
+ ##
801
+ class Engine
802
+ #include Evaluator
803
+ #include Converter
804
+ #include Generator
805
+
806
+ def initialize(input=nil, properties={})
807
+ #@input = input
808
+ init_generator(properties)
809
+ init_converter(properties)
810
+ init_evaluator(properties)
811
+ @src = convert(input) if input
812
+ end
813
+
814
+
815
+ ##
816
+ ## convert input string and set it to @src
817
+ ##
818
+ def convert!(input)
819
+ @src = convert(input)
820
+ end
821
+
822
+
823
+ ##
824
+ ## load file, write cache file, and return engine object.
825
+ ## this method create cache file (filename + '.cache') automatically.
826
+ ##
827
+ def self.load_file(filename, properties={})
828
+ cachename = filename + '.cache'
829
+ properties[:filename] = filename
830
+ if test(?f, cachename) && File.mtime(filename) <= File.mtime(cachename)
831
+ engine = self.new(nil, properties)
832
+ engine.src = File.read(cachename)
833
+ else
834
+ input = File.open(filename, 'rb') { |f| f.read }
835
+ engine = self.new(input, properties)
836
+ File.open(cachename, 'w') do |f|
837
+ f.flock(File::LOCK_EX)
838
+ f.write(engine.src)
839
+ end
840
+ end
841
+ engine.src.untaint # ok?
842
+ return engine
843
+ end
844
+
845
+
846
+ ##
847
+ ## helper method to convert and evaluate input text with context object.
848
+ ## context may be Binding, Hash, or Object.
849
+ ##
850
+ def process(input, context=nil, filename=nil)
851
+ code = convert(input)
852
+ filename ||= '(erubis)'
853
+ if context.is_a?(Binding)
854
+ return eval(code, context, filename)
855
+ else
856
+ context = Context.new(context) if context.is_a?(Hash)
857
+ return context.instance_eval(code, filename)
858
+ end
859
+ end
860
+
861
+
862
+ ##
863
+ ## helper method evaluate Proc object with contect object.
864
+ ## context may be Binding, Hash, or Object.
865
+ ##
866
+ def process_proc(proc_obj, context=nil, filename=nil)
867
+ if context.is_a?(Binding)
868
+ filename ||= '(erubis)'
869
+ return eval(proc_obj, context, filename)
870
+ else
871
+ context = Context.new(context) if context.is_a?(Hash)
872
+ return context.instance_eval(&proc_obj)
873
+ end
874
+ end
875
+
876
+
877
+ end # end of class Engine
878
+
879
+
880
+ ##
881
+ ## (abstract) base engine class for Eruby, Eperl, Ejava, and so on.
882
+ ## subclass must include generator.
883
+ ##
884
+ class Basic::Engine < Engine
885
+ include Evaluator
886
+ include Basic::Converter
887
+ include Generator
888
+ end
889
+
890
+
891
+ class PI::Engine < Engine
892
+ include Evaluator
893
+ include PI::Converter
894
+ include Generator
895
+ end
896
+
897
+
898
+ end
899
+ #--end of require 'erubis/engine'
900
+ #require 'erubis/generator'
901
+ #require 'erubis/converter'
902
+ #require 'erubis/evaluator'
903
+ #require 'erubis/error'
904
+ #require 'erubis/context'
905
+ #--begin of require 'erubis/helper'
906
+ ##
907
+ ## $Rev: 89 $
908
+ ## $Release: 2.4.0 $
909
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
910
+ ##
911
+
912
+
913
+ module Erubis
914
+
915
+ ##
916
+ ## helper for xml
917
+ ##
918
+ module XmlHelper
919
+
920
+ module_function
921
+
922
+ ESCAPE_TABLE = {
923
+ '&' => '&amp;',
924
+ '<' => '&lt;',
925
+ '>' => '&gt;',
926
+ '"' => '&quot;',
927
+ "'" => '&#039;',
928
+ }
929
+
930
+ def escape_xml(value)
931
+ value.to_s.gsub(/[&<>"]/) { |s| ESCAPE_TABLE[s] } # or /[&<>"']/
932
+ #value.to_s.gsub(/[&<>"]/) { ESCAPE_TABLE[$&] }
933
+ end
934
+
935
+ def escape_xml2(value)
936
+ return value.to_s.gsub(/\&/,'&amp;').gsub(/</,'&lt;').gsub(/>/,'&gt;').gsub(/"/,'&quot;')
937
+ end
938
+
939
+ alias h escape_xml
940
+ alias html_escape escape_xml
941
+
942
+ def url_encode(str)
943
+ return str.gsub(/[^-_.a-zA-Z0-9]+/) { |s|
944
+ s.unpack('C*').collect { |i| "%%%02X" % i }.join
945
+ }
946
+ end
947
+
948
+ alias u url_encode
949
+
950
+ end
951
+
952
+
953
+ end
954
+ #--end of require 'erubis/helper'
955
+ #--begin of require 'erubis/enhancer'
956
+ ##
957
+ ## $Rev: 77 $
958
+ ## $Release: 2.4.0 $
959
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
960
+ ##
961
+
962
+
963
+ module Erubis
964
+
965
+
966
+ ##
967
+ ## switch '<%= ... %>' to escaped and '<%== ... %>' to unescaped
968
+ ##
969
+ ## ex.
970
+ ## class XmlEruby < Eruby
971
+ ## include EscapeEnhancer
972
+ ## end
973
+ ##
974
+ ## this is language-indenedent.
975
+ ##
976
+ module EscapeEnhancer
977
+
978
+ def self.desc # :nodoc:
979
+ "switch '<%= %>' to escaped and '<%== %>' to unescaped"
980
+ end
981
+
982
+ #--
983
+ #def self.included(klass)
984
+ # klass.class_eval <<-END
985
+ # alias _add_expr_literal add_expr_literal
986
+ # alias _add_expr_escaped add_expr_escaped
987
+ # alias add_expr_literal _add_expr_escaped
988
+ # alias add_expr_escaped _add_expr_literal
989
+ # END
990
+ #end
991
+ #++
992
+
993
+ def add_expr(src, code, indicator)
994
+ case indicator
995
+ when '='
996
+ @escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
997
+ when '=='
998
+ @escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
999
+ when '==='
1000
+ add_expr_debug(src, code)
1001
+ end
1002
+ end
1003
+
1004
+ end
1005
+
1006
+
1007
+ #--
1008
+ ## (obsolete)
1009
+ #module FastEnhancer
1010
+ #end
1011
+ #++
1012
+
1013
+
1014
+ ##
1015
+ ## use $stdout instead of string
1016
+ ##
1017
+ ## this is only for Eruby.
1018
+ ##
1019
+ module StdoutEnhancer
1020
+
1021
+ def self.desc # :nodoc:
1022
+ "use $stdout instead of array buffer or string buffer"
1023
+ end
1024
+
1025
+ def add_preamble(src)
1026
+ src << "_buf = $stdout;"
1027
+ end
1028
+
1029
+ def add_postamble(src)
1030
+ src << "\n''\n"
1031
+ end
1032
+
1033
+ end
1034
+
1035
+
1036
+ ##
1037
+ ## use print statement instead of '_buf << ...'
1038
+ ##
1039
+ ## this is only for Eruby.
1040
+ ##
1041
+ module PrintOutEnhancer
1042
+
1043
+ def self.desc # :nodoc:
1044
+ "use print statement instead of '_buf << ...'"
1045
+ end
1046
+
1047
+ def add_preamble(src)
1048
+ end
1049
+
1050
+ def add_text(src, text)
1051
+ src << " print '" << escape_text(text) << "';" unless text.empty?
1052
+ end
1053
+
1054
+ def add_expr_literal(src, code)
1055
+ src << ' print((' << code << ').to_s);'
1056
+ end
1057
+
1058
+ def add_expr_escaped(src, code)
1059
+ src << ' print ' << escaped_expr(code) << ';'
1060
+ end
1061
+
1062
+ def add_postamble(src)
1063
+ src << "\n" unless src[-1] == ?\n
1064
+ end
1065
+
1066
+ end
1067
+
1068
+
1069
+ ##
1070
+ ## enable print function
1071
+ ##
1072
+ ## Notice: use Eruby#evaluate() and don't use Eruby#result()
1073
+ ## to be enable print function.
1074
+ ##
1075
+ ## this is only for Eruby.
1076
+ ##
1077
+ module PrintEnabledEnhancer
1078
+
1079
+ def self.desc # :nodoc:
1080
+ "enable to use print function in '<% %>'"
1081
+ end
1082
+
1083
+ def add_preamble(src)
1084
+ src << "@_buf = "
1085
+ super
1086
+ end
1087
+
1088
+ def print(*args)
1089
+ args.each do |arg|
1090
+ @_buf << arg.to_s
1091
+ end
1092
+ end
1093
+
1094
+ def evaluate(context=nil)
1095
+ _src = @src
1096
+ if context.is_a?(Hash)
1097
+ context.each do |key, val| instance_variable_set("@#{key}", val) end
1098
+ elsif context
1099
+ context.instance_variables.each do |name|
1100
+ instance_variable_set(name, context.instance_variable_get(name))
1101
+ end
1102
+ end
1103
+ return instance_eval(_src, (@filename || '(erubis)'))
1104
+ end
1105
+
1106
+ end
1107
+
1108
+
1109
+ ##
1110
+ ## return array instead of string
1111
+ ##
1112
+ ## this is only for Eruby.
1113
+ ##
1114
+ module ArrayEnhancer
1115
+
1116
+ def self.desc # :nodoc:
1117
+ "return array instead of string"
1118
+ end
1119
+
1120
+ def add_preamble(src)
1121
+ src << "_buf = [];"
1122
+ end
1123
+
1124
+ def add_postamble(src)
1125
+ src << "\n" unless src[-1] == ?\n
1126
+ src << "_buf\n"
1127
+ end
1128
+
1129
+ end
1130
+
1131
+
1132
+ ##
1133
+ ## use an Array object as buffer (included in Eruby by default)
1134
+ ##
1135
+ ## this is only for Eruby.
1136
+ ##
1137
+ module ArrayBufferEnhancer
1138
+
1139
+ def self.desc # :nodoc:
1140
+ "use an Array object for buffering (included in Eruby class)"
1141
+ end
1142
+
1143
+ def add_preamble(src)
1144
+ src << "_buf = [];"
1145
+ end
1146
+
1147
+ def add_postamble(src)
1148
+ src << "\n" unless src[-1] == ?\n
1149
+ src << "_buf.join\n"
1150
+ end
1151
+
1152
+ end
1153
+
1154
+
1155
+ ##
1156
+ ## use String class for buffering
1157
+ ##
1158
+ ## this is only for Eruby.
1159
+ ##
1160
+ module StringBufferEnhancer
1161
+
1162
+ def self.desc # :nodoc:
1163
+ "use a String object for buffering"
1164
+ end
1165
+
1166
+ def add_preamble(src)
1167
+ src << "_buf = '';"
1168
+ end
1169
+
1170
+ def add_postamble(src)
1171
+ src << "\n" unless src[-1] == ?\n
1172
+ src << "_buf.to_s\n"
1173
+ end
1174
+
1175
+ end
1176
+
1177
+
1178
+ ##
1179
+ ## use StringIO class for buffering
1180
+ ##
1181
+ ## this is only for Eruby.
1182
+ ##
1183
+ module StringIOEnhancer # :nodoc:
1184
+
1185
+ def self.desc # :nodoc:
1186
+ "use a StringIO object for buffering"
1187
+ end
1188
+
1189
+ def add_preamble(src)
1190
+ src << "_buf = StringIO.new;"
1191
+ end
1192
+
1193
+ def add_postamble(src)
1194
+ src << "\n" unless src[-1] == ?\n
1195
+ src << "_buf.string\n"
1196
+ end
1197
+
1198
+ end
1199
+
1200
+
1201
+ ##
1202
+ ## set buffer variable name to '_erbout' as well as '_buf'
1203
+ ##
1204
+ ## this is only for Eruby.
1205
+ ##
1206
+ module ErboutEnhancer
1207
+
1208
+ def self.desc # :nodoc:
1209
+ "set '_erbout = _buf = \"\";' to be compatible with ERB."
1210
+ end
1211
+
1212
+ def add_preamble(src)
1213
+ src << "_erbout = _buf = '';"
1214
+ end
1215
+
1216
+ def add_postamble(src)
1217
+ src << "\n" unless src[-1] == ?\n
1218
+ src << "_buf.to_s\n"
1219
+ end
1220
+
1221
+ end
1222
+
1223
+
1224
+ ##
1225
+ ## remove text and leave code, especially useful when debugging.
1226
+ ##
1227
+ ## ex.
1228
+ ## $ erubis -s -E NoText file.eruby | more
1229
+ ##
1230
+ ## this is language independent.
1231
+ ##
1232
+ module NoTextEnhancer
1233
+
1234
+ def self.desc # :nodoc:
1235
+ "remove text and leave code (useful when debugging)"
1236
+ end
1237
+
1238
+ def add_text(src, text)
1239
+ src << ("\n" * text.count("\n"))
1240
+ if text[-1] != ?\n
1241
+ text =~ /^(.*?)\z/
1242
+ src << (' ' * $1.length)
1243
+ end
1244
+ end
1245
+
1246
+ end
1247
+
1248
+
1249
+ ##
1250
+ ## remove code and leave text, especially useful when validating HTML tags.
1251
+ ##
1252
+ ## ex.
1253
+ ## $ erubis -s -E NoCode file.eruby | tidy -errors
1254
+ ##
1255
+ ## this is language independent.
1256
+ ##
1257
+ module NoCodeEnhancer
1258
+
1259
+ def self.desc # :nodoc:
1260
+ "remove code and leave text (useful when validating HTML)"
1261
+ end
1262
+
1263
+ def add_preamble(src)
1264
+ end
1265
+
1266
+ def add_postamble(src)
1267
+ end
1268
+
1269
+ def add_text(src, text)
1270
+ src << text
1271
+ end
1272
+
1273
+ def add_expr(src, code, indicator)
1274
+ src << "\n" * code.count("\n")
1275
+ end
1276
+
1277
+ def add_stmt(src, code)
1278
+ src << "\n" * code.count("\n")
1279
+ end
1280
+
1281
+ end
1282
+
1283
+
1284
+ ##
1285
+ ## get convert faster, but spaces around '<%...%>' are not trimmed.
1286
+ ##
1287
+ ## this is language-independent.
1288
+ ##
1289
+ module SimplifyEnhancer
1290
+
1291
+ def self.desc # :nodoc:
1292
+ "get convert faster but leave spaces around '<% %>'"
1293
+ end
1294
+
1295
+ #DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
1296
+ SIMPLE_REGEXP = /<%(=+|\#)?(.*?)-?%>/m
1297
+
1298
+ def convert(input)
1299
+ src = ""
1300
+ add_preamble(src)
1301
+ #regexp = pattern_regexp(@pattern)
1302
+ pos = 0
1303
+ input.scan(SIMPLE_REGEXP) do |indicator, code|
1304
+ match = Regexp.last_match
1305
+ index = match.begin(0)
1306
+ text = input[pos, index - pos]
1307
+ pos = match.end(0)
1308
+ add_text(src, text)
1309
+ if !indicator # <% %>
1310
+ add_stmt(src, code)
1311
+ elsif indicator[0] == ?\# # <%# %>
1312
+ n = code.count("\n")
1313
+ add_stmt(src, "\n" * n)
1314
+ else # <%= %>
1315
+ add_expr(src, code, indicator)
1316
+ end
1317
+ end
1318
+ rest = $' || input
1319
+ add_text(src, rest)
1320
+ add_postamble(src)
1321
+ return src
1322
+ end
1323
+
1324
+ end
1325
+
1326
+
1327
+ ##
1328
+ ## enable to use other embedded expression pattern (default is '\[= =\]').
1329
+ ##
1330
+ ## notice! this is an experimental. spec may change in the future.
1331
+ ##
1332
+ ## ex.
1333
+ ## input = <<END
1334
+ ## <% for item in list %>
1335
+ ## <%= item %> : <%== item %>
1336
+ ## [= item =] : [== item =]
1337
+ ## <% end %>
1338
+ ## END
1339
+ ##
1340
+ ## class BiPatternEruby
1341
+ ## include BiPatternEnhancer
1342
+ ## end
1343
+ ## eruby = BiPatternEruby.new(input, :bipattern=>'\[= =\]')
1344
+ ## list = ['<a>', 'b&b', '"c"']
1345
+ ## print eruby.result(binding())
1346
+ ##
1347
+ ## ## output
1348
+ ## <a> : &lt;a&gt;
1349
+ ## <a> : &lt;a&gt;
1350
+ ## b&b : b&amp;b
1351
+ ## b&b : b&amp;b
1352
+ ## "c" : &quot;c&quot;
1353
+ ## "c" : &quot;c&quot;
1354
+ ##
1355
+ ## this is language independent.
1356
+ ##
1357
+ module BiPatternEnhancer
1358
+
1359
+ def self.desc # :nodoc:
1360
+ "another embedded expression pattern (default '\[= =\]')."
1361
+ end
1362
+
1363
+ def initialize(input, properties={})
1364
+ self.bipattern = properties[:bipattern] # or '\$\{ \}'
1365
+ super
1366
+ end
1367
+
1368
+ ## when pat is nil then '\[= =\]' is used
1369
+ def bipattern=(pat) # :nodoc:
1370
+ @bipattern = pat || '\[= =\]'
1371
+ pre, post = @bipattern.split()
1372
+ @bipattern_regexp = /(.*?)#{pre}(=*)(.*?)#{post}/m
1373
+ end
1374
+
1375
+ def add_text(src, text)
1376
+ return unless text
1377
+ text.scan(@bipattern_regexp) do |txt, indicator, code|
1378
+ super(src, txt)
1379
+ add_expr(src, code, '=' + indicator)
1380
+ end
1381
+ rest = $' || text
1382
+ super(src, rest)
1383
+ end
1384
+
1385
+ end
1386
+
1387
+
1388
+ ##
1389
+ ## regards lines starting with '%' as program code
1390
+ ##
1391
+ ## this is for compatibility to eruby and ERB.
1392
+ ##
1393
+ ## this is language-independent.
1394
+ ##
1395
+ module PercentLineEnhancer
1396
+
1397
+ def self.desc # :nodoc:
1398
+ "regard lines starting with '%' as program code"
1399
+ end
1400
+
1401
+ PERCENT_LINE_PATTERN = /(.*?)^\%(.*?\r?\n)/m
1402
+
1403
+ def add_text(src, text)
1404
+ text.scan(PERCENT_LINE_PATTERN) do |txt, line|
1405
+ super(src, txt)
1406
+ if line[0] == ?%
1407
+ super(src, line)
1408
+ else
1409
+ add_stmt(src, line)
1410
+ end
1411
+ end
1412
+ rest = $' || text
1413
+ super(src, rest)
1414
+ end
1415
+
1416
+ end
1417
+
1418
+
1419
+ ##
1420
+ ## [experimental] allow header and footer in eRuby script
1421
+ ##
1422
+ ## ex.
1423
+ ## ====================
1424
+ ## ## without header and footer
1425
+ ## $ cat ex1.eruby
1426
+ ## <% def list_items(list) %>
1427
+ ## <% for item in list %>
1428
+ ## <li><%= item %></li>
1429
+ ## <% end %>
1430
+ ## <% end %>
1431
+ ##
1432
+ ## $ erubis -s ex1.eruby
1433
+ ## _buf = []; def list_items(list)
1434
+ ## ; for item in list
1435
+ ## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
1436
+ ## '; end
1437
+ ## ; end
1438
+ ## ;
1439
+ ## _buf.join
1440
+ ##
1441
+ ## ## with header and footer
1442
+ ## $ cat ex2.eruby
1443
+ ## <!--#header:
1444
+ ## def list_items(list)
1445
+ ## #-->
1446
+ ## <% for item in list %>
1447
+ ## <li><%= item %></li>
1448
+ ## <% end %>
1449
+ ## <!--#footer:
1450
+ ## end
1451
+ ## #-->
1452
+ ##
1453
+ ## $ erubis -s -c HeaderFooterEruby ex4.eruby
1454
+ ##
1455
+ ## def list_items(list)
1456
+ ## _buf = []; _buf << '
1457
+ ## '; for item in list
1458
+ ## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
1459
+ ## '; end
1460
+ ## ; _buf << '
1461
+ ## ';
1462
+ ## _buf.join
1463
+ ## end
1464
+ ##
1465
+ ## ====================
1466
+ ##
1467
+ ## this is language-independent.
1468
+ ##
1469
+ module HeaderFooterEnhancer
1470
+
1471
+ def self.desc # :nodoc:
1472
+ "allow header/footer in document (ex. '<!--#header: #-->')"
1473
+ end
1474
+
1475
+ HEADER_FOOTER_PATTERN = /(.*?)(^[ \t]*)?<!--\#(\w+):(.*?)\#-->([ \t]*\r?\n)?/m
1476
+
1477
+ def add_text(src, text)
1478
+ text.scan(HEADER_FOOTER_PATTERN) do |txt, lspace, word, content, rspace|
1479
+ flag_trim = @trim && lspace && rspace
1480
+ super(src, txt)
1481
+ content = "#{lspace}#{content}#{rspace}" if flag_trim
1482
+ super(src, lspace) if !flag_trim && lspace
1483
+ instance_variable_set("@#{word}", content)
1484
+ super(src, rspace) if !flag_trim && rspace
1485
+ end
1486
+ rest = $' || text
1487
+ super(src, rest)
1488
+ end
1489
+
1490
+ attr_accessor :header, :footer
1491
+
1492
+ def convert(input)
1493
+ source = super
1494
+ return @src = "#{@header}#{source}#{@footer}"
1495
+ end
1496
+
1497
+ end
1498
+
1499
+
1500
+ ##
1501
+ ## delete indentation of HTML.
1502
+ ##
1503
+ ## this is language-independent.
1504
+ ##
1505
+ module DeleteIndentEnhancer
1506
+
1507
+ def self.desc # :nodoc:
1508
+ "delete indentation of HTML."
1509
+ end
1510
+
1511
+ def convert_input(src, input)
1512
+ input = input.gsub(/^[ \t]+</, '<')
1513
+ super(src, input)
1514
+ end
1515
+
1516
+ end
1517
+
1518
+
1519
+ ##
1520
+ ## convert "<h1><%=title%></h1>" into "_buf << %Q`<h1>#{title}</h1>`"
1521
+ ##
1522
+ ## this is only for Eruby.
1523
+ ##
1524
+ module InterpolationEnhancer
1525
+
1526
+ def self.desc # :nodoc:
1527
+ "convert '<p><%=text%></p>' into '_buf << %Q`<p>\#{text}</p>`'"
1528
+ end
1529
+
1530
+ def convert_input(src, input)
1531
+ pat = @pattern
1532
+ regexp = pat.nil? || pat == '<% %>' ? Basic::Converter::DEFAULT_REGEXP : pattern_regexp(pat)
1533
+ pos = 0
1534
+ is_bol = true # is beginning of line
1535
+ str = ''
1536
+ input.scan(regexp) do |indicator, code, rspace|
1537
+ match = Regexp.last_match()
1538
+ len = match.begin(0) - pos
1539
+ text = input[pos, len]
1540
+ pos = match.end(0)
1541
+ ch = indicator ? indicator[0] : nil
1542
+ lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
1543
+ is_bol = rspace ? true : false
1544
+ _add_text_to_str(str, text)
1545
+ ## * when '<%= %>', do nothing
1546
+ ## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
1547
+ if ch == ?= # <%= %>
1548
+ str << lspace if lspace
1549
+ add_expr(str, code, indicator)
1550
+ str << rspace if rspace
1551
+ elsif ch == ?\# # <%# %>
1552
+ n = code.count("\n") + (rspace ? 1 : 0)
1553
+ if @trim && lspace && rspace
1554
+ add_text(src, str)
1555
+ str = ''
1556
+ add_stmt(src, "\n" * n)
1557
+ else
1558
+ str << lspace if lspace
1559
+ add_text(src, str)
1560
+ str = ''
1561
+ add_stmt(src, "\n" * n)
1562
+ str << rspace if rspace
1563
+ end
1564
+ else # <% %>
1565
+ if @trim && lspace && rspace
1566
+ add_text(src, str)
1567
+ str = ''
1568
+ add_stmt(src, "#{lspace}#{code}#{rspace}")
1569
+ else
1570
+ str << lspace if lspace
1571
+ add_text(src, str)
1572
+ str = ''
1573
+ add_stmt(src, code)
1574
+ str << rspace if rspace
1575
+ end
1576
+ end
1577
+ end
1578
+ rest = $' || input # add input when no matched
1579
+ _add_text_to_str(str, rest)
1580
+ add_text(src, str)
1581
+ end
1582
+
1583
+ def add_text(src, text)
1584
+ return if !text || text.empty?
1585
+ #src << " _buf << %Q`" << text << "`;"
1586
+ if text[-1] == ?\n
1587
+ text[-1] = "\\n"
1588
+ src << " _buf << %Q`" << text << "`\n"
1589
+ else
1590
+ src << " _buf << %Q`" << text << "`;"
1591
+ end
1592
+ end
1593
+
1594
+ def _add_text_to_str(str, text)
1595
+ return if !text || text.empty?
1596
+ text.gsub!(/['\#\\]/, '\\\\\&')
1597
+ str << text
1598
+ end
1599
+
1600
+ def add_expr_escaped(str, code)
1601
+ str << "\#{#{escaped_expr(code)}}"
1602
+ end
1603
+
1604
+ def add_expr_literal(str, code)
1605
+ str << "\#{#{code}}"
1606
+ end
1607
+
1608
+ end
1609
+
1610
+
1611
+ end
1612
+ #--end of require 'erubis/enhancer'
1613
+ #require 'erubis/tiny'
1614
+ #--begin of require 'erubis/engine/eruby'
1615
+ ##
1616
+ ## $Rev: 77 $
1617
+ ## $Release: 2.4.0 $
1618
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
1619
+ ##
1620
+
1621
+ #--already included require 'erubis/engine'
1622
+ #--already included require 'erubis/enhancer'
1623
+
1624
+
1625
+ module Erubis
1626
+
1627
+
1628
+ ##
1629
+ ## code generator for Ruby
1630
+ ##
1631
+ module RubyGenerator
1632
+ include Generator
1633
+ #include ArrayBufferEnhancer
1634
+ include StringBufferEnhancer
1635
+
1636
+ def init_generator(properties={})
1637
+ super
1638
+ @escapefunc ||= "Erubis::XmlHelper.escape_xml"
1639
+ end
1640
+
1641
+ def self.supported_properties() # :nodoc:
1642
+ return []
1643
+ end
1644
+
1645
+ def escape_text(text)
1646
+ text.gsub(/['\\]/, '\\\\\&') # "'" => "\\'", '\\' => '\\\\'
1647
+ end
1648
+
1649
+ def escaped_expr(code)
1650
+ return "#{@escapefunc}(#{code})"
1651
+ end
1652
+
1653
+ #--
1654
+ #def add_preamble(src)
1655
+ # src << "_buf = [];"
1656
+ #end
1657
+ #++
1658
+
1659
+ def add_text(src, text)
1660
+ src << " _buf << '" << escape_text(text) << "';" unless text.empty?
1661
+ end
1662
+
1663
+ def add_stmt(src, code)
1664
+ #src << code << ';'
1665
+ src << code
1666
+ src << ';' unless code[-1] == ?\n
1667
+ end
1668
+
1669
+ def add_expr_literal(src, code)
1670
+ src << ' _buf << (' << code << ').to_s;'
1671
+ end
1672
+
1673
+ def add_expr_escaped(src, code)
1674
+ src << ' _buf << ' << escaped_expr(code) << ';'
1675
+ end
1676
+
1677
+ def add_expr_debug(src, code)
1678
+ code.strip!
1679
+ s = (code.dump =~ /\A"(.*)"\z/) && $1
1680
+ src << ' $stderr.puts("*** debug: ' << s << '=#{(' << code << ').inspect}");'
1681
+ end
1682
+
1683
+ #--
1684
+ #def add_postamble(src)
1685
+ # src << "\n_buf.join\n"
1686
+ #end
1687
+ #++
1688
+
1689
+ end
1690
+
1691
+
1692
+ ##
1693
+ ## engine for Ruby
1694
+ ##
1695
+ class Eruby < Basic::Engine
1696
+ include RubyEvaluator
1697
+ include RubyGenerator
1698
+ end
1699
+
1700
+
1701
+ ##
1702
+ ## fast engine for Ruby
1703
+ ##
1704
+ class FastEruby < Eruby
1705
+ include InterpolationEnhancer
1706
+ end
1707
+
1708
+
1709
+ ##
1710
+ ## swtich '<%= %>' to escaped and '<%== %>' to not escaped
1711
+ ##
1712
+ class EscapedEruby < Eruby
1713
+ include EscapeEnhancer
1714
+ end
1715
+
1716
+
1717
+ ##
1718
+ ## sanitize expression (<%= ... %>) by default
1719
+ ##
1720
+ ## this is equivalent to EscapedEruby and is prepared only for compatibility.
1721
+ ##
1722
+ class XmlEruby < Eruby
1723
+ include EscapeEnhancer
1724
+ end
1725
+
1726
+
1727
+ class PI::Eruby < PI::Engine
1728
+ include RubyEvaluator
1729
+ include RubyGenerator
1730
+
1731
+ def init_converter(properties={})
1732
+ @pi = 'rb'
1733
+ super(properties)
1734
+ end
1735
+
1736
+ end
1737
+
1738
+
1739
+ end
1740
+ #--end of require 'erubis/engine/eruby'
1741
+ #require 'erubis/engine/enhanced' # enhanced eruby engines
1742
+ #require 'erubis/engine/optimized' # generates optimized ruby code
1743
+ #require 'erubis/engine/ephp'
1744
+ #require 'erubis/engine/ec'
1745
+ #require 'erubis/engine/ejava'
1746
+ #require 'erubis/engine/escheme'
1747
+ #require 'erubis/engine/eperl'
1748
+ #require 'erubis/engine/ejavascript'
1749
+
1750
+ #--begin of require 'erubis/local-setting'
1751
+ ##
1752
+ ## $Rev: 77 $
1753
+ ## $Release: 2.4.0 $
1754
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
1755
+ ##
1756
+
1757
+ ##
1758
+ ## you can add site-local settings here.
1759
+ ## this files is required by erubis.rb
1760
+ ##
1761
+ #--end of require 'erubis/local-setting'
1762
+ #--end of require 'erubis'
1763
+ #--begin of require 'erubis/tiny'
1764
+ ##
1765
+ ## $Rev: 77 $
1766
+ ## $Release: 2.4.0 $
1767
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
1768
+ ##
1769
+
1770
+ module Erubis
1771
+
1772
+ ##
1773
+ ## tiny and the simplest implementation of eRuby
1774
+ ##
1775
+ ## ex.
1776
+ ## eruby = TinyEruby.new(File.read('example.rhtml'))
1777
+ ## print eruby.src # print ruby code
1778
+ ## print eruby.result(binding()) # eval ruby code with Binding object
1779
+ ## print eruby.evalute(context) # eval ruby code with context object
1780
+ ##
1781
+ class TinyEruby
1782
+
1783
+ def initialize(input=nil)
1784
+ @src = convert(input) if input
1785
+ end
1786
+ attr_reader :src
1787
+
1788
+ EMBEDDED_PATTERN = /<%(=+|\#)?(.*?)-?%>/m
1789
+
1790
+ def convert(input)
1791
+ src = "_buf = '';" # preamble
1792
+ pos = 0
1793
+ input.scan(EMBEDDED_PATTERN) do |indicator, code|
1794
+ match = Regexp.last_match
1795
+ len = match.begin(0) - pos
1796
+ text = input[pos, len]
1797
+ pos = match.end(0)
1798
+ #src << " _buf << '" << escape_text(text) << "';"
1799
+ text.gsub!(/['\\]/, '\\\\\&')
1800
+ src << " _buf << '" << text << "';" unless text.empty?
1801
+ if !indicator # <% %>
1802
+ src << code << ";"
1803
+ elsif indicator == '#' # <%# %>
1804
+ src << ("\n" * code.count("\n"))
1805
+ else # <%= %>
1806
+ src << " _buf << (" << code << ").to_s;"
1807
+ end
1808
+ end
1809
+ rest = $' || input
1810
+ #src << " _buf << '" << escape_text(rest) << "';"
1811
+ rest.gsub!(/['\\]/, '\\\\\&')
1812
+ src << " _buf << '" << rest << "';" unless rest.empty?
1813
+ src << "\n_buf.to_s\n" # postamble
1814
+ return src
1815
+ end
1816
+
1817
+ #def escape_text(text)
1818
+ # return text.gsub!(/['\\]/, '\\\\\&') || text
1819
+ #end
1820
+
1821
+ def result(_binding=TOPLEVEL_BINDING)
1822
+ eval @src, _binding
1823
+ end
1824
+
1825
+ def evaluate(_context=Object.new)
1826
+ if _context.is_a?(Hash)
1827
+ _obj = Object.new
1828
+ _context.each do |k, v| _obj.instance_variable_set("@#{k}", v) end
1829
+ _context = _obj
1830
+ end
1831
+ _context.instance_eval @src
1832
+ end
1833
+
1834
+ end
1835
+
1836
+
1837
+
1838
+ module PI
1839
+ end
1840
+
1841
+ class PI::TinyEruby
1842
+
1843
+ def initialize(input=nil, options={})
1844
+ @escape = options[:escape] || 'Erubis::XmlHelper.escape_xml'
1845
+ @src = convert(input) if input
1846
+ end
1847
+
1848
+ attr_reader :src
1849
+
1850
+ EMBEDDED_PATTERN = /(^[ \t]*)?<\?rb(\s.*?)\?>([ \t]*\r?\n)?|@(!+)?\{(.*?)\}@/m
1851
+
1852
+ def convert(input)
1853
+ src = "_buf = '';" # preamble
1854
+ pos = 0
1855
+ input.scan(EMBEDDED_PATTERN) do |lspace, stmt, rspace, indicator, expr|
1856
+ match = Regexp.last_match
1857
+ len = match.begin(0) - pos
1858
+ text = input[pos, len]
1859
+ pos = match.end(0)
1860
+ #src << " _buf << '" << escape_text(text) << "';"
1861
+ text.gsub!(/['\\]/, '\\\\\&')
1862
+ src << " _buf << '" << text << "';" unless text.empty?
1863
+ if stmt # <?rb ... ?>
1864
+ if lspace && rspace
1865
+ src << "#{lspace}#{stmt}#{rspace}"
1866
+ else
1867
+ src << " _buf << '" << lspace << "';" if lspace
1868
+ src << stmt << ";"
1869
+ src << " _buf << '" << rspace << "';" if rspace
1870
+ end
1871
+ else # ${...}, $!{...}
1872
+ if !indicator
1873
+ src << " _buf << " << @escape << "(" << expr << ");"
1874
+ elsif indicator == '!'
1875
+ src << " _buf << (" << expr << ").to_s;"
1876
+ end
1877
+ end
1878
+ end
1879
+ rest = $' || input
1880
+ #src << " _buf << '" << escape_text(rest) << "';"
1881
+ rest.gsub!(/['\\]/, '\\\\\&')
1882
+ src << " _buf << '" << rest << "';" unless rest.empty?
1883
+ src << "\n_buf.to_s\n" # postamble
1884
+ return src
1885
+ end
1886
+
1887
+ #def escape_text(text)
1888
+ # return text.gsub!(/['\\]/, '\\\\\&') || text
1889
+ #end
1890
+
1891
+ def result(_binding=TOPLEVEL_BINDING)
1892
+ eval @src, _binding
1893
+ end
1894
+
1895
+ def evaluate(_context=Object.new)
1896
+ if _context.is_a?(Hash)
1897
+ _obj = Object.new
1898
+ _context.each do |k, v| _obj.instance_variable_set("@#{k}", v) end
1899
+ _context = _obj
1900
+ end
1901
+ _context.instance_eval @src
1902
+ end
1903
+
1904
+ end
1905
+
1906
+
1907
+ end
1908
+ #--end of require 'erubis/tiny'
1909
+ #--begin of require 'erubis/engine/enhanced'
1910
+ ##
1911
+ ## $Rev: 77 $
1912
+ ## $Release: 2.4.0 $
1913
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
1914
+ ##
1915
+
1916
+ #--already included require 'erubis/enhancer'
1917
+ #--already included require 'erubis/engine/eruby'
1918
+
1919
+
1920
+ module Erubis
1921
+
1922
+
1923
+ #--
1924
+ ## moved to engine/ruby.rb
1925
+ #class EscapedEruby < Eruby
1926
+ # include EscapeEnhancer
1927
+ #end
1928
+ #++
1929
+
1930
+
1931
+ #--
1932
+ ### (obsolete)
1933
+ #class FastEruby < Eruby
1934
+ # include FastEnhancer
1935
+ #end
1936
+ #++
1937
+
1938
+
1939
+ class StdoutEruby < Eruby
1940
+ include StdoutEnhancer
1941
+ end
1942
+
1943
+
1944
+ class PrintOutEruby < Eruby
1945
+ include PrintOutEnhancer
1946
+ end
1947
+
1948
+
1949
+ class PrintEnabledEruby < Eruby
1950
+ include PrintEnabledEnhancer
1951
+ end
1952
+
1953
+
1954
+ class ArrayEruby < Eruby
1955
+ include ArrayEnhancer
1956
+ end
1957
+
1958
+
1959
+ class ArrayBufferEruby < Eruby
1960
+ include ArrayBufferEnhancer
1961
+ end
1962
+
1963
+
1964
+ class StringBufferEruby < Eruby
1965
+ include StringBufferEnhancer
1966
+ end
1967
+
1968
+
1969
+ class StringIOEruby < Eruby
1970
+ include StringIOEnhancer
1971
+ end
1972
+
1973
+
1974
+ class ErboutEruby < Eruby
1975
+ include ErboutEnhancer
1976
+ end
1977
+
1978
+
1979
+ class NoTextEruby < Eruby
1980
+ include NoTextEnhancer
1981
+ end
1982
+
1983
+
1984
+ class NoCodeEruby < Eruby
1985
+ include NoCodeEnhancer
1986
+ end
1987
+
1988
+
1989
+ class SimplifiedEruby < Eruby
1990
+ include SimplifyEnhancer
1991
+ end
1992
+
1993
+
1994
+ class StdoutSimplifiedEruby < Eruby
1995
+ include StdoutEnhancer
1996
+ include SimplifyEnhancer
1997
+ end
1998
+
1999
+
2000
+ class PrintOutSimplifiedEruby < Eruby
2001
+ include PrintOutEnhancer
2002
+ include SimplifyEnhancer
2003
+ end
2004
+
2005
+
2006
+ class BiPatternEruby < Eruby
2007
+ include BiPatternEnhancer
2008
+ end
2009
+
2010
+
2011
+ class PercentLineEruby < Eruby
2012
+ include PercentLineEnhancer
2013
+ end
2014
+
2015
+
2016
+ class HeaderFooterEruby < Eruby
2017
+ include HeaderFooterEnhancer
2018
+ end
2019
+
2020
+
2021
+ class DeleteIndentEruby < Eruby
2022
+ include DeleteIndentEnhancer
2023
+ end
2024
+
2025
+
2026
+ class InterpolationEruby < Eruby
2027
+ include InterpolationEnhancer
2028
+ end
2029
+
2030
+
2031
+ end
2032
+ #--end of require 'erubis/engine/enhanced'
2033
+ #--begin of require 'erubis/engine/optimized'
2034
+ ##
2035
+ ## $Rev: 77 $
2036
+ ## $Release: 2.4.0 $
2037
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
2038
+ ##
2039
+
2040
+
2041
+ #--already included require 'erubis/engine/eruby'
2042
+
2043
+
2044
+ module Erubis
2045
+
2046
+
2047
+ module OptimizedGenerator
2048
+ include Generator
2049
+
2050
+ def self.supported_properties() # :nodoc:
2051
+ return []
2052
+ end
2053
+
2054
+ def init_generator(properties={})
2055
+ super
2056
+ @escapefunc ||= "Erubis::XmlHelper.escape_xml"
2057
+ @initialized = false
2058
+ @prev_is_expr = false
2059
+ end
2060
+
2061
+ protected
2062
+
2063
+ def escape_text(text)
2064
+ text.gsub(/['\\]/, '\\\\\&') # "'" => "\\'", '\\' => '\\\\'
2065
+ end
2066
+
2067
+ def escaped_expr(code)
2068
+ @escapefunc ||= 'Erubis::XmlHelper.escape_xml'
2069
+ return "#{@escapefunc}(#{code})"
2070
+ end
2071
+
2072
+ def switch_to_expr(src)
2073
+ return if @prev_is_expr
2074
+ @prev_is_expr = true
2075
+ src << ' _buf'
2076
+ end
2077
+
2078
+ def switch_to_stmt(src)
2079
+ return unless @prev_is_expr
2080
+ @prev_is_expr = false
2081
+ src << ';'
2082
+ end
2083
+
2084
+ def add_preamble(src)
2085
+ #@initialized = false
2086
+ #@prev_is_expr = false
2087
+ end
2088
+
2089
+ def add_text(src, text)
2090
+ return if text.empty?
2091
+ if @initialized
2092
+ switch_to_expr(src)
2093
+ src << " << '" << escape_text(text) << "'"
2094
+ else
2095
+ src << "_buf = '" << escape_text(text) << "';"
2096
+ @initialized = true
2097
+ end
2098
+ end
2099
+
2100
+ def add_stmt(src, code)
2101
+ switch_to_stmt(src) if @initialized
2102
+ #super
2103
+ src << code
2104
+ src << ';' unless code[-1] == ?\n
2105
+ end
2106
+
2107
+ def add_expr_literal(src, code)
2108
+ unless @initialized; src << "_buf = ''"; @initialized = true; end
2109
+ switch_to_expr(src)
2110
+ src << " << (" << code << ").to_s"
2111
+ end
2112
+
2113
+ def add_expr_escaped(src, code)
2114
+ unless @initialized; src << "_buf = ''"; @initialized = true; end
2115
+ switch_to_expr(src)
2116
+ src << " << " << escaped_expr(code)
2117
+ end
2118
+
2119
+ def add_expr_debug(src, code)
2120
+ code.strip!
2121
+ s = (code.dump =~ /\A"(.*)"\z/) && $1
2122
+ src << ' $stderr.puts("*** debug: ' << s << '=#{(' << code << ').inspect}");'
2123
+ end
2124
+
2125
+ def add_postamble(src)
2126
+ #super if @initialized
2127
+ src << "\n_buf\n" if @initialized
2128
+ end
2129
+
2130
+ end # end of class OptimizedEruby
2131
+
2132
+
2133
+ ##
2134
+ ## Eruby class which generates optimized ruby code
2135
+ ##
2136
+ class OptimizedEruby < Basic::Engine # Eruby
2137
+ include RubyEvaluator
2138
+ include OptimizedGenerator
2139
+
2140
+ def init_converter(properties={})
2141
+ @pi = 'rb'
2142
+ super(properties)
2143
+ end
2144
+
2145
+ end
2146
+
2147
+
2148
+ ##
2149
+ ## XmlEruby class which generates optimized ruby code
2150
+ ##
2151
+ class OptimizedXmlEruby < OptimizedEruby
2152
+ include EscapeEnhancer
2153
+
2154
+ def add_expr_debug(src, code)
2155
+ switch_to_stmt(src) if indicator == '===' && !@initialized
2156
+ super
2157
+ end
2158
+
2159
+ end # end of class OptimizedXmlEruby
2160
+
2161
+ end
2162
+ #--end of require 'erubis/engine/optimized'
2163
+ #--already included require 'erubis/engine/eruby'
2164
+ #--begin of require 'erubis/engine/ephp'
2165
+ ##
2166
+ ## $Rev: 77 $
2167
+ ## $Release: 2.4.0 $
2168
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
2169
+ ##
2170
+
2171
+ #--already included require 'erubis/engine'
2172
+ #--already included require 'erubis/enhancer'
2173
+
2174
+
2175
+ module Erubis
2176
+
2177
+
2178
+ module PhpGenerator
2179
+ include Generator
2180
+
2181
+ def self.supported_properties() # :nodoc:
2182
+ return []
2183
+ end
2184
+
2185
+ def init_generator(properties={})
2186
+ super
2187
+ @escapefunc ||= 'htmlspecialchars'
2188
+ end
2189
+
2190
+ def add_preamble(src)
2191
+ # empty
2192
+ end
2193
+
2194
+ def escape_text(text)
2195
+ return text.gsub!(/<\?xml\b/, '<<?php ?>?xml') || text
2196
+ end
2197
+
2198
+ def add_text(src, text)
2199
+ src << escape_text(text)
2200
+ end
2201
+
2202
+ def add_expr_literal(src, code)
2203
+ code.strip!
2204
+ src << "<?php echo #{code}; ?>"
2205
+ end
2206
+
2207
+ def add_expr_escaped(src, code)
2208
+ add_expr_literal(src, escaped_expr(code))
2209
+ end
2210
+
2211
+ def add_expr_debug(src, code)
2212
+ code.strip!
2213
+ s = code.gsub(/\'/, "\\'")
2214
+ src << "<?php error_log('*** debug: #{s}='.(#{code}), 0); ?>"
2215
+ end
2216
+
2217
+ def add_stmt(src, code)
2218
+ src << "<?php"
2219
+ src << " " if code[0] != ?\ #
2220
+ if code[-1] == ?\n
2221
+ code.chomp!
2222
+ src << code << "?>\n"
2223
+ else
2224
+ src << code << "?>"
2225
+ end
2226
+ end
2227
+
2228
+ def add_postamble(src)
2229
+ # empty
2230
+ end
2231
+
2232
+ end
2233
+
2234
+
2235
+ ##
2236
+ ## engine for PHP
2237
+ ##
2238
+ class Ephp < Basic::Engine
2239
+ include PhpGenerator
2240
+ end
2241
+
2242
+
2243
+ class EscapedEphp < Ephp
2244
+ include EscapeEnhancer
2245
+ end
2246
+
2247
+
2248
+ #class XmlEphp < Ephp
2249
+ # include EscapeEnhancer
2250
+ #end
2251
+
2252
+
2253
+ class PI::Ephp < PI::Engine
2254
+ include PhpGenerator
2255
+
2256
+ def init_converter(properties={})
2257
+ @pi = 'php'
2258
+ super(properties)
2259
+ end
2260
+
2261
+ end
2262
+
2263
+
2264
+ end
2265
+ #--end of require 'erubis/engine/ephp'
2266
+ #--begin of require 'erubis/engine/ec'
2267
+ ##
2268
+ ## $Rev: 77 $
2269
+ ## $Release: 2.4.0 $
2270
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
2271
+ ##
2272
+
2273
+ #--already included require 'erubis/engine'
2274
+ #--already included require 'erubis/enhancer'
2275
+
2276
+
2277
+ module Erubis
2278
+
2279
+
2280
+ module CGenerator
2281
+ include Generator
2282
+
2283
+ def self.supported_properties() # :nodoc:
2284
+ return [
2285
+ [:indent, '', "indent spaces (ex. ' ')"],
2286
+ [:out, 'stdout', "output file pointer name"],
2287
+ ]
2288
+ end
2289
+
2290
+ def init_generator(properties={})
2291
+ super
2292
+ @escapefunc ||= "escape"
2293
+ @indent = properties[:indent] || ''
2294
+ @out = properties[:out] || 'stdout'
2295
+ end
2296
+
2297
+ def add_preamble(src)
2298
+ src << "#line 1 \"#{self.filename}\"\n" if self.filename
2299
+ end
2300
+
2301
+ def escape_text(text)
2302
+ @@table_ ||= { "\r"=>"\\r", "\n"=>"\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" }
2303
+ text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] }
2304
+ return text
2305
+ end
2306
+
2307
+ def escaped_expr(code)
2308
+ return "#{@escapefunc}(#{code.strip}, #{@out})"
2309
+ end
2310
+
2311
+ def add_text(src, text)
2312
+ return if text.empty?
2313
+ src << (src.empty? || src[-1] == ?\n ? @indent : ' ')
2314
+ src << "fputs("
2315
+ i = 0
2316
+ text.each_line do |line|
2317
+ src << "\n" << @indent << ' ' if i > 0
2318
+ i += 1
2319
+ src << '"' << escape_text(line) << '"'
2320
+ end
2321
+ src << ", #{@out});" #<< (text[-1] == ?\n ? "\n" : "")
2322
+ src << "\n" if text[-1] == ?\n
2323
+ end
2324
+
2325
+ def add_stmt(src, code)
2326
+ src << code
2327
+ end
2328
+
2329
+ def add_expr_literal(src, code)
2330
+ src << @indent if src.empty? || src[-1] == ?\n
2331
+ src << " fprintf(#{@out}, " << code.strip << ');'
2332
+ end
2333
+
2334
+ def add_expr_escaped(src, code)
2335
+ src << @indent if src.empty? || src[-1] == ?\n
2336
+ src << ' ' << escaped_expr(code) << ';'
2337
+ end
2338
+
2339
+ def add_expr_debug(src, code)
2340
+ code.strip!
2341
+ s = nil
2342
+ if code =~ /\A\".*?\"\s*,\s*(.*)/
2343
+ s = $1.gsub(/[%"]/, '\\\1') + '='
2344
+ end
2345
+ src << @indent if src.empty? || src[-1] == ?\n
2346
+ src << " fprintf(stderr, \"*** debug: #{s}\" #{code});"
2347
+ end
2348
+
2349
+ def add_postamble(src)
2350
+ # empty
2351
+ end
2352
+
2353
+ end
2354
+
2355
+
2356
+ ##
2357
+ ## engine for C
2358
+ ##
2359
+ class Ec < Basic::Engine
2360
+ include CGenerator
2361
+ end
2362
+
2363
+
2364
+ class EscapedEc < Ec
2365
+ include EscapeEnhancer
2366
+ end
2367
+
2368
+
2369
+ #class XmlEc < Ec
2370
+ # include EscapeEnhancer
2371
+ #end
2372
+
2373
+ class PI::Ec < PI::Engine
2374
+ include CGenerator
2375
+
2376
+ def init_converter(properties={})
2377
+ @pi = 'c'
2378
+ super(properties)
2379
+ end
2380
+
2381
+ end
2382
+
2383
+
2384
+ end
2385
+ #--end of require 'erubis/engine/ec'
2386
+ #--begin of require 'erubis/engine/ejava'
2387
+ ##
2388
+ ## $Rev: 77 $
2389
+ ## $Release: 2.4.0 $
2390
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
2391
+ ##
2392
+
2393
+ #--already included require 'erubis/engine'
2394
+ #--already included require 'erubis/enhancer'
2395
+
2396
+
2397
+ module Erubis
2398
+
2399
+
2400
+ module JavaGenerator
2401
+ include Generator
2402
+
2403
+ def self.supported_properties() # :nodoc:
2404
+ return [
2405
+ [:indent, '', "indent spaces (ex. ' ')"],
2406
+ [:buf, '_buf', "output buffer name"],
2407
+ [:bufclass, 'StringBuffer', "output buffer class (ex. 'StringBuilder')"],
2408
+ ]
2409
+ end
2410
+
2411
+ def init_generator(properties={})
2412
+ super
2413
+ @escapefunc ||= 'escape'
2414
+ @indent = properties[:indent] || ''
2415
+ @buf = properties[:buf] || '_buf'
2416
+ @bufclass = properties[:bufclass] || 'StringBuffer'
2417
+ end
2418
+
2419
+ def add_preamble(src)
2420
+ src << "#{@indent}#{@bufclass} #{@buf} = new #{@bufclass}();"
2421
+ end
2422
+
2423
+ def escape_text(text)
2424
+ @@table_ ||= { "\r"=>"\\r", "\n"=>"\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" }
2425
+ return text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] } || text
2426
+ end
2427
+
2428
+ def add_text(src, text)
2429
+ return if text.empty?
2430
+ src << (src.empty? || src[-1] == ?\n ? @indent : ' ')
2431
+ src << @buf << ".append("
2432
+ i = 0
2433
+ text.each_line do |line|
2434
+ src << "\n" << @indent << ' + ' if i > 0
2435
+ i += 1
2436
+ src << '"' << escape_text(line) << '"'
2437
+ end
2438
+ src << ");" << (text[-1] == ?\n ? "\n" : "")
2439
+ end
2440
+
2441
+ def add_stmt(src, code)
2442
+ src << code
2443
+ end
2444
+
2445
+ def add_expr_literal(src, code)
2446
+ src << @indent if src.empty? || src[-1] == ?\n
2447
+ code.strip!
2448
+ src << " #{@buf}.append(#{code});"
2449
+ end
2450
+
2451
+ def add_expr_escaped(src, code)
2452
+ add_expr_literal(src, escaped_expr(code))
2453
+ end
2454
+
2455
+ def add_expr_debug(src, code)
2456
+ code.strip!
2457
+ src << @indent if src.empty? || src[-1] == ?\n
2458
+ src << " System.err.println(\"*** debug: #{code}=\"+(#{code}));"
2459
+ end
2460
+
2461
+ def add_postamble(src)
2462
+ src << "\n" if src[-1] == ?;
2463
+ src << @indent << "return " << @buf << ".toString();\n"
2464
+ #src << @indent << "System.out.print(" << @buf << ".toString());\n"
2465
+ end
2466
+
2467
+ end
2468
+
2469
+
2470
+ ##
2471
+ ## engine for Java
2472
+ ##
2473
+ class Ejava < Basic::Engine
2474
+ include JavaGenerator
2475
+ end
2476
+
2477
+
2478
+ class EscapedEjava < Ejava
2479
+ include EscapeEnhancer
2480
+ end
2481
+
2482
+
2483
+ #class XmlEjava < Ejava
2484
+ # include EscapeEnhancer
2485
+ #end
2486
+
2487
+ class PI::Ejava < PI::Engine
2488
+ include JavaGenerator
2489
+
2490
+ def init_converter(properties={})
2491
+ @pi = 'java'
2492
+ super(properties)
2493
+ end
2494
+
2495
+ end
2496
+
2497
+ end
2498
+ #--end of require 'erubis/engine/ejava'
2499
+ #--begin of require 'erubis/engine/escheme'
2500
+ ##
2501
+ ## $Rev: 77 $
2502
+ ## $Release: 2.4.0 $
2503
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
2504
+ ##
2505
+
2506
+ #--already included require 'erubis/engine'
2507
+ #--already included require 'erubis/enhancer'
2508
+
2509
+
2510
+ module Erubis
2511
+
2512
+
2513
+ module SchemeGenerator
2514
+ include Generator
2515
+
2516
+ def self.supported_properties() # :nodoc:
2517
+ return [
2518
+ [:func, '_add', "function name (ex. 'display')"],
2519
+ ]
2520
+ end
2521
+
2522
+ def init_generator(properties={})
2523
+ super
2524
+ @escapefunc ||= 'escape'
2525
+ @func = properties[:func] || '_add' # or 'display'
2526
+ end
2527
+
2528
+ def add_preamble(src)
2529
+ return unless @func == '_add'
2530
+ src << "(let ((_buf '())) " + \
2531
+ "(define (_add x) (set! _buf (cons x _buf))) "
2532
+ #src << "(let* ((_buf '())" + \
2533
+ # " (_add (lambda (x) (set! _buf (cons x _buf))))) "
2534
+ end
2535
+
2536
+ def escape_text(text)
2537
+ @table_ ||= { '"'=>'\\"', '\\'=>'\\\\' }
2538
+ text.gsub!(/["\\]/) { |m| @table_[m] }
2539
+ return text
2540
+ end
2541
+
2542
+ def escaped_expr(code)
2543
+ code.strip!
2544
+ return "(#{@escapefunc} #{code})"
2545
+ end
2546
+
2547
+ def add_text(src, text)
2548
+ return if text.empty?
2549
+ t = escape_text(text)
2550
+ if t[-1] == ?\n
2551
+ t[-1, 1] = ''
2552
+ src << "(#{@func} \"" << t << "\\n\")\n"
2553
+ else
2554
+ src << "(#{@func} \"" << t << '")'
2555
+ end
2556
+ end
2557
+
2558
+ def add_stmt(src, code)
2559
+ src << code
2560
+ end
2561
+
2562
+ def add_expr_literal(src, code)
2563
+ code.strip!
2564
+ src << "(#{@func} #{code})"
2565
+ end
2566
+
2567
+ def add_expr_escaped(src, code)
2568
+ add_expr_literal(src, escaped_expr(code))
2569
+ end
2570
+
2571
+ def add_expr_debug(src, code)
2572
+ s = (code.strip! || code).gsub(/\"/, '\\"')
2573
+ src << "(display \"*** debug: #{s}=\")(display #{code.strip})(display \"\\n\")"
2574
+ end
2575
+
2576
+ def add_postamble(src)
2577
+ return unless @func == '_add'
2578
+ src << "\n" unless src[-1] == ?\n
2579
+ src << " (reverse _buf))\n"
2580
+ end
2581
+
2582
+ end
2583
+
2584
+
2585
+ ##
2586
+ ## engine for Scheme
2587
+ ##
2588
+ class Escheme < Basic::Engine
2589
+ include SchemeGenerator
2590
+ end
2591
+
2592
+
2593
+ class EscapedEscheme < Escheme
2594
+ include EscapeEnhancer
2595
+ end
2596
+
2597
+
2598
+ #class XmlEscheme < Escheme
2599
+ # include EscapeEnhancer
2600
+ #end
2601
+
2602
+
2603
+ class PI::Escheme < PI::Engine
2604
+ include SchemeGenerator
2605
+
2606
+ def init_converter(properties={})
2607
+ @pi = 'scheme'
2608
+ super(properties)
2609
+ end
2610
+
2611
+ end
2612
+
2613
+
2614
+ end
2615
+ #--end of require 'erubis/engine/escheme'
2616
+ #--begin of require 'erubis/engine/eperl'
2617
+ ##
2618
+ ## $Rev: 77 $
2619
+ ## $Release: 2.4.0 $
2620
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
2621
+ ##
2622
+
2623
+ #--already included require 'erubis/engine'
2624
+ #--already included require 'erubis/enhancer'
2625
+
2626
+
2627
+ module Erubis
2628
+
2629
+
2630
+ module PerlGenerator
2631
+ include Generator
2632
+
2633
+ def self.supported_properties() # :nodoc:
2634
+ return [
2635
+ [:func, 'print', "function name"],
2636
+ ]
2637
+ end
2638
+
2639
+ def init_generator(properties={})
2640
+ super
2641
+ @escapefunc ||= 'encode_entities'
2642
+ @func = properties[:func] || 'print'
2643
+ end
2644
+
2645
+ def add_preamble(src)
2646
+ src << "use HTML::Entities; ";
2647
+ end
2648
+
2649
+ def escape_text(text)
2650
+ return text.gsub!(/['\\]/, '\\\\\&') || text
2651
+ end
2652
+
2653
+ def add_text(src, text)
2654
+ src << @func << "('" << escape_text(text) << "'); " unless text.empty?
2655
+ end
2656
+
2657
+ def add_expr_literal(src, code)
2658
+ code.strip!
2659
+ src << @func << "(" << code << "); "
2660
+ end
2661
+
2662
+ def add_expr_escaped(src, code)
2663
+ add_expr_literal(src, escaped_expr(code))
2664
+ end
2665
+
2666
+ def add_expr_debug(src, code)
2667
+ code.strip!
2668
+ s = code.gsub(/\'/, "\\'")
2669
+ src << @func << "('*** debug: #{code}=', #{code}, \"\\n\");"
2670
+ end
2671
+
2672
+ def add_stmt(src, code)
2673
+ src << code
2674
+ end
2675
+
2676
+ def add_postamble(src)
2677
+ src << "\n" unless src[-1] == ?\n
2678
+ end
2679
+
2680
+ end
2681
+
2682
+
2683
+ ##
2684
+ ## engine for Perl
2685
+ ##
2686
+ class Eperl < Basic::Engine
2687
+ include PerlGenerator
2688
+ end
2689
+
2690
+
2691
+ class EscapedEperl < Eperl
2692
+ include EscapeEnhancer
2693
+ end
2694
+
2695
+
2696
+ #class XmlEperl < Eperl
2697
+ # include EscapeEnhancer
2698
+ #end
2699
+
2700
+
2701
+ class PI::Eperl < PI::Engine
2702
+ include PerlGenerator
2703
+
2704
+ def init_converter(properties={})
2705
+ @pi = 'perl'
2706
+ super(properties)
2707
+ end
2708
+
2709
+ end
2710
+
2711
+
2712
+ end
2713
+ #--end of require 'erubis/engine/eperl'
2714
+ #--begin of require 'erubis/engine/ejavascript'
2715
+ ##
2716
+ ## $Rev: 77 $
2717
+ ## $Release: 2.4.0 $
2718
+ ## copyright(c) 2006-2007 kuwata-lab.com all rights reserved.
2719
+ ##
2720
+
2721
+ #--already included require 'erubis/engine'
2722
+ #--already included require 'erubis/enhancer'
2723
+
2724
+
2725
+ module Erubis
2726
+
2727
+
2728
+ module JavascriptGenerator
2729
+ include Generator
2730
+
2731
+ def self.supported_properties() # :nodoc:
2732
+ list = []
2733
+ #list << [:indent, '', "indent spaces (ex. ' ')"]
2734
+ #list << [:buf, '_buf', "output buffer name"]
2735
+ return list
2736
+ end
2737
+
2738
+ def init_generator(properties={})
2739
+ super
2740
+ @escapefunc ||= 'escape'
2741
+ @indent = properties[:indent] || ''
2742
+ @buf = properties[:out] || '_buf'
2743
+ end
2744
+
2745
+ def add_preamble(src)
2746
+ src << "#{@indent}var #{@buf} = [];"
2747
+ end
2748
+
2749
+ def escape_text(text)
2750
+ @@table_ ||= { "\r"=>"\\r", "\n"=>"\\n\\\n", "\t"=>"\\t", '"'=>'\\"', "\\"=>"\\\\" }
2751
+ return text.gsub!(/[\r\n\t"\\]/) { |m| @@table_[m] } || text
2752
+ end
2753
+
2754
+ def add_indent(src, indent)
2755
+ src << (src.empty? || src[-1] == ?\n ? indent : ' ')
2756
+ end
2757
+
2758
+ def add_text(src, text)
2759
+ return if text.empty?
2760
+ add_indent(src, @indent)
2761
+ src << @buf << '.push("'
2762
+ s = escape_text(text)
2763
+ if s[-1] == ?\n
2764
+ s[-2, 2] = ''
2765
+ src << s << "\");\n"
2766
+ else
2767
+ src << s << "\");"
2768
+ end
2769
+ end
2770
+
2771
+ def add_stmt(src, code)
2772
+ src << code
2773
+ end
2774
+
2775
+ def add_expr_literal(src, code)
2776
+ add_indent(src, @indent)
2777
+ code.strip!
2778
+ src << "#{@buf}.push(#{code});"
2779
+ end
2780
+
2781
+ def add_expr_escaped(src, code)
2782
+ add_expr_literal(src, escaped_expr(code))
2783
+ end
2784
+
2785
+ def add_expr_debug(src, code)
2786
+ add_indent(src, @indent)
2787
+ code.strip!
2788
+ src << "alert(\"*** debug: #{code}=\"+(#{code}));"
2789
+ end
2790
+
2791
+ def add_postamble(src)
2792
+ src << "\n" if src[-1] == ?;
2793
+ src << @indent << 'document.write(' << @buf << ".join(\"\"));\n"
2794
+ end
2795
+
2796
+ end
2797
+
2798
+
2799
+ ##
2800
+ ## engine for JavaScript
2801
+ ##
2802
+ class Ejavascript < Basic::Engine
2803
+ include JavascriptGenerator
2804
+ end
2805
+
2806
+
2807
+ class EscapedEjavascript < Ejavascript
2808
+ include EscapeEnhancer
2809
+ end
2810
+
2811
+
2812
+ #class XmlEjavascript < Ejavascript
2813
+ # include EscapeEnhancer
2814
+ #end
2815
+
2816
+
2817
+ class PI::Ejavascript < PI::Engine
2818
+ include JavascriptGenerator
2819
+
2820
+ def init_converter(properties={})
2821
+ @pi = 'js'
2822
+ super(properties)
2823
+ end
2824
+
2825
+ end
2826
+
2827
+
2828
+ end
2829
+ #--end of require 'erubis/engine/ejavascript'
2830
+
2831
+
2832
+ module Erubis
2833
+
2834
+
2835
+ Ejs = Ejavascript
2836
+ EscapedEjs = EscapedEjavascript
2837
+
2838
+
2839
+ class CommandOptionError < ErubisError
2840
+ end
2841
+
2842
+
2843
+ ##
2844
+ ## main class of command
2845
+ ##
2846
+ ## ex.
2847
+ ## Main.main(ARGV)
2848
+ ##
2849
+ class Main
2850
+
2851
+ def self.main(argv=ARGV)
2852
+ status = 0
2853
+ begin
2854
+ Main.new.execute(ARGV)
2855
+ rescue CommandOptionError => ex
2856
+ $stderr.puts ex.message
2857
+ status = 1
2858
+ end
2859
+ exit(status)
2860
+ end
2861
+
2862
+ def initialize
2863
+ @single_options = "hvxztTSbeBXNUC"
2864
+ @arg_options = "pcrfKIlaE" #C
2865
+ @option_names = {
2866
+ ?h => :help,
2867
+ ?v => :version,
2868
+ ?x => :source,
2869
+ ?z => :syntax,
2870
+ ?T => :unexpand,
2871
+ ?t => :untabify, # obsolete
2872
+ ?S => :intern,
2873
+ ?b => :bodyonly,
2874
+ ?B => :binding,
2875
+ ?p => :pattern,
2876
+ ?c => :context,
2877
+ #?C => :class,
2878
+ ?e => :escape,
2879
+ ?r => :requires,
2880
+ ?f => :datafiles,
2881
+ ?K => :kanji,
2882
+ ?I => :includes,
2883
+ ?l => :lang,
2884
+ ?a => :action,
2885
+ ?E => :enhancers,
2886
+ ?X => :notext,
2887
+ ?N => :linenum,
2888
+ ?U => :unique,
2889
+ ?C => :compact,
2890
+ }
2891
+ assert unless @single_options.length + @arg_options.length == @option_names.length
2892
+ (@single_options + @arg_options).each_byte do |ch|
2893
+ assert unless @option_names.key?(ch)
2894
+ end
2895
+ end
2896
+
2897
+
2898
+ def execute(argv=ARGV)
2899
+ ## parse command-line options
2900
+ options, properties = parse_argv(argv, @single_options, @arg_options)
2901
+ filenames = argv
2902
+ options[?h] = true if properties[:help]
2903
+ opts = Object.new
2904
+ arr = @option_names.collect { |ch, name| "def #{name}; @#{name}; end\n" }
2905
+ opts.instance_eval arr.join
2906
+ options.each do |ch, val|
2907
+ name = @option_names[ch]
2908
+ opts.instance_variable_set("@#{name}", val)
2909
+ end
2910
+
2911
+ ## help, version, enhancer list
2912
+ if opts.help || opts.version
2913
+ puts version() if opts.version
2914
+ puts usage() if opts.help
2915
+ puts show_properties() if opts.help
2916
+ puts show_enhancers() if opts.help
2917
+ return
2918
+ end
2919
+
2920
+ ## include path
2921
+ opts.includes.split(/,/).each do |path|
2922
+ $: << path
2923
+ end if opts.includes
2924
+
2925
+ ## require library
2926
+ opts.requires.split(/,/).each do |library|
2927
+ require library
2928
+ end if opts.requires
2929
+
2930
+ ## action
2931
+ action = opts.action
2932
+ action ||= 'syntax' if opts.syntax
2933
+ action ||= 'convert' if opts.source || opts.notext
2934
+
2935
+ ## lang
2936
+ lang = opts.lang || 'ruby'
2937
+ action ||= 'convert' if opts.lang
2938
+
2939
+ ## class name of Eruby
2940
+ #classname = opts.class
2941
+ classname = nil
2942
+ klass = get_classobj(classname, lang, properties[:pi])
2943
+
2944
+ ## kanji code
2945
+ $KCODE = opts.kanji if opts.kanji
2946
+
2947
+ ## read context values from yaml file
2948
+ datafiles = opts.datafiles
2949
+ context = load_datafiles(datafiles, opts)
2950
+
2951
+ ## parse context data
2952
+ if opts.context
2953
+ context = parse_context_data(opts.context, opts)
2954
+ end
2955
+
2956
+ ## properties for engine
2957
+ properties[:escape] = true if opts.escape && !properties.key?(:escape)
2958
+ properties[:pattern] = opts.pattern if opts.pattern
2959
+ #properties[:trim] = false if opts.notrim
2960
+ properties[:preamble] = properties[:postamble] = false if opts.bodyonly
2961
+ properties[:pi] = nil if properties[:pi] == true
2962
+
2963
+ ## create engine and extend enhancers
2964
+ engine = klass.new(nil, properties)
2965
+ enhancers = get_enhancers(opts.enhancers)
2966
+ #enhancers.push(Erubis::EscapeEnhancer) if opts.escape
2967
+ enhancers.each do |enhancer|
2968
+ engine.extend(enhancer)
2969
+ engine.bipattern = properties[:bipattern] if enhancer == Erubis::BiPatternEnhancer
2970
+ end
2971
+
2972
+ ## no-text
2973
+ engine.extend(Erubis::NoTextEnhancer) if opts.notext
2974
+
2975
+ ## convert and execute
2976
+ val = nil
2977
+ msg = "Syntax OK\n"
2978
+ if filenames && !filenames.empty?
2979
+ filenames.each do |filename|
2980
+ test(?f, filename) or raise CommandOptionError.new("#{filename}: file not found.")
2981
+ engine.filename = filename
2982
+ engine.convert!(File.read(filename))
2983
+ val = do_action(action, engine, context, filename, opts)
2984
+ msg = nil if val
2985
+ end
2986
+ else
2987
+ engine.filename = filename = '(stdin)'
2988
+ engine.convert!($stdin.read())
2989
+ val = do_action(action, engine, context, filename, opts)
2990
+ msg = nil if val
2991
+ end
2992
+ print msg if action == 'syntax' && msg
2993
+
2994
+ end
2995
+
2996
+ private
2997
+
2998
+ def do_action(action, engine, context, filename, opts)
2999
+ case action
3000
+ when 'convert'
3001
+ s = manipulate_src(engine.src, opts)
3002
+ when nil, 'exec', 'execute'
3003
+ s = opts.binding ? engine.result(context.to_hash) : engine.evaluate(context)
3004
+ when 'syntax'
3005
+ s = check_syntax(filename, engine.src)
3006
+ else
3007
+ raise "*** internal error"
3008
+ end
3009
+ print s if s
3010
+ return s
3011
+ end
3012
+
3013
+ def manipulate_src(source, opts)
3014
+ flag_linenum = opts.linenum
3015
+ flag_unique = opts.unique
3016
+ flag_compact = opts.compact
3017
+ if flag_linenum
3018
+ n = 0
3019
+ source.gsub!(/^/) { n += 1; "%5d: " % n }
3020
+ source.gsub!(/^ *\d+:\s+?\n/, '') if flag_compact
3021
+ source.gsub!(/(^ *\d+:\s+?\n)+/, "\n") if flag_unique
3022
+ else
3023
+ source.gsub!(/^\s*?\n/, '') if flag_compact
3024
+ source.gsub!(/(^\s*?\n)+/, "\n") if flag_unique
3025
+ end
3026
+ return source
3027
+ end
3028
+
3029
+ def usage(command=nil)
3030
+ command ||= File.basename($0)
3031
+ buf = []
3032
+ buf << "erubis - embedded program converter for multi-language"
3033
+ buf << "Usage: #{command} [..options..] [file ...]"
3034
+ buf << " -h, --help : help"
3035
+ buf << " -v : version"
3036
+ buf << " -x : show converted code"
3037
+ buf << " -X : show converted code, only ruby code and no text part"
3038
+ buf << " -N : numbering: add line numbers (for '-x/-X')"
3039
+ buf << " -U : unique: compress empty lines to a line (for '-x/-X')"
3040
+ buf << " -C : compact: remove empty lines (for '-x/-X')"
3041
+ buf << " -b : body only: no preamble nor postamble (for '-x/-X')"
3042
+ buf << " -z : syntax checking"
3043
+ buf << " -e : escape (equal to '--E Escape')"
3044
+ buf << " -p pattern : embedded pattern (default '<% %>')"
3045
+ buf << " -l lang : convert but no execute (ruby/php/c/java/scheme/perl/js)"
3046
+ buf << " -E e1,e2,... : enhancer names (Escape, PercentLine, BiPattern, ...)"
3047
+ buf << " -I path : library include path"
3048
+ buf << " -K kanji : kanji code (euc/sjis/utf8) (default none)"
3049
+ buf << " -c context : context data string (yaml inline style or ruby code)"
3050
+ buf << " -f datafile : context data file ('*.yaml', '*.yml', or '*.rb')"
3051
+ #buf << " -t : expand tab characters in YAML file"
3052
+ buf << " -T : don't expand tab characters in YAML file"
3053
+ buf << " -S : convert mapping key from string to symbol in YAML file"
3054
+ buf << " -B : invoke 'result(binding)' instead of 'evaluate(context)'"
3055
+ buf << " --pi=name : parse '<?name ... ?>' instead of '<% ... %>'"
3056
+ #'
3057
+ # -T : don't trim spaces around '<% %>'
3058
+ # -c class : class name (XmlEruby/PercentLineEruby/...) (default Eruby)
3059
+ # -r library : require library
3060
+ # -a : action (convert/execute)
3061
+ return buf.join("\n")
3062
+ end
3063
+
3064
+ def collect_supported_properties(erubis_klass)
3065
+ list = []
3066
+ erubis_klass.ancestors.each do |klass|
3067
+ if klass.respond_to?(:supported_properties)
3068
+ list.concat(klass.supported_properties)
3069
+ end
3070
+ end
3071
+ return list
3072
+ end
3073
+
3074
+ def show_properties
3075
+ s = "supported properties:\n"
3076
+ basic_props = collect_supported_properties(Erubis::Basic::Engine)
3077
+ pi_props = collect_supported_properties(Erubis::PI::Engine)
3078
+ list = []
3079
+ common_props = basic_props & pi_props
3080
+ list << ['(common)', common_props]
3081
+ list << ['(basic)', basic_props - common_props]
3082
+ list << ['(pi)', pi_props - common_props]
3083
+ %w[ruby php c java scheme perl javascript].each do |lang|
3084
+ klass = Erubis.const_get("E#{lang}")
3085
+ list << [lang, collect_supported_properties(klass) - basic_props]
3086
+ end
3087
+ list.each do |lang, props|
3088
+ s << " * #{lang}\n"
3089
+ props.each do |name, default_val, desc|
3090
+ s << (" --%-23s : %s\n" % ["#{name}=#{default_val.inspect}", desc])
3091
+ end
3092
+ end
3093
+ s << "\n"
3094
+ return s
3095
+ end
3096
+
3097
+ def show_enhancers
3098
+ s = "enhancers:\n"
3099
+ list = []
3100
+ ObjectSpace.each_object(Module) do |m| list << m end
3101
+ list.sort_by { |m| m.name }.each do |m|
3102
+ next unless m.name =~ /\AErubis::(.*)Enhancer\z/
3103
+ name = $1
3104
+ desc = m.desc
3105
+ s << (" %-13s : %s\n" % [name, desc])
3106
+ end
3107
+ return s
3108
+ end
3109
+
3110
+ def version
3111
+ return Erubis::VERSION
3112
+ end
3113
+
3114
+ def parse_argv(argv, arg_none='', arg_required='', arg_optional='')
3115
+ options = {}
3116
+ context = {}
3117
+ while argv[0] && argv[0][0] == ?-
3118
+ optstr = argv.shift
3119
+ optstr = optstr[1, optstr.length-1]
3120
+ #
3121
+ if optstr[0] == ?- # context
3122
+ unless optstr =~ /\A\-([-\w]+)(?:=(.*))?/
3123
+ raise CommandOptionError.new("-#{optstr}: invalid context value.")
3124
+ end
3125
+ name = $1; value = $2
3126
+ name = name.gsub(/-/, '_').intern
3127
+ #value = value.nil? ? true : YAML.load(value) # error, why?
3128
+ value = value.nil? ? true : YAML.load("---\n#{value}\n")
3129
+ context[name] = value
3130
+ #
3131
+ else # options
3132
+ while optstr && !optstr.empty?
3133
+ optchar = optstr[0]
3134
+ optstr[0,1] = ""
3135
+ if arg_none.include?(optchar)
3136
+ options[optchar] = true
3137
+ elsif arg_required.include?(optchar)
3138
+ arg = optstr.empty? ? argv.shift : optstr
3139
+ unless arg
3140
+ mesg = "-#{optchar.chr}: #{@option_args[optchar]} required."
3141
+ raise CommandOptionError.new(mesg)
3142
+ end
3143
+ options[optchar] = arg
3144
+ optstr = nil
3145
+ elsif arg_optional.include?(optchar)
3146
+ arg = optstr.empty? ? true : optstr
3147
+ options[optchar] = arg
3148
+ optstr = nil
3149
+ else
3150
+ raise CommandOptionError.new("-#{optchar.chr}: unknown option.")
3151
+ end
3152
+ end
3153
+ end
3154
+ #
3155
+ end # end of while
3156
+
3157
+ return options, context
3158
+ end
3159
+
3160
+
3161
+ def untabify(str, width=8)
3162
+ list = str.split(/\t/)
3163
+ last = list.pop
3164
+ sb = ''
3165
+ list.each do |s|
3166
+ column = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length
3167
+ n = width - (column % width)
3168
+ sb << s << (' ' * n)
3169
+ end
3170
+ sb << last
3171
+ return sb
3172
+ end
3173
+ #--
3174
+ #def untabify(str, width=8)
3175
+ # sb = ''
3176
+ # str.scan(/(.*?)\t/m) do |s, |
3177
+ # len = (n = s.rindex(?\n)) ? s.length - n - 1 : s.length
3178
+ # sb << s << (" " * (width - len % width))
3179
+ # end
3180
+ # return $' ? (sb << $') : str
3181
+ #end
3182
+ #++
3183
+
3184
+
3185
+ def get_classobj(classname, lang, pi)
3186
+ classname ||= "E#{lang}"
3187
+ base_module = pi ? Erubis::PI : Erubis
3188
+ begin
3189
+ klass = base_module.const_get(classname)
3190
+ rescue NameError
3191
+ klass = nil
3192
+ end
3193
+ unless klass
3194
+ if lang
3195
+ msg = "-l #{lang}: invalid language name (class #{base_module.name}::#{classname} not found)."
3196
+ else
3197
+ msg = "-c #{classname}: invalid class name."
3198
+ end
3199
+ raise CommandOptionError.new(msg)
3200
+ end
3201
+ return klass
3202
+ end
3203
+
3204
+ def get_enhancers(enhancer_names)
3205
+ return [] unless enhancer_names
3206
+ enhancers = []
3207
+ shortname = nil
3208
+ begin
3209
+ enhancer_names.split(/,/).each do |shortname|
3210
+ enhancers << Erubis.const_get("#{shortname}Enhancer")
3211
+ end
3212
+ rescue NameError
3213
+ raise CommandOptionError.new("#{shortname}: no such Enhancer (try '-E' to show all enhancers).")
3214
+ end
3215
+ return enhancers
3216
+ end
3217
+
3218
+ def load_datafiles(filenames, opts)
3219
+ context = Erubis::Context.new
3220
+ return context unless filenames
3221
+ filenames.split(/,/).each do |filename|
3222
+ filename.strip!
3223
+ test(?f, filename) or raise CommandOptionError.new("#{filename}: file not found.")
3224
+ if filename =~ /\.ya?ml$/
3225
+ if opts.unexpand
3226
+ ydoc = YAML.load_file(filename)
3227
+ else
3228
+ ydoc = YAML.load(untabify(File.read(filename)))
3229
+ end
3230
+ ydoc.is_a?(Hash) or raise CommandOptionError.new("#{filename}: root object is not a mapping.")
3231
+ intern_hash_keys(ydoc) if opts.intern
3232
+ context.update(ydoc)
3233
+ elsif filename =~ /\.rb$/
3234
+ str = File.read(filename)
3235
+ context2 = Erubis::Context.new
3236
+ _instance_eval(context2, str)
3237
+ context.update(context2)
3238
+ else
3239
+ CommandOptionError.new("#{filename}: '*.yaml', '*.yml', or '*.rb' required.")
3240
+ end
3241
+ end
3242
+ return context
3243
+ end
3244
+
3245
+ def _instance_eval(_context, _str)
3246
+ _context.instance_eval(_str)
3247
+ end
3248
+
3249
+ def parse_context_data(context_str, opts)
3250
+ if context_str[0] == ?{
3251
+ require 'yaml'
3252
+ ydoc = YAML.load(context_str)
3253
+ unless ydoc.is_a?(Hash)
3254
+ raise CommandOptionError.new("-c: root object is not a mapping.")
3255
+ end
3256
+ intern_hash_keys(ydoc) if opts.intern
3257
+ return ydoc
3258
+ else
3259
+ context = Erubis::Context.new
3260
+ context.instance_eval(context_str, '-c')
3261
+ return context
3262
+ end
3263
+ end
3264
+
3265
+ def intern_hash_keys(obj, done={})
3266
+ return if done.key?(obj.__id__)
3267
+ case obj
3268
+ when Hash
3269
+ done[obj.__id__] = obj
3270
+ obj.keys.each do |key|
3271
+ obj[key.intern] = obj.delete(key) if key.is_a?(String)
3272
+ end
3273
+ obj.values.each do |val|
3274
+ intern_hash_keys(val, done) if val.is_a?(Hash) || val.is_a?(Array)
3275
+ end
3276
+ when Array
3277
+ done[obj.__id__] = obj
3278
+ obj.each do |val|
3279
+ intern_hash_keys(val, done) if val.is_a?(Hash) || val.is_a?(Array)
3280
+ end
3281
+ end
3282
+ end
3283
+
3284
+ def check_syntax(filename, src)
3285
+ require 'open3'
3286
+ stdin, stdout, stderr = Open3.popen3('ruby -wc')
3287
+ stdin.write(src)
3288
+ stdin.close
3289
+ result = stdout.read()
3290
+ stdout.close()
3291
+ errmsg = stderr.read()
3292
+ stderr.close()
3293
+ return nil unless errmsg && !errmsg.empty?
3294
+ errmsg =~ /\A-:(\d+): /
3295
+ linenum, message = $1, $'
3296
+ return "#{filename}:#{linenum}: #{message}"
3297
+ end
3298
+
3299
+ end
3300
+
3301
+ end
3302
+ #--end of require 'erubis/main'
10
3303
 
11
3304
  Erubis::Main.main(ARGV)