philiprehberger-html_builder 0.6.0 → 0.8.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 +12 -0
- data/README.md +46 -0
- data/lib/philiprehberger/html_builder/builder.rb +65 -0
- data/lib/philiprehberger/html_builder/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ae55b58a30868dfa5bc7afd1f3300723c88c8f05e97470571a02619e07dd3dfd
|
|
4
|
+
data.tar.gz: 39f947b3d37ac2a914da3fa42f99dd9a3fddd9d43b09498d548b03eae269b694
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4147ae709bc12f9c0d422169e2a8183bc90a92d3ae9b19cda6c62ad7fa927923b436d0a68f1c496eee6de843d9585a26a60786fee3611c3bcf8c765cf0365bd
|
|
7
|
+
data.tar.gz: 37cbec88f3da3293458bdbd8c42270c44d7f6f1b6f1e4b7052154968df53de570f9921dd27f9dde0a4221c56cf1ade5902168c2b98bd7af1f32327134b0b08f2
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.0] - 2026-05-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `Builder#capture { ... }` — render a block in an isolated scope and return the HTML as a string without appending to the current document. Inherits previously defined components, so component reuse works inside captured fragments. Pairs naturally with `Builder#raw` for inserting the captured HTML into the document.
|
|
14
|
+
- Card image reference in the README for registry-side rendering
|
|
15
|
+
|
|
16
|
+
## [0.7.0] - 2026-04-26
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `merge_attrs(*hashes)` helper for merging attribute hashes; concatenates `:class` (space-separated) and `:style` (semicolon-separated) values rather than overwriting
|
|
20
|
+
- `aria(**pairs)` helper for building ARIA attribute hashes from snake_case keyword pairs (converted to `aria-kebab-case` string keys; nil values omitted)
|
|
21
|
+
|
|
10
22
|
## [0.6.0] - 2026-04-16
|
|
11
23
|
|
|
12
24
|
### Added
|
data/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
[](https://rubygems.org/gems/philiprehberger-html_builder)
|
|
5
5
|
[](https://github.com/philiprehberger/rb-html-builder/commits/main)
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
Programmatic HTML builder with tag DSL, auto-escaping, form helpers, components, and output formatting
|
|
8
10
|
|
|
9
11
|
## Requirements
|
|
@@ -163,6 +165,28 @@ end
|
|
|
163
165
|
# => '<ul><li><strong>Alice</strong></li><li><strong>Bob</strong></li></ul>'
|
|
164
166
|
```
|
|
165
167
|
|
|
168
|
+
### Attribute Helpers
|
|
169
|
+
|
|
170
|
+
Merge multiple attribute hashes — concatenating `:class` (single space) and `:style` (`'; '`) values rather than overwriting — and build ARIA attribute hashes from snake_case keyword pairs:
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
Philiprehberger::HtmlBuilder.build do
|
|
174
|
+
base = { class: 'btn', style: 'color: red' }
|
|
175
|
+
variant = { class: 'btn-primary', style: 'font-weight: bold' }
|
|
176
|
+
attrs = merge_attrs(base, variant)
|
|
177
|
+
|
|
178
|
+
button('Save', **attrs)
|
|
179
|
+
end
|
|
180
|
+
# => '<button class="btn btn-primary" style="color: red; font-weight: bold">Save</button>'
|
|
181
|
+
|
|
182
|
+
Philiprehberger::HtmlBuilder.build do
|
|
183
|
+
aria(label: 'Save', expanded: false, describedby: nil)
|
|
184
|
+
end
|
|
185
|
+
# => { 'aria-label' => 'Save', 'aria-expanded' => 'false' }
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`merge_attrs` joins `:class` values with a single space and `:style` values with `'; '`. Other keys follow last-write-wins, and input hashes are not mutated. `aria` converts snake_case keys to `aria-kebab-case` string keys, stringifies values, and omits keys whose value is `nil`.
|
|
189
|
+
|
|
166
190
|
### CSS Class Helpers
|
|
167
191
|
|
|
168
192
|
Build conditional CSS class strings from mixed arguments. Strings are included as-is, hash keys are included when their value is truthy:
|
|
@@ -176,6 +200,25 @@ end
|
|
|
176
200
|
# => <div class="btn btn-lg active">Click me</div>
|
|
177
201
|
```
|
|
178
202
|
|
|
203
|
+
### Capturing Fragments
|
|
204
|
+
|
|
205
|
+
Render a block in a separate scope and capture the HTML as a string instead of appending it to the document. Useful when the same fragment is needed in more than one place or as a value:
|
|
206
|
+
|
|
207
|
+
```ruby
|
|
208
|
+
Philiprehberger::HtmlBuilder.build do
|
|
209
|
+
badge = capture { strong 'NEW', class: 'badge' }
|
|
210
|
+
article do
|
|
211
|
+
h2 'Headline'
|
|
212
|
+
raw badge
|
|
213
|
+
p 'Body text'
|
|
214
|
+
raw badge
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
# => <article><h2>Headline</h2><strong class="badge">NEW</strong><p>Body text</p><strong class="badge">NEW</strong></article>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`capture` inherits any components defined on the parent builder, so component reuse works inside captured fragments.
|
|
221
|
+
|
|
179
222
|
### Fragment Caching
|
|
180
223
|
|
|
181
224
|
Cache rendered block results by key. On subsequent calls with the same key, the cached HTML is returned without re-executing the block:
|
|
@@ -324,7 +367,10 @@ Philiprehberger::HtmlBuilder.merge(header, body, footer)
|
|
|
324
367
|
| `Builder#submit(text, **attrs)` | Generate a submit button (default text "Submit") |
|
|
325
368
|
| `Builder#list(items, ordered:, **attrs, &block)` | Build a `<ul>` or `<ol>` from an array of items |
|
|
326
369
|
| `Builder#class_names(*args)` | Build a conditional CSS class string from strings and hashes |
|
|
370
|
+
| `Builder#merge_attrs(*hashes)` | Merge attribute hashes, concatenating `:class` (space) and `:style` (`'; '`) values |
|
|
371
|
+
| `Builder#aria(**pairs)` | Build an ARIA attribute hash from snake_case keys (rendered as `aria-kebab-case`); omits nil values |
|
|
327
372
|
| `Builder#cache(key) { ... }` | Cache rendered block output by key; return cached HTML on repeat calls |
|
|
373
|
+
| `Builder#capture { ... }` | Render the block in an isolated scope and return its HTML string (no append) |
|
|
328
374
|
| `Escape.html(value)` | Escape HTML special characters in a string |
|
|
329
375
|
|
|
330
376
|
## Development
|
|
@@ -265,6 +265,71 @@ module Philiprehberger
|
|
|
265
265
|
result.join(' ')
|
|
266
266
|
end
|
|
267
267
|
|
|
268
|
+
# Merge multiple attribute hashes into one
|
|
269
|
+
#
|
|
270
|
+
# `:class` values are concatenated with a single space, and `:style` values
|
|
271
|
+
# are concatenated with a semicolon and space. All other keys follow last-write-wins
|
|
272
|
+
# semantics. Input hashes are not mutated.
|
|
273
|
+
#
|
|
274
|
+
# @param hashes [Array<Hash>] one or more attribute hashes to merge
|
|
275
|
+
# @return [Hash] the merged attribute hash
|
|
276
|
+
def merge_attrs(*hashes)
|
|
277
|
+
result = {}
|
|
278
|
+
hashes.each do |h|
|
|
279
|
+
next if h.nil?
|
|
280
|
+
|
|
281
|
+
h.each do |key, value|
|
|
282
|
+
if key == :class && result.key?(:class)
|
|
283
|
+
result[:class] = "#{result[:class]} #{value}"
|
|
284
|
+
elsif key == :style && result.key?(:style)
|
|
285
|
+
result[:style] = "#{result[:style]}; #{value}"
|
|
286
|
+
else
|
|
287
|
+
result[key] = value
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
result
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Build an ARIA attribute hash from keyword pairs
|
|
295
|
+
#
|
|
296
|
+
# snake_case keys are converted to `aria-kebab-case` string keys. Values are
|
|
297
|
+
# converted to strings. Keys whose value is `nil` are omitted from the result.
|
|
298
|
+
#
|
|
299
|
+
# @param pairs [Hash] keyword pairs to convert into ARIA attributes
|
|
300
|
+
# @return [Hash<String, String>] hash with `"aria-*"` string keys and string values
|
|
301
|
+
def aria(**pairs)
|
|
302
|
+
result = {}
|
|
303
|
+
pairs.each do |key, value|
|
|
304
|
+
next if value.nil?
|
|
305
|
+
|
|
306
|
+
result["aria-#{key.to_s.tr('_', '-')}"] = value.to_s
|
|
307
|
+
end
|
|
308
|
+
result
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Render a block in an isolated builder scope and return the HTML string
|
|
312
|
+
# without appending anything to the current document.
|
|
313
|
+
#
|
|
314
|
+
# Useful for template helpers that need to compute a fragment once and
|
|
315
|
+
# then insert it in multiple places (e.g. via {#raw}) or pass it as a
|
|
316
|
+
# string attribute (e.g. ``title:`` containing a small piece of escaped
|
|
317
|
+
# markup) — none of which can be expressed with the regular tag DSL
|
|
318
|
+
# because every tag method side-effects into the current scope.
|
|
319
|
+
#
|
|
320
|
+
# @yield block evaluated against a fresh builder; the same DSL methods
|
|
321
|
+
# (tags, `text`, `raw`, components, etc.) are available
|
|
322
|
+
# @return [String] the captured HTML (minified)
|
|
323
|
+
# @raise [Error] if no block is given
|
|
324
|
+
def capture(&block)
|
|
325
|
+
raise Error, 'a block is required for capture' unless block
|
|
326
|
+
|
|
327
|
+
nested = Builder.new
|
|
328
|
+
@components.each { |name, b| nested.instance_variable_get(:@components)[name] = b }
|
|
329
|
+
nested.instance_eval(&block)
|
|
330
|
+
nested.to_html
|
|
331
|
+
end
|
|
332
|
+
|
|
268
333
|
# Cache a rendered block result by key
|
|
269
334
|
#
|
|
270
335
|
# On the first call with a given key, the block is executed, its rendered
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-html_builder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Build HTML programmatically using a clean tag DSL with nested blocks,
|
|
14
14
|
automatic content escaping, void element support, and attribute hashes.
|