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
|
@@ -14,31 +14,34 @@ module Hecks
|
|
|
14
14
|
include AttributeCollector
|
|
15
15
|
include WordGate
|
|
16
16
|
|
|
17
|
-
# `to:` —
|
|
18
|
-
# operation body
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# (domain_port.bluebook) for operation/tells/asks to receive it —
|
|
23
|
-
# confirmed by grep across every lib/hecks/language file, not
|
|
24
|
-
# assumed.
|
|
17
|
+
# `to:` — the sanctioned replacement for `reference_to` inside an
|
|
18
|
+
# operation body: `reference_to_impl`'s own refusal message tells
|
|
19
|
+
# authors to "pass the receiving aggregate in to:", and this is
|
|
20
|
+
# the argument DomainPort's own grammar (domain_port.bluebook)
|
|
21
|
+
# gives operation/tells/asks to receive it.
|
|
25
22
|
#
|
|
26
|
-
#
|
|
23
|
+
# **Genuine routing metadata, not an attribute** — matching
|
|
27
24
|
# rust/parser/src/parse/domain_port.rs's own header comment ("The
|
|
28
25
|
# receiving aggregate is routing metadata supplied by to:, not an
|
|
29
26
|
# operation attribute"), which anticipated this shape before either
|
|
30
27
|
# side actually built it. Stored separately (below, threaded to
|
|
31
28
|
# PortOperation as `to:`) rather than reusing reference_to_impl's
|
|
32
29
|
# own attribute-adding path — Dispatcher#port_invocation
|
|
33
|
-
# (lib/hecks/runtime/dispatcher.rb) is the
|
|
30
|
+
# (lib/hecks/runtime/dispatcher.rb) is the one place that resolves
|
|
34
31
|
# routing at dispatch time; it gained a second, purely additive
|
|
35
|
-
# branch for this (falls back to a plain attribute
|
|
32
|
+
# branch for this (falls back to a plain attribute named for the
|
|
36
33
|
# owning aggregate's own identified_by field, the same "declare
|
|
37
34
|
# only external facts with attribute" the refusal message
|
|
38
35
|
# describes) rather than folding `to:` into identity_attribute's
|
|
39
36
|
# existing Reference-attribute scan, which every operation already
|
|
40
37
|
# in the corpus (Banking, pizzas, lifeadelics' own vendored
|
|
41
38
|
# PaymentGateway) still relies on unchanged.
|
|
39
|
+
#
|
|
40
|
+
# @param name [String] the operation's name
|
|
41
|
+
# @param to [Symbol, String, Module, nil] the aggregate the operation routes to; stored
|
|
42
|
+
# demodulised, and nil when the operation names none
|
|
43
|
+
# @param owner [String, nil] name of the aggregate the enclosing port is declared on
|
|
44
|
+
# @param direction [Symbol] `:inbound` for `operation`/`tells`, `:outbound` for `asks`
|
|
42
45
|
def initialize(name, to: nil, owner: nil, direction: :inbound)
|
|
43
46
|
@name = name
|
|
44
47
|
@to = to && Naming.demodulise(to)
|
|
@@ -47,23 +50,33 @@ module Hecks
|
|
|
47
50
|
@emits = []
|
|
48
51
|
end
|
|
49
52
|
|
|
50
|
-
#
|
|
53
|
+
# Refuses `reference_to` inside an operation body, except while shadow-parsing, where it
|
|
54
|
+
# adds the reference attribute frozen era text declared.
|
|
55
|
+
#
|
|
56
|
+
# Always an attribute, never the self-reference `CommandBuilder`
|
|
51
57
|
# spells — a port operation has no `creates?`/`acts_on` distinction
|
|
52
58
|
# to protect, so there is nothing for the self-reference branch to be
|
|
53
|
-
#
|
|
59
|
+
# for here. `reference_to Payment, as: :payment_id` reads the same
|
|
54
60
|
# even though the target happens to equal the owning aggregate.
|
|
55
|
-
#
|
|
56
|
-
# dispatch (slice 4b).
|
|
57
|
-
# GenericDispatch::BOOTSTRAP_CALLS_FALLBACK
|
|
61
|
+
# Answers the `reference_to` word through the table's `calls:`
|
|
62
|
+
# column — item #13's full metaprogrammed dispatch (slice 4b).
|
|
63
|
+
# Bootstrap-reachable, in `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
58
64
|
#
|
|
59
|
-
#
|
|
65
|
+
# Disabled, #335 — kept only for MetaValidator's own shadow-parsing
|
|
60
66
|
# pass (the self-hosted grammar's own KeywordSeed/ArgumentSeed rows
|
|
61
67
|
# for "reference_to" in this context are themselves declared using
|
|
62
68
|
# this construct, one level up — deleting the Ruby method would
|
|
63
69
|
# break the language describing itself, not just old domain
|
|
64
|
-
# authors). Every
|
|
70
|
+
# authors). Every real domain author reaches `to:` instead, above —
|
|
65
71
|
# a genuinely different mechanism now, not a relocated spelling of
|
|
66
72
|
# this one (see `to:`'s own comment).
|
|
73
|
+
#
|
|
74
|
+
# @param type [Symbol, String, Module] the referenced aggregate, written as a bare constant
|
|
75
|
+
# @param as [Symbol, nil] the attribute's name; nil derives it from the target, as in
|
|
76
|
+
# `:payment_id` under shadow-parsing
|
|
77
|
+
# @return [nil] nothing useful; the reference lands in `attributes`
|
|
78
|
+
# @raise [Bluebook::DSL::Malformed] always outside shadow-parsing, since routing belongs
|
|
79
|
+
# in `to:`; under shadow-parsing, if the attribute name is already declared
|
|
67
80
|
def reference_to_impl(type, as: nil)
|
|
68
81
|
unless MetaValidator.shadow_parsing?
|
|
69
82
|
raise Malformed,
|
|
@@ -74,9 +87,13 @@ module Hecks
|
|
|
74
87
|
add_reference!(type, as: as)
|
|
75
88
|
end
|
|
76
89
|
|
|
90
|
+
# Names one event an inbound operation records once the external fact has arrived.
|
|
91
|
+
#
|
|
92
|
+
# @param event_name [String, Symbol] the event's name, such as `"PaymentSettled"`
|
|
93
|
+
# @return [Array<String>] every event named so far, this one last
|
|
77
94
|
def emits(event_name) = @emits << event_name.to_s
|
|
78
95
|
|
|
79
|
-
#
|
|
96
|
+
# The two halves of an `asks`. An outbound call has exactly two
|
|
80
97
|
# endings and the chapter names both — `answers` for what came back,
|
|
81
98
|
# `refuses` for what the outside said instead. They are separate words
|
|
82
99
|
# rather than two `emits` because a reader has to be able to tell them
|
|
@@ -87,6 +104,12 @@ module Hecks
|
|
|
87
104
|
# kind-driven coerce-and-assign with nothing else, now executed
|
|
88
105
|
# by `GenericDispatch`.
|
|
89
106
|
|
|
107
|
+
# Assembles the operation, refusing words that belong to the other direction.
|
|
108
|
+
#
|
|
109
|
+
# @return [Bluebook::PortOperation] the operation with its attributes, events and routing
|
|
110
|
+
# @raise [Bluebook::DSL::Malformed] if an inbound operation declares no `emits` or
|
|
111
|
+
# declares `answers`/`refuses`, or an outbound one declares `emits` or lacks either
|
|
112
|
+
# `answers` or `refuses`
|
|
90
113
|
def build
|
|
91
114
|
outbound = @direction == :outbound
|
|
92
115
|
refuse_wrong_words!(outbound)
|
|
@@ -96,7 +119,7 @@ module Hecks
|
|
|
96
119
|
direction: @direction, answers: @answers, refuses: @refuses, to: @to
|
|
97
120
|
)
|
|
98
121
|
|
|
99
|
-
#
|
|
122
|
+
# An inbound operation still has to say something. Only inbound: an
|
|
100
123
|
# `asks` says it with `answers`/`refuses` instead, and
|
|
101
124
|
# `refuse_wrong_words!` above has already insisted on both.
|
|
102
125
|
if !outbound && @emits.empty?
|
|
@@ -110,7 +133,7 @@ module Hecks
|
|
|
110
133
|
|
|
111
134
|
private
|
|
112
135
|
|
|
113
|
-
#
|
|
136
|
+
# The one place a reference attribute actually gets added — both
|
|
114
137
|
# `to:` (initialize, above) and reference_to_impl's own shadow-
|
|
115
138
|
# parsing branch call this, so there is exactly one real
|
|
116
139
|
# implementation of "carry a Reference-typed external fact,"
|
|
@@ -120,7 +143,7 @@ module Hecks
|
|
|
120
143
|
attribute_impl(as || default_reference_name(target), Reference.new(target))
|
|
121
144
|
end
|
|
122
145
|
|
|
123
|
-
#
|
|
146
|
+
# **Each direction refuses the other's words**. `emits` on an `asks` looks
|
|
124
147
|
# right and is not: it would name one ending and leave the other
|
|
125
148
|
# nowhere. `answers` on a `tells` is worse — there is no channel back
|
|
126
149
|
# to an inbound caller at all, so it would read as a promise the
|
|
@@ -147,10 +170,21 @@ module Hecks
|
|
|
147
170
|
end
|
|
148
171
|
end
|
|
149
172
|
|
|
173
|
+
# Evaluates one operation block against a fresh builder and returns what it built.
|
|
174
|
+
#
|
|
150
175
|
# `private` above (scoping the instance methods between it and here)
|
|
151
176
|
# doesn't reach a singleton method — correctly so: `.build` is this
|
|
152
177
|
# builder's real public entry point (DomainPortBuilder calls it),
|
|
153
178
|
# never meant to be private.
|
|
179
|
+
#
|
|
180
|
+
# @param name [String] the operation's name
|
|
181
|
+
# @param to [Symbol, String, Module, nil] the aggregate the operation routes to, or nil
|
|
182
|
+
# @param owner [String, nil] name of the aggregate the enclosing port is declared on
|
|
183
|
+
# @param direction [Symbol] `:inbound` for `operation`/`tells`, `:outbound` for `asks`
|
|
184
|
+
# @yield the operation body, evaluated with the builder as `self`; may be omitted
|
|
185
|
+
# @return [Bluebook::PortOperation] the built operation
|
|
186
|
+
# @raise [Bluebook::DSL::Malformed] if the body uses the other direction's words, omits
|
|
187
|
+
# the ones its own direction requires, or uses a word the grammar does not admit
|
|
154
188
|
# rubocop:disable-next Lint/IneffectiveAccessModifier
|
|
155
189
|
def self.build(name, to: nil, owner: nil, direction: :inbound, &block)
|
|
156
190
|
builder = new(name, to: to, owner: owner, direction: direction)
|
|
@@ -7,7 +7,7 @@ module Hecks
|
|
|
7
7
|
# correlates its instances (`correlates_by`), and the `transition`-
|
|
8
8
|
# declared state machine whose `dispatch`es (each optionally paired
|
|
9
9
|
# with its own per-dispatch `compensates`) become its handlers. States
|
|
10
|
-
# are
|
|
10
|
+
# are derived from the transitions rather than declared separately
|
|
11
11
|
# (S7, ADR 0025).
|
|
12
12
|
class ProcessManagerBuilder
|
|
13
13
|
GRAMMAR_CONTEXT = "ProcessManager".freeze
|
|
@@ -21,13 +21,13 @@ module Hecks
|
|
|
21
21
|
@handlers = []
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
# `starts_on Transfer::TransferRequested` —
|
|
25
|
-
#
|
|
24
|
+
# `starts_on Transfer::TransferRequested` — bare constant
|
|
25
|
+
# accepted (ADR 0025, S6 — "events first-class"), resolved
|
|
26
26
|
# through `ConstShim` the same way `on_impl`/`transition_impl`
|
|
27
27
|
# resolve an event reference — but through `Naming.
|
|
28
|
-
# event_name_ref`,
|
|
28
|
+
# event_name_ref`, not `Naming.event_ref` (that method's own
|
|
29
29
|
# header has the full account: `SagaInterpreter` matches
|
|
30
|
-
# `pm.starts_on`/`pm.ends_on` against a
|
|
30
|
+
# `pm.starts_on`/`pm.ends_on` against a bare `event.name`, never
|
|
31
31
|
# a "." qualified one). A plain String still passes through
|
|
32
32
|
# unchanged, both for `shadow_parse` and for any corpus site a
|
|
33
33
|
# future pass hasn't migrated yet.
|
|
@@ -45,7 +45,7 @@ module Hecks
|
|
|
45
45
|
# kind-driven coerce-and-assign with nothing else, executed by
|
|
46
46
|
# `GenericDispatch`.
|
|
47
47
|
#
|
|
48
|
-
# `starts_on`/`ends_on`
|
|
48
|
+
# `starts_on`/`ends_on` used to be table-driven the same way,
|
|
49
49
|
# until ADR 0025 S6 gave each a second, `kind: "constant"`
|
|
50
50
|
# ArgumentSeed row (`starts_on Transfer::TransferRequested`) —
|
|
51
51
|
# `GenericDispatch::COERCE_BY_KIND` only knows `"text"`/
|
|
@@ -54,25 +54,25 @@ module Hecks
|
|
|
54
54
|
# (`arguments.size == 1` below), so both words are hand-written
|
|
55
55
|
# again, `calls:`-routed like `transition` already is.
|
|
56
56
|
|
|
57
|
-
#
|
|
58
|
-
# reactions"): the
|
|
57
|
+
# One state-machine vocabulary (S7, ADR 0025 — "events and
|
|
58
|
+
# reactions"): the same word `Lifecycle#transition` already
|
|
59
59
|
# carries, one level over — `transition "AccountDebited" =>
|
|
60
60
|
# "awaiting_credit", from: "requested" do ... end` replaces `on
|
|
61
61
|
# "AccountDebited", transition: { "requested" => "awaiting_
|
|
62
62
|
# credit" } do ... end`. Same bare rocket-pair argument shape
|
|
63
|
-
# (not a
|
|
63
|
+
# (not a named `transition:` kwarg wrapping a second Hash), same
|
|
64
64
|
# `from:` — including the array form Lifecycle's own commands
|
|
65
65
|
# could already take and a process manager's own events could
|
|
66
|
-
# not — and the states a procedure runs on are
|
|
66
|
+
# not — and the states a procedure runs on are derived from the
|
|
67
67
|
# transitions that name them, the same way `Behaviour::Lifecycle
|
|
68
68
|
# #states` already derives an aggregate's ; `state "x"` lines
|
|
69
69
|
# duplicated exactly what the transition list already said,
|
|
70
70
|
# and could drift from it (`validate!`'s own "undeclared state"
|
|
71
71
|
# check existed only because they could).
|
|
72
72
|
#
|
|
73
|
-
# `starts_on`/`ends_on` are
|
|
73
|
+
# `starts_on`/`ends_on` are not unified into this — verified
|
|
74
74
|
# against the real corpus rather than assumed: Settlement's own
|
|
75
|
-
# `ends_on "TransferSettled"` names an event
|
|
75
|
+
# `ends_on "TransferSettled"` names an event none of its own
|
|
76
76
|
# transitions ever handle (`Transfer.Settle`'s own emission, a
|
|
77
77
|
# full step downstream of the transition that dispatches it),
|
|
78
78
|
# so "the terminal state's own event" is not a fact the
|
|
@@ -81,15 +81,15 @@ module Hecks
|
|
|
81
81
|
# cover the case, which is not less vocabulary than keeping the
|
|
82
82
|
# one that already says it correctly.
|
|
83
83
|
#
|
|
84
|
-
#
|
|
84
|
+
# Expands immediately, unlike `Lifecycle#transition` (which
|
|
85
85
|
# defers to `Behaviour::Lifecycle#expand`, called at emission
|
|
86
86
|
# time) — `ProcessManager`'s own IR constructor takes `states:`/
|
|
87
87
|
# `handlers:` exactly as it always has, so the runtime
|
|
88
88
|
# (`Behaviour::ProcessManager`, `SagaInterpreter`, saga
|
|
89
89
|
# persistence/rehydration) needs no change at all: what changed
|
|
90
|
-
# is how the
|
|
90
|
+
# is how the declaration reaches that same shape, not the shape
|
|
91
91
|
# a real run ever sees or persists.
|
|
92
|
-
#
|
|
92
|
+
# Renamed from `transition` — item #13's full metaprogrammed
|
|
93
93
|
# dispatch (slice 4c). Not bootstrap-reachable (checked
|
|
94
94
|
# directly — no core/attached chapter declares a ProcessManager
|
|
95
95
|
# of its own).
|
|
@@ -97,9 +97,9 @@ module Hecks
|
|
|
97
97
|
mapping = mapping.dup
|
|
98
98
|
from = mapping.delete(:from)
|
|
99
99
|
|
|
100
|
-
#
|
|
100
|
+
# Always required, unlike `Lifecycle#transition`'s own `from:`
|
|
101
101
|
# — an aggregate's unconstrained transition is admitted from
|
|
102
|
-
#
|
|
102
|
+
# any current state (`Behaviour::Lifecycle#applies_from?`
|
|
103
103
|
# returns true for a nil `from`), a reading `SagaInterpreter#
|
|
104
104
|
# advance_saga`'s own admission check does not share: it tests
|
|
105
105
|
# `instance[:state] == handler.from_state` by plain equality,
|
|
@@ -120,14 +120,14 @@ module Hecks
|
|
|
120
120
|
handler.instance_eval(&block) if block
|
|
121
121
|
|
|
122
122
|
mapping.each do |event_type, target|
|
|
123
|
-
#
|
|
123
|
+
# Bare constant accepted (ADR 0025, S6 — "events first-
|
|
124
124
|
# class"), `transition Account::AccountDebited => "state"` —
|
|
125
|
-
# `Naming.event_name_ref`,
|
|
125
|
+
# `Naming.event_name_ref`, not the dotted `Naming.event_ref`
|
|
126
126
|
# transform `PolicyBuilder#on_impl` uses (that method's own
|
|
127
127
|
# header has the full account, found live wiring a real
|
|
128
128
|
# migrated corpus site into `bin/model_check` for the first
|
|
129
129
|
# time: `SagaInterpreter#advance_saga` matches `handler.
|
|
130
|
-
# event_type` against a
|
|
130
|
+
# event_type` against a bare `event.name`, never a "."
|
|
131
131
|
# qualified one — a policy's own cross-aggregate match
|
|
132
132
|
# works differently, splitting the qualifier apart from the
|
|
133
133
|
# name rather than comparing the whole string). Writing the
|
|
@@ -163,12 +163,12 @@ module Hecks
|
|
|
163
163
|
|
|
164
164
|
private
|
|
165
165
|
|
|
166
|
-
#
|
|
166
|
+
# One declared transition is several rows when `from` names more
|
|
167
167
|
# than one source state — `Behaviour::Lifecycle#expand`'s own
|
|
168
168
|
# comment, the identical fan-out, one level over: a
|
|
169
169
|
# `ProcessManagerHandler` only ever carries a single `from_state`,
|
|
170
170
|
# so a `from: [...]` transition mints one row per source, each
|
|
171
|
-
# carrying the
|
|
171
|
+
# carrying the same dispatches.
|
|
172
172
|
def expand(event_type, transition, dispatches)
|
|
173
173
|
sources = transition.from.nil? ? [nil] : Array(transition.from)
|
|
174
174
|
|
|
@@ -182,12 +182,12 @@ module Hecks
|
|
|
182
182
|
end
|
|
183
183
|
end
|
|
184
184
|
|
|
185
|
-
#
|
|
185
|
+
# Derived, not declared (S7) — every state this procedure ever
|
|
186
186
|
# runs on is already named by some transition's own `from_state`
|
|
187
187
|
# or `to_state`; a state nothing transitions into or out of is
|
|
188
188
|
# not a state this procedure has, the same reading
|
|
189
189
|
# `Behaviour::Lifecycle#states` already gives an aggregate's own
|
|
190
|
-
# field.
|
|
190
|
+
# field. First-seen order, walking declaration order — `begin_
|
|
191
191
|
# saga`'s own `pm.states.first` is what a fresh instance starts
|
|
192
192
|
# in, so the order has to survive the derivation, not just the
|
|
193
193
|
# membership.
|
|
@@ -201,15 +201,15 @@ module Hecks
|
|
|
201
201
|
"nothing would tie its events to one instance"
|
|
202
202
|
end
|
|
203
203
|
|
|
204
|
-
#
|
|
204
|
+
# **The field, named** — never the value object that carries it. A bare
|
|
205
205
|
# `correlates_by :end_to_end` reads whatever the payload holds under
|
|
206
|
-
# that key
|
|
206
|
+
# that key as the correlation key, and what a non-scalar key even
|
|
207
207
|
# is stays open (the object itself? its serialised text?).
|
|
208
208
|
# Requiring the dotted spelling —
|
|
209
209
|
# `:"end_to_end.value"` — makes every correlates_by name a scalar
|
|
210
210
|
# by construction, the same discipline `identified_by` already
|
|
211
211
|
# holds a head to. This is a syntactic check, not a type check: it
|
|
212
|
-
# does not know or care whether the field
|
|
212
|
+
# does not know or care whether the field is a value object, only
|
|
213
213
|
# that the declaration cannot leave that question open.
|
|
214
214
|
unless @correlates_by.to_s.include?(".")
|
|
215
215
|
raise InvalidProcessManager, "#{@name} correlates_by #{@correlates_by.inspect}, which names a whole " \
|
|
@@ -231,7 +231,7 @@ module Hecks
|
|
|
231
231
|
end
|
|
232
232
|
|
|
233
233
|
# C10.3 — a leg is selected by (event, current state), so two
|
|
234
|
-
# legs answering the
|
|
234
|
+
# legs answering the same event from the same state would leave
|
|
235
235
|
# the runtime to pick by declaration order, silently. Refused
|
|
236
236
|
# here, where the declaration can still be read whole. (`from:
|
|
237
237
|
# [...]` fan-out counts: `transition E => "a", from: ["x", "y"]`
|
|
@@ -253,7 +253,7 @@ module Hecks
|
|
|
253
253
|
end
|
|
254
254
|
end
|
|
255
255
|
|
|
256
|
-
#
|
|
256
|
+
# The body of one `transition ... do ... end` block — collects the
|
|
257
257
|
# `dispatch` calls (each optionally opening its own `compensates`
|
|
258
258
|
# via the nested `DispatchBuilder`) that fire when this transition
|
|
259
259
|
# is taken.
|
|
@@ -266,17 +266,17 @@ module Hecks
|
|
|
266
266
|
|
|
267
267
|
def initialize = @dispatches = []
|
|
268
268
|
|
|
269
|
-
#
|
|
269
|
+
# The command itself (ADR 0025, "events and reactions" — command
|
|
270
270
|
# references become first-class), same shape and same reasons
|
|
271
271
|
# as `PolicyBuilder#trigger`'s own header — bare constant live,
|
|
272
272
|
# quoted text only under shadow-parsing (S0a's bridge; frozen
|
|
273
273
|
# era text still writes `dispatch "Banking::Account.Debit"`).
|
|
274
274
|
#
|
|
275
|
-
#
|
|
275
|
+
# Renamed from `dispatch` — item #13's full metaprogrammed
|
|
276
276
|
# dispatch (slice 4), same reasoning as trigger_impl above.
|
|
277
277
|
#
|
|
278
|
-
#
|
|
279
|
-
#
|
|
278
|
+
# An optional block opens `compensates` on this dispatch
|
|
279
|
+
# specifically — per-dispatch saga compensation, replacing a
|
|
280
280
|
# hand-written list at the saga's own `on :refused` leg. Real,
|
|
281
281
|
# live bug this closes: `examples/banking/bluebook/transfers_
|
|
282
282
|
# and_payments.bluebook`'s own `Settlement` saga wrote a
|
|
@@ -315,9 +315,9 @@ module Hecks
|
|
|
315
315
|
spec
|
|
316
316
|
end
|
|
317
317
|
|
|
318
|
-
#
|
|
318
|
+
# The nested scope `dispatch ... do ... end` opens — one word
|
|
319
319
|
# only (`compensates`), the compensating half of the dispatch it
|
|
320
|
-
# sits inside. Its own `compensates_impl` builds a
|
|
320
|
+
# sits inside. Its own `compensates_impl` builds a second
|
|
321
321
|
# `DispatchSpec`, shape-identical to `HandlerBuilder#dispatch_
|
|
322
322
|
# impl`'s own — a compensation takes the exact same two
|
|
323
323
|
# arguments (a bare command constant, an optional `with:`)
|
|
@@ -29,7 +29,7 @@ module Hecks
|
|
|
29
29
|
# exactly like any other. No "acts on itself" case to
|
|
30
30
|
# distinguish here the way a command's own reference_to has —
|
|
31
31
|
# a query has no root of its own to act on, only parameters.
|
|
32
|
-
#
|
|
32
|
+
# Renamed from `reference_to` — item #13's full metaprogrammed
|
|
33
33
|
# dispatch (slice 4b). Bootstrap-reachable, in
|
|
34
34
|
# GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
|
|
35
35
|
def reference_to_impl(type, as: nil, optional: false)
|
|
@@ -69,14 +69,14 @@ module Hecks
|
|
|
69
69
|
|
|
70
70
|
private
|
|
71
71
|
|
|
72
|
-
# A block parameter names one of the
|
|
72
|
+
# A block parameter names one of the owner's (the aggregate or entity
|
|
73
73
|
# this query is declared on) own already-declared attributes —
|
|
74
74
|
# `query "ForDecision" do |decision| where decision: :decision end`
|
|
75
75
|
# on Submission, whose own `attribute :decision, DecisionRef` already
|
|
76
76
|
# says what `decision` is. Restating `attribute :decision, DecisionRef`
|
|
77
77
|
# a second time inside the query was pure duplication; this derives
|
|
78
78
|
# the same type from the owner instead. Only fills in a name the block
|
|
79
|
-
# body did
|
|
79
|
+
# body did not already declare explicitly (checked after instance_eval
|
|
80
80
|
# runs, so an existing bluebook still spelling it out both ways keeps
|
|
81
81
|
# working unchanged — this only removes the need to, never refuses
|
|
82
82
|
# the choice to). A block parameter matching nothing on the owner is
|
|
@@ -90,8 +90,8 @@ module Hecks
|
|
|
90
90
|
owner_attr = owner_attributes.find { |a| a.name == param_name }
|
|
91
91
|
next unless owner_attr
|
|
92
92
|
|
|
93
|
-
# `Attribute.new`
|
|
94
|
-
# entry — `owner_attr.type` is already
|
|
93
|
+
# `Attribute.new` directly, not the public `attribute(...)` DSL
|
|
94
|
+
# entry — `owner_attr.type` is already spelled (a demodulised
|
|
95
95
|
# String, `Attribute#spell`'s own doing), not a bareword the
|
|
96
96
|
# bluebook author typed, so it must not run through
|
|
97
97
|
# `AttributeCollector#attribute`'s quoted-type refusal (ADR
|
|
@@ -23,7 +23,7 @@ module Hecks
|
|
|
23
23
|
@description = value
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
#
|
|
26
|
+
# Renamed from `reference_to` — item #13's full metaprogrammed
|
|
27
27
|
# dispatch (slice 4b). Bootstrap-reachable, in
|
|
28
28
|
# GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
|
|
29
29
|
def reference_to_impl(type, as: nil)
|
|
@@ -34,13 +34,13 @@ module Hecks
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Order-independent. `many:` is decided by comparing the included type
|
|
37
|
-
# against the reference target, so this used to
|
|
37
|
+
# against the reference target, so this used to refuse an include
|
|
38
38
|
# declared before the reference — a rule guarding an implementation
|
|
39
39
|
# limitation rather than a truth about read models. The includes are
|
|
40
40
|
# collected raw and resolved at build, when the reference is known, so
|
|
41
41
|
# there is no rule left to enforce.
|
|
42
|
-
#
|
|
43
|
-
# metaprogrammed dispatch (slice 4c). `include`
|
|
42
|
+
# Renamed from `include`/`group_by` — item #13's full
|
|
43
|
+
# metaprogrammed dispatch (slice 4c). `include` is bootstrap-
|
|
44
44
|
# reachable (every core chapter's own `read_model` names which
|
|
45
45
|
# aggregates it includes with it — a first grep dismissed this
|
|
46
46
|
# as `Module#include` noise and was wrong; the cold-boot test
|
|
@@ -48,13 +48,13 @@ module Hecks
|
|
|
48
48
|
# BOOTSTRAP_CALLS_FALLBACK; `group_by` is not (no core read_model
|
|
49
49
|
# groups). The class-level `include WordGate` this file's own
|
|
50
50
|
# class body uses is `Module#include`, a different receiver,
|
|
51
|
-
# unaffected by renaming this
|
|
51
|
+
# unaffected by renaming this instance method either way.
|
|
52
52
|
def include_impl(type, as: nil)
|
|
53
53
|
@includes ||= []
|
|
54
54
|
@includes << [Naming.demodulise(type), as]
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
# `on:` (ADR 0055) —
|
|
57
|
+
# `on:` (ADR 0055) — overrides of `QuerySpecification::Common::DSL`'s
|
|
58
58
|
# shared `where_impl`/`order_by_impl`/`limit_impl`/`offset_impl`,
|
|
59
59
|
# scoped to `ReadModelBuilder` alone rather than added to the shared
|
|
60
60
|
# module `Query` also mixes in: a plain `query` has no
|
|
@@ -63,10 +63,10 @@ module Hecks
|
|
|
63
63
|
# means a `Query`'s own `where(..., on: X)` gets Ruby's own loud
|
|
64
64
|
# `unknown keyword: :on` instead of quietly doing nothing.
|
|
65
65
|
#
|
|
66
|
-
# `on:` names the target by
|
|
66
|
+
# `on:` names the target by type (`on: Character`), resolved the
|
|
67
67
|
# same way `reference_to`/`include` already resolve their own type
|
|
68
68
|
# argument (`Naming.demodulise`) — not by the include's own `as:`
|
|
69
|
-
# alias. A read model that `include`s the
|
|
69
|
+
# alias. A read model that `include`s the same type twice under two
|
|
70
70
|
# different `as:` has no way to say which one `on:` means today; no
|
|
71
71
|
# real corpus read model does this, so it's a real, deliberate scope
|
|
72
72
|
# limit (see ADR 0055), not an oversight.
|
|
@@ -77,14 +77,14 @@ module Hecks
|
|
|
77
77
|
# `status: "disputed"` captured as `**kwargs` (GenericDispatch's own
|
|
78
78
|
# `builder.send(calls, *args, **kwargs, &block)`), and Ruby stops
|
|
79
79
|
# auto-converting a bare `**hash` call into a plain positional Hash
|
|
80
|
-
#
|
|
80
|
+
# the moment a method declares any real keyword parameter — so a
|
|
81
81
|
# `(clauses, on: nil)` signature raised "wrong number of arguments
|
|
82
82
|
# (given 0, expected 1)" on every ordinary `where(field: value)`
|
|
83
83
|
# call, never reaching `on:` at all. `**rest` sidesteps this: Ruby
|
|
84
84
|
# still auto-splits `on:` into the declared keyword and gathers
|
|
85
|
-
# every
|
|
85
|
+
# every other key into `rest` regardless of how the caller wrote it.
|
|
86
86
|
#
|
|
87
|
-
# `QuerySpecification::Common::WhereClause` etc —
|
|
87
|
+
# `QuerySpecification::Common::WhereClause` etc — fully qualified,
|
|
88
88
|
# not the bare names `dsl.rb`'s own shared `where_impl` gets away
|
|
89
89
|
# with. That file is lexically nested inside `Common` itself, so
|
|
90
90
|
# `WhereClause` resolves directly; this class is nested inside
|
|
@@ -92,7 +92,7 @@ module Hecks
|
|
|
92
92
|
# `QuerySpecification::Common` at all — a bare `WhereClause` here
|
|
93
93
|
# falls through to `const_missing` and, mid-bluebook-load, that's
|
|
94
94
|
# `ConstShim`, which resolves it against the self-hosted grammar
|
|
95
|
-
# domain's
|
|
95
|
+
# domain's own unrelated `WhereClause` construct instead (a `Module`,
|
|
96
96
|
# not this `Struct`) — found directly by reproducing "undefined
|
|
97
97
|
# method `new' for module WhereClause" against a real corpus load,
|
|
98
98
|
# not guessed.
|
|
@@ -120,18 +120,18 @@ module Hecks
|
|
|
120
120
|
@offset = QuerySpecification::Common::OffsetSpec.new(value: value, target: resolve_target(on))
|
|
121
121
|
end
|
|
122
122
|
|
|
123
|
-
#
|
|
123
|
+
# Names which of the eligible head's own fields to nest its rows
|
|
124
124
|
# under — one level per field, the leaf being that row with the
|
|
125
125
|
# named fields removed (they're already spent, as the keys that
|
|
126
126
|
# reached it). The same "exactly one many-side head" rule
|
|
127
127
|
# `seal_query_options` already enforces for where/order_by/etc
|
|
128
128
|
# applies here too (`seal_group_by`) — grouping is a question
|
|
129
|
-
# about
|
|
129
|
+
# about one collection's own rows, same as those are.
|
|
130
130
|
def group_by_impl(*fields)
|
|
131
131
|
# Hash rows, `{field:}`, not bare symbols — same shape
|
|
132
132
|
# `aggregate_heads` already uses for exactly the reason it
|
|
133
133
|
# does: the language's own self-hosted grammar (`projection
|
|
134
|
-
# .bluebook`'s `GroupByField`) has to have
|
|
134
|
+
# .bluebook`'s `GroupByField`) has to have something to read a
|
|
135
135
|
# `field:` off of when `Judge` walks this list generically: a
|
|
136
136
|
# bare `Symbol` has no attribute of its own to read.
|
|
137
137
|
@group_by = fields.map { |field| { field: field.to_sym } }
|
|
@@ -139,23 +139,23 @@ module Hecks
|
|
|
139
139
|
|
|
140
140
|
# `count` -- a bare row count over the eligible many-side head's
|
|
141
141
|
# own rows (after `where`/`order_by`/`limit`/`offset` apply, the
|
|
142
|
-
# same rows `group_by` itself would nest) --
|
|
143
|
-
# match", not "which ones". A sibling
|
|
142
|
+
# same rows `group_by` itself would nest) -- answers "how many
|
|
143
|
+
# match", not "which ones". A sibling reduction to `group_by`,
|
|
144
144
|
# not a filter: `seal_aggregation` refuses combining it with
|
|
145
145
|
# `group_by` or with `median`, the same "exactly one many-side
|
|
146
146
|
# head" rule `seal_group_by` already enforces for the same
|
|
147
147
|
# reason -- a bare marker, so `@count` is left unset (nil, not
|
|
148
|
-
# false) rather than defaulted, matching the "
|
|
149
|
-
#
|
|
148
|
+
# false) rather than defaulted, matching the "absent is not
|
|
149
|
+
# empty" reading `Lifecycle`'s own optional fields already rely
|
|
150
150
|
# on for the Judge's setter dispatch (Behaviour::ReadModel#
|
|
151
151
|
# count?, ReadModelInterpreter#aggregation_target).
|
|
152
152
|
# `count` — item #13's full metaprogrammed dispatch, slice 1
|
|
153
|
-
# (whole-project table-unification survey): the
|
|
153
|
+
# (whole-project table-unification survey): the only Keyword row
|
|
154
154
|
# filling `count` — a bare marker, now stored as literal `true`
|
|
155
155
|
# by `GenericDispatch` off that same table fact.
|
|
156
156
|
|
|
157
|
-
# `median(field)` -- the median
|
|
158
|
-
# across the eligible many-side head's own rows.
|
|
157
|
+
# `median(field)` -- the median value of one numeric field
|
|
158
|
+
# across the eligible many-side head's own rows. Even count: the
|
|
159
159
|
# average of its two middle values (the standard definition,
|
|
160
160
|
# not "the lower of the two") -- see
|
|
161
161
|
# Runtime::ReadModelInterpreter#median for where that lands and
|
|
@@ -168,16 +168,16 @@ module Hecks
|
|
|
168
168
|
# same shape as `count`, above (a bare, kind-driven coerce-and-
|
|
169
169
|
# assign).
|
|
170
170
|
|
|
171
|
-
# `reference_to` is now
|
|
172
|
-
#
|
|
171
|
+
# `reference_to` is now optional — a read model with no root is a
|
|
172
|
+
# bulk one: every `include`d head reads its own aggregate whole
|
|
173
173
|
# (no FK match against a root that doesn't exist), and dispatch
|
|
174
|
-
# takes no id argument at all. This used to be
|
|
174
|
+
# takes no id argument at all. This used to be required, on the
|
|
175
175
|
# assumption a read model was always "one root record's own
|
|
176
176
|
# cross-aggregate view" — true of every real corpus report so
|
|
177
177
|
# far, but not a truth about read models themselves: `group_by`'s
|
|
178
|
-
# own real use (nesting an aggregate's
|
|
178
|
+
# own real use (nesting an aggregate's own whole table by its own
|
|
179
179
|
# field values) has no root to speak of. Still needs to describe
|
|
180
|
-
#
|
|
180
|
+
# something — zero includes and no reference is refused.
|
|
181
181
|
def build
|
|
182
182
|
if !@reference_target && Array(@includes).empty?
|
|
183
183
|
raise Malformed,
|
|
@@ -213,18 +213,18 @@ module Hecks
|
|
|
213
213
|
# (the "one" side, the reference target itself, is a single row;
|
|
214
214
|
# ordering, paging, or tenant-scoping one row means nothing). ADR
|
|
215
215
|
# 0055 gave `where`/`order_by`/`limit`/`offset` an `on:` to name
|
|
216
|
-
#
|
|
216
|
+
# which many-side collection they mean, so this asks two questions
|
|
217
217
|
# now instead of one:
|
|
218
218
|
#
|
|
219
219
|
# 1. Does every declared `on:` actually name a many-side included
|
|
220
220
|
# aggregate? Checked regardless of how many many-side heads
|
|
221
221
|
# exist — a typo refuses immediately, not only once ambiguity
|
|
222
222
|
# would otherwise bite.
|
|
223
|
-
# 2. Is there still an
|
|
223
|
+
# 2. Is there still an untargeted option declared (including
|
|
224
224
|
# `authorize`'s own `tenant:`, which has no `on:` of its own —
|
|
225
225
|
# a real, deliberate scope limit, see ADR 0055)? An untargeted
|
|
226
226
|
# option still needs exactly one many-side head to mean
|
|
227
|
-
# anything unambiguous — the
|
|
227
|
+
# anything unambiguous — the original rule, unchanged, and
|
|
228
228
|
# still worded the same way (`spec/runtime/
|
|
229
229
|
# read_model_interpreter_spec.rb`'s existing refusal regex
|
|
230
230
|
# still matches).
|
|
@@ -246,7 +246,7 @@ module Hecks
|
|
|
246
246
|
end
|
|
247
247
|
|
|
248
248
|
# Question 1 of `seal_query_options`'s own two, split out to keep
|
|
249
|
-
# both under the same "one job per method" shape every
|
|
249
|
+
# both under the same "one job per method" shape every other seal in
|
|
250
250
|
# this file already holds to (each raises its own one Malformed, for
|
|
251
251
|
# its own one reason).
|
|
252
252
|
def validate_declared_targets!(many)
|
|
@@ -275,7 +275,7 @@ module Hecks
|
|
|
275
275
|
end
|
|
276
276
|
|
|
277
277
|
# Same shape as `seal_query_options`, same reason — `group_by`
|
|
278
|
-
# answers a question about
|
|
278
|
+
# answers a question about one collection's own rows, so zero or
|
|
279
279
|
# several many-side heads leaves it with no unambiguous target.
|
|
280
280
|
def seal_group_by
|
|
281
281
|
return unless @group_by&.any?
|
|
@@ -289,10 +289,10 @@ module Hecks
|
|
|
289
289
|
"own rows; name which one by including only it"
|
|
290
290
|
end
|
|
291
291
|
|
|
292
|
-
# `count`/`median` are the
|
|
292
|
+
# `count`/`median` are the other two reductions a read model may
|
|
293
293
|
# declare over its one eligible collection — same "exactly one
|
|
294
294
|
# many-side head" rule as `seal_group_by`, plus a rule
|
|
295
|
-
# `seal_group_by` doesn't need: a read model reports
|
|
295
|
+
# `seal_group_by` doesn't need: a read model reports one shape,
|
|
296
296
|
# so `count` and `median` cannot both be declared, and neither
|
|
297
297
|
# may combine with `group_by` (nesting rows and reducing them to
|
|
298
298
|
# a scalar are answers to different questions ; a caller asking
|