slimi 0.7.8 → 0.8.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/lib/slimi/engine.rb +1 -0
- data/lib/slimi/errors.rb +9 -0
- data/lib/slimi/filters/splat.rb +176 -0
- data/lib/slimi/filters.rb +1 -0
- data/lib/slimi/parser.rb +12 -2
- data/lib/slimi/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f85a2a7fb060ffa915242323704ec46c52f7bc224084013c08b7832ccaaa4e2
|
|
4
|
+
data.tar.gz: d76831908983154a2f80f8f2ed433ee76e29d5d016eb7a9b29e2b0f292d7f54f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4fac9d1b99931a94dee91a14721e0f657b1abd44f8f71110240d50bed95583793f42fee67bd5c0986857fcc356aae5efc2d20c077569c7658898bbf7af2e69b2
|
|
7
|
+
data.tar.gz: b21697a773720dc4604f7a21df0625df5671c52e7b4f48bcefcfc8bb5941b8f7af3b2776e0dc881d8eef218f53bb4f551d064e5180e68408d5167f5248fc58cf
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
data/lib/slimi/engine.rb
CHANGED
data/lib/slimi/errors.rb
CHANGED
|
@@ -62,5 +62,14 @@ module Slimi
|
|
|
62
62
|
|
|
63
63
|
class UnknownLineIndicatorError < SlimSyntaxError
|
|
64
64
|
end
|
|
65
|
+
|
|
66
|
+
class RenderError < BaseError
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class InvalidAttributeNameError < RenderError
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class MultipleAttributesError < RenderError
|
|
73
|
+
end
|
|
65
74
|
end
|
|
66
75
|
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Slimi
|
|
4
|
+
module Filters
|
|
5
|
+
class Splat < Base
|
|
6
|
+
define_options :attr_quote, :format, :merge_attrs, :sort_attrs, use_html_safe: ''.respond_to?(:html_safe?)
|
|
7
|
+
|
|
8
|
+
# @param [Array] exp
|
|
9
|
+
# @return [Array]
|
|
10
|
+
def call(exp)
|
|
11
|
+
@splat_options = nil
|
|
12
|
+
exp = compile(exp)
|
|
13
|
+
if @splat_options
|
|
14
|
+
[:multi, [:code, "#{@splat_options} = #{splat_builder_options.inspect}"], exp]
|
|
15
|
+
else
|
|
16
|
+
exp
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# @param [Array<Array>] attrs
|
|
21
|
+
# @return [Array]
|
|
22
|
+
def on_html_attrs(*attrs)
|
|
23
|
+
return super unless attrs.any? { |attr| splat?(attr) }
|
|
24
|
+
|
|
25
|
+
@splat_options ||= unique_name
|
|
26
|
+
builder = unique_name
|
|
27
|
+
result = [:multi]
|
|
28
|
+
result << [:code, "#{builder} = ::#{Builder.name}.new(#{@splat_options})"]
|
|
29
|
+
attrs.each do |attr|
|
|
30
|
+
result << compile_attribute(builder, attr)
|
|
31
|
+
end
|
|
32
|
+
result << [:dynamic, "#{builder}.to_s"]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# @param [String] builder
|
|
38
|
+
# @param [Array] attr
|
|
39
|
+
# @return [Array]
|
|
40
|
+
def compile_attribute(builder, attr)
|
|
41
|
+
return [:code, "#{builder}.splat((#{attr[2]}))"] if splat?(attr)
|
|
42
|
+
|
|
43
|
+
_, _, name, value = attr
|
|
44
|
+
if value[0] == :slimi && value[1] == :attrvalue
|
|
45
|
+
# Pass the raw expression and escape flag through, so that the Builder can
|
|
46
|
+
# apply the same merge / boolean / nil / escape rules as Filters::Attribute.
|
|
47
|
+
[:code, "#{builder}.code_attribute(#{name.to_s.inspect}, #{value[2]}, (#{value[3]}))"]
|
|
48
|
+
elsif value == [:multi]
|
|
49
|
+
# Boolean attribute, e.g. `div(*foo disabled)`.
|
|
50
|
+
[:code, "#{builder}.code_attribute(#{name.to_s.inspect}, false, true)"]
|
|
51
|
+
else
|
|
52
|
+
# Static or interpolated value whose escaping is compiled into the capture,
|
|
53
|
+
# so the Builder must not escape it again.
|
|
54
|
+
tmp = unique_name
|
|
55
|
+
[:multi,
|
|
56
|
+
[:capture, tmp, compile(value)],
|
|
57
|
+
[:code, "#{builder}.attribute(#{name.to_s.inspect}, #{tmp})"]]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @param [Array] attr
|
|
62
|
+
# @return [Boolean]
|
|
63
|
+
def splat?(attr)
|
|
64
|
+
attr[0] == :slimi && attr[1] == :splat
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @return [Hash]
|
|
68
|
+
def splat_builder_options
|
|
69
|
+
{
|
|
70
|
+
attr_quote: options[:attr_quote],
|
|
71
|
+
format: options[:format],
|
|
72
|
+
merge_attrs: options[:merge_attrs],
|
|
73
|
+
sort_attrs: options[:sort_attrs],
|
|
74
|
+
use_html_safe: options[:use_html_safe]
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class Builder
|
|
79
|
+
# https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
|
|
80
|
+
INVALID_ATTRIBUTE_NAME_REGEXP = %r{[ \0"'>/=]}.freeze
|
|
81
|
+
|
|
82
|
+
# @param [Hash] options
|
|
83
|
+
def initialize(options)
|
|
84
|
+
@attr_quote = options[:attr_quote]
|
|
85
|
+
@format = options[:format]
|
|
86
|
+
@merge_attrs = options[:merge_attrs] || {}
|
|
87
|
+
@sort_attrs = options.fetch(:sort_attrs, true)
|
|
88
|
+
@use_html_safe = options[:use_html_safe]
|
|
89
|
+
@attributes = {}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Add an attribute value already rendered and escaped by the compiled template.
|
|
93
|
+
# @param [String] name
|
|
94
|
+
# @param [String] value
|
|
95
|
+
def attribute(name, value)
|
|
96
|
+
store(name, value)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Add an attribute value, applying merge / boolean / nil / escape rules.
|
|
100
|
+
# @param [String] name
|
|
101
|
+
# @param [Boolean] escape
|
|
102
|
+
# @param [Object] value
|
|
103
|
+
def code_attribute(name, escape, value)
|
|
104
|
+
if (delimiter = @merge_attrs[name])
|
|
105
|
+
value = value.is_a?(::Array) ? value.flatten.map(&:to_s).reject(&:empty?).join(delimiter) : value.to_s
|
|
106
|
+
store(name, escape_html(escape, value)) unless value.empty?
|
|
107
|
+
elsif value.is_a?(::Hash)
|
|
108
|
+
value.each do |key, nested_value|
|
|
109
|
+
code_attribute("#{name}-#{key}", escape, nested_value)
|
|
110
|
+
end
|
|
111
|
+
elsif value != false && !value.nil?
|
|
112
|
+
store(name, value == true ? true : escape_html(escape, value.to_s))
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# @param [Hash] hash
|
|
117
|
+
def splat(hash)
|
|
118
|
+
hash.each do |name, value|
|
|
119
|
+
code_attribute(name.to_s, true, value)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# @return [String]
|
|
124
|
+
def to_s
|
|
125
|
+
attributes = @sort_attrs ? @attributes.sort_by(&:first) : @attributes
|
|
126
|
+
attributes.map { |name, value| render(name, value) }.join
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
# @param [String] name
|
|
132
|
+
# @param [String, true] value
|
|
133
|
+
def store(name, value)
|
|
134
|
+
raise Errors::InvalidAttributeNameError, "Invalid attribute name '#{name}' was rendered" if INVALID_ATTRIBUTE_NAME_REGEXP.match?(name)
|
|
135
|
+
|
|
136
|
+
if @attributes.key?(name)
|
|
137
|
+
delimiter = @merge_attrs[name]
|
|
138
|
+
raise Errors::MultipleAttributesError, "Multiple #{name} attributes specified" unless delimiter
|
|
139
|
+
|
|
140
|
+
@attributes[name] = "#{@attributes[name]}#{delimiter}#{value}"
|
|
141
|
+
else
|
|
142
|
+
@attributes[name] = value
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# @param [String] name
|
|
147
|
+
# @param [String, true] value
|
|
148
|
+
# @return [String]
|
|
149
|
+
def render(name, value)
|
|
150
|
+
if value == true
|
|
151
|
+
if @format == :xhtml
|
|
152
|
+
" #{name}=#{@attr_quote}#{@attr_quote}"
|
|
153
|
+
else
|
|
154
|
+
" #{name}"
|
|
155
|
+
end
|
|
156
|
+
else
|
|
157
|
+
" #{name}=#{@attr_quote}#{value}#{@attr_quote}"
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# @param [Boolean] escape
|
|
162
|
+
# @param [String] string
|
|
163
|
+
# @return [String]
|
|
164
|
+
def escape_html(escape, string)
|
|
165
|
+
return string unless escape
|
|
166
|
+
|
|
167
|
+
if @use_html_safe
|
|
168
|
+
::Temple::Utils.escape_html_safe(string)
|
|
169
|
+
else
|
|
170
|
+
::Temple::Utils.escape_html(string)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
data/lib/slimi/filters.rb
CHANGED
|
@@ -11,6 +11,7 @@ module Slimi
|
|
|
11
11
|
autoload :EndInserter, 'slimi/filters/end_inserter'
|
|
12
12
|
autoload :Interpolation, 'slimi/filters/interpolation'
|
|
13
13
|
autoload :Output, 'slimi/filters/output'
|
|
14
|
+
autoload :Splat, 'slimi/filters/splat'
|
|
14
15
|
autoload :Text, 'slimi/filters/text'
|
|
15
16
|
autoload :Unposition, 'slimi/filters/unposition'
|
|
16
17
|
end
|
data/lib/slimi/parser.rb
CHANGED
|
@@ -38,6 +38,7 @@ module Slimi
|
|
|
38
38
|
@attribute_shortcut_regexp = factory.attribute_shortcut_regexp
|
|
39
39
|
@attribute_delimiter_regexp = factory.attribute_delimiter_regexp
|
|
40
40
|
@quoted_attribute_regexp = factory.quoted_attribute_regexp
|
|
41
|
+
@splat_attribute_regexp = factory.splat_attribute_regexp
|
|
41
42
|
@tag_name_regexp = factory.tag_name_regexp
|
|
42
43
|
@attribute_name_regexp = factory.attribute_name_regexp
|
|
43
44
|
@ruby_attribute_regexp = factory.ruby_attribute_regexp
|
|
@@ -259,9 +260,13 @@ module Slimi
|
|
|
259
260
|
attribute_delimiter_closing_part_regexp = /[ \t]*#{attribute_delimiter_closing_regexp}/
|
|
260
261
|
end
|
|
261
262
|
|
|
262
|
-
# TODO: Support splat attributes.
|
|
263
263
|
loop do
|
|
264
|
-
if @scanner.skip(@
|
|
264
|
+
if @scanner.skip(@splat_attribute_regexp)
|
|
265
|
+
charpos = @scanner.charpos
|
|
266
|
+
code = parse_ruby_attribute_value(attribute_delimiter_closing)
|
|
267
|
+
syntax_error!(Errors::InvalidEmptyAttributeError) if code.empty?
|
|
268
|
+
attributes << [:slimi, :position, charpos, charpos + code.length, [:slimi, :splat, code]]
|
|
269
|
+
elsif @scanner.skip(@quoted_attribute_regexp)
|
|
265
270
|
attribute_name = @scanner[1]
|
|
266
271
|
escape = @scanner[2].empty?
|
|
267
272
|
quote = @scanner[3]
|
|
@@ -676,6 +681,11 @@ module Slimi
|
|
|
676
681
|
/#{attribute_name_regexp}[ \t]*=(=?)[ \t]*("|')/
|
|
677
682
|
end
|
|
678
683
|
|
|
684
|
+
# @return [Regexp]
|
|
685
|
+
def splat_attribute_regexp
|
|
686
|
+
/[ \t]*\*(?=[^\s])/
|
|
687
|
+
end
|
|
688
|
+
|
|
679
689
|
# @return [Regexp]
|
|
680
690
|
def ruby_attribute_delimiter_regexp
|
|
681
691
|
::Regexp.union(@ruby_attribute_delimiters.keys)
|
data/lib/slimi/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: slimi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ryo Nakamura
|
|
@@ -85,6 +85,7 @@ files:
|
|
|
85
85
|
- lib/slimi/filters/end_inserter.rb
|
|
86
86
|
- lib/slimi/filters/interpolation.rb
|
|
87
87
|
- lib/slimi/filters/output.rb
|
|
88
|
+
- lib/slimi/filters/splat.rb
|
|
88
89
|
- lib/slimi/filters/text.rb
|
|
89
90
|
- lib/slimi/filters/unposition.rb
|
|
90
91
|
- lib/slimi/parser.rb
|