rblade 0.6.1 → 1.0.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: b46e864d0ce53aee73ce1353a29f538c54954a4afeb988de4975b64076d5fe3c
4
- data.tar.gz: 12a74fcc836959872de298418892c16b89ce5690d63cb1def6be4c7cadb6a51a
3
+ metadata.gz: 76b47c51f5796359252d33fe3865bb7189cd4de068579fee57520c881879755d
4
+ data.tar.gz: ba1288cd0382290deb526f4faf4c5fe7c15d78372fca5248d41b4861f1ac6abb
5
5
  SHA512:
6
- metadata.gz: b824b61c6fc8d08f57f09de311d4b9d0dfcf2d236712eb5aa5664f27d2819083e66488518e9fa21eb6f8360f332e64676e2ee01f02979d08effdb746eddd1415
7
- data.tar.gz: 3c47fc2906be91c9a908c022a57a3222366142479c2167173605e0d31db9bdceaf0bca10a7a58ae9273d0fff5999679c943b92af204f7cc0448ce1e48146fafd
6
+ metadata.gz: 4783a57cbe6b7f2173f0435b2024ed3c83dff68819637d2eb759addd7ca485fd7b6d3c89987c8a07ff2561bded344037161bbb67b7bbc8147575e9adc568ce1d
7
+ data.tar.gz: 91f27168157d7448e7e26979a4dc60a731a6471027a8c5ddceb5ac4e23d51f3b39de0d61a2ba1970ec58bc726ccdb3316ed5e6389c463b63c61dd358e8e64f2f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 1.0.0 [UNRELEASED]
2
+ - Add quick reference and examples
3
+ - Add @shouldRender directive
4
+ - Add support for ERB style `<%==` unsafe prints
5
+ - Fix bugs with statement argument error handling
6
+ - Remove deprecated "breakIf" and "nextIf" statements
7
+ - Rename "echo" compilers to "print"
8
+ - Update README
9
+
1
10
  ## 0.6.1 [2024-08-02]
2
11
  - Fix broken build
3
12
 
@@ -56,4 +65,4 @@
56
65
  - Add attributes, class and style manager
57
66
 
58
67
  ## 0.1.0 [2024-07-22]
59
- - Initial release
68
+ - Initial release
data/README.md CHANGED
@@ -1,10 +1,18 @@
1
+ <a name="rblade-templates"></a>
1
2
  # RBlade Templates
2
3
 
