hecks 2.7.0 → 2.9.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.
Files changed (463) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +11 -0
  3. data/lib/hecks/adapters/driven/claude_code.rb +4 -104
  4. data/lib/hecks/adapters/driven/d1.rb +44 -237
  5. data/lib/hecks/adapters/driven/folder.rb +17 -76
  6. data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
  7. data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
  8. data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
  9. data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
  10. data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
  11. data/lib/hecks/adapters/driven/heki.rb +13 -42
  12. data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
  13. data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
  14. data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
  15. data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
  16. data/lib/hecks/adapters/driven/lambda.rb +10 -84
  17. data/lib/hecks/adapters/driven/local_storage.rb +5 -40
  18. data/lib/hecks/adapters/driven/memory.rb +18 -99
  19. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
  20. data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
  21. data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
  22. data/lib/hecks/adapters/driven/postgres/reconnect.rb +4 -30
  23. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
  24. data/lib/hecks/adapters/driven/postgres.rb +145 -134
  25. data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
  26. data/lib/hecks/adapters/driven/prism.rb +7 -30
  27. data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
  28. data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
  29. data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
  30. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
  31. data/lib/hecks/adapters/driven/sqlite.rb +128 -65
  32. data/lib/hecks/adapters/driven/tenant_provisioner.rb +15 -37
  33. data/lib/hecks/adapters/driven.rb +5 -36
  34. data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
  35. data/lib/hecks/behaviors/dsl.rb +7 -25
  36. data/lib/hecks/behaviors/expectations.rb +32 -142
  37. data/lib/hecks/behaviors/ir.rb +7 -20
  38. data/lib/hecks/behaviors/rspec.rb +6 -17
  39. data/lib/hecks/behaviors/runner.rb +10 -34
  40. data/lib/hecks/behaviors.rb +4 -8
  41. data/lib/hecks/bluebook/aggregate.rb +9 -75
  42. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
  43. data/lib/hecks/bluebook/assembly/build.rb +4 -15
  44. data/lib/hecks/bluebook/assembly/contract.rb +15 -136
  45. data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
  46. data/lib/hecks/bluebook/assembly/marks.rb +31 -223
  47. data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
  48. data/lib/hecks/bluebook/assembly.rb +6 -45
  49. data/lib/hecks/bluebook/attribute.rb +12 -61
  50. data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
  51. data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
  52. data/lib/hecks/bluebook/behaviour/chapter.rb +17 -42
  53. data/lib/hecks/bluebook/behaviour/command.rb +14 -66
  54. data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
  55. data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
  56. data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
  57. data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
  58. data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
  59. data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
  60. data/lib/hecks/bluebook/behaviour/read_model.rb +38 -26
  61. data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
  62. data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
  63. data/lib/hecks/bluebook/capabilities.rb +17 -49
  64. data/lib/hecks/bluebook/chapter.rb +10 -48
  65. data/lib/hecks/bluebook/command.rb +23 -81
  66. data/lib/hecks/bluebook/domain_port.rb +14 -63
  67. data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
  68. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
  69. data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
  70. data/lib/hecks/bluebook/dsl/binding_proxy.rb +17 -65
  71. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
  72. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
  73. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
  74. data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
  75. data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
  76. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
  77. data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
  78. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
  79. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -189
  80. data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
  81. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
  82. data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
  83. data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
  84. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
  85. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
  86. data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
  87. data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
  88. data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
  89. data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
  90. data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
  91. data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
  92. data/lib/hecks/bluebook/dsl/world_builder.rb +19 -106
  93. data/lib/hecks/bluebook/entity.rb +13 -71
  94. data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
  95. data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
  96. data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
  97. data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
  98. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
  99. data/lib/hecks/bluebook/expression/resolver.rb +37 -660
  100. data/lib/hecks/bluebook/hexagon.rb +5 -28
  101. data/lib/hecks/bluebook/lifecycle.rb +5 -16
  102. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
  103. data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
  104. data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
  105. data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
  106. data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
  107. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
  108. data/lib/hecks/bluebook/meta_validator/shapes.rb +54 -268
  109. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
  110. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
  111. data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
  112. data/lib/hecks/bluebook/meta_validator.rb +79 -349
  113. data/lib/hecks/bluebook/model_check/client_profile.rb +10 -91
  114. data/lib/hecks/bluebook/model_check.rb +102 -600
  115. data/lib/hecks/bluebook/pattern_subset.rb +11 -69
  116. data/lib/hecks/bluebook/process_manager.rb +13 -83
  117. data/lib/hecks/bluebook/project_loader.rb +3 -39
  118. data/lib/hecks/bluebook/project_register.rb +12 -60
  119. data/lib/hecks/bluebook/query.rb +9 -48
  120. data/lib/hecks/bluebook/read_model.rb +57 -81
  121. data/lib/hecks/bluebook/reference.rb +7 -41
  122. data/lib/hecks/bluebook/smoke_test.rb +8 -98
  123. data/lib/hecks/bluebook/synthesizer.rb +13 -62
  124. data/lib/hecks/bluebook/translation.rb +26 -62
  125. data/lib/hecks/bluebook/value_object.rb +11 -36
  126. data/lib/hecks/bluebook.rb +5 -22
  127. data/lib/hecks/cache_dir.rb +84 -0
  128. data/lib/hecks/cli/document.rb +86 -0
  129. data/lib/hecks/cli/ir.rb +45 -0
  130. data/lib/hecks/cli/mcp.rb +31 -0
  131. data/lib/hecks/cli/mcp_door.rb +366 -0
  132. data/lib/hecks/cli/model_check.rb +203 -0
  133. data/lib/hecks/cli/project_cli.rb +90 -0
  134. data/lib/hecks/cli/project_diagrams.rb +36 -0
  135. data/lib/hecks/cli/run.rb +173 -0
  136. data/lib/hecks/cli/smoke_test.rb +45 -0
  137. data/lib/hecks/cli/stores.rb +53 -0
  138. data/lib/hecks/cli.rb +160 -0
  139. data/lib/hecks/construct.rb +8 -34
  140. data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
  141. data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
  142. data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
  143. data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
  144. data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
  145. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
  146. data/lib/hecks/embryonaut_bluebook.rb +4 -71
  147. data/lib/hecks/facade/cli_door.rb +24 -106
  148. data/lib/hecks/facade/cli_runner.rb +28 -172
  149. data/lib/hecks/facade/command_request.rb +13 -33
  150. data/lib/hecks/facade/handle.rb +34 -121
  151. data/lib/hecks/facade/json_door.rb +31 -146
  152. data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
  153. data/lib/hecks/facade/surface/chapter.rb +10 -54
  154. data/lib/hecks/facade/surface.rb +8 -23
  155. data/lib/hecks/facade.rb +10 -25
  156. data/lib/hecks/forms/app.rb +15 -70
  157. data/lib/hecks/forms/command_form_renderer.rb +10 -29
  158. data/lib/hecks/forms/field_renderer.rb +17 -156
  159. data/lib/hecks/forms/field_shape.rb +30 -222
  160. data/lib/hecks/forms/html.rb +12 -47
  161. data/lib/hecks/forms/index_renderer.rb +1 -4
  162. data/lib/hecks/forms/page.rb +3 -9
  163. data/lib/hecks/forms/params.rb +14 -60
  164. data/lib/hecks/forms/port_argument.rb +2 -19
  165. data/lib/hecks/forms/query_form_renderer.rb +4 -79
  166. data/lib/hecks/forms/record_renderer.rb +8 -75
  167. data/lib/hecks/forms/record_table.rb +7 -50
  168. data/lib/hecks/forms/reference_options.rb +3 -30
  169. data/lib/hecks/forms/value_object_shape.rb +8 -35
  170. data/lib/hecks/forms.rb +5 -33
  171. data/lib/hecks/fqn.rb +5 -12
  172. data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
  173. data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
  174. data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
  175. data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
  176. data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
  177. data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
  178. data/lib/hecks/framework.rb +11 -80
  179. data/lib/hecks/freezer.rb +5 -39
  180. data/lib/hecks/grammar/expression.bluebook +15 -40
  181. data/lib/hecks/grammar/translation.bluebook +3 -6
  182. data/lib/hecks/grammar.rb +16 -41
  183. data/lib/hecks/ir.rb +14 -83
  184. data/lib/hecks/language/adapter.bluebook +2 -7
  185. data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
  186. data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
  187. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
  188. data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
  189. data/lib/hecks/language/bluebook/command.bluebook +27 -144
  190. data/lib/hecks/language/bluebook/entity.bluebook +24 -97
  191. data/lib/hecks/language/bluebook/policy.bluebook +16 -67
  192. data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
  193. data/lib/hecks/language/bluebook/projection.bluebook +126 -60
  194. data/lib/hecks/language/bluebook/query.bluebook +12 -43
  195. data/lib/hecks/language/bluebook/shape.bluebook +23 -73
  196. data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
  197. data/lib/hecks/language/bluebook/vocabulary.bluebook +98 -197
  198. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
  199. data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
  200. data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
  201. data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
  202. data/lib/hecks/language/oidc.json +35 -0
  203. data/lib/hecks/language/port.bluebook +4 -17
  204. data/lib/hecks/language/translation/translation.bluebook +3 -9
  205. data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
  206. data/lib/hecks/language/world/wiring.bluebook +1 -3
  207. data/lib/hecks/language/world/world.bluebook +5 -7
  208. data/lib/hecks/literal.rb +8 -50
  209. data/lib/hecks/mcp_door_scope.rb +95 -0
  210. data/lib/hecks/mcp_stdio_guard.rb +4 -41
  211. data/lib/hecks/naming.rb +29 -137
  212. data/lib/hecks/ports/access_control.rb +7 -23
  213. data/lib/hecks/ports/agent/answers.rb +5 -23
  214. data/lib/hecks/ports/agent.rb +47 -172
  215. data/lib/hecks/ports/authentication.rb +12 -39
  216. data/lib/hecks/ports/authorization.rb +16 -43
  217. data/lib/hecks/ports/clock.rb +5 -45
  218. data/lib/hecks/ports/extraction.rb +7 -12
  219. data/lib/hecks/ports/identity_assignment.rb +8 -29
  220. data/lib/hecks/ports/identity_generation.rb +6 -29
  221. data/lib/hecks/ports/identity_resolution.rb +2 -10
  222. data/lib/hecks/ports/key_vault.rb +9 -45
  223. data/lib/hecks/ports/persistence/append_only.rb +62 -65
  224. data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
  225. data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
  226. data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
  227. data/lib/hecks/ports/persistence/plugin.rb +13 -42
  228. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
  229. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
  230. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
  231. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +7 -26
  232. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
  233. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
  234. data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
  235. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
  236. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
  237. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
  238. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
  239. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
  240. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
  241. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
  242. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
  243. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
  244. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
  245. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
  246. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
  247. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
  248. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
  249. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
  250. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
  251. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
  252. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
  253. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
  254. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
  255. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
  256. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
  257. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +22 -122
  258. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
  259. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
  260. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
  261. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
  262. data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
  263. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
  264. data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
  265. data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
  266. data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
  267. data/lib/hecks/ports/persistence/state_codec.rb +23 -103
  268. data/lib/hecks/ports/persistence.rb +7 -14
  269. data/lib/hecks/ports/projection.rb +42 -84
  270. data/lib/hecks/ports/query/in_memory.rb +10 -58
  271. data/lib/hecks/ports/query/ordering.rb +11 -32
  272. data/lib/hecks/ports/query.rb +14 -37
  273. data/lib/hecks/ports.rb +2 -8
  274. data/lib/hecks/projections/bootstrap_table.rb +11 -43
  275. data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
  276. data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
  277. data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
  278. data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
  279. data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
  280. data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
  281. data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
  282. data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
  283. data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
  284. data/lib/hecks/projections/deploy/fargate.rb +35 -202
  285. data/lib/hecks/projections/deploy/lambda.rb +151 -679
  286. data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
  287. data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
  288. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
  289. data/lib/hecks/projections/deploy/preview/script.rb +2 -14
  290. data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
  291. data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
  292. data/lib/hecks/projections/deploy/preview/template.rb +2 -58
  293. data/lib/hecks/projections/deploy/preview.rb +2 -67
  294. data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
  295. data/lib/hecks/projections/deploy/scripts.rb +4 -57
  296. data/lib/hecks/projections/deploy/shared.rb +47 -146
  297. data/lib/hecks/projections/deploy/smoke.rb +4 -61
  298. data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
  299. data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
  300. data/lib/hecks/projections/deploy/template_diff.rb +5 -57
  301. data/lib/hecks/projections/diagrams.rb +20 -582
  302. data/lib/hecks/projections/glossary/html.rb +14 -130
  303. data/lib/hecks/projections/glossary/markdown.rb +8 -69
  304. data/lib/hecks/projections/glossary/mermaid.rb +9 -66
  305. data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
  306. data/lib/hecks/projections/glossary/sentences.rb +14 -159
  307. data/lib/hecks/projections/glossary.rb +30 -222
  308. data/lib/hecks/projections/ir.rb +3 -10
  309. data/lib/hecks/projections/model/deviations.rb +15 -42
  310. data/lib/hecks/projections/model.rb +12 -51
  311. data/lib/hecks/projections/oidc.rb +12 -56
  312. data/lib/hecks/projections/parser_table.rb +9 -28
  313. data/lib/hecks/projections/reference.rb +9 -16
  314. data/lib/hecks/projections/rust_vocabulary.rb +12 -250
  315. data/lib/hecks/projections/shape.rb +3 -29
  316. data/lib/hecks/projections/statements.rb +7 -86
  317. data/lib/hecks/projections/vocabulary.rb +6 -22
  318. data/lib/hecks/projections.rb +2 -16
  319. data/lib/hecks/projector/cli_projector.rb +60 -375
  320. data/lib/hecks/projector/docs_projector.rb +10 -201
  321. data/lib/hecks/projector/exporter.rb +46 -138
  322. data/lib/hecks/projector/ir_projector.rb +2 -9
  323. data/lib/hecks/projector/narrate_projector.rb +15 -189
  324. data/lib/hecks/projector/target.rb +13 -76
  325. data/lib/hecks/projector.rb +30 -121
  326. data/lib/hecks/query_specification/common/comparators.rb +5 -26
  327. data/lib/hecks/query_specification/common/comparison.rb +35 -143
  328. data/lib/hecks/query_specification/common/dsl.rb +8 -24
  329. data/lib/hecks/query_specification/common/null_policy.rb +20 -70
  330. data/lib/hecks/query_specification/common/options.rb +2 -6
  331. data/lib/hecks/query_specification/common/where_clause.rb +2 -10
  332. data/lib/hecks/query_specification/field_path.rb +37 -77
  333. data/lib/hecks/query_specification/hop_path.rb +12 -76
  334. data/lib/hecks/release/gem_pin.rb +2 -26
  335. data/lib/hecks/release/runner/ci_publisher.rb +93 -0
  336. data/lib/hecks/release/runner/commands.rb +66 -0
  337. data/lib/hecks/release/runner/console.rb +52 -0
  338. data/lib/hecks/release/runner/gem_publisher.rb +43 -0
  339. data/lib/hecks/release/runner/git.rb +48 -0
  340. data/lib/hecks/release/runner/npm_publisher.rb +75 -0
  341. data/lib/hecks/release/runner/preflight.rb +104 -0
  342. data/lib/hecks/release/runner/published.rb +55 -0
  343. data/lib/hecks/release/runner/tagger.rb +80 -0
  344. data/lib/hecks/release/runner.rb +215 -0
  345. data/lib/hecks/rendering.rb +4 -26
  346. data/lib/hecks/router/namespace_installer.rb +12 -36
  347. data/lib/hecks/router.rb +4 -8
  348. data/lib/hecks/runtime/aggregate_lock.rb +8 -33
  349. data/lib/hecks/runtime/boot_gates.rb +3 -18
  350. data/lib/hecks/runtime/caller.rb +19 -59
  351. data/lib/hecks/runtime/capability_graph.rb +5 -21
  352. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
  353. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
  354. data/lib/hecks/runtime/command_interpreter.rb +125 -307
  355. data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
  356. data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
  357. data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
  358. data/lib/hecks/runtime/command_rules/emission.rb +8 -21
  359. data/lib/hecks/runtime/command_rules/references.rb +23 -202
  360. data/lib/hecks/runtime/dependency_planning.rb +30 -102
  361. data/lib/hecks/runtime/dispatcher.rb +82 -241
  362. data/lib/hecks/runtime/entity_element.rb +40 -462
  363. data/lib/hecks/runtime/entity_interpreter.rb +65 -220
  364. data/lib/hecks/runtime/errors.rb +32 -115
  365. data/lib/hecks/runtime/event.rb +3 -25
  366. data/lib/hecks/runtime/identity.rb +18 -154
  367. data/lib/hecks/runtime/instance.rb +18 -150
  368. data/lib/hecks/runtime/interpreting.rb +11 -70
  369. data/lib/hecks/runtime/invocation.rb +28 -164
  370. data/lib/hecks/runtime/loader.rb +48 -186
  371. data/lib/hecks/runtime/outbox.rb +38 -261
  372. data/lib/hecks/runtime/policy_interpreter.rb +38 -200
  373. data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
  374. data/lib/hecks/runtime/query_interpreter.rb +45 -187
  375. data/lib/hecks/runtime/reaction_invocation.rb +36 -138
  376. data/lib/hecks/runtime/read_model_interpreter.rb +139 -208
  377. data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
  378. data/lib/hecks/runtime/reference_hop.rb +8 -81
  379. data/lib/hecks/runtime/refusal_wording.rb +8 -89
  380. data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
  381. data/lib/hecks/runtime/registry/verification.rb +52 -225
  382. data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
  383. data/lib/hecks/runtime/registry.rb +103 -244
  384. data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
  385. data/lib/hecks/runtime/routing.rb +4 -15
  386. data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
  387. data/lib/hecks/runtime/saga_interpreter.rb +79 -263
  388. data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
  389. data/lib/hecks/runtime/tenant_check.rb +6 -68
  390. data/lib/hecks/runtime/tenant_scope.rb +3 -28
  391. data/lib/hecks/runtime/value/admission.rb +16 -67
  392. data/lib/hecks/runtime/value/coercion.rb +155 -641
  393. data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
  394. data/lib/hecks/runtime/value.rb +12 -84
  395. data/lib/hecks/runtime.rb +17 -65
  396. data/lib/hecks/storehouse.rb +146 -567
  397. data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
  398. data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
  399. data/lib/hecks/vendoring/git_environment.rb +2 -8
  400. data/lib/hecks/vendoring/git_source.rb +4 -10
  401. data/lib/hecks/vendoring.rb +7 -46
  402. data/lib/hecks/version.rb +3 -14
  403. data/lib/hecks/vocabulary.rb +5 -0
  404. data/lib/hecks.rb +32 -82
  405. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
  406. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
  407. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
  408. metadata +30 -59
  409. data/lib/hecks/bench/cli.rb +0 -74
  410. data/lib/hecks/bench/environment.rb +0 -100
  411. data/lib/hecks/bench/postgres_probe.rb +0 -41
  412. data/lib/hecks/bench/report.rb +0 -116
  413. data/lib/hecks/bench/ruby_runner.rb +0 -151
  414. data/lib/hecks/bench/run.rb +0 -55
  415. data/lib/hecks/bench/rust_runner.rb +0 -165
  416. data/lib/hecks/bench/stats.rb +0 -75
  417. data/lib/hecks/bench/suite.rb +0 -172
  418. data/lib/hecks/bench/workload.rb +0 -137
  419. data/lib/hecks/bench.rb +0 -28
  420. data/lib/hecks/codemod.rb +0 -443
  421. data/lib/hecks/corpus.rb +0 -479
  422. data/lib/hecks/doc/reference.rb +0 -585
  423. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
  424. data/lib/hecks/fuzzing/combination_miner.rb +0 -178
  425. data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
  426. data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
  427. data/lib/hecks/fuzzing/differential.rb +0 -192
  428. data/lib/hecks/fuzzing/domain_generator.rb +0 -871
  429. data/lib/hecks/fuzzing/era_boundary.rb +0 -154
  430. data/lib/hecks/fuzzing/form_census.rb +0 -285
  431. data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
  432. data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
  433. data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
  434. data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
  435. data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
  436. data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
  437. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
  438. data/lib/hecks/fuzzing/properties/guards.rb +0 -398
  439. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
  440. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
  441. data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
  442. data/lib/hecks/fuzzing/properties/querying.rb +0 -260
  443. data/lib/hecks/fuzzing/properties.rb +0 -267
  444. data/lib/hecks/fuzzing/qa_settings.rb +0 -164
  445. data/lib/hecks/fuzzing/replay.rb +0 -930
  446. data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
  447. data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
  448. data/lib/hecks/fuzzing/self_consistency.rb +0 -825
  449. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
  450. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
  451. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
  452. data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
  453. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
  454. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
  455. data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
  456. data/lib/hecks/fuzzing/shrinker.rb +0 -309
  457. data/lib/hecks/fuzzing/structural_skips.rb +0 -53
  458. data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
  459. data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
  460. data/lib/hecks/fuzzing/value_generator.rb +0 -340
  461. data/lib/hecks/fuzzing.rb +0 -23
  462. data/lib/hecks/grammar/evolve.rb +0 -524
  463. data/lib/hecks/query_ir.rb +0 -482
