antlers 0.5.0 → 0.7.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 +4 -4
- data/lib/antlers.rb +5 -5
- data/lib/factories/node_factory.rb +35 -21
- data/lib/interfaces/branch_node.rb +2 -2
- data/lib/lexer.rb +43 -11
- data/lib/modules/namespace.rb +19 -0
- data/lib/modules/props.rb +0 -10
- data/lib/modules/variables.rb +4 -2
- data/lib/nodes/for_node.rb +2 -4
- data/lib/nodes/form_node.rb +31 -0
- data/lib/nodes/prop_node.rb +35 -10
- data/lib/nodes/slot_node.rb +9 -4
- data/lib/nodes/var_node.rb +1 -1
- data/lib/nodes/yield_node.rb +2 -2
- data/lib/parser.rb +39 -33
- data/lib/version.rb +1 -1
- metadata +24 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4e01a292dd41ce2f99fd7a6bdd5a375d3d2c36a3e4d5280ccaaaf91be9bcde38
|
|
4
|
+
data.tar.gz: 84227a375d02da8663bfdf04efaf496be097f7898840d25bc4f02b0c62bc2fff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c449e355a550e43aa65e1e00ba482e858b07a1fb0f983325808a538769d6e8eb78ed5dad4a5a3fdd3f24348db7001e59876894b2a98d882e38dd9fa10078104a
|
|
7
|
+
data.tar.gz: 5f71032b3da08b3675f52045f513a810bf2228a1c3308ce7dcde2c53596f7525060dbbd34d05ffb1b04cf8672c045a9e117b727f58a972361a32603e53c2d6ff
|
data/lib/antlers.rb
CHANGED
|
@@ -5,15 +5,15 @@ require_relative 'parser'
|
|
|
5
5
|
|
|
6
6
|
module Antlers
|
|
7
7
|
class << self
|
|
8
|
-
def ast(template)
|
|
8
|
+
def ast(template:, namespace: nil)
|
|
9
9
|
return template unless template.include?('<{') || template.include?('{')
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
Parser.parse(
|
|
11
|
+
sequence = Lexer.new.parse(template)
|
|
12
|
+
Parser.new(namespace:).parse(sequence:)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def render(ast:, current_binding:, parent_binding: nil, slot_node: nil
|
|
16
|
-
ast.render(current_binding:, parent_binding:, slot_node
|
|
15
|
+
def render(ast:, current_binding:, parent_binding: nil, slot_node: nil)
|
|
16
|
+
ast.render(current_binding:, parent_binding:, slot_node:)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../nodes/for_node'
|
|
4
|
+
require_relative '../nodes/form_node'
|
|
4
5
|
require_relative '../nodes/prop_node'
|
|
5
6
|
require_relative '../nodes/slot_node'
|
|
6
7
|
require_relative '../nodes/var_node'
|
|
@@ -8,27 +9,40 @@ require_relative '../nodes/yield_node'
|
|
|
8
9
|
|
|
9
10
|
module Antlers
|
|
10
11
|
class NodeFactory
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
value, key, items = segment.values_at(:for_def, :key, :in)
|
|
14
|
-
ForNode.new(name: value, key:, value:, items:)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def prop_node(segment:)
|
|
18
|
-
PropNode.new(name: segment[:prop], props: segment[:props])
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def slot_node(segment:)
|
|
22
|
-
SlotNode.new(name: segment[:slot_def], props: segment[:props])
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def var_node(segment:)
|
|
26
|
-
VarNode.new(value: segment[:var])
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def yield_node(segment:)
|
|
30
|
-
YieldNode.new(name: segment[:slot])
|
|
31
|
-
end
|
|
12
|
+
def initialize(namespace:)
|
|
13
|
+
@namespace = namespace
|
|
32
14
|
end
|
|
15
|
+
|
|
16
|
+
def for_node(segment:)
|
|
17
|
+
value, key, items = segment.values_at(:for_def, :key, :in)
|
|
18
|
+
ForNode.new(name: value, key:, value:, items:)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def form_node(segment:)
|
|
22
|
+
action, method = segment.values_at(:form_def, :method)
|
|
23
|
+
return FormNode.new(name: action, action:, method:) if method
|
|
24
|
+
|
|
25
|
+
FormNode.new(name: action, action:)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def prop_node(segment:)
|
|
29
|
+
PropNode.new(name: segment[:prop], props: segment[:props], namespace:)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def slot_node(segment:)
|
|
33
|
+
SlotNode.new(name: segment[:slot_def], props: segment[:props], namespace:)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def var_node(segment:)
|
|
37
|
+
VarNode.new(value: segment[:var])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def yield_node(segment:)
|
|
41
|
+
YieldNode.new(name: segment[:slot])
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
attr_reader :namespace
|
|
33
47
|
end
|
|
34
48
|
end
|
|
@@ -12,12 +12,12 @@ module Antlers
|
|
|
12
12
|
@children = children
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def render(current_binding: nil, parent_binding: nil, slot_node: nil
|
|
15
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil)
|
|
16
16
|
output = ''
|
|
17
17
|
|
|
18
18
|
@children.each do |child|
|
|
19
19
|
# Antlers nodes respond to "render", whereas HTML is stored as a string and output as is.
|
|
20
|
-
output += (child.respond_to?(:render) ? child.render(current_binding:, parent_binding:, slot_node
|
|
20
|
+
output += (child.respond_to?(:render) ? child.render(current_binding:, parent_binding:, slot_node:) : child) || ''
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
output
|
data/lib/lexer.rb
CHANGED
|
@@ -8,9 +8,12 @@ module Antlers
|
|
|
8
8
|
class LexerParseError < StandardError; end
|
|
9
9
|
|
|
10
10
|
class Lexer
|
|
11
|
+
FOR_KEYWORDS = ['for:', 'in:', ':for'].freeze
|
|
12
|
+
FORM_KEYWORDS = ['form:', ':form'].freeze
|
|
13
|
+
|
|
11
14
|
def initialize
|
|
12
15
|
@delimiters = ['<{', '}>', '{', '}']
|
|
13
|
-
@keywords = ['if:',
|
|
16
|
+
@keywords = ['if:', *FORM_KEYWORDS, *FOR_KEYWORDS, 'slot:', ':slot']
|
|
14
17
|
@cursor = 0
|
|
15
18
|
end
|
|
16
19
|
|
|
@@ -51,10 +54,11 @@ module Antlers
|
|
|
51
54
|
|
|
52
55
|
name, props, keywords = parse_segment(antlers_segment:)
|
|
53
56
|
|
|
54
|
-
return slot_yield if slot_yield?(keywords)
|
|
57
|
+
return slot_yield if slot_yield?(keywords:)
|
|
55
58
|
return slot(name:, props:) if slot?(name)
|
|
56
59
|
return prop(name:, props:) if prop?(name)
|
|
57
60
|
return for_loop(keywords:) if for_loop?(keywords:)
|
|
61
|
+
return form(keywords:) if form?(keywords:)
|
|
58
62
|
|
|
59
63
|
raise LexerParseError, "Unrecognised syntax: '#{antlers_segment}'"
|
|
60
64
|
end
|
|
@@ -62,25 +66,46 @@ module Antlers
|
|
|
62
66
|
def parse_segment(antlers_segment:)
|
|
63
67
|
name_and_props, *keywords = antlers_segment.split(/(#{Regexp.union(@keywords)})/)
|
|
64
68
|
name, *props = name_and_props.split(' ')
|
|
69
|
+
[name, props, parse_keywords(keywords:)]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def parse_keywords(keywords:)
|
|
73
|
+
key_values = {}
|
|
74
|
+
|
|
75
|
+
while (keyword = keywords.shift)
|
|
76
|
+
keyword.strip!
|
|
77
|
+
value = keyword.end_with?(':') && value?(keywords.first.strip) ? keywords.shift.strip : nil
|
|
78
|
+
key_values[keyword] = value
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
key_values
|
|
82
|
+
end
|
|
65
83
|
|
|
66
|
-
|
|
84
|
+
def value?(string)
|
|
85
|
+
!(string.start_with?(':') || string.end_with?(':'))
|
|
67
86
|
end
|
|
68
87
|
|
|
88
|
+
# TODO: Refactor every constant, match and result method into its own class. Loop through every class and return the first match.
|
|
89
|
+
|
|
69
90
|
def var?(segments:)
|
|
70
91
|
first, _, last = segments[@cursor..@cursor + 3].map(&:strip)
|
|
71
92
|
first == '{' && last == '}'
|
|
72
93
|
end
|
|
73
94
|
|
|
74
95
|
def for_loop?(keywords:)
|
|
75
|
-
|
|
96
|
+
FOR_KEYWORDS.include?(keywords.keys.first)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def form?(keywords:)
|
|
100
|
+
FORM_KEYWORDS.include?(keywords.keys.first)
|
|
76
101
|
end
|
|
77
102
|
|
|
78
103
|
def slot?(name)
|
|
79
104
|
name && (name.start_with?(':') || name.end_with?(':'))
|
|
80
105
|
end
|
|
81
106
|
|
|
82
|
-
def slot_yield?(keywords)
|
|
83
|
-
keywords.include?(':slot')
|
|
107
|
+
def slot_yield?(keywords:)
|
|
108
|
+
keywords.keys.include?(':slot')
|
|
84
109
|
end
|
|
85
110
|
|
|
86
111
|
def prop?(name)
|
|
@@ -95,11 +120,9 @@ module Antlers
|
|
|
95
120
|
end
|
|
96
121
|
|
|
97
122
|
def for_loop(keywords:)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
*key, value = key_values['for:'].split(',').map(&:strip)
|
|
102
|
-
for_def = { for_def: value, in: key_values['in:'] }
|
|
123
|
+
if keywords['for:']
|
|
124
|
+
*key, value = keywords['for:'].split(',').map(&:strip)
|
|
125
|
+
for_def = { for_def: value, in: keywords['in:'] }
|
|
103
126
|
for_def[:key] = key.first unless key.empty?
|
|
104
127
|
return for_def
|
|
105
128
|
end
|
|
@@ -108,6 +131,15 @@ module Antlers
|
|
|
108
131
|
{ for_end: 'level_1' }
|
|
109
132
|
end
|
|
110
133
|
|
|
134
|
+
def form(keywords:)
|
|
135
|
+
if keywords.key?('form:')
|
|
136
|
+
action = keywords['form:'] ? keywords['form:'][1...-1] : nil
|
|
137
|
+
return { form_def: action }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
{ form_end: 'level_1' }
|
|
141
|
+
end
|
|
142
|
+
|
|
111
143
|
def slot(name:, props:)
|
|
112
144
|
if name.end_with?(':')
|
|
113
145
|
slot_def = { slot_def: name.delete_suffix(':') }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Antlers
|
|
4
|
+
module Namespace
|
|
5
|
+
def class_constant(namespace:, name:)
|
|
6
|
+
return Object.const_get(name) if name.start_with?('::')
|
|
7
|
+
|
|
8
|
+
class_from_namespace(namespace:, name:)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def class_from_namespace(namespace:, name:)
|
|
12
|
+
namespace_with_name = [namespace, name].join('::')
|
|
13
|
+
return Object.const_get(namespace_with_name) if Object.const_defined?(namespace_with_name)
|
|
14
|
+
|
|
15
|
+
namespace.pop
|
|
16
|
+
class_from_namespace(namespace:, name:)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/modules/props.rb
CHANGED
|
@@ -32,15 +32,5 @@ module Antlers
|
|
|
32
32
|
|
|
33
33
|
evaluated_props
|
|
34
34
|
end
|
|
35
|
-
|
|
36
|
-
def class_from_namespace(namespace:, name:)
|
|
37
|
-
return Object.const_get(name) if Object.const_defined?(name) || name.start_with?('::') || namespace.empty?
|
|
38
|
-
|
|
39
|
-
namespace_with_name = [namespace, name].join('::')
|
|
40
|
-
return Object.const_get(namespace_with_name) if Object.const_defined?(namespace_with_name)
|
|
41
|
-
|
|
42
|
-
namespace.pop
|
|
43
|
-
class_from_namespace(namespace:, name:)
|
|
44
|
-
end
|
|
45
35
|
end
|
|
46
36
|
end
|
data/lib/modules/variables.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative '../support/queries'
|
|
2
4
|
|
|
3
5
|
module Antlers
|
|
@@ -15,7 +17,7 @@ module Antlers
|
|
|
15
17
|
name, *chain = name.split('.')
|
|
16
18
|
|
|
17
19
|
result = method_var(name:, current_binding:)
|
|
18
|
-
return method_chain(result:, chain
|
|
20
|
+
return method_chain(result:, chain:) if chain.count > 0
|
|
19
21
|
return result if result
|
|
20
22
|
|
|
21
23
|
nil
|
|
@@ -39,7 +41,7 @@ module Antlers
|
|
|
39
41
|
nil
|
|
40
42
|
end
|
|
41
43
|
|
|
42
|
-
def method_chain(result:, chain
|
|
44
|
+
def method_chain(result:, chain:)
|
|
43
45
|
chain.reduce(result) do |result, method_call|
|
|
44
46
|
result.send(method_call.to_sym)
|
|
45
47
|
end
|
data/lib/nodes/for_node.rb
CHANGED
|
@@ -9,8 +9,6 @@ module Antlers
|
|
|
9
9
|
include Props
|
|
10
10
|
include Variables
|
|
11
11
|
|
|
12
|
-
attr_accessor :children
|
|
13
|
-
|
|
14
12
|
def initialize(name:, items:, value:, key: nil, props: [], children: [])
|
|
15
13
|
super(name:, props:, children:)
|
|
16
14
|
|
|
@@ -19,7 +17,7 @@ module Antlers
|
|
|
19
17
|
@key = key
|
|
20
18
|
end
|
|
21
19
|
|
|
22
|
-
def render(current_binding: nil, parent_binding: nil, slot_node: nil
|
|
20
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil)
|
|
23
21
|
output = ''
|
|
24
22
|
|
|
25
23
|
evaluate(name: @items, current_binding:).each do |value|
|
|
@@ -31,7 +29,7 @@ module Antlers
|
|
|
31
29
|
|
|
32
30
|
@children.each do |child|
|
|
33
31
|
# Antlers nodes respond to "render", whereas HTML is stored as a string and output as is.
|
|
34
|
-
output += (child.respond_to?(:render) ? child.render(current_binding:, parent_binding:, slot_node
|
|
32
|
+
output += (child.respond_to?(:render) ? child.render(current_binding:, parent_binding:, slot_node:) : child) || ''
|
|
35
33
|
end
|
|
36
34
|
end
|
|
37
35
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/branch_node'
|
|
4
|
+
require_relative '../modules/props'
|
|
5
|
+
require_relative '../modules/variables'
|
|
6
|
+
|
|
7
|
+
module Antlers
|
|
8
|
+
class FormNode < BranchNode
|
|
9
|
+
include Props
|
|
10
|
+
include Variables
|
|
11
|
+
|
|
12
|
+
def initialize(name:, action: nil, method: 'POST', props: [], children: [])
|
|
13
|
+
super(name:, props:, children:)
|
|
14
|
+
|
|
15
|
+
@action = action
|
|
16
|
+
@method = method
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil)
|
|
20
|
+
output = "<form action='#{@action}' method='#{@method}'>"
|
|
21
|
+
|
|
22
|
+
@children.each do |child|
|
|
23
|
+
# Antlers nodes respond to "render", whereas HTML is stored as a string and output as is.
|
|
24
|
+
output += (child.respond_to?(:render) ? child.render(current_binding:, parent_binding:, slot_node:) : child) || ''
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
output += '</form>'
|
|
28
|
+
output
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/nodes/prop_node.rb
CHANGED
|
@@ -1,26 +1,51 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../interfaces/leaf_node'
|
|
4
|
+
require_relative '../modules/namespace'
|
|
4
5
|
require_relative '../modules/props'
|
|
5
6
|
|
|
6
7
|
module Antlers
|
|
7
8
|
class PropNode < LeafNode
|
|
8
|
-
include
|
|
9
|
+
include Namespace
|
|
10
|
+
include Props # Immediate parent ancestor that super() refers to.
|
|
9
11
|
|
|
10
|
-
def
|
|
12
|
+
def initialize(name:, props: {}, namespace: nil, **)
|
|
13
|
+
super(name:, props:)
|
|
14
|
+
|
|
15
|
+
@namespace = namespace
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Classes referenced via "<{ MyNode }>" must implement class/instance and render/render_template methods (See LowNode).
|
|
19
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil)
|
|
11
20
|
props = evaluate_props(props: @props, current_binding:)
|
|
12
21
|
event = create_render_event(props:)
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
# TODO: Get LowLoad to load constants defined in "<{ MyNode }>" syntax so that we can resolve namespace/params on class load.
|
|
24
|
+
klass = class_constant(namespace: @namespace&.split('::') || [], name: @name)
|
|
25
|
+
class_proxy = Lowkey[klass.to_s].first[klass.to_s]
|
|
26
|
+
instance = create_instance(class_proxy:, klass:, event:)
|
|
27
|
+
|
|
28
|
+
return instance.render_template(event:, parent_binding:, props:) if klass.template
|
|
29
|
+
|
|
30
|
+
render_args(class_proxy:, instance:, event:, props:)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def create_instance(class_proxy:, klass:, event:)
|
|
36
|
+
initialize_params = class_proxy.instance_methods[:initialize]&.tagged_params(:keyword)&.map(&:name) || []
|
|
37
|
+
return klass.new(event:, **props) if initialize_params.include?(:event) && initialize_params.count > 1
|
|
38
|
+
return klass.new(event:) if initialize_params.include?(:event)
|
|
39
|
+
|
|
40
|
+
klass.new
|
|
41
|
+
end
|
|
19
42
|
|
|
20
|
-
|
|
21
|
-
|
|
43
|
+
def render_args(class_proxy:, instance:, event:, props:)
|
|
44
|
+
render_params = class_proxy.instance_methods[:render]&.tagged_params(:keyword)&.map(&:name) || []
|
|
45
|
+
return instance.render(event:, **props) if render_params.include?(:event) && render_params.count > 1
|
|
46
|
+
return instance.render(event:) if render_params.include?(:event)
|
|
22
47
|
|
|
23
|
-
|
|
48
|
+
instance.render
|
|
24
49
|
end
|
|
25
50
|
end
|
|
26
51
|
end
|
data/lib/nodes/slot_node.rb
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../interfaces/branch_node'
|
|
4
|
+
require_relative '../modules/namespace'
|
|
4
5
|
require_relative '../modules/props'
|
|
5
6
|
|
|
6
7
|
module Antlers
|
|
7
8
|
class SlotNode < BranchNode
|
|
8
|
-
include
|
|
9
|
+
include Namespace
|
|
10
|
+
include Props # Immediate parent ancestor which props are passed to.
|
|
9
11
|
|
|
10
12
|
attr_accessor :children
|
|
11
13
|
|
|
12
|
-
def initialize(name:, props: [], children: [])
|
|
14
|
+
def initialize(name:, namespace:, props: [], children: [], **)
|
|
13
15
|
super(name:, props:, children:)
|
|
16
|
+
|
|
17
|
+
@namespace = namespace
|
|
14
18
|
end
|
|
15
19
|
|
|
16
|
-
def render(current_binding: nil, parent_binding: nil, slot_node: nil
|
|
20
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil)
|
|
17
21
|
props = evaluate_props(props: @props, current_binding:)
|
|
18
22
|
event = create_render_event(props:)
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
# TODO: Get LowLoad to load constants defined in "<{ MyNode }>" syntax so that we can resolve namespace/params on class load.
|
|
25
|
+
klass = class_constant(namespace: @namespace&.split('::') || [], name:)
|
|
21
26
|
instance = klass.new(event:)
|
|
22
27
|
|
|
23
28
|
# Classes referenced via "<{ ChildNode }>" must implement class/instance render/render_template methods (See LowNode).
|
data/lib/nodes/var_node.rb
CHANGED
|
@@ -17,7 +17,7 @@ module Antlers
|
|
|
17
17
|
@value = value
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def render(current_binding: nil, parent_binding: nil, slot_node: nil
|
|
20
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil)
|
|
21
21
|
ERB::Util.html_escape(evaluate(name: @value, current_binding:) || fallback(@value))
|
|
22
22
|
end
|
|
23
23
|
end
|
data/lib/nodes/yield_node.rb
CHANGED
|
@@ -9,13 +9,13 @@ module Antlers
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
# Renders the children of the parent node in the binding of the parent.
|
|
12
|
-
def render(current_binding: nil, parent_binding: nil, slot_node: nil
|
|
12
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil)
|
|
13
13
|
output = ''
|
|
14
14
|
|
|
15
15
|
slot_node.children.each do |child|
|
|
16
16
|
# Antlers nodes respond to "render", whereas HTML is stored as a string and output as is.
|
|
17
17
|
if child.respond_to?(:render) # rubocop:disable Style/ConditionalAssignment
|
|
18
|
-
output += child.render(current_binding: parent_binding, parent_binding: nil, slot_node: nil
|
|
18
|
+
output += child.render(current_binding: parent_binding, parent_binding: nil, slot_node: nil)
|
|
19
19
|
else
|
|
20
20
|
output += child || ''
|
|
21
21
|
end
|
data/lib/parser.rb
CHANGED
|
@@ -4,44 +4,50 @@ require_relative 'factories/node_factory'
|
|
|
4
4
|
require_relative 'nodes/root_node'
|
|
5
5
|
|
|
6
6
|
module Antlers
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
end
|
|
7
|
+
class Parser
|
|
8
|
+
def initialize(namespace: nil)
|
|
9
|
+
@factory = NodeFactory.new(namespace:)
|
|
10
|
+
end
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
node.children << NodeFactory.var_node(segment:)
|
|
21
|
-
elsif segment[:prop]
|
|
22
|
-
node.children << NodeFactory.prop_node(segment:)
|
|
23
|
-
elsif segment[:slot]
|
|
24
|
-
node.children << NodeFactory.yield_node(segment:)
|
|
25
|
-
elsif segment[:slot_def]
|
|
26
|
-
slot_node = NodeFactory.slot_node(segment:)
|
|
27
|
-
node.children << slot_node
|
|
28
|
-
sub_branch(node: slot_node, sequence:, end_key: :slot_end, end_name: slot_node.name)
|
|
29
|
-
elsif segment[:for_def]
|
|
30
|
-
for_node = NodeFactory.for_node(segment:)
|
|
31
|
-
node.children << for_node
|
|
32
|
-
sub_branch(node: for_node, sequence:, end_key: :for_end, end_name: 'level_1')
|
|
33
|
-
end
|
|
34
|
-
end
|
|
12
|
+
def parse(sequence:, id: :root_node)
|
|
13
|
+
branch(node: RootNode.new(name: id), sequence:)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def branch(node:, sequence:) # rubocop:disable Metrics/AbcSize
|
|
17
|
+
until sequence.empty?
|
|
18
|
+
segment = sequence.shift
|
|
35
19
|
|
|
36
|
-
|
|
20
|
+
if segment.is_a?(String)
|
|
21
|
+
node.children << segment
|
|
22
|
+
elsif segment[:var]
|
|
23
|
+
node.children << @factory.var_node(segment:)
|
|
24
|
+
elsif segment[:prop]
|
|
25
|
+
node.children << @factory.prop_node(segment:)
|
|
26
|
+
elsif segment[:slot]
|
|
27
|
+
node.children << @factory.yield_node(segment:)
|
|
28
|
+
elsif segment[:slot_def]
|
|
29
|
+
slot_node = @factory.slot_node(segment:)
|
|
30
|
+
node.children << slot_node
|
|
31
|
+
sub_branch(node: slot_node, sequence:, end_key: :slot_end, end_name: slot_node.name)
|
|
32
|
+
elsif segment[:for_def]
|
|
33
|
+
for_node = @factory.for_node(segment:)
|
|
34
|
+
node.children << for_node
|
|
35
|
+
sub_branch(node: for_node, sequence:, end_key: :for_end, end_name: 'level_1')
|
|
36
|
+
elsif segment[:form_def]
|
|
37
|
+
form_node = @factory.form_node(segment:)
|
|
38
|
+
node.children << form_node
|
|
39
|
+
sub_branch(node: form_node, sequence:, end_key: :form_end, end_name: 'level_1')
|
|
40
|
+
end
|
|
37
41
|
end
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
node
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def sub_branch(node:, sequence:, end_key:, end_name: nil)
|
|
47
|
+
sub_sequence = []
|
|
48
|
+
sub_sequence << sequence.shift until sequence.first.is_a?(Hash) && sequence.first[end_key] == end_name
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
end
|
|
50
|
+
branch(node:, sequence: sub_sequence)
|
|
45
51
|
end
|
|
46
52
|
end
|
|
47
53
|
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: antlers
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -37,7 +37,22 @@ dependencies:
|
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '0'
|
|
40
|
-
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: lowkey
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
description: A templating language embedded within HTML that is itself embedded within
|
|
55
|
+
Ruby/RBX
|
|
41
56
|
email:
|
|
42
57
|
- maediprichard@gmail.com
|
|
43
58
|
executables: []
|
|
@@ -50,9 +65,11 @@ files:
|
|
|
50
65
|
- lib/interfaces/branch_node.rb
|
|
51
66
|
- lib/interfaces/leaf_node.rb
|
|
52
67
|
- lib/lexer.rb
|
|
68
|
+
- lib/modules/namespace.rb
|
|
53
69
|
- lib/modules/props.rb
|
|
54
70
|
- lib/modules/variables.rb
|
|
55
71
|
- lib/nodes/for_node.rb
|
|
72
|
+
- lib/nodes/form_node.rb
|
|
56
73
|
- lib/nodes/prop_node.rb
|
|
57
74
|
- lib/nodes/root_node.rb
|
|
58
75
|
- lib/nodes/slot_node.rb
|
|
@@ -61,11 +78,11 @@ files:
|
|
|
61
78
|
- lib/parser.rb
|
|
62
79
|
- lib/support/queries.rb
|
|
63
80
|
- lib/version.rb
|
|
64
|
-
homepage: https://
|
|
81
|
+
homepage: https://github.com/raindeer-rb/antlers
|
|
65
82
|
licenses: []
|
|
66
83
|
metadata:
|
|
67
|
-
homepage_uri: https://
|
|
68
|
-
source_code_uri: https://
|
|
84
|
+
homepage_uri: https://github.com/raindeer-rb/antlers
|
|
85
|
+
source_code_uri: https://github.com/raindeer-rb/antlers/src/branch/main
|
|
69
86
|
rdoc_options: []
|
|
70
87
|
require_paths:
|
|
71
88
|
- lib
|
|
@@ -80,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
80
97
|
- !ruby/object:Gem::Version
|
|
81
98
|
version: '0'
|
|
82
99
|
requirements: []
|
|
83
|
-
rubygems_version: 4.0.
|
|
100
|
+
rubygems_version: 4.0.6
|
|
84
101
|
specification_version: 4
|
|
85
|
-
summary:
|
|
102
|
+
summary: Templating language inside HTML inside Ruby
|
|
86
103
|
test_files: []
|