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
|
@@ -6,10 +6,10 @@ module Hecks
|
|
|
6
6
|
# The language now spells its fields exactly as the IR spells them, so the
|
|
7
7
|
# judge reads almost everything straight through: `command.givens`,
|
|
8
8
|
# `value_object.invariants`, `read_model.aggregate_heads`. What remains here
|
|
9
|
-
# is not naming drift — it is places where the IR's
|
|
9
|
+
# is not naming drift — it is places where the IR's shape differs from the
|
|
10
10
|
# language's, and no amount of renaming would close that:
|
|
11
11
|
#
|
|
12
|
-
# transitions one declaration expands to
|
|
12
|
+
# transitions one declaration expands to several rows, because `from`
|
|
13
13
|
# may be a list of states
|
|
14
14
|
# value_objects the IR holds objects ; the language holds their names
|
|
15
15
|
# normalisations not on the bluebook at all — they come from the canonical
|
|
@@ -18,14 +18,21 @@ module Hecks
|
|
|
18
18
|
# lifecycle one IR object feeding two separate fields
|
|
19
19
|
#
|
|
20
20
|
# Everything in this file is a difference in shape. If something here is
|
|
21
|
-
# only a difference in
|
|
21
|
+
# only a difference in name, it is in the wrong file: rename the language.
|
|
22
22
|
module Readings
|
|
23
23
|
# A list the walk is about to offer, as rows it can shape into dispatches.
|
|
24
24
|
#
|
|
25
|
-
#
|
|
25
|
+
# From the table. This was nine hand-written cases keyed "Category.list", and
|
|
26
26
|
# every one of them was a fact `Assembly::Contracts` is the right place to
|
|
27
27
|
# keep: which shaper turns this list into rows. A list with no shaper reads
|
|
28
28
|
# straight off the node, which is most of them.
|
|
29
|
+
#
|
|
30
|
+
# @param category [String] the node's own construct category, such
|
|
31
|
+
# as `"Command"` or `"ValueObject"`
|
|
32
|
+
# @param list_name [String] the appendable list's own field name
|
|
33
|
+
# @param node [Object] the built IR node being judged
|
|
34
|
+
# @return [Array<Hash>] one row per element of `node`'s own
|
|
35
|
+
# `list_name`, shaped by the category's own shaper if it has one
|
|
29
36
|
def rows_for(category, list_name, node)
|
|
30
37
|
shaper = Assembly.contract(category).shaper(list_name)
|
|
31
38
|
return Array(node.public_send(list_name)) unless shaper
|
|
@@ -34,44 +41,84 @@ module Hecks
|
|
|
34
41
|
end
|
|
35
42
|
|
|
36
43
|
# A where-clause is read through its own to_h, which is where the IR spells a
|
|
37
|
-
# symbol argument as ":ceiling". Reading the
|
|
44
|
+
# symbol argument as ":ceiling". Reading the object instead lost the colon,
|
|
38
45
|
# and nothing downstream could tell an argument from a literal of the same
|
|
39
46
|
# name.
|
|
47
|
+
#
|
|
48
|
+
# @param node [Object] the built IR node being judged (a `Query` or
|
|
49
|
+
# `ReadModel`)
|
|
50
|
+
# @return [Array<Hash{Symbol => Object}>] each declared `where`
|
|
51
|
+
# clause's own fields
|
|
40
52
|
def where_rows(node) = Array(node.wheres).map(&:to_h)
|
|
41
53
|
|
|
42
|
-
# The language holds a value object's
|
|
54
|
+
# The language holds a value object's name here ; the IR holds the object.
|
|
55
|
+
#
|
|
56
|
+
# @param node [Object] the built IR node being judged (an
|
|
57
|
+
# `Aggregate`)
|
|
58
|
+
# @return [Array<Hash{Symbol => String}>] `{name: ...}` for every
|
|
59
|
+
# declared value object
|
|
43
60
|
def value_object_names(node) = node.value_objects.map { |shape| { name: shape.hecks_name } }
|
|
44
61
|
|
|
45
|
-
#
|
|
62
|
+
# An identity is a list of parts, so it is offered one part at a time — the
|
|
46
63
|
# same way attributes and transitions are. The IR holds the paths ; the
|
|
47
|
-
# language holds a row per path, and the
|
|
64
|
+
# language holds a row per path, and the order between them is the whole
|
|
48
65
|
# meaning, because the identity is their join.
|
|
66
|
+
#
|
|
67
|
+
# @param node [Object] the built IR node being judged (an
|
|
68
|
+
# `Aggregate` or `Entity`)
|
|
69
|
+
# @return [Array<Hash{Symbol => String}>] `{value: ...}` for every
|
|
70
|
+
# identity path part, in order
|
|
49
71
|
def identity_rows(node) = node.identity_paths.map { |path| { value: path } }
|
|
50
72
|
|
|
51
73
|
# Through to_h, which is where Bluebook.render_value spells a symbol argument as
|
|
52
74
|
# ":source". The raw with_spec lost the colon, and a binding that reads an
|
|
53
75
|
# argument became indistinguishable from one carrying a literal string.
|
|
76
|
+
#
|
|
77
|
+
# @param node [Object] the built IR node being judged (a `Dispatch`)
|
|
78
|
+
# @return [Array<Hash{Symbol => Object}>] `pair_rows`' own return
|
|
79
|
+
# for `node`'s own `with_spec`
|
|
54
80
|
def with_spec_rows(node) = pair_rows(node.to_h[:with_spec])
|
|
55
81
|
|
|
56
|
-
#
|
|
82
|
+
# The same read, one level in — `compensates` folds `DispatchSpec`
|
|
57
83
|
# into the language's own `compensates_command_name`/
|
|
58
84
|
# `compensates_with_spec` (`Assembly::Contracts`' own comment on
|
|
59
|
-
# "Dispatch"), so its
|
|
85
|
+
# "Dispatch"), so its own with_spec pairs live nested one hash
|
|
60
86
|
# down from where `with_spec_rows` looks. `&.dig(...)` — no
|
|
61
87
|
# compensation at all is not an error, it is `pair_rows(nil)`, empty.
|
|
88
|
+
#
|
|
89
|
+
# @param node [Object] the built IR node being judged (a `Dispatch`)
|
|
90
|
+
# @return [Array<Hash{Symbol => Object}>] `pair_rows`' own return
|
|
91
|
+
# for `node`'s own `compensates.with_spec`, `[]` when `node` has
|
|
92
|
+
# no compensation
|
|
62
93
|
def compensates_with_spec_rows(node) = pair_rows(node.to_h[:compensates]&.dig(:with_spec))
|
|
63
94
|
|
|
64
95
|
# A read model carries the same options an ask does, plus its filters — see
|
|
65
96
|
# option_rows.
|
|
97
|
+
#
|
|
98
|
+
# @param node [Object] the built IR node being judged (a
|
|
99
|
+
# `ReadModel`)
|
|
100
|
+
# @return [Array<Hash{Symbol => Object}>] `option_rows`' own return,
|
|
101
|
+
# with filters included
|
|
66
102
|
def read_model_option_rows(node) = option_rows(node, filters: true)
|
|
67
103
|
|
|
68
104
|
# The canonical-form table is the expression grammar's, not this chapter's, so
|
|
69
105
|
# the node is not consulted at all.
|
|
106
|
+
#
|
|
107
|
+
# @param _node [Object] unused; every node shares the one table
|
|
108
|
+
# @return [Array<Hash{Symbol => Object}>] `normalisation_rows`' own
|
|
109
|
+
# return
|
|
70
110
|
def normalisation_table(_node) = normalisation_rows
|
|
71
111
|
|
|
72
112
|
# `lifecycle :status do transition "Retire" => "retired", from: ["issued", "active"] end`
|
|
73
|
-
# is
|
|
113
|
+
# is one declaration and two transitions. Offering it once would leave the
|
|
74
114
|
# second unjudged, which is the whole failure this judge exists to avoid.
|
|
115
|
+
#
|
|
116
|
+
# @param node [Object] the built IR node being judged (an
|
|
117
|
+
# `Aggregate` or `Entity`)
|
|
118
|
+
# @return [Array<Hash{Symbol => Object}>] `:command`, `:from_state`,
|
|
119
|
+
# and `:to_state`, one row per declared `from` state (or one row
|
|
120
|
+
# with a `nil` `:from_state` for an unconstrained transition);
|
|
121
|
+
# `[]` if `node` declares no lifecycle
|
|
75
122
|
def transition_rows(node)
|
|
76
123
|
lifecycle = node.respond_to?(:lifecycle) ? node.lifecycle : nil
|
|
77
124
|
return [] unless lifecycle
|
|
@@ -84,17 +131,21 @@ module Hecks
|
|
|
84
131
|
end
|
|
85
132
|
end
|
|
86
133
|
|
|
87
|
-
# An
|
|
134
|
+
# An open map — a member's fields, a dispatch's argument bindings — has no
|
|
88
135
|
# value object that can hold it, so each entry becomes its own row. This is
|
|
89
136
|
# why Member and Dispatch are roots in the language rather than lists.
|
|
137
|
+
#
|
|
138
|
+
# @param map [Hash, nil] the open map to flatten, or `nil`
|
|
139
|
+
# @return [Array<Hash{Symbol => Object}>] `{key:, value:}` for every
|
|
140
|
+
# entry, `[]` when `map` is `nil`
|
|
90
141
|
def pair_rows(map)
|
|
91
142
|
Array(map&.to_h).map { |key, value| { key: key, value: value } }
|
|
92
143
|
end
|
|
93
144
|
|
|
94
|
-
#
|
|
145
|
+
# Every specification option an ask carries, flattened to rows.
|
|
95
146
|
#
|
|
96
147
|
# `offset`, `cursor`, `nulls`, `authorize` and `inspect_query` are
|
|
97
|
-
# five options, one compound (authorize names a policy
|
|
148
|
+
# five options, one compound (authorize names a policy and a
|
|
98
149
|
# tenant). `extra_options_to_h` already spells every one of them
|
|
99
150
|
# and drops the absent ones, so this reads that rather than
|
|
100
151
|
# naming them here — a sixth option needs no change on either
|
|
@@ -103,18 +154,18 @@ module Hecks
|
|
|
103
154
|
# `filters: true` adds a read model's wheres, order_by and limit —
|
|
104
155
|
# `at` tells repeated rows apart, so two wheres do not collapse.
|
|
105
156
|
#
|
|
106
|
-
#
|
|
157
|
+
# The language may hold more than `to_h` carries, and this is where that
|
|
107
158
|
# mattered. Until 2026-08-11, `ReadModel#to_h` omitted all three —
|
|
108
159
|
# `extra_options_to_h` rejects them by name, still does — so a read
|
|
109
160
|
# model's filtering had never been in the wire contract, and I first
|
|
110
161
|
# read that as a wall: if the wire cannot carry it, the language cannot
|
|
111
162
|
# hold it, and a graph assembled from the language must lose it.
|
|
112
163
|
#
|
|
113
|
-
# That was the wrong conclusion. `to_h` is a
|
|
114
|
-
# is the
|
|
164
|
+
# That was the wrong conclusion. `to_h` is a projection ; the language
|
|
165
|
+
# is the source. They have to agree about everything
|
|
115
166
|
# to_h spells, not about everything the language knows. Held as option
|
|
116
167
|
# rows, the filters survived the round trip regardless of whether the
|
|
117
|
-
# wire carried them too — which is exactly why, when a
|
|
168
|
+
# wire carried them too — which is exactly why, when a later task
|
|
118
169
|
# (Rust read-model codegen) needed `wheres`/`order_by`/`limit` on the
|
|
119
170
|
# wire for an unrelated reason, `ReadModel#to_h` could be extended to
|
|
120
171
|
# spell them (the same mechanism `Query#to_h` already used) without
|
|
@@ -125,6 +176,14 @@ module Hecks
|
|
|
125
176
|
#
|
|
126
177
|
# Named `wheres`, `order_by` and `limit` so they gather back into exactly the
|
|
127
178
|
# declaration keys the assembly already reads.
|
|
179
|
+
#
|
|
180
|
+
# @param node [Object] the built IR node being judged (a `Query` or
|
|
181
|
+
# `ReadModel`)
|
|
182
|
+
# @param filters [Boolean] whether to also flatten `node`'s own
|
|
183
|
+
# `wheres`/`order_by`/`limit`
|
|
184
|
+
# @return [Array<Hash{Symbol => Object}>] `[]` if `node` answers no
|
|
185
|
+
# `extra_options_to_h`; otherwise `parts`' own rows for every
|
|
186
|
+
# option `node` carries
|
|
128
187
|
def option_rows(node, filters: false)
|
|
129
188
|
return [] unless node.respond_to?(:extra_options_to_h)
|
|
130
189
|
|
|
@@ -139,6 +198,14 @@ module Hecks
|
|
|
139
198
|
end
|
|
140
199
|
end
|
|
141
200
|
|
|
201
|
+
# Reads a read model's own filtering fields, in the shape
|
|
202
|
+
# `option_rows` expects to flatten alongside its ordinary options.
|
|
203
|
+
#
|
|
204
|
+
# @param node [Object] the built IR node being judged (a
|
|
205
|
+
# `ReadModel`)
|
|
206
|
+
# @return [Hash{Symbol => Object}] `:wheres` (`Array<Hash>`),
|
|
207
|
+
# `:order_by`, and `:limit` (each `Hash` or absent), absent keys
|
|
208
|
+
# dropped
|
|
142
209
|
def filter_options(node)
|
|
143
210
|
{
|
|
144
211
|
wheres: Array(node.wheres).map(&:to_h),
|
|
@@ -147,24 +214,40 @@ module Hecks
|
|
|
147
214
|
}.reject { |_, held| held.nil? || held == [] }
|
|
148
215
|
end
|
|
149
216
|
|
|
217
|
+
# Flattens one option's own value (or one element of a repeated
|
|
218
|
+
# option) into key/value rows.
|
|
219
|
+
#
|
|
220
|
+
# @param option [Symbol] the option's own name
|
|
221
|
+
# @param held [Object] the option's own value (a Hash-coercible
|
|
222
|
+
# object) — one repetition's worth, for a repeated option
|
|
223
|
+
# @param at [Integer, nil] the repetition index, for a repeated
|
|
224
|
+
# option; `nil` otherwise
|
|
225
|
+
# @return [Array<Hash{Symbol => String, Object}>] `:option`, `:key`,
|
|
226
|
+
# `:value`, and `:at` (stringified, `nil` when `at` is `nil`), one
|
|
227
|
+
# per key of `held`
|
|
150
228
|
def parts(option, held, at)
|
|
151
229
|
Hash(held).map do |key, value|
|
|
152
230
|
{ option: option.to_s, key: key.to_s, value: value, at: at&.to_s }
|
|
153
231
|
end
|
|
154
232
|
end
|
|
155
233
|
|
|
156
|
-
# A mutation is
|
|
157
|
-
# field/kind/source triple — and an append binds
|
|
234
|
+
# A mutation is one declaration, but the language's Change holds a single
|
|
235
|
+
# field/kind/source triple — and an append binds several fields at once
|
|
158
236
|
# (`append: { name: :name, amount: :amount }`). So an append is offered
|
|
159
237
|
# once per binding, and each one is judged.
|
|
160
238
|
#
|
|
161
|
-
#
|
|
162
|
-
#
|
|
163
|
-
#
|
|
239
|
+
# Sending `field: v(""), kind: v("argument"), source: v("")` here
|
|
240
|
+
# instead — three stubbed values — would hand every rule about what
|
|
241
|
+
# a mutation reads a blank, so it could never refuse.
|
|
242
|
+
#
|
|
243
|
+
# @param node [Object] the built IR node being judged (a `Command`)
|
|
244
|
+
# @return [Array<Hash{Symbol => Object}>] one row per binding for an
|
|
245
|
+
# append/delegate/corrects mutation, or `set_row`'s own single-row
|
|
246
|
+
# array for a set/increment/decrement
|
|
164
247
|
def mutation_rows(node)
|
|
165
248
|
Array(node.mutations).flat_map do |mutation|
|
|
166
249
|
# `:delegate`/`:corrects` (CommandBuilder#delegates_to's and
|
|
167
|
-
# #corrects_impl's own comments) ride the
|
|
250
|
+
# #corrects_impl's own comments) ride the same multi-binding
|
|
168
251
|
# shape `:append` does — `with: {...}`/the assembled
|
|
169
252
|
# `as:`/`reason:`/`reverses:` hash is a field map, same as
|
|
170
253
|
# append's own `fields:`.
|
|
@@ -174,7 +257,7 @@ module Hecks
|
|
|
174
257
|
# Spelled the way Mutation#appended_fields spells it, because
|
|
175
258
|
# Assembly::Marks reads this row back through the same reader it
|
|
176
259
|
# reads that field with. `then_set :marks, append: { direction:
|
|
177
|
-
# "out" }` binds a
|
|
260
|
+
# "out" }` binds a literal, and storing it raw made it
|
|
178
261
|
# indistinguishable from an argument called out.
|
|
179
262
|
{ target: mutation.target, op: mutation.op, field: field,
|
|
180
263
|
kind: argument.is_a?(Symbol) ? "argument" : "literal",
|
|
@@ -185,6 +268,11 @@ module Hecks
|
|
|
185
268
|
|
|
186
269
|
# A set/increment/decrement reads one thing: a command argument, or a
|
|
187
270
|
# literal written into the bluebook.
|
|
271
|
+
#
|
|
272
|
+
# @param mutation [Bluebook::Mutation] a set/increment/decrement
|
|
273
|
+
# mutation
|
|
274
|
+
# @return [Array(Hash{Symbol => Object})] a single-element array
|
|
275
|
+
# holding `:target`, `:op`, `:field`, `:kind`, and `:source`
|
|
188
276
|
def set_row(mutation)
|
|
189
277
|
classified = mutation.to_h[:source] || {}
|
|
190
278
|
|
|
@@ -196,6 +284,10 @@ module Hecks
|
|
|
196
284
|
# The normalisation table belongs to the expression grammar, not to any one
|
|
197
285
|
# bluebook — it is how the canonical form of a rule is spelled. The language
|
|
198
286
|
# models it because a bluebook's rules are canonicalised on the way in.
|
|
287
|
+
#
|
|
288
|
+
# @return [Array<Hash{Symbol => Object}>] one row per admitted
|
|
289
|
+
# normalisation rule (`:strategy`, `:source_token`, `:replacement`,
|
|
290
|
+
# `:boundary`, `:position`), `[]` if the table cannot be read
|
|
199
291
|
def normalisation_rows
|
|
200
292
|
table = Expression::CanonicalForm.table
|
|
201
293
|
return [] unless table
|
|
@@ -215,17 +307,19 @@ module Hecks
|
|
|
215
307
|
[]
|
|
216
308
|
end
|
|
217
309
|
|
|
218
|
-
# What the
|
|
310
|
+
# What the bluebook calls a node, whichever kind of thing the node is.
|
|
219
311
|
#
|
|
220
|
-
#
|
|
221
|
-
#
|
|
222
|
-
#
|
|
223
|
-
#
|
|
312
|
+
# Every construct answers `hecks_name` now, so there is nothing to sniff
|
|
313
|
+
# (`respond_to?(:hecks_name) ? … : node.name`) or choose between. This
|
|
314
|
+
# disappears entirely when the DSL stops handing the judge nodes at all.
|
|
315
|
+
#
|
|
316
|
+
# @param node [Object] the built IR node being judged
|
|
317
|
+
# @return [String] the node's own declared name
|
|
224
318
|
def declared_name(node) = node.hecks_name
|
|
225
319
|
|
|
226
320
|
# One field of a Declare payload. Mostly a reader of the same name — the
|
|
227
321
|
# exceptions are fields the IR keeps somewhere else, or not at all.
|
|
228
|
-
#
|
|
322
|
+
# Read from the table, not from a branch per category.
|
|
229
323
|
#
|
|
230
324
|
# These were eight hand-written cases — `Entity.owner`, `Member.shape`, two
|
|
231
325
|
# lifecycle members twice over, and three of a query's — each one restating
|
|
@@ -233,11 +327,23 @@ module Hecks
|
|
|
233
327
|
# `:parent` there ; a folded field names the object and member it lives in.
|
|
234
328
|
# So the exceptions are looked up rather than repeated, and a new fold is one
|
|
235
329
|
# line in one file instead of two lines in two.
|
|
330
|
+
#
|
|
331
|
+
# @param category [String] the node's own construct category, such
|
|
332
|
+
# as `"Command"` or `"ValueObject"`
|
|
333
|
+
# @param node [Object] the built IR node being judged
|
|
334
|
+
# @param field [Symbol] the Declare payload field to read
|
|
335
|
+
# @param parent_id [String] the id the containment walk carries in
|
|
336
|
+
# from one level up, offered when `field` is the parent pointer
|
|
337
|
+
# @return [Object] `field`'s own value: `node`'s declared name,
|
|
338
|
+
# `parent_id`, a folded member's value (`through`), the read
|
|
339
|
+
# model's own limit value, an encoded literal (`provenance`), or
|
|
340
|
+
# `node.public_send(field)`; `nil` if `node` does not respond to
|
|
341
|
+
# `field`
|
|
236
342
|
def field_value(category, node, field, parent_id)
|
|
237
343
|
return declared_name(node) if field == :name
|
|
238
344
|
|
|
239
345
|
contract = Assembly.contract(category)
|
|
240
|
-
# A setter names its target as a
|
|
346
|
+
# A setter names its target as a string and a Declare field arrives a Symbol,
|
|
241
347
|
# so the lookup keys on a Symbol either way. The case statement this replaced
|
|
242
348
|
# was type-blind because it interpolated ; a Hash is not.
|
|
243
349
|
named = field.to_sym
|
|
@@ -246,19 +352,19 @@ module Hecks
|
|
|
246
352
|
object, member = contract.folded(named)
|
|
247
353
|
return through(node, object, member) if member
|
|
248
354
|
|
|
249
|
-
# `limit` is a language field
|
|
355
|
+
# `limit` is a language field and an object in the IR — `Array(an_object)`
|
|
250
356
|
# wraps rather than destructures, so offering it stored
|
|
251
357
|
# "#<struct LimitSpec value=3>".
|
|
252
358
|
return node.limit&.to_h&.fetch(:value, nil) if "#{category}.#{field}" == "Query.limit"
|
|
253
359
|
|
|
254
|
-
# `provenance from: {...}` is a
|
|
360
|
+
# `provenance from: {...}` is a hash offered into a text field, and
|
|
255
361
|
# handing it over raw let the runtime's own coercion spell it — which
|
|
256
362
|
# meant Ruby's `Hash#to_s`, whose spelling changed under us between
|
|
257
363
|
# 3.3 and 3.4. Encoded here, the same way `default:` already is and the
|
|
258
364
|
# same way Shapes#provenance reads it back.
|
|
259
365
|
return encode_literal(node.provenance) if field == :provenance
|
|
260
366
|
|
|
261
|
-
# `identified_by` is no longer a
|
|
367
|
+
# `identified_by` is no longer a field of any declaration — it is a list,
|
|
262
368
|
# filled by Identify one part at a time, so it is read through `identity_rows`
|
|
263
369
|
# like every other list rather than special-cased here. What this branch
|
|
264
370
|
# existed to protect is now structural : a path cannot come back as its head,
|
|
@@ -268,9 +374,17 @@ module Hecks
|
|
|
268
374
|
end
|
|
269
375
|
|
|
270
376
|
# One member of the object a field folds into. `to_h` first, because the
|
|
271
|
-
# member names are the ones the IR
|
|
377
|
+
# member names are the ones the IR spells — a Lifecycle's `default`, an
|
|
272
378
|
# OrderBy's `direction` — and reading the object raw is how a colon or a type
|
|
273
379
|
# goes missing.
|
|
380
|
+
#
|
|
381
|
+
# @param node [Object] the built IR node being judged
|
|
382
|
+
# @param object [Symbol] the member object's own field name on
|
|
383
|
+
# `node`, such as `:lifecycle`
|
|
384
|
+
# @param member [Symbol] the field to read off that object, such as
|
|
385
|
+
# `:default`; `:transitions` returns the object itself
|
|
386
|
+
# @return [Object, nil] the member's own value, or `nil` if `node`
|
|
387
|
+
# does not respond to `object` or that object is absent
|
|
274
388
|
def through(node, object, member)
|
|
275
389
|
held = node.respond_to?(object) ? node.public_send(object) : nil
|
|
276
390
|
return nil unless held
|
|
@@ -279,8 +393,15 @@ module Hecks
|
|
|
279
393
|
end
|
|
280
394
|
|
|
281
395
|
# What a setting command writes. A setter whose source is absent is not
|
|
282
|
-
# dispatched at all —
|
|
396
|
+
# dispatched at all — absent is not empty, and offering "" would turn every
|
|
283
397
|
# "if you declare it, declare something" rule into "you must declare it".
|
|
398
|
+
#
|
|
399
|
+
# @param category [String] the node's own construct category
|
|
400
|
+
# @param node [Object] the built IR node being judged
|
|
401
|
+
# @param target [Symbol] the setter's own target field
|
|
402
|
+
# @return [Object, nil] the target's own value: the closed set's own
|
|
403
|
+
# row count, a folded member's value (`through`), or
|
|
404
|
+
# `node.public_send(target)`; `nil` if absent
|
|
284
405
|
def setter_value(category, node, target)
|
|
285
406
|
# `rows` folds into `closed_set` and `members` between them, with no single
|
|
286
407
|
# member to name, so it keeps its own reading — see Contract#folded.
|
|
@@ -292,65 +413,94 @@ module Hecks
|
|
|
292
413
|
node.respond_to?(target) ? node.public_send(target) : nil
|
|
293
414
|
end
|
|
294
415
|
|
|
295
|
-
# Only a
|
|
416
|
+
# Only a declared closed set has a row count. An empty one is the defect,
|
|
296
417
|
# so `rows` must stay absent rather than arrive as zero.
|
|
418
|
+
#
|
|
419
|
+
# @param node [Object] the built IR node being judged (a
|
|
420
|
+
# `ValueObject`)
|
|
421
|
+
# @return [Integer, nil] the closed set's own row count, or `nil` if
|
|
422
|
+
# `node` is not a declared closed set
|
|
297
423
|
def closed_set_size(node)
|
|
298
424
|
return nil unless node.respond_to?(:closed_set?) && node.closed_set?
|
|
299
425
|
|
|
300
426
|
Array(node.members).size
|
|
301
427
|
end
|
|
302
428
|
|
|
303
|
-
# `Reference<Customer>` is an IR
|
|
429
|
+
# `Reference<Customer>` is an IR encoding, not a domain fact. The fact is
|
|
304
430
|
# that the attribute points at Customer's head — so the language is offered
|
|
305
431
|
# that head's ID, and resolution does the rest. Encoding and decoding both
|
|
306
432
|
# live here, because this is where the IR's shape differs from the
|
|
307
433
|
# language's and nowhere else should know the spelling.
|
|
434
|
+
#
|
|
435
|
+
# @param row [Bluebook::Attribute] the attribute being offered
|
|
436
|
+
# @param aggregate_id [String] the owning head's own id, read for
|
|
437
|
+
# its chapter prefix
|
|
438
|
+
# @return [String, nil] the target head's own id, or `nil` if `row`
|
|
439
|
+
# is not a `reference_to` attribute
|
|
308
440
|
def points_at(row, aggregate_id)
|
|
309
441
|
return nil unless row.reference?
|
|
310
442
|
|
|
311
|
-
#
|
|
443
|
+
# The chapter this head is in, and the head it points at — which is exactly
|
|
312
444
|
# how an aggregate is identified, so it is built the same way rather than
|
|
313
|
-
# spelled again with a separator of its own. This is dispatched as a
|
|
314
|
-
#
|
|
315
|
-
# `repository.find` against the target Aggregate-within-Meta record's
|
|
316
|
-
# stored id — so it
|
|
445
|
+
# spelled again with a separator of its own. This is dispatched as a real
|
|
446
|
+
# reference value (`Aggregate.Reference`'s `points_at:`), resolved by
|
|
447
|
+
# `repository.find` against the target Aggregate-within-Meta record's own
|
|
448
|
+
# stored id — so it must equal what that record's identity actually
|
|
317
449
|
# derives, not a wire-format spelling. `reference_type`, below, is the
|
|
318
450
|
# separate later reader that un-derives it back into "Reference<X>".
|
|
319
451
|
Naming.identity([aggregate_id.split(Naming::IDENTITY_JOIN).first, row.type.target_name])
|
|
320
452
|
end
|
|
321
453
|
|
|
322
|
-
# A
|
|
454
|
+
# A literal, written so it can be read back exactly.
|
|
323
455
|
#
|
|
324
456
|
# The language holds a default and a literal mutation source as text, and
|
|
325
457
|
# `to_s` threw the type away: 0.0 came back "0.0", and `{ value: "good" }`
|
|
326
458
|
# came back its inspect string with nowhere to say it had been a hash. The
|
|
327
459
|
# language already stores code as text — `canonical: "cents >= 0"` — so an
|
|
328
|
-
# encoding is in keeping; it simply has to be
|
|
460
|
+
# encoding is in keeping; it simply has to be self-describing. That rule is
|
|
329
461
|
# now Hecks::Literal's, stated once and shared with every other
|
|
330
462
|
# to_h-bound literal field ; Shapes#decode_literal reads it back.
|
|
331
463
|
#
|
|
332
464
|
# nil stays nil rather than becoming "nil": absent is a real answer here,
|
|
333
465
|
# and the language's own field is optional.
|
|
466
|
+
#
|
|
467
|
+
# @param value [Object] the value to encode: `nil`, `Symbol`,
|
|
468
|
+
# `String`, `Hecks::StateRef`, `true`, `false`, `Integer`, `Float`,
|
|
469
|
+
# `Hash`, or `Array` (recursively)
|
|
470
|
+
# @return [String, nil] the self-describing wire spelling, or `nil`
|
|
471
|
+
# when `value` is `nil`
|
|
334
472
|
def encode_literal(value) = value.nil? ? nil : Literal.render(value)
|
|
335
473
|
|
|
336
474
|
# The way back out: an aggregate id becomes the type the IR spells. The
|
|
337
|
-
# id is a
|
|
475
|
+
# id is a join of chapter + name (Naming::IDENTITY_JOIN, the same join
|
|
338
476
|
# `points_at` built it with, not the "::" a real bluebook's own type
|
|
339
477
|
# names never carry) ; the wire format wants only the bare name.
|
|
478
|
+
#
|
|
479
|
+
# @param points_at_id [String] a target head's own id, as `points_at`
|
|
480
|
+
# built it
|
|
481
|
+
# @return [String] `"Reference<Name>"`, the bare target name
|
|
340
482
|
def reference_type(points_at_id) = "Reference<#{points_at_id.to_s.split(Naming::IDENTITY_JOIN).last}>"
|
|
341
483
|
|
|
342
484
|
# One value out of a row, named by the value object's field.
|
|
485
|
+
#
|
|
486
|
+
# @param row [Hash, Object] the row to read: a Hash, an object
|
|
487
|
+
# answering `field` directly, an object answering `to_h`, or a
|
|
488
|
+
# bare scalar
|
|
489
|
+
# @param field [Symbol] the field name to read
|
|
490
|
+
# @return [Object] `row[field]` for a Hash, `row.public_send(field)`
|
|
491
|
+
# when `row` answers `field` directly, `row.to_h[field]` for
|
|
492
|
+
# anything else to_h-able, or `row` itself for a bare scalar
|
|
343
493
|
def row_value(row, field)
|
|
344
|
-
# A Hash
|
|
494
|
+
# A Hash first. Hash answers to `key` (Hash#key(value)) and to `value` on
|
|
345
495
|
# some rows, so asking respond_to? before checking for a Hash reads a
|
|
346
496
|
# member pair through entirely the wrong method.
|
|
347
497
|
return row[field] if row.is_a?(Hash)
|
|
348
498
|
return row.public_send(field) if row.respond_to?(field)
|
|
349
|
-
# A Struct answers to [] but
|
|
499
|
+
# A Struct answers to [] but raises for a member it does not have, so it
|
|
350
500
|
# is read through to_h — a field the row simply lacks reads as absent.
|
|
351
501
|
return row.to_h[field] if row.respond_to?(:to_h) && !row.is_a?(String)
|
|
352
502
|
|
|
353
|
-
# A bare scalar row — `emits` is a list of event
|
|
503
|
+
# A bare scalar row — `emits` is a list of event names, and the
|
|
354
504
|
# Announcement value object has to call that string something.
|
|
355
505
|
row
|
|
356
506
|
end
|