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
|
@@ -3,34 +3,35 @@ module Hecks
|
|
|
3
3
|
module MetaValidator
|
|
4
4
|
# Offers every declaration in a built bluebook to the meta-domain.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# —
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
6
|
+
# There are no hand-written branches, one per category: that shape's
|
|
7
|
+
# real cost is any verb the language declares that the judge never
|
|
8
|
+
# offers — a branch nobody wrote for it — going undetected, since a
|
|
9
|
+
# branch that does not exist cannot fail. `Command.Argument` and
|
|
10
|
+
# `ValueObject.Field` were both admitted that way once, undetected;
|
|
11
|
+
# every rule hanging off them was decoration until offered for real.
|
|
12
12
|
#
|
|
13
|
-
# So
|
|
14
|
-
#
|
|
15
|
-
# each list through the command that appends to it, then each child. A
|
|
16
|
-
# in the plan with no offer is now impossible — there is no
|
|
17
|
-
# which to forget one, and spec/judge_coverage_spec
|
|
13
|
+
# So instead the judge walks: it reads the plan the language makes of
|
|
14
|
+
# itself (Plan), and for each node offers the creating command, then
|
|
15
|
+
# each list through the command that appends to it, then each child. A
|
|
16
|
+
# verb in the plan with no offer is now impossible — there is no
|
|
17
|
+
# branch left in which to forget one, and spec/judge_coverage_spec
|
|
18
|
+
# holds it to that.
|
|
18
19
|
#
|
|
19
|
-
# What is
|
|
20
|
+
# What is not uniform lives in Readings, and only where the IR's shape
|
|
20
21
|
# differs from the language's. Naming differences do not appear at all: the
|
|
21
22
|
# language spells its fields as the IR spells them.
|
|
22
23
|
#
|
|
23
|
-
# Only the
|
|
24
|
+
# Only the dispatch half of the round trip lives here. Reconstruction is the
|
|
24
25
|
# experiment's business ; judging does not need it.
|
|
25
26
|
class Judge
|
|
26
27
|
include Readings
|
|
27
28
|
|
|
28
|
-
# Children offered
|
|
29
|
+
# Children offered before the parent's own lists, in this order. An
|
|
29
30
|
# attribute's type is offered as the id of the thing it names, so the
|
|
30
31
|
# value objects have to exist before anything that can name one — an
|
|
31
|
-
# aggregate's own attributes,
|
|
32
|
+
# aggregate's own attributes, and an entity's own (M13: an entity is its
|
|
32
33
|
# own root, repeating the aggregate's whole shape one level down, so its
|
|
33
|
-
# attributes resolve against the
|
|
34
|
+
# attributes resolve against the same value-object pool). ValueObject
|
|
34
35
|
# first, Entity second, so an entity's own attributes are never offered
|
|
35
36
|
# before the value objects they may reference exist — a self-hosting
|
|
36
37
|
# casualty found live: the meta-grammar's own Handler/Dispatch/Member/
|
|
@@ -43,7 +44,7 @@ module Hecks
|
|
|
43
44
|
# see `detail_node`'s own use of this constant, below.
|
|
44
45
|
EAGER_CHILDREN = { "Aggregate" => %w[ValueObject Entity] }.freeze
|
|
45
46
|
|
|
46
|
-
# Categories an
|
|
47
|
+
# Categories an entity declares as well as an aggregate. The IR reuses
|
|
47
48
|
# Command and Query for a piece's own commands and queries, so the
|
|
48
49
|
# language reuses Command and Query — and the plan cannot express a second
|
|
49
50
|
# parent, because a category's parent is derived from the one `*_id` argument
|
|
@@ -52,15 +53,17 @@ module Hecks
|
|
|
52
53
|
|
|
53
54
|
attr_reader :refusals
|
|
54
55
|
|
|
55
|
-
#
|
|
56
|
+
# **The records survive the verdict**.
|
|
56
57
|
#
|
|
57
|
-
# Judging a bluebook and
|
|
58
|
+
# Judging a bluebook and holding one differ by exactly this: whether anyone
|
|
58
59
|
# keeps the runtime the declarations were dispatched into. Nobody did, so
|
|
59
60
|
# `spec/round_trip_spec` reached the records by `Judge.allocate` and four
|
|
60
61
|
# `instance_variable_set` calls. Reading the chapter back is the point now,
|
|
61
62
|
# not a curiosity, so the runtime is simply readable.
|
|
62
63
|
attr_reader :runtime
|
|
63
64
|
|
|
65
|
+
# @param bluebook [Object] a built bluebook chapter graph, as
|
|
66
|
+
# `MetaValidator.call` receives it
|
|
64
67
|
def initialize(bluebook)
|
|
65
68
|
@bluebook = bluebook
|
|
66
69
|
@refusals = []
|
|
@@ -71,7 +74,7 @@ module Hecks
|
|
|
71
74
|
|
|
72
75
|
private
|
|
73
76
|
|
|
74
|
-
#
|
|
77
|
+
# Absent is not empty. The rules read "if you declare it, declare
|
|
75
78
|
# something" — a description never given is legal. Passing "" for a nil
|
|
76
79
|
# turns every one of them into "you must declare it".
|
|
77
80
|
#
|
|
@@ -85,7 +88,7 @@ module Hecks
|
|
|
85
88
|
{ value: text.to_s }
|
|
86
89
|
end
|
|
87
90
|
|
|
88
|
-
# A
|
|
91
|
+
# A reference is an ID, and an ID is a scalar.
|
|
89
92
|
#
|
|
90
93
|
# Every other field goes to the meta-domain as a one-field value object,
|
|
91
94
|
# because that is what it is. A reference is not: it carries the id of a
|
|
@@ -105,31 +108,45 @@ module Hecks
|
|
|
105
108
|
yield
|
|
106
109
|
rescue Runtime::GivenNotMet, Runtime::InvariantViolation,
|
|
107
110
|
Runtime::TypeMismatch, Runtime::NotFound => e
|
|
108
|
-
# NotFound is a
|
|
109
|
-
# its value object, so "no ValueObject with id …"
|
|
111
|
+
# NotFound is a verdict, not noise. An attribute's type is a reference to
|
|
112
|
+
# its value object, so "no ValueObject with id …" is the rule `attributes
|
|
110
113
|
# must use value-object types` refusing.
|
|
111
114
|
@refusals << "#{label}: #{e.message}"
|
|
112
115
|
rescue Runtime::UnknownVerb
|
|
113
116
|
nil
|
|
114
117
|
end
|
|
115
118
|
|
|
119
|
+
# QualityControl BUG#125 — every dispatch the judge makes into the
|
|
120
|
+
# meta-domain goes through here, and only here, so this is the one
|
|
121
|
+
# place `Runtime::Value.judge_bootstrapping` needs to wrap: it tells
|
|
122
|
+
# `Value::Coercion#check_scalar_shapes` this construction is the
|
|
123
|
+
# language's own self-hosted grammar walk, not a real domain's
|
|
124
|
+
# command, so a String-typed meta-grammar field (`Normalise`'s
|
|
125
|
+
# `position`, a `RuleText` — `appends`' generic walk-index handling
|
|
126
|
+
# collides with that field's own name, see the flag's own comment
|
|
127
|
+
# in coercion.rb) may still arrive as the raw Integer `appends`
|
|
128
|
+
# hands it. Scoped to exactly this method: nothing outside a judge's
|
|
129
|
+
# own dispatch ever runs inside it, so an ordinary domain command
|
|
130
|
+
# (a real caller's own PieceId, Money, …) is never affected.
|
|
116
131
|
def send_to(verb, label, to: nil, **payload)
|
|
117
|
-
|
|
132
|
+
Runtime::Value.judge_bootstrapping do
|
|
133
|
+
offer(label) { @runtime.dispatch(verb, to: to, with: args(payload)) }
|
|
134
|
+
end
|
|
118
135
|
end
|
|
119
136
|
|
|
120
|
-
#
|
|
137
|
+
# The receiver, spelled the way a real caller addresses it.
|
|
121
138
|
#
|
|
122
139
|
# `receiver` is an internal accumulator — it tracks the aggregate this
|
|
123
|
-
# walk is inside
|
|
140
|
+
# walk is inside and, once it crosses into a real entity-owned category
|
|
124
141
|
# (Member/Handler/Dispatch — S17, ADR 0026), every entity hop on top of
|
|
125
142
|
# it. But `Routing.envelope`/`parse_envelope_hash` do not want that
|
|
126
143
|
# shape restated when there is no entity to route to: a plain aggregate
|
|
127
144
|
# command is addressed by its bare id (`Facade::Handle#dispatch` — "to:
|
|
128
145
|
# @id" — and `CommandRequest`'s own header: "aggregate command: { to:
|
|
129
146
|
# "record-id", ... }"), never `{aggregate:, entities: []}` — that hash
|
|
130
|
-
# is the
|
|
147
|
+
# is the entity route's own shape, degenerate with nothing in it.
|
|
131
148
|
#
|
|
132
|
-
# So
|
|
149
|
+
# So this is the one place `receiver` turns into a `to:` value: bare
|
|
133
150
|
# when there is no entity hop (the overwhelming common case — every
|
|
134
151
|
# category but those three), the full envelope only when there
|
|
135
152
|
# genuinely is one.
|
|
@@ -144,19 +161,19 @@ module Hecks
|
|
|
144
161
|
detail_node("Bluebook", @bluebook, nil, 0)
|
|
145
162
|
end
|
|
146
163
|
|
|
147
|
-
#
|
|
164
|
+
# Declared before detailed, for every set of siblings.
|
|
148
165
|
#
|
|
149
|
-
#
|
|
150
|
-
#
|
|
151
|
-
#
|
|
152
|
-
#
|
|
153
|
-
# to be declared earlier in the file
|
|
154
|
-
# written above Account.
|
|
166
|
+
# Offering a node whole — declared, then its lists, then its children
|
|
167
|
+
# — one sibling at a time would mean an aggregate's attributes are
|
|
168
|
+
# offered before its later siblings exist, so an attribute that
|
|
169
|
+
# points at another aggregate could only resolve if that aggregate
|
|
170
|
+
# happens to be declared earlier in the file: banking would survive
|
|
171
|
+
# on luck, Customer written above Account.
|
|
155
172
|
#
|
|
156
173
|
# So siblings are declared in one pass and detailed in a second. It is the
|
|
157
174
|
# same ordering the walk already used one level down — value objects before
|
|
158
175
|
# the attributes that name them — lifted to the level above, and it is what
|
|
159
|
-
# lets a reference be a
|
|
176
|
+
# lets a reference be a reference rather than a string nobody can check.
|
|
160
177
|
def declare_node(category, node, parent_id, index, extra = {}, receiver: nil)
|
|
161
178
|
plan = @plan.category(category)
|
|
162
179
|
return unless plan
|
|
@@ -169,34 +186,34 @@ module Hecks
|
|
|
169
186
|
return unless plan
|
|
170
187
|
|
|
171
188
|
id = identify(category, parent_id, node, index)
|
|
172
|
-
# `extra` is the
|
|
173
|
-
# ancestor above this node (Handler's own `event_type`
|
|
189
|
+
# `extra` is the cumulative identity of every entity-owned
|
|
190
|
+
# ancestor above this node (Handler's own `event_type` and
|
|
174
191
|
# ProcessManager's own `bluebook`/`name`, by the time Dispatch
|
|
175
192
|
# is reached — S17, ADR 0026's two-level chain). `identity`
|
|
176
|
-
# adds
|
|
193
|
+
# adds this node's own on top — computed for every category,
|
|
177
194
|
# entity-owned or not, because a node need not be entity-owned
|
|
178
|
-
# itself to
|
|
179
|
-
# its `aggregate:`/`name:`) — it is simply what
|
|
195
|
+
# itself to own one (ValueObject isn't, and Member still needs
|
|
196
|
+
# its `aggregate:`/`name:`) — it is simply what this node's own
|
|
180
197
|
# `identified_by` resolves to, the same fields `identify` two
|
|
181
|
-
# lines up already derives the joined id
|
|
198
|
+
# lines up already derives the joined id from.
|
|
182
199
|
#
|
|
183
|
-
# `own` is the
|
|
184
|
-
# only when
|
|
200
|
+
# `own` is the subset actually spent on a dispatch payload —
|
|
201
|
+
# only when this category is itself entity-owned, since an
|
|
185
202
|
# ordinary category (Command's own "Rule"/"Argument", offered
|
|
186
|
-
# through the
|
|
203
|
+
# through the same `extra` slot for a different reason, see
|
|
187
204
|
# `within_entity` below) locates the record it attaches to
|
|
188
205
|
# through the parent id `id:` already carries, and merging
|
|
189
|
-
# unrecognized `aggregate:`/`entity_id:` into
|
|
206
|
+
# unrecognized `aggregate:`/`entity_id:` into their payload
|
|
190
207
|
# would have the runtime refuse them for an argument they
|
|
191
208
|
# never declared.
|
|
192
209
|
identity = extra.merge(node_identity(plan, category, node, index, parent_id))
|
|
193
210
|
receiver ||= { aggregate: id, entities: [] }
|
|
194
211
|
eager, later = children_of(category).partition { |child| eager?(category, child) }
|
|
195
|
-
#
|
|
212
|
+
# Ordered as `EAGER_CHILDREN` declares, not as `children_of` happens to
|
|
196
213
|
# list them — `children_of` reads `@plan.names`, whose own order is an
|
|
197
214
|
# accident of which .bluebook file registered which category first,
|
|
198
215
|
# never a promise about which of two eager children exists before the
|
|
199
|
-
# other. `EAGER_CHILDREN`'s own array
|
|
216
|
+
# other. `EAGER_CHILDREN`'s own array is that promise (ValueObject
|
|
200
217
|
# before Entity), so the walk keeps only what this parent actually
|
|
201
218
|
# has, in the order the constant states — see that constant's own
|
|
202
219
|
# comment for the bug this exact reordering fixes.
|
|
@@ -204,12 +221,12 @@ module Hecks
|
|
|
204
221
|
|
|
205
222
|
eager.each { |child| walk_all(child, node, id, entity_child_extra(child, identity), receiver: receiver) }
|
|
206
223
|
setters(plan, category, node, receiver)
|
|
207
|
-
#
|
|
208
|
-
# walks an aggregate's
|
|
224
|
+
# Before `appends`, not after — the same reason `EAGER_CHILDREN`
|
|
225
|
+
# walks an aggregate's own entities before its own attributes
|
|
209
226
|
# (M13): a piece nested inside a piece (Handler's own
|
|
210
227
|
# `dispatches, list_of(Dispatch)` — S17, ADR 0026) must exist
|
|
211
228
|
# before this piece's own attribute list can reference it as a
|
|
212
|
-
#
|
|
229
|
+
# held entity, the same way `Account#ledger` needs Account's own
|
|
213
230
|
# entities walked eagerly. `nest_entities` is a no-op for every
|
|
214
231
|
# category but "Entity" (its own early return), so reordering it
|
|
215
232
|
# ahead of `appends` costs nothing for anything else that walks
|
|
@@ -221,11 +238,11 @@ module Hecks
|
|
|
221
238
|
sealers(plan, category, receiver)
|
|
222
239
|
end
|
|
223
240
|
|
|
224
|
-
#
|
|
225
|
-
# own dotted dispatch needs every
|
|
241
|
+
# What a child's own `extra` starts from. An entity-owned child's
|
|
242
|
+
# own dotted dispatch needs every ancestor's identity, which is
|
|
226
243
|
# exactly `identity` — already accumulated one level at a time by
|
|
227
244
|
# `detail_node` itself (regardless of whether each ancestor is
|
|
228
|
-
#
|
|
245
|
+
# itself entity-owned — ValueObject contributes its own `aggregate:
|
|
229
246
|
# `/`name:` to Member's payload despite being an ordinary top-
|
|
230
247
|
# level category), so there is nothing left to re-derive here. An
|
|
231
248
|
# ordinary child (one with a real top-level aggregate of its own
|
|
@@ -234,15 +251,15 @@ module Hecks
|
|
|
234
251
|
@plan.category(child)&.entity_owned ? identity : {}
|
|
235
252
|
end
|
|
236
253
|
|
|
237
|
-
#
|
|
254
|
+
# One node's own identity, read off its own declaration — S17,
|
|
238
255
|
# ADR 0026. Three cases, the same three `identify`/`identity_part`
|
|
239
256
|
# already resolve one level up, unified here because a chain now
|
|
240
257
|
# walks more than one level (Handler -> Dispatch) and each level
|
|
241
|
-
# needs the
|
|
258
|
+
# needs the same three answered about itself, not just the first:
|
|
242
259
|
#
|
|
243
260
|
# the parent link (plan.parent_key) -> `parent_id`, the id
|
|
244
261
|
# the walk already carries in from one level up
|
|
245
|
-
# a walk-minted one (POSITION)
|
|
262
|
+
# a walk-minted one (`POSITION`) -> the walk index itself ;
|
|
246
263
|
# never a stored field (Member's own header:
|
|
247
264
|
# "position is not a mint — it is read straight
|
|
248
265
|
# out of the source file")
|
|
@@ -251,12 +268,12 @@ module Hecks
|
|
|
251
268
|
# (Handler's own `event_type`, Dispatch's own
|
|
252
269
|
# `command_name`)
|
|
253
270
|
#
|
|
254
|
-
# `carried` still decides bare-vs-wrapped the normal way ; POSITION
|
|
271
|
+
# `carried` still decides bare-vs-wrapped the normal way ; `POSITION`
|
|
255
272
|
# is the one case with no verb to ask `carried` about (`plan.
|
|
256
273
|
# declare` is always nil for an entity-owned category — Plan#read's
|
|
257
274
|
# own comment says why), so it is minted straight as a value object,
|
|
258
275
|
# matching exactly what `declare`'s own field loop already mints a
|
|
259
|
-
# POSITION field as.
|
|
276
|
+
# `POSITION` field as.
|
|
260
277
|
def node_identity(plan, category, node, index, parent_id)
|
|
261
278
|
plan.identity_paths.each_with_object({}) do |path, fields|
|
|
262
279
|
head = path.to_s.split(".").first
|
|
@@ -271,9 +288,9 @@ module Hecks
|
|
|
271
288
|
end
|
|
272
289
|
end
|
|
273
290
|
|
|
274
|
-
#
|
|
291
|
+
# The full dotted prefix a category's own verbs hang off — the
|
|
275
292
|
# plain name for an ordinary category (its own top-level
|
|
276
|
-
# aggregate reaches every verb bare), or its
|
|
293
|
+
# aggregate reaches every verb bare), or its parent's own prefix
|
|
277
294
|
# with this category's name appended, for an entity-owned one.
|
|
278
295
|
# Dispatch's own parent, Handler, is itself entity-owned (S17,
|
|
279
296
|
# ADR 0026's two-level chain — `ProcessManager.Handler.Dispatch`),
|
|
@@ -284,9 +301,9 @@ module Hecks
|
|
|
284
301
|
"#{dotted_prefix(@plan.category(plan.parent))}.#{plan.name}"
|
|
285
302
|
end
|
|
286
303
|
|
|
287
|
-
#
|
|
304
|
+
# Entity-owned categories have no top-level aggregate for the runtime
|
|
288
305
|
# to route a bare verb into any more — `Member`'s own "Pair" reaches
|
|
289
|
-
# the runtime as `ValueObject.Member.Pair`, and a
|
|
306
|
+
# the runtime as `ValueObject.Member.Pair`, and a nested one
|
|
290
307
|
# (`Dispatch`, inside `Handler`) reaches it as `ProcessManager.
|
|
291
308
|
# Handler.Dispatch.Bind` — the dotted shape `EntityInterpreter#call`
|
|
292
309
|
# already splits any real entity's own verb into, one hop per
|
|
@@ -312,12 +329,12 @@ module Hecks
|
|
|
312
329
|
end
|
|
313
330
|
end
|
|
314
331
|
|
|
315
|
-
# A piece's commands and queries, addressed under the
|
|
332
|
+
# A piece's commands and queries, addressed under the piece so two commands
|
|
316
333
|
# of the same name on an aggregate and on one of its entities cannot collide,
|
|
317
334
|
# while `aggregate` still names the aggregate the reference resolves
|
|
318
335
|
# against and `entity_id` says which piece declared it. `entity_id`
|
|
319
|
-
# keeps its own `_id` — an
|
|
320
|
-
# never touched by ADR 0025's rename (only the
|
|
336
|
+
# keeps its own `_id` — an explicit `as:` on `reference_to Entity`,
|
|
337
|
+
# never touched by ADR 0025's rename (only the default, un-aliased
|
|
321
338
|
# mint dropped the suffix; `aggregate` did precisely because
|
|
322
339
|
# `Command#reference_to Aggregate`/`Query#reference_to Aggregate`
|
|
323
340
|
# carry no `as:` of their own).
|
|
@@ -333,26 +350,26 @@ module Hecks
|
|
|
333
350
|
end
|
|
334
351
|
end
|
|
335
352
|
|
|
336
|
-
#
|
|
353
|
+
# An entity may nest further entities — S17, ADR 0026's own words:
|
|
337
354
|
# "That is what `entity` is for, and `entity` is declared by the
|
|
338
355
|
# language and used zero times in it." `Dispatch`, inside
|
|
339
|
-
# `Handler`, is the first real use. The
|
|
356
|
+
# `Handler`, is the first real use. The generic "Entity" category
|
|
340
357
|
# cannot express this through `children_of`/`EAGER_CHILDREN` the
|
|
341
358
|
# way Aggregate's own entities/value_objects can — there is only
|
|
342
|
-
#
|
|
359
|
+
# one "Entity" Plan category, describing what any entity looks
|
|
343
360
|
# like, not one per nesting level — so this recurses by hand,
|
|
344
361
|
# the same special case `within_entity` (above) already is for
|
|
345
362
|
# Command/Query.
|
|
346
363
|
#
|
|
347
364
|
# `owner` is the field this repurposes — `entity.bluebook`
|
|
348
365
|
# declares it (`attribute :owner, EntityText`) and it has held
|
|
349
|
-
# exactly one value since ADR 0025's rename: the
|
|
350
|
-
# `aggregate` already carries, kept as a wrapped-text
|
|
366
|
+
# exactly one value since ADR 0025's rename: the same id
|
|
367
|
+
# `aggregate` already carries, kept as a wrapped-text copy,
|
|
351
368
|
# never read back anywhere else in this codebase (grep finds no
|
|
352
|
-
# second reference). For a
|
|
353
|
-
# diverge — `aggregate` stays the
|
|
369
|
+
# second reference). For a nested entity, the two finally
|
|
370
|
+
# diverge — `aggregate` stays the root (Dispatch resolves
|
|
354
371
|
# exactly the way any other Entity record does, by its root
|
|
355
|
-
# aggregate), and `owner` becomes
|
|
372
|
+
# aggregate), and `owner` becomes this entity's own direct
|
|
356
373
|
# parent (Handler, not ProcessManager) — which is exactly the
|
|
357
374
|
# fact `Reconstruction#direct_entities` needs to tell a
|
|
358
375
|
# root-level entity apart from a nested one sharing the same
|
|
@@ -367,13 +384,14 @@ module Hecks
|
|
|
367
384
|
})
|
|
368
385
|
end
|
|
369
386
|
|
|
370
|
-
#
|
|
387
|
+
# Where it sits among its siblings is a fact about the walk, not about the
|
|
371
388
|
# node : a command does not know it is the third command on its aggregate.
|
|
372
389
|
# The walk knows, so the walk supplies it, and every other field still
|
|
373
|
-
# comes from the node.
|
|
374
|
-
# meta store
|
|
375
|
-
# ask ordered any other way would
|
|
376
|
-
# the one reader that must have the
|
|
390
|
+
# comes from the node. Without this, declaration order would survive only
|
|
391
|
+
# because the meta store happens to iterate in insertion order — an
|
|
392
|
+
# accident that an ask ordered any other way would take away, and
|
|
393
|
+
# Reconstruction is the one reader that must have the source's order
|
|
394
|
+
# rather than a stable one.
|
|
377
395
|
# `private` above has no effect on a constant; kept here anyway,
|
|
378
396
|
# beside the method that reads it, for the narrative.
|
|
379
397
|
# rubocop:disable-next Lint/UselessConstantScoping
|
|
@@ -416,7 +434,7 @@ module Hecks
|
|
|
416
434
|
plan.appends.each do |list_name, append|
|
|
417
435
|
rows_for(category, list_name, node).each_with_index do |row, index|
|
|
418
436
|
chosen = append_for(category, list_name, append, row, node)
|
|
419
|
-
# `position`
|
|
437
|
+
# `position` is the walk index here exactly as it is in `declare` —
|
|
420
438
|
# an appended element that names its position (ValueObject.Member,
|
|
421
439
|
# S17) is ordered by where the walk found it, never by a field the
|
|
422
440
|
# row happens to hold.
|
|
@@ -436,15 +454,15 @@ module Hecks
|
|
|
436
454
|
end
|
|
437
455
|
end
|
|
438
456
|
|
|
439
|
-
#
|
|
457
|
+
# Which aggregate owns the value objects an attribute's type may
|
|
440
458
|
# resolve against. An aggregate owns its own — `id` already names
|
|
441
459
|
# it. An entity never has value objects of its own (Entity
|
|
442
460
|
# deliberately never answers `value_object` — see entity.rb's own
|
|
443
|
-
# comment on why); its attributes read the
|
|
461
|
+
# comment on why); its attributes read the same pool its
|
|
444
462
|
# enclosing aggregate declares, one level up the construct tree
|
|
445
463
|
# no matter how many entities deep this attribute is nested —
|
|
446
464
|
# `parent_id` names it because `detail_node`/`nest_entities`
|
|
447
|
-
# thread the
|
|
465
|
+
# thread the root aggregate's id down as `parent_id` at every
|
|
448
466
|
# entity level, never the direct (possibly entity) parent.
|
|
449
467
|
def owning_aggregate_ref(category, id, parent_id)
|
|
450
468
|
category == "Entity" ? parent_id : id
|
|
@@ -455,30 +473,30 @@ module Hecks
|
|
|
455
473
|
plan.sealers.each { |verb| send_to("Bluebook::#{verb_for(plan, verb)}", id, to: address(receiver)) }
|
|
456
474
|
end
|
|
457
475
|
|
|
458
|
-
# An aggregate's or an entity's attribute names its value object by
|
|
476
|
+
# An aggregate's or an entity's attribute names its value object by type,
|
|
459
477
|
# and the language models that as a reference — so the type is offered as
|
|
460
478
|
# the value object's own id. This is the rule "attributes must use
|
|
461
479
|
# value-object types", enforced by reference resolution rather than by a
|
|
462
|
-
# predicate — for a
|
|
480
|
+
# predicate — for a head's own attributes, aggregate or entity alike: an
|
|
463
481
|
# entity is its own root, repeating the aggregate's whole shape one level
|
|
464
482
|
# down (entity.rb's own words), and an undeclared type on an entity's
|
|
465
483
|
# attribute must fail the same reference resolution an aggregate's own
|
|
466
484
|
# does, not go unchecked because only "Aggregate.attributes" was ever
|
|
467
485
|
# asked.
|
|
468
|
-
# An attribute's type is offered as the ID
|
|
486
|
+
# An attribute's type is offered as the ID of the thing it names, so the
|
|
469
487
|
# language resolves it as a reference and "the type is declared" costs no
|
|
470
488
|
# predicate. Three kinds, three ids: a value object and an entity both hang
|
|
471
489
|
# off this aggregate, so they share its prefix; another aggregate's head
|
|
472
490
|
# hangs off the chapter.
|
|
473
491
|
def cell(category, list_name, row, field, id, append, aggregate_id)
|
|
474
492
|
value = row_value(row, field)
|
|
475
|
-
# A default keeps its
|
|
493
|
+
# A default keeps its type by being written as a literal — 0.0 rather than
|
|
476
494
|
# "0.0" — because the language holds it as text and text alone forgets.
|
|
477
495
|
return encode_literal(value) if field == :default
|
|
478
496
|
return value unless field == :type
|
|
479
|
-
# A
|
|
497
|
+
# A reference names another head wherever it is written — on a head, on
|
|
480
498
|
# a command, on a piece, on an ask — so it is offered as that head's
|
|
481
|
-
# id in all four. Only a
|
|
499
|
+
# id in all four. Only a head's own attributes additionally qualify
|
|
482
500
|
# an ordinary type into a value object's id ; a command argument's
|
|
483
501
|
# type is text and stays text.
|
|
484
502
|
return points_at(row, id) if append.verb == "Reference"
|
|
@@ -487,7 +505,7 @@ module Hecks
|
|
|
487
505
|
Naming.identity([owning_aggregate_id(aggregate_id, value), value])
|
|
488
506
|
end
|
|
489
507
|
|
|
490
|
-
# A
|
|
508
|
+
# A head's own attributes — an aggregate's, or an entity's (its own root,
|
|
491
509
|
# one level down). Every other "attributes" list belongs to something that
|
|
492
510
|
# is not a head at all (a command's arguments, a value object's own
|
|
493
511
|
# fields), and a type written there is a name, not a reference — the same
|
|
@@ -496,27 +514,27 @@ module Hecks
|
|
|
496
514
|
list_name.to_s == "attributes" && %w[Aggregate Entity].include?(category)
|
|
497
515
|
end
|
|
498
516
|
|
|
499
|
-
# `id`
|
|
517
|
+
# `id` names the attribute's own aggregate, not necessarily the
|
|
500
518
|
# value object's — Wave 7's own translation.bluebook/translation_
|
|
501
519
|
# aggregate.bluebook split proved the difference live:
|
|
502
520
|
# TranslationAggregate's own `was`/every rename-rule's own `from`/
|
|
503
|
-
# `to`/... all deliberately reuse the
|
|
521
|
+
# `to`/... all deliberately reuse the sibling "Translation"
|
|
504
522
|
# aggregate's own `TranslationName` (that file's own header:
|
|
505
523
|
# "the shared TranslationName every non-identity field below
|
|
506
524
|
# uses"), a real, intentional cross-aggregate reuse — not the
|
|
507
|
-
# local-only ownership every
|
|
525
|
+
# local-only ownership every other real domain in this corpus
|
|
508
526
|
# happens to have used until now.
|
|
509
527
|
#
|
|
510
528
|
# `id` (already `Naming.identity([chapter, aggregate])`-joined)
|
|
511
|
-
# only ever composes with the
|
|
529
|
+
# only ever composes with the local aggregate for real, non-
|
|
512
530
|
# entity-owned attributes — an entity's own `id` never matches
|
|
513
|
-
# any
|
|
531
|
+
# any top-level aggregate here, so `local` stays nil and this
|
|
514
532
|
# returns `id` unchanged, exactly the prior behavior. Same for
|
|
515
|
-
# every attribute whose type
|
|
533
|
+
# every attribute whose type is locally declared (the overwhelming
|
|
516
534
|
# common case, Banking's own Customer/Account included) — this
|
|
517
|
-
# only ever changes the answer when the local aggregate does
|
|
535
|
+
# only ever changes the answer when the local aggregate does not
|
|
518
536
|
# declare `value` itself, falling back to the first (declaration-
|
|
519
|
-
# order)
|
|
537
|
+
# order) other aggregate in the same chapter that does.
|
|
520
538
|
def owning_aggregate_id(id, value)
|
|
521
539
|
local = @bluebook.aggregates.find { |aggregate| Naming.identity([@bluebook.name, aggregate.name]) == id }
|
|
522
540
|
return id unless local
|
|
@@ -549,13 +567,13 @@ module Hecks
|
|
|
549
567
|
# Reference its type is Reference<X>, another aggregate's head
|
|
550
568
|
# Holds its type names an entity this aggregate declares
|
|
551
569
|
#
|
|
552
|
-
#
|
|
570
|
+
# Nothing is skipped any more. Reference and Holds did not exist, so the
|
|
553
571
|
# walk dropped both kinds and the meta-domain silently did not contain
|
|
554
572
|
# Account#customer_id or Account#ledger.
|
|
555
|
-
# Each alternate carries its
|
|
573
|
+
# Each alternate carries its own map, read from the language. Borrowing the
|
|
556
574
|
# primary's map dispatched `type:` where Reference declares `points_at:`,
|
|
557
575
|
# and the payload gate caught it — which is the gate paying for itself.
|
|
558
|
-
# `reference_to` can be written in
|
|
576
|
+
# `reference_to` can be written in four places — on a head, a command, a
|
|
559
577
|
# piece, an ask — and each keeps its own list of attributes, so each
|
|
560
578
|
# needs the Reference alternate. Only a head can hold a piece, so Holds
|
|
561
579
|
# stays where it was.
|
|
@@ -573,7 +591,7 @@ module Hecks
|
|
|
573
591
|
|
|
574
592
|
def reference_row?(row) = row.respond_to?(:reference?) && row.reference?
|
|
575
593
|
|
|
576
|
-
# Only a
|
|
594
|
+
# Only a head declares pieces, and now that every attribute list reaches
|
|
577
595
|
# this, the node may be a command, a piece or an ask — none of which
|
|
578
596
|
# answer `entities` at all.
|
|
579
597
|
def entity_row?(row, node)
|
|
@@ -582,7 +600,7 @@ module Hecks
|
|
|
582
600
|
Array(node.entities).any? { |entity| entity.hecks_name == row.type.to_s }
|
|
583
601
|
end
|
|
584
602
|
|
|
585
|
-
# A
|
|
603
|
+
# A record's ID is its declared identity, joined — the same join the runtime
|
|
586
604
|
# does, off the same declaration, because there is only one way to name a
|
|
587
605
|
# thing and it should be written once.
|
|
588
606
|
#
|
|
@@ -602,14 +620,14 @@ module Hecks
|
|
|
602
620
|
Naming.identity(plan.identity_paths.map { |path| identity_part(plan, path, parent_id, node, index, category) })
|
|
603
621
|
end
|
|
604
622
|
|
|
605
|
-
# "owner_id" is a
|
|
606
|
-
# whichever record is walking
|
|
623
|
+
# "owner_id" is a second reserved head, beside `position` : it names
|
|
624
|
+
# whichever record is walking this one right now, aggregate or entity
|
|
607
625
|
# alike, without saying which — Command and Query read it so an
|
|
608
626
|
# entity's verbs are the entity's own. It is never a declared
|
|
609
627
|
# attribute (declaring one for a field that names two different types
|
|
610
628
|
# would be a lie about which), so it cannot be read through
|
|
611
629
|
# `field_value` ; it is read the same way `plan.parent_key` already is,
|
|
612
|
-
# because it
|
|
630
|
+
# because it is that fact, spelled for the walk's immediate parent
|
|
613
631
|
# rather than for one specific kind of one.
|
|
614
632
|
# `private` above has no effect on a constant; kept here anyway,
|
|
615
633
|
# beside the method that reads it, for the narrative.
|
|
@@ -640,8 +658,8 @@ module Hecks
|
|
|
640
658
|
|
|
641
659
|
# Command -> commands, ValueObject -> value_objects, Query -> queries.
|
|
642
660
|
# Convention, not a table : the IR names a collection after what it holds.
|
|
643
|
-
# The pluraliser lives in Naming
|
|
644
|
-
# one
|
|
661
|
+
# The pluraliser lives in Naming, kept to exactly one implementation — a
|
|
662
|
+
# second one risks disagreeing with it — see Naming.plural.
|
|
645
663
|
def collection_reader(category) = Naming.plural(Naming.snake(category))
|
|
646
664
|
end
|
|
647
665
|
end
|