slimi 0.4.1 → 0.6.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 +19 -0
- data/CHANGELOG.md +37 -1
- data/Gemfile +4 -3
- data/Gemfile.lock +39 -7
- data/README.md +46 -18
- data/lib/slimi/engine.rb +39 -0
- data/lib/slimi/errors.rb +4 -1
- data/lib/slimi/filters/amble.rb +23 -0
- data/lib/slimi/filters/attribute.rb +63 -0
- data/lib/slimi/filters/base.rb +41 -0
- data/lib/slimi/filters/control.rb +19 -0
- data/lib/slimi/filters/do_inserter.rb +27 -0
- data/lib/slimi/filters/embedded.rb +255 -0
- data/lib/slimi/filters/end_inserter.rb +77 -0
- data/lib/slimi/filters/interpolation.rb +2 -24
- data/lib/slimi/filters/output.rb +33 -0
- data/lib/slimi/filters/text.rb +15 -0
- data/lib/slimi/filters/unposition.rb +7 -23
- data/lib/slimi/filters.rb +9 -0
- data/lib/slimi/parser.rb +27 -17
- data/lib/slimi/rails_template_handler.rb +77 -0
- data/lib/slimi/railtie.rb +14 -0
- data/lib/slimi/version.rb +1 -1
- data/lib/slimi.rb +4 -0
- data/slimi.gemspec +1 -0
- metadata +28 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12b1a25a767a9ca17da4d9d99cf520d857131cd9d28acaf73d18a7696354c694
|
4
|
+
data.tar.gz: fee58aeb65387fd4846e74ce6885bb96e97dca9db3f2f95578693e20752249a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d97004ce75686f701d0a27bb8ac200e56956d5556fe4dbc874e523870d4c0467ee6eba5d5a1d7e34fc6d792d6df17ec85df689a297e94be56d44dadd33902c66
|
7
|
+
data.tar.gz: d719cc21f5288051ae6e7be83cb1f85e82000dc0e7345160d0087f3d61d9aab9a17ce45978437ceefa6d47ce063034022170e2062fd5404ee4182f245edf0792
|
data/.rubocop.yml
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rspec
|
3
|
+
|
1
4
|
AllCops:
|
2
5
|
NewCops: enable
|
3
6
|
SuggestExtensions: false
|
@@ -12,5 +15,21 @@ Lint/InterpolationCheck:
|
|
12
15
|
Metrics:
|
13
16
|
Enabled: false
|
14
17
|
|
18
|
+
RSpec/AnyInstance:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
RSpec/ImplicitSubject:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
RSpec/MultipleExpectations:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
RSpec/NamedSubject:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Security/Eval:
|
31
|
+
Exclude:
|
32
|
+
- 'spec/**/*.rb'
|
33
|
+
|
15
34
|
Style/Documentation:
|
16
35
|
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,42 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## 0.6.0 - 2022-01-03
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
- Support annotate_rendered_view_with_filenames.
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
- Rename expression name from slim to slimi.
|
14
|
+
|
15
|
+
### Fixed
|
16
|
+
|
17
|
+
- Fix bug at registering handler to ActionView.
|
18
|
+
- Fix Engine options at RailsTemplateHandler.
|
19
|
+
- Define missing :generator option at Engine.
|
20
|
+
|
21
|
+
## 0.5.1 - 2022-01-02
|
22
|
+
|
23
|
+
### Changed
|
24
|
+
|
25
|
+
- Wrap slim attrvalue by slimi position expression.
|
26
|
+
|
27
|
+
## 0.5.0 - 2022-01-02
|
28
|
+
|
29
|
+
### Added
|
30
|
+
|
31
|
+
- Add Slimi::Engine.
|
32
|
+
|
33
|
+
## 0.4.2 - 2021-12-27
|
34
|
+
|
35
|
+
### Fixed
|
36
|
+
|
37
|
+
- Fix bug on quoted attribute parser.
|
38
|
+
- Remove trailing line-ending from source line of syntax error message.
|
39
|
+
- Support multi-line attributes.
|
40
|
+
|
5
41
|
## 0.4.1 - 2021-12-26
|
6
42
|
|
7
43
|
### Fixed
|
@@ -24,7 +60,7 @@
|
|
24
60
|
|
25
61
|
### Added
|
26
62
|
|
27
|
-
- Support
|
63
|
+
- Support Ruby attributes.
|
28
64
|
|
29
65
|
### Fixed
|
30
66
|
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,17 +1,47 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
slimi (0.
|
4
|
+
slimi (0.6.0)
|
5
5
|
temple
|
6
|
+
tilt
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
9
10
|
specs:
|
11
|
+
actionview (7.0.0)
|
12
|
+
activesupport (= 7.0.0)
|
13
|
+
builder (~> 3.1)
|
14
|
+
erubi (~> 1.4)
|
15
|
+
rails-dom-testing (~> 2.0)
|
16
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
17
|
+
activesupport (7.0.0)
|
18
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
19
|
+
i18n (>= 1.6, < 2)
|
20
|
+
minitest (>= 5.1)
|
21
|
+
tzinfo (~> 2.0)
|
10
22
|
ast (2.4.2)
|
23
|
+
builder (3.2.4)
|
24
|
+
concurrent-ruby (1.1.9)
|
25
|
+
crass (1.0.6)
|
11
26
|
diff-lcs (1.4.4)
|
27
|
+
erubi (1.10.0)
|
28
|
+
i18n (1.8.11)
|
29
|
+
concurrent-ruby (~> 1.0)
|
30
|
+
loofah (2.13.0)
|
31
|
+
crass (~> 1.0.2)
|
32
|
+
nokogiri (>= 1.5.9)
|
33
|
+
minitest (5.15.0)
|
34
|
+
nokogiri (1.12.5-x86_64-linux)
|
35
|
+
racc (~> 1.4)
|
12
36
|
parallel (1.21.0)
|
13
37
|
parser (3.0.3.2)
|
14
38
|
ast (~> 2.4.1)
|
39
|
+
racc (1.6.0)
|
40
|
+
rails-dom-testing (2.0.3)
|
41
|
+
activesupport (>= 4.2.0)
|
42
|
+
nokogiri (>= 1.6)
|
43
|
+
rails-html-sanitizer (1.4.2)
|
44
|
+
loofah (~> 2.3)
|
15
45
|
rainbow (3.0.0)
|
16
46
|
rake (13.0.6)
|
17
47
|
regexp_parser (2.2.0)
|
@@ -40,22 +70,24 @@ GEM
|
|
40
70
|
unicode-display_width (>= 1.4.0, < 3.0)
|
41
71
|
rubocop-ast (1.15.0)
|
42
72
|
parser (>= 3.0.1.1)
|
73
|
+
rubocop-rspec (2.6.0)
|
74
|
+
rubocop (~> 1.19)
|
43
75
|
ruby-progressbar (1.11.0)
|
44
|
-
slim (4.1.0)
|
45
|
-
temple (>= 0.7.6, < 0.9)
|
46
|
-
tilt (>= 2.0.6, < 2.1)
|
47
76
|
temple (0.8.2)
|
48
77
|
tilt (2.0.10)
|
78
|
+
tzinfo (2.0.4)
|
79
|
+
concurrent-ruby (~> 1.0)
|
49
80
|
unicode-display_width (2.1.0)
|
50
81
|
|
51
82
|
PLATFORMS
|
52
83
|
x86_64-linux
|
53
84
|
|
54
85
|
DEPENDENCIES
|
86
|
+
actionview
|
55
87
|
rake (~> 13.0)
|
56
|
-
rspec
|
57
|
-
rubocop
|
58
|
-
|
88
|
+
rspec
|
89
|
+
rubocop
|
90
|
+
rubocop-rspec
|
59
91
|
slimi!
|
60
92
|
|
61
93
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -5,28 +5,56 @@
|
|
5
5
|
|
6
6
|
Yet another implementation for [Slim](https://github.com/slim-template/slim) template language.
|
7
7
|
|
8
|
-
|
8
|
+
## Introduction
|
9
9
|
|
10
|
-
|
10
|
+
Slimi provides almost the same functionality as Slim, with a few additional useful features,
|
11
|
+
such as generating AST with detailed location information about embedded Ruby codes.
|
11
12
|
|
12
|
-
|
13
|
+
Originally, Slimi was developed for [Slimcop](https://github.com/r7kamura/slimcop), a RuboCop runner for Slim template.
|
14
|
+
It uses Slimi to apply `rubocop --auto-correct` to embedded Ruby codes in Slim template.
|
13
15
|
|
14
|
-
|
15
|
-
gem 'slimi'
|
16
|
-
```
|
17
|
-
|
18
|
-
And then execute:
|
19
|
-
|
20
|
-
```
|
21
|
-
bundle install
|
22
|
-
```
|
16
|
+
## Usage
|
23
17
|
|
24
|
-
|
18
|
+
Just replace `gem 'slim'` with `gem 'slimi'` in your application's Gemfile.
|
25
19
|
|
20
|
+
```ruby
|
21
|
+
gem 'slimi'
|
26
22
|
```
|
27
|
-
gem install slimi
|
28
|
-
```
|
29
|
-
|
30
|
-
## Usage
|
31
23
|
|
32
|
-
|
24
|
+
## Compatibility
|
25
|
+
|
26
|
+
- Line indicators
|
27
|
+
- [x] Vebatim text
|
28
|
+
- [x] Inline HTML
|
29
|
+
- [x] Control
|
30
|
+
- [x] Output
|
31
|
+
- [x] HTML comment
|
32
|
+
- [x] Code comment
|
33
|
+
- [x] IE conditional comment
|
34
|
+
- Tags
|
35
|
+
- [x] Doctype declaration
|
36
|
+
- [x] Closed tags
|
37
|
+
- [x] Trailing and leading white space
|
38
|
+
- [x] Inline tags
|
39
|
+
- [x] Text content
|
40
|
+
- [x] Dynamic content
|
41
|
+
- [x] Tag shortcuts
|
42
|
+
- [ ] Dynamic tags
|
43
|
+
- Attributes
|
44
|
+
- [x] Attributes wrapper
|
45
|
+
- [x] Quoted attributes
|
46
|
+
- [x] Ruby attributes
|
47
|
+
- [x] Boolean attributes
|
48
|
+
- [x] Attribute merging
|
49
|
+
- [x] Attribute shortcuts
|
50
|
+
- [ ] Splat attributes
|
51
|
+
- Plugins
|
52
|
+
- [ ] Include partials
|
53
|
+
- [ ] Translator/I18n
|
54
|
+
- [ ] Logic-less mode
|
55
|
+
- [ ] Smart text mode
|
56
|
+
- Etc.
|
57
|
+
- [ ] CLI tools
|
58
|
+
- Slimi-only features
|
59
|
+
- [x] Embedded Ruby code location
|
60
|
+
- [x] annotate_rendered_view_with_filenames
|
data/lib/slimi/engine.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temple'
|
4
|
+
|
5
|
+
module Slimi
|
6
|
+
# Convert Slim code into Ruby code.
|
7
|
+
class Engine < ::Temple::Engine
|
8
|
+
define_options(
|
9
|
+
attr_quote: '"',
|
10
|
+
default_tag: 'div',
|
11
|
+
format: :xhtml,
|
12
|
+
generator: ::Temple::Generators::StringBuffer,
|
13
|
+
merge_attrs: { 'class' => ' ' },
|
14
|
+
pretty: false,
|
15
|
+
sort_attrs: true
|
16
|
+
)
|
17
|
+
|
18
|
+
use Parser
|
19
|
+
use Filters::Unposition
|
20
|
+
use Filters::Embedded
|
21
|
+
use Filters::Interpolation
|
22
|
+
use Filters::DoInserter
|
23
|
+
use Filters::EndInserter
|
24
|
+
use Filters::Control
|
25
|
+
use Filters::Output
|
26
|
+
use Filters::Text
|
27
|
+
html :AttributeSorter
|
28
|
+
html :AttributeMerger
|
29
|
+
use Filters::Attribute
|
30
|
+
use(:AttributeRemover) { ::Temple::HTML::AttributeRemover.new(remove_empty_attrs: options[:merge_attrs].keys) }
|
31
|
+
html :Pretty
|
32
|
+
use Filters::Amble
|
33
|
+
filter :Escapable
|
34
|
+
filter :ControlFlow
|
35
|
+
filter :MultiFlattener
|
36
|
+
filter :StaticMerger
|
37
|
+
generator :StringBuffer
|
38
|
+
end
|
39
|
+
end
|
data/lib/slimi/errors.rb
CHANGED
@@ -23,7 +23,7 @@ module Slimi
|
|
23
23
|
def to_s
|
24
24
|
<<~TEXT
|
25
25
|
#{error_type} at #{@file_path}:#{@line_number}:#{@column}
|
26
|
-
#{@line}
|
26
|
+
#{@line.rstrip}
|
27
27
|
#{' ' * (@column - 1)}^
|
28
28
|
TEXT
|
29
29
|
end
|
@@ -36,6 +36,9 @@ module Slimi
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
class AttributeClosingDelimiterNotFoundError < SlimSyntaxError
|
40
|
+
end
|
41
|
+
|
39
42
|
class InvalidEmptyAttributeError < SlimSyntaxError
|
40
43
|
end
|
41
44
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slimi
|
4
|
+
module Filters
|
5
|
+
# Support Rails annotate_rendered_view_with_filenames feature.
|
6
|
+
class Amble < Base
|
7
|
+
define_options(
|
8
|
+
:postamble,
|
9
|
+
:preamble
|
10
|
+
)
|
11
|
+
|
12
|
+
# @param [Array] expression
|
13
|
+
# @return [Array]
|
14
|
+
def call(expression)
|
15
|
+
result = %i[multi]
|
16
|
+
result << [:static, options[:preamble]] if options[:preamble]
|
17
|
+
result << expression
|
18
|
+
result << [:static, options[:postamble]] if options[:postamble]
|
19
|
+
result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slimi
|
4
|
+
module Filters
|
5
|
+
# Handle `[:slimi, :attributes, ...]`.
|
6
|
+
class Attribute < Base
|
7
|
+
define_options :merge_attrs
|
8
|
+
|
9
|
+
# @param [Array<Array>] expressions
|
10
|
+
# @return [Array]
|
11
|
+
def on_html_attrs(*expressions)
|
12
|
+
[:multi, *expressions.map { |expression| compile(expression) }]
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [String] name
|
16
|
+
# @param [Array] value
|
17
|
+
# @return [Array]
|
18
|
+
def on_html_attr(name, value)
|
19
|
+
if value[0] == :slimi && value[1] == :attrvalue && !options[:merge_attrs][name]
|
20
|
+
escape = value[2]
|
21
|
+
code = value[3]
|
22
|
+
case code
|
23
|
+
when 'true'
|
24
|
+
[:html, :attr, name, [:multi]]
|
25
|
+
when 'false', 'nil'
|
26
|
+
[:multi]
|
27
|
+
else
|
28
|
+
tmp = unique_name
|
29
|
+
[:multi,
|
30
|
+
[:code, "#{tmp} = #{code}"],
|
31
|
+
[:if, tmp,
|
32
|
+
[:if, "#{tmp} == true",
|
33
|
+
[:html, :attr, name, [:multi]],
|
34
|
+
[:html, :attr, name, [:escape, escape, [:dynamic, tmp]]]]]]
|
35
|
+
end
|
36
|
+
else
|
37
|
+
@attr = name
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param [Boolean] escape
|
43
|
+
# @param [String] code\
|
44
|
+
# @return [Array]\
|
45
|
+
def on_slimi_attrvalue(escape, code)
|
46
|
+
if (delimiter = options[:merge_attrs][@attr])
|
47
|
+
tmp = unique_name
|
48
|
+
[:multi,
|
49
|
+
[:code, "#{tmp} = #{code}"],
|
50
|
+
[:if, "Array === #{tmp}",
|
51
|
+
[:multi,
|
52
|
+
[:code, "#{tmp} = #{tmp}.flatten"],
|
53
|
+
[:code, "#{tmp}.map!(&:to_s)"],
|
54
|
+
[:code, "#{tmp}.reject!(&:empty?)"],
|
55
|
+
[:escape, escape, [:dynamic, "#{tmp}.join(#{delimiter.inspect})"]]],
|
56
|
+
[:escape, escape, [:dynamic, tmp]]]]
|
57
|
+
else
|
58
|
+
[:escape, escape, [:dynamic, code]]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temple'
|
4
|
+
|
5
|
+
module Slimi
|
6
|
+
module Filters
|
7
|
+
# Pass-through some expressions which are unknown for Temple.
|
8
|
+
class Base < ::Temple::HTML::Filter
|
9
|
+
# @param [String] code
|
10
|
+
# @param [Array] expression
|
11
|
+
# @return [Array]
|
12
|
+
def on_slimi_control(code, expression)
|
13
|
+
[:slimi, :control, code, compile(expression)]
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param [String] type
|
17
|
+
# @param [String] code
|
18
|
+
# @param [Array] expression
|
19
|
+
# @param [Array] attributes
|
20
|
+
# @return [Array]
|
21
|
+
def on_slimi_embedded(type, expression, attributes)
|
22
|
+
[:slimi, :embedded, type, compile(expression), attributes]
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param [Boolean] escape
|
26
|
+
# @param [String] code
|
27
|
+
# @param [Array] expression
|
28
|
+
# @return [Array]
|
29
|
+
def on_slimi_output(escape, code, expression)
|
30
|
+
[:slimi, :output, escape, code, compile(expression)]
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param [String] type
|
34
|
+
# @param [Array] expression
|
35
|
+
# @return [Array]
|
36
|
+
def on_slimi_text(type, expression)
|
37
|
+
[:slimi, :text, type, compile(expression)]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slimi
|
4
|
+
module Filters
|
5
|
+
# Handle `[:slimi, :control, code, multi]`.
|
6
|
+
class Control < Base
|
7
|
+
# @param [String] code
|
8
|
+
# @param [Array] multi
|
9
|
+
# @return [Array]
|
10
|
+
def on_slimi_control(code, multi)
|
11
|
+
[
|
12
|
+
:multi,
|
13
|
+
[:code, code],
|
14
|
+
compile(multi)
|
15
|
+
]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slimi
|
4
|
+
module Filters
|
5
|
+
# Append missing `do` to embedded Ruby code.
|
6
|
+
class DoInserter < Base
|
7
|
+
VALID_RUBY_LINE_REGEXP = /(\A(if|unless|else|elsif|when|begin|rescue|ensure|case)\b)|\bdo\s*(\|[^|]*\|\s*)?\Z/.freeze
|
8
|
+
|
9
|
+
# @param [String] code
|
10
|
+
# @param [Array] expressio
|
11
|
+
# @return [Array]
|
12
|
+
def on_slimi_control(code, expression)
|
13
|
+
code += ' do' unless code.match?(VALID_RUBY_LINE_REGEXP) || empty_exp?(expression)
|
14
|
+
[:slimi, :control, code, compile(expression)]
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param [Boolean] escape
|
18
|
+
# @param [String] code
|
19
|
+
# @param [Array] expression
|
20
|
+
# @return [Array]
|
21
|
+
def on_slimi_output(escape, code, expression)
|
22
|
+
code += ' do' unless code.match?(VALID_RUBY_LINE_REGEXP) || empty_exp?(expression)
|
23
|
+
[:slimi, :output, escape, code, compile(expression)]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slimi
|
4
|
+
module Filters
|
5
|
+
# @api private
|
6
|
+
class TextCollector < Base
|
7
|
+
def call(exp)
|
8
|
+
@collected = ''
|
9
|
+
super(exp)
|
10
|
+
@collected
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_slimi_interpolate(text)
|
14
|
+
@collected << text
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
class NewlineCollector < Base
|
21
|
+
def call(exp)
|
22
|
+
@collected = [:multi]
|
23
|
+
super(exp)
|
24
|
+
@collected
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_newline
|
28
|
+
@collected << [:newline]
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
class OutputProtector < Base
|
35
|
+
def call(exp)
|
36
|
+
@protect = []
|
37
|
+
@collected = ''
|
38
|
+
@tag = "%#{object_id.abs.to_s(36)}%"
|
39
|
+
super(exp)
|
40
|
+
@collected
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_static(text)
|
44
|
+
@collected << text
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_slimi_output(escape, text, content)
|
49
|
+
@collected << @tag
|
50
|
+
@protect << [:slimi, :output, escape, text, content]
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def unprotect(text)
|
55
|
+
block = [:multi]
|
56
|
+
while text =~ /#{@tag}/
|
57
|
+
block << [:static, Regexp.last_match.pre_match]
|
58
|
+
block << @protect.shift
|
59
|
+
text = Regexp.last_match.post_match
|
60
|
+
end
|
61
|
+
block << [:static, text]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Temple filter which processes embedded engines
|
66
|
+
# @api private
|
67
|
+
class Embedded < Base
|
68
|
+
@engines = {}
|
69
|
+
|
70
|
+
class << self
|
71
|
+
attr_reader :engines
|
72
|
+
|
73
|
+
# Register embedded engine
|
74
|
+
#
|
75
|
+
# @param [String] name Name of the engine
|
76
|
+
# @param [Class] klass Engine class
|
77
|
+
# @param option_filter List of options to pass to engine.
|
78
|
+
# Last argument can be default option hash.
|
79
|
+
def register(name, klass, *option_filter)
|
80
|
+
name = name.to_sym
|
81
|
+
local_options = option_filter.last.respond_to?(:to_hash) ? option_filter.pop.to_hash : {}
|
82
|
+
define_options(name, *option_filter)
|
83
|
+
klass.define_options(name)
|
84
|
+
engines[name.to_sym] = proc do |options|
|
85
|
+
klass.new({}.update(options).delete_if { |k, _v| !option_filter.include?(k) && k != name }.update(local_options))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def create(name, options)
|
90
|
+
constructor = engines[name] || raise(Temple::FilterError, "Embedded engine #{name} not found")
|
91
|
+
constructor.call(options)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
define_options :enable_engines, :disable_engines
|
96
|
+
|
97
|
+
def initialize(opts = {})
|
98
|
+
super
|
99
|
+
@engines = {}
|
100
|
+
@enabled = normalize_engine_list(options[:enable_engines])
|
101
|
+
@disabled = normalize_engine_list(options[:disable_engines])
|
102
|
+
end
|
103
|
+
|
104
|
+
def on_slimi_embedded(name, body, attrs)
|
105
|
+
name = name.to_sym
|
106
|
+
raise(Temple::FilterError, "Embedded engine #{name} is disabled") unless enabled?(name)
|
107
|
+
|
108
|
+
@engines[name] ||= self.class.create(name, options)
|
109
|
+
@engines[name].on_slimi_embedded(name, body, attrs)
|
110
|
+
end
|
111
|
+
|
112
|
+
def enabled?(name)
|
113
|
+
(!@enabled || @enabled.include?(name)) &&
|
114
|
+
(!@disabled || !@disabled.include?(name))
|
115
|
+
end
|
116
|
+
|
117
|
+
protected
|
118
|
+
|
119
|
+
def normalize_engine_list(list)
|
120
|
+
raise(ArgumentError, 'Option :enable_engines/:disable_engines must be String or Symbol list') unless !list || list.is_a?(Array)
|
121
|
+
|
122
|
+
list&.map(&:to_sym)
|
123
|
+
end
|
124
|
+
|
125
|
+
class Engine < Base
|
126
|
+
protected
|
127
|
+
|
128
|
+
def collect_text(body)
|
129
|
+
@text_collector ||= TextCollector.new
|
130
|
+
@text_collector.call(body)
|
131
|
+
end
|
132
|
+
|
133
|
+
def collect_newlines(body)
|
134
|
+
@newline_collector ||= NewlineCollector.new
|
135
|
+
@newline_collector.call(body)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Basic tilt engine
|
140
|
+
class TiltEngine < Engine
|
141
|
+
def on_slimi_embedded(engine, body, _attrs)
|
142
|
+
tilt_engine = Tilt[engine] || raise(Temple::FilterError, "Tilt engine #{engine} is not available.")
|
143
|
+
tilt_options = options[engine.to_sym] || {}
|
144
|
+
tilt_options[:default_encoding] ||= 'utf-8'
|
145
|
+
[:multi, tilt_render(tilt_engine, tilt_options, collect_text(body)), collect_newlines(body)]
|
146
|
+
end
|
147
|
+
|
148
|
+
protected
|
149
|
+
|
150
|
+
def tilt_render(tilt_engine, tilt_options, text)
|
151
|
+
[:static, tilt_engine.new(tilt_options) { text }.render]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Sass engine which supports :pretty option
|
156
|
+
class SassEngine < TiltEngine
|
157
|
+
define_options :pretty
|
158
|
+
|
159
|
+
protected
|
160
|
+
|
161
|
+
def tilt_render(tilt_engine, tilt_options, text)
|
162
|
+
text = tilt_engine.new(tilt_options.merge(
|
163
|
+
style: options[:pretty] ? :expanded : :compressed,
|
164
|
+
cache: false
|
165
|
+
)) { text }.render
|
166
|
+
text = text.chomp
|
167
|
+
[:static, text]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Static template with interpolated ruby code
|
172
|
+
class InterpolateTiltEngine < TiltEngine
|
173
|
+
def collect_text(body)
|
174
|
+
output_protector.call(interpolation.call(body))
|
175
|
+
end
|
176
|
+
|
177
|
+
def tilt_render(tilt_engine, tilt_options, text)
|
178
|
+
output_protector.unprotect(tilt_engine.new(tilt_options) { text }.render)
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def interpolation
|
184
|
+
@interpolation ||= Interpolation.new
|
185
|
+
end
|
186
|
+
|
187
|
+
def output_protector
|
188
|
+
@output_protector ||= OutputProtector.new
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Tag wrapper engine
|
193
|
+
# Generates a html tag and wraps another engine (specified via :engine option)
|
194
|
+
class TagEngine < Engine
|
195
|
+
disable_option_validator!
|
196
|
+
|
197
|
+
def on_slimi_embedded(engine, body, attrs)
|
198
|
+
unless options[:attributes].empty?
|
199
|
+
options[:attributes].map do |k, v|
|
200
|
+
attrs << [:html, :attr, k, [:static, v]]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
if options[:engine]
|
205
|
+
opts = {}.update(options)
|
206
|
+
opts.delete(:engine)
|
207
|
+
opts.delete(:tag)
|
208
|
+
opts.delete(:attributes)
|
209
|
+
@engine ||= options[:engine].new(opts)
|
210
|
+
body = @engine.on_slimi_embedded(engine, body, attrs)
|
211
|
+
end
|
212
|
+
|
213
|
+
[:html, :tag, options[:tag], attrs, body]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Javascript wrapper engine.
|
218
|
+
# Like TagEngine, but can wrap content in html comment or cdata.
|
219
|
+
class JavaScriptEngine < TagEngine
|
220
|
+
disable_option_validator!
|
221
|
+
|
222
|
+
set_options tag: :script, attributes: {}
|
223
|
+
|
224
|
+
def on_slimi_embedded(engine, body, attrs)
|
225
|
+
super(engine, [:html, :js, body], attrs)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Embeds ruby code
|
230
|
+
class RubyEngine < Engine
|
231
|
+
def on_slimi_embedded(_engine, body, _attrs)
|
232
|
+
[:multi, [:newline], [:code, "#{collect_text(body)}\n"]]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# These engines are executed at compile time, embedded ruby is interpolated
|
237
|
+
register :markdown, InterpolateTiltEngine
|
238
|
+
register :textile, InterpolateTiltEngine
|
239
|
+
register :rdoc, InterpolateTiltEngine
|
240
|
+
|
241
|
+
# These engines are executed at compile time
|
242
|
+
register :coffee, JavaScriptEngine, engine: TiltEngine
|
243
|
+
register :less, TagEngine, tag: :style, attributes: { type: 'text/css' }, engine: TiltEngine
|
244
|
+
register :sass, TagEngine, :pretty, tag: :style, attributes: { type: 'text/css' }, engine: SassEngine
|
245
|
+
register :scss, TagEngine, :pretty, tag: :style, attributes: { type: 'text/css' }, engine: SassEngine
|
246
|
+
|
247
|
+
# Embedded javascript/css
|
248
|
+
register :javascript, JavaScriptEngine
|
249
|
+
register :css, TagEngine, tag: :style, attributes: { type: 'text/css' }
|
250
|
+
|
251
|
+
# Embedded ruby code
|
252
|
+
register :ruby, RubyEngine
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slimi
|
4
|
+
module Filters
|
5
|
+
# Append missing `end` line to embedded Ruby code in control block.
|
6
|
+
class EndInserter < Base
|
7
|
+
# @param [Array<Array>] expressions
|
8
|
+
def on_multi(*expressions)
|
9
|
+
result = [:multi]
|
10
|
+
prev_indent = false
|
11
|
+
expressions.each do |source|
|
12
|
+
expression = Expression.new(source)
|
13
|
+
if expression.control?
|
14
|
+
raise ::Temple::FilterError, 'Explicit end statements are forbidden.' if expression.end?
|
15
|
+
|
16
|
+
result << code_end if prev_indent && !expression.else?
|
17
|
+
|
18
|
+
prev_indent = expression.if?
|
19
|
+
elsif !expression.newline? && prev_indent
|
20
|
+
result << code_end
|
21
|
+
prev_indent = false
|
22
|
+
end
|
23
|
+
|
24
|
+
result << compile(source)
|
25
|
+
end
|
26
|
+
|
27
|
+
result << code_end if prev_indent
|
28
|
+
result
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# @return [Array]
|
34
|
+
def code_end
|
35
|
+
[:code, 'end']
|
36
|
+
end
|
37
|
+
|
38
|
+
class Expression
|
39
|
+
IF_REGEXP = /\A(if|begin|unless|else|elsif|when|rescue|ensure)\b|\bdo\s*(\|[^|]*\|)?\s*$/.freeze
|
40
|
+
|
41
|
+
ELSE_REGEXP = /\A(else|elsif|when|rescue|ensure)\b/.freeze
|
42
|
+
|
43
|
+
END_REGEXP = /\Aend\b/.freeze
|
44
|
+
|
45
|
+
# @param [Array] expression
|
46
|
+
def initialize(expression)
|
47
|
+
@expression = expression
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Boolean]
|
51
|
+
def control?
|
52
|
+
@expression[0] == :slimi && @expression[1] == :control
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [Boolean]
|
56
|
+
def if?
|
57
|
+
@expression[2].match?(IF_REGEXP)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [Boolean]
|
61
|
+
def else?
|
62
|
+
@expression[2].match?(ELSE_REGEXP)
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Boolean]
|
66
|
+
def end?
|
67
|
+
@expression[2].match?(END_REGEXP)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Boolean]
|
71
|
+
def newline?
|
72
|
+
@expression[0] == :newline
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -4,29 +4,7 @@ require 'strscan'
|
|
4
4
|
|
5
5
|
module Slimi
|
6
6
|
module Filters
|
7
|
-
class Interpolation
|
8
|
-
def initialize(*); end
|
9
|
-
|
10
|
-
def call(node)
|
11
|
-
convert(node)
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def convert(value)
|
17
|
-
if value.instance_of?(::Array)
|
18
|
-
if value[0] == :slimi && value[1] == :interpolate
|
19
|
-
on_slimi_interpolate(value[2], value[3], value[4])
|
20
|
-
else
|
21
|
-
value.map do |element|
|
22
|
-
call(element)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
else
|
26
|
-
value
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
7
|
+
class Interpolation < Base
|
30
8
|
# @param [Integer] begin_
|
31
9
|
# @param [Integer] end_
|
32
10
|
# @return [Array] S-expression.
|
@@ -47,7 +25,7 @@ module Slimi
|
|
47
25
|
else
|
48
26
|
escape = false
|
49
27
|
end
|
50
|
-
block << [:slimi, :position, begin2, begin2 + code.length, [:
|
28
|
+
block << [:slimi, :position, begin2, begin2 + code.length, [:slimi, :output, escape, code, [:multi]]]
|
51
29
|
elsif (value = scanner.scan(/([#\\]?[^#\\]*([#\\][^\\\#{][^#\\]*)*)/)) # rubocop:disable Lint/DuplicateBranch
|
52
30
|
block << [:static, value]
|
53
31
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slimi
|
4
|
+
module Filters
|
5
|
+
# Handle `[:slimi, :output, escape, code, multi]`.
|
6
|
+
class Output < Base
|
7
|
+
define_options :disable_capture
|
8
|
+
|
9
|
+
IF_REGEXP = /\A(if|unless)\b|\bdo\s*(\|[^|]*\|)?\s*$/.freeze
|
10
|
+
|
11
|
+
# @param [Boolean] escape
|
12
|
+
# @param [String] code
|
13
|
+
# @param [Array] multi
|
14
|
+
# @return [Array]
|
15
|
+
def on_slimi_output(escape, code, multi)
|
16
|
+
if code.match?(IF_REGEXP)
|
17
|
+
tmp = unique_name
|
18
|
+
[
|
19
|
+
:multi,
|
20
|
+
[:block, "#{tmp} = #{code}", options[:disable_capture] ? compile(multi) : [:capture, unique_name, compile(multi)]],
|
21
|
+
[:escape, escape, [:dynamic, tmp]]
|
22
|
+
]
|
23
|
+
else
|
24
|
+
[
|
25
|
+
:multi,
|
26
|
+
[:escape, escape, [:dynamic, code]],
|
27
|
+
multi
|
28
|
+
]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slimi
|
4
|
+
module Filters
|
5
|
+
# Handle `[:slimi, :text, multi]`.
|
6
|
+
class Text < Base
|
7
|
+
# @param [Symbol] _type
|
8
|
+
# @param [Array] multi
|
9
|
+
# @return [Array]
|
10
|
+
def on_slimi_text(_type, multi)
|
11
|
+
compile(multi)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -2,29 +2,13 @@
|
|
2
2
|
|
3
3
|
module Slimi
|
4
4
|
module Filters
|
5
|
-
class Unposition
|
6
|
-
|
7
|
-
|
8
|
-
# @param [Array]
|
9
|
-
# @return [Array]
|
10
|
-
def
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def convert(value)
|
17
|
-
if value.instance_of?(::Array)
|
18
|
-
if value[0] == :slimi && value[1] == :position
|
19
|
-
call(value[4])
|
20
|
-
else
|
21
|
-
value.map do |element|
|
22
|
-
call(element)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
else
|
26
|
-
value
|
27
|
-
end
|
5
|
+
class Unposition < Base
|
6
|
+
# @param [Integer] _begin
|
7
|
+
# @param [Integer] _end
|
8
|
+
# @param [Array] expression
|
9
|
+
# @return [Array]
|
10
|
+
def on_slimi_position(_begin, _end, expression)
|
11
|
+
compile(expression)
|
28
12
|
end
|
29
13
|
end
|
30
14
|
end
|
data/lib/slimi/filters.rb
CHANGED
@@ -2,7 +2,16 @@
|
|
2
2
|
|
3
3
|
module Slimi
|
4
4
|
module Filters
|
5
|
+
autoload :Amble, 'slimi/filters/amble'
|
6
|
+
autoload :Attribute, 'slimi/filters/attribute'
|
7
|
+
autoload :Base, 'slimi/filters/base'
|
8
|
+
autoload :Control, 'slimi/filters/control'
|
9
|
+
autoload :DoInserter, 'slimi/filters/do_inserter'
|
10
|
+
autoload :Embedded, 'slimi/filters/embedded'
|
11
|
+
autoload :EndInserter, 'slimi/filters/end_inserter'
|
5
12
|
autoload :Interpolation, 'slimi/filters/interpolation'
|
13
|
+
autoload :Output, 'slimi/filters/output'
|
14
|
+
autoload :Text, 'slimi/filters/text'
|
6
15
|
autoload :Unposition, 'slimi/filters/unposition'
|
7
16
|
end
|
8
17
|
end
|
data/lib/slimi/parser.rb
CHANGED
@@ -115,7 +115,7 @@ module Slimi
|
|
115
115
|
|
116
116
|
embedded_template_engine_name = @scanner[1]
|
117
117
|
attributes = parse_attributes
|
118
|
-
@stacks.last << [:
|
118
|
+
@stacks.last << [:slimi, :embedded, embedded_template_engine_name, parse_text_block, attributes]
|
119
119
|
end
|
120
120
|
|
121
121
|
# @return [Boolean]
|
@@ -150,14 +150,14 @@ module Slimi
|
|
150
150
|
block = [:multi]
|
151
151
|
@stacks.last.insert(-2, [:static, ' ']) if with_leading_white_space2
|
152
152
|
@scanner.skip(/[ \t]+/)
|
153
|
-
tag << with_position { [:
|
153
|
+
tag << with_position { [:slimi, :output, escape, parse_broken_lines, block] }
|
154
154
|
@stacks.last << [:static, ' '] if with_trailing_white_space2
|
155
155
|
@stacks << block
|
156
156
|
elsif @scanner.skip(%r{[ \t]*/[ \t]*})
|
157
157
|
syntax_error!(Errors::UnexpectedTextAfterClosedTagError) unless @scanner.match?(/\r?\n/)
|
158
158
|
else
|
159
159
|
@scanner.skip(/[ \t]+/)
|
160
|
-
tag << [:
|
160
|
+
tag << [:slimi, :text, :inline, parse_text_block]
|
161
161
|
end
|
162
162
|
true
|
163
163
|
else
|
@@ -220,18 +220,24 @@ module Slimi
|
|
220
220
|
value = +''
|
221
221
|
count = 0
|
222
222
|
loop do
|
223
|
-
if @scanner.match?(/#{quote}/)
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
223
|
+
if @scanner.match?(/#{quote}/)
|
224
|
+
if count.zero?
|
225
|
+
end_ = @scanner.charpos
|
226
|
+
@scanner.pos += @scanner.matched_size
|
227
|
+
break
|
228
|
+
else
|
229
|
+
@scanner.pos += @scanner.matched_size
|
230
|
+
value << @scanner.matched
|
231
|
+
end
|
232
|
+
elsif @scanner.skip(/\{/)
|
230
233
|
count += 1
|
234
|
+
value << @scanner.matched
|
231
235
|
elsif @scanner.skip(/\}/)
|
232
236
|
count -= 1
|
237
|
+
value << @scanner.matched
|
238
|
+
else
|
239
|
+
value << @scanner.scan(/[^{}#{quote}]*/)
|
233
240
|
end
|
234
|
-
value << @scanner.scan(/[^{}#{quote}]*/)
|
235
241
|
end
|
236
242
|
[:slimi, :interpolate, begin_, end_, value]
|
237
243
|
end
|
@@ -263,9 +269,10 @@ module Slimi
|
|
263
269
|
elsif @scanner.skip(@ruby_attribute_regexp)
|
264
270
|
attribute_name = @scanner[1]
|
265
271
|
escape = @scanner[2].empty?
|
272
|
+
charpos = @scanner.charpos
|
266
273
|
attribute_value = parse_ruby_attribute_value(attribute_delimiter_closing)
|
267
274
|
syntax_error!(Errors::InvalidEmptyAttributeError) if attribute_value.empty?
|
268
|
-
attributes << [:html, :attr, attribute_name, [:
|
275
|
+
attributes << [:html, :attr, attribute_name, [:slimi, :position, charpos, charpos + attribute_value.length, [:slimi, :attrvalue, escape, attribute_value]]]
|
269
276
|
elsif !attribute_delimiter_closing_part_regexp
|
270
277
|
break
|
271
278
|
elsif @scanner.skip(boolean_attribute_regexp)
|
@@ -273,7 +280,10 @@ module Slimi
|
|
273
280
|
elsif @scanner.skip(attribute_delimiter_closing_part_regexp) # rubocop:disable Lint/DuplicateBranch
|
274
281
|
break
|
275
282
|
else
|
276
|
-
|
283
|
+
@scanner.skip(/[ \t]+/)
|
284
|
+
expect_line_ending
|
285
|
+
|
286
|
+
syntax_error!(Errors::AttributeClosingDelimiterNotFoundError) if @scanner.eos?
|
277
287
|
end
|
278
288
|
end
|
279
289
|
|
@@ -324,7 +334,7 @@ module Slimi
|
|
324
334
|
def parse_html_comment
|
325
335
|
if @scanner.skip(%r{/!})
|
326
336
|
text_block = parse_text_block
|
327
|
-
text = [:
|
337
|
+
text = [:slimi, :text, :verbatim, text_block]
|
328
338
|
@stacks.last << [:html, :comment, text]
|
329
339
|
true
|
330
340
|
else
|
@@ -371,7 +381,7 @@ module Slimi
|
|
371
381
|
def parse_verbatim_text_block_inner
|
372
382
|
if @scanner.skip(/([|']) ?/)
|
373
383
|
with_trailing_white_space = @scanner[1] == "'"
|
374
|
-
@stacks.last << [:
|
384
|
+
@stacks.last << [:slimi, :text, :verbatim, parse_text_block]
|
375
385
|
@stacks.last << [:static, ' '] if with_trailing_white_space
|
376
386
|
true
|
377
387
|
else
|
@@ -410,7 +420,7 @@ module Slimi
|
|
410
420
|
if @scanner.skip(/-/)
|
411
421
|
block = [:multi]
|
412
422
|
@scanner.skip(/[ \t]+/)
|
413
|
-
@stacks.last << with_position { [:
|
423
|
+
@stacks.last << with_position { [:slimi, :control, parse_broken_lines, block] }
|
414
424
|
@stacks << block
|
415
425
|
true
|
416
426
|
else
|
@@ -433,7 +443,7 @@ module Slimi
|
|
433
443
|
block = [:multi]
|
434
444
|
@stacks.last << [:static, ' '] if with_trailing_white_space
|
435
445
|
@scanner.skip(/[ \t]+/)
|
436
|
-
@stacks.last << with_position { [:
|
446
|
+
@stacks.last << with_position { [:slimi, :output, escape, parse_broken_lines, block] }
|
437
447
|
@stacks.last << [:static, ' '] if with_leading_white_space
|
438
448
|
@stacks << block
|
439
449
|
else
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slimi
|
4
|
+
# Render Slim template in response to requests from Rails.
|
5
|
+
class RailsTemplateHandler
|
6
|
+
# @param [ActionView::Template] template
|
7
|
+
# @param [String, nil] source
|
8
|
+
# @return [String]
|
9
|
+
def call(template, source = nil)
|
10
|
+
Renderer.new(
|
11
|
+
source: source,
|
12
|
+
template: template
|
13
|
+
).call
|
14
|
+
end
|
15
|
+
|
16
|
+
# Render HTML from given source and options.
|
17
|
+
class Renderer
|
18
|
+
# @param [String] source
|
19
|
+
# @param [ActionView::Template] template
|
20
|
+
def initialize(
|
21
|
+
source:,
|
22
|
+
template:
|
23
|
+
)
|
24
|
+
@source = source
|
25
|
+
@template = template
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [String]
|
29
|
+
def call
|
30
|
+
engine.call(source)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# @return [Slimi::Engine]
|
36
|
+
def engine
|
37
|
+
Engine.new(engine_options)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Hash{Symbol => Object}]
|
41
|
+
def engine_options
|
42
|
+
engine_default_options.merge(engine_amble_options)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Hash{Symbol => Object}]
|
46
|
+
def engine_default_options
|
47
|
+
{
|
48
|
+
generator: ::Temple::Generators::RailsOutputBuffer,
|
49
|
+
streaming: true,
|
50
|
+
use_html_safe: true
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Hash{Symbol => Object}]
|
55
|
+
def engine_amble_options
|
56
|
+
if with_annotate_rendered_view_with_filenames?
|
57
|
+
{
|
58
|
+
postamble: "<!-- END #{@template.short_identifier} -->\n",
|
59
|
+
preamble: "<!-- BEGIN #{@template.short_identifier} -->\n"
|
60
|
+
}
|
61
|
+
else
|
62
|
+
{}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [String]
|
67
|
+
def source
|
68
|
+
@source || @template.source
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [Boolean]
|
72
|
+
def with_annotate_rendered_view_with_filenames?
|
73
|
+
::ActionView::Base.try(:annotate_rendered_view_with_filenames) && @template.format == :html
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Slimi
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
initializer 'Register Slimi template handler' do
|
6
|
+
::ActiveSupport.on_load(:action_view) do
|
7
|
+
::ActionView::Template.register_template_handler(
|
8
|
+
:slim,
|
9
|
+
RailsTemplateHandler.new
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/slimi/version.rb
CHANGED
data/lib/slimi.rb
CHANGED
@@ -3,8 +3,12 @@
|
|
3
3
|
require_relative 'slimi/version'
|
4
4
|
|
5
5
|
module Slimi
|
6
|
+
autoload :Engine, 'slimi/engine'
|
6
7
|
autoload :Errors, 'slimi/errors'
|
7
8
|
autoload :Filters, 'slimi/filters'
|
8
9
|
autoload :Parser, 'slimi/parser'
|
10
|
+
autoload :RailsTemplateHandler, 'slimi/rails_template_handler'
|
9
11
|
autoload :Range, 'slimi/range'
|
10
12
|
end
|
13
|
+
|
14
|
+
require_relative 'slimi/railtie' if defined?(Rails)
|
data/slimi.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slimi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryo Nakamura
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: temple
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tilt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
description:
|
28
42
|
email:
|
29
43
|
- r7kamura@gmail.com
|
@@ -43,11 +57,23 @@ files:
|
|
43
57
|
- bin/console
|
44
58
|
- bin/setup
|
45
59
|
- lib/slimi.rb
|
60
|
+
- lib/slimi/engine.rb
|
46
61
|
- lib/slimi/errors.rb
|
47
62
|
- lib/slimi/filters.rb
|
63
|
+
- lib/slimi/filters/amble.rb
|
64
|
+
- lib/slimi/filters/attribute.rb
|
65
|
+
- lib/slimi/filters/base.rb
|
66
|
+
- lib/slimi/filters/control.rb
|
67
|
+
- lib/slimi/filters/do_inserter.rb
|
68
|
+
- lib/slimi/filters/embedded.rb
|
69
|
+
- lib/slimi/filters/end_inserter.rb
|
48
70
|
- lib/slimi/filters/interpolation.rb
|
71
|
+
- lib/slimi/filters/output.rb
|
72
|
+
- lib/slimi/filters/text.rb
|
49
73
|
- lib/slimi/filters/unposition.rb
|
50
74
|
- lib/slimi/parser.rb
|
75
|
+
- lib/slimi/rails_template_handler.rb
|
76
|
+
- lib/slimi/railtie.rb
|
51
77
|
- lib/slimi/range.rb
|
52
78
|
- lib/slimi/version.rb
|
53
79
|
- slimi.gemspec
|