serbea 2.3.0 → 2.4.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 +29 -0
- data/CHANGELOG.md +9 -0
- data/Rakefile +4 -1
- data/bin/rubocop +27 -0
- data/lib/serbea/helpers.rb +13 -7
- data/lib/serbea/pipeline.rb +26 -16
- data/lib/serbea/template_engine.rb +128 -150
- data/lib/serbea.rb +4 -1
- data/lib/version.rb +4 -2
- data/serbea.gemspec +3 -2
- metadata +22 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16a4fb4aa837acca31f21651fb20a6eb9390b2f1fe71b17dcb5da1a6bea4648a
|
|
4
|
+
data.tar.gz: 37bdc48cd38eae83c124c7c290b6340aeac3e5e0f0e9739a627c5b543cba4b30
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: beeb54542ca3610c6f70b166ad8a072d2dced0ff54b141de82102f8dd49c67b539773fdae7fd1620c2f09915be926faa5c4661352a93044de00c42f6152522ea
|
|
7
|
+
data.tar.gz: d72e6d3a26cd13119c257706f07451ef6122f4179a6a3100c508992ebfb19e0a008a50c1b406ef7409ebc75d0cc6e2f8c06ce83c5960f15541451c2113740891
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require: rubocop-bridgetown
|
|
2
|
+
inherit_gem:
|
|
3
|
+
rubocop-bridgetown: .rubocop.yml
|
|
4
|
+
|
|
5
|
+
AllCops:
|
|
6
|
+
TargetRubyVersion: 3.1
|
|
7
|
+
Exclude:
|
|
8
|
+
- bin/**/*
|
|
9
|
+
- exe/**/*
|
|
10
|
+
- test/**/*
|
|
11
|
+
- tmp/**/*
|
|
12
|
+
- Gemfile
|
|
13
|
+
- Rakefile
|
|
14
|
+
- docs/**/*
|
|
15
|
+
- lib/rouge/**/*
|
|
16
|
+
- serbea-rails/**/*
|
|
17
|
+
- serbea.gemspec
|
|
18
|
+
|
|
19
|
+
Bridgetown/NoPutsAllowed:
|
|
20
|
+
Enabled: false
|
|
21
|
+
|
|
22
|
+
Bridgetown/InsecureHeredoc:
|
|
23
|
+
Enabled: false
|
|
24
|
+
|
|
25
|
+
Layout/LineLength:
|
|
26
|
+
Enabled: false
|
|
27
|
+
|
|
28
|
+
Metrics/BlockNesting:
|
|
29
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
data/Rakefile
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "bundler/gem_tasks"
|
|
2
2
|
require "rake/testtask"
|
|
3
|
+
require "rubocop/rake_task"
|
|
3
4
|
require "bundler"
|
|
4
5
|
|
|
5
6
|
Bundler.setup
|
|
@@ -9,4 +10,6 @@ Rake::TestTask.new(:test) do |t|
|
|
|
9
10
|
t.warning = false
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
RuboCop::RakeTask.new
|
|
14
|
+
|
|
15
|
+
task :default => [:rubocop, :test]
|
data/bin/rubocop
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'rubocop' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
|
12
|
+
|
|
13
|
+
bundle_binstub = File.expand_path("bundle", __dir__)
|
|
14
|
+
|
|
15
|
+
if File.file?(bundle_binstub)
|
|
16
|
+
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
|
|
17
|
+
load(bundle_binstub)
|
|
18
|
+
else
|
|
19
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
20
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
require "rubygems"
|
|
25
|
+
require "bundler/setup"
|
|
26
|
+
|
|
27
|
+
load Gem.bin_path("rubocop", "rubocop")
|
data/lib/serbea/helpers.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
unless "".respond_to?(:html_safe)
|
|
2
4
|
# The simplest HTML safety "polyfill" around
|
|
3
5
|
class String
|
|
@@ -13,7 +15,7 @@ end
|
|
|
13
15
|
|
|
14
16
|
module Serbea
|
|
15
17
|
module Helpers
|
|
16
|
-
def self.included(
|
|
18
|
+
def self.included(_mod)
|
|
17
19
|
Serbea::Pipeline.deny_value_method %i(escape h prepend append assign_to)
|
|
18
20
|
end
|
|
19
21
|
|
|
@@ -21,10 +23,10 @@ module Serbea
|
|
|
21
23
|
previous_buffer_state = @_erbout
|
|
22
24
|
@_erbout = Serbea::OutputBuffer.new
|
|
23
25
|
result = yield(*args)
|
|
24
|
-
result = @_erbout
|
|
26
|
+
result = @_erbout unless @_erbout.empty?
|
|
25
27
|
@_erbout = previous_buffer_state
|
|
26
28
|
|
|
27
|
-
Serbea::OutputBuffer
|
|
29
|
+
result.is_a?(Serbea::OutputBuffer) ? result.html_safe : result
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
def pipeline(context, value)
|
|
@@ -38,11 +40,15 @@ module Serbea
|
|
|
38
40
|
end
|
|
39
41
|
alias_method :macro, :helper
|
|
40
42
|
|
|
41
|
-
def import(
|
|
43
|
+
def import(...)
|
|
42
44
|
helper_names = %i(partial render)
|
|
43
45
|
available_helper = helper_names.find { |meth| respond_to?(meth) }
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
unless available_helper
|
|
47
|
+
raise Serbea::Error,
|
|
48
|
+
"Serbea Error: no `render' or `partial' helper available in #{self.class}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
available_helper == :partial ? partial(...) : render(...)
|
|
46
52
|
nil
|
|
47
53
|
end
|
|
48
54
|
|
|
@@ -65,7 +71,7 @@ module Serbea
|
|
|
65
71
|
end
|
|
66
72
|
|
|
67
73
|
def assign_to(input, varname, preserve: false)
|
|
68
|
-
|
|
74
|
+
instance_variable_set("@#{varname}", input)
|
|
69
75
|
preserve ? input : nil
|
|
70
76
|
end
|
|
71
77
|
end
|
data/lib/serbea/pipeline.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "set"
|
|
2
4
|
|
|
3
5
|
module Serbea
|
|
@@ -25,9 +27,7 @@ module Serbea
|
|
|
25
27
|
attr_accessor :output
|
|
26
28
|
end
|
|
27
29
|
|
|
28
|
-
if include_helpers
|
|
29
|
-
anon.include include_helpers
|
|
30
|
-
end
|
|
30
|
+
anon.include include_helpers if include_helpers
|
|
31
31
|
|
|
32
32
|
pipeline_obj = anon.new
|
|
33
33
|
|
|
@@ -40,27 +40,29 @@ module Serbea
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# @param processor [Proc]
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
class << self
|
|
44
|
+
attr_writer :output_processor
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
# @return [Proc]
|
|
48
48
|
def self.output_processor
|
|
49
|
-
@output_processor ||=
|
|
50
|
-
|
|
49
|
+
@output_processor ||= ->(input) do
|
|
50
|
+
!input.html_safe? && autoescape ? Erubi.h(input) : input.html_safe
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
class << self
|
|
55
|
+
attr_writer :autoescape
|
|
56
56
|
end
|
|
57
|
+
|
|
57
58
|
def self.autoescape
|
|
58
|
-
@autoescape.nil?
|
|
59
|
+
@autoescape.nil? || @autoescape
|
|
59
60
|
end
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
class << self
|
|
63
|
+
attr_writer :raise_on_missing_filters
|
|
63
64
|
end
|
|
65
|
+
|
|
64
66
|
def self.raise_on_missing_filters
|
|
65
67
|
@raise_on_missing_filters ||= false
|
|
66
68
|
end
|
|
@@ -68,6 +70,7 @@ module Serbea
|
|
|
68
70
|
def self.deny_value_method(name)
|
|
69
71
|
value_methods_denylist.merge Array(name)
|
|
70
72
|
end
|
|
73
|
+
|
|
71
74
|
def self.value_methods_denylist
|
|
72
75
|
@value_methods_denylist ||= Set.new
|
|
73
76
|
end
|
|
@@ -87,6 +90,7 @@ module Serbea
|
|
|
87
90
|
def self.polluted_method(name)
|
|
88
91
|
polluted_methods_list.merge Array(name)
|
|
89
92
|
end
|
|
93
|
+
|
|
90
94
|
def self.polluted_methods_list
|
|
91
95
|
@polluted_methods_list ||= Set.new(%i(select to_json))
|
|
92
96
|
end
|
|
@@ -98,6 +102,7 @@ module Serbea
|
|
|
98
102
|
@value = value
|
|
99
103
|
end
|
|
100
104
|
|
|
105
|
+
# rubocop:disable Metrics
|
|
101
106
|
def filter(name, *args, **kwargs)
|
|
102
107
|
if @value.respond_to?(name) && !self.class.value_methods_denylist.include?(name)
|
|
103
108
|
if args.last.is_a?(Proc)
|
|
@@ -115,27 +120,32 @@ module Serbea
|
|
|
115
120
|
@value = var.call(@value, *args, **kwargs)
|
|
116
121
|
else
|
|
117
122
|
"Serbea warning: Filter '#{name}' does not respond to call".tap do |warning|
|
|
118
|
-
self.class.raise_on_missing_filters ? raise(
|
|
123
|
+
self.class.raise_on_missing_filters ? raise(
|
|
124
|
+
Serbea::FilterMissing, warning
|
|
125
|
+
) : warn(warning)
|
|
119
126
|
end
|
|
120
127
|
end
|
|
121
128
|
else
|
|
122
129
|
"Serbea warning: Filter `#{name}' not found".tap do |warning|
|
|
123
|
-
self.class.raise_on_missing_filters ? raise(
|
|
130
|
+
self.class.raise_on_missing_filters ? raise(
|
|
131
|
+
Serbea::FilterMissing, warning
|
|
132
|
+
) : warn(warning)
|
|
124
133
|
end
|
|
125
134
|
end
|
|
126
135
|
|
|
127
136
|
self
|
|
128
137
|
end
|
|
138
|
+
# rubocop:enable Metrics
|
|
129
139
|
|
|
130
140
|
def to_s
|
|
131
141
|
self.class.output_processor.call(@value.is_a?(String) ? @value : @value.to_s)
|
|
132
142
|
end
|
|
133
|
-
|
|
143
|
+
|
|
134
144
|
def |(*)
|
|
135
145
|
self
|
|
136
146
|
end
|
|
137
147
|
|
|
138
|
-
def method_missing(...)
|
|
148
|
+
def method_missing(...) # rubocop:disable Style/MissingRespondToMissing
|
|
139
149
|
filter(...)
|
|
140
150
|
end
|
|
141
151
|
|
|
@@ -1,28 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "strscan"
|
|
2
4
|
|
|
3
5
|
module Serbea
|
|
4
|
-
class OutputBuffer <
|
|
5
|
-
def concat_to_s(input)
|
|
6
|
-
concat input.to_s
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
alias_method :safe_append=, :concat_to_s
|
|
10
|
-
alias_method :append=, :concat_to_s
|
|
11
|
-
alias_method :safe_expr_append=, :concat_to_s
|
|
6
|
+
class OutputBuffer < Erubi::CaptureBlockEngine::Buffer
|
|
12
7
|
end
|
|
13
8
|
|
|
14
|
-
class TemplateEngine < Erubi::
|
|
15
|
-
FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m
|
|
9
|
+
class TemplateEngine < Erubi::CaptureBlockEngine
|
|
10
|
+
FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m
|
|
16
11
|
|
|
17
12
|
def self.directive(new_directive, directive_resolution)
|
|
18
13
|
directives[new_directive.to_s] = directive_resolution
|
|
19
14
|
end
|
|
20
15
|
|
|
16
|
+
# rubocop:disable Metrics
|
|
21
17
|
def self.directives
|
|
22
18
|
@directives ||= {
|
|
23
19
|
"@" => ->(code, buffer) do
|
|
24
|
-
pieces = code.split
|
|
25
|
-
if pieces[0].start_with?(
|
|
20
|
+
pieces = code.split
|
|
21
|
+
if pieces[0].start_with?(%r{[A-Z]}) # Ruby class name
|
|
26
22
|
pieces[0].prepend " "
|
|
27
23
|
pieces[0] << ".new("
|
|
28
24
|
else # string or something else
|
|
@@ -31,194 +27,176 @@ module Serbea
|
|
|
31
27
|
|
|
32
28
|
includes_block = false
|
|
33
29
|
pieces.reverse.each do |piece|
|
|
34
|
-
|
|
35
|
-
piece.prepend(") ")
|
|
36
|
-
includes_block = true
|
|
37
|
-
break
|
|
38
|
-
end
|
|
39
|
-
end
|
|
30
|
+
next unless piece == "do" && (pieces.last == "do" || pieces.last.end_with?("|"))
|
|
40
31
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
pieces.last << ")"
|
|
45
|
-
buffer << "{%= render#{pieces.join(" ")} %}"
|
|
32
|
+
piece.prepend(") ")
|
|
33
|
+
includes_block = true
|
|
34
|
+
break
|
|
46
35
|
end
|
|
36
|
+
|
|
37
|
+
pieces.last << ")" unless includes_block
|
|
38
|
+
buffer << "{%= render#{pieces.join(" ")} %}"
|
|
47
39
|
end,
|
|
48
40
|
"`" => ->(code, buffer) do
|
|
49
41
|
buffer << "{%= %`"
|
|
50
42
|
buffer << code.gsub(%r("([^\\]?)\#{(.*?)}"), "\"\\1\#{h(\\2)}\"")
|
|
51
43
|
buffer << ".strip %}"
|
|
52
|
-
end
|
|
44
|
+
end,
|
|
53
45
|
}
|
|
54
46
|
end
|
|
47
|
+
# rubocop:enable Metrics
|
|
55
48
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
end
|
|
49
|
+
class << self
|
|
50
|
+
attr_writer :front_matter_preamble
|
|
59
51
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
def front_matter_preamble
|
|
53
|
+
@front_matter_preamble ||= "frontmatter = YAML.load"
|
|
54
|
+
end
|
|
63
55
|
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
def yaml_header?(template)
|
|
57
|
+
template.lines.first&.match? %r!\A---\s*\r?\n!
|
|
58
|
+
end
|
|
59
|
+
alias_method :has_yaml_header?, :yaml_header?
|
|
66
60
|
end
|
|
67
61
|
|
|
68
|
-
def initialize(input, properties={})
|
|
69
|
-
properties[:regexp] =
|
|
62
|
+
def initialize(input, properties = {})
|
|
63
|
+
properties[:regexp] = %r{{%(={1,2}|-|\#|%)?(.*?)([-=])?%}([ \t]*\r?\n)?}m
|
|
70
64
|
properties[:strip_front_matter] = true unless properties.key?(:strip_front_matter)
|
|
71
|
-
super
|
|
65
|
+
super(process_serbea_input(input, properties), properties)
|
|
72
66
|
end
|
|
73
67
|
|
|
68
|
+
# rubocop:disable Metrics
|
|
74
69
|
def process_serbea_input(template, properties)
|
|
75
|
-
buff = ""
|
|
76
|
-
|
|
70
|
+
buff = +""
|
|
71
|
+
|
|
77
72
|
string = template.dup
|
|
78
|
-
if properties[:strip_front_matter] && self.class.
|
|
79
|
-
if
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
string.post_match
|
|
87
|
-
end
|
|
73
|
+
if properties[:strip_front_matter] && self.class.yaml_header?(string) && (string = string.match(FRONT_MATTER_REGEXP))
|
|
74
|
+
require "yaml" if self.class.front_matter_preamble.include?(" = YAML.load")
|
|
75
|
+
|
|
76
|
+
string = "{% #{self.class.front_matter_preamble} <<~YAMLDATA\n#{
|
|
77
|
+
string[1].sub(%r{^---\n}, "")
|
|
78
|
+
}YAMLDATA\n%}#{
|
|
79
|
+
string[2].sub(%r{^---\n}, "")
|
|
80
|
+
}#{string.post_match}"
|
|
88
81
|
end
|
|
89
|
-
|
|
82
|
+
|
|
90
83
|
# Ensure the raw "tag" will strip out all ERB-style processing
|
|
91
84
|
until string.empty?
|
|
92
|
-
text, code, string = string.partition(
|
|
93
|
-
|
|
85
|
+
text, code, string = string.partition(%r{{% raw %}(.*?){% endraw %}}m)
|
|
86
|
+
|
|
94
87
|
buff << text
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
88
|
+
next unless code.length.positive?
|
|
89
|
+
|
|
90
|
+
buff << ::Regexp.last_match(1)
|
|
91
|
+
.gsub("{{", "__RAW_START_PRINT__")
|
|
92
|
+
.gsub("}}", "__RAW_END_PRINT__")
|
|
93
|
+
.gsub("{%", "__RAW_START_EVAL__")
|
|
94
|
+
.gsub("%}", "__RAW_END_EVAL__")
|
|
102
95
|
end
|
|
103
96
|
|
|
104
97
|
# Process any pipelines
|
|
105
98
|
string = buff
|
|
106
|
-
buff = ""
|
|
99
|
+
buff = +""
|
|
107
100
|
until string.empty?
|
|
108
|
-
text, code, string = string.partition(
|
|
109
|
-
|
|
101
|
+
text, code, string = string.partition(%r{{{(.*?)}}}m)
|
|
102
|
+
|
|
110
103
|
buff << text
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
else
|
|
134
|
-
# finally, we have liftoff!
|
|
135
|
-
segments << portion.sub(/\|>?$/, "")
|
|
136
|
-
end
|
|
137
|
-
end
|
|
104
|
+
next unless code.length.positive?
|
|
105
|
+
|
|
106
|
+
original_line_length = code.lines.size
|
|
107
|
+
|
|
108
|
+
s = StringScanner.new(::Regexp.last_match(1))
|
|
109
|
+
escaped_segment = ""
|
|
110
|
+
segments = []
|
|
111
|
+
until s.eos?
|
|
112
|
+
portion = s.scan_until(%r{\|>?})
|
|
113
|
+
if portion
|
|
114
|
+
if portion.end_with?('\|')
|
|
115
|
+
# the pipe is escaped, so save that for later
|
|
116
|
+
escaped_segment += portion.sub(%r{\\\|$}, "|")
|
|
117
|
+
elsif escaped_segment.length.positive?
|
|
118
|
+
# we already have escaped content, so finish that up
|
|
119
|
+
segments << (escaped_segment + portion.sub(%r{\|>?$}, ""))
|
|
120
|
+
escaped_segment = ""
|
|
121
|
+
elsif s.check(%r{\|})
|
|
122
|
+
# let's find out if this is actionable now
|
|
123
|
+
s.pos += 1
|
|
124
|
+
escaped_segment += "#{portion}|"
|
|
125
|
+
# nope, the next character is another pipe, so let's escape
|
|
138
126
|
else
|
|
139
|
-
# we
|
|
140
|
-
|
|
141
|
-
# escape and get the rest
|
|
142
|
-
segments << escaped_segment + s.rest
|
|
143
|
-
else
|
|
144
|
-
# or just the rest will do
|
|
145
|
-
segments << s.rest
|
|
146
|
-
end
|
|
147
|
-
s.terminate
|
|
127
|
+
# finally, we have liftoff!
|
|
128
|
+
segments << portion.sub(%r{\|>?$}, "")
|
|
148
129
|
end
|
|
130
|
+
else
|
|
131
|
+
# we've reached the last bit of the code
|
|
132
|
+
segments << if escaped_segment.length.positive?
|
|
133
|
+
# escape and get the rest
|
|
134
|
+
(escaped_segment + s.rest)
|
|
135
|
+
else
|
|
136
|
+
# or just the rest will do
|
|
137
|
+
s.rest
|
|
138
|
+
end
|
|
139
|
+
s.terminate
|
|
149
140
|
end
|
|
141
|
+
end
|
|
150
142
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
143
|
+
segments[0] = "pipeline(binding, (#{segments[0].strip}))"
|
|
144
|
+
segments[1..].each_with_index do |segment, index|
|
|
145
|
+
filter, args = segment.strip.match(%r{([^ :]*)(.*)}m).captures
|
|
146
|
+
segments[index + 1] = ".filter(:#{filter}"
|
|
147
|
+
segments[index + 1] += if args == ""
|
|
148
|
+
")"
|
|
149
|
+
else
|
|
150
|
+
",#{args.sub(%r{^:}, "")})"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
161
153
|
|
|
162
|
-
|
|
163
|
-
|
|
154
|
+
subs = "{%= #{segments.join} %}"
|
|
155
|
+
buff << subs
|
|
164
156
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
end
|
|
157
|
+
(original_line_length - subs.lines.size).times do
|
|
158
|
+
buff << "\n{% %}" # preserve original line length
|
|
168
159
|
end
|
|
169
160
|
end
|
|
170
161
|
|
|
171
162
|
# Process any render directives
|
|
172
163
|
string = buff
|
|
173
|
-
buff = ""
|
|
164
|
+
buff = +""
|
|
165
|
+
end_matches = ["end", ""]
|
|
174
166
|
until string.empty?
|
|
175
|
-
text, code, string = string.partition(
|
|
167
|
+
text, code, string = string.partition(%r{{%@([a-z_`]+)?(.*?)%}}m)
|
|
176
168
|
|
|
177
169
|
buff << text
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
end
|
|
190
|
-
else
|
|
191
|
-
raise Serbea::Error, "Handler for Serbea template directive `#{$1}' not found"
|
|
192
|
-
end
|
|
193
|
-
else
|
|
194
|
-
buff << "{% end %}"
|
|
170
|
+
next unless code.length.positive?
|
|
171
|
+
|
|
172
|
+
code = ::Regexp.last_match(2)
|
|
173
|
+
if end_matches.include? code.strip
|
|
174
|
+
buff << "{% end %}"
|
|
175
|
+
else
|
|
176
|
+
directive = ::Regexp.last_match(1) ? self.class.directives[::Regexp.last_match(1)] : self.class.directives["@"]
|
|
177
|
+
|
|
178
|
+
unless directive
|
|
179
|
+
raise Serbea::Error,
|
|
180
|
+
"Handler for Serbea template directive `#{::Regexp.last_match(1)}' not found"
|
|
195
181
|
end
|
|
182
|
+
|
|
183
|
+
additional_length = "#{buff}#{code}".lines.size
|
|
184
|
+
directive.(code, buff)
|
|
185
|
+
(additional_length - buff.lines.size).times do
|
|
186
|
+
buff << "{% %}\n" # preserve original directive line length
|
|
187
|
+
end
|
|
188
|
+
|
|
196
189
|
end
|
|
197
190
|
end
|
|
198
191
|
|
|
199
192
|
buff
|
|
200
193
|
end
|
|
194
|
+
# rubocop:enable Metrics
|
|
201
195
|
|
|
202
196
|
private
|
|
203
197
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
@src << ";#{@bufvar};" if code.strip.split(".").first == "end"
|
|
207
|
-
@src << ';' unless code[Erubi::RANGE_LAST] == "\n"
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
# pulled from Rails' ActionView
|
|
211
|
-
BLOCK_EXPR = %r!\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z!.freeze
|
|
212
|
-
|
|
213
|
-
def add_expression(indicator, code)
|
|
214
|
-
if BLOCK_EXPR.match?(code)
|
|
215
|
-
src << "#{@bufvar}.append= " << code
|
|
216
|
-
else
|
|
217
|
-
super
|
|
218
|
-
end
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
# Don't allow == to output escaped strings, as that's the opposite of Rails
|
|
198
|
+
# Don't allow `{%==` to output escaped strings, it should be identical
|
|
199
|
+
# to `{%=`
|
|
222
200
|
def add_expression_result_escaped(code)
|
|
223
201
|
add_expression_result(code)
|
|
224
202
|
end
|
|
@@ -226,11 +204,11 @@ module Serbea
|
|
|
226
204
|
def add_postamble(postamble)
|
|
227
205
|
src << postamble
|
|
228
206
|
src << "#{@bufvar}.html_safe"
|
|
229
|
-
|
|
207
|
+
|
|
230
208
|
src.gsub!("__RAW_START_PRINT__", "{{")
|
|
231
209
|
src.gsub!("__RAW_END_PRINT__", "}}")
|
|
232
210
|
src.gsub!("__RAW_START_EVAL__", "{%")
|
|
233
211
|
src.gsub!("__RAW_END_EVAL__", "%}")
|
|
234
212
|
end
|
|
235
213
|
end
|
|
236
|
-
end
|
|
214
|
+
end
|
data/lib/serbea.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "tilt"
|
|
2
4
|
require "tilt/erubi"
|
|
5
|
+
require "erubi/capture_block"
|
|
3
6
|
|
|
4
7
|
module Serbea
|
|
5
8
|
class Error < StandardError; end
|
|
@@ -16,7 +19,7 @@ module Tilt
|
|
|
16
19
|
def prepare
|
|
17
20
|
@options.merge!(
|
|
18
21
|
outvar: "@_erbout",
|
|
19
|
-
bufval: "Serbea::OutputBuffer.new",
|
|
22
|
+
bufval: "Serbea::OutputBuffer.new",
|
|
20
23
|
literal_prefix: "{%",
|
|
21
24
|
literal_postfix: "%}",
|
|
22
25
|
engine_class: Serbea::TemplateEngine
|
data/lib/version.rb
CHANGED
data/serbea.gemspec
CHANGED
|
@@ -16,8 +16,9 @@ Gem::Specification.new do |spec|
|
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r!^(test|script|spec|features|docs|serbea-rails)/!) }
|
|
17
17
|
spec.require_paths = ["lib"]
|
|
18
18
|
|
|
19
|
-
spec.add_runtime_dependency("erubi", ">= 1.
|
|
20
|
-
spec.add_runtime_dependency("tilt", ">= 2.
|
|
19
|
+
spec.add_runtime_dependency("erubi", ">= 1.13")
|
|
20
|
+
spec.add_runtime_dependency("tilt", ">= 2.6")
|
|
21
21
|
|
|
22
22
|
spec.add_development_dependency("rake", "~> 13.0")
|
|
23
|
+
spec.add_development_dependency("rubocop-bridgetown", "~> 0.7")
|
|
23
24
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: serbea
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bridgetown Team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-11-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: erubi
|
|
@@ -16,28 +16,28 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.
|
|
19
|
+
version: '1.13'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.
|
|
26
|
+
version: '1.13'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: tilt
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '2.
|
|
33
|
+
version: '2.6'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '2.
|
|
40
|
+
version: '2.6'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rake
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,6 +52,20 @@ dependencies:
|
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '13.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rubocop-bridgetown
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0.7'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0.7'
|
|
55
69
|
description:
|
|
56
70
|
email: maintainers@bridgetownrb.com
|
|
57
71
|
executables: []
|
|
@@ -60,6 +74,7 @@ extra_rdoc_files: []
|
|
|
60
74
|
files:
|
|
61
75
|
- ".github/workflows/ci.yml"
|
|
62
76
|
- ".gitignore"
|
|
77
|
+
- ".rubocop.yml"
|
|
63
78
|
- ".ruby-version"
|
|
64
79
|
- CHANGELOG.md
|
|
65
80
|
- Gemfile
|
|
@@ -67,6 +82,7 @@ files:
|
|
|
67
82
|
- README.md
|
|
68
83
|
- Rakefile
|
|
69
84
|
- bin/rake
|
|
85
|
+
- bin/rubocop
|
|
70
86
|
- lib/rouge/lexers/serbea.rb
|
|
71
87
|
- lib/serbea.rb
|
|
72
88
|
- lib/serbea/helpers.rb
|