bade 0.2.5 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Bade.gemspec +5 -4
- data/Gemfile +6 -2
- data/README.md +3 -3
- data/lib/bade/ast/document.rb +1 -1
- data/lib/bade/ast/node/value_node.rb +10 -1
- data/lib/bade/ast/node.rb +12 -2
- data/lib/bade/ast/node_registrator.rb +3 -2
- data/lib/bade/ast/string_serializer.rb +3 -5
- data/lib/bade/generator.rb +73 -19
- data/lib/bade/parser/parser_lines.rb +8 -6
- data/lib/bade/parser/parser_mixin.rb +7 -1
- data/lib/bade/parser.rb +4 -2
- data/lib/bade/precompiled.rb +2 -2
- data/lib/bade/renderer.rb +66 -20
- data/lib/bade/ruby2_keywords.rb +1 -0
- data/lib/bade/ruby_extensions/string.rb +4 -3
- data/lib/bade/runtime/block.rb +47 -9
- data/lib/bade/runtime/globals_tracker.rb +59 -0
- data/lib/bade/runtime/mixin.rb +18 -6
- data/lib/bade/runtime/render_binding.rb +56 -13
- data/lib/bade/runtime.rb +79 -1
- data/lib/bade/version.rb +1 -1
- data/lib/bade.rb +1 -0
- metadata +20 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 202b283306b0a05d870766ff8d7c17f8312727843632958adb2bb3f2300a673f
|
4
|
+
data.tar.gz: '0820f4f77eb7936b77748257faa1fd4ee51b206d4a7929c656f717127c7b2248'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ecd2dbf7aa570c8051a2e1b59a1a537583cd2cd5005bb06328795de099fab4213a9cfd616a788d66e333a862253d6aecedd928632e3edb923f021e86658f6780
|
7
|
+
data.tar.gz: 8633aba2120eaf8f60f96a357cde8eda8c32d862e92dca3c060f66c014bf1031e5bc2916077ee1d1a7de6494cc62c426ded062065c664949acaf849fce71de66
|
data/Bade.gemspec
CHANGED
@@ -14,16 +14,17 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.summary = 'Minimalistic template engine for Ruby.'
|
15
15
|
spec.homepage = 'https://github.com/epuber-io/bade'
|
16
16
|
spec.license = 'MIT'
|
17
|
-
spec.
|
17
|
+
spec.metadata = { 'rubygems_mfa_required' => 'true' }
|
18
|
+
spec.required_ruby_version = '>= 2.5'
|
18
19
|
|
19
20
|
spec.files = Dir['bin/**/*'] + Dir['lib/**/*'] + %w[Bade.gemspec Gemfile README.md]
|
20
21
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
22
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
23
|
spec.require_paths = ['lib']
|
23
24
|
|
24
|
-
spec.add_dependency 'psych', '>= 2.2', '<
|
25
|
+
spec.add_dependency 'psych', '>= 2.2', '< 5.0'
|
25
26
|
|
26
|
-
spec.add_development_dependency '
|
27
|
+
spec.add_development_dependency 'fakefs', '~> 1.3'
|
27
28
|
spec.add_development_dependency 'rspec', '~> 3.2'
|
28
|
-
spec.add_development_dependency 'rubocop', '~>
|
29
|
+
spec.add_development_dependency 'rubocop', '~> 1.14'
|
29
30
|
end
|
data/Gemfile
CHANGED
@@ -5,9 +5,13 @@ source 'https://rubygems.org'
|
|
5
5
|
gemspec
|
6
6
|
|
7
7
|
gem 'benchmark-ips', require: false
|
8
|
-
gem 'coveralls', require: false
|
9
8
|
|
10
|
-
group :
|
9
|
+
group :dev, optional: true do
|
10
|
+
gem 'debase', require: false
|
11
|
+
gem 'ruby-debug-ide', require: false
|
12
|
+
end
|
13
|
+
|
14
|
+
group :benchmarks, optional: true do
|
11
15
|
gem 'flamegraph'
|
12
16
|
gem 'ruby-prof'
|
13
17
|
end
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
# Bade
|
3
3
|
|
4
|
-
[![Gem Version](https://badge.fury.io/rb/bade.svg)](http://badge.fury.io/rb/bade) [![Build Status](https://
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/bade.svg)](http://badge.fury.io/rb/bade) [![Build Status](https://github.com/epuber-io/bade/actions/workflows/tests.yml/badge.svg)](https://github.com/epuber-io/bade/actions) [![Coverage Status](https://coveralls.io/repos/epuber-io/bade/badge.svg?branch=master&service=github)](https://coveralls.io/github/epuber-io/bade?branch=master) [![Inline docs](https://inch-ci.org/github/epuber-io/bade.svg?branch=master)](https://inch-ci.org/github/epuber-io/bade)
|
5
5
|
|
6
6
|
|
7
7
|
Minimalistic template engine written in Ruby for Ruby. Developed mainly to help with creating e-books. Highly influenced by [Jade](http://jade-lang.com) and [Slim](http://slim-lang.com).
|
@@ -30,7 +30,7 @@ Or install it yourself as:
|
|
30
30
|
|
31
31
|
## Development
|
32
32
|
|
33
|
-
After checking out the repo, run `bundle install` to install dependencies. Then, run `rake spec` to run the tests.
|
33
|
+
After checking out the repo, run `bundle install --with dev` to install dependencies. Then, run `rake spec` to run the tests.
|
34
34
|
|
35
35
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
36
36
|
|
@@ -40,7 +40,7 @@ To install this gem onto your local machine, run `bundle exec rake install`.
|
|
40
40
|
Bug reports and pull requests are welcome on GitHub at https://github.com/epuber-io/bade. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
41
41
|
|
42
42
|
|
43
|
-
##
|
43
|
+
## TODO
|
44
44
|
|
45
45
|
- [ ] create documentation about syntax
|
46
46
|
- [ ] create several examples
|
data/lib/bade/ast/document.rb
CHANGED
@@ -17,17 +17,26 @@ module Bade
|
|
17
17
|
#
|
18
18
|
attr_accessor :conditional
|
19
19
|
|
20
|
+
# @return [String, nil]
|
21
|
+
#
|
22
|
+
attr_accessor :default_value
|
23
|
+
|
20
24
|
ruby2_keywords def initialize(*args)
|
21
25
|
super(*args)
|
22
26
|
|
23
27
|
@escaped = false
|
24
28
|
@conditional = false
|
29
|
+
@default_value = nil
|
25
30
|
end
|
26
31
|
|
27
32
|
# @param [ValueNode] other
|
28
33
|
#
|
29
34
|
def ==(other)
|
30
|
-
super &&
|
35
|
+
super &&
|
36
|
+
value == other.value &&
|
37
|
+
escaped == other.escaped &&
|
38
|
+
conditional == other.conditional &&
|
39
|
+
default_value == other.default_value
|
31
40
|
end
|
32
41
|
end
|
33
42
|
end
|
data/lib/bade/ast/node.rb
CHANGED
@@ -10,7 +10,11 @@ module Bade
|
|
10
10
|
#
|
11
11
|
attr_reader :type
|
12
12
|
|
13
|
-
# @return [
|
13
|
+
# @return [Bade::AST::Node, nil]
|
14
|
+
#
|
15
|
+
attr_accessor :parent
|
16
|
+
|
17
|
+
# @return [Array<Bade::AST::Node>]
|
14
18
|
#
|
15
19
|
attr_accessor :children
|
16
20
|
|
@@ -20,10 +24,16 @@ module Bade
|
|
20
24
|
#
|
21
25
|
attr_reader :lineno
|
22
26
|
|
23
|
-
|
27
|
+
# @return [String] filename
|
28
|
+
#
|
29
|
+
attr_reader :filename
|
30
|
+
|
31
|
+
def initialize(type, parent = nil, lineno: nil, filename: nil)
|
24
32
|
@type = type
|
33
|
+
@parent = parent
|
25
34
|
@children = []
|
26
35
|
@lineno = lineno
|
36
|
+
@filename = filename
|
27
37
|
end
|
28
38
|
|
29
39
|
def to_s
|
@@ -38,12 +38,12 @@ module Bade
|
|
38
38
|
#
|
39
39
|
# @return [Bade::AST::Node]
|
40
40
|
#
|
41
|
-
def create(type, lineno)
|
41
|
+
def create(type, parent, lineno: nil, filename: nil)
|
42
42
|
klass = registered_types[type]
|
43
43
|
|
44
44
|
raise ::KeyError, "Undefined node type #{type.inspect}" if klass.nil?
|
45
45
|
|
46
|
-
klass.new(type, lineno: lineno)
|
46
|
+
klass.new(type, parent, lineno: lineno, filename: filename)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
@@ -55,6 +55,7 @@ module Bade
|
|
55
55
|
register_type DoctypeNode, :doctype
|
56
56
|
|
57
57
|
register_type ValueNode, :import
|
58
|
+
register_type ValueNode, :yield
|
58
59
|
|
59
60
|
# --- Comments
|
60
61
|
|
@@ -37,13 +37,13 @@ module Bade
|
|
37
37
|
|
38
38
|
children_s = ''
|
39
39
|
if node.children.count > 0
|
40
|
-
children_s = "\n
|
40
|
+
children_s = "\n#{node.children.map { |n| node_to_s(n, level + 1) }.join("\n")}\n#{indent}"
|
41
41
|
end
|
42
42
|
|
43
43
|
other = ''
|
44
44
|
|
45
45
|
case node
|
46
|
-
when TagNode
|
46
|
+
when TagNode, MixinCommonNode
|
47
47
|
other = node.name
|
48
48
|
when KeyValueNode
|
49
49
|
other = "#{node.name}:#{node.value}"
|
@@ -56,15 +56,13 @@ module Bade
|
|
56
56
|
''
|
57
57
|
end
|
58
58
|
other = "#{escaped_sign}#{node.value}"
|
59
|
-
when MixinCommonNode
|
60
|
-
other = node.name
|
61
59
|
when Node
|
62
60
|
# nothing
|
63
61
|
else
|
64
62
|
raise "Unknown node class #{node.class} of type #{node.type} for serializing"
|
65
63
|
end
|
66
64
|
|
67
|
-
other =
|
65
|
+
other = " #{other}" if other && !other.empty?
|
68
66
|
|
69
67
|
"#{indent}(#{type_s}#{other}#{children_s})"
|
70
68
|
end
|
data/lib/bade/generator.rb
CHANGED
@@ -75,7 +75,7 @@ module Bade
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def buff_code(text)
|
78
|
-
@buff << ' ' * @code_indent
|
78
|
+
@buff << "#{' ' * @code_indent}#{text}"
|
79
79
|
end
|
80
80
|
|
81
81
|
# @param document [Bade::Document]
|
@@ -86,6 +86,7 @@ module Bade
|
|
86
86
|
end
|
87
87
|
|
88
88
|
buff_code("# ----- start file #{document.file_path}") unless document.file_path.nil?
|
89
|
+
buff_code "__buffs_push(#{location(filename: document.file_path, lineno: 0, label: '<top>')})"
|
89
90
|
|
90
91
|
new_root = if @optimize
|
91
92
|
Optimizer.new(document.root).optimize
|
@@ -115,6 +116,8 @@ module Bade
|
|
115
116
|
# @param current_node [Node]
|
116
117
|
#
|
117
118
|
def visit_node(current_node)
|
119
|
+
update_location_node(current_node)
|
120
|
+
|
118
121
|
case current_node.type
|
119
122
|
when :root
|
120
123
|
visit_node_children(current_node)
|
@@ -134,7 +137,7 @@ module Bade
|
|
134
137
|
buff_print_text ' -->'
|
135
138
|
|
136
139
|
when :comment
|
137
|
-
comment_text =
|
140
|
+
comment_text = "##{current_node.children.map(&:value).join("\n#")}"
|
138
141
|
buff_code(comment_text)
|
139
142
|
|
140
143
|
when :doctype
|
@@ -166,8 +169,11 @@ module Bade
|
|
166
169
|
"#{base_path}.rb"
|
167
170
|
end
|
168
171
|
|
169
|
-
buff_code "
|
170
|
-
|
172
|
+
buff_code "__load('#{load_path}')" unless load_path.nil?
|
173
|
+
when :yield
|
174
|
+
block_name = DEFAULT_BLOCK_NAME
|
175
|
+
method = current_node.conditional ? 'call' : 'call!'
|
176
|
+
buff_code "#{block_name}.#{method}"
|
171
177
|
else
|
172
178
|
raise "Unknown type #{current_node.type}"
|
173
179
|
end
|
@@ -267,19 +273,22 @@ module Bade
|
|
267
273
|
params = mixin_node.params
|
268
274
|
result = []
|
269
275
|
|
270
|
-
|
276
|
+
case mixin_node.type
|
277
|
+
when :mixin_call
|
271
278
|
blocks = mixin_node.blocks
|
272
279
|
|
273
280
|
other_children = (mixin_node.children - mixin_node.blocks - mixin_node.params)
|
274
281
|
if other_children.count { |n| n.type != :newline } > 0
|
275
|
-
def_block_node = AST::NodeRegistrator.create(:mixin_block, mixin_node.lineno)
|
282
|
+
def_block_node = AST::NodeRegistrator.create(:mixin_block, mixin_node, lineno: mixin_node.lineno)
|
276
283
|
def_block_node.name = DEFAULT_BLOCK_NAME
|
277
284
|
def_block_node.children = other_children
|
278
285
|
|
279
286
|
blocks << def_block_node
|
280
287
|
end
|
281
288
|
|
282
|
-
if
|
289
|
+
if blocks.empty?
|
290
|
+
result << '{}'
|
291
|
+
else
|
283
292
|
buff_code '__blocks = {}'
|
284
293
|
|
285
294
|
blocks.each do |block|
|
@@ -287,17 +296,18 @@ module Bade
|
|
287
296
|
end
|
288
297
|
|
289
298
|
result << '__blocks.dup'
|
290
|
-
else
|
291
|
-
result << '{}'
|
292
299
|
end
|
293
|
-
|
300
|
+
when :mixin_decl
|
294
301
|
result << '__blocks'
|
295
302
|
end
|
296
303
|
|
304
|
+
# positional params
|
305
|
+
result += params.select { |n| n.type == :mixin_param }
|
306
|
+
.map { |param| param.default_value ? "#{param.value} = #{param.default_value}" : param.value }
|
297
307
|
|
298
|
-
#
|
299
|
-
result += params.select { |n| n.type == :
|
300
|
-
|
308
|
+
# key-value params
|
309
|
+
result += params.select { |n| n.type == :mixin_key_param }
|
310
|
+
.map { |param| "#{param.name}: #{param.value}" }
|
301
311
|
|
302
312
|
result.join(', ')
|
303
313
|
end
|
@@ -309,14 +319,10 @@ module Bade
|
|
309
319
|
# @return [nil]
|
310
320
|
#
|
311
321
|
def block_definition(block_node)
|
312
|
-
buff_code "__blocks['#{block_node.name}'] = __create_block('#{block_node.name}') do"
|
322
|
+
buff_code "__blocks['#{block_node.name}'] = __create_block('#{block_node.name}', #{location_node(block_node)}) do"
|
313
323
|
|
314
324
|
code_indent do
|
315
|
-
buff_code '__buffs_push()'
|
316
|
-
|
317
325
|
visit_node_children(block_node)
|
318
|
-
|
319
|
-
buff_code '__buffs_pop()'
|
320
326
|
end
|
321
327
|
|
322
328
|
buff_code 'end'
|
@@ -350,7 +356,7 @@ module Bade
|
|
350
356
|
#
|
351
357
|
def visit_block_decl(current_node)
|
352
358
|
params = formatted_mixin_params(current_node)
|
353
|
-
buff_code "#{MIXINS_NAME}['#{current_node.name}'] = __create_mixin('#{current_node.name}', &lambda { |#{params}|"
|
359
|
+
buff_code "#{MIXINS_NAME}['#{current_node.name}'] = __create_mixin('#{current_node.name}', #{location_node(current_node)}, &lambda { |#{params}|"
|
354
360
|
|
355
361
|
code_indent do
|
356
362
|
blocks_name_declaration(current_node)
|
@@ -367,6 +373,54 @@ module Bade
|
|
367
373
|
def escape_double_quotes!(str)
|
368
374
|
str.gsub!(/"/, '\"')
|
369
375
|
end
|
376
|
+
|
377
|
+
# @param [Bade::AST::Node]
|
378
|
+
# @return [Void]
|
379
|
+
def update_location_node(node)
|
380
|
+
should_skip = case node.type
|
381
|
+
when :code
|
382
|
+
value = node.value.strip
|
383
|
+
|
384
|
+
%w[end else }].include?(value) || value.match(/^when /)
|
385
|
+
when :newline
|
386
|
+
true
|
387
|
+
else
|
388
|
+
false
|
389
|
+
end
|
390
|
+
return if should_skip
|
391
|
+
return if node.lineno.nil?
|
392
|
+
|
393
|
+
buff_code "__update_lineno(#{node.lineno})"
|
394
|
+
end
|
395
|
+
|
396
|
+
# @param [String] filename
|
397
|
+
# @param [Fixnum] lineno
|
398
|
+
# @param [String] label
|
399
|
+
# @return [String]
|
400
|
+
def location(filename:, lineno:, label:)
|
401
|
+
args = [
|
402
|
+
filename ? "path: '#{filename}'" : nil,
|
403
|
+
"lineno: #{lineno}",
|
404
|
+
"label: '#{label}'",
|
405
|
+
].compact
|
406
|
+
|
407
|
+
"Location.new(#{args.join(',')})"
|
408
|
+
end
|
409
|
+
|
410
|
+
# @param [Node] node
|
411
|
+
# @return [String]
|
412
|
+
def location_node(node)
|
413
|
+
label = case node.type
|
414
|
+
when :mixin_decl
|
415
|
+
"+#{node.name}"
|
416
|
+
when :mixin_block
|
417
|
+
"#{node.name} in +#{node.parent.name}"
|
418
|
+
else
|
419
|
+
node.name
|
420
|
+
end
|
421
|
+
|
422
|
+
location(filename: node.filename, lineno: node.lineno, label: label)
|
423
|
+
end
|
370
424
|
end
|
371
425
|
|
372
426
|
# backward compatibility
|
@@ -7,6 +7,7 @@ module Bade
|
|
7
7
|
class Parser
|
8
8
|
module LineIndicatorRegexps
|
9
9
|
IMPORT = /\Aimport /.freeze
|
10
|
+
YIELD = /\Ayield(!?)/.freeze
|
10
11
|
MIXIN_DECL = /\Amixin #{NAME_RE_STRING}/.freeze
|
11
12
|
MIXIN_CALL = /\A\+#{NAME_RE_STRING}/.freeze
|
12
13
|
BLOCK_DECLARATION = /\Ablock #{NAME_RE_STRING}/.freeze
|
@@ -135,6 +136,11 @@ module Bade
|
|
135
136
|
@line = $'
|
136
137
|
parse_import
|
137
138
|
|
139
|
+
when LineIndicatorRegexps::YIELD
|
140
|
+
@line = $'
|
141
|
+
node = append_node(:yield, add: true)
|
142
|
+
node.conditional = $1.nil?
|
143
|
+
|
138
144
|
when LineIndicatorRegexps::MIXIN_DECL
|
139
145
|
# Mixin declaration
|
140
146
|
@line = $'
|
@@ -195,12 +201,8 @@ module Bade
|
|
195
201
|
@line = $' if $1
|
196
202
|
parse_tag($&)
|
197
203
|
|
198
|
-
when LineIndicatorRegexps::TAG_CLASS_START_BLOCK
|
199
|
-
# Found class
|
200
|
-
parse_tag 'div'
|
201
|
-
|
202
|
-
when LineIndicatorRegexps::TAG_ID_START_BLOCK
|
203
|
-
# Found id name -> implicit div
|
204
|
+
when LineIndicatorRegexps::TAG_CLASS_START_BLOCK, LineIndicatorRegexps::TAG_ID_START_BLOCK
|
205
|
+
# Found a class or id selector.
|
204
206
|
parse_tag 'div'
|
205
207
|
|
206
208
|
else
|
@@ -18,6 +18,7 @@ module Bade
|
|
18
18
|
PARAMS_PARAM_NAME = /\A\s*#{NAME_RE_STRING}/.freeze
|
19
19
|
PARAMS_BLOCK_NAME = /\A\s*&#{NAME_RE_STRING}/.freeze
|
20
20
|
PARAMS_KEY_PARAM_NAME = CODE_ATTR_RE
|
21
|
+
PARAMS_PARAM_DEFAULT_START = /\A\s*=/.freeze
|
21
22
|
end
|
22
23
|
|
23
24
|
def parse_mixin_call(mixin_name)
|
@@ -114,7 +115,12 @@ module Bade
|
|
114
115
|
|
115
116
|
when MixinRegexps::PARAMS_PARAM_NAME
|
116
117
|
@line = $'
|
117
|
-
append_node(:mixin_param, value: $1)
|
118
|
+
attr_node = append_node(:mixin_param, value: $1)
|
119
|
+
|
120
|
+
if @line =~ MixinRegexps::PARAMS_PARAM_DEFAULT_START
|
121
|
+
@line = $'
|
122
|
+
attr_node.default_value = parse_ruby_code(ParseRubyCodeRegexps::END_PARAMS_ARG)
|
123
|
+
end
|
118
124
|
|
119
125
|
when MixinRegexps::PARAMS_BLOCK_NAME
|
120
126
|
@line = $'
|
data/lib/bade/parser.rb
CHANGED
@@ -17,6 +17,8 @@ module Bade
|
|
17
17
|
attr_reader :error, :file, :line, :lineno, :column
|
18
18
|
|
19
19
|
def initialize(error, file, line, lineno, column)
|
20
|
+
super(error)
|
21
|
+
|
20
22
|
@error = error
|
21
23
|
@file = file || '(__TEMPLATE__)'
|
22
24
|
@line = line.to_s
|
@@ -60,7 +62,7 @@ module Bade
|
|
60
62
|
@file_path = file_path
|
61
63
|
|
62
64
|
@tab_re = /\G((?: {#{tabsize}})*) {0,#{tabsize - 1}}\t/
|
63
|
-
@tab =
|
65
|
+
@tab = "\1#{' ' * tabsize}"
|
64
66
|
|
65
67
|
reset
|
66
68
|
end
|
@@ -106,7 +108,7 @@ module Bade
|
|
106
108
|
@stacks << @stacks.last.dup while indent >= @stacks.length
|
107
109
|
|
108
110
|
parent = @stacks[indent].last
|
109
|
-
node = AST::NodeRegistrator.create(type, @lineno)
|
111
|
+
node = AST::NodeRegistrator.create(type, parent, lineno: @lineno, filename: @file_path)
|
110
112
|
parent.children << node
|
111
113
|
|
112
114
|
node.value = value unless value.nil?
|
data/lib/bade/precompiled.rb
CHANGED
@@ -26,9 +26,9 @@ module Bade
|
|
26
26
|
end
|
27
27
|
|
28
28
|
hash = if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.0')
|
29
|
-
|
29
|
+
Psych.safe_load(file, filename: file.path, permitted_classes: [Symbol])
|
30
30
|
else
|
31
|
-
|
31
|
+
Psych.safe_load(file, [Symbol])
|
32
32
|
end
|
33
33
|
raise LoadError, 'YAML file is not in valid format' unless hash.is_a?(Hash)
|
34
34
|
|
data/lib/bade/renderer.rb
CHANGED
@@ -9,7 +9,7 @@ require_relative 'precompiled'
|
|
9
9
|
|
10
10
|
module Bade
|
11
11
|
class Renderer
|
12
|
-
class LoadError < ::RuntimeError
|
12
|
+
class LoadError < Bade::Runtime::RuntimeError
|
13
13
|
# @return [String]
|
14
14
|
#
|
15
15
|
attr_reader :loading_path
|
@@ -22,18 +22,29 @@ module Bade
|
|
22
22
|
# @param [String] reference_path reference file from which is load performed
|
23
23
|
# @param [String] msg standard message
|
24
24
|
#
|
25
|
-
def initialize(loading_path, reference_path, msg =
|
26
|
-
super(msg)
|
25
|
+
def initialize(loading_path, reference_path, msg, template_backtrace = [])
|
26
|
+
super(msg, template_backtrace)
|
27
27
|
@loading_path = loading_path
|
28
28
|
@reference_path = reference_path
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
class << self
|
33
|
+
def _globals_tracker
|
34
|
+
@globals_tracker ||= Bade::Runtime::GlobalsTracker.new
|
35
|
+
end
|
36
|
+
|
37
|
+
# When set to true it will remove all constants that template created. Off by default.
|
38
|
+
#
|
39
|
+
# @return [Boolean]
|
40
|
+
attr_accessor :clear_constants
|
34
41
|
end
|
35
42
|
|
36
|
-
|
43
|
+
# @param clear_constants [Boolean] When set to true it will remove all constants that template created. Off by default.
|
44
|
+
def initialize(clear_constants: Bade::Renderer.clear_constants)
|
45
|
+
@optimize = false
|
46
|
+
@clear_constants = clear_constants
|
47
|
+
end
|
37
48
|
|
38
49
|
# @return [String]
|
39
50
|
#
|
@@ -59,6 +70,10 @@ module Bade
|
|
59
70
|
#
|
60
71
|
attr_accessor :optimize
|
61
72
|
|
73
|
+
# @return [Boolean] When set to true it will remove all constants that template created. Off by default.
|
74
|
+
#
|
75
|
+
attr_accessor :clear_constants
|
76
|
+
|
62
77
|
|
63
78
|
# ----------------------------------------------------------------------------- #
|
64
79
|
# Internal attributes
|
@@ -77,8 +92,8 @@ module Bade
|
|
77
92
|
#
|
78
93
|
# @return [Renderer] preconfigured instance of this class
|
79
94
|
#
|
80
|
-
def self.from_source(source, file_path = nil)
|
81
|
-
inst = new
|
95
|
+
def self.from_source(source, file_path = nil, clear_constants: Bade::Renderer.clear_constants)
|
96
|
+
inst = new(clear_constants: clear_constants)
|
82
97
|
inst.source_text = source
|
83
98
|
inst.file_path = file_path
|
84
99
|
inst
|
@@ -88,14 +103,14 @@ module Bade
|
|
88
103
|
#
|
89
104
|
# @return [Renderer] preconfigured instance of this class
|
90
105
|
#
|
91
|
-
def self.from_file(file)
|
106
|
+
def self.from_file(file, clear_constants: Bade::Renderer.clear_constants)
|
92
107
|
path = if file.is_a?(File)
|
93
108
|
file.path
|
94
109
|
else
|
95
110
|
file
|
96
111
|
end
|
97
112
|
|
98
|
-
from_source(nil, path)
|
113
|
+
from_source(nil, path, clear_constants: clear_constants)
|
99
114
|
end
|
100
115
|
|
101
116
|
# Method to create Renderer from Precompiled object, for example when you want to reuse precompiled object from disk
|
@@ -104,8 +119,8 @@ module Bade
|
|
104
119
|
#
|
105
120
|
# @return [Renderer] preconfigured instance of this class
|
106
121
|
#
|
107
|
-
def self.from_precompiled(precompiled)
|
108
|
-
inst = new
|
122
|
+
def self.from_precompiled(precompiled, clear_constants: Bade::Renderer.clear_constants)
|
123
|
+
inst = new(clear_constants: clear_constants)
|
109
124
|
inst.precompiled = precompiled
|
110
125
|
inst
|
111
126
|
end
|
@@ -125,11 +140,20 @@ module Bade
|
|
125
140
|
self
|
126
141
|
end
|
127
142
|
|
143
|
+
# @return [self]
|
128
144
|
def with_binding(binding)
|
129
145
|
self.lambda_binding = binding
|
130
146
|
self
|
131
147
|
end
|
132
148
|
|
149
|
+
# @param [RenderBinding] binding
|
150
|
+
# @return [self]
|
151
|
+
def with_render_binding(binding)
|
152
|
+
self.lambda_binding = nil
|
153
|
+
self.render_binding = binding
|
154
|
+
self
|
155
|
+
end
|
156
|
+
|
133
157
|
def optimized
|
134
158
|
self.optimize = true
|
135
159
|
self
|
@@ -173,10 +197,12 @@ module Bade
|
|
173
197
|
# @return [Proc]
|
174
198
|
#
|
175
199
|
def lambda_instance
|
176
|
-
|
177
|
-
lambda_binding
|
178
|
-
|
179
|
-
|
200
|
+
_catching_globals do
|
201
|
+
if lambda_binding
|
202
|
+
lambda_binding.eval(lambda_string, file_path || Bade::Runtime::TEMPLATE_FILE_NAME)
|
203
|
+
else
|
204
|
+
render_binding.instance_eval(lambda_string, file_path || Bade::Runtime::TEMPLATE_FILE_NAME)
|
205
|
+
end
|
180
206
|
end
|
181
207
|
end
|
182
208
|
|
@@ -197,13 +223,22 @@ module Bade
|
|
197
223
|
Generator::NEW_LINE_NAME.to_sym => new_line,
|
198
224
|
Generator::BASE_INDENT_NAME.to_sym => indent,
|
199
225
|
}
|
200
|
-
run_vars.
|
226
|
+
run_vars.compact! # remove nil values
|
201
227
|
|
202
|
-
|
228
|
+
begin
|
229
|
+
return _catching_globals do
|
230
|
+
lambda_instance.call(**run_vars)
|
231
|
+
end
|
232
|
+
rescue Bade::Runtime::RuntimeError => e
|
233
|
+
raise e
|
234
|
+
rescue Exception => e
|
235
|
+
msg = "Exception raised during execution of template: #{e}"
|
236
|
+
raise Bade::Runtime::RuntimeError.wrap_existing_error(msg, e, render_binding.__location_stack)
|
237
|
+
ensure
|
238
|
+
self.class._globals_tracker.clear_constants if @clear_constants
|
239
|
+
end
|
203
240
|
end
|
204
241
|
|
205
|
-
|
206
|
-
|
207
242
|
private
|
208
243
|
|
209
244
|
# @param [String] content source code of the template
|
@@ -275,5 +310,16 @@ module Bade
|
|
275
310
|
end
|
276
311
|
end
|
277
312
|
end
|
313
|
+
|
314
|
+
def _catching_globals(&block)
|
315
|
+
if @clear_constants
|
316
|
+
self.class._globals_tracker.catch(&block)
|
317
|
+
else
|
318
|
+
block.call
|
319
|
+
end
|
320
|
+
end
|
278
321
|
end
|
279
322
|
end
|
323
|
+
|
324
|
+
# Set default value to clear_constants
|
325
|
+
Bade::Renderer.clear_constants = false
|
data/lib/bade/ruby2_keywords.rb
CHANGED
@@ -83,9 +83,10 @@ class String
|
|
83
83
|
count = 0
|
84
84
|
|
85
85
|
each_char do |char|
|
86
|
-
|
86
|
+
case char
|
87
|
+
when SPACE_CHAR
|
87
88
|
count += 1
|
88
|
-
|
89
|
+
when TAB_CHAR
|
89
90
|
count += tabsize
|
90
91
|
else
|
91
92
|
break
|
@@ -100,7 +101,7 @@ class String
|
|
100
101
|
#
|
101
102
|
def strip_heredoc
|
102
103
|
min_val = scan(/^[ \t]*(?=\S)/).min
|
103
|
-
indent =
|
104
|
+
indent = min_val&.size || 0
|
104
105
|
gsub(/^[ \t]{#{indent}}/, '')
|
105
106
|
end
|
106
107
|
end
|
data/lib/bade/runtime/block.rb
CHANGED
@@ -4,8 +4,6 @@ require_relative '../ruby2_keywords'
|
|
4
4
|
|
5
5
|
module Bade
|
6
6
|
module Runtime
|
7
|
-
class RuntimeError < ::StandardError; end
|
8
|
-
|
9
7
|
class Block
|
10
8
|
class MissingBlockDefinitionError < RuntimeError
|
11
9
|
# @return [String]
|
@@ -16,8 +14,8 @@ module Bade
|
|
16
14
|
#
|
17
15
|
attr_accessor :context
|
18
16
|
|
19
|
-
def initialize(name, context, msg
|
20
|
-
super()
|
17
|
+
def initialize(name, context, msg, template_backtrace)
|
18
|
+
super(msg, template_backtrace)
|
21
19
|
|
22
20
|
self.name = name
|
23
21
|
self.context = context
|
@@ -38,34 +36,51 @@ module Bade
|
|
38
36
|
#
|
39
37
|
attr_reader :name
|
40
38
|
|
39
|
+
# @return [RenderBinding::Location, nil]
|
40
|
+
#
|
41
|
+
attr_reader :location
|
42
|
+
|
41
43
|
# @return [RenderBinding]
|
42
44
|
#
|
43
45
|
attr_reader :render_binding
|
44
46
|
|
45
47
|
# @param [String] name name of the block
|
48
|
+
# @param [RenderBinding::Location, nil] location
|
46
49
|
# @param [RenderBinding] render_binding reference to current binding instance
|
47
50
|
# @param [Proc] block reference to lambda
|
48
51
|
#
|
49
|
-
def initialize(name, render_binding, &block)
|
52
|
+
def initialize(name, location, render_binding, &block)
|
50
53
|
@name = name
|
54
|
+
@location = location
|
51
55
|
@render_binding = render_binding
|
52
56
|
@block = block
|
53
57
|
end
|
54
58
|
|
55
59
|
# --- Calling methods
|
56
60
|
|
61
|
+
# Calls the block and adds rendered content into current buffer stack.
|
62
|
+
#
|
63
|
+
# @return [Void]
|
57
64
|
ruby2_keywords def call(*args)
|
58
65
|
call!(*args) unless @block.nil?
|
59
66
|
end
|
60
67
|
|
68
|
+
# Calls the block and adds rendered content into current buffer stack.
|
69
|
+
#
|
70
|
+
# @return [Void]
|
61
71
|
ruby2_keywords def call!(*args)
|
62
|
-
raise MissingBlockDefinitionError.new(name, :call) if @block.nil?
|
72
|
+
raise MissingBlockDefinitionError.new(name, :call, nil, render_binding.__location_stack) if @block.nil?
|
63
73
|
|
64
|
-
|
74
|
+
__call(*args)
|
65
75
|
end
|
66
76
|
|
67
77
|
# --- Rendering methods
|
68
78
|
|
79
|
+
# Calls the block and returns rendered content in string.
|
80
|
+
#
|
81
|
+
# Returns empty string when there is no block.
|
82
|
+
#
|
83
|
+
# @return [String]
|
69
84
|
def render(*args)
|
70
85
|
if @block.nil?
|
71
86
|
''
|
@@ -74,10 +89,33 @@ module Bade
|
|
74
89
|
end
|
75
90
|
end
|
76
91
|
|
92
|
+
# Calls the block and returns rendered content in string.
|
93
|
+
#
|
94
|
+
# Throws error when there is no block.
|
95
|
+
#
|
96
|
+
# @return [String]
|
77
97
|
def render!(*args)
|
78
|
-
raise MissingBlockDefinitionError.new(name, :render) if @block.nil?
|
98
|
+
raise MissingBlockDefinitionError.new(name, :render, nil, render_binding.__location_stack) if @block.nil?
|
99
|
+
|
100
|
+
loc = location.dup
|
101
|
+
render_binding.__buffs_push(loc)
|
102
|
+
|
103
|
+
@block.call(*args)
|
104
|
+
|
105
|
+
render_binding.__buffs_pop&.join || ''
|
106
|
+
end
|
107
|
+
|
108
|
+
# Calls the block and adds rendered content into current buffer stack.
|
109
|
+
#
|
110
|
+
# @return [Void]
|
111
|
+
ruby2_keywords def __call(*args)
|
112
|
+
loc = location.dup
|
113
|
+
render_binding.__buffs_push(loc)
|
114
|
+
|
115
|
+
@block.call(*args)
|
79
116
|
|
80
|
-
|
117
|
+
res = render_binding.__buffs_pop
|
118
|
+
render_binding.__buff&.concat(res) if !res.nil? && !res.empty?
|
81
119
|
end
|
82
120
|
end
|
83
121
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bade
|
4
|
+
module Runtime
|
5
|
+
# Tracks created global variables and constants in block.
|
6
|
+
class GlobalsTracker
|
7
|
+
# @return [Array<Symbol>]
|
8
|
+
attr_accessor :caught_variables
|
9
|
+
|
10
|
+
# @return [Array<[Object, :Symbol]>]
|
11
|
+
attr_accessor :caught_constants
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@caught_variables = []
|
15
|
+
@caught_constants = []
|
16
|
+
end
|
17
|
+
|
18
|
+
# @yieldreturn [T]
|
19
|
+
# @return [T]
|
20
|
+
def catch
|
21
|
+
before_variables = global_variables
|
22
|
+
before_global_constants = Object.constants
|
23
|
+
before_binding_constants = Bade::Runtime::RenderBinding.constants(false)
|
24
|
+
|
25
|
+
res = nil
|
26
|
+
begin
|
27
|
+
res = yield
|
28
|
+
ensure
|
29
|
+
@caught_variables += global_variables - before_variables
|
30
|
+
|
31
|
+
@caught_constants += (Object.constants - before_global_constants)
|
32
|
+
.map { |name| [Object, name] }
|
33
|
+
@caught_constants += (Bade::Runtime::RenderBinding.constants(false) - before_binding_constants)
|
34
|
+
.map { |name| [Bade::Runtime::RenderBinding, name] }
|
35
|
+
end
|
36
|
+
|
37
|
+
res
|
38
|
+
end
|
39
|
+
|
40
|
+
def clear_all
|
41
|
+
clear_global_variables
|
42
|
+
clear_constants
|
43
|
+
end
|
44
|
+
|
45
|
+
def clear_constants
|
46
|
+
@caught_constants.each do |(obj, name)|
|
47
|
+
obj.send(:remove_const, name) if obj.const_defined?(name)
|
48
|
+
end
|
49
|
+
@caught_constants = []
|
50
|
+
end
|
51
|
+
|
52
|
+
def clear_global_variables
|
53
|
+
@caught_variables.each do |name|
|
54
|
+
eval("#{name} = nil", binding, __FILE__, __LINE__)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/bade/runtime/mixin.rb
CHANGED
@@ -9,8 +9,8 @@ module Bade
|
|
9
9
|
class Mixin < Block
|
10
10
|
ruby2_keywords def call!(blocks, *args)
|
11
11
|
begin
|
12
|
-
|
13
|
-
rescue ArgumentError => e
|
12
|
+
__call(blocks, *args)
|
13
|
+
rescue ::ArgumentError => e
|
14
14
|
case e.message
|
15
15
|
when /\Awrong number of arguments \(given ([0-9]+), expected ([0-9]+)\)\Z/,
|
16
16
|
/\Awrong number of arguments \(([0-9]+) for ([0-9]+)\)\Z/
|
@@ -19,12 +19,19 @@ module Bade
|
|
19
19
|
# minus one, because first argument is always hash of blocks
|
20
20
|
given = $1.to_i - 1
|
21
21
|
expected = $2.to_i - 1
|
22
|
-
|
22
|
+
msg = "wrong number of arguments (given #{given}, expected #{expected}) for mixin `#{name}`"
|
23
|
+
raise Bade::Runtime::ArgumentError.new(msg, render_binding.__location_stack)
|
23
24
|
|
24
25
|
when /\Aunknown keyword: (.*)\Z/
|
25
26
|
# handle unknown key-value parameter
|
26
27
|
key_name = $1
|
27
|
-
|
28
|
+
msg = "unknown key-value argument `#{key_name}` for mixin `#{name}`"
|
29
|
+
raise Bade::Runtime::ArgumentError.new(msg, render_binding.__location_stack)
|
30
|
+
|
31
|
+
when /\Amissing keyword: :?(.*)\Z/
|
32
|
+
key_name = $1
|
33
|
+
msg = "missing value for required key-value argument `#{key_name}` for mixin `#{name}`"
|
34
|
+
raise Bade::Runtime::ArgumentError.new(msg, render_binding.__location_stack)
|
28
35
|
|
29
36
|
else
|
30
37
|
raise
|
@@ -36,10 +43,15 @@ module Bade
|
|
36
43
|
when :render
|
37
44
|
"Mixin `#{name}` requires block to get rendered content of block `#{e.name}`"
|
38
45
|
else
|
39
|
-
raise ::ArgumentError
|
46
|
+
raise Bade::Runtime::ArgumentError.new("Unknown context #{e.context} of error #{e}!",
|
47
|
+
render_binding.__location_stack)
|
40
48
|
end
|
41
49
|
|
42
|
-
raise Block::MissingBlockDefinitionError.new(e.name, e.context, msg)
|
50
|
+
raise Block::MissingBlockDefinitionError.new(e.name, e.context, msg, render_binding.__location_stack)
|
51
|
+
|
52
|
+
rescue Exception => e
|
53
|
+
msg = "Exception raised during execution of mixin `#{name}`: #{e}"
|
54
|
+
raise Bade::Runtime::RuntimeError.wrap_existing_error(msg, e, render_binding.__location_stack)
|
43
55
|
end
|
44
56
|
end
|
45
57
|
end
|
@@ -5,12 +5,15 @@ require_relative 'block'
|
|
5
5
|
module Bade
|
6
6
|
module Runtime
|
7
7
|
class RenderBinding
|
8
|
-
|
8
|
+
Location = Bade::Runtime::Location
|
9
9
|
|
10
10
|
# @return [Array<Array<String>>]
|
11
11
|
#
|
12
12
|
attr_accessor :__buffs_stack
|
13
13
|
|
14
|
+
# @return [Array<Location>]
|
15
|
+
attr_accessor :__location_stack
|
16
|
+
|
14
17
|
# @return [Hash<String, Mixin>]
|
15
18
|
#
|
16
19
|
attr_accessor :__mixins
|
@@ -34,13 +37,14 @@ module Bade
|
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
37
|
-
# Resets this binding to default state, this method should be
|
40
|
+
# Resets this binding to default state, this method should be evoked after running the template lambda
|
38
41
|
#
|
39
42
|
# @return [nil]
|
40
43
|
#
|
41
44
|
def __reset
|
42
|
-
@__buffs_stack = [
|
43
|
-
@
|
45
|
+
@__buffs_stack = []
|
46
|
+
@__location_stack = []
|
47
|
+
@__mixins = Hash.new { |_hash, key| raise Bade::Runtime::KeyError.new("Undefined mixin '#{key}'", __location_stack) }
|
44
48
|
end
|
45
49
|
|
46
50
|
# @return [Binding]
|
@@ -51,26 +55,56 @@ module Bade
|
|
51
55
|
|
52
56
|
# Shortcut for creating blocks
|
53
57
|
#
|
54
|
-
def __create_block(name, &block)
|
55
|
-
Bade::Runtime::Block.new(name, self, &block)
|
58
|
+
def __create_block(name, location = nil, &block)
|
59
|
+
Bade::Runtime::Block.new(name, location, self, &block)
|
56
60
|
end
|
57
61
|
|
58
|
-
def __create_mixin(name, &block)
|
59
|
-
Bade::Runtime::Mixin.new(name, self, &block)
|
62
|
+
def __create_mixin(name, location, &block)
|
63
|
+
Bade::Runtime::Mixin.new(name, location, self, &block)
|
60
64
|
end
|
61
65
|
|
62
|
-
# --- Methods for dealing with pushing and
|
66
|
+
# --- Methods for dealing with pushing and popping buffers in stack
|
63
67
|
|
64
68
|
def __buff
|
65
|
-
__buffs_stack.
|
69
|
+
__buffs_stack.first
|
66
70
|
end
|
67
71
|
|
68
|
-
|
69
|
-
|
72
|
+
# @param [RenderBinding::Location, nil] location
|
73
|
+
def __buffs_push(location)
|
74
|
+
__buffs_stack.unshift([])
|
75
|
+
__location_stack.unshift(location) unless location.nil?
|
70
76
|
end
|
71
77
|
|
78
|
+
# @return [Array<String>, nil]
|
72
79
|
def __buffs_pop
|
73
|
-
|
80
|
+
__location_stack.shift
|
81
|
+
__buffs_stack.shift
|
82
|
+
end
|
83
|
+
|
84
|
+
# --- Other internal methods
|
85
|
+
|
86
|
+
# @param [String] filename
|
87
|
+
def __load(filename)
|
88
|
+
# FakeFS does not fake `load` method
|
89
|
+
if defined?(:FakeFS) && FakeFS.activated?
|
90
|
+
# rubocop:disable Security/Eval
|
91
|
+
eval(File.read(filename), __get_binding, filename)
|
92
|
+
# rubocop:enable Security/Eval
|
93
|
+
else
|
94
|
+
load(filename)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# @param [String] filename
|
99
|
+
def require_relative(filename)
|
100
|
+
# FakeFS does not fake `require_relative` method
|
101
|
+
if defined?(:FakeFS) && FakeFS.activated?
|
102
|
+
# rubocop:disable Security/Eval
|
103
|
+
eval(File.read(filename), __get_binding, filename)
|
104
|
+
# rubocop:enable Security/Eval
|
105
|
+
else
|
106
|
+
Kernel.require_relative(filename)
|
107
|
+
end
|
74
108
|
end
|
75
109
|
|
76
110
|
# Escape input text with html escapes
|
@@ -94,6 +128,15 @@ module Bade
|
|
94
128
|
|
95
129
|
%( #{name}="#{values.join(' ')}")
|
96
130
|
end
|
131
|
+
|
132
|
+
def __update_lineno(number)
|
133
|
+
__location_stack.first&.lineno = number
|
134
|
+
end
|
135
|
+
|
136
|
+
# @return [Location, nil]
|
137
|
+
def __current_location
|
138
|
+
__location_stack.first
|
139
|
+
end
|
97
140
|
end
|
98
141
|
end
|
99
142
|
end
|
data/lib/bade/runtime.rb
CHANGED
@@ -1,9 +1,87 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
3
|
+
module Bade
|
4
4
|
module Runtime
|
5
|
+
Location = Struct.new(:path, :lineno, :label, keyword_init: true) do
|
6
|
+
def to_s
|
7
|
+
"#{path || TEMPLATE_FILE_NAME}:#{lineno}:in `#{label}'"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class RuntimeError < ::StandardError
|
12
|
+
# @return [Array<Location>]
|
13
|
+
#
|
14
|
+
attr_reader :template_backtrace
|
15
|
+
|
16
|
+
# @param [String] msg
|
17
|
+
# @param [Array<Location>] template_backtrace
|
18
|
+
# @param [Exception, nil] original
|
19
|
+
def initialize(msg, template_backtrace = [], original: nil)
|
20
|
+
super(msg)
|
21
|
+
@template_backtrace = template_backtrace
|
22
|
+
@original = original
|
23
|
+
end
|
24
|
+
|
25
|
+
def message
|
26
|
+
if @template_backtrace.empty?
|
27
|
+
super
|
28
|
+
else
|
29
|
+
<<~MSG.rstrip
|
30
|
+
#{super}
|
31
|
+
template backtrace:
|
32
|
+
#{__formatted_backtrace.join("\n")}
|
33
|
+
MSG
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def cause
|
38
|
+
@original
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Array<String>]
|
42
|
+
def __formatted_backtrace
|
43
|
+
bt = @template_backtrace
|
44
|
+
|
45
|
+
# delete first location if is same as second (can happen when arguments are incorrect)
|
46
|
+
last = bt.first
|
47
|
+
bt.delete_at(0) if last && bt.length > 1 && last == bt[1]
|
48
|
+
|
49
|
+
bt.map { |loc| " #{loc}" }
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param [Array<Thread::Backtrace::Location>, nil] locations
|
53
|
+
def self.process_locations(locations)
|
54
|
+
return [] if locations.nil?
|
55
|
+
|
56
|
+
index = locations&.find_index { |loc| loc.path == TEMPLATE_FILE_NAME || loc.path&.include?('.bade') }
|
57
|
+
return [] if index.nil?
|
58
|
+
|
59
|
+
new_locations = locations[0...index] || []
|
60
|
+
|
61
|
+
new_locations.map do |loc|
|
62
|
+
Location.new(path: loc.path, lineno: loc.lineno, label: loc.label)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param [String] msg
|
67
|
+
# @param [Exception] error
|
68
|
+
# @param [Array<Location>] template_backtrace
|
69
|
+
# @return [RuntimeError]
|
70
|
+
def self.wrap_existing_error(msg, error, template_backtrace)
|
71
|
+
locs = Bade::Runtime::RuntimeError.process_locations(error.backtrace_locations) + template_backtrace
|
72
|
+
Bade::Runtime::RuntimeError.new(msg, locs, original: error)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class KeyError < RuntimeError; end
|
77
|
+
|
78
|
+
class ArgumentError < RuntimeError; end
|
79
|
+
|
80
|
+
TEMPLATE_FILE_NAME = '(__template__)'.freeze
|
81
|
+
|
5
82
|
require_relative 'runtime/block'
|
6
83
|
require_relative 'runtime/mixin'
|
7
84
|
require_relative 'runtime/render_binding'
|
85
|
+
require_relative 'runtime/globals_tracker'
|
8
86
|
end
|
9
87
|
end
|
data/lib/bade/version.rb
CHANGED
data/lib/bade.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bade
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Kříž
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: psych
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '2.2'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '5.0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,21 +29,21 @@ dependencies:
|
|
29
29
|
version: '2.2'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '5.0'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: fakefs
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - "
|
37
|
+
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
39
|
+
version: '1.3'
|
40
40
|
type: :development
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
|
-
- - "
|
44
|
+
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '
|
46
|
+
version: '1.3'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -64,15 +64,15 @@ dependencies:
|
|
64
64
|
requirements:
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version:
|
67
|
+
version: '1.14'
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version:
|
75
|
-
description:
|
74
|
+
version: '1.14'
|
75
|
+
description:
|
76
76
|
email:
|
77
77
|
- samnung@gmail.com
|
78
78
|
executables: []
|
@@ -109,14 +109,16 @@ files:
|
|
109
109
|
- lib/bade/ruby_extensions/string.rb
|
110
110
|
- lib/bade/runtime.rb
|
111
111
|
- lib/bade/runtime/block.rb
|
112
|
+
- lib/bade/runtime/globals_tracker.rb
|
112
113
|
- lib/bade/runtime/mixin.rb
|
113
114
|
- lib/bade/runtime/render_binding.rb
|
114
115
|
- lib/bade/version.rb
|
115
116
|
homepage: https://github.com/epuber-io/bade
|
116
117
|
licenses:
|
117
118
|
- MIT
|
118
|
-
metadata:
|
119
|
-
|
119
|
+
metadata:
|
120
|
+
rubygems_mfa_required: 'true'
|
121
|
+
post_install_message:
|
120
122
|
rdoc_options: []
|
121
123
|
require_paths:
|
122
124
|
- lib
|
@@ -124,15 +126,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
124
126
|
requirements:
|
125
127
|
- - ">="
|
126
128
|
- !ruby/object:Gem::Version
|
127
|
-
version: '2.
|
129
|
+
version: '2.5'
|
128
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
131
|
requirements:
|
130
132
|
- - ">="
|
131
133
|
- !ruby/object:Gem::Version
|
132
134
|
version: '0'
|
133
135
|
requirements: []
|
134
|
-
rubygems_version: 3.
|
135
|
-
signing_key:
|
136
|
+
rubygems_version: 3.3.8
|
137
|
+
signing_key:
|
136
138
|
specification_version: 4
|
137
139
|
summary: Minimalistic template engine for Ruby.
|
138
140
|
test_files: []
|