slim 1.3.9 → 2.0.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -8
  3. data/.travis.yml +8 -7
  4. data/CHANGES +35 -0
  5. data/Gemfile +9 -9
  6. data/README.md +94 -176
  7. data/Rakefile +7 -14
  8. data/benchmarks/run-benchmarks.rb +9 -37
  9. data/doc/logic_less.md +140 -0
  10. data/doc/translator.md +31 -0
  11. data/lib/slim.rb +3 -1
  12. data/lib/slim/code_attributes.rb +20 -20
  13. data/lib/slim/command.rb +16 -6
  14. data/lib/slim/do_inserter.rb +33 -0
  15. data/lib/slim/embedded.rb +0 -6
  16. data/lib/slim/end_inserter.rb +2 -2
  17. data/lib/slim/engine.rb +9 -23
  18. data/lib/slim/erb_converter.rb +14 -0
  19. data/lib/slim/filter.rb +2 -2
  20. data/lib/slim/logic_less/context.rb +8 -0
  21. data/lib/slim/logic_less/filter.rb +12 -11
  22. data/lib/slim/parser.rb +57 -113
  23. data/lib/slim/splat/builder.rb +79 -0
  24. data/lib/slim/splat/filter.rb +93 -0
  25. data/lib/slim/version.rb +1 -1
  26. data/slim.gemspec +1 -1
  27. data/test/core/helper.rb +1 -3
  28. data/test/core/test_code_blocks.rb +51 -0
  29. data/test/core/test_code_evaluation.rb +4 -12
  30. data/test/core/test_embedded_engines.rb +18 -44
  31. data/test/core/test_encoding.rb +8 -1
  32. data/test/core/test_erb_converter.rb +67 -0
  33. data/test/core/test_html_attributes.rb +19 -25
  34. data/test/core/test_html_escaping.rb +10 -2
  35. data/test/core/test_html_structure.rb +6 -6
  36. data/test/core/test_parser_errors.rb +1 -1
  37. data/test/core/test_ruby_errors.rb +2 -6
  38. data/test/core/test_thread_options.rb +4 -4
  39. data/test/core/test_unicode.rb +18 -0
  40. data/test/literate/TESTS.md +193 -34
  41. data/test/logic_less/test_logic_less.rb +17 -0
  42. data/test/rails/app/controllers/application_controller.rb +0 -1
  43. data/test/rails/app/controllers/entries_controller.rb +5 -0
  44. data/test/rails/app/controllers/slim_controller.rb +3 -0
  45. data/test/rails/app/models/entry.rb +16 -0
  46. data/test/rails/app/views/entries/edit.html.slim +3 -0
  47. data/test/rails/app/views/slim/form_for.html.slim +2 -0
  48. data/test/rails/app/views/slim/xml.slim +1 -0
  49. data/test/rails/config/application.rb +2 -2
  50. data/test/rails/config/environments/test.rb +1 -1
  51. data/test/rails/config/routes.rb +1 -1
  52. data/test/rails/test/helper.rb +0 -3
  53. data/test/rails/test/test_slim.rb +13 -8
  54. data/test/translator/test_translator.rb +13 -2
  55. metadata +17 -14
  56. data/lib/slim/splat_attributes.rb +0 -113
  57. data/test/rails/app/controllers/parents_controller.rb +0 -85
  58. data/test/rails/app/models/child.rb +0 -3
  59. data/test/rails/app/models/parent.rb +0 -4
  60. data/test/rails/app/views/parents/_form.html.slim +0 -8
  61. data/test/rails/app/views/parents/edit.html.slim +0 -2
  62. data/test/rails/app/views/parents/new.html.slim +0 -2
  63. data/test/rails/app/views/parents/show.html.slim +0 -5
  64. data/test/rails/config/database.yml +0 -4
  65. data/test/rails/db/migrate/20101220223037_parents_and_children.rb +0 -17
data/lib/slim/engine.rb CHANGED
@@ -5,37 +5,23 @@ module Slim
5
5
  # This overwrites some Temple default options or sets default options for Slim specific filters.
6
6
  # It is recommended to set the default settings only once in the code and avoid duplication. Only use
7
7
  # `define_options` when you have to override some default settings.
