antlers 0.2.0 → 0.3.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 +1 -3
- data/lib/factories/node_factory.rb +5 -0
- data/lib/lexer.rb +21 -3
- data/lib/modules/props.rb +3 -1
- data/lib/modules/variables.rb +19 -0
- data/lib/nodes/for_node.rb +36 -0
- data/lib/nodes/prop_node.rb +5 -5
- data/lib/nodes/slot_node.rb +1 -1
- data/lib/nodes/var_node.rb +4 -20
- data/lib/parser.rb +20 -13
- data/lib/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 57d224246ca8ba02aac3045b0f06904d8cebb0cfdb7f3d518d3c646324a1e3b6
|
|
4
|
+
data.tar.gz: e8b7787ccfc284dbb1d370292ba4238576e346c75bc957d5da70067220635ba7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4cb6632bbbafb3ec12cc27256b2ae9e2134e3b621e28a7edcfd269bfff828d128cd0f659d7135a734b7792d121477c0ca012c3215a1eef442d3106bb637e89d
|
|
7
|
+
data.tar.gz: 0a0f5f6fc83e72a7253a737956c3a4cff5961999722b23b96ad44716b3ad2b2e40ce7742145cea8610f9604467a438a12000a0609cd9408487f85472f362cb3d
|
data/lib/antlers.rb
CHANGED
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
require_relative 'lexer'
|
|
4
4
|
require_relative 'parser'
|
|
5
5
|
|
|
6
|
-
require 'low_event'
|
|
7
|
-
|
|
8
6
|
module Antlers
|
|
9
7
|
class << self
|
|
10
|
-
def
|
|
8
|
+
def ast(template)
|
|
11
9
|
return template unless template.include?('<{') || template.include?('{')
|
|
12
10
|
|
|
13
11
|
lexemes = Lexer.new.parse(template)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../nodes/for_node'
|
|
3
4
|
require_relative '../nodes/prop_node'
|
|
4
5
|
require_relative '../nodes/slot_node'
|
|
5
6
|
require_relative '../nodes/var_node'
|
|
@@ -8,6 +9,10 @@ require_relative '../nodes/yield_node'
|
|
|
8
9
|
module Antlers
|
|
9
10
|
class NodeFactory
|
|
10
11
|
class << self
|
|
12
|
+
def for_node(segment:)
|
|
13
|
+
ForNode.new(name: segment[:for_def], item: segment[:for_def], items: segment[:in])
|
|
14
|
+
end
|
|
15
|
+
|
|
11
16
|
def prop_node(segment:)
|
|
12
17
|
PropNode.new(name: segment[:prop], props: segment[:props])
|
|
13
18
|
end
|
data/lib/lexer.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Antlers
|
|
|
10
10
|
class Lexer
|
|
11
11
|
def initialize
|
|
12
12
|
@delimiters = ['<{', '}>', '{', '}']
|
|
13
|
-
@keywords = ['if:', 'for:', 'in:', 'slot:', ':slot']
|
|
13
|
+
@keywords = ['if:', 'for:', 'in:', ':for', 'slot:', ':slot']
|
|
14
14
|
@cursor = 0
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -54,15 +54,16 @@ module Antlers
|
|
|
54
54
|
return slot_yield if slot_yield?(keywords)
|
|
55
55
|
return slot(name:, props:) if slot?(name)
|
|
56
56
|
return prop(name:, props:) if prop?(name)
|
|
57
|
+
return for_loop(keywords:) if for_loop?(keywords:)
|
|
57
58
|
|
|
58
|
-
raise LexerParseError, "
|
|
59
|
+
raise LexerParseError, "Unrecognised syntax: '#{antlers_segment}'"
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def parse_segment(antlers_segment:)
|
|
62
63
|
name_and_props, *keywords = antlers_segment.split(/(#{Regexp.union(@keywords)})/)
|
|
63
64
|
name, *props = name_and_props.split(' ')
|
|
64
65
|
|
|
65
|
-
[name, props, keywords]
|
|
66
|
+
[name, props, keywords.map(&:strip)]
|
|
66
67
|
end
|
|
67
68
|
|
|
68
69
|
def var?(segments:)
|
|
@@ -70,6 +71,10 @@ module Antlers
|
|
|
70
71
|
first == '{' && last == '}'
|
|
71
72
|
end
|
|
72
73
|
|
|
74
|
+
def for_loop?(keywords:)
|
|
75
|
+
keywords.first == 'for:' || keywords.first == ':for'
|
|
76
|
+
end
|
|
77
|
+
|
|
73
78
|
def slot?(name)
|
|
74
79
|
name && (name.start_with?(':') || name.end_with?(':'))
|
|
75
80
|
end
|
|
@@ -91,6 +96,19 @@ module Antlers
|
|
|
91
96
|
{ var: antlers_segment }
|
|
92
97
|
end
|
|
93
98
|
|
|
99
|
+
def for_loop(keywords:)
|
|
100
|
+
key_values = keywords.count % 2 == 0 ? keywords.each_slice(2).to_h : {}
|
|
101
|
+
|
|
102
|
+
if key_values['for:']
|
|
103
|
+
for_def = { for_def: key_values['for:'] }
|
|
104
|
+
for_def[:in] = key_values['in:']
|
|
105
|
+
return for_def
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# TODO: Keep track of which for loop we're in to allow nested for loops.
|
|
109
|
+
{ for_end: 'level_1' }
|
|
110
|
+
end
|
|
111
|
+
|
|
94
112
|
def slot(name:, props:)
|
|
95
113
|
if name.end_with?(':')
|
|
96
114
|
slot_def = { slot_def: name.delete_suffix(':') }
|
data/lib/modules/props.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'low_event'
|
|
4
|
+
|
|
3
5
|
module Antlers
|
|
4
6
|
module Props
|
|
5
7
|
attr_accessor :props
|
|
@@ -12,7 +14,7 @@ module Antlers
|
|
|
12
14
|
|
|
13
15
|
private
|
|
14
16
|
|
|
15
|
-
def
|
|
17
|
+
def create_render_event(props:)
|
|
16
18
|
Low::Events::RenderEvent.new(action: :render, props:)
|
|
17
19
|
end
|
|
18
20
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Antlers
|
|
2
|
+
module Variables
|
|
3
|
+
# A variable is deliberately limited in what it can represent.
|
|
4
|
+
# 1. An instance variable
|
|
5
|
+
# 2. A method call/local variable
|
|
6
|
+
# 3. A static string
|
|
7
|
+
def evaluate_variable(name:, current_binding:)
|
|
8
|
+
if current_binding
|
|
9
|
+
return current_binding.receiver.instance_variable_get(name) if name.start_with?('@')
|
|
10
|
+
return current_binding.local_variable_get(name) if current_binding.local_variable_defined?(name)
|
|
11
|
+
return current_binding.receiver.send(name.to_sym) if current_binding.receiver.respond_to?(name.to_sym)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@value.to_s
|
|
15
|
+
rescue NameError
|
|
16
|
+
@value.to_s
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
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 ForNode < BranchNode
|
|
9
|
+
include Props
|
|
10
|
+
include Variables
|
|
11
|
+
|
|
12
|
+
attr_accessor :children
|
|
13
|
+
|
|
14
|
+
def initialize(name:, item:, items:, props: [], children: [])
|
|
15
|
+
super(name:, props:, children:)
|
|
16
|
+
|
|
17
|
+
@item = item
|
|
18
|
+
@items = items
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
22
|
+
output = ''
|
|
23
|
+
|
|
24
|
+
evaluate_variable(name: @items, current_binding:).each do |item|
|
|
25
|
+
current_binding.local_variable_set(@item, item)
|
|
26
|
+
|
|
27
|
+
@children.each do |child|
|
|
28
|
+
# Antlers nodes respond to "render", whereas HTML is stored as a string and output as is.
|
|
29
|
+
output += (child.respond_to?(:render) ? child.render(current_binding:, parent_binding:, slot_node:, namespace:) : child) || ''
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
output
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/nodes/prop_node.rb
CHANGED
|
@@ -9,15 +9,15 @@ module Antlers
|
|
|
9
9
|
|
|
10
10
|
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
11
11
|
props = evaluate_props(props: @props, current_binding:)
|
|
12
|
-
event =
|
|
12
|
+
event = create_render_event(props:)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
renderable_klass = class_from_namespace(namespace: namespace&.split('::') || [], name: @name)
|
|
15
|
+
renderable_instance = renderable_klass.new(event:)
|
|
16
16
|
|
|
17
17
|
# Classes referenced via "<{ ChildNode }>" must implement class/instance render/render_template methods (See LowNode).
|
|
18
|
-
return
|
|
18
|
+
return renderable_instance.render_template(event:, parent_binding:, props:) if renderable_klass.template
|
|
19
19
|
|
|
20
|
-
props.empty? ?
|
|
20
|
+
props.empty? ? renderable_instance.render(event:) : renderable_instance.render(event:, **props)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
data/lib/nodes/slot_node.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Antlers
|
|
|
15
15
|
|
|
16
16
|
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
17
17
|
props = evaluate_props(props: @props, current_binding:)
|
|
18
|
-
event =
|
|
18
|
+
event = create_render_event(props:)
|
|
19
19
|
|
|
20
20
|
klass = class_from_namespace(namespace: namespace&.split('::') || [], name: @name)
|
|
21
21
|
instance = klass.new(event:)
|
data/lib/nodes/var_node.rb
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
require 'erb'
|
|
4
4
|
|
|
5
5
|
require_relative '../interfaces/leaf_node'
|
|
6
|
+
require_relative '../modules/variables'
|
|
6
7
|
|
|
7
8
|
module Antlers
|
|
8
9
|
class VarNode < LeafNode
|
|
10
|
+
include Variables
|
|
11
|
+
|
|
9
12
|
attr_reader :value
|
|
10
13
|
|
|
11
14
|
def initialize(name: :var, value:)
|
|
@@ -15,26 +18,7 @@ module Antlers
|
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
18
|
-
ERB::Util.html_escape(
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
# A variable is deliberately limited in what it can represent.
|
|
24
|
-
# 1. An instance/local variable
|
|
25
|
-
# 2. A method call
|
|
26
|
-
# 3. A static string
|
|
27
|
-
def evaluate_value(current_binding)
|
|
28
|
-
if current_binding
|
|
29
|
-
return current_binding.receiver.instance_variable_get(@value) if @value.start_with?('@')
|
|
30
|
-
return current_binding.local_variable_get(@value) if current_binding.local_variable_defined?(@value)
|
|
31
|
-
return current_binding.receiver.send(@value.to_sym) if current_binding.receiver.respond_to?(@value.to_sym)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
@value
|
|
35
|
-
rescue NameError => e
|
|
36
|
-
# TODO: Must be a better way to handle variables input as literal strings.
|
|
37
|
-
@value
|
|
21
|
+
ERB::Util.html_escape(evaluate_variable(name: @value, current_binding:) || @value)
|
|
38
22
|
end
|
|
39
23
|
end
|
|
40
24
|
end
|
data/lib/parser.rb
CHANGED
|
@@ -7,33 +7,40 @@ module Antlers
|
|
|
7
7
|
module Parser
|
|
8
8
|
class << self
|
|
9
9
|
def parse(sequence, id: :root_node)
|
|
10
|
-
branch(
|
|
10
|
+
branch(node: RootNode.new(name: id), sequence:)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def branch(
|
|
13
|
+
def branch(node:, sequence:) # rubocop:disable Metrics/AbcSize
|
|
14
14
|
until sequence.empty?
|
|
15
15
|
segment = sequence.shift
|
|
16
16
|
|
|
17
17
|
if segment.is_a?(String)
|
|
18
|
-
|
|
18
|
+
node.children << segment
|
|
19
19
|
elsif segment[:var]
|
|
20
|
-
|
|
20
|
+
node.children << NodeFactory.var_node(segment:)
|
|
21
21
|
elsif segment[:prop]
|
|
22
|
-
|
|
22
|
+
node.children << NodeFactory.prop_node(segment:)
|
|
23
23
|
elsif segment[:slot]
|
|
24
|
-
|
|
24
|
+
node.children << NodeFactory.yield_node(segment:)
|
|
25
25
|
elsif segment[:slot_def]
|
|
26
26
|
slot_node = NodeFactory.slot_node(segment:)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
node
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def sub_branch(node:, sequence:, end_key:, end_name: nil)
|
|
40
|
+
sub_sequence = []
|
|
41
|
+
sub_sequence << sequence.shift until sequence.first.is_a?(Hash) && sequence.first[end_key] == end_name
|
|
42
|
+
|
|
43
|
+
branch(node:, sequence: sub_sequence)
|
|
37
44
|
end
|
|
38
45
|
end
|
|
39
46
|
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.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -51,6 +51,8 @@ files:
|
|
|
51
51
|
- lib/interfaces/leaf_node.rb
|
|
52
52
|
- lib/lexer.rb
|
|
53
53
|
- lib/modules/props.rb
|
|
54
|
+
- lib/modules/variables.rb
|
|
55
|
+
- lib/nodes/for_node.rb
|
|
54
56
|
- lib/nodes/prop_node.rb
|
|
55
57
|
- lib/nodes/root_node.rb
|
|
56
58
|
- lib/nodes/slot_node.rb
|
|
@@ -78,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
78
80
|
- !ruby/object:Gem::Version
|
|
79
81
|
version: '0'
|
|
80
82
|
requirements: []
|
|
81
|
-
rubygems_version:
|
|
83
|
+
rubygems_version: 4.0.10
|
|
82
84
|
specification_version: 4
|
|
83
85
|
summary: Deer to be different
|
|
84
86
|
test_files: []
|