hecks 1.3.0 → 1.5.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/lib/hecks/adapters/driven/claude_code.rb +72 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +83 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
- data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
- data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
- data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
- data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +106 -9
- data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
- data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
- data/lib/hecks/adapters/driven/lambda.rb +82 -35
- data/lib/hecks/adapters/driven/local_storage.rb +83 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -1
- data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
- data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
- data/lib/hecks/adapters/driven/postgres.rb +175 -28
- data/lib/hecks/adapters/driven/prism.rb +50 -11
- data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
- data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
- data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
- data/lib/hecks/adapters/driven/sqlite.rb +181 -21
- data/lib/hecks/adapters/driven/system_clock.rb +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
- data/lib/hecks/adapters/driven.rb +6 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
- data/lib/hecks/behaviors/dsl.rb +60 -2
- data/lib/hecks/behaviors/expectations.rb +190 -29
- data/lib/hecks/behaviors/ir.rb +12 -1
- data/lib/hecks/behaviors/rspec.rb +9 -1
- data/lib/hecks/behaviors/runner.rb +21 -2
- data/lib/hecks/behaviors.rb +9 -1
- data/lib/hecks/bluebook/aggregate.rb +43 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
- data/lib/hecks/bluebook/assembly/build.rb +26 -5
- data/lib/hecks/bluebook/assembly/contract.rb +98 -23
- data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
- data/lib/hecks/bluebook/assembly/marks.rb +159 -30
- data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
- data/lib/hecks/bluebook/assembly.rb +32 -14
- data/lib/hecks/bluebook/attribute.rb +26 -12
- data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
- data/lib/hecks/bluebook/behaviour/command.rb +55 -25
- data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
- data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
- data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
- data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
- data/lib/hecks/bluebook/behaviour/query.rb +6 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +51 -9
- data/lib/hecks/bluebook/command.rb +62 -17
- data/lib/hecks/bluebook/domain_port.rb +34 -9
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +282 -123
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
- data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
- data/lib/hecks/bluebook/dsl/entity_builder.rb +191 -61
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
- data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +111 -47
- data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
- data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
- data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
- data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
- data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
- data/lib/hecks/bluebook/entity.rb +40 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
- data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
- data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
- data/lib/hecks/bluebook/expression/resolver.rb +369 -128
- data/lib/hecks/bluebook/hexagon.rb +35 -1
- data/lib/hecks/bluebook/lifecycle.rb +12 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
- data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
- data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
- data/lib/hecks/bluebook/meta_validator.rb +235 -139
- data/lib/hecks/bluebook/model_check.rb +434 -104
- data/lib/hecks/bluebook/pattern_subset.rb +32 -10
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +27 -14
- data/lib/hecks/bluebook/project_discovery.rb +5 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +50 -6
- data/lib/hecks/bluebook/query.rb +31 -4
- data/lib/hecks/bluebook/read_model.rb +35 -15
- data/lib/hecks/bluebook/reference.rb +26 -13
- data/lib/hecks/bluebook/smoke_test.rb +46 -23
- data/lib/hecks/bluebook/synthesizer.rb +46 -12
- data/lib/hecks/bluebook/translation.rb +34 -5
- data/lib/hecks/bluebook/value_object.rb +29 -11
- data/lib/hecks/bluebook.rb +5 -6
- data/lib/hecks/codemod.rb +138 -50
- data/lib/hecks/construct.rb +21 -7
- data/lib/hecks/corpus.rb +438 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/doc/reference.rb +200 -31
- data/lib/hecks/embryonaut_bluebook.rb +38 -15
- data/lib/hecks/facade/cli_door.rb +69 -10
- data/lib/hecks/facade/cli_runner.rb +105 -24
- data/lib/hecks/facade/command_request.rb +23 -0
- data/lib/hecks/facade/handle.rb +155 -35
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +50 -27
- data/lib/hecks/facade/surface/chapter.rb +26 -17
- data/lib/hecks/facade/surface.rb +16 -3
- data/lib/hecks/facade.rb +15 -4
- data/lib/hecks/forms/app.rb +46 -30
- data/lib/hecks/forms/command_form_renderer.rb +70 -9
- data/lib/hecks/forms/field_renderer.rb +142 -6
- data/lib/hecks/forms/field_shape.rb +183 -19
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/page.rb +14 -0
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +14 -2
- data/lib/hecks/forms/query_form_renderer.rb +65 -2
- data/lib/hecks/forms/record_renderer.rb +60 -2
- data/lib/hecks/forms/record_table.rb +28 -1
- data/lib/hecks/forms/reference_options.rb +24 -0
- data/lib/hecks/forms/value_object_shape.rb +13 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +59 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
- data/lib/hecks/framework/oidc.json +15 -0
- data/lib/hecks/framework.rb +79 -25
- data/lib/hecks/freezer.rb +27 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
- data/lib/hecks/fuzzing/combination_miner.rb +178 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
- data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
- data/lib/hecks/fuzzing/differential.rb +192 -0
- data/lib/hecks/fuzzing/domain_generator.rb +871 -0
- data/lib/hecks/fuzzing/era_boundary.rb +68 -19
- data/lib/hecks/fuzzing/form_census.rb +121 -20
- data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
- data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
- data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
- data/lib/hecks/fuzzing/properties/guards.rb +86 -42
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
- data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
- data/lib/hecks/fuzzing/properties/querying.rb +82 -28
- data/lib/hecks/fuzzing/properties.rb +84 -51
- data/lib/hecks/fuzzing/qa_settings.rb +164 -0
- data/lib/hecks/fuzzing/replay.rb +241 -133
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
- data/lib/hecks/fuzzing/self_consistency.rb +315 -137
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +59 -32
- data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
- data/lib/hecks/fuzzing/shrinker.rb +309 -0
- data/lib/hecks/fuzzing/structural_skips.rb +37 -130
- data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
- data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
- data/lib/hecks/fuzzing/value_generator.rb +110 -22
- data/lib/hecks/fuzzing.rb +5 -0
- data/lib/hecks/grammar/evolve.rb +188 -12
- data/lib/hecks/grammar.rb +53 -7
- data/lib/hecks/ir.rb +51 -20
- data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
- data/lib/hecks/language/bluebook/policy.bluebook +11 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +41 -9
- data/lib/hecks/naming.rb +112 -31
- data/lib/hecks/ports/access_control.rb +53 -2
- data/lib/hecks/ports/agent/answers.rb +83 -6
- data/lib/hecks/ports/agent.rb +119 -35
- data/lib/hecks/ports/authentication.rb +44 -4
- data/lib/hecks/ports/authorization.rb +50 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +23 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +17 -1
- data/lib/hecks/ports/key_vault.port +6 -0
- data/lib/hecks/ports/key_vault.rb +58 -0
- data/lib/hecks/ports/loading.rb +4 -0
- data/lib/hecks/ports/persistence/append_only.rb +172 -8
- data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
- data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
- data/lib/hecks/ports/persistence/execution.rb +4 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
- data/lib/hecks/ports/persistence/plugin.rb +42 -4
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +34 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +95 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +48 -8
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +58 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
- data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
- data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
- data/lib/hecks/ports/persistence/state_codec.rb +319 -0
- data/lib/hecks/ports/persistence.rb +36 -1
- data/lib/hecks/ports/projection.rb +61 -7
- data/lib/hecks/ports/query/in_memory.rb +41 -8
- data/lib/hecks/ports/query/ordering.rb +21 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projections/bootstrap_table.rb +147 -0
- data/lib/hecks/projections/diagrams.rb +315 -79
- data/lib/hecks/projections/glossary/html.rb +91 -3
- data/lib/hecks/projections/glossary/markdown.rb +50 -3
- data/lib/hecks/projections/glossary/mermaid.rb +49 -1
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sentences.rb +110 -5
- data/lib/hecks/projections/glossary.rb +173 -31
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +62 -17
- data/lib/hecks/projections/model.rb +75 -21
- data/lib/hecks/projections/oidc.rb +45 -8
- data/lib/hecks/projections/parser_table.rb +53 -12
- data/lib/hecks/projections/reference.rb +15 -3
- data/lib/hecks/projections/rust_vocabulary.rb +646 -0
- data/lib/hecks/projections/shape.rb +10 -2
- data/lib/hecks/projections/statements.rb +69 -22
- data/lib/hecks/projections/vocabulary.rb +26 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +245 -37
- data/lib/hecks/projector/docs_projector.rb +154 -28
- data/lib/hecks/projector/exporter.rb +104 -29
- data/lib/hecks/projector/ir_projector.rb +7 -1
- data/lib/hecks/projector/narrate_projector.rb +144 -23
- data/lib/hecks/projector/target.rb +42 -18
- data/lib/hecks/projector.rb +86 -18
- data/lib/hecks/query_ir.rb +94 -47
- data/lib/hecks/query_specification/common/comparators.rb +19 -3
- data/lib/hecks/query_specification/common/comparison.rb +109 -27
- data/lib/hecks/query_specification/common/dsl.rb +65 -9
- data/lib/hecks/query_specification/common/null_policy.rb +61 -15
- data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
- data/lib/hecks/query_specification/common/options.rb +25 -0
- data/lib/hecks/query_specification/field_path.rb +69 -15
- data/lib/hecks/query_specification/hop_path.rb +57 -20
- data/lib/hecks/query_specification/read_model/specification.rb +4 -0
- data/lib/hecks/rendering.rb +9 -3
- data/lib/hecks/router/namespace_installer.rb +16 -3
- data/lib/hecks/router.rb +56 -1
- data/lib/hecks/runtime/aggregate_lock.rb +20 -11
- data/lib/hecks/runtime/boot_gates.rb +21 -3
- data/lib/hecks/runtime/caller.rb +40 -8
- data/lib/hecks/runtime/capability_graph.rb +13 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
- data/lib/hecks/runtime/command_interpreter.rb +195 -113
- data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
- data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
- data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
- data/lib/hecks/runtime/command_rules/emission.rb +18 -1
- data/lib/hecks/runtime/command_rules/references.rb +145 -55
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +56 -11
- data/lib/hecks/runtime/dispatcher.rb +263 -92
- data/lib/hecks/runtime/entity_element.rb +304 -67
- data/lib/hecks/runtime/entity_interpreter.rb +149 -88
- data/lib/hecks/runtime/errors.rb +37 -23
- data/lib/hecks/runtime/event.rb +15 -10
- data/lib/hecks/runtime/identity.rb +91 -23
- data/lib/hecks/runtime/instance.rb +103 -18
- data/lib/hecks/runtime/interpreting.rb +24 -16
- data/lib/hecks/runtime/invocation.rb +358 -0
- data/lib/hecks/runtime/loader.rb +105 -19
- data/lib/hecks/runtime/outbox.rb +164 -26
- data/lib/hecks/runtime/policy_interpreter.rb +73 -60
- data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
- data/lib/hecks/runtime/query_interpreter.rb +93 -74
- data/lib/hecks/runtime/reaction_invocation.rb +73 -28
- data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
- data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
- data/lib/hecks/runtime/reference_hop.rb +48 -6
- data/lib/hecks/runtime/refusal_wording.rb +142 -115
- data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
- data/lib/hecks/runtime/registry/verification.rb +153 -28
- data/lib/hecks/runtime/registry.rb +202 -32
- data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
- data/lib/hecks/runtime/routing.rb +36 -154
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
- data/lib/hecks/runtime/saga_interpreter.rb +90 -76
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +33 -13
- data/lib/hecks/runtime/tenant_scope.rb +23 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +555 -142
- data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
- data/lib/hecks/runtime/value.rb +71 -21
- data/lib/hecks/runtime.rb +39 -7
- data/lib/hecks/storehouse.rb +368 -72
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +205 -4
- data/lib/hecks.rb +91 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
- metadata +28 -2
data/lib/hecks/doc/reference.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Hecks
|
|
|
6
6
|
# The DSL reference, projected from the language's own Syntax chapter
|
|
7
7
|
# — the same Keyword/Argument rows the conformance specs hold equal
|
|
8
8
|
# to the live builders. Nothing here is described twice: the tables
|
|
9
|
-
# come from the declaration, the
|
|
9
|
+
# come from the declaration, the prose is hand-written between
|
|
10
10
|
# markers the generator preserves, and the golden spec refuses a
|
|
11
11
|
# tree where the two have drifted.
|
|
12
12
|
#
|
|
@@ -19,9 +19,9 @@ module Hecks
|
|
|
19
19
|
GENERATED_END = "<!-- generated:end -->".freeze
|
|
20
20
|
TODO_SENTINEL = "<!-- TODO: document this word -->".freeze
|
|
21
21
|
|
|
22
|
-
# A
|
|
22
|
+
# A page's own hand-written opening, harvested under a key no word
|
|
23
23
|
# can ever collide with (words are strings off the Syntax chapter;
|
|
24
|
-
# this is a Symbol). It exists so a page can boot
|
|
24
|
+
# this is a Symbol). It exists so a page can boot once — load a real
|
|
25
25
|
# corpus chapter, wire its hexagon — and have every word's example
|
|
26
26
|
# below run against that single boot, the way a guide's opening
|
|
27
27
|
# `ruby boot` block already does. Without it each word would have to
|
|
@@ -31,18 +31,36 @@ module Hecks
|
|
|
31
31
|
|
|
32
32
|
module_function
|
|
33
33
|
|
|
34
|
+
# The marker opening one word's generated region.
|
|
35
|
+
#
|
|
36
|
+
# @param word [String, Symbol, #to_s] the word this section documents
|
|
37
|
+
# @return [String] the HTML comment marking that word's generated region open
|
|
34
38
|
def generated_begin(word) = "<!-- generated:begin word=#{word} -->"
|
|
35
39
|
|
|
36
|
-
# Keyed by
|
|
40
|
+
# Keyed by region rather than by word — the same marker convention,
|
|
37
41
|
# used for the parts of a page that are not about one word: a
|
|
38
42
|
# page's generated lede here, README's generated indexes below.
|
|
43
|
+
#
|
|
44
|
+
# @param id [String, Symbol, #to_s] the region's id, such as `"page"` or `"tools"`
|
|
45
|
+
# @return [String] the HTML comment marking that region's generated region open
|
|
39
46
|
def region_begin(id) = "<!-- generated:begin id=#{id} -->"
|
|
40
47
|
|
|
48
|
+
# The language's own Syntax aggregate, read off the judged grammar chapter.
|
|
49
|
+
#
|
|
50
|
+
# @return [Bluebook::Aggregate, nil] the Syntax aggregate, or nil if the
|
|
51
|
+
# grammar's Bluebook chapter declares none by that name
|
|
41
52
|
def syntax
|
|
42
53
|
meta = Bluebook::MetaValidator.grammar_registry.bluebook("Bluebook")
|
|
43
54
|
meta.aggregates.find { |aggregate| aggregate.hecks_name == "Syntax" }
|
|
44
55
|
end
|
|
45
56
|
|
|
57
|
+
# Reads one closed-set value object's declared members off the Syntax
|
|
58
|
+
# aggregate, as string-valued Hashes.
|
|
59
|
+
#
|
|
60
|
+
# @param name [String, Symbol, #to_s] the value object's `hecks_name`,
|
|
61
|
+
# such as `"Keyword"`
|
|
62
|
+
# @return [Array<Hash{Symbol => String}>] each declared member's fields,
|
|
63
|
+
# values stringified
|
|
46
64
|
def rows(name)
|
|
47
65
|
syntax.value_objects.find { |vo| vo.hecks_name == name }
|
|
48
66
|
.members.map { |row| row.to_h.transform_values(&:to_s) }
|
|
@@ -53,31 +71,60 @@ module Hecks
|
|
|
53
71
|
# really is a lifecycle. `SyntaxBoot.call` discovers the static
|
|
54
72
|
# aggregate-local seed rows (`KeywordSeed`/`ArgumentSeed`), dispatches each
|
|
55
73
|
# one through the real admission/lifecycle door, and hands back the
|
|
56
|
-
# same shape `rows
|
|
57
|
-
# change.
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
74
|
+
# same shape `rows`, above, already produces — nothing below this
|
|
75
|
+
# needed to change.
|
|
76
|
+
#
|
|
77
|
+
# This module holds no separate `@keywords ||=` memo of its own —
|
|
78
|
+
# delegates straight through to `SyntaxBoot.call`'s own cache
|
|
79
|
+
# (keyed on the grammar registry's own chapter set — see its
|
|
80
|
+
# comment) rather than shadowing it. A second, local cache on top
|
|
81
|
+
# of that one would double-cache with no way to invalidate either
|
|
82
|
+
# half: whichever call happened to land first would lock in
|
|
83
|
+
# forever, even one caught mid-build missing every Paging-attached
|
|
84
|
+
# word (limit/offset/cursor/nulls).
|
|
85
|
+
#
|
|
86
|
+
# @return [Array<Hash{Symbol => String}>] every declared Keyword row
|
|
68
87
|
def keywords = Bluebook::MetaValidator::SyntaxBoot.call[:keywords]
|
|
88
|
+
|
|
89
|
+
# Every declared Argument row.
|
|
90
|
+
#
|
|
91
|
+
# @return [Array<Hash{Symbol => String}>] every declared Argument row
|
|
69
92
|
def arguments = Bluebook::MetaValidator::SyntaxBoot.call[:arguments]
|
|
70
93
|
|
|
94
|
+
# Reads a row's declared status, defaulting when it declared none.
|
|
95
|
+
#
|
|
96
|
+
# @param row [Hash{Symbol => String}] a Keyword or Argument row
|
|
97
|
+
# @return [String] the row's declared status, defaulting to `"admitted"`
|
|
98
|
+
# when it declared none
|
|
71
99
|
def status_of(row) = row[:status].to_s.empty? ? "admitted" : row[:status].to_s
|
|
100
|
+
|
|
101
|
+
# Whether a row is still current enough to appear in the reference.
|
|
102
|
+
#
|
|
103
|
+
# @param row [Hash{Symbol => String}] a Keyword or Argument row
|
|
104
|
+
# @return [Boolean] true if the row's status is `"admitted"` or `"deprecated"`
|
|
72
105
|
def live?(row) = %w[admitted deprecated].include?(status_of(row))
|
|
73
106
|
|
|
107
|
+
# Every distinct context a keyword is declared in.
|
|
108
|
+
#
|
|
109
|
+
# @return [Array<String>] every distinct context a keyword is declared in
|
|
74
110
|
def contexts = keywords.map { |row| row[:context] }.uniq
|
|
75
111
|
|
|
112
|
+
# Derives a context's reference page filename.
|
|
113
|
+
#
|
|
114
|
+
# @param context [String, Symbol, #to_s] a context name, such as `"File"`
|
|
115
|
+
# @return [String] the reference page's filename for that context
|
|
76
116
|
def page_name(context) = "#{Naming.snake(context)}.md"
|
|
77
117
|
|
|
78
118
|
# Every reference page, rendered fresh — prose carried over from
|
|
79
119
|
# the committed pages, new words seeded with the sentinel, orphaned
|
|
80
120
|
# prose refused.
|
|
121
|
+
#
|
|
122
|
+
# @param directory [String] path to the directory holding the committed
|
|
123
|
+
# reference pages, read for their hand-written prose
|
|
124
|
+
# @return [Hash{String => String}] every page's filename (plus `"index.md"`)
|
|
125
|
+
# mapped to its freshly rendered Markdown content
|
|
126
|
+
# @raise [RuntimeError] if a committed page carries prose for a word the
|
|
127
|
+
# language no longer declares in that context
|
|
81
128
|
def pages(directory)
|
|
82
129
|
contexts.each_with_object({}) do |context, pages|
|
|
83
130
|
path = File.join(directory, page_name(context))
|
|
@@ -86,13 +133,22 @@ module Hecks
|
|
|
86
133
|
end.merge("index.md" => render_index)
|
|
87
134
|
end
|
|
88
135
|
|
|
89
|
-
# A
|
|
136
|
+
# A word admitting two forms has two rows — syntax.bluebook's own
|
|
90
137
|
# stated rule, and `identified_by` (a block, or a bare argument and
|
|
91
|
-
# none) is the case that made it real again. One
|
|
138
|
+
# none) is the case that made it real again. One section per word all
|
|
92
139
|
# the same: the prose is the word's rather than the form's, and the
|
|
93
140
|
# argument rows join by (word, context) and so already cover every
|
|
94
141
|
# form. Grouped rather than rendered per row, or a reader would meet
|
|
95
142
|
# the same heading and the same paragraph twice.
|
|
143
|
+
#
|
|
144
|
+
# @param context [String] the context name, such as `"File"`
|
|
145
|
+
# @param prose [Hash{String, Symbol => String}] hand-written prose harvested
|
|
146
|
+
# from the committed page, keyed by word (or `PREAMBLE` for the page's lede)
|
|
147
|
+
# @param path [String] the page's file path, used only in the orphan-refusal
|
|
148
|
+
# message below
|
|
149
|
+
# @return [String] the page's full rendered Markdown
|
|
150
|
+
# @raise [RuntimeError] if `prose` carries a key for a word the language no
|
|
151
|
+
# longer declares in `context`
|
|
96
152
|
def render_page(context, prose, path)
|
|
97
153
|
words = keywords.select { |row| row[:context] == context }.group_by { |row| row[:word] }
|
|
98
154
|
orphans = prose.keys - words.keys - [PREAMBLE]
|
|
@@ -119,6 +175,10 @@ module Hecks
|
|
|
119
175
|
PAGE
|
|
120
176
|
end
|
|
121
177
|
|
|
178
|
+
# The one-line description of where a context's words are typed.
|
|
179
|
+
#
|
|
180
|
+
# @param context [String] the context name, such as `"File"` or `"Command"`
|
|
181
|
+
# @return [String] a sentence naming where words in this context are typed
|
|
122
182
|
def context_lede(context)
|
|
123
183
|
openers = keywords.select { |row| row[:opens] == context }
|
|
124
184
|
return "Words available at the top of a file." if context == "File"
|
|
@@ -128,9 +188,15 @@ module Hecks
|
|
|
128
188
|
inside.empty? ? "Words available in the #{context} body." : "Words available inside #{inside}."
|
|
129
189
|
end
|
|
130
190
|
|
|
131
|
-
# One
|
|
191
|
+
# One spelling per form, everything else off the first row — the
|
|
132
192
|
# columns that differ between two forms of one word are `body` (which
|
|
133
193
|
# is what the spelling shows) and nothing else.
|
|
194
|
+
#
|
|
195
|
+
# @param forms [Array<Hash{Symbol => String}>] one word's Keyword rows, one
|
|
196
|
+
# per admitted form
|
|
197
|
+
# @param prose [String, nil] the word's hand-written prose, or nil if none
|
|
198
|
+
# was harvested
|
|
199
|
+
# @return [String] the word's rendered section, generated table plus prose
|
|
134
200
|
def render_word(forms, prose)
|
|
135
201
|
row = forms.first
|
|
136
202
|
table = argument_table(row)
|
|
@@ -152,15 +218,30 @@ module Hecks
|
|
|
152
218
|
WORD
|
|
153
219
|
end
|
|
154
220
|
|
|
221
|
+
# Falls back to the TODO sentinel when a word has no prose yet.
|
|
222
|
+
#
|
|
223
|
+
# @param prose [String, nil] a word's hand-written prose, or nil if none
|
|
224
|
+
# was harvested
|
|
225
|
+
# @return [String] `prose` stripped, or the TODO sentinel if it is blank
|
|
155
226
|
def prose_or_sentinel(prose)
|
|
156
227
|
text = prose.to_s.strip
|
|
157
228
|
text.empty? ? TODO_SENTINEL : text
|
|
158
229
|
end
|
|
159
230
|
|
|
231
|
+
# Finds the Argument rows declared for one Keyword row's word.
|
|
232
|
+
#
|
|
233
|
+
# @param row [Hash{Symbol => String}] a Keyword row
|
|
234
|
+
# @return [Array<Hash{Symbol => String}>] the Argument rows declared for
|
|
235
|
+
# this row's word, in this row's context
|
|
160
236
|
def word_arguments(row)
|
|
161
237
|
arguments.select { |arg| arg[:keyword] == row[:word] && arg[:context] == row[:context] }
|
|
162
238
|
end
|
|
163
239
|
|
|
240
|
+
# Builds the call spelling shown for one word.
|
|
241
|
+
#
|
|
242
|
+
# @param row [Hash{Symbol => String}] a Keyword row
|
|
243
|
+
# @return [String] the word's call spelling, positional arguments then
|
|
244
|
+
# named ones, with a trailing `do ... end` unless its body is `"none"`
|
|
164
245
|
def signature(row)
|
|
165
246
|
positional = word_arguments(row).reject { |arg| arg[:at].to_s.empty? }
|
|
166
247
|
.sort_by { |arg| arg[:at].to_i }
|
|
@@ -172,6 +253,11 @@ module Hecks
|
|
|
172
253
|
row[:body].to_s == "none" ? base : "#{base} do ... end"
|
|
173
254
|
end
|
|
174
255
|
|
|
256
|
+
# Renders one word's arguments as a Markdown table.
|
|
257
|
+
#
|
|
258
|
+
# @param row [Hash{Symbol => String}] a Keyword row
|
|
259
|
+
# @return [String] a Markdown table of the row's arguments, or `""` if it
|
|
260
|
+
# declares none
|
|
175
261
|
def argument_table(row)
|
|
176
262
|
args = word_arguments(row)
|
|
177
263
|
return "" if args.empty?
|
|
@@ -184,6 +270,10 @@ module Hecks
|
|
|
184
270
|
"#{lines.join("\n")}\n"
|
|
185
271
|
end
|
|
186
272
|
|
|
273
|
+
# Renders the reference index page.
|
|
274
|
+
#
|
|
275
|
+
# @return [String] the reference index page's rendered Markdown, one
|
|
276
|
+
# linked entry per context
|
|
187
277
|
def render_index
|
|
188
278
|
listed = contexts.map do |context|
|
|
189
279
|
count = keywords.select { |row| row[:context] == context }.map { |row| row[:word] }.uniq.size
|
|
@@ -204,7 +294,7 @@ module Hecks
|
|
|
204
294
|
# Prose keyed by word: everything between a section's generated
|
|
205
295
|
# region and the next `## ` heading (or end of file).
|
|
206
296
|
#
|
|
207
|
-
# Starts on PREAMBLE rather than nil so the text between the
|
|
297
|
+
# Starts on `PREAMBLE` rather than nil so the text between the page's
|
|
208
298
|
# own generated lede and its first word heading is carried over too
|
|
209
299
|
# instead of being silently dropped. A page written before that
|
|
210
300
|
# region existed has no generated marker ahead of its first `## `,
|
|
@@ -212,8 +302,13 @@ module Hecks
|
|
|
212
302
|
# preamble is invented — the older shape reads back unchanged.
|
|
213
303
|
# A single-pass line-scanning state machine (current/collecting/
|
|
214
304
|
# buffer/in_fence) — each branch mutates shared local state that
|
|
215
|
-
# carries into the
|
|
305
|
+
# carries into the next iteration, so splitting per branch would
|
|
216
306
|
# mean passing all four back and forth by reference every line.
|
|
307
|
+
#
|
|
308
|
+
# @param text [String] a committed reference page's full Markdown source
|
|
309
|
+
# @return [Hash{String, Symbol => String}] hand-written prose, keyed by
|
|
310
|
+
# word (or `PREAMBLE` for the page's lede); empty and TODO-sentinel-only
|
|
311
|
+
# entries are dropped
|
|
217
312
|
# rubocop:disable-next Metrics/PerceivedComplexity
|
|
218
313
|
def harvest(text)
|
|
219
314
|
prose = {}
|
|
@@ -221,7 +316,7 @@ module Hecks
|
|
|
221
316
|
collecting = false
|
|
222
317
|
buffer = []
|
|
223
318
|
|
|
224
|
-
# A
|
|
319
|
+
# A heading inside a fence is not a heading. `## something` is an
|
|
225
320
|
# ordinary Ruby comment, and now that every word's section carries
|
|
226
321
|
# runnable code, one written at the left margin would otherwise
|
|
227
322
|
# end that section mid-example and orphan the rest of it under a
|
|
@@ -246,6 +341,13 @@ module Hecks
|
|
|
246
341
|
prose.reject { |_word, text_| text_.empty? || text_ == TODO_SENTINEL }
|
|
247
342
|
end
|
|
248
343
|
|
|
344
|
+
# Renders every reference page and writes each to `directory`.
|
|
345
|
+
#
|
|
346
|
+
# @param directory [String] path to the directory to write pages into,
|
|
347
|
+
# created if it does not exist
|
|
348
|
+
# @return [void]
|
|
349
|
+
# @raise [RuntimeError] if a committed page carries prose for a word the
|
|
350
|
+
# language no longer declares in that context
|
|
249
351
|
def write!(directory)
|
|
250
352
|
FileUtils.mkdir_p(directory)
|
|
251
353
|
pages(directory).each do |name, content|
|
|
@@ -257,6 +359,10 @@ module Hecks
|
|
|
257
359
|
# reference page, keyed by region id instead of a word, so the
|
|
258
360
|
# index a reader lands on first can't drift from what actually
|
|
259
361
|
# exists on disk either.
|
|
362
|
+
#
|
|
363
|
+
# @param root [String] the repository root
|
|
364
|
+
# @return [Hash{String => String}] each region id mapped to its freshly
|
|
365
|
+
# rendered content
|
|
260
366
|
def readme_regions(root)
|
|
261
367
|
{
|
|
262
368
|
"guides" => guide_index(root),
|
|
@@ -267,6 +373,11 @@ module Hecks
|
|
|
267
373
|
}
|
|
268
374
|
end
|
|
269
375
|
|
|
376
|
+
# Lists every committed guide, linked and titled by its own heading.
|
|
377
|
+
#
|
|
378
|
+
# @param root [String] the repository root
|
|
379
|
+
# @return [String] a Markdown list linking every committed guide, titled
|
|
380
|
+
# by its own `# ` heading
|
|
270
381
|
def guide_index(root)
|
|
271
382
|
paths = Dir.glob(File.join(root, "docs/implemented/guides/*.md"))
|
|
272
383
|
.reject { |p| %w[AUTHORING.md].include?(File.basename(p)) }
|
|
@@ -278,6 +389,11 @@ module Hecks
|
|
|
278
389
|
lines.join("\n")
|
|
279
390
|
end
|
|
280
391
|
|
|
392
|
+
# Links the reference index, with its context count.
|
|
393
|
+
#
|
|
394
|
+
# @param _root [String] unused; kept for the shape `readme_regions` calls
|
|
395
|
+
# every region renderer with
|
|
396
|
+
# @return [String] one line linking the reference index, with its context count
|
|
281
397
|
def reference_index(_root)
|
|
282
398
|
count = contexts.size
|
|
283
399
|
"[The DSL reference](docs/implemented/reference/index.md) — #{count} contexts, generated from " \
|
|
@@ -285,6 +401,11 @@ module Hecks
|
|
|
285
401
|
"`spec/reference_golden_spec.rb`."
|
|
286
402
|
end
|
|
287
403
|
|
|
404
|
+
# Lists every `bin/` script that opens with a comment, one row each.
|
|
405
|
+
#
|
|
406
|
+
# @param root [String] the repository root
|
|
407
|
+
# @return [String] a Markdown table of every `bin/` script with an opening
|
|
408
|
+
# comment, one row each
|
|
288
409
|
def tool_table(root)
|
|
289
410
|
scripts = Dir.glob(File.join(root, "bin/*")).select { |p| File.file?(p) }.sort
|
|
290
411
|
rows = scripts.filter_map { |path| [path, tool_summary(path)] }.select { |_, desc| desc }
|
|
@@ -293,11 +414,15 @@ module Hecks
|
|
|
293
414
|
lines.join("\n")
|
|
294
415
|
end
|
|
295
416
|
|
|
296
|
-
# The opening comment
|
|
417
|
+
# The opening comment paragraph, not just the first line — a table
|
|
297
418
|
# cell that trails off mid-clause reads worse than one that runs a
|
|
298
419
|
# little long and says "...". A code-bearing comment (`field.name`,
|
|
299
420
|
# `pattern:`) makes naive sentence-splitting on "." or ":" cut in
|
|
300
|
-
# the wrong place, so this truncates on
|
|
421
|
+
# the wrong place, so this truncates on length alone.
|
|
422
|
+
#
|
|
423
|
+
# @param path [String] a `bin/` script's path
|
|
424
|
+
# @return [String, nil] its opening comment paragraph, truncated to 140
|
|
425
|
+
# characters, or nil if the script opens with no comment
|
|
301
426
|
def tool_summary(path)
|
|
302
427
|
comment_lines = []
|
|
303
428
|
started = false
|
|
@@ -317,13 +442,17 @@ module Hecks
|
|
|
317
442
|
text.length > 140 ? "#{text[0, 137]}..." : text
|
|
318
443
|
end
|
|
319
444
|
|
|
320
|
-
#
|
|
445
|
+
# One real, committed file, read fresh — not re-derived from a boot
|
|
321
446
|
# (this module never requires `hecks/projections/diagrams`, and
|
|
322
447
|
# shouldn't just to draw one example). `docs/generated/diagrams/`
|
|
323
448
|
# is already held to the declaration by `spec/diagrams_spec.rb`'s
|
|
324
449
|
# own drift check; this just quotes its own output, so the two
|
|
325
450
|
# can't independently drift from each other either — a stale
|
|
326
|
-
# Order_lifecycle.mmd fails
|
|
451
|
+
# Order_lifecycle.mmd fails that spec long before this one runs.
|
|
452
|
+
#
|
|
453
|
+
# @param root [String] the repository root
|
|
454
|
+
# @return [String] a Markdown section showcasing the generated diagram
|
|
455
|
+
# tooling, quoting `examples/pizzas`'s own committed Order lifecycle diagram
|
|
327
456
|
def diagram_showcase(root)
|
|
328
457
|
lifecycle = File.read(File.join(root, "docs/generated/diagrams/pizzas/Order_lifecycle.mmd")).strip
|
|
329
458
|
<<~MARKDOWN.strip
|
|
@@ -337,6 +466,11 @@ module Hecks
|
|
|
337
466
|
MARKDOWN
|
|
338
467
|
end
|
|
339
468
|
|
|
469
|
+
# Lists every example domain, with its own declared vision.
|
|
470
|
+
#
|
|
471
|
+
# @param root [String] the repository root
|
|
472
|
+
# @return [String] a Markdown list of every example domain with a
|
|
473
|
+
# `.bluebook` file, each with its own declared `vision` text
|
|
340
474
|
def corpus_roster(root)
|
|
341
475
|
dirs = Dir.glob(File.join(root, "examples/*/"))
|
|
342
476
|
lines = dirs.filter_map do |dir|
|
|
@@ -351,6 +485,12 @@ module Hecks
|
|
|
351
485
|
lines.join("\n")
|
|
352
486
|
end
|
|
353
487
|
|
|
488
|
+
# Replaces every generated region inside `text` with its freshly rendered
|
|
489
|
+
# content, leaving the hand-written parts of README untouched.
|
|
490
|
+
#
|
|
491
|
+
# @param root [String] the repository root
|
|
492
|
+
# @param text [String] the README's current full text
|
|
493
|
+
# @return [String] the README's text, with each generated region refreshed
|
|
354
494
|
def render_readme(root, text)
|
|
355
495
|
readme_regions(root).reduce(text) do |current, (id, content)|
|
|
356
496
|
pattern = /#{Regexp.escape(region_begin(id))}.*?#{Regexp.escape(GENERATED_END)}/m
|
|
@@ -358,12 +498,16 @@ module Hecks
|
|
|
358
498
|
end
|
|
359
499
|
end
|
|
360
500
|
|
|
501
|
+
# Regenerates README's generated regions in place.
|
|
502
|
+
#
|
|
503
|
+
# @param root [String] the repository root
|
|
504
|
+
# @return [void]
|
|
361
505
|
def write_readme!(root)
|
|
362
506
|
path = File.join(root, "README.md")
|
|
363
507
|
File.write(path, render_readme(root, File.read(path)))
|
|
364
508
|
end
|
|
365
509
|
|
|
366
|
-
# An example
|
|
510
|
+
# An example a reader can see and the harness will actually run.
|
|
367
511
|
# `ruby skip` is display-only by the doctest harness's own rule, and
|
|
368
512
|
# a hidden `<!-- doctest:boot -->` block is setup rather than an
|
|
369
513
|
# example — a word whose only "example" is invisible or inert is a
|
|
@@ -371,9 +515,14 @@ module Hecks
|
|
|
371
515
|
# gate exists to refuse.
|
|
372
516
|
EXAMPLE_FENCE = /^```ruby(?: bluebook| boot)?[ \t]*$/
|
|
373
517
|
|
|
518
|
+
# Whether a word's prose carries a runnable example.
|
|
519
|
+
#
|
|
520
|
+
# @param prose [String, nil] a word's hand-written prose, or nil
|
|
521
|
+
# @return [Boolean] true if `prose` contains a runnable `ruby` or
|
|
522
|
+
# `ruby bluebook`/`ruby boot` fenced example
|
|
374
523
|
def exemplified?(prose) = prose.to_s.match?(EXAMPLE_FENCE)
|
|
375
524
|
|
|
376
|
-
#
|
|
525
|
+
# Every live word, paired with its prose. Both coverage gates ask a
|
|
377
526
|
# question about this same walk and differ only in what they ask of
|
|
378
527
|
# the prose, so they share it rather than each re-deriving the page
|
|
379
528
|
# set — the two are meant to move together, and one drifting past
|
|
@@ -382,6 +531,12 @@ module Hecks
|
|
|
382
531
|
#
|
|
383
532
|
# `harvest` already rejects empty prose and the TODO sentinel, so a
|
|
384
533
|
# word with nothing written for it arrives here with a nil.
|
|
534
|
+
#
|
|
535
|
+
# @param directory [String] path to the directory holding the committed
|
|
536
|
+
# reference pages
|
|
537
|
+
# @return [Array(String, String, String), Array(String, String, nil)]
|
|
538
|
+
# `[word, context, prose]` for every live (admitted or deprecated) word,
|
|
539
|
+
# `prose` nil if none was harvested
|
|
385
540
|
def live_words(directory)
|
|
386
541
|
rows = contexts.flat_map do |context|
|
|
387
542
|
path = File.join(directory, page_name(context))
|
|
@@ -392,21 +547,35 @@ module Hecks
|
|
|
392
547
|
rows.uniq { |word, context, _| [word, context] }
|
|
393
548
|
end
|
|
394
549
|
|
|
550
|
+
# Disambiguates a word by the context it is declared in.
|
|
551
|
+
#
|
|
552
|
+
# @param word [String] a word
|
|
553
|
+
# @param context [String] the context it is declared in
|
|
554
|
+
# @return [String] the word, disambiguated by its context
|
|
395
555
|
def name_of(word, context) = "#{word} (#{context})"
|
|
396
556
|
|
|
397
|
-
# The coverage gate's question: every
|
|
557
|
+
# The coverage gate's question: every live word with no prose yet.
|
|
558
|
+
#
|
|
559
|
+
# @param directory [String] path to the directory holding the committed
|
|
560
|
+
# reference pages
|
|
561
|
+
# @return [Array<String>] each undocumented live word, named by `name_of`
|
|
398
562
|
def undocumented(directory)
|
|
399
563
|
live_words(directory).reject { |_word, _context, prose| prose }
|
|
400
564
|
.map { |word, context, _| name_of(word, context) }
|
|
401
565
|
end
|
|
402
566
|
|
|
403
|
-
# The
|
|
567
|
+
# The second coverage gate: prose is a declaration, and a
|
|
404
568
|
# declaration nothing runs cannot disagree with anything. A word
|
|
405
569
|
# documented only in sentences can go stale — or describe a word
|
|
406
570
|
# the runtime never wired at all, which this repository has already
|
|
407
571
|
# shipped twice (`read_model`'s where/order_by/limit/offset, and
|
|
408
|
-
# `role`/`goal` on a command). An example that
|
|
572
|
+
# `role`/`goal` on a command). An example that runs is the only
|
|
409
573
|
# documentation that can go red.
|
|
574
|
+
#
|
|
575
|
+
# @param directory [String] path to the directory holding the committed
|
|
576
|
+
# reference pages
|
|
577
|
+
# @return [Array<String>] each live word with prose but no runnable
|
|
578
|
+
# example, named by `name_of`
|
|
410
579
|
def unexemplified(directory)
|
|
411
580
|
live_words(directory).reject { |_word, _context, prose| exemplified?(prose) }
|
|
412
581
|
.map { |word, context, _| name_of(word, context) }
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
module Hecks
|
|
2
|
-
# A
|
|
3
|
-
# for members that don't ship inside hecks's own lib/ at all: a
|
|
4
|
-
# separate, independently-versioned package
|
|
5
|
-
# (github.com/chrisyoung/embryonaut_bluebooks) that a consuming project
|
|
6
|
-
# vendors into its OWN checkout, the same way a project already vendors
|
|
7
|
-
# hecks itself (bin/vendor_hecks, vendor/hecks/).
|
|
2
|
+
# A vendored, external bluebook.
|
|
8
3
|
#
|
|
9
|
-
#
|
|
4
|
+
# ## What it is
|
|
5
|
+
#
|
|
6
|
+
# Same shape as Framework (framework.rb), for members that don't ship
|
|
7
|
+
# inside hecks's own lib/ at all: a separate, independently-versioned
|
|
8
|
+
# package (github.com/chrisyoung/embryonaut_bluebooks) that a consuming
|
|
9
|
+
# project vendors into its own checkout, the same way a project already
|
|
10
|
+
# vendors hecks itself (bin/vendor_hecks, vendor/hecks/).
|
|
11
|
+
#
|
|
12
|
+
# ## Recovered, not rebuilt
|
|
13
|
+
#
|
|
14
|
+
# This module and its `uses_embryonaut_bluebook`
|
|
10
15
|
# DSL word (hecksagon_builder.rb) were built on a prior commit of this
|
|
11
16
|
# repo (933d1dd), vendored out to a real consumer (lifeadelics/domain,
|
|
12
17
|
# for embryonaut_bluebooks/payments), and then lost from this repo's own
|
|
@@ -17,19 +22,23 @@ module Hecks
|
|
|
17
22
|
# conventions rather than copied wholesale, since the two trees had
|
|
18
23
|
# otherwise diverged for weeks in both directions.
|
|
19
24
|
#
|
|
20
|
-
#
|
|
25
|
+
# ## Resolution
|
|
26
|
+
#
|
|
27
|
+
# Resolved from the consuming registry's own root, not this gem's
|
|
21
28
|
# __dir__ — Framework::ROOT can be a fixed, `__dir__`-relative constant
|
|
22
29
|
# because framework members ship inside this gem; an embryonaut bluebook
|
|
23
|
-
# ships inside the
|
|
30
|
+
# ships inside the consumer's own checkout instead, at
|
|
24
31
|
# `<registry.root>/vendor/embryonaut_bluebooks/<name>/bluebook/`. There
|
|
25
32
|
# is no fixed answer until a registry (and its root) actually exists, so
|
|
26
33
|
# this resolves lazily, per call — the same reason `uses_framework`
|
|
27
34
|
# itself only runs at hecksagon-build time, when a real registry is
|
|
28
35
|
# current.
|
|
29
36
|
#
|
|
30
|
-
#
|
|
37
|
+
# ## Load order
|
|
38
|
+
#
|
|
39
|
+
# Every `.bluebook` file in the package, sorted — not just one. Unlike a
|
|
31
40
|
# framework member (one file, named by its own stem), a vendored package
|
|
32
|
-
# can span several bluebook files that reopen the
|
|
41
|
+
# can span several bluebook files that reopen the same `Hecks.bluebook`
|
|
33
42
|
# (embryonaut_bluebooks/payments/bluebook/{payment,payments,policies}
|
|
34
43
|
# .bluebook all reopen "Payments"). Load order matters — policies
|
|
35
44
|
# .bluebook names `Payment::Succeed` and needs the aggregate already
|
|
@@ -37,21 +46,35 @@ module Hecks
|
|
|
37
46
|
# payment < payments < policies, the same reason that package's own
|
|
38
47
|
# files are named to fall in that order in the first place.
|
|
39
48
|
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
49
|
+
# ## Scope
|
|
50
|
+
#
|
|
51
|
+
# Only the bluebook files — same restriction Framework draws, same
|
|
52
|
+
# reason: a `.hecksagon`/`.port`/`.adapter` is a wiring decision
|
|
42
53
|
# (persistence, which processor adapter is bound) that belongs to
|
|
43
54
|
# whoever is deploying, never baked into the vendored package itself.
|
|
44
55
|
# embryonaut_bluebooks/payments ships its own mock `.hecksagon` for its
|
|
45
|
-
# own spec suite; a consumer declares its
|
|
56
|
+
# own spec suite; a consumer declares its own separate
|
|
46
57
|
# `Hecks.hecksagon "Payments" do ... end` to bind real storage/adapters
|
|
47
58
|
# — see Framework's own comment for the fuller reasoning, identical here.
|
|
48
59
|
#
|
|
49
|
-
#
|
|
60
|
+
# ## Idempotency
|
|
61
|
+
#
|
|
62
|
+
# Idempotent the same way `Framework.load!` is — checked against the
|
|
50
63
|
# bluebook this package actually declares (`Naming.pascal("payments")`
|
|
51
64
|
# => "Payments"), not a separate ledger. A vendored package's directory
|
|
52
65
|
# name and its declared `Hecks.bluebook` name are the one convention
|
|
53
66
|
# this reuses from Framework rather than reinventing.
|
|
54
67
|
module EmbryonautBluebook
|
|
68
|
+
# Loads a vendored embryonaut bluebook package's `.bluebook` files, once
|
|
69
|
+
# per registry.
|
|
70
|
+
#
|
|
71
|
+
# @param name [String, Symbol] the vendored package's directory name, such
|
|
72
|
+
# as `"payments"`
|
|
73
|
+
# @param registry [Runtime::Registry, nil] the registry to vendor into and
|
|
74
|
+
# check for an existing load; defaults to the current boot registry
|
|
75
|
+
# @return [void]
|
|
76
|
+
# @raise [Runtime::WiringError] if `registry` has no root, or no vendored
|
|
77
|
+
# package named `name` is checked out
|
|
55
78
|
def self.load!(name, registry: Hecks.current_registry)
|
|
56
79
|
unless registry&.root
|
|
57
80
|
raise Runtime::WiringError,
|