rblade 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9ff80cf4efc20f03172676c57e11c4fe325d5ce304cf855416a4b6d119a654a
4
- data.tar.gz: 7283baf017d3b28a4609160551bc265f27355bb501d2f42a9303fe1bb825e837
3
+ metadata.gz: 388670a7f7f3d90c6249e149cd51e898dbadc66d182e0d0111948677cb85719c
4
+ data.tar.gz: 8dd2d5415191fb833a850b9ba9f2e0fd1d51a3722d466cd410c28c9c02e079dd
5
5
  SHA512:
6
- metadata.gz: dce7d4c704f87acab8e1b2d9999acca4fd77aa5739845fa82417304e5223d6fed5dc5be5a52dc46fe003546635907346029ddc995656b6773bb63aaba19ab90e
7
- data.tar.gz: 9336ac0fdb096d3d6c05992916db5bbb594e965ac4c036e99f86b65dd77062842b42f825979fa3b013b84a35f2d8913c3ef6a43a0ec3163cc2c491da521a16bb
6
+ metadata.gz: 65742f3fbb5ffca6bbbf05e94b4d9838cbf488ae835c1607abdb73cca9c04308a5de142a356ecdbd374019eb73ef3a6ccbe9dec732fceaa555ab580ca0a7ebec
7
+ data.tar.gz: 0bb1616971c4dc02d919f9c432bf478e2f18a2021116834aac10e7be340720e0f75855c997beb5a45fb8c456cb980d5dfeaa7e44e49681c28c1643b1b168d15d
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ## 0.2.0 [2024-07-23]
2
+ - Fix slots being calculated without parent's context
3
+ - Add @props statement
4
+ - Add attributes, class and style manager
5
+
6
+ ## 0.1.0 [2024-07-22]
7
+ - Initial release
@@ -2,6 +2,11 @@ require "rblade/component_store"
2
2
 
3
3
  module RBlade
4
4
  class CompilesComponents
5
+ def initialize
6
+ @component_count = 0
7
+ @component_stack = []
8
+ end
9
+
5
10
  def compile!(tokens)
6
11
  tokens.each do |token|
7
12
  if [:component, :component_start, :component_end].none? token.type
@@ -21,25 +26,50 @@ module RBlade
21
26
  private
22
27
 
23
28
  def compile_token_start token
24
- attributes = compile_attributes token
25
- "def _component(attributes={#{attributes[:arguments].join(",")}});#{attributes[:assignments].join}_out='';"
29
+ @component_count += 1
30
+
31
+ @component_stack << {
32
+ name: token.value[:name],
33
+ attributes: token.value[:attributes],
34
+ index: @component_count
35
+ }
36
+
37
+ "_comp_#{@component_count}_swap=_out;_out='';"
26
38
  end
27
39
 
28
40
  def compile_token_end token
29
- code = "slot=_out;_out='';_stacks=[];"
41
+ component = @component_stack.pop
42
+ if token.value[:name] != component[:name]
43
+ raise StandardError.new "Unexpected closing tag (#{token.value[:name]}) expecting #{component[:name]}"
44
+ end
45
+
46
+ attributes = compile_attributes component[:attributes]
47
+
48
+ code = "def _component(slot,attributes);#{attributes[:assignments].join}_out='';"
49
+ code << "_stacks=[];"
50
+ code << "attributes=RBlade::AttributesManager.new(attributes);"
30
51
  code << ComponentStore.fetchComponent(token.value[:name])
31
- code << "RBlade::StackManager.get(_stacks) + _out;end;_out<<_component();"
52
+ code << "RBlade::StackManager.get(_stacks) + _out;end;"
53
+ code << "_slot=_out;_out=_comp_#{component[:index]}_swap;"
54
+ code << "_out<<_component(_slot,{#{attributes[:arguments].join(",")}});"
32
55
 
33
56
  code
34
57
  end
35
58
 
36
- def compile_attributes token
59
+ def compile_attributes attributes
37
60
  attribute_arguments = []
38
61
  attribute_assignments = []
39
62
 
