rblade 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/README.md +10 -17
- data/lib/rblade/compiler/compiles_components.rb +29 -11
- data/lib/rblade/compiler/compiles_echos.rb +4 -4
- data/lib/rblade/compiler/statements/compiles_props.rb +23 -4
- data/lib/rblade/compiler.rb +9 -0
- data/lib/rblade/helpers/attributes_manager.rb +7 -1
- data/lib/rblade/helpers/html_string.rb +4 -0
- data/lib/rblade/helpers/slot_manager.rb +23 -0
- data/lib/rblade/helpers/stack_manager.rb +4 -0
- data/lib/rblade/rails_template.rb +1 -0
- data/rblade.gemspec +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d795f1e81eb6dd60c3c581ccb22de43921bfab0997eb45daf6309298fb83fc5d
|
4
|
+
data.tar.gz: 2189078e98ad7ede598bc77c524bb1a88b396390098dc451565567e37532ba03
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8206e2599b2cd9556e2ff9eb03a5789bd87aa73d2776edce7f35c02c1bde56497838d7dece88e05a986775df1a21371d2ef1adf37a7aef75ed6a442e5fe0c5a
|
7
|
+
data.tar.gz: e0b5eeaff73cc4530c70cd6c3f054b561948feaa4e845f31b29c496f465c2d73de6d822d4828b740b40ced23fd85658cbbfc5f5120fc2c564ae343bb2fc4b551
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
## 0.
|
1
|
+
## 0.5.0
|
2
|
+
- Add support for slots
|
3
|
+
- Change @props to only add valid variable names to global scope
|
4
|
+
- Change @props to remove from attributes array
|
5
|
+
|
6
|
+
## 0.4.0 [2024-07-29]
|
2
7
|
- Add @class and @style statements
|
3
8
|
- Add @env and @production statements
|
4
9
|
- Add @once, @pushOnce and @prependOnce statements
|
data/README.md
CHANGED
@@ -31,9 +31,9 @@ Hello, {{ name }}.
|
|
31
31
|
```
|
32
32
|
|
33
33
|
> [!NOTE]
|
34
|
-
> RBlade's `{{ }}`
|
34
|
+
> RBlade's `{{ }}` print statements are automatically sent through Rails' `h` function to prevent XSS attacks.
|
35
35
|
|
36
|
-
You are not limited to displaying the contents of the variables passed to the view. You may also print the results of any Ruby function. In fact, you can put any Ruby code you wish inside of a
|
36
|
+
You are not limited to displaying the contents of the variables passed to the view. You may also print the results of any Ruby function. In fact, you can put any Ruby code you wish inside of a RBlade print directive:
|
37
37
|
|
38
38
|
```rblade
|
39
39
|
The current UNIX timestamp is {{ Time.now.to_i }}.
|
@@ -77,7 +77,7 @@ The `@` symbol may also be used to escape RBlade directives:
|
|
77
77
|
<a name="the-at-verbatim-directive"></a>
|
78
78
|
#### The `@verbatim` Directive
|
79
79
|
|
80
|
-
If you are displaying JavaScript variables in a large portion of your template, you may wrap the HTML in the `@verbatim` directive so that you do not have to prefix each Blade
|
80
|
+
If you are displaying JavaScript variables in a large portion of your template, you may wrap the HTML in the `@verbatim` directive so that you do not have to prefix each Blade print directive with an `@` symbol:
|
81
81
|
|
82
82
|
```rblade
|
83
83
|
@verbatim
|
@@ -530,7 +530,7 @@ You can pass data to RBlade components using HTML attributes. Hard-coded strings
|
|
530
530
|
|
531
531
|
#### Component Properties
|
532
532
|
|
533
|
-
You can define a component's data properties using a `@props`
|
533
|
+
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:
|
534
534
|
|
535
535
|
```rblade
|
536
536
|
{{-- alert.rblade --}}
|
@@ -538,25 +538,18 @@ You can define a component's data properties using a `@props` statement at the t
|
|
538
538
|
<div class="{{ type }}">{{ message }}</div>
|
539
539
|
```
|
540
540
|
|
541
|
-
The `@props`
|
541
|
+
The `@props` directive accepts a `Hash` where the key is the name of the attribute, and the value is the default value for the property. You can use the special `_required` value to represent a property with no default that must always be defined:
|
542
542
|
|
543
543
|
```rblade
|
544
544
|
{{-- This will give an error because the alert component requires a message propery --}}
|
545
545
|
<x-alert/>
|
546
546
|
```
|
547
547
|
|
548
|
-
|
548
|
+
All properties in the `@props` directive are automatically removed from `attributes`. Properties with names that aren't valid Ruby variable names or are Ruby reserved keywords are not created as local variables. However, you can reference them via the `attributes` local variable:
|
549
549
|
|
550
550
|
```rblade
|
551
|
-
@props({"data-value":
|
552
|
-
<div>{{
|
553
|
-
```
|
554
|
-
|
555
|
-
Properties that contain other non-alphanumeric characters or are Ruby reserved keywords are not created as local variables. However, you can reference them via the `attributes` local variable:
|
556
|
-
|
557
|
-
```rblade
|
558
|
-
@props({"for": _required})
|
559
|
-
<div>{{ attributes[:for] }}</div>
|
551
|
+
@props({"for": _required, "data-value": nil})
|
552
|
+
<div>{{ attributes[:for] }} {{ attributes[:'data-value'] }}</div>
|
560
553
|
```
|
561
554
|
|
562
555
|
<a name="short-attribute-syntax"></a>
|
@@ -774,11 +767,11 @@ We may pass content to the `slot` by injecting content into the component:
|
|
774
767
|
</x-alert>
|
775
768
|
```
|
776
769
|
|
777
|
-
**TODO this**
|
778
770
|
Sometimes a component may need to render multiple different slots in different locations within the component. Let's modify our alert component to allow for the injection of a "title" slot:
|
779
771
|
|
780
772
|
```rblade
|
781
773
|
{{-- /app/views/components/alert.rblade --}}
|
774
|
+
@props({title: _required})
|
782
775
|
<span class="alert-title">{{ title }}</span>
|
783
776
|
<div class="alert alert-danger">
|
784
777
|
{{ slot }}
|
@@ -796,7 +789,7 @@ You may define the content of the named slot using the `x-slot` tag. Any content
|
|
796
789
|
<strong>Whoops!</strong> Something went wrong!
|
797
790
|
</x-alert>
|
798
791
|
```
|
799
|
-
|
792
|
+
**TODO this**
|
800
793
|
You may invoke a slot's `isEmpty` method to determine if the slot contains content:
|
801
794
|
|
802
795
|
```rblade
|
@@ -26,17 +26,16 @@ module RBlade
|
|
26
26
|
private
|
27
27
|
|
28
28
|
def compile_token_start token
|
29
|
-
|
30
|
-
|
31
|
-
@component_stack << {
|
29
|
+
component = {
|
32
30
|
name: token.value[:name],
|
33
|
-
index: @
|
31
|
+
index: @component_stack.count
|
34
32
|
}
|
33
|
+
@component_stack << component
|
35
34
|
|
36
35
|
attributes = compile_attributes token.value[:attributes]
|
37
36
|
|
38
|
-
code = "_c#{
|
39
|
-
code << "_c#{
|
37
|
+
code = "_c#{component[:index]}_swap=_out;_out='';"
|
38
|
+
code << "_c#{component[:index]}_attr={#{attributes.join(",")}};"
|
40
39
|
|
41
40
|
code
|
42
41
|
end
|
@@ -50,15 +49,34 @@ module RBlade
|
|
50
49
|
raise StandardError.new "Unexpected closing tag (#{token.value[:name]}) expecting #{component[:name]}"
|
51
50
|
end
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
namespace = nil
|
53
|
+
name = component[:name]
|
54
|
+
if name.match '::'
|
55
|
+
namespace, name = component[:name].split("::")
|
56
|
+
end
|
57
|
+
|
58
|
+
code = if namespace == 'slot'
|
59
|
+
compile_slot_end name, component
|
60
|
+
else
|
61
|
+
compile_component_end component
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def compile_slot_end name, component
|
66
|
+
parent = @component_stack.last
|
67
|
+
|
68
|
+
code = "_c#{parent[:index]}_attr[:'#{RBlade.escape_quotes(name)}']=RBlade::SlotManager.new(_out,_c#{component[:index]}_attr);"
|
69
|
+
code << "_out=_c#{component[:index]}_swap;_c#{component[:index]}_swap=nil;_c#{component[:index]}_attr=nil;"
|
56
70
|
|
57
71
|
code
|
58
72
|
end
|
59
73
|
|
60
|
-
def
|
61
|
-
|
74
|
+
def compile_component_end component
|
75
|
+
code = "_slot=RBlade::SlotManager.new(_out);_out=_c#{component[:index]}_swap;"
|
76
|
+
code << "_out<<#{ComponentStore.component(component[:name])}(_slot,_c#{component[:index]}_attr);"
|
77
|
+
code << "_slot=nil;_c#{component[:index]}_swap=nil;_c#{component[:index]}_attr=nil;"
|
78
|
+
|
79
|
+
code
|
62
80
|
end
|
63
81
|
|
64
82
|
def compile_attributes attributes
|
@@ -8,8 +8,8 @@ module RBlade
|
|
8
8
|
private
|
9
9
|
|
10
10
|
def compile_regular_echos!(tokens)
|
11
|
-
compile_echos! tokens, "{{", "}}", "
|
12
|
-
compile_echos! tokens, "<%=", "%>", "
|
11
|
+
compile_echos! tokens, "{{", "}}", "RBlade.e"
|
12
|
+
compile_echos! tokens, "<%=", "%>", "RBlade.e"
|
13
13
|
end
|
14
14
|
|
15
15
|
def compile_unsafe_echos!(tokens)
|
@@ -35,8 +35,8 @@ module RBlade
|
|
35
35
|
segments.delete_at i
|
36
36
|
segments.delete_at i + 1
|
37
37
|
segment_value = "_out<<"
|
38
|
-
|
39
|
-
segment_value <<= if !wrapper_function.nil?
|
38
|
+
|
39
|
+
segment_value <<= if !wrapper_function.nil?
|
40
40
|
wrapper_function + "(" + segments[i] + ");"
|
41
41
|
else
|
42
42
|
"(" + segments[i] + ").to_s;"
|
@@ -10,14 +10,23 @@ module RBlade
|
|
10
10
|
|
11
11
|
props = extractProps args[0]
|
12
12
|
props.map do |key, value|
|
13
|
+
compiled_code = ""
|
13
14
|
if value == "_required"
|
14
|
-
"if !
|
15
|
+
compiled_code << "if !attributes.has?(:'#{RBlade.escape_quotes(key)}');raise \"Props statement: #{key} is not defined\";end;"
|
16
|
+
end
|
17
|
+
if isValidVariableName key
|
18
|
+
compiled_code << "#{key}=attributes[:'#{RBlade.escape_quotes(key)}'].nil? ? #{value} : attributes[:'#{RBlade.escape_quotes(key)}'];"
|
19
|
+
compiled_code << "attributes.delete :'#{RBlade.escape_quotes(key)}';"
|
15
20
|
else
|
16
|
-
|
21
|
+
compiled_code << "attributes.default(:'#{RBlade.escape_quotes(key)}', #{value});"
|
17
22
|
end
|
23
|
+
|
24
|
+
compiled_code
|
18
25
|
end.join
|
19
26
|
end
|
20
27
|
|
28
|
+
private
|
29
|
+
|
21
30
|
def extractProps prop_string
|
22
31
|
if !prop_string.start_with?("{") || !prop_string.end_with?("}")
|
23
32
|
raise StandardError.new "Props statement: expecting hash as parameter"
|
@@ -31,9 +40,9 @@ module RBlade
|
|
31
40
|
|
32
41
|
key, value = prop.split(/^
|
33
42
|
(?:
|
34
|
-
(
|
43
|
+
'(.+)':
|
35
44
|
|
|
36
|
-
(
|
45
|
+
"(.+)":
|
37
46
|
|
|
38
47
|
([^ :]+):
|
39
48
|
|
|
@@ -56,6 +65,16 @@ module RBlade
|
|
56
65
|
|
57
66
|
props
|
58
67
|
end
|
68
|
+
|
69
|
+
RUBY_RESERVED_KEYWORDS = %w[__FILE__ __LINE__ alias and begin BEGIN break case class def defined? do else elsif end END ensure false for if in module next nil not or redo rescue retry return self super then true undef unless until when while yield].freeze
|
70
|
+
|
71
|
+
def isValidVariableName key
|
72
|
+
return false unless key.match?(/^[a-zA-Z_][a-zA-Z0-9_]*$/)
|
73
|
+
|
74
|
+
return false if RUBY_RESERVED_KEYWORDS.include? key
|
75
|
+
|
76
|
+
true
|
77
|
+
end
|
59
78
|
end
|
60
79
|
end
|
61
80
|
end
|
data/lib/rblade/compiler.rb
CHANGED
@@ -6,6 +6,7 @@ require "rblade/compiler/compiles_verbatim"
|
|
6
6
|
require "rblade/compiler/compiles_statements"
|
7
7
|
require "rblade/compiler/tokenizes_components"
|
8
8
|
require "rblade/compiler/tokenizes_statements"
|
9
|
+
require "rblade/helpers/html_string"
|
9
10
|
|
10
11
|
Token = Struct.new(:type, :value)
|
11
12
|
|
@@ -19,6 +20,14 @@ module RBlade
|
|
19
20
|
string.gsub(/['\\\x0]/, '\\\\\0')
|
20
21
|
end
|
21
22
|
|
23
|
+
def self.e string
|
24
|
+
if string.is_a? HtmlString
|
25
|
+
string
|
26
|
+
else
|
27
|
+
h(string)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
22
31
|
class Compiler
|
23
32
|
def self.compileString(string_template)
|
24
33
|
tokens = [Token.new(:unprocessed, string_template)]
|
@@ -1,5 +1,7 @@
|
|
1
|
+
require "rblade/helpers/html_string"
|
2
|
+
|
1
3
|
module RBlade
|
2
|
-
class AttributesManager
|
4
|
+
class AttributesManager < HtmlString
|
3
5
|
@attributes = {}
|
4
6
|
def initialize attributes
|
5
7
|
@attributes = attributes
|
@@ -33,6 +35,10 @@ module RBlade
|
|
33
35
|
end.join " "
|
34
36
|
end
|
35
37
|
|
38
|
+
def to_str
|
39
|
+
to_s
|
40
|
+
end
|
41
|
+
|
36
42
|
def only(keys)
|
37
43
|
keys = if keys.is_a? Array
|
38
44
|
keys.map(&:to_sym)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "rblade/helpers/attributes_manager"
|
2
|
+
require "rblade/helpers/html_string"
|
3
|
+
|
4
|
+
module RBlade
|
5
|
+
class SlotManager < HtmlString
|
6
|
+
def initialize content, attributes = nil
|
7
|
+
@content = content
|
8
|
+
@attributes = attributes && AttributesManager.new(attributes)
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
@content
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_str
|
16
|
+
to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def attributes
|
20
|
+
@attributes
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -3,6 +3,7 @@ require "rblade/compiler"
|
|
3
3
|
require "rblade/component_store"
|
4
4
|
require "rblade/helpers/attributes_manager"
|
5
5
|
require "rblade/helpers/class_manager"
|
6
|
+
require "rblade/helpers/slot_manager"
|
6
7
|
require "rblade/helpers/stack_manager"
|
7
8
|
require "rblade/helpers/style_manager"
|
8
9
|
|
data/rblade.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "rblade"
|
3
|
-
s.version = "0.
|
3
|
+
s.version = "0.5.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"]
|
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.
|
4
|
+
version: 0.5.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-
|
11
|
+
date: 2024-07-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -103,6 +103,8 @@ files:
|
|
103
103
|
- lib/rblade/component_store.rb
|
104
104
|
- lib/rblade/helpers/attributes_manager.rb
|
105
105
|
- lib/rblade/helpers/class_manager.rb
|
106
|
+
- lib/rblade/helpers/html_string.rb
|
107
|
+
- lib/rblade/helpers/slot_manager.rb
|
106
108
|
- lib/rblade/helpers/stack_manager.rb
|
107
109
|
- lib/rblade/helpers/style_manager.rb
|
108
110
|
- lib/rblade/helpers/tokenizer.rb
|