hecks 1.2.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 +60 -2
- 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/postgres_era.adapter +5 -0
- 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 +145 -0
- data/lib/hecks/behaviors/dsl.rb +2 -2
- data/lib/hecks/behaviors/expectations.rb +51 -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 +27 -3
- 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 +122 -86
- 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 +301 -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 +183 -20
- 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 +361 -0
- 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 +124 -0
- data/lib/hecks/fuzzing/form_census.rb +199 -0
- 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 +226 -38
- data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +161 -0
- data/lib/hecks/fuzzing/properties/corrections.rb +100 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +321 -41
- data/lib/hecks/fuzzing/properties/guards.rb +129 -26
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +31 -11
- data/lib/hecks/fuzzing/properties/outbox.rb +142 -0
- data/lib/hecks/fuzzing/properties/querying.rb +17 -17
- data/lib/hecks/fuzzing/properties.rb +84 -39
- data/lib/hecks/fuzzing/qa_settings.rb +152 -0
- data/lib/hecks/fuzzing/replay.rb +224 -114
- data/lib/hecks/fuzzing/rotation_priority.rb +94 -0
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
- data/lib/hecks/fuzzing/self_consistency.rb +676 -0
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +526 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +101 -30
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +55 -8
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +31 -11
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +123 -32
- data/lib/hecks/fuzzing/sequence_generator.rb +152 -31
- data/lib/hecks/fuzzing/shrinker.rb +197 -0
- data/lib/hecks/fuzzing/structural_skips.rb +39 -0
- data/lib/hecks/fuzzing/sweep_depth.rb +53 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +180 -0
- data/lib/hecks/fuzzing/value_generator.rb +66 -14
- data/lib/hecks/fuzzing.rb +11 -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 +365 -15
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +9 -9
- data/lib/hecks/naming.rb +89 -21
- 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 +160 -58
- 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 +44 -6
- 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 +338 -83
- 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 +29 -6
- 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 +250 -0
- data/lib/hecks/projections/glossary/markdown.rb +105 -0
- data/lib/hecks/projections/glossary/mermaid.rb +110 -0
- data/lib/hecks/projections/glossary/page.css +271 -0
- data/lib/hecks/projections/glossary/page.js +72 -0
- data/lib/hecks/projections/glossary/sections.rb +17 -0
- data/lib/hecks/projections/glossary/sentences.rb +205 -0
- data/lib/hecks/projections/glossary.rb +214 -286
- 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 +15 -22
- 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 +132 -24
- 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 +64 -67
- 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 +94 -19
- 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 +282 -46
- data/lib/hecks/runtime/entity_interpreter.rb +160 -69
- 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 +111 -56
- data/lib/hecks/runtime/reaction_invocation.rb +76 -9
- 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 -112
- 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 +56 -27
- data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
- data/lib/hecks/runtime/routing.rb +10 -88
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
- data/lib/hecks/runtime/saga_interpreter.rb +93 -56
- 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 +379 -226
- data/lib/hecks/runtime/value/entity_list_coercion.rb +248 -0
- data/lib/hecks/runtime/value.rb +28 -23
- 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 +207 -5
- 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 +43 -2
|
@@ -11,6 +11,14 @@ module Hecks
|
|
|
11
11
|
class << self
|
|
12
12
|
attr_accessor :resolver
|
|
13
13
|
|
|
14
|
+
# Installs a resolver for the duration of a block, restoring the earlier one afterwards
|
|
15
|
+
# even if the block raises.
|
|
16
|
+
#
|
|
17
|
+
# @param resolver [#call] called with the missing constant's name as a Symbol; whatever
|
|
18
|
+
# it returns is what the bare constant evaluates to
|
|
19
|
+
# @yield the DSL code whose undeclared constants the resolver should answer
|
|
20
|
+
# @yieldreturn [Object] any value; it becomes this method's result
|
|
21
|
+
# @return [Object] whatever the block returns
|
|
14
22
|
def with(resolver)
|
|
15
23
|
previous = @resolver
|
|
16
24
|
@resolver = resolver
|
|
@@ -19,45 +27,63 @@ module Hecks
|
|
|
19
27
|
@resolver = previous
|
|
20
28
|
end
|
|
21
29
|
|
|
30
|
+
# Reports whether a resolver is installed, meaning code is running inside a DSL block.
|
|
31
|
+
#
|
|
32
|
+
# @return [Boolean] true inside a `with` block, or after `resolver=` set one directly
|
|
22
33
|
def active? = !@resolver.nil?
|
|
23
34
|
end
|
|
24
35
|
|
|
25
|
-
#
|
|
36
|
+
# The scoped-constant bridge (ADR 0025, docs/dsl-work-slices.md's
|
|
26
37
|
# S0b) — a Symbol cannot answer `::`, so `Account::Debit` and
|
|
27
38
|
# `admits: Account::LedgerDirection` could never resolve past
|
|
28
|
-
# their
|
|
39
|
+
# their first segment while `Account` returned a bare Symbol
|
|
29
40
|
# (`resolver = ->(const) { const }`, bluebook_builder.rb's own
|
|
30
|
-
# comment on why that was right for a
|
|
41
|
+
# comment on why that was right for a bare name): Ruby's `::`
|
|
31
42
|
# operator raises `TypeError` on the returned value before any
|
|
32
43
|
# DSL code runs at all, unless that value is itself a Module.
|
|
33
44
|
#
|
|
34
|
-
# A
|
|
35
|
-
# that reason — nothing else answers `::`. Every
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
45
|
+
# A real `Module` subclass, not a decorated Symbol, for exactly
|
|
46
|
+
# that reason — nothing else answers `::`. Every consumer of a
|
|
47
|
+
# bareword type keeps working duck-typed: `Attribute#spell`'s
|
|
48
|
+
# `type.is_a?(Module)` branch fires for one of these where a
|
|
49
|
+
# bare Symbol falls to `type.to_s`, and `Naming.demodulise`
|
|
50
|
+
# (`path.split("::").last`) gives the identical string either
|
|
51
|
+
# way for a single segment — the two branches are equivalent
|
|
52
|
+
# for a name with no `::` in it, which is every unscoped
|
|
53
|
+
# bareword.
|
|
43
54
|
class ScopedConstant < Module
|
|
55
|
+
# Wraps a constant path, the form a `ConstShim` resolver hands back for a bareword.
|
|
56
|
+
#
|
|
57
|
+
# @param path [Symbol, String] one segment such as `:Account`, or a `::`-joined path
|
|
58
|
+
# @return [Bluebook::DSL::ConstShim::ScopedConstant] a module standing in for that path
|
|
44
59
|
def self.for(path) = new(path.to_s)
|
|
45
60
|
|
|
61
|
+
# @param path [String] the constant path this module stands in for
|
|
46
62
|
def initialize(path)
|
|
47
63
|
super()
|
|
48
64
|
@path = path
|
|
49
65
|
end
|
|
50
66
|
|
|
51
|
-
#
|
|
67
|
+
# Extends the path by one segment, so `Account::Debit` resolves past `Account`.
|
|
68
|
+
#
|
|
69
|
+
# One more segment, the same way an unresolved const anywhere
|
|
52
70
|
# else does — `Account::Debit::Anything` keeps chaining rather
|
|
53
71
|
# than refusing, since nothing here knows how deep a reference
|
|
54
72
|
# is meant to go; the DSL keyword that finally reads `.to_s`
|
|
55
73
|
# is the one place that does.
|
|
74
|
+
#
|
|
75
|
+
# @param name [Symbol] the segment written after `::`
|
|
76
|
+
# @return [Bluebook::DSL::ConstShim::ScopedConstant] a new constant for the longer path
|
|
56
77
|
def const_missing(name) = ScopedConstant.for("#{@path}::#{name}")
|
|
57
78
|
|
|
58
79
|
def to_s = @path
|
|
59
80
|
def to_sym = @path.to_sym
|
|
60
81
|
def inspect = @path
|
|
82
|
+
|
|
83
|
+
# Exposes the raw path under a name no ordinary `Module` answers, so `==` can compare
|
|
84
|
+
# two scoped constants without going through `to_s`.
|
|
85
|
+
#
|
|
86
|
+
# @return [String] the `::`-joined path, such as `"Account::Debit"`
|
|
61
87
|
def hecks_path = @path
|
|
62
88
|
|
|
63
89
|
def ==(other) = other.is_a?(ScopedConstant) ? @path == other.hecks_path : @path.to_sym == other
|
|
@@ -65,15 +91,20 @@ module Hecks
|
|
|
65
91
|
def hash = @path.hash
|
|
66
92
|
end
|
|
67
93
|
|
|
68
|
-
# A
|
|
94
|
+
# A scoped name is written as text, not as a constant path — see the
|
|
69
95
|
# note on `admits:` in AttributeCollector. A resolver returning a
|
|
70
96
|
# Module (so that `Vocabulary::QueryComparator` reaches a second
|
|
71
97
|
# `const_missing`) was tried and cannot be made to hold : `Facade::
|
|
72
|
-
# Surface` installs
|
|
98
|
+
# Surface` installs every aggregate name as a top-level constant, so
|
|
73
99
|
# once any facade is built, `Vocabulary` resolves to that real module
|
|
74
100
|
# and never reaches this hook at all. A spelling that works only
|
|
75
101
|
# before a facade exists is worse than one that always works.
|
|
76
102
|
module Hook
|
|
103
|
+
# Answers an undeclared top-level constant from the active resolver, if there is one.
|
|
104
|
+
#
|
|
105
|
+
# @param name [Symbol] the missing constant's name
|
|
106
|
+
# @return [Object] whatever the active resolver returns for `name`
|
|
107
|
+
# @raise [NameError] if no resolver is installed, as Ruby raises for any unknown constant
|
|
77
108
|
def const_missing(name)
|
|
78
109
|
resolver = ConstShim.resolver
|
|
79
110
|
resolver ? resolver.call(name) : super
|
|
@@ -14,21 +14,27 @@ module Hecks
|
|
|
14
14
|
|
|
15
15
|
include WordGate
|
|
16
16
|
|
|
17
|
-
# `legacy_bare_port:` —
|
|
17
|
+
# `legacy_bare_port:` — only `Hecks.port`'s own top-level method
|
|
18
18
|
# (lib/hecks.rb) passes `true`. `PortBuilder#build` never refused
|
|
19
19
|
# an empty build (no verb, no signal, nothing) — `Port.new(verb:
|
|
20
20
|
# nil, signal: :reply)` is a real, allowed shape dsl_spec.rb's own
|
|
21
21
|
# "a port" tests rely on (`signal`-only, no `verb` at all). The
|
|
22
|
-
#
|
|
23
|
-
# (`HecksagonBuilder#port_impl`) callers both reach this
|
|
22
|
+
# aggregate-scoped (`BindingProxy#port`) and hecksagon-root
|
|
23
|
+
# (`HecksagonBuilder#port_impl`) callers both reach this same
|
|
24
24
|
# class with `owner: nil` too when they're building the bare-verb
|
|
25
|
-
# shape (`port_impl`'s own root-level port can be
|
|
25
|
+
# shape (`port_impl`'s own root-level port can be either shape,
|
|
26
26
|
# decided only after `build` returns) — so `owner.nil?` cannot be
|
|
27
27
|
# the discriminator between "old Hecks.port semantics" and "real
|
|
28
28
|
# DomainPort semantics"; those two callers correctly want the
|
|
29
29
|
# stricter "declares no verb and no operations" refusal `build`
|
|
30
30
|
# already raises below, unchanged. Only the literal top-level
|
|
31
31
|
# `.port` file caller wants the older, looser rule.
|
|
32
|
+
#
|
|
33
|
+
# @param name [String] the port's name
|
|
34
|
+
# @param owner [String, nil] name of the aggregate the port is declared on, handed to
|
|
35
|
+
# each operation's builder; nil for a root-level or top-level port
|
|
36
|
+
# @param legacy_bare_port [Boolean] true only for `Hecks.port`: an empty body then builds
|
|
37
|
+
# a verbless `Port` rather than being refused
|
|
32
38
|
def initialize(name, owner: nil, legacy_bare_port: false)
|
|
33
39
|
@name = name
|
|
34
40
|
@owner = owner
|
|
@@ -38,40 +44,63 @@ module Hecks
|
|
|
38
44
|
@legacy_bare_port = legacy_bare_port
|
|
39
45
|
end
|
|
40
46
|
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
47
|
+
# Declares an inbound operation: a fact the outside world delivers to this domain.
|
|
48
|
+
#
|
|
49
|
+
# **What the outside tells us** — an external fact arriving, translated
|
|
50
|
+
# into this domain's own word for it. Spelled `operation` or `tells`;
|
|
51
|
+
# `operation` stays because the corpus is full of it, and renaming
|
|
52
|
+
# a word costs every chapter that uses it for no gain a reader can
|
|
53
|
+
# feel.
|
|
46
54
|
#
|
|
47
|
-
#
|
|
48
|
-
# (slice 4c). `operation`/`tells` are
|
|
55
|
+
# Answers both words — item #13's full metaprogrammed dispatch
|
|
56
|
+
# (slice 4c). `operation`/`tells` are two separate Keyword rows
|
|
49
57
|
# (a word admitting two forms) that both name `calls: "tells_impl"`
|
|
50
|
-
# — the routing between the two spellings
|
|
58
|
+
# — the routing between the two spellings lives in the table,
|
|
51
59
|
# not in a Ruby `alias`. Not bootstrap-reachable (checked
|
|
52
|
-
# directly), so
|
|
60
|
+
# directly), so its `BOOTSTRAP_CALLS_FALLBACK` row is never consulted.
|
|
61
|
+
#
|
|
62
|
+
# @param name [String] the operation's name, such as `"PaymentSettled"`
|
|
63
|
+
# @param to [Symbol, String, Module, nil] the aggregate the operation routes to, written
|
|
64
|
+
# as a bare constant; nil leaves routing to the operation's own attributes
|
|
65
|
+
# @yield the operation body (`attribute`, `emits`), evaluated against a
|
|
66
|
+
# `PortOperationBuilder`
|
|
67
|
+
# @return [Array<Bluebook::PortOperation>] every operation declared so far, this one last
|
|
68
|
+
# @raise [Bluebook::DSL::Malformed] if the body declares no `emits`, or uses `answers` or
|
|
69
|
+
# `refuses`, which belong to an `asks`
|
|
53
70
|
def tells_impl(name, to: nil, &)
|
|
54
71
|
@operations << PortOperationBuilder.build(name, to: to, owner: @owner, direction: :inbound, &)
|
|
55
72
|
end
|
|
56
73
|
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
74
|
+
# Declares an outbound operation: a question this domain puts to the outside world.
|
|
75
|
+
#
|
|
76
|
+
# **What we ask of the outside** — the direction that lets a domain
|
|
77
|
+
# call an adapter, not only be called by one. An `asks` is dispatched
|
|
78
|
+
# like any other port operation, so
|
|
60
79
|
# a `policy` can trigger it off an event, and it comes back as one of
|
|
61
80
|
# the two events it named — which is what makes the outside world
|
|
62
81
|
# something the model can reason about rather than a place exceptions
|
|
63
82
|
# come from.
|
|
64
83
|
#
|
|
65
|
-
#
|
|
66
|
-
# (slice 4c), same reasoning as
|
|
84
|
+
# Answers the `asks` word through the table's `calls:` column — item
|
|
85
|
+
# #13's full metaprogrammed dispatch (slice 4c), same reasoning as
|
|
86
|
+
# `tells_impl` above.
|
|
87
|
+
#
|
|
88
|
+
# @param name [String] the operation's name
|
|
89
|
+
# @param to [Symbol, String, Module, nil] the aggregate the operation routes to, written
|
|
90
|
+
# as a bare constant; nil leaves routing to the operation's own attributes
|
|
91
|
+
# @yield the operation body (`attribute`, `answers`, `refuses`), evaluated against a
|
|
92
|
+
# `PortOperationBuilder`
|
|
93
|
+
# @return [Array<Bluebook::PortOperation>] every operation declared so far, this one last
|
|
94
|
+
# @raise [Bluebook::DSL::Malformed] if the body declares `emits`, or lacks either
|
|
95
|
+
# `answers` or `refuses`
|
|
67
96
|
def asks_impl(name, to: nil, &)
|
|
68
97
|
@operations << PortOperationBuilder.build(name, to: to, owner: @owner, direction: :outbound, &)
|
|
69
98
|
end
|
|
70
99
|
|
|
71
|
-
#
|
|
100
|
+
# The driven half of the same word. `operation`/`emits` translates an
|
|
72
101
|
# inbound fact into this domain's own event vocabulary — there is no
|
|
73
102
|
# channel back to a caller beyond the events it emits. `verb` is the
|
|
74
|
-
# opposite direction: the domain calling
|
|
103
|
+
# opposite direction: the domain calling out to a swappable adapter
|
|
75
104
|
# and getting a real value back (a checkout URL, a fetched document),
|
|
76
105
|
# exactly what `Hecks.port "name" do verb "x" end` already builds —
|
|
77
106
|
# this is that same `Port`, reached from the same `port` call
|
|
@@ -83,9 +112,12 @@ module Hecks
|
|
|
83
112
|
# coerce-and-assign with nothing else, now executed by
|
|
84
113
|
# `GenericDispatch`.
|
|
85
114
|
|
|
115
|
+
# Names the verb aggregates call this port by, which makes it a driven `Port` rather than
|
|
116
|
+
# a `DomainPort` of operations.
|
|
117
|
+
#
|
|
86
118
|
# `Hecks.port "x" do verb "y"; signal :effect end`'s own two words,
|
|
87
119
|
# reachable here too — a bare-verb `DomainPortBuilder.build` falls
|
|
88
|
-
# back to the
|
|
120
|
+
# back to the same `Port` object `PortBuilder` produces (`build`,
|
|
89
121
|
# below), so any `.port` file can migrate to being parsed by this
|
|
90
122
|
# builder with zero change to its own text, or to any caller that
|
|
91
123
|
# reads `.verb`/`.signal` off the `Port` it gets back. Ordinary
|
|
@@ -93,20 +125,40 @@ module Hecks
|
|
|
93
125
|
# header is explicit that a word answered this way never reaches
|
|
94
126
|
# its `method_missing`, so no new self-hosted grammar row is
|
|
95
127
|
# needed for either word under this context.
|
|
128
|
+
#
|
|
129
|
+
# @param value [String, Symbol] the verb, such as `"charged_by"`
|
|
130
|
+
# @return [String] the verb as stored
|
|
96
131
|
def verb(value) = @verb = value.to_s
|
|
132
|
+
|
|
133
|
+
# Sets whether a verb-shaped port hands a value back; one left unset signals `:reply`.
|
|
134
|
+
#
|
|
135
|
+
# @param value [Symbol, String] `:reply` when the adapter answers with a value,
|
|
136
|
+
# `:effect` when it is called only for its effect
|
|
137
|
+
# @return [Symbol] the signal as stored
|
|
97
138
|
def signal(value) = @signal = value.to_sym
|
|
98
139
|
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
140
|
+
# Declares one method an adapter bound to a verb-shaped port must respond to.
|
|
141
|
+
#
|
|
142
|
+
# **The method contract** — `PortBuilder#answers`'s own twin: a
|
|
143
|
+
# `.port` file parsed through this builder (the repoint
|
|
144
|
+
# `lib/hecks.rb#port`'s own comment describes) can declare one
|
|
145
|
+
# (`extraction.port`'s own
|
|
103
146
|
# `answers :canonical`, real, live corpus text) — this builder's
|
|
104
147
|
# bare-verb fallback needs to carry it through to the same `Port`
|
|
105
148
|
# object `PortBuilder` itself would have built, or the migration
|
|
106
149
|
# would silently drop a method-contract check for any `.port`
|
|
107
150
|
# file that uses this word.
|
|
151
|
+
#
|
|
152
|
+
# @param name [Symbol, String] the method name, such as `:canonical`
|
|
153
|
+
# @return [Array<Symbol>] every method declared so far, this one last
|
|
108
154
|
def answers(name) = @answers << name.to_sym
|
|
109
155
|
|
|
156
|
+
# Assembles whichever of the two port shapes the body declared.
|
|
157
|
+
#
|
|
158
|
+
# @return [Bluebook::Port, Bluebook::DomainPort] a `Port` when the body named a `verb`
|
|
159
|
+
# (or was empty under `legacy_bare_port:`), otherwise a `DomainPort` of its operations
|
|
160
|
+
# @raise [Bluebook::DSL::Malformed] if the body declares both a verb and operations,
|
|
161
|
+
# declares neither without `legacy_bare_port:`, or the port language refuses the `Port`
|
|
110
162
|
def build
|
|
111
163
|
if @verb && !@operations.empty?
|
|
112
164
|
raise Malformed,
|
|
@@ -123,6 +175,19 @@ module Hecks
|
|
|
123
175
|
DomainPort.new(name: @name, operations: @operations)
|
|
124
176
|
end
|
|
125
177
|
|
|
178
|
+
# Evaluates a `port` block against a fresh builder and returns whichever shape it declared.
|
|
179
|
+
#
|
|
180
|
+
# @param name [String] the port's name
|
|
181
|
+
# @param owner [String, nil] name of the aggregate the port is declared on, or nil for a
|
|
182
|
+
# root-level or top-level port
|
|
183
|
+
# @param legacy_bare_port [Boolean] true only for `Hecks.port`, which lets an empty body
|
|
184
|
+
# build a verbless `Port`
|
|
185
|
+
# @yield the port body, evaluated with the builder as `self`; may be omitted
|
|
186
|
+
# @return [Bluebook::Port, Bluebook::DomainPort] a verb-shaped `Port`, or a `DomainPort`
|
|
187
|
+
# holding the declared operations
|
|
188
|
+
# @raise [Bluebook::DSL::Malformed] if the body declares both shapes, declares neither
|
|
189
|
+
# without `legacy_bare_port:`, holds an operation its builder refuses, or uses a word
|
|
190
|
+
# the `DomainPort` grammar does not admit
|
|
126
191
|
def self.build(name, owner: nil, legacy_bare_port: false, &block)
|
|
127
192
|
builder = new(name, owner: owner, legacy_bare_port: legacy_bare_port)
|
|
128
193
|
builder.instance_eval(&block) if block
|
|
@@ -30,18 +30,18 @@ module Hecks
|
|
|
30
30
|
@owner_value_objects = owner_value_objects
|
|
31
31
|
@identity_name_prefix = identity_name_prefix || Naming.demodulise(name)
|
|
32
32
|
@identity_value_object_installer = identity_value_object_installer
|
|
33
|
-
#
|
|
34
|
-
#
|
|
33
|
+
# The aggregate-wide cross-entity given pool — one hash, the
|
|
34
|
+
# same object, threaded unchanged through every piece nested
|
|
35
35
|
# under one aggregate however deep (the identical shape
|
|
36
36
|
# `@owner_value_objects` already threads — see this class'
|
|
37
37
|
# own `entity` comment). `given`'s own block form writes
|
|
38
|
-
# through to it; a
|
|
38
|
+
# through to it; a sibling piece's bare command-level
|
|
39
39
|
# reference reads from it via `CommandBuilder#
|
|
40
40
|
# reference_named_given`.
|
|
41
41
|
@owner_named_givens = owner_named_givens
|
|
42
|
-
#
|
|
42
|
+
# **One level wider still** — the chapter-wide, entity-scoped pool
|
|
43
43
|
# (the piece analogue of `AggregateBuilder#@chapter_named_givens`,
|
|
44
|
-
# one level down). `@aggregate_name` names
|
|
44
|
+
# one level down). `@aggregate_name` names this piece's own
|
|
45
45
|
# root, so the write-through below can key itself
|
|
46
46
|
# "AggregateName.EntityName" — the same dotted addressing
|
|
47
47
|
# convention `declared_by:` already uses chapter-wide, one
|
|
@@ -51,10 +51,10 @@ module Hecks
|
|
|
51
51
|
@aggregate_name = aggregate_name || Naming.demodulise(name)
|
|
52
52
|
@chapter_entity_named_givens = chapter_entity_named_givens
|
|
53
53
|
@chapter_entity_pending_givens = chapter_entity_pending_givens
|
|
54
|
-
#
|
|
54
|
+
# **Deferred construction** — see `AggregateBuilder#drain_pending!`'s
|
|
55
55
|
# own comment; the identical mechanism, one level down, so a
|
|
56
56
|
# nested piece's own commands (Dispatch inside Handler) see
|
|
57
|
-
# every
|
|
57
|
+
# every sibling entity/command/query this piece goes on to
|
|
58
58
|
# declare, not just whatever came before it textually.
|
|
59
59
|
@pending_entities = []
|
|
60
60
|
@pending_commands = []
|
|
@@ -63,12 +63,12 @@ module Hecks
|
|
|
63
63
|
|
|
64
64
|
def description(value) = @description = value
|
|
65
65
|
|
|
66
|
-
#
|
|
66
|
+
# The same field AggregateBuilder's own reference_to builds — a
|
|
67
67
|
# piece can hold a reference to another root exactly the way its
|
|
68
68
|
# own head can (Card.assignee_id, a Team's own id), just never
|
|
69
|
-
# to another
|
|
69
|
+
# to another piece, since there's no cross-piece addressing
|
|
70
70
|
# anywhere in this language to resolve one against.
|
|
71
|
-
#
|
|
71
|
+
# Renamed from `reference_to` — item #13's full metaprogrammed
|
|
72
72
|
# dispatch (slice 4b). Bootstrap-reachable, in
|
|
73
73
|
# GenericDispatch::BOOTSTRAP_CALLS_FALLBACK.
|
|
74
74
|
def reference_to_impl(type, as: nil, optional: false)
|
|
@@ -108,8 +108,8 @@ module Hecks
|
|
|
108
108
|
relationship_attribute(target, :belongs_to, as || Naming.snake(target).to_sym, optional: optional)
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
-
# A
|
|
112
|
-
# `identified_by :sequence` names the
|
|
111
|
+
# A piece is known by a field, not by a whole value object.
|
|
112
|
+
# `identified_by :sequence` names the scalar inside it, which is
|
|
113
113
|
# what an id actually is — a LedgerEntry is entry 3, not entry
|
|
114
114
|
# {"value":3}. `identified_by` itself is AttributeCollector's own
|
|
115
115
|
# shared method (S9) — the two constructs cannot drift apart in
|
|
@@ -118,10 +118,10 @@ module Hecks
|
|
|
118
118
|
# declare for the same reason a head may.
|
|
119
119
|
|
|
120
120
|
# `from:` — see `AggregateBuilder#command`'s own comment; the
|
|
121
|
-
#
|
|
121
|
+
# same guard, checked against this piece's own lifecycle field
|
|
122
122
|
# (S10, ADR 0025 — a piece's own state machine is checkable the
|
|
123
123
|
# same way a head's is).
|
|
124
|
-
#
|
|
124
|
+
# Renamed from `command`/`query`/`entity`/`lifecycle` (all below)
|
|
125
125
|
# — item #13's full metaprogrammed dispatch (slice 4c), same
|
|
126
126
|
# reasoning as AggregateBuilder's own siblings: bootstrap-
|
|
127
127
|
# reachable, in BOOTSTRAP_CALLS_FALLBACK.
|
|
@@ -133,14 +133,14 @@ module Hecks
|
|
|
133
133
|
@pending_queries << [name, block]
|
|
134
134
|
end
|
|
135
135
|
|
|
136
|
-
# S17, ADR 0026 —
|
|
136
|
+
# S17, ADR 0026 — a piece nested inside a piece. "A `Dispatch`
|
|
137
137
|
# [has] no life outside its `Handler`" (the ADR's own words) —
|
|
138
138
|
# the same reason `Member` nests inside `ValueObject`, one level
|
|
139
139
|
# further in. `owner_value_objects` passes straight through
|
|
140
140
|
# unchanged, not re-derived from this entity's own attributes —
|
|
141
141
|
# a piece mints no value objects of its own at any depth, so a
|
|
142
|
-
#
|
|
143
|
-
# against the
|
|
142
|
+
# nested piece's bare `identified_by :field` still resolves
|
|
143
|
+
# against the same root aggregate's value objects an outer
|
|
144
144
|
# piece's already does (`AggregateBuilder#entity`'s own comment
|
|
145
145
|
# names this pool ; there is exactly one of them, however deep
|
|
146
146
|
# the nesting goes).
|
|
@@ -152,15 +152,15 @@ module Hecks
|
|
|
152
152
|
@lifecycle = LifecycleBuilder.build(field, default: default, &)
|
|
153
153
|
end
|
|
154
154
|
|
|
155
|
-
# A
|
|
156
|
-
#
|
|
155
|
+
# A precondition shared across this piece's own commands, declared
|
|
156
|
+
# once — the same move `AggregateBuilder#given` already makes,
|
|
157
157
|
# one level down. Real, live redundancy this closes: banking's
|
|
158
158
|
# own `LedgerEntry.Amend`/`LedgerEntry.Reverse` each repeated
|
|
159
159
|
# `given("customer is active") { parent.customer.status ==
|
|
160
160
|
# "active" }` and `given("account is open") { parent.status ==
|
|
161
161
|
# "open" }`, byte for byte, because a piece had no way to declare
|
|
162
|
-
# either once and reference it back — only the
|
|
163
|
-
#
|
|
162
|
+
# either once and reference it back — only the aggregate could.
|
|
163
|
+
# Declare before the commands that reference it, the same
|
|
164
164
|
# ordering `AggregateBuilder#given`'s own comment names — though
|
|
165
165
|
# since ADR 0028, `command` only queues a descriptor and actually
|
|
166
166
|
# builds at `#drain_pending!` time, well after this whole block
|
|
@@ -168,36 +168,36 @@ module Hecks
|
|
|
168
168
|
# order within the block no longer actually matters here; named
|
|
169
169
|
# for the reader anyway, since `given`'s own resolution logic
|
|
170
170
|
# (`CommandBuilder#reference_named_given`) still reads whatever
|
|
171
|
-
# `@named_givens` holds
|
|
171
|
+
# `@named_givens` holds at the command's own build time, not by
|
|
172
172
|
# magic.
|
|
173
|
-
#
|
|
173
|
+
# Renamed from `given` — item #13's full metaprogrammed dispatch
|
|
174
174
|
# (slice 4b), same reasoning as reference_to_impl above.
|
|
175
175
|
#
|
|
176
|
-
#
|
|
177
|
-
#
|
|
176
|
+
# Bare — no block — references another piece's own declaration,
|
|
177
|
+
# anywhere in the chapter, not just a sibling under this same
|
|
178
178
|
# aggregate — one level wider than round 4's own cross-entity
|
|
179
179
|
# sharing, mirroring `AggregateBuilder#given_impl`'s own
|
|
180
180
|
# chapter-wide shape exactly one level down. Real, live corpus
|
|
181
181
|
# this closes: `Account::LedgerEntry` and `SafeDepositBox::Visit`
|
|
182
|
-
# — two pieces under two
|
|
182
|
+
# — two pieces under two different aggregates — independently
|
|
183
183
|
# typed `given("customer is active") { parent.customer.status ==
|
|
184
184
|
# "active" }` byte for byte; neither the aggregate-level chapter
|
|
185
|
-
# pool (a
|
|
185
|
+
# pool (a different canonical — bare `customer.status`, the
|
|
186
186
|
# wrong scope for a piece's own command) nor the existing
|
|
187
187
|
# same-aggregate cross-entity pool (`@owner_named_givens`, scoped
|
|
188
|
-
# to
|
|
188
|
+
# to one aggregate's own entity tree) could reach across the
|
|
189
189
|
# aggregate boundary. Resolved against `@chapter_entity_named_
|
|
190
190
|
# givens`, keyed "AggregateName.EntityName" — see
|
|
191
191
|
# `#reference_named_chapter_entity_given`'s own comment for the
|
|
192
192
|
# algorithm and `docs/implemented/resolution-rules/
|
|
193
193
|
# chapter-entity-given.md` for the full write-up.
|
|
194
194
|
#
|
|
195
|
-
# `declared_by:` is a
|
|
195
|
+
# `declared_by:` is a plain string ("Account.LedgerEntry"), not a
|
|
196
196
|
# constant — unlike `AggregateBuilder#given_impl`'s own
|
|
197
197
|
# `declared_by:`, which names a real aggregate constant. A piece
|
|
198
198
|
# has no first-class, independently-addressable reference
|
|
199
199
|
# anywhere in this language (only its owning aggregate does);
|
|
200
|
-
# inventing one to make this
|
|
200
|
+
# inventing one to make this one argument spelling symmetrical
|
|
201
201
|
# with the aggregate-level word is a real, separate, unscoped
|
|
202
202
|
# feature this fix does not need — ships textual now, the same
|
|
203
203
|
# way `admits:` shipped textual before its own constant-bridge
|
|
@@ -209,40 +209,40 @@ module Hecks
|
|
|
209
209
|
named = build_rule(Given, description, predicate, owner_name: @name, word: "given",
|
|
210
210
|
extraction_failure: "its source could not be read, so no other runtime could ever evaluate it")
|
|
211
211
|
@named_givens[description] = named
|
|
212
|
-
#
|
|
212
|
+
# Write-through, first-declared-wins (`||=`) — a second piece
|
|
213
213
|
# under the same aggregate independently declaring the exact
|
|
214
214
|
# same description stays purely local to itself (no silent
|
|
215
215
|
# overwrite of whatever the first piece already shared;
|
|
216
216
|
# real, live case a fuzzer or a future codemod could easily
|
|
217
|
-
# surface: two pieces phrasing an
|
|
217
|
+
# surface: two pieces phrasing an unrelated rule identically
|
|
218
218
|
# by coincidence, same as an aggregate-level given already
|
|
219
219
|
# tolerates today).
|
|
220
220
|
@owner_named_givens[description] ||= named
|
|
221
|
-
#
|
|
221
|
+
# **Write-through, per owner** — the chapter-wide analogue of the
|
|
222
222
|
# line above, keyed by [description, this piece's own dotted
|
|
223
223
|
# "Aggregate.Entity" name] rather than description alone, the
|
|
224
224
|
# identical reasoning `AggregateBuilder#given_impl`'s own
|
|
225
|
-
# chapter write-through gives: two
|
|
226
|
-
# in the chapter) independently declaring the
|
|
227
|
-
# are two
|
|
225
|
+
# chapter write-through gives: two different pieces (anywhere
|
|
226
|
+
# in the chapter) independently declaring the same description
|
|
227
|
+
# are two distinct candidates a later bare reference chooses
|
|
228
228
|
# between (via `declared_by:` once there is more than one),
|
|
229
229
|
# never silently merged into one slot.
|
|
230
230
|
@chapter_entity_named_givens[description] ||= {}
|
|
231
231
|
@chapter_entity_named_givens[description]["#{@aggregate_name}.#{@name}"] ||= named
|
|
232
232
|
end
|
|
233
233
|
|
|
234
|
-
# A
|
|
234
|
+
# A piece's own shape rule (S10, ADR 0025's own "Rules" shape,
|
|
235
235
|
# one level down from `ValueObjectBuilder#invariant`, whose
|
|
236
236
|
# extraction/error pattern this mirrors) — checked against
|
|
237
|
-
#
|
|
237
|
+
# every instance of this piece the aggregate holds, not once
|
|
238
238
|
# against the aggregate's own flat state
|
|
239
239
|
# (`Admissibility#enforce_invariants`'s own recursive walk).
|
|
240
240
|
# No reference-by-name form (unlike `given`) — no known corpus
|
|
241
|
-
# need for a piece's own invariant to be shared with a
|
|
241
|
+
# need for a piece's own invariant to be shared with a sibling
|
|
242
242
|
# piece yet; if that need shows up, it is `given`'s own
|
|
243
243
|
# cross-entity write-through pattern to extend, not a reason to
|
|
244
244
|
# invent a second one here speculatively.
|
|
245
|
-
#
|
|
245
|
+
# Renamed from `invariant` — item #13's full metaprogrammed
|
|
246
246
|
# dispatch (slice 4b), same reasoning as given_impl above.
|
|
247
247
|
def invariant_impl(description, &predicate)
|
|
248
248
|
@invariants << build_rule(Invariant, description, predicate, owner_name: @name, word: "invariant",
|
|
@@ -283,25 +283,25 @@ module Hecks
|
|
|
283
283
|
|
|
284
284
|
private
|
|
285
285
|
|
|
286
|
-
#
|
|
287
|
-
#
|
|
286
|
+
# Primitive 2 (RuleReference#resolve_owner_keyed) — the chapter-
|
|
287
|
+
# wide, entity-scoped analogue of `AggregateBuilder#
|
|
288
288
|
# reference_named_chapter_given`; the three branches below are
|
|
289
|
-
# this construct's
|
|
289
|
+
# this construct's own refusal wording, not shared, matching that
|
|
290
290
|
# method's own precedent (`declared_by:` only exists on `given`
|
|
291
|
-
# so far, at either scope).
|
|
291
|
+
# so far, at either scope). Unresolved is deferred, not raised
|
|
292
292
|
# here — see `#pending_chapter_entity_given`, below.
|
|
293
293
|
#
|
|
294
|
-
#
|
|
294
|
+
# **Writes through to `@owner_named_givens` too** — not just
|
|
295
295
|
# `@named_givens` — or this piece resolving a description via the
|
|
296
|
-
#
|
|
296
|
+
# wider, chapter pool would leave the narrower, same-aggregate
|
|
297
297
|
# pool (`EntityBuilder#given_impl`'s own block-form write-through)
|
|
298
|
-
# never populated for this description, breaking any
|
|
298
|
+
# never populated for this description, breaking any sibling
|
|
299
299
|
# piece's existing command-level bare reference
|
|
300
300
|
# (`CommandBuilder#reference_named_given`) that depends on it —
|
|
301
301
|
# real, live corpus: `SafeDepositBox::KeyIssuance.Return`'s own
|
|
302
302
|
# bare `given("customer is active")` resolves through
|
|
303
303
|
# `@owner_named_givens`, populated by `Visit`'s declaration
|
|
304
|
-
# whether `Visit` types the predicate itself
|
|
304
|
+
# whether `Visit` types the predicate itself or (now) references
|
|
305
305
|
# `Account::LedgerEntry`'s instead — this write keeps that
|
|
306
306
|
# working unchanged either way, `||=` so nothing here overrides
|
|
307
307
|
# an actual local declaration if one is ever added later.
|
|
@@ -329,10 +329,10 @@ module Hecks
|
|
|
329
329
|
@owner_named_givens[description] ||= named
|
|
330
330
|
end
|
|
331
331
|
|
|
332
|
-
# A
|
|
332
|
+
# A chapter may be split across files — the identical reason
|
|
333
333
|
# `AggregateBuilder#pending_chapter_given` defers rather than
|
|
334
334
|
# raising the moment a bare reference outruns what's loaded so
|
|
335
|
-
# far. Hands back a
|
|
335
|
+
# far. Hands back a placeholder `Given`, embedded by Ruby object
|
|
336
336
|
# reference in this piece's own `preconditions`, and queues the
|
|
337
337
|
# request in `@chapter_entity_pending_givens` —
|
|
338
338
|
# `BluebookBuilder#resolve_pending_chapter_entity_givens!`
|
|
@@ -344,22 +344,22 @@ module Hecks
|
|
|
344
344
|
placeholder
|
|
345
345
|
end
|
|
346
346
|
|
|
347
|
-
# A
|
|
347
|
+
# A piece's own `one_of` lands on its aggregate. A type-position
|
|
348
348
|
# `one_of("never_moved", "moved")` on an entity attribute
|
|
349
349
|
# synthesizes a closed-set value object — and until this, that
|
|
350
|
-
# object was built and then
|
|
350
|
+
# object was built and then dropped: `Entity.declare` carries no
|
|
351
351
|
# value objects, so the synthesized set existed nowhere in the
|
|
352
352
|
# finished graph. The attribute stayed typed "Moved" with nothing
|
|
353
353
|
# to resolve it: runtime admission had no closed set to enforce
|
|
354
354
|
# (the one_of was decorative), and the fuzzer's ValueGenerator
|
|
355
355
|
# crashed every run on the first domain to declare one — a chess
|
|
356
356
|
# King/Rook's own castling flag — with `does not know primitive
|
|
357
|
-
# type "Moved"`. Installed through the
|
|
357
|
+
# type "Moved"`. Installed through the same hook an entity's own
|
|
358
358
|
# identity value object already rides to the aggregate
|
|
359
359
|
# (`identity_value_object_installer`, threaded unchanged through
|
|
360
360
|
# nested pieces). Two sibling pieces synthesizing the same set
|
|
361
|
-
# (King's and Rook's own `moved`) install it once; the same
|
|
362
|
-
# with a
|
|
361
|
+
# (King's and Rook's own `moved`) install it once; the same name
|
|
362
|
+
# with a different member list is refused as the collision it is,
|
|
363
363
|
# never first-wins silently.
|
|
364
364
|
def install_closed_sets!
|
|
365
365
|
return unless @identity_value_object_installer
|
|
@@ -435,7 +435,7 @@ module Hecks
|
|
|
435
435
|
|
|
436
436
|
# `identified_by`'s own resolution pool (AttributeCollector#resolve_
|
|
437
437
|
# pending_identity!'s hook, S9) — a piece mints no value objects of
|
|
438
|
-
# its own, so a bare field's own type resolves against its
|
|
438
|
+
# its own, so a bare field's own type resolves against its owner
|
|
439
439
|
# aggregate's, passed in at declaration (`AggregateBuilder#entity`).
|
|
440
440
|
def identity_pool = @owner_value_objects
|
|
441
441
|
|