hecks 0.2.0 → 1.0.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 (419) hide show
  1. checksums.yaml +5 -5
  2. data/lib/hecks/adapters/driven/claude_code.adapter +3 -0
  3. data/lib/hecks/adapters/driven/claude_code.rb +127 -0
  4. data/lib/hecks/adapters/driven/d1.adapter +12 -0
  5. data/lib/hecks/adapters/driven/d1.rb +427 -0
  6. data/lib/hecks/adapters/driven/folder.adapter +3 -0
  7. data/lib/hecks/adapters/driven/folder.rb +199 -0
  8. data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
  9. data/lib/hecks/adapters/driven/google_authentication.rb +100 -0
  10. data/lib/hecks/adapters/driven/governance_authorization.adapter +3 -0
  11. data/lib/hecks/adapters/driven/governance_authorization.rb +101 -0
  12. data/lib/hecks/adapters/driven/heki/journal.rb +56 -0
  13. data/lib/hecks/adapters/driven/heki/saga_store.rb +99 -0
  14. data/lib/hecks/adapters/driven/heki/snapshot.rb +65 -0
  15. data/lib/hecks/adapters/driven/heki.adapter +4 -0
  16. data/lib/hecks/adapters/driven/heki.rb +179 -0
  17. data/lib/hecks/adapters/driven/identity_registry.adapter +3 -0
  18. data/lib/hecks/adapters/driven/identity_registry.rb +26 -0
  19. data/lib/hecks/adapters/driven/in_memory_ordering.rb +51 -0
  20. data/lib/hecks/adapters/driven/lambda/client.rb +63 -0
  21. data/lib/hecks/adapters/driven/lambda.adapter +4 -0
  22. data/lib/hecks/adapters/driven/lambda.rb +145 -0
  23. data/lib/hecks/adapters/driven/memory.adapter +3 -0
  24. data/lib/hecks/adapters/driven/memory.rb +103 -0
  25. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +3 -0
  26. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +28 -0
  27. data/lib/hecks/adapters/driven/postgres/codec.rb +88 -0
  28. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +207 -0
  29. data/lib/hecks/adapters/driven/postgres.adapter +5 -0
  30. data/lib/hecks/adapters/driven/postgres.rb +438 -0
  31. data/lib/hecks/adapters/driven/postgres_era.adapter +17 -0
  32. data/lib/hecks/adapters/driven/prism.adapter +3 -0
  33. data/lib/hecks/adapters/driven/prism.rb +80 -0
  34. data/lib/hecks/adapters/driven/secure_random_identity.adapter +3 -0
  35. data/lib/hecks/adapters/driven/secure_random_identity.rb +14 -0
  36. data/lib/hecks/adapters/driven/sql_query_builder.rb +221 -0
  37. data/lib/hecks/adapters/driven/sqlite/codec.rb +80 -0
  38. data/lib/hecks/adapters/driven/sqlite/projection.rb +172 -0
  39. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +192 -0
  40. data/lib/hecks/adapters/driven/sqlite.adapter +9 -0
  41. data/lib/hecks/adapters/driven/sqlite.rb +306 -0
  42. data/lib/hecks/adapters/driven/system_clock.adapter +3 -0
  43. data/lib/hecks/adapters/driven/system_clock.rb +14 -0
  44. data/lib/hecks/adapters/driven.rb +54 -0
  45. data/lib/hecks/adapters.rb +6 -0
  46. data/lib/hecks/behaviors/dsl.rb +125 -0
  47. data/lib/hecks/behaviors/expectations.rb +340 -0
  48. data/lib/hecks/behaviors/ir.rb +31 -0
  49. data/lib/hecks/behaviors/rspec.rb +42 -0
  50. data/lib/hecks/behaviors/runner.rb +96 -0
  51. data/lib/hecks/behaviors.rb +25 -0
  52. data/lib/hecks/bluebook/aggregate.rb +108 -0
  53. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +134 -0
  54. data/lib/hecks/bluebook/assembly/build.rb +48 -0
  55. data/lib/hecks/bluebook/assembly/contract.rb +112 -0
  56. data/lib/hecks/bluebook/assembly/contracts.rb +438 -0
  57. data/lib/hecks/bluebook/assembly/marks.rb +228 -0
  58. data/lib/hecks/bluebook/assembly/specializer.rb +70 -0
  59. data/lib/hecks/bluebook/assembly.rb +91 -0
  60. data/lib/hecks/bluebook/attribute.rb +96 -0
  61. data/lib/hecks/bluebook/behaviour/aggregate.rb +83 -0
  62. data/lib/hecks/bluebook/behaviour/attribute.rb +27 -0
  63. data/lib/hecks/bluebook/behaviour/chapter.rb +72 -0
  64. data/lib/hecks/bluebook/behaviour/command.rb +116 -0
  65. data/lib/hecks/bluebook/behaviour/domain_port.rb +25 -0
  66. data/lib/hecks/bluebook/behaviour/entity.rb +59 -0
  67. data/lib/hecks/bluebook/behaviour/hexagon.rb +50 -0
  68. data/lib/hecks/bluebook/behaviour/lifecycle.rb +68 -0
  69. data/lib/hecks/bluebook/behaviour/policy.rb +52 -0
  70. data/lib/hecks/bluebook/behaviour/process_manager.rb +51 -0
  71. data/lib/hecks/bluebook/behaviour/query.rb +10 -0
  72. data/lib/hecks/bluebook/behaviour/read_model.rb +29 -0
  73. data/lib/hecks/bluebook/behaviour/traits.rb +81 -0
  74. data/lib/hecks/bluebook/behaviour/value_object.rb +33 -0
  75. data/lib/hecks/bluebook/chapter.rb +78 -0
  76. data/lib/hecks/bluebook/command.rb +124 -0
  77. data/lib/hecks/bluebook/domain_port.rb +102 -0
  78. data/lib/hecks/bluebook/dsl/adapter_builder.rb +34 -0
  79. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +1018 -0
  80. data/lib/hecks/bluebook/dsl/attribute_collector.rb +348 -0
  81. data/lib/hecks/bluebook/dsl/binding_proxy.rb +71 -0
  82. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +1087 -0
  83. data/lib/hecks/bluebook/dsl/command_builder.rb +714 -0
  84. data/lib/hecks/bluebook/dsl/const_shim.rb +81 -0
  85. data/lib/hecks/bluebook/dsl/domain_port_builder.rb +79 -0
  86. data/lib/hecks/bluebook/dsl/entity_builder.rb +430 -0
  87. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +366 -0
  88. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +163 -0
  89. data/lib/hecks/bluebook/dsl/identity_declaration.rb +191 -0
  90. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +44 -0
  91. data/lib/hecks/bluebook/dsl/malformed.rb +7 -0
  92. data/lib/hecks/bluebook/dsl/policy_builder.rb +135 -0
  93. data/lib/hecks/bluebook/dsl/port_builder.rb +39 -0
  94. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +142 -0
  95. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +306 -0
  96. data/lib/hecks/bluebook/dsl/query_builder.rb +113 -0
  97. data/lib/hecks/bluebook/dsl/read_model_builder.rb +233 -0
  98. data/lib/hecks/bluebook/dsl/rule_reference.rb +173 -0
  99. data/lib/hecks/bluebook/dsl/translation_builder.rb +243 -0
  100. data/lib/hecks/bluebook/dsl/value_object_builder.rb +178 -0
  101. data/lib/hecks/bluebook/dsl/word_gate.rb +228 -0
  102. data/lib/hecks/bluebook/dsl/world_builder.rb +117 -0
  103. data/lib/hecks/bluebook/dsl.rb +45 -0
  104. data/lib/hecks/bluebook/entity.rb +103 -0
  105. data/lib/hecks/bluebook/expression/canonical_form.rb +123 -0
  106. data/lib/hecks/bluebook/expression/evaluator.rb +304 -0
  107. data/lib/hecks/bluebook/expression/projection.json +218 -0
  108. data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +233 -0
  109. data/lib/hecks/bluebook/expression/resolver.rb +781 -0
  110. data/lib/hecks/bluebook/expression.rb +13 -0
  111. data/lib/hecks/bluebook/hexagon.rb +60 -0
  112. data/lib/hecks/bluebook/lifecycle.rb +42 -0
  113. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +54 -0
  114. data/lib/hecks/bluebook/meta_validator/judge.rb +621 -0
  115. data/lib/hecks/bluebook/meta_validator/plan.rb +332 -0
  116. data/lib/hecks/bluebook/meta_validator/port_judge.rb +51 -0
  117. data/lib/hecks/bluebook/meta_validator/readings.rb +360 -0
  118. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +351 -0
  119. data/lib/hecks/bluebook/meta_validator/shapes.rb +266 -0
  120. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +255 -0
  121. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +138 -0
  122. data/lib/hecks/bluebook/meta_validator/world_judge.rb +78 -0
  123. data/lib/hecks/bluebook/meta_validator.rb +542 -0
  124. data/lib/hecks/bluebook/model_check.rb +445 -0
  125. data/lib/hecks/bluebook/pattern_subset.rb +184 -0
  126. data/lib/hecks/bluebook/policy.rb +41 -0
  127. data/lib/hecks/bluebook/process_manager.rb +128 -0
  128. data/lib/hecks/bluebook/project_discovery.rb +30 -0
  129. data/lib/hecks/bluebook/project_loader.rb +40 -0
  130. data/lib/hecks/bluebook/project_register.rb +82 -0
  131. data/lib/hecks/bluebook/query.rb +61 -0
  132. data/lib/hecks/bluebook/read_model.rb +109 -0
  133. data/lib/hecks/bluebook/reference.rb +74 -0
  134. data/lib/hecks/bluebook/smoke_test.rb +166 -0
  135. data/lib/hecks/bluebook/synthesizer.rb +95 -0
  136. data/lib/hecks/bluebook/translation.rb +92 -0
  137. data/lib/hecks/bluebook/value_object.rb +66 -0
  138. data/lib/hecks/bluebook.rb +74 -0
  139. data/lib/hecks/codemod.rb +342 -0
  140. data/lib/hecks/construct.rb +71 -0
  141. data/lib/hecks/deploy/bluebook/deploy.bluebook +219 -0
  142. data/lib/hecks/deploy/bluebook/deploy.hecksagon +4 -0
  143. data/lib/hecks/deploy/oidc.json +18 -0
  144. data/lib/hecks/doc/reference.rb +410 -0
  145. data/lib/hecks/embryonaut_bluebook.rb +75 -0
  146. data/lib/hecks/facade/cli_door.rb +119 -0
  147. data/lib/hecks/facade/cli_runner.rb +190 -0
  148. data/lib/hecks/facade/command_request.rb +105 -0
  149. data/lib/hecks/facade/handle.rb +173 -0
  150. data/lib/hecks/facade/json_door.rb +166 -0
  151. data/lib/hecks/facade/surface/aggregate_door.rb +185 -0
  152. data/lib/hecks/facade/surface/chapter.rb +107 -0
  153. data/lib/hecks/facade/surface.rb +48 -0
  154. data/lib/hecks/facade.rb +44 -0
  155. data/lib/hecks/forms/app.rb +341 -0
  156. data/lib/hecks/forms/command_form_renderer.rb +113 -0
  157. data/lib/hecks/forms/examples/banking_console.bluebook +3 -0
  158. data/lib/hecks/forms/field_renderer.rb +177 -0
  159. data/lib/hecks/forms/field_shape.rb +232 -0
  160. data/lib/hecks/forms/html.rb +84 -0
  161. data/lib/hecks/forms/index_renderer.rb +35 -0
  162. data/lib/hecks/forms/page.rb +157 -0
  163. data/lib/hecks/forms/params.rb +161 -0
  164. data/lib/hecks/forms/port_argument.rb +46 -0
  165. data/lib/hecks/forms/query_form_renderer.rb +114 -0
  166. data/lib/hecks/forms/record_renderer.rb +119 -0
  167. data/lib/hecks/forms/record_table.rb +68 -0
  168. data/lib/hecks/forms/reference_options.rb +30 -0
  169. data/lib/hecks/forms/value_object_shape.rb +46 -0
  170. data/lib/hecks/forms.rb +54 -0
  171. data/lib/hecks/fqn.rb +94 -0
  172. data/lib/hecks/framework/bluebook/compliance.bluebook +1 -0
  173. data/lib/hecks/framework/bluebook/console_settings.bluebook +489 -0
  174. data/lib/hecks/framework/bluebook/framework.hecksagon +32 -0
  175. data/lib/hecks/framework/bluebook/governance.bluebook +145 -0
  176. data/lib/hecks/framework/bluebook/identity.bluebook +90 -0
  177. data/lib/hecks/framework/oidc.json +39 -0
  178. data/lib/hecks/framework.rb +90 -0
  179. data/lib/hecks/freezer.rb +67 -0
  180. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +527 -0
  181. data/lib/hecks/fuzzing/invalid_value_generator.rb +86 -0
  182. data/lib/hecks/fuzzing/isolated_boot.rb +286 -0
  183. data/lib/hecks/fuzzing/properties.rb +1192 -0
  184. data/lib/hecks/fuzzing/replay.rb +668 -0
  185. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +93 -0
  186. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +80 -0
  187. data/lib/hecks/fuzzing/sequence_generator/picker.rb +96 -0
  188. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +250 -0
  189. data/lib/hecks/fuzzing/sequence_generator.rb +131 -0
  190. data/lib/hecks/fuzzing/value_generator.rb +200 -0
  191. data/lib/hecks/fuzzing.rb +12 -0
  192. data/lib/hecks/grammar/evolve.rb +324 -0
  193. data/lib/hecks/grammar/expression.bluebook +420 -0
  194. data/lib/hecks/grammar/expression_operators.json +1648 -0
  195. data/lib/hecks/grammar/grammar.hecksagon +20 -0
  196. data/lib/hecks/grammar/oidc.json +69 -0
  197. data/lib/hecks/grammar/translation.bluebook +203 -0
  198. data/lib/hecks/grammar.rb +178 -0
  199. data/lib/hecks/ir.rb +126 -0
  200. data/lib/hecks/language/adapter.bluebook +116 -0
  201. data/lib/hecks/language/bluebook/aggregate.bluebook +581 -0
  202. data/lib/hecks/language/bluebook/attaches/paging.bluebook +76 -0
  203. data/lib/hecks/language/bluebook/bluebook.bluebook +256 -0
  204. data/lib/hecks/language/bluebook/bluebook.hecksagon +24 -0
  205. data/lib/hecks/language/bluebook/command.bluebook +471 -0
  206. data/lib/hecks/language/bluebook/entity.bluebook +392 -0
  207. data/lib/hecks/language/bluebook/policy.bluebook +189 -0
  208. data/lib/hecks/language/bluebook/process_manager.bluebook +381 -0
  209. data/lib/hecks/language/bluebook/projection.bluebook +267 -0
  210. data/lib/hecks/language/bluebook/query.bluebook +245 -0
  211. data/lib/hecks/language/bluebook/shape.bluebook +292 -0
  212. data/lib/hecks/language/bluebook/syntax.bluebook +448 -0
  213. data/lib/hecks/language/bluebook/vocabulary.bluebook +379 -0
  214. data/lib/hecks/language/hecksagon/adapter_binding.bluebook +51 -0
  215. data/lib/hecks/language/hecksagon/domain_port.bluebook +76 -0
  216. data/lib/hecks/language/hecksagon/hecksagon.bluebook +131 -0
  217. data/lib/hecks/language/hecksagon/port_operation.bluebook +102 -0
  218. data/lib/hecks/language/oidc.json +333 -0
  219. data/lib/hecks/language/port.bluebook +120 -0
  220. data/lib/hecks/language/translation/translation.bluebook +110 -0
  221. data/lib/hecks/language/translation/translation_aggregate.bluebook +267 -0
  222. data/lib/hecks/language/world/wiring.bluebook +62 -0
  223. data/lib/hecks/language/world/world.bluebook +84 -0
  224. data/lib/hecks/literal.rb +125 -0
  225. data/lib/hecks/naming.rb +174 -0
  226. data/lib/hecks/ports/access_control.port +9 -0
  227. data/lib/hecks/ports/access_control.rb +62 -0
  228. data/lib/hecks/ports/agent/answers.rb +104 -0
  229. data/lib/hecks/ports/agent.port +8 -0
  230. data/lib/hecks/ports/agent.rb +167 -0
  231. data/lib/hecks/ports/authentication.port +6 -0
  232. data/lib/hecks/ports/authentication.rb +50 -0
  233. data/lib/hecks/ports/authorization.port +7 -0
  234. data/lib/hecks/ports/authorization.rb +62 -0
  235. data/lib/hecks/ports/clock.port +5 -0
  236. data/lib/hecks/ports/clock.rb +62 -0
  237. data/lib/hecks/ports/extraction.port +5 -0
  238. data/lib/hecks/ports/extraction.rb +37 -0
  239. data/lib/hecks/ports/identity_assignment.port +5 -0
  240. data/lib/hecks/ports/identity_assignment.rb +45 -0
  241. data/lib/hecks/ports/identity_generation.port +5 -0
  242. data/lib/hecks/ports/identity_generation.rb +49 -0
  243. data/lib/hecks/ports/identity_resolution.port +5 -0
  244. data/lib/hecks/ports/identity_resolution.rb +40 -0
  245. data/lib/hecks/ports/loading.port +4 -0
  246. data/lib/hecks/ports/loading.rb +13 -0
  247. data/lib/hecks/ports/persistence/append_only.rb +138 -0
  248. data/lib/hecks/ports/persistence/binding_policy.rb +56 -0
  249. data/lib/hecks/ports/persistence/execution.rb +23 -0
  250. data/lib/hecks/ports/persistence/null_saga_store.rb +25 -0
  251. data/lib/hecks/ports/persistence/plugin.rb +54 -0
  252. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +169 -0
  253. data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +128 -0
  254. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +161 -0
  255. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +61 -0
  256. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +304 -0
  257. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +171 -0
  258. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +190 -0
  259. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +478 -0
  260. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +166 -0
  261. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +314 -0
  262. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +168 -0
  263. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +170 -0
  264. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +134 -0
  265. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +137 -0
  266. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +89 -0
  267. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +85 -0
  268. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +43 -0
  269. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +132 -0
  270. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +74 -0
  271. data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +758 -0
  272. data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +120 -0
  273. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +31 -0
  274. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +46 -0
  275. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +102 -0
  276. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +46 -0
  277. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +70 -0
  278. data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +72 -0
  279. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +120 -0
  280. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +183 -0
  281. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +41 -0
  282. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +28 -0
  283. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +30 -0
  284. data/lib/hecks/ports/persistence/plugins/era/translation.rb +11 -0
  285. data/lib/hecks/ports/persistence/plugins/era.rb +48 -0
  286. data/lib/hecks/ports/persistence/remote_runtime.rb +42 -0
  287. data/lib/hecks/ports/persistence/repository_factory.rb +30 -0
  288. data/lib/hecks/ports/persistence.port +4 -0
  289. data/lib/hecks/ports/persistence.rb +39 -0
  290. data/lib/hecks/ports/projection.port +4 -0
  291. data/lib/hecks/ports/projection.rb +96 -0
  292. data/lib/hecks/ports/query/in_memory.rb +60 -0
  293. data/lib/hecks/ports/query/ordering.rb +41 -0
  294. data/lib/hecks/ports/query.rb +36 -0
  295. data/lib/hecks/ports.rb +27 -0
  296. data/lib/hecks/projections/diagrams.rb +642 -0
  297. data/lib/hecks/projections/ir.rb +18 -0
  298. data/lib/hecks/projections/model/deviations.rb +98 -0
  299. data/lib/hecks/projections/model.rb +145 -0
  300. data/lib/hecks/projections/oidc.rb +110 -0
  301. data/lib/hecks/projections/parser_table.rb +159 -0
  302. data/lib/hecks/projections/reference.rb +38 -0
  303. data/lib/hecks/projections/shape.rb +44 -0
  304. data/lib/hecks/projections/statements.rb +110 -0
  305. data/lib/hecks/projections/vocabulary.rb +100 -0
  306. data/lib/hecks/projections.rb +32 -0
  307. data/lib/hecks/projector/cli_projector.rb +446 -0
  308. data/lib/hecks/projector/docs_projector.rb +321 -0
  309. data/lib/hecks/projector/exporter.rb +158 -0
  310. data/lib/hecks/projector/ir_projector.rb +18 -0
  311. data/lib/hecks/projector/narrate_projector.rb +243 -0
  312. data/lib/hecks/projector/target.rb +97 -0
  313. data/lib/hecks/projector.rb +186 -0
  314. data/lib/hecks/query_ir.rb +411 -0
  315. data/lib/hecks/query_specification/common/authorization_spec.rb +9 -0
  316. data/lib/hecks/query_specification/common/comparators.rb +25 -0
  317. data/lib/hecks/query_specification/common/comparison.rb +174 -0
  318. data/lib/hecks/query_specification/common/cursor_spec.rb +9 -0
  319. data/lib/hecks/query_specification/common/dsl.rb +58 -0
  320. data/lib/hecks/query_specification/common/inspection_spec.rb +9 -0
  321. data/lib/hecks/query_specification/common/limit_spec.rb +9 -0
  322. data/lib/hecks/query_specification/common/null_policy.rb +92 -0
  323. data/lib/hecks/query_specification/common/null_semantics.rb +11 -0
  324. data/lib/hecks/query_specification/common/offset_spec.rb +9 -0
  325. data/lib/hecks/query_specification/common/options.rb +32 -0
  326. data/lib/hecks/query_specification/common/order_by.rb +9 -0
  327. data/lib/hecks/query_specification/common/specification.rb +10 -0
  328. data/lib/hecks/query_specification/common/where_clause.rb +9 -0
  329. data/lib/hecks/query_specification/field_path.rb +107 -0
  330. data/lib/hecks/query_specification/hop_path.rb +132 -0
  331. data/lib/hecks/query_specification/read_model/specification.rb +18 -0
  332. data/lib/hecks/query_specification.rb +14 -0
  333. data/lib/hecks/rendering.rb +48 -0
  334. data/lib/hecks/router/namespace_installer.rb +157 -0
  335. data/lib/hecks/router.rb +70 -0
  336. data/lib/hecks/runtime/aggregate_lock.rb +45 -0
  337. data/lib/hecks/runtime/boot_gates.rb +41 -0
  338. data/lib/hecks/runtime/caller.rb +66 -0
  339. data/lib/hecks/runtime/capability_graph.rb +44 -0
  340. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +135 -0
  341. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +260 -0
  342. data/lib/hecks/runtime/command_interpreter.rb +497 -0
  343. data/lib/hecks/runtime/command_rules/admissibility.rb +357 -0
  344. data/lib/hecks/runtime/command_rules/arithmetic.rb +260 -0
  345. data/lib/hecks/runtime/command_rules/authorization.rb +65 -0
  346. data/lib/hecks/runtime/command_rules/emission.rb +34 -0
  347. data/lib/hecks/runtime/command_rules/references.rb +189 -0
  348. data/lib/hecks/runtime/command_rules.rb +28 -0
  349. data/lib/hecks/runtime/dependency_planning.rb +245 -0
  350. data/lib/hecks/runtime/dispatcher.rb +301 -0
  351. data/lib/hecks/runtime/entity_element.rb +253 -0
  352. data/lib/hecks/runtime/entity_interpreter.rb +299 -0
  353. data/lib/hecks/runtime/errors.rb +122 -0
  354. data/lib/hecks/runtime/event.rb +51 -0
  355. data/lib/hecks/runtime/identity.rb +156 -0
  356. data/lib/hecks/runtime/instance.rb +175 -0
  357. data/lib/hecks/runtime/interpreting.rb +90 -0
  358. data/lib/hecks/runtime/loader.rb +184 -0
  359. data/lib/hecks/runtime/policy_interpreter.rb +366 -0
  360. data/lib/hecks/runtime/port_operation_interpreter.rb +210 -0
  361. data/lib/hecks/runtime/query_interpreter.rb +286 -0
  362. data/lib/hecks/runtime/reaction_invocation.rb +253 -0
  363. data/lib/hecks/runtime/read_model_interpreter.rb +341 -0
  364. data/lib/hecks/runtime/rebuild_sweep.rb +74 -0
  365. data/lib/hecks/runtime/reference_hop.rb +99 -0
  366. data/lib/hecks/runtime/refusal_wording.rb +126 -0
  367. data/lib/hecks/runtime/registry/saga_persistence.rb +142 -0
  368. data/lib/hecks/runtime/registry/verification.rb +246 -0
  369. data/lib/hecks/runtime/registry.rb +279 -0
  370. data/lib/hecks/runtime/remote_dispatcher.rb +143 -0
  371. data/lib/hecks/runtime/routing.rb +96 -0
  372. data/lib/hecks/runtime/saga_interpreter/correlation.rb +97 -0
  373. data/lib/hecks/runtime/saga_interpreter.rb +503 -0
  374. data/lib/hecks/runtime/saga_pending_dispatch.rb +45 -0
  375. data/lib/hecks/runtime/tenant_check.rb +84 -0
  376. data/lib/hecks/runtime/tenant_scope.rb +55 -0
  377. data/lib/hecks/runtime/value/admission.rb +130 -0
  378. data/lib/hecks/runtime/value/coercion.rb +565 -0
  379. data/lib/hecks/runtime/value/invariant_violation.rb +5 -0
  380. data/lib/hecks/runtime/value.rb +125 -0
  381. data/lib/hecks/runtime.rb +107 -0
  382. data/lib/hecks/storehouse.rb +632 -0
  383. data/lib/hecks/version.rb +16 -0
  384. data/lib/hecks/vocabulary.rb +218 -0
  385. data/lib/hecks.rb +123 -6
  386. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +90 -0
  387. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +128 -0
  388. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +160 -0
  389. metadata +412 -222
  390. data/bin/hecks +0 -7
  391. data/bin/hecks-package +0 -65
  392. data/bin/hecks_console +0 -12
  393. data/bin/hecks_serverless +0 -6
  394. data/lib/cli/build.rb +0 -14
  395. data/lib/cli/command_runner.rb +0 -28
  396. data/lib/cli/console.rb +0 -10
  397. data/lib/cli/generate.rb +0 -37
  398. data/lib/cli/hecks-cli.rb +0 -27
  399. data/lib/cli/test.rb +0 -57
  400. data/lib/console/commands.rb +0 -8
  401. data/lib/console/hecks-console.rb +0 -1
  402. data/lib/packager/README.md +0 -0
  403. data/lib/packager/app_runner.rb +0 -21
  404. data/lib/packager/args.rb +0 -26
  405. data/lib/packager/compatibility/fixnum.rb +0 -6
  406. data/lib/packager/hecks.rb +0 -39
  407. data/lib/packager/query_runner.rb +0 -21
  408. data/lib/packager/resources/Dockerfile +0 -11
  409. data/lib/packager/resources/app_binary +0 -7
  410. data/lib/packager/resources/bundle_config +0 -3
  411. data/lib/packager/resources/traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz +0 -0
  412. data/lib/packager/resources/traveling-ruby-20150715-2.2.2-osx.tar.gz +0 -0
  413. data/lib/serverless/Domain +0 -32
  414. data/lib/serverless/cli.rb +0 -75
  415. data/lib/serverless/resources/command_name.js +0 -5
  416. data/lib/serverless/resources/environment.js +0 -7
  417. data/lib/serverless/resources/handler.js.tt +0 -28
  418. data/lib/serverless/resources/run_binary.js +0 -22
  419. data/lib/serverless/resources/serverless.yml +0 -20
