rblade 1.2.1 → 2.0.2

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.
@@ -9,7 +9,7 @@ module RBlade
9
9
 
10
10
  def compileBreak args
11
11
  if args&.count&.> 1
12
- raise StandardError.new "Break statement: wrong number of arguments (given #{args.count}, expecting 0 or 1)"
12
+ raise RBladeTemplateError.new "Break statement: wrong number of arguments (given #{args.count}, expecting 0 or 1)"
13
13
  end
14
14
 
15
15
  if args.nil?
@@ -21,13 +21,13 @@ module RBlade
21
21
 
22
22
  def compileEach args
23
23
  if args.nil? || args.count > 2
24
- raise StandardError.new "Each statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 or 2)"
24
+ raise RBladeTemplateError.new "Each statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 or 2)"
25
25
  end
26
26
  last_arg, collection = args.pop.split(" in ")
27
27
  args.push(last_arg)
28
28
 
29
29
  if collection.nil?
30
- raise StandardError.new "Each statement: collection not found (expecting 'in')"
30
+ raise RBladeTemplateError.new "Each statement: collection not found (expecting 'in')"
31
31
  end
32
32
 
33
33
  "#{collection}.each do |#{args.join(",")}|;"
@@ -35,13 +35,13 @@ module RBlade
35
35
 
36
36
  def compileEachWithIndex args
37
37
  if args.nil? || args.count > 3
38
- raise StandardError.new "Each with index statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 to 3)"
38
+ raise RBladeTemplateError.new "Each with index statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 to 3)"
39
39
  end
40
40
  last_arg, collection = args.pop.split(" in ")
41
41
  args.push(last_arg)
42
42
 
43
43
  if collection.nil?
44
- raise StandardError.new "Each with index statement: collection not found (expecting 'in')"
44
+ raise RBladeTemplateError.new "Each with index statement: collection not found (expecting 'in')"
45
45
  end
46
46
 
47
47
  # Special case for 3 arguments: the first 2 arguments are the key/value pair in a Hash, and
@@ -55,13 +55,13 @@ module RBlade
55
55
 
56
56
  def compileEachElse args
57
57
  if args.nil? || args.count > 2
58
- raise StandardError.new "Each else statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
58
+ raise RBladeTemplateError.new "Each else statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
59
59
  end
60
60
  last_arg, collection = args.pop.split(" in ")
61
61
  args.push(last_arg)
62
62
 
63
63
  if collection.nil?
64
- raise StandardError.new "Each else statement: collection not found (expecting 'in')"
64
+ raise RBladeTemplateError.new "Each else statement: collection not found (expecting 'in')"
65
65
  end
66
66
 
67
67
  @loop_else_counter += 1
@@ -71,13 +71,13 @@ module RBlade
71
71
 
72
72
  def compileEachWithIndexElse args
73
73
  if args.nil? || args.count > 3
74
- raise StandardError.new "Each with index statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 to 3)"
74
+ raise RBladeTemplateError.new "Each with index statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 to 3)"
75
75
  end
76
76
  last_arg, collection = args.pop.split(" in ")
77
77
  args.push(last_arg)
78
78
 
79
79
  if collection.nil?
80
- raise StandardError.new "Each with index statement: collection not found (expecting 'in')"
80
+ raise RBladeTemplateError.new "Each with index statement: collection not found (expecting 'in')"
81
81
  end
82
82
 
83
83
  @loop_else_counter += 1
@@ -93,7 +93,7 @@ module RBlade
93
93
 
94
94
  def compileFor args
95
95
  if args&.count != 1
96
- raise StandardError.new "For statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
96
+ raise RBladeTemplateError.new "For statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
97
97
  end
98
98
 
99
99
  "for #{args[0]};"
@@ -101,7 +101,7 @@ module RBlade
101
101
 
102
102
  def compileForElse args
103
103
  if args&.count != 1
104
- raise StandardError.new "For else statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
104
+ raise RBladeTemplateError.new "For else statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
105
105
  end
106
106
  @loop_else_counter += 1
107
107
 
@@ -110,7 +110,7 @@ module RBlade
110
110
 
111
111
  def compileEmpty args
112
112
  unless args.nil?