@@ -7,18 +7,14 @@ require_relative "params"
7
7
 
8
8
  module Hecks
9
9
  module Forms
10
- # Command -> the page body for its HTML form — the working half of
11
- # what `command_form.bluebook` names (see docs/command-form-and-query-
12
- # form-bluebook.md). Every command in every loaded, exposed bluebook
13
- # renders through here — nothing here is per-command code; see that
14
- # doc for the 1:1 rule this deliberately holds to for now.
10
+ # Renders a command as the page body of its HTML form; no per-command code
11
+ # (see docs/command-form-and-query-form-bluebook.md).
15
12
  module CommandFormRenderer
16
13
  # Lists every field a command's form shows: the `to` identity picker first (unless the
17
14
  # command creates its aggregate), then one field per command attribute.
18
15
  #
19
- # The same field list a POST handler needs to cast raw params against
20
- # (params.rb's `Params.extract`) — one derivation, so a page never
21
- # renders an input the submit handler doesn't also expect.
16
+ # The same list `Params.extract` casts a POST against, so a page never renders
17
+ # an input the submit handler does not expect.
22
18
  #
23
19
  # @param aggregate [Bluebook::Aggregate] the aggregate that declares the command
24
20
  # @param command [Bluebook::Command] the command the form submits
@@ -33,25 +29,14 @@ module Hecks
33
29
  # Renders the page body for one command: header, refusal banner, the form itself, and
