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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 148aef838267196453c729800e51c4d3b1f194586f5226e00c659d8f3fc80e58
4
- data.tar.gz: dff1e493ea8a08976cd6cdd1fc80cf937cb2582bc41630c2ca6198fc9eab5ada
3
+ metadata.gz: 5a90545fdee8d531bb87e12dd7943c0486daf3eac68f1d0c69ba216c7595f452
4
+ data.tar.gz: cdbf2cbc427cb9dd590768d493f99c91f22ac51a0a3caa172abb0b6c96b81b57
5
5
  SHA512:
6
- metadata.gz: 86853f78cc0d8647ab5d639664dbb12d5e7101165641d91e4afdbc2affb3cf2c5434ea85bc7f9f92cec7ed571d34a4f23dd4bc0db6eaea9522df29e986df071f
7
- data.tar.gz: 335be41aa317132979197275b9c6fad798525af1247d2878239229e4f81b6beaac916000af24a1da0ccd61ea22ee3356127aa7a485fe806cc9385ef8945370be
6
+ metadata.gz: 88708cad2d3145bfe33fb2ca65acbd795b3f8b3074da64f53e8d053d09925a3f750627893c303c2000fe4e9e8328abf4b9fe60a8e1c925a4ccb8966f37887fff
7
+ data.tar.gz: 8b21e240977c4f4c0c0387c244dcfb215141e453e04fde9f47762cb59ec22f43ee8056a17c39f41037d52b2a3d2bde8f66ad2b7623d8f8819f9444eebb075741
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.required_ruby_version = '>= 2.0'
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', '< 4.0'
25
+ spec.add_dependency 'psych', '>= 2.2', '< 5.0'
25
26
 
26
- spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'fakefs', '~> 1.3'
27
28
  spec.add_development_dependency 'rspec', '~> 3.2'
28
- spec.add_development_dependency 'rubocop', '~> 0.50.0'
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 :banchmarks, optional: true do
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://travis-ci.org/epuber-io/bade.svg?branch=master)](https://travis-ci.org/epuber-io/bade) [![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](http://inch-ci.org/github/epuber-io/bade.svg?branch=master)](http://inch-ci.org/github/epuber-io/bade)
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
- ## TODO
43
+ ## TODO
44
44
 
45
45
  - [ ] create documentation about syntax
46
46
  - [ ] create several examples
@@ -24,7 +24,7 @@ module Bade
24
24
 
25
25
  # @param root [Bade::Node]
26
26
  #
27
- def initialize(root: Node.new(:root), file_path: nil)
27
+ def initialize(root: Node.new(:root, nil), file_path: nil)
28
28
  @root = root
29
29
 
30
30
  @file_path = file_path.dup.freeze unless file_path.nil?
@@ -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 && value == other.value && escaped == other.escaped && conditional == other.conditional
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 [Array<Bade::Node>]
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
- def initialize(type, lineno: nil)
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" + node.children.map { |n| node_to_s(n, level + 1) }.join("\n") + "\n#{indent}"
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 = ' ' + other if other && !other.empty?
65
+ other = " #{other}" if other && !other.empty?
68
66
 
69
67
  "#{indent}(#{type_s}#{other}#{children_s})"
70
68
  end
@@ -41,6 +41,7 @@ module Bade
41
41
  code_indent do
42
42
  buff_code "self.#{NEW_LINE_NAME} = #{NEW_LINE_NAME}"
43
43
  buff_code "self.#{BASE_INDENT_NAME} = #{BASE_INDENT_NAME}"
44
+ buff_code "__buffs_push(#{location(filename: document.file_path, lineno: 0, label: '<top>')})"
44
45
 
45
46
  visit_document(document)
46
47
 
@@ -75,7 +76,7 @@ module Bade
75
76
  end
76
77
 
77
78
  def buff_code(text)
78
- @buff << ' ' * @code_indent + text
79
+ @buff << "#{' ' * @code_indent}#{text}"
79
80
  end
80
81
 
81
82
  # @param document [Bade::Document]
@@ -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 = '#' + current_node.children.map(&:value).join("\n#")
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 "load('#{load_path}')" unless load_path.nil?
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
- if mixin_node.type == :mixin_call
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 !blocks.empty?
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
- elsif mixin_node.type == :mixin_decl
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
- # normal params
299
- result += params.select { |n| n.type == :mixin_param }.map(&:value)
300
- result += params.select { |n| n.type == :mixin_key_param }.map { |param| "#{param.name}: #{param.value}" }
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,55 @@ 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|elsif) /)
385
+ when :newline
386
+ true
387
+ else
388
+ false
389
+ end
390
+
391
+ return if should_skip
392
+ return if node.lineno.nil?
393
+
394
+ buff_code "__update_lineno(#{node.lineno})"
395
+ end
396
+
397
+ # @param [String] filename
398
+ # @param [Fixnum] lineno
399
+ # @param [String] label
400
+ # @return [String]
401
+ def location(filename:, lineno:, label:)
402
+ args = [
403
+ filename ? "path: '#{filename}'" : nil,
404
+ "lineno: #{lineno}",
405
+ "label: '#{label}'",
406
+ ].compact
407
+
408
+ "Location.new(#{args.join(',')})"
409
+ end
410
+
411
+ # @param [Node] node
412
+ # @return [String]
413
+ def location_node(node)
414
+ label = case node.type
415
+ when :mixin_decl
416
+ "+#{node.name}"
417
+ when :mixin_block
418
+ "#{node.name} in +#{node.parent.name}"
419
+ else
420
+ node.name
421
+ end
422
+
423
+ location(filename: node.filename, lineno: node.lineno, label: label)
424
+ end
370
425
  end
371
426
 
372
427
  # 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 name -> implicit div
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
@@ -27,7 +29,7 @@ module Bade
27
29
  def to_s
28
30
  line = @line.lstrip
29
31
  column = @column + line.size - @line.size
30
- <<-MSG.strip_heredoc
32
+ <<~MSG
31
33
  #{error}
32
34
  #{file}, Line #{lineno}, Column #{@column}
33
35
  #{line}
@@ -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 = '\1' + ' ' * tabsize
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?
@@ -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
- YAML.safe_load(file, filename: file.path, permitted_classes: [Symbol])
29
+ Psych.safe_load(file, filename: file.path, permitted_classes: [Symbol])
30
30
  else
31
- YAML.safe_load(file, [Symbol])
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 = nil)
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
- def initialize
33
- @optimize = false
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
- TEMPLATE_FILE_NAME = '(__template__)'.freeze
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
- if lambda_binding
177
- lambda_binding.eval(lambda_string, file_path || TEMPLATE_FILE_NAME)
178
- else
179
- render_binding.instance_eval(lambda_string, file_path || TEMPLATE_FILE_NAME)
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.reject! { |_key, value| value.nil? } # remove nil values
226
+ run_vars.compact! # remove nil values
201
227
 
202
- lambda_instance.call(**run_vars)
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
@@ -1,2 +1,3 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  def ruby2_keywords(*) end if RUBY_VERSION < '2.7'