40
- token.value[:attributes].each do |attribute|
41
- if attribute[:type] == "class" || attribute[:type] == "style"
42
- attribute_arguments.push "'#{attribute[:type]}': #{attribute[:value]}'"
63
+ attributes.each do |attribute|
64
+ if attribute[:type] == "class"
65
+ attribute_arguments.push "'class': RBlade::ClassManager.new(#{attribute[:value]})"
66
+ attribute_assignments.push "_class = attributes[:class];"
67
+
68
+ next
69
+ end
70
+ if attribute[:type] == "style"
71
+ attribute_arguments.push "'style': RBlade::StyleManager.new(#{attribute[:value]})"
72
+ attribute_assignments.push "_style = attributes[:style];"
43
73
 
44
74
  next
45
75
  end
@@ -49,7 +79,7 @@ module RBlade
49
79
  end
50
80
 
51
81
  if attribute[:type] == "ruby"
52
- attribute_arguments.push "'#{attribute[:name]}': (#{attribute[:value]})'"
82
+ attribute_arguments.push "'#{attribute[:name]}': (#{attribute[:value]})"
53
83
  end
54
84
 
55
85
  if attribute[:type] == "pass_through"
@@ -35,11 +35,12 @@ module RBlade
35
35
  segments.delete_at i
36
36
  segments.delete_at i + 1
37
37
  segment_value = "_out<<"
38
- # Special case for slot - we want this to be able to output HTML
39
- if !wrapper_function.nil? && segments[i] != "slot"
40
- segment_value <<= wrapper_function
38
+ # Special case for slot and attributes - we want this to be able to output HTML
39
+ segment_value <<= if !wrapper_function.nil? && segments[i] != "slot" && !segments[i].start_with?("attributes")
40
+ wrapper_function + "(" + segments[i] + ");"
41
+ else
42
+ "(" + segments[i] + ").to_s;"
41
43
  end
42
- segment_value <<= "(" + segments[i] + ");"
43
44
  segments[i] = Token.new(:echo, segment_value)
44
45
 
45
46
  i += 1
@@ -1,6 +1,7 @@
1
1
  require "rblade/compiler/statements/compiles_conditionals"
2
2
  require "rblade/compiler/statements/compiles_inline_ruby"
3
3
  require "rblade/compiler/statements/compiles_loops"
4
+ require "rblade/compiler/statements/compiles_props"
4
5
  require "rblade/compiler/statements/compiles_stacks"
5
6
 
6
7
  module RBlade
@@ -88,6 +89,7 @@ module RBlade
88
89
  "next" => [CompilesLoops, :compileNext],
89
90
  "nextif" => [CompilesLoops, :compileNextIf],
90
91
  "prepend" => [CompilesStacks, :compilePrepend],
92
+ "props" => [CompilesProps, :compileProps],
91
93
  "push" => [CompilesStacks, :compilePush],
92
94
  "readonly" => [CompilesConditionals, :compileReadonly],
93
95
  "required" => [CompilesConditionals, :compileRequired],