34
30
  # the inspect panel.
35
31
  #
36
- # `registry` serves only to populate a `:reference` field's
37
- # `<select>` with real records (including the identity picker itself,
38
- # for a non-creating command — see `identity_field` below). `values`/
39
- # `error` carry a sticky re-render after a refused submission; leave
40
- # both nil for a fresh form. `prefill` carries values a caller
41
- # arrived with (typically `?to=...` off a record's own detail page) —
42
- # kept separate from `values` because a prefill is not an error retry
43
- # and should not be treated as one by a future reader of this code.
32
+ # `registry` populates `:reference` selects. `values` and `error` re-render after a
33
+ # refusal; `prefill` carries values the caller arrived with (`?to=...`), not a retry.
44
34
  #
45
35
  # @param registry [Runtime::Registry] the booted registry, read for reference options
46
- # @param domain [String] name of the domain (chapter) the aggregate belongs to
47
- # @param aggregate [Bluebook::Aggregate] the aggregate that declares the command
48
- # @param command [Bluebook::Command] the command the form submits
49
36
  # @param action [String] URL path the form posts to
50
- # @param values [Hash{String => Object}, nil] the raw submission to re-show after a
51
- # refusal, keyed by dotted field path; nil falls back to `prefill`
37
+ # @param values [Hash{String => Object}, nil] raw submission to re-show; nil uses `prefill`
52
38
  # @param error [Exception, nil] the refusal to show in the banner; nil for a fresh form
