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
|
@@ -5,12 +5,12 @@ module Hecks
|
|
|
5
5
|
# Command, ValueObject, Query, ...); see `Contract`'s own header
|
|
6
6
|
# (contract.rb) for the struct format each entry below fills in.
|
|
7
7
|
class Assembly
|
|
8
|
-
#
|
|
8
|
+
# One table, where there were five hand-written mirrors of it.
|
|
9
9
|
#
|
|
10
10
|
# `bluebook.bluebook` already declares what every construct is made of, and
|
|
11
11
|
# `Plan` already reads it — parent, fields, lists, setters. What the language
|
|
12
12
|
# cannot say is the three things Ruby needs to rebuild one, so those are here
|
|
13
|
-
# and
|
|
13
|
+
# and only those:
|
|
14
14
|
#
|
|
15
15
|
# holder which class holds it
|
|
16
16
|
# make :declare for a construct that became a class, :new for an
|
|
@@ -19,23 +19,28 @@ module Hecks
|
|
|
19
19
|
# fields each keyword the constructor takes, as
|
|
20
20
|
# keyword => [key in the declaration, how to read it]
|
|
21
21
|
#
|
|
22
|
-
# A
|
|
22
|
+
# A reader is a symbol naming a method on Marks, or :plain for a value that
|
|
23
23
|
# needs no decoding, or [:each, reader] for a list. Nothing here is a rule
|
|
24
24
|
# about whether a declaration is admissible — the language settled that on the
|
|
25
25
|
# way in. This is only how a spelling becomes an object again.
|
|
26
26
|
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
27
|
+
# Why a table and not a method per category: a hand-written branch per
|
|
28
|
+
# category can decorate a verb the language declares without the walk ever
|
|
29
|
+
# offering it, and nothing goes red, because a branch that does not exist
|
|
30
|
+
# cannot fail. An assembler with a method per category is the same shape.
|
|
31
|
+
# The table avoids it because spec/assembly_spec checks it against the
|
|
32
|
+
# language: a field the language declares that no contract consumes is a
|
|
33
|
+
# failure, not a silence.
|
|
34
34
|
#
|
|
35
35
|
# The `derived` list is how a field says it needs no assembling — a parent
|
|
36
36
|
# pointer the containment tree already knows, or something computed from what
|
|
37
37
|
# is here (`query_name` is `Naming.snake(name)`, `creates?` is whether a verb
|
|
38
38
|
# names a root). Naming one is a claim, and the coverage gate holds it.
|
|
39
|
+
#
|
|
40
|
+
# @param category [String, Symbol] the construct category name, such as
|
|
41
|
+
# `"Aggregate"` or `:Command`
|
|
42
|
+
# @return [Bluebook::Assembly::Contract] the category's field contract
|
|
43
|
+
# @raise [KeyError] if no contract is registered for `category`
|
|
39
44
|
def self.contract(category) = CONTRACTS.fetch(category.to_s)
|
|
40
45
|
|
|
41
46
|
CONTRACTS = {
|
|
@@ -47,7 +52,8 @@ module Hecks
|
|
|
47
52
|
vision: [:vision, :plain],
|
|
48
53
|
classification: [:classification, :plain],
|
|
49
54
|
formerly_known_as: [:formerly_known_as, :plain],
|
|
50
|
-
attaches_to: [:attaches_to, :plain]
|
|
55
|
+
attaches_to: [:attaches_to, :plain],
|
|
56
|
+
provides: [:provides, :plain]
|
|
51
57
|
},
|
|
52
58
|
rows: { normalisations: :normalisation_table },
|
|
53
59
|
derived: { normalisations: :elsewhere }
|
|
@@ -60,7 +66,7 @@ module Hecks
|
|
|
60
66
|
description: [:description, :plain],
|
|
61
67
|
identified_by: [:identified_by, :plain],
|
|
62
68
|
attributes: [:attributes, [:each, :attribute]],
|
|
63
|
-
#
|
|
69
|
+
# The aggregate boundary, and the precondition a command may
|
|
64
70
|
# reference by name (S10, ADR 0025 — "Rules"). Same reader
|
|
65
71
|
# shapes ValueObject's own `invariants`/Command's own
|
|
66
72
|
# `givens` already use — `invariant` builds an Invariant,
|
|
@@ -101,7 +107,7 @@ module Hecks
|
|
|
101
107
|
ensures: [:ensures, [:each, :given]],
|
|
102
108
|
mutations: [:mutations, [:each, :mutation]],
|
|
103
109
|
emits: [:emits, :plain],
|
|
104
|
-
#
|
|
110
|
+
# Lifecycle state as a command guard (S10, ADR 0025) — a
|
|
105
111
|
# literal the same way `provenance`/`default:` already are;
|
|
106
112
|
# one state or an array of them, or nil for a command with
|
|
107
113
|
# no such guard.
|
|
@@ -138,7 +144,7 @@ module Hecks
|
|
|
138
144
|
wheres: [:wheres, [:each, :where_clause]],
|
|
139
145
|
order_by: [:order_by, :order_by],
|
|
140
146
|
limit: [:limit, :limit],
|
|
141
|
-
# Held by the language as an
|
|
147
|
+
# Held by the language as an open map, so every one of these reads the
|
|
142
148
|
# same way and a ninth option needs no new field on either side.
|
|
143
149
|
offset: [:offset, [:option, :offset]],
|
|
144
150
|
cursor: [:cursor, [:option, :cursor]],
|
|
@@ -162,20 +168,20 @@ module Hecks
|
|
|
162
168
|
fields: {
|
|
163
169
|
name: [:name, :plain],
|
|
164
170
|
description: [:description, :plain],
|
|
165
|
-
# A
|
|
166
|
-
#
|
|
167
|
-
#
|
|
168
|
-
#
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
#
|
|
171
|
+
# A list of paths, exactly as an aggregate's is, with no room left for
|
|
172
|
+
# the two to differ. A Symbol here and a String there would look
|
|
173
|
+
# identical to `to_h`'s byte-equality check, since both render as a
|
|
174
|
+
# string, while diverging at runtime: the assembled graph would carry a
|
|
175
|
+
# String, and `element_of`'s lookup of `args["sequence"]` in a
|
|
176
|
+
# symbol-keyed payload would find nothing — refusing "Reverse acts on
|
|
177
|
+
# one LedgerEntry — pass sequence:" while sequence was in fact passed.
|
|
172
178
|
identified_by: [:identified_by, :plain],
|
|
173
179
|
attributes: [:attributes, [:each, :shape_field]],
|
|
174
|
-
# ADR 0028 — the
|
|
180
|
+
# ADR 0028 — the same shape Aggregate's own `preconditions`
|
|
175
181
|
# already carries, one level down: a piece's own named
|
|
176
182
|
# `given`, referenced back by one of its own commands.
|
|
177
183
|
preconditions: [:preconditions, [:each, :given]],
|
|
178
|
-
# Round 7 — the
|
|
184
|
+
# Round 7 — the same shape Aggregate's own `invariants`
|
|
179
185
|
# already carries, one level down: checked against every
|
|
180
186
|
# instance of this piece, not the aggregate's own flat state.
|
|
181
187
|
invariants: [:invariants, [:each, :invariant]]
|
|
@@ -195,17 +201,18 @@ module Hecks
|
|
|
195
201
|
"Policy" => Contract.new(
|
|
196
202
|
holder: Policy, make: :new,
|
|
197
203
|
fields: {
|
|
198
|
-
name:
|
|
199
|
-
aggregate:
|
|
200
|
-
on_event:
|
|
201
|
-
trigger_command:
|
|
202
|
-
target_domain:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
name: [:name, :plain],
|
|
205
|
+
aggregate: [:aggregate, :plain],
|
|
206
|
+
on_event: [:on_event, :plain],
|
|
207
|
+
trigger_command: [:trigger_command, :plain],
|
|
208
|
+
target_domain: [:target_domain, :plain],
|
|
209
|
+
expect_undelivered: [:expect_undelivered, :plain],
|
|
210
|
+
where: [:where, :plain],
|
|
211
|
+
for_each: [:for_each, :plain],
|
|
212
|
+
with_spec: [:with_spec, :bindings]
|
|
206
213
|
},
|
|
207
214
|
rows: { with_spec: :with_spec_rows },
|
|
208
|
-
reads: { with_spec: [:from, :with_spec] },
|
|
215
|
+
reads: { with_spec: [:from, :with_spec], expect_undelivered: :expect_undelivered? },
|
|
209
216
|
derived: { position: :walk }
|
|
210
217
|
),
|
|
211
218
|
|
|
@@ -213,7 +220,7 @@ module Hecks
|
|
|
213
220
|
holder: ProcessManager, make: :new,
|
|
214
221
|
fields: {
|
|
215
222
|
name: [:name, :plain],
|
|
216
|
-
# A
|
|
223
|
+
# **A symbol**. `SagaInterpreter` does `event.payload[pm.correlates_by]` — a
|
|
217
224
|
# hash lookup on a symbol-keyed payload — and `value == pm.correlates_by`
|
|
218
225
|
# when resolving a leg s bindings. A String there finds nothing and
|
|
219
226
|
# resolves to nothing, so the wire never advanced and a drawer that
|
|
@@ -224,7 +231,7 @@ module Hecks
|
|
|
224
231
|
states: [:states, :plain]
|
|
225
232
|
},
|
|
226
233
|
reads: { states: :names },
|
|
227
|
-
# S17, ADR 0026 — `handlers` is a
|
|
234
|
+
# S17, ADR 0026 — `handlers` is a real feature of the language's
|
|
228
235
|
# own "ProcessManager" declaration now (`attribute :handlers,
|
|
229
236
|
# list_of(Handler)`, reaction.bluebook), consumed by the judge
|
|
230
237
|
# walking `Plan`'s own containment tree (`Handler`'s own
|
|
@@ -237,7 +244,7 @@ module Hecks
|
|
|
237
244
|
# S17, ADR 0026 — Handler is a genuine entity now, nested under
|
|
238
245
|
# ProcessManager (`entity "Handler"`, reaction.bluebook). Neither
|
|
239
246
|
# `position` nor `handler` is a stored field any more: a saga
|
|
240
|
-
# answers each event
|
|
247
|
+
# answers each event once, so `event_type` is Handler's own real,
|
|
241
248
|
# non-positional identity (no walk-minted `position` to derive),
|
|
242
249
|
# and the process manager it belongs to is structural now — which
|
|
243
250
|
# list this element sits in, not a stored field to fold a parent
|
|
@@ -251,7 +258,7 @@ module Hecks
|
|
|
251
258
|
},
|
|
252
259
|
# `dispatches` — same reason ProcessManager's own `handlers`
|
|
253
260
|
# claim, above, is `:children` : a real feature of the
|
|
254
|
-
#
|
|
261
|
+
# language's own "Handler" declaration (`attribute :dispatches,
|
|
255
262
|
# list_of(Dispatch)`), consumed by the judge walking `Plan`'s
|
|
256
263
|
# own containment tree rather than by any field this contract
|
|
257
264
|
# reads.
|
|
@@ -261,31 +268,31 @@ module Hecks
|
|
|
261
268
|
# S17, ADR 0026 — Dispatch is a genuine entity now, nested under
|
|
262
269
|
# Handler (`entity "Dispatch"`, process_manager.bluebook) — two
|
|
263
270
|
# levels deep, "no life outside its Handler" (the ADR's own
|
|
264
|
-
# words). `command_name`
|
|
265
|
-
#
|
|
266
|
-
#
|
|
267
|
-
# real handler
|
|
268
|
-
# `position`
|
|
271
|
+
# words). `command_name` alone is not Dispatch's own identity —
|
|
272
|
+
# items #151/#152 (`process_manager.bluebook`'s own
|
|
273
|
+
# `DispatchPosition` comment) found it collides the instant a
|
|
274
|
+
# real handler fans the same command out more than once, so
|
|
275
|
+
# `position` joins it, walk-minted the same way every other
|
|
269
276
|
# category's own `position` is (`derived: { position: :walk }`,
|
|
270
277
|
# the same entry ProcessManager's own contract carries above) —
|
|
271
278
|
# never a stored field on `DispatchSpec` itself, exactly like
|
|
272
279
|
# `handler` before it.
|
|
273
|
-
# `compensates_command_name`/`compensates_with_spec` —
|
|
274
|
-
# the
|
|
275
|
-
# (one IR
|
|
280
|
+
# `compensates_command_name`/`compensates_with_spec` — folded,
|
|
281
|
+
# the same kind `Lifecycle`'s own `state_field`/`default` claim
|
|
282
|
+
# (one IR object, `DispatchSpec#compensates`, feeding two
|
|
276
283
|
# separate language fields): `Readings#field_value`'s own
|
|
277
284
|
# `contract.folded` branch reads
|
|
278
285
|
# `node.compensates.to_h[:command_name]` / `[:with_spec]` for
|
|
279
|
-
# the
|
|
286
|
+
# the judge's offering side, nil-safe when there is no
|
|
280
287
|
# compensation at all (`through`'s own `return nil unless
|
|
281
|
-
# held`). `compensates_with_spec`
|
|
282
|
-
# (`compensates_with_spec_rows`, readings.rb) for the
|
|
288
|
+
# held`). `compensates_with_spec` also needs its own row shaper
|
|
289
|
+
# (`compensates_with_spec_rows`, readings.rb) for the judge's
|
|
283
290
|
# list-offering side — the with_spec pairs still need one
|
|
284
291
|
# "BindCompensation" append per pair, the same reason
|
|
285
292
|
# `with_spec` itself needs `with_spec_rows`, one level deeper.
|
|
286
293
|
# `Reconstruction#dispatch` reads the two flat fields back off
|
|
287
294
|
# the row and assembles the nested `DispatchSpec` by hand — a
|
|
288
|
-
# nested
|
|
295
|
+
# nested object is not one of the shapes `declaration()`'s own
|
|
289
296
|
# generic per-field hash-build can produce, the identical reason
|
|
290
297
|
# `handler`/`process_manager` pass a `:children` list through
|
|
291
298
|
# `extra:` instead.
|
|
@@ -372,7 +379,7 @@ module Hecks
|
|
|
372
379
|
# is what `Assembly::Build` needs (fed the native `to_h`
|
|
373
380
|
# value directly, already `true`/`nil`/a String), and the
|
|
374
381
|
# `reads:` entry below is what `Reconstruction` needs
|
|
375
|
-
# instead (fed the
|
|
382
|
+
# instead (fed the stringified meta-domain row).
|
|
376
383
|
count: [:count, :plain],
|
|
377
384
|
median_field: [:median_field, :plain],
|
|
378
385
|
# A read model inherits every option an ask has, so it reads them the
|
|
@@ -392,12 +399,12 @@ module Hecks
|
|
|
392
399
|
# `nil` — even though a read model's row never carries `wheres` as a
|
|
393
400
|
# native field the way Query's does (`[:each, :where_clause]` over an
|
|
394
401
|
# absent `row[:wheres]` is `Array(nil).map { ... }`, i.e. `[]`, always).
|
|
395
|
-
# The
|
|
402
|
+
# The real values, when a read model declares any, arrive through
|
|
396
403
|
# `options_of(row)`'s merge in `read_model` below (dispatched as
|
|
397
404
|
# generic Option rows, `read_model_option_rows`/`filter_options` — not
|
|
398
405
|
# as dedicated where-clause rows), which overrides this default. Until
|
|
399
406
|
# `ReadModel#to_h` spelled `wheres`/`order_by`/`limit`
|
|
400
|
-
# unconditionally, `round_trip_spec`'s own "
|
|
407
|
+
# unconditionally, `round_trip_spec`'s own "source keys only" +compare
|
|
401
408
|
# never asked about this key at all, so the `nil`-vs-`[]` gap between
|
|
402
409
|
# this contract's default and `to_h`'s own `[]` default went unnoticed.
|
|
403
410
|
# `order_by`/`limit` need no matching entry — the generic reader's
|
|
@@ -419,7 +426,7 @@ module Hecks
|
|
|
419
426
|
}
|
|
420
427
|
),
|
|
421
428
|
|
|
422
|
-
# A member's pairs are an
|
|
429
|
+
# A member's pairs are an open map, which is why Member is its own root in
|
|
423
430
|
# the language. The IR keeps them as a plain hash on the value object, so
|
|
424
431
|
# they are assembled with their shape rather than as a construct.
|
|
425
432
|
# S17, ADR 0026 — Member is a genuine entity now, nested under
|
|
@@ -6,10 +6,10 @@ module Hecks
|
|
|
6
6
|
# `to_h` spells things as text so the export stands on its own, and every
|
|
7
7
|
# one of those spellings has to come back apart here. This is the same family
|
|
8
8
|
# of work `MetaValidator::Shapes` does for the reconstruction — the difference
|
|
9
|
-
# is that Shapes rebuilds
|
|
9
|
+
# is that Shapes rebuilds hashes and this rebuilds objects, so it has to
|
|
10
10
|
# recover types rather than just strings.
|
|
11
11
|
#
|
|
12
|
-
#
|
|
12
|
+
# Encoding losses are the largest family of bug in this codebase, and every
|
|
13
13
|
# member has the same shape: reading an object where `to_h` holds a spelling.
|
|
14
14
|
# So each method below names the spelling it inverts.
|
|
15
15
|
module Marks
|
|
@@ -18,6 +18,14 @@ module Hecks
|
|
|
18
18
|
# `Attribute#to_h` spells a type with `to_s`, so a reference arrives as
|
|
19
19
|
# "Reference<Customer>" and has to become an edge again. Everything else is
|
|
20
20
|
# a name and stays one.
|
|
21
|
+
#
|
|
22
|
+
# @param field [Hash{Symbol => Object}] one attribute's declared row, keyed
|
|
23
|
+
# `:type` (String, a plain type name or `"Reference<Target>"`), `:name`
|
|
24
|
+
# (String), `:list` and `:optional` (read as truthy), `:default` (Object,
|
|
25
|
+
# nil), `:pattern` (String, nil), `:admits` (String, nil) and
|
|
26
|
+
# `:relationship` (String, nil), each the raw declared value
|
|
27
|
+
# @return [Bluebook::Attribute] the attribute, with `type` resolved to a
|
|
28
|
+
# `Bluebook::Reference` when the row spelled one
|
|
21
29
|
def attribute(field)
|
|
22
30
|
type = field[:type].to_s
|
|
23
31
|
target = type[/\AReference<(.+)>\z/, 1]
|
|
@@ -27,7 +35,7 @@ module Hecks
|
|
|
27
35
|
type: target ? Reference.new(target) : type,
|
|
28
36
|
list: field[:list] ? true : false,
|
|
29
37
|
default: field[:default],
|
|
30
|
-
# The
|
|
38
|
+
# The last place optionality can be dropped, and the one that was
|
|
31
39
|
# dropping it. Every bluebook in the registry is the round-trip
|
|
32
40
|
# product — MetaValidator dispatches the declaration in and reads it
|
|
33
41
|
# back — so a fact this constructor does not carry is a fact the
|
|
@@ -35,9 +43,9 @@ module Hecks
|
|
|
35
43
|
# wrote it.
|
|
36
44
|
optional: field[:optional] ? true : false,
|
|
37
45
|
pattern: field[:pattern],
|
|
38
|
-
#
|
|
46
|
+
# **The same lesson, one fact later**. `admits` is not on `to_h` — the
|
|
39
47
|
# wire does not carry it, on purpose — but it must still survive the
|
|
40
|
-
# round trip, because the grammar registry keeps the
|
|
48
|
+
# round trip, because the grammar registry keeps the assembled graph
|
|
41
49
|
# and downstream projections read the link off that. Dropped here, the
|
|
42
50
|
# language could not say `admits` about itself no matter how plainly
|
|
43
51
|
# the source wrote it, which is word for word what the note above
|
|
@@ -50,27 +58,45 @@ module Hecks
|
|
|
50
58
|
# The same shape. Two names because a head's field and a verb's argument are
|
|
51
59
|
# different things in the language even though the IR keeps one class for
|
|
52
60
|
# both — `Aggregate.Attribute` and `Command.Argument` are separate verbs.
|
|
61
|
+
#
|
|
62
|
+
# @param field [Hash{Symbol => Object}] see `attribute`
|
|
63
|
+
# @return [Bluebook::Attribute] see `attribute`
|
|
53
64
|
def shape_field(field) = attribute(field)
|
|
54
65
|
|
|
55
|
-
#
|
|
66
|
+
# One part of an identity. It goes in as a row so the language can hold an
|
|
56
67
|
# ordered list of them, and comes back out as the path it always was —
|
|
57
68
|
# a String, because `identity_paths` splits paths and never symbols.
|
|
69
|
+
#
|
|
70
|
+
# @param part [Hash{Symbol => Object}] one identity-path row, keyed `:value`
|
|
71
|
+
# (Object, read via `to_s`)
|
|
72
|
+
# @return [String] the path segment
|
|
58
73
|
def identity_path(part) = part[:value].to_s
|
|
59
74
|
|
|
60
|
-
# A member's fields — an
|
|
75
|
+
# A member's fields — an open map, which is why Member is its own root in
|
|
61
76
|
# the language and why the pairs arrive as a list rather than a value object.
|
|
62
77
|
#
|
|
63
|
-
# The values are
|
|
78
|
+
# The values are unmarked, because `ValueObject#to_h` spells them with `to_s`
|
|
64
79
|
# and the language stores them as text: `member code: "JPY", minor_units: 0`
|
|
65
80
|
# came back with a minor_units of "0", and a closed set that admits the string
|
|
66
81
|
# would refuse the number the caller passes.
|
|
82
|
+
#
|
|
83
|
+
# @param pairs [Array<Array(String, Object)>] one member's own field pairs,
|
|
84
|
+
# each `[field_name, raw_value]` as declared
|
|
85
|
+
# @return [Hash{Symbol => Object}] the member's fields, keyed by field name,
|
|
86
|
+
# each value read back to its native type through `unmark_scalar`
|
|
67
87
|
def member(pairs)
|
|
68
88
|
pairs.to_h { |key, value| [key.to_sym, unmark_scalar(value)] }
|
|
69
89
|
end
|
|
70
90
|
|
|
71
91
|
# A read model's gathered head. The keys must be symbols whichever way the
|
|
72
|
-
# declaration arrived, and `as` must be one too: it
|
|
92
|
+
# declaration arrived, and `as` must be one too: it names the reader the
|
|
73
93
|
# projection answers to, and `ReadModel#to_h` spells it `to_s`.
|
|
94
|
+
#
|
|
95
|
+
# @param row [Hash{Symbol => Object}] one aggregate-head row, keyed
|
|
96
|
+
# `:aggregate` (String), `:as` (String, the reader name) and `:many`
|
|
97
|
+
# (Boolean)
|
|
98
|
+
# @return [Hash{Symbol => Object}] the head, with every key a Symbol and
|
|
99
|
+
# `:as` read back to a Symbol
|
|
74
100
|
def head(row)
|
|
75
101
|
row.to_h { |key, value| [key.to_sym, key.to_sym == :as ? value.to_sym : value] }
|
|
76
102
|
end
|
|
@@ -78,12 +104,20 @@ module Hecks
|
|
|
78
104
|
# A group_by field's own name — the builder's native shape is already
|
|
79
105
|
# `{field: :symbol}`, so this matches it rather than leaving `field`
|
|
80
106
|
# as the String `Shapes#group_by_field` reads back.
|
|
107
|
+
#
|
|
108
|
+
# @param row [Hash{Symbol => String}] one group-by row, keyed `:field`
|
|
109
|
+
# @return [Hash{field: Symbol}] the row with `:field` read back to a Symbol
|
|
81
110
|
def group_by_field(row) = { field: row[:field].to_sym }
|
|
82
111
|
|
|
83
112
|
# A scalar that was written as itself rather than inspected — a member's
|
|
84
113
|
# value, where the language holds text and the type has to be read back from
|
|
85
114
|
# the shape of it. Unlike `read`, a bare word stays a String here, because
|
|
86
115
|
# a closed set admits words far more often than symbols.
|
|
116
|
+
#
|
|
117
|
+
# @param value [Object] the raw declared scalar, read via `to_s`
|
|
118
|
+
# @return [true, false, Integer, Float, String] `true`/`false` for those
|
|
119
|
+
# literal words, a number when the text matches a bare integer or decimal
|
|
120
|
+
# pattern, or the string itself otherwise
|
|
87
121
|
def unmark_scalar(value)
|
|
88
122
|
text = value.to_s
|
|
89
123
|
return true if text == "true"
|
|
@@ -97,12 +131,32 @@ module Hecks
|
|
|
97
131
|
# A saga's argument bindings. Each value rides Literal's spelling, which
|
|
98
132
|
# marks a Symbol with a leading colon — lose it and an argument reads as a
|
|
99
133
|
# string of the same name.
|
|
134
|
+
#
|
|
135
|
+
# @param with [Array<Array(String, String)>, nil] the declared bindings,
|
|
136
|
+
# each `[argument_name, literal_spelling]` pair, or `nil` for none
|
|
137
|
+
# @return [Hash{Symbol => Object}] the bindings, keyed by argument name,
|
|
138
|
+
# each value read back through `Literal.read`
|
|
100
139
|
def bindings(with) = Array(with).to_h { |key, value| [key.to_sym, read(value)] }
|
|
101
140
|
|
|
141
|
+
# Builds one aggregate or value object invariant from its declared row.
|
|
142
|
+
#
|
|
143
|
+
# @param rule [Hash{Symbol => String}] one invariant's declared row, keyed
|
|
144
|
+
# `:description` and `:canonical` (the canonical-form rendering of the
|
|
145
|
+
# rule's expression)
|
|
146
|
+
# @return [Bluebook::Invariant] the invariant, with `predicate`/`ast` left
|
|
147
|
+
# `nil` — `Expression::Evaluator` parses `canonical` on demand when
|
|
148
|
+
# neither is set
|
|
102
149
|
def invariant(rule)
|
|
103
150
|
Invariant.new(description: rule[:description], canonical: rule[:canonical])
|
|
104
151
|
end
|
|
105
152
|
|
|
153
|
+
# Builds one command precondition from its declared row.
|
|
154
|
+
#
|
|
155
|
+
# @param rule [Hash{Symbol => String}] one given's declared row, keyed
|
|
156
|
+
# `:description` and `:canonical` (the canonical-form rendering of the
|
|
157
|
+
# rule's expression)
|
|
158
|
+
# @return [Bluebook::Given] the given, with `predicate`/`ast` left `nil` —
|
|
159
|
+
# `Expression::Evaluator` parses `canonical` on demand when neither is set
|
|
106
160
|
def given(rule)
|
|
107
161
|
Given.new(description: rule[:description], canonical: rule[:canonical])
|
|
108
162
|
end
|
|
@@ -111,6 +165,11 @@ module Hecks
|
|
|
111
165
|
# All three fields are identifiers, unlike Invariant/Given's own
|
|
112
166
|
# free text, so — like `attribute`'s own `name`/`type` below —
|
|
113
167
|
# they come back as Symbols.
|
|
168
|
+
#
|
|
169
|
+
# @param row [Hash{Symbol => String}] one projected-field's declared row,
|
|
170
|
+
# keyed `:name`, `:reference` and `:remote_field` (each an identifier)
|
|
171
|
+
# @return [Bluebook::ProjectedField] the projected field, with each
|
|
172
|
+
# identifier read back to a Symbol
|
|
114
173
|
def projected_field(row)
|
|
115
174
|
ProjectedField.new(name: row[:name].to_sym, reference: row[:reference].to_sym,
|
|
116
175
|
remote_field: row[:remote_field].to_sym)
|
|
@@ -118,16 +177,24 @@ module Hecks
|
|
|
118
177
|
|
|
119
178
|
# `Mutation#to_h` branches on the operation, so this does too.
|
|
120
179
|
#
|
|
121
|
-
# An
|
|
122
|
-
# Symbol, wearing its colon) or a
|
|
180
|
+
# An append binds several fields at once, each either an argument (a
|
|
181
|
+
# Symbol, wearing its colon) or a literal — the distinction that is the
|
|
123
182
|
# whole reason `append: { direction: "out" }` was once indistinguishable
|
|
124
183
|
# from an argument named `out`.
|
|
184
|
+
#
|
|
185
|
+
# @param change [Hash{Symbol => Object}] one mutation's declared row, keyed
|
|
186
|
+
# `:target` (String), `:op` (String, such as `"set"`, `"append"`,
|
|
187
|
+
# `"delegate"` or `"corrects"`), `:fields` (Array, for the append-shaped
|
|
188
|
+
# ops) and `:source` (Hash, for any other op)
|
|
189
|
+
# @return [Bluebook::Mutation] the mutation, with `source` a
|
|
190
|
+
# `Hash{Symbol => Object}` of field bindings for an append-shaped op, or
|
|
191
|
+
# `classified`'s own result for any other
|
|
125
192
|
def mutation(change)
|
|
126
193
|
target = change[:target].to_sym
|
|
127
194
|
op = change[:op].to_sym
|
|
128
195
|
|
|
129
196
|
# `:delegate`/`:corrects` (CommandBuilder#delegates_to's and
|
|
130
|
-
# #corrects_impl's own comments) ride the
|
|
197
|
+
# #corrects_impl's own comments) ride the same multi-binding
|
|
131
198
|
# shape `:append` does.
|
|
132
199
|
return Mutation.new(target: target, op: op, source: appended(change[:fields])) if [:append, :delegate,
|
|
133
200
|
:corrects].include?(op)
|
|
@@ -135,12 +202,27 @@ module Hecks
|
|
|
135
202
|
Mutation.new(target: target, op: op, source: classified(change[:source]))
|
|
136
203
|
end
|
|
137
204
|
|
|
205
|
+
# Reads an append mutation's field bindings back off their declared rows.
|
|
206
|
+
#
|
|
207
|
+
# @param fields [Array<Array(String, String)>, nil] the append's declared
|
|
208
|
+
# field bindings, each `[field_name, literal_spelling]` pair, or `nil`
|
|
209
|
+
# for none
|
|
210
|
+
# @return [Hash{Symbol => Object}] the bindings, keyed by field name, each
|
|
211
|
+
# value read back through `Literal.read`
|
|
138
212
|
def appended(fields)
|
|
139
213
|
Array(fields).to_h { |field, source| [field.to_sym, read(source)] }
|
|
140
214
|
end
|
|
141
215
|
|
|
142
|
-
# A
|
|
216
|
+
# A set reads one thing, and `classified_source` said which: an argument by
|
|
143
217
|
# name, or a literal by value.
|
|
218
|
+
#
|
|
219
|
+
# @param source [Hash{Symbol => Object}, nil] the mutation's declared
|
|
220
|
+
# source, keyed `:kind` (String, `"argument"`, `"state"` or a literal
|
|
221
|
+
# kind) plus `:name` (String, for `"argument"`/`"state"`) or `:value`
|
|
222
|
+
# (Object, for a literal), or `nil` for no source
|
|
223
|
+
# @return [Symbol, Hecks::StateRef, Object, nil] the command argument name,
|
|
224
|
+
# a state self-reference, the literal value, or `nil` when `source` is
|
|
225
|
+
# `nil`
|
|
144
226
|
def classified(source)
|
|
145
227
|
return nil if source.nil?
|
|
146
228
|
|
|
@@ -151,41 +233,57 @@ module Hecks
|
|
|
151
233
|
end
|
|
152
234
|
end
|
|
153
235
|
|
|
154
|
-
#
|
|
236
|
+
# Every literal field on the wire, read back — one spelling, one reader.
|
|
155
237
|
#
|
|
156
238
|
# A where-clause value, a saga's argument bindings, an append binding, a
|
|
157
239
|
# limit: all of them ride Literal's self-describing form, so all of them
|
|
158
|
-
# come back through
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
#
|
|
162
|
-
#
|
|
163
|
-
#
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
#
|
|
167
|
-
#
|
|
168
|
-
#
|
|
169
|
-
#
|
|
170
|
-
#
|
|
240
|
+
# come back through this one reader, never a second one that might
|
|
241
|
+
# disagree about a quoted string or a number.
|
|
242
|
+
#
|
|
243
|
+
# An object literal is the one that bites hardest. A saga leg binds
|
|
244
|
+
# `narrative: { text: "transfer out" }` — a value object's fields written
|
|
245
|
+
# inline — and reading it as a plain string rather than through `Literal`
|
|
246
|
+
# would reach the runtime as `"{:text=>\"transfer out\"}"`, which coercion
|
|
247
|
+
# refuses: the debit leg is never delivered, and the whole settlement wire
|
|
248
|
+
# stops — banking emits TransferRequested five times and TransferDebited
|
|
249
|
+
# never. A whole-history replay gate is what catches this, because a saga
|
|
250
|
+
# that silently does nothing looks exactly like a saga with nothing to do.
|
|
251
|
+
#
|
|
252
|
+
# @param value [String, Object] wire spelling produced by `Literal.render`,
|
|
253
|
+
# or a bare word
|
|
254
|
+
# @return [nil, true, false, Integer, Float, Symbol, Hecks::StateRef,
|
|
255
|
+
# String, Hash, Array] the value read back — see `Literal.read`
|
|
171
256
|
def read(value) = Literal.read(value)
|
|
172
257
|
|
|
173
258
|
# `target:` (ADR 0055) — read straight off the wire, unconverted:
|
|
174
|
-
# it's already the bare aggregate-name
|
|
259
|
+
# it's already the bare aggregate-name string `WhereClause#to_h`/
|
|
175
260
|
# `OrderBy#to_h`/`LimitSpec#to_h` wrote (`resolve_target`'s own
|
|
176
261
|
# `Naming.demodulise` already ran once, at DSL-build time; this is
|
|
177
|
-
# the
|
|
262
|
+
# the replay path every real boot actually goes through, reading
|
|
178
263
|
# that same wire shape back — see this class's own header). Absent
|
|
179
264
|
# from `clause`/`declared` entirely on older wire data that never
|
|
180
265
|
# declared `on:` — `clause[:target]`/`declared[:target]` reads
|
|
181
266
|
# `nil` for a missing key exactly like an explicit `nil` would,
|
|
182
267
|
# so this is additive, not a migration.
|
|
268
|
+
#
|
|
269
|
+
# @param clause [Hash{Symbol => Object}] one where-clause's declared row,
|
|
270
|
+
# keyed `:field` (String), `:op` (String, a comparator name), `:value`
|
|
271
|
+
# (String, Literal's spelling) and `:target` (String, `nil` when absent)
|
|
272
|
+
# @return [QuerySpecification::Common::WhereClause] the clause, with `op`
|
|
273
|
+
# read to a Symbol and `value` read back through `Literal.read`
|
|
183
274
|
def where_clause(clause)
|
|
184
275
|
QuerySpecification::Common::WhereClause.new(
|
|
185
276
|
field: clause[:field], op: clause[:op].to_sym, value: read(clause[:value]), target: clause[:target]
|
|
186
277
|
)
|
|
187
278
|
end
|
|
188
279
|
|
|
280
|
+
# Builds a query's declared ordering, if it declares one.
|
|
281
|
+
#
|
|
282
|
+
# @param declared [Hash{Symbol => Object}, nil] the declared order-by row,
|
|
283
|
+
# keyed `:field` (String), `:direction` (String) and `:target` (String,
|
|
284
|
+
# `nil` when absent), or `nil` when the construct declares no ordering
|
|
285
|
+
# @return [QuerySpecification::Common::OrderBy, nil] the order-by, with
|
|
286
|
+
# `direction` read to a Symbol, or `nil` when `declared` is `nil`
|
|
189
287
|
def order_by(declared)
|
|
190
288
|
return nil unless declared
|
|
191
289
|
|
|
@@ -194,13 +292,21 @@ module Hecks
|
|
|
194
292
|
)
|
|
195
293
|
end
|
|
196
294
|
|
|
295
|
+
# Builds a query's declared limit, if it declares one.
|
|
296
|
+
#
|
|
297
|
+
# @param declared [Hash{Symbol => Object}, nil] the declared limit row,
|
|
298
|
+
# keyed `:value` (String, Literal's spelling) and `:target` (String,
|
|
299
|
+
# `nil` when absent), or `nil` when the construct declares no limit
|
|
300
|
+
# @return [QuerySpecification::Common::LimitSpec, nil] the limit, with
|
|
301
|
+
# `value` read back through `Literal.read`, or `nil` when `declared` is
|
|
302
|
+
# `nil`
|
|
197
303
|
def limit(declared)
|
|
198
304
|
return nil unless declared
|
|
199
305
|
|
|
200
306
|
QuerySpecification::Common::LimitSpec.new(value: read(declared[:value]), target: declared[:target])
|
|
201
307
|
end
|
|
202
308
|
|
|
203
|
-
#
|
|
309
|
+
# Every other specification option, from one table.
|
|
204
310
|
#
|
|
205
311
|
# Each entry names the struct and which of its members carry a value that
|
|
206
312
|
# rode Literal's spelling rather than plain text. A
|
|
@@ -219,6 +325,20 @@ module Hecks
|
|
|
219
325
|
# with `to_s`, so the colon is not there to strip.
|
|
220
326
|
SYMBOLIC = %i[mode policy tenant].freeze
|
|
221
327
|
|
|
328
|
+
# Builds one query specification option from its declared row and `OPTIONS`.
|
|
329
|
+
#
|
|
330
|
+
# @param name [Symbol] the option's name, a key of `OPTIONS` (`:offset`,
|
|
331
|
+
# `:cursor`, `:null_semantics`, `:authorization` or `:inspection`)
|
|
332
|
+
# @param declared [Hash{Symbol => Object}, nil] the option's declared row,
|
|
333
|
+
# or `nil` when the construct does not declare this option
|
|
334
|
+
# @return [QuerySpecification::Common::OffsetSpec,
|
|
335
|
+
# QuerySpecification::Common::CursorSpec,
|
|
336
|
+
# QuerySpecification::Common::NullSemantics,
|
|
337
|
+
# QuerySpecification::Common::AuthorizationSpec,
|
|
338
|
+
# QuerySpecification::Common::InspectionSpec, nil] the option built
|
|
339
|
+
# through the holder `OPTIONS` names for `name`, or `nil` when `declared`
|
|
340
|
+
# is `nil`
|
|
341
|
+
# @raise [KeyError] if `name` is not a key of `OPTIONS`
|
|
222
342
|
def option(name, declared)
|
|
223
343
|
return nil if declared.nil?
|
|
224
344
|
|
|
@@ -226,6 +346,15 @@ module Hecks
|
|
|
226
346
|
holder.new(**Hash(declared).to_h { |key, value| [key, option_value(key, value, marked)] })
|
|
227
347
|
end
|
|
228
348
|
|
|
349
|
+
# Reads one option member's declared value back to its native type.
|
|
350
|
+
#
|
|
351
|
+
# @param key [Symbol] the option member's name being read
|
|
352
|
+
# @param value [Object, nil] the declared value for that member
|
|
353
|
+
# @param marked [Array<Symbol>] the members of this option that ride
|
|
354
|
+
# Literal's spelling, from `OPTIONS`
|
|
355
|
+
# @return [Object, nil] `value` read back through `Literal.read` when
|
|
356
|
+
# `key` is marked, a Symbol when `key` is one of `SYMBOLIC`, the raw
|
|
357
|
+
# `value` otherwise, or `nil` when `value` is `nil`
|
|
229
358
|
def option_value(key, value, marked)
|
|
230
359
|
return nil if value.nil?
|
|
231
360
|
return read(value) if marked.include?(key)
|