hecks 3.13.0 → 4.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.
- checksums.yaml +4 -4
- data/examples/pizzas/adapters/fake_processor.rb +34 -0
- data/examples/pizzas/adapters/payment_signature.rb +66 -0
- data/examples/pizzas/adapters/payment_webhook.rb +87 -0
- data/examples/pizzas/bluebook/pizzas.behaviors +33 -0
- data/examples/pizzas/bluebook/pizzas.bluebook +4 -4
- data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
- data/examples/pizzas/config.ru +22 -0
- data/lib/hecks/adapters/driven/prism.rb +0 -2
- data/lib/hecks/adapters/driven.rb +0 -2
- data/lib/hecks/adapters/driving/json.rb +1 -7
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +15 -5
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
- data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
- data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
- data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
- data/lib/hecks/bluebook/hecksagon.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
- data/lib/hecks/bluebook/meta_validator.rb +0 -11
- data/lib/hecks/bluebook/named_pattern.rb +33 -0
- data/lib/hecks/bluebook/translation.rb +18 -10
- data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
- data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
- data/lib/hecks/bluebook/translation_expression.rb +39 -0
- data/lib/hecks/bluebook/translation_plan.rb +70 -0
- data/lib/hecks/bluebook.rb +3 -0
- data/lib/hecks/chapters.rb +3 -3
- data/lib/hecks/cli/domain_stub.rb +1 -1
- data/lib/hecks/cli/interview_aggregate.rb +4 -4
- data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
- data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
- data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
- data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
- data/lib/hecks/composer/adapters/jev.adapter +3 -0
- data/lib/hecks/composer/adapters/jev.rb +66 -0
- data/lib/hecks/composer/agent.rb +113 -0
- data/lib/hecks/composer/assistant.rb +110 -0
- data/lib/hecks/composer/behavior_run.rb +53 -0
- data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
- data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
- data/lib/hecks/composer/board.rb +65 -0
- data/lib/hecks/composer/board_edit/command_text.rb +39 -0
- data/lib/hecks/composer/board_edit/source.rb +84 -0
- data/lib/hecks/composer/board_edit.rb +144 -0
- data/lib/hecks/composer/check.rb +89 -0
- data/lib/hecks/composer/domains.rb +81 -0
- data/lib/hecks/composer/jobs.rb +134 -0
- data/lib/hecks/composer/layout.rb +37 -0
- data/lib/hecks/composer/live.rb +150 -0
- data/lib/hecks/composer/outline.rb +48 -0
- data/lib/hecks/composer/page.html +989 -0
- data/lib/hecks/composer/panels.rb +142 -0
- data/lib/hecks/composer/parse.rb +83 -0
- data/lib/hecks/composer/plain.rb +28 -0
- data/lib/hecks/composer/ports/composer_agent.port +7 -0
- data/lib/hecks/composer/session.rb +141 -0
- data/lib/hecks/composer/toolbox.rb +166 -0
- data/lib/hecks/composer/web.rb +156 -0
- data/lib/hecks/composer/workbench.rb +51 -0
- data/lib/hecks/composer/workspace.rb +81 -0
- data/lib/hecks/composer.rb +17 -0
- data/lib/hecks/corpus/adapters.rb +22 -0
- data/lib/hecks/corpus/rust.rb +1 -1
- data/lib/hecks/corpus/worlds.rb +19 -0
- data/lib/hecks/corpus.rb +5 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +34 -34
- data/lib/hecks/forms/app/commands.rb +2 -3
- data/lib/hecks/forms/app/queries.rb +2 -3
- data/lib/hecks/forms/app/records.rb +17 -5
- data/lib/hecks/forms/command_form_renderer.rb +42 -16
- data/lib/hecks/forms/field_controls.rb +2 -2
- data/lib/hecks/forms/field_renderer.rb +1 -1
- data/lib/hecks/forms/field_shape.rb +36 -17
- data/lib/hecks/forms/index_renderer.rb +2 -1
- data/lib/hecks/forms/page.rb +1 -1
- data/lib/hecks/forms/params.rb +0 -13
- data/lib/hecks/forms/query_form_renderer.rb +7 -6
- data/lib/hecks/forms/record_renderer.rb +11 -7
- data/lib/hecks/forms/rule_words.rb +100 -0
- data/lib/hecks/forms/value_object_shape.rb +2 -1
- data/lib/hecks/framework/bluebook/change.bluebook +2 -2
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
- data/lib/hecks/fuzzing/domain_generator/block_rendering.rb +1 -1
- data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
- data/lib/hecks/fuzzing/replay/filters.rb +2 -6
- data/lib/hecks/gate/stages.yml +25 -0
- data/lib/hecks/grammar/evolve/seeds.rb +0 -6
- data/lib/hecks/grammar/evolve.rb +0 -3
- data/lib/hecks/grammar/translation.bluebook +1 -1
- data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
- data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
- data/lib/hecks/hecks/codebase.behaviors +6 -0
- data/lib/hecks/hecks/codebase.bluebook +58 -37
- data/lib/hecks/hecks/composer.behaviors +68 -0
- data/lib/hecks/hecks/context_map.hecksagon +1 -1
- data/lib/hecks/hecks/custodian.behaviors +0 -6
- data/lib/hecks/hecks/custodian.bluebook +15 -92
- data/lib/hecks/hecks/hecks.bluebook +2 -2
- data/lib/hecks/hecks/hecks.hecksagon +2 -2
- data/lib/hecks/hecks/hecks.world +1 -1
- data/lib/hecks/hecks/rehearsal.bluebook +10 -16
- data/lib/hecks/hecks/tooling.bluebook +22 -22
- data/lib/hecks/kernel_capabilities/templates.rb +4 -4
- data/lib/hecks/kernel_capabilities.rb +1 -1
- data/lib/hecks/language/bluebook/aggregate.bluebook +1 -0
- data/lib/hecks/language/bluebook/command.bluebook +1 -0
- data/lib/hecks/language/bluebook/entity.bluebook +1 -0
- data/lib/hecks/language/bluebook/query.bluebook +1 -0
- data/lib/hecks/language/bluebook/shape.bluebook +8 -7
- data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
- data/lib/hecks/language/hecksagon/port_operation.bluebook +1 -0
- data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
- data/lib/hecks/ports/access_control.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
- data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
- data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
- data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
- data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
- data/lib/hecks/ports.rb +0 -1
- data/lib/hecks/projection_files.rb +3 -1
- data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
- data/lib/hecks/projections/glossary/entries.rb +10 -4
- data/lib/hecks/projections/glossary/groups.rb +42 -0
- data/lib/hecks/projections/glossary/html/page.rb +7 -2
- data/lib/hecks/projections/glossary/html/parser.rb +1 -1
- data/lib/hecks/projections/glossary/html/sections.rb +31 -11
- data/lib/hecks/projections/glossary/html.rb +1 -2
- data/lib/hecks/projections/glossary/markdown.rb +31 -28
- data/lib/hecks/projections/glossary/mermaid.rb +3 -0
- data/lib/hecks/projections/glossary/page.css +143 -153
- data/lib/hecks/projections/glossary/page.js +63 -3
- data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
- data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
- data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
- data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
- data/lib/hecks/projections/glossary/sentences.rb +13 -3
- data/lib/hecks/projections/glossary/slugs.rb +4 -1
- data/lib/hecks/projections/glossary.rb +2 -1
- data/lib/hecks/projections/plain_english.rb +97 -0
- data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
- data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
- data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
- data/lib/hecks/projections/rust_vocabulary.rb +14 -6
- data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
- data/lib/hecks/projections/statements/actions.rb +99 -0
- data/lib/hecks/projections/statements/relationships.rb +86 -0
- data/lib/hecks/projections/statements.rb +15 -24
- data/lib/hecks/projector/exporter/adapters.rb +112 -0
- data/lib/hecks/projector/exporter/translations.rb +18 -35
- data/lib/hecks/projector/exporter/worlds.rb +103 -0
- data/lib/hecks/projector/exporter.rb +5 -1
- data/lib/hecks/quality_control/adapters/agent.rb +6 -7
- data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
- data/lib/hecks/quality_control/quality_control.bluebook +51 -51
- data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
- data/lib/hecks/runtime/outbox.rb +2 -9
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
- data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
- data/lib/hecks/runtime/value.rb +13 -0
- data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
- data/lib/hecks/rust_build/wasm.rb +0 -12
- data/lib/hecks/rust_build/write_if_changed.rb +12 -12
- data/lib/hecks/rust_format.rb +77 -0
- data/lib/hecks/site/bluebook/site.bluebook +5 -5
- data/lib/hecks/sme/bluebook/sme.bluebook +12 -12
- data/lib/hecks/tickets/bluebook/tickets.bluebook +3 -3
- data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
- data/lib/hecks/tools/fuzz_sweep.rb +1 -3
- data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
- data/lib/hecks/tools/translation_audit.rb +8 -10
- data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +2 -1
- data/lib/hecks.rb +3 -0
- data/prebuilt/{58208399bada10b0db99928c48e38789288fc58f7b71a72b21bfa386680e32ed.marshal → 02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal} +0 -0
- data/prebuilt/{2b59737fbdbd5f44edda0fddce2611f45d76087b3a7c782d1f1a17ea58c77948.marshal → bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal} +0 -0
- data/prebuilt/verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.json +1 -0
- data/rust/Cargo.toml +40 -0
- data/rust/build/Cargo.toml +36 -0
- data/rust/build/clippy.toml +4 -0
- data/rust/build/src/adapters_pass.rs +196 -0
- data/rust/build/src/binding_pass.rs +54 -13
- data/rust/build/src/build_artifact.rs +23 -5
- data/rust/build/src/cargo_sync.rs +69 -26
- data/rust/build/src/lineage_pass.rs +101 -87
- data/rust/build/src/main.rs +11 -2
- data/rust/build/src/pipeline.rs +80 -19
- data/rust/build/src/reserved_names.rs +6 -50
- data/rust/build/src/resolve.rs +82 -35
- data/rust/build/src/rustfmt.rs +100 -0
- data/rust/build/src/seams_pass.rs +85 -16
- data/rust/build/src/subprocess.rs +30 -6
- data/rust/build/src/tmp.rs +5 -2
- data/rust/build/src/translation_pass.rs +72 -22
- data/rust/build/src/world_pass.rs +109 -0
- data/rust/clippy.toml +4 -0
- data/rust/codegen/Cargo.toml +36 -0
- data/rust/codegen/clippy.toml +4 -0
- data/rust/codegen/src/asks.rs +40 -14
- data/rust/codegen/src/attr.rs +2 -2
- data/rust/codegen/src/bridging.rs +676 -117
- data/rust/codegen/src/commands.rs +979 -373
- data/rust/codegen/src/constraints.rs +88 -25
- data/rust/codegen/src/dependency_planning.rs +128 -34
- data/rust/codegen/src/domain_generator.rs +300 -170
- data/rust/codegen/src/exemplar.rs +87 -32
- data/rust/codegen/src/expr_emitter.rs +54 -6
- data/rust/codegen/src/fielded.rs +193 -41
- data/rust/codegen/src/hecks_naming.rs +124 -16
- data/rust/codegen/src/hecks_naming_tests.rs +81 -0
- data/rust/codegen/src/json.rs +80 -44
- data/rust/codegen/src/json_codec.rs +585 -149
- data/rust/codegen/src/literal.rs +104 -138
- data/rust/codegen/src/main.rs +145 -52
- data/rust/codegen/src/manifest.rs +76 -14
- data/rust/codegen/src/mutations.rs +1212 -239
- data/rust/codegen/src/naming.rs +111 -60
- data/rust/codegen/src/optional_pass.rs +90 -28
- data/rust/codegen/src/ports.rs +5 -4
- data/rust/codegen/src/prelude.rs +139 -49
- data/rust/codegen/src/queries.rs +601 -226
- data/rust/codegen/src/reactions.rs +268 -74
- data/rust/codegen/src/read_models.rs +606 -205
- data/rust/codegen/src/reference_specs.rs +16 -3
- data/rust/codegen/src/registry.rs +128 -79
- data/rust/codegen/src/reserved_names.rs +6 -50
- data/rust/codegen/src/shared.rs +71 -22
- data/rust/codegen/src/sidecars.rs +15 -5
- data/rust/codegen/src/skip_reason.rs +8 -2
- data/rust/codegen/src/types.rs +306 -108
- data/rust/host/Cargo.lock +1 -0
- data/rust/host/Cargo.toml +40 -4
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/clippy.toml +4 -0
- data/rust/host/src/api.rs +698 -218
- data/rust/host/src/auth.rs +491 -214
- data/rust/host/src/b64.rs +91 -0
- data/rust/host/src/bin/expr_harness.rs +6 -2
- data/rust/host/src/bin/lineage_harness.rs +47 -11
- data/rust/host/src/bin/mint_harness.rs +70 -37
- data/rust/host/src/bin/validate_harness.rs +16 -8
- data/rust/host/src/boot.rs +144 -68
- data/rust/host/src/boundary_fuzz/api.rs +130 -32
- data/rust/host/src/boundary_fuzz/auth.rs +108 -23
- data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
- data/rust/host/src/boundary_fuzz/ir.rs +164 -12
- data/rust/host/src/boundary_fuzz/server.rs +283 -36
- data/rust/host/src/boundary_fuzz/web.rs +56 -8
- data/rust/host/src/checkout.rs +78 -32
- data/rust/host/src/clock.rs +8 -0
- data/rust/host/src/commerce_ir.rs +333 -95
- data/rust/host/src/crypto.rs +33 -0
- data/rust/host/src/dispatch.rs +990 -331
- data/rust/host/src/expr_json.rs +1021 -273
- data/rust/host/src/extension.rs +24 -4
- data/rust/host/src/field_hints.rs +11 -7
- data/rust/host/src/fuzz_support.rs +85 -13
- data/rust/host/src/host.rs +100 -0
- data/rust/host/src/http.rs +22 -0
- data/rust/host/src/ir.rs +326 -16
- data/rust/host/src/journal.rs +277 -176
- data/rust/host/src/lambda_client.rs +219 -53
- data/rust/host/src/lib.rs +16 -4
- data/rust/host/src/log.rs +62 -11
- data/rust/host/src/mint.rs +1153 -326
- data/rust/host/src/needs.rs +185 -44
- data/rust/host/src/payments/keystore.rs +197 -45
- data/rust/host/src/payments/roles.rs +191 -0
- data/rust/host/src/payments/tests.rs +1445 -275
- data/rust/host/src/payments.rs +409 -121
- data/rust/host/src/platform_signin/attack_tests.rs +5 -5
- data/rust/host/src/platform_signin/config.rs +11 -11
- data/rust/host/src/platform_signin/cookie.rs +2 -2
- data/rust/host/src/platform_signin/extension.rs +2 -1
- data/rust/host/src/platform_signin/handler.rs +9 -8
- data/rust/host/src/platform_signin/jti.rs +4 -2
- data/rust/host/src/platform_signin/jwks.rs +2 -1
- data/rust/host/src/platform_signin/jws.rs +2 -37
- data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
- data/rust/host/src/platform_signin/mode_tests.rs +5 -3
- data/rust/host/src/platform_signin/pages.rs +20 -20
- data/rust/host/src/platform_signin/session.rs +8 -1
- data/rust/host/src/platform_signin/settings.rs +6 -6
- data/rust/host/src/platform_signin/strict_json.rs +12 -9
- data/rust/host/src/platform_signin/verify.rs +41 -41
- data/rust/host/src/platform_signin/wiring_support.rs +13 -18
- data/rust/host/src/presentation.rs +207 -63
- data/rust/host/src/presentation_write.rs +502 -187
- data/rust/host/src/query_step.rs +69 -18
- data/rust/host/src/rate_limit.rs +541 -142
- data/rust/host/src/reference_transform.rs +77 -25
- data/rust/host/src/reference_validate.rs +443 -104
- data/rust/host/src/resend.rs +190 -55
- data/rust/host/src/secrets.rs +116 -21
- data/rust/host/src/server.rs +483 -104
- data/rust/host/src/storage_shape.rs +88 -21
- data/rust/host/src/test_pg.rs +17 -7
- data/rust/host/src/ui_schema.rs +495 -207
- data/rust/host/src/wasm_runner.rs +21 -6
- data/rust/host/src/web/commerce.rs +101 -23
- data/rust/host/src/web/newsletter/cooldown.rs +10 -3
- data/rust/host/src/web/newsletter/template.rs +26 -7
- data/rust/host/src/web/newsletter/tests.rs +112 -23
- data/rust/host/src/web/newsletter.rs +209 -59
- data/rust/host/src/web/newsletter_send.rs +251 -79
- data/rust/host/src/web/registration_receipt/tests.rs +16 -4
- data/rust/host/src/web/registration_receipt.rs +100 -24
- data/rust/host/src/web/registrations.rs +1384 -332
- data/rust/host/src/web/resend_webhook.rs +138 -80
- data/rust/host/src/web.rs +2416 -508
- data/rust/lsp/Cargo.toml +36 -0
- data/rust/lsp/clippy.toml +4 -0
- data/rust/lsp/src/json.rs +29 -22
- data/rust/lsp/src/main.rs +81 -31
- data/rust/lsp/src/outline.rs +23 -7
- data/rust/lsp/src/rpc.rs +17 -4
- data/rust/parser/Cargo.toml +36 -0
- data/rust/parser/clippy.toml +4 -0
- data/rust/parser/src/build/closed_sets.rs +6 -1
- data/rust/parser/src/build/identity.rs +4 -3
- data/rust/parser/src/build/mod.rs +1 -0
- data/rust/parser/src/build/named_pattern.rs +26 -0
- data/rust/parser/src/build/naming.rs +2 -80
- data/rust/parser/src/build/pattern_subset.rs +6 -8
- data/rust/parser/src/build/query_derive.rs +3 -1
- data/rust/parser/src/build/query_inference.rs +14 -5
- data/rust/parser/src/build/read_model.rs +4 -1
- data/rust/parser/src/build/references.rs +4 -3
- data/rust/parser/src/canonical.rs +34 -15
- data/rust/parser/src/emit.rs +187 -105
- data/rust/parser/src/expr/ast_json.rs +134 -42
- data/rust/parser/src/expr/evaluator.rs +27 -9
- data/rust/parser/src/expr/mod.rs +54 -13
- data/rust/parser/src/expr/resolver.rs +299 -92
- data/rust/parser/src/ir.rs +35 -3
- data/rust/parser/src/keywords.rs +7168 -510
- data/rust/parser/src/lex.rs +11 -25
- data/rust/parser/src/lib.rs +1 -0
- data/rust/parser/src/main.rs +65 -6
- data/rust/parser/src/parse/adapter.rs +157 -0
- data/rust/parser/src/parse/aggregate.rs +19 -10
- data/rust/parser/src/parse/chapter.rs +16 -17
- data/rust/parser/src/parse/command.rs +35 -16
- data/rust/parser/src/parse/domain_port.rs +4 -7
- data/rust/parser/src/parse/entity.rs +21 -11
- data/rust/parser/src/parse/hecksagon.rs +10 -4
- data/rust/parser/src/parse/lifecycle.rs +9 -5
- data/rust/parser/src/parse/mod.rs +43 -91
- data/rust/parser/src/parse/needs.rs +21 -12
- data/rust/parser/src/parse/policy.rs +2 -2
- data/rust/parser/src/parse/process_manager.rs +9 -11
- data/rust/parser/src/parse/query.rs +8 -4
- data/rust/parser/src/parse/read_model.rs +57 -16
- data/rust/parser/src/parse/value_object.rs +7 -2
- data/rust/parser/src/parse/world.rs +322 -0
- data/rust/parser/src/ruby_value.rs +7 -6
- data/rust/src/exemplar/json.rs +4 -4
- data/rust/src/exemplar/mutations.rs +18 -0
- data/rust/src/exemplar/read_models.rs +1 -10
- data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
- data/rust/src/kernel/attribute_shapes/list.rs +2 -2
- data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
- data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
- data/rust/src/kernel/cli.rs +229 -112
- data/rust/src/kernel/dispatch.rs +323 -115
- data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
- data/rust/src/kernel/expr.rs +203 -86
- data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
- data/rust/src/kernel/expression_operators/comparison.rs +87 -2
- data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
- data/rust/src/kernel/expression_operators/logical.rs +9 -3
- data/rust/src/kernel/expression_operators/membership.rs +3 -1
- data/rust/src/kernel/expression_operators/mod.rs +34 -21
- data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
- data/rust/src/kernel/expression_operators/positional.rs +84 -30
- data/rust/src/kernel/expression_operators/presence.rs +52 -13
- data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
- data/rust/src/kernel/expression_operators/sized.rs +24 -8
- data/rust/src/kernel/expression_operators/text.rs +120 -27
- data/rust/src/kernel/expression_operators/to_string.rs +10 -4
- data/rust/src/kernel/json.rs +230 -148
- data/rust/src/kernel/mod.rs +46 -33
- data/rust/src/kernel/named_query.rs +63 -15
- data/rust/src/kernel/needs.rs +112 -25
- data/rust/src/kernel/orchestrate.rs +980 -581
- data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
- data/rust/src/kernel/pattern.rs +107 -34
- data/rust/src/kernel/query_comparators.rs +118 -41
- data/rust/src/kernel/query_ordering.rs +13 -5
- data/rust/src/kernel/read_model.rs +479 -181
- data/rust/src/kernel/reference_lookup.rs +122 -26
- data/rust/src/kernel/repository.rs +293 -66
- data/rust/src/kernel/routing.rs +58 -42
- data/rust/src/kernel/ruby_float.rs +139 -0
- data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
- data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
- data/rust/src/kernel/vocab/field_hint.rs +19 -25
- data/rust/src/kernel/vocab/mod.rs +6 -6
- data/rust/src/kernel/vocab/query_comparator.rs +23 -24
- data/rust/src/kernel/vocab/refusal_template.rs +501 -297
- data/rust/src/lib.rs +50 -0
- data/rust/src/main.rs +4 -1
- data/rust/web/Cargo.toml +36 -0
- data/rust/web/clippy.toml +4 -0
- metadata +85 -15
- data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
- data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
- data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
- data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
- data/lib/hecks/ports/authentication.port +0 -6
- data/lib/hecks/ports/authentication.rb +0 -67
- data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
- data/prebuilt/verdicts-48318ca172edd21443b1ad02801c697fff100b746e828f09db987647641a5df2.json +0 -1
- data/rust/host/src/approval.rs +0 -652
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "json"
|
|
2
|
+
require_relative "expression_sql"
|
|
2
3
|
|
|
3
4
|
module Hecks
|
|
4
5
|
module Translation
|
|
@@ -7,19 +8,34 @@ module Hecks
|
|
|
7
8
|
module RuleCompiler
|
|
8
9
|
module_function
|
|
9
10
|
|
|
10
|
-
# Compiles one edge's rename/move/convert/drop/
|
|
11
|
+
# Compiles one edge's derive/rename/move/convert/drop/backfill rules into a nested
|
|
11
12
|
# SQL expression over `state`, composed innermost-first in that phase order.
|
|
12
13
|
def compile_rules(declared)
|
|
13
|
-
expression =
|
|
14
|
+
expression = compile_derives("state", declared.derives)
|
|
15
|
+
expression = compile_renames(expression, declared.renames)
|
|
14
16
|
expression = compile_moves(expression, declared.moves)
|
|
15
17
|
expression = compile_converts(expression, declared.converts)
|
|
16
18
|
expression = compile_drops(expression, declared.drops)
|
|
17
|
-
expression = compile_computes(expression, declared.computes)
|
|
18
19
|
# Backfilled last, same order as `Lineage#translate`'s in-process pass, so the
|
|
19
20
|
# boot-time mint audit (which reads only this compiled SQL) sees the same result.
|
|
20
21
|
compile_backfills(expression, declared.backfills)
|
|
21
22
|
end
|
|
22
23
|
|
|
24
|
+
# Wraps `expression` in each derive, every one reading the record as it was stored.
|
|
25
|
+
def compile_derives(expression, derives)
|
|
26
|
+
derives.inject(expression) { |sql, derive| compile_derive(sql, derive) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Wraps `expression` so a derive's text lands at its destination when the expression has a
|
|
30
|
+
# value; the expression reads the stored `state`, never the rules composed before it.
|
|
31
|
+
def compile_derive(expression, derive)
|
|
32
|
+
value = ExpressionSql.compile(derive.tree, "state")
|
|
33
|
+
"(SELECT CASE WHEN __d IS NULL THEN __s " \
|
|
34
|
+
"ELSE hecks_tr_insert(__s, #{path_literal(derive.to)}, to_jsonb(__d), " \
|
|
35
|
+
"#{text_literal("derive #{derive.to}")}) END " \
|
|
36
|
+
"FROM (SELECT (#{expression}) AS __s) __outer, LATERAL (SELECT #{value} AS __d) __derived)"
|
|
37
|
+
end
|
|
38
|
+
|
|
23
39
|
# Folds each rule of one kind into `expression`, innermost-first.
|
|
24
40
|
def compile_renames(expression, renames)
|
|
25
41
|
renames.inject(expression) { |sql, (old_name, new_name)| compile_rename(sql, old_name, new_name) }
|
|
@@ -31,8 +47,6 @@ module Hecks
|
|
|
31
47
|
|
|
32
48
|
def compile_drops(expression, drops) = drops.inject(expression) { |sql, name| compile_drop(sql, name) }
|
|
33
49
|
|
|
34
|
-
def compile_computes(expression, computes) = computes.inject(expression) { |sql, compute| compile_compute(sql, compute) }
|
|
35
|
-
|
|
36
50
|
def compile_backfills(expression, backfills)
|
|
37
51
|
backfills.inject(expression) { |sql, backfill| compile_backfill(sql, backfill) }
|
|
38
52
|
end
|
|
@@ -71,25 +85,12 @@ module Hecks
|
|
|
71
85
|
"CASE WHEN #{guard} THEN #{compile_id_expression(declared)} ELSE aggregate_id END AS aggregate_id"
|
|
72
86
|
end
|
|
73
87
|
|
|
74
|
-
#
|
|
75
|
-
#
|
|
88
|
+
# A `rekey_to` id: the value of the derive it names, read from the record's stored `state`,
|
|
89
|
+
# or the old id for a record where the derive has no value.
|
|
76
90
|
def compile_id_expression(declared)
|
|
77
91
|
rekey = declared.rekeys.first
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
# Wraps `expression` so a compute's SQL result lands at its declared destination
|
|
82
|
-
# when the source path is present; the whole record stays readable as `__s`.
|
|
83
|
-
def compile_compute(expression, compute)
|
|
84
|
-
from = compute.from.to_s
|
|
85
|
-
to = compute.to.to_s
|
|
86
|
-
"(SELECT CASE WHEN (__x).present THEN " \
|
|
87
|
-
"hecks_tr_insert((__x).remaining, #{path_literal(to)}, to_jsonb((#{compute.sql})), " \
|
|
88
|
-
"#{text_literal("compute #{from} to: #{to}")}) " \
|
|
89
|
-
"ELSE __s END " \
|
|
90
|
-
"FROM (SELECT (#{expression}) AS __s) __outer, " \
|
|
91
|
-
"LATERAL (SELECT hecks_tr_extract(__s, #{path_literal(from)}) AS __x) __extract, " \
|
|
92
|
-
"LATERAL (SELECT (__s #>> #{path_literal(from)}) AS #{quote(from)}) __fields)"
|
|
92
|
+
derive = declared.derives.find { |candidate| candidate.to == rekey.to }
|
|
93
|
+
"COALESCE(#{ExpressionSql.compile(derive.tree, "state")}, aggregate_id)"
|
|
93
94
|
end
|
|
94
95
|
|
|
95
96
|
# Wraps `expression` so a backfill's default lands at its declared path only when
|
|
@@ -103,13 +104,6 @@ module Hecks
|
|
|
103
104
|
"FROM (SELECT (#{expression}) AS __s) __outer)"
|
|
104
105
|
end
|
|
105
106
|
|
|
106
|
-
# Quotes a Postgres identifier; requires "pg" lazily so a non-Postgres-bound
|
|
107
|
-
# domain never gains a hard dependency on the gem just by loading this file.
|
|
108
|
-
def quote(name)
|
|
109
|
-
require "pg"
|
|
110
|
-
PG::Connection.quote_ident(name.to_s)
|
|
111
|
-
end
|
|
112
|
-
|
|
113
107
|
# Renders a Ruby value as a single-quoted SQL text literal, escaping embedded quotes.
|
|
114
108
|
def text_literal(text) = "'#{text.to_s.gsub("'", "''")}'"
|
|
115
109
|
|
|
@@ -5,7 +5,7 @@ require_relative "scaffold/writer"
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module Translation
|
|
7
7
|
# Diffs the held era's storage shape against the current one and writes the edge file.
|
|
8
|
-
# Ambiguities become parse-refusing `unresolved` constructs; it never proposes
|
|
8
|
+
# Ambiguities become parse-refusing `unresolved` constructs; it never proposes drop or derive.
|
|
9
9
|
module Scaffold
|
|
10
10
|
Edge = Struct.new(:domain, :from, :to, :ordinal, :label, :aggregates, :retired, keyword_init: true)
|
|
11
11
|
ScaffoldedAggregate = Struct.new(:name, :was, :rules, keyword_init: true)
|
|
@@ -18,20 +18,17 @@ module Hecks
|
|
|
18
18
|
|
|
19
19
|
# Registers this plugin's era gates on one boot's gate list.
|
|
20
20
|
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
21
|
+
# One `:pre_verify` gate, `era_check` (ADR 0031), registered only when the registry
|
|
22
|
+
# binds something lineage-capable.
|
|
23
23
|
#
|
|
24
24
|
# @param registry [Runtime::Registry] the registry being booted, asked whether any
|
|
25
25
|
# bluebook's first aggregate is bound to a lineage-capable adapter
|
|
26
26
|
# @param gates [Runtime::BootGates] this boot's gate list, registered onto in place
|
|
27
27
|
# @return [Runtime::BootGates, nil] `gates` when `:era_check` was registered; nil when
|
|
28
|
-
# the registry binds nothing lineage-capable
|
|
28
|
+
# the registry binds nothing lineage-capable
|
|
29
29
|
# @raise [Runtime::WiringError] if an aggregate's persistence binding is missing,
|
|
30
30
|
# ambiguous, or carries an unsupported role
|
|
31
31
|
def contribute_boot_gates(registry, gates)
|
|
32
|
-
gates.register(:era_compute_rules, lambda { |reg, _dir|
|
|
33
|
-
Runtime::EraCheck.check_compute_rules_for_registry!(reg)
|
|
34
|
-
}, phase: :pre_verify)
|
|
35
32
|
gates.register(:era_check, Runtime::EraCheck.method(:check_lineage!), phase: :pre_verify) if
|
|
36
33
|
Runtime::EraCheck.lineage_capable_registry?(registry)
|
|
37
34
|
end
|
data/lib/hecks/ports.rb
CHANGED
|
@@ -17,7 +17,6 @@ require_relative "ports/identity_generation"
|
|
|
17
17
|
require_relative "ports/key_vault"
|
|
18
18
|
require_relative "ports/authorization"
|
|
19
19
|
require_relative "ports/identity_resolution"
|
|
20
|
-
require_relative "ports/authentication"
|
|
21
20
|
require_relative "ports/access_control"
|
|
22
21
|
require_relative "ports/identity_assignment"
|
|
23
22
|
require_relative "ports/agent"
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require "json"
|
|
5
5
|
require "tempfile"
|
|
6
|
+
require_relative "rust_format"
|
|
6
7
|
require_relative "projection_files/texts"
|
|
7
8
|
require_relative "projection_files/expression_tables"
|
|
8
9
|
|
|
@@ -55,7 +56,8 @@ module Hecks
|
|
|
55
56
|
def build(name, root: ROOT)
|
|
56
57
|
raise ArgumentError, "no projection named #{name.inspect}" unless NAMES.include?(name.to_sym)
|
|
57
58
|
|
|
58
|
-
public_send(name, root)
|
|
59
|
+
result = public_send(name, root)
|
|
60
|
+
Result.new(RustFormat.files(result.content), result.stale)
|
|
59
61
|
end
|
|
60
62
|
|
|
61
63
|
# Writes one projection, each file through a temporary file and a rename so a reader never sees
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# @@web_logical_id@@ is the ONE exception -- real Google OAuth token
|
|
3
|
-
# exchange (
|
|
3
|
+
# exchange (the Rust web host's Google sign-in posting to
|
|
4
4
|
# https://oauth2.googleapis.com/token) is a genuine third-party HTTPS
|
|
5
5
|
# call with no AWS PrivateLink/VPC Endpoint option (unlike
|
|
6
6
|
# lambda:InvokeFunction, above), and @@web_logical_id@@ is ALSO
|
|
@@ -77,20 +77,26 @@ module Hecks
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def verb_entries(bluebook, homes)
|
|
80
|
-
|
|
80
|
+
shapes = bluebook.aggregates.to_h { |aggregate| [aggregate.hecks_name, aggregate.value_objects] }
|
|
81
|
+
holders(bluebook).flat_map do |holder|
|
|
82
|
+
command_entries(holder, homes) + query_entries(holder, homes, shapes[homes[holder.hecks_name]])
|
|
83
|
+
end
|
|
81
84
|
end
|
|
82
85
|
|
|
83
86
|
def command_entries(holder, homes)
|
|
84
87
|
holder.commands.map do |command|
|
|
85
88
|
Entry.new(name: command.hecks_name, kind: :command, within: holder.hecks_name,
|
|
86
|
-
section: homes[holder.hecks_name],
|
|
89
|
+
section: homes[holder.hecks_name],
|
|
90
|
+
facts: { command: command, holder: holder, within: homes[holder.hecks_name] })
|
|
87
91
|
end
|
|
88
92
|
end
|
|
89
93
|
|
|
90
|
-
|
|
94
|
+
# A query carries its holder and the aggregate's value objects, to speak its fields.
|
|
95
|
+
def query_entries(holder, homes, value_objects)
|
|
91
96
|
holder.queries.map do |query|
|
|
92
97
|
Entry.new(name: query.hecks_name, kind: :query, within: holder.hecks_name,
|
|
93
|
-
section: homes[holder.hecks_name],
|
|
98
|
+
section: homes[holder.hecks_name],
|
|
99
|
+
facts: { query: query, holder: holder, value_objects: Array(value_objects) })
|
|
94
100
|
end
|
|
95
101
|
end
|
|
96
102
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Projections
|
|
3
|
+
module Glossary
|
|
4
|
+
# The groups an aggregate's terms are listed under, so a reader meets what can be done
|
|
5
|
+
# with a thing before the lists and the parts it is made of. Each group is a `###`
|
|
6
|
+
# heading in the Markdown, so GitHub and the page give the terms the same slugs.
|
|
7
|
+
module Groups
|
|
8
|
+
# Kind => the group's heading, in the order the groups are shown.
|
|
9
|
+
LABELS = {
|
|
10
|
+
command: "Actions", event: "What it records", policy: "What happens on its own",
|
|
11
|
+
saga: "What happens on its own", query: "Lists", entity: "What it is made of",
|
|
12
|
+
value_object: "What it is made of"
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
ORDER = LABELS.values.uniq.freeze
|
|
16
|
+
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# @param entry [Glossary::Entry] a term
|
|
20
|
+
# @return [Integer] where its group sits; terms outside every group come first
|
|
21
|
+
def rank(entry) = ORDER.index(LABELS[entry.kind]) || -1
|
|
22
|
+
|
|
23
|
+
# A section's terms with a group heading before the first term of each group. Only an
|
|
24
|
+
# aggregate's section is grouped.
|
|
25
|
+
#
|
|
26
|
+
# @param section [Glossary::Section] the section, its terms already sorted
|
|
27
|
+
# @return [Array<Array(Symbol, Object)>] `[:group, label]` and `[:term, entry]` pairs
|
|
28
|
+
def walk(section)
|
|
29
|
+
return section.terms.map { |entry| [:term, entry] } unless section.aggregate
|
|
30
|
+
|
|
31
|
+
last = nil
|
|
32
|
+
section.terms.flat_map do |entry|
|
|
33
|
+
label = LABELS[entry.kind]
|
|
34
|
+
heading = label == last ? [] : [[:group, label]]
|
|
35
|
+
last = label
|
|
36
|
+
heading + [[:term, entry]]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -41,6 +41,7 @@ module Hecks
|
|
|
41
41
|
<p class="sub">Glossary</p>
|
|
42
42
|
<input type="search" placeholder="Find a term" aria-label="Find a term" autocomplete="off">
|
|
43
43
|
<p class="count"></p>
|
|
44
|
+
<p class="progress" hidden></p>
|
|
44
45
|
<ol>
|
|
45
46
|
@@ITEMS@@
|
|
46
47
|
</ol>
|
|
@@ -69,7 +70,7 @@ module Hecks
|
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
def rail(title, sections, slugs)
|
|
72
|
-
domain =
|
|
73
|
+
domain = Page.domain(title)
|
|
73
74
|
items = sections.map do |section|
|
|
74
75
|
heading = section[:heading]
|
|
75
76
|
%(<li><a href="##{slugs[heading]}">#{Html.inline(heading.text)}</a></li>)
|
|
@@ -77,13 +78,17 @@ module Hecks
|
|
|
77
78
|
fill(RAIL, "DOMAIN" => Html.escape(domain), "ITEMS" => items.join("\n"))
|
|
78
79
|
end
|
|
79
80
|
|
|
81
|
+
# @param title [String] the document's title, such as "Pizzas glossary"
|
|
82
|
+
# @return [String] the domain's name alone
|
|
83
|
+
def domain(title) = title.delete_suffix(" glossary")
|
|
84
|
+
|
|
80
85
|
def front_matter(blocks)
|
|
81
86
|
["<header>", *blocks.filter_map { |block| front_part(block) }, "</header>"].join("\n")
|
|
82
87
|
end
|
|
83
88
|
|
|
84
89
|
def front_part(block)
|
|
85
90
|
case block.type
|
|
86
|
-
when :heading then "<h1>#{Html.inline(block.text
|
|
91
|
+
when :heading then "<h1>#{Html.inline(Page.domain(block.text))}</h1>"
|
|
87
92
|
when :quote then "<p class=\"vision\">#{Html.inline(block.text)}</p>"
|
|
88
93
|
when :paragraph then "<p class=\"lede\">#{Html.inline(block.text)}</p>"
|
|
89
94
|
when :mermaid then Html.figure(block, "How it all fits together")
|
|
@@ -25,7 +25,7 @@ module Hecks
|
|
|
25
25
|
|
|
26
26
|
case line
|
|
27
27
|
when /\A```mermaid/ then fence(lines, index)
|
|
28
|
-
when /\A(\#{1,
|
|
28
|
+
when /\A(\#{1,4}) (.+)\z/ then [heading(Regexp.last_match), index + 1]
|
|
29
29
|
when /\A> / then [Block.new(type: :quote, text: line[2..]), index + 1]
|
|
30
30
|
when /\A- / then list(lines, index)
|
|
31
31
|
else paragraph(lines, index)
|
|
@@ -4,6 +4,11 @@ module Hecks
|
|
|
4
4
|
module Html
|
|
5
5
|
# Groups parsed blocks into the page's sections and renders each section's terms.
|
|
6
6
|
module Sections
|
|
7
|
+
# Paragraph openings that mark a rule, and the class that sets each apart on the page.
|
|
8
|
+
LEADS = {
|
|
9
|
+
"Always true:" => "rule", "Allowed only when:" => "rule", "Afterwards, always:" => "rule"
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
7
12
|
module_function
|
|
8
13
|
|
|
9
14
|
# GitHub's slugs for these headings, so links the Markdown carries land here too.
|
|
@@ -26,14 +31,14 @@ module Hecks
|
|
|
26
31
|
sections
|
|
27
32
|
end
|
|
28
33
|
|
|
29
|
-
# Each `###` opens
|
|
30
|
-
# captions whatever figure or list follows it.
|
|
34
|
+
# Each `###` opens a group of terms and each `####` a term running to the next. A
|
|
35
|
+
# bold-only paragraph captions whatever figure or list follows it.
|
|
31
36
|
def section_html(section, slugs)
|
|
32
37
|
heading = section[:heading]
|
|
33
38
|
state = { parts: [%(<section id="#{slugs[heading]}">), "<h2>#{Html.inline(heading.text)}</h2>"],
|
|
34
|
-
caption: nil,
|
|
39
|
+
caption: nil, open: nil }
|
|
35
40
|
section[:blocks].drop(1).each { |block| section_block(block, state, slugs) }
|
|
36
|
-
state
|
|
41
|
+
close(state, nil)
|
|
37
42
|
state[:parts] << "</section>"
|
|
38
43
|
state[:parts].join("\n")
|
|
39
44
|
end
|
|
@@ -41,7 +46,7 @@ module Hecks
|
|
|
41
46
|
# Renders one block into `state[:parts]`, mutating `state`.
|
|
42
47
|
def section_block(block, state, slugs)
|
|
43
48
|
case block.type
|
|
44
|
-
when :heading then open_term(block, state, slugs)
|
|
49
|
+
when :heading then block.level == 3 ? open_group(block, state, slugs) : open_term(block, state, slugs)
|
|
45
50
|
when :mermaid then state[:parts] << Html.figure(block, state.delete(:caption))
|
|
46
51
|
when :list then list(block, state)
|
|
47
52
|
when :quote then state[:parts] << "<p class=\"about\">#{Html.inline(block.text)}</p>"
|
|
@@ -49,11 +54,26 @@ module Hecks
|
|
|
49
54
|
end
|
|
50
55
|
end
|
|
51
56
|
|
|
57
|
+
def open_group(block, state, slugs)
|
|
58
|
+
close(state, nil)
|
|
59
|
+
state[:parts] << %(<h3 class="group" id="#{slugs[block]}">#{Html.inline(block.text)}</h3>)
|
|
60
|
+
end
|
|
61
|
+
|
|
52
62
|
def open_term(block, state, slugs)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
parts << %(<article class="term" id="#{slugs[block]}">) << "<
|
|
56
|
-
state[:
|
|
63
|
+
close(state, :terms)
|
|
64
|
+
state[:parts] << "<div class=\"terms\">" unless state[:open]
|
|
65
|
+
state[:parts] << %(<article class="term" id="#{slugs[block]}">) << "<h4>#{Html.inline(block.text)}</h4>"
|
|
66
|
+
state[:open] = :article
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Closes the open article, and the terms around it unless `keep` is `:terms`.
|
|
70
|
+
def close(state, keep)
|
|
71
|
+
state[:parts] << "</article>" if state[:open] == :article
|
|
72
|
+
state[:open] = :terms if state[:open]
|
|
73
|
+
return if keep == :terms
|
|
74
|
+
|
|
75
|
+
state[:parts] << "</div>" if state[:open]
|
|
76
|
+
state[:open] = nil
|
|
57
77
|
end
|
|
58
78
|
|
|
59
79
|
def list(block, state)
|
|
@@ -67,8 +87,8 @@ module Hecks
|
|
|
67
87
|
if text.match?(/\A\*\*[^*]+\*\*\z/)
|
|
68
88
|
state[:caption] = text.delete("*")
|
|
69
89
|
else
|
|
70
|
-
klass = text.start_with?(
|
|
71
|
-
state[:parts] << "<p#{klass}>#{Html.inline(text)}</p>"
|
|
90
|
+
klass = LEADS.find { |lead, _class| text.start_with?(lead) }&.last
|
|
91
|
+
state[:parts] << "<p#{%( class="#{klass}") if klass}>#{Html.inline(text)}</p>"
|
|
72
92
|
end
|
|
73
93
|
end
|
|
74
94
|
end
|
|
@@ -11,8 +11,7 @@ module Hecks
|
|
|
11
11
|
module Html
|
|
12
12
|
Block = Struct.new(:type, :text, :items, :level, keyword_init: true)
|
|
13
13
|
|
|
14
|
-
FONTS = "https://fonts.googleapis.com/css2?family=
|
|
15
|
-
"0,6..72,600;1,6..72,400&family=Instrument+Sans:wght@400;500;600&display=swap".freeze
|
|
14
|
+
FONTS = "https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600;700&display=swap".freeze
|
|
16
15
|
MERMAID = "https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.1/mermaid.min.js".freeze
|
|
17
16
|
|
|
18
17
|
module_function
|
|
@@ -10,21 +10,22 @@ module Hecks
|
|
|
10
10
|
# Renders `glossary.md`, the source the glossary page is built from.
|
|
11
11
|
# Uses only Markdown that GitHub renders as-is, so the file reads on its own.
|
|
12
12
|
module Markdown
|
|
13
|
-
LEDE = "
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
13
|
+
LEDE = "The terms %<name>s uses, written the way the people who work in it would say them. " \
|
|
14
|
+
"Each term sits under the thing it belongs to: first what can be done with it, then " \
|
|
15
|
+
"what it records, the lists you can ask for, and what it is made of. The page is " \
|
|
16
|
+
"generated from the working specification, so it describes what the system does today. " \
|
|
17
|
+
"If a sentence reads wrong to you, the specification is wrong, and it is worth saying so.".freeze
|
|
17
18
|
|
|
18
19
|
STANDING = {
|
|
19
|
-
ROLES => "Who does what.
|
|
20
|
-
READ_MODELS => "Questions answered
|
|
21
|
-
REACTIONS => "What happens on its own
|
|
20
|
+
ROLES => "Who does what. Each role is listed once, with the actions it is responsible for.",
|
|
21
|
+
READ_MODELS => "Questions answered by reading more than one of the things above.",
|
|
22
|
+
REACTIONS => "What happens on its own when something arrives from outside this specification."
|
|
22
23
|
}.freeze
|
|
23
24
|
|
|
24
25
|
module_function
|
|
25
26
|
|
|
26
27
|
# Titles the document after its chapter.
|
|
27
|
-
def title(bluebook) = "#{bluebook.name}
|
|
28
|
+
def title(bluebook) = "#{bluebook.name} glossary"
|
|
28
29
|
|
|
29
30
|
# Renders the full `glossary.md` document.
|
|
30
31
|
#
|
|
@@ -41,13 +42,15 @@ module Hecks
|
|
|
41
42
|
parts = ["# #{title(bluebook)}"]
|
|
42
43
|
parts << "> #{bluebook.vision}" if bluebook.vision
|
|
43
44
|
parts << format(LEDE, name: bluebook.name)
|
|
44
|
-
parts << fence(Mermaid.map(bluebook))
|
|
45
|
+
parts << fence(Mermaid.map(bluebook)) if bluebook.aggregates.size > 1
|
|
45
46
|
parts.join("\n\n")
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
# Renders one `##` section: its opening, then every term inside it.
|
|
49
50
|
def section_text(section, document)
|
|
50
|
-
terms = section.
|
|
51
|
+
terms = Groups.walk(section).map do |kind, item|
|
|
52
|
+
kind == :group ? "### #{item}" : term_text(item, document.index)
|
|
53
|
+
end
|
|
51
54
|
["## #{section.title}", *section_opening(section, document), *terms].join("\n\n")
|
|
52
55
|
end
|
|
53
56
|
|
|
@@ -67,11 +70,19 @@ module Hecks
|
|
|
67
70
|
parts + diagrams(aggregate, bluebook) + facts(always_true(aggregate, bluebook), marked)
|
|
68
71
|
end
|
|
69
72
|
|
|
70
|
-
# The "How it fits" diagram, and "How it moves"
|
|
73
|
+
# The "How it fits" diagram when the aggregate has neighbours to draw, and "How it moves"
|
|
74
|
+
# with each move in words when it has a lifecycle.
|
|
71
75
|
def diagrams(aggregate, bluebook)
|
|
72
|
-
parts = [
|
|
73
|
-
parts << "**How it
|
|
74
|
-
parts
|
|
76
|
+
parts = []
|
|
77
|
+
parts << "**How it fits**" << fence(Mermaid.context(aggregate, bluebook)) if Mermaid.neighbours?(aggregate, bluebook)
|
|
78
|
+
parts + moves(aggregate)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def moves(aggregate)
|
|
82
|
+
return [] unless aggregate.lifecycle
|
|
83
|
+
|
|
84
|
+
lines = Statements::Actions.lifecycle(aggregate).drop(1).map { |sentence| "- #{sentence}" }
|
|
85
|
+
["**How it moves**", fence(Mermaid.lifecycle(aggregate)), lines.join("\n")]
|
|
75
86
|
end
|
|
76
87
|
|
|
77
88
|
# The "Always true" and "Handled as sensitive" lists, each only when it has lines.
|
|
@@ -85,25 +96,17 @@ module Hecks
|
|
|
85
96
|
parts
|
|
86
97
|
end
|
|
87
98
|
|
|
88
|
-
# The statements projection's sentences for `aggregate` and
|
|
89
|
-
#
|
|
90
|
-
|
|
91
|
-
def always_true(aggregate, bluebook)
|
|
92
|
-
names = bluebook.aggregates.flat_map { |other| [other.hecks_name, *other.entities.map(&:hecks_name)] }
|
|
99
|
+
# The statements projection's relationship and invariant sentences for `aggregate` and
|
|
100
|
+
# its entities, each said once.
|
|
101
|
+
def always_true(aggregate, _bluebook)
|
|
93
102
|
[aggregate, *aggregate.entities].flat_map do |holder|
|
|
94
|
-
Statements.attribute_statements(holder)
|
|
95
|
-
Statements.invariant_statements(holder)
|
|
103
|
+
Statements.attribute_statements(holder) + Statements.invariant_statements(holder)
|
|
96
104
|
end.uniq
|
|
97
105
|
end
|
|
98
106
|
|
|
99
|
-
#
|
|
100
|
-
def spoken(sentence, names)
|
|
101
|
-
names.reduce(sentence) { |text, name| text.gsub(/\b#{Regexp.escape(name)}\b/, Naming.words(name)) }
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# Renders one term's `###` heading and its paragraphs.
|
|
107
|
+
# Renders one term's `####` heading and its paragraphs.
|
|
105
108
|
def term_text(entry, index)
|
|
106
|
-
["
|
|
109
|
+
["#### #{entry.headword}", *Sentences.paragraphs(entry, index)].join("\n\n")
|
|
107
110
|
end
|
|
108
111
|
|
|
109
112
|
# Wraps a Mermaid diagram's source in a fenced code block.
|
|
@@ -35,6 +35,9 @@ module Hecks
|
|
|
35
35
|
(lines << " classDef focus stroke-width:3px").uniq.join("\n")
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
# @return [Boolean] whether the aggregate has anything around it worth drawing
|
|
39
|
+
def neighbours?(aggregate, bluebook) = !neighbours(aggregate, bluebook).empty?
|
|
40
|
+
|
|
38
41
|
# The nodes and edges for the aggregate's entities, what it references, and what
|
|
39
42
|
# references it.
|
|
40
43
|
def neighbours(aggregate, bluebook)
|