53
- # @param prefill [Hash{String => String}] values the caller arrived with, keyed by
54
- # dotted field path; ignored when `values` is given
39
+ # @param prefill [Hash{String => String}] values the caller arrived with, by dotted path
55
40
  # @return [String] the HTML page body, without the surrounding page chrome
56
41
  def self.render(registry:, domain:, aggregate:, command:, action:, values: nil, error: nil, prefill: {})
57
42
  all_fields = fields_for(aggregate, command)
@@ -130,12 +115,8 @@ module Hecks
130
115
  # Maps a refusal to per-field messages; always empty, so every refusal is shown in
131
116
  # the banner rather than beside a field.
132
117
  #
133
- # No structured field attribution exists on a domain refusal today
134
- # (it is a typed exception with a rendered message — see
135
- # docs/command-form-and-query-form-bluebook.md's note on
136
- # `RefusalWording`), so this returns empty rather than guessing which
137
- # field a message meant; the banner above carries the real text
138
- # instead of a misattributed hint.
118
+ # A domain refusal carries no field attribution, so the banner shows the message
119
+ # instead of guessing a field.
139
120
  #
140
121
  # @param _error [Exception, nil] the refusal raised by the submission; ignored
141
122
  # @return [Hash] always empty; keys would be dotted field paths
