antlers 0.5.0 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 181c1f53191d4d32c458d74cc67b1d97082d0c023631b0af0fe726dc0db86d9e
4
- data.tar.gz: 010ab20070af4634c783f2aa8573f8fa69be674bf96451df15e59f434cc8fa90
3
+ metadata.gz: b0d88855d8351d4ce9b4ebf2bfeaf0fc05faef8a33a882ffd206fa1c8241ec9e
4
+ data.tar.gz: e72f72db347e9f0f442e0a15cb4dce3f2c7405bf1bd7da6b33ab919a7003785b
5
5
  SHA512:
6
- metadata.gz: d50a398b59defec9de55b59e45c3bd1b57d56f846221b6e00bbd080522909225f6a9e62cd6bc7326b30d60322faf30fbaecf41cada8ed04caa076bafc44be1d0
7
- data.tar.gz: 4c8e9d78e0ee5ce5fbb6dcdb1c76e3ebd91f1c2600cd6accbb066cce81ccad9038aed99f65c6f3c28c99265dcfe7a95729bae02f07ca17283d81821153e7f9ae
6
+ metadata.gz: 6f1e6d6f8f512d327ee25068a3286d8f1e557407bf99f4ec274cd8c8e7336ef9c2d9db9980e66d13ce58b8055d07f6908108afcb8cd91d2da65224ac567946d4
7
+ data.tar.gz: 27520e044188ae13cde780d7438286e510dae3874cd2218debf2901a1b3c7e9909847c44ae38ce4353e8b0482afa2516fa47857836070a58e8683934865a58c7
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
- lexemes = Lexer.new.parse(template)
12
- Parser.parse(lexemes)
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, namespace: nil)
16
- ast.render(current_binding:, parent_binding:, slot_node:, namespace:)
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,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../nodes/if_node'
3
4
  require_relative '../nodes/for_node'
5
+ require_relative '../nodes/form_node'
4
6
  require_relative '../nodes/prop_node'
5
7
  require_relative '../nodes/slot_node'
6
8
  require_relative '../nodes/var_node'
@@ -8,27 +10,48 @@ require_relative '../nodes/yield_node'
8
10
 
9
11
  module Antlers
10
12
  class NodeFactory
11
- class << self
12
- def for_node(segment:)
13
- value, key, items = segment.values_at(:for_def, :key, :in)
14
- ForNode.new(name: value, key:, value:, items:)
15
- end
13
+ def initialize(namespace:)
14
+ @namespace = namespace
15
+ end
16
+
17
+ def if_node(segment:)
18
+ IfNode.new(name: segment[:if_def], value: segment[:if_def])
19
+ end
20
+
21
+ def for_node(segment:)
22
+ value, key, items = segment.values_at(:for_def, :key, :in)
23
+ ForNode.new(name: value, key:, value:, items:)
24
+ end
16
25
 
17
- def prop_node(segment:)
18
- PropNode.new(name: segment[:prop], props: segment[:props])
19
- end
26
+ def form_node(segment:)
27
+ action, method = segment.values_at(:form_def, :method)
28
+ return FormNode.new(name: action, action:, method:) if method
20
29
 
21
- def slot_node(segment:)
22
- SlotNode.new(name: segment[:slot_def], props: segment[:props])
23
- end
30
+ FormNode.new(name: action, action:)
31
+ end
24
32
 
25
- def var_node(segment:)
26
- VarNode.new(value: segment[:var])
27
- end
33
+ def prop_node(segment:)
34
+ PropNode.new(name: segment[:prop], props: segment[:props], namespace:)
35
+ end
28
36
 
29
- def yield_node(segment:)
30
- YieldNode.new(name: segment[:slot])
31
- end
37
+ def slot_node(segment:)
38
+ SlotNode.new(name: segment[:slot_def], props: segment[:props], namespace:)
32
39
  end
40
+
41
+ def var_node(segment:)
42
+ VarNode.new(value: segment[:var])
43
+ end
44
+
45
+ def raw_var_node(segment:)
46
+ VarNode.new(value: segment[:raw_var], raw: true)
47
+ end
48
+
49
+ def yield_node(segment:)
50
+ YieldNode.new(name: segment[:slot])
51
+ end
52
+
53
+ private
54
+
55
+ attr_reader :namespace
33
56
  end
34
57
  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, namespace: 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:, namespace:) : child) || ''
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,13 @@ module Antlers
8
8
  class LexerParseError < StandardError; end