113
- raise StandardError.new "Empty statement: wrong number of arguments (given #{args.count}, expecting 0)"
113
+ raise RBladeTemplateError.new "Empty statement: wrong number of arguments (given #{args.count}, expecting 0)"
114
114
  end
115
115
 
116
116
  @loop_else_counter -= 1
@@ -120,7 +120,7 @@ module RBlade
120
120
 
121
121
  def compileNext args
122
122
  if args&.count&.> 1
123
- raise StandardError.new "Next statement: wrong number of arguments (given #{args.count}, expecting 0 or 1)"
123
+ raise RBladeTemplateError.new "Next statement: wrong number of arguments (given #{args.count}, expecting 0 or 1)"
124
124
  end
125
125
 
126
126
  if args.nil?
@@ -132,7 +132,7 @@ module RBlade
132
132
 
133
133
  def compileUntil args
134
134
  if args&.count != 1
135
- raise StandardError.new "Until statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
135
+ raise RBladeTemplateError.new "Until statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
136
136
  end
137
137
 
138
138
  "until #{args[0]};"
@@ -140,7 +140,7 @@ module RBlade
140
140
 
141
141
  def compileWhile args
142
142
  if args&.count != 1
143
- raise StandardError.new "While statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
143
+ raise RBladeTemplateError.new "While statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
144
144
  end
145
145
 
146
146
  "while #{args[0]};"
@@ -9,50 +9,32 @@ module RBlade
9
9
 
10
10
  def compileOnce args
11
11
  if args&.count&.> 1
12
- raise StandardError.new "Once statement: wrong number of arguments (given #{args.count}, expecting 0 or 1)"
12
+ raise RBladeTemplateError.new "Once statement: wrong number of arguments (given #{args.count}, expecting 0 or 1)"
13
13
  end
14
14
 
15
15
  once_id = args.nil? ? ":_#{@once_counter += 1}" : args[0]
16
16
 
17
- "unless $_once_tokens.include? #{once_id};$_once_tokens<<#{once_id};"
17
+ "unless @_rblade_once_tokens.include? #{once_id};@_rblade_once_tokens<<#{once_id};"
18
18
  end
19
19
 
20
20
  def compilePushOnce args
21
21
  if args&.count != 1 && args&.count != 2
22
- raise StandardError.new "Push once statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 or 2)"
22
+ raise RBladeTemplateError.new "Push once statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 or 2)"
23
23
  end
24
24
  @once_counter += 1
25
25
  once_id = args[1].nil? ? ":_#{@once_counter}" : args[1]
26
26
 
27
- "unless $_once_tokens.include? #{once_id};$_once_tokens<<#{once_id};" \
28
- << "_p1_#{@once_counter}=#{args[0]};_p1_#{@once_counter}_b=_out;_out=+'';"
29
- end
30
-
31
- def compileEndPushOnce args
32
- unless args.nil?
33
- raise StandardError.new "End push once statement: wrong number of arguments (given #{args.count}, expecting 0)"
34
- end
35
-
36
- "RBlade::StackManager.push(_p1_#{@once_counter}, _out);_out=_p1_#{@once_counter}_b;end;"
27
+ "(@_rblade_once_tokens.include? #{once_id}) || @_rblade_once_tokens<<#{once_id} && @_rblade_stack_manager.push(#{args[0]}, @output_buffer) do;"
37
28
  end
38
29
 
39
30
  def compilePrependOnce args
40
31
  if args&.count != 1 && args&.count != 2
41
- raise StandardError.new "Prepend once statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 or 2)"
32
+ raise RBladeTemplateError.new "Prepend once statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 or 2)"
42
33
  end
43
34
  @once_counter += 1
44
35
  once_id = args[1].nil? ? ":_#{@once_counter}" : args[1]
45
36
 
