rblade 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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