9
9
 
10
10
  class Lexer
11
+ IF_KEYWORDS = ['if:', ':if'].freeze
12
+ FOR_KEYWORDS = ['for:', 'in:', ':for'].freeze
13
+ FORM_KEYWORDS = ['form:', ':form'].freeze
14
+
11
15
  def initialize
12
16
  @delimiters = ['<{', '}>', '{', '}']
13
- @keywords = ['if:', 'for:', 'in:', ':for', 'slot:', ':slot']
17
+ @keywords = [*IF_KEYWORDS, *FORM_KEYWORDS, *FOR_KEYWORDS, 'slot:', ':slot']
14
18
  @cursor = 0
15
19
  end
16
20
 
@@ -41,20 +45,23 @@ module Antlers
41
45
 
42
46
  def antlers_segment(segments:)
43
47
  next_segment = segments[@cursor + 1]
44
- return nil unless next_segment && (segments[@cursor] == '<{' || var?(segments:))
48
+ return nil unless next_segment && (segments[@cursor] == '<{' || brackets?(segments:))
45
49
 
46
50
  next_segment
47
51
  end
48
52
 
49
53
  def antlers_lexeme(antlers_segment:, segments:)
50
- return var(antlers_segment:) if var?(segments:)
54
+ return var(antlers_segment:) if brackets?(segments:)
51
55
 
52
56
  name, props, keywords = parse_segment(antlers_segment:)
53
57
 
54
- return slot_yield if slot_yield?(keywords)
58
+ return slot_yield if slot_yield?(keywords:)
55
59
  return slot(name:, props:) if slot?(name)
56
60
  return prop(name:, props:) if prop?(name)
61
+ return if_block(keywords:) if if_block?(keywords:)
57
62
  return for_loop(keywords:) if for_loop?(keywords:)
63
+ return form(keywords:) if form?(keywords:)
64
+ return var(antlers_segment:, raw: true) if deerheads?(segments:)
58
65
 
59
66
  raise LexerParseError, "Unrecognised syntax: '#{antlers_segment}'"
60
67
  end
@@ -62,44 +69,80 @@ module Antlers
62
69
  def parse_segment(antlers_segment:)
