rblade 0.1.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 +7 -0
- data/.gitignore +2 -0
- data/.standard.yml +3 -0
- data/Gemfile +3 -0
- data/Rakefile +6 -0
- data/do +94 -0
- data/docker-compose.yml +7 -0
- data/lib/rblade/compiler/compiles_comments.rb +12 -0
- data/lib/rblade/compiler/compiles_components.rb +75 -0
- data/lib/rblade/compiler/compiles_echos.rb +59 -0
- data/lib/rblade/compiler/compiles_ruby.rb +57 -0
- data/lib/rblade/compiler/compiles_statements.rb +103 -0
- data/lib/rblade/compiler/statements/compiles_conditionals.rb +93 -0
- data/lib/rblade/compiler/statements/compiles_inline_ruby.rb +17 -0
- data/lib/rblade/compiler/statements/compiles_loops.rb +109 -0
- data/lib/rblade/compiler/statements/compiles_stacks.rb +65 -0
- data/lib/rblade/compiler/tokenizes_components.rb +187 -0
- data/lib/rblade/compiler/tokenizes_statements.rb +171 -0
- data/lib/rblade/compiler.rb +60 -0
- data/lib/rblade/component_store.rb +42 -0
- data/lib/rblade/helpers/stack_manager.rb +58 -0
- data/lib/rblade/rails_template.rb +14 -0
- data/lib/rblade/railtie.rb +14 -0
- data/lib/rblade.rb +2 -0
- data/rblade.gemspec +18 -0
- metadata +122 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
module RBlade
|
2
|
+
class CompilesStatements
|
3
|
+
class CompilesStacks
|
4
|
+
def initialize
|
5
|
+
@push_counter = 0
|
6
|
+
end
|
7
|
+
|
8
|
+
def compileStack args
|
9
|
+
if args&.count != 1
|
10
|
+
raise StandardError.new "Stack statement: wrong number of arguments (given #{args&.count}, expecting 1)"
|
11
|
+
end
|
12
|
+
|
13
|
+
"RBlade::StackManager.initialize(#{args[0]}, _out);_stacks.push(#{args[0]});_out = '';"
|
14
|
+
end
|
15
|
+
|
16
|
+
def compilePrepend args
|
17
|
+
if args.nil? || args.count > 2
|
18
|
+
raise StandardError.new "Prepend statement: wrong number of arguments (given #{args&.count}, expecting 1 or 2)"
|
19
|
+
end
|
20
|
+
|
21
|
+
if args.count == 2
|
22
|
+
"RBlade::StackManager.prepend(#{args[0]}, #{args[1]});"
|
23
|
+
else
|
24
|
+
@push_counter += 1
|
25
|
+
|
26
|
+
"_push_#{@push_counter}_name=#{args[0]};_push_#{@push_counter}_buffer=_out;_out='';"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def compileEndPrepend args
|
31
|
+
if !args.nil?
|
32
|
+
raise StandardError.new "End prepend statement: wrong number of arguments (given #{args&.count}, expecting 0)"
|
33
|
+
end
|
34
|
+
|
35
|
+
@push_counter -= 1
|
36
|
+
|
37
|
+
"RBlade::StackManager.prepend(_push_#{@push_counter + 1}_name, _out);_out=_push_#{@push_counter + 1}_buffer;"
|
38
|
+
end
|
39
|
+
|
40
|
+
def compilePush args
|
41
|
+
if args.nil? || args.count > 2
|
42
|
+
raise StandardError.new "Push statement: wrong number of arguments (given #{args&.count}, expecting 1 or 2)"
|
43
|
+
end
|
44
|
+
|
45
|
+
if args.count == 2
|
46
|
+
"RBlade::StackManager.push(#{args[0]}, #{args[1]});"
|
47
|
+
else
|
48
|
+
@push_counter += 1
|
49
|
+
|
50
|
+
"_push_#{@push_counter}_name=#{args[0]};_push_#{@push_counter}_buffer=_out;_out='';"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def compileEndPush args
|
55
|
+
if !args.nil?
|
56
|
+
raise StandardError.new "End push statement: wrong number of arguments (given #{args&.count}, expecting 0)"
|
57
|
+
end
|
58
|
+
|
59
|
+
@push_counter -= 1
|
60
|
+
|
61
|
+
"RBlade::StackManager.push(_push_#{@push_counter + 1}_name, _out);_out=_push_#{@push_counter + 1}_buffer;"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require "ripper"
|
2
|
+
|
3
|
+
module RBlade
|
4
|
+
class TokenizesComponents
|
5
|
+
def tokenize!(tokens)
|
6
|
+
tokens.map! do |token|
|
7
|
+
next(token) if token.type != :unprocessed
|
8
|
+
|
9
|
+
segments = tokenizeComponentOpeningTags token.value
|
10
|
+
|
11
|
+
i = 0
|
12
|
+
while i < segments.count
|
13
|
+
if segments[i] == "</" && segments[i + 1]&.match(/x[-:]/)
|
14
|
+
segments[i] = Token.new(type: :component_end, value: {name: segments[i + 1][2..]})
|
15
|
+
|
16
|
+
segments.delete_at i + 1
|
17
|
+
i += 1
|
18
|
+
elsif segments[i] == "<" && segments[i + 1]&.match(/x[-:]/)
|
19
|
+
name = segments[i + 1][2..]
|
20
|
+
raw_attributes = (segments[i + 2] != ">") ? tokenizeAttributes(segments[i + 2]) : nil
|
21
|
+
|
22
|
+
attributes = processAttributes raw_attributes
|
23
|
+
|
24
|
+
if raw_attributes.nil?
|
25
|
+
segments.delete_at i + 1
|
26
|
+
else
|
27
|
+
while segments[i + 1] != ">" && segments[i + 1] != "/>"
|
28
|
+
segments.delete_at i + 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
token_type = (segments[i + 1] == "/>") ? :component : :component_start
|
33
|
+
segments[i] = Token.new(type: token_type, value: {name:, attributes:})
|
34
|
+
segments.delete_at i + 1
|
35
|
+
|
36
|
+
i += 1
|
37
|
+
elsif !segments[i].nil? && segments[i] != ""
|
38
|
+
segments[i] = Token.new(type: :unprocessed, value: segments[i])
|
39
|
+
|
40
|
+
i += 1
|
41
|
+
else
|
42
|
+
segments.delete_at i
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
segments
|
47
|
+
end.flatten!
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def processAttributes raw_attributes
|
53
|
+
attributes = []
|
54
|
+
i = 0
|
55
|
+
while i < raw_attributes.count
|
56
|
+
name = raw_attributes[i]
|
57
|
+
if name == "@class" || name == "@style"
|
58
|
+
attributes.push({type: name[1..], value: raw_attributes[i + 1][1..-2]})
|
59
|
+
i += 2
|
60
|
+
elsif name[0..1] == "{{"
|
61
|
+
attributes.push({type: "attributes", value: raw_attributes[i + 1][2..-2]})
|
62
|
+
i += 1
|
63
|
+
else
|
64
|
+
attribute = {name:}
|
65
|
+
|
66
|
+
if raw_attributes[i + 1] == "="
|
67
|
+
attribute[:value] = raw_attributes[i + 2]
|
68
|
+
i += 3
|
69
|
+
else
|
70
|
+
i += 1
|
71
|
+
end
|
72
|
+
|
73
|
+
# The "::" at the start of attributes is used to escape attribute names beginning with ":"
|
74
|
+
if name[0..1] == "::"
|
75
|
+
attribute[:type] = "string"
|
76
|
+
attribute[:name].delete_prefix! ":"
|
77
|
+
attributes.push(attribute)
|
78
|
+
next
|
79
|
+
end
|
80
|
+
|
81
|
+
if name[0] == ":"
|
82
|
+
attribute[:type] = attribute[:value].nil? ? "pass_through" : "ruby"
|
83
|
+
attribute[:name].delete_prefix! ":"
|
84
|
+
attributes.push(attribute)
|
85
|
+
next
|
86
|
+
end
|
87
|
+
|
88
|
+
attribute[:type] = if attribute[:value].nil?
|
89
|
+
"empty"
|
90
|
+
else
|
91
|
+
"string"
|
92
|
+
end
|
93
|
+
attributes.push(attribute)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
attributes
|
98
|
+
end
|
99
|
+
|
100
|
+
def tokenizeComponentOpeningTags value
|
101
|
+
value.split(%r/
|
102
|
+
# Opening and self-closing tags
|
103
|
+
(?:
|
104
|
+
(<)
|
105
|
+
\s*
|
106
|
+
(x[-\:][\w\-\:\.]*)
|
107
|
+
((?:
|
108
|
+
\s+
|
109
|
+
(?:
|
110
|
+
(?:
|
111
|
+
@class(\( (?: (?>[^()]+) | \g<-1> )* \))
|
112
|
+
)
|
113
|
+
|
|
114
|
+
(?:
|
115
|
+
@style(\( (?: (?>[^()]+)| \g<-1> )* \))
|
116
|
+
)
|
117
|
+
|
|
118
|
+
(
|
119
|
+
\{\{\s*attributes(?:[^}]+?)?\s*\}\}
|
120
|
+
)
|
121
|
+
|
|
122
|
+
(?:
|
123
|
+
[\w\-:.@%]+
|
124
|
+
(?:
|
125
|
+
=
|
126
|
+
(?:
|
127
|
+
"[^"]*"
|
128
|
+
|
|
129
|
+
'[^\']*'
|
130
|
+
|
|
131
|
+
[^'"=<>\s]+
|
132
|
+
)
|
133
|
+
)?
|
134
|
+
)
|
135
|
+
)
|
136
|
+
)*)
|
137
|
+
\s*
|
138
|
+
(?<![=\-])
|
139
|
+
(\/?>)
|
140
|
+
)
|
141
|
+
|
|
142
|
+
# Closing tags
|
143
|
+
(?:
|
144
|
+
(<\/)
|
145
|
+
\s*
|
146
|
+
(x[-\:][\w\-\:\.]*)
|
147
|
+
\s*
|
148
|
+
>
|
149
|
+
)
|
150
|
+
/x)
|
151
|
+
end
|
152
|
+
|
153
|
+
def tokenizeAttributes segment
|
154
|
+
segment.scan(%r/
|
155
|
+
(?<=\s|^)
|
156
|
+
(?:
|
157
|
+
(?:
|
158
|
+
(@class)(\( (?: (?>[^()]+) | \g<-1> )* \))
|
159
|
+
)
|
160
|
+
|
|
161
|
+
(?:
|
162
|
+
(@style)(\( (?: (?>[^()]+)| \g<-1> )* \))
|
163
|
+
)
|
164
|
+
|
|
165
|
+
(?:
|
166
|
+
(\{\{)\s*attributes([^}]+?)?\s*(\}\})
|
167
|
+
)
|
168
|
+
|
|
169
|
+
(?:
|
170
|
+
([\w\-:.@%]+)
|
171
|
+
(?:
|
172
|
+
(=)
|
173
|
+
(?:
|
174
|
+
"([^"]*)"
|
175
|
+
|
|
176
|
+
'([^\']*)'
|
177
|
+
|
|
178
|
+
([^'"=<>\s]+)
|
179
|
+
)
|
180
|
+
)?
|
181
|
+
)
|
182
|
+
)
|
183
|
+
(?=\s|$)
|
184
|
+
/x).flatten.compact
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require "ripper"
|
2
|
+
|
3
|
+
module RBlade
|
4
|
+
class TokenizesStatements
|
5
|
+
def tokenize!(tokens)
|
6
|
+
tokens.map! do |token|
|
7
|
+
next(token) if token.type != :unprocessed
|
8
|
+
|
9
|
+
segments = token.value.split(/
|
10
|
+
(?:^|[\b\s])
|
11
|
+
(@@?)
|
12
|
+
(\w+(?:::\w+)?)
|
13
|
+
(?:[ \t]*
|
14
|
+
(\(.*?\))
|
15
|
+
)?/mx)
|
16
|
+
|
17
|
+
parseSegments! segments
|
18
|
+
end.flatten!
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def parseSegments! segments
|
24
|
+
i = 0
|
25
|
+
while i < segments.count
|
26
|
+
segment = segments[i]
|
27
|
+
|
28
|
+
# The @ symbol is used to escape blade directives so we return it unprocessed
|
29
|
+
if segment == "@@"
|
30
|
+
segments[i] = Token.new(type: :unprocessed, value: segment[1..] + segments[i + 1])
|
31
|
+
segments.delete_at i + 1
|
32
|
+
|
33
|
+
i += 1
|
34
|
+
elsif segment == "@"
|
35
|
+
tokenizeStatement! segments, i
|
36
|
+
|
37
|
+
# Remove trailing whitespace if it exists, but don't double dip when another statement follows
|
38
|
+
if !segments[i + 1].nil? && segments[i + 1].match(/^\s/) && (segments[i + 1].length > 1 || segments[i + 2].nil?)
|
39
|
+
segments[i + 1].slice! 0, 1
|
40
|
+
end
|
41
|
+
|
42
|
+
i += 1
|
43
|
+
elsif !segments[i].nil? && segments[i] != ""
|
44
|
+
segments[i] = Token.new(type: :unprocessed, value: segments[i])
|
45
|
+
|
46
|
+
i += 1
|
47
|
+
else
|
48
|
+
segments.delete_at i
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
segments
|
53
|
+
end
|
54
|
+
|
55
|
+
def tokenizeStatement!(segments, i)
|
56
|
+
statement_data = {name: segments[i + 1]}
|
57
|
+
segments.delete_at i + 1
|
58
|
+
|
59
|
+
if segments.count > i + 1 && segments[i + 1][0] == "("
|
60
|
+
arguments = tokenizeArguments! segments, i + 1
|
61
|
+
|
62
|
+
if !arguments.nil?
|
63
|
+
statement_data[:arguments] = arguments
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
segments[i] = Token.new(type: :statement, value: statement_data)
|
68
|
+
end
|
69
|
+
|
70
|
+
def tokenizeArguments!(segments, segment_index)
|
71
|
+
success = expandSegmentToEndParenthesis! segments, segment_index
|
72
|
+
|
73
|
+
# If no matching parentheses were found, so we combine the argument string with the next segment
|
74
|
+
if !success
|
75
|
+
if !segments[segment_index + 1].nil?
|
76
|
+
segments[segment_index] <<= segments[segment_index + 1]
|
77
|
+
segments.delete_at segment_index + 1
|
78
|
+
end
|
79
|
+
|
80
|
+
return nil
|
81
|
+
end
|
82
|
+
|
83
|
+
arguments = extractArguments segments[segment_index]
|
84
|
+
segments.delete_at segment_index
|
85
|
+
|
86
|
+
arguments
|
87
|
+
end
|
88
|
+
|
89
|
+
def expandSegmentToEndParenthesis! segments, segment_index
|
90
|
+
parentheses_difference = 0
|
91
|
+
tokens = nil
|
92
|
+
|
93
|
+
loop do
|
94
|
+
tokens = Ripper.lex(segments[segment_index]).map { |token| token[1] }
|
95
|
+
parentheses_difference = tokens.count(:on_lparen) - tokens.count(:on_rparen)
|
96
|
+
|
97
|
+
break if parentheses_difference.zero? || segments[segment_index + 1].nil?
|
98
|
+
|
99
|
+
index = segments[segment_index + 1].each_char.find_index { |c| c == ")" && (parentheses_difference -= 1).zero? }
|
100
|
+
|
101
|
+
if index.nil?
|
102
|
+
segments[segment_index] << segments[segment_index + 1]
|
103
|
+
segments.delete_at segment_index + 1
|
104
|
+
else
|
105
|
+
segments[segment_index] << segments[segment_index + 1].slice!(0..index)
|
106
|
+
end
|
107
|
+
|
108
|
+
break if segments[segment_index + 1].nil?
|
109
|
+
end
|
110
|
+
|
111
|
+
parentheses_difference.zero?
|
112
|
+
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
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "rblade/compiler/compiles_comments"
|
2
|
+
require "rblade/compiler/compiles_components"
|
3
|
+
require "rblade/compiler/compiles_echos"
|
4
|
+
require "rblade/compiler/compiles_ruby"
|
5
|
+
require "rblade/compiler/compiles_statements"
|
6
|
+
require "rblade/compiler/tokenizes_components"
|
7
|
+
require "rblade/compiler/tokenizes_statements"
|
8
|
+
|
9
|
+
Token = Struct.new(:type, :value)
|
10
|
+
|
11
|
+
if !defined?(h)
|
12
|
+
require "erb/escape"
|
13
|
+
define_method(:h, ERB::Escape.instance_method(:html_escape))
|
14
|
+
end
|
15
|
+
|
16
|
+
module RBlade
|
17
|
+
def self.escape_quotes string
|
18
|
+
string.gsub(/['\\\x0]/, '\\\\\0')
|
19
|
+
end
|
20
|
+
|
21
|
+
class Compiler
|
22
|
+
def self.compileString(string_template)
|
23
|
+
tokens = [Token.new(:unprocessed, string_template)]
|
24
|
+
|
25
|
+
CompilesComments.new.compile! tokens
|
26
|
+
CompilesEchos.new.compile! tokens
|
27
|
+
CompilesRuby.new.compile! tokens
|
28
|
+
TokenizesComponents.new.tokenize! tokens
|
29
|
+
TokenizesStatements.new.tokenize! tokens
|
30
|
+
CompilesStatements.new.compile! tokens
|
31
|
+
CompilesComponents.new.compile! tokens
|
32
|
+
|
33
|
+
compileTokens tokens
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.compileAttributeString(string_template)
|
37
|
+
tokens = [Token.new(:unprocessed, string_template)]
|
38
|
+
|
39
|
+
CompilesRuby.compile! tokens
|
40
|
+
CompilesComments.compile!(tokens)
|
41
|
+
CompilesEchos.compile!(tokens)
|
42
|
+
|
43
|
+
compileTokens tokens
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.compileTokens tokens
|
47
|
+
output = ""
|
48
|
+
|
49
|
+
tokens.each do |token, cake|
|
50
|
+
if token.type == :unprocessed || token.type == :raw_text
|
51
|
+
output << "_out<<'" << RBlade.escape_quotes(token.value) << "';"
|
52
|
+
else
|
53
|
+
output << token.value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
output
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "rblade/compiler"
|
2
|
+
|
3
|
+
module RBlade
|
4
|
+
FILE_EXTENSIONS = [".blade", ".html.blade"]
|
5
|
+
|
6
|
+
class ComponentStore
|
7
|
+
def self.fetchComponent name
|
8
|
+
namespace = nil
|
9
|
+
path = name
|
10
|
+
|
11
|
+
if name.match '::'
|
12
|
+
namespace, path = name.split('::')
|
13
|
+
end
|
14
|
+
|
15
|
+
path.gsub! '.', '/'
|
16
|
+
|
17
|
+
@@templatePaths[namespace]&.each do |base_path|
|
18
|
+
FILE_EXTENSIONS.each do |extension|
|
19
|
+
if File.exist? base_path + path + extension
|
20
|
+
return RBlade::Compiler.compileString File.read(base_path + path + extension)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
raise StandardError.new "Unknown component #{name}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.add_path path, namespace = nil
|
29
|
+
path = path.to_s
|
30
|
+
if !path.end_with? "/"
|
31
|
+
path = path + "/"
|
32
|
+
end
|
33
|
+
|
34
|
+
@@templatePaths[namespace] ||= []
|
35
|
+
@@templatePaths[namespace] << path
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
@@templatePaths = {}
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module RBlade
|
2
|
+
class StackManager
|
3
|
+
def self.initialize stack_name, before_stack
|
4
|
+
@@stacks[stack_name] ||= Stack.new
|
5
|
+
@@stacks[stack_name].set_before_stack before_stack
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.clear
|
9
|
+
@@stacks = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.push stack_name, code
|
13
|
+
@@stacks[stack_name] ||= Stack.new
|
14
|
+
@@stacks[stack_name].push code.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.prepend stack_name, code
|
18
|
+
@@stacks[stack_name] ||= Stack.new
|
19
|
+
@@stacks[stack_name].prepend code.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.get(stacks)
|
23
|
+
stacks.map do |name|
|
24
|
+
out = @@stacks[name].to_s
|
25
|
+
@@stacks.delete name
|
26
|
+
|
27
|
+
out
|
28
|
+
end.join
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
@@stacks = {}
|
34
|
+
|
35
|
+
class Stack
|
36
|
+
def initialize
|
37
|
+
@prepends = ""
|
38
|
+
@stack = ""
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_before_stack before_stack
|
42
|
+
@before_stack = before_stack
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
"#{@before_stack}#{@prepends}#{@stack}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def push code
|
50
|
+
@stack << code
|
51
|
+
end
|
52
|
+
|
53
|
+
def prepend code
|
54
|
+
@prepends << code
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "rails"
|
2
|
+
require "rblade/compiler"
|
3
|
+
require "rblade/helpers/stack_manager"
|
4
|
+
|
5
|
+
module RBlade
|
6
|
+
class RailsTemplate
|
7
|
+
def call(template, source = nil)
|
8
|
+
RBlade::StackManager.clear
|
9
|
+
setup = "foo = 'FOO';bar = 'BAR';_out='';_stacks=[];"
|
10
|
+
setdown = "RBlade::StackManager.get(_stacks) + _out"
|
11
|
+
setup + RBlade::Compiler.compileString(source || template.source) + setdown
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "rails"
|
2
|
+
require "rblade/rails_template"
|
3
|
+
require "rblade/component_store"
|
4
|
+
|
5
|
+
module RBlade
|
6
|
+
class Railtie < ::Rails::Railtie
|
7
|
+
initializer :rblade, before: :load_config_initializers do |app|
|
8
|
+
ActionView::Template.register_template_handler(:blade, RBlade::RailsTemplate.new)
|
9
|
+
|
10
|
+
RBlade::ComponentStore.add_path(Rails.root.join("app", "views", "components"))
|
11
|
+
RBlade::ComponentStore.add_path(Rails.root.join("app", "views", "layouts"), "layout")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/rblade.rb
ADDED
data/rblade.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "rblade"
|
3
|
+
s.version = "0.1.0"
|
4
|
+
s.summary = "Blade templates for ruby"
|
5
|
+
s.description = "A port of the Laravel blade templating engine to ruby"
|
6
|
+
s.authors = ["Simon J"]
|
7
|
+
s.email = "2857218+mwnciau@users.noreply.github.com"
|
8
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|storage)/}) }
|
9
|
+
s.require_paths = ['lib']
|
10
|
+
s.homepage = "https://rubygems.org/gems/rblade"
|
11
|
+
s.license = "MIT"
|
12
|
+
s.required_ruby_version = ">= 3.0.0"
|
13
|
+
|
14
|
+
s.add_development_dependency "minitest", "~> 5.0"
|
15
|
+
s.add_development_dependency "minitest-reporters", "~> 1.1"
|
16
|
+
s.add_development_dependency "standard", "~> 1.3"
|
17
|
+
s.add_development_dependency "rails", "~> 7.0"
|
18
|
+
end
|