@@ -0,0 +1,61 @@
1
+ require "rblade/helpers/tokenizer"
2
+
3
+ module RBlade
4
+ class CompilesStatements
5
+ class CompilesProps
6
+ def compileProps args
7
+ if args.nil?
8
+ raise StandardError.new "Props statement: wrong number of arguments (given #{args&.count}, expecting 1)"
9
+ end
10
+
11
+ props = extractProps args[0]
12
+ props.map do |key, value|
13
+ if value == "_required"
14
+ "if !defined?(#{key});raise \"Props statement: #{key} is not defined\";end;"
15
+ else
16
+ "if !defined?(#{key});#{key}=#{value};end;"
17
+ end
18
+ end.join
19
+ end
20
+
21
+ def extractProps prop_string
22
+ if !prop_string.start_with?("{") || !prop_string.end_with?("}")
23
+ raise StandardError.new "Props statement: expecting hash as parameter"
24
+ end
25
+
26
+ props = {}
27
+ prop_strings = Tokenizer.extractCommaSeparatedValues prop_string[1..-2]
28
+
29
+ prop_strings.each do |prop|
30
+ prop.strip!
31
+
32
+ key, value = prop.split(/^
33
+ (?:
34
+ ('.+'):
35
+ |
36
+ (".+"):
37
+ |
38
+ ([^ :]+):
39
+ |
40
+ :(?:
41
+ '(.+?)'
42
+ |
43
+ "(.+?)"
44
+ |
45
+ ([^"' ]+)
46
+ )\s*=>
47
+ )
48
+ \s*(.+?)$
49
+ /x).reject(&:empty?)
50
+
51
+ if key.nil? || value.nil?
52
+ raise StandardError.new "Props statement: invalid property hash"
53
+ end
54
+ props[key] = value
55
+ end
56
+
57
+ props
58
+ end
59
+ end
60
+ end
61
+ end
@@ -128,7 +128,7 @@ module RBlade
128
128
  |
129
129
  '[^\']*'
130
130
  |
131
- [^'"=<>\s]+
131
+ [^'"=<>\s\/]+
132
132
  )
133
133
  )?
134
134
  )
@@ -175,7 +175,7 @@ module RBlade
175
175
  |
176
176
  '([^\']*)'
177
177
  |
178
- ([^'"=<>\s]+)
178
+ ([^'"=<>\s\/]+)
179
179
  )
180
180
  )?
181
181
  )
@@ -1,3 +1,4 @@
1
+ require "rblade/helpers/tokenizer"
1
2
  require "ripper"
2
3
 
3
4
  module RBlade
@@ -80,7 +81,7 @@ module RBlade
80
81
  return nil
81
82
  end
82
83
 
83
- arguments = extractArguments segments[segment_index]
84
+ arguments = Tokenizer.extractCommaSeparatedValues segments[segment_index][1..-2]
84
85
  segments.delete_at segment_index
85
86
 
86
87
  arguments
@@ -110,62 +111,5 @@ module RBlade
110
111
 
111
112
  parentheses_difference.zero?
112
113
  end
113
-
114
- def extractArguments(segment)
115
- # Add a comma to the end to delimit the end of the last argument
116
- segment = segment[1..-2] + ","
117
- segment_lines = segment.lines
118
-
119
- tokens = Ripper.lex segment
120
- arguments = []
121
-
122
- current_line = 1
123
- current_index = 0
124
- bracket_count = {
125
- "[]": 0,
126
- "{}": 0,
127
- "()": 0
128
- }
129
- tokens.each do |token|
130
- case token[1]
131
- when :on_lbracket
132
- bracket_count[:[]] += 1
133
- when :on_rbracket
134
- bracket_count[:[]] -= 1
135
- when :on_lbrace
136
- bracket_count[:"{}"] += 1
137
- when :on_rbrace
138
- bracket_count[:"{}"] -= 1
139
- when :on_lparen
140
- bracket_count[:"()"] += 1
141
- when :on_rparen
142
- bracket_count[:"()"] -= 1
143
- when :on_comma
144
- if bracket_count[:[]] != 0 || bracket_count[:"{}"] != 0 || bracket_count[:"()"] != 0
145
- next
146
- end
147
-
148
- argument = ""
149
-
150
- # Concatenate all lines up to this token's line, including the tail end of the current line
151
- if token[0][0] != current_line
152
- (current_line...token[0][0]).each do |i|
153
- argument << (segment_lines[i - 1].slice(current_index..-1) || "")
154
- current_index = 0
155
- end
156
- current_line = token[0][0]
157
- end
158
- argument <<= segment_lines[current_line - 1].slice(current_index...token[0][1])
159
-
160
- arguments.push argument.strip
161
-
162
- current_index = token[0][1] + 1
163
- end
164
- end
165
-
166
- return nil if arguments.count == 1 && arguments.first == ""
167
-
168
- arguments
169
- end
170
114
  end
171
115
  end
@@ -8,13 +8,13 @@ module RBlade
8
8
  namespace = nil
9
9
  path = name
10
10
 
11
- if name.match '::'
12
- namespace, path = name.split('::')
11
+ if name.match? "::"
12
+ namespace, path = name.split("::")
13
13
  end
14
14
 
15
- path.gsub! '.', '/'
15
+ path.tr! ".", "/"
16
16
 
17
- @@templatePaths[namespace]&.each do |base_path|
17
+ @@template_paths[namespace]&.each do |base_path|
18
18
  FILE_EXTENSIONS.each do |extension|
19
19
  if File.exist? base_path + path + extension
20
20
  return RBlade::Compiler.compileString File.read(base_path + path + extension)
@@ -28,15 +28,15 @@ module RBlade
28
28
  def self.add_path path, namespace = nil
29
29
  path = path.to_s
30
30
  if !path.end_with? "/"
31
- path = path + "/"
31
+ path += "/"
32
32
  end
33
33
 
34
- @@templatePaths[namespace] ||= []
35
- @@templatePaths[namespace] << path
34
+ @@template_paths[namespace] ||= []
35
+ @@template_paths[namespace] << path
36
36
  end
37
37
 
38
38
  private
39
39
 
40
- @@templatePaths = {}
40
+ @@template_paths = {}
41
41
  end
42
42
  end
@@ -0,0 +1,62 @@
1
+ module RBlade
2
+ class AttributesManager
3
+ @attributes = {}
4
+ def initialize attributes
5
+ @attributes = attributes
6
+ end
7
+
8
+ def to_s attributes = nil
9
+ attributes ||= @attributes
10
+
11
+ attributes.map do |key, value|
12
+ "#{key}=\"#{h(value)}\""
13
+ end.join " "
14
+ end
15
+
16
+ def only(keys)
17
+ keys = if keys.is_a? Array
18
+ keys.map(&:to_sym)
19
+ else
20
+ [keys.to_sym]
21
+ end
22
+
23
+ self.class.new @attributes.slice(*keys)
24
+ end
25
+
26
+ def except(keys)
27
+ keys = if keys.is_a? Array
28
+ keys.map(&:to_sym)
29
+ else
30
+ [keys.to_sym]
31
+ end
32
+
33
+ self.class.new @attributes.except(*keys)
34
+ end
35
+
36
+ def merge(default_attributes)
37
+ new_attributes = default_attributes
38
+
39
+ @attributes.each do |key, value|
40
+ if key == :class && !new_attributes[key].nil?
41
+ unless new_attributes[key].end_with? " "
42
+ new_attributes[key] << " "
43
+ end
44
+ new_attributes[key] << value.to_s
45
+ next
46
+ end
47
+
48
+ if key == :style && !new_attributes[key].nil?
49
+ unless new_attributes[key].end_with? ";"
50
+ new_attributes[key] << ";"
51
+ end
52
+ new_attributes[key] << value.to_s
53
+ next
54
+ end
55
+
56
+ new_attributes[key] = value
57
+ end
58
+
59
+ self.class.new new_attributes
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,23 @@
1
+ module RBlade
2
+ class ClassManager
3
+ def initialize classes
4
+ if classes.is_a? String
5
+ @classes = classes
6
+ elsif classes.is_a? Array
7
+ @classes = classes.join " "
8
+ elsif classes.is_a? Hash
9
+ @classes = ""
10
+ classes.map do |value, predicate|
11
+ if predicate
12
+ @classes << "#{value} "
13
+ end
14
+ end
15
+ @classes.rstrip!
16
+ end
17
+ end
18
+
19
+ def to_s
20
+ @classes
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module RBlade
2
+ class StyleManager
3
+ def initialize styles
4
+ if styles.is_a? String
5
+ @styles = styles.strip
6
+ unless @styles == "" || @styles.end_with?(";")
7
+ @styles << ";"
8
+ end
9
+ elsif styles.is_a? Array
10
+ @styles = styles.map do |style|
11
+ style = style.strip
12
+ unless style.end_with? ";"
13
+ style << ";"
14
+ end
15
+
16
+ style
17
+ end.join
18
+ elsif styles.is_a? Hash
19
+ @styles = ""
20
+ styles.each do |value, predicate|
21
+ if predicate
22
+ value = value.to_s.strip
23
+ unless value.end_with? ";"
24
+ value << ";"
25
+ end
26
+
27
+ @styles << value
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ def to_s
34
+ @styles
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,63 @@
1
+ module RBlade
2
+ class Tokenizer
3
+ def self.extractCommaSeparatedValues segment
4
+ unless segment.match?(/,\s*\z/)
5
+ # Add a comma to the end to delimit the end of the last argument
6
+ segment += ","
7
+ end
8
+ segment_lines = segment.lines
9
+
10
+ tokens = Ripper.lex segment
11
+ arguments = []
12
+
13
+ current_line = 1
14
+ current_index = 0
15
+ bracket_count = {
16
+ "[]": 0,
17
+ "{}": 0,
18
+ "()": 0
19
+ }
20
+ tokens.each do |token|
21
+ case token[1]
22
+ when :on_lbracket
23
+ bracket_count[:[]] += 1
24
+ when :on_rbracket
25
+ bracket_count[:[]] -= 1
26
+ when :on_lbrace
27
+ bracket_count[:"{}"] += 1
28
+ when :on_rbrace
29
+ bracket_count[:"{}"] -= 1
30
+ when :on_lparen
31
+ bracket_count[:"()"] += 1
32
+ when :on_rparen
33
+ bracket_count[:"()"] -= 1
34
+ when :on_comma
35
+ if bracket_count[:[]] != 0 || bracket_count[:"{}"] != 0 || bracket_count[:"()"] != 0
36
+ next
37
+ end
38
+
39
+ argument = ""
40
+
41
+ # Concatenate all lines up to this token's line, including the tail end of the current line
42
+ if token[0][0] != current_line
43
+ (current_line...token[0][0]).each do |i|
44
+ argument << (segment_lines[i - 1].slice(current_index..-1) || "")
45
+ current_index = 0
46
+ end
47
+ current_line = token[0][0]
48
+ end
49
+ argument <<= segment_lines[current_line - 1].slice(current_index...token[0][1])
50
+ argument.strip!
51
+
52
+ arguments.push argument
53
+
54
+ current_index = token[0][1] + 1
55
+ end
56
+ end
57
+
58
+ return nil if arguments.count == 1 && arguments.first == ""
59
+
60
+ arguments
61
+ end
62
+ end
63
+ end
@@ -1,12 +1,15 @@
1
1
  require "rails"
2
2
  require "rblade/compiler"
3
+ require "rblade/helpers/attributes_manager"
4
+ require "rblade/helpers/class_manager"
3
5
  require "rblade/helpers/stack_manager"
6
+ require "rblade/helpers/style_manager"
4
7
 
5
8
  module RBlade
6
9
  class RailsTemplate
7
10
  def call(template, source = nil)
8
11
  RBlade::StackManager.clear
9
- setup = "foo = 'FOO';bar = 'BAR';_out='';_stacks=[];"
12
+ setup = "_out='';_stacks=[];"
10
13
  setdown = "RBlade::StackManager.get(_stacks) + _out"
11
14
  setup + RBlade::Compiler.compileString(source || template.source) + setdown
12
15
  end
data/rblade.gemspec CHANGED
@@ -1,12 +1,12 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rblade"
3
- s.version = "0.1.0"
3
+ s.version = "0.2.0"
4
4
  s.summary = "Blade templates for ruby"
5
5
  s.description = "A port of the Laravel blade templating engine to ruby"
6
6
  s.authors = ["Simon J"]
7
7
  s.email = "2857218+mwnciau@users.noreply.github.com"
8
8
  s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|storage)/}) }
