serbea 2.3.0 → 2.4.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8aebfee6e539271599cc9d84ec8c5220a13cd7746c82b687d257fc431b60fb5c
4
- data.tar.gz: c61b07f0c4402f067e354fa652cd32eaec060768bcfdf0228ca2d208d7402c77
3
+ metadata.gz: 4a2207393defcac51a7aa477e09e7abd41b3ae96334c7dc2412dbdf22807db7c
4
+ data.tar.gz: 5c37928958ad113ab4a493783d3c15f55904c8a4b9e26ec58c041ee2bcca8037
5
5
  SHA512:
6
- metadata.gz: 7a3372bddc2a463c1d3dd631ff3777ae60671bb2a20463a7fea9db3da8fb5a2b28cd5338fb251c454358ff33ddf58f7fe6e40ce5c4e320e6df2c042000463ae9
7
- data.tar.gz: 8cf230f8b0aa7fafb00c73ca80ff4805952ce6b3aa3cb3a7a781d8a1621bb7b1299ced2d30aa03dda2604e350fba7a6109861be90267f3c579a9d9bd8a3bd5c7
6
+ metadata.gz: 3133a8151625c32b6a5b14b235ac524e8fa22cd0fbc6e61efc4e9f60583907c5b96a83b693951daae6442511c2dde434867ea9bdd727e007f0129cde693d0bd0
7
+ data.tar.gz: edd49253c26b54b161783065ad511918f53381d16e33ab638faf60e3941be9c7f55ccd151f0ae6bcf10142db365a667119433f0b4e1b6dde08eb64767ad85199
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
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ .
6
+
7
+ ## 2.4.1
8
+
9
+ - Ensure output buffer class itself is marked as HTML safe
10
+
11
+ ## 2.4.0
12
+
13
+ - Convert to using new `Erubi::CaptureBlockEngine`
14
+ - Add RuboCop to repo and apply many suggested fixes
15
+
3
16
  ## 2.3.0
4
17
 
5
18
  - Migrate repo to Codeberg
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
- task :default => :test
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")
@@ -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(mod)
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.empty? ? result : @_erbout
26
+ result = @_erbout unless @_erbout.empty?
25
27
  @_erbout = previous_buffer_state
26
28
 
27
- Serbea::OutputBuffer === result ? result.html_safe : result
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(*args, **kwargs, &block)
43
+ def import(...)
42
44
  helper_names = %i(partial render)
43
45
  available_helper = helper_names.find { |meth| respond_to?(meth) }
44
- raise Serbea::Error, "Serbea Error: no `render' or `partial' helper available in #{self.class}" unless available_helper
45
- available_helper == :partial ? partial(*args, **kwargs, &block) : render(*args, **kwargs, &block)
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
- self.instance_variable_set("@#{varname}", input)
74
+ instance_variable_set("@#{varname}", input)
69
75
  preserve ? input : nil
70
76
  end
71
77
  end
@@ -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
- def self.output_processor=(processor)
44
- @output_processor = processor
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 ||= lambda do |input|
50
- (!input.html_safe? && self.autoescape) ? Erubi.h(input) : input.html_safe
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
- def self.autoescape=(config_boolean)
55
- @autoescape = config_boolean
54
+ class << self
55
+ attr_writer :autoescape
56
56
  end
57
+
57
58
  def self.autoescape
58
- @autoescape.nil? ? true : @autoescape
59
+ @autoescape.nil? || @autoescape
59
60
  end
60
61
 
61
- def self.raise_on_missing_filters=(config_boolean)
62
- @raise_on_missing_filters = config_boolean
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(Serbea::FilterMissing, warning) : STDERR.puts(warning)
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(Serbea::FilterMissing, warning) : STDERR.puts(warning)
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,32 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "strscan"
2
4
 
3
5
  module Serbea
4
- class OutputBuffer < String
5
- def concat_to_s(input)
6
- concat input.to_s
6
+ class OutputBuffer < Erubi::CaptureBlockEngine::Buffer
7
+ def initialize(*)
8
+ super
9
+ @html_safe = true
7
10
  end
8
11
 
9
- alias_method :safe_append=, :concat_to_s
10
- alias_method :append=, :concat_to_s
11
- alias_method :safe_expr_append=, :concat_to_s
12
+ def to_s
13
+ super.tap { _1.instance_variable_set(:@html_safe, true) if html_safe? }
14
+ end
12
15
  end
13
16
 
