bade 0.3.0 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 202b283306b0a05d870766ff8d7c17f8312727843632958adb2bb3f2300a673f
4
- data.tar.gz: '0820f4f77eb7936b77748257faa1fd4ee51b206d4a7929c656f717127c7b2248'
3
+ metadata.gz: 631a47a8189e2172e6133dbcdd577b741446c493bd4d4fb9e3e7b3e902928891
4
+ data.tar.gz: 975e1ba152e0792d950577e6dbdcf12562a23300692479dc5a9b237c6c06b1e7
5
5
  SHA512:
6
- metadata.gz: ecd2dbf7aa570c8051a2e1b59a1a537583cd2cd5005bb06328795de099fab4213a9cfd616a788d66e333a862253d6aecedd928632e3edb923f021e86658f6780
7
- data.tar.gz: 8633aba2120eaf8f60f96a357cde8eda8c32d862e92dca3c060f66c014bf1031e5bc2916077ee1d1a7de6494cc62c426ded062065c664949acaf849fce71de66
6
+ metadata.gz: 1ebff9a8a22bd7a6dc902e76af2aeec794f95942469f2cc572d97b6cf19d8ceb23c0b1a778391b612a3b35583b5e7c9cc0dfdd9b3f6ea7423e5af943625659b1
7
+ data.tar.gz: ea97fdd39475caf4270535dd7b4af2dbcbcf929bd22cdb080b5b2a5618ad81f8e2796a467b3428a404dd789cb6443b34b9b889ddd32ed37288e1055b5c8446f9
@@ -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
 
@@ -86,7 +87,6 @@ module Bade
86
87
  end
87
88
 
88
89
  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>')})"
90
90
 
91
91
  new_root = if @optimize
92
92
  Optimizer.new(document.root).optimize
@@ -381,12 +381,13 @@ module Bade
381
381
  when :code
382
382
  value = node.value.strip
383
383
 
384
- %w[end else }].include?(value) || value.match(/^when /)
384
+ %w[end else }].include?(value) || value.match(/^(when|elsif) /)
385
385
  when :newline
386
386
  true
387
387
  else
388
388
  false
389
389
  end
390
+
390
391
  return if should_skip
391
392
  return if node.lineno.nil?
392
393
 
data/lib/bade/parser.rb CHANGED
@@ -29,7 +29,7 @@ module Bade
29
29
  def to_s
30
30
  line = @line.lstrip
31
31
  column = @column + line.size - @line.size
32
- <<-MSG.strip_heredoc
32
+ <<~MSG
33
33
  #{error}
34
34
  #{file}, Line #{lineno}, Column #{@column}
35
35
  #{line}
@@ -95,13 +95,4 @@ class String
95
95
 
96
96
  count
97
97
  end
