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
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
4
|
+
# **What an attribute does**. The declared half — name, type, list,
|
|
5
5
|
# default, optional, pattern, admits — is what the language states
|
|
6
6
|
# in `aggregate.bluebook`'s own `Field`. These are the questions
|
|
7
|
-
# readers ask
|
|
7
|
+
# readers ask about that shape, which no declaration states.
|
|
8
8
|
module Attribute
|
|
9
9
|
def list? = @list
|
|
10
10
|
def scalar? = !@list
|
|
11
11
|
def reference? = @type.is_a?(Reference)
|
|
12
12
|
|
|
13
|
-
#
|
|
13
|
+
# May this fact be left out?
|
|
14
14
|
#
|
|
15
15
|
# Required is the default and by far the common case — a command takes
|
|
16
|
-
# the arguments it declares, and all of them — so the
|
|
16
|
+
# the arguments it declares, and all of them — so the exception is what
|
|
17
17
|
# gets marked. Marking the other way would annotate almost every
|
|
18
18
|
# attribute in the corpus to say nothing.
|
|
19
19
|
#
|
|
20
|
-
# Only a
|
|
20
|
+
# Only a command enforces this. An aggregate's own attributes are filled
|
|
21
21
|
# by the commands that set them, and a value object's by its
|
|
22
22
|
# constructor ; neither is a payload anyone hands in.
|
|
23
23
|
def optional? = @optional
|
|
@@ -3,17 +3,17 @@ require_relative "traits"
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Bluebook
|
|
5
5
|
module Behaviour
|
|
6
|
-
#
|
|
6
|
+
# **What a chapter does**. The declared half is the roll-call of what a
|
|
7
7
|
# bluebook holds; these are the finders over it, plus `verbs` — the
|
|
8
8
|
# chapter's own list of every dispatchable name, which is derived
|
|
9
9
|
# from the aggregates rather than declared anywhere.
|
|
10
10
|
module Chapter
|
|
11
11
|
include Owns
|
|
12
12
|
|
|
13
|
-
#
|
|
14
|
-
# declaration does not state: that a chapter is the
|
|
13
|
+
# The hook the generated constructor calls. Three things a
|
|
14
|
+
# declaration does not state: that a chapter is the root of the
|
|
15
15
|
# owner chain (nothing declares it, it is what having no owner
|
|
16
|
-
#
|
|
16
|
+
# means), the ports table — which a `.hecksagon` fills later, so
|
|
17
17
|
# the bluebook cannot declare it — and stamping its own children,
|
|
18
18
|
# the same act an Aggregate performs one level down.
|
|
19
19
|
def settle
|
|
@@ -28,7 +28,25 @@ module Hecks
|
|
|
28
28
|
def read_model(named) = @read_models.find { |model| model.name == named.to_s || model.query_name == named.to_s }
|
|
29
29
|
def port(named) = @ports_by_name[named.to_s]
|
|
30
30
|
|
|
31
|
-
#
|
|
31
|
+
# What this chapter declared it provides — `{ key => local verb }`
|
|
32
|
+
# for one capability, or nil when it declares none. Read by
|
|
33
|
+
# everything that used to recognise the Governance chapter by its
|
|
34
|
+
# name (`Registry#authorization_provider_for`).
|
|
35
|
+
def provision(capability)
|
|
36
|
+
rows = @provides.select { |row| row.capability == capability.to_s }
|
|
37
|
+
rows.empty? ? nil : rows.to_h { |row| [row.key.to_sym, row.verb] }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def provides?(capability) = !provision(capability).nil?
|
|
41
|
+
|
|
42
|
+
# The declared verb for `key`, qualified with this chapter's own
|
|
43
|
+
# name — the spelling `Dispatcher#dispatch`/`#query` take.
|
|
44
|
+
def provided_verb(capability, key)
|
|
45
|
+
local = provision(capability)&.fetch(key.to_sym, nil)
|
|
46
|
+
local && "#{name}::#{local}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# A port is declared in the hecksagon, not the bluebook — so it
|
|
32
50
|
# attaches after the chapter already exists, the same way an
|
|
33
51
|
# aggregate's own ports do.
|
|
34
52
|
def add_port(port)
|
|
@@ -3,8 +3,8 @@ require_relative "traits"
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Bluebook
|
|
5
5
|
module Behaviour
|
|
6
|
-
#
|
|
7
|
-
#
|
|
6
|
+
# **What a command does**. Extended, not included — a command is a
|
|
7
|
+
# class, one per declared verb.
|
|
8
8
|
module Command
|
|
9
9
|
include Indexed
|
|
10
10
|
|
|
@@ -17,8 +17,8 @@ module Hecks
|
|
|
17
17
|
|
|
18
18
|
# The construct this verb acts upon — the construct itself, not its name.
|
|
19
19
|
#
|
|
20
|
-
# A verb declared on an
|
|
21
|
-
# self-references, because an element is addressed
|
|
20
|
+
# A verb declared on an entity always acts on that piece. It never
|
|
21
|
+
# self-references, because an element is addressed through its parent —
|
|
22
22
|
# which means `creates?` answers true for every one of them, and reading
|
|
23
23
|
# `acts_on` off `creates?` alone would report that `LedgerEntry.Amend`
|
|
24
24
|
# brings a ledger entry into being. Three of banking's commands were
|
|
@@ -27,9 +27,9 @@ module Hecks
|
|
|
27
27
|
# On an aggregate, a creating command acts on no existing root, so nil is
|
|
28
28
|
# the truth: there is nothing there yet.
|
|
29
29
|
def acts_on
|
|
30
|
-
#
|
|
30
|
+
# Fully qualified, and it has to be: inside `module Behaviour`
|
|
31
31
|
# the bare name `Entity` resolves to Behaviour::Entity — this
|
|
32
|
-
# module's
|
|
32
|
+
# module's sibling — not to the construct. Comparing a Class to
|
|
33
33
|
# a Module answers nil, so the guard silently fell through and
|
|
34
34
|
# every entity verb reported acting on nothing.
|
|
35
35
|
return hecks_owner if hecks_owner.is_a?(Class) && hecks_owner < Bluebook::Entity
|
|
@@ -39,8 +39,8 @@ module Hecks
|
|
|
39
39
|
|
|
40
40
|
def creates? = @references.nil?
|
|
41
41
|
|
|
42
|
-
#
|
|
43
|
-
# its givens
|
|
42
|
+
# Every reason this verb can refuse on a rule — the descriptions of
|
|
43
|
+
# its givens and its ensures, the exact text the runtime quotes
|
|
44
44
|
# after "refused — " when a guard is not met (see
|
|
45
45
|
# command_rules/admissibility.rb's GivenNotMet/EnsuresNotMet). A
|
|
46
46
|
# property that asks "did the runtime only ever refuse for a rule
|
|
@@ -49,8 +49,8 @@ module Hecks
|
|
|
49
49
|
# (behavior.bluebook's Rule) and an unnamed one quotes nothing.
|
|
50
50
|
def guard_descriptions = (@givens + @ensures).map(&:description).compact
|
|
51
51
|
|
|
52
|
-
#
|
|
53
|
-
# for
|
|
52
|
+
# The argument name that addresses an instance of `aggregate_name`
|
|
53
|
+
# for this command — the one fact `PolicyInterpreter`'s own
|
|
54
54
|
# `for_each` fan-out needs and, until this reading existed, had
|
|
55
55
|
# to guess at (see git blame: `Behaviour::Policy
|
|
56
56
|
# #fan_out_reference_key`, which hardcoded `<aggregate>_id`
|
|
@@ -58,28 +58,28 @@ module Hecks
|
|
|
58
58
|
# addressing command as a result — `Account.Freeze`, addressed
|
|
59
59
|
# by `number`/`account`, not `account_id`).
|
|
60
60
|
#
|
|
61
|
-
#
|
|
61
|
+
# Two shapes, the same two `CommandBuilder#reference_to` already
|
|
62
62
|
# tells apart at declare time (command_builder.rb's own comment
|
|
63
|
-
# on `cross_reference` — "`as:`
|
|
64
|
-
# command can point at another instance of its
|
|
63
|
+
# on `cross_reference` — "`as:` means a named attribute... a
|
|
64
|
+
# command can point at another instance of its own kind"):
|
|
65
65
|
#
|
|
66
|
-
#
|
|
67
|
-
# is declared
|
|
66
|
+
# self-addressing — `references == aggregate_name` (this verb
|
|
67
|
+
# is declared on the very aggregate it acts on, `reference_to
|
|
68
68
|
# Account` on a command Account itself owns). No attribute was
|
|
69
|
-
# minted for it at all; the
|
|
69
|
+
# minted for it at all; the same bare key
|
|
70
70
|
# `CommandInterpreter::ArgumentGate#reference_key` already
|
|
71
71
|
# accepts as "addressing, not describing" is reused here
|
|
72
72
|
# rather than re-derived — one door, not two.
|
|
73
73
|
#
|
|
74
|
-
#
|
|
75
|
-
# attribute whose
|
|
76
|
-
# on `Account.Open`, or whatever `as:` named it). Its
|
|
74
|
+
# cross-referencing — a real, declared reference-typed
|
|
75
|
+
# attribute whose own target is `aggregate_name` (`customer_id`
|
|
76
|
+
# on `Account.Open`, or whatever `as:` named it). Its name is
|
|
77
77
|
# the key, exactly as declared — never re-derived from the
|
|
78
78
|
# target's name, because an `as:` reference's name and its
|
|
79
79
|
# target's snake case can legitimately differ (`Transfer`'s own
|
|
80
80
|
# `source`/`destination`, both `Reference<Account>`).
|
|
81
81
|
#
|
|
82
|
-
# `nil` when neither shape matches — a
|
|
82
|
+
# `nil` when neither shape matches — a creating command (nothing
|
|
83
83
|
# to address yet) or one that simply never references this
|
|
84
84
|
# aggregate at all. A caller minting a fan-out dispatch is
|
|
85
85
|
# expected to treat `nil` as "this command cannot be addressed by
|
|
@@ -92,11 +92,11 @@ module Hecks
|
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
-
# A
|
|
95
|
+
# **A mutation's own readings**. Included (not extended) — Mutation is
|
|
96
96
|
# a Struct, so these are instance methods.
|
|
97
97
|
module Mutation
|
|
98
|
-
# An
|
|
99
|
-
#
|
|
98
|
+
# An append binds several fields at once, each from either a command
|
|
99
|
+
# argument (a Symbol) or a literal. It used to spell the Symbol bare
|
|
100
100
|
# and inspect the rest, which is the opposite of what a where-clause
|
|
101
101
|
# did with the same two kinds — see Hecks::Literal.
|
|
102
102
|
def appended_fields = source.transform_values { |value| Literal.render(value) }
|
|
@@ -3,17 +3,17 @@ require_relative "traits"
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Bluebook
|
|
5
5
|
module Behaviour
|
|
6
|
-
#
|
|
6
|
+
# **What one port operation does**.
|
|
7
7
|
module PortOperation
|
|
8
8
|
include Indexed
|
|
9
9
|
|
|
10
|
-
# An operation declares no reference of its own — the
|
|
10
|
+
# An operation declares no reference of its own — the owner is
|
|
11
11
|
# what it acts for, and `identity_attribute` is how that is found.
|
|
12
12
|
def references = nil
|
|
13
13
|
|
|
14
|
-
#
|
|
14
|
+
# Never a creating command — a port operation always acts on an
|
|
15
15
|
# aggregate that already exists (`operation.to`/`identity_attribute`
|
|
16
|
-
# both name where its
|
|
16
|
+
# both name where its receiver comes from, never a birth). Answered
|
|
17
17
|
# explicitly, not derived from `references` the way `Command
|
|
18
18
|
# #creates?` is (`references.nil?` would read every operation as
|
|
19
19
|
# creating, since `references` above is unconditionally nil) —
|
|
@@ -27,10 +27,10 @@ module Hecks
|
|
|
27
27
|
@attributes.find { |attribute| attribute.reference? && attribute.type.target_name == owner_name.to_s }
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
#
|
|
30
|
+
# The same reading `Command#addressing_key_for` gives, minus its
|
|
31
31
|
# self-addressing branch — a port operation's `references` is
|
|
32
32
|
# unconditionally nil (above), so it never means "this verb is
|
|
33
|
-
# declared
|
|
33
|
+
# declared on the very aggregate it acts on" the way a command's
|
|
34
34
|
# does; a port operation's only path back to its owner is a real,
|
|
35
35
|
# declared reference-typed attribute, which is exactly what
|
|
36
36
|
# `identity_attribute` already finds. Needed for the identical
|
|
@@ -40,7 +40,7 @@ module Hecks
|
|
|
40
40
|
def addressing_key_for(aggregate_name) = identity_attribute(aggregate_name)&.name
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
#
|
|
43
|
+
# **What a port does** — one finder over its declared operations.
|
|
44
44
|
module DomainPort
|
|
45
45
|
def operation(named) = @operations.find { |op| op.hecks_name == named.to_s }
|
|
46
46
|
end
|
|
@@ -3,8 +3,8 @@ require_relative "traits"
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Bluebook
|
|
5
5
|
module Behaviour
|
|
6
|
-
#
|
|
7
|
-
#
|
|
6
|
+
# **What an entity does**. Extended, not included — an entity is a
|
|
7
|
+
# class, so this is singleton behaviour.
|
|
8
8
|
#
|
|
9
9
|
# `settle` is the same seam an Aggregate has, reached from `absorb`
|
|
10
10
|
# rather than from a constructor because a declared entity is built
|
|
@@ -29,26 +29,26 @@ module Hecks
|
|
|
29
29
|
@queries_by_name = index_by_hecks_name(@queries)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
# S17, ADR 0026 — `@entities`, now genuinely
|
|
32
|
+
# S17, ADR 0026 — `@entities`, now genuinely nested entities
|
|
33
33
|
# (Dispatch, inside Handler) rather than always `[]`. Kept as a
|
|
34
34
|
# real reader rather than a hardcoded empty list for two
|
|
35
35
|
# reasons at once: `Value::Coercion#for_attribute` calls
|
|
36
|
-
# `.entities` on
|
|
37
|
-
# an entity's own — the moment it meets
|
|
36
|
+
# `.entities` on whatever owner it is handed — an aggregate's or
|
|
37
|
+
# an entity's own — the moment it meets any `list_of(...)`
|
|
38
38
|
# attribute (entity-typed or not — `hydrate_entity_list`'s own
|
|
39
39
|
# fallback, `return value unless entity`, only runs once
|
|
40
40
|
# `.entities` has already answered) ; and `EntityInterpreter`
|
|
41
|
-
# now walks a
|
|
41
|
+
# now walks a dotted chain of entities one level at a time
|
|
42
42
|
# (`walk_entity_chain`) exactly the way an aggregate's own
|
|
43
43
|
# `.entities` is walked for its direct children. Entity's own
|
|
44
44
|
# header comment already promises it stays "structurally
|
|
45
45
|
# interchangeable with an aggregate" for exactly this reason.
|
|
46
46
|
def entities = @entities || []
|
|
47
47
|
|
|
48
|
-
# A piece
|
|
48
|
+
# A piece owns the verbs declared on it, so they can state an
|
|
49
49
|
# identity — `Banking::Account.Ledger.Deposit` rather than a
|
|
50
50
|
# command that cannot say what it belongs to. Separate from
|
|
51
|
-
# `settle` because `declare` stamps
|
|
51
|
+
# `settle` because `declare` stamps after absorbing, once the
|
|
52
52
|
# subclass that will own them exists. `@entities` too now
|
|
53
53
|
# (S17, ADR 0026) — a nested entity states its own owner chain
|
|
54
54
|
# exactly the way a nested command does.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
4
|
+
# **What a hecksagon does** — the lookups over its declared binds.
|
|
5
5
|
module Hecksagon
|
|
6
6
|
# Aggregate-specific bind wins when one was declared; otherwise
|
|
7
7
|
# falls back to a domain-level default (`b.aggregate.nil?` — see
|
|
@@ -21,7 +21,7 @@ module Hecks
|
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
#
|
|
24
|
+
# **What a world does** — settings lookup, with the adapter-specific
|
|
25
25
|
# entry falling back to the verb's own.
|
|
26
26
|
module World
|
|
27
27
|
def for_verb(verb) = @settings.fetch(verb.to_s, {})
|
|
@@ -30,13 +30,13 @@ module Hecks
|
|
|
30
30
|
# end`) only answers for the adapter it actually names — falling
|
|
31
31
|
# back to it unconditionally applies one adapter's settings to an
|
|
32
32
|
# unrelated one. Real, corpus-caught bug: a hecksagon binding two
|
|
33
|
-
# aggregates to two different adapters under the
|
|
33
|
+
# aggregates to two different adapters under the same verb (one to
|
|
34
34
|
# Heki, one to Memory) sent Memory's lookup down Heki's generic
|
|
35
35
|
# entry, then failed `check_settings` with "Memory does not
|
|
36
36
|
# declare :dir" — the generic entry's own `settings[:adapter]`
|
|
37
37
|
# names Heki, not Memory, so the fallback was never actually for
|
|
38
38
|
# this bind. `{}` is exactly right when nothing was configured for
|
|
39
|
-
#
|
|
39
|
+
# this adapter — Memory, which takes no values at all.
|
|
40
40
|
def for_binding(verb, adapter)
|
|
41
41
|
qualified = @settings["#{verb}:#{adapter.to_s.downcase}"]
|
|
42
42
|
return qualified if qualified
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
5
|
-
# state and a transition list. Everything here
|
|
4
|
+
# **What a lifecycle does**. Its declared half is a field, a starting
|
|
5
|
+
# state and a transition list. Everything here reads that — which
|
|
6
6
|
# states exist, which transition a command takes, and how one
|
|
7
7
|
# declared transition expands into the several rows the emission
|
|
8
8
|
# carries when `from` names more than one source state.
|
|
@@ -21,7 +21,7 @@ module Hecks
|
|
|
21
21
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
|
-
#
|
|
24
|
+
# One declared transition is several rows when `from` names more
|
|
25
25
|
# than one source state — the emission carries them flat, so the
|
|
26
26
|
# fan-out happens here rather than in whatever reads it.
|
|
27
27
|
def expand(command, transition)
|
|
@@ -37,8 +37,8 @@ module Hecks
|
|
|
37
37
|
return nil if matches.empty?
|
|
38
38
|
return matches.first unless current_state
|
|
39
39
|
|
|
40
|
-
#
|
|
41
|
-
#
|
|
40
|
+
# Not `|| matches.first` — that used to silently hand back an
|
|
41
|
+
# arbitrary declared transition for `command` whenever none of
|
|
42
42
|
# them actually admitted `current_state`, picking a `target`
|
|
43
43
|
# that command dispatch would in fact have refused (that
|
|
44
44
|
# refusal is `CommandRules::Admissibility#admissible_transition`'s
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
4
|
+
# **What a policy does**. Its declared half is four plain fields; these
|
|
5
5
|
# are the readings taken off them.
|
|
6
6
|
module Policy
|
|
7
|
-
# The
|
|
7
|
+
# The bluebook's name for this construct, asked the same way of a class
|
|
8
8
|
# that has crossed over and of an IR object that has not. Collapses into
|
|
9
9
|
# Construct when this one crosses.
|
|
10
10
|
def hecks_name = @name
|
|
@@ -13,18 +13,18 @@ module Hecks
|
|
|
13
13
|
|
|
14
14
|
def event_name = Naming.unqualified(@on_event)
|
|
15
15
|
|
|
16
|
-
#
|
|
16
|
+
# Whether this policy fans out — `for_each` names a query, and a
|
|
17
17
|
# non-empty one turns a single reaction into one dispatch per row
|
|
18
18
|
# the query answers. Read by the interpreter that runs the fan-out
|
|
19
19
|
# and by the property that checks it dispatched once per row.
|
|
20
20
|
def fans_out? = !@for_each.to_s.empty?
|
|
21
21
|
|
|
22
|
-
#
|
|
22
|
+
# Whether this policy is guarded — a non-empty `where` decides
|
|
23
23
|
# whether the policy fires at all, read against the triggering
|
|
24
24
|
# event's own payload.
|
|
25
25
|
def guarded? = !@where.to_s.empty?
|
|
26
26
|
|
|
27
|
-
#
|
|
27
|
+
# The structured form of `where`, derived once — the same tree
|
|
28
28
|
# `AstJson.emit_predicate` spells for every rule row, memoized
|
|
29
29
|
# here because a policy is consulted once per event, not once per
|
|
30
30
|
# boot. Nil when there is no `where`, exactly as the wire carries
|
|
@@ -42,9 +42,9 @@ module Hecks
|
|
|
42
42
|
# it; an unmet where is a silent skip).
|
|
43
43
|
def where_rule = @where_rule ||= Given.new(description: nil, canonical: @where, ast: where_ast)
|
|
44
44
|
|
|
45
|
-
#
|
|
45
|
+
# The fan-out query's route, split the way the runtime runs it:
|
|
46
46
|
# `[query_domain, aggregate_name, query_name]`. The query runs
|
|
47
|
-
# against the triggering event's
|
|
47
|
+
# against the triggering event's own domain unless `for_each`
|
|
48
48
|
# names one ("Domain::Aggregate.query"). Deliberately independent
|
|
49
49
|
# of `across`/`target_domain`, which name where `trigger` fires,
|
|
50
50
|
# not where the fan-out's own query runs.
|
|
@@ -54,13 +54,13 @@ module Hecks
|
|
|
54
54
|
[domain, aggregate, query_name]
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
#
|
|
58
|
-
# under used to be minted
|
|
59
|
-
# _id` — a real bug: a target command declared
|
|
57
|
+
# The argument name a fan-out dispatch mints each matched row's id
|
|
58
|
+
# under used to be minted here, unconditionally, as `<aggregate>
|
|
59
|
+
# _id` — a real bug: a target command declared on the very
|
|
60
60
|
# aggregate it self-references (`Account.Freeze`) is addressed by
|
|
61
|
-
# its
|
|
61
|
+
# its own identity field's name, not a synthetic foreign key, and
|
|
62
62
|
# every such dispatch refused. That question is not this policy's
|
|
63
|
-
# to answer at all — it depends on the
|
|
63
|
+
# to answer at all — it depends on the target command's own
|
|
64
64
|
# declared shape, not on the aggregate name alone — so it now
|
|
65
65
|
# lives on `Behaviour::Command#addressing_key_for`, asked of the
|
|
66
66
|
# resolved target command by `PolicyInterpreter#addressing_key_for`.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
4
|
+
# **What a process manager does**. Its declared half is the trigger,
|
|
5
5
|
# the states and the handlers; the compensation half — `saga` — is
|
|
6
|
-
#
|
|
6
|
+
# derived from the handler that answers a refusal, not declared.
|
|
7
7
|
module ProcessManager
|
|
8
8
|
def hecks_name = @name
|
|
9
9
|
|
|
10
|
-
#
|
|
10
|
+
# The leg that answers — selected by (event, current state), C10.3
|
|
11
11
|
# (docs/semantics/bluebook-semantics.md). Two legs may answer the
|
|
12
12
|
# same event from different states; which one runs is decided by
|
|
13
13
|
# the state the instance is in, never by declaration order. With
|
|
@@ -25,7 +25,7 @@ module Hecks
|
|
|
25
25
|
|
|
26
26
|
def handles?(event) = @handlers.any? { |h| h.event_type == event.to_s }
|
|
27
27
|
|
|
28
|
-
#
|
|
28
|
+
# Whether a state is one this procedure declares — asked of a value
|
|
29
29
|
# a real run left a saga instance holding (its live or rehydrated
|
|
30
30
|
# state), the way `Lifecycle#states` is asked of an aggregate's
|
|
31
31
|
# resting field. A rehydrated instance in a state no handler could
|
|
@@ -43,13 +43,13 @@ module Hecks
|
|
|
43
43
|
leg = handler_for(Bluebook::ProcessManager::REFUSED)
|
|
44
44
|
return nil unless leg
|
|
45
45
|
|
|
46
|
-
# `compensations` — a
|
|
46
|
+
# `compensations` — a static preview, declaration order, not
|
|
47
47
|
# one instance's own runtime history (which legs a given
|
|
48
48
|
# instance actually completed is per-instance state,
|
|
49
49
|
# `SagaInterpreter`'s own `completed_compensations`, not a
|
|
50
50
|
# fact `Saga` — a pure declaration reading — could ever hold).
|
|
51
|
-
# Every `compensates`
|
|
52
|
-
# forward declaration order,
|
|
51
|
+
# Every `compensates` any handler's own dispatch declares,
|
|
52
|
+
# forward declaration order, then whatever this leg's own
|
|
53
53
|
# hand-written body still lists — coexistence, not replacement
|
|
54
54
|
# (`ProcessManagerBuilder::HandlerBuilder#dispatch_impl`'s own
|
|
55
55
|
# comment): a saga can derive some of its compensation and
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
4
|
+
# **What a read model does**. Its declared half is the gathered heads
|
|
5
5
|
# and the query shape; these are readings taken off them.
|
|
6
6
|
module ReadModel
|
|
7
7
|
def group_by_fields = @group_by.map { |row| row[:field].to_sym }
|
|
@@ -14,11 +14,11 @@ module Hecks
|
|
|
14
14
|
|
|
15
15
|
def query_name = Naming.snake(@name)
|
|
16
16
|
|
|
17
|
-
#
|
|
17
|
+
# Which gathered heads the filtering applies to (ADR 0055) — plural,
|
|
18
18
|
# since `where`/`order_by`/`limit`/`offset` can now each independently
|
|
19
19
|
# name a many-side head via `on:` once there's more than one. A read
|
|
20
20
|
# model with a single many-side head keeps the old reading: every
|
|
21
|
-
#
|
|
21
|
+
# untargeted option (plus `group_by`/`count`/`median`, still
|
|
22
22
|
# single-head-only — ADR 0055) applies to it, same as before `on:`
|
|
23
23
|
# existed. With several many-side heads, only the ones actually named
|
|
24
24
|
# by a targeted option are eligible.
|
|
@@ -33,7 +33,7 @@ module Hecks
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
# The pre-`on:` reading (ADR 0055), unchanged: with exactly one
|
|
36
|
-
# many-side head, every
|
|
36
|
+
# many-side head, every untargeted option (plus `group_by`/`count`/
|
|
37
37
|
# `median`, still single-head-only) applies to it — split out only
|
|
38
38
|
# to keep `filtered_head_names` itself under this file's own
|
|
39
39
|
# complexity budget, not because the two questions differ in kind.
|
|
@@ -43,13 +43,13 @@ module Hecks
|
|
|
43
43
|
declared ? [many.first[:as]] : []
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
#
|
|
46
|
+
# The where/order_by/limit/offset that apply to one eligible head
|
|
47
47
|
# (ADR 0055) — a small view `Ports::Query::InMemory.execute` reads
|
|
48
48
|
# exactly the way it already reads a whole `Query`/`ReadModel`
|
|
49
49
|
# (`.wheres`/`.order_by`/`.limit`/`.offset`/`.null_semantics`), scoped
|
|
50
|
-
# to `head_as`'s own aggregate: an
|
|
51
|
-
# `head_as` is the read model's
|
|
52
|
-
# reading, unchanged) ; a
|
|
50
|
+
# to `head_as`'s own aggregate: an untargeted option applies when
|
|
51
|
+
# `head_as` is the read model's one many-side head (the pre-`on:`
|
|
52
|
+
# reading, unchanged) ; a targeted one applies when its `target`
|
|
53
53
|
# resolves to `head_as`'s own aggregate.
|
|
54
54
|
FilteredOptions = Struct.new(:wheres, :order_by, :limit, :offset, :null_semantics)
|
|
55
55
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
4
|
+
# The behaviour more than one construct shares.
|
|
5
5
|
#
|
|
6
6
|
# None of this is new duplication. `attribute(named)` was written
|
|
7
7
|
# out four separate times — on Aggregate, Entity, Command and
|
|
@@ -11,20 +11,20 @@ module Hecks
|
|
|
11
11
|
# files reads as four methods, and the same method in four modules
|
|
12
12
|
# named `Behaviour::*` reads as one repeated.
|
|
13
13
|
#
|
|
14
|
-
# Written to work whether the construct is
|
|
15
|
-
# ordinary object like Aggregate) or
|
|
14
|
+
# Written to work whether the construct is included into (an
|
|
15
|
+
# ordinary object like Aggregate) or extended into (a class-shaped
|
|
16
16
|
# one like Command), because every one of them reads instance
|
|
17
17
|
# variables and nothing else.
|
|
18
18
|
|
|
19
|
-
# A
|
|
19
|
+
# A construct whose identity is a join of declared paths.
|
|
20
20
|
#
|
|
21
|
-
#
|
|
21
|
+
# The paths, in declaration order, because the identity is their join.
|
|
22
22
|
# "number.value" says which field carries the identity ; several paths
|
|
23
23
|
# say the identity is made of several facts, which is what anything
|
|
24
24
|
# named beneath another thing needs. `identity_heads` are the attributes
|
|
25
25
|
# those paths start at — what every reader that looks up or coerces an
|
|
26
26
|
# attribute actually wants — and `identified_by` is the single head,
|
|
27
|
-
# offered
|
|
27
|
+
# offered only when there is one path to have a head of. A composite has
|
|
28
28
|
# no single head, and answering with the first would be a guess ; the
|
|
29
29
|
# readers that need all of them ask for `identity_heads`.
|
|
30
30
|
module Identified
|
|
@@ -35,7 +35,7 @@ module Hecks
|
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
# A
|
|
38
|
+
# A construct that answers for its own declarations by name.
|
|
39
39
|
#
|
|
40
40
|
# Indexed once, since the declarations are final by the time the
|
|
41
41
|
# construct exists — every dispatch asks these finders by name, and a
|
|
@@ -43,19 +43,19 @@ module Hecks
|
|
|
43
43
|
# had already settled at boot.
|
|
44
44
|
#
|
|
45
45
|
# `index_by_name` takes the collections to index rather than naming
|
|
46
|
-
# them, because
|
|
46
|
+
# them, because which collections a construct has is exactly the part
|
|
47
47
|
# that differs: an aggregate has five, an entity three, a command one.
|
|
48
48
|
module Indexed
|
|
49
|
-
# Keyed by
|
|
49
|
+
# Keyed by symbol — an attribute is asked for by its declared
|
|
50
50
|
# symbol name everywhere in the runtime.
|
|
51
51
|
def index_attributes(attributes)
|
|
52
52
|
@attributes_by_name = attributes.to_h { |held| [held.name, held] }
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
# Keyed by
|
|
55
|
+
# Keyed by string and by `hecks_name` — a command, query or value
|
|
56
56
|
# object is a construct whose Ruby `name` is something else
|
|
57
57
|
# entirely (a constant path, or nothing at all for an anonymous
|
|
58
|
-
# class), so the
|
|
58
|
+
# class), so the declared name is the only one worth indexing.
|
|
59
59
|
def index_by_hecks_name(collection)
|
|
60
60
|
collection.to_h { |held| [held.hecks_name, held] }
|
|
61
61
|
end
|
|
@@ -65,7 +65,7 @@ module Hecks
|
|
|
65
65
|
def query(named) = @queries_by_name[named.to_s]
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
# A
|
|
68
|
+
# A construct that owns what it declares.
|
|
69
69
|
#
|
|
70
70
|
# Owner links are only ever read lazily — hecks_fqn at ask time,
|
|
71
71
|
# Reference#resolve at dispatch time — so the moment the construct
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
5
|
-
# is a
|
|
4
|
+
# **What a value object does**. Extended, not included — a value object
|
|
5
|
+
# is a class (`Class.new(self)`, one per declared shape), so its
|
|
6
6
|
# behaviour is singleton behaviour, and the holding half's `absorb`
|
|
7
7
|
# is what a generated constructor would be.
|
|
8
8
|
module ValueObject
|
|
9
|
-
# A one_of
|
|
9
|
+
# A one_of declared but left empty used to be indistinguishable from no
|
|
10
10
|
# one_of at all — both are `members: []` — so the rule about it could
|
|
11
11
|
# only live in the builder. Recording the declaration lets the language
|
|
12
|
-
# judge it, the same way an empty attribute
|
|
12
|
+
# judge it, the same way an empty attribute name survives into the IR
|
|
13
13
|
# and is judged there.
|
|
14
14
|
def closed_set? = @closed_set
|
|
15
15
|
|
|
16
16
|
def attribute(named) = attributes.find { |held| held.name == named.to_sym }
|
|
17
17
|
|
|
18
18
|
# A single-attribute value object (EmailAddress{address},
|
|
19
|
-
# CustomerNumber{value}) is a
|
|
19
|
+
# CustomerNumber{value}) is a name for a scalar, not a genuine
|
|
20
20
|
# group — [[feedback_name_the_scalar_field]]. `adapters/driven/
|
|
21
21
|
# sql_query_builder.rb` and `fuzzing/invalid_value_generator.rb`
|
|
22
22
|
# now read through this rather than inlining the check.
|
|
23
23
|
#
|
|
24
24
|
# `forms/field_shape.rb`'s own two `attributes.first.name` sites
|
|
25
25
|
# (`closed_set_options`/`closed_set_field`) look identical but are
|
|
26
|
-
#
|
|
26
|
+
# not the same question — they pick a closed set's discriminant
|
|
27
27
|
# column, and a closed set can be genuinely multi-attribute
|
|
28
28
|
# (`Runtime::Value::Admission#member_matches?`'s own comment
|
|
29
29
|
# names a real one: `StatementFrequency`'s `cadence`/
|