8
- define_options :attr_quote,
9
- :merge_attrs,
10
- :pretty => false,
8
+ define_options :pretty => false,
11
9
  :sort_attrs => true,
12
- :generator => Temple::Generators::ArrayBuffer,
13
- :default_tag => 'div',
14
10
  :attr_quote => '"',
15
11
  :merge_attrs => {'class' => ' '},
16
- :escape_quoted_attrs => false
17
-
18
- # Removed in 2.0
19
- define_deprecated_options :remove_empty_attrs,
20
- :chain,
21
- :escape_quoted_attrs,
22
- :attr_wrapper,
23
- :attr_delimiter
24
-
25
- def initialize(opts = {})
26
- super
27
- deprecated = {}
28
- deprecated[:merge_attrs] = options[:attr_delimiter] if options.include? :attr_delimiter
29
- deprecated[:attr_quote] = options[:attr_wrapper] if options.include? :attr_wrapper
30
- @options = Temple::ImmutableHash.new(deprecated, @options) unless deprecated.empty?
31
- end
12
+ :encoding => 'utf-8',
13
+ :generator => Temple::Generators::ArrayBuffer,
14
+ :default_tag => 'div'
32
15
 
33
- use Slim::Parser, :file, :tabsize, :encoding, :shortcut, :default_tag, :escape_quoted_attrs
16
+ filter :Encoding, :encoding
17
+ filter :RemoveBOM
18
+ use Slim::Parser, :file, :tabsize, :shortcut, :default_tag
34
19
  use Slim::Embedded, :enable_engines, :disable_engines, :pretty
35
20
  use Slim::Interpolation
21
+ use Slim::Splat::Filter, :merge_attrs, :attr_quote, :sort_attrs, :default_tag, :hyphen_attrs
22
+ use Slim::DoInserter
36
23
  use Slim::EndInserter
37
24
  use Slim::Controls, :disable_capture
38
- use Slim::SplatAttributes, :merge_attrs, :attr_quote, :sort_attrs, :default_tag
39
25
  html :AttributeSorter, :sort_attrs
40
26
  html :AttributeMerger, :merge_attrs
41
27
  use Slim::CodeAttributes, :merge_attrs
@@ -0,0 +1,14 @@
1
+ require 'slim'
2
+
3
+ module Slim
4
+ # Slim to ERB converter
5
+ #
6
+ # @example Conversion
7
+ # Slim::ERBConverter.new(options).call(slim_code) # outputs erb_code
8
+ #
9
+ # @api public
10
+ class ERBConverter < Engine
11
+ replace :Optimizer, Temple::Filters::CodeMerger
12
+ replace :Generator, Temple::Generators::ERB
13
+ end
14
+ end
data/lib/slim/filter.rb CHANGED
@@ -23,8 +23,8 @@ module Slim
23
23
  end
24
24
 
25
25
  # Pass-through handler
26
- def on_slim_output(code, escape, content)
27
- [:slim, :output, code, escape, compile(content)]
26
+ def on_slim_output(escape, code, content)
27
+ [:slim, :output, escape, code, compile(content)]
28
28
  end
29
29
  end
30
30
  end
@@ -45,6 +45,10 @@ module Slim
45
45
  yield if !value || (value.respond_to?(:empty?) && value.empty?)
46
46
  end
47
47
 
48
+ def to_s
49
+ scope.to_s
50
+ end
51
+
48
52
  private
49
53
 
50
54
  class Scope
@@ -89,6 +93,10 @@ module Slim
89
93
  @parent[name] if @parent
90
94
  end
91
95
 
96
+ def to_s
97
+ @dict.to_s
98
+ end
99
+
92
100
  private
93
101
 
94
102
  def has_key?(name)
@@ -12,17 +12,11 @@ module Slim
12
12
 
13
13
  def initialize(opts = {})
14
14
  super
15
- access = options[:dictionary_access]
16
- if access == :wrapped
17
- warn 'Slim::LogicLess - Wrapped dictionary access is deprecated and unsupported in Slim 2.0'
18
- access = DEFAULT_ACCESS_ORDER
19
- else
20
- access = [access].flatten.compact
21
- access.each do |type|
22
- raise ArgumentError, "Invalid dictionary access #{type.inspect}" unless DEFAULT_ACCESS_ORDER.include?(type)
23
- end
24
- raise ArgumentError, 'Option dictionary access is missing' if access.empty?
15
+ access = [options[:dictionary_access]].flatten.compact
16
+ access.each do |type|
17
+ raise ArgumentError, "Invalid dictionary access #{type.inspect}" unless DEFAULT_ACCESS_ORDER.include?(type)
25
18
  end