4
+ RBlade is a simple, yet powerful templating engine for Ruby on Rails, inspired by [Laravel Blade](https://laravel.com/docs/blade). Unlike other Rails templating engines, RBlade prioritises the use of components and partials within templates.
5
+
6
+ RBlade template files use the `.rblade` file extension and are typically stored in the `app/views` directory.
7
+
8
+ <a name="displaying-data"></a>
9
+ ## Table of Contents
3
10
  - [RBlade Templates](#rblade-templates)
4
- * [Introduction](#introduction)
11
+ * [Table of Contents](#table-of-contents)
12
+ * [Getting Started](#getting-started)
5
13
  * [Displaying Data](#displaying-data)
6
14
  + [HTML Entity Encoding](#html-entity-encoding)
7
- + [Blade and JavaScript Frameworks](#blade-and-javascript-frameworks)
15
+ + [RBlade and JavaScript Frameworks](#rblade-and-javascript-frameworks)
8
16
  - [The `@verbatim` Directive](#the-at-verbatim-directive)
9
17
  * [RBlade Directives](#rblade-directives)
10
18
  + [If Statements](#if-statements)
@@ -37,15 +45,24 @@
37
45
  + [Method Field](#method-field)
38
46
  * [Stacks](#stacks)
39
47
 
40
- <a name="introduction"></a>
41
- ## Introduction
48
+ <a name="getting-started"></a>
49
+ ## Getting Started
50
+
51
+ Add RBlade to your Rails project by adding it to your Gemfile:
52
+
53
+ ```
54
+ bundle add rblade
55
+ ```
56
+
57
+ RBlade will automatically be detected and start parsing templates ending in `.rblade`.
58
+
59
+ For a quick overview of RBlade's capabilities, refer to the [reference file](REFERENCE.md) or take a look at the [examples](examples/README.md).
42
60
 
43
- RBlade is a simple, yet powerful templating engine for Ruby on Rails, inspired by [Laravel Blade](https://laravel.com/docs/blade). Unlike some templating engines, RBlade does not restrict you from using plain Ruby code in your templates. Blade template files use the `.rblade` file extension and are typically stored in the `app/views` directory.
44
61
 
45
62
  <a name="displaying-data"></a>
46
63
  ## Displaying Data
47
64
 
48
- You may display data that is passed to your RBlade views by wrapping the variable in curly braces. For example, given the following controller method:
65
+ You can display data that is passed to your RBlade views by wrapping the variable in curly braces. For example, given the following controller method:
49
66
 
50
67
  ```ruby
51
68
  def index
@@ -53,16 +70,16 @@ def index
53
70
  end
54
71
  ```
55
72
 
56
- You may display the contents of the `name` variable like so:
73
+ You can display the contents of the `name` variable like so:
57
74
 
58
75
  ```rblade
59
76
  Hello, {{ name }}.
60
77
  ```
61
78
 
62
79
  > [!NOTE]
63
- > RBlade's `{{ }}` print statements are automatically sent through Rails' `h` function to prevent XSS attacks.
80
+ > RBlade's `{{ }}` print directives are automatically sent through Rails' `h` function to prevent XSS attacks.
64
81
 
65
- 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:
82
+ You are not limited to displaying the contents of the variables passed to the view. You can also print the results of any Ruby function. In fact, you can put any Ruby code you wish inside of a RBlade print directive:
66
83
 
67
84
  ```rblade
68
85
  The current UNIX timestamp is {{ Time.now.to_i }}.
@@ -71,19 +88,19 @@ The current UNIX timestamp is {{ Time.now.to_i }}.
71
88
  <a name="html-entity-encoding"></a>
72
89
  ### HTML Entity Encoding
73
90
 
74
- By default, RBlade `{{ }}` statements are automatically sent through Rails' `h` function to prevent XSS attacks. If you do not want your data to be escaped, you may use the following syntax:
91
+ By default, RBlade `{{ }}` directives are automatically sent through Rails' `h` function to prevent XSS attacks. If you do not want your data to be escaped, you can use the following syntax:
75
92
 
76
93
  ```rblade
77
94
  Hello, {!! name !!}.
78
95
  ```
79
96
 
80
97
  > [!WARNING]
81
- > Be very careful when echoing content that is supplied by users of your application. You should typically use the escaped, double curly brace syntax to prevent XSS attacks when displaying user supplied data.
98
+ > Be very careful when printing content that is supplied by users of your application. You should typically use the escaped, double curly brace syntax to prevent XSS attacks when displaying user supplied data.
82
99
 
83
- <a name="blade-and-javascript-frameworks"></a>
84
- ### Blade and JavaScript Frameworks
100
+ <a name="rblade-and-javascript-frameworks"></a>
101
+ ### RBlade and JavaScript Frameworks
85
102
 
86
- Since many JavaScript frameworks also use "curly" braces to indicate a given expression should be displayed in the browser, you may use the `@` symbol to inform the RBlade rendering engine an expression should remain untouched. For example:
103
+ Since many JavaScript frameworks also use "curly" braces to indicate a given expression should be displayed in the browser, you can use the `@` symbol to inform the RBlade rendering engine an expression should remain untouched. For example:
87
104
 
88
105
  ```rblade
89
106
  <h1>Laravel</h1>
@@ -91,12 +108,12 @@ Since many JavaScript frameworks also use "curly" braces to indicate a given exp
91
108
  Hello, @{{ name }}.
92
109
  ```
93
110
 
94
- In this example, the `@` symbol will be removed by Blade; however, `{{ name }}` expression will remain untouched by the RBlade engine, allowing it to be rendered by your JavaScript framework.
111
+ In this example, the `@` symbol will be removed by RBlade; however, `{{ name }}` expression will remain untouched by the RBlade engine, allowing it to be rendered by your JavaScript framework.
95
112
 
96
- The `@` symbol may also be used to escape RBlade directives:
113
+ The `@` symbol can also be used to escape RBlade directives:
97
114
 
98
115
  ```rblade
99
- {{-- Blade template --}}
116
+ {{-- RBlade template --}}
100
117
  @@if()
101
118
 
102
119
  <!-- HTML output -->
@@ -106,7 +123,7 @@ The `@` symbol may also be used to escape RBlade directives:
106
123
  <a name="the-at-verbatim-directive"></a>
107
124
  #### The `@verbatim` Directive
108
125
 
109
- 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:
126
+ If you are displaying JavaScript variables in a large portion of your template, you can wrap the HTML in the `@verbatim` directive so that you do not have to prefix each RBlade print directive with an `@` symbol:
110
127
 
111
128
  ```rblade
112
129
  @verbatim
@@ -116,18 +133,18 @@ If you are displaying JavaScript variables in a large portion of your template,
116
133
  @endverbatim
117
134
  ```
118
135
 
119
- <a name="blade-directives"></a>
136
+ <a name="rblade-directives"></a>
120
137
  ## RBlade Directives
121
138
 
122
139
  In addition to template inheritance and displaying data, RBlade also provides convenient shortcuts for common Ruby control structures, such as conditional statements and loops. These shortcuts provide a very clean, terse way of working with Ruby control structures while also remaining familiar to their ruby counterparts.
123
140
 
124
141
  > [!NOTE]
125
- > RBlade directives are case insensitive and ignore underscores, so depending on your preference, all of `@endif`, `@endIf` and `@end_if` are identical.
142
+ > RBlade directives are case insensitive and ignore underscores, so depending on your preference, all of `@endIf`, `@endIf` and `@end_if` are identical.
126
143
 
127
144
  <a name="if-statements"></a>
128
145
  ### If Statements
129
146
 
130
- You may construct `if` statements using the `@if`, `@elseIf`, `@else`, `@endIf`, `@unless`, and `@endUnless` directives. These directives function identically to their Ruby counterparts:
147
+ You can construct `if` statements using the `@if`, `@elseIf`, `@else`, `@endIf`, `@unless`, and `@endUnless` directives. These directives function identically to their Ruby counterparts:
131
148
 
132
149
  ```rblade
133
150
  @unless(records.nil?)
@@ -141,20 +158,20 @@ You may construct `if` statements using the `@if`, `@elseIf`, `@else`, `@endIf`,
141
158
  @endUnless
142
159
  ```
143
160
 
144
- In addition to the conditional directives above, the `@blank?`, `defined?`, `@empty?`, `@nil?` and `@present` directives may be used as convenient shortcuts:
161
+ In addition to the conditional directives above, the `@blank?`, `defined?`, `@empty?`, `@nil?` and `@present` directives can be used as convenient shortcuts:
145
162
 
146
163
  ```rblade
147
164
  @present?(records)
148
165
  // records is defined and is not nil
149
166
  @else
150
- // These directives are shortcuts to if statements so @else may be used
167
+ // Since these directives are compiled to if statements, you can also use the @else directive
151
168
  @endempty?
152
169
  ```
153
170
 
154
171
  <a name="environment-directives"></a>
155
172
  #### Environment Directives
156
173
 
157
- You may check if the application is running in the production environment using the `@production` directive:
174
+ You can check if the application is running in the production environment using the `@production` directive:
158
175
 
159
176
  ```rblade
160
177
  @production
@@ -162,7 +179,7 @@ You may check if the application is running in the production environment using
162
179
  @endProduction
163
180
  ```
164
181
 
165
- Or, you may determine if the application is running in a specific environment using the `@env` directive:
182
+ Or, you can determine if the application is running in a specific environment using the `@env` directive:
166
183
 
167
184
  ```rblade
168
185
  @env('staging')
@@ -238,7 +255,7 @@ for (user in users)
238
255
  @endFor
239
256
  ```
240
257
 
241
- You may also include the continuation or break condition within the directive declaration:
258
+ You can also include the continuation or break condition within the directive declaration:
242
259
 
243
260
  ```rblade
244
261
  @for (user in users)
@@ -253,7 +270,7 @@ You may also include the continuation or break condition within the directive de
253
270
  <a name="conditional-classes-and-styles"></a>
254
271
  ### Conditional Classes & Styles
255
272
 
256
- The `@class` directive conditionally adds CSS classes. The directive accepts a `Hash` of classes where the key contains the class or classes you wish to add, and the value is a boolean expression:
273
+ The `@class` directive conditionally adds CSS classes. The directive accepts a Hash of classes where the key contains the class or classes you wish to add, and the value is a boolean expression:
257
274
 
258
275
  ```rblade
259
276
  @ruby
@@ -271,7 +288,7 @@ The `@class` directive conditionally adds CSS classes. The directive accepts a `
271
288
  <span class="p-4 text-gray-500 bg-red"></span>
272
289
  ```
273
290
 
274
- Likewise, the `@style` directive may be used to conditionally add inline CSS styles to an HTML element:
291
+ Likewise, the `@style` directive can be used to conditionally add inline CSS styles to an HTML element:
275
292
 
276
293
  ```rblade
277
294
  @ruby
@@ -289,7 +306,7 @@ Likewise, the `@style` directive may be used to conditionally add inline CSS sty
289
306
  <a name="additional-attributes"></a>
290
307
  ### Additional Attributes
291
308
 
292
- For convenience, you may use the `@checked` directive to easily indicate if a given HTML checkbox input is "checked". This directive will print `checked` if the provided condition evaluates to `true`:
309
+ For convenience, you can use the `@checked` directive to easily indicate if a given HTML checkbox input is "checked". This directive will print `checked` if the provided condition evaluates to `true`:
293
310
 
294
311
  ```rblade
295
312
  <input type="checkbox"
@@ -298,7 +315,7 @@ For convenience, you may use the `@checked` directive to easily indicate if a gi
298
315
  @checked(user.active)) />
299
316
  ```
300
317
 
301
- Likewise, the `@selected` directive may be used to indicate if a given select option should be "selected":
318
+ Likewise, the `@selected` directive can be used to indicate if a given select option should be "selected":
302
319
 
303
320
  ```rblade
304
321
  <select name="version">
@@ -310,13 +327,13 @@ Likewise, the `@selected` directive may be used to indicate if a given select op
310
327
  </select>
311
328
  ```
312
329
 
313
- Additionally, the `@disabled` directive may be used to indicate if a given element should be "disabled":
330
+ Additionally, the `@disabled` directive can be used to indicate if a given element should be "disabled":
314
331
 
315
332
  ```rblade
316
333
  <button type="submit" @disabled(isDisabled)>Submit</button>
317
334
  ```
318
335
 
319
- Moreover, the `@readonly` directive may be used to indicate if a given element should be "readonly":
336
+ Moreover, the `@readonly` directive can be used to indicate if a given element should be "readonly":
320
337
 
321
338
  ```rblade
322
339
  <input type="email"
@@ -325,7 +342,7 @@ Moreover, the `@readonly` directive may be used to indicate if a given element s
325
342
  @readonly(!user.isAdmin) />
326
343
  ```
327
344
 
328
- In addition, the `@required` directive may be used to indicate if a given element should be "required":
345
+ In addition, the `@required` directive can be used to indicate if a given element should be "required":
329
346
 
330
347
  ```rblade
331
348
  <input type="text"
@@ -337,7 +354,7 @@ In addition, the `@required` directive may be used to indicate if a given elemen
337
354
  <a name="the-once-directive"></a>
338
355
  ### The `@once` Directive
339
356
 
340
- The `@once` directive allows you to define a portion of the template that will only be evaluated once per rendering cycle. This may be useful for pushing a given piece of JavaScript into the page's header using [stacks](#stacks). For example, if you are rendering a given [component](#components) within a loop, you may wish to only push the JavaScript to the header the first time the component is rendered:
357
+ The `@once` directive allows you to define a portion of the template that will only be evaluated once per rendering cycle. This can be useful for pushing a given piece of JavaScript into the page's header using [stacks](#stacks). For example, if you are rendering a given [component](#components) within a loop, you may wish to only push the JavaScript to the header the first time the component is rendered:
341
358
 
342
359
  ```rblade
343
360
  @once
@@ -402,7 +419,7 @@ Components are a way of including sub-views into your templates. To illustrate h
402
419
 
403
420
  First, we will create a new `alert.rblade` file in the `app/views/components/forms` directory. Templates in the `app/views/components` directory and its subdirectories are are automatically discovered as components, so no further registration is required. Both `.rblade` and `.html.rblade` are valid extensions for RBlade components.
404
421
 
405
- Once your component has been created, it may be rendered using its tag alias:
422
+ Once your component has been created, it can be rendered using its tag alias:
406
423
 
407
424
  ```rblade
408
425
  <x-forms.alert/>
@@ -411,7 +428,7 @@ Once your component has been created, it may be rendered using its tag alias:
411
428
  <a name="rendering-components"></a>
412
429
  ### Rendering Components
413
430
 
414
- To display a component, you may use a Blade component tag within one of your Blade templates. Blade component tags start with the string `x-` followed by the kebab case name of the component class:
431
+ To display a component, you can use a RBlade component tag within one of your RBlade templates. RBlade component tags start with the string `x-` followed by the kebab case name of the component class:
415
432
 
416
433
  ```rblade
417
434
  {{-- Render the `alert` component in app/views/components/ --}}
@@ -462,7 +479,7 @@ You can define a component's data properties using a `@props` directive at the t
462
479
  <div class="{{ type }}">{{ message }}</div>
463
480
  ```
464
481
 
465
- 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:
482
+ 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:
466
483
 
467
484
  ```rblade
468
485
  {{-- This will give an error because the alert component requires a message propery --}}
@@ -479,7 +496,7 @@ All properties in the `@props` directive are automatically removed from `attribu
479
496
  <a name="short-attribute-syntax"></a>
480
497
  #### Short Attribute Syntax
481
498
 
482
- When passing attributes to components, you may also use a "short attribute" syntax. This is often convenient since attribute names frequently match the variable names they correspond to:
499
+ When passing attributes to components, you can also use a "short attribute" syntax. This is often convenient since attribute names frequently match the variable names they correspond to:
483
500
 
484
501
  ```rblade
485
502
  {{-- Short attribute syntax... --}}
@@ -492,7 +509,7 @@ When passing attributes to components, you may also use a "short attribute" synt
492
509
  <a name="escaping-attribute-rendering"></a>
493
510
  #### Escaping Attribute Rendering
494
511
 
495
- Since some JavaScript frameworks such as Alpine.js also use colon-prefixed attributes, you may use a double colon (`::`) prefix to inform RBlade that the attribute is not a Ruby expression. For example, given the following component:
512
+ 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 component:
496
513
 
497
514
  ```rblade
498
515
  <x-button ::class="{ danger: isDeleting }">
@@ -511,13 +528,13 @@ The following HTML will be rendered by RBlade:
511
528
  <a name="component-attributes"></a>
512
529
  ### Component Attributes
513
530
 
514
- We've already examined how to pass data attributes to a component; however, sometimes you may need to specify additional HTML attributes, such as `class`, that are not part of the data required for a component to function. Typically, you want to pass these additional attributes down to the root element of the component template. For example, imagine we want to render an `alert` component like so:
531
+ We've already examined how to pass data attributes to a component; however, sometimes you can need to specify additional HTML attributes, such as `class`, that are not part of the data required for a component to function. Typically, you want to pass these additional attributes down to the root element of the component template. For example, imagine we want to render an `alert` component like so:
515
532
 
516
533
  ```rblade
517
534
  <x-alert type="error" :message class="mt-4"/>
518
535
  ```
519
536
 
520
- All of the attributes that are not part of the component's constructor will automatically be added to the component's "attribute manager". This attribute manager is automatically made available to the component via the `attributes` variable. All of the attributes may be rendered within the component by printing this variable:
537
+ All of the attributes that are not part of the component's constructor will automatically be added to the component's "attribute manager". This attribute manager is automatically made available to the component via the `attributes` variable. All of the attributes can be rendered within the component by printing this variable:
521
538
 
522
539
  ```rblade
523
540
  <div {{ attributes }}>
@@ -528,7 +545,7 @@ All of the attributes that are not part of the component's constructor will auto
528
545
  <a name="default-and-merged-attributes"></a>
529
546
  #### Default & Merged Attributes
530
547
 
531
- Sometimes you may need to specify default values for attributes or merge additional values into some of the component's attributes. To accomplish this, you may 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:
548
+ 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:
532
549
 
533
550
  ```rblade
534
551
  <div {{ attributes.merge({"class": "alert alert-#{type}"}) }}>
@@ -606,19 +623,19 @@ If you need to merge other attributes onto your component, you can chain the `me
606
623
 
607
624
  The attributes manager is a wrapper around the Ruby Hash class. Unless explicitly overwritten, any methods called on the attributes manager will call that same method on the underlying Hash.
608
625
 
609
- You may filter attributes using the `filter` and `slice` methods. These methods call `filter` and `slice` on the underlying Hash and return a new attributes manager with the result.
626
+ You can filter attributes using the `filter` and `slice` methods. These methods call `filter` and `slice` on the underlying Hash and return a new attributes manager with the result.
610
627
 
611
628
  ```rblade
612
629
  {{ attributes.filter { |k, v| k == 'foo'} }}
613
630
  {{ attributes.slice :foo }}
614
631
  ```
615
632
 
616
- If you would like to check if an attribute is present on the component, you may use the `has?` method. This method accepts the attribute name as its only argument and returns a boolean indicating whether or not the attribute is present:
633
+ If you would like to check if an attribute is present on the component, you can use the `has?` method. This method accepts the attribute name as its only argument and returns a boolean indicating whether or not the attribute is present:
617
634
 
618
635
  ```rblade
619
636
  @if (attributes.has?(:class))
620
637
  <div>Class attribute is present</div>
621
- @endif
638
+ @endIf
622
639
  ```
623
640
 
624
641
  If multiple parameters are passed to the `has?` method, the method will determine if all of the given attributes are present on the component:
@@ -626,15 +643,15 @@ If multiple parameters are passed to the `has?` method, the method will determin
626
643
  ```rblade
627
644
  @if (attributes.has?('name', 'class'))
628
645
  <div>All of the attributes are present</div>
629
- @endif
646
+ @endIf
630
647
  ```
631
648
 
632
- The `has_any?` method may be used to determine if any of the given attributes are present on the component:
649
+ The `has_any?` method can be used to determine if any of the given attributes are present on the component:
633
650
 
634
651
  ```rblade
635
652
  @if (attributes.has_any?('href', ':href', 'v-bind:href'))
636
653
  <div>One of the attributes is present</div>
637
- @endif
654
+ @endIf
638
655
  ```
639
656
 
640
657
  <a name="slots"></a>
@@ -649,7 +666,7 @@ You will often need to pass additional content to your component via "slots". Th
649
666
  </div>
650
667
  ```
651
668
 
652
- We may pass content to the `slot` by injecting content into the component:
669
+ We can pass content to the `slot` by injecting content into the component:
653
670
 
654
671
  ```rblade
655
672
  <x-alert>
@@ -657,6 +674,9 @@ We may pass content to the `slot` by injecting content into the component:
657
674
  </x-alert>
658
675
  ```
659
676
 
677
+ > [!NOTE]
678
+ > You can instead use `<//>` as the closing tag of RBlade components; however, this will bypass some of the template sanity checking that the compiler performs.
679
+
660
680
  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:
661
681
 
662
682
  ```rblade
@@ -668,7 +688,7 @@ Sometimes a component may need to render multiple different slots in different l
668
688
  </div>
669
689
  ```
670
690
 
671
- You may define the content of the named slot using the `x-slot` tag. Any content not within an explicit `x-slot` tag will be passed to the component in the `slot` variable:
691
+ You can define the content of the named slot using the `x-slot` tag. Any content not within an explicit `x-slot` tag will be passed to the component in the `slot` variable:
672
692
 
673
693
  ```xml
674
694
  <x-alert>
@@ -690,14 +710,14 @@ The slot object extends the String interface, so you can invoke a slot's `empty?
690
710
  This is default content if the slot is empty.
691
711
  @else
692
712
  {{ slot }}
693
- @endif
713
+ @endIf
694
714
  </div>
695
715
  ```
696
716
 
697
717
  <a name="slot-attributes"></a>
698
718
  #### Slot Attributes
699
719
 
700
- Like RBlade components, you may assign additional [attributes](#component-attributes) to slots such as CSS class names:
720
+ Like RBlade components, you can assign additional [attributes](#component-attributes) to slots such as CSS class names:
701
721
 
702
722
  ```xml
703
723
  <x-card class="shadow-sm">
@@ -713,7 +733,7 @@ Like RBlade components, you may assign additional [attributes](#component-attrib
713
733
  </x-card>
714
734
  ```
715
735
 
716
- To interact with slot attributes, you may access the `attributes` property of the slot's variable. For more information on how to interact with attributes, please consult the documentation on [component attributes](#component-attributes):
736
+ To interact with slot attributes, you can access the `attributes` property of the slot's variable. For more information on how to interact with attributes, please consult the documentation on [component attributes](#component-attributes):
717
737
 
718
738
  ```rblade
719
739
  @props({
@@ -734,6 +754,18 @@ To interact with slot attributes, you may access the `attributes` property of th
734
754
  </div>
735
755
  ```
736
756
 
757
+ <a name="conditional-rendering"></a>
758
+ #### Conditional Rendering
759
+
760
+ Sometimes, you may wish to return early from a component without printing anything. For example, if you make an error component and no errors are passed in as properties, you might want to skip rendering. You can use the `@shouldRender` directive anywhere within a component to prevent the component from being rendered:
761
+
762
+ ```rblade
763
+ {{-- components/error.rblade --}}
764
+ @props({errors: []})
765
+ @shouldRender(errors.present?)
766
+ ...
767
+ ```
768
+
737
769
  <a name="registering-additional-component-directories"></a>
738
770
  ### Registering Additional Component Directories
739
771
 
@@ -822,7 +854,7 @@ RBlade allows you to push to named stacks which can be rendered elsewhere in ano
822
854
  @endPush
823
855
  ```
824
856
 
825
- If you would like to `@push` content if a given boolean expression evaluates to `true`, you may use the `@pushif` directive:
857
+ If you would like to `@push` content if a given boolean expression evaluates to `true`, you can use the `@pushif` directive:
826
858
 
827
859
  ```rblade
828
860
  @pushIf(shouldPush, 'scripts')
@@ -830,7 +862,7 @@ If you would like to `@push` content if a given boolean expression evaluates to
830
862
  @endPushIf
831
863
  ```
832
864
 
833
- You may push to a stack as many times as needed. To render the complete stack contents, pass the name of the stack to the `@stack` directive:
865
+ You can push to a stack as many times as needed. To render the complete stack contents, pass the name of the stack to the `@stack` directive:
834
866
 
835
867
  ```rblade
836
868
  <head>
data/REFERENCE.md ADDED
@@ -0,0 +1,138 @@
1
+ <a name="quick-reference"></a>
2
+ # Quick Reference
3
+
4
+ A table below provides a quick overview of RBlade syntax and directives. The [readme](README.md) has a more in depth look at RBlade's capabilities.
5
+
6
+ | Syntax | Description |
7
+ |:--------------------------------------------------------|:------------------------------------------------------------------------------------------|
8
+ | `{{ RUBY_EXPRESSION }}`<br/>`<%= RUBY_EXPRESSION %>` | Print the string value of the ruby expression, escaping HTML special characters |
9
+ | `{!! RUBY_EXPRESSION !!}`<br/>`<%== RUBY_EXPRESSION %>` | Print the string value of the ruby expression, _without_ escaping HTML special characters |
10
+ | `@ruby( RUBY_EXPRESSION )` | Execute an inline ruby expression |
11
+ | `@ruby ... @endRuby` | Execute a block of ruby code |
12
+ | `{{-- ... --}}`<br/>`<%# ... %>` | Comments, removed from the compiled template with no performance cost |
13
+ | `@verbatim ... @endVerbatim` | Print the given content without parsing RBlade directives |
14
+
15
+ <a name="quick-reference-components"></a>
16
+ ## Components
17
+
18
+ By default, RBlade will look for components in the `app/views/components` folder. Additionally, components in the `layout` namespace are found in the `app/views/layouts` folder, and components in the `view` namespace are found in the `app/views` folder.
19
+
20
+ | Syntax | Description |
21
+ |:---------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------|
22
+ | `<x-component.name/>` | Render the component found at `app/views/components/name{.rblade,.html.rblade}` or `app/views/components/name/index{.rblade,.html.rblade}` |
23
+ | `<x-layout::name/>` | Render the "name" component found in the "layout" namespace folder |
24
+ | `<x-component.name>...</x-component.name>` | Render the component with the given slot content |
25
+ | `<x-component.name>...<//>` | Short closing tag syntax (note: this bypasses some sanity checking during compilation) |
26
+ | `<x-name attribute="STRING"/>` | Pass a string value to a component |
27
+ | `<x-name :attribute="RUBY_EXPRESSION"/>` | Pass a ruby expression, executed in the local scope, to a component |
28
+ | `<x-name :attribute/>` | Pass the `attribute` local variable into a component |
29
+ | `<x-name @class({'bg-red-600': is_error})/>` | Conditionally pass classes to a component |
30
+ | `<x-name @style({'bg-red-600': is_error})/>` | Conditionally pass styles to a component |
31
+ | `<x-name attribute/>` | Pass an attribute to a component with value `true` |
32
+ | `<x-name {{ attributes }}/>` | Pass attributes to a child component |
33
+ | `@props({header: "Header"})` | Remove `header` from the attributes Hash and introduce it as a local variable, using the specified value as a default |
34
+ | `@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
+ | `{{ slot }}` | Output the block content passed into the current component |
36
+ | `<x-name><x-slot::header><h1>Header</h1><//>Content<//>` | Pass a named block to a component |
37
+ | `{{ header }}` | Output the contents of a named block |
38
+ | `<div {{ header.attributes }}>` | Output the attributes passed into a named block |
39
+
40
+ <a name="quick-reference-attributes"></a>
41
+ ## Attributes
42
+
43
+ The attributes variable is an instance of a class that manages attributes. As well as the methods used in the examples below, you can use any method belonging to the Hash class.
44
+
45
+ | Syntax | Description |
46
+ |:-------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------|
47
+ | `<div {{ attributes }}>` | Print the contents of attributes as HTML attributes |
48
+ | `<div {{ attributes.merge({class: "text-black", type: "button"}) }}>` | Merge in additional attributes, combining the `:class` and `:style`, and setting defaults for other values |
49
+ | `<div {{ attributes.except(['type']) }}>` | Output the attributes array excluding the given keys |
50
+ | `<div {{ attributes.only(['type']) }}>` | Output only the given keys of the attributes array |
51
+ | `<div {{ attributes.filter { \|key, value\| key.start_with? "on:" } }}>` | Output the attributes for which the block returns true |
52
+ | `<div @class({'bg-red-600': is_error})>` | Conditionally print classes in an HTML class attribute |
53
+ | `<div @style({'bg-red-600': is_error})>` | Conditionally print styles in an HTML style attribute |
54
+
55
+ <a name="quick-reference-conditions"></a>
56
+ ## Conditions
57
+
58
+ | Syntax | Description |
59
+ |:----------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------|
60
+ | `@if( RUBY_EXPRESSION ) ... @endIf` | Compiles to a Ruby if statement |
61
+ | `@if( RUBY_EXPRESSION ) ... @else ... @endIf` | Compiles to a Ruby if/else statement |
62
+ | `@if( RUBY_EXPRESSION ) ... @elsif( RUBY_EXPRESSION ) ... @endIf` | Compiles to a Ruby if/elsif statement |
63
+ | `@unless( RUBY_EXPRESSION ) ... @endunless` | Compiles to a Ruby unless statement |
64
+ | `@case( RUBY_EXPRESSION ) @when(1) ... @when(2) ... @else ... @endIf` | Compiles to a Ruby case statement |
65
+ | `@blank?( RUBY_EXPRESSION ) ... @endBlank?` | Compiles to a Ruby if statement that calls `blank?` method on the given expression |
66
+ | `@defined?( RUBY_EXPRESSION ) ... @endDefined?` | Compiles to a Ruby if statement that calls `defined?` function on the given expression |
67
+ | `@empty?( RUBY_EXPRESSION ) ... @endEmpty?` | Compiles to a Ruby if statement that calls `empty?` method on the given expression |
68
+ | `@nil?( RUBY_EXPRESSION ) ... @endNil?` | Compiles to a Ruby if statement that calls `nil?` method on the given expression |
69
+ | `@present?( RUBY_EXPRESSION ) ... @endPresent?` | Compiles to a Ruby if statement that calls `present?` method on the given expression |
70
+ | `@env(['development', 'test']) ... @endEnv` | Compiles to a Ruby if statement that checks if the current Rails environment matches any of the given environments |
71
+ | `@production ... @endProduction` | Shortcut for `@env('production')` |
72
+ | `@once ... @endOnce` | Render the given block the first time it appears in the template |
73
+ | `@once('unique key') ... @endOnce` | Render the given block the first time "unique key" is used in the template |
74
+
75
+ <a name="quick-reference-loops"></a>
76
+ ## Loops
77
+
78
+ | Syntax | Description |
79
+ |:------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------|
80
+ | `@while( looping ) ... @endWhile` | Compiles to a Ruby while statement |
81
+ | `@until( finished ) ... @endUntil` | Compiles to a Ruby until statement |
82
+ | `@for( i in 1..10 ) ... @endFor` | Compiles to a Ruby for loop |
83
+ | `@each( i in 1..10 ) ... @endEach` | Calls `each` on the given collection, with `\|i\|` as the block argument |
84
+ | `@each( key, value in {a: 1} ) ... @endEach` | Calls `each` on the given Hash, with `\|key, value\|` as the block arguments |
85
+ | `@forElse( i in 1..10 ) ... @empty ... @endForElse` | Compiles to a for loop as above, but the block after `@empty` is printed if the given collection is empty |
86
+ | `@eachElse( i in 1..10 ) ... @empty ... @endEachElse` | Compiles to a each loop as above, but the block after `@empty` is printed if the given collection is empty |
87
+ | `@break` | Break out of the current loop |
88
+ | `@next` | Go to the next iteration in the current loop |
89
+ | `@break( RUBY_EXPRESSION )` | Break out of the current loop if the expression evaluate to true |
90
+ | `@next( RUBY_EXPRESSION )` | Go to the next iteration in the current loop if the expression evaluate to true |
91
+
92
+ <a name="quick-reference-forms"></a>
93
+ ## Forms
94
+
95
+ | Syntax | Description |
96
+ |:---------------------------------|:-----------------------------------------------------------------------------------------------------------------|
97
+ | `@old(:email, user[:email])` | Fetches `:email` from the params Hash, defaulting to the user's email if it doesn't exist |
98
+ | `@method('PATCH')` | Prints a hidden input setting the HTTP request type to the given value using the Rails MethodOverride middleware |
99
+ | `@DELETE` | Shortcut for `@method('DELETE')` |
100
+ | `@PATCH` | Shortcut for `@method('PATCH')` |
101
+ | `@PUT` | Shortcut for `@method('PUT')` |
102
+ | `@checked( RUBY_EXPRESSION )` | Prints "checked" if the ruby expression evaluates to true |
103
+ | `@disabled( RUBY_EXPRESSION )` | Prints "disabled" if the ruby expression evaluates to true |
104
+ | `@readonly( RUBY_EXPRESSION )` | Prints "readonly" if the ruby expression evaluates to true |
105
+ | `@required( RUBY_EXPRESSION )` | Prints "required" if the ruby expression evaluates to true |
106
+ | `@selected( RUBY_EXPRESSION )` | Prints "selected" if the ruby expression evaluates to true |
107
+
108
+ <a name="quick-reference-stacks"></a>
109
+ ## Stacks
110
+
111
+ Stacks are a way of rendering content outside of the usual document order. For example, you could define a "sidebar" stack, then you'd be able to add content to that sidebar from anywhere else in the site.
112
+
113
+ Note that the stack is printed once the current view or component finishes.
114
+
115
+ | Syntax | Description |
116
+ |:------------------------------------------------------------|:--------------------------------------------------------------------------------------------|
117
+ | `@stack('scripts')` | Start a stack with the name 'scripts'. Stacks can be pushed to elsewhere in the code. |
118
+ | `@push('scripts') ... @endPush` | Add block content to the 'scripts' stack |
119
+ | `@prepend('scripts') ... @endPrepend` | Add block content to the start of the 'scripts' stack |
120
+ | `@pushIf(RUBY_EXPRESSION, 'scripts') ... @endPushIf` | Add block content to the 'scripts' stack if the expression evaluate to true |
121
+ | `@prependIf(RUBY_EXPRESSION, 'scripts') ... @endprependIf` | Add block content to the start of the 'scripts' stack if the expression evaluate to true |
122
+ | `@pushOnce('scripts') ... @endPushOnce` | Add block content to the 'scripts' stack only once |
123
+ | `@prependOnce('scripts') ... @endPrependOnce` | Add block content to the start of the 'scripts' stack only once |
124
+ | `@pushOnce('scripts', 'unique key') ... @endPushOnce` | Add block content to the 'scripts' stack the first time "unique key" is pushed |
125
+ | `@prependOnce('scripts', 'unique key') ... @endPrependOnce` | Add block content to the start of the 'scripts' stack the first time "unique key" is pushed |
126
+
127
+ ## Tips
128
+
129
+ * Except for `@push`, `@prepend` and their variants, all end directives can simply be replaced with `@end` if preferred:
130
+ - `@nil?(...) ... @endnil?`
131
+ - `@nil?(...) ... @endnil`
132
+ - `@nil?(...) ... @end`
133
+ * Except for `@ruby` and `@verbatim`, directives are case insensitive and can contain underscores. The following are identical:
134
+ - `@pushonce`
135
+ - `@pushOnce`
136
+ - `@PushOnce`
137
+ - `@push_once`
138
+ - `@PUSHONCE`
@@ -0,0 +1,3 @@
1
+ # RBlade Examples
2
+
3
+ This folder is structured like a Rails `app/views` folder and contains some examples of RBlade templates.
@@ -0,0 +1,23 @@
1
+ @ruby
2
+ # Typically these values would be set in the controller
3
+ banner_image = 'banner.png'
4
+ alert = nil
5
+ @endruby
6
+ <x-layout::app title="Home page">
7
+ <x-content.alert :alert/>
8
+ <x-content.alert alert="This is an alert"/>
9
+ <x-h1 blue>Welcome to the home page!</x-h1>
10
+
11
+ <x-p class="mt-2">
12
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alias amet architecto, at commodi consectetur ipsa itaque magni minus neque nostrum pariatur possimus recusandae sunt! Est explicabo praesentium quidem vero voluptatum!
13
+ </x-p>
14
+
15
+ @if (banner_image)
16
+ <x-content.hero class="mt-4">
17
+ <x-slot::heading class="text-blue-600">
18
+ Beatae placeat porro quibusdam repudiandae sunt?
19
+ </x-slot::heading>
20
+ Adipisci blanditiis, dolor dolorem, ea iste laudantium minima natus nesciunt nostrum odio perferendis praesentium quae, similique.
21
+ </x-content.hero>
22
+ @endif
23
+ </x-layout::app>
@@ -0,0 +1,6 @@
1
+ @props({alert: _required})
2
+ @shouldRender(alert.present?)
3
+ <p class="py-2 px-3 bg-amber-100 border border-amber-500">
4
+ @@alert
5
+ {{ alert }}
6
+ </p>
@@ -0,0 +1,14 @@
1
+ @props({heading: nil})
2
+ @pushOnce('scripts')
3
+ <script src="/js/hero.js"></script>
4
+ @endPushOnce
5
+ <div class="bg-blue-50 p-16">
6
+ <div class="max-w-96">
7
+ @if (heading)
8
+ <h2 {{ heading.attributes.merge({class: "text-lg font-bold"}) }}>
9
+ {{ heading }}
10
+ </h2>
11
+ @end
12
+ <p class="mt-4">{{ slot }}</p>
13
+ </div>
14
+ </div>
@@ -0,0 +1,6 @@
1
+ @props({blue: false})
2
+ <h1 {{ attributes.merge({
3
+ class: "text-xl font-bold #{blue ? 'text-blue-700' : 'text-black'}"
4
+ }) }}>
5
+ {{ slot }}
6
+ </h1>
@@ -0,0 +1,14 @@
1
+ <p {{ attributes.merge({
2
+ class: "
3
+ text-base font-normal
4
+ {{--
5
+ The tailwindcss-unimportant plugin works well with component design:
6
+ the "-:" prefix allows the consumer of the component to easily override
7
+ the component classes and extend the component.
8
+ See: https://www.npmjs.com/package/tailwindcss-unimportant
9
+ --}}
10
+ -:mt-1
11
+ "
12
+ }) }}>
13
+ {{ slot }}
14
+ </p>
@@ -0,0 +1,16 @@
1
+ @props({title: _required})
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
8
+ <title>{{ title }}</title>
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+ @stack('scripts')
11
+ </head>
12
+ <body>
13
+ <div {{ attributes.merge({class: "max-w-prose mt-8 mx-auto flex flex-col gap-4"}) }}>
14
+ {{ slot }}
15
+ </body>
16
+ </html>
@@ -51,11 +51,11 @@ module RBlade
51
51
 
52
52
  namespace = nil
53
53
  name = component[:name]
54
- if name.match '::'
54
+ if name.match? "::"
55
55
  namespace, name = component[:name].split("::")
56
56
  end
57
57
 
58
- code = if namespace == 'slot'
58
+ if namespace == "slot"
59
59
  compile_slot_end name, component
60
60
  else
61
61
  compile_component_end component
@@ -1,22 +1,23 @@
1
1
  module RBlade
2
- class CompilesEchos
2
+ class CompilesPrints
3
3
  def compile!(tokens)
4
- compile_regular_echos!(tokens)
5
- compile_unsafe_echos!(tokens)
4
+ compile_unsafe_prints!(tokens)
5
+ compile_regular_prints!(tokens)
6
6
  end
7
7
 
8
8
  private
9
9
 
10
- def compile_regular_echos!(tokens)
11
- compile_echos! tokens, "{{", "}}", "RBlade.e"
12
- compile_echos! tokens, "<%=", "%>", "RBlade.e"
10
+ def compile_regular_prints!(tokens)
11
+ compile_prints! tokens, "{{", "}}", "RBlade.e"
12
+ compile_prints! tokens, "<%=", "%>", "RBlade.e"
13
13
  end
14
14
 
15
- def compile_unsafe_echos!(tokens)
16
- compile_echos! tokens, "{!!", "!!}"
15
+ def compile_unsafe_prints!(tokens)
16
+ compile_prints! tokens, "{!!", "!!}"
17
+ compile_prints! tokens, "<%==", "%>"
17
18
  end
18
19
 
19
- def compile_echos!(tokens, start_token, end_token, wrapper_function = nil)
20
+ def compile_prints!(tokens, start_token, end_token, wrapper_function = nil)
20
21
  tokens.map! do |token|
21
22
  next(token) if token.type != :unprocessed
22
23
 
@@ -41,7 +42,7 @@ module RBlade
41
42
  else
42
43
  "(" + segments[i] + ").to_s;"
43
44
  end
44
- segments[i] = Token.new(:echo, segment_value)
45
+ segments[i] = Token.new(:print, segment_value)
45
46
 
46
47
  i += 1
47
48
  elsif !segments[i].nil? && segments[i] != ""
@@ -1,10 +1,10 @@
1
+ require "rblade/compiler/statements/compiles_component_helpers"
1
2
  require "rblade/compiler/statements/compiles_conditionals"
2
3
  require "rblade/compiler/statements/compiles_form"
3
4
  require "rblade/compiler/statements/compiles_html_attributes"
4
5
  require "rblade/compiler/statements/compiles_inline_ruby"
5
6
  require "rblade/compiler/statements/compiles_loops"
6
7
  require "rblade/compiler/statements/compiles_once"
7
- require "rblade/compiler/statements/compiles_props"
8
8
  require "rblade/compiler/statements/compiles_stacks"
9
9
 
10
10
  module RBlade
@@ -71,7 +71,6 @@ module RBlade
71
71
  @@statement_handlers = {
72
72
  "blank?" => [CompilesConditionals, :compileBlank],
73
73
  "break" => [CompilesLoops, :compileBreak],
74
- "breakif" => [CompilesLoops, :compileBreakIf],
75
74
  "case" => [CompilesConditionals, :compileCase],
76
75
  "checked" => [CompilesConditionals, :compileChecked],
77
76
  "class" => [CompilesHtmlAttributes, :compileClass],
@@ -97,7 +96,6 @@ module RBlade
97
96
  "if" => [CompilesConditionals, :compileIf],
98
97
  "method" => [CompilesForm, :compileMethod],
99
98
  "next" => [CompilesLoops, :compileNext],
100
- "nextif" => [CompilesLoops, :compileNextIf],
101
99
  "nil?" => [CompilesConditionals, :compileNil],
102
100
  "old" => [CompilesForm, :compileOld],
103
101
  "once" => [CompilesOnce, :compileOnce],
@@ -107,7 +105,7 @@ module RBlade
107
105
  "prependonce" => [CompilesOnce, :compilePrependOnce],
108
106
  "present?" => [CompilesConditionals, :compilePresent],
109
107
  "production" => [CompilesConditionals, :compileProduction],
110
- "props" => [CompilesProps, :compileProps],
108
+ "props" => [CompilesComponentHelpers, :compileProps],
111
109
  "push" => [CompilesStacks, :compilePush],
112
110
  "pushif" => [CompilesStacks, :compilePushIf],
113
111
  "pushonce" => [CompilesOnce, :compilePushOnce],
@@ -116,6 +114,7 @@ module RBlade
116
114
  "required" => [CompilesConditionals, :compileRequired],
117
115
  "ruby" => [CompilesInlineRuby, :compile],
118
116
  "selected" => [CompilesConditionals, :compileSelected],
117
+ "shouldrender" => [CompilesComponentHelpers, :compileShouldRender],
119
118
  "stack" => [CompilesStacks, :compileStack],
120
119
  "style" => [CompilesHtmlAttributes, :compileStyle],
121
120
  "unless" => [CompilesConditionals, :compileUnless],
@@ -2,10 +2,18 @@ require "rblade/helpers/tokenizer"
2
2
 
3
3
  module RBlade
4
4
  class CompilesStatements
5
- class CompilesProps
5
+ class CompilesComponentHelpers
6
+ def compileShouldRender args
7
+ if args&.count != 1
8
+ raise StandardError.new "Should render statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
9
+ end
10
+
11
+ "unless(#{args[0]});return'';end;"
12
+ end
13
+
6
14
  def compileProps args
7
- if args.nil?
8
- raise StandardError.new "Props statement: wrong number of arguments (given #{args&.count}, expecting 1)"
15
+ if args&.count != 1
16
+ raise StandardError.new "Props statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
9
17
  end
10
18
 
11
19
  props = extractProps args[0]
@@ -13,8 +21,8 @@ module RBlade
13
21
  compiled_code = ""
14
22
  if value == "_required"
15
23
  compiled_code << "if !attributes.has?(:'#{RBlade.escape_quotes(key)}');raise \"Props statement: #{key} is not defined\";end;"
16
- end
17
- if isValidVariableName key
24
+ compiled_code << "#{key}=attributes[:'#{RBlade.escape_quotes(key)}'];attributes.delete :'#{RBlade.escape_quotes(key)}';"
25
+ elsif isValidVariableName key
18
26
  compiled_code << "#{key}=attributes[:'#{RBlade.escape_quotes(key)}'].nil? ? #{value} : attributes[:'#{RBlade.escape_quotes(key)}'];"
19
27
  compiled_code << "attributes.delete :'#{RBlade.escape_quotes(key)}';"
20
28
  else
@@ -58,8 +58,8 @@ module RBlade
58
58
  end
59
59
 
60
60
  def compileElse args
61
- if !args.nil?
62
- raise StandardError.new "Else statement: wrong number of arguments (given #{args&.count || 0}, expecting 0)"
61
+ unless args.nil?
62
+ raise StandardError.new "Else statement: wrong number of arguments (given #{args.count}, expecting 0)"
63
63
  end
64
64
 
65
65
  "else;"
@@ -82,8 +82,8 @@ module RBlade
82
82
  end
83
83
 
84
84
  def compileWhen args
85
- if args.nil? || args.count == 0
86
- raise StandardError.new "When statement: wrong number of arguments (given #{args&.count || 0}, expecting at least 1)"
85
+ if args.nil?
86
+ raise StandardError.new "When statement: wrong number of arguments (given 0, expecting at least 1)"
87
87
  end
88
88
 
89
89
  "when #{args.join ","};"
@@ -141,19 +141,11 @@ module RBlade
141
141
 
142
142
  def compileProduction args
143
143
  unless args.nil?
144
- raise StandardError.new "Production statement: wrong number of arguments (given #{args.count}, expecting 1)"
144
+ raise StandardError.new "Production statement: wrong number of arguments (given #{args.count}, expecting 0)"
145
145
  end
146
146
 
147
147
  "if Rails.env.production?;"
148
148
  end
149
-
150
- def compileOnce args
151
- if args&.count&.> 1
152
- raise StandardError.new "Production statement: wrong number of arguments (given #{args.count}, expecting 0 or 1)"
153
- end
154
-
155
- args[0].nil? ? "" : args[0]
156
- end
157
149
  end
158
150
  end
159
151
  end
@@ -3,40 +3,40 @@ module RBlade
3
3
  class CompilesForm
4
4
  def compileMethod args
5
5
  if args&.count != 1
6
- raise StandardError.new "Once statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
6
+ raise StandardError.new "Method statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
7
7
  end
8
- method = RBlade.h(args[0].tr "\"'", "")
8
+ method = RBlade.h(args[0].tr("\"'", ""))
9
9
 
10
- %[_out<<'<input type="hidden" name="_method" value="#{method}">';]
10
+ %(_out<<'<input type="hidden" name="_method" value="#{method}">';)
11
11
  end
12
12
 
13
13
  def compileDelete args
14
- if !args.nil?
15
- raise StandardError.new "Once statement: wrong number of arguments (given #{args.count}, expecting 0)"
14
+ unless args.nil?
15
+ raise StandardError.new "Delete statement: wrong number of arguments (given #{args.count}, expecting 0)"
16
16
  end
17
17
 
18
- compileMethod(['DELETE'])
18
+ compileMethod(["DELETE"])
19
19
  end
20
20
 
21
21
  def compilePatch args
22
- if !args.nil?
23
- raise StandardError.new "Once statement: wrong number of arguments (given #{args.count}, expecting 0)"
22
+ unless args.nil?
23
+ raise StandardError.new "Patch statement: wrong number of arguments (given #{args.count}, expecting 0)"
24
24
  end
25
25
 
26
- compileMethod(['PATCH'])
26
+ compileMethod(["PATCH"])
27
27
  end
28
28
 
29
29
  def compilePut args
30
- if !args.nil?
31
- raise StandardError.new "Once statement: wrong number of arguments (given #{args.count}, expecting 0)"
30
+ unless args.nil?
31
+ raise StandardError.new "Put statement: wrong number of arguments (given #{args.count}, expecting 0)"
32
32
  end
33
33
 
34
- compileMethod(['PUT'])
34
+ compileMethod(["PUT"])
35
35
  end
36
36
 
37
37
  def compileOld args
38
38
  if args.nil? || args.count > 2
39
- raise StandardError.new "Once statement: wrong number of arguments (given #{args.count}, expecting 0)"
39
+ raise StandardError.new "Old statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 or 2)"
40
40
  end
41
41
 
42
42
  default_value = args[1] || "''"
@@ -7,7 +7,7 @@ module RBlade
7
7
 
8
8
  def compileBreak args
9
9
  if args&.count&.> 1
10
- raise StandardError.new "Break statement: wrong number of arguments (given #{args&.count}, expecting 0 or 1)"
10
+ raise StandardError.new "Break statement: wrong number of arguments (given #{args.count}, expecting 0 or 1)"
11
11
  end
12
12
 
13
13
  if args.nil?
@@ -31,7 +31,7 @@ module RBlade
31
31
 
32
32
  def compileEachElse args
33
33
  if args.nil? || args.count > 2
34
- raise StandardError.new "Each statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
34
+ raise StandardError.new "Each else statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
35
35
  end
36
36
  # Allow variables to be a key, value pair
37
37
  args = args.join ","
@@ -59,7 +59,11 @@ module RBlade
59
59
  "_looped_#{@loop_else_counter}=false;for #{args[0]};_looped_#{@loop_else_counter}=true;"
60
60
  end
61
61
 
62
- def compileEmpty
62
+ def compileEmpty args
63
+ unless args.nil?
64
+ raise StandardError.new "Empty statement: wrong number of arguments (given #{args.count}, expecting 0)"
65
+ end
66
+
63
67
  @loop_else_counter -= 1
64
68
 
65
69
  "end;if !_looped_#{@loop_else_counter + 1};"
@@ -67,7 +71,7 @@ module RBlade
67
71
 
68
72
  def compileNext args
69
73
  if args&.count&.> 1
70
- raise StandardError.new "For statement: wrong number of arguments (given #{args&.count || 0}, expecting 0 or 1)"
74
+ raise StandardError.new "Next statement: wrong number of arguments (given #{args.count}, expecting 0 or 1)"
71
75
  end
72
76
 
73
77
  if args.nil?
@@ -17,7 +17,7 @@ module RBlade
17
17
 
18
18
  def compilePushOnce args
19
19
  if args&.count != 1 && args&.count != 2
20
- raise StandardError.new "Push once statement: wrong number of arguments (given #{args.count}, expecting 1 or 2)"
20
+ raise StandardError.new "Push once statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 or 2)"
21
21
  end
22
22
  @once_counter += 1
23
23
  once_id = args[1].nil? ? ":_#{@once_counter}" : args[1]
@@ -27,8 +27,8 @@ module RBlade
27
27
  end
28
28
 
29
29
  def compileEndPushOnce args
30
- if !args.nil?
31
- raise StandardError.new "End push once statement: wrong number of arguments (given #{args&.count}, expecting 0)"
30
+ unless args.nil?
31
+ raise StandardError.new "End push once statement: wrong number of arguments (given #{args.count}, expecting 0)"
32
32
  end
33
33
 
34
34
  "RBlade::StackManager.push(_p1_#{@once_counter}, _out);_out=_p1_#{@once_counter}_b;end;"
@@ -36,7 +36,7 @@ module RBlade
36
36
 
37
37
  def compilePrependOnce args
38
38
  if args&.count != 1 && args&.count != 2
39
- raise StandardError.new "Prepend once statement: wrong number of arguments (given #{args.count}, expecting 1 or 2)"
39
+ raise StandardError.new "Prepend once statement: wrong number of arguments (given #{args&.count || 0}, expecting 1 or 2)"
40
40
  end
41
41
  @once_counter += 1
42
42
  once_id = args[1].nil? ? ":_#{@once_counter}" : args[1]
@@ -46,8 +46,8 @@ module RBlade
46
46
  end
47
47
 
48
48
  def compileEndPrependOnce args
49
- if !args.nil?
50
- raise StandardError.new "End prepend once statement: wrong number of arguments (given #{args&.count}, expecting 0)"
49
+ unless args.nil?
50
+ raise StandardError.new "End prepend once statement: wrong number of arguments (given #{args.count}, expecting 0)"
51
51
  end
52
52
 
53
53
  "RBlade::StackManager.prepend(_p1_#{@once_counter}, _out);_out=_p1_#{@once_counter}_b;end;"
@@ -7,7 +7,7 @@ module RBlade
7
7
 
8
8
  def compileStack args
9
9
  if args&.count != 1
10
- raise StandardError.new "Stack statement: wrong number of arguments (given #{args&.count}, expecting 1)"
10
+ raise StandardError.new "Stack statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
11
11
  end
12
12
 
13
13
  "RBlade::StackManager.initialize(#{args[0]}, _out);_stacks.push(#{args[0]});_out = '';"
@@ -15,7 +15,7 @@ module RBlade
15
15
 
16
16
  def compilePrepend args
17
17
  if args&.count != 1
18
- raise StandardError.new "Prepend statement: wrong number of arguments (given #{args&.count}, expecting 1)"
18
+ raise StandardError.new "Prepend statement: wrong number of arguments (given #{args&.count || 0}, expecting 1)"
19
19
  end
20
20
 
21
21
  @push_counter += 1
@@ -24,7 +24,7 @@ module RBlade
24
24
  end
25
25
 
26
26
  def compileEndPrepend args
27
- if !args.nil?
27
+ unless args.nil?
28
28
  raise StandardError.new "End prepend statement: wrong number of arguments (given #{args&.count}, expecting 0)"
29
29
  end
30
30
 
@@ -42,8 +42,8 @@ module RBlade
42
42
  end
43
43
 
44
44
  def compileEndPrependIf args
45
- if !args.nil?
46
- raise StandardError.new "End push if statement: wrong number of arguments (given #{args&.count}, expecting 0)"
45
+ unless args.nil?
46
+ raise StandardError.new "End prepend if statement: wrong number of arguments (given #{args.count}, expecting 0)"
47
47
  end
48
48
 
49
49
  "end;" + compileEndPush(nil)
@@ -61,15 +61,15 @@ module RBlade
61
61
 
62
62
  def compilePushIf args
63
63
  if args&.count != 2
64
- raise StandardError.new "Push if statement: wrong number of arguments (given #{args&.count}, expecting 2)"
64
+ raise StandardError.new "Push if statement: wrong number of arguments (given #{args&.count || 0}, expecting 2)"
65
65
  end
66
66
 
67
67
  "if #{args[0]};" + compilePush([args[1]])
68
68
  end
69
69
 
70
70
  def compileEndPush args
71
- if !args.nil?
72
- raise StandardError.new "End push statement: wrong number of arguments (given #{args&.count}, expecting 0)"
71
+ unless args.nil?
72
+ raise StandardError.new "End push statement: wrong number of arguments (given #{args.count}, expecting 0)"
73
73
  end
74
74
 
75
75
  @push_counter -= 1
@@ -78,8 +78,8 @@ module RBlade
78
78
  end
79
79
 
80
80
  def compileEndPushIf args
81
- if !args.nil?
82
- raise StandardError.new "End push if statement: wrong number of arguments (given #{args&.count}, expecting 0)"
81
+ unless args.nil?
82
+ raise StandardError.new "End push if statement: wrong number of arguments (given #{args.count}, expecting 0)"
83
83
  end
84
84
 
85
85
  "end;" + compileEndPush(nil)
@@ -79,7 +79,7 @@ module RBlade
79
79
  case segments[i][:value][:name]
80
80
  when "case"
81
81
  # Remove any whitespace before a when statement
82
- until segments[i + 1].nil? || segments[i + 1] == '@'
82
+ until segments[i + 1].nil? || segments[i + 1] == "@"
83
83
  segments.delete_at i + 1
84
84
  end
85
85
  end
@@ -1,6 +1,6 @@
1
1
  require "rblade/compiler/compiles_comments"
2
2
  require "rblade/compiler/compiles_components"
3
- require "rblade/compiler/compiles_echos"
3
+ require "rblade/compiler/compiles_prints"
4
4
  require "rblade/compiler/compiles_ruby"
5
5
  require "rblade/compiler/compiles_verbatim"
6
6
  require "rblade/compiler/compiles_statements"
@@ -37,7 +37,7 @@ module RBlade
37
37
  CompilesRuby.new.compile! tokens
38
38
  TokenizesComponents.new.tokenize! tokens
39
39
  TokenizesStatements.new.tokenize! tokens
40
- CompilesEchos.new.compile! tokens
40
+ CompilesPrints.new.compile! tokens
41
41
  CompilesStatements.new.compile! tokens
42
42
  CompilesComponents.new.compile! tokens
43
43
 
@@ -49,7 +49,7 @@ module RBlade
49
49
 
50
50
  CompilesComments.compile!(tokens)
51
51
  CompilesRuby.compile! tokens
52
- CompilesEchos.compile!(tokens)
52
+ CompilesPrints.compile!(tokens)
53
53
 
54
54
  compileTokens tokens
55
55
  end
@@ -15,15 +15,17 @@ module RBlade
15
15
  @attributes[key]
16
16
  end
17
17
 
18
- def has?(key)
19
- !@attributes[key].nil?
18
+ def has?(*keys)
19
+ keys.map!(&:to_sym)
20
+
21
+ keys.all? { |key| @attributes.has_key? key }
20
22
  end
21
23
 
22
- def method_missing(method, *, &block)
24
+ def method_missing(method, *, &)
23
25
  if [:select, :filter, :slice].include? method
24
- AttributesManager.new @attributes.send(method, *, &block)
26
+ AttributesManager.new @attributes.send(method, *, &)
25
27
  else
26
- @attributes.send(method, *, &block)
28
+ @attributes.send(method, *, &)
27
29
  end
28
30
  end
29
31
 
@@ -91,12 +93,6 @@ module RBlade
91
93
  AttributesManager.new new_attributes
92
94
  end
93
95
 
94
- def has?(*keys)
95
- keys.map!(&:to_sym)
96
-
97
- keys.all? { |key| @attributes.has_key? key }
98
- end
99
-
100
96
  def has_any?(*keys)
101
97
  keys.map!(&:to_sym)
102
98
 
@@ -24,8 +24,6 @@ module RBlade
24
24
  @content.respond_to?(method_name)
25
25
  end
26
26
 
27
- def attributes
28
- @attributes
29
- end
27
+ attr_reader :attributes
30
28
  end
31
29
  end
data/rblade.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rblade"
3
- s.version = "0.6.1"
3
+ s.version = "1.0.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.6.1
4
+ version: 1.0.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-08-02 00:00:00.000000000 Z
11
+ date: 2024-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -79,24 +79,32 @@ files:
79
79
  - Gemfile
80
80
  - LICENSE.md
81
81
  - README.md
82
+ - REFERENCE.md
82
83
  - Rakefile
83
84
  - do
84
85
  - docker-compose.yml
86
+ - examples/README.md
87
+ - examples/application/home.rblade
88
+ - examples/components/content/alert.rblade
89
+ - examples/components/content/hero.rblade
90
+ - examples/components/h1.rblade
91
+ - examples/components/p.rblade
92
+ - examples/layouts/app.rblade
85
93
  - lib/rblade.rb
86
94
  - lib/rblade/compiler.rb
87
95
  - lib/rblade/compiler/compiles_comments.rb
88
96
  - lib/rblade/compiler/compiles_components.rb
89
- - lib/rblade/compiler/compiles_echos.rb
97
+ - lib/rblade/compiler/compiles_prints.rb
90
98
  - lib/rblade/compiler/compiles_ruby.rb
91
99
  - lib/rblade/compiler/compiles_statements.rb
92
100
  - lib/rblade/compiler/compiles_verbatim.rb
101
+ - lib/rblade/compiler/statements/compiles_component_helpers.rb
93
102
  - lib/rblade/compiler/statements/compiles_conditionals.rb
94
103
  - lib/rblade/compiler/statements/compiles_form.rb
95
104
  - lib/rblade/compiler/statements/compiles_html_attributes.rb
96
105
  - lib/rblade/compiler/statements/compiles_inline_ruby.rb
97
106
  - lib/rblade/compiler/statements/compiles_loops.rb
98
107
  - lib/rblade/compiler/statements/compiles_once.rb
99
- - lib/rblade/compiler/statements/compiles_props.rb
100
108
  - lib/rblade/compiler/statements/compiles_stacks.rb
101
109
  - lib/rblade/compiler/tokenizes_components.rb
102
110
  - lib/rblade/compiler/tokenizes_statements.rb