antlers 0.0.0 → 0.2.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 +21 -0
- data/lib/factories/node_factory.rb +28 -0
- data/lib/interfaces/antler_node.rb +20 -0
- data/lib/interfaces/branch_node.rb +26 -0
- data/lib/interfaces/leaf_node.rb +8 -0
- data/lib/lexer.rb +135 -0
- data/lib/modules/props.rb +47 -0
- data/lib/nodes/prop_node.rb +23 -0
- data/lib/nodes/root_node.rb +11 -0
- data/lib/nodes/slot_node.rb +29 -0
- data/lib/nodes/var_node.rb +40 -0
- data/lib/nodes/yield_node.rb +23 -0
- data/lib/parser.rb +40 -0
- data/lib/queries.rb +15 -0
- data/lib/version.rb +1 -1
- metadata +34 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eca914b3e0a81f538aaa1e4db0b477a9b46c0934d365786962d2a391c957b3ee
|
|
4
|
+
data.tar.gz: 7f46ddc34ff748822e99537b59e0300fe6c094abe79d2cd042ad78ad7cb9068d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca62292741c30d4a3ed0a0c0b62dcb20a895eb3018d55b179ef3512f47e078628eff04089b285490add876d1cfa5ca473a4b85b7bdf01d69d56e459b0364e1bb
|
|
7
|
+
data.tar.gz: db84676dac56bce48b73b1019d73924c7c82c7e4c98d6b5a9c9dddbae2e8defd68336f8ec83e64154403a2f9d1fe611d441ebec99f5f2c2a899bb2fe6a7acfc4
|
data/lib/antlers.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lexer'
|
|
4
|
+
require_relative 'parser'
|
|
5
|
+
|
|
6
|
+
require 'low_event'
|
|
7
|
+
|
|
8
|
+
module Antlers
|
|
9
|
+
class << self
|
|
10
|
+
def parse(template)
|
|
11
|
+
return template unless template.include?('<{') || template.include?('{')
|
|
12
|
+
|
|
13
|
+
lexemes = Lexer.new.parse(template)
|
|
14
|
+
Parser.parse(lexemes)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render(ast:, current_binding:, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
18
|
+
ast.render(current_binding:, parent_binding:, slot_node:, namespace:)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../nodes/prop_node'
|
|
4
|
+
require_relative '../nodes/slot_node'
|
|
5
|
+
require_relative '../nodes/var_node'
|
|
6
|
+
require_relative '../nodes/yield_node'
|
|
7
|
+
|
|
8
|
+
module Antlers
|
|
9
|
+
class NodeFactory
|
|
10
|
+
class << self
|
|
11
|
+
def prop_node(segment:)
|
|
12
|
+
PropNode.new(name: segment[:prop], props: segment[:props])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def slot_node(segment:)
|
|
16
|
+
SlotNode.new(name: segment[:slot_def], props: segment[:props])
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def var_node(segment:)
|
|
20
|
+
VarNode.new(value: segment[:var])
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def yield_node(segment:)
|
|
24
|
+
YieldNode.new(name: segment[:slot])
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Antlers
|
|
4
|
+
class AntlerNode
|
|
5
|
+
attr_reader :name
|
|
6
|
+
|
|
7
|
+
def initialize(name:)
|
|
8
|
+
@name = name
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
12
|
+
raise NotImplementedError
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Consider instance a value object on comparison.
|
|
16
|
+
def ==(other) = other.class == self.class
|
|
17
|
+
def eql?(other) = self == other
|
|
18
|
+
def hash = [self.class].hash
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'antler_node'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
class BranchNode < AntlerNode
|
|
7
|
+
attr_accessor :children
|
|
8
|
+
|
|
9
|
+
def initialize(name:, children: [])
|
|
10
|
+
super(name:)
|
|
11
|
+
|
|
12
|
+
@children = children
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
16
|
+
output = ''
|
|
17
|
+
|
|
18
|
+
@children.each do |child|
|
|
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:, namespace:) : child) || ''
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
output
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/lexer.rb
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'queries'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
extend Queries
|
|
7
|
+
|
|
8
|
+
class LexerParseError < StandardError; end
|
|
9
|
+
|
|
10
|
+
class Lexer
|
|
11
|
+
def initialize
|
|
12
|
+
@delimiters = ['<{', '}>', '{', '}']
|
|
13
|
+
@keywords = ['if:', 'for:', 'in:', 'slot:', ':slot']
|
|
14
|
+
@cursor = 0
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def parse(template)
|
|
18
|
+
@cursor = 0
|
|
19
|
+
sequence = []
|
|
20
|
+
|
|
21
|
+
# Split on delimiters and retain capture groups.
|
|
22
|
+
segments = template.split(/(#{Regexp.union(@delimiters)})/).map(&:strip)
|
|
23
|
+
|
|
24
|
+
until segments[@cursor].nil?
|
|
25
|
+
if (antlers_segment = antlers_segment(segments:))
|
|
26
|
+
sequence << antlers_lexeme(antlers_segment:, segments:)
|
|
27
|
+
# Skipping: ['{', 'expression', '}']
|
|
28
|
+
# Skipping: ['<{', 'name + props + keywords', '}>']
|
|
29
|
+
@cursor += 3
|
|
30
|
+
else
|
|
31
|
+
segment = segments[@cursor]
|
|
32
|
+
sequence << segment unless segment.empty?
|
|
33
|
+
@cursor += 1
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
sequence
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def antlers_segment(segments:)
|
|
43
|
+
next_segment = segments[@cursor + 1]
|
|
44
|
+
return nil unless next_segment && (segments[@cursor] == '<{' || var?(segments:))
|
|
45
|
+
|
|
46
|
+
next_segment
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def antlers_lexeme(antlers_segment:, segments:)
|
|
50
|
+
return var(antlers_segment:) if var?(segments:)
|
|
51
|
+
|
|
52
|
+
name, props, keywords = parse_segment(antlers_segment:)
|
|
53
|
+
|
|
54
|
+
return slot_yield if slot_yield?(keywords)
|
|
55
|
+
return slot(name:, props:) if slot?(name)
|
|
56
|
+
return prop(name:, props:) if prop?(name)
|
|
57
|
+
|
|
58
|
+
raise LexerParseError, "Couldn't parse antlers syntax: '#{antlers_segment}'"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def parse_segment(antlers_segment:)
|
|
62
|
+
name_and_props, *keywords = antlers_segment.split(/(#{Regexp.union(@keywords)})/)
|
|
63
|
+
name, *props = name_and_props.split(' ')
|
|
64
|
+
|
|
65
|
+
[name, props, keywords]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def var?(segments:)
|
|
69
|
+
first, middle, last = segments[@cursor..@cursor + 3].map(&:strip)
|
|
70
|
+
first == '{' && last == '}'
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def slot?(name)
|
|
74
|
+
name && (name.start_with?(':') || name.end_with?(':'))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def slot_yield?(keywords)
|
|
78
|
+
keywords.include?(':slot')
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def prop?(name)
|
|
82
|
+
name && [*'A'..'Z'].include?(name[0])
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def var(antlers_segment:)
|
|
86
|
+
# String is already interpolated or not depending on user input on the template layer, now we store it without those template quotes.
|
|
87
|
+
if Queries.user_defined_string?(antlers_segment)
|
|
88
|
+
antlers_segment = antlers_segment[1..-2]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
{ var: antlers_segment }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def slot(name:, props:)
|
|
95
|
+
if name.end_with?(':')
|
|
96
|
+
slot_def = { slot_def: name.delete_suffix(':') }
|
|
97
|
+
slot_def[:props] = props(props) unless props.empty?
|
|
98
|
+
return slot_def
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
{ slot_end: name.delete_prefix(':') }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def slot_yield
|
|
105
|
+
{ slot: :default }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def prop(name:, props:)
|
|
109
|
+
prop = { prop: name }
|
|
110
|
+
prop[:props] = props(props) unless props.empty?
|
|
111
|
+
prop
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def props(props)
|
|
115
|
+
odd_props = props.join(' ').split(/(=)|\s/)
|
|
116
|
+
|
|
117
|
+
return {} unless odd_props.any?
|
|
118
|
+
|
|
119
|
+
props = {}
|
|
120
|
+
until odd_props.empty?
|
|
121
|
+
prop = odd_props.shift
|
|
122
|
+
value = nil
|
|
123
|
+
|
|
124
|
+
if odd_props.first == '='
|
|
125
|
+
odd_props.shift
|
|
126
|
+
value = odd_props.shift
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
props[prop.to_sym] = value
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
props
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Antlers
|
|
4
|
+
module Props
|
|
5
|
+
attr_accessor :props
|
|
6
|
+
|
|
7
|
+
def initialize(name:, props: {}, **)
|
|
8
|
+
super(name:, **)
|
|
9
|
+
|
|
10
|
+
@props = props
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def create_event(props:)
|
|
16
|
+
Low::Events::RenderEvent.new(action: :render, props:)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def evaluate_props(props:, current_binding:)
|
|
20
|
+
return {} if props.nil?
|
|
21
|
+
|
|
22
|
+
evaluated_props = {}
|
|
23
|
+
|
|
24
|
+
props.each do |name, value|
|
|
25
|
+
receiver = current_binding.receiver
|
|
26
|
+
|
|
27
|
+
if receiver.respond_to?(value.to_sym)
|
|
28
|
+
evaluated_props[name] = receiver.send(value.to_sym)
|
|
29
|
+
elsif value.start_with?('@')
|
|
30
|
+
evaluated_props[name] = receiver.instance_variable_get(value)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
evaluated_props
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def class_from_namespace(namespace:, name:)
|
|
38
|
+
return Object.const_get(name) if Object.const_defined?(name) || name.start_with?('::') || namespace.empty?
|
|
39
|
+
|
|
40
|
+
namespace_with_name = [namespace, name].join('::')
|
|
41
|
+
return Object.const_get(namespace_with_name) if Object.const_defined?(namespace_with_name)
|
|
42
|
+
|
|
43
|
+
namespace.pop
|
|
44
|
+
class_from_namespace(namespace:, name:)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/leaf_node'
|
|
4
|
+
require_relative '../modules/props'
|
|
5
|
+
|
|
6
|
+
module Antlers
|
|
7
|
+
class PropNode < LeafNode
|
|
8
|
+
include Props
|
|
9
|
+
|
|
10
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
11
|
+
props = evaluate_props(props: @props, current_binding:)
|
|
12
|
+
event = create_event(props:)
|
|
13
|
+
|
|
14
|
+
klass = class_from_namespace(namespace: namespace&.split('::') || [], name: @name)
|
|
15
|
+
instance = klass.new(event:)
|
|
16
|
+
|
|
17
|
+
# Classes referenced via "<{ ChildNode }>" must implement class/instance render/render_template methods (See LowNode).
|
|
18
|
+
return instance.render_template(event:, parent_binding:, props:) if klass.template
|
|
19
|
+
|
|
20
|
+
props.empty? ? instance.render(event:) : instance.render(event:, **props)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/branch_node'
|
|
4
|
+
require_relative '../modules/props'
|
|
5
|
+
|
|
6
|
+
module Antlers
|
|
7
|
+
class SlotNode < BranchNode
|
|
8
|
+
include Props
|
|
9
|
+
|
|
10
|
+
attr_accessor :children
|
|
11
|
+
|
|
12
|
+
def initialize(name:, props: [], children: [])
|
|
13
|
+
super(name:, props:, children:)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
17
|
+
props = evaluate_props(props: @props, current_binding:)
|
|
18
|
+
event = create_event(props:)
|
|
19
|
+
|
|
20
|
+
klass = class_from_namespace(namespace: namespace&.split('::') || [], name: @name)
|
|
21
|
+
instance = klass.new(event:)
|
|
22
|
+
|
|
23
|
+
# Classes referenced via "<{ ChildNode }>" must implement class/instance render/render_template methods (See LowNode).
|
|
24
|
+
return instance.render_template(event:, parent_binding: current_binding, slot_node: self, props:) if klass.template
|
|
25
|
+
|
|
26
|
+
props.empty? ? instance.render(event:) : instance.render(event:, **props)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'erb'
|
|
4
|
+
|
|
5
|
+
require_relative '../interfaces/leaf_node'
|
|
6
|
+
|
|
7
|
+
module Antlers
|
|
8
|
+
class VarNode < LeafNode
|
|
9
|
+
attr_reader :value
|
|
10
|
+
|
|
11
|
+
def initialize(name: :var, value:)
|
|
12
|
+
super(name:)
|
|
13
|
+
|
|
14
|
+
@value = value
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
|
|
18
|
+
ERB::Util.html_escape(evaluate_value(current_binding))
|
|
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
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/leaf_node'
|
|
4
|
+
|
|
5
|
+
module Antlers
|
|
6
|
+
class YieldNode < BranchNode
|
|
7
|
+
def initialize(name: :default)
|
|
8
|
+
super(name:)
|
|
9
|
+
end
|
|
10
|
+
|
|
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, namespace: nil)
|
|
13
|
+
output = ''
|
|
14
|
+
|
|
15
|
+
slot_node.children.each do |child|
|
|
16
|
+
# Antlers nodes respond to "render", whereas HTML is stored as a string and output as is.
|
|
17
|
+
output += (child.respond_to?(:render) ? child.render(current_binding: parent_binding, parent_binding: nil, slot_node: nil, namespace:) : child) || ''
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
output
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/parser.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'factories/node_factory'
|
|
4
|
+
require_relative 'nodes/root_node'
|
|
5
|
+
|
|
6
|
+
module Antlers
|
|
7
|
+
module Parser
|
|
8
|
+
class << self
|
|
9
|
+
def parse(sequence, id: :root_node)
|
|
10
|
+
branch(branch_node: RootNode.new(name: id), sequence:)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def branch(branch_node:, sequence:) # rubocop:disable Metrics/AbcSize
|
|
14
|
+
until sequence.empty?
|
|
15
|
+
segment = sequence.shift
|
|
16
|
+
|
|
17
|
+
if segment.is_a?(String)
|
|
18
|
+
branch_node.children << segment
|
|
19
|
+
elsif segment[:var]
|
|
20
|
+
branch_node.children << NodeFactory.var_node(segment:)
|
|
21
|
+
elsif segment[:prop]
|
|
22
|
+
branch_node.children << NodeFactory.prop_node(segment:)
|
|
23
|
+
elsif segment[:slot]
|
|
24
|
+
branch_node.children << NodeFactory.yield_node(segment:)
|
|
25
|
+
elsif segment[:slot_def]
|
|
26
|
+
slot_node = NodeFactory.slot_node(segment:)
|
|
27
|
+
branch_node.children << slot_node
|
|
28
|
+
|
|
29
|
+
sub_sequence = []
|
|
30
|
+
sub_sequence << sequence.shift until sequence.first.is_a?(Hash) && sequence.first[:slot_end] == slot_node.name
|
|
31
|
+
|
|
32
|
+
branch(branch_node: slot_node, sequence: sub_sequence)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
branch_node
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/queries.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Antlers
|
|
4
|
+
module Queries
|
|
5
|
+
class << self
|
|
6
|
+
def user_defined_string?(string)
|
|
7
|
+
wrapped_in?(string, %q{'}) || wrapped_in?(string, %q{"})
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def wrapped_in?(string, delimeter)
|
|
11
|
+
string[0] == delimeter && string[-1] == delimeter
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
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.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -10,19 +10,33 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
|
-
name:
|
|
13
|
+
name: low_event
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
|
-
- - "
|
|
16
|
+
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
18
|
+
version: '0'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
|
-
- - "
|
|
23
|
+
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: erb
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
description: A new Ruby templating language. Coming soon...
|
|
27
41
|
email:
|
|
28
42
|
- maediprichard@gmail.com
|
|
@@ -30,6 +44,20 @@ executables: []
|
|
|
30
44
|
extensions: []
|
|
31
45
|
extra_rdoc_files: []
|
|
32
46
|
files:
|
|
47
|
+
- lib/antlers.rb
|
|
48
|
+
- lib/factories/node_factory.rb
|
|
49
|
+
- lib/interfaces/antler_node.rb
|
|
50
|
+
- lib/interfaces/branch_node.rb
|
|
51
|
+
- lib/interfaces/leaf_node.rb
|
|
52
|
+
- lib/lexer.rb
|
|
53
|
+
- lib/modules/props.rb
|
|
54
|
+
- lib/nodes/prop_node.rb
|
|
55
|
+
- lib/nodes/root_node.rb
|
|
56
|
+
- lib/nodes/slot_node.rb
|
|
57
|
+
- lib/nodes/var_node.rb
|
|
58
|
+
- lib/nodes/yield_node.rb
|
|
59
|
+
- lib/parser.rb
|
|
60
|
+
- lib/queries.rb
|
|
33
61
|
- lib/version.rb
|
|
34
62
|
homepage: https://codeberg.org/raindeer/antlers
|
|
35
63
|
licenses: []
|