philiprehberger-html_builder 0.4.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a1603e5f6aca91335bcd511801b9bf397d9dfc1b003f96113991639b5a36133
4
- data.tar.gz: 3ec0488c7996705017a7d33cf53a0b1828557b602efbf312d271f5e6b062f99e
3
+ metadata.gz: 2422583ee49313a76a8eb70b50d6d8de0040b3f8ea9a9e22722013b0ab7537f5
4
+ data.tar.gz: b61ca71f0b42e279fdd9fa4375b7d67c7c9c8db7a85cc4c6575d5e9ba455b87e
5
5
  SHA512:
6
- metadata.gz: 7694f0a5189a3f0fb4bfe1eac88edc7ef971ab6d48c21d280eb26d7bbd9d0048728effe37f89c14ec770296def739a12ac2c4d65ecef97502dba9924f3226765
7
- data.tar.gz: f156d490b18bf1a1db8010064e0d2fd83887b7928faf24558290844b27189e40aa23132adf8984e9f7481348702df825cd616bba6c4b956884c6c170175c74af
6
+ metadata.gz: 16b555765bd466535c05261da722bdacd670ee72bae7fc635e09de789a02eed4ad6f9020e17404fcb140e34060e1c1be2c5bfff05674e8c5d3867e4f9c8917ed
7
+ data.tar.gz: 75d90b85a6a865213dda114f6e3880d1f059a96ebbfbe348ad872b702fd644c5eece3e86bb56bb1237eae573b74c721dcbf004d3377a05503fce95319e200b69
data/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.0] - 2026-04-16
11
+
12
+ ### Added
13
+ - HTML5 `<!DOCTYPE html>` helper via `Builder#doctype` and `HtmlBuilder.document`
14
+
15
+ ## [0.5.0] - 2026-04-15
16
+
17
+ ### Added
18
+ - `list(items, ordered: false, **attrs, &block)` helper for building `<ul>` or `<ol>` lists from an array of items with optional custom rendering via block
19
+
10
20
  ## [0.4.0] - 2026-04-15
11
21
 
12
22
  ### Added
data/README.md CHANGED
@@ -142,6 +142,27 @@ Philiprehberger::HtmlBuilder.build do
142
142
  end
143
143
  ```
144
144
 
145
+ ### Lists
146
+
147
+ Build `<ul>` or `<ol>` lists from an array of items. Items are text-escaped by default. Pass `ordered: true` for an ordered list. Use a block for custom rendering of each item:
148
+
149
+ ```ruby
150
+ Philiprehberger::HtmlBuilder.build do
151
+ list(%w[Apple Banana Cherry])
152
+ end
153
+ # => '<ul><li>Apple</li><li>Banana</li><li>Cherry</li></ul>'
154
+
155
+ Philiprehberger::HtmlBuilder.build do
156
+ list(%w[First Second], ordered: true, class: 'steps')
157
+ end
158
+ # => '<ol class="steps"><li>First</li><li>Second</li></ol>'
159
+
160
+ Philiprehberger::HtmlBuilder.build do
161
+ list(%w[Alice Bob]) { |name| strong name }
162
+ end
163
+ # => '<ul><li><strong>Alice</strong></li><li><strong>Bob</strong></li></ul>'
164
+ ```
165
+
145
166
  ### CSS Class Helpers
146
167
 
147
168
  Build conditional CSS class strings from mixed arguments. Strings are included as-is, hash keys are included when their value is truthy:
@@ -206,6 +227,31 @@ end
206
227
 
207
228
  Components without parameters use a simple block with no arguments. Components with parameters receive a hash of locals.
208
229
 
230
+ ### HTML5 Documents
231
+
232
+ Emit a standards-compliant `<!DOCTYPE html>` declaration via the `doctype` DSL helper, or use `HtmlBuilder.document` for a full HTML5 document shortcut that prefixes the doctype automatically. The block decides the root element, so no hardcoded `<html>` wrapper is added:
233
+
234
+ ```ruby
235
+ Philiprehberger::HtmlBuilder.build do
236
+ doctype
237
+ html { head { title 'Home' } }
238
+ end
239
+ # => '<!DOCTYPE html><html><head><title>Home</title></head></html>'
240
+
241
+ Philiprehberger::HtmlBuilder.document do
242
+ html do
243
+ head { title 'Home' }
244
+ body { h1 'Welcome' }
245
+ end
246
+ end
247
+ # => "<!DOCTYPE html>\n<html><head><title>Home</title></head><body><h1>Welcome</h1></body></html>"
248
+
249
+ Philiprehberger::HtmlBuilder.document(pretty: true) do
250
+ html { head { title 'Home' } }
251
+ end
252
+ # pretty-printed with the doctype on its own line
253
+ ```
254
+
209
255
  ### Output Modes
210
256
 
211
257
  Choose between minified and pretty-printed output:
@@ -258,12 +304,14 @@ Philiprehberger::HtmlBuilder.merge(header, body, footer)
258
304
  | `HtmlBuilder.build { ... }` | Build minified HTML using the tag DSL, returns a string |
259
305
  | `HtmlBuilder.build_pretty { ... }` | Build pretty-printed HTML with indentation |
260
306
  | `HtmlBuilder.build_minified { ... }` | Alias for `build`, explicitly produces minified output |
307
+ | `HtmlBuilder.document(pretty:, indent_size:) { ... }` | Build an HTML5 document; prefixes `<!DOCTYPE html>` before the block output |
261
308
  | `HtmlBuilder.merge(*fragments)` | Merge multiple HTML fragment strings into one |
262
309
  | `HtmlBuilder.escape(value)` | Escape HTML special characters in a string using the DSL's escaper |
263
310
  | `Builder#to_html` | Render builder contents to a minified HTML string |
