rblade 3.0.0 → 3.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 +4 -4
- data/.github/workflows/ruby.yml +4 -0
- data/CHANGELOG.md +7 -0
- data/README.md +78 -4
- data/Rakefile +1 -1
- data/do +10 -10
- data/lib/rblade/compiler/compiles_comments.rb +1 -1
- data/lib/rblade/compiler/compiles_components.rb +2 -2
- data/lib/rblade/compiler/statements/compiles_component_helpers.rb +0 -2
- data/lib/rblade/compiler/tokenizes_statements.rb +136 -126
- data/lib/rblade/component_store.rb +1 -1
- data/lib/rblade/helpers/regular_expressions.rb +135 -0
- data/lib/rblade/railtie.rb +16 -6
- data/rblade.gemspec +2 -2
- metadata +5 -5
- data/lib/rblade/helpers/tokenizer.rb +0 -65
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 565a823219a85db94f6fa31e42de52cab5d1de562c89c42243a0105d54eab581
|
4
|
+
data.tar.gz: 484ca84492233eabd1c61a534be991cfdb489273751e4e43a1b5ac400965b867
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3fa94d7c7c3f19f3671dd346bffedbee1fdaaf3b93ce0928cba1b6bb7d24a7c7da83c016863df9aa8c5cbdd0d5519d65efef7aa18a59a77a7861764e8188f03
|
7
|
+
data.tar.gz: 29325e82f35eb1e6b949b49684ea5c78cea910ffeeacf18ef012c39ff6cbe2a73e26bd47d1753734cc712421b19a958dab54224c024c25ca580b53b34b6dcb9a
|
data/.github/workflows/ruby.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 3.1.0 [2025-04-02]
|
2
|
+
- Add ability to use slots in dynamic components and the component view helper method
|
3
|
+
- Change statement matching to use regular expressions to improve compile performance
|
4
|
+
|
5
|
+
## 3.0.1 [2025-03-18]
|
6
|
+
- Fix dynamic components not working when `RBlade.component_helper_method_name` is set
|
7
|
+
|
1
8
|
## 3.0.0 [2025-03-18]
|
2
9
|
- Add ability to add raw directive handlers that add ruby code to the template
|
3
10
|
- Add `RBlade.direct_component_rendering` option to allow RBlade components to be rendered directly
|
data/README.md
CHANGED
@@ -62,6 +62,7 @@ For a quick overview of RBlade's capabilities, refer to the [reference file](REF
|
|
62
62
|
+ [Method Field](#method-field)
|
63
63
|
* [Stacks](#stacks)
|
64
64
|
* [Integrating RBlade With Other Templates](#rblade-integration)
|
65
|
+
* [Limitations](#limitations)
|
65
66
|
|
66
67
|
<a name="displaying-data"></a>
|
67
68
|
## Displaying Data
|
@@ -976,7 +977,40 @@ If you would like to prepend content onto the beginning of a stack, you should u
|
|
976
977
|
<a name="rblade-integration"></a>
|
977
978
|
## Integrating RBlade With Other Templates
|
978
979
|
|
979
|
-
You might want to use RBlade components within other templates, e.g. if you are using a component library that uses them.
|
980
|
+
You might want to use RBlade components within other templates, e.g. if you are using a component library that uses them. The `component` helper method lets you easily include RBlade components in your ERB (or other) templates:
|
981
|
+
|
982
|
+
```erb
|
983
|
+
<%= component "button", class: "mt-4", colour: "green" do %>
|
984
|
+
<b>My button</b>
|
985
|
+
<% end %>
|
986
|
+
```
|
987
|
+
|
988
|
+
The component method also passes a proc that can be used to specify slots:
|
989
|
+
|
990
|
+
```erb
|
991
|
+
<%= component "card" do |slot| %>
|
992
|
+
<%# The "class" attribute and the contents of the block will be passed in as the "heading" attribute to the card component %>
|
993
|
+
<% slot :heading, class: "font-bold" do %>
|
994
|
+
Heading
|
995
|
+
<% end %>
|
996
|
+
Content
|
997
|
+
<% end %>
|
998
|
+
```
|
999
|
+
|
1000
|
+
If preferred, the `component` method can be renamed using the `RBlade.component_helper_method_name` option:
|
1001
|
+
|
1002
|
+
```ruby
|
1003
|
+
# config/initializers/rblade.rb
|
1004
|
+
|
1005
|
+
# Change the name of the component helper method
|
1006
|
+
RBlade.component_helper_method_name = :rblade_component
|
1007
|
+
|
1008
|
+
|
1009
|
+
# app/views/home/show.erb
|
1010
|
+
<%= rblade_component "my_component" %>
|
1011
|
+
```
|
1012
|
+
|
1013
|
+
By default, RBlade layouts are not compatible with other templates, and components cannot be rendered directly, but this can be enabled using the `direct_component_rendering` option.
|
980
1014
|
|
981
1015
|
```ruby
|
982
1016
|
# config/initializers/rblade.rb
|
@@ -985,13 +1019,53 @@ You might want to use RBlade components within other templates, e.g. if you are
|
|
985
1019
|
RBlade.direct_component_rendering = true
|
986
1020
|
```
|
987
1021
|
|
988
|
-
Once enabled, RBlade components can be rendered using `render
|
1022
|
+
Once enabled, RBlade components can be used as layouts for ERB templates, or rendered directly using `render`. Block contents are passed to the component in the `slot` variable, `attributes` is initialized using `local_assigns`, and the `@props` directive will look for content set using `content_for`.
|
989
1023
|
|
990
1024
|
```erb
|
991
1025
|
<%= render template: "components/button", locals: {class: "mt-4", slot: capture do %>
|
992
|
-
|
1026
|
+
<% content_for: :title, "My title" %>
|
1027
|
+
<b>My content</b>
|
993
1028
|
<% end } %>
|
994
1029
|
```
|
995
1030
|
|
996
1031
|
> [!NOTE]
|
997
|
-
>
|
1032
|
+
> Using the `component` helper instead of RBlade's component syntax does not take advantage of RBlade's component caching
|
1033
|
+
|
1034
|
+
|
1035
|
+
<a name="limitations"></a>
|
1036
|
+
## Limitations
|
1037
|
+
|
1038
|
+
### Regular expressions in RBlade directives
|
1039
|
+
|
1040
|
+
Regular expression literals may cause improper bracket matching in RBlade directive. To work around this, use the `%r` percent literal syntax.
|
1041
|
+
|
1042
|
+
```rblade
|
1043
|
+
{{-- Parentheses in regular expressions may cause incorrect matching --}}
|
1044
|
+
@ruby(/\)/)
|
1045
|
+
|
1046
|
+
{{-- A workaround is to use the alternative %r syntax --}}
|
1047
|
+
@ruby(%r/\)/)
|
1048
|
+
```
|
1049
|
+
|
1050
|
+
### `<<` in RBlade directives
|
1051
|
+
|
1052
|
+
The append operator is assumed to be a HEREDOC if followed immediately by a word character (a-z, A-Z, _), `-` or `~`.
|
1053
|
+
|
1054
|
+
```rblade
|
1055
|
+
{{-- If using `<<` to append, add a space after the operator --}}
|
1056
|
+
@ruby(string << "extra")
|
1057
|
+
```
|
1058
|
+
|
1059
|
+
### End brackets in print statements
|
1060
|
+
|
1061
|
+
Print statements cannot contain their end bracket in strings or other literals:
|
1062
|
+
|
1063
|
+
```rblade
|
1064
|
+
# These examples will cause a syntax error
|
1065
|
+
{{ '}}' }}
|
1066
|
+
<%= 'foo%>' %>
|
1067
|
+
|
1068
|
+
# A workaround is to use the alternative syntax
|
1069
|
+
<%= 'foo}}' %>
|
1070
|
+
{{ 'foo%>' }}
|
1071
|
+
```
|
data/Rakefile
CHANGED
data/do
CHANGED
@@ -43,36 +43,36 @@ fi
|
|
43
43
|
|
44
44
|
if [ "$1" == "run" ] || [ "$1" == "r" ]
|
45
45
|
then
|
46
|
-
echo Running: ${DOCKER_COMPOSE_COMMAND} run blade ${@:2}
|
47
|
-
${DOCKER_COMPOSE_COMMAND} run blade "${@:2}"
|
46
|
+
echo Running: ${DOCKER_COMPOSE_COMMAND} run --remove-orphans --rm blade ${@:2}
|
47
|
+
${DOCKER_COMPOSE_COMMAND} run --remove-orphans --rm blade "${@:2}"
|
48
48
|
exit 0
|
49
49
|
fi
|
50
50
|
|
51
51
|
if [ "$1" == "rake" ] || [ "$1" == "rk" ]
|
52
52
|
then
|
53
|
-
echo Running: ${DOCKER_COMPOSE_COMMAND} run blade rake ${@:2}
|
54
|
-
${DOCKER_COMPOSE_COMMAND} run blade rake "${@:2}"
|
53
|
+
echo Running: ${DOCKER_COMPOSE_COMMAND} run --remove-orphans --rm blade rake ${@:2}
|
54
|
+
${DOCKER_COMPOSE_COMMAND} run --remove-orphans --rm blade rake "${@:2}"
|
55
55
|
exit 0
|
56
56
|
fi
|
57
57
|
|
58
58
|
if [ "$1" == "cs" ] || [ "$1" == "c" ]
|
59
59
|
then
|
60
|
-
echo Running: ${DOCKER_COMPOSE_COMMAND} run blade rubocop "${@:2}"
|
61
|
-
${DOCKER_COMPOSE_COMMAND} run blade rubocop "${@:2}"
|
60
|
+
echo Running: ${DOCKER_COMPOSE_COMMAND} run --remove-orphans --rm blade rubocop "${@:2}"
|
61
|
+
${DOCKER_COMPOSE_COMMAND} run --remove-orphans --rm blade rubocop "${@:2}"
|
62
62
|
exit 0
|
63
63
|
fi
|
64
64
|
|
65
65
|
if [ "$1" == "cs:fix" ]
|
66
66
|
then
|
67
|
-
echo Running: ${DOCKER_COMPOSE_COMMAND} run blade rubocop --autocorrect "${@:2}"
|
68
|
-
${DOCKER_COMPOSE_COMMAND} run blade rubocop --autocorrect "${@:2}"
|
67
|
+
echo Running: ${DOCKER_COMPOSE_COMMAND} run --remove-orphans --rm blade rubocop --autocorrect "${@:2}"
|
68
|
+
${DOCKER_COMPOSE_COMMAND} run --remove-orphans --rm blade rubocop --autocorrect "${@:2}"
|
69
69
|
exit 0
|
70
70
|
fi
|
71
71
|
|
72
72
|
if [ "$1" == "test" ] || [ "$1" == "t" ]
|
73
73
|
then
|
74
|
-
echo Running: ${DOCKER_COMPOSE_COMMAND} run blade rake test "${@:2}"
|
75
|
-
${DOCKER_COMPOSE_COMMAND} run blade rake test "${@:2}"
|
74
|
+
echo Running: ${DOCKER_COMPOSE_COMMAND} run --remove-orphans --rm blade rake test "${@:2}"
|
75
|
+
${DOCKER_COMPOSE_COMMAND} run --remove-orphans --rm blade rake test "${@:2}"
|
76
76
|
exit 0
|
77
77
|
fi
|
78
78
|
|
@@ -45,7 +45,7 @@ module RBlade
|
|
45
45
|
attributes = compile_attributes token.value[:attributes]
|
46
46
|
|
47
47
|
if component[:name].start_with? "slot::"
|
48
|
-
"_slot.call(:'#{RBlade.escape_quotes(component[:name].delete_prefix("slot::"))}', {#{attributes.join(",")}}) do;"
|
48
|
+
"_slot.call(:'#{RBlade.escape_quotes(component[:name].delete_prefix("slot::"))}', **{#{attributes.join(",")}}) do;"
|
49
49
|
else
|
50
50
|
"#{@component_store.component(component[:name])}(RBlade::AttributesManager.new({#{attributes.join(",")}})) do |_slot|;"
|
51
51
|
end
|
@@ -67,7 +67,7 @@ module RBlade
|
|
67
67
|
|
68
68
|
attributes = compile_attributes token.value[:attributes]
|
69
69
|
|
70
|
-
"@output_buffer.raw_buffer
|
70
|
+
"@output_buffer.raw_buffer<<#{RBlade.component_helper_method_name}(#{component_value}, '#{RBlade.escape_quotes(@component_store.current_view_name)}', #{attributes.join ","}) do |_slot|;"
|
71
71
|
end
|
72
72
|
|
73
73
|
def compile_token_end(token)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "rblade/helpers/
|
3
|
+
require "rblade/helpers/regular_expressions"
|
4
4
|
require "ripper"
|
5
5
|
|
6
6
|
module RBlade
|
@@ -9,161 +9,171 @@ module RBlade
|
|
9
9
|
tokens.map! do |token|
|
10
10
|
next(token) if token.type != :unprocessed
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
current_match_id = nil
|
13
|
+
segments = []
|
14
|
+
token.value.split(/
|
15
|
+
\s?(?<!\w|@)
|
14
16
|
(?:
|
15
17
|
(?:
|
16
|
-
(
|
17
|
-
(
|
18
|
+
(?<escaped_at>@@)
|
19
|
+
(?=\w++[!\?]?(?!\w))
|
18
20
|
)
|
19
21
|
|
|
20
22
|
(?:
|
21
|
-
|
22
|
-
(
|
23
|
-
(
|
24
|
-
|
23
|
+
@
|
24
|
+
(?# Statement name )
|
25
|
+
(?<statement_name>\w++[!\?]?)
|
26
|
+
(?# Optional parameters )
|
27
|
+
(?:
|
28
|
+
[ \t]*+
|
29
|
+
(?# Matched parentheses )
|
30
|
+
(?<statement_arguments>
|
31
|
+
\(
|
32
|
+
(?:
|
33
|
+
[^()#{RegularExpressions::RUBY_STRING_CHARACTERS}]++
|
34
|
+
|
|
35
|
+
#{RegularExpressions::RUBY_STRING}
|
36
|
+
|
|
37
|
+
\g<statement_arguments>
|
38
|
+
)*+
|
39
|
+
\)
|
40
|
+
)
|
25
41
|
)?
|
26
42
|
)
|
27
43
|
)
|
28
|
-
|
29
|
-
/
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
# The @ symbol is used to escape blade directives so we return it unprocessed
|
43
|
-
if segment == "@@"
|
44
|
-
segments[i] = Token.new(type: :unprocessed, value: segment[1..] + segments[i + 1])
|
45
|
-
segments.delete_at i + 1
|
46
|
-
|
47
|
-
i += 1
|
48
|
-
elsif segment == "@"
|
49
|
-
statement_handle = segments[i + 1].downcase.tr "_", ""
|
50
|
-
if CompilesStatements.has_handler(statement_handle)
|
51
|
-
tokenize_statement! statement_handle, segments, i
|
52
|
-
handle_special_cases! segments, i
|
53
|
-
|
54
|
-
segments.delete_at(i + 1) if segments[i + 1]&.match?(/\A\s\z/)
|
55
|
-
if segments[i - 1].is_a?(Token) && segments[i - 1].type == :unprocessed && segments[i - 1].value.match?(/\A\s\z/)
|
56
|
-
segments.delete_at i - 1
|
57
|
-
i -= 1
|
44
|
+
\s?
|
45
|
+
/xo) do |before_match|
|
46
|
+
next if current_match_id == $~.object_id
|
47
|
+
current_match_id = $~.object_id
|
48
|
+
|
49
|
+
# Add the current string to the segment list
|
50
|
+
unless before_match == ""
|
51
|
+
# Skip output between case and when statements
|
52
|
+
unless segments.last&.type == :statement && segments.last&.value&.[](:name) == "case"
|
53
|
+
if segments.last && segments.last.type == :unprocessed
|
54
|
+
segments.last.value << before_match
|
55
|
+
else
|
56
|
+
segments << Token.new(type: :unprocessed, value: before_match)
|
57
|
+
end
|
58
58
|
end
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
segments.
|
59
|
+
end
|
60
|
+
next if $~.nil?
|
61
|
+
|
62
|
+
# Skip escaped statements
|
63
|
+
if $~&.[](:escaped_at) == "@@"
|
64
|
+
segment = $&
|
65
|
+
# Remove the first or second @, depending on whether there is whitespace
|
66
|
+
segment.slice!(1).inspect
|
67
|
+
if segments.last && segments.last.type == :unprocessed
|
68
|
+
segments.last.value << segment
|
69
|
+
else
|
70
|
+
segments << Token.new(type: :unprocessed, value: segment)
|
71
71
|
end
|
72
|
+
|
73
|
+
next
|
72
74
|
end
|
73
75
|
|
74
|
-
|
75
|
-
|
76
|
-
|
76
|
+
statement_handle = $~[:statement_name].downcase.tr("_", "")
|
77
|
+
unless CompilesStatements.has_handler(statement_handle)
|
78
|
+
if segments.last && segments.last.type == :unprocessed
|
79
|
+
segments.last.value << $&
|
80
|
+
else
|
81
|
+
segments << Token.new(type: :unprocessed, value: $&)
|
82
|
+
end
|
77
83
|
|
78
|
-
|
79
|
-
|
80
|
-
segments.delete_at i
|
81
|
-
end
|
82
|
-
end
|
84
|
+
next
|
85
|
+
end
|
83
86
|
|
84
|
-
|
85
|
-
end
|
87
|
+
statement_data = {name: statement_handle}
|
86
88
|
|
87
|
-
|
88
|
-
|
89
|
-
statement_data = {name: handle}
|
89
|
+
unless $~[:statement_arguments].blank?
|
90
|
+
arguments = tokenize_arguments! statement_handle, $~[:statement_arguments]
|
90
91
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
92
|
+
unless arguments.nil?
|
93
|
+
statement_data[:arguments] = arguments
|
94
|
+
end
|
95
95
|
|
96
|
-
|
97
|
-
arguments = tokenize_arguments! handle, segments, i + 1
|
96
|
+
end
|
98
97
|
|
99
|
-
|
100
|
-
statement_data[:arguments] = arguments
|
98
|
+
segments << Token.new(type: :statement, value: statement_data)
|
101
99
|
end
|
102
|
-
end
|
103
100
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
def handle_special_cases!(segments, i)
|
108
|
-
if segments[i][:value][:name] == "case"
|
109
|
-
# Remove any whitespace before a when statement
|
110
|
-
until segments[i + 1].nil? || segments[i + 1] == "@"
|
111
|
-
segments.delete_at i + 1
|
112
|
-
end
|
113
|
-
end
|
101
|
+
segments
|
102
|
+
end.flatten!
|
114
103
|
end
|
115
104
|
|
116
|
-
|
117
|
-
success = expand_segment_to_end_parenthesis! segments, segment_index
|
118
|
-
|
119
|
-
# If no matching parentheses were found, so we combine the argument string with the next segment
|
120
|
-
unless success
|
121
|
-
unless segments[segment_index + 1].nil?
|
122
|
-
segments[segment_index] <<= segments[segment_index + 1]
|
123
|
-
segments.delete_at segment_index + 1
|
124
|
-
end
|
125
|
-
|
126
|
-
return nil
|
127
|
-
end
|
105
|
+
private
|
128
106
|
|
129
|
-
|
130
|
-
argument_string
|
107
|
+
def tokenize_arguments!(statement_handle, argument_string)
|
108
|
+
argument_string.delete_prefix! "("
|
109
|
+
argument_string.delete_suffix! ")"
|
131
110
|
|
132
|
-
# Special case for the props statement: remove the wrapping braces if they exist
|
133
111
|
if statement_handle == "props"
|
134
|
-
if
|
135
|
-
|
136
|
-
|
112
|
+
# Special case for the props statement: remove wrapping braces if they exist
|
113
|
+
argument_string.delete_prefix! "{"
|
114
|
+
argument_string.delete_suffix! "}"
|
137
115
|
end
|
138
116
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
arguments
|
143
|
-
end
|
144
|
-
|
145
|
-
def expand_segment_to_end_parenthesis!(segments, segment_index)
|
146
|
-
parentheses_difference = 0
|
147
|
-
|
148
|
-
loop do
|
149
|
-
tokens = Ripper.lex(segments[segment_index]).map { |token| token[1] }
|
150
|
-
parentheses_difference = tokens.count(:on_lparen) - tokens.count(:on_rparen)
|
151
|
-
|
152
|
-
break if parentheses_difference.zero? || segments[segment_index + 1].nil?
|
153
|
-
|
154
|
-
index = segments[segment_index + 1].each_char.find_index { |c| c == ")" && (parentheses_difference -= 1).zero? }
|
117
|
+
argument_string.strip!
|
118
|
+
return nil if argument_string == ""
|
155
119
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
120
|
+
current_match_id = nil
|
121
|
+
arguments = []
|
122
|
+
argument_string.split(/
|
123
|
+
\G
|
124
|
+
(?<argument>
|
125
|
+
(?:
|
126
|
+
[^,\(\{\[#{RegularExpressions::RUBY_STRING_CHARACTERS}]++
|
127
|
+
|
|
128
|
+
#{RegularExpressions::RUBY_STRING}
|
129
|
+
|
|
130
|
+
(?<parentheses>
|
131
|
+
\(
|
132
|
+
(?:
|
133
|
+
[^\(\)#{RegularExpressions::RUBY_STRING_CHARACTERS}]++
|
134
|
+
|
|
135
|
+
\g<string>
|
136
|
+
|
|
137
|
+
\g<parentheses>
|
138
|
+
)*+
|
139
|
+
\)
|
140
|
+
)
|
141
|
+
|
|
142
|
+
(?<brackets>
|
143
|
+
\[
|
144
|
+
(?:
|
145
|
+
[^\[\]#{RegularExpressions::RUBY_STRING_CHARACTERS}]++
|
146
|
+
|
|
147
|
+
\g<string>
|
148
|
+
|
|
149
|
+
\g<brackets>
|
150
|
+
)*+
|
151
|
+
\]
|
152
|
+
)
|
153
|
+
|
|
154
|
+
(?<braces>
|
155
|
+
\{
|
156
|
+
(?:
|
157
|
+
[^\{\}#{RegularExpressions::RUBY_STRING_CHARACTERS}]++
|
158
|
+
|
|
159
|
+
\g<string>
|
160
|
+
|
|
161
|
+
\g<braces>
|
162
|
+
)*+
|
163
|
+
\}
|
164
|
+
)
|
165
|
+
)*+
|
166
|
+
)
|
167
|
+
,
|
168
|
+
/xmo, -1) do |x|
|
169
|
+
next if current_match_id == $~.object_id
|
170
|
+
current_match_id = $~.object_id
|
171
|
+
|
172
|
+
argument = ($~&.[](:argument) || x).strip
|
173
|
+
arguments << argument unless argument == ""
|
164
174
|
end
|
165
175
|
|
166
|
-
|
176
|
+
arguments
|
167
177
|
end
|
168
178
|
end
|
169
179
|
end
|
@@ -86,7 +86,7 @@ module RBlade
|
|
86
86
|
|
87
87
|
compiled_component = RBlade::Compiler.compile_string(template, self)
|
88
88
|
|
89
|
-
@component_definitions << "def self._rblade_component_#{escaped_name}(attributes,&);slot=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;"
|
89
|
+
@component_definitions << "def self._rblade_component_#{escaped_name}(attributes,&);slot=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;slot.nil?;_stacks=[];@output_buffer.raw_buffer<<@output_buffer.capture do;#{compiled_component}@output_buffer.raw_buffer.prepend(@_rblade_stack_manager.get(_stacks));end;end;"
|
90
90
|
|
91
91
|
@component_method_names[name] = "_rblade_component_#{escaped_name}"
|
92
92
|
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module RBlade::RegularExpressions
|
2
|
+
RUBY_STRING_CHARACTERS = "\"'%?<"
|
3
|
+
RUBY_STRING = /
|
4
|
+
(?<string>
|
5
|
+
(?# Interpolated strings )
|
6
|
+
"
|
7
|
+
(?:
|
8
|
+
[^#"\\]++
|
9
|
+
|
|
10
|
+
\#(?<curly>\{
|
11
|
+
(?:
|
12
|
+
[^"'{}?%]++
|
13
|
+
|
|
14
|
+
\g<string>
|
15
|
+
|
|
16
|
+
\g<curly>
|
17
|
+
)*+
|
18
|
+
\})
|
19
|
+
|
|
20
|
+
\\.
|
21
|
+
|
|
22
|
+
(?!\#\{)\#[@$]?
|
23
|
+
)*+
|
24
|
+
"
|
25
|
+
|
|
26
|
+
(?# Non interpolated strings )
|
27
|
+
'
|
28
|
+
(?:
|
29
|
+
[^'\\]++
|
30
|
+
|
|
31
|
+
\\.
|
32
|
+
)*+
|
33
|
+
'
|
34
|
+
|
|
35
|
+
(?# Non interpolated percent expressions )
|
36
|
+
%[qwis]
|
37
|
+
(?:
|
38
|
+
(?<ni_parentheses> \( (?: [^()\\]++ | \\. | \g<ni_parentheses> )*+ \) )
|
39
|
+
|
|
40
|
+
(?<ni_brackets> \[ (?: [^\[\]\\]++ | \\. | \g<ni_brackets> )*+ \] )
|
41
|
+
|
|
42
|
+
(?<ni_crocs> < (?: [^<>\\]++ | \\. | \g<ni_crocs> )*+ > )
|
43
|
+
|
|
44
|
+
(?<ni_braces> \{ (?: [^{}\\]++ | \\. | \g<ni_braces> )*+ \} )
|
45
|
+
|
|
46
|
+
(?<percent_delimiter>[\x00-\x7F&&[^a-zA-Z0-9(\[{<]])
|
47
|
+
(?:
|
48
|
+
[a-zA-Z0-9(\[{<[^\x00-\x7F]]++
|
49
|
+
|
|
50
|
+
\\.
|
51
|
+
|
|
52
|
+
(?!\k<percent_delimiter>)[^\\]
|
53
|
+
)*?
|
54
|
+
\k<percent_delimiter>
|
55
|
+
)
|
56
|
+
|
|
57
|
+
(?# Interpolated percent expressions )
|
58
|
+
%[QWIrx]?
|
59
|
+
(?:
|
60
|
+
(?<i_parentheses> \( (?: [^()\\#]++ | \#\g<curly> | \\. | \g<i_parentheses> )*+ \) )
|
61
|
+
|
|
62
|
+
(?<i_brackets> \[ (?: [^\[\]\\#]++ | \#\g<curly> | \\. | \g<i_brackets> )*+ \] )
|
63
|
+
|
|
64
|
+
(?<i_crocs> < (?: [^<>\\#]++ | \#\g<curly> | \\. | \g<i_crocs> )*+ > )
|
65
|
+
|
|
66
|
+
(?<i_braces> \{ (?: [^{}\\#]++ | \#\g<curly> | \\. | \g<i_braces> )*+ \} )
|
67
|
+
|
|
68
|
+
\g<percent_delimiter>
|
69
|
+
(?:
|
70
|
+
[a-zA-Z0-9(\[{<[^\x00-\x7F]]++
|
71
|
+
|
|
72
|
+
\#\g<curly>
|
73
|
+
|
|
74
|
+
\\.
|
75
|
+
|
|
76
|
+
(?!\k<percent_delimiter>)[^\\#]
|
77
|
+
|
|
78
|
+
(?!\#\{)\#[@$]?+
|
79
|
+
)*?
|
80
|
+
(?!\#\{)\k<percent_delimiter>
|
81
|
+
)
|
82
|
+
|
|
83
|
+
(?# Interpolated HEREDOC)
|
84
|
+
<<(?:(?<heredoc_delimiter>[a-zA-Z_]\w*+)|"\g<heredoc_delimiter>")[^\n]*+\n
|
85
|
+
(?:
|
86
|
+
(?!\k<heredoc_delimiter>$)[^\#\n]++
|
87
|
+
|
|
88
|
+
\#\g<curly>
|
89
|
+
|
|
90
|
+
(?!\#\{)\#[^\#\n]++
|
91
|
+
|
|
92
|
+
\n
|
93
|
+
)*?
|
94
|
+
\n\k<heredoc_delimiter>$
|
95
|
+
|
|
96
|
+
(?# Interpolated HEREDOC with leading spaces)
|
97
|
+
<<[-~](?:\g<heredoc_delimiter>|"\g<heredoc_delimiter>")[^\n]*+\n
|
98
|
+
(?:
|
99
|
+
(?!\k<heredoc_delimiter>$)[^\#\n]++
|
100
|
+
|
|
101
|
+
\#\g<curly>
|
102
|
+
|
|
103
|
+
(?!\#\{)\#[^\#\n]++
|
104
|
+
|
|
105
|
+
\n
|
106
|
+
)*?
|
107
|
+
\n\s*+\k<heredoc_delimiter>$
|
108
|
+
|
|
109
|
+
(?# Non-interpolated HEREDOC)
|
110
|
+
<<'\g<heredoc_delimiter>'[^\n]*+\n
|
111
|
+
(?:
|
112
|
+
(?!\k<heredoc_delimiter>$)[^\n]*+\n
|
113
|
+
)*+
|
114
|
+
\k<heredoc_delimiter>$
|
115
|
+
|
|
116
|
+
(?# Non-interpolated HEREDOC with leading spaces)
|
117
|
+
<<[-~]'\g<heredoc_delimiter>'[^\n]*+\n
|
118
|
+
(?:
|
119
|
+
(?!\s*\k<heredoc_delimiter>$)[^\n]*+\n
|
120
|
+
)*+
|
121
|
+
\s*\k<heredoc_delimiter>$
|
122
|
+
|
|
123
|
+
(?<!\w)\?.
|
124
|
+
|
|
125
|
+
(?# Consume characters that aren't string literals)
|
126
|
+
(?<=\w)\?
|
127
|
+
|
|
128
|
+
(?# A percentage sign that's not a percent literal)
|
129
|
+
%(?![qwisQWIrx]?+\g<percent_delimiter>)
|
130
|
+
)
|
131
|
+
|
|
132
|
+
(?# Consume angled brackets that aren't HEREDOCs)
|
133
|
+
<(?!<[~-]?['"a-zA-Z_])
|
134
|
+
/mx
|
135
|
+
end
|
data/lib/rblade/railtie.rb
CHANGED
@@ -23,7 +23,7 @@ module RBlade
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def setup_component_view_helper(mod)
|
26
|
-
mod.send(:define_method, RBlade.component_helper_method_name) do |component_name, current_view = nil, **attributes, &block|
|
26
|
+
mod.send(:define_method, RBlade.component_helper_method_name) do |component_name, current_view = nil, slot: "", **attributes, &block|
|
27
27
|
# If this is a relative path, prepend with the previous component name's base
|
28
28
|
if !current_view.nil? && component_name.start_with?(".")
|
29
29
|
component_name = current_view.sub(/[^\.]++\z/, "") + component_name.delete_prefix(".")
|
@@ -37,12 +37,22 @@ module RBlade
|
|
37
37
|
end
|
38
38
|
path.sub!(/(?:\.[^.]++)?\.rblade\z/, "")
|
39
39
|
|
40
|
-
|
41
|
-
slot: block.nil? ? attributes.delete(:slot) || -"" : capture(&block),
|
42
|
-
attributes: RBlade::AttributesManager.new(attributes),
|
43
|
-
}
|
40
|
+
attributes = RBlade::AttributesManager.new(attributes)
|
44
41
|
|
45
|
-
|
42
|
+
unless block.nil?
|
43
|
+
value = nil
|
44
|
+
slot = @output_buffer.capture do
|
45
|
+
value = block.call(->(name, **slot_attributes, &slot_block) do
|
46
|
+
attributes[name] = RBlade::SlotManager.new(@output_buffer.capture(&slot_block), slot_attributes)
|
47
|
+
end)
|
48
|
+
end
|
49
|
+
|
50
|
+
if slot.blank?
|
51
|
+
slot = value || ""
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
render template: path, locals: {slot: RBlade::SlotManager.new(slot), attributes:}
|
46
56
|
end
|
47
57
|
end
|
48
58
|
end
|
data/rblade.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "rblade"
|
3
|
-
s.version = "3.
|
3
|
+
s.version = "3.1.0"
|
4
4
|
s.summary = "A component-first templating engine for Rails"
|
5
5
|
s.description = "RBlade is a simple, yet powerful templating engine for Ruby on Rails, inspired by Laravel Blade."
|
6
6
|
s.authors = ["Simon J"]
|
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
s.add_development_dependency "minitest", "~> 5.0"
|
16
16
|
s.add_development_dependency "minitest-reporters", "~> 1.1"
|
17
|
-
s.add_development_dependency "standard", ">= 1.
|
17
|
+
s.add_development_dependency "standard", ">= 1.35.1"
|
18
18
|
s.add_development_dependency "rubocop", ">= 1.73"
|
19
19
|
s.add_development_dependency "rails", ">= 7.0"
|
20
20
|
s.add_development_dependency "benchmark-ips"
|
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: 3.
|
4
|
+
version: 3.1.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: 2025-
|
11
|
+
date: 2025-04-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 1.35.1
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 1.35.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rubocop
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -154,10 +154,10 @@ files:
|
|
154
154
|
- lib/rblade/component_store.rb
|
155
155
|
- lib/rblade/helpers/attributes_manager.rb
|
156
156
|
- lib/rblade/helpers/class_manager.rb
|
157
|
+
- lib/rblade/helpers/regular_expressions.rb
|
157
158
|
- lib/rblade/helpers/slot_manager.rb
|
158
159
|
- lib/rblade/helpers/stack_manager.rb
|
159
160
|
- lib/rblade/helpers/style_manager.rb
|
160
|
-
- lib/rblade/helpers/tokenizer.rb
|
161
161
|
- lib/rblade/rails_template.rb
|
162
162
|
- lib/rblade/railtie.rb
|
163
163
|
- rblade.gemspec
|
@@ -1,65 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RBlade
|
4
|
-
class Tokenizer
|
5
|
-
def self.extract_comma_separated_values(segment)
|
6
|
-
unless segment.match?(/,\s*+\z/)
|
7
|
-
# Add a comma to the end to delimit the end of the last argument
|
8
|
-
segment += ","
|
9
|
-
end
|
10
|
-
segment_lines = segment.lines
|
11
|
-
|
12
|
-
tokens = Ripper.lex segment
|
13
|
-
arguments = []
|
14
|
-
|
15
|
-
current_line = 1
|
16
|
-
current_index = 0
|
17
|
-
bracket_count = {
|
18
|
-
"[]": 0,
|
19
|
-
"{}": 0,
|
20
|
-
"()": 0,
|
21
|
-
}
|
22
|
-
tokens.each do |token|
|
23
|
-
case token[1]
|
24
|
-
when :on_lbracket
|
25
|
-
bracket_count[:[]] += 1
|
26
|
-
when :on_rbracket
|
27
|
-
bracket_count[:[]] -= 1
|
28
|
-
when :on_lbrace
|
29
|
-
bracket_count[:"{}"] += 1
|
30
|
-
when :on_rbrace
|
31
|
-
bracket_count[:"{}"] -= 1
|
32
|
-
when :on_lparen
|
33
|
-
bracket_count[:"()"] += 1
|
34
|
-
when :on_rparen
|
35
|
-
bracket_count[:"()"] -= 1
|
36
|
-
when :on_comma
|
37
|
-
if bracket_count[:[]] != 0 || bracket_count[:"{}"] != 0 || bracket_count[:"()"] != 0
|
38
|
-
next
|
39
|
-
end
|
40
|
-
|
41
|
-
argument = +""
|
42
|
-
|
43
|
-
# Concatenate all lines up to this token's line, including the tail end of the current line
|
44
|
-
if token[0][0] != current_line
|
45
|
-
(current_line...token[0][0]).each do |i|
|
46
|
-
argument << (segment_lines[i - 1].slice(current_index..-1) || "")
|
47
|
-
current_index = 0
|
48
|
-
end
|
49
|
-
current_line = token[0][0]
|
50
|
-
end
|
51
|
-
argument << segment_lines[current_line - 1].slice(current_index...token[0][1])
|
52
|
-
argument.strip!
|
53
|
-
|
54
|
-
arguments.push argument
|
55
|
-
|
56
|
-
current_index = token[0][1] + 1
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
return nil if arguments.count == 1 && arguments[0] == ""
|
61
|
-
|
62
|
-
arguments
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|