14
- class TemplateEngine < Erubi::Engine
15
- FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m.freeze
17
+ class TemplateEngine < Erubi::CaptureBlockEngine
18
+ FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m
16
19
 
17
20
  def self.directive(new_directive, directive_resolution)
18
21
  directives[new_directive.to_s] = directive_resolution
19
22
  end
20
23
 
24
+ # rubocop:disable Metrics
21
25
  def self.directives
22
26
  @directives ||= {
23
27
  "@" => ->(code, buffer) do
24
- pieces = code.split(" ")
25
- if pieces[0].start_with?(/[A-Z]/) # Ruby class name
28
+ pieces = code.split
29
+ if pieces[0].start_with?(%r{[A-Z]}) # Ruby class name
26
30
  pieces[0].prepend " "
27
31
  pieces[0] << ".new("
28
32
  else # string or something else
@@ -31,194 +35,176 @@ module Serbea
31
35
 
32
36
  includes_block = false
33
37
  pieces.reverse.each do |piece|
34
- if piece == "do" && (pieces.last == "do" || pieces.last.end_with?("|"))
35
- piece.prepend(") ")
36
- includes_block = true
37
- break
38
- end
39
- end
38
+ next unless piece == "do" && (pieces.last == "do" || pieces.last.end_with?("|"))
40
39
 
41
- if includes_block
42
- buffer << "{%= render#{pieces.join(" ")} %}"
43
- else
44
- pieces.last << ")"
45
- buffer << "{%= render#{pieces.join(" ")} %}"
40
+ piece.prepend(") ")
41
+ includes_block = true
42
+ break
46
43
  end
44
+
45
+ pieces.last << ")" unless includes_block
46
+ buffer << "{%= render#{pieces.join(" ")} %}"
47
47
  end,
48
48
  "`" => ->(code, buffer) do
49
49
  buffer << "{%= %`"
50
50
  buffer << code.gsub(%r("([^\\]?)\#{(.*?)}"), "\"\\1\#{h(\\2)}\"")
51
51
  buffer << ".strip %}"
52
- end
52
+ end,
53
53
  }
54
54
  end
55
+ # rubocop:enable Metrics
55
56
 
56
- def self.front_matter_preamble=(varname)
57
- @front_matter_preamble = varname
58
- end
57
+ class << self
58
+ attr_writer :front_matter_preamble
59
59
 
60
- def self.front_matter_preamble
61
- @front_matter_preamble ||= "frontmatter = YAML.load"
62
- end
60
+ def front_matter_preamble
61
+ @front_matter_preamble ||= "frontmatter = YAML.load"
62
+ end
63
63
 
64
- def self.has_yaml_header?(template)
65
- template.lines.first&.match? %r!\A---\s*\r?\n!
64
+ def yaml_header?(template)
65
+ template.lines.first&.match? %r!\A---\s*\r?\n!
66
+ end
67
+ alias_method :has_yaml_header?, :yaml_header?
66
68
  end
67
69
 
