bade 0.2.3 → 0.3.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 +5 -5
- data/Bade.gemspec +9 -7
- data/Gemfile +12 -3
- data/README.md +3 -3
- data/lib/bade/ast/document.rb +1 -1
- data/lib/bade/ast/node/mixin_node.rb +3 -3
- data/lib/bade/ast/node/static_text_node.rb +4 -2
- data/lib/bade/ast/node/value_node.rb +14 -3
- data/lib/bade/ast/node.rb +12 -3
- data/lib/bade/ast/node_registrator.rb +3 -2
- data/lib/bade/ast/string_serializer.rb +3 -5
- data/lib/bade/generator.rb +75 -25
- data/lib/bade/parser/parser_constants.rb +4 -4
- data/lib/bade/parser/parser_lines.rb +21 -19
- data/lib/bade/parser/parser_mixin.rb +17 -11
- data/lib/bade/parser/parser_ruby_code.rb +17 -7
- data/lib/bade/parser/parser_tag.rb +4 -4
- data/lib/bade/parser/parser_text.rb +3 -3
- data/lib/bade/parser.rb +7 -3
- data/lib/bade/precompiled.rb +6 -3
- data/lib/bade/renderer.rb +69 -20
- data/lib/bade/ruby2_keywords.rb +3 -0
- data/lib/bade/ruby_extensions/string.rb +4 -8
- data/lib/bade/runtime/block.rb +51 -11
- data/lib/bade/runtime/globals_tracker.rb +59 -0
- data/lib/bade/runtime/mixin.rb +47 -32
- 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 +32 -24
@@ -6,8 +6,8 @@ module Bade
|
|
6
6
|
|
7
7
|
class Parser
|
8
8
|
module ParseRubyCodeRegexps
|
9
|
-
END_NEW_LINE = /\A\s*\n
|
10
|
-
END_PARAMS_ARG = /\A\s*[,)]
|
9
|
+
END_NEW_LINE = /\A\s*\n/.freeze
|
10
|
+
END_PARAMS_ARG = /\A\s*[,)]/.freeze
|
11
11
|
end
|
12
12
|
|
13
13
|
# Parse ruby code, ended with outer delimiters
|
@@ -16,17 +16,27 @@ module Bade
|
|
16
16
|
#
|
17
17
|
# @return [Void] parsed ruby code
|
18
18
|
#
|
19
|
-
def parse_ruby_code(outer_delimiters)
|
19
|
+
def parse_ruby_code(outer_delimiters, allow_multiline: false)
|
20
20
|
code = String.new
|
21
21
|
end_re = if outer_delimiters.is_a?(Regexp)
|
22
22
|
outer_delimiters
|
23
23
|
else
|
24
24
|
/\A\s*[#{Regexp.escape outer_delimiters.to_s}]/
|
25
25
|
end
|
26
|
+
|
26
27
|
delimiters = []
|
27
28
|
string_start_quote_char = nil
|
28
29
|
|
29
|
-
|
30
|
+
loop do
|
31
|
+
break if !allow_multiline && @line.empty?
|
32
|
+
break if allow_multiline && @line.empty? && (@lines && @lines.empty?)
|
33
|
+
break if delimiters.empty? && @line =~ end_re
|
34
|
+
|
35
|
+
if @line.empty? && allow_multiline && !(@lines && @lines.empty?)
|
36
|
+
next_line
|
37
|
+
code << "\n"
|
38
|
+
end
|
39
|
+
|
30
40
|
char = @line[0]
|
31
41
|
|
32
42
|
# backslash escaped delimiter
|
@@ -70,7 +80,7 @@ module Bade
|
|
70
80
|
'{' => '}',
|
71
81
|
}.freeze
|
72
82
|
|
73
|
-
RUBY_QUOTES = %w
|
83
|
+
RUBY_QUOTES = %w[' "].freeze
|
74
84
|
|
75
85
|
RUBY_NOT_NESTABLE_DELIMITERS = RUBY_QUOTES
|
76
86
|
|
@@ -78,7 +88,7 @@ module Bade
|
|
78
88
|
RUBY_END_DELIMITERS = (%w(\) ] }) + RUBY_NOT_NESTABLE_DELIMITERS).freeze
|
79
89
|
RUBY_ALL_DELIMITERS = (RUBY_START_DELIMITERS + RUBY_END_DELIMITERS).uniq.freeze
|
80
90
|
|
81
|
-
RUBY_START_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_START_DELIMITERS.join}]
|
82
|
-
RUBY_END_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_END_DELIMITERS.join}]
|
91
|
+
RUBY_START_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_START_DELIMITERS.join}]/.freeze
|
92
|
+
RUBY_END_DELIMITERS_RE = /\A[#{Regexp.escape RUBY_END_DELIMITERS.join}]/.freeze
|
83
93
|
end
|
84
94
|
end
|
@@ -6,12 +6,12 @@ module Bade
|
|
6
6
|
|
7
7
|
class Parser
|
8
8
|
module TagRegexps
|
9
|
-
BLOCK_EXPANSION = /\A:\s
|
9
|
+
BLOCK_EXPANSION = /\A:\s+/.freeze
|
10
10
|
OUTPUT_CODE = LineIndicatorRegexps::OUTPUT_BLOCK
|
11
|
-
TEXT_START = /\A
|
11
|
+
TEXT_START = /\A /.freeze
|
12
12
|
|
13
|
-
PARAMS_ARGS_DELIMITER = /\A\s
|
14
|
-
PARAMS_END = /\A\s*\)
|
13
|
+
PARAMS_ARGS_DELIMITER = /\A\s*,/.freeze
|
14
|
+
PARAMS_END = /\A\s*\)/.freeze
|
15
15
|
end
|
16
16
|
|
17
17
|
# @param [String] tag tag name
|
@@ -6,8 +6,8 @@ module Bade
|
|
6
6
|
|
7
7
|
class Parser
|
8
8
|
module TextRegexps
|
9
|
-
INTERPOLATION_START = /(\\)?(&|#)\{
|
10
|
-
INTERPOLATION_END = /\A\}
|
9
|
+
INTERPOLATION_START = /(\\)?(&|#)\{/.freeze
|
10
|
+
INTERPOLATION_END = /\A\}/.freeze
|
11
11
|
end
|
12
12
|
|
13
13
|
def parse_text
|
@@ -64,7 +64,7 @@ module Bade
|
|
64
64
|
|
65
65
|
next_line
|
66
66
|
|
67
|
-
@line.remove_indent!(text_indent
|
67
|
+
@line.remove_indent!(text_indent || indent, @tabsize)
|
68
68
|
|
69
69
|
parse_text
|
70
70
|
|
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?
|
@@ -126,7 +128,9 @@ module Bade
|
|
126
128
|
|
127
129
|
def parse_import
|
128
130
|
# TODO: change this to something better
|
129
|
-
|
131
|
+
# rubocop:disable Security/Eval
|
132
|
+
path = eval(@line)
|
133
|
+
# rubocop:enable Security/Eval
|
130
134
|
append_node(:import, value: path)
|
131
135
|
|
132
136
|
@dependency_paths << path unless @dependency_paths.include?(path)
|
data/lib/bade/precompiled.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
|
3
|
+
require 'psych'
|
5
4
|
|
6
5
|
module Bade
|
7
6
|
class Precompiled
|
@@ -26,7 +25,11 @@ module Bade
|
|
26
25
|
file
|
27
26
|
end
|
28
27
|
|
29
|
-
hash =
|
28
|
+
hash = if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.0')
|
29
|
+
Psych.safe_load(file, filename: file.path, permitted_classes: [Symbol])
|
30
|
+
else
|
31
|
+
Psych.safe_load(file, [Symbol])
|
32
|
+
end
|
30
33
|
raise LoadError, 'YAML file is not in valid format' unless hash.is_a?(Hash)
|
31
34
|
|
32
35
|
file_path = hash[:source_file_path]
|
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
|
@@ -164,17 +188,21 @@ module Bade
|
|
164
188
|
|
165
189
|
# @return [RenderBinding]
|
166
190
|
#
|
191
|
+
# rubocop:disable Lint/DuplicateMethods
|
167
192
|
def render_binding
|
168
193
|
@render_binding ||= Runtime::RenderBinding.new(locals || {})
|
169
194
|
end
|
195
|
+
# rubocop:enable Lint/DuplicateMethods
|
170
196
|
|
171
197
|
# @return [Proc]
|
172
198
|
#
|
173
199
|
def lambda_instance
|
174
|
-
|
175
|
-
lambda_binding
|
176
|
-
|
177
|
-
|
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
|
178
206
|
end
|
179
207
|
end
|
180
208
|
|
@@ -195,13 +223,22 @@ module Bade
|
|
195
223
|
Generator::NEW_LINE_NAME.to_sym => new_line,
|
196
224
|
Generator::BASE_INDENT_NAME.to_sym => indent,
|
197
225
|
}
|
198
|
-
run_vars.
|
226
|
+
run_vars.compact! # remove nil values
|
199
227
|
|
200
|
-
|
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
|
201
240
|
end
|
202
241
|
|
203
|
-
|
204
|
-
|
205
242
|
private
|
206
243
|
|
207
244
|
# @param [String] content source code of the template
|
@@ -249,6 +286,7 @@ module Bade
|
|
249
286
|
|
250
287
|
if File.exist?(sub_path)
|
251
288
|
return if sub_path.end_with?('.rb') # handled in Generator
|
289
|
+
|
252
290
|
sub_path
|
253
291
|
else
|
254
292
|
bade_path = "#{sub_path}.bade"
|
@@ -272,5 +310,16 @@ module Bade
|
|
272
310
|
end
|
273
311
|
end
|
274
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
|
275
321
|
end
|
276
322
|
end
|
323
|
+
|
324
|
+
# Set default value to clear_constants
|
325
|
+
Bade::Renderer.clear_constants = false
|
@@ -13,12 +13,10 @@ class String
|
|
13
13
|
%('#{self}')
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
16
|
def blank?
|
18
17
|
strip.empty?
|
19
18
|
end
|
20
19
|
|
21
|
-
|
22
20
|
def remove_last(count = 1)
|
23
21
|
slice(0, length - count)
|
24
22
|
end
|
@@ -27,7 +25,6 @@ class String
|
|
27
25
|
slice!(length - count, count)
|
28
26
|
end
|
29
27
|
|
30
|
-
|
31
28
|
def remove_first(count = 1)
|
32
29
|
slice(count, length - count)
|
33
30
|
end
|
@@ -67,7 +64,6 @@ class String
|
|
67
64
|
remove_first(__chars_count_for_indent(indent, tabsize))
|
68
65
|
end
|
69
66
|
|
70
|
-
|
71
67
|
# Remove indent
|
72
68
|
#
|
73
69
|
# @param [Int] indent
|
@@ -77,7 +73,6 @@ class String
|
|
77
73
|
remove_first!(__chars_count_for_indent(indent, tabsize))
|
78
74
|
end
|
79
75
|
|
80
|
-
|
81
76
|
# Calculate indent for line
|
82
77
|
#
|
83
78
|
# @param [Int] tabsize
|
@@ -88,9 +83,10 @@ class String
|
|
88
83
|
count = 0
|
89
84
|
|
90
85
|
each_char do |char|
|
91
|
-
|
86
|
+
case char
|
87
|
+
when SPACE_CHAR
|
92
88
|
count += 1
|
93
|
-
|
89
|
+
when TAB_CHAR
|
94
90
|
count += tabsize
|
95
91
|
else
|
96
92
|
break
|
@@ -105,7 +101,7 @@ class String
|
|
105
101
|
#
|
106
102
|
def strip_heredoc
|
107
103
|
min_val = scan(/^[ \t]*(?=\S)/).min
|
108
|
-
indent =
|
104
|
+
indent = min_val&.size || 0
|
109
105
|
gsub(/^[ \t]{#{indent}}/, '')
|
110
106
|
end
|
111
107
|
end
|
data/lib/bade/runtime/block.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../ruby2_keywords'
|
4
|
+
|
3
5
|
module Bade
|
4
6
|
module Runtime
|
5
|
-
class RuntimeError < ::StandardError; end
|
6
|
-
|
7
7
|
class Block
|
8
8
|
class MissingBlockDefinitionError < RuntimeError
|
9
9
|
# @return [String]
|
@@ -14,8 +14,8 @@ module Bade
|
|
14
14
|
#
|
15
15
|
attr_accessor :context
|
16
16
|
|
17
|
-
def initialize(name, context, msg
|
18
|
-
super()
|
17
|
+
def initialize(name, context, msg, template_backtrace)
|
18
|
+
super(msg, template_backtrace)
|
19
19
|
|
20
20
|
self.name = name
|
21
21
|
self.context = context
|
@@ -36,34 +36,51 @@ module Bade
|
|
36
36
|
#
|
37
37
|
attr_reader :name
|
38
38
|
|
39
|
+
# @return [RenderBinding::Location, nil]
|
40
|
+
#
|
41
|
+
attr_reader :location
|
42
|
+
|
39
43
|
# @return [RenderBinding]
|
40
44
|
#
|
41
45
|
attr_reader :render_binding
|
42
46
|
|
43
47
|
# @param [String] name name of the block
|
48
|
+
# @param [RenderBinding::Location, nil] location
|
44
49
|
# @param [RenderBinding] render_binding reference to current binding instance
|
45
50
|
# @param [Proc] block reference to lambda
|
46
51
|
#
|
47
|
-
def initialize(name, render_binding, &block)
|
52
|
+
def initialize(name, location, render_binding, &block)
|
48
53
|
@name = name
|
54
|
+
@location = location
|
49
55
|
@render_binding = render_binding
|
50
56
|
@block = block
|
51
57
|
end
|
52
58
|
|
53
59
|
# --- Calling methods
|
54
60
|
|
55
|
-
|
61
|
+
# Calls the block and adds rendered content into current buffer stack.
|
62
|
+
#
|
63
|
+
# @return [Void]
|
64
|
+
ruby2_keywords def call(*args)
|
56
65
|
call!(*args) unless @block.nil?
|
57
66
|
end
|
58
67
|
|
59
|
-
|
60
|
-
|
68
|
+
# Calls the block and adds rendered content into current buffer stack.
|
69
|
+
#
|
70
|
+
# @return [Void]
|
71
|
+
ruby2_keywords def call!(*args)
|
72
|
+
raise MissingBlockDefinitionError.new(name, :call, nil, render_binding.__location_stack) if @block.nil?
|
61
73
|
|
62
|
-
|
74
|
+
__call(*args)
|
63
75
|
end
|
64
76
|
|
65
77
|
# --- Rendering methods
|
66
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]
|
67
84
|
def render(*args)
|
68
85
|
if @block.nil?
|
69
86
|
''
|
@@ -72,10 +89,33 @@ module Bade
|
|
72
89
|
end
|
73
90
|
end
|
74
91
|
|
92
|
+
# Calls the block and returns rendered content in string.
|
93
|
+
#
|
94
|
+
# Throws error when there is no block.
|
95
|
+
#
|
96
|
+
# @return [String]
|
75
97
|
def render!(*args)
|
76
|
-
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)
|
77
116
|
|
78
|
-
|
117
|
+
res = render_binding.__buffs_pop
|
118
|
+
render_binding.__buff&.concat(res) if !res.nil? && !res.empty?
|
79
119
|
end
|
80
120
|
end
|
81
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
@@ -1,43 +1,58 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../ruby2_keywords'
|
4
|
+
|
3
5
|
module Bade
|
4
6
|
module Runtime
|
5
7
|
require_relative 'block'
|
6
8
|
|
7
9
|
class Mixin < Block
|
8
|
-
def call!(blocks, *args)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
10
|
+
ruby2_keywords def call!(blocks, *args)
|
11
|
+
begin
|
12
|
+
__call(blocks, *args)
|
13
|
+
rescue ::ArgumentError => e
|
14
|
+
case e.message
|
15
|
+
when /\Awrong number of arguments \(given ([0-9]+), expected ([0-9]+)\)\Z/,
|
16
|
+
/\Awrong number of arguments \(([0-9]+) for ([0-9]+)\)\Z/
|
17
|
+
# handle incorrect parameters count
|
18
|
+
|
19
|
+
# minus one, because first argument is always hash of blocks
|
20
|
+
given = $1.to_i - 1
|
21
|
+
expected = $2.to_i - 1
|
22
|
+
msg = "wrong number of arguments (given #{given}, expected #{expected}) for mixin `#{name}`"
|
23
|
+
raise Bade::Runtime::ArgumentError.new(msg, render_binding.__location_stack)
|
24
|
+
|
25
|
+
when /\Aunknown keyword: (.*)\Z/
|
26
|
+
# handle unknown key-value parameter
|
27
|
+
key_name = $1
|
28
|
+
msg = "unknown key-value argument `#{key_name}` for mixin `#{name}`"
|
29
|
+
raise Bade::Runtime::ArgumentError.new(msg, render_binding.__location_stack)
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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)
|
35
|
+
|
36
|
+
else
|
37
|
+
raise
|
38
|
+
end
|
39
|
+
rescue Block::MissingBlockDefinitionError => e
|
40
|
+
msg = case e.context
|
41
|
+
when :call
|
42
|
+
"Mixin `#{name}` requires block to get called of block `#{e.name}`"
|
43
|
+
when :render
|
44
|
+
"Mixin `#{name}` requires block to get rendered content of block `#{e.name}`"
|
45
|
+
else
|
46
|
+
raise Bade::Runtime::ArgumentError.new("Unknown context #{e.context} of error #{e}!",
|
47
|
+
render_binding.__location_stack)
|
48
|
+
end
|
49
|
+
|
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)
|
55
|
+
end
|
41
56
|
end
|
42
57
|
end
|
43
58
|
end
|