orb_template 0.2.4 → 0.3.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/CHANGELOG.md +30 -0
- data/README.md +163 -25
- data/lib/orb/ast/tag_node.rb +3 -1
- data/lib/orb/temple/compiler.rb +23 -16
- data/lib/orb/temple/filters.rb +13 -0
- data/lib/orb/version.rb +1 -1
- data/lib/orb.rb +6 -2
- metadata +3 -7
- data/CODE_OF_CONDUCT.md +0 -132
- data/Makefile +0 -50
- data/Rakefile +0 -21
- data/docs/2026-03-12-security-analysis.md +0 -715
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9cef68c21c57f074ee08bb76aacb4d88df41ff5726ca65cf3ef1c579e5d56e8d
|
|
4
|
+
data.tar.gz: 3fb10347aada2d9fcd845e8590aaea771c2260b907cdc55f87927ff2afde44de
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 05debe94445dee051e126bf574ba1ed3d2ea93030fe86225261da50ae33af39c7d77b75d9b7f2607e98b655c8076c83a4dc5c111ef8810292353d1f2104fdb65
|
|
7
|
+
data.tar.gz: 9a2469f3a8a7379f84cbcf3bd78644b15f7bc89e5da2c350d73e9b82c60cbf8f97dff305bee834a470b4021c2a685aee672a1a6fb1097e0fdf9c7eef53eee0f2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.3.0] - 2026-11-04
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Breaking:** Require Ruby 3.4 or newer.
|
|
8
|
+
- `ORB.html_escape` now delegates to `Temple::Utils.escape_html`. (`lib/orb.rb`)
|
|
9
|
+
- `make test` now also runs the benchmark suite, and `make trivy` lists lower-severity findings without failing on them.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Stop loading `cgi/util`, which removes the load warning on Ruby 4.0. `ORB.html_escape` still handles non-ASCII-compatible strings such as UTF-16 and returns them in their original encoding. (`lib/orb.rb`)
|
|
14
|
+
- `ORB::VERSION` is now defined after `require "orb"`. Previously it raised `NameError` when the gem was loaded outside bundler. (`lib/orb.rb`)
|
|
15
|
+
|
|
16
|
+
## [0.2.5] - 2026-04-28
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `:unwrap` directive — conditionally strips a wrapper element while keeping its children. When the condition is true, only the children are rendered; when false, the element renders normally. Works on both HTML elements and components, and composes with `:if` and `:for`. (`lib/orb/temple/compiler.rb`, `lib/orb/temple/filters.rb`, `lib/orb/ast/tag_node.rb`)
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Fixed directive priority order: `:for` is now always outermost, followed by `:if`, then `:unwrap`. Previously, `:if` and `:unwrap` wrapped outside `:for`, which caused `NameError` at runtime when `:if` or `:unwrap` conditions referenced the loop variable (e.g., `<li :if={visible?(item)} :for="item in @items">`). (`lib/orb/temple/compiler.rb`)
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Refactored `compiler_directives?` to use a `COMPILER_DIRECTIVES` constant set instead of chained `||` conditions, making it trivial to add future directives (`lib/orb/ast/tag_node.rb`)
|
|
29
|
+
- Refactored `transform_directives_for_tag_node` into `handle_if`, `handle_unwrap`, and `handle_for` helper methods, keeping the entry point as a single-line priority chain (`lib/orb/temple/compiler.rb`)
|
|
30
|
+
- Replaced `rails` gem with `railties`, `actionpack`, and `activemodel` in the test dummy app, removing unused frameworks (ActionCable, ActionText, ActiveStorage, ActionMailbox, ActionMailer, ActiveRecord) and reducing the dependency surface from 116 to 98 gems
|
|
31
|
+
- Added `make security` target running bundler-audit, brakeman, and trivy; integrated into the `make test` pipeline
|
|
32
|
+
|
|
3
33
|
## [0.2.4] - 2026-03-22
|
|
4
34
|
|
|
5
35
|
### Fixed
|
data/README.md
CHANGED
|
@@ -28,12 +28,12 @@ https://github.com/user-attachments/assets/8380b9a8-2063-40f3-a9b6-1b5d623d6f31
|
|
|
28
28
|
|
|
29
29
|
**The ORB Way**
|
|
30
30
|
|
|
31
|
-
```
|
|
31
|
+
```jsx
|
|
32
32
|
<Card title="Your friends">
|
|
33
33
|
<Card:Section title="Birthdays today">
|
|
34
34
|
<List>
|
|
35
35
|
<List.Item :for={friend in @friends}>
|
|
36
|
-
<Link url={
|
|
36
|
+
<Link url={member_path(friend)}>{{friend.name}}</Link>
|
|
37
37
|
</List.Item>
|
|
38
38
|
</List>
|
|
39
39
|
</Card:Section>
|
|
@@ -118,15 +118,15 @@ But that's still not ideal. The code is still verbose, and the heavy use of Ruby
|
|
|
118
118
|
|
|
119
119
|
The `ORB` template language takes another path and allows you to write components exactly how you picture them: as elements in a document tree.
|
|
120
120
|
|
|
121
|
-
```
|
|
121
|
+
```jsx
|
|
122
122
|
<Card title="Your friends">
|
|
123
123
|
<Card:Section title="Birthdays today">
|
|
124
124
|
<List>
|
|
125
125
|
<List.Item>
|
|
126
|
-
<Link url={member_path(2)}
|
|
126
|
+
<Link url={member_path(2)}>Carl Schwartz (27)</Link>
|
|
127
127
|
</List.Item>
|
|
128
128
|
<List.Item>
|
|
129
|
-
<Link url={member_path(3)}
|
|
129
|
+
<Link url={member_path(3)}>Floralie Brain (38)</Link>
|
|
130
130
|
</List.Item>
|
|
131
131
|
</List>
|
|
132
132
|
</Card:Section>
|
|
@@ -171,7 +171,7 @@ This will instruct the `ORB` engine to look for components under the `MyComponen
|
|
|
171
171
|
|
|
172
172
|
Regular HTML tags are fully supported by `ORB`. Just write your HTML tags as you are used to and you are good to go.
|
|
173
173
|
|
|
174
|
-
```
|
|
174
|
+
```jsx
|
|
175
175
|
<div id="page-banner" class="banner" aria-role="alert">
|
|
176
176
|
<span class="message">Hello World!</span>
|
|
177
177
|
</div>
|
|
@@ -181,7 +181,7 @@ Regular HTML tags are fully supported by `ORB`. Just write your HTML tags as you
|
|
|
181
181
|
|
|
182
182
|
`ORB` supports Ruby expressions in the code through double curly-braces (mustache-like syntax). The code inside the curly-braces will be evaluated at render time, and the result will be HTML-safe escaped and inserted into the output.:
|
|
183
183
|
|
|
184
|
-
```
|
|
184
|
+
```jsx
|
|
185
185
|
<div id="page-banner" class="banner" aria-role="alert">
|
|
186
186
|
<span class="message">{{banner.message}}</span>
|
|
187
187
|
</div>
|
|
@@ -189,7 +189,7 @@ Regular HTML tags are fully supported by `ORB`. Just write your HTML tags as you
|
|
|
189
189
|
|
|
190
190
|
Should you need to execute non-printing code, for instance to assign local variables, you can use the non-printing expression syntax with percent signs:
|
|
191
191
|
|
|
192
|
-
```
|
|
192
|
+
```jsx
|
|
193
193
|
{% user = current_user %}
|
|
194
194
|
|
|
195
195
|
<div id="page-banner" class="banner" aria-role="alert">
|
|
@@ -203,7 +203,7 @@ The `ORB` language allows you to define dynamic attribute values for HTML tags t
|
|
|
203
203
|
|
|
204
204
|
Example:
|
|
205
205
|
|
|
206
|
-
```
|
|
206
|
+
```jsx
|
|
207
207
|
<div id={dom_id(banner)} class={banner.classes}>
|
|
208
208
|
<span class="message">{{banner.message}}</span>
|
|
209
209
|
</div>
|
|
@@ -213,42 +213,142 @@ Example:
|
|
|
213
213
|
|
|
214
214
|
`ORB` supports flow control constructs through block instructions. The general syntax for a block is:
|
|
215
215
|
|
|
216
|
-
```
|
|
216
|
+
```jsx
|
|
217
217
|
{#blockname expression} ... {/blockname}
|
|
218
218
|
```
|
|
219
219
|
|
|
220
220
|
For example, a `Banner` may be conditionally rendered through an `{#if}` block construct like this:
|
|
221
221
|
|
|
222
|
-
```
|
|
222
|
+
```jsx
|
|
223
223
|
{#if banner.urgent?}
|
|
224
|
-
<div id={dom_id(banner)} class={banner.classNames}>
|
|
225
|
-
|
|
226
|
-
</div>
|
|
224
|
+
<div id={dom_id(banner)} class={banner.classNames}>
|
|
225
|
+
<span class="message">{{banner.message}}</span>
|
|
226
|
+
</div>
|
|
227
227
|
{/if}
|
|
228
228
|
```
|
|
229
229
|
|
|
230
230
|
Since control flow is such a common thing in templates, `ORB` provides special syntactic sugar for the `{#if}` and `{#for}` blocks through the `:if` and `:for` directives on HTML tags. The above example can thus be rewritten as:
|
|
231
231
|
|
|
232
|
-
```
|
|
232
|
+
```jsx
|
|
233
233
|
<div id={dom_id(banner)} class={banner.classNames} :if={banner.urgent?}>
|
|
234
234
|
<span class="message">{{banner.message}}</span>
|
|
235
235
|
</div>
|
|
236
236
|
```
|
|
237
237
|
|
|
238
|
+
#### The `:unwrap` Directive
|
|
239
|
+
|
|
240
|
+
The `:unwrap` directive conditionally strips a wrapper element while keeping its children. When the condition is true, only the children are rendered. When false, the element renders normally with its children inside.
|
|
241
|
+
|
|
242
|
+
```jsx
|
|
243
|
+
<div class="tooltip-wrapper" :unwrap={!@needs_tooltip}>
|
|
244
|
+
<span>Always rendered</span>
|
|
245
|
+
</div>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
| `:unwrap` condition | Result |
|
|
249
|
+
|---|---|
|
|
250
|
+
| `true` | Children only (`<span>Always rendered</span>`) |
|
|
251
|
+
| `false` | Full element (`<div class="tooltip-wrapper"><span>...</span></div>`) |
|
|
252
|
+
|
|
253
|
+
This eliminates the need to duplicate child markup when a wrapper is conditional:
|
|
254
|
+
|
|
255
|
+
```jsx
|
|
256
|
+
{!-- Before: duplicated children --}
|
|
257
|
+
<Tooltip :if={@is_last_method} description="Only active method.">
|
|
258
|
+
<Toggle><Toggle:Option label="On" /><Toggle:Option label="Off" /></Toggle>
|
|
259
|
+
</Tooltip>
|
|
260
|
+
{#if !@is_last_method}
|
|
261
|
+
<Toggle><Toggle:Option label="On" /><Toggle:Option label="Off" /></Toggle>
|
|
262
|
+
{/if}
|
|
263
|
+
|
|
264
|
+
{!-- After: single child block, zero duplication --}
|
|
265
|
+
<Tooltip description="Only active method." :unwrap={!@is_last_method}>
|
|
266
|
+
<Toggle><Toggle:Option label="On" /><Toggle:Option label="Off" /></Toggle>
|
|
267
|
+
</Tooltip>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
`:unwrap` works on both HTML elements and components.
|
|
271
|
+
|
|
272
|
+
#### Combining Directives
|
|
273
|
+
|
|
274
|
+
Multiple directives can be combined on the same element. When they are, they are evaluated in a fixed priority order, regardless of the order they appear in the template:
|
|
275
|
+
|
|
276
|
+
> **`:for`** (outermost) > **`:if`** > **`:unwrap`** (innermost)
|
|
277
|
+
|
|
278
|
+
This ordering ensures that loop variables introduced by `:for` are always in scope for `:if` and `:unwrap` conditions.
|
|
279
|
+
|
|
280
|
+
**`:for` + `:if`** — filter items inside a loop:
|
|
281
|
+
|
|
282
|
+
```jsx
|
|
283
|
+
<li :for="item in @items" :if={item.visible?}>{{item.name}}</li>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
The `:for` loop runs first, then `:if` is evaluated per iteration with `item` in scope:
|
|
287
|
+
|
|
288
|
+
```ruby
|
|
289
|
+
@items.each do |item|
|
|
290
|
+
if item.visible?
|
|
291
|
+
_buf << "<li>" << escape_html(item.name) << "</li>"
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**`:for` + `:unwrap`** — conditionally strip wrappers per item:
|
|
297
|
+
|
|
298
|
+
```jsx
|
|
299
|
+
<li :for="item in @items" :unwrap={item.inline?}>{{item.name}}</li>
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
@items.each do |item|
|
|
304
|
+
if item.inline?
|
|
305
|
+
_buf << escape_html(item.name)
|
|
306
|
+
else
|
|
307
|
+
_buf << "<li>" << escape_html(item.name) << "</li>"
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**`:if` + `:unwrap`** — conditionally render, conditionally wrap:
|
|
313
|
+
|
|
314
|
+
```jsx
|
|
315
|
+
<Tooltip description="..." :if={@show_section} :unwrap={!@needs_tooltip}>
|
|
316
|
+
<Badge label="Status" />
|
|
317
|
+
</Tooltip>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
| `@show_section` | `@needs_tooltip` | Result |
|
|
321
|
+
|---|---|---|
|
|
322
|
+
| `false` | (any) | Nothing |
|
|
323
|
+
| `true` | `true` | `<Tooltip>` wrapping `<Badge>` |
|
|
324
|
+
| `true` | `false` | `<Badge>` alone |
|
|
325
|
+
|
|
326
|
+
**All three** — loop, filter, and conditionally wrap:
|
|
327
|
+
|
|
328
|
+
```jsx
|
|
329
|
+
<li :for="item in @items" :if={item.visible?} :unwrap={item.inline?}>
|
|
330
|
+
{{item.name}}
|
|
331
|
+
</li>
|
|
332
|
+
```
|
|
333
|
+
|
|
238
334
|
### Splatted Attributes
|
|
239
335
|
|
|
240
336
|
`ORB` supports attribute splatting for both HTML tags and view components through the `**attributes` syntax. The attribute name for the splat must be a `Hash`; all key-value pairs will be added as attributes to the tag. For example:
|
|
241
337
|
|
|
242
|
-
```
|
|
243
|
-
<div **banner_attributes
|
|
338
|
+
```jsx
|
|
339
|
+
<div **banner_attributes>
|
|
340
|
+
<!-- other content ... -->
|
|
341
|
+
</div>
|
|
244
342
|
```
|
|
245
343
|
|
|
246
344
|
### Splatted Attribute Expressions
|
|
247
345
|
|
|
248
346
|
You can also use an expression to define the splatted attributes through the `**{expression}` syntax. The expression must evaluate to a `Hash`, and all key-value pairs will be added as attributes to the tag. For example:
|
|
249
347
|
|
|
250
|
-
```
|
|
251
|
-
<div **{dynamic_attributes}
|
|
348
|
+
```jsx
|
|
349
|
+
<div **{dynamic_attributes(member)}>
|
|
350
|
+
<!-- other content ... -->
|
|
351
|
+
</div>
|
|
252
352
|
```
|
|
253
353
|
|
|
254
354
|
### View Components
|
|
@@ -272,19 +372,56 @@ class MyComponents::Button < ViewComponent::Base
|
|
|
272
372
|
end
|
|
273
373
|
```
|
|
274
374
|
|
|
275
|
-
|
|
375
|
+
You can render this component in an ORB template `show.html.orb` as:
|
|
276
376
|
|
|
277
377
|
```jsx
|
|
278
378
|
<Button url="/click_me">I am a button</Button>
|
|
279
379
|
```
|
|
280
380
|
|
|
281
|
-
|
|
381
|
+
### Inline Templates
|
|
382
|
+
|
|
383
|
+
You can define the markup for a component inline using `orb_template` to use the ORB template language for the component's own view template:
|
|
384
|
+
|
|
385
|
+
```ruby
|
|
386
|
+
class ButtonComponent < ViewComponent::Base
|
|
387
|
+
def initialize(url:)
|
|
388
|
+
@url = url
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
orb_template <<~ORB
|
|
392
|
+
<a href={@url} class="btn">{{ content }}</a>
|
|
393
|
+
ORB
|
|
394
|
+
end
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
If your template needs string interpolation, you have several options:
|
|
398
|
+
|
|
399
|
+
```ruby
|
|
400
|
+
# 1. Single-quoted heredoc (recommended) — #{} is preserved for render time
|
|
401
|
+
orb_template <<~'ORB'
|
|
402
|
+
<div id={"#{@id}_suffix"}>Hello</div>
|
|
403
|
+
ORB
|
|
404
|
+
|
|
405
|
+
# 2. String concatenation — avoids #{} entirely
|
|
406
|
+
orb_template <<~ORB
|
|
407
|
+
<div id={@id + "_suffix"}>Hello</div>
|
|
408
|
+
ORB
|
|
409
|
+
|
|
410
|
+
# 3. Helper method — move the interpolation into Ruby
|
|
411
|
+
orb_template <<~ORB
|
|
412
|
+
<div id={suffixed_id}>Hello</div>
|
|
413
|
+
ORB
|
|
414
|
+
|
|
415
|
+
def suffixed_id
|
|
416
|
+
"#{@id}_suffix"
|
|
417
|
+
end
|
|
418
|
+
```
|
|
282
419
|
|
|
283
420
|
### ViewComponent Slots
|
|
284
421
|
|
|
285
422
|
`ORB` also provides a DSL for invoking a component slot and passing content to the slot through the `Component:Slot` syntax. For example, if you have a `Card` component that defines a `Sections` slot via `renders_many :sections, Card::Section`, you can invoke and fill the slot in an `ORB` template like this:
|
|
286
423
|
|
|
287
|
-
```
|
|
424
|
+
```jsx
|
|
288
425
|
<Card title="Products">
|
|
289
426
|
<Card:Section title="Description">
|
|
290
427
|
<p>Blue t-shirt in size L ...</p>
|
|
@@ -298,13 +435,13 @@ Tip: Slot names are case-insensitive, so `<Card:section> ... </Card:section>` al
|
|
|
298
435
|
|
|
299
436
|
Sometimes, you may want to organize your components in sub-namespaces, or use components from other libraries. `ORB` supports this through dot notation in the tag names. For example, if you have a `MyComponents::Admin::Button` component, you can render it in an `ORB` template like this:
|
|
300
437
|
|
|
301
|
-
```
|
|
438
|
+
```jsx
|
|
302
439
|
<Admin.Button url="/admin/click_me">Admin Button</Admin.Button>
|
|
303
440
|
```
|
|
304
441
|
|
|
305
442
|
If you have a third-party component `ThirdParty::UI::Modal`, you can render it like this:
|
|
306
443
|
|
|
307
|
-
```
|
|
444
|
+
```jsx
|
|
308
445
|
<ThirdParty.UI.Modal title="Terms and Conditions">
|
|
309
446
|
<p>...</p>
|
|
310
447
|
</ThirdParty.UI.Modal>
|
|
@@ -313,7 +450,7 @@ If you have a third-party component `ThirdParty::UI::Modal`, you can render it l
|
|
|
313
450
|
To make life easier when using components from a specific namespace frequently, you can configure additional namespaces in the `ORB` configuration:
|
|
314
451
|
|
|
315
452
|
```ruby
|
|
316
|
-
ORB.namespaces = ['MyComponents ThirdParty::UI']
|
|
453
|
+
ORB.namespaces = ['MyComponents', 'ThirdParty::UI']
|
|
317
454
|
```
|
|
318
455
|
|
|
319
456
|
Namespaces defined in this way will be searched in order of definition when resolving component tag names in templates.
|
|
@@ -387,6 +524,7 @@ To enable `Tailwindcss` support for ORB, add this to your `settings.json`:
|
|
|
387
524
|
- [x] `**{expression}` splats for html tags, components and slots
|
|
388
525
|
- [x] `:if` directive
|
|
389
526
|
- [x] `:for` directive
|
|
527
|
+
- [x] `:unwrap` directive
|
|
390
528
|
- [x] verbatim tags
|
|
391
529
|
- [x] ensure output safety and proper escaping of output
|
|
392
530
|
- [x] security review and hardening of compilation pipeline
|
data/lib/orb/ast/tag_node.rb
CHANGED
|
@@ -192,8 +192,10 @@ module ORB
|
|
|
192
192
|
directives.any?
|
|
193
193
|
end
|
|
194
194
|
|
|
195
|
+
COMPILER_DIRECTIVES = %i[if unwrap for].freeze
|
|
196
|
+
|
|
195
197
|
def compiler_directives?
|
|
196
|
-
directives.any? { |k, _v| k
|
|
198
|
+
directives.any? { |k, _v| COMPILER_DIRECTIVES.include?(k) }
|
|
197
199
|
end
|
|
198
200
|
|
|
199
201
|
##
|
data/lib/orb/temple/compiler.rb
CHANGED
|
@@ -155,24 +155,31 @@ module ORB
|
|
|
155
155
|
|
|
156
156
|
# Compile a tag node with directives
|
|
157
157
|
def transform_directives_for_tag_node(node)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if if_directive
|
|
161
|
-
node.remove_directive(:if)
|
|
162
|
-
return [:if,
|
|
163
|
-
if_directive,
|
|
164
|
-
transform(node)]
|
|
165
|
-
end
|
|
158
|
+
handle_for(node) || handle_if(node) || handle_unwrap(node) || transform(node)
|
|
159
|
+
end
|
|
166
160
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
161
|
+
def handle_if(node)
|
|
162
|
+
directive = node.directives.fetch(:if, false)
|
|
163
|
+
return unless directive
|
|
164
|
+
|
|
165
|
+
node.remove_directive(:if)
|
|
166
|
+
[:if, directive, transform(node)]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def handle_unwrap(node)
|
|
170
|
+
directive = node.directives.fetch(:unwrap, false)
|
|
171
|
+
return unless directive
|
|
172
|
+
|
|
173
|
+
node.remove_directive(:unwrap)
|
|
174
|
+
[:orb, :unwrap, directive, transform(node), transform_children(node)]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def handle_for(node)
|
|
178
|
+
directive = node.directives.fetch(:for, false)
|
|
179
|
+
return unless directive
|
|
173
180
|
|
|
174
|
-
|
|
175
|
-
transform(node)
|
|
181
|
+
node.remove_directive(:for)
|
|
182
|
+
[:orb, :for, directive, transform(node)]
|
|
176
183
|
end
|
|
177
184
|
|
|
178
185
|
# Compile a dynamic tag node
|
data/lib/orb/temple/filters.rb
CHANGED
|
@@ -127,6 +127,19 @@ module ORB
|
|
|
127
127
|
result
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
+
# Handle an unwrap directive expression `[:orb, :unwrap, condition, element, children]`
|
|
131
|
+
#
|
|
132
|
+
# When condition is true, renders only the children (unwrapped).
|
|
133
|
+
# When condition is false, renders the full element with children inside.
|
|
134
|
+
#
|
|
135
|
+
# @param [String] condition The condition to be evaluated
|
|
136
|
+
# @param [Array] element The full element Temple expression
|
|
137
|
+
# @param [Array] children The children-only Temple expression
|
|
138
|
+
# @return [Array] compiled Temple expression
|
|
139
|
+
def on_orb_unwrap(condition, element, children)
|
|
140
|
+
[:if, condition, compile(children), compile(element)]
|
|
141
|
+
end
|
|
142
|
+
|
|
130
143
|
# Handle a for block expression `[:orb, :for, expression, content]`
|
|
131
144
|
#
|
|
132
145
|
# @param [String] expression The iterator expression to be evaluated
|
data/lib/orb/version.rb
CHANGED
data/lib/orb.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'temple'
|
|
4
|
-
require 'cgi/util'
|
|
5
4
|
|
|
5
|
+
require_relative "orb/version"
|
|
6
6
|
require_relative "orb/errors"
|
|
7
7
|
require_relative "orb/token"
|
|
8
8
|
require_relative "orb/tokenizer"
|
|
@@ -34,7 +34,11 @@ module ORB
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def self.html_escape(str)
|
|
37
|
-
|
|
37
|
+
str = str.to_s
|
|
38
|
+
return ::Temple::Utils.escape_html(str) if str.encoding.ascii_compatible?
|
|
39
|
+
|
|
40
|
+
# The native escaper can't handle encodings such as UTF-16 on Ruby 3.4.
|
|
41
|
+
::Temple::Utils.escape_html(str.encode(Encoding::UTF_8)).encode(str.encoding)
|
|
38
42
|
end
|
|
39
43
|
end
|
|
40
44
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: orb_template
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- KUY.io Inc.
|
|
@@ -34,12 +34,8 @@ extensions: []
|
|
|
34
34
|
extra_rdoc_files: []
|
|
35
35
|
files:
|
|
36
36
|
- CHANGELOG.md
|
|
37
|
-
- CODE_OF_CONDUCT.md
|
|
38
37
|
- LICENSE.txt
|
|
39
|
-
- Makefile
|
|
40
38
|
- README.md
|
|
41
|
-
- Rakefile
|
|
42
|
-
- docs/2026-03-12-security-analysis.md
|
|
43
39
|
- lib/orb.rb
|
|
44
40
|
- lib/orb/ast.rb
|
|
45
41
|
- lib/orb/ast/abstract_node.rb
|
|
@@ -92,14 +88,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
92
88
|
requirements:
|
|
93
89
|
- - ">="
|
|
94
90
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: 3.
|
|
91
|
+
version: '3.4'
|
|
96
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
93
|
requirements:
|
|
98
94
|
- - ">="
|
|
99
95
|
- !ruby/object:Gem::Version
|
|
100
96
|
version: '0'
|
|
101
97
|
requirements: []
|
|
102
|
-
rubygems_version: 4.0.
|
|
98
|
+
rubygems_version: 4.0.23
|
|
103
99
|
specification_version: 4
|
|
104
100
|
summary: The ORB template language for Ruby.
|
|
105
101
|
test_files: []
|
data/CODE_OF_CONDUCT.md
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
|
2
|
-
|
|
3
|
-
## Our Pledge
|
|
4
|
-
|
|
5
|
-
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
-
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
-
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
-
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
-
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
10
|
-
identity and orientation.
|
|
11
|
-
|
|
12
|
-
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
-
diverse, inclusive, and healthy community.
|
|
14
|
-
|
|
15
|
-
## Our Standards
|
|
16
|
-
|
|
17
|
-
Examples of behavior that contributes to a positive environment for our
|
|
18
|
-
community include:
|
|
19
|
-
|
|
20
|
-
* Demonstrating empathy and kindness toward other people
|
|
21
|
-
* Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
-
* Giving and gracefully accepting constructive feedback
|
|
23
|
-
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
-
and learning from the experience
|
|
25
|
-
* Focusing on what is best not just for us as individuals, but for the overall
|
|
26
|
-
community
|
|
27
|
-
|
|
28
|
-
Examples of unacceptable behavior include:
|
|
29
|
-
|
|
30
|
-
* The use of sexualized language or imagery, and sexual attention or advances of
|
|
31
|
-
any kind
|
|
32
|
-
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
-
* Public or private harassment
|
|
34
|
-
* Publishing others' private information, such as a physical or email address,
|
|
35
|
-
without their explicit permission
|
|
36
|
-
* Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
-
professional setting
|
|
38
|
-
|
|
39
|
-
## Enforcement Responsibilities
|
|
40
|
-
|
|
41
|
-
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
-
acceptable behavior and will take appropriate and fair corrective action in
|
|
43
|
-
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
44
|
-
or harmful.
|
|
45
|
-
|
|
46
|
-
Community leaders have the right and responsibility to remove, edit, or reject
|
|
47
|
-
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
48
|
-
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
49
|
-
decisions when appropriate.
|
|
50
|
-
|
|
51
|
-
## Scope
|
|
52
|
-
|
|
53
|
-
This Code of Conduct applies within all community spaces, and also applies when
|
|
54
|
-
an individual is officially representing the community in public spaces.
|
|
55
|
-
Examples of representing our community include using an official email address,
|
|
56
|
-
posting via an official social media account, or acting as an appointed
|
|
57
|
-
representative at an online or offline event.
|
|
58
|
-
|
|
59
|
-
## Enforcement
|
|
60
|
-
|
|
61
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
62
|
-
reported to the community leaders responsible for enforcement at
|
|
63
|
-
[INSERT CONTACT METHOD].
|
|
64
|
-
All complaints will be reviewed and investigated promptly and fairly.
|
|
65
|
-
|
|
66
|
-
All community leaders are obligated to respect the privacy and security of the
|
|
67
|
-
reporter of any incident.
|
|
68
|
-
|
|
69
|
-
## Enforcement Guidelines
|
|
70
|
-
|
|
71
|
-
Community leaders will follow these Community Impact Guidelines in determining
|
|
72
|
-
the consequences for any action they deem in violation of this Code of Conduct:
|
|
73
|
-
|
|
74
|
-
### 1. Correction
|
|
75
|
-
|
|
76
|
-
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
77
|
-
unprofessional or unwelcome in the community.
|
|
78
|
-
|
|
79
|
-
**Consequence**: A private, written warning from community leaders, providing
|
|
80
|
-
clarity around the nature of the violation and an explanation of why the
|
|
81
|
-
behavior was inappropriate. A public apology may be requested.
|
|
82
|
-
|
|
83
|
-
### 2. Warning
|
|
84
|
-
|
|
85
|
-
**Community Impact**: A violation through a single incident or series of
|
|
86
|
-
actions.
|
|
87
|
-
|
|
88
|
-
**Consequence**: A warning with consequences for continued behavior. No
|
|
89
|
-
interaction with the people involved, including unsolicited interaction with
|
|
90
|
-
those enforcing the Code of Conduct, for a specified period of time. This
|
|
91
|
-
includes avoiding interactions in community spaces as well as external channels
|
|
92
|
-
like social media. Violating these terms may lead to a temporary or permanent
|
|
93
|
-
ban.
|
|
94
|
-
|
|
95
|
-
### 3. Temporary Ban
|
|
96
|
-
|
|
97
|
-
**Community Impact**: A serious violation of community standards, including
|
|
98
|
-
sustained inappropriate behavior.
|
|
99
|
-
|
|
100
|
-
**Consequence**: A temporary ban from any sort of interaction or public
|
|
101
|
-
communication with the community for a specified period of time. No public or
|
|
102
|
-
private interaction with the people involved, including unsolicited interaction
|
|
103
|
-
with those enforcing the Code of Conduct, is allowed during this period.
|
|
104
|
-
Violating these terms may lead to a permanent ban.
|
|
105
|
-
|
|
106
|
-
### 4. Permanent Ban
|
|
107
|
-
|
|
108
|
-
**Community Impact**: Demonstrating a pattern of violation of community
|
|
109
|
-
standards, including sustained inappropriate behavior, harassment of an
|
|
110
|
-
individual, or aggression toward or disparagement of classes of individuals.
|
|
111
|
-
|
|
112
|
-
**Consequence**: A permanent ban from any sort of public interaction within the
|
|
113
|
-
community.
|
|
114
|
-
|
|
115
|
-
## Attribution
|
|
116
|
-
|
|
117
|
-
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
118
|
-
version 2.1, available at
|
|
119
|
-
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
120
|
-
|
|
121
|
-
Community Impact Guidelines were inspired by
|
|
122
|
-
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
123
|
-
|
|
124
|
-
For answers to common questions about this code of conduct, see the FAQ at
|
|
125
|
-
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
|
126
|
-
[https://www.contributor-covenant.org/translations][translations].
|
|
127
|
-
|
|
128
|
-
[homepage]: https://www.contributor-covenant.org
|
|
129
|
-
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
130
|
-
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
131
|
-
[FAQ]: https://www.contributor-covenant.org/faq
|
|
132
|
-
[translations]: https://www.contributor-covenant.org/translations
|