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
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
4
|
+
# What a query does beyond holding its declared shape.
|
|
5
5
|
module Query
|
|
6
|
+
# Finds a declared result attribute by name.
|
|
7
|
+
#
|
|
8
|
+
# @param named [String, Symbol] the attribute's declared name
|
|
9
|
+
# @return [Bluebook::Attribute, nil] the attribute, or `nil` if none is declared
|
|
10
|
+
# by that name
|
|
6
11
|
def attribute(named) = @attributes.find { |a| a.name == named.to_sym }
|
|
7
12
|
end
|
|
8
13
|
end
|
|
@@ -1,27 +1,38 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
4
|
+
# **What a read model does**. Its declared half is the gathered heads
|
|
5
5
|
# and the query shape; these are readings taken off them.
|
|
6
6
|
module ReadModel
|
|
7
|
+
# Lists the fields this read model groups rows by.
|
|
8
|
+
#
|
|
9
|
+
# @return [Array<Symbol>] each declared `group_by` field's name, in declaration order
|
|
7
10
|
def group_by_fields = @group_by.map { |row| row[:field].to_sym }
|
|
8
11
|
|
|
9
12
|
# `!!` rather than a bare `@count` — the DSL/reconstruction
|
|
10
13
|
# boundary (ReadModel#initialize) already normalises to
|
|
11
14
|
# `true`/`nil`, so this is belt and braces against a future
|
|
12
15
|
# caller constructing a ReadModel by hand with `count: false`.
|
|
16
|
+
#
|
|
17
|
+
# @return [Boolean] whether this read model reduces to a row count
|
|
13
18
|
def count? = !!@count
|
|
14
19
|
|
|
20
|
+
# Names the verb `Dispatcher#query` looks this read model up by.
|
|
21
|
+
#
|
|
22
|
+
# @return [String] this read model's name in `snake_case`
|
|
15
23
|
def query_name = Naming.snake(@name)
|
|
16
24
|
|
|
17
|
-
#
|
|
25
|
+
# Which gathered heads the filtering applies to (ADR 0055) — plural,
|
|
18
26
|
# since `where`/`order_by`/`limit`/`offset` can now each independently
|
|
19
27
|
# name a many-side head via `on:` once there's more than one. A read
|
|
20
28
|
# model with a single many-side head keeps the old reading: every
|
|
21
|
-
#
|
|
29
|
+
# untargeted option (plus `group_by`/`count`/`median`, still
|
|
22
30
|
# single-head-only — ADR 0055) applies to it, same as before `on:`
|
|
23
31
|
# existed. With several many-side heads, only the ones actually named
|
|
24
32
|
# by a targeted option are eligible.
|
|
33
|
+
#
|
|
34
|
+
# @return [Array<Symbol>] the `:as` name of each many-side head that filtering
|
|
35
|
+
# applies to; `[]` if this read model has no many-side head
|
|
25
36
|
def filtered_head_names
|
|
26
37
|
many = @aggregate_heads.select { |head| head[:many] }
|
|
27
38
|
return [] if many.empty?
|
|
@@ -33,26 +44,36 @@ module Hecks
|
|
|
33
44
|
end
|
|
34
45
|
|
|
35
46
|
# The pre-`on:` reading (ADR 0055), unchanged: with exactly one
|
|
36
|
-
# many-side head, every
|
|
47
|
+
# many-side head, every untargeted option (plus `group_by`/`count`/
|
|
37
48
|
# `median`, still single-head-only) applies to it — split out only
|
|
38
49
|
# to keep `filtered_head_names` itself under this file's own
|
|
39
50
|
# complexity budget, not because the two questions differ in kind.
|
|
51
|
+
#
|
|
52
|
+
# @param many [Array<Hash{Symbol => Object}>] the read model's many-side
|
|
53
|
+
# `aggregate_heads` rows; must hold exactly one
|
|
54
|
+
# @return [Array<Symbol>] `[the one head's :as name]` if any filtering option is
|
|
55
|
+
# declared, else `[]`
|
|
40
56
|
def single_filtered_head_name(many)
|
|
41
57
|
declared = wheres.any? || order_by || limit || offset || authorization&.tenant ||
|
|
42
58
|
@group_by.any? || count? || @median_field
|
|
43
59
|
declared ? [many.first[:as]] : []
|
|
44
60
|
end
|
|
45
61
|
|
|
46
|
-
#
|
|
62
|
+
# The where/order_by/limit/offset that apply to one eligible head
|
|
47
63
|
# (ADR 0055) — a small view `Ports::Query::InMemory.execute` reads
|
|
48
64
|
# exactly the way it already reads a whole `Query`/`ReadModel`
|
|
49
65
|
# (`.wheres`/`.order_by`/`.limit`/`.offset`/`.null_semantics`), scoped
|
|
50
|
-
# to `head_as`'s own aggregate: an
|
|
51
|
-
# `head_as` is the read model's
|
|
52
|
-
# reading, unchanged) ; a
|
|
66
|
+
# to `head_as`'s own aggregate: an untargeted option applies when
|
|
67
|
+
# `head_as` is the read model's one many-side head (the pre-`on:`
|
|
68
|
+
# reading, unchanged) ; a targeted one applies when its `target`
|
|
53
69
|
# resolves to `head_as`'s own aggregate.
|
|
54
70
|
FilteredOptions = Struct.new(:wheres, :order_by, :limit, :offset, :null_semantics)
|
|
55
71
|
|
|
72
|
+
# Scopes this read model's filtering options down to one included head.
|
|
73
|
+
#
|
|
74
|
+
# @param head_as [Symbol] the `:as` name of the head to scope filtering to
|
|
75
|
+
# @return [FilteredOptions] the `wheres`/`order_by`/`limit`/`offset` that apply to
|
|
76
|
+
# `head_as`, and this read model's own `null_semantics`
|
|
56
77
|
def options_for(head_as)
|
|
57
78
|
many = @aggregate_heads.select { |head| head[:many] }
|
|
58
79
|
aggregate_name = @aggregate_heads.find { |head| head[:as] == head_as }&.fetch(:aggregate)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
4
|
+
# The behaviour more than one construct shares.
|
|
5
5
|
#
|
|
6
6
|
# None of this is new duplication. `attribute(named)` was written
|
|
7
7
|
# out four separate times — on Aggregate, Entity, Command and
|
|
@@ -11,23 +11,27 @@ module Hecks
|
|
|
11
11
|
# files reads as four methods, and the same method in four modules
|
|
12
12
|
# named `Behaviour::*` reads as one repeated.
|
|
13
13
|
#
|
|
14
|
-
# Written to work whether the construct is
|
|
15
|
-
# ordinary object like Aggregate) or
|
|
14
|
+
# Written to work whether the construct is included into (an
|
|
15
|
+
# ordinary object like Aggregate) or extended into (a class-shaped
|
|
16
16
|
# one like Command), because every one of them reads instance
|
|
17
17
|
# variables and nothing else.
|
|
18
18
|
|
|
19
|
-
# A
|
|
19
|
+
# A construct whose identity is a join of declared paths.
|
|
20
20
|
#
|
|
21
|
-
#
|
|
21
|
+
# The paths, in declaration order, because the identity is their join.
|
|
22
22
|
# "number.value" says which field carries the identity ; several paths
|
|
23
23
|
# say the identity is made of several facts, which is what anything
|
|
24
24
|
# named beneath another thing needs. `identity_heads` are the attributes
|
|
25
25
|
# those paths start at — what every reader that looks up or coerces an
|
|
26
26
|
# attribute actually wants — and `identified_by` is the single head,
|
|
27
|
-
# offered
|
|
27
|
+
# offered only when there is one path to have a head of. A composite has
|
|
28
28
|
# no single head, and answering with the first would be a guess ; the
|
|
29
29
|
# readers that need all of them ask for `identity_heads`.
|
|
30
30
|
module Identified
|
|
31
|
+
# Derives `identity_paths`, `identity_heads` and the single-head form of
|
|
32
|
+
# `identified_by` from the declared `identified_by` path(s).
|
|
33
|
+
#
|
|
34
|
+
# @return [void]
|
|
31
35
|
def derive_identity
|
|
32
36
|
@identity_paths = Array(@identified_by).map(&:to_s).reject(&:empty?)
|
|
33
37
|
@identity_heads = @identity_paths.map { |path| path.split(".").first.to_sym }.uniq
|
|
@@ -35,7 +39,7 @@ module Hecks
|
|
|
35
39
|
end
|
|
36
40
|
end
|
|
37
41
|
|
|
38
|
-
# A
|
|
42
|
+
# A construct that answers for its own declarations by name.
|
|
39
43
|
#
|
|
40
44
|
# Indexed once, since the declarations are final by the time the
|
|
41
45
|
# construct exists — every dispatch asks these finders by name, and a
|
|
@@ -43,35 +47,67 @@ module Hecks
|
|
|
43
47
|
# had already settled at boot.
|
|
44
48
|
#
|
|
45
49
|
# `index_by_name` takes the collections to index rather than naming
|
|
46
|
-
# them, because
|
|
50
|
+
# them, because which collections a construct has is exactly the part
|
|
47
51
|
# that differs: an aggregate has five, an entity three, a command one.
|
|
48
52
|
module Indexed
|
|
49
|
-
# Keyed by
|
|
53
|
+
# Keyed by symbol — an attribute is asked for by its declared
|
|
50
54
|
# symbol name everywhere in the runtime.
|
|
55
|
+
#
|
|
56
|
+
# @param attributes [Array<Bluebook::Attribute>] the construct's declared
|
|
57
|
+
# attributes
|
|
58
|
+
# @return [void]
|
|
51
59
|
def index_attributes(attributes)
|
|
52
60
|
@attributes_by_name = attributes.to_h { |held| [held.name, held] }
|
|
53
61
|
end
|
|
54
62
|
|
|
55
|
-
# Keyed by
|
|
63
|
+
# Keyed by string and by `hecks_name` — a command, query or value
|
|
56
64
|
# object is a construct whose Ruby `name` is something else
|
|
57
65
|
# entirely (a constant path, or nothing at all for an anonymous
|
|
58
|
-
# class), so the
|
|
66
|
+
# class), so the declared name is the only one worth indexing.
|
|
67
|
+
#
|
|
68
|
+
# @param collection [Array<#hecks_name>] declared constructs to index —
|
|
69
|
+
# value objects, commands or queries, each answering `hecks_name`
|
|
70
|
+
# @return [Hash{String => Object}] the collection keyed by each member's
|
|
71
|
+
# `hecks_name`
|
|
59
72
|
def index_by_hecks_name(collection)
|
|
60
73
|
collection.to_h { |held| [held.hecks_name, held] }
|
|
61
74
|
end
|
|
62
75
|
|
|
76
|
+
# Finds a declared attribute by its declared name.
|
|
77
|
+
#
|
|
78
|
+
# @param named [String, Symbol] the attribute's declared name
|
|
79
|
+
# @return [Bluebook::Attribute, nil] the attribute named `named`, or `nil`
|
|
80
|
+
# if none is declared under that name
|
|
63
81
|
def attribute(named) = @attributes_by_name[named.to_sym]
|
|
82
|
+
|
|
83
|
+
# Finds a declared command by its declared name.
|
|
84
|
+
#
|
|
85
|
+
# @param named [String, Symbol] the command's declared name
|
|
86
|
+
# @return [Class, nil] the command class (a `Bluebook::Command` subclass)
|
|
87
|
+
# named `named`, or `nil` if none is declared under that name
|
|
64
88
|
def command(named) = @commands_by_name[named.to_s]
|
|
89
|
+
|
|
90
|
+
# Finds a declared query by its declared name.
|
|
91
|
+
#
|
|
92
|
+
# @param named [String, Symbol] the query's declared name
|
|
93
|
+
# @return [Bluebook::Query, nil] the query named `named`, or `nil` if none
|
|
94
|
+
# is declared under that name
|
|
65
95
|
def query(named) = @queries_by_name[named.to_s]
|
|
66
96
|
end
|
|
67
97
|
|
|
68
|
-
# A
|
|
98
|
+
# A construct that owns what it declares.
|
|
69
99
|
#
|
|
70
100
|
# Owner links are only ever read lazily — hecks_fqn at ask time,
|
|
71
101
|
# Reference#resolve at dispatch time — so the moment the construct
|
|
72
102
|
# is complete is the right stamping point: its declarations are
|
|
73
103
|
# final, and nothing outside needs to remember to stamp them.
|
|
74
104
|
module Owns
|
|
105
|
+
# Stamps each of `children` as owned by this construct.
|
|
106
|
+
#
|
|
107
|
+
# @param children [Array<Object>] the construct's own declared children —
|
|
108
|
+
# arrays or bare objects, flattened before stamping — each answering
|
|
109
|
+
# `hecks_owner=`
|
|
110
|
+
# @return [void]
|
|
75
111
|
def stamp(*children)
|
|
76
112
|
children.flatten.each { |child| child.hecks_owner = self }
|
|
77
113
|
end
|
|
@@ -1,35 +1,47 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
5
|
-
# is a
|
|
4
|
+
# **What a value object does**. Extended, not included — a value object
|
|
5
|
+
# is a class (`Class.new(self)`, one per declared shape), so its
|
|
6
6
|
# behaviour is singleton behaviour, and the holding half's `absorb`
|
|
7
7
|
# is what a generated constructor would be.
|
|
8
8
|
module ValueObject
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
9
|
+
# Recorded as its own fact (`closed_set`), not left to be inferred from
|
|
10
|
+
# `members: []` alone — an empty `one_of` and no `one_of` at all would
|
|
11
|
+
# otherwise be indistinguishable, which would leave the rule about it
|
|
12
|
+
# only livable in the builder. Recording the declaration lets the
|
|
13
|
+
# language judge it instead, the same way an empty attribute name
|
|
14
|
+
# survives into the IR and is judged there.
|
|
15
|
+
#
|
|
16
|
+
# @return [Boolean] whether this value object declares `one_of`, even
|
|
17
|
+
# if left empty
|
|
14
18
|
def closed_set? = @closed_set
|
|
15
19
|
|
|
20
|
+
# Finds a declared attribute by its declared name.
|
|
21
|
+
#
|
|
22
|
+
# @param named [String, Symbol] the attribute's declared name
|
|
23
|
+
# @return [Bluebook::Attribute, nil] the attribute named `named`, or
|
|
24
|
+
# `nil` if none is declared under that name
|
|
16
25
|
def attribute(named) = attributes.find { |held| held.name == named.to_sym }
|
|
17
26
|
|
|
18
27
|
# A single-attribute value object (EmailAddress{address},
|
|
19
|
-
# CustomerNumber{value}) is a
|
|
28
|
+
# CustomerNumber{value}) is a name for a scalar, not a genuine
|
|
20
29
|
# group — [[feedback_name_the_scalar_field]]. `adapters/driven/
|
|
21
30
|
# sql_query_builder.rb` and `fuzzing/invalid_value_generator.rb`
|
|
22
31
|
# now read through this rather than inlining the check.
|
|
23
32
|
#
|
|
24
33
|
# `forms/field_shape.rb`'s own two `attributes.first.name` sites
|
|
25
34
|
# (`closed_set_options`/`closed_set_field`) look identical but are
|
|
26
|
-
#
|
|
35
|
+
# not the same question — they pick a closed set's discriminant
|
|
27
36
|
# column, and a closed set can be genuinely multi-attribute
|
|
28
37
|
# (`Runtime::Value::Admission#member_matches?`'s own comment
|
|
29
38
|
# names a real one: `StatementFrequency`'s `cadence`/
|
|
30
39
|
# `retention_months`/`paper_fee_cents`). `sole_attribute` would
|
|
31
40
|
# return `nil` for that shape and break the discriminant lookup —
|
|
32
41
|
# left as `.first` on purpose, not a missed migration.
|
|
42
|
+
#
|
|
43
|
+
# @return [Bluebook::Attribute, nil] this value object's only
|
|
44
|
+
# attribute, or `nil` when it has zero or more than one
|
|
33
45
|
def sole_attribute
|
|
34
46
|
attributes.first if attributes.size == 1
|
|
35
47
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
# What a chapter may declare it provides, and what each capability
|
|
4
|
+
# must name. A chapter that `provides "authorization"` is trusted in
|
|
5
|
+
# place of a check for the literal name "Governance" — by the role
|
|
6
|
+
# check at dispatch, the ungoverned-role refusal at boot, the
|
|
7
|
+
# authorization adapter and the fuzzer — so the declaration has to be
|
|
8
|
+
# complete and point at real verbs. `BluebookBuilder::Validation
|
|
9
|
+
# #validate_provisions!` holds every `provides` to this table.
|
|
10
|
+
#
|
|
11
|
+
# key => :command | :query the kind of verb that key must name
|
|
12
|
+
module Capabilities
|
|
13
|
+
AUTHORIZATION = "authorization".freeze
|
|
14
|
+
|
|
15
|
+
CONTRACTS = {
|
|
16
|
+
AUTHORIZATION => {
|
|
17
|
+
# every assignment an actor holds, current or historical
|
|
18
|
+
assignments: :query,
|
|
19
|
+
# the command that grants an actor a role
|
|
20
|
+
grant: :command,
|
|
21
|
+
# every grant of one role acting as another
|
|
22
|
+
transitions: :query
|
|
23
|
+
}.freeze
|
|
24
|
+
}.freeze
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
require_relative "behaviour/chapter"
|
|
2
|
+
require_relative "capabilities"
|
|
2
3
|
require_relative "../ir"
|
|
3
4
|
|
|
4
5
|
module Hecks
|
|
5
6
|
module Bluebook
|
|
6
|
-
#
|
|
7
|
+
# The chapter, and no longer the namespace it lives in.
|
|
7
8
|
#
|
|
8
|
-
# `Hecks::Bluebook` was briefly a
|
|
9
|
+
# `Hecks::Bluebook` was briefly a class, because dropping the
|
|
9
10
|
# old `IR::` segment would otherwise have produced
|
|
10
11
|
# `Bluebook::Bluebook` — a class shadowing its own enclosing module.
|
|
11
12
|
# Naming the construct what the code already called it everywhere
|
|
12
13
|
# (a chapter; this file was already chapter.rb) removes the clash
|
|
13
14
|
# at the source, so the namespace goes back to being a module.
|
|
14
15
|
#
|
|
15
|
-
# Measured before changing: `Bluebook` was used
|
|
16
|
+
# Measured before changing: `Bluebook` was used as a class in five
|
|
16
17
|
# places and as a namespace in seventy-seven files.
|
|
17
18
|
class Chapter
|
|
18
19
|
include Construct
|
|
19
20
|
include Behaviour::Chapter
|
|
20
21
|
include Hecks::IR
|
|
21
22
|
|
|
22
|
-
#
|
|
23
|
-
# "v1", a business fact the
|
|
23
|
+
# **The schema's own version** — not a domain's `version:` (Banking's
|
|
24
|
+
# "v1", a business fact the author chose), but the shape `to_h`
|
|
24
25
|
# itself emits. A consumer reading exported IR with no Ruby DSL to
|
|
25
26
|
# cross-check against (a build-time generator, a stored snapshot)
|
|
26
|
-
# needs to know
|
|
27
|
+
# needs to know which shape it's holding before it can safely
|
|
27
28
|
# interpret any of the rest of it. Bump this when `to_h`'s own
|
|
28
29
|
# shape changes in a way a consumer would need to know about —
|
|
29
30
|
# not when a domain's own declarations change.
|
|
@@ -40,7 +41,7 @@ module Hecks
|
|
|
40
41
|
# `bluebook.formerly_known_as` off the live Ruby object) and the
|
|
41
42
|
# meta-validator's cache key is `SHA256(JSON(bluebook.to_h))` — so
|
|
42
43
|
# a fact this method didn't spell was a fact two chapters
|
|
43
|
-
# differing
|
|
44
|
+
# differing only by their old name could hash identically on,
|
|
44
45
|
# same as read-model filters before them. Spelled here for the
|
|
45
46
|
# same reason `version`/`vision` are: a plain field, present
|
|
46
47
|
# (possibly nil) rather than silently absent.
|
|
@@ -50,15 +51,55 @@ module Hecks
|
|
|
50
51
|
policies: many(:policies),
|
|
51
52
|
process_managers: many(:process_managers),
|
|
52
53
|
attaches_to: :attaches_to,
|
|
54
|
+
# A capability this chapter answers (`provides "authorization",
|
|
55
|
+
# grant: "RoleAssignment.Assign", ...`), one row per key, in the
|
|
56
|
+
# order written. Present (possibly empty) on every chapter, the
|
|
57
|
+
# same reading `attaches_to` beside it gives.
|
|
58
|
+
provides: -> { provides.map(&:to_h) },
|
|
53
59
|
canonical_form: -> { Expression::CanonicalForm.table }
|
|
54
60
|
)
|
|
55
61
|
|
|
62
|
+
# **One row of a declared capability** — `verb` is chapter-local
|
|
63
|
+
# ("RoleAssignment.Assign"); `Behaviour::Chapter#provided_verb`
|
|
64
|
+
# qualifies it with the chapter's own name.
|
|
65
|
+
Provision = Struct.new(:capability, :key, :verb, keyword_init: true) do
|
|
66
|
+
# Coerces one declared or reconstructed `provides` row into a `Provision`.
|
|
67
|
+
#
|
|
68
|
+
# @param row [Bluebook::Chapter::Provision, #to_h] a `Provision` already, or
|
|
69
|
+
# anything answering `to_h` with `capability`/`key`/`verb` entries
|
|
70
|
+
# @return [Bluebook::Chapter::Provision] `row` itself if it already is one, else
|
|
71
|
+
# a new `Provision` built from its fields
|
|
72
|
+
def self.from(row)
|
|
73
|
+
return row if row.is_a?(self)
|
|
74
|
+
|
|
75
|
+
fields = row.to_h.transform_keys(&:to_sym)
|
|
76
|
+
new(capability: fields[:capability].to_s, key: fields[:key].to_s, verb: fields[:verb].to_s)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
56
80
|
attr_reader :name, :version, :vision, :aggregates, :policies, :process_managers,
|
|
57
|
-
:classification, :read_models, :ports, :formerly_known_as, :attaches_to
|
|
81
|
+
:classification, :read_models, :ports, :formerly_known_as, :attaches_to, :provides
|
|
58
82
|
|
|
83
|
+
# @param name [String, Symbol] the chapter's declared name
|
|
84
|
+
# @param version [String, Symbol, nil] the chapter's declared business version,
|
|
85
|
+
# such as `"v1"`, or `nil` if it declares none
|
|
86
|
+
# @param vision [String, nil] the chapter's declared vision statement
|
|
87
|
+
# @param aggregates [Array<Bluebook::Aggregate>] the chapter's declared aggregates
|
|
88
|
+
# @param policies [Array<Bluebook::Policy>] every reaction declared across the
|
|
89
|
+
# chapter's own aggregates, hoisted here
|
|
90
|
+
# @param process_managers [Array<Bluebook::ProcessManager>] the chapter's declared
|
|
91
|
+
# process managers
|
|
92
|
+
# @param classification [String, Symbol, nil] whether this chapter is central to
|
|
93
|
+
# its project's own domain model, or `nil` if undeclared
|
|
94
|
+
# @param read_models [Array<Bluebook::ReadModel>] the chapter's declared read models
|
|
95
|
+
# @param formerly_known_as [String, Symbol, nil] an earlier name this domain was
|
|
96
|
+
# known under, or `nil` if it was never renamed
|
|
97
|
+
# @param attaches_to [Array<String, Symbol>] the contexts this chapter attaches to
|
|
98
|
+
# @param provides [Array<Bluebook::Chapter::Provision, Hash>] the capabilities this
|
|
99
|
+
# chapter declares it provides
|
|
59
100
|
def initialize(name:, version: nil, vision: nil, aggregates: [], policies: [],
|
|
60
101
|
process_managers: [], classification: nil, read_models: [], formerly_known_as: nil,
|
|
61
|
-
attaches_to: [])
|
|
102
|
+
attaches_to: [], provides: [])
|
|
62
103
|
@policies = policies
|
|
63
104
|
@process_managers = process_managers
|
|
64
105
|
@name = name.to_s
|
|
@@ -71,6 +112,7 @@ module Hecks
|
|
|
71
112
|
@classification = classification&.to_s
|
|
72
113
|
@formerly_known_as = formerly_known_as&.to_s
|
|
73
114
|
@attaches_to = Array(attaches_to).map(&:to_s)
|
|
115
|
+
@provides = Array(provides).map { |row| Provision.from(row) }
|
|
74
116
|
settle
|
|
75
117
|
end
|
|
76
118
|
end
|
|
@@ -10,14 +10,12 @@ module Hecks
|
|
|
10
10
|
include Hecks::IR
|
|
11
11
|
include Behaviour::Mutation
|
|
12
12
|
|
|
13
|
-
# `sign:` — item #5 of the whole-project table-unification survey
|
|
14
|
-
# `increment`/`decrement`'s own +1/-1
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# (Runtime::CommandRules::Arithmetic::MUTATION_OPS, held equal to
|
|
20
|
-
# Vocabulary::MutationOp by spec/vocabulary_conformance_spec.rb).
|
|
13
|
+
# `sign:` — item #5 of the whole-project table-unification survey:
|
|
14
|
+
# `increment`/`decrement`'s own +1/-1 comes from `Vocabulary::MutationOp`
|
|
15
|
+
# here, the same table `Runtime::CommandRules::Arithmetic::MUTATION_OPS`
|
|
16
|
+
# already reads on the Ruby runtime side, rather than a ternary on the op
|
|
17
|
+
# name re-deriving it independently in each of two Rust codegen scripts
|
|
18
|
+
# (rust/project/mutations.rb, rust/codegen/src/mutations.rs).
|
|
21
19
|
# Reads `Vocabulary::MutationOp` directly (plain data, no framework
|
|
22
20
|
# dependency — safe during parsing, same reason `RuleReference`'s
|
|
23
21
|
# own bootstrap concerns don't apply here) rather than requiring
|
|
@@ -25,20 +23,29 @@ module Hecks
|
|
|
25
23
|
# real layering inversion (runtime depends on bluebook, not the
|
|
26
24
|
# reverse). "" (not nil) for ops with no sign, matching every other
|
|
27
25
|
# optional IR text field's own absent-is-empty-string convention.
|
|
26
|
+
#
|
|
27
|
+
# @param oper [String, Symbol] the mutation's operation name, such as
|
|
28
|
+
# `"increment"` or `"decrement"`
|
|
29
|
+
# @return [String] the operation's sign from `Vocabulary::MutationOp`
|
|
30
|
+
# (`"+"` or `"-"`), or `""` when the op has none or is not found
|
|
28
31
|
def self.sign_for(oper)
|
|
29
32
|
Hecks::Vocabulary.rows("MutationOp").find { |row| row["name"] == oper.to_s }&.fetch("sign", "") || ""
|
|
30
33
|
end
|
|
31
34
|
|
|
32
35
|
emits_ir(target: :target, op: :op, sign: -> { Mutation.sign_for(op) })
|
|
33
36
|
|
|
34
|
-
#
|
|
35
|
-
#
|
|
37
|
+
# The one genuinely branching emission in the model: an append (or a
|
|
38
|
+
# delegate — `CommandBuilder#delegates_to`'s own comment gives the
|
|
36
39
|
# full reasoning for reusing this exact wire shape rather than
|
|
37
40
|
# inventing a parallel one) binds several fields at once and carries
|
|
38
41
|
# `fields:`, everything else carries a single `source:`. Declared
|
|
39
42
|
# emission covers the fixed head; `super` supplies it and this adds
|
|
40
43
|
# the tail, which is why a construct with a variable shape needs no
|
|
41
44
|
# new mixin API.
|
|
45
|
+
# @return [Hash{Symbol => Object}] the declared emission — `super`'s fixed
|
|
46
|
+
# head, plus `fields:` (a Hash of bound field values) for an
|
|
47
|
+
# append-shaped op, or `source:` (`classified_source`'s own result) for
|
|
48
|
+
# any other
|
|
42
49
|
def to_h
|
|
43
50
|
return super.merge(fields: appended_fields) if [:append, :delegate, :corrects].include?(op)
|
|
44
51
|
|
|
@@ -46,9 +53,9 @@ module Hecks
|
|
|
46
53
|
end
|
|
47
54
|
end
|
|
48
55
|
|
|
49
|
-
# A command, as a
|
|
56
|
+
# A command, as a Ruby class.
|
|
50
57
|
#
|
|
51
|
-
#
|
|
58
|
+
# Not nested as a constant, and that is a finding rather than a shortcut. A
|
|
52
59
|
# command and a value object may legitimately share a name inside one
|
|
53
60
|
# aggregate — the language does it six times, and means it: the command
|
|
54
61
|
# `Argument` is the verb that appends to the `arguments` list whose element
|
|
@@ -56,10 +63,10 @@ module Hecks
|
|
|
56
63
|
# So `Bluebook::Command::Argument` cannot be both, and a single constant
|
|
57
64
|
# namespace cannot index a kind-ambiguous name. The same follows for
|
|
58
65
|
# `hecks_fqn` : `Bluebook::Command.Argument` names both, which is why the judge's
|
|
59
|
-
# ids only work per-category, each in its own repository.
|
|
60
|
-
# (
|
|
66
|
+
# ids only work per-category, each in its own repository. Identity is
|
|
67
|
+
# (kind, FQN), not fqn.
|
|
61
68
|
#
|
|
62
|
-
# It is a declaration holder anyway, because that is where the
|
|
69
|
+
# It is a declaration holder anyway, because that is where the edges live.
|
|
63
70
|
# `acts_on` answers with the owning construct itself — the Aggregate,
|
|
64
71
|
# or the entity holder for a piece's verb — rather than the name of one.
|
|
65
72
|
# The invocation door (`pizza.add_topping`) is the facade's business, a
|
|
@@ -79,8 +86,8 @@ module Hecks
|
|
|
79
86
|
ensures: -> { ensures.map { |rule| Expression::AstJson.rule_row(rule) } },
|
|
80
87
|
mutations: many(:mutations),
|
|
81
88
|
emits: :emits,
|
|
82
|
-
#
|
|
83
|
-
# 0025 — "lifecycle state becomes a command guard") — a
|
|
89
|
+
# The lifecycle state this command is admissible from (S10, ADR
|
|
90
|
+
# 0025 — "lifecycle state becomes a command guard") — a guard,
|
|
84
91
|
# not a transition: `command "Debit", from: "open"` replaces
|
|
85
92
|
# `given("account is open") { status == "open" }`, checked
|
|
86
93
|
# against the owning construct's own lifecycle field the same
|
|
@@ -95,6 +102,29 @@ module Hecks
|
|
|
95
102
|
attr_reader :role, :goal, :attributes, :givens, :ensures, :mutations, :emits, :references,
|
|
96
103
|
:from, :provenance
|
|
97
104
|
|
|
105
|
+
# Mints one command as its own anonymous class, a subclass of whichever
|
|
106
|
+
# `Command`-descended class `declare` is called on.
|
|
107
|
+
#
|
|
108
|
+
# @param name [String, Symbol] the command's declared name
|
|
109
|
+
# @param role [String, nil] the command's declared role text
|
|
110
|
+
# @param goal [String, nil] the command's declared goal text
|
|
111
|
+
# @param attributes [Array<Bluebook::Attribute>] the command's declared
|
|
112
|
+
# arguments
|
|
113
|
+
# @param givens [Array<Bluebook::Given>] the command's declared
|
|
114
|
+
# preconditions
|
|
115
|
+
# @param ensures [Array<Bluebook::Given>] the command's declared
|
|
116
|
+
# postconditions
|
|
117
|
+
# @param mutations [Array<Bluebook::Mutation>] the state changes this
|
|
118
|
+
# command applies
|
|
119
|
+
# @param emits [Array<String>] the event names this command may emit
|
|
120
|
+
# @param references [String, Symbol, nil] the aggregate name this verb's
|
|
121
|
+
# `reference_to` addresses, or `nil` for a verb declared on the
|
|
122
|
+
# aggregate it acts on
|
|
123
|
+
# @param from [String, Array<String>, nil] the lifecycle state(s) this
|
|
124
|
+
# command is admissible from, or `nil` for no such guard
|
|
125
|
+
# @param provenance [Object, nil] the command's declared canonical
|
|
126
|
+
# source, captured exactly as written, or `nil` if it declares none
|
|
127
|
+
# @return [Class] the new command class
|
|
98
128
|
def declare(name:, role: nil, goal: nil, attributes: [], givens: [], ensures: [],
|
|
99
129
|
mutations: [], emits: [], references: nil, from: nil, provenance: nil)
|
|
100
130
|
verb = Class.new(self)
|
|
@@ -105,6 +135,21 @@ module Hecks
|
|
|
105
135
|
verb
|
|
106
136
|
end
|
|
107
137
|
|
|
138
|
+
# Assigns what the language declares, then hands off to the behaviour's
|
|
139
|
+
# own `settle` — indexing attributes, the one thing not derivable from
|
|
140
|
+
# the declaration.
|
|
141
|
+
#
|
|
142
|
+
# @param role [String, nil] see `declare`
|
|
143
|
+
# @param goal [String, nil] see `declare`
|
|
144
|
+
# @param attributes [Array<Bluebook::Attribute>] see `declare`
|
|
145
|
+
# @param givens [Array<Bluebook::Given>] see `declare`
|
|
146
|
+
# @param ensures [Array<Bluebook::Given>] see `declare`
|
|
147
|
+
# @param mutations [Array<Bluebook::Mutation>] see `declare`
|
|
148
|
+
# @param emits [Array<String>] see `declare`
|
|
149
|
+
# @param references [String, nil] see `declare`
|
|
150
|
+
# @param from [String, Array<String>, nil] see `declare`
|
|
151
|
+
# @param provenance [Object, nil] see `declare`
|
|
152
|
+
# @return [Class] self, once attributes are indexed
|
|
108
153
|
def absorb(role:, goal:, attributes:, givens:, ensures:, mutations:, emits:, references:,
|
|
109
154
|
from: nil, provenance: nil)
|
|
110
155
|
@role = role
|