63
70
  name_and_props, *keywords = antlers_segment.split(/(#{Regexp.union(@keywords)})/)
64
71
  name, *props = name_and_props.split(' ')
72
+ [name, props, parse_keywords(keywords:)]
73
+ end
74
+
75
+ def parse_keywords(keywords:)
76
+ key_values = {}
77
+
78
+ while (keyword = keywords.shift)
79
+ keyword.strip!
80
+ value = keyword.end_with?(':') && value?(keywords.first.strip) ? keywords.shift.strip : nil
81
+ key_values[keyword] = value
82
+ end
83
+
84
+ key_values
85
+ end
65
86
 
66
- [name, props, keywords.map(&:strip)]
87
+ def value?(string)
88
+ !(string.start_with?(':') || string.end_with?(':'))
67
89
  end
68
90
 
69
- def var?(segments:)
91
+ # TODO: Refactor every constant, match and result method into its own class. Loop through every class and return the first match.
92
+
93
+ def brackets?(segments:)
70
94
  first, _, last = segments[@cursor..@cursor + 3].map(&:strip)
71
95
  first == '{' && last == '}'
72
96
  end
73
97
 
98
+ def deerheads?(segments:)
99
+ first, _, last = segments[@cursor..@cursor + 3].map(&:strip)
100
+ first == '<{' && last == '}>'
101
+ end
102
+
103
+ def if_block?(keywords:)
104
+ IF_KEYWORDS.include?(keywords.keys.first)
105
+ end
106
+
74
107
  def for_loop?(keywords:)
75
- ['for:', ':for'].include?(keywords.first)
108
+ FOR_KEYWORDS.include?(keywords.keys.first)
109
+ end
110
+
111
+ def form?(keywords:)
112
+ FORM_KEYWORDS.include?(keywords.keys.first)
76
113
  end
77
114
 
78
115
  def slot?(name)
79
116
  name && (name.start_with?(':') || name.end_with?(':'))
80
117
  end
81
118
 
82
- def slot_yield?(keywords)
83
- keywords.include?(':slot')
119
+ def slot_yield?(keywords:)
120
+ keywords.keys.include?(':slot')
84
121
  end
85
122
 
86
123
  def prop?(name)
87
124
  name && [*'A'..'Z'].include?(name[0])
88
125
  end
89
126
 
90
- def var(antlers_segment:)
127
+ def var(antlers_segment:, raw: false)
91
128
  # String is already interpolated or not depending on user input on the template layer, now we store it without those template quotes.
92
129
  antlers_segment = antlers_segment[1..-2] if Queries.user_defined_string?(antlers_segment)
93
130
 
131
+ return { raw_var: antlers_segment } if raw
132
+
94
133
  { var: antlers_segment }
95
134
  end
96
135
 
97
- def for_loop(keywords:)
98
- key_values = keywords.count.even? ? keywords.each_slice(2).to_h : {}
136
+ def if_block(keywords:)
137
+ return { if_def: keywords['if:'] } if keywords.key?('if:')
138
+
139
+ { if_end: 'level_1' }
140
+ end
99
141
 
100
- if key_values['for:']
101
- *key, value = key_values['for:'].split(',').map(&:strip)
102
- for_def = { for_def: value, in: key_values['in:'] }
142
+ def for_loop(keywords:)
143
+ if keywords['for:']
144
+ *key, value = keywords['for:'].split(',').map(&:strip)
145
+ for_def = { for_def: value, in: keywords['in:'] }
103
146
  for_def[:key] = key.first unless key.empty?
104
147
  return for_def
105
148
  end
@@ -108,6 +151,15 @@ module Antlers
108
151
  { for_end: 'level_1' }
109
152
  end
110
153
 
154
+ def form(keywords:)
155
+ if keywords.key?('form:')
156
+ action = keywords['form:'] ? keywords['form:'][1...-1] : nil
157
+ return { form_def: action }
158
+ end
159
+
160
+ { form_end: 'level_1' }
161
+ end
162
+
111
163
  def slot(name:, props:)
112
164
  if name.end_with?(':')
113
165
  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
@@ -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:, current_binding:) if chain.count > 0
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:, current_binding:)
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
@@ -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, namespace: 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:, namespace:) : child) || ''
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
@@ -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 IfNode < BranchNode
9
+ include Props
10
+ include Variables
11
+
12
+ def initialize(name:, value:, props: [], children: [])
13
+ super(name:, props:, children:)
14
+
15
+ @value = value
16
+ end
17
+
18
+ def render(current_binding: nil, parent_binding: nil, slot_node: nil)
19
+ output = ''.dup
20
+
21
+ if evaluate(name: @value, current_binding:)
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
+ end
27
+
28
+ output
29
+ end
30
+ end
31
+ end
@@ -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 Props
9
+ include Namespace
10
+ include Props # Immediate parent ancestor that super() refers to.
9
11
 
10
- def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
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
- renderable_klass = class_from_namespace(namespace: namespace&.split('::') || [], name: @name)
15
- # TODO: There's currently 2 results for "Lowkey[renderable_klass.to_s]", this should not be.
16
- # TODO: Only provide args that are defined, similar to how render_template does it.
17
- initialize_method = Lowkey[renderable_klass.to_s].first[renderable_klass.to_s][:initialize]
18
- renderable_instance = initialize_method ? renderable_klass.new(event:, **props) : renderable_klass.new(event:)
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
- # Classes referenced via "<{ ChildNode }>" must implement class/instance render/render_template methods (See LowNode).
21
- return renderable_instance.render_template(event:, parent_binding:, props:) if renderable_klass.template
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
- props.empty? ? renderable_instance.render(event:) : renderable_instance.render(event:, **props)
48
+ instance.render
24
49
  end
25
50
  end
26
51
  end
@@ -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 Props
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, namespace: 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
- klass = class_from_namespace(namespace: namespace&.split('::') || [], name: @name)
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).
@@ -11,14 +11,19 @@ module Antlers
11
11
 
12
12
  attr_reader :value
13
13
 
14
- def initialize(value:, name: :var)
14
+ def initialize(value:, name: :var, raw: false)
15
15
  super(name:)
16
16
 
17
17
  @value = value
18
+ @raw = raw
18
19
  end
19
20
 
20
- def render(current_binding: nil, parent_binding: nil, slot_node: nil, namespace: nil)
21
- ERB::Util.html_escape(evaluate(name: @value, current_binding:) || fallback(@value))
21
+ def render(current_binding: nil, parent_binding: nil, slot_node: nil)
22
+ result = evaluate(name: @value, current_binding:) || fallback(@value)
23
+
24
+ return result if @raw
25
+
26
+ ERB::Util.html_escape(result)
22
27
  end