@@ -4,31 +4,13 @@ require_relative "../naming"
4
4
 
5
5
  module Hecks
6
6
  module Forms
7
- # A Field (field_shape.rb) -> the `<div class="field">...</div>` or
8
- # `<fieldset>...</fieldset>` markup for it. One renderer, called
9
- # recursively for a group's children, shared by CommandFormRenderer
10
- # (POST) and QueryFormRenderer (GET) — the same attribute shape asks
11
- # for the same input either way; only the surrounding `<form>`'s
12
- # method differs.
7
+ # Renders a Field (field_shape.rb) as form markup, recursing into a group's children.
8
+ # Shared by CommandFormRenderer (POST) and QueryFormRenderer (GET).
13
9
  module FieldRenderer
14
- # Renders one field, and recursively a group's children, as form markup with any held
15
- # value filled back in.
10
+ # Renders one field, filling any held value back in.
16
11
  #
17
- # `values` is the hash a sticky re-render (a rejected command,
18
- # a submitted query) carries back — read with the same dotted path a
19
- # Field already carries, via `dig`. `reference_options` maps a
20
- # `:reference` field's own path to `[[id, label], ...]`, built by the
21
- # caller (it needs a repository; this module stays pure markup).
22
- #
23
- # @param field [Forms::Field] the field to render
24
- # @param values [Hash{String, Symbol => Object}] held values, either flat and keyed by
25
- # dotted path or nested by path segment; see `dig`
26
- # @param errors [Hash{String => String}, nil] message per dotted field path; nil for none
27
- # @param reference_options [Hash{String => Array<Array>, nil}] `[id, label]` pairs per
28
- # `:reference` field path, as `ReferenceOptions.collect` builds them; a nil or empty
29
- # entry renders a plain text input
30
- # @return [String] HTML for the field: a `<fieldset>` for `:group` and `:money`, a
31
- # `<div class="field">` otherwise
12
+ # `reference_options` is built by the caller, since it needs a repository and
13
+ # this module stays pure markup.
32
14
  def self.render(field, values: {}, errors: nil, reference_options: {})
