bade 0.2.5 → 0.3.2
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/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 +74 -19
- data/lib/bade/parser/parser_lines.rb +8 -6
- data/lib/bade/parser/parser_mixin.rb +7 -1
- data/lib/bade/parser.rb +5 -3
- 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 +3 -11
- data/lib/bade/runtime/block.rb +47 -9
- data/lib/bade/runtime/globals_tracker.rb +88 -0
- data/lib/bade/runtime/mixin.rb +21 -6
- data/lib/bade/runtime/render_binding.rb +56 -13
- data/lib/bade/runtime/utils/where.rb +101 -0
- data/lib/bade/runtime.rb +108 -1
- data/lib/bade/version.rb +1 -1
- data/lib/bade.rb +1 -0
- metadata +17 -14
@@ -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
|
@@ -94,13 +95,4 @@ class String
|
|
94
95
|
|
95
96
|
count
|
96
97
|
end
|
97
|
-
|
98
|
-
# source: http://apidock.com/rails/String/strip_heredoc
|
99
|
-
# @return [String]
|
100
|
-
#
|
101
|
-
def strip_heredoc
|
102
|
-
min_val = scan(/^[ \t]*(?=\S)/).min
|
103
|
-
indent = (min_val && min_val.size) || 0
|
104
|
-
gsub(/^[ \t]{#{indent}}/, '')
|
105
|
-
end
|
106
98
|
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,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'utils/where'
|
4
|
+
|
5
|
+
module Bade
|
6
|
+
module Runtime
|
7
|
+
# Tracks created global variables and constants in block.
|
8
|
+
class GlobalsTracker
|
9
|
+
# @return [Array<Symbol>]
|
10
|
+
attr_accessor :caught_variables
|
11
|
+
|
12
|
+
# @return [Array<[Object, :Symbol]>]
|
13
|
+
attr_accessor :caught_constants
|
14
|
+
|
15
|
+
# @return [Array<String>, nil]
|
16
|
+
attr_accessor :constants_location_prefixes
|
17
|
+
|
18
|
+
# @param [Array<String>, nil] constants_location_prefixes If given, only constants whose location starts with one
|
19
|
+
# of the prefixes will be removed. If nil, all constants
|
20
|
+
# will be removed.
|
21
|
+
def initialize(constants_location_prefixes: nil)
|
22
|
+
@caught_variables = []
|
23
|
+
@caught_constants = []
|
24
|
+
@constants_location_prefixes = constants_location_prefixes
|
25
|
+
end
|
26
|
+
|
27
|
+
# @yieldreturn [T]
|
28
|
+
# @return [T]
|
29
|
+
def catch
|
30
|
+
before_variables = global_variables
|
31
|
+
before_global_constants = Object.constants
|
32
|
+
before_binding_constants = Bade::Runtime::RenderBinding.constants(false)
|
33
|
+
|
34
|
+
res = nil
|
35
|
+
begin
|
36
|
+
res = yield
|
37
|
+
ensure
|
38
|
+
@caught_variables += global_variables - before_variables
|
39
|
+
|
40
|
+
@caught_constants += (Object.constants - before_global_constants)
|
41
|
+
.map { |name| [Object, name] }
|
42
|
+
@caught_constants += (Bade::Runtime::RenderBinding.constants(false) - before_binding_constants)
|
43
|
+
.map { |name| [Bade::Runtime::RenderBinding, name] }
|
44
|
+
end
|
45
|
+
|
46
|
+
res
|
47
|
+
end
|
48
|
+
|
49
|
+
def clear_all
|
50
|
+
clear_global_variables
|
51
|
+
clear_constants
|
52
|
+
end
|
53
|
+
|
54
|
+
def clear_constants
|
55
|
+
_filtered_constants.each do |(obj, name)|
|
56
|
+
obj.send(:remove_const, name)
|
57
|
+
end
|
58
|
+
@caught_constants = []
|
59
|
+
end
|
60
|
+
|
61
|
+
def clear_global_variables
|
62
|
+
@caught_variables.each do |name|
|
63
|
+
eval("#{name} = nil", binding, __FILE__, __LINE__)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Filteres caught constants by location prefixes and returns ones that should be removed.
|
68
|
+
#
|
69
|
+
# @return [Array<[Object, :Symbol]>]
|
70
|
+
def _filtered_constants
|
71
|
+
@caught_constants.select do |(obj, name)|
|
72
|
+
next unless obj.const_defined?(name)
|
73
|
+
next true if constants_location_prefixes.nil?
|
74
|
+
|
75
|
+
konst = obj.const_get(name)
|
76
|
+
begin
|
77
|
+
location = Bade.where_is(konst)
|
78
|
+
rescue ::ArgumentError
|
79
|
+
next
|
80
|
+
end
|
81
|
+
|
82
|
+
path = location.first
|
83
|
+
constants_location_prefixes&.any? { |prefix| path.start_with?(prefix) }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
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,18 @@ 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 RuntimeError
|
53
|
+
raise
|
54
|
+
|
55
|
+
rescue Exception => e
|
56
|
+
msg = "Exception raised during execution of mixin `#{name}`: #{e}"
|
57
|
+
raise Bade::Runtime::RuntimeError.wrap_existing_error(msg, e, render_binding.__location_stack)
|
43
58
|
end
|
44
59
|
end
|
45
60
|
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 Object.const_defined?(:FakeFS) && Object.const_get(: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 Object.const_defined?(:FakeFS) && Object.const_get(: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
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Inspired by https://gist.github.com/wtaysom/1236979
|
4
|
+
|
5
|
+
module Bade
|
6
|
+
module Where
|
7
|
+
class << self
|
8
|
+
def is_proc(proc)
|
9
|
+
source_location(proc)
|
10
|
+
end
|
11
|
+
|
12
|
+
def is_method(klass, method_name)
|
13
|
+
source_location(klass.method(method_name))
|
14
|
+
end
|
15
|
+
|
16
|
+
def is_instance_method(klass, method_name)
|
17
|
+
source_location(klass.instance_method(method_name))
|
18
|
+
end
|
19
|
+
|
20
|
+
def are_methods(klass, method_name)
|
21
|
+
are_via_extractor(:method, klass, method_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def are_instance_methods(klass, method_name)
|
25
|
+
are_via_extractor(:method, klass, method_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def is_class(klass)
|
29
|
+
defined_methods(klass)
|
30
|
+
.group_by { |sl| sl[0] }
|
31
|
+
.map do |file, sls|
|
32
|
+
lines = sls.map { |sl| sl[1] }
|
33
|
+
count = lines.size
|
34
|
+
line = lines.min
|
35
|
+
|
36
|
+
{
|
37
|
+
file: file,
|
38
|
+
count: count,
|
39
|
+
line: line
|
40
|
+
}
|
41
|
+
end
|
42
|
+
.sort_by { |fc| fc[:count] }
|
43
|
+
.map { |fc| [fc[:file], fc[:line]] }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Raises ArgumentError if klass does not have any Ruby methods defined in it.
|
47
|
+
def is_class_primarily(klass)
|
48
|
+
source_locations = is_class(klass)
|
49
|
+
if source_locations.empty?
|
50
|
+
methods = defined_methods(klass)
|
51
|
+
msg = if methods.empty?
|
52
|
+
"#{klass} has no methods"
|
53
|
+
else
|
54
|
+
"#{klass} only has built-in methods (#{methods.size} in total)"
|
55
|
+
end
|
56
|
+
|
57
|
+
raise ::ArgumentError, msg
|
58
|
+
end
|
59
|
+
source_locations[0]
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def source_location(method)
|
65
|
+
method.source_location || (
|
66
|
+
method.to_s =~ /: (.*)>/
|
67
|
+
Regexp.last_match(1)
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def are_via_extractor(extractor, klass, method_name)
|
72
|
+
klass.ancestors
|
73
|
+
.map do |ancestor|
|
74
|
+
method = ancestor.send(extractor, method_name)
|
75
|
+
source_location(method) if method.owner == ancestor
|
76
|
+
end
|
77
|
+
.compact
|
78
|
+
end
|
79
|
+
|
80
|
+
def defined_methods(klass)
|
81
|
+
methods = klass.methods(false).map { |m| klass.method(m) } +
|
82
|
+
klass.instance_methods(false).map { |m| klass.instance_method(m) }
|
83
|
+
methods
|
84
|
+
.map(&:source_location)
|
85
|
+
.compact
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.where_is(klass, method = nil)
|
91
|
+
if method
|
92
|
+
begin
|
93
|
+
Where.is_instance_method(klass, method)
|
94
|
+
rescue NameError
|
95
|
+
Where.is_method(klass, method)
|
96
|
+
end
|
97
|
+
else
|
98
|
+
Where.is_class_primarily(klass)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/bade/runtime.rb
CHANGED
@@ -1,9 +1,116 @@
|
|
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 template?
|
7
|
+
path == TEMPLATE_FILE_NAME || path&.include?('.bade')
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"#{path || TEMPLATE_FILE_NAME}:#{lineno}:in `#{label}'"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class RuntimeError < ::StandardError
|
16
|
+
# @return [Array<Location>]
|
17
|
+
#
|
18
|
+
attr_reader :template_backtrace
|
19
|
+
|
20
|
+
# @return [Boolean]
|
21
|
+
#
|
22
|
+
attr_reader :print_locations_warning
|
23
|
+
|
24
|
+
# @param [String] msg
|
25
|
+
# @param [Array<Location>] template_backtrace
|
26
|
+
# @param [Exception, nil] original
|
27
|
+
def initialize(msg, template_backtrace = [], original: nil, print_locations_warning: false)
|
28
|
+
super(msg)
|
29
|
+
@template_backtrace = template_backtrace
|
30
|
+
@original = original
|
31
|
+
@print_locations_warning = print_locations_warning
|
32
|
+
end
|
33
|
+
|
34
|
+
def message
|
35
|
+
if @template_backtrace.empty?
|
36
|
+
super
|
37
|
+
else
|
38
|
+
warning = if print_locations_warning
|
39
|
+
<<~TEXT
|
40
|
+
|
41
|
+
!!! WARNING !!!, filenames and line numbers of functions can be misleading due to using Ruby
|
42
|
+
functions in different Bade file. Trust only functions names. Mixins are fine.
|
43
|
+
|
44
|
+
This will be fixed in https://github.com/epuber-io/bade/issues/32
|
45
|
+
TEXT
|
46
|
+
else
|
47
|
+
''
|
48
|
+
end
|
49
|
+
|
50
|
+
<<~MSG.rstrip
|
51
|
+
#{super}
|
52
|
+
template backtrace:
|
53
|
+
#{__formatted_backtrace.join("\n")}
|
54
|
+
#{warning}
|
55
|
+
MSG
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def cause
|
60
|
+
@original
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Array<String>]
|
64
|
+
def __formatted_backtrace
|
65
|
+
bt = @template_backtrace
|
66
|
+
|
67
|
+
# delete first location if is same as second (can happen when arguments are incorrect)
|
68
|
+
last = bt.first
|
69
|
+
bt.delete_at(0) if last && bt.length > 1 && last == bt[1]
|
70
|
+
|
71
|
+
bt.map { |loc| " #{loc}" }
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param [Array<Thread::Backtrace::Location>, nil] locations
|
75
|
+
def self.process_locations(locations)
|
76
|
+
return [] if locations.nil?
|
77
|
+
|
78
|
+
# map to Bade's Location
|
79
|
+
new_locations = locations.map { |loc| Location.new(path: loc.path, lineno: loc.lineno, label: loc.label) }
|
80
|
+
|
81
|
+
# find location to use or drop
|
82
|
+
index = new_locations.rindex(&:template?)
|
83
|
+
return [] if index.nil?
|
84
|
+
|
85
|
+
# get only locations inside template
|
86
|
+
new_locations = new_locations[0...index] || []
|
87
|
+
|
88
|
+
# filter out not interested locations
|
89
|
+
new_locations
|
90
|
+
.reject { |loc| loc.path.start_with?(__dir__) }
|
91
|
+
.reject { |loc| loc.template? && loc.label.include?('lambda_instance') }
|
92
|
+
end
|
93
|
+
|
94
|
+
# @param [String] msg
|
95
|
+
# @param [Exception] error
|
96
|
+
# @param [Array<Location>] template_backtrace
|
97
|
+
# @return [RuntimeError]
|
98
|
+
def self.wrap_existing_error(msg, error, template_backtrace)
|
99
|
+
ruby_locs = Bade::Runtime::RuntimeError.process_locations(error.backtrace_locations)
|
100
|
+
locs = ruby_locs + template_backtrace
|
101
|
+
Bade::Runtime::RuntimeError.new(msg, locs, original: error, print_locations_warning: !ruby_locs.empty?)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class KeyError < RuntimeError; end
|
106
|
+
|
107
|
+
class ArgumentError < RuntimeError; end
|
108
|
+
|
109
|
+
TEMPLATE_FILE_NAME = '(__template__)'.freeze
|
110
|
+
|
5
111
|
require_relative 'runtime/block'
|
6
112
|
require_relative 'runtime/mixin'
|
7
113
|
require_relative 'runtime/render_binding'
|
114
|
+
require_relative 'runtime/globals_tracker'
|
8
115
|
end
|
9
116
|
end
|
data/lib/bade/version.rb
CHANGED