46
- "unless $_once_tokens.include? #{once_id};$_once_tokens<<#{once_id};" \
47
- << "_p1_#{@once_counter}=#{args[0]};_p1_#{@once_counter}_b=_out;_out=+'';"
48
- end
49
-
50
- def compileEndPrependOnce args
51
- unless args.nil?
52
- raise StandardError.new "End prepend once statement: wrong number of arguments (given #{args.count}, expecting 0)"
53
- end
54
-
55
- "RBlade::StackManager.prepend(_p1_#{@once_counter}, _out);_out=_p1_#{@once_counter}_b;end;"
37
+ "(@_rblade_once_tokens.include? #{once_id}) || @_rblade_once_tokens<<#{once_id} && @_rblade_stack_manager.prepend(#{args[0]}, @output_buffer) do;"
56
38
  end
57
39
  end
58
40
  end
@@ -3,88 +3,44 @@
3
3
  module RBlade
4
4
  class CompilesStatements
5
5
  class CompilesStacks
6
- def initialize
7
- @push_counter = 0
8
- end
9
-
10
6
  def compileStack args
11
7
  if args&.count != 1
12
- raise StandardError.new "Stack statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
8
+ raise RBladeTemplateError.new "Stack statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
13
9
  end
14
10
 
15
- "RBlade::StackManager.initialize(#{args[0]}, _out);_stacks.push(#{args[0]});_out=+'';"
11
+ "@_rblade_stack_manager.initialize_stack(#{args[0]}, @output_buffer);_stacks.push(#{args[0]});"
16
12
  end
17
13
 
18
14
  def compilePrepend args
19
15
  if args&.count != 1
20
- raise StandardError.new "Prepend statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
16
+ raise RBladeTemplateError.new "Prepend statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
21
17
  end
22
18
 
23
- @push_counter += 1
24
-
25
- "_p_#{@push_counter}=#{args[0]};_p_#{@push_counter}_b=_out;_out=+'';"
26
- end
27
-
28
- def compileEndPrepend args
29
- unless args.nil?
30
- raise StandardError.new "End prepend statement: wrong number of arguments (given #{args&.count}, expecting 0)"
31
- end
32
-
33
- @push_counter -= 1
34
-
35
- "RBlade::StackManager.prepend(_p_#{@push_counter + 1}, _out);_out=_p_#{@push_counter + 1}_b;"
19
+ "@_rblade_stack_manager.prepend(#{args[0]}, @output_buffer) do;"
36
20
  end
37
21
 
38
22
  def compilePrependIf args
39
23
  if args&.count != 2
40
- raise StandardError.new "Prepend if statement: wrong number of arguments (given #{args&.count}, expecting 2)"
41
- end
42
-
43
- "if #{args[0]};" + compilePrepend([args[1]])
44
- end
45
-
46
- def compileEndPrependIf args
47
- unless args.nil?
48
- raise StandardError.new "End prepend if statement: wrong number of arguments (given #{args.count}, expecting 0)"
24
+ raise RBladeTemplateError.new "Prepend if statement: wrong number of arguments (given #{args&.count}, expecting 2)"
49
25
  end
50
26
 
51
- "end;" + compileEndPush(nil)
27
+ "(#{args[0]}) && @_rblade_stack_manager.prepend(#{args[1]}, @output_buffer) do;"
52
28
  end
53
29
 
54
30
  def compilePush args
55
31
  if args&.count != 1
56
- raise StandardError.new "Push statement: wrong number of arguments (given #{args&.count}, expecting 1)"
32
+ raise RBladeTemplateError.new "Push statement: wrong number of arguments (given #{args&.count}, expecting 1)"
57
33
  end
58
34
 
59
- @push_counter += 1
60
-
61
- "_p_#{@push_counter}=#{args[0]};_p_#{@push_counter}_b=_out;_out=+'';"
35
+ "@_rblade_stack_manager.push(#{args[0]}, @output_buffer) do;"
62
36
  end
63
37
 
64
38
  def compilePushIf args
65
39
  if args&.count != 2
66
- raise StandardError.new "Push if statement: wrong number of arguments (given #{args&.count || 0}, expecting 2)"
67
- end
68
-
69
- "if #{args[0]};" + compilePush([args[1]])
70
- end
71
-
72
- def compileEndPush args
73
- unless args.nil?
74
- raise StandardError.new "End push statement: wrong number of arguments (given #{args.count}, expecting 0)"
75
- end
76
-
77
- @push_counter -= 1
78
-
79
- "RBlade::StackManager.push(_p_#{@push_counter + 1}, _out);_out=_p_#{@push_counter + 1}_b;"
80
- end
81
-
82
- def compileEndPushIf args
83
- unless args.nil?
84
- raise StandardError.new "End push if statement: wrong number of arguments (given #{args.count}, expecting 0)"
40
+ raise RBladeTemplateError.new "Push if statement: wrong number of arguments (given #{args&.count || 0}, expecting 2)"
85
41
  end
