bade 0.2.4 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,12 +5,15 @@ require_relative 'block'
5
5
  module Bade
6
6
  module Runtime
7
7
  class RenderBinding
8
- class KeyError < ::StandardError; end
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 envoked after running the template lambda
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
- @__mixins = Hash.new { |_hash, key| raise "Undefined mixin '#{key}'" }
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 poping buffers in stack
66
+ # --- Methods for dealing with pushing and popping buffers in stack
63
67
 
64
68
  def __buff
65
- __buffs_stack.last
69
+ __buffs_stack.first
66
70
  end
67
71
 
68
- def __buffs_push
69
- __buffs_stack.push([])
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
- __buffs_stack.pop
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,87 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Epuber
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bade
4
- VERSION = '0.2.4'.freeze
4
+ VERSION = '0.3.1'.freeze
5
5
  end
data/lib/bade.rb CHANGED
@@ -7,4 +7,5 @@ module Bade
7
7
  require_relative 'bade/generator'
8
8
  require_relative 'bade/renderer'
9
9
  require_relative 'bade/optimizer'
10
+ require_relative 'bade/precompiled'
10
11
  end
metadata CHANGED
@@ -1,72 +1,78 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bade
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.1
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: 2018-09-05 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
- name: bundler
14
+ name: psych
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1'
20
- type: :development
19
+ version: '2.2'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '5.0'
23
+ type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '2.2'
30
+ - - "<"
25
31
  - !ruby/object:Gem::Version
26
- version: '1'
32
+ version: '5.0'
27
33
  - !ruby/object:Gem::Dependency
28
- name: rspec
34
+ name: fakefs
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
37
  - - "~>"
32
38
  - !ruby/object:Gem::Version
33
- version: '3.2'
39
+ version: '1.3'
34
40
  type: :development
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
44
  - - "~>"
39
45
  - !ruby/object:Gem::Version
40
- version: '3.2'
46
+ version: '1.3'
41
47
  - !ruby/object:Gem::Dependency
42
- name: rake
48
+ name: rspec
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
51
  - - "~>"
46
52
  - !ruby/object:Gem::Version
47
- version: '11'
53
+ version: '3.2'
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
58
  - - "~>"
53
59
  - !ruby/object:Gem::Version
54
- version: '11'
60
+ version: '3.2'
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: rubocop
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
65
  - - "~>"
60
66
  - !ruby/object:Gem::Version
61
- version: '0.35'
67
+ version: '1.14'
62
68
  type: :development
63
69
  prerelease: false
64
70
  version_requirements: !ruby/object:Gem::Requirement
65
71
  requirements:
66
72
  - - "~>"
67
73
  - !ruby/object:Gem::Version
68
- version: '0.35'
69
- description:
74
+ version: '1.14'
75
+ description:
70
76
  email:
71
77
  - samnung@gmail.com
72
78
  executables: []
@@ -98,18 +104,22 @@ files:
98
104
  - lib/bade/parser/parser_text.rb
99
105
  - lib/bade/precompiled.rb
100
106
  - lib/bade/renderer.rb
107
+ - lib/bade/ruby2_keywords.rb
101
108
  - lib/bade/ruby_extensions/array.rb
102
109
  - lib/bade/ruby_extensions/string.rb
103
110
  - lib/bade/runtime.rb
104
111
  - lib/bade/runtime/block.rb
112
+ - lib/bade/runtime/globals_tracker.rb
105
113
  - lib/bade/runtime/mixin.rb
106
114
  - lib/bade/runtime/render_binding.rb
115
+ - lib/bade/runtime/utils/where.rb
107
116
  - lib/bade/version.rb
108
117
  homepage: https://github.com/epuber-io/bade
109
118
  licenses:
110
119
  - MIT
111
- metadata: {}
112
- post_install_message:
120
+ metadata:
121
+ rubygems_mfa_required: 'true'
122
+ post_install_message:
113
123
  rdoc_options: []
114
124
  require_paths:
115
125
  - lib
@@ -117,16 +127,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
117
127
  requirements:
118
128
  - - ">="
119
129
  - !ruby/object:Gem::Version
120
- version: '2.0'
130
+ version: '2.5'
121
131
  required_rubygems_version: !ruby/object:Gem::Requirement
122
132
  requirements:
123
133
  - - ">="
124
134
  - !ruby/object:Gem::Version
125
135
  version: '0'
126
136
  requirements: []
127
- rubyforge_project:
128
- rubygems_version: 2.7.7
129
- signing_key:
137
+ rubygems_version: 3.3.9
138
+ signing_key:
130
139
  specification_version: 4
131
140
  summary: Minimalistic template engine for Ruby.
132
141
  test_files: []