rhales 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +4 -4
- data/.github/workflows/release-gem.yml +158 -0
- data/.github/workflows/ruby-lint.yml +1 -1
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +37 -0
- data/Gemfile.lock +6 -6
- data/README.md +1 -1
- data/examples/token-loader.rue +119 -0
- data/lib/rhales/core/template_engine.rb +10 -8
- data/lib/rhales/parsers/rue_format_parser.rb +1 -1
- data/lib/rhales/version.rb +1 -1
- data/rhales.gemspec +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 249890f733fb9b88bbabe22bcc327aa5d0422c00ea595e4b67751e335e790feb
|
|
4
|
+
data.tar.gz: 69259f0ab5cc091cc2c4fab33e3eee13859e28ef789e522119e6702f695155ab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c424d4d348c48686ddb60fe068d2839627c552d94a03a507b9cf54f1d39d11dc4d18aaefccc8731ec0aa8fb79d72ef18ea838460d0e36f5d69b5575d7d15ac3d
|
|
7
|
+
data.tar.gz: 65139df9054a39981ca7f6fd51818544aca039847d4a1d60521e879c2d542f3ca1f2ca9bbeb030154cc735404882d5ca0ede2fcabe64ef55a8194c622518c963
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -26,7 +26,7 @@ jobs:
|
|
|
26
26
|
strategy:
|
|
27
27
|
fail-fast: true
|
|
28
28
|
matrix:
|
|
29
|
-
ruby: ['3.4', '3.5']
|
|
29
|
+
ruby: ['3.2', '3.3', '3.4', '3.5']
|
|
30
30
|
|
|
31
31
|
steps:
|
|
32
32
|
- uses: actions/checkout@v4
|
|
@@ -50,7 +50,7 @@ jobs:
|
|
|
50
50
|
strategy:
|
|
51
51
|
fail-fast: true
|
|
52
52
|
matrix:
|
|
53
|
-
ruby: ['3.4', '3.5']
|
|
53
|
+
ruby: ['3.2', '3.3', '3.4', '3.5']
|
|
54
54
|
|
|
55
55
|
steps:
|
|
56
56
|
- uses: actions/checkout@v4
|
|
@@ -77,7 +77,7 @@ jobs:
|
|
|
77
77
|
strategy:
|
|
78
78
|
fail-fast: false
|
|
79
79
|
matrix:
|
|
80
|
-
ruby: ['3.4', '3.5']
|
|
80
|
+
ruby: ['3.2', '3.3', '3.4', '3.5']
|
|
81
81
|
|
|
82
82
|
steps:
|
|
83
83
|
- uses: actions/checkout@v4
|
|
@@ -99,7 +99,7 @@ jobs:
|
|
|
99
99
|
strategy:
|
|
100
100
|
fail-fast: false
|
|
101
101
|
matrix:
|
|
102
|
-
ruby: ['3.4', '3.5']
|
|
102
|
+
ruby: ['3.2', '3.3', '3.4', '3.5']
|
|
103
103
|
|
|
104
104
|
steps:
|
|
105
105
|
- uses: actions/checkout@v4
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Release Gem Workflow
|
|
2
|
+
#
|
|
3
|
+
# Automatically builds and publishes the rhales gem to RubyGems.org when a
|
|
4
|
+
# GitHub release is published with a semantic version tag (vMAJOR.MINOR.PATCH,
|
|
5
|
+
# e.g. v0.6.1, v1.2.3).
|
|
6
|
+
#
|
|
7
|
+
# Follows the canonical RubyGems Trusted Publishing workflow:
|
|
8
|
+
# https://guides.rubygems.org/trusted-publishing/
|
|
9
|
+
#
|
|
10
|
+
# ============================================================================
|
|
11
|
+
# SETUP INSTRUCTIONS (one-time)
|
|
12
|
+
# ============================================================================
|
|
13
|
+
#
|
|
14
|
+
# This workflow uses RubyGems Trusted Publishing (OIDC). No long-lived API
|
|
15
|
+
# key needs to be stored in GitHub.
|
|
16
|
+
#
|
|
17
|
+
# ----------------------------------------------------------------------------
|
|
18
|
+
# 1. Configure the trusted publisher on RubyGems.org
|
|
19
|
+
# ----------------------------------------------------------------------------
|
|
20
|
+
#
|
|
21
|
+
# a. Sign in to https://rubygems.org and enable MFA on your account
|
|
22
|
+
# (required for trusted publishing).
|
|
23
|
+
#
|
|
24
|
+
# b. Open the gem's trusted publisher page (works for both existing gems
|
|
25
|
+
# and the first-ever publish):
|
|
26
|
+
# Existing gem: https://rubygems.org/gems/rhales/trusted_publishers
|
|
27
|
+
# First publish: https://rubygems.org/profile/oidc/pending_trusted_publishers/new
|
|
28
|
+
#
|
|
29
|
+
# c. Click "Create" and fill in:
|
|
30
|
+
# Publisher type: GitHub Actions
|
|
31
|
+
# Repository owner: onetimesecret
|
|
32
|
+
# Repository name: rhales
|
|
33
|
+
# Workflow filename: release-gem.yml
|
|
34
|
+
# Environment: rubygems.org (must match `environment.name` below)
|
|
35
|
+
#
|
|
36
|
+
# d. Save. RubyGems will now accept short-lived OIDC tokens issued by this
|
|
37
|
+
# workflow running in this repository.
|
|
38
|
+
#
|
|
39
|
+
# ----------------------------------------------------------------------------
|
|
40
|
+
# 2. Configure the GitHub environment
|
|
41
|
+
# ----------------------------------------------------------------------------
|
|
42
|
+
#
|
|
43
|
+
# a. In GitHub: Settings -> Environments -> New environment
|
|
44
|
+
# Name: `rubygems.org` (must match `environment.name` below)
|
|
45
|
+
#
|
|
46
|
+
# b. Under "Deployment branches and tags", restrict to tags matching:
|
|
47
|
+
# v*.*.*
|
|
48
|
+
# This prevents the environment (and its OIDC token) from being used
|
|
49
|
+
# from any branch or non-release tag.
|
|
50
|
+
#
|
|
51
|
+
# c. Optional but recommended: add required reviewers to gate publishes
|
|
52
|
+
# behind manual approval.
|
|
53
|
+
#
|
|
54
|
+
# No GitHub secrets are required - OIDC handles authentication.
|
|
55
|
+
#
|
|
56
|
+
# ----------------------------------------------------------------------------
|
|
57
|
+
# 3. Cutting a release
|
|
58
|
+
# ----------------------------------------------------------------------------
|
|
59
|
+
#
|
|
60
|
+
# a. Bump Rhales::VERSION in lib/rhales/version.rb (semver: MAJOR.MINOR.PATCH).
|
|
61
|
+
# b. Update CHANGELOG.md and commit on main.
|
|
62
|
+
# c. On GitHub, draft a Release with tag `vX.Y.Z` (matching the constant)
|
|
63
|
+
# and publish it. This workflow runs automatically: it verifies the tag
|
|
64
|
+
# matches the gemspec version, builds the gem, and pushes it.
|
|
65
|
+
#
|
|
66
|
+
# ----------------------------------------------------------------------------
|
|
67
|
+
# Fallback: API key (only if Trusted Publishing isn't an option)
|
|
68
|
+
# ----------------------------------------------------------------------------
|
|
69
|
+
#
|
|
70
|
+
# 1. Create an API key at https://rubygems.org/profile/api_keys with scope
|
|
71
|
+
# "Push rubygem" restricted to the `rhales` gem.
|
|
72
|
+
# 2. Store it as a repo secret named `RUBYGEMS_API_KEY`.
|
|
73
|
+
# 3. Drop the `id-token: write` permission and `environment:` block, and
|
|
74
|
+
# replace the `rubygems/release-gem` step with:
|
|
75
|
+
#
|
|
76
|
+
# - name: Publish to RubyGems
|
|
77
|
+
# env:
|
|
78
|
+
# GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
|
79
|
+
# run: gem push rhales-*.gem
|
|
80
|
+
#
|
|
81
|
+
# ----------------------------------------------------------------------------
|
|
82
|
+
# Action provenance (SHA pins)
|
|
83
|
+
# ----------------------------------------------------------------------------
|
|
84
|
+
#
|
|
85
|
+
# All third-party actions below are pinned to a full commit SHA, per GitHub's
|
|
86
|
+
# secure-use guidance for release-critical workflows. The accompanying
|
|
87
|
+
# comment records the tag the SHA was resolved from so renovate/dependabot
|
|
88
|
+
# can keep them current.
|
|
89
|
+
#
|
|
90
|
+
# actions/checkout - official GitHub action
|
|
91
|
+
# ruby/setup-ruby - official Ruby org action (GitHub-verified creator)
|
|
92
|
+
# rubygems/release-gem - official RubyGems action for trusted publishing
|
|
93
|
+
#
|
|
94
|
+
# ============================================================================
|
|
95
|
+
|
|
96
|
+
name: Release Gem
|
|
97
|
+
|
|
98
|
+
on:
|
|
99
|
+
release:
|
|
100
|
+
types: [published]
|
|
101
|
+
|
|
102
|
+
# Coarse default. Each job re-declares the narrowest permissions it needs.
|
|
103
|
+
permissions:
|
|
104
|
+
contents: read
|
|
105
|
+
|
|
106
|
+
# Don't allow two release runs to race - one published tag, one publish.
|
|
107
|
+
concurrency:
|
|
108
|
+
group: release-gem-${{ github.event.release.tag_name }}
|
|
109
|
+
cancel-in-progress: false
|
|
110
|
+
|
|
111
|
+
jobs:
|
|
112
|
+
release:
|
|
113
|
+
name: Build and push gem
|
|
114
|
+
runs-on: ubuntu-latest
|
|
115
|
+
timeout-minutes: 10
|
|
116
|
+
|
|
117
|
+
environment:
|
|
118
|
+
name: rubygems.org
|
|
119
|
+
url: https://rubygems.org/gems/rhales
|
|
120
|
+
|
|
121
|
+
permissions:
|
|
122
|
+
contents: write # rubygems/release-gem attaches built .gem to the GitHub release
|
|
123
|
+
id-token: write # OIDC token for RubyGems Trusted Publishing
|
|
124
|
+
|
|
125
|
+
steps:
|
|
126
|
+
- name: Checkout
|
|
127
|
+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
|
128
|
+
with:
|
|
129
|
+
persist-credentials: false
|
|
130
|
+
|
|
131
|
+
- name: Set up Ruby
|
|
132
|
+
uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1.310.0
|
|
133
|
+
with:
|
|
134
|
+
bundler-cache: true
|
|
135
|
+
ruby-version: ruby # latest stable Ruby installed by setup-ruby
|
|
136
|
+
|
|
137
|
+
- name: Verify release tag matches Rhales::VERSION
|
|
138
|
+
env:
|
|
139
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
140
|
+
run: |
|
|
141
|
+
set -euo pipefail
|
|
142
|
+
case "${RELEASE_TAG}" in
|
|
143
|
+
v[0-9]*.[0-9]*.[0-9]*) ;;
|
|
144
|
+
*)
|
|
145
|
+
echo "Release tag '${RELEASE_TAG}' is not a vMAJOR.MINOR.PATCH semver tag." >&2
|
|
146
|
+
exit 1
|
|
147
|
+
;;
|
|
148
|
+
esac
|
|
149
|
+
tag_version="${RELEASE_TAG#v}"
|
|
150
|
+
gem_version="$(ruby -r ./lib/rhales/version.rb -e 'print Rhales::VERSION')"
|
|
151
|
+
if [ "${tag_version}" != "${gem_version}" ]; then
|
|
152
|
+
echo "Release tag ${RELEASE_TAG} (${tag_version}) != Rhales::VERSION (${gem_version})." >&2
|
|
153
|
+
exit 1
|
|
154
|
+
fi
|
|
155
|
+
echo "Releasing rhales ${gem_version} from tag ${RELEASE_TAG}"
|
|
156
|
+
|
|
157
|
+
- name: Build and push gem to RubyGems
|
|
158
|
+
uses: rubygems/release-gem@6317d8d1f7e28c24d28f6eff169ea854948bd9f7 # v1.2.0
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.2] - 2026-05-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Automated gem release workflow** (`.github/workflows/release-gem.yml`):
|
|
14
|
+
builds and publishes the gem to RubyGems.org via Trusted Publishing
|
|
15
|
+
(OIDC) whenever a GitHub Release is published with a `vMAJOR.MINOR.PATCH`
|
|
16
|
+
tag. Verifies the tag matches `Rhales::VERSION` before pushing.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- **Dependencies**: bumped `unicode-emoji` from 4.0.4 to 4.2.0 and
|
|
20
|
+
`unicode-display_width` from 3.1.4 to 3.2.0. The previous
|
|
21
|
+
`unicode-emoji` cap of `< Ruby 4.0` broke `bundle install` on Ruby 4.x;
|
|
22
|
+
4.2.0 lifts that cap.
|
|
23
|
+
- **Gemfile.lock**: platform list normalized via
|
|
24
|
+
`bundle lock --normalize-platforms` so platform-specific gems no longer
|
|
25
|
+
trigger setup-ruby warnings on CI.
|
|
26
|
+
|
|
27
|
+
## [0.6.1] - 2026-05-25
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- **Server-rendered random tokens example** (`examples/token-loader.rue`):
|
|
31
|
+
pattern-teaching example showing how to generate per-request data with
|
|
32
|
+
`SecureRandom.hex` in a Ruby view model, validate a nested
|
|
33
|
+
`z.array(z.object(...))` shape, and walk it with nested `{{#each}}` blocks
|
|
34
|
+
that fall through to the current outer item without explicit bindings. The
|
|
35
|
+
original loader-animation use case is documented as a CSS-only follow-on in
|
|
36
|
+
the `<logic>` block. (#49)
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- **Minimum Ruby version lowered from 3.4 to 3.2** - broader compatibility with stable Ruby releases; CI now exercises 3.2, 3.3, 3.4, and 3.5
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
- `{{#each}}` block variable `@last` now correctly returns `true` for the final
|
|
43
|
+
iteration instead of always `false`. Enables comma-separated output via
|
|
44
|
+
`{{#unless @last}},{{/unless}}` and similar patterns. `EachContext` now
|
|
45
|
+
accepts and stores the collection's total length.
|
|
46
|
+
|
|
10
47
|
## [0.6.0] - 2026-03-21
|
|
11
48
|
|
|
12
49
|
### Added
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
rhales (0.6.
|
|
4
|
+
rhales (0.6.2)
|
|
5
5
|
json_schemer (~> 2)
|
|
6
6
|
logger
|
|
7
7
|
tilt (~> 2)
|
|
@@ -153,14 +153,14 @@ GEM
|
|
|
153
153
|
prettier_print (>= 1.2.0)
|
|
154
154
|
tilt (2.6.1)
|
|
155
155
|
tsort (0.2.0)
|
|
156
|
-
unicode-display_width (3.
|
|
157
|
-
unicode-emoji (~> 4.
|
|
158
|
-
unicode-emoji (4.0
|
|
156
|
+
unicode-display_width (3.2.0)
|
|
157
|
+
unicode-emoji (~> 4.1)
|
|
158
|
+
unicode-emoji (4.2.0)
|
|
159
159
|
yard (0.9.37)
|
|
160
160
|
zeitwerk (2.7.3)
|
|
161
161
|
|
|
162
162
|
PLATFORMS
|
|
163
|
-
arm64-darwin
|
|
163
|
+
arm64-darwin
|
|
164
164
|
ruby
|
|
165
165
|
|
|
166
166
|
DEPENDENCIES
|
|
@@ -186,4 +186,4 @@ DEPENDENCIES
|
|
|
186
186
|
yard (~> 0.9)
|
|
187
187
|
|
|
188
188
|
BUNDLED WITH
|
|
189
|
-
2.
|
|
189
|
+
2.7.2
|
data/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Rhales is a **type-safe contract enforcement framework** for server-rendered pag
|
|
|
12
12
|
- ✅ **External Schema References**: Reference TypeScript schema files via `src` attribute for single-source-of-truth patterns
|
|
13
13
|
- ✅ **Multi-directory Search**: Configure `schema_search_paths` to search multiple directories for shared schemas
|
|
14
14
|
- ✅ **tsx Import Mode**: Bundle external schemas with imports via esbuild (`schema_use_tsx_import`)
|
|
15
|
-
- ✅ **Ruby 3.
|
|
15
|
+
- ✅ **Ruby 3.2+ Supported**: Minimum Ruby version
|
|
16
16
|
|
|
17
17
|
**v0.5 features:** Schema-first design, type safety, simplified API, clear context layers, schema tooling.
|
|
18
18
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<!-- examples/token-loader.rue -->
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
Example: Server-Rendered Random Tokens with Nested Iteration
|
|
5
|
+
|
|
6
|
+
Demonstrates four rhales patterns in one small template:
|
|
7
|
+
|
|
8
|
+
1. Generating per-request random data on the server (SecureRandom.hex)
|
|
9
|
+
and passing it to the template via the client: context, instead of
|
|
10
|
+
relying on client JavaScript to fill the page in.
|
|
11
|
+
|
|
12
|
+
2. Validating a nested-array-of-objects shape with
|
|
13
|
+
z.array(z.object(...)) in the <schema> block.
|
|
14
|
+
|
|
15
|
+
3. Iterating with {{#each}} over a top-level array.
|
|
16
|
+
|
|
17
|
+
4. Iterating again inside the outer block to walk a nested array.
|
|
18
|
+
The inner {{#each}} resolves its variable name against the current
|
|
19
|
+
outer item first, so no explicit binding (`as |cell|`) or path
|
|
20
|
+
prefix (`this.glyphs`) is needed.
|
|
21
|
+
|
|
22
|
+
Each render produces a different output. The most common use case is
|
|
23
|
+
a pre-boot loader animated with CSS (see the "Use as a loader" note
|
|
24
|
+
below), but the same shape applies anywhere you want server-generated
|
|
25
|
+
per-request decorative data: identicons, correlation IDs displayed on
|
|
26
|
+
error pages, example IDs in onboarding flows, etc.
|
|
27
|
+
-->
|
|
28
|
+
|
|
29
|
+
<schema lang="js-zod" window="loaderTokens">
|
|
30
|
+
const schema = z.object({
|
|
31
|
+
cells: z.array(z.object({
|
|
32
|
+
glyphs: z.array(z.string().length(1)).length(17)
|
|
33
|
+
})).length(5)
|
|
34
|
+
});
|
|
35
|
+
</schema>
|
|
36
|
+
|
|
37
|
+
<template>
|
|
38
|
+
<style nonce="{{nonce}}">
|
|
39
|
+
.tokens {
|
|
40
|
+
display: inline-flex;
|
|
41
|
+
gap: .3rem;
|
|
42
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
43
|
+
letter-spacing: 0.05em;
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
46
|
+
|
|
47
|
+
<div class="tokens">
|
|
48
|
+
{{#each cells}}<span class="cell">{{#each glyphs}}{{.}}{{/each}}</span>{{/each}}
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|
|
51
|
+
|
|
52
|
+
<logic>
|
|
53
|
+
# Server-Rendered Random Tokens
|
|
54
|
+
#
|
|
55
|
+
# Backend Usage (Ruby):
|
|
56
|
+
#
|
|
57
|
+
# require 'securerandom'
|
|
58
|
+
#
|
|
59
|
+
# cells = Array.new(5) {
|
|
60
|
+
# { glyphs: Array.new(17) { SecureRandom.hex(1)[0] } }
|
|
61
|
+
# }
|
|
62
|
+
#
|
|
63
|
+
# view = Rhales::View.new(
|
|
64
|
+
# request,
|
|
65
|
+
# client: { cells: cells },
|
|
66
|
+
# server: { pageTitle: 'Loading...' }
|
|
67
|
+
# )
|
|
68
|
+
#
|
|
69
|
+
# html = view.render('token-loader')
|
|
70
|
+
#
|
|
71
|
+
# Why these patterns:
|
|
72
|
+
#
|
|
73
|
+
# 1. Randomness lives in the view model, not the template. Rhales
|
|
74
|
+
# templates don't expose Random.new or SecureRandom directly --
|
|
75
|
+
# generate the data in Ruby and hand it to the template via the
|
|
76
|
+
# client: context. Templates stay pure-render and the data flow
|
|
77
|
+
# is explicit.
|
|
78
|
+
#
|
|
79
|
+
# 2. CSPRNG over Kernel#rand. Even for decorative output, SecureRandom
|
|
80
|
+
# signals intent and avoids questions later about why a templating
|
|
81
|
+
# layer is pulling from a seeded RNG.
|
|
82
|
+
#
|
|
83
|
+
# 3. Fresh data per cell, not a shared pool. Generating 5 x 17 = 85
|
|
84
|
+
# fresh glyphs (vs. 17 glyphs shuffled five ways) avoids visible
|
|
85
|
+
# rhyming between adjacent columns.
|
|
86
|
+
#
|
|
87
|
+
# 4. Nested {{#each}} resolves against the current outer item. Inside
|
|
88
|
+
# {{#each cells}}, the inner {{#each glyphs}} looks up "glyphs" on
|
|
89
|
+
# each cell hash automatically -- no need to write {{#each
|
|
90
|
+
# this.glyphs}} or pass an explicit binding. See
|
|
91
|
+
# lib/rhales/core/template_engine.rb EachContext#get for the
|
|
92
|
+
# fallthrough order (current item -> parent context).
|
|
93
|
+
#
|
|
94
|
+
# 5. Cache layer caveat. If a fragment cache wraps the response, the
|
|
95
|
+
# "fresh on every render" guarantee becomes "fresh per cache miss."
|
|
96
|
+
# Worst case is the same tokens until the cache TTL expires, which
|
|
97
|
+
# matches the behavior of a fully static template. For anything
|
|
98
|
+
# where per-request uniqueness actually matters, exclude this block
|
|
99
|
+
# from the cache.
|
|
100
|
+
#
|
|
101
|
+
# Use as a loader:
|
|
102
|
+
#
|
|
103
|
+
# The original motivation for this template was a pre-boot SPA loader
|
|
104
|
+
# that "looks like" something is being computed. To wire that up, wrap
|
|
105
|
+
# the output in role="status" with a visually-hidden label so screen
|
|
106
|
+
# readers announce it correctly, and add CSS that scrolls each cell's
|
|
107
|
+
# glyphs vertically before locking onto a final character. The 17
|
|
108
|
+
# glyphs per cell are sized for a 16-step CSS animation with one
|
|
109
|
+
# resting frame:
|
|
110
|
+
#
|
|
111
|
+
# <div class="tokens" role="status" aria-label="Loading">
|
|
112
|
+
# ... iteration ...
|
|
113
|
+
# <span class="visually-hidden">Loading</span>
|
|
114
|
+
# </div>
|
|
115
|
+
#
|
|
116
|
+
# The animation itself is just CSS keyframes on the iterated spans and
|
|
117
|
+
# is independent of rhales -- the rhales-specific work ends at
|
|
118
|
+
# rendering the markup with fresh random characters per request.
|
|
119
|
+
</logic>
|
|
@@ -202,9 +202,11 @@ module Rhales
|
|
|
202
202
|
items = get_variable_value(items_var)
|
|
203
203
|
|
|
204
204
|
if items.respond_to?(:each)
|
|
205
|
-
items.
|
|
205
|
+
items_array = items.to_a
|
|
206
|
+
total = items_array.size
|
|
207
|
+
items_array.map.with_index do |item, index|
|
|
206
208
|
# Create context for each iteration
|
|
207
|
-
item_context = create_each_context(item, index, items_var)
|
|
209
|
+
item_context = create_each_context(item, index, items_var, total)
|
|
208
210
|
engine = self.class.new('', item_context, partial_resolver: @partial_resolver)
|
|
209
211
|
engine.send(:render_content_nodes, block_content)
|
|
210
212
|
end.join
|
|
@@ -312,8 +314,8 @@ module Rhales
|
|
|
312
314
|
end
|
|
313
315
|
|
|
314
316
|
# Create context for each iteration
|
|
315
|
-
def create_each_context(item, index, items_var)
|
|
316
|
-
EachContext.new(@context, item, index, items_var)
|
|
317
|
+
def create_each_context(item, index, items_var, total = nil)
|
|
318
|
+
EachContext.new(@context, item, index, items_var, total)
|
|
317
319
|
end
|
|
318
320
|
|
|
319
321
|
# HTML escape for XSS protection
|
|
@@ -323,13 +325,14 @@ module Rhales
|
|
|
323
325
|
|
|
324
326
|
# Context wrapper for {{#each}} iterations
|
|
325
327
|
class EachContext
|
|
326
|
-
attr_reader :parent_context, :current_item, :current_index, :items_var
|
|
328
|
+
attr_reader :parent_context, :current_item, :current_index, :items_var, :total
|
|
327
329
|
|
|
328
|
-
def initialize(parent_context, current_item, current_index, items_var)
|
|
330
|
+
def initialize(parent_context, current_item, current_index, items_var, total = nil)
|
|
329
331
|
@parent_context = parent_context
|
|
330
332
|
@current_item = current_item
|
|
331
333
|
@current_index = current_index
|
|
332
334
|
@items_var = items_var
|
|
335
|
+
@total = total
|
|
333
336
|
end
|
|
334
337
|
|
|
335
338
|
def get(variable_name)
|
|
@@ -342,8 +345,7 @@ module Rhales
|
|
|
342
345
|
when '@first'
|
|
343
346
|
return @current_index == 0
|
|
344
347
|
when '@last'
|
|
345
|
-
|
|
346
|
-
return false
|
|
348
|
+
return @total ? @current_index == @total - 1 : false
|
|
347
349
|
end
|
|
348
350
|
|
|
349
351
|
# Check if it's a property of the current item
|
|
@@ -343,7 +343,7 @@ module Rhales
|
|
|
343
343
|
end
|
|
344
344
|
|
|
345
345
|
# Preprocess content to strip XML/HTML comments outside of sections
|
|
346
|
-
# Uses Ruby 3.
|
|
346
|
+
# Uses Ruby 3.2+ pattern matching for robust, secure parsing
|
|
347
347
|
def preprocess_content(content)
|
|
348
348
|
tokens = tokenize_content(content)
|
|
349
349
|
|
data/lib/rhales/version.rb
CHANGED
data/rhales.gemspec
CHANGED
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
|
|
22
22
|
spec.homepage = 'https://github.com/onetimesecret/rhales'
|
|
23
23
|
spec.license = 'MIT'
|
|
24
|
-
spec.required_ruby_version = '>= 3.
|
|
24
|
+
spec.required_ruby_version = '>= 3.2'
|
|
25
25
|
|
|
26
26
|
spec.metadata['source_code_uri'] = 'https://github.com/onetimesecret/rhales'
|
|
27
27
|
spec.metadata['changelog_uri'] = 'https://github.com/onetimesecret/rhales/blob/main/CHANGELOG.md'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rhales
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- delano
|
|
@@ -70,6 +70,7 @@ files:
|
|
|
70
70
|
- ".github/workflows/claude-code-review.yml"
|
|
71
71
|
- ".github/workflows/claude.yml"
|
|
72
72
|
- ".github/workflows/code-smells.yml"
|
|
73
|
+
- ".github/workflows/release-gem.yml"
|
|
73
74
|
- ".github/workflows/ruby-lint.yml"
|
|
74
75
|
- ".github/workflows/yardoc.yml"
|
|
75
76
|
- ".gitignore"
|
|
@@ -120,6 +121,7 @@ files:
|
|
|
120
121
|
- examples/dashboard-with-charts.rue
|
|
121
122
|
- examples/form-with-validation.rue
|
|
122
123
|
- examples/simple-page.rue
|
|
124
|
+
- examples/token-loader.rue
|
|
123
125
|
- examples/vue.rue
|
|
124
126
|
- generate-json-schemas.ts
|
|
125
127
|
- json_schemer_migration_summary.md
|
|
@@ -189,14 +191,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
189
191
|
requirements:
|
|
190
192
|
- - ">="
|
|
191
193
|
- !ruby/object:Gem::Version
|
|
192
|
-
version: '3.
|
|
194
|
+
version: '3.2'
|
|
193
195
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
194
196
|
requirements:
|
|
195
197
|
- - ">="
|
|
196
198
|
- !ruby/object:Gem::Version
|
|
197
199
|
version: '0'
|
|
198
200
|
requirements: []
|
|
199
|
-
rubygems_version:
|
|
201
|
+
rubygems_version: 4.0.10
|
|
200
202
|
specification_version: 4
|
|
201
203
|
summary: Rhales - Server-rendered components with client-side hydration (RSFCs)
|
|
202
204
|
test_files: []
|