erb 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,471 @@
1
+ #--
2
+ # ERB::Compiler
3
+ #
4
+ # Compiles ERB templates into Ruby code; the compiled code produces the
5
+ # template result when evaluated. ERB::Compiler provides hooks to define how
6
+ # generated output is handled.
7
+ #
8
+ # Internally ERB does something like this to generate the code returned by
9
+ # ERB#src:
10
+ #
11
+ # compiler = ERB::Compiler.new('<>')
12
+ # compiler.pre_cmd = ["_erbout=+''"]
13
+ # compiler.put_cmd = "_erbout.<<"
14
+ # compiler.insert_cmd = "_erbout.<<"
15
+ # compiler.post_cmd = ["_erbout"]
16
+ #
17
+ # code, enc = compiler.compile("Got <%= obj %>!\n")
18
+ # puts code
19
+ #
20
+ # <i>Generates</i>:
21
+ #
22
+ # #coding:UTF-8
23
+ # _erbout=+''; _erbout.<< "Got ".freeze; _erbout.<<(( obj ).to_s); _erbout.<< "!\n".freeze; _erbout
24
+ #
25
+ # By default the output is sent to the print method. For example:
26
+ #
27
+ # compiler = ERB::Compiler.new('<>')
28
+ # code, enc = compiler.compile("Got <%= obj %>!\n")
29
+ # puts code
30
+ #
31
+ # <i>Generates</i>:
32
+ #
33
+ # #coding:UTF-8
34
+ # print "Got ".freeze; print(( obj ).to_s); print "!\n".freeze
35
+ #
36
+ # == Evaluation
37
+ #
38
+ # The compiled code can be used in any context where the names in the code
39
+ # correctly resolve. Using the last example, each of these print 'Got It!'
40
+ #
41
+ # Evaluate using a variable:
42
+ #
43
+ # obj = 'It'
44
+ # eval code
45
+ #
46
+ # Evaluate using an input:
47
+ #
48
+ # mod = Module.new
49
+ # mod.module_eval %{
50
+ # def get(obj)
51
+ # #{code}
52
+ # end
53
+ # }
54
+ # extend mod
55
+ # get('It')
56
+ #
57
+ # Evaluate using an accessor:
58
+ #
59
+ # klass = Class.new Object
60
+ # klass.class_eval %{
61
+ # attr_accessor :obj
62
+ # def initialize(obj)
63
+ # @obj = obj
64
+ # end
65
+ # def get_it
66
+ # #{code}
67
+ # end
68
+ # }
69
+ # klass.new('It').get_it
70
+ #
71
+ # Good! See also ERB#def_method, ERB#def_module, and ERB#def_class.
72
+ class ERB::Compiler # :nodoc:
73
+ class PercentLine # :nodoc:
74
+ def initialize(str)
75
+ @value = str
76
+ end
77
+ attr_reader :value
78
+ alias :to_s :value
79
+ end
80
+
81
+ class Scanner # :nodoc:
82
+ @scanner_map = {}
83
+ class << self
84
+ def register_scanner(klass, trim_mode, percent)
85
+ @scanner_map[[trim_mode, percent]] = klass
86
+ end
87
+ alias :regist_scanner :register_scanner
88
+ end
89
+
90
+ def self.default_scanner=(klass)
91
+ @default_scanner = klass
92
+ end
93
+
94
+ def self.make_scanner(src, trim_mode, percent)
95
+ klass = @scanner_map.fetch([trim_mode, percent], @default_scanner)
96
+ klass.new(src, trim_mode, percent)
97
+ end
98
+
99
+ DEFAULT_STAGS = %w(<%% <%= <%# <%).freeze
100
+ DEFAULT_ETAGS = %w(%%> %>).freeze
101
+ def initialize(src, trim_mode, percent)
102
+ @src = src
103
+ @stag = nil
104
+ @stags = DEFAULT_STAGS
105
+ @etags = DEFAULT_ETAGS
106
+ end
107
+ attr_accessor :stag
108
+ attr_reader :stags, :etags
109
+
110
+ def scan; end
111
+ end
112
+
113
+ class TrimScanner < Scanner # :nodoc:
114
+ def initialize(src, trim_mode, percent)
115
+ super
116
+ @trim_mode = trim_mode
117
+ @percent = percent
118
+ if @trim_mode == '>'
119
+ @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
120
+ @scan_line = self.method(:trim_line1)
121
+ elsif @trim_mode == '<>'
122
+ @scan_reg = /(.*?)(%>\r?\n|#{(stags + etags).join('|')}|\n|\z)/m
123
+ @scan_line = self.method(:trim_line2)
124
+ elsif @trim_mode == '-'
125
+ @scan_reg = /(.*?)(^[ \t]*<%\-|<%\-|-%>\r?\n|-%>|#{(stags + etags).join('|')}|\z)/m
126
+ @scan_line = self.method(:explicit_trim_line)
127
+ else
128
+ @scan_reg = /(.*?)(#{(stags + etags).join('|')}|\n|\z)/m
129
+ @scan_line = self.method(:scan_line)
130
+ end
131
+ end
132
+
133
+ def scan(&block)
134
+ @stag = nil
135
+ if @percent
136
+ @src.each_line do |line|
137
+ percent_line(line, &block)
138
+ end
139
+ else
140
+ @scan_line.call(@src, &block)
141
+ end
142
+ nil
143
+ end
144
+
145
+ def percent_line(line, &block)
146
+ if @stag || line[0] != ?%
147
+ return @scan_line.call(line, &block)
148
+ end
149
+
150
+ line[0] = ''
151
+ if line[0] == ?%
152
+ @scan_line.call(line, &block)
153
+ else
154
+ yield(PercentLine.new(line.chomp))
155
+ end
156
+ end
157
+
158
+ def scan_line(line)
159
+ line.scan(@scan_reg) do |tokens|
160
+ tokens.each do |token|
161
+ next if token.empty?
162
+ yield(token)
163
+ end
164
+ end
165
+ end
166
+
167
+ def trim_line1(line)
168
+ line.scan(@scan_reg) do |tokens|
169
+ tokens.each do |token|
170
+ next if token.empty?
171
+ if token == "%>\n" || token == "%>\r\n"
172
+ yield('%>')
173
+ yield(:cr)
174
+ else
175
+ yield(token)
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ def trim_line2(line)
182
+ head = nil
183
+ line.scan(@scan_reg) do |tokens|
184
+ tokens.each do |token|
185
+ next if token.empty?
186
+ head = token unless head
187
+ if token == "%>\n" || token == "%>\r\n"
188
+ yield('%>')
189
+ if is_erb_stag?(head)
190
+ yield(:cr)
191
+ else
192
+ yield("\n")
193
+ end
194
+ head = nil
195
+ else
196
+ yield(token)
197
+ head = nil if token == "\n"
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ def explicit_trim_line(line)
204
+ line.scan(@scan_reg) do |tokens|
205
+ tokens.each do |token|
206
+ next if token.empty?
207
+ if @stag.nil? && /[ \t]*<%-/ =~ token
208
+ yield('<%')
209
+ elsif @stag && (token == "-%>\n" || token == "-%>\r\n")
210
+ yield('%>')
211
+ yield(:cr)
212
+ elsif @stag && token == '-%>'
213
+ yield('%>')
214
+ else
215
+ yield(token)
216
+ end
217
+ end
218
+ end
219
+ end
220
+
221
+ ERB_STAG = %w(<%= <%# <%)
222
+ def is_erb_stag?(s)
223
+ ERB_STAG.member?(s)
224
+ end
225
+ end
226
+
227
+ Scanner.default_scanner = TrimScanner
228
+
229
+ begin
230
+ require 'strscan'
231
+ rescue LoadError
232
+ else
233
+ class SimpleScanner < Scanner # :nodoc:
234
+ def scan
235
+ stag_reg = (stags == DEFAULT_STAGS) ? /(.*?)(<%[%=#]?|\z)/m : /(.*?)(#{stags.join('|')}|\z)/m
236
+ etag_reg = (etags == DEFAULT_ETAGS) ? /(.*?)(%%?>|\z)/m : /(.*?)(#{etags.join('|')}|\z)/m
237
+ scanner = StringScanner.new(@src)
238
+ while ! scanner.eos?
239
+ scanner.scan(@stag ? etag_reg : stag_reg)
240
+ yield(scanner[1])
241
+ yield(scanner[2])
242
+ end
243
+ end
244
+ end
245
+ Scanner.register_scanner(SimpleScanner, nil, false)
246
+
247
+ class ExplicitScanner < Scanner # :nodoc:
248
+ def scan
249
+ stag_reg = /(.*?)(^[ \t]*<%-|<%-|#{stags.join('|')}|\z)/m
250
+ etag_reg = /(.*?)(-%>|#{etags.join('|')}|\z)/m
251
+ scanner = StringScanner.new(@src)
252
+ while ! scanner.eos?
253
+ scanner.scan(@stag ? etag_reg : stag_reg)
254
+ yield(scanner[1])
255
+
256
+ elem = scanner[2]
257
+ if /[ \t]*<%-/ =~ elem
258
+ yield('<%')
259
+ elsif elem == '-%>'
260
+ yield('%>')
261
+ yield(:cr) if scanner.scan(/(\r?\n|\z)/)
262
+ else
263
+ yield(elem)
264
+ end
265
+ end
266
+ end
267
+ end
268
+ Scanner.register_scanner(ExplicitScanner, '-', false)
269
+ end
270
+
271
+ class Buffer # :nodoc:
272
+ def initialize(compiler, enc=nil, frozen=nil)
273
+ @compiler = compiler
274
+ @line = []
275
+ @script = +''
276
+ @script << "#coding:#{enc}\n" if enc
277
+ @script << "#frozen-string-literal:#{frozen}\n" unless frozen.nil?
278
+ @compiler.pre_cmd.each do |x|
279
+ push(x)
280
+ end
281
+ end
282
+ attr_reader :script
283
+
284
+ def push(cmd)
285
+ @line << cmd
286
+ end
287
+
288
+ def cr
289
+ @script << (@line.join('; '))
290
+ @line = []
291
+ @script << "\n"
292
+ end
293
+
294
+ def close
295
+ return unless @line
296
+ @compiler.post_cmd.each do |x|
297
+ push(x)
298
+ end
299
+ @script << (@line.join('; '))
300
+ @line = nil
301
+ end
302
+ end
303
+
304
+ def add_put_cmd(out, content)
305
+ out.push("#{@put_cmd} #{content.dump}.freeze#{"\n" * content.count("\n")}")
306
+ end
307
+
308
+ def add_insert_cmd(out, content)
309
+ out.push("#{@insert_cmd}((#{content}).to_s)")
310
+ end
311
+
312
+ # Compiles an ERB template into Ruby code. Returns an array of the code
313
+ # and encoding like ["code", Encoding].
314
+ def compile(s)
315
+ enc = s.encoding
316
+ raise ArgumentError, "#{enc} is not ASCII compatible" if enc.dummy?
317
+ s = s.b # see String#b
318
+ magic_comment = detect_magic_comment(s, enc)
319
+ out = Buffer.new(self, *magic_comment)
320
+
321
+ self.content = +''
322
+ scanner = make_scanner(s)
323
+ scanner.scan do |token|
324
+ next if token.nil?
325
+ next if token == ''
326
+ if scanner.stag.nil?
327
+ compile_stag(token, out, scanner)
328
+ else
329
+ compile_etag(token, out, scanner)
330
+ end
331
+ end
332
+ add_put_cmd(out, content) if content.size > 0
333
+ out.close
334
+ return out.script, *magic_comment
335
+ end
336
+
337
+ def compile_stag(stag, out, scanner)
338
+ case stag
339
+ when PercentLine
340
+ add_put_cmd(out, content) if content.size > 0
341
+ self.content = +''
342
+ out.push(stag.to_s)
343
+ out.cr
344
+ when :cr
345
+ out.cr
346
+ when '<%', '<%=', '<%#'
347
+ scanner.stag = stag
348
+ add_put_cmd(out, content) if content.size > 0
349
+ self.content = +''
350
+ when "\n"
351
+ content << "\n"
352
+ add_put_cmd(out, content)
353
+ self.content = +''
354
+ when '<%%'
355
+ content << '<%'
356
+ else
357
+ content << stag
358
+ end
359
+ end
360
+
361
+ def compile_etag(etag, out, scanner)
362
+ case etag
363
+ when '%>'
364
+ compile_content(scanner.stag, out)
365
+ scanner.stag = nil
366
+ self.content = +''
367
+ when '%%>'
368
+ content << '%>'
369
+ else
370
+ content << etag
371
+ end
372
+ end
373
+
374
+ def compile_content(stag, out)
375
+ case stag
376
+ when '<%'
377
+ if content[-1] == ?\n
378
+ content.chop!
379
+ out.push(content)
380
+ out.cr
381
+ else
382
+ out.push(content)
383
+ end
384
+ when '<%='
385
+ add_insert_cmd(out, content)
386
+ when '<%#'
387
+ # commented out
388
+ end
389
+ end
390
+
391
+ def prepare_trim_mode(mode) # :nodoc:
392
+ case mode
393
+ when 1
394
+ return [false, '>']
395
+ when 2
396
+ return [false, '<>']
397
+ when 0, nil
398
+ return [false, nil]
399
+ when String
400
+ unless mode.match?(/\A(%|-|>|<>){1,2}\z/)
401
+ warn_invalid_trim_mode(mode, uplevel: 5)
402
+ end
403
+
404
+ perc = mode.include?('%')
405
+ if mode.include?('-')
406
+ return [perc, '-']
407
+ elsif mode.include?('<>')
408
+ return [perc, '<>']
409
+ elsif mode.include?('>')
410
+ return [perc, '>']
411
+ else
412
+ [perc, nil]
413
+ end
414
+ else
415
+ warn_invalid_trim_mode(mode, uplevel: 5)
416
+ return [false, nil]
417
+ end
418
+ end
419
+
420
+ def make_scanner(src) # :nodoc:
421
+ Scanner.make_scanner(src, @trim_mode, @percent)
422
+ end
423
+
424
+ # Construct a new compiler using the trim_mode. See ERB::new for available
425
+ # trim modes.
426
+ def initialize(trim_mode)
427
+ @percent, @trim_mode = prepare_trim_mode(trim_mode)
428
+ @put_cmd = 'print'
429
+ @insert_cmd = @put_cmd
430
+ @pre_cmd = []
431
+ @post_cmd = []
432
+ end
433
+ attr_reader :percent, :trim_mode
434
+
435
+ # The command to handle text that ends with a newline
436
+ attr_accessor :put_cmd
437
+
438
+ # The command to handle text that is inserted prior to a newline
439
+ attr_accessor :insert_cmd
440
+
441
+ # An array of commands prepended to compiled code
442
+ attr_accessor :pre_cmd
443
+
444
+ # An array of commands appended to compiled code
445
+ attr_accessor :post_cmd
446
+
447
+ private
448
+
449
+ # A buffered text in #compile
450
+ attr_accessor :content
451
+
452
+ def detect_magic_comment(s, enc = nil)
453
+ re = @percent ? /\G(?:<%#(.*)%>|%#(.*)\n)/ : /\G<%#(.*)%>/
454
+ frozen = nil
455
+ s.scan(re) do
456
+ comment = $+
457
+ comment = $1 if comment[/-\*-\s*([^\s].*?)\s*-\*-$/]
458
+ case comment
459
+ when %r"coding\s*[=:]\s*([[:alnum:]\-_]+)"
460
+ enc = Encoding.find($1.sub(/-(?:mac|dos|unix)/i, ''))
461
+ when %r"frozen[-_]string[-_]literal\s*:\s*([[:alnum:]]+)"
462
+ frozen = $1
463
+ end
464
+ end
465
+ return enc, frozen
466
+ end
467
+
468
+ def warn_invalid_trim_mode(mode, uplevel:)
469
+ warn "Invalid ERB trim mode: #{mode.inspect} (trim_mode: nil, 0, 1, 2, or String composed of '%' and/or '-', '>', '<>')", uplevel: uplevel + 1
470
+ end
471
+ end
@@ -0,0 +1,46 @@
1
+ #--
2
+ # ERB::DefMethod
3
+ #
4
+ # Utility module to define eRuby script as instance method.
5
+ #
6
+ # === Example
7
+ #
8
+ # example.rhtml:
9
+ # <% for item in @items %>
10
+ # <b><%= item %></b>
11
+ # <% end %>
12
+ #
13
+ # example.rb:
14
+ # require 'erb'
15
+ # class MyClass
16
+ # extend ERB::DefMethod
17
+ # def_erb_method('render()', 'example.rhtml')
18
+ # def initialize(items)
19
+ # @items = items
20
+ # end
21
+ # end
22
+ # print MyClass.new([10,20,30]).render()
23
+ #
24
+ # result:
25
+ #
26
+ # <b>10</b>
27
+ #
28
+ # <b>20</b>
29
+ #
30
+ # <b>30</b>
31
+ #
32
+ module ERB::DefMethod
33
+ # define _methodname_ as instance method of current module, using ERB
34
+ # object or eRuby file
35
+ def def_erb_method(methodname, erb_or_fname)
36
+ if erb_or_fname.kind_of? String
37
+ fname = erb_or_fname
38
+ erb = ERB.new(File.read(fname))
39
+ erb.def_method(self, methodname, fname)
40
+ else
41
+ erb = erb_or_fname
42
+ erb.def_method(self, methodname, erb.filename || '(ERB)')
43
+ end
44
+ end
45
+ module_function :def_erb_method
46
+ end
data/lib/erb/util.rb ADDED
@@ -0,0 +1,59 @@
1
+ #--
2
+ # ERB::Escape
3
+ #
4
+ # A subset of ERB::Util. Unlike ERB::Util#html_escape, we expect/hope
5
+ # Rails will not monkey-patch ERB::Escape#html_escape.
6
+ begin
7
+ require 'erb/escape'
8
+ rescue LoadError # JRuby can't load .so
9
+ end
10
+ unless defined?(ERB::Escape) # JRuby
11
+ module ERB::Escape
12
+ def html_escape(s)
13
+ CGI.escapeHTML(s.to_s)
14
+ end
15
+ module_function :html_escape
16
+ end
17
+ end
18
+
19
+ #--
20
+ # ERB::Util
21
+ #
22
+ # A utility module for conversion routines, often handy in HTML generation.
23
+ module ERB::Util
24
+ #
25
+ # A utility method for escaping HTML tag characters in _s_.
26
+ #
27
+ # require "erb"
28
+ # include ERB::Util
29
+ #
30
+ # puts html_escape("is a > 0 & a < 10?")
31
+ #
32
+ # _Generates_
33
+ #
34
+ # is a &gt; 0 &amp; a &lt; 10?
35
+ #
36
+ include ERB::Escape # html_escape
37
+ module_function :html_escape
38
+ alias h html_escape
39
+ module_function :h
40
+
41
+ #
42
+ # A utility method for encoding the String _s_ as a URL.
43
+ #
44
+ # require "erb"
45
+ # include ERB::Util
46
+ #
47
+ # puts url_encode("Programming Ruby: The Pragmatic Programmer's Guide")
48
+ #
49
+ # _Generates_
50
+ #
51
+ # Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide
52
+ #
53
+ def url_encode(s)
54
+ CGI.escapeURIComponent(s.to_s)
55
+ end
56
+ alias u url_encode
57
+ module_function :u
58
+ module_function :url_encode
59
+ end
data/lib/erb/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  class ERB
3
- VERSION = '3.0.0'
3
+ VERSION = '4.0.0'
4
4
  private_constant :VERSION
5
5
  end