slimi 0.7.8 → 0.9.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/.rubocop.yml +0 -1
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +18 -10
- data/README.md +1 -1
- data/lib/slimi/engine.rb +1 -0
- data/lib/slimi/errors.rb +9 -0
- data/lib/slimi/filters/do_inserter.rb +1 -1
- data/lib/slimi/filters/end_inserter.rb +3 -3
- data/lib/slimi/filters/output.rb +1 -1
- 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/rails_template_handler.rb +2 -2
- data/lib/slimi/version.rb +1 -1
- data/slimi.gemspec +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: df7564efc95dcfc968f262e77fff5be0daa2c779c43f8da3ee858a9508264685
|
|
4
|
+
data.tar.gz: 414c5620fb99766908c117ef3ae49d14df04206d6121714fb167ec4a4773678b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 12999b7675a1e9aa480cc32c8380a4a09f906376ce34e545db48f481991e1edb6d10bd3b8d6c0f1e09596d357b084b2f92d9958bee66149c27eacb50d0c76991
|
|
7
|
+
data.tar.gz: 037a95dd6470e185b63c6c145706cf27c9271aa2d1017c8ab5806dd87f96a8a05d0f779d5dcb279c7bd242043f80d9af43e2da2515997f0fcbb4b33aa303defd
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
slimi (0.
|
|
4
|
+
slimi (0.9.0)
|
|
5
5
|
temple
|
|
6
6
|
thor
|
|
7
7
|
tilt
|
|
@@ -9,22 +9,25 @@ PATH
|
|
|
9
9
|
GEM
|
|
10
10
|
remote: https://rubygems.org/
|
|
11
11
|
specs:
|
|
12
|
-
actionview (
|
|
13
|
-
activesupport (=
|
|
12
|
+
actionview (8.1.3)
|
|
13
|
+
activesupport (= 8.1.3)
|
|
14
14
|
builder (~> 3.1)
|
|
15
15
|
erubi (~> 1.11)
|
|
16
16
|
rails-dom-testing (~> 2.2)
|
|
17
17
|
rails-html-sanitizer (~> 1.6)
|
|
18
|
-
activesupport (
|
|
18
|
+
activesupport (8.1.3)
|
|
19
19
|
base64
|
|
20
20
|
bigdecimal
|
|
21
|
-
concurrent-ruby (~> 1.0, >= 1.
|
|
21
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
22
22
|
connection_pool (>= 2.2.5)
|
|
23
23
|
drb
|
|
24
24
|
i18n (>= 1.6, < 2)
|
|
25
|
+
json
|
|
26
|
+
logger (>= 1.4.2)
|
|
25
27
|
minitest (>= 5.1)
|
|
26
|
-
|
|
27
|
-
tzinfo (~> 2.0)
|
|
28
|
+
securerandom (>= 0.3)
|
|
29
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
30
|
+
uri (>= 0.13.1)
|
|
28
31
|
ast (2.4.2)
|
|
29
32
|
base64 (0.2.0)
|
|
30
33
|
bigdecimal (3.1.8)
|
|
@@ -39,17 +42,20 @@ GEM
|
|
|
39
42
|
concurrent-ruby (~> 1.0)
|
|
40
43
|
json (2.7.2)
|
|
41
44
|
language_server-protocol (3.17.0.3)
|
|
45
|
+
logger (1.7.0)
|
|
42
46
|
loofah (2.22.0)
|
|
43
47
|
crass (~> 1.0.2)
|
|
44
48
|
nokogiri (>= 1.12.0)
|
|
45
|
-
minitest (
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
minitest (6.0.6)
|
|
50
|
+
drb (~> 2.0)
|
|
51
|
+
prism (~> 1.5)
|
|
52
|
+
nokogiri (1.19.3-x86_64-linux-gnu)
|
|
48
53
|
racc (~> 1.4)
|
|
49
54
|
parallel (1.25.1)
|
|
50
55
|
parser (3.3.4.0)
|
|
51
56
|
ast (~> 2.4.1)
|
|
52
57
|
racc
|
|
58
|
+
prism (1.9.0)
|
|
53
59
|
racc (1.8.0)
|
|
54
60
|
rails-dom-testing (2.2.0)
|
|
55
61
|
activesupport (>= 5.0.0)
|
|
@@ -92,6 +98,7 @@ GEM
|
|
|
92
98
|
rubocop-rspec (3.0.3)
|
|
93
99
|
rubocop (~> 1.61)
|
|
94
100
|
ruby-progressbar (1.13.0)
|
|
101
|
+
securerandom (0.4.1)
|
|
95
102
|
strscan (3.1.0)
|
|
96
103
|
temple (0.10.3)
|
|
97
104
|
thor (1.3.2)
|
|
@@ -99,6 +106,7 @@ GEM
|
|
|
99
106
|
tzinfo (2.0.6)
|
|
100
107
|
concurrent-ruby (~> 1.0)
|
|
101
108
|
unicode-display_width (2.5.0)
|
|
109
|
+
uri (1.1.1)
|
|
102
110
|
|
|
103
111
|
PLATFORMS
|
|
104
112
|
x86_64-linux
|
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
|
|
@@ -4,7 +4,7 @@ module Slimi
|
|
|
4
4
|
module Filters
|
|
5
5
|
# Append missing `do` to embedded Ruby code.
|
|
6
6
|
class DoInserter < Base
|
|
7
|
-
VALID_RUBY_LINE_REGEXP = /(\A(if|unless|else|elsif|when|begin|rescue|ensure|case)\b)|\bdo\s*(\|[^|]*\|\s*)?\Z
|
|
7
|
+
VALID_RUBY_LINE_REGEXP = /(\A(if|unless|else|elsif|when|begin|rescue|ensure|case)\b)|\bdo\s*(\|[^|]*\|\s*)?\Z/
|
|
8
8
|
|
|
9
9
|
# @param [String] code
|
|
10
10
|
# @param [Array] expressio
|
|
@@ -36,11 +36,11 @@ module Slimi
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
class Expression
|
|
39
|
-
IF_REGEXP = /\A(if|begin|unless|else|elsif|when|rescue|ensure)\b|\bdo\s*(\|[^|]*\|)?\s
|
|
39
|
+
IF_REGEXP = /\A(if|begin|unless|else|elsif|when|rescue|ensure)\b|\bdo\s*(\|[^|]*\|)?\s*$/
|
|
40
40
|
|
|
41
|
-
ELSE_REGEXP = /\A(else|elsif|when|rescue|ensure)\b
|
|
41
|
+
ELSE_REGEXP = /\A(else|elsif|when|rescue|ensure)\b/
|
|
42
42
|
|
|
43
|
-
END_REGEXP = /\Aend\b
|
|
43
|
+
END_REGEXP = /\Aend\b/
|
|
44
44
|
|
|
45
45
|
# @param [Array] expression
|
|
46
46
|
def initialize(expression)
|
data/lib/slimi/filters/output.rb
CHANGED
|
@@ -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"'>/=]}
|
|
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
data/slimi.gemspec
CHANGED
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
spec.summary = 'Yet another implementation for Slim template language.'
|
|
12
12
|
spec.homepage = 'https://github.com/r7kamura/slimi'
|
|
13
13
|
spec.license = 'MIT'
|
|
14
|
-
spec.required_ruby_version = '>=
|
|
14
|
+
spec.required_ruby_version = '>= 3.3'
|
|
15
15
|
|
|
16
16
|
spec.metadata['homepage_uri'] = spec.homepage
|
|
17
17
|
spec.metadata['source_code_uri'] = spec.homepage
|
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.9.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
|
|
@@ -108,14 +109,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
108
109
|
requirements:
|
|
109
110
|
- - ">="
|
|
110
111
|
- !ruby/object:Gem::Version
|
|
111
|
-
version:
|
|
112
|
+
version: '3.3'
|
|
112
113
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
114
|
requirements:
|
|
114
115
|
- - ">="
|
|
115
116
|
- !ruby/object:Gem::Version
|
|
116
117
|
version: '0'
|
|
117
118
|
requirements: []
|
|
118
|
-
rubygems_version: 3.6.
|
|
119
|
+
rubygems_version: 3.6.7
|
|
119
120
|
specification_version: 4
|
|
120
121
|
summary: Yet another implementation for Slim template language.
|
|
121
122
|
test_files: []
|