slim 1.3.0 → 1.3.2
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.
- data/.travis.yml +9 -5
- data/CHANGES +23 -0
- data/Gemfile +13 -2
- data/README.md +346 -105
- data/Rakefile +21 -9
- data/lib/slim.rb +3 -3
- data/lib/slim/boolean_attributes.rb +67 -0
- data/lib/slim/command.rb +23 -23
- data/lib/slim/control_structures.rb +57 -0
- data/lib/slim/embedded_engine.rb +80 -43
- data/lib/slim/end_inserter.rb +1 -1
- data/lib/slim/engine.rb +21 -15
- data/lib/slim/filter.rb +0 -6
- data/lib/slim/grammar.rb +2 -7
- data/lib/slim/interpolation.rb +1 -1
- data/lib/slim/logic_less/filter.rb +8 -8
- data/lib/slim/logic_less/wrapper.rb +1 -1
- data/lib/slim/parser.rb +51 -52
- data/lib/slim/splat_attributes.rb +112 -0
- data/lib/slim/translator.rb +13 -12
- data/lib/slim/version.rb +1 -1
- data/slim.gemspec +1 -1
- data/test/{slim → core}/helper.rb +3 -7
- data/test/{slim → core}/test_code_blocks.rb +0 -0
- data/test/{slim → core}/test_code_escaping.rb +4 -4
- data/test/{slim → core}/test_code_evaluation.rb +3 -112
- data/test/{slim → core}/test_code_output.rb +0 -0
- data/test/{slim → core}/test_code_structure.rb +0 -0
- data/test/{slim → core}/test_embedded_engines.rb +8 -3
- data/test/{slim → core}/test_encoding.rb +0 -0
- data/test/core/test_html_attributes.rb +218 -0
- data/test/{slim → core}/test_html_escaping.rb +17 -0
- data/test/{slim → core}/test_html_structure.rb +13 -98
- data/test/{slim → core}/test_parser_errors.rb +24 -15
- data/test/{slim → core}/test_pretty.rb +0 -0
- data/test/{slim → core}/test_ruby_errors.rb +7 -0
- data/test/{slim → core}/test_slim_template.rb +0 -0
- data/test/{slim → core}/test_text_interpolation.rb +2 -2
- data/test/core/test_thread_options.rb +18 -0
- data/test/{slim/logic_less → logic_less}/test_logic_less.rb +21 -0
- data/test/{slim/logic_less → logic_less}/test_wrapper.rb +3 -3
- data/test/rails/app/controllers/slim_controller.rb +4 -2
- data/test/rails/app/views/parents/_form.html.slim +1 -0
- data/test/rails/app/views/parents/edit.html.slim +2 -1
- data/test/rails/app/views/parents/new.html.slim +2 -1
- data/test/rails/app/views/slim/thread_options.html.slim +1 -0
- data/test/rails/test/test_slim.rb +6 -4
- data/test/{slim/translator → translator}/test_translator.rb +0 -0
- metadata +44 -82
- data/lib/slim/compiler.rb +0 -194
- data/test/rails/app/views/slim/nil.html.slim +0 -1
- data/test/slim/test_chain_manipulation.rb +0 -42
data/lib/slim/end_inserter.rb
CHANGED
@@ -24,7 +24,7 @@ module Slim
|
|
24
24
|
|
25
25
|
exps.each do |exp|
|
26
26
|
if control?(exp)
|
27
|
-
raise 'Explicit end statements are forbidden' if exp[2] =~ END_REGEX
|
27
|
+
raise(Temple::FilterError, 'Explicit end statements are forbidden') if exp[2] =~ END_REGEX
|
28
28
|
|
29
29
|
# Two control code in a row. If this one is *not*
|
30
30
|
# an else block, we should close the previous one.
|
data/lib/slim/engine.rb
CHANGED
@@ -4,29 +4,35 @@ module Slim
|
|
4
4
|
class Engine < Temple::Engine
|
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
|
-
# `
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
:default_tag => 'div'
|
7
|
+
# `define_options` when you have to override some default settings.
|
8
|
+
define_options :pretty => false,
|
9
|
+
:sort_attrs => true,
|
10
|
+
:attr_wrapper => '"',
|
11
|
+
:attr_delimiter => {'class' => ' '},
|
12
|
+
:generator => Temple::Generators::ArrayBuffer,
|
13
|
+
:default_tag => 'div'
|
15
14
|
|
16
|
-
|
15
|
+
# TODO: Remove these options in 1.4.0
|
16
|
+
define_deprecated_options :remove_empty_attrs, :chain
|
17
|
+
|
18
|
+
use Slim::Parser, :file, :tabsize, :encoding, :shortcut, :default_tag, :escape_quoted_attrs
|
17
19
|
use Slim::EmbeddedEngine, :enable_engines, :disable_engines, :pretty
|
18
20
|
use Slim::Interpolation
|
19
21
|
use Slim::EndInserter
|
20
|
-
use Slim::
|
21
|
-
|
22
|
+
use Slim::ControlStructures, :disable_capture
|
23
|
+
use Slim::SplatAttributes, :attr_delimiter, :attr_wrapper, :sort_attrs, :default_tag
|
22
24
|
html :AttributeSorter, :sort_attrs
|
23
|
-
html :
|
25
|
+
html :AttributeMerger, :attr_delimiter
|
26
|
+
use Slim::BooleanAttributes, :attr_delimiter
|
24
27
|
html :Pretty, :format, :attr_wrapper, :pretty, :indent
|
25
28
|
filter :Escapable, :use_html_safe, :disable_escape
|
26
29
|
filter :ControlFlow
|
27
30
|
filter :MultiFlattener
|
28
|
-
use
|
29
|
-
|
30
|
-
|
31
|
+
use :Optimizer do
|
32
|
+
(options[:streaming] ? Temple::Filters::StaticMerger : Temple::Filters::DynamicInliner).new
|
33
|
+
end
|
34
|
+
use :Generator do
|
35
|
+
options[:generator].new(options.to_hash.reject {|k,v| !options[:generator].default_options.valid_keys.include?(k) })
|
36
|
+
end
|
31
37
|
end
|
32
38
|
end
|
data/lib/slim/filter.rb
CHANGED
@@ -31,11 +31,5 @@ module Slim
|
|
31
31
|
def on_slim_attrs(*attrs)
|
32
32
|
[:slim, :attrs, *attrs.map {|a| compile(a) }]
|
33
33
|
end
|
34
|
-
|
35
|
-
# Pass-through handler
|
36
|
-
def on_slim_tag(name, attrs, content = nil)
|
37
|
-
tag = [:slim, :tag, name, compile(attrs)]
|
38
|
-
content ? (tag << compile(content)) : tag
|
39
|
-
end
|
40
34
|
end
|
41
35
|
end
|
data/lib/slim/grammar.rb
CHANGED
@@ -10,14 +10,9 @@ module Slim
|
|
10
10
|
[:slim, :interpolate, String] |
|
11
11
|
[:slim, :embedded, String, Expression] |
|
12
12
|
[:slim, :text, Expression] |
|
13
|
-
[:slim, :
|
13
|
+
[:slim, :attrvalue, Bool, String]
|
14
14
|
|
15
|
-
|
16
|
-
[:slim, :attrs, 'SlimAttr*']
|
17
|
-
|
18
|
-
SlimAttr <<
|
19
|
-
HTMLAttr |
|
20
|
-
[:slim, :attr, String, Bool, String] |
|
15
|
+
HTMLAttr <<
|
21
16
|
[:slim, :splat, String]
|
22
17
|
end
|
23
18
|
end
|
data/lib/slim/interpolation.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
module Slim
|
2
|
-
# Handle logic
|
2
|
+
# Handle logic less mode
|
3
3
|
# This filter can be activated with the option "logic_less"
|
4
4
|
# @api private
|
5
5
|
class LogicLess < Filter
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
define_options :logic_less => true,
|
7
|
+
:dictionary => 'self',
|
8
|
+
:dictionary_access => :wrapped # :symbol, :string, :wrapped
|
9
9
|
|
10
10
|
def initialize(opts = {})
|
11
11
|
super
|
12
12
|
unless [:string, :symbol, :wrapped].include?(options[:dictionary_access])
|
13
|
-
raise "Invalid dictionary access #{options[:dictionary_access].inspect}"
|
13
|
+
raise ArgumentError, "Invalid dictionary access #{options[:dictionary_access].inspect}"
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -37,7 +37,7 @@ module Slim
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def on_slim_output(escape, name, content)
|
40
|
-
raise 'Output statements with content are forbidden in logic less mode' if !empty_exp?(content)
|
40
|
+
raise(Temple::FilterError, 'Output statements with content are forbidden in logic less mode') if !empty_exp?(content)
|
41
41
|
[:slim, :output, escape, access(name), content]
|
42
42
|
end
|
43
43
|
|
@@ -50,11 +50,11 @@ module Slim
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def on_dynamic(code)
|
53
|
-
raise 'Embedded code is forbidden in logic less mode'
|
53
|
+
raise Temple::FilterError, 'Embedded code is forbidden in logic less mode'
|
54
54
|
end
|
55
55
|
|
56
56
|
def on_code(code)
|
57
|
-
raise 'Embedded code is forbidden in logic less mode'
|
57
|
+
raise Temple::FilterError, 'Embedded code is forbidden in logic less mode'
|
58
58
|
end
|
59
59
|
|
60
60
|
protected
|
data/lib/slim/parser.rb
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
module Slim
|
2
2
|
# Parses Slim code and transforms it to a Temple expression
|
3
3
|
# @api private
|
4
|
-
class Parser
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
4
|
+
class Parser < Temple::Parser
|
5
|
+
define_options :file,
|
6
|
+
:default_tag,
|
7
|
+
:escape_quoted_attrs => false,
|
8
|
+
:tabsize => 4,
|
9
|
+
:encoding => 'utf-8',
|
10
|
+
:shortcut => {
|
11
|
+
'#' => 'id',
|
12
|
+
'.' => 'class'
|
13
|
+
}
|
13
14
|
|
14
15
|
class SyntaxError < StandardError
|
15
16
|
attr_reader :error, :file, :line, :lineno, :column
|
@@ -26,22 +27,22 @@ module Slim
|
|
26
27
|
line = @line.strip
|
27
28
|
column = @column + line.size - @line.size
|
28
29
|
%{#{error}
|
29
|
-
#{file}, Line #{lineno}
|
30
|
+
#{file}, Line #{lineno}, Column #{@column}
|
30
31
|
#{line}
|
31
32
|
#{' ' * column}^
|
32
33
|
}
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
36
|
-
def initialize(
|
37
|
+
def initialize(opts = {})
|
37
38
|
super
|
38
|
-
@tab = ' ' *
|
39
|
+
@tab = ' ' * options[:tabsize]
|
39
40
|
@shortcut = {}
|
40
|
-
|
41
|
+
options[:shortcut].each do |k,v|
|
41
42
|
@shortcut[k] = if v =~ /\A([^\s]+)\s+([^\s]+)\Z/
|
42
43
|
[$1, $2]
|
43
44
|
else
|
44
|
-
[
|
45
|
+
[options[:default_tag], v]
|
45
46
|
end
|
46
47
|
end
|
47
48
|
shortcut = "[#{Regexp.escape @shortcut.keys.join}]"
|
@@ -72,7 +73,7 @@ module Slim
|
|
72
73
|
result
|
73
74
|
end
|
74
75
|
|
75
|
-
|
76
|
+
protected
|
76
77
|
|
77
78
|
DELIMITERS = {
|
78
79
|
'(' => ')',
|
@@ -82,8 +83,8 @@ module Slim
|
|
82
83
|
|
83
84
|
DELIMITER_REGEX = /\A[#{Regexp.escape DELIMITERS.keys.join}]/
|
84
85
|
ATTR_NAME = '\A\s*(\w[:\w-]*)'
|
85
|
-
QUOTED_ATTR_REGEX = /#{ATTR_NAME}=("|')/
|
86
|
-
CODE_ATTR_REGEX = /#{ATTR_NAME}
|
86
|
+
QUOTED_ATTR_REGEX = /#{ATTR_NAME}=(=?)("|')/
|
87
|
+
CODE_ATTR_REGEX = /#{ATTR_NAME}=(=?)/
|
87
88
|
|
88
89
|
def reset(lines = nil, stacks = nil)
|
89
90
|
# Since you can indent however you like in Slim, we need to keep a list
|
@@ -176,25 +177,28 @@ module Slim
|
|
176
177
|
|
177
178
|
def parse_line_indicators
|
178
179
|
case @line
|
180
|
+
when /\A\/!( ?)/
|
181
|
+
# HTML comment
|
182
|
+
@stacks.last << [:html, :comment, [:slim, :text, parse_text_block($', @indents.last + $1.size + 2)]]
|
183
|
+
when /\A\/\[\s*(.*?)\s*\]\s*\Z/
|
184
|
+
# HTML conditional comment
|
185
|
+
block = [:multi]
|
186
|
+
@stacks.last << [:html, :condcomment, $1, block]
|
187
|
+
@stacks << block
|
179
188
|
when /\A\//
|
180
|
-
#
|
181
|
-
|
182
|
-
|
183
|
-
@stacks.last << [:html, :comment, [:slim, :text, parse_text_block($2, @indents.last + $1.size + 2)]]
|
184
|
-
elsif @line =~ %r{\A/\[\s*(.*?)\s*\]\s*\Z}
|
185
|
-
# HTML conditional comment
|
186
|
-
block = [:multi]
|
187
|
-
@stacks.last << [:html, :condcomment, $1, block]
|
188
|
-
@stacks << block
|
189
|
-
else
|
190
|
-
# Slim comment
|
191
|
-
parse_comment_block
|
192
|
-
end
|
193
|
-
when /\A([\|'])( ?)(.*)\Z/
|
189
|
+
# Slim comment
|
190
|
+
parse_comment_block
|
191
|
+
when /\A([\|'])( ?)/
|
194
192
|
# Found a text block.
|
195
193
|
trailing_ws = $1 == "'"
|
196
|
-
@stacks.last << [:slim, :text, parse_text_block($
|
194
|
+
@stacks.last << [:slim, :text, parse_text_block($', @indents.last + $2.size + 1)]
|
197
195
|
@stacks.last << [:static, ' '] if trailing_ws
|
196
|
+
when /\A</
|
197
|
+
# Inline html
|
198
|
+
# @stacks.last << parse_text_block(@line, @indents.last + 1)
|
199
|
+
block = [:multi]
|
200
|
+
@stacks.last << [:multi, [:slim, :interpolate, @line], block]
|
201
|
+
@stacks << block
|
198
202
|
when /\A-/
|
199
203
|
# Found a code block.
|
200
204
|
# We expect the line to be broken or the next line to be indented.
|
@@ -289,7 +293,7 @@ module Slim
|
|
289
293
|
end
|
290
294
|
|
291
295
|
def parse_tag(tag)
|
292
|
-
tag = [:
|
296
|
+
tag = [:html, :tag, @shortcut[tag] ? @shortcut[tag][0] : tag, parse_attributes]
|
293
297
|
@stacks.last << tag
|
294
298
|
|
295
299
|
case @line
|
@@ -325,7 +329,7 @@ module Slim
|
|
325
329
|
end
|
326
330
|
|
327
331
|
def parse_attributes
|
328
|
-
attributes = [:
|
332
|
+
attributes = [:html, :attrs]
|
329
333
|
|
330
334
|
# Find any shortcut attributes
|
331
335
|
while @line =~ @shortcut_regex
|
@@ -356,20 +360,21 @@ module Slim
|
|
356
360
|
when QUOTED_ATTR_REGEX
|
357
361
|
# Value is quoted (static)
|
358
362
|
@line = $'
|
359
|
-
attributes << [:html, :attr, $1,
|
363
|
+
attributes << [:html, :attr, $1,
|
364
|
+
[:escape, options[:escape_quoted_attrs] && $2.empty?,
|
365
|
+
[:slim, :interpolate, parse_quoted_attribute($3)]]]
|
360
366
|
when CODE_ATTR_REGEX
|
361
367
|
# Value is ruby code
|
362
368
|
@line = $'
|
363
|
-
escape = @line[0] != ?=
|
364
|
-
@line.slice!(0) unless escape
|
365
369
|
name = $1
|
370
|
+
escape = $2.empty?
|
366
371
|
value = parse_ruby_code(delimiter)
|
367
372
|
# Remove attribute wrapper which doesn't belong to the ruby code
|
368
373
|
# e.g id=[hash[:a] + hash[:b]]
|
369
374
|
value = value[1..-2] if value =~ DELIMITER_REGEX &&
|
370
375
|
DELIMITERS[$&] == value[-1, 1]
|
371
376
|
syntax_error!('Invalid empty attribute') if value.empty?
|
372
|
-
attributes << [:
|
377
|
+
attributes << [:html, :attr, name, [:slim, :attrvalue, escape, value]]
|
373
378
|
else
|
374
379
|
break unless delimiter
|
375
380
|
|
@@ -377,7 +382,7 @@ module Slim
|
|
377
382
|
when boolean_attr_regex
|
378
383
|
# Boolean attribute
|
379
384
|
@line = $'
|
380
|
-
attributes << [:
|
385
|
+
attributes << [:html, :attr, $1, [:slim, :attrvalue, false, 'true']]
|
381
386
|
when end_regex
|
382
387
|
# Find ending delimiter
|
383
388
|
@line = $'
|
@@ -389,11 +394,8 @@ module Slim
|
|
389
394
|
|
390
395
|
# Attributes span multiple lines
|
391
396
|
@stacks.last << [:newline]
|
392
|
-
|
393
|
-
next_line
|
394
|
-
:orig_line => orig_line,
|
395
|
-
:lineno => lineno,
|
396
|
-
:column => orig_line.size)
|
397
|
+
syntax_error!("Expected closing delimiter #{delimiter}") if @lines.empty?
|
398
|
+
next_line
|
397
399
|
end
|
398
400
|
end
|
399
401
|
end
|
@@ -442,19 +444,16 @@ module Slim
|
|
442
444
|
end
|
443
445
|
|
444
446
|
syntax_error!("Expected closing brace }") if count != 0
|
447
|
+
syntax_error!("Expected closing quote #{quote}") if @line[0] != quote[0]
|
445
448
|
@line.slice!(0)
|
449
|
+
|
446
450
|
value
|
447
451
|
end
|
448
452
|
|
449
453
|
# Helper for raising exceptions
|
450
|
-
def syntax_error!(message
|
451
|
-
|
452
|
-
|
453
|
-
args[:lineno] ||= @lineno
|
454
|
-
args[:column] ||= args[:orig_line] && args[:line] ?
|
455
|
-
args[:orig_line].size - args[:line].size : 0
|
456
|
-
raise SyntaxError.new(message, options[:file],
|
457
|
-
args[:orig_line], args[:lineno], args[:column])
|
454
|
+
def syntax_error!(message)
|
455
|
+
raise SyntaxError.new(message, options[:file], @orig_line, @lineno,
|
456
|
+
@orig_line && @line ? @orig_line.size - @line.size : 0)
|
458
457
|
end
|
459
458
|
end
|
460
459
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Slim
|
2
|
+
# @api private
|
3
|
+
class SplatAttributes < Filter
|
4
|
+
define_options :attr_delimiter, :attr_wrapper, :sort_attrs, :default_tag
|
5
|
+
|
6
|
+
def call(exp)
|
7
|
+
@attr_delimiter, @splat_used = unique_name, false
|
8
|
+
exp = compile(exp)
|
9
|
+
if @splat_used
|
10
|
+
[:multi, [:code, "#{@attr_delimiter} = #{@options[:attr_delimiter].inspect}"], exp]
|
11
|
+
else
|
12
|
+
exp
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Handle tag expression `[:html, :tag, name, attrs, content]`
|
17
|
+
#
|
18
|
+
# @param [String] name Tag name
|
19
|
+
# @param [Array] attrs Temple expression
|
20
|
+
# @param [Array] content Temple expression
|
21
|
+
# @return [Array] Compiled temple expression
|
22
|
+
def on_html_tag(name, attrs, content = nil)
|
23
|
+
return super if name != '*'
|
24
|
+
hash, merger, formatter = splat_attributes(attrs[2..-1])
|
25
|
+
tmp = unique_name
|
26
|
+
tag = [:multi,
|
27
|
+
merger,
|
28
|
+
[:code, "#{tmp} = #{hash}.delete('tag').to_s"],
|
29
|
+
[:if, "#{tmp}.empty?",
|
30
|
+
[:code, "#{tmp} = #{@options[:default_tag].inspect}"]],
|
31
|
+
[:static, '<'],
|
32
|
+
[:dynamic, "#{tmp}"],
|
33
|
+
formatter]
|
34
|
+
tag << if content
|
35
|
+
[:multi,
|
36
|
+
[:static, '>'],
|
37
|
+
compile(content),
|
38
|
+
[:static, '</'],
|
39
|
+
[:dynamic, "#{tmp}"],
|
40
|
+
[:static, '>']]
|
41
|
+
else
|
42
|
+
[:static, '/>']
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Handle attributes expression `[:html, :attrs, *attrs]`
|
47
|
+
#
|
48
|
+
# @param [Array] attrs Array of temple expressions
|
49
|
+
# @return [Array] Compiled temple expression
|
50
|
+
def on_html_attrs(*attrs)
|
51
|
+
return super if attrs.all? {|attr| attr[1] != :splat}
|
52
|
+
hash, merger, formatter = splat_attributes(attrs)
|
53
|
+
[:multi, merger, formatter]
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def splat_attributes(attrs)
|
59
|
+
@splat_used = true
|
60
|
+
|
61
|
+
hash, name, value, tmp = unique_name, unique_name, unique_name, unique_name
|
62
|
+
|
63
|
+
merger = [:multi, [:code, "#{hash} = {}"]]
|
64
|
+
attrs.each do |attr|
|
65
|
+
merger << if attr[0] == :html && attr[1] == :attr
|
66
|
+
[:multi,
|
67
|
+
[:capture, tmp, compile(attr[3])],
|
68
|
+
[:code, "(#{hash}[#{attr[2].inspect}] ||= []) << #{tmp}"]]
|
69
|
+
elsif attr[0] == :slim
|
70
|
+
if attr[1] == :attr
|
71
|
+
[:code, "(#{hash}[#{attr[2].inspect}] ||= []) << (#{attr[4]})"]
|
72
|
+
elsif attr[1] == :splat
|
73
|
+
[:code, "(#{attr[2]}).each {|#{name},#{value}| (#{hash}[#{name}.to_s] ||= []) << (#{value}) }"]
|
74
|
+
else
|
75
|
+
attr
|
76
|
+
end
|
77
|
+
else
|
78
|
+
attr
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
merger << [:block, "#{hash}.each do |#{name},#{value}|",
|
83
|
+
[:multi,
|
84
|
+
[:code, "#{value}.flatten!"],
|
85
|
+
[:if, "#{value}.size > 1 && !#{@attr_delimiter}[#{name}]",
|
86
|
+
[:code, %{raise("Multiple #\{#{name}\} attributes specified")}]],
|
87
|
+
[:code, "#{value}.compact!"],
|
88
|
+
[:case, "#{value}.size",
|
89
|
+
['0',
|
90
|
+
[:code, "#{hash}[#{name}] = nil"]],
|
91
|
+
['1',
|
92
|
+
[:case, "#{value}.first",
|
93
|
+
['true', [:code, "#{hash}[#{name}] = #{name}"]],
|
94
|
+
['false, nil', [:code, "#{hash}[#{name}] = nil"]],
|
95
|
+
[:else, [:code, "#{hash}[#{name}] = #{value}.first"]]]],
|
96
|
+
[:else,
|
97
|
+
[:code, "#{hash}[#{name}] = #{value}.join(#{@attr_delimiter}[#{name}].to_s)"]]]]]
|
98
|
+
|
99
|
+
attr = [:if, value,
|
100
|
+
[:multi,
|
101
|
+
[:static, ' '],
|
102
|
+
[:dynamic, name],
|
103
|
+
[:static, "=#{options[:attr_wrapper]}"],
|
104
|
+
[:escape, true, [:dynamic, value]],
|
105
|
+
[:static, options[:attr_wrapper]]]]
|
106
|
+
enumerator = options[:sort_attrs] ? "#{hash}.sort_by {|#{name},#{value}| #{name} }" : hash
|
107
|
+
formatter = [:block, "#{enumerator}.each do |#{name},#{value}|", attr]
|
108
|
+
|
109
|
+
return hash, merger, formatter
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|