9
- s.require_paths = ['lib']
9
+ s.require_paths = ["lib"]
10
10
  s.homepage = "https://rubygems.org/gems/rblade"
11
11
  s.license = "MIT"
12
12
  s.required_ruby_version = ">= 3.0.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rblade
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon J
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-22 00:00:00.000000000 Z
11
+ date: 2024-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -74,6 +74,7 @@ extra_rdoc_files: []
74
74
  files:
75
75
  - ".gitignore"
76
76
  - ".standard.yml"
77
+ - CHANGELOG.md
77
78
  - Gemfile
78
79
  - Rakefile
79
80
  - do
@@ -88,11 +89,16 @@ files:
88
89
  - lib/rblade/compiler/statements/compiles_conditionals.rb
89
90
  - lib/rblade/compiler/statements/compiles_inline_ruby.rb
90
91
  - lib/rblade/compiler/statements/compiles_loops.rb
92
+ - lib/rblade/compiler/statements/compiles_props.rb
91
93
  - lib/rblade/compiler/statements/compiles_stacks.rb
92
94
  - lib/rblade/compiler/tokenizes_components.rb
93
95
  - lib/rblade/compiler/tokenizes_statements.rb
94
96
  - lib/rblade/component_store.rb
97
+ - lib/rblade/helpers/attributes_manager.rb
98
+ - lib/rblade/helpers/class_manager.rb
95
99
  - lib/rblade/helpers/stack_manager.rb
100
+ - lib/rblade/helpers/style_manager.rb
101
+ - lib/rblade/helpers/tokenizer.rb
96
102
  - lib/rblade/rails_template.rb
97
103
  - lib/rblade/railtie.rb
98
104
  - rblade.gemspec