19
+ raise ArgumentError, 'Option dictionary access is missing' if access.empty?
26
20
  @access = access.inspect
27
21
  end
28
22
 
@@ -73,7 +67,14 @@ module Slim
73
67
  private
74
68
 
75
69
  def access(name)
76
- name == 'yield' ? name : "#{@context}[#{name.to_sym.inspect}]"
70
+ case name
71
+ when 'yield'
72
+ 'yield'
73
+ when 'self'
74
+ "#{@context}.to_s"
75
+ else
76
+ "#{@context}[#{name.to_sym.inspect}]"
77
+ end
77
78
  end
78
79
  end
79
80
  end
data/lib/slim/parser.rb CHANGED
@@ -1,12 +1,11 @@
1
+ # coding: utf-8
1
2
  module Slim
2
3
  # Parses Slim code and transforms it to a Temple expression
3
4
  # @api private
4
5
  class Parser < Temple::Parser
5
6
  define_options :file,
6
7
  :default_tag,
7
- :escape_quoted_attrs,
8
8
  :tabsize => 4,
9
- :encoding => 'utf-8',
10
9
  :shortcut => {
11
10
  '#' => { :attr => 'id' },
12
11
  '.' => { :attr => 'class' }
@@ -46,16 +45,15 @@ module Slim
46
45
  end
47
46
  @tag_shortcut, @attr_shortcut = {}, {}
48
47
  options[:shortcut].each do |k,v|
49
- v = deprecated_shortcut(v) if String === v
50
48
  raise ArgumentError, 'Shortcut requires :tag and/or :attr' unless (v[:attr] || v[:tag]) && (v.keys - [:attr, :tag]).empty?
51
49
  @tag_shortcut[k] = v[:tag] || options[:default_tag]
52
50
  if v.include?(:attr)
53
51
  @attr_shortcut[k] = v[:attr]
54
- raise ArgumentError, 'You can only use special characters for attribute shortcuts' if k =~ /[\w-]/
52
+ raise ArgumentError, 'You can only use special characters for attribute shortcuts' if k =~ /(#{WORD_RE}|-)/
55
53
  end
56
54
  end
57
- @attr_shortcut_regex = /\A(#{Regexp.union @attr_shortcut.keys})(\w[\w-]*\w|\w+)/
58
- @tag_regex = /\A(?:#{Regexp.union @tag_shortcut.keys}|\*(?=[^\s]+)|(\w[\w:-]*\w|\w+))/
55
+ @attr_shortcut_re = /\A(#{Regexp.union @attr_shortcut.keys})(#{WORD_RE}(?:#{WORD_RE}|-)*#{WORD_RE}|#{WORD_RE}+)/
56
+ @tag_re = /\A(?:#{Regexp.union @tag_shortcut.keys}|\*(?=[^\s]+)|(#{WORD_RE}(?:#{WORD_RE}|:|-)*#{WORD_RE}|#{WORD_RE}+))/
59
57
  end
60
58
 
61
59
  # Compile string to Temple expression
@@ -63,8 +61,6 @@ module Slim
63
61
  # @param [String] str Slim code
64
62
  # @return [Array] Temple expression representing the code]]
65
63
  def call(str)
66
- str = remove_bom(set_encoding(str))
67
-
68
64
  result = [:multi]
69
65
  reset(str.split(/\r?\n/), [result])
70
66
 
@@ -76,57 +72,18 @@ module Slim
76
72
 
77
73
  protected
78
74
 
79
- DELIMITERS = {
75
+ DELIMS = {
80
76
  '(' => ')',
81
77
  '[' => ']',
82
78
  '{' => '}',
83
79
  }.freeze
84
80
 
85
- ATTR_DELIM_REGEX = /\A\s*[#{Regexp.escape DELIMITERS.keys.join}]/
86
- DELIMITER_REGEX = /\A[#{Regexp.escape DELIMITERS.keys.join}]/
87
- ATTR_NAME = '\A\s*(\w[:\w-]*)'
88
- QUOTED_ATTR_REGEX = /#{ATTR_NAME}=(=?)("|')/
89
- CODE_ATTR_REGEX = /#{ATTR_NAME}=(=?)/
90
- QUOTED_ATTR_REGEX_20 = /#{ATTR_NAME}\s*=(=?)\s*("|')/
91
- CODE_ATTR_REGEX_20 = /#{ATTR_NAME}\s*=(=?)\s*/
92
-
93
- # Convert deprecated string shortcut to hash
94
- def deprecated_shortcut(v)
95
- warn "Slim :shortcut string values are deprecated and unsupported in Slim 2.0, use hash like { '#' => { :tag => 'div', :attr => 'id' } }"
96
- if v =~ /\A([^\s]+)\s+([^\s]+)\Z/
97
- { :tag => $1, :attr => $2 }
98
- else
99
- { :attr => v }
100
- end
101
- end
102
-
103
- # Set string encoding if option is set
104
- def set_encoding(s)
105
- if options[:encoding] && s.respond_to?(:encoding)
106
- old_enc = s.encoding
107
- s = s.dup if s.frozen?
108
- s.force_encoding(options[:encoding])
109
- # Fall back to old encoding if new encoding is invalid
110
- unless s.valid_encoding?
111
- s.force_encoding(old_enc)
112
- s.force_encoding(Encoding::BINARY) unless s.valid_encoding?
113
- end
114
- end
115
- s
116
- end
117
-
118
- # Remove unicode byte order mark from string
119
- def remove_bom(s)
120
- if s.respond_to?(:encoding)
121
- if s.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?/
122
- s.gsub(Regexp.new("\\A\uFEFF".encode(s.encoding.name)), '')
123
- else
124
- s
125
- end
126
- else
127
- s.gsub(/\A\xEF\xBB\xBF/, '')
128
- end
129
- end
81
+ WORD_RE = ''.respond_to?(:encoding) ? '\p{Word}' : '\w'
82
+ DELIM_RE = /\A[#{Regexp.escape DELIMS.keys.join}]/
83
+ ATTR_DELIM_RE = /\A\s*([#{Regexp.escape DELIMS.keys.join}])/
84
+ ATTR_NAME = "\\A\\s*(#{WORD_RE}(?:#{WORD_RE}|:|-)*)"
85
+ QUOTED_ATTR_RE = /#{ATTR_NAME}\s*=(=?)\s*("|')/
86
+ CODE_ATTR_RE = /#{ATTR_NAME}\s*=(=?)\s*/
130
87
 
131
88
  def reset(lines = nil, stacks = nil)
132
89
  # Since you can indent however you like in Slim, we need to keep a list
@@ -237,7 +194,6 @@ module Slim
237
194
  @stacks.last << [:static, ' '] if trailing_ws
238
195
  when /\A</
239
196
  # Inline html
240
- # @stacks.last << parse_text_block(@line, @indents.last + 1)
241
197
  block = [:multi]
242
198
  @stacks.last << [:multi, [:slim, :interpolate, @line], block]
243
199
  @stacks << block
@@ -248,14 +204,15 @@ module Slim
248
204
  block = [:multi]
249
205
  @stacks.last << [:slim, :control, parse_broken_line, block]
250
206
  @stacks << block
251
- when /\A=/
207
+ when /\A=(=?)(['<>]*)/
252
208
  # Found an output block.
253
209
  # We expect the line to be broken or the next line to be indented.
254
- @line =~ /\A=(=?)('?)/
255
210
  @line = $'
211
+ trailing_ws = $2.include?('\'') || $2.include?('>')
256
212
  block = [:multi]
213
+ @stacks.last << [:static, ' '] if $2.include?('<')
257
214
  @stacks.last << [:slim, :output, $1.empty?, parse_broken_line, block]
258
- @stacks.last << [:static, ' '] unless $2.empty?
215
+ @stacks.last << [:static, ' '] if trailing_ws
259
216
  @stacks << block
260
217
  when /\A(\w+):\s*\Z/
261
218
  # Embedded template detected. It is treated as block.
@@ -263,7 +220,7 @@ module Slim
263
220
  when /\Adoctype\s+/i
264
221
  # Found doctype declaration
265
222
  @stacks.last << [:html, :doctype, $'.strip]
266
- when @tag_regex
223
+ when @tag_re
267
224
  # Found a HTML tag.
268
225
  @line = $' if $1
269
226
  parse_tag($&)
@@ -340,14 +297,33 @@ module Slim
340
297
  tag = @tag_shortcut[tag]
341
298
  end
342
299
 
343
- tag = [:html, :tag, tag, parse_attributes]
300
+ # Find any shortcut attributes
301
+ attributes = [:html, :attrs]
302
+ while @line =~ @attr_shortcut_re
303
+ # The class/id attribute is :static instead of :slim :interpolate,
304
+ # because we don't want text interpolation in .class or #id shortcut
305
+ attributes << [:html, :attr, @attr_shortcut[$1], [:static, $2]]
306
+ @line = $'
307
+ end
308
+
309
+ @line =~ /\A[<>']*/
310
+ @line = $'
311
+ trailing_ws = $&.include?('\'') || $&.include?('>')
312
+ leading_ws = $&.include?('<')
313
+
314
+ parse_attributes(attributes)
315
+
316
+ tag = [:html, :tag, tag, attributes]
317
+
318
+ @stacks.last << [:static, ' '] if leading_ws
344
319
  @stacks.last << tag
320
+ @stacks.last << [:static, ' '] if trailing_ws
345
321
 
346
322
  case @line
347
323
  when /\A\s*:\s*/
348
324
  # Block expansion
349
325
  @line = $'
350
- (@line =~ @tag_regex) || syntax_error!('Expected tag')
326
+ (@line =~ @tag_re) || syntax_error!('Expected tag')
351
327
  @line = $' if $1
352
328
  content = [:multi]
353
329
  tag << content
@@ -355,12 +331,14 @@ module Slim
355
331
  @stacks << content
356
332
  parse_tag($&)
357
333
  @stacks.delete_at(i)
358
- when /\A\s*=(=?)('?)/
334
+ when /\A\s*=(=?)(['<>]*)/
359
335
  # Handle output code
360
336
  @line = $'
337
+ trailing_ws2 = $2.include?('\'') || $2.include?('>')
361
338
  block = [:multi]
339
+ @stacks.last << [:static, ' '] if !leading_ws && $2.include?('<')
362
340
  tag << [:slim, :output, $1 != '=', parse_broken_line, block]
363
- @stacks.last << [:static, ' '] unless $2.empty?
341
+ @stacks.last << [:static, ' '] if !trailing_ws && trailing_ws2
364
342
  @stacks << block
365
343
  when /\A\s*\/\s*/
366
344
  # Closed tag. Do nothing
@@ -377,31 +355,17 @@ module Slim
377
355
  end
378
356
  end
379
357
 
380
- def parse_attributes
381
- attributes = [:html, :attrs]
382
-
383
- # Find any shortcut attributes
384
- while @line =~ @attr_shortcut_regex
385
- # The class/id attribute is :static instead of :slim :interpolate,
386
- # because we don't want text interpolation in .class or #id shortcut
387
- attributes << [:html, :attr, @attr_shortcut[$1], [:static, $2]]
388
- @line = $'
389
- end
390
-
358
+ def parse_attributes(attributes)
391
359
  # Check to see if there is a delimiter right after the tag name
392
360
  delimiter = nil
393
- if @line =~ ATTR_DELIM_REGEX
394
- if $&.size > 1
395
- warn "#{options[:file]}:#{@lineno} - spaces around attribute delimiters will be allowed in Slim 2.0. Your code is incompatible."
396
- else
397
- delimiter = DELIMITERS[$&]
398
- @line.slice!(0)
399
- end
361
+ if @line =~ ATTR_DELIM_RE
362
+ delimiter = DELIMS[$1]
363
+ @line = $'
400
364
  end
401
365
 
402
366
  if delimiter
403
- boolean_attr_regex = /#{ATTR_NAME}(?=(\s|#{Regexp.escape delimiter}|\Z))/
404
- end_regex = /\A\s*#{Regexp.escape delimiter}/
367
+ boolean_attr_re = /#{ATTR_NAME}(?=(\s|#{Regexp.escape delimiter}|\Z))/
368
+ end_re = /\A\s*#{Regexp.escape delimiter}/
405
369
  end
406
370
 
407
371
  while true
@@ -410,46 +374,28 @@ module Slim
410
374
  # Splat attribute
411
375
  @line = $'
412
376
  attributes << [:slim, :splat, parse_ruby_code(delimiter)]
413
- when QUOTED_ATTR_REGEX
377
+ when QUOTED_ATTR_RE
414
378
  # Value is quoted (static)
415
379
  @line = $'
416
- name = $1
417
- escape = $2.empty?
418
- quote = $3
419
- value = parse_quoted_attribute(quote)
420
- if escape && !options[:escape_quoted_attrs] && value =~ /&(amp|quot|gt|lt);/
421
- warn "#{options[:file]}:#{@lineno} - quoted attribute value '#{value}' might be double escaped in Slim 2.0. Remove manually escaped entities and set :escape_quoted_attrs => true! :escaped_quoted_attrs is activated on Slim 2.0 by default."
422
- end
423
- attributes << [:html, :attr, name,
424
- [:escape, options[:escape_quoted_attrs] && escape,
425
- [:slim, :interpolate, value]]]
426
- when CODE_ATTR_REGEX
380
+ attributes << [:html, :attr, $1,
381
+ [:escape, $2.empty?, [:slim, :interpolate, parse_quoted_attribute($3)]]]
382
+ when CODE_ATTR_RE
427
383
  # Value is ruby code
428
384
  @line = $'
429
385
  name = $1
430
386
  escape = $2.empty?
431
387
  value = parse_ruby_code(delimiter)
432
- # Remove attribute wrapper which doesn't belong to the ruby code
433
- # e.g id=[hash[:a] + hash[:b]]
434
- if value =~ /\A[\[\{]/ && DELIMITERS[$&] == value[-1, 1]
435
- warn "#{options[:file]}:#{@lineno} - ruby attribute value #{value} with curly braces/brackets is deprecated and unsupported in Slim 2.0. Use parentheses!"
436
- value = value[1..-2]
437
- end
438
388
  syntax_error!('Invalid empty attribute') if value.empty?
439
389
  attributes << [:html, :attr, name, [:slim, :attrvalue, escape, value]]
440
390
  else
441
- if @line =~ QUOTED_ATTR_REGEX_20 || @line =~ CODE_ATTR_REGEX_20
442
- warn "#{options[:file]}:#{@lineno} - you have spaces around =, this will be interpreted as attribute in Slim 2.0."
443
- end
444
-
445
391
  break unless delimiter
446
392
 
447
393
  case @line
448
- when boolean_attr_regex
394
+ when boolean_attr_re
449
395
  # Boolean attribute
450
396
  @line = $'
451
397
  attributes << [:html, :attr, $1, [:slim, :attrvalue, false, 'true']]
452
- when end_regex
398
+ when end_re
453
399
  # Find ending delimiter
454
400
  @line = $'
455
401
  break
@@ -465,17 +411,15 @@ module Slim
465
411
  end
466
412
  end
467
413
  end
468
-
469
- attributes
470
414
  end
471
415
 
472
416
  def parse_ruby_code(outer_delimiter)
473
417
  code, count, delimiter, close_delimiter = '', 0, nil, nil
474
418
 
475
419
  # Attribute ends with space or attribute delimiter
476
- end_regex = /\A[\s#{Regexp.escape outer_delimiter.to_s}]/
420
+ end_re = /\A[\s#{Regexp.escape outer_delimiter.to_s}]/
477
421
 
478
- until @line.empty? || (count == 0 && @line =~ end_regex)
422
+ until @line.empty? || (count == 0 && @line =~ end_re)
479
423
  if @line =~ /\A[,\\]\Z/
480
424
  code << @line << "\n"
481
425
  expect_next_line
@@ -486,9 +430,9 @@ module Slim
486
430
  elsif @line[0] == close_delimiter[0]
487
431
  count -= 1
488
432
  end
489
- elsif @line =~ DELIMITER_REGEX
433
+ elsif @line =~ DELIM_RE
490
434
  count = 1
491
- delimiter, close_delimiter = $&, DELIMITERS[$&]
435
+ delimiter, close_delimiter = $&, DELIMS[$&]
492
436
  end
493
437
  code << @line.slice!(0)
494
438
  end