@@ -0,0 +1,348 @@
1
+ module Hecks
2
+ module Bluebook
3
+ module DSL
4
+ module AttributeCollector
5
+ ListOf = Struct.new(:type)
6
+ OneOf = Struct.new(:values)
7
+
8
+ UNSET = Object.new.freeze
9
+ private_constant :UNSET
10
+
11
+ def attributes = @attributes ||= []
12
+
13
+ # Value objects synthesised from inline closed sets, collected here and
14
+ # installed by whoever owns value objects (the aggregate).
15
+ def closed_sets = @closed_sets ||= []
16
+
17
+ # `admits:` names a closed set that is ALREADY DECLARED elsewhere —
18
+ #
19
+ # attribute :op, String, admits: "Vocabulary::QueryComparator"
20
+ #
21
+ # which is the difference between it and `one_of`: `one_of` SYNTHESISES
22
+ # a fresh value object named for the attribute, so it can only ever name
23
+ # something new. `admits` points at a set the language already holds, so
24
+ # the same set can be named from many places without being written twice.
25
+ #
26
+ # QUALIFIED, because a closed set is a value object INSIDE an aggregate
27
+ # and `reference_to` reaches heads only — so this is text, checked where
28
+ # it is read rather than by reference resolution.
29
+ #
30
+ # AND WRITTEN AS TEXT, not as the constant path `Vocabulary::QueryComparator`
31
+ # it reads like. The constant spelling was tried — `ConstShim` returning
32
+ # a Module so Ruby's `::` reaches a second `const_missing` — and it
33
+ # cannot hold: `Facade::Surface` installs every aggregate name as a
34
+ # TOP-LEVEL constant, so the moment any facade exists, `Vocabulary`
35
+ # resolves to that module and the shim is never asked. A spelling that
36
+ # works only until a facade is built is worse than a quoted one.
37
+ #
38
+ # THE TYPE POSITION TAKES A BARE CONSTANT, ALWAYS REQUIRED (ADR 0025,
39
+ # "Attributes") — omitting it (a mint default of String) and quoting
40
+ # it as text both refuse now. Neither had a real reason left: no
41
+ # corpus attribute ever omitted the type, and `ConstShim#const_missing`
42
+ # (S0b) already resolves a bare, not-yet-declared constant to the
43
+ # SAME forward reference the quoted form existed for — a bareword
44
+ # `Name` reaches a value object named "Name" declared later in the
45
+ # same block exactly as `"Name"` used to, `spell`'s own `to_s`
46
+ # renders either one identically. Neither form appears in any frozen
47
+ # era text (checked directly), so both are refused unconditionally —
48
+ # nothing for `MetaValidator.shadow_parsing?` to answer for.
49
+ # RENAMED FROM `attribute` — item #13's full metaprogrammed
50
+ # dispatch (slice 3, whole-project table-unification survey).
51
+ # The word `attribute` itself is no longer a real method any
52
+ # builder answers directly: every (context, word) Keyword row
53
+ # for it carries `calls: "attribute_impl"`, and `GenericDispatch`
54
+ # forwards the whole call here untouched — this method's own
55
+ # body is exactly what `attribute` always was, unchanged, just
56
+ # reached generically now rather than by Ruby's own direct
57
+ # method lookup. `attribute_collector_spec.rb` (`AttributeCollector
58
+ # has no method without a test` — dsl_coverage_spec.rb) and the
59
+ # bootstrap fallback (`GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`) both
60
+ # name this same string; they must never drift apart.
61
+ def attribute_impl(name, type = UNSET, default: nil, optional: false, pattern: nil,
62
+ admits: nil, one_of: nil)
63
+ # moved to the language: FieldName invariant, on Root.Attribute
64
+
65
+ refuse_duplicate_attribute!(name)
66
+
67
+ if type.equal?(UNSET)
68
+ raise Malformed, "#{name} declares no type — attribute :#{name}, SomeType is required, " \
69
+ "there is no default"
70
+ end
71
+
72
+ # `list_of("X")` carries the same quoted text one level down — a
73
+ # bare constant is required there too, checked before it unwraps
74
+ # below, not after (once unwrapped, a plain String in the `type:`
75
+ # slot looks identical to one that legitimately belongs there).
76
+ quoted = type.is_a?(ListOf) ? type.type : type
77
+ if quoted.is_a?(::String)
78
+ raise Malformed, "#{name}'s type #{quoted.inspect} is quoted text — give the bare constant " \
79
+ "(#{quoted}) instead; a forward reference to a value object declared later " \
80
+ "in the same block already resolves without quoting"
81
+ end
82
+
83
+ refuse_unshared_pattern(name, pattern) if pattern
84
+
85
+ type = synthesise_closed_set(name, type) if type.is_a?(OneOf)
86
+ list = type.is_a?(ListOf)
87
+ attributes << Attribute.new(
88
+ name: name,
89
+ type: list ? type.type : type,
90
+ list: list,
91
+ default: default,
92
+ optional: optional,
93
+ pattern: pattern,
94
+ admits: admits
95
+ )
96
+
97
+ install_inline_closed_set(name, one_of) if one_of
98
+ end
99
+
100
+ # RENAMED FROM `list_of` — item #13's full metaprogrammed
101
+ # dispatch (slice 5). Called in an attribute's own TYPE
102
+ # position (`attribute :x, list_of(Y)`), never through a `def
103
+ # list_of` any ONE builder answers as its own word — reached
104
+ # via `WordGate#word_gate_dispatch`'s new "Type"-context
105
+ # fallback, the same one `one_of_impl` below uses. Bootstrap-
106
+ # reachable (every core chapter's own list-typed attributes use
107
+ # it), so `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK` carries a
108
+ # SINGLE `["Type", "list_of"]` entry rather than one per calling
109
+ # context — the bootstrap branch checks that key too now, same
110
+ # reasoning as the ordinary fallback.
111
+ def list_of_impl(type) = ListOf.new(type)
112
+
113
+ # `reference_to Account` MINTS `:account` — no `_id` — the default
114
+ # every `reference_to`-shaped word (`AggregateBuilder`,
115
+ # `EntityBuilder`, `CommandBuilder#cross_reference`,
116
+ # `QueryBuilder`, `PortOperationBuilder`, all `include
117
+ # AttributeCollector`) shares, since dropping the suffix is what
118
+ # makes `/` a real traversal operator possible at all (ADR 0025,
119
+ # "References" — a hop crosses at `/`, a field walk crosses at
120
+ # `.`; deriving the hop name from `account_id` is what made an
121
+ # explicit operator impossible before).
122
+ #
123
+ # LEGACY UNDER SHADOW-PARSING (S0a's own bridge): frozen era text
124
+ # was minted under the OLD default, and `EraGuard.shadow_parse`
125
+ # reconstructs a held aggregate's shape to DIFF against the
126
+ # current one — reading that text through the NEW default would
127
+ # silently reconstruct the WRONG historical name, not merely
128
+ # refuse a spelling the way S1's `identified_by` legacy forms do.
129
+ # This is a mint DEFAULT changing, not a syntax being removed, so
130
+ # there is nothing to refuse here — only a fork in what gets
131
+ # minted when `as:` is omitted.
132
+ private def default_reference_name(target)
133
+ suffix = MetaValidator.shadow_parsing? ? "_id" : ""
134
+ :"#{Naming.snake(target)}#{suffix}"
135
+ end
136
+
137
+ # A closed set declared INLINE on the attribute:
138
+ #
139
+ # attribute :status, one_of("open", "shut")
140
+ #
141
+ # Desugars to a value object named for the attribute, so it goes through
142
+ # exactly the machinery a hand-written one_of does — and so the
143
+ # attribute's type is still a DECLARED value object, which is now a
144
+ # structural rule rather than a predicate.
145
+ #
146
+ # An earlier reading of this spelling parsed it and threw the values
147
+ # away: the attribute became a plain String and the closed set meant
148
+ # nothing, in a construct that looked supported. The desugaring is
149
+ # pinned now — the same bluebook must always yield the same IR.
150
+ # RENAMED FROM `one_of` — item #13's full metaprogrammed dispatch
151
+ # (slice 5), same reasoning as list_of_impl above. SAME NAME as
152
+ # `ValueObjectBuilder#one_of_impl`'s own override on purpose —
153
+ # that method's own `super(*values)` call (the no-block, bare
154
+ # type-position case) resolves by METHOD NAME up the ancestor
155
+ # chain, and renaming only one side would silently break it.
156
+ def one_of_impl(*values) = OneOf.new(values)
157
+
158
+ private
159
+
160
+ def relationship_attribute(target, kind, name, optional: false, list: false)
161
+ refuse_duplicate_attribute!(name)
162
+ attributes << Attribute.new(
163
+ name: name,
164
+ type: Reference.new(target),
165
+ list: list,
166
+ optional: optional,
167
+ relationship: kind
168
+ )
169
+ end
170
+
171
+ # `one_of:` NAMES A CLOSED SET ON THE FIELD ITSELF (ADR 0025,
172
+ # "Attributes") — joining `pattern:`/`admits:` where value
173
+ # constraints already live, for the one context where it means
174
+ # anything: a `value_object` block, where it replaces the old
175
+ # `one_of do member ... end` wrapper for a SINGLE-FIELD set (a
176
+ # multi-field set still writes bare `member` lines, unwrapped).
177
+ # `ValueObjectBuilder` overrides this; every other includer
178
+ # (Aggregate/Entity/Command/Query/PortOperation) inherits this
179
+ # refusal — those contexts already have the unchanged type-position
180
+ # form (`attribute :status, one_of("open", "shut")`) for an
181
+ # anonymous inline set, so `one_of:` naming a *field* there would be
182
+ # a second spelling of the same idea, not a new one.
183
+ def install_inline_closed_set(name, _values)
184
+ raise Malformed,
185
+ "#{name}'s one_of: only means something inside a value_object — name a closed set " \
186
+ "with the type-position one_of(...) instead"
187
+ end
188
+
189
+ # A NAME DECLARED TWICE ON THE SAME OWNER IS TWO ATTRIBUTES SHARING
190
+ # ONE NAME, and nothing downstream disambiguates them — every
191
+ # reader that walks `attributes` looking for one by name
192
+ # (`seal_mutation_targets`, `seal_query_field`, `projects`'s own
193
+ # local check, `Instance#[]`, ...) uses `Array#find`/`any?`, which
194
+ # silently answers whichever declaration happens to come first and
195
+ # discards the second. Used to boot clean and stay that way : both
196
+ # declarations survived into the IR, one of them permanently
197
+ # unreachable by name. Refused HERE, at the one place every owner
198
+ # (Aggregate/Entity/Command/Query/PortOperation/ValueObject, each
199
+ # `include AttributeCollector`) mints an attribute through, rather
200
+ # than taught to each of those readers individually.
201
+ def refuse_duplicate_attribute!(name)
202
+ return unless attributes.any? { |attribute| attribute.name == name }
203
+
204
+ raise Malformed, "#{name} is declared twice — an attribute name is declared once, not twice"
205
+ end
206
+
207
+ # A pattern is refused AT DECLARATION, not when a value first meets it :
208
+ # a regex whose meaning depends on which engine reads it is a defect in
209
+ # the bluebook, and a bluebook that loads is one whose patterns carry
210
+ # one meaning. PatternSubset says which those are, and why each is refused.
211
+ def refuse_unshared_pattern(name, pattern)
212
+ rejection = PatternSubset.validate(pattern)
213
+ return unless rejection
214
+
215
+ raise Malformed,
216
+ "#{name}'s pattern #{pattern.inspect} uses a #{rejection.construct} — " \
217
+ "#{rejection.reason}"
218
+ end
219
+
220
+ def synthesise_closed_set(name, one_of)
221
+ type = Naming.pascal(name)
222
+ closed_sets << ValueObject.declare(
223
+ name: type,
224
+ attributes: [Attribute.new(name: :value, type: "String")],
225
+ members: one_of.values.map { |value| { value: value.to_s } },
226
+ closed_set: true
227
+ )
228
+ type
229
+ end
230
+
231
+ # Every selected identity head contributes all of its recursively
232
+ # scalar leaves in declaration order. Named, inline and compound-key
233
+ # declarations therefore share one flattening rule.
234
+ def resolve_identity_field!(field, value_objects, context_name)
235
+ attr = attributes.find { |a| a.name == field }
236
+
237
+ # `:id` OR AN `_id`-SUFFIXED NAME WITH NO MATCHING ATTRIBUTE is
238
+ # not a typo to refuse — it is the language's own WALK-PARENT/
239
+ # FALLBACK-IDENTITY convention. The `_id` suffix is the meta-
240
+ # domain's own (the `Command`/`Entity`/`Aggregate` etc. records
241
+ # identify by `owner_id`/`bluebook_id`/`aggregate_id`, supplied
242
+ # by the judge's replay rather than declared locally — see
243
+ # `behavior.bluebook`'s own "owner_id is not a declared
244
+ # attribute" comment); bare `:id` is `Instance#materialize_
245
+ # identity!`'s own fallback name (`@aggregate.identified_by ||
246
+ # :id`) made explicit rather than left implicit — declaring
247
+ # `identified_by :id` says out loud what omitting `identified_by`
248
+ # entirely already meant. `reference_to` mints the `_id` suffix
249
+ # for the same reason `attr.reference?` below resolves bare: an
250
+ # `_id`(-shaped) name is already a scalar by the language's own
251
+ # spelling convention, walk-supplied or locally declared alike.
252
+ return [field.to_s] if attr.nil? && (field.to_s == "id" || field.to_s.end_with?("_id"))
253
+
254
+ raise Malformed, "#{context_name}.identified_by :#{field} names no attribute #{context_name} declares" unless attr
255
+
256
+ identity_paths_for_attribute(attr, value_objects, context_name, field.to_s, [])
257
+ end
258
+
259
+ # A named or inline identity mints one structured field, then expands
260
+ # each scalar leaf beneath it into the existing path-shaped IR.
261
+ def resolve_identity_type!(type, as, insert_at, value_objects, context_name)
262
+ target = Naming.demodulise(type.respond_to?(:hecks_name) ? type.hecks_name : type)
263
+ matches = value_objects.select { |value_object| value_object.hecks_name.to_s == target }
264
+ if matches.size > 1
265
+ raise Malformed, "#{context_name}.identified_by names duplicate value object #{target}"
266
+ end
267
+
268
+ vo = type.respond_to?(:attributes) ? type : matches.first
269
+ raise Malformed, "#{context_name}.identified_by names #{target}, which is not a declared value object" unless vo
270
+ if vo.attributes.empty?
271
+ raise Malformed, "#{context_name}.identified_by names #{target}, which declares no attributes"
272
+ end
273
+
274
+ field = (as || Naming.snake(target)).to_sym
275
+ if attributes.any? { |attribute| attribute.name == field }
276
+ raise Malformed,
277
+ "#{context_name}.identified_by #{target} mints :#{field}, but that attribute is already declared"
278
+ end
279
+
280
+ # `Attribute.new` DIRECTLY, not the public `attribute(...)` DSL
281
+ # entry — `target` is `Naming.demodulise`'d TEXT, not a bareword
282
+ # the bluebook author typed (the type position's own quoted-text
283
+ # refusal is about DSL source, not internal minting), and `vo`
284
+ # ITSELF can't be passed either: it is the real, already-built
285
+ # `ValueObject` subclass, permanently anonymous from Ruby's own
286
+ # `to_s` (only `hecks_name` carries its name) — `Attribute#spell`
287
+ # would demodulise it to "#<Class:0x...>", not "PizzaName".
288
+ attributes << Attribute.new(name: field, type: target)
289
+ # MOVED to `insert_at` — the attribute count AT THE MOMENT
290
+ # `identified_by` was actually called, captured by the caller —
291
+ # not left where `attribute` just appended it. Resolution happens
292
+ # at BUILD time, after every other attribute in the block has
293
+ # already run, so appending would put the identity field LAST
294
+ # regardless of where `identified_by` was actually written.
295
+ # Most real bluebooks write it first (insert_at 0); one (a
296
+ # ScheduledPayment corpus member) writes it after a reference_to
297
+ # and an attribute — this matches either, and whatever a person
298
+ # hand-writing `attribute field, Type` at that exact point,
299
+ # the way this used to be required, would have produced.
300
+ attributes.insert(insert_at, attributes.pop)
301
+ vo.attributes.flat_map do |attribute|
302
+ identity_paths_for_attribute(attribute, value_objects, context_name,
303
+ "#{field}.#{attribute.name}", [target])
304
+ end
305
+ end
306
+
307
+ def identity_paths_for_attribute(attribute, value_objects, context_name, path, visited)
308
+ if attribute.list?
309
+ raise Malformed,
310
+ "#{context_name}'s identity member #{path} is a list — an identity member must be scalar"
311
+ end
312
+ if attribute.optional?
313
+ raise Malformed,
314
+ "#{context_name}'s identity member #{path} is optional — an identity must be wholly known"
315
+ end
316
+
317
+ return [path] if attribute.reference?
318
+
319
+ nested = value_objects.find { |value_object| value_object.hecks_name.to_s == attribute.type.to_s }
320
+ return [path] unless nested
321
+
322
+ if visited.include?(nested.hecks_name.to_s)
323
+ cycle = [*visited, nested.hecks_name.to_s].join(" -> ")
324
+ raise Malformed, "#{context_name}'s identity value objects form a cycle: #{cycle}"
325
+ end
326
+
327
+ # A BARE FIELD DERIVES ONE SCALAR. `identified_by :ref` (or one leg
328
+ # of a compound `identified_by :a, :b`) names a single field, and
329
+ # deriving ITS path only makes sense while every value object along
330
+ # the way wraps exactly one field itself — the same "single-field
331
+ # value object" ADR 0025 names this shape after. A multi-field
332
+ # value object here used to expand silently into every one of its
333
+ # own fields, minting an unannounced compound key nothing declared.
334
+ if nested.attributes.size != 1
335
+ candidates = nested.attributes.map(&:name).join(", ")
336
+ raise Malformed,
337
+ "#{context_name}.identified_by :#{path} names #{nested.hecks_name}, which has " \
338
+ "#{nested.attributes.size} field#{'s' unless nested.attributes.size == 1} (#{candidates})"
339
+ end
340
+
341
+ member = nested.attributes.first
342
+ identity_paths_for_attribute(member, value_objects, context_name, "#{path}.#{member.name}",
343
+ [*visited, nested.hecks_name.to_s])
344
+ end
345
+ end
346
+ end
347
+ end
348
+ end
@@ -0,0 +1,71 @@
1
+ module Hecks
2
+ module Bluebook
3
+ module DSL
4
+ class BindingProxy
5
+ def self.namespace(domain, collector)
6
+ Module.new do
7
+ define_singleton_method(:const_missing) do |aggregate|
8
+ BindingProxy.new("#{domain}::#{aggregate}", collector)
9
+ end
10
+ end
11
+ end
12
+
13
+ def initialize(fqn, collector)
14
+ @fqn = fqn
15
+ @collector = collector
16
+ end
17
+
18
+ # THE AGGREGATE-SCOPED PORT — `Payments::Payment.port("Gateway") do
19
+ # ... end`, the same receiver a plain bind like `.persisted_by(...)`
20
+ # already reaches, because a port belongs to exactly one aggregate
21
+ # the same way a bind does. A REAL method, not method_missing : its
22
+ # shape (a name and a block building operations) has nothing to do
23
+ # with `Bind`, so it does not belong in that generic verb path.
24
+ def port(name, &block)
25
+ domain, aggregate_name = @fqn.split("::")
26
+ aggregate_ir = Hecks.current_registry.bluebook(domain)&.aggregate(aggregate_name) or
27
+ raise Malformed, "#{@fqn} declares no such aggregate — a port needs one to belong to"
28
+
29
+ # See HecksagonBuilder#port's own comment on why this resolver
30
+ # swap is needed : ConstShim's active resolver is one global for
31
+ # the whole dynamic extent, and it is currently this file's own
32
+ # BindingProxy-minting one, which would turn a bare `Pizza` inside
33
+ # `reference_to Pizza` into another BindingProxy instead of a name.
34
+ built = ConstShim.with(->(const) { const }) { DomainPortBuilder.build(name, owner: aggregate_name, &block) }
35
+
36
+ # A `verb`-shaped port is a plain `Port` — the exact struct
37
+ # `Hecks.port`'s own top-level method registers, so it goes
38
+ # through the same `add_port` the registry already answers for
39
+ # that call. It belongs to no aggregate IR the way an
40
+ # operations-shaped `DomainPort` does; the aggregate above
41
+ # was only needed to resolve `owner` for the operations branch.
42
+ if built.is_a?(Port)
43
+ Hecks.current_registry.add_port(built)
44
+ return self
45
+ end
46
+
47
+ built.operations.each do |operation|
48
+ operation.attributes.select(&:reference?).each { |attribute| attribute.type.declared_in = aggregate_ir }
49
+ end
50
+ aggregate_ir.add_port(built)
51
+ self
52
+ end
53
+
54
+ def method_missing(verb, *args, **kwargs, &block)
55
+ @collector << Bind.new(
56
+ aggregate: @fqn,
57
+ verb: verb.to_s,
58
+ adapter: args.first.to_s,
59
+ role: kwargs[:role]&.to_s
60
+ )
61
+ block&.call
62
+ self
63
+ end
64
+
65
+ def respond_to_missing?(_name, _include_private = false) = true
66
+
67
+ def to_s = @fqn
68
+ end
69
+ end
70
+ end
71
+ end