98
-
99
- # source: http://apidock.com/rails/String/strip_heredoc
100
- # @return [String]
101
- #
102
- def strip_heredoc
103
- min_val = scan(/^[ \t]*(?=\S)/).min
104
- indent = min_val&.size || 0
105
- gsub(/^[ \t]{#{indent}}/, '')
106
- end
107
98
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'utils/where'
4
+
3
5
  module Bade
4
6
  module Runtime
5
7
  # Tracks created global variables and constants in block.
@@ -10,9 +12,16 @@ module Bade
10
12
  # @return [Array<[Object, :Symbol]>]
11
13
  attr_accessor :caught_constants
12
14
 
13
- def initialize
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)
14
22
  @caught_variables = []
15
23
  @caught_constants = []
24
+ @constants_location_prefixes = constants_location_prefixes
16
25
  end
17
26
 
18
27
  # @yieldreturn [T]
@@ -43,8 +52,8 @@ module Bade
43
52
  end
44
53
 
45
54
  def clear_constants
46
- @caught_constants.each do |(obj, name)|
47
- obj.send(:remove_const, name) if obj.const_defined?(name)
55
+ _filtered_constants.each do |(obj, name)|
56
+ obj.send(:remove_const, name)
48
57
  end
49
58
  @caught_constants = []
50
59
  end
@@ -54,6 +63,26 @@ module Bade
54
63
  eval("#{name} = nil", binding, __FILE__, __LINE__)
55
64
  end
56
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
57
86
  end
58
87
  end
59
88
  end
@@ -49,6 +49,9 @@ module Bade
49
49
 
50
50
  raise Block::MissingBlockDefinitionError.new(e.name, e.context, msg, render_binding.__location_stack)
51
51
 
52
+ rescue RuntimeError
53
+ raise
54
+
52
55
  rescue Exception => e
53
56
  msg = "Exception raised during execution of mixin `#{name}`: #{e}"
54
57
  raise Bade::Runtime::RuntimeError.wrap_existing_error(msg, e, render_binding.__location_stack)
@@ -86,7 +86,7 @@ module Bade
86
86
  # @param [String] filename
87
87
  def __load(filename)
88
88
  # FakeFS does not fake `load` method
89
- if defined?(:FakeFS) && FakeFS.activated?
89
+ if Object.const_defined?(:FakeFS) && Object.const_get(:FakeFS).activated?
90
90
  # rubocop:disable Security/Eval
91
91
  eval(File.read(filename), __get_binding, filename)
92
92
  # rubocop:enable Security/Eval
@@ -98,7 +98,7 @@ module Bade
98
98
  # @param [String] filename
99
99
  def require_relative(filename)
100
100
  # FakeFS does not fake `require_relative` method
101
- if defined?(:FakeFS) && FakeFS.activated?
101
+ if Object.const_defined?(:FakeFS) && Object.const_get(:FakeFS).activated?
102
102
  # rubocop:disable Security/Eval
103
103
  eval(File.read(filename), __get_binding, filename)
104
104
  # rubocop:enable Security/Eval
@@ -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
@@ -3,6 +3,10 @@
3
3
  module Bade
4
4
  module Runtime
5
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
+
6
10
  def to_s
7
11
  "#{path || TEMPLATE_FILE_NAME}:#{lineno}:in `#{label}'"
8
12
  end
@@ -13,23 +17,41 @@ module Bade
13
17
  #
14
18
  attr_reader :template_backtrace
15
19
 
20
+ # @return [Boolean]
21
+ #
22
+ attr_reader :print_locations_warning
23
+
16
24
  # @param [String] msg
17
25
  # @param [Array<Location>] template_backtrace
18
26
  # @param [Exception, nil] original
19
- def initialize(msg, template_backtrace = [], original: nil)
27
+ def initialize(msg, template_backtrace = [], original: nil, print_locations_warning: false)
20
28
  super(msg)
21
29
  @template_backtrace = template_backtrace
22
30
  @original = original
31
+ @print_locations_warning = print_locations_warning
23
32
  end
24
33
 
25
- def message
34
+ def to_s
26
35
  if @template_backtrace.empty?
27
36
  super
28
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
+
29
50
  <<~MSG.rstrip
30
51
  #{super}
31
52
  template backtrace:
32
53
  #{__formatted_backtrace.join("\n")}
54
+ #{warning}
33
55
  MSG
34
56
  end
35
57
  end
@@ -53,14 +75,20 @@ module Bade
53
75
  def self.process_locations(locations)
54
76
  return [] if locations.nil?
55
77
 
56
- index = locations&.find_index { |loc| loc.path == TEMPLATE_FILE_NAME || loc.path&.include?('.bade') }
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?)
57
83
  return [] if index.nil?
58
84
 
59
- new_locations = locations[0...index] || []
85
+ # get only locations inside template
86
+ new_locations = new_locations[0...index] || []
60
87
 
61
- new_locations.map do |loc|
62
- Location.new(path: loc.path, lineno: loc.lineno, label: loc.label)
63
- end
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') }
64
92
  end
65
93
 
66
94
  # @param [String] msg
@@ -68,8 +96,9 @@ module Bade
68
96
  # @param [Array<Location>] template_backtrace
69
97
  # @return [RuntimeError]
70
98
  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)
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?)
73
102
  end
74
103
  end
75
104
 
data/lib/bade/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bade
4
- VERSION = '0.3.0'.freeze
4
+ VERSION = '0.3.3'.freeze
5
5
  end
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.3.0
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Kříž
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-21 00:00:00.000000000 Z
11
+ date: 2022-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: psych
@@ -112,6 +112,7 @@ files:
112
112
  - lib/bade/runtime/globals_tracker.rb
113
113
  - lib/bade/runtime/mixin.rb
114
114
  - lib/bade/runtime/render_binding.rb
115
+ - lib/bade/runtime/utils/where.rb
115
116
  - lib/bade/version.rb
116
117
  homepage: https://github.com/epuber-io/bade
117
118
  licenses:
@@ -133,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
134
  - !ruby/object:Gem::Version
134
135
  version: '0'
135
136
  requirements: []
136
- rubygems_version: 3.3.8
137
+ rubygems_version: 3.3.9
137
138
  signing_key:
138
139
  specification_version: 4
139
140
  summary: Minimalistic template engine for Ruby.