23
28
  end
24
29
  end
@@ -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, namespace: 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, namespace:)
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,56 @@ require_relative 'factories/node_factory'
4
4
  require_relative 'nodes/root_node'
5
5
 
6
6
  module Antlers
7
- module Parser
8
- class << self
9
- def parse(sequence, id: :root_node)
10
- branch(node: RootNode.new(name: id), sequence:)
11
- end
7
+ class Parser
8
+ def initialize(namespace: nil)
9
+ @factory = NodeFactory.new(namespace:)
10
+ end
12
11
 
13
- def branch(node:, sequence:) # rubocop:disable Metrics/AbcSize
14
- until sequence.empty?
15
- segment = sequence.shift
16
-
17
- if segment.is_a?(String)
18
- node.children << segment
19
- elsif segment[:var]
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
- node
20
+ if segment.is_a?(String)
21
+ node.children << segment
22
+ elsif segment[:var]
23
+ node.children << @factory.var_node(segment:)
24
+ elsif segment[:raw_var]
25
+ node.children << @factory.raw_var_node(segment:)
26
+ elsif segment[:prop]
27
+ node.children << @factory.prop_node(segment:)
28
+ elsif segment[:slot]
29
+ node.children << @factory.yield_node(segment:)
30
+ elsif segment[:slot_def]
31
+ slot_node = @factory.slot_node(segment:)
32
+ node.children << slot_node
33
+ sub_branch(node: slot_node, sequence:, end_key: :slot_end, end_name: slot_node.name)
34
+ elsif segment[:if_def]
35
+ if_node = @factory.if_node(segment:)
36
+ node.children << if_node
37
+ sub_branch(node: if_node, sequence:, end_key: :if_end, end_name: 'level_1')
38
+ elsif segment[:for_def]
39
+ for_node = @factory.for_node(segment:)
40
+ node.children << for_node
41
+ sub_branch(node: for_node, sequence:, end_key: :for_end, end_name: 'level_1')
42
+ elsif segment[:form_def]
43
+ form_node = @factory.form_node(segment:)
44
+ node.children << form_node
45
+ sub_branch(node: form_node, sequence:, end_key: :form_end, end_name: 'level_1')
46
+ end
37
47
  end
38
48
 
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
49
+ node
50
+ end
51
+
52
+ def sub_branch(node:, sequence:, end_key:, end_name: nil)
53
+ sub_sequence = []
54
+ sub_sequence << sequence.shift until sequence.first.is_a?(Hash) && sequence.first[end_key] == end_name
42
55
 
43
- branch(node:, sequence: sub_sequence)
44
- end
56
+ branch(node:, sequence: sub_sequence)
45
57
  end
46
58
  end
47
59
  end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Antlers
4
- VERSION = '0.5.0'
4
+ VERSION = '0.8.0'
5
5
  end
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.5.0
4
+ version: 0.8.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
- description: A new Ruby templating language. Coming soon...
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,12 @@ 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
73
+ - lib/nodes/if_node.rb
56
74
  - lib/nodes/prop_node.rb
57
75
  - lib/nodes/root_node.rb
58
76
  - lib/nodes/slot_node.rb
@@ -61,11 +79,11 @@ files:
61
79
  - lib/parser.rb
62
80
  - lib/support/queries.rb
63
81
  - lib/version.rb
64
- homepage: https://codeberg.org/raindeer/antlers
82
+ homepage: https://github.com/raindeer-rb/antlers
65
83
  licenses: []
66
84
  metadata:
67
- homepage_uri: https://codeberg.org/raindeer/antlers
68
- source_code_uri: https://codeberg.org/raindeer/antlers/src/branch/main
85
+ homepage_uri: https://github.com/raindeer-rb/antlers
86
+ source_code_uri: https://github.com/raindeer-rb/antlers/src/branch/main
69
87
  rdoc_options: []
70
88
  require_paths:
71
89
  - lib
@@ -80,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
98
  - !ruby/object:Gem::Version
81
99
  version: '0'
82
100
  requirements: []
83
- rubygems_version: 4.0.10
101
+ rubygems_version: 4.0.6
84
102
  specification_version: 4
85
- summary: Deer to be different
103
+ summary: Templating language inside HTML inside Ruby
86
104
  test_files: []