33
15
  case field.kind
34
16
  when :group then group(field, values, errors, reference_options, tag: "fieldset")
@@ -38,17 +20,6 @@ module Hecks
38
20
  end
39
21
  end
40
22
 
41
- # Renders a field's children inside one enclosing element, with the field's label as
42
- # its `<legend>`.
43
- #
44
- # @param field [Forms::Field] a `:group` or `:money` field carrying `children`
45
- # @param values [Hash{String, Symbol => Object}] held values; see `dig`
46
- # @param errors [Hash{String => String}, nil] message per dotted field path
47
- # @param reference_options [Hash{String => Array<Array>, nil}] `[id, label]` pairs per
48
- # `:reference` field path
49
- # @param tag [String] name of the enclosing element, such as `"fieldset"`
50
- # @param css [String, nil] class attribute for the enclosing element; nil for none
51
- # @return [String] HTML for the enclosing element and every child
52
23
  def self.group(field, values, errors, reference_options, tag:, css: nil)
53
24
  inner = field.children.map { |child| render(child, values: values, errors: errors, reference_options: reference_options) }
54
25
  <<~HTML
@@ -59,14 +30,7 @@ module Hecks
59
30
  HTML
60
31
  end
61
32
 
62
- # Renders a `:list` field as one textarea taking an element per line, which is the shape
63
- # `Params.extract_list` reads back.
64
- #
65
- # @param field [Forms::Field] a `:list` field whose first child describes one element
66
- # @param values [Hash{String, Symbol => Object}] held values; the list's entry may be an
67
- # Array, a newline-joined String, or absent
68
- # @param _errors [Hash{String => String}, nil] ignored; a list shows no per-field message
69
- # @return [String] HTML for the labelled textarea and its help line
33
+ # One textarea, an element per line: the shape `Params.extract_list` reads back.
70
34
  def self.list(field, values, _errors)
71
35
  item = field.children.first
72
36
  current = Array(dig(values, field.path)).join("\n")
@@ -80,15 +44,6 @@ module Hecks
80
44
  HTML
81
45
  end
82
46
 
83
- # Renders a single-value field with the control its `kind` calls for, wrapped with its
84
- # label, help text and error.
85
- #
86
- # @param field [Forms::Field] a leaf field
87
- # @param values [Hash{String, Symbol => Object}] held values; see `dig`
88
- # @param errors [Hash{String => String}, nil] message per dotted field path
89
- # @param reference_options [Hash{String => Array<Array>, nil}] `[id, label]` pairs per
90
- # `:reference` field path
91
- # @return [String] HTML for the `<div class="field">` block
92
47
  def self.leaf(field, values, errors, reference_options)
93
48
  value = dig(values, field.path)
94
49
  error = errors && errors[field.path]
@@ -103,14 +58,7 @@ module Hecks
103
58
  wrap(field, body, error)
104
59
  end
105
60
 
