hecks 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/hecks/adapters/driven/claude_code.rb +72 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +83 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
- data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
- data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
- data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
- data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +106 -9
- data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
- data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
- data/lib/hecks/adapters/driven/lambda.rb +82 -35
- data/lib/hecks/adapters/driven/local_storage.rb +83 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -1
- data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
- data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
- data/lib/hecks/adapters/driven/postgres.rb +175 -28
- data/lib/hecks/adapters/driven/prism.rb +50 -11
- data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
- data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
- data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
- data/lib/hecks/adapters/driven/sqlite.rb +181 -21
- data/lib/hecks/adapters/driven/system_clock.rb +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
- data/lib/hecks/adapters/driven.rb +6 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
- data/lib/hecks/behaviors/dsl.rb +60 -2
- data/lib/hecks/behaviors/expectations.rb +190 -29
- data/lib/hecks/behaviors/ir.rb +12 -1
- data/lib/hecks/behaviors/rspec.rb +9 -1
- data/lib/hecks/behaviors/runner.rb +21 -2
- data/lib/hecks/behaviors.rb +9 -1
- data/lib/hecks/bluebook/aggregate.rb +43 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
- data/lib/hecks/bluebook/assembly/build.rb +26 -5
- data/lib/hecks/bluebook/assembly/contract.rb +98 -23
- data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
- data/lib/hecks/bluebook/assembly/marks.rb +159 -30
- data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
- data/lib/hecks/bluebook/assembly.rb +32 -14
- data/lib/hecks/bluebook/attribute.rb +26 -12
- data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
- data/lib/hecks/bluebook/behaviour/command.rb +55 -25
- data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
- data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
- data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
- data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
- data/lib/hecks/bluebook/behaviour/query.rb +6 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +51 -9
- data/lib/hecks/bluebook/command.rb +62 -17
- data/lib/hecks/bluebook/domain_port.rb +34 -9
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +282 -123
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
- data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
- data/lib/hecks/bluebook/dsl/entity_builder.rb +191 -61
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
- data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +111 -47
- data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
- data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
- data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
- data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
- data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
- data/lib/hecks/bluebook/entity.rb +40 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
- data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
- data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
- data/lib/hecks/bluebook/expression/resolver.rb +369 -128
- data/lib/hecks/bluebook/hexagon.rb +35 -1
- data/lib/hecks/bluebook/lifecycle.rb +12 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
- data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
- data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
- data/lib/hecks/bluebook/meta_validator.rb +235 -139
- data/lib/hecks/bluebook/model_check.rb +434 -104
- data/lib/hecks/bluebook/pattern_subset.rb +32 -10
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +27 -14
- data/lib/hecks/bluebook/project_discovery.rb +5 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +50 -6
- data/lib/hecks/bluebook/query.rb +31 -4
- data/lib/hecks/bluebook/read_model.rb +35 -15
- data/lib/hecks/bluebook/reference.rb +26 -13
- data/lib/hecks/bluebook/smoke_test.rb +46 -23
- data/lib/hecks/bluebook/synthesizer.rb +46 -12
- data/lib/hecks/bluebook/translation.rb +34 -5
- data/lib/hecks/bluebook/value_object.rb +29 -11
- data/lib/hecks/bluebook.rb +5 -6
- data/lib/hecks/codemod.rb +138 -50
- data/lib/hecks/construct.rb +21 -7
- data/lib/hecks/corpus.rb +438 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/doc/reference.rb +200 -31
- data/lib/hecks/embryonaut_bluebook.rb +38 -15
- data/lib/hecks/facade/cli_door.rb +69 -10
- data/lib/hecks/facade/cli_runner.rb +105 -24
- data/lib/hecks/facade/command_request.rb +23 -0
- data/lib/hecks/facade/handle.rb +155 -35
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +50 -27
- data/lib/hecks/facade/surface/chapter.rb +26 -17
- data/lib/hecks/facade/surface.rb +16 -3
- data/lib/hecks/facade.rb +15 -4
- data/lib/hecks/forms/app.rb +46 -30
- data/lib/hecks/forms/command_form_renderer.rb +70 -9
- data/lib/hecks/forms/field_renderer.rb +142 -6
- data/lib/hecks/forms/field_shape.rb +183 -19
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/page.rb +14 -0
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +14 -2
- data/lib/hecks/forms/query_form_renderer.rb +65 -2
- data/lib/hecks/forms/record_renderer.rb +60 -2
- data/lib/hecks/forms/record_table.rb +28 -1
- data/lib/hecks/forms/reference_options.rb +24 -0
- data/lib/hecks/forms/value_object_shape.rb +13 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +59 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
- data/lib/hecks/framework/oidc.json +15 -0
- data/lib/hecks/framework.rb +79 -25
- data/lib/hecks/freezer.rb +27 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
- data/lib/hecks/fuzzing/combination_miner.rb +178 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
- data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
- data/lib/hecks/fuzzing/differential.rb +192 -0
- data/lib/hecks/fuzzing/domain_generator.rb +871 -0
- data/lib/hecks/fuzzing/era_boundary.rb +68 -19
- data/lib/hecks/fuzzing/form_census.rb +121 -20
- data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
- data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
- data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
- data/lib/hecks/fuzzing/properties/guards.rb +86 -42
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
- data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
- data/lib/hecks/fuzzing/properties/querying.rb +82 -28
- data/lib/hecks/fuzzing/properties.rb +84 -51
- data/lib/hecks/fuzzing/qa_settings.rb +164 -0
- data/lib/hecks/fuzzing/replay.rb +241 -133
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
- data/lib/hecks/fuzzing/self_consistency.rb +315 -137
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +59 -32
- data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
- data/lib/hecks/fuzzing/shrinker.rb +309 -0
- data/lib/hecks/fuzzing/structural_skips.rb +37 -130
- data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
- data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
- data/lib/hecks/fuzzing/value_generator.rb +110 -22
- data/lib/hecks/fuzzing.rb +5 -0
- data/lib/hecks/grammar/evolve.rb +188 -12
- data/lib/hecks/grammar.rb +53 -7
- data/lib/hecks/ir.rb +51 -20
- data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
- data/lib/hecks/language/bluebook/policy.bluebook +11 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +41 -9
- data/lib/hecks/naming.rb +112 -31
- data/lib/hecks/ports/access_control.rb +53 -2
- data/lib/hecks/ports/agent/answers.rb +83 -6
- data/lib/hecks/ports/agent.rb +119 -35
- data/lib/hecks/ports/authentication.rb +44 -4
- data/lib/hecks/ports/authorization.rb +50 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +23 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +17 -1
- data/lib/hecks/ports/key_vault.port +6 -0
- data/lib/hecks/ports/key_vault.rb +58 -0
- data/lib/hecks/ports/loading.rb +4 -0
- data/lib/hecks/ports/persistence/append_only.rb +172 -8
- data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
- data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
- data/lib/hecks/ports/persistence/execution.rb +4 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
- data/lib/hecks/ports/persistence/plugin.rb +42 -4
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +34 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +95 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +48 -8
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +58 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
- data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
- data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
- data/lib/hecks/ports/persistence/state_codec.rb +319 -0
- data/lib/hecks/ports/persistence.rb +36 -1
- data/lib/hecks/ports/projection.rb +61 -7
- data/lib/hecks/ports/query/in_memory.rb +41 -8
- data/lib/hecks/ports/query/ordering.rb +21 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projections/bootstrap_table.rb +147 -0
- data/lib/hecks/projections/diagrams.rb +315 -79
- data/lib/hecks/projections/glossary/html.rb +91 -3
- data/lib/hecks/projections/glossary/markdown.rb +50 -3
- data/lib/hecks/projections/glossary/mermaid.rb +49 -1
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sentences.rb +110 -5
- data/lib/hecks/projections/glossary.rb +173 -31
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +62 -17
- data/lib/hecks/projections/model.rb +75 -21
- data/lib/hecks/projections/oidc.rb +45 -8
- data/lib/hecks/projections/parser_table.rb +53 -12
- data/lib/hecks/projections/reference.rb +15 -3
- data/lib/hecks/projections/rust_vocabulary.rb +646 -0
- data/lib/hecks/projections/shape.rb +10 -2
- data/lib/hecks/projections/statements.rb +69 -22
- data/lib/hecks/projections/vocabulary.rb +26 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +245 -37
- data/lib/hecks/projector/docs_projector.rb +154 -28
- data/lib/hecks/projector/exporter.rb +104 -29
- data/lib/hecks/projector/ir_projector.rb +7 -1
- data/lib/hecks/projector/narrate_projector.rb +144 -23
- data/lib/hecks/projector/target.rb +42 -18
- data/lib/hecks/projector.rb +86 -18
- data/lib/hecks/query_ir.rb +94 -47
- data/lib/hecks/query_specification/common/comparators.rb +19 -3
- data/lib/hecks/query_specification/common/comparison.rb +109 -27
- data/lib/hecks/query_specification/common/dsl.rb +65 -9
- data/lib/hecks/query_specification/common/null_policy.rb +61 -15
- data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
- data/lib/hecks/query_specification/common/options.rb +25 -0
- data/lib/hecks/query_specification/field_path.rb +69 -15
- data/lib/hecks/query_specification/hop_path.rb +57 -20
- data/lib/hecks/query_specification/read_model/specification.rb +4 -0
- data/lib/hecks/rendering.rb +9 -3
- data/lib/hecks/router/namespace_installer.rb +16 -3
- data/lib/hecks/router.rb +56 -1
- data/lib/hecks/runtime/aggregate_lock.rb +20 -11
- data/lib/hecks/runtime/boot_gates.rb +21 -3
- data/lib/hecks/runtime/caller.rb +40 -8
- data/lib/hecks/runtime/capability_graph.rb +13 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
- data/lib/hecks/runtime/command_interpreter.rb +195 -113
- data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
- data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
- data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
- data/lib/hecks/runtime/command_rules/emission.rb +18 -1
- data/lib/hecks/runtime/command_rules/references.rb +145 -55
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +56 -11
- data/lib/hecks/runtime/dispatcher.rb +263 -92
- data/lib/hecks/runtime/entity_element.rb +304 -67
- data/lib/hecks/runtime/entity_interpreter.rb +149 -88
- data/lib/hecks/runtime/errors.rb +37 -23
- data/lib/hecks/runtime/event.rb +15 -10
- data/lib/hecks/runtime/identity.rb +91 -23
- data/lib/hecks/runtime/instance.rb +103 -18
- data/lib/hecks/runtime/interpreting.rb +24 -16
- data/lib/hecks/runtime/invocation.rb +358 -0
- data/lib/hecks/runtime/loader.rb +105 -19
- data/lib/hecks/runtime/outbox.rb +164 -26
- data/lib/hecks/runtime/policy_interpreter.rb +73 -60
- data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
- data/lib/hecks/runtime/query_interpreter.rb +93 -74
- data/lib/hecks/runtime/reaction_invocation.rb +73 -28
- data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
- data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
- data/lib/hecks/runtime/reference_hop.rb +48 -6
- data/lib/hecks/runtime/refusal_wording.rb +142 -115
- data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
- data/lib/hecks/runtime/registry/verification.rb +153 -28
- data/lib/hecks/runtime/registry.rb +202 -32
- data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
- data/lib/hecks/runtime/routing.rb +36 -154
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
- data/lib/hecks/runtime/saga_interpreter.rb +90 -76
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +33 -13
- data/lib/hecks/runtime/tenant_scope.rb +23 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +555 -142
- data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
- data/lib/hecks/runtime/value.rb +71 -21
- data/lib/hecks/runtime.rb +39 -7
- data/lib/hecks/storehouse.rb +368 -72
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +205 -4
- data/lib/hecks.rb +91 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
- metadata +28 -2
|
@@ -18,6 +18,22 @@ module Hecks
|
|
|
18
18
|
include RuleReference
|
|
19
19
|
include WordGate
|
|
20
20
|
|
|
21
|
+
# @param name [String] the entity's name, as written after `entity`
|
|
22
|
+
# @param owner_value_objects [Array<Bluebook::ValueObject>] the owning aggregate's value
|
|
23
|
+
# objects, threaded through unchanged for this piece's `identified_by` to resolve
|
|
24
|
+
# against
|
|
25
|
+
# @param owner_named_givens [Hash{String => Bluebook::Given}] the aggregate-wide,
|
|
26
|
+
# cross-entity given pool, shared and written through by `given_impl`
|
|
27
|
+
# @param identity_name_prefix [String, nil] the prefix a synthesized identity value
|
|
28
|
+
# object's name takes; nil derives it from `name`
|
|
29
|
+
# @param identity_value_object_installer [#call, nil] called with a synthesized value
|
|
30
|
+
# object to install it onto the owning aggregate; nil when there is none
|
|
31
|
+
# @param aggregate_name [String, nil] the root aggregate's name, for chapter-wide given
|
|
32
|
+
# keys; nil derives it from `name`
|
|
33
|
+
# @param chapter_entity_named_givens [Hash{String => Hash{String => Bluebook::Given}}]
|
|
34
|
+
# the chapter-wide, entity-scoped given pool, shared and written through
|
|
35
|
+
# @param chapter_entity_pending_givens [Array<Hash>] unresolved chapter-wide bare given
|
|
36
|
+
# references, appended to when this piece's own reference cannot resolve yet
|
|
21
37
|
def initialize(name, owner_value_objects: [], owner_named_givens: {},
|
|
22
38
|
identity_name_prefix: nil, identity_value_object_installer: nil,
|
|
23
39
|
aggregate_name: nil, chapter_entity_named_givens: {}, chapter_entity_pending_givens: [])
|
|
@@ -30,18 +46,18 @@ module Hecks
|
|
|
30
46
|
@owner_value_objects = owner_value_objects
|
|
31
47
|
@identity_name_prefix = identity_name_prefix || Naming.demodulise(name)
|
|
32
48
|
@identity_value_object_installer = identity_value_object_installer
|
|
33
|
-
#
|
|
34
|
-
#
|
|
49
|
+
# The aggregate-wide cross-entity given pool — one hash, the
|
|
50
|
+
# same object, threaded unchanged through every piece nested
|
|
35
51
|
# under one aggregate however deep (the identical shape
|
|
36
52
|
# `@owner_value_objects` already threads — see this class'
|
|
37
53
|
# own `entity` comment). `given`'s own block form writes
|
|
38
|
-
# through to it; a
|
|
54
|
+
# through to it; a sibling piece's bare command-level
|
|
39
55
|
# reference reads from it via `CommandBuilder#
|
|
40
56
|
# reference_named_given`.
|
|
41
57
|
@owner_named_givens = owner_named_givens
|
|
42
|
-
#
|
|
58
|
+
# **One level wider still** — the chapter-wide, entity-scoped pool
|
|
43
59
|
# (the piece analogue of `AggregateBuilder#@chapter_named_givens`,
|
|
44
|
-
# one level down). `@aggregate_name` names
|
|
60
|
+
# one level down). `@aggregate_name` names this piece's own
|
|
45
61
|
# root, so the write-through below can key itself
|
|
46
62
|
# "AggregateName.EntityName" — the same dotted addressing
|
|
47
63
|
# convention `declared_by:` already uses chapter-wide, one
|
|
@@ -51,26 +67,41 @@ module Hecks
|
|
|
51
67
|
@aggregate_name = aggregate_name || Naming.demodulise(name)
|
|
52
68
|
@chapter_entity_named_givens = chapter_entity_named_givens
|
|
53
69
|
@chapter_entity_pending_givens = chapter_entity_pending_givens
|
|
54
|
-
#
|
|
70
|
+
# **Deferred construction** — see `AggregateBuilder#drain_pending!`'s
|
|
55
71
|
# own comment; the identical mechanism, one level down, so a
|
|
56
72
|
# nested piece's own commands (Dispatch inside Handler) see
|
|
57
|
-
# every
|
|
73
|
+
# every sibling entity/command/query this piece goes on to
|
|
58
74
|
# declare, not just whatever came before it textually.
|
|
59
75
|
@pending_entities = []
|
|
60
76
|
@pending_commands = []
|
|
61
77
|
@pending_queries = []
|
|
62
78
|
end
|
|
63
79
|
|
|
80
|
+
# Sets the human-readable description shown for this entity.
|
|
81
|
+
#
|
|
82
|
+
# @param value [String] the description text
|
|
83
|
+
# @return [String] the description as stored
|
|
64
84
|
def description(value) = @description = value
|
|
65
85
|
|
|
66
|
-
#
|
|
86
|
+
# Declares a reference from this piece to another aggregate's identity.
|
|
87
|
+
#
|
|
88
|
+
# The same field AggregateBuilder's own reference_to builds — a
|
|
67
89
|
# piece can hold a reference to another root exactly the way its
|
|
68
90
|
# own head can (Card.assignee_id, a Team's own id), just never
|
|
69
|
-
# to another
|
|
91
|
+
# to another piece, since there's no cross-piece addressing
|
|
70
92
|
# anywhere in this language to resolve one against.
|
|
71
|
-
#
|
|
93
|
+
#
|
|
94
|
+
# Answers the `reference_to` word through the table's `calls:`
|
|
95
|
+
# column — item #13's full metaprogrammed
|
|
72
96
|
# dispatch (slice 4b). Bootstrap-reachable, in
|
|
73
|
-
# GenericDispatch::BOOTSTRAP_CALLS_FALLBACK
|
|
97
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
98
|
+
#
|
|
99
|
+
# @param type [Module, Symbol, String] the referenced aggregate, written as a bare
|
|
100
|
+
# constant
|
|
101
|
+
# @param as [Symbol, nil] the attribute's name; nil derives it from `type`
|
|
102
|
+
# @param optional [Boolean] whether the reference may be absent
|
|
103
|
+
# @return [void]
|
|
104
|
+
# @raise [Bluebook::DSL::Malformed] if `as` (or the derived name) is already declared
|
|
74
105
|
def reference_to_impl(type, as: nil, optional: false)
|
|
75
106
|
target = Naming.demodulise(type)
|
|
76
107
|
relationship_attribute(target, :reference_to,
|
|
@@ -86,6 +117,14 @@ module Hecks
|
|
|
86
117
|
# `*_impl` to match AggregateBuilder's own siblings, item #13's
|
|
87
118
|
# full metaprogrammed dispatch convention.
|
|
88
119
|
# rubocop:disable Naming/PredicatePrefix
|
|
120
|
+
# Declares a list-typed relationship to another entity, referenced by its plural name.
|
|
121
|
+
#
|
|
122
|
+
# @param type [Module, Symbol, String] the related entity's plural name, a bare constant
|
|
123
|
+
# @param as [Symbol, nil] the attribute's name; nil derives it from `type`
|
|
124
|
+
# @param options [Hash] must be empty; kept only to name which unsupported keyword was
|
|
125
|
+
# given in the refusal message
|
|
126
|
+
# @return [void]
|
|
127
|
+
# @raise [Bluebook::DSL::Malformed] if any keyword argument is given
|
|
89
128
|
def has_many_impl(type, as: nil, **options)
|
|
90
129
|
unless options.empty?
|
|
91
130
|
raise Malformed,
|
|
@@ -97,19 +136,31 @@ module Hecks
|
|
|
97
136
|
as || Naming.snake(plural).to_sym, list: true)
|
|
98
137
|
end
|
|
99
138
|
|
|
139
|
+
# Declares a single-valued relationship to another entity.
|
|
140
|
+
#
|
|
141
|
+
# @param type [Module, Symbol, String] the related entity, a bare constant
|
|
142
|
+
# @param as [Symbol, nil] the attribute's name; nil derives it from `type`
|
|
143
|
+
# @param optional [Boolean] whether the relationship may be absent
|
|
144
|
+
# @return [void]
|
|
100
145
|
def has_one_impl(type, as: nil, optional: false)
|
|
101
146
|
target = Naming.demodulise(type)
|
|
102
147
|
relationship_attribute(target, :has_one, as || Naming.snake(target).to_sym, optional: optional)
|
|
103
148
|
end
|
|
104
149
|
# rubocop:enable Naming/PredicatePrefix
|
|
105
150
|
|
|
151
|
+
# Declares a single-valued relationship to the entity that owns this one.
|
|
152
|
+
#
|
|
153
|
+
# @param type [Module, Symbol, String] the owning entity, a bare constant
|
|
154
|
+
# @param as [Symbol, nil] the attribute's name; nil derives it from `type`
|
|
155
|
+
# @param optional [Boolean] whether the relationship may be absent
|
|
156
|
+
# @return [void]
|
|
106
157
|
def belongs_to_impl(type, as: nil, optional: false)
|
|
107
158
|
target = Naming.demodulise(type)
|
|
108
159
|
relationship_attribute(target, :belongs_to, as || Naming.snake(target).to_sym, optional: optional)
|
|
109
160
|
end
|
|
110
161
|
|
|
111
|
-
# A
|
|
112
|
-
# `identified_by :sequence` names the
|
|
162
|
+
# A piece is known by a field, not by a whole value object.
|
|
163
|
+
# `identified_by :sequence` names the scalar inside it, which is
|
|
113
164
|
# what an id actually is — a LedgerEntry is entry 3, not entry
|
|
114
165
|
# {"value":3}. `identified_by` itself is AttributeCollector's own
|
|
115
166
|
# shared method (S9) — the two constructs cannot drift apart in
|
|
@@ -117,50 +168,77 @@ module Hecks
|
|
|
117
168
|
# (`identified_by :branch_code, :box_number`), which a piece may
|
|
118
169
|
# declare for the same reason a head may.
|
|
119
170
|
|
|
171
|
+
# Queues a command declared on this piece, built later once every sibling has been seen.
|
|
172
|
+
#
|
|
120
173
|
# `from:` — see `AggregateBuilder#command`'s own comment; the
|
|
121
|
-
#
|
|
174
|
+
# same guard, checked against this piece's own lifecycle field
|
|
122
175
|
# (S10, ADR 0025 — a piece's own state machine is checkable the
|
|
123
176
|
# same way a head's is).
|
|
124
|
-
#
|
|
125
|
-
#
|
|
177
|
+
#
|
|
178
|
+
# Answers the `command` word through the table's `calls:` column,
|
|
179
|
+
# along with its siblings `query`/`entity`/`lifecycle` below —
|
|
180
|
+
# item #13's full metaprogrammed dispatch (slice 4c), same
|
|
126
181
|
# reasoning as AggregateBuilder's own siblings: bootstrap-
|
|
127
|
-
# reachable, in BOOTSTRAP_CALLS_FALLBACK
|
|
182
|
+
# reachable, in `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
183
|
+
#
|
|
184
|
+
# @param name [String] the command's name
|
|
185
|
+
# @param from [String, Symbol, Array<String, Symbol>, nil] the lifecycle state(s) this
|
|
186
|
+
# command guards from; nil admits from any state
|
|
187
|
+
# @yield the command body, evaluated against a `CommandBuilder` once drained
|
|
188
|
+
# @return [Array<Array>] every pending command queued so far, this one last
|
|
128
189
|
def command_impl(name, from: nil, &block)
|
|
129
190
|
@pending_commands << [name, from, block]
|
|
130
191
|
end
|
|
131
192
|
|
|
193
|
+
# Queues a query declared on this piece, built later once every sibling has been seen.
|
|
194
|
+
#
|
|
195
|
+
# @param name [String] the query's name
|
|
196
|
+
# @yield the query body, evaluated against a `QueryBuilder` once drained
|
|
197
|
+
# @return [Array<Array>] every pending query queued so far, this one last
|
|
132
198
|
def query_impl(name, &block)
|
|
133
199
|
@pending_queries << [name, block]
|
|
134
200
|
end
|
|
135
201
|
|
|
136
|
-
#
|
|
202
|
+
# Queues a piece nested inside this one, built later once every sibling has been seen.
|
|
203
|
+
#
|
|
204
|
+
# S17, ADR 0026 — a piece nested inside a piece. "A `Dispatch`
|
|
137
205
|
# [has] no life outside its `Handler`" (the ADR's own words) —
|
|
138
206
|
# the same reason `Member` nests inside `ValueObject`, one level
|
|
139
207
|
# further in. `owner_value_objects` passes straight through
|
|
140
208
|
# unchanged, not re-derived from this entity's own attributes —
|
|
141
209
|
# a piece mints no value objects of its own at any depth, so a
|
|
142
|
-
#
|
|
143
|
-
# against the
|
|
210
|
+
# nested piece's bare `identified_by :field` still resolves
|
|
211
|
+
# against the same root aggregate's value objects an outer
|
|
144
212
|
# piece's already does (`AggregateBuilder#entity`'s own comment
|
|
145
213
|
# names this pool ; there is exactly one of them, however deep
|
|
146
214
|
# the nesting goes).
|
|
215
|
+
# @param name [String] the nested piece's name
|
|
216
|
+
# @yield the piece body, evaluated against an `EntityBuilder` once drained
|
|
217
|
+
# @return [Array<Array>] every pending piece queued so far, this one last
|
|
147
218
|
def entity_impl(name, &block)
|
|
148
219
|
@pending_entities << [name, block]
|
|
149
220
|
end
|
|
150
221
|
|
|
222
|
+
# Declares this piece's own state machine.
|
|
223
|
+
#
|
|
224
|
+
# @param field [Symbol, String] the attribute the state machine lives on
|
|
225
|
+
# @param default [String, Symbol] the state a new record starts in
|
|
226
|
+
# @yield the lifecycle body of `transition` rows, evaluated against a `LifecycleBuilder`
|
|
227
|
+
# @return [Bluebook::Lifecycle] the built state machine
|
|
228
|
+
# @raise [Bluebook::DSL::Malformed] if two transitions for one command overlap
|
|
151
229
|
def lifecycle_impl(field, default:, &)
|
|
152
230
|
@lifecycle = LifecycleBuilder.build(field, default: default, &)
|
|
153
231
|
end
|
|
154
232
|
|
|
155
|
-
# A
|
|
156
|
-
#
|
|
233
|
+
# A precondition shared across this piece's own commands, declared
|
|
234
|
+
# once — the same move `AggregateBuilder#given` already makes,
|
|
157
235
|
# one level down. Real, live redundancy this closes: banking's
|
|
158
236
|
# own `LedgerEntry.Amend`/`LedgerEntry.Reverse` each repeated
|
|
159
237
|
# `given("customer is active") { parent.customer.status ==
|
|
160
238
|
# "active" }` and `given("account is open") { parent.status ==
|
|
161
239
|
# "open" }`, byte for byte, because a piece had no way to declare
|
|
162
|
-
# either once and reference it back — only the
|
|
163
|
-
#
|
|
240
|
+
# either once and reference it back — only the aggregate could.
|
|
241
|
+
# Declare before the commands that reference it, the same
|
|
164
242
|
# ordering `AggregateBuilder#given`'s own comment names — though
|
|
165
243
|
# since ADR 0028, `command` only queues a descriptor and actually
|
|
166
244
|
# builds at `#drain_pending!` time, well after this whole block
|
|
@@ -168,87 +246,119 @@ module Hecks
|
|
|
168
246
|
# order within the block no longer actually matters here; named
|
|
169
247
|
# for the reader anyway, since `given`'s own resolution logic
|
|
170
248
|
# (`CommandBuilder#reference_named_given`) still reads whatever
|
|
171
|
-
# `@named_givens` holds
|
|
249
|
+
# `@named_givens` holds at the command's own build time, not by
|
|
172
250
|
# magic.
|
|
173
|
-
#
|
|
174
|
-
#
|
|
251
|
+
# Declares a rule this piece's own commands must satisfy, or references one a sibling
|
|
252
|
+
# piece anywhere in the chapter already declared.
|
|
175
253
|
#
|
|
176
|
-
#
|
|
177
|
-
#
|
|
254
|
+
# Answers the `given` word through the table's `calls:` column —
|
|
255
|
+
# item #13's full metaprogrammed dispatch
|
|
256
|
+
# (slice 4b), same reasoning as `reference_to_impl` above.
|
|
257
|
+
#
|
|
258
|
+
# Bare — no block — references another piece's own declaration,
|
|
259
|
+
# anywhere in the chapter, not just a sibling under this same
|
|
178
260
|
# aggregate — one level wider than round 4's own cross-entity
|
|
179
261
|
# sharing, mirroring `AggregateBuilder#given_impl`'s own
|
|
180
262
|
# chapter-wide shape exactly one level down. Real, live corpus
|
|
181
263
|
# this closes: `Account::LedgerEntry` and `SafeDepositBox::Visit`
|
|
182
|
-
# — two pieces under two
|
|
264
|
+
# — two pieces under two different aggregates — independently
|
|
183
265
|
# typed `given("customer is active") { parent.customer.status ==
|
|
184
266
|
# "active" }` byte for byte; neither the aggregate-level chapter
|
|
185
|
-
# pool (a
|
|
267
|
+
# pool (a different canonical — bare `customer.status`, the
|
|
186
268
|
# wrong scope for a piece's own command) nor the existing
|
|
187
269
|
# same-aggregate cross-entity pool (`@owner_named_givens`, scoped
|
|
188
|
-
# to
|
|
270
|
+
# to one aggregate's own entity tree) could reach across the
|
|
189
271
|
# aggregate boundary. Resolved against `@chapter_entity_named_
|
|
190
272
|
# givens`, keyed "AggregateName.EntityName" — see
|
|
191
273
|
# `#reference_named_chapter_entity_given`'s own comment for the
|
|
192
274
|
# algorithm and `docs/implemented/resolution-rules/
|
|
193
275
|
# chapter-entity-given.md` for the full write-up.
|
|
194
276
|
#
|
|
195
|
-
# `declared_by:` is a
|
|
277
|
+
# `declared_by:` is a plain string ("Account.LedgerEntry"), not a
|
|
196
278
|
# constant — unlike `AggregateBuilder#given_impl`'s own
|
|
197
279
|
# `declared_by:`, which names a real aggregate constant. A piece
|
|
198
280
|
# has no first-class, independently-addressable reference
|
|
199
281
|
# anywhere in this language (only its owning aggregate does);
|
|
200
|
-
# inventing one to make this
|
|
282
|
+
# inventing one to make this one argument spelling symmetrical
|
|
201
283
|
# with the aggregate-level word is a real, separate, unscoped
|
|
202
284
|
# feature this fix does not need — ships textual now, the same
|
|
203
285
|
# way `admits:` shipped textual before its own constant-bridge
|
|
204
286
|
# existed, revisited only if a genuine, separate need for
|
|
205
287
|
# constant-addressed pieces shows up later.
|
|
288
|
+
#
|
|
289
|
+
# @param description [String] the rule's description; also the name a sibling piece
|
|
290
|
+
# references it by when no block is given
|
|
291
|
+
# @param declared_by [String, nil] disambiguates which piece's own rule to reference,
|
|
292
|
+
# as `"Aggregate.Entity"`, when more than one shares `description`; only meaningful
|
|
293
|
+
# with no block
|
|
294
|
+
# @yield the predicate body; evaluated for its extracted source, never called directly
|
|
295
|
+
# @return [void]
|
|
296
|
+
# @raise [Bluebook::DSL::Malformed] if given a block whose source cannot be extracted;
|
|
297
|
+
# given no block, the description is immediately ambiguous between more than one
|
|
298
|
+
# already-loaded piece with no `declared_by` to disambiguate; an unresolved reference
|
|
299
|
+
# defers instead, and may still raise once the whole chapter has loaded, if it then
|
|
300
|
+
# resolves to none or more than one candidate
|
|
206
301
|
def given_impl(description, declared_by: nil, &predicate)
|
|
207
302
|
return reference_named_chapter_entity_given(description, declared_by: declared_by) unless predicate
|
|
208
303
|
|
|
209
304
|
named = build_rule(Given, description, predicate, owner_name: @name, word: "given",
|
|
210
305
|
extraction_failure: "its source could not be read, so no other runtime could ever evaluate it")
|
|
211
306
|
@named_givens[description] = named
|
|
212
|
-
#
|
|
307
|
+
# Write-through, first-declared-wins (`||=`) — a second piece
|
|
213
308
|
# under the same aggregate independently declaring the exact
|
|
214
309
|
# same description stays purely local to itself (no silent
|
|
215
310
|
# overwrite of whatever the first piece already shared;
|
|
216
311
|
# real, live case a fuzzer or a future codemod could easily
|
|
217
|
-
# surface: two pieces phrasing an
|
|
312
|
+
# surface: two pieces phrasing an unrelated rule identically
|
|
218
313
|
# by coincidence, same as an aggregate-level given already
|
|
219
314
|
# tolerates today).
|
|
220
315
|
@owner_named_givens[description] ||= named
|
|
221
|
-
#
|
|
316
|
+
# **Write-through, per owner** — the chapter-wide analogue of the
|
|
222
317
|
# line above, keyed by [description, this piece's own dotted
|
|
223
318
|
# "Aggregate.Entity" name] rather than description alone, the
|
|
224
319
|
# identical reasoning `AggregateBuilder#given_impl`'s own
|
|
225
|
-
# chapter write-through gives: two
|
|
226
|
-
# in the chapter) independently declaring the
|
|
227
|
-
# are two
|
|
320
|
+
# chapter write-through gives: two different pieces (anywhere
|
|
321
|
+
# in the chapter) independently declaring the same description
|
|
322
|
+
# are two distinct candidates a later bare reference chooses
|
|
228
323
|
# between (via `declared_by:` once there is more than one),
|
|
229
324
|
# never silently merged into one slot.
|
|
230
325
|
@chapter_entity_named_givens[description] ||= {}
|
|
231
326
|
@chapter_entity_named_givens[description]["#{@aggregate_name}.#{@name}"] ||= named
|
|
232
327
|
end
|
|
233
328
|
|
|
234
|
-
# A
|
|
329
|
+
# A piece's own shape rule (S10, ADR 0025's own "Rules" shape,
|
|
235
330
|
# one level down from `ValueObjectBuilder#invariant`, whose
|
|
236
331
|
# extraction/error pattern this mirrors) — checked against
|
|
237
|
-
#
|
|
332
|
+
# every instance of this piece the aggregate holds, not once
|
|
238
333
|
# against the aggregate's own flat state
|
|
239
334
|
# (`Admissibility#enforce_invariants`'s own recursive walk).
|
|
240
335
|
# No reference-by-name form (unlike `given`) — no known corpus
|
|
241
|
-
# need for a piece's own invariant to be shared with a
|
|
336
|
+
# need for a piece's own invariant to be shared with a sibling
|
|
242
337
|
# piece yet; if that need shows up, it is `given`'s own
|
|
243
338
|
# cross-entity write-through pattern to extend, not a reason to
|
|
244
339
|
# invent a second one here speculatively.
|
|
245
|
-
#
|
|
246
|
-
#
|
|
340
|
+
# Declares a rule every instance of this piece must satisfy.
|
|
341
|
+
#
|
|
342
|
+
# Answers the `invariant` word through the table's `calls:`
|
|
343
|
+
# column — item #13's full metaprogrammed
|
|
344
|
+
# dispatch (slice 4b), same reasoning as `given_impl` above.
|
|
345
|
+
#
|
|
346
|
+
# @param description [String] the rule's description
|
|
347
|
+
# @yield the predicate body; evaluated for its extracted source, never called directly
|
|
348
|
+
# @return [void]
|
|
349
|
+
# @raise [Bluebook::DSL::Malformed] if the block's source could not be extracted
|
|
247
350
|
def invariant_impl(description, &predicate)
|
|
248
351
|
@invariants << build_rule(Invariant, description, predicate, owner_name: @name, word: "invariant",
|
|
249
352
|
extraction_failure: "it would be a rule the IR cannot carry")
|
|
250
353
|
end
|
|
251
354
|
|
|
355
|
+
# Assembles the declared attributes, relationships, nested constructs and rules into an
|
|
356
|
+
# `Entity`.
|
|
357
|
+
#
|
|
358
|
+
# @return [Bluebook::Entity] the built piece
|
|
359
|
+
# @raise [Bluebook::DSL::Malformed] if identity resolution, a lifecycle guard on a
|
|
360
|
+
# command with no lifecycle, a lifecycle-field mutation outside a transition, or a
|
|
361
|
+
# synthesized closed set colliding with one the aggregate already holds fails
|
|
252
362
|
def build
|
|
253
363
|
drain_pending!
|
|
254
364
|
resolve_pending_identity!
|
|
@@ -268,6 +378,26 @@ module Hecks
|
|
|
268
378
|
)
|
|
269
379
|
end
|
|
270
380
|
|
|
381
|
+
# Evaluates an `entity` block against a fresh builder and returns what it built.
|
|
382
|
+
#
|
|
383
|
+
# @param name [String] the entity's name
|
|
384
|
+
# @param owner_value_objects [Array<Bluebook::ValueObject>] the owning aggregate's value
|
|
385
|
+
# objects, for `identified_by` to resolve against
|
|
386
|
+
# @param owner_named_givens [Hash{String => Bluebook::Given}] the aggregate-wide,
|
|
387
|
+
# cross-entity given pool
|
|
388
|
+
# @param identity_name_prefix [String, nil] the prefix a synthesized identity value
|
|
389
|
+
# object's name takes
|
|
390
|
+
# @param identity_value_object_installer [#call, nil] called to install a synthesized
|
|
391
|
+
# value object onto the owning aggregate
|
|
392
|
+
# @param aggregate_name [String, nil] the root aggregate's name, for chapter-wide given
|
|
393
|
+
# keys
|
|
394
|
+
# @param chapter_entity_named_givens [Hash{String => Hash{String => Bluebook::Given}}]
|
|
395
|
+
# the chapter-wide, entity-scoped given pool
|
|
396
|
+
# @param chapter_entity_pending_givens [Array<Hash>] unresolved chapter-wide bare given
|
|
397
|
+
# references
|
|
398
|
+
# @yield the entity body, evaluated with the builder as `self`; may be omitted
|
|
399
|
+
# @return [Bluebook::Entity] the built piece
|
|
400
|
+
# @raise [Bluebook::DSL::Malformed] if the body fails any check `#build` raises
|
|
271
401
|
def self.build(name, owner_value_objects: [], owner_named_givens: {},
|
|
272
402
|
identity_name_prefix: nil, identity_value_object_installer: nil,
|
|
273
403
|
aggregate_name: nil, chapter_entity_named_givens: {}, chapter_entity_pending_givens: [], &block)
|
|
@@ -283,25 +413,25 @@ module Hecks
|
|
|
283
413
|
|
|
284
414
|
private
|
|
285
415
|
|
|
286
|
-
#
|
|
287
|
-
#
|
|
416
|
+
# Primitive 2 (RuleReference#resolve_owner_keyed) — the chapter-
|
|
417
|
+
# wide, entity-scoped analogue of `AggregateBuilder#
|
|
288
418
|
# reference_named_chapter_given`; the three branches below are
|
|
289
|
-
# this construct's
|
|
419
|
+
# this construct's own refusal wording, not shared, matching that
|
|
290
420
|
# method's own precedent (`declared_by:` only exists on `given`
|
|
291
|
-
# so far, at either scope).
|
|
421
|
+
# so far, at either scope). Unresolved is deferred, not raised
|
|
292
422
|
# here — see `#pending_chapter_entity_given`, below.
|
|
293
423
|
#
|
|
294
|
-
#
|
|
424
|
+
# **Writes through to `@owner_named_givens` too** — not just
|
|
295
425
|
# `@named_givens` — or this piece resolving a description via the
|
|
296
|
-
#
|
|
426
|
+
# wider, chapter pool would leave the narrower, same-aggregate
|
|
297
427
|
# pool (`EntityBuilder#given_impl`'s own block-form write-through)
|
|
298
|
-
# never populated for this description, breaking any
|
|
428
|
+
# never populated for this description, breaking any sibling
|
|
299
429
|
# piece's existing command-level bare reference
|
|
300
430
|
# (`CommandBuilder#reference_named_given`) that depends on it —
|
|
301
431
|
# real, live corpus: `SafeDepositBox::KeyIssuance.Return`'s own
|
|
302
432
|
# bare `given("customer is active")` resolves through
|
|
303
433
|
# `@owner_named_givens`, populated by `Visit`'s declaration
|
|
304
|
-
# whether `Visit` types the predicate itself
|
|
434
|
+
# whether `Visit` types the predicate itself or (now) references
|
|
305
435
|
# `Account::LedgerEntry`'s instead — this write keeps that
|
|
306
436
|
# working unchanged either way, `||=` so nothing here overrides
|
|
307
437
|
# an actual local declaration if one is ever added later.
|
|
@@ -329,10 +459,10 @@ module Hecks
|
|
|
329
459
|
@owner_named_givens[description] ||= named
|
|
330
460
|
end
|
|
331
461
|
|
|
332
|
-
# A
|
|
462
|
+
# A chapter may be split across files — the identical reason
|
|
333
463
|
# `AggregateBuilder#pending_chapter_given` defers rather than
|
|
334
464
|
# raising the moment a bare reference outruns what's loaded so
|
|
335
|
-
# far. Hands back a
|
|
465
|
+
# far. Hands back a placeholder `Given`, embedded by Ruby object
|
|
336
466
|
# reference in this piece's own `preconditions`, and queues the
|
|
337
467
|
# request in `@chapter_entity_pending_givens` —
|
|
338
468
|
# `BluebookBuilder#resolve_pending_chapter_entity_givens!`
|
|
@@ -344,22 +474,22 @@ module Hecks
|
|
|
344
474
|
placeholder
|
|
345
475
|
end
|
|
346
476
|
|
|
347
|
-
# A
|
|
477
|
+
# A piece's own `one_of` lands on its aggregate. A type-position
|
|
348
478
|
# `one_of("never_moved", "moved")` on an entity attribute
|
|
349
479
|
# synthesizes a closed-set value object — and until this, that
|
|
350
|
-
# object was built and then
|
|
480
|
+
# object was built and then dropped: `Entity.declare` carries no
|
|
351
481
|
# value objects, so the synthesized set existed nowhere in the
|
|
352
482
|
# finished graph. The attribute stayed typed "Moved" with nothing
|
|
353
483
|
# to resolve it: runtime admission had no closed set to enforce
|
|
354
484
|
# (the one_of was decorative), and the fuzzer's ValueGenerator
|
|
355
485
|
# crashed every run on the first domain to declare one — a chess
|
|
356
486
|
# King/Rook's own castling flag — with `does not know primitive
|
|
357
|
-
# type "Moved"`. Installed through the
|
|
487
|
+
# type "Moved"`. Installed through the same hook an entity's own
|
|
358
488
|
# identity value object already rides to the aggregate
|
|
359
489
|
# (`identity_value_object_installer`, threaded unchanged through
|
|
360
490
|
# 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
|
|
491
|
+
# (King's and Rook's own `moved`) install it once; the same name
|
|
492
|
+
# with a different member list is refused as the collision it is,
|
|
363
493
|
# never first-wins silently.
|
|
364
494
|
def install_closed_sets!
|
|
365
495
|
return unless @identity_value_object_installer
|
|
@@ -435,7 +565,7 @@ module Hecks
|
|
|
435
565
|
|
|
436
566
|
# `identified_by`'s own resolution pool (AttributeCollector#resolve_
|
|
437
567
|
# 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
|
|
568
|
+
# its own, so a bare field's own type resolves against its owner
|
|
439
569
|
# aggregate's, passed in at declaration (`AggregateBuilder#entity`).
|
|
440
570
|
def identity_pool = @owner_value_objects
|
|
441
571
|
|