rblade 2.0.2 → 3.0.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/.rubocop.yml +24 -0
- data/CHANGELOG.md +11 -0
- data/README.md +83 -16
- data/REFERENCE.md +4 -2
- data/do +4 -4
- data/docker-compose.yml +4 -1
- data/lib/rblade/compiler/compiles_comments.rb +2 -2
- data/lib/rblade/compiler/compiles_components.rb +26 -5
- data/lib/rblade/compiler/compiles_injections.rb +81 -0
- data/lib/rblade/compiler/compiles_statements.rb +69 -64
- data/lib/rblade/compiler/compiles_verbatim.rb +1 -1
- data/lib/rblade/compiler/statements/compiles_component_helpers.rb +17 -13
- data/lib/rblade/compiler/statements/compiles_conditionals.rb +18 -18
- data/lib/rblade/compiler/statements/compiles_form.rb +8 -8
- data/lib/rblade/compiler/statements/compiles_html_attributes.rb +2 -2
- data/lib/rblade/compiler/statements/compiles_inline_ruby.rb +1 -1
- data/lib/rblade/compiler/statements/compiles_loops.rb +11 -11
- data/lib/rblade/compiler/statements/compiles_once.rb +3 -3
- data/lib/rblade/compiler/statements/compiles_stacks.rb +5 -5
- data/lib/rblade/compiler/tokenizes_components.rb +30 -31
- data/lib/rblade/compiler/tokenizes_statements.rb +29 -30
- data/lib/rblade/compiler.rb +20 -19
- data/lib/rblade/component_store.rb +20 -20
- data/lib/rblade/helpers/attributes_manager.rb +10 -9
- data/lib/rblade/helpers/class_manager.rb +1 -1
- data/lib/rblade/helpers/slot_manager.rb +2 -2
- data/lib/rblade/helpers/stack_manager.rb +3 -3
- data/lib/rblade/helpers/style_manager.rb +1 -1
- data/lib/rblade/helpers/tokenizer.rb +5 -5
- data/lib/rblade/rails_template.rb +9 -2
- data/lib/rblade/railtie.rb +34 -2
- data/rblade.gemspec +4 -1
- metadata +50 -8
- data/lib/rblade/compiler/compiles_prints.rb +0 -83
- data/lib/rblade/compiler/compiles_ruby.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5760d0a2af4a86bf0ffcc9451abb3a46357029b9f5fea33afdf8305282c3d5e3
|
4
|
+
data.tar.gz: aaf764e044528fac1287219268ed117d332fd13204eb52971de9a88402665780
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d38f0abf519963a665c5780a2e2328f4bc357455c2cbec99d49362eb76f4529ece7edd65b08ec4c9669700a2c0fca5feeade897fff938aa5c8ed64790f96c152
|
7
|
+
data.tar.gz: 4ef6e7d9bffb6b9a4360af6ffe7030d9cac15ce6b3c67da546af9739659cb68ea801f06e4ddeaa67f66ce48e4885d4b2931a090518d39b05b994592bd7db0447
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
AllCops:
|
2
|
+
DisabledByDefault: true
|
3
|
+
SuggestExtensions: false
|
4
|
+
TargetRubyVersion: 3.4
|
5
|
+
UseCache: true
|
6
|
+
|
7
|
+
inherit_gem:
|
8
|
+
standard: config/base.yml
|
9
|
+
|
10
|
+
# Enforce commas at the end of multiline arguments, hashes and arrays, improving git diffs
|
11
|
+
Style/TrailingCommaInArrayLiteral:
|
12
|
+
EnforcedStyleForMultiline: diff_comma
|
13
|
+
|
14
|
+
Style/TrailingCommaInHashLiteral:
|
15
|
+
EnforcedStyleForMultiline: diff_comma
|
16
|
+
|
17
|
+
Style/TrailingCommaInArguments:
|
18
|
+
EnforcedStyleForMultiline: comma
|
19
|
+
|
20
|
+
# Enforce standardisation of method definitions
|
21
|
+
Style/MethodDefParentheses:
|
22
|
+
Enabled: true
|
23
|
+
Naming/MethodName:
|
24
|
+
Enabled: true
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## 3.0.0 [2025-03-18]
|
2
|
+
- Add ability to add raw directive handlers that add ruby code to the template
|
3
|
+
- Add `RBlade.direct_component_rendering` option to allow RBlade components to be rendered directly
|
4
|
+
- Add `component` view helper for rendering RBlade components in other templates
|
5
|
+
- Add support for dynamic components using `<x-dynamic component="...">`
|
6
|
+
- Improve performance of regular expressions
|
7
|
+
- Refactor public RBlade methods
|
8
|
+
- Remove deprecated "_required" option for props statement values (use "required" instead)
|
9
|
+
- Update README
|
10
|
+
- Fix issue with component tokenizer hanging
|
11
|
+
|
1
12
|
## 2.0.2 [2025-03-10]
|
2
13
|
- Fix issue with empty component attributes
|
3
14
|
|
data/README.md
CHANGED
@@ -54,12 +54,14 @@ For a quick overview of RBlade's capabilities, refer to the [reference file](REF
|
|
54
54
|
- [Retrieving and Filtering Attributes](#retrieving-and-filtering-attributes)
|
55
55
|
+ [Slots](#slots)
|
56
56
|
- [Slot Attributes](#slot-attributes)
|
57
|
+
+ [Dynamic Components](#dynamic-components)
|
57
58
|
+ [Registering Additional Component Directories](#registering-additional-component-directories)
|
58
59
|
+ [Index Components](#index-components)
|
59
60
|
* [Forms](#forms)
|
60
61
|
+ [Old Input](#old-input)
|
61
62
|
+ [Method Field](#method-field)
|
62
63
|
* [Stacks](#stacks)
|
64
|
+
* [Integrating RBlade With Other Templates](#rblade-integration)
|
63
65
|
|
64
66
|
<a name="displaying-data"></a>
|
65
67
|
## Displaying Data
|
@@ -140,7 +142,7 @@ If you are displaying JavaScript variables in a large portion of your template,
|
|
140
142
|
<div class="container">
|
141
143
|
Hello, {{ name }}.
|
142
144
|
</div>
|
143
|
-
@
|
145
|
+
@endVerbatim
|
144
146
|
```
|
145
147
|
|
146
148
|
<a name="rblade-directives"></a>
|
@@ -175,7 +177,7 @@ In addition to the conditional directives above, the `@blank?`, `defined?`, `@em
|
|
175
177
|
// records is defined and is not nil
|
176
178
|
@else
|
177
179
|
// Since these directives are compiled to if statements, you can also use the @else directive
|
178
|
-
@
|
180
|
+
@endEmpty?
|
179
181
|
```
|
180
182
|
|
181
183
|
<a name="environment-directives"></a>
|
@@ -304,12 +306,12 @@ The `@class` directive conditionally adds CSS classes. The directive accepts a H
|
|
304
306
|
hasError = true;
|
305
307
|
@endRuby
|
306
308
|
|
307
|
-
<span @class(
|
309
|
+
<span @class(
|
308
310
|
"p-4": true,
|
309
311
|
"font-bold": isActive,
|
310
312
|
"text-gray-500": !isActive,
|
311
313
|
"bg-red": hasError,
|
312
|
-
|
314
|
+
)></span>
|
313
315
|
|
314
316
|
<span class="p-4 text-gray-500 bg-red"></span>
|
315
317
|
```
|
@@ -321,10 +323,10 @@ Likewise, the `@style` directive can be used to conditionally add inline CSS sty
|
|
321
323
|
isActive = true;
|
322
324
|
@endRuby
|
323
325
|
|
324
|
-
<span @style(
|
326
|
+
<span @style(
|
325
327
|
"background-color: red": true,
|
326
328
|
"font-weight: bold" => isActive,
|
327
|
-
|
329
|
+
)></span>
|
328
330
|
|
329
331
|
<span style="background-color: red; font-weight: bold;"></span>
|
330
332
|
```
|
@@ -432,9 +434,7 @@ In some situations, it's useful to embed Ruby code into your views. You can use
|
|
432
434
|
<a name="custom-directives"></a>
|
433
435
|
### Custom Directives
|
434
436
|
|
435
|
-
RBlade also allows you to define your own directives using the `RBlade.register_directive_handler`
|
436
|
-
method. When the compiler encounters the custom directive, it will call the provided block and
|
437
|
-
output the returned value.
|
437
|
+
RBlade also allows you to define your own output directives using the `RBlade.register_directive_handler` method. When the compiler encounters the custom directive, it will call the provided block and output the returned value.
|
438
438
|
|
439
439
|
```rblade
|
440
440
|
RBlade::register_directive_handler('sum') do |args|
|
@@ -446,6 +446,17 @@ end
|
|
446
446
|
@sum(1, 2, 3) -> 6
|
447
447
|
```
|
448
448
|
|
449
|
+
The `RBlade.register_raw_directive_handler` method allows you to register a directive that outputs raw Ruby code into the template. When the compiler encounters the custom directive, it will call the provided block insert the returned value into the template. The returned ruby code must end in a semicolon, `;`.
|
450
|
+
|
451
|
+
```rblade
|
452
|
+
RBlade::register_raw_directive_handler('not') do |args|
|
453
|
+
"if !(#{args[0]});"
|
454
|
+
end
|
455
|
+
|
456
|
+
@not(true) 1 @endNot -> ""
|
457
|
+
@not(false) 1 @endNot -> "1"
|
458
|
+
```
|
459
|
+
|
449
460
|
<a name="comments"></a>
|
450
461
|
### Comments
|
451
462
|
|
@@ -506,12 +517,26 @@ Namespaced components can be rendered using the namespace as a prefix to the nam
|
|
506
517
|
<a name="passing-data-to-components"></a>
|
507
518
|
### Passing Data to Components
|
508
519
|
|
509
|
-
You can pass data to RBlade components using HTML attributes.
|
520
|
+
You can pass data to RBlade components using HTML-style attributes. Within these attributes, you can use `{{ ... }}` to insert Ruby code.
|
521
|
+
|
522
|
+
If the attribute name begins with a colon, `:`, the contents are parsed as Ruby.
|
510
523
|
|
511
524
|
```rblade
|
512
|
-
|
525
|
+
@ruby(what = 'Something')
|
526
|
+
@ruby(message = "#{what} happened!")
|
527
|
+
|
528
|
+
{{-- The message attribute will be identical in all of the following components --}}
|
529
|
+
<x-alert message="Something happened!"/>
|
530
|
+
<x-alert message="{{ what }} happened!"/>
|
531
|
+
<x-alert :message="message"/>
|
532
|
+
<x-alert :message/>
|
533
|
+
<x-alert message={{ message }}/>
|
534
|
+
<x-alert message="{{ message }}"/>
|
513
535
|
```
|
514
536
|
|
537
|
+
> [!NOTE]
|
538
|
+
> When using print blocks within an attribute, e.g. `{{ {hash: true} }}`, the contents will be converted to a string. If the print block is the entirety of the attribute, or the attribute name starts with a colon, the contents are passed through as-is.
|
539
|
+
|
515
540
|
#### Component Properties
|
516
541
|
|
517
542
|
You can define a component's data properties using a `@props` directive at the top of the component. You can then reference these properties using local variables within the template:
|
@@ -552,9 +577,13 @@ When passing attributes to components, you can also use a "short attribute" synt
|
|
552
577
|
<a name="escaping-attribute-rendering"></a>
|
553
578
|
#### Escaping Attribute Rendering
|
554
579
|
|
555
|
-
Since some JavaScript frameworks such as Alpine.js also use colon-prefixed attributes, you can use a double colon (`::`) prefix to inform RBlade that the attribute is not a Ruby expression. For example, given the following
|
580
|
+
Since some JavaScript frameworks such as Alpine.js also use colon-prefixed attributes, you can use a double colon (`::`) prefix to inform RBlade that the attribute is not a Ruby expression. For example, given the following template:
|
556
581
|
|
557
582
|
```rblade
|
583
|
+
# The button component:
|
584
|
+
<button {{ attributes }}>{{ slot }}</button>
|
585
|
+
|
586
|
+
# Our template
|
558
587
|
<x-button ::class="{ danger: isDeleting }">
|
559
588
|
Submit
|
560
589
|
</x-button>
|
@@ -591,7 +620,7 @@ All of the attributes that are not part of the component's constructor will auto
|
|
591
620
|
Sometimes you can need to specify default values for attributes or merge additional values into some of the component's attributes. To accomplish this, you can use the attribute manager's `merge` method. This method is particularly useful for defining a set of default CSS classes that should always be applied to a component:
|
592
621
|
|
593
622
|
```rblade
|
594
|
-
<div {{ attributes.merge(
|
623
|
+
<div {{ attributes.merge("class": "alert alert-#{type}") }}>
|
595
624
|
{{ message }}
|
596
625
|
</div>
|
597
626
|
```
|
@@ -618,7 +647,7 @@ Both the `class` and `style` attributes are combined this way when using the `at
|
|
618
647
|
When merging attributes that are not `class` or `style`, the values provided to the `merge` method will be considered the "default" values of the attribute. However, unlike the `class` and `style` attributes, these defaults will be overwritten if the attribute is defined in the component tag. For example:
|
619
648
|
|
620
649
|
```rblade
|
621
|
-
<button {{ attributes.merge(
|
650
|
+
<button {{ attributes.merge(type: "button") }}>
|
622
651
|
{{ slot }}
|
623
652
|
</button>
|
624
653
|
```
|
@@ -645,7 +674,7 @@ The rendered HTML of the `button` component in this example would be:
|
|
645
674
|
Sometimes you may wish to merge classes if a given condition is `true`. You can accomplish this via the `class` method, which accepts a Hash of classes where the array key contains the class or classes you wish to add, while the value is a boolean expression:
|
646
675
|
|
647
676
|
```rblade
|
648
|
-
<div {{ attributes.class(
|
677
|
+
<div {{ attributes.class('p-4': true, 'bg-red': hasError) }}>
|
649
678
|
{{ message }}
|
650
679
|
</div>
|
651
680
|
```
|
@@ -653,7 +682,7 @@ Sometimes you may wish to merge classes if a given condition is `true`. You can
|
|
653
682
|
If you need to merge other attributes onto your component, you can chain the `merge` method onto the `class` method:
|
654
683
|
|
655
684
|
```rblade
|
656
|
-
<button {{ attributes.class(
|
685
|
+
<button {{ attributes.class('bg-red': hasError).merge(type: 'button') }}>
|
657
686
|
{{ slot }}
|
658
687
|
</button>
|
659
688
|
```
|
@@ -809,6 +838,20 @@ Sometimes, you may wish to return early from a component without printing anythi
|
|
809
838
|
...
|
810
839
|
```
|
811
840
|
|
841
|
+
<a name="dynamic-components"></a>
|
842
|
+
### Dynamic Components
|
843
|
+
|
844
|
+
Sometimes you may need to render a component but not know which component should be rendered until runtime. In this situation, you may use RBlades's built-in dynamic component to render the component based on a runtime value or variable:
|
845
|
+
|
846
|
+
```rblade
|
847
|
+
<% component_name = 'button' %>
|
848
|
+
|
849
|
+
<x-dynamic :component="component_name"/>
|
850
|
+
```
|
851
|
+
|
852
|
+
> [!NOTE]
|
853
|
+
> Dynamic components use the `component` helper class, and thus don't take advantage of RBlade's performance improvements over using partials
|
854
|
+
|
812
855
|
<a name="registering-additional-component-directories"></a>
|
813
856
|
### Registering Additional Component Directories
|
814
857
|
|
@@ -928,3 +971,27 @@ If you would like to prepend content onto the beginning of a stack, you should u
|
|
928
971
|
This will be first...
|
929
972
|
@endPrepend
|
930
973
|
```
|
974
|
+
|
975
|
+
|
976
|
+
<a name="rblade-integration"></a>
|
977
|
+
## Integrating RBlade With Other Templates
|
978
|
+
|
979
|
+
You might want to use RBlade components within other templates, e.g. if you are using a component library that uses them. By default, RBlade components cannot be rendered directly, but this can be enabled using the `direct_component_rendering` option.
|
980
|
+
|
981
|
+
```ruby
|
982
|
+
# config/initializers/rblade.rb
|
983
|
+
|
984
|
+
# Enable partial rendering of RBlade components
|
985
|
+
RBlade.direct_component_rendering = true
|
986
|
+
```
|
987
|
+
|
988
|
+
Once enabled, RBlade components can be rendered using `render partial: ...`. 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
|
+
|
990
|
+
```erb
|
991
|
+
<%= render template: "components/button", locals: {class: "mt-4", slot: capture do %>
|
992
|
+
|
993
|
+
<% end } %>
|
994
|
+
```
|
995
|
+
|
996
|
+
> [!NOTE]
|
997
|
+
> RBlade's `{{ }}` print directives are automatically sent through Rails' `h` function to prevent XSS attacks.
|
data/REFERENCE.md
CHANGED
@@ -24,12 +24,14 @@ By default, RBlade will look for components in the `app/views/components` folder
|
|
24
24
|
| `<x-component.name>...</x-component.name>` | Render the component with the given slot content |
|
25
25
|
| `<x-component.name>...<//>` | Short closing tag syntax (note: this bypasses some sanity checking during compilation) |
|
26
26
|
| `<x-name attribute="STRING"/>` | Pass a string value to a component |
|
27
|
+
| `<x-name attribute="STRING{{ RUBY_EXPRESSION }}"/>` | Pass a string value to a component with an interpolated ruby value |
|
27
28
|
| `<x-name :attribute="RUBY_EXPRESSION"/>` | Pass a ruby expression, executed in the local scope, to a component |
|
28
29
|
| `<x-name :attribute/>` | Pass the `attribute` local variable into a component |
|
29
30
|
| `<x-name @class({'bg-red-600': is_error})/>` | Conditionally pass classes to a component |
|
30
31
|
| `<x-name @style({'bg-red-600': is_error})/>` | Conditionally pass styles to a component |
|
31
32
|
| `<x-name attribute/>` | Pass an attribute to a component with value `true` |
|
32
33
|
| `<x-name {{ attributes }}/>` | Pass attributes to a child component |
|
34
|
+
| `<x-dynamic component="name"/>` | Dynamically render a component based on the runetime value of the `component` attribute |
|
33
35
|
| `@props(header: "Header")` | Remove `header` from the attributes Hash and introduce it as a local variable, using the specified value as a default |
|
34
36
|
| `@props(header: required)` | Remove `header` from the attributes Hash and introduce it as a local variable, raising an error if it is not set |
|
35
37
|
| `{{ slot }}` | Output the block content passed into the current component |
|
@@ -130,11 +132,11 @@ Note that the stack is printed once the current view or component finishes.
|
|
130
132
|
|
131
133
|
## Tips
|
132
134
|
|
133
|
-
*
|
135
|
+
* All end directives can simply be replaced with `@end` if preferred:
|
134
136
|
- `@nil?(...) ... @endnil?`
|
135
137
|
- `@nil?(...) ... @endnil`
|
136
138
|
- `@nil?(...) ... @end`
|
137
|
-
*
|
139
|
+
* Directives are case insensitive and can contain underscores. The following are identical:
|
138
140
|
- `@pushonce`
|
139
141
|
- `@pushOnce`
|
140
142
|
- `@PushOnce`
|
data/do
CHANGED
@@ -57,15 +57,15 @@ fi
|
|
57
57
|
|
58
58
|
if [ "$1" == "cs" ] || [ "$1" == "c" ]
|
59
59
|
then
|
60
|
-
echo Running: ${DOCKER_COMPOSE_COMMAND} run blade
|
61
|
-
${DOCKER_COMPOSE_COMMAND} run blade
|
60
|
+
echo Running: ${DOCKER_COMPOSE_COMMAND} run blade rubocop "${@:2}"
|
61
|
+
${DOCKER_COMPOSE_COMMAND} run 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
|
68
|
-
${DOCKER_COMPOSE_COMMAND} run blade
|
67
|
+
echo Running: ${DOCKER_COMPOSE_COMMAND} run blade rubocop --autocorrect "${@:2}"
|
68
|
+
${DOCKER_COMPOSE_COMMAND} run blade rubocop --autocorrect "${@:2}"
|
69
69
|
exit 0
|
70
70
|
fi
|
71
71
|
|
data/docker-compose.yml
CHANGED
@@ -6,8 +6,8 @@ module RBlade
|
|
6
6
|
tokens.each do |token|
|
7
7
|
next if token.type != :unprocessed
|
8
8
|
|
9
|
-
token.value.gsub!(/\{\{
|
10
|
-
token.value.gsub!(
|
9
|
+
token.value.gsub!(/\{\{--(?:[^-]++|-)*?--}}/, "")
|
10
|
+
token.value.gsub!(/<%#(?:[^%]++|-)*?%>/, "")
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -34,12 +34,14 @@ module RBlade
|
|
34
34
|
|
35
35
|
private
|
36
36
|
|
37
|
-
def compile_token_start
|
37
|
+
def compile_token_start(token)
|
38
38
|
component = {
|
39
|
-
name: token.value[:name]
|
39
|
+
name: token.value[:name],
|
40
40
|
}
|
41
41
|
@component_stack << component
|
42
42
|
|
43
|
+
return compile_dynamic_component(token) if component[:name] == "dynamic"
|
44
|
+
|
43
45
|
attributes = compile_attributes token.value[:attributes]
|
44
46
|
|
45
47
|
if component[:name].start_with? "slot::"
|
@@ -49,7 +51,26 @@ module RBlade
|
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
52
|
-
def
|
54
|
+
def compile_dynamic_component(token)
|
55
|
+
component_index = token.value[:attributes].index { |item| item[:name] == "component" }
|
56
|
+
component = token.value[:attributes].delete_at(component_index)
|
57
|
+
component_value = case component[:type]
|
58
|
+
when "string"
|
59
|
+
process_string_attribute(component[:value])
|
60
|
+
when "ruby"
|
61
|
+
component[:value]
|
62
|
+
when "pass_through"
|
63
|
+
component[:name]
|
64
|
+
else
|
65
|
+
raise RBladeTemplateError.new "Component compiler: unexpected attribute type for component attribute (#{attribute[:type]})"
|
66
|
+
end
|
67
|
+
|
68
|
+
attributes = compile_attributes token.value[:attributes]
|
69
|
+
|
70
|
+
"@output_buffer.raw_buffer<<component(#{component_value}, '#{RBlade.escape_quotes(@component_store.current_view_name)}', #{attributes.join ","}) do;"
|
71
|
+
end
|
72
|
+
|
73
|
+
def compile_token_end(token)
|
53
74
|
component = @component_stack.pop
|
54
75
|
if component.nil?
|
55
76
|
raise RBladeTemplateError.new "Unexpected closing tag </x-#{token.value[:name]}>"
|
@@ -62,7 +83,7 @@ module RBlade
|
|
62
83
|
"end;"
|
63
84
|
end
|
64
85
|
|
65
|
-
def compile_attributes
|
86
|
+
def compile_attributes(attributes)
|
66
87
|
attributes.map do |attribute|
|
67
88
|
case attribute[:type]
|
68
89
|
when "class"
|
@@ -86,7 +107,7 @@ module RBlade
|
|
86
107
|
end
|
87
108
|
|
88
109
|
def process_string_attribute(string)
|
89
|
-
result = string.split(/((?<!@)\{\{
|
110
|
+
result = string.split(/((?<!@)\{\{(?:[^}]++|\})*?\}\})/).map do |substring|
|
90
111
|
if substring.start_with?("{{") && substring.end_with?("}}")
|
91
112
|
"(#{substring[2..-3]}).to_s"
|
92
113
|
elsif !substring.empty?
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RBlade
|
4
|
+
class CompilesInjections
|
5
|
+
def compile!(tokens)
|
6
|
+
tokens.map! do |token|
|
7
|
+
next(token) if token.type != :unprocessed
|
8
|
+
|
9
|
+
segments = token.value.split(/
|
10
|
+
(@?\{!!)(\s*+(?:[^!}%@]++|[!}%@])+?\s*+)(!!})
|
11
|
+
|
|
12
|
+
(@?\{\{)(\s*+(?:[^!}%@]++|[!}%@])+?\s*+)(}})
|
13
|
+
|
|
14
|
+
(\s?(?<!\w)@?@ruby)(\s++(?:[^!}%@]++|[!}%@])+?\s*+)((?<!\w)@end_?ruby(?!\w)\s?)
|
15
|
+
|
|
16
|
+
(<%%?=?=?)(\s*+(?:[^!}%@]++|[!}%@])+?\s*+)(%%?>)
|
17
|
+
/xi)
|
18
|
+
|
19
|
+
i = 0
|
20
|
+
while i < segments.count
|
21
|
+
case segments[i]
|
22
|
+
when "{{", "<%="
|
23
|
+
segments[i] = create_token(segments[i + 1].strip, true)
|
24
|
+
when "<%", /\A\s?@ruby\z/i
|
25
|
+
segments[i + 1].strip!
|
26
|
+
segments[i + 1] << ";" unless segments[i + 1].end_with?(";")
|
27
|
+
segments[i] = Token.new(type: :ruby, value: segments[i + 1])
|
28
|
+
when "{!!", "<%=="
|
29
|
+
segments[i] = create_token(segments[i + 1].strip, false)
|
30
|
+
when "@{!!", "@{{", /\A\s?@@ruby\z/i
|
31
|
+
segments[i].sub!("@", "")
|
32
|
+
segments[i] = Token.new(type: :raw_text, value: "#{segments[i]}#{segments[i + 1]}#{segments[i + 2]}")
|
33
|
+
when "<%%", "<%%=", "<%%=="
|
34
|
+
segments[i] = Token.new(type: :raw_text, value: "<#{segments[i].delete_prefix!("<%")}#{segments[i + 1]}%>")
|
35
|
+
when "", nil
|
36
|
+
segments.delete_at i
|
37
|
+
next
|
38
|
+
else
|
39
|
+
segments[i] = Token.new(type: :unprocessed, value: segments[i])
|
40
|
+
i += 1
|
41
|
+
next
|
42
|
+
end
|
43
|
+
|
44
|
+
segments.delete_at i + 1
|
45
|
+
segments.delete_at i + 1
|
46
|
+
i += 1
|
47
|
+
end
|
48
|
+
|
49
|
+
segments
|
50
|
+
end.flatten!
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def create_token(expression, escape_html)
|
56
|
+
# Don't try to print ends
|
57
|
+
if expression.match?(/\A(?:}|end(?![[:alnum:]_]|[^\0-\177]))/i)
|
58
|
+
return Token.new(:ruby, "#{expression};")
|
59
|
+
end
|
60
|
+
|
61
|
+
segment_value = if escape_html
|
62
|
+
"@output_buffer.append=#{expression};"
|
63
|
+
# If this is a block, don't wrap in parentheses
|
64
|
+
elsif expression.match?(/
|
65
|
+
(?:\{|do)\s*+
|
66
|
+
(
|
67
|
+
\|\s*+
|
68
|
+
[a-zA-Z0-9_]++\s*+
|
69
|
+
(,\s*+[a-zA-Z0-9_]++)?\s*+
|
70
|
+
\|
|
71
|
+
)?
|
72
|
+
\z/x)
|
73
|
+
"@output_buffer.safe_expr_append=#{expression};"
|
74
|
+
else
|
75
|
+
"@output_buffer.raw_buffer<<(#{expression}).to_s;"
|
76
|
+
end
|
77
|
+
|
78
|
+
Token.new(:print, segment_value)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|