bade 0.2.4 → 0.3.1
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 +9 -8
- 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 +74 -24
- 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 +88 -0
- data/lib/bade/runtime/mixin.rb +47 -32
- data/lib/bade/runtime/render_binding.rb +56 -13
- data/lib/bade/runtime/utils/where.rb +101 -0
- data/lib/bade/runtime.rb +79 -1
- data/lib/bade/version.rb +1 -1
- data/lib/bade.rb +1 -0
- metadata +33 -24
@@ -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,87 @@
|
|
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 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
data/lib/bade.rb
CHANGED
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.
|
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:
|
11
|
+
date: 2022-03-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: psych
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
|
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: '
|
32
|
+
version: '5.0'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
34
|
+
name: fakefs
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
37
|
- - "~>"
|
32
38
|
- !ruby/object:Gem::Version
|
33
|
-
version: '3
|
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
|
46
|
+
version: '1.3'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
48
|
+
name: rspec
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
44
50
|
requirements:
|
45
51
|
- - "~>"
|
46
52
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
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: '
|
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: '
|
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: '
|
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
|
-
|
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.
|
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
|
-
|
128
|
-
|
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: []
|