hecks 1.3.0 → 1.4.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 +7 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +10 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +8 -8
- data/lib/hecks/adapters/driven/governance_authorization.rb +31 -13
- data/lib/hecks/adapters/driven/heki/journal.rb +5 -4
- data/lib/hecks/adapters/driven/heki/saga_store.rb +5 -5
- data/lib/hecks/adapters/driven/heki.rb +13 -7
- data/lib/hecks/adapters/driven/identity_registry.rb +2 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -3
- data/lib/hecks/adapters/driven/lambda/client.rb +34 -9
- data/lib/hecks/adapters/driven/lambda.rb +39 -33
- data/lib/hecks/adapters/driven/local_storage.rb +17 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +1 -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 +4 -4
- 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.rb +4 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +18 -18
- data/lib/hecks/behaviors/dsl.rb +2 -2
- data/lib/hecks/behaviors/expectations.rb +23 -23
- data/lib/hecks/behaviors/ir.rb +1 -1
- data/lib/hecks/behaviors/rspec.rb +1 -1
- data/lib/hecks/behaviors/runner.rb +2 -2
- data/lib/hecks/behaviors.rb +1 -1
- data/lib/hecks/bluebook/aggregate.rb +13 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
- data/lib/hecks/bluebook/assembly/build.rb +1 -1
- data/lib/hecks/bluebook/assembly/contract.rb +39 -16
- data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
- data/lib/hecks/bluebook/assembly/marks.rb +18 -18
- data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
- data/lib/hecks/bluebook/assembly.rb +9 -9
- data/lib/hecks/bluebook/attribute.rb +9 -9
- data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
- data/lib/hecks/bluebook/behaviour/command.rb +23 -23
- data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -7
- data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
- data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
- data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
- data/lib/hecks/bluebook/behaviour/query.rb +1 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +28 -9
- data/lib/hecks/bluebook/command.rb +12 -12
- data/lib/hecks/bluebook/domain_port.rb +9 -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 +96 -96
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
- 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 +56 -56
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
- 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 +35 -35
- data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
- data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
- data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
- 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 +11 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
- data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
- data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
- data/lib/hecks/bluebook/expression/resolver.rb +60 -62
- data/lib/hecks/bluebook/hexagon.rb +1 -1
- data/lib/hecks/bluebook/lifecycle.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +101 -87
- data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
- data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
- data/lib/hecks/bluebook/meta_validator.rb +70 -70
- data/lib/hecks/bluebook/model_check.rb +170 -84
- data/lib/hecks/bluebook/pattern_subset.rb +9 -9
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +14 -14
- data/lib/hecks/bluebook/project_register.rb +6 -6
- data/lib/hecks/bluebook/query.rb +4 -4
- data/lib/hecks/bluebook/read_model.rb +14 -14
- data/lib/hecks/bluebook/reference.rb +8 -8
- data/lib/hecks/bluebook/smoke_test.rb +19 -19
- data/lib/hecks/bluebook/synthesizer.rb +12 -12
- data/lib/hecks/bluebook/translation.rb +4 -4
- data/lib/hecks/bluebook/value_object.rb +6 -6
- data/lib/hecks/bluebook.rb +2 -2
- data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
- data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
- data/lib/hecks/codemod.rb +36 -35
- data/lib/hecks/construct.rb +6 -6
- data/lib/hecks/corpus.rb +317 -0
- data/lib/hecks/deprecation.rb +95 -0
- data/lib/hecks/doc/reference.rb +19 -19
- data/lib/hecks/embryonaut_bluebook.rb +11 -11
- 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 +79 -32
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +42 -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 +180 -19
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +2 -2
- data/lib/hecks/forms/query_form_renderer.rb +2 -2
- data/lib/hecks/forms/record_renderer.rb +2 -2
- data/lib/hecks/forms/record_table.rb +1 -1
- data/lib/hecks/forms/value_object_shape.rb +3 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +1 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework.rb +48 -17
- data/lib/hecks/freezer.rb +11 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
- data/lib/hecks/fuzzing/combination_miner.rb +119 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +140 -45
- data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
- data/lib/hecks/fuzzing/differential.rb +158 -0
- data/lib/hecks/fuzzing/domain_generator.rb +694 -0
- data/lib/hecks/fuzzing/era_boundary.rb +37 -18
- data/lib/hecks/fuzzing/form_census.rb +35 -20
- data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +62 -62
- data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +19 -21
- data/lib/hecks/fuzzing/properties/corrections.rb +9 -9
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +154 -49
- data/lib/hecks/fuzzing/properties/guards.rb +42 -42
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +22 -35
- data/lib/hecks/fuzzing/properties/outbox.rb +24 -24
- data/lib/hecks/fuzzing/properties/querying.rb +17 -17
- data/lib/hecks/fuzzing/properties.rb +64 -40
- data/lib/hecks/fuzzing/qa_settings.rb +152 -0
- data/lib/hecks/fuzzing/replay.rb +114 -114
- data/lib/hecks/fuzzing/rotation_priority.rb +10 -10
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
- data/lib/hecks/fuzzing/self_consistency.rb +141 -112
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +35 -35
- 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 +58 -31
- data/lib/hecks/fuzzing/sequence_generator.rb +111 -23
- data/lib/hecks/fuzzing/shrinker.rb +197 -0
- data/lib/hecks/fuzzing/structural_skips.rb +23 -130
- data/lib/hecks/fuzzing/sweep_depth.rb +4 -4
- data/lib/hecks/fuzzing/target_capabilities.rb +49 -18
- data/lib/hecks/fuzzing/value_generator.rb +14 -14
- data/lib/hecks/fuzzing.rb +5 -0
- data/lib/hecks/grammar/evolve.rb +10 -10
- data/lib/hecks/grammar.rb +7 -7
- data/lib/hecks/ir.rb +13 -13
- 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/oidc.json +5 -0
- data/lib/hecks/literal.rb +9 -9
- data/lib/hecks/naming.rb +25 -25
- data/lib/hecks/ports/access_control.rb +58 -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 +53 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +24 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +18 -1
- 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 +28 -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 +57 -4
- 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 +42 -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 +18 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -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 +3 -3
- data/lib/hecks/ports/query/ordering.rb +6 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/projections/bootstrap_table.rb +112 -0
- data/lib/hecks/projections/diagrams.rb +75 -75
- data/lib/hecks/projections/glossary/html.rb +3 -3
- data/lib/hecks/projections/glossary/markdown.rb +3 -3
- data/lib/hecks/projections/glossary/mermaid.rb +1 -1
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sentences.rb +5 -5
- data/lib/hecks/projections/glossary.rb +15 -15
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +18 -17
- data/lib/hecks/projections/model.rb +25 -21
- data/lib/hecks/projections/oidc.rb +7 -7
- data/lib/hecks/projections/parser_table.rb +5 -5
- data/lib/hecks/projections/reference.rb +3 -3
- data/lib/hecks/projections/rust_vocabulary.rb +443 -0
- data/lib/hecks/projections/shape.rb +2 -2
- data/lib/hecks/projections/statements.rb +11 -11
- data/lib/hecks/projections/vocabulary.rb +9 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +29 -29
- data/lib/hecks/projector/docs_projector.rb +13 -13
- data/lib/hecks/projector/exporter.rb +42 -21
- data/lib/hecks/projector/ir_projector.rb +1 -1
- data/lib/hecks/projector/narrate_projector.rb +11 -11
- data/lib/hecks/projector/target.rb +13 -13
- data/lib/hecks/projector.rb +15 -15
- data/lib/hecks/query_ir.rb +47 -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 +57 -13
- 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 +3 -3
- data/lib/hecks/router/namespace_installer.rb +3 -3
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/aggregate_lock.rb +11 -11
- data/lib/hecks/runtime/boot_gates.rb +3 -3
- data/lib/hecks/runtime/caller.rb +8 -8
- data/lib/hecks/runtime/capability_graph.rb +2 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +42 -63
- data/lib/hecks/runtime/command_interpreter.rb +159 -102
- data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
- data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
- 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 +35 -35
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +11 -11
- data/lib/hecks/runtime/dispatcher.rb +283 -90
- data/lib/hecks/runtime/entity_element.rb +151 -67
- data/lib/hecks/runtime/entity_interpreter.rb +108 -82
- data/lib/hecks/runtime/errors.rb +19 -19
- data/lib/hecks/runtime/event.rb +6 -6
- data/lib/hecks/runtime/identity.rb +22 -22
- data/lib/hecks/runtime/instance.rb +39 -14
- data/lib/hecks/runtime/interpreting.rb +12 -12
- data/lib/hecks/runtime/invocation.rb +276 -0
- data/lib/hecks/runtime/loader.rb +14 -14
- data/lib/hecks/runtime/outbox.rb +23 -23
- data/lib/hecks/runtime/policy_interpreter.rb +54 -54
- data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
- data/lib/hecks/runtime/query_interpreter.rb +55 -64
- data/lib/hecks/runtime/reaction_invocation.rb +20 -20
- data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
- data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
- data/lib/hecks/runtime/reference_hop.rb +6 -6
- data/lib/hecks/runtime/refusal_wording.rb +92 -115
- data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
- data/lib/hecks/runtime/registry/verification.rb +36 -26
- data/lib/hecks/runtime/registry.rb +45 -28
- data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
- data/lib/hecks/runtime/routing.rb +10 -153
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
- data/lib/hecks/runtime/saga_interpreter.rb +64 -64
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +9 -9
- data/lib/hecks/runtime/tenant_scope.rb +5 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +318 -127
- data/lib/hecks/runtime/value/entity_list_coercion.rb +34 -34
- data/lib/hecks/runtime/value.rb +21 -21
- data/lib/hecks/runtime.rb +7 -7
- data/lib/hecks/storehouse.rb +64 -64
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +205 -4
- data/lib/hecks.rb +13 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
- metadata +22 -2
|
@@ -2,8 +2,8 @@ module Hecks
|
|
|
2
2
|
module Bluebook
|
|
3
3
|
module DSL
|
|
4
4
|
class BluebookBuilder
|
|
5
|
-
#
|
|
6
|
-
#
|
|
5
|
+
# The self.validate_*/self.correlation_*/self.event_*/self.walk_*
|
|
6
|
+
# cluster — every whole-chapter, cross-aggregate check `#build`
|
|
7
7
|
# runs once a chapter is fully assembled (reference targets, event
|
|
8
8
|
# shapes, correlation keys, query hops, projected fields,
|
|
9
9
|
# bidirectional-reference cycles), plus each one's own private
|
|
@@ -27,10 +27,10 @@ module Hecks
|
|
|
27
27
|
# an instance method, matching what `def self.foo` already made
|
|
28
28
|
# every one of these before the split.
|
|
29
29
|
module Validation
|
|
30
|
-
#
|
|
30
|
+
# **Every whole-chapter check, in one place** — the battery `#build`
|
|
31
31
|
# used to run inline, now a pure function of an assembled
|
|
32
32
|
# `Bluebook::Chapter` so `MetaValidator.judge_deferred!` can run
|
|
33
|
-
# it too, once, on a chapter whose files have
|
|
33
|
+
# it too, once, on a chapter whose files have all loaded (see
|
|
34
34
|
# `#build`'s own comment for why that split exists at all).
|
|
35
35
|
# Public, not `private_class_method`'d, for exactly that second
|
|
36
36
|
# caller — `MetaValidator` needs to reach this with no builder
|
|
@@ -49,12 +49,12 @@ module Hecks
|
|
|
49
49
|
|
|
50
50
|
# Every hop AggregateBuilder#seal_query_field recognised and
|
|
51
51
|
# deferred gets checked for real here — the earliest point a
|
|
52
|
-
# hop
|
|
52
|
+
# hop can be checked, for exactly the reason
|
|
53
53
|
# validate_no_bidirectional_references! above already gives:
|
|
54
54
|
# `Bluebook.new` just stamped `hecks_owner` on every
|
|
55
55
|
# aggregate, so `Reference#resolve` finally has a chapter to
|
|
56
56
|
# walk. Before this line every target in the file (including
|
|
57
|
-
# ones declared
|
|
57
|
+
# ones declared above the aggregate doing the asking) would
|
|
58
58
|
# have resolved to nil.
|
|
59
59
|
infer_hop_query_arguments!(bluebook)
|
|
60
60
|
validate_query_hops!(bluebook)
|
|
@@ -63,27 +63,64 @@ module Hecks
|
|
|
63
63
|
# own reference cannot resolve until every aggregate in the
|
|
64
64
|
# chapter is real and owner-stamped (S12, ADR 0025).
|
|
65
65
|
validate_projected_fields!(bluebook)
|
|
66
|
+
|
|
67
|
+
validate_provisions!(bluebook)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# **What a declared capability must name**. A `provides` row is only
|
|
71
|
+
# worth trusting in place of a name check if it is checked: an
|
|
72
|
+
# unknown capability, a missing or extra key, or a verb that is
|
|
73
|
+
# not this chapter's own command/query of the right kind would
|
|
74
|
+
# otherwise wire a role check to nothing, in silence.
|
|
75
|
+
def validate_provisions!(bluebook)
|
|
76
|
+
bluebook.provides.group_by(&:capability).each do |capability, rows|
|
|
77
|
+
contract = Capabilities::CONTRACTS.fetch(capability) do
|
|
78
|
+
raise Malformed, "#{bluebook.name} provides #{capability.inspect}, which is no capability the " \
|
|
79
|
+
"language knows — known: #{Capabilities::CONTRACTS.keys.sort.join(', ')}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
keys = rows.map { |row| row.key.to_sym }
|
|
83
|
+
unless keys.sort == contract.keys.sort
|
|
84
|
+
raise Malformed, "#{bluebook.name} provides #{capability.inspect} with #{keys.join(', ')}, but " \
|
|
85
|
+
"#{capability} needs exactly #{contract.keys.join(', ')}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
rows.each { |row| validate_provided_verb!(bluebook, capability, row, contract.fetch(row.key.to_sym)) }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def validate_provided_verb!(bluebook, capability, row, kind)
|
|
93
|
+
aggregate_name, member = row.verb.split(".", 2)
|
|
94
|
+
aggregate = bluebook.aggregate(aggregate_name)
|
|
95
|
+
return if aggregate && member && provided_member_names(aggregate, kind).include?(member)
|
|
96
|
+
|
|
97
|
+
raise Malformed, "#{bluebook.name} provides #{capability.inspect} #{row.key}: #{row.verb.inspect}, " \
|
|
98
|
+
"which names no #{kind} this chapter declares (spelled \"Aggregate.#{kind.capitalize}\")"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def provided_member_names(aggregate, kind)
|
|
102
|
+
kind == :command ? aggregate.commands.map(&:hecks_name) : aggregate.queries.map(&:name)
|
|
66
103
|
end
|
|
67
104
|
|
|
68
|
-
#
|
|
105
|
+
# An entity command may not name itself as its root.
|
|
69
106
|
#
|
|
70
107
|
# That is the whole of what is left here, and it needs saying plainly
|
|
71
108
|
# because the sentence this used to raise — "references must target
|
|
72
109
|
# aggregate heads" — was never what it checked.
|
|
73
110
|
#
|
|
74
|
-
# `CommandBuilder#reference_to` sets `references`
|
|
111
|
+
# `CommandBuilder#reference_to` sets `references` only when the target's
|
|
75
112
|
# bare name equals the owner's ; anything else becomes a reference
|
|
76
|
-
#
|
|
113
|
+
# attribute. So on an aggregate command `references` is always a copy of
|
|
77
114
|
# that aggregate's own name, and looking it up in an index of aggregates
|
|
78
|
-
# is a
|
|
115
|
+
# is a tautology — that branch never refused anything and structurally
|
|
79
116
|
# could not. Verified across all eight golden chapters before deleting it.
|
|
80
117
|
#
|
|
81
|
-
# On a
|
|
118
|
+
# On a piece's command the owner is the entity, and an entity is not a
|
|
82
119
|
# head, so what this actually refuses is `reference_to <its own name>`
|
|
83
|
-
# written inside `entity do … end`. A piece is reached
|
|
120
|
+
# written inside `entity do … end`. A piece is reached through its
|
|
84
121
|
# aggregate ; a command on one addresses the aggregate, never the piece.
|
|
85
122
|
#
|
|
86
|
-
# Reference
|
|
123
|
+
# Reference attributes are the language's business now — offered as the
|
|
87
124
|
# head's own id and resolved as references, so `Aggregate.Reference` and
|
|
88
125
|
# `Command.Reference` refuse an undeclared head with no predicate at all.
|
|
89
126
|
def validate_reference_value_objects!(aggregates)
|
|
@@ -106,19 +143,19 @@ module Hecks
|
|
|
106
143
|
"an entity command is addressed through its aggregate; #{violations.uniq.join('; ')}"
|
|
107
144
|
end
|
|
108
145
|
|
|
109
|
-
#
|
|
146
|
+
# Events are first-class by convention, not by declaration (ADR
|
|
110
147
|
# 0025, "events and reactions" — "a domain event is a value
|
|
111
148
|
# object with its own attributes, not a label"). No new `event
|
|
112
149
|
# do ... end` construct exists to hand-author and keep in step
|
|
113
150
|
# with every emitting command by hand — an event's own known
|
|
114
|
-
# shape
|
|
115
|
-
# this is the
|
|
151
|
+
# shape is whichever command(s) declare `emits` for its name, and
|
|
152
|
+
# this is the one thing that has to hold for that convention to
|
|
116
153
|
# mean anything: every command that emits a given name has to
|
|
117
154
|
# agree on what it carries. An event is one fact; a fact does not
|
|
118
155
|
# carry two different truths depending on who is telling it.
|
|
119
156
|
#
|
|
120
|
-
#
|
|
121
|
-
# `admits:`/`default:` are refinements
|
|
157
|
+
# Structural fields only (name/type/list/optional) — `pattern:`/
|
|
158
|
+
# `admits:`/`default:` are refinements on a field, not a second
|
|
122
159
|
# claim about what the payload holds, so two emitting commands
|
|
123
160
|
# are free to differ there without actually disagreeing about
|
|
124
161
|
# the event's own shape.
|
|
@@ -136,26 +173,26 @@ module Hecks
|
|
|
136
173
|
end
|
|
137
174
|
end
|
|
138
175
|
|
|
139
|
-
#
|
|
176
|
+
# The "expensive half" the ADR names: "with: { account: :account }
|
|
140
177
|
# projecting into a reaction that has no declared contract ...
|
|
141
178
|
# breaks at dispatch rather than at load." Checked here, now that
|
|
142
179
|
# `validate_event_shapes!` (above) guarantees at most one real
|
|
143
180
|
# shape per event name, and command references being first-class
|
|
144
|
-
# (`Naming.command_ref`) means the
|
|
181
|
+
# (`Naming.command_ref`) means the target side is a real
|
|
145
182
|
# resolvable command, not a string that might be a typo.
|
|
146
183
|
#
|
|
147
|
-
#
|
|
184
|
+
# **Same-chapter only, on purpose** — a `with:` whose source event or
|
|
148
185
|
# target command lives outside this chapter (an `across` policy
|
|
149
186
|
# reacting to another domain's event entirely) is silently left
|
|
150
187
|
# unchecked rather than refused: there is nothing here yet to
|
|
151
188
|
# check it against, and "unresolvable" is not the same claim as
|
|
152
189
|
# "wrong."
|
|
153
190
|
#
|
|
154
|
-
# A FOR_EACH
|
|
155
|
-
# `with:`'s symbols read the
|
|
191
|
+
# A FOR_EACH policy's source isn't the event at all — a fan-out
|
|
192
|
+
# `with:`'s symbols read the query row `for_each` answers
|
|
156
193
|
# (FreezeAccountsOnSuspension's own comment: "`account` is the
|
|
157
194
|
# key the fan-out merges for each row"), which this has no shape
|
|
158
|
-
# for; the
|
|
195
|
+
# for; the source half is skipped for those, the target half
|
|
159
196
|
# (does the dispatched command actually declare the field) still
|
|
160
197
|
# runs, since that half is true regardless of where the value
|
|
161
198
|
# came from.
|
|
@@ -184,11 +221,11 @@ module Hecks
|
|
|
184
221
|
end
|
|
185
222
|
|
|
186
223
|
# `process_manager:` is present only for a process manager's own dispatch — a
|
|
187
|
-
# saga leg's source symbol resolves against the
|
|
224
|
+
# saga leg's source symbol resolves against the current triggering
|
|
188
225
|
# event first, same as a policy, but falls all the way back to the
|
|
189
|
-
# saga's own
|
|
226
|
+
# saga's own memory when the current event does not carry it
|
|
190
227
|
# (`SagaInterpreter#dispatch_args`, its own last `else`) — and
|
|
191
|
-
# memory starts as the
|
|
228
|
+
# memory starts as the opening event's payload
|
|
192
229
|
# (`SagaInterpreter#instance = { ..., memory: event.payload }`,
|
|
193
230
|
# never updated after), never the leg's own. Settlement's own
|
|
194
231
|
# comment names exactly this: "the credit leg reads a destination
|
|
@@ -211,7 +248,7 @@ module Hecks
|
|
|
211
248
|
source_shape = event_name && event_shape_for(event_name, aggregates)
|
|
212
249
|
memory_shape = process_manager && event_shape_for(process_manager.starts_on, aggregates)
|
|
213
250
|
correlation = process_manager&.correlates_by && process_manager.correlation_head
|
|
214
|
-
# A
|
|
251
|
+
# A policy's source also carries the emitter's own identity —
|
|
215
252
|
# `PolicyInterpreter#emitter_identity`, the runtime half of this.
|
|
216
253
|
# An entity command's event never declares its aggregate's
|
|
217
254
|
# identity (it arrives through `reference_to`, not an
|
|
@@ -243,15 +280,15 @@ module Hecks
|
|
|
243
280
|
end
|
|
244
281
|
end
|
|
245
282
|
|
|
246
|
-
# A command's
|
|
283
|
+
# A command's own `reference_to` (bare, no `as:`) never lands in
|
|
247
284
|
# `attributes` — `CommandBuilder#reference_to`'s self-reference
|
|
248
285
|
# branch sets `command.references` instead (S2), and mints no new
|
|
249
|
-
# field at all. What addresses it is not one name but the
|
|
250
|
-
#
|
|
286
|
+
# field at all. What addresses it is not one name but the same
|
|
287
|
+
# set `CommandInterpreter::ArgumentGate#refuse_unknown_arguments`
|
|
251
288
|
# already accepts at dispatch time — `:id`, the owning aggregate's
|
|
252
289
|
# own `identity_heads` (real corpus proof — `Account.Debit`
|
|
253
290
|
# dispatched everywhere as `number: ...`, `Account`'s own
|
|
254
|
-
# `identified_by`),
|
|
291
|
+
# `identified_by`), and `Naming.reference_key(command.references)`
|
|
255
292
|
# (real corpus proof — `FreezeAccountsOnSuspension`'s `for_each`
|
|
256
293
|
# fan-out, whose own comment reads "`account` is the key the
|
|
257
294
|
# fan-out merges for each row it answers"). Both are simultaneously
|
|
@@ -271,9 +308,9 @@ module Hecks
|
|
|
271
308
|
referenced.identity_heads.include?(field) || Naming.reference_key(command.references) == field
|
|
272
309
|
end
|
|
273
310
|
|
|
274
|
-
#
|
|
311
|
+
# The fourth addressing key `ArgumentGate#refuse_unknown_arguments`
|
|
275
312
|
# accepts, alongside `:id`/`identity_heads`/`reference_key` — every
|
|
276
|
-
# saga in
|
|
313
|
+
# saga in this domain's own `correlates_by` head, carried through
|
|
277
314
|
# every dispatch as pure passthrough (Settlement's own comment:
|
|
278
315
|
# "`reference:` carries the correlation forward... this is pure
|
|
279
316
|
# passthrough, not an addressing key"). A command declaring none of
|
|
@@ -283,7 +320,7 @@ module Hecks
|
|
|
283
320
|
process_managers.filter_map { |pm| pm.correlates_by && pm.correlation_head }
|
|
284
321
|
end
|
|
285
322
|
|
|
286
|
-
# Every command this chapter declares, an aggregate's own
|
|
323
|
+
# Every command this chapter declares, an aggregate's own and
|
|
287
324
|
# every entity nested inside one, paired with a name for what
|
|
288
325
|
# declares it — shared by `validate_event_shapes!` and
|
|
289
326
|
# `validate_with_projections!`'s own command lookup, the same
|
|
@@ -299,9 +336,9 @@ module Hecks
|
|
|
299
336
|
end
|
|
300
337
|
end
|
|
301
338
|
|
|
302
|
-
#
|
|
339
|
+
# **Not memoised** — this used to be `@event_emitters ||=` on the
|
|
303
340
|
# builder instance, which is safe for a one-file chapter but
|
|
304
|
-
# wrong for one split across several: the
|
|
341
|
+
# wrong for one split across several: the first file's build()
|
|
305
342
|
# call would compute and cache it from whatever `@aggregates`
|
|
306
343
|
# held at that moment, and every later file's own validation
|
|
307
344
|
# would keep reading that same stale snapshot, silently missing
|
|
@@ -314,26 +351,26 @@ module Hecks
|
|
|
314
351
|
end
|
|
315
352
|
end
|
|
316
353
|
|
|
317
|
-
#
|
|
354
|
+
# **Structural, not nominal**. Two commands on two different
|
|
318
355
|
# aggregates that both `emits "SameEvent"` are free to type a
|
|
319
|
-
# field through two
|
|
356
|
+
# field through two different, locally-scoped wrapper value
|
|
320
357
|
# objects (e.g. one aggregate's own `value: SomeText` vs
|
|
321
358
|
# another's `value: OtherText`, exactly the per-aggregate "own
|
|
322
359
|
# text VO" convention every aggregate in this grammar already
|
|
323
360
|
# follows for everything from `RuleText` to `FieldRef`) without
|
|
324
361
|
# actually disagreeing about the event's shape — comparing
|
|
325
|
-
# `a.type` by
|
|
362
|
+
# `a.type` by name would flag that as a violation for no real
|
|
326
363
|
# reason: an event is one fact, and two isomorphic wrapper types
|
|
327
364
|
# tell an identical one. So a value-object type is unwrapped to
|
|
328
|
-
# its
|
|
365
|
+
# its own attribute shape (recursively — a wrapper could itself
|
|
329
366
|
# wrap another) before comparing, and only a primitive type
|
|
330
367
|
# (nothing left to unwrap) or two VOs that truly differ once
|
|
331
368
|
# unwrapped still counts as a real mismatch. `owner` carries the
|
|
332
369
|
# type's `value_object` lookup — a command's own attributes only
|
|
333
|
-
# know their type's
|
|
370
|
+
# know their type's name, never the aggregate that declared it,
|
|
334
371
|
# and two sibling aggregates in one chapter each keep a
|
|
335
372
|
# same-named VO private to themselves, so the unwrap has to ask
|
|
336
|
-
# the
|
|
373
|
+
# the same aggregate the field's own command belongs to, never a
|
|
337
374
|
# neighbor's.
|
|
338
375
|
def event_shape(command, owner)
|
|
339
376
|
command.attributes.map { |a| [a.name, unwrap_shape(owner, a.type.to_s), a.list?, a.optional?] }.sort
|
|
@@ -351,10 +388,10 @@ module Hecks
|
|
|
351
388
|
shape.attributes.map { |a| [a.name, unwrap_shape(owner, a.type.to_s, seen + [type_name]), a.list?, a.optional?] }.sort
|
|
352
389
|
end
|
|
353
390
|
|
|
354
|
-
# `owner` (from `each_command`) is a plain
|
|
391
|
+
# `owner` (from `each_command`) is a plain string — the aggregate's
|
|
355
392
|
# `hecks_name` alone, or `"Aggregate.Entity"` for an entity's own
|
|
356
|
-
# command. Either way the
|
|
357
|
-
# typed with are the
|
|
393
|
+
# command. Either way the value objects a command's fields can be
|
|
394
|
+
# typed with are the aggregate's own (`Entity` carries no
|
|
358
395
|
# `value_object` lookup of its own — the whole rest of this file
|
|
359
396
|
# already resolves hop/type lookups only at the aggregate level,
|
|
360
397
|
# e.g. `validate_hop_tail!`'s `target.value_object(type)`), so only
|
|
@@ -372,7 +409,7 @@ module Hecks
|
|
|
372
409
|
end
|
|
373
410
|
|
|
374
411
|
# The identity heads of the aggregate that emits `event_name` — an
|
|
375
|
-
# entity's event is stamped with its
|
|
412
|
+
# entity's event is stamped with its owning aggregate's identity
|
|
376
413
|
# (`Event#id` is the parent's), so an owner spelled "Game.Knight"
|
|
377
414
|
# answers Game's heads.
|
|
378
415
|
def event_identity_heads_for(event_name, aggregates)
|
|
@@ -384,7 +421,7 @@ module Hecks
|
|
|
384
421
|
return [] unless aggregate
|
|
385
422
|
|
|
386
423
|
heads = aggregate.identity_heads.map(&:to_sym)
|
|
387
|
-
#
|
|
424
|
+
# An entity's event also carries the piece's own identity — the
|
|
388
425
|
# args a piece was addressed by are the args its event announces
|
|
389
426
|
# (`Emission#emit`: `payload: args`), so `id`-shaped heads are
|
|
390
427
|
# genuinely there at runtime even though no `attribute` line on
|
|
@@ -400,9 +437,9 @@ module Hecks
|
|
|
400
437
|
end
|
|
401
438
|
end
|
|
402
439
|
|
|
403
|
-
# A
|
|
440
|
+
# A reference ring is not a modelling choice, it is a missing one
|
|
404
441
|
# — a DDD aggregate is a consistency boundary precisely because
|
|
405
|
-
# something outside it can only ever point
|
|
442
|
+
# something outside it can only ever point in, by id, never the
|
|
406
443
|
# other way. A caller must be able to reason about one aggregate
|
|
407
444
|
# alone ; a ring back to where it started means no aggregate in
|
|
408
445
|
# it is a boundary anyone can reason about without the rest of
|
|
@@ -414,11 +451,11 @@ module Hecks
|
|
|
414
451
|
# aggregate finishes building long before it can know whether
|
|
415
452
|
# some later aggregate in the same file points back at it.
|
|
416
453
|
#
|
|
417
|
-
#
|
|
454
|
+
# Acyclic within a chapter (ADR 0025, "References") — widened
|
|
418
455
|
# from the direct pair (A -> B -> A) this used to catch alone to
|
|
419
456
|
# any ring, however long (A -> B -> C -> A), the same DFS
|
|
420
457
|
# coloring a reference graph needs for any cycle. A cross-chapter
|
|
421
|
-
# reference is
|
|
458
|
+
# reference is unreachable here rather than unchecked:
|
|
422
459
|
# `Reference#resolve` is scoped to its own chapter by
|
|
423
460
|
# construction, so a target this chapter never declares is a
|
|
424
461
|
# dangling name, not an edge — `edges.key?` below is what keeps
|
|
@@ -443,7 +480,7 @@ module Hecks
|
|
|
443
480
|
end
|
|
444
481
|
|
|
445
482
|
# Plain DFS with a visiting/done coloring, over the reference
|
|
446
|
-
# graph
|
|
483
|
+
# graph this chapter's own aggregates declare. Returns the ring
|
|
447
484
|
# itself (in the order it closes), or nil.
|
|
448
485
|
def find_reference_cycle(edges)
|
|
449
486
|
state = {}
|
|
@@ -475,10 +512,10 @@ module Hecks
|
|
|
475
512
|
nil
|
|
476
513
|
end
|
|
477
514
|
|
|
478
|
-
#
|
|
479
|
-
# recognised the
|
|
515
|
+
# **The other half of a hop** — AggregateBuilder#seal_query_field
|
|
516
|
+
# recognised the head of a dotted where-field that names one of
|
|
480
517
|
# its own references and deferred it here, unable to check
|
|
481
|
-
# further: it cannot yet resolve what the reference points
|
|
518
|
+
# further: it cannot yet resolve what the reference points at.
|
|
482
519
|
# This runs once every aggregate exists in one chapter, so it
|
|
483
520
|
# can.
|
|
484
521
|
#
|
|
@@ -486,11 +523,11 @@ module Hecks
|
|
|
486
523
|
# refused outright, immediately, back in seal_query_field
|
|
487
524
|
# itself (that answer never needed the target's shape).
|
|
488
525
|
#
|
|
489
|
-
#
|
|
526
|
+
# An entity's own queries did reach `EntityBuilder#reference_to`
|
|
490
527
|
# (added after this comment first claimed otherwise — S9, ADR
|
|
491
|
-
# 0025) without ever reaching
|
|
528
|
+
# 0025) without ever reaching here: tier-1 sealing
|
|
492
529
|
# (`AggregateBuilder#query_surfaces`) already recognises a hop
|
|
493
|
-
# on an entity's own field and
|
|
530
|
+
# on an entity's own field and defers it exactly like an
|
|
494
531
|
# aggregate's, but nothing ever walked entity queries at tier 2
|
|
495
532
|
# to check the deferral — a bad hop, or even a well-formed one,
|
|
496
533
|
# built silently and then matched nothing at runtime
|
|
@@ -540,7 +577,7 @@ module Hecks
|
|
|
540
577
|
end
|
|
541
578
|
end
|
|
542
579
|
|
|
543
|
-
#
|
|
580
|
+
# The leaf `infer_hop_query_arguments!` infers for one resolved
|
|
544
581
|
# hop plan — a pure function of `plan` and the symbolic `name`
|
|
545
582
|
# it is naming, pulled out because it is a self-contained
|
|
546
583
|
# computation with no dependency on the enclosing loop's own
|
|
@@ -602,10 +639,10 @@ module Hecks
|
|
|
602
639
|
validate_hop_tail!(aggregate, query, clause, target, plan.tail)
|
|
603
640
|
end
|
|
604
641
|
|
|
605
|
-
# The same three-way answer seal_query_field gives for its
|
|
642
|
+
# The same three-way answer seal_query_field gives for its own
|
|
606
643
|
# aggregate's fields — landing on a real scalar (fine), landing
|
|
607
644
|
# on a value object (refused by name), or naming nothing at all
|
|
608
|
-
# (refused by name) — asked instead of the hop's
|
|
645
|
+
# (refused by name) — asked instead of the hop's target aggregate,
|
|
609
646
|
# since that is whose shape the tail actually has to answer for.
|
|
610
647
|
def validate_hop_tail!(aggregate, query, clause, target, tail)
|
|
611
648
|
name, *nested = tail.to_s.split(".")
|
|
@@ -632,11 +669,11 @@ module Hecks
|
|
|
632
669
|
"not exist matches nothing and refuses nothing"
|
|
633
670
|
end
|
|
634
671
|
|
|
635
|
-
# A
|
|
672
|
+
# A where hop with an ordered comparator is legitimate ("client
|
|
636
673
|
# whose balance > 500") — AggregateBuilder#seal_ordered_comparator
|
|
637
674
|
# already deferred this exact check for the same reason every
|
|
638
675
|
# other hop check is deferred, and this is where it gets asked,
|
|
639
|
-
# against the hop's
|
|
676
|
+
# against the hop's target instead of the querying aggregate.
|
|
640
677
|
def validate_hop_comparator!(aggregate, query, clause, target, attribute, nested)
|
|
641
678
|
return unless AggregateBuilder::ORDERED_COMPARATORS.include?(clause.op.to_s.to_sym)
|
|
642
679
|
return if attribute &&
|
|
@@ -650,10 +687,10 @@ module Hecks
|
|
|
650
687
|
"anything else the adapters answer differently or not at all"
|
|
651
688
|
end
|
|
652
689
|
|
|
653
|
-
#
|
|
690
|
+
# The target half of `projects` validation (S12, ADR 0025) —
|
|
654
691
|
# `AggregateBuilder#seal_projected_fields` already checked the
|
|
655
|
-
#
|
|
656
|
-
# `reference_to` on
|
|
692
|
+
# local half at declare time (the reference names a real
|
|
693
|
+
# `reference_to` on this aggregate); this checks the reference
|
|
657
694
|
# actually resolves to a real aggregate in this chapter, and
|
|
658
695
|
# that aggregate really declares `remote_field` as a scalar.
|
|
659
696
|
#
|
|
@@ -671,7 +708,7 @@ module Hecks
|
|
|
671
708
|
end
|
|
672
709
|
end
|
|
673
710
|
|
|
674
|
-
# A linear decision tree of validation rules over
|
|
711
|
+
# A linear decision tree of validation rules over one resolved
|
|
675
712
|
# hop plan, each already explained by its own comment above
|
|
676
713
|
# (the lifecycle fallback, the chained-projection fallback, the
|
|
677
714
|
# final scalar check) — a fixed, closed sequence "resolve, then
|
|
@@ -693,17 +730,17 @@ module Hecks
|
|
|
693
730
|
target = plan.hops.last.target
|
|
694
731
|
remote_attribute = target.attributes.find { |candidate| candidate.name.to_s == plan.tail }
|
|
695
732
|
|
|
696
|
-
#
|
|
697
|
-
#
|
|
733
|
+
# The worked example itself (ADR 0025) reads through a
|
|
734
|
+
# lifecycle field — banking's Customer.status is `lifecycle
|
|
698
735
|
# :status`, never a plain `attribute` — the same fallback
|
|
699
736
|
# validate_hop_tail! already gives a query's own hop tail. A
|
|
700
737
|
# lifecycle field is always a plain string by construction ;
|
|
701
738
|
# nothing further to check once it matches by name.
|
|
702
739
|
return if remote_attribute.nil? && target.lifecycle&.field.to_s == plan.tail
|
|
703
740
|
|
|
704
|
-
# A
|
|
741
|
+
# A projection may chain through another projection (S12, ADR
|
|
705
742
|
# 0025's own boundary rule, followed through) — `target`'s
|
|
706
|
-
#
|
|
743
|
+
# own projected fields live in `projected_fields`, a
|
|
707
744
|
# separate list from `attributes`, so a match there is
|
|
708
745
|
# invisible to the check above even though it names a real,
|
|
709
746
|
# always-current, stored field. `Transfer.projects
|
|
@@ -737,10 +774,10 @@ module Hecks
|
|
|
737
774
|
!attribute.list? && !attribute.reference? && target.value_object(attribute.type).nil?
|
|
738
775
|
end
|
|
739
776
|
|
|
740
|
-
# `correlates_by`
|
|
777
|
+
# `correlates_by` names a scalar, now checked rather than trusted.
|
|
741
778
|
#
|
|
742
779
|
# ProcessManagerBuilder#validate! already refuses a bare, undotted
|
|
743
|
-
# spelling — a
|
|
780
|
+
# spelling — a syntactic guarantee that the declaration cannot leave
|
|
744
781
|
# the question open. It cannot go further: a process manager is built
|
|
745
782
|
# in isolation, before this chapter's aggregates exist to check
|
|
746
783
|
# against. Here, with the whole document assembled, the dotted path
|