106
- # Wraps a rendered control with its label, help text and error message; a `:boolean`
107
- # gets no label here because `checkbox` renders its own beside the box.
108
- #
109
- # @param field [Forms::Field] the field the control belongs to
110
- # @param body [String] HTML of the control itself
111
- # @param error [String, nil] message shown as an alert; nil for none
112
- # @return [String] HTML for the `<div class="field">` block, classed `has-error` when
113
- # `error` is given
61
+ # A :boolean gets no label here because `checkbox` renders its own beside the box.
114
62
  def self.wrap(field, body, error)
115
63
  <<~HTML
116
64
  <div class="field#{' has-error' if error}">
@@ -122,13 +70,6 @@ module Hecks
122
70
  HTML
123
71
  end
124
72
 
125
- # Renders an `<input>` of the field's `html_type`, falling back to the field's default
126
- # for a missing value; a whole-cents number also gets a live preview span.
127
- #
128
- # @param field [Forms::Field] a leaf field
129
- # @param value [Object, nil] the held value, rendered with `to_s`; nil shows the default
130
- # @return [String] HTML for the input, followed by the preview `<span>` when the field
131
- # is a `:number` whose help text mentions cents
132
73
  def self.input(field, value)
133
74
  money = field.kind == :number && field.help.to_s.include?("cents")