264
311
  | `Builder#to_pretty_html` | Render builder contents to a pretty-printed HTML string |
265
312
  | `Builder#text(content)` | Add escaped text content to the current element |
266
313
  | `Builder#raw(html)` | Add raw HTML without escaping |
314
+ | `Builder#doctype` | Emit an HTML5 `<!DOCTYPE html>` declaration |
267
315
  | `Builder#render_if(condition) { ... }` | Conditionally render a block if condition is truthy |
268
316
  | `Builder#render_unless(condition) { ... }` | Conditionally render a block if condition is falsy |
269
317
  | `Builder#define_component(name) { ... }` | Define a reusable named block |
@@ -274,6 +322,7 @@ Philiprehberger::HtmlBuilder.merge(header, body, footer)
274
322
  | `Builder#textarea_field(name, content, label_text:, **attrs)` | Build a label + textarea |
275
323
  | `Builder#hidden_field(name, value)` | Generate a hidden input element |
276
324
  | `Builder#submit(text, **attrs)` | Generate a submit button (default text "Submit") |
325
+ | `Builder#list(items, ordered:, **attrs, &block)` | Build a `<ul>` or `<ol>` from an array of items |
277
326
  | `Builder#class_names(*args)` | Build a conditional CSS class string from strings and hashes |
278
327
  | `Builder#cache(key) { ... }` | Cache rendered block output by key; return cached HTML on repeat calls |
279
328
  | `Escape.html(value)` | Escape HTML special characters in a string |
@@ -80,6 +80,16 @@ module Philiprehberger
80
80
  current_children << node
81
81
  end
82
82
 
83
+ # Emit an HTML5 doctype declaration (`<!DOCTYPE html>`)
84
+ #
85
+ # Has no children and no attributes. In pretty mode the declaration is
86
+ # rendered on its own line at the current indentation.
87
+ #
88
+ # @return [void]
89
+ def doctype
90
+ current_children << DoctypeNode.new
91
+ end
92
+
83
93
  # Conditionally render a block if the condition is truthy
84
94
  #
85
95
  # @param condition [Object] the condition to evaluate
@@ -216,6 +226,26 @@ module Philiprehberger
216
226
  button(text, type: 'submit', **attrs)
217
227
  end
218
228
 
229
+ # Build a list (ul or ol) from an array of items
230
+ #
231
+ # @param items [Array] the list items
232
+ # @param ordered [Boolean] use ol instead of ul (default false)
233
+ # @param attrs [Hash] additional attributes for the list element
234
+ # @yield [item] optional block for custom rendering of each item
235
+ # @return [Node]
236
+ def list(items, ordered: false, **attrs, &block)
237
+ tag_name = ordered ? :ol : :ul
238
+ send(tag_name, **attrs) do
239
+ items.each do |item|
240
+ if block
241
+ li { block.call(item) }
242
+ else
243
+ li item.to_s
244
+ end
245
+ end
246
+ end
247
+ end
248
+
219
249
  # Build a space-joined CSS class string from mixed arguments
220
250
  #
221
251
  # Strings are included as-is. Hash keys are included when their value is truthy.
@@ -286,5 +316,19 @@ module Philiprehberger
286
316
  end
287
317
  end
288
318
  end
319
+
320
+ # A node that renders the HTML5 doctype declaration
321
+ class DoctypeNode
322
+ DECLARATION = '<!DOCTYPE html>'
323
+
324
+ # @return [String] the doctype declaration
325
+ def to_html(indent: nil, indent_size: 2)
326
+ if indent
327
+ "#{' ' * (indent * indent_size)}#{DECLARATION}"
328
+ else
329
+ DECLARATION
330
+ end
331
+ end
332
+ end
289
333
  end
290
334
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module HtmlBuilder
5
- VERSION = '0.4.0'
5
+ VERSION = '0.6.0'
6
6
  end
7
7
  end
@@ -45,6 +45,27 @@ module Philiprehberger
45
45
  build(&)
46
46
  end
47
47
 
48
+ # Build a full HTML5 document: emits `<!DOCTYPE html>` followed by the
49
+ # rendered block, separated by a newline.
50
+ #
51
+ # The block is evaluated at the root level exactly like `.build` / `.build_pretty`,
52
+ # so the caller decides whether to add an `<html>` wrapper. When `pretty: true`,
53
+ # output is pretty-printed with the given indent size.
54
+ #
55
+ # @param pretty [Boolean] whether to pretty-print the block output (default false)
56
+ # @param indent_size [Integer] number of spaces per indent level when pretty (default 2)
57
+ # @yield [Builder] the builder instance for DSL evaluation
58
+ # @return [String] the rendered HTML document string
59
+ # @raise [Error] if no block is given
60
+ def self.document(pretty: false, indent_size: 2, &block)
61
+ raise Error, 'a block is required' unless block
62
+
63
+ builder = Builder.new
64
+ builder.instance_eval(&block)
65
+ body = pretty ? builder.to_pretty_html(indent_size: indent_size) : builder.to_html
66
+ "#{DoctypeNode::DECLARATION}\n#{body}"
67
+ end
68
+
48
69
  # Merge multiple HTML fragment strings into one
49
70
  #
50
71
  # @param fragments [Array<String>] HTML fragments to merge
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.0
4
+ version: 0.6.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-04-15 00:00:00.000000000 Z
11
+ date: 2026-04-16 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.