68
- def initialize(input, properties={})
69
- properties[:regexp] = /{%(={1,2}|-|\#|%)?(.*?)([-=])?%}([ \t]*\r?\n)?/m
70
+ def initialize(input, properties = {})
71
+ properties[:regexp] = %r{{%(={1,2}|-|\#|%)?(.*?)([-=])?%}([ \t]*\r?\n)?}m
70
72
  properties[:strip_front_matter] = true unless properties.key?(:strip_front_matter)
71
- super process_serbea_input(input, properties), properties
73
+ super(process_serbea_input(input, properties), properties)
72
74
  end
73
75
 
76
+ # rubocop:disable Metrics
74
77
  def process_serbea_input(template, properties)
75
- buff = ""
76
-
78
+ buff = +""
79
+
77
80
  string = template.dup
78
- if properties[:strip_front_matter] && self.class.has_yaml_header?(string)
79
- if string = string.match(FRONT_MATTER_REGEXP)
80
- require "yaml" if self.class.front_matter_preamble.include?(" = YAML.load")
81
-
82
- string = "{% #{self.class.front_matter_preamble} <<~YAMLDATA\n" +
83
- string[1].sub(/^---\n/,'') +
84
- "YAMLDATA\n%}" +
85
- string[2].sub(/^---\n/, '') +
86
- string.post_match
87
- end
81
+ if properties[:strip_front_matter] && self.class.yaml_header?(string) && (string = string.match(FRONT_MATTER_REGEXP))
82
+ require "yaml" if self.class.front_matter_preamble.include?(" = YAML.load")
83
+
84
+ string = "{% #{self.class.front_matter_preamble} <<~YAMLDATA\n#{
85
+ string[1].sub(%r{^---\n}, "")
86
+ }YAMLDATA\n%}#{
87
+ string[2].sub(%r{^---\n}, "")
88
+ }#{string.post_match}"
88
89
  end
89
-
90
+
90
91
  # Ensure the raw "tag" will strip out all ERB-style processing
91
92
  until string.empty?
92
- text, code, string = string.partition(/{% raw %}(.*?){% endraw %}/m)
93
-
93
+ text, code, string = string.partition(%r{{% raw %}(.*?){% endraw %}}m)
94
+
94
95
  buff << text
95
- if code.length > 0
96
- buff << $1.
97
- gsub("{{", "__RAW_START_PRINT__").
98
- gsub("}}", "__RAW_END_PRINT__").
99
- gsub("{%", "__RAW_START_EVAL__").
100
- gsub("%}", "__RAW_END_EVAL__")
101
- end
96
+ next unless code.length.positive?
97
+
98
+ buff << ::Regexp.last_match(1)
99
+ .gsub("{{", "__RAW_START_PRINT__")
100
+ .gsub("}}", "__RAW_END_PRINT__")
101
+ .gsub("{%", "__RAW_START_EVAL__")
102
+ .gsub("%}", "__RAW_END_EVAL__")
102
103
  end
103
104
 
104
105
  # Process any pipelines
105
106
  string = buff
106
- buff = ""
107
+ buff = +""
107
108
  until string.empty?
108
- text, code, string = string.partition(/{{(.*?)}}/m)
109
-
109
+ text, code, string = string.partition(%r{{{(.*?)}}}m)
110
+
110
111
  buff << text
111
- if code.length > 0
112
- original_line_length = code.lines.size
113
-
114
- s = StringScanner.new($1)
115
- escaped_segment = ""
116
- segments = []
117
- until s.eos?
118
- portion = s.scan_until(/\|>?/)
119
- if portion
120
- if portion.end_with?('\|')
121
- # the pipe is escaped, so save that for later
122
- escaped_segment += portion.sub(/\\\|$/, "|")
123
- elsif escaped_segment.length > 0
124
- # we already have escaped content, so finish that up
125
- segments << escaped_segment + portion.sub(/\|>?$/, "")
126
- escaped_segment = ""
127
- else
128
- # let's find out if this is actionable now
129
- if s.check(/\|/)
130
- # nope, the next character is another pipe, so let's escape
131
- s.pos += 1
132
- escaped_segment += portion + "|"
133
- else
134
- # finally, we have liftoff!
135
- segments << portion.sub(/\|>?$/, "")
136
- end
137
- end
112
+ next unless code.length.positive?
113
+
114
+ original_line_length = code.lines.size
115
+
116
+ s = StringScanner.new(::Regexp.last_match(1))
117
+ escaped_segment = ""
118
+ segments = []
119
+ until s.eos?
120
+ portion = s.scan_until(%r{\|>?})
121
+ if portion
122
+ if portion.end_with?('\|')
123
+ # the pipe is escaped, so save that for later
124
+ escaped_segment += portion.sub(%r{\\\|$}, "|")
125
+ elsif escaped_segment.length.positive?
126
+ # we already have escaped content, so finish that up
127
+ segments << (escaped_segment + portion.sub(%r{\|>?$}, ""))
128
+ escaped_segment = ""
129
+ elsif s.check(%r{\|})
130
+ # let's find out if this is actionable now
131
+ s.pos += 1
132
+ escaped_segment += "#{portion}|"
133
+ # nope, the next character is another pipe, so let's escape
138
134
  else
139
- # we've reached the last bit of the code
140
- if escaped_segment.length > 0
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
135
+ # finally, we have liftoff!
136
+ segments << portion.sub(%r{\|>?$}, "")
148
137
  end
138
+ else
139
+ # we've reached the last bit of the code
140
+ segments << if escaped_segment.length.positive?
141
+ # escape and get the rest
142
+ (escaped_segment + s.rest)
143
+ else
144
+ # or just the rest will do
145
+ s.rest
146
+ end
147
+ s.terminate
149
148
  end
149
+ end
150
150
 
151
- segments[0] = "pipeline(binding, (#{segments[0].strip}))"
152
- segments[1..-1].each_with_index do |segment, index|
153
- filter, args = segment.strip.match(/([^ :]*)(.*)/m).captures
154
- segments[index + 1] = ".filter(:" + filter
155
- if args == ""
156
- segments[index + 1] += ")"
157
- else
158
- segments[index + 1] += "," + args.sub(/^:/, "") + ")"
159
- end
160
- end
151
+ segments[0] = "pipeline(binding, (#{segments[0].strip}))"
152
+ segments[1..].each_with_index do |segment, index|
153
+ filter, args = segment.strip.match(%r{([^ :]*)(.*)}m).captures
154
+ segments[index + 1] = ".filter(:#{filter}"
155
+ segments[index + 1] += if args == ""
156
+ ")"
157
+ else
158
+ ",#{args.sub(%r{^:}, "")})"
159
+ end
160
+ end
161
161
 
162
- subs = "{%= #{segments.join} %}"
163
- buff << subs
162
+ subs = "{%= #{segments.join} %}"
163
+ buff << subs
164
164
 
165
- (original_line_length - subs.lines.size).times do
166
- buff << "\n{% %}" # preserve original line length
167
- end
165
+ (original_line_length - subs.lines.size).times do
166
+ buff << "\n{% %}" # preserve original line length
168
167
  end
169
168
  end
170
169
 
171
170
  # Process any render directives
172
171
  string = buff
173
- buff = ""
172
+ buff = +""
173
+ end_matches = ["end", ""]
174
174
  until string.empty?
175
- text, code, string = string.partition(/{%@([a-z_`]+)?(.*?)%}/m)
175
+ text, code, string = string.partition(%r{{%@([a-z_`]+)?(.*?)%}}m)
176
176
 
177
177
  buff << text
178
- if code.length > 0
179
- directive = $1
180
- code = $2
181
- unless ["end", ""].include? code.strip
182
- directive = $1 ? self.class.directives[$1] : self.class.directives["@"]
183
-
184
- if directive
185
- additional_length = "#{buff}#{code}".lines.size
186
- directive.(code, buff)
187
- (additional_length - buff.lines.size).times do
188
- buff << "{% %}\n" # preserve original directive line length
189
- end
190
- else
191
- raise Serbea::Error, "Handler for Serbea template directive `#{$1}' not found"
192
- end
193
- else
194
- buff << "{% end %}"
178
+ next unless code.length.positive?
179
+
180
+ code = ::Regexp.last_match(2)
181
+ if end_matches.include? code.strip
182
+ buff << "{% end %}"
183
+ else
184
+ directive = ::Regexp.last_match(1) ? self.class.directives[::Regexp.last_match(1)] : self.class.directives["@"]
185
+
186
+ unless directive
187
+ raise Serbea::Error,
188
+ "Handler for Serbea template directive `#{::Regexp.last_match(1)}' not found"
195
189
  end
190
+
191
+ additional_length = "#{buff}#{code}".lines.size
192
+ directive.(code, buff)
193
+ (additional_length - buff.lines.size).times do
194
+ buff << "{% %}\n" # preserve original directive line length
195
+ end
196
+
196
197
  end
197
198
  end
198
199
 
199
200
  buff
200
201
  end
202
+ # rubocop:enable Metrics
201
203
 
202
204
  private
203
205
 
204
- def add_code(code)
205
- @src << code
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
206
+ # Don't allow `{%==` to output escaped strings, it should be identical
207
+ # to `{%=`
222
208
  def add_expression_result_escaped(code)
223
209
  add_expression_result(code)
224
210
  end
@@ -226,11 +212,11 @@ module Serbea
226
212
  def add_postamble(postamble)
227
213
  src << postamble
228
214
  src << "#{@bufvar}.html_safe"
229
-
215
+
230
216
  src.gsub!("__RAW_START_PRINT__", "{{")
231
217
  src.gsub!("__RAW_END_PRINT__", "}}")
232
218
  src.gsub!("__RAW_START_EVAL__", "{%")
233
219
  src.gsub!("__RAW_END_EVAL__", "%}")
234
220
  end
235
221
  end
236
- end
222
+ 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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Serbea
2
- VERSION = "2.3.0"
3
- end
4
+ VERSION = "2.4.1"
5
+ end
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.11")
20
- spec.add_runtime_dependency("tilt", ">= 2.1")
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.3.0
4
+ version: 2.4.1
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-09-11 00:00:00.000000000 Z
11
+ date: 2025-11-26 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.11'
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.11'
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.1'
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.1'
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