134
75
  tag = Tag.void("input", id: dom_id(field.path), name: field.path, type: field.html_type,
@@ -140,23 +81,11 @@ module Hecks
140
81
  %(#{tag} <span id="#{dom_id(field.path)}-preview" class="mono help" aria-live="polite"></span>)
141
82
  end
142
83
 
143
- # Renders a `<textarea>` holding the value as escaped text; unlike `input`, the field's
144
- # default is not filled in.
145
- #
146
- # @param field [Forms::Field] a `:textarea` field
147
- # @param value [Object, nil] the held value, rendered with `to_s`; nil renders empty
148
- # @return [String] HTML for the textarea
149
84
  def self.textarea(field, value)
150
85
  %(<textarea id="#{dom_id(field.path)}" name="#{Escape.attr(field.path)}" #{aria(field)}>#{Escape.html(value)}</textarea>)
151
86
  end
152
87
 
153
- # Renders a checkbox preceded by a hidden `"0"` input of the same name, so an unticked
154
- # box still submits a value, with the field's label beside it.
155
- #
156
- # @param field [Forms::Field] a `:boolean` field
157
- # @param value [Boolean, String, nil] the held value; `true`, `"true"`, `"on"` and `"1"`
158
- # tick the box, and nil falls back to a default of exactly `true`
159
- # @return [String] HTML for the checkbox row
88
+ # The hidden "0" input makes an unticked box still submit a value.
160
89
  def self.checkbox(field, value)
161
90
  checked = value.nil? ? field.default == true : [true, "true", "on", "1"].include?(value)
162
91
  <<~HTML
@@ -168,13 +97,6 @@ module Hecks
168
97
  HTML
169
98
  end
170
99
 
171
- # Renders one radio button per option, pre-checking the one equal to the held value or,
172
- # failing that, to the field's default.
173
- #
174
- # @param field [Forms::Field] a `:radio` field whose `options` are `[value, label]` pairs
175
- # @param value [Object, nil] the held value, compared with `to_s`; nil falls back to the
176
- # default
177
- # @return [String] HTML for the `<div role="radiogroup">` block
178
100
  def self.radio_group(field, value)
179
101
  selected = value || field.default
180
102
  options = field.options.map do |option_value, option_label|
@@ -187,13 +109,6 @@ module Hecks
187
109
  %(<div class="radio-group" role="radiogroup">#{options.join}</div>)
188
110
  end
189
111
 
190
- # Renders a `<select>` over the field's fixed options, with a blank "—" entry first when
191
- # the field is optional.
192
- #
193
- # @param field [Forms::Field] a `:select` field whose `options` are `[value, label]` pairs
194
- # @param value [Object, nil] the held value, compared with `to_s`; nil falls back to the
195
- # default
196
- # @return [String] HTML for the select
197
112
  def self.select(field, value)
198
113
  selected = value || field.default
199
114
  options = field.options.map do |option_value, option_label|
@@ -204,16 +119,7 @@ module Hecks
204
119
  %(<select id="#{dom_id(field.path)}" name="#{Escape.attr(field.path)}" #{aria(field)}>#{blank}#{options.join}</select>)
205
120
  end
206
121
 
207
- # Renders a `<select>` of existing records for a `:reference` field, degrading to a plain
208
- # text id input when no records are on offer.
209
- #
210
- # @param field [Forms::Field] a `:reference` field; its `html_type` is set to `"text"`
211
- # in place when the text fallback is taken
212
- # @param value [Object, nil] the held id, compared with `to_s`; nil preselects the
213
- # disabled "choose one…" entry of a required field
214
- # @param options [Array<Array>, nil] `[id, label]` pairs to offer; nil or empty takes the
215
- # text fallback
216
- # @return [String] HTML for the select, or for the fallback `<input>`
122
+ # Falls back to a plain text id input when no records are on offer.
217
123
  def self.reference_select(field, value, options)
218
124
  return input(field.tap { |f| f.html_type = "text" }, value) unless options && !options.empty?
219
125
 
@@ -228,78 +134,33 @@ module Hecks
228
134
  %(<select id="#{dom_id(field.path)}" name="#{Escape.attr(field.path)}" #{aria(field)}>#{blank}#{rendered.join}</select>)
229
135
  end
230
136
 
231
- # Renders the asterisk that marks a required field's label or legend.
232
- #
233
- # @param field [Forms::Field] the field being labelled
234
- # @return [String] HTML for the mark, or `""` when the field is optional
235
137
  def self.required_mark(field) = field.required? ? %(<span class="required-mark" title="required">*</span>) : ""
236
138
 
237
- # Derives the element id of a field's control from its dotted path, such as
238
- # `"f-amount-cents"` for `"amount.cents"`.
239
- #
240
- # @param path [String, Symbol] the field's dotted path
241
- # @return [String] the id, `"f-"` followed by the path with dots turned to hyphens
139
+ # "amount.cents" -> "f-amount-cents".
242
140
  def self.dom_id(path) = "f-#{path.to_s.tr('.', '-')}"
243
141
 
244
- # Picks the `pattern` attribute an input carries, withholding it from a `:reference`
245
- # field, whose input takes a record id rather than the attribute's own value.
246
- #
247
- # @param field [Forms::Field] a leaf field
248
- # @return [String, nil] the regex source the attribute declares as `pattern:`; nil for a
249
- # `:reference` field or when none is declared
142
+ # A :reference input takes a record id, so the attribute's own pattern must not apply.
250
143
  def self.leaf_pattern(field) = field.kind == :reference ? nil : field.pattern
251
144
 
252
- # Names the preview span a whole-cents input's `data-money-cents` attribute points at.
253
- #
254
- # @param field [Forms::Field] the cents field
255
- # @return [String] a CSS id selector, such as `"#f-amount-cents-preview"`
256
145
  def self.money_preview_target(field) = "##{dom_id(field.path)}-preview"
257
146
 
258
- # Renders the `required` and `aria-describedby` attributes of a control as an
259
- # attribute string, for the controls assembled by hand rather than through `Tag.void`.
260
- #
261
- # @param field [Forms::Field] the field the control belongs to
262
- # @return [String] the rendered attributes, or `""` when the field is optional and has
263
- # no help text
147
+ # String form, for controls assembled by hand rather than through `Tag.void`.
264
148
  def self.aria(field)
265
149
  Tag.attrs(required: field.required?, aria_describedby: field.help ? "#{dom_id(field.path)}-help" : nil)
266
150
  end
267
151
 
268
- # Builds the `required` and `aria-describedby` attributes of a control as a Hash, to
269
- # splat into `Tag.void`.
270
- #
271
- # @param field [Forms::Field] the field the control belongs to
272
- # @return [Hash{Symbol => Boolean, String, nil}] `:required` and `:aria_describedby`;
273
- # the latter is the help span's element id, or nil when the field has no help text
152
+ # Hash form of `aria`, to splat into `Tag.void`.
274
153
  def self.aria_attrs(field)
275
154
  { required: field.required?, aria_describedby: field.help ? "#{dom_id(field.path)}-help" : nil }
276
155
  end
277
156
 
278
- # Reads the value held for a dotted field path out of either spelling a form's values
279
- # arrive in, flat or nested, without losing a stored `false`.
280
- #
281
- # `values` is a nested hash; `path` a dotted string using the same
282
- # segment spelling the hash keys are built from (`Params.nest` in
283
- # params.rb) — symbols one level down from a group, strings at the
284
- # flat top when a sticky POST re-render hands raw params back untouched.
285
- #
286
- # @param values [Hash{String, Symbol => Object}, Object] held values; anything that is
287
- # not a Hash, nil included, reads as nil
288
- # @param path [String, Symbol] the field's dotted path, such as `"amount.cents"`
289
- # @return [Object, nil] the held value exactly as stored (a raw form String, or a typed
290
- # value from a record's state); nil when no key on the path answers
157
+ # Reads the value held for a dotted path from either a flat or a nested values hash.
291
158
  def self.dig(values, path)
292
159
  return nil unless values.is_a?(Hash)
293
160
 
294
- # A sticky re-render after a refused submission hands back the raw
295
- # flat params (`{"amount.cents"=>"1050"}` — the same shape the form
296
- # posted, string values and all, dotted key intact) ; a prefill from
297
- # an existing record's own state hands back a nested hash instead
298
- # (`{amount: {cents: 1050}}`). Flat wins when both would answer,
299
- # since only the raw form is ever what the caller actually typed.
300
- # `key?` decides which spelling answers, at every step below —
301
- # never `||`, which would treat a genuinely-held `false` the
302
- # same as an absent key and fall through to `nil`.
161
+ # A sticky re-render hands back raw flat params ({"amount.cents"=>"1050"}); a prefill
162
+ # from a record's state is nested ({amount: {cents: 1050}}). Flat wins when both answer.
163
+ # Test with `key?`, never `||`, so a held `false` is not mistaken for an absent key.
303
164
  str = path.to_s
304
165
  return values[str] if values.key?(str)
305
166