86
42
 
87
- "end;" + compileEndPush(nil)
43
+ "(#{args[0]}) && @_rblade_stack_manager.push(#{args[1]}, @output_buffer) do;"
88
44
  end
89
45
  end
90
46
  end
@@ -12,7 +12,7 @@ module RBlade
12
12
 
13
13
  i = 0
14
14
  while i < segments.count
15
- if segments[i] == "</" && segments[i + 1]&.match(/x[-:]/)
15
+ if segments[i] == "</" && segments[i + 1]&.match?(/x[-:]/)
16
16
  segments[i] = Token.new(type: :component_end, value: {name: segments[i + 1][2..]})
17
17
 
18
18
  segments.delete_at i + 1
@@ -20,7 +20,7 @@ module RBlade
20
20
  elsif segments[i] == "<//>"
21
21
  segments[i] = Token.new(type: :component_unsafe_end)
22
22
  i += 1
23
- elsif segments[i] == "<" && segments[i + 1]&.match(/x[-:]/)
23
+ elsif segments[i] == "<" && segments[i + 1]&.match?(/x[-:]/)
24
24
  name = segments[i + 1][2..]
25
25
  raw_attributes = (segments[i + 2] != ">") ? tokenizeAttributes(segments[i + 2]) : nil
26
26
 
@@ -66,7 +66,7 @@ module RBlade
66
66
  attributes.push({type: "attributes", value: raw_attributes[i + 1]})
67
67
  i += 3
68
68
  else
69
- attribute = {name:}
69
+ attribute = {}
70
70
 
71
71
  if raw_attributes[i + 1] == "="
72
72
  attribute[:value] = raw_attributes[i + 2]
@@ -78,18 +78,28 @@ module RBlade
78
78
  # The "::" at the start of attributes is used to escape attribute names beginning with ":"
79
79
  if name[0..1] == "::"
80
80
  attribute[:type] = "string"
81
- attribute[:name].delete_prefix! ":"
81
+ attribute[:name] = name[1..]
82
+ attributes.push(attribute)
83
+ next
84
+ end
85
+
86
+ # If the entire value is a single interpolated string, make this a ruby value
87
+ if attribute[:value]&.match?(/\A\{\{([^}]|(?!\}\})\})*\}\}\Z/)
88
+ attribute[:type] = "ruby"
89
+ attribute[:name] = name
90
+ attribute[:value] = attribute[:value][2..-3]
82
91
  attributes.push(attribute)
83
92
  next
84
93
  end
85
94
 
86
95
  if name[0] == ":"
87
96
  attribute[:type] = attribute[:value].nil? ? "pass_through" : "ruby"
88
- attribute[:name].delete_prefix! ":"
97
+ attribute[:name] = name[1..]
89
98
  attributes.push(attribute)
90
99
  next
91
100
  end
92
101
 
102
+ attribute[:name] = name
93
103
  attribute[:type] = if attribute[:value].nil?
94
104
  "empty"
95
105
  else
@@ -129,11 +139,11 @@ module RBlade
129
139
  (?:
130
140
  =
131
141
  (?:
132
- "[^"]*"
142
+ "(?:[^"{]*?|(?<!@)\{\{.*?\}\}|\{)*"
133
143
  |
134
- '[^\']*'
144
+ '(?:[^'{]*?|(?<!@)\{\{.*?\}\}|\{)*'
135
145
  |
136
- [^'"=<>\s\/]+
146
+ (?:[^'"=<>\s\/{]+|(?<!@)\{\{.*?\}\}|\{)*
137
147
  )
138
148
  )?
139
149
  )
@@ -178,11 +188,11 @@ module RBlade
178
188
  (?:
179
189
  (=)
180
190
  (?:
181
- "([^"]*)"
191
+ "((?:[^"{]*?|(?<!@)\{\{.*?\}\}|\{)*)"
182
192
  |
183
- '([^\']*)'
193
+ '((?:[^'{]*?|(?<!@)\{\{.*?\}\}|\{)*)'
184
194
  |
185
- ([^'"=<>\s\/]+)
195
+ ((?:[^'"=<>\s\/{]+|(?<!@)\{\{.*?\}\}|\{)*)
186
196
  )
187
197
  )?
188
198
  )
@@ -10,7 +10,7 @@ module RBlade
10
10
  next(token) if token.type != :unprocessed
11
11
 
12
12
  segments = token.value.split(/
13
- \s?(?<!\w)
13
+ (\s)?(?<!\w)
14
14
  (?:
15
15
  (?:
16
16
  (@@)
@@ -25,7 +25,7 @@ module RBlade
25
25
  )?
26
26
  )
27
27
  )
28
- \s?
28
+ (\s)?
29
29
  /mx)
30
30
 
31
31
  parseSegments! segments
@@ -49,12 +49,18 @@ module RBlade
49
49
  if CompilesStatements.has_handler(segments[i + 1])
50
50
  tokenizeStatement! segments, i
51
51
  handleSpecialCases! segments, i
52
+
53
+ segments.delete_at(i + 1) if segments[i + 1]&.match?(/\A\s\Z/)
54
+ if segments[i - 1].is_a?(Token) && segments[i - 1].type == :unprocessed && segments[i - 1].value.match?(/\A\s\Z/)
55
+ segments.delete_at i - 1
56
+ i -= 1
57
+ end
52
58
  else
53
59
  # For unhandled statements, restore the original string
54
60
  segments[i] = Token.new(type: :unprocessed, value: segments[i] + segments[i + 1])
55
61
  segments.delete_at i + 1
56
62
 
57
- if segments.count > i + 2 && segments[i + 1].match(/^[ \t]*$/) && segments[i + 2][0] == "("
63
+ if segments.count > i + 2 && segments[i + 1].match?(/\A[ \t]*\Z/) && segments[i + 2][0] == "("
58
64
  segments[i].value += segments[i + 1] + segments[i + 2]
59
65
  segments.delete_at i + 1
60
66
  segments.delete_at i + 1
@@ -82,7 +88,7 @@ module RBlade
82
88
  statement_name = segments.delete_at i + 1
83
89
 
84
90
  # Remove optional whitespace
85
- if segments.count > i + 2 && segments[i + 1].match(/^[ \t]*$/) && segments[i + 2][0] == "("
91
+ if segments.count > i + 2 && segments[i + 1].match?(/\A[ \t]*\Z/) && segments[i + 2][0] == "("
86
92
  segments.delete_at i + 1
87
93
  end
88
94
 
@@ -8,7 +8,6 @@ require "rblade/compiler/compiles_verbatim"
8
8
  require "rblade/compiler/compiles_statements"
9
9
  require "rblade/compiler/tokenizes_components"
10
10
  require "rblade/compiler/tokenizes_statements"
11
- require "rblade/helpers/html_string"
12
11
  require "active_support/core_ext/string/output_safety"
13
12
 
14
13
  Token = Struct.new(:type, :value)
@@ -23,13 +22,7 @@ module RBlade
23
22
  string.gsub(/['\\\x0]/, '\\\\\0')
24
23
  end
25
24
 
26
- def self.e(string)
27
- if string.is_a?(HtmlString) || string.is_a?(ActiveSupport::SafeBuffer)
28
- string
29
- else
30
- h(string)
31
- end
32
- end
25
+ class RBladeTemplateError < StandardError; end
33
26
 
34
27
  # Register a new custom directive by providing a class and method that will compile the directive into ruby code.
35
28
  #
@@ -41,7 +34,7 @@ module RBlade
41
34
  end
42
35
 
43
36
  class Compiler
44
- def self.compileString(string_template)
37
+ def self.compileString(string_template, component_store)
45
38
  tokens = [Token.new(:unprocessed, string_template)]
46
39
 
47
40
  CompilesVerbatim.new.compile! tokens
@@ -51,7 +44,10 @@ module RBlade
51
44
  CompilesPrints.new.compile! tokens
52
45
  TokenizesStatements.new.tokenize! tokens
53
46
  CompilesStatements.new.compile! tokens
54
- CompilesComponents.new.compile! tokens
47
+
48
+ component_compiler = CompilesComponents.new(component_store)
49
+ component_compiler.compile! tokens
50
+ component_compiler.ensure_all_tags_closed
55
51
 
56
52
  compileTokens tokens
57
53
  end
@@ -69,12 +65,25 @@ module RBlade
69
65
  def self.compileTokens tokens
70
66
  output = +""
71
67
 
72
- tokens.each do |token|
73
- output << if token.type == :unprocessed || token.type == :raw_text
74
- "_out<<'#{RBlade.escape_quotes(token.value)}';"
68
+ i = 0
69
+ while i < tokens.count
70
+ token = tokens[i]
71
+ if token.type == :unprocessed || token.type == :raw_text
72
+ output << "@output_buffer.raw_buffer<<-'"
73
+
74
+ # Merge together consecutive prints
75
+ while tokens[i + 1]&.type == :unprocessed || tokens[i + 1]&.type == :raw_text
76
+ output << RBlade.escape_quotes(token.value)
77
+ i += 1
78
+ token = tokens[i]
79
+ end
80
+
81
+ output << RBlade.escape_quotes(token.value)
82
+ output << "';"
75
83
  else
76
- token.value
84
+ output << token.value
77
85
  end
86
+ i += 1
78
87
  end
79
88
 
80
89
  output
@@ -1,22 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RBlade
4
- FILE_EXTENSIONS = [".rblade", ".html.rblade"]
5
-
6
4
  class ComponentStore
5
+ FILE_EXTENSIONS = [".rblade", ".html.rblade"]
6
+
7
+ def initialize
8
+ @component_definitions = +""
9
+ @component_name_stack = []
10
+ @component_method_names = {}
11
+ end
12
+
7
13
  # Retrieve the method name for a component, and compile it if it hasn't already been compiled
8
- def self.component full_name
14
+ def component(full_name)
9
15
  # If this is a relative path, prepend with the previous component name's base
10
16
  if full_name.start_with? "."
11
- full_name = @@component_name_stack.last.gsub(/\.[^\.]+$/, "") + full_name
17
+ full_name = @component_name_stack.last.gsub(/\.[^\.]+\Z/, "") + full_name
12
18
  end
13
19
 
14
20
  # Ensure each component is only compiled once
15
- unless @@component_method_names[full_name].nil?
16
- return @@component_method_names[full_name]
21
+ unless @component_method_names[full_name].nil?
22
+ return @component_method_names[full_name]
17
23
  end
18
24
 
19
- @@component_name_stack << full_name
25
+ @component_name_stack << full_name
20
26
 
21
27
  namespace = nil
22
28
  name = full_name
@@ -26,36 +32,32 @@ module RBlade
26
32
  end
27
33
 
28
34
  method_name = compile_component full_name, File.read(find_component_file(namespace, name))
29
- @@component_name_stack.pop
35
+ @component_name_stack.pop
30
36
 
31
37
  method_name
32
38
  end
33
39
 
34
- def self.add_path path, namespace = nil
40
+ def self.add_path(path, namespace = nil)
35
41
  path = path.to_s
36
- if !path.end_with? "/"
37
- path += "/"
42
+ unless path.end_with? "/"
43
+ path << "/"
38
44
  end
39
45
 
40
46
  @@template_paths[namespace] ||= []
41
47
  @@template_paths[namespace] << path
42
48
  end
43
49
 
44
- def self.view_name view_name
45
- @@component_name_stack.push view_name
50
+ def view_name(view_name)
51
+ @component_name_stack.push view_name
46
52
  end
47
53
 
48
- def self.get
49
- @@component_definitions
54
+ def get
55
+ @component_definitions
50
56
  end
51
57
 
52
- def self.clear
53
- @@component_definitions = +""
54
- @@component_method_names = {}
55
- @@component_name_stack = []
56
- end
58
+ private
57
59
 
58
- def self.find_component_file namespace, name
60
+ def find_component_file(namespace, name)
59
61
  file_path = name.tr ".", "/"
60
62
 
61
63
  @@template_paths[namespace]&.each do |base_path|
@@ -65,32 +67,30 @@ module RBlade
65
67
  end
66
68
  if File.exist? base_path + file_path + "/index" + extension
67
69
  # Account for index files for relative components
68
- @@component_name_stack << @@component_name_stack.pop + ".index"
70
+ @component_name_stack << @component_name_stack.pop + ".index"
69
71
  return "#{base_path}#{file_path}/index#{extension}"
70
72
  end
71
73
  end
72
74
  end
73
75
 
74
- raise StandardError.new "Unknown component #{namespace}::#{name}"
76
+ raise RBladeTemplateError.new "Unknown component #{namespace}::#{name}"
75
77
  end
76
- private_class_method :find_component_file
77
78
 
78
- def self.compile_component(name, code)
79
- @@component_method_names[name] = "_c#{@@component_method_names.count}"
79
+ def compile_component(name, template)
80
+ escaped_name = name.gsub(/[^0-9a-zA-Z_]/) do |match|
81
+ # Convert invalid characters to hex
82
+ (match == ".") ? "__" : "_#{match.unpack1("H*")}_"
83
+ end
80
84
 
81
- compiled_component = RBlade::Compiler.compileString(code)
85
+ compiled_component = RBlade::Compiler.compileString(template, self)
82
86
 
83
- @@component_definitions << "def #{@@component_method_names[name]}(slot,attributes,params,session,flash,cookies);_out=+'';_stacks=[];attributes=RBlade::AttributesManager.new(attributes);#{compiled_component}RBlade::StackManager.get(_stacks) + _out;end;"
87
+ slot_assignment = compiled_component.match?(/\Wslot\W/) ? "slot=" : ""
84
88
 
85
- @@component_method_names[name]
86
- end
87
- private_class_method :compile_component
89
+ @component_definitions << "def self._rblade_component_#{escaped_name}(attributes,&);#{slot_assignment}if block_given?;RBlade::SlotManager.new(@output_buffer.capture(->(name, slot_attributes, &slot_block)do;attributes[name]=RBlade::SlotManager.new(@output_buffer.capture(&slot_block), slot_attributes);end,&));end;_stacks=[];@output_buffer.raw_buffer<<@output_buffer.capture do;#{compiled_component}@output_buffer.raw_buffer.prepend(@_rblade_stack_manager.get(_stacks));end;end;"
88
90
 
89
- private
91
+ @component_method_names[name] = "_rblade_component_#{escaped_name}"
92
+ end
90
93
 
91
- @@component_definitions = +""
92
- @@component_name_stack = []
93
- @@component_method_names = {}
94
94
  @@template_paths = {}
95
95
  end
96
96
  end
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rblade/helpers/html_string"
4
-
5
3
  module RBlade
6
- class AttributesManager < HtmlString
4
+ class AttributesManager
7
5
  @attributes = {}
8
6
  def initialize attributes
9
7
  @attributes = attributes
@@ -35,16 +33,20 @@ module RBlade
35
33
  @attributes.respond_to?(method_name)
36
34
  end
37
35
 
38
- def to_s attributes = nil
36
+ def html_safe?
37
+ true
38
+ end
39
+
40
+ def to_str attributes = nil
39
41
  attributes ||= @attributes
40
42
 
41
43
  attributes.map do |key, value|
42
44
  (value == true) ? key : "#{key}=\"#{h(value)}\""
43
- end.join " "
45
+ end.join(" ")
44
46
  end
45
47
 
46
- def to_str
47
- to_s
48
+ def to_s
49
+ self
48
50
  end
49
51
 
50
52
  def only(keys)
@@ -111,7 +113,7 @@ module RBlade
111
113
  return classes_1
112
114
  end
113
115
 
114
- classes_combined = classes_1
116
+ classes_combined = +classes_1
115
117
  unless classes_combined.end_with? " "
116
118
  classes_combined << " "
117
119
  end
@@ -128,7 +130,7 @@ module RBlade
128
130
  return styles_1
129
131
  end
130
132
 
131
- styles_combined = styles_1
133
+ styles_combined = +styles_1
132
134
  unless styles_combined.end_with? ";"
133
135
  styles_combined << ";"
134
136
  end