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
|
@@ -1,55 +1,77 @@
|
|
|
1
|
+
require_relative "bootstrap_table"
|
|
2
|
+
|
|
1
3
|
module Hecks
|
|
2
4
|
module Bluebook
|
|
3
5
|
module DSL
|
|
4
|
-
#
|
|
6
|
+
# The three resolution primitives the S10 given/invariant family's
|
|
5
7
|
# own "declared once, referenced by name" mechanism (ADR 0025)
|
|
6
8
|
# reduces to, at every scope this language has grown one so far
|
|
7
9
|
# (a command referencing its owner or a sibling piece's entity-wide
|
|
8
10
|
# pool; an aggregate referencing another aggregate chapter-wide; a
|
|
9
11
|
# value object referencing a sibling value object on the same
|
|
10
|
-
# aggregate) — extracted here, once, so the
|
|
12
|
+
# aggregate) — extracted here, once, so the next scope this family
|
|
11
13
|
# widens to (there will be one — see docs/resolution-rules/
|
|
12
14
|
# chapter-given.md's own "Known limitations" for two already named)
|
|
13
15
|
# reuses one of these three shapes instead of a fourth hand-written
|
|
14
16
|
# near-duplicate resolver.
|
|
15
17
|
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
18
|
+
# ## Not one unified algorithm
|
|
19
|
+
#
|
|
20
|
+
# A real design question this file answers directly: the three existing
|
|
21
|
+
# resolvers are not superficially different, they are structurally different (a
|
|
22
|
+
# multi-pool fallback chain; one pool keyed by declaring owner,
|
|
23
|
+
# needing disambiguation; a live scan over already-built sibling
|
|
21
24
|
# objects with no separate pool at all) — forcing them into one
|
|
22
25
|
# shape would be a real behavior change (see `#lookup`, below, for
|
|
23
26
|
# which construct uses which), not the pure internal refactor this
|
|
24
|
-
# module is. `build_rule` is the one piece that
|
|
27
|
+
# module is. `build_rule` is the one piece that was genuinely
|
|
25
28
|
# identical across all 7 declaring methods (`given`×3,
|
|
26
29
|
# `invariant`×3, `ensures`×1) before this file existed — extract
|
|
27
30
|
# predicate source, refuse if extraction failed, build the struct.
|
|
28
31
|
#
|
|
29
|
-
#
|
|
32
|
+
# ## Resolution rules read off the grammar table, not a Ruby-only mirror
|
|
33
|
+
#
|
|
34
|
+
# `#lookup`/`#verify_resolves_via!` read which construct uses which
|
|
30
35
|
# primitive off the self-hosted grammar table itself
|
|
31
|
-
# (`Keyword#resolves_via`, `syntax.bluebook`)
|
|
32
|
-
#
|
|
33
|
-
# round ago) — so a real domain's own boot, not just `bundle exec
|
|
34
|
-
# rspec`, fails loudly the moment the table and this file's own
|
|
36
|
+
# (`Keyword#resolves_via`, `syntax.bluebook`), so a real domain's own boot, not just
|
|
37
|
+
# `bundle exec rspec`, fails loudly the moment the table and this file's own
|
|
35
38
|
# hand-written resolution methods disagree.
|
|
36
39
|
module RuleReference
|
|
37
40
|
module_function
|
|
38
41
|
|
|
42
|
+
# Extracts a predicate block's source and builds the rule struct that holds it,
|
|
43
|
+
# refusing when the source could not be read.
|
|
44
|
+
#
|
|
39
45
|
# `struct_class` is `Given` or `Invariant` (both `Struct.new(
|
|
40
|
-
# :description, :canonical, :predicate, keyword_init: true)` —
|
|
46
|
+
# :description, :canonical, :predicate, :ast, keyword_init: true)` —
|
|
41
47
|
# `Given` lives in command.rb, `Invariant` in value_object.rb).
|
|
42
|
-
# `owner_name`/`word` are
|
|
48
|
+
# `owner_name`/`word` are only for the refusal message's own
|
|
43
49
|
# wording. `extraction_failure` is the tail of that same
|
|
44
|
-
# message, and stays a
|
|
50
|
+
# message, and stays a required parameter rather than one
|
|
45
51
|
# hardcoded string on purpose — `given` ("its source could not
|
|
46
52
|
# be read, so no other runtime could ever evaluate it"),
|
|
47
53
|
# `invariant` ("it would be a rule the IR cannot carry"), and
|
|
48
54
|
# `ensures` ("a postcondition is carried as text, and this one
|
|
49
|
-
# has none") each already had their
|
|
55
|
+
# has none") each already had their own exact wording before
|
|
50
56
|
# this method existed; unifying them into one generic sentence
|
|
51
57
|
# would be a real (if small) behavior change this refactor is
|
|
52
58
|
# not making.
|
|
59
|
+
#
|
|
60
|
+
# @param struct_class [Class] the rule struct to build — `Bluebook::Given` or
|
|
61
|
+
# `Bluebook::Invariant`
|
|
62
|
+
# @param description [String] the rule's own description, as declared by the caller's
|
|
63
|
+
# own `given`/`invariant`/`ensures` word
|
|
64
|
+
# @param predicate [Proc] the rule's own body block, never called here — only its
|
|
65
|
+
# extracted source is used
|
|
66
|
+
# @param owner_name [String] the declaring construct's own name, for the refusal message
|
|
67
|
+
# @param word [String] the declaring word (`"given"`, `"invariant"`, or `"ensures"`),
|
|
68
|
+
# for the refusal message
|
|
69
|
+
# @param extraction_failure [String] the refusal message's own tail, naming what an
|
|
70
|
+
# unreadable predicate would mean for this particular word
|
|
71
|
+
# @return [Bluebook::Given, Bluebook::Invariant] the built rule struct, an instance of
|
|
72
|
+
# `struct_class`
|
|
73
|
+
# @raise [Bluebook::DSL::Malformed] if `predicate`'s source could not be extracted, or
|
|
74
|
+
# it matches against a pattern construct `Expression::AstJson::PatternSubset` refuses
|
|
53
75
|
def build_rule(struct_class, description, predicate, owner_name:, word:, extraction_failure:)
|
|
54
76
|
canonical = Ports::Extraction.canonical(predicate)
|
|
55
77
|
|
|
@@ -64,35 +86,45 @@ module Hecks
|
|
|
64
86
|
struct_class.new(description: description, canonical: canonical, predicate: predicate, ast: ast)
|
|
65
87
|
end
|
|
66
88
|
|
|
67
|
-
#
|
|
89
|
+
# Primitive 1 — an ordered chain of flat `Hash[description] =>
|
|
68
90
|
# Rule` pools, first match wins. `CommandBuilder#given`'s own
|
|
69
|
-
# two-pool shape (its
|
|
91
|
+
# two-pool shape (its own owner's `named_givens`, then a sibling
|
|
70
92
|
# piece's entity-wide pool) is this with a 2-element chain — a
|
|
71
93
|
# future single-pool bare reference is the same primitive with a
|
|
72
94
|
# 1-element chain, not a separate "just look in one hash" method.
|
|
95
|
+
# @param pools [Array<Hash{String => Bluebook::Given, Bluebook::Invariant}>] pools to
|
|
96
|
+
# search in order; the first pool holding `description` wins
|
|
97
|
+
# @param description [String] the rule's own description to find
|
|
98
|
+
# @return [Bluebook::Given, Bluebook::Invariant, nil] the matching rule, or `nil` if no
|
|
99
|
+
# pool has one
|
|
73
100
|
def resolve_hash_chain(pools, description)
|
|
74
101
|
pools.each { |pool| return pool[description] if pool.key?(description) }
|
|
75
102
|
nil
|
|
76
103
|
end
|
|
77
104
|
|
|
78
|
-
#
|
|
105
|
+
# Primitive 2 — one pool keyed by declaring owner,
|
|
79
106
|
# `Hash[description][owner] => Rule` — `AggregateBuilder#given`'s
|
|
80
107
|
# own chapter-wide shape, the only construct so far where the
|
|
81
|
-
#
|
|
108
|
+
# same description can mean two genuinely different predicates
|
|
82
109
|
# (docs/implemented/resolution-rules/chapter-given.md). Returns the full
|
|
83
|
-
# candidates Hash (0, 1, or many entries) — deliberately
|
|
110
|
+
# candidates Hash (0, 1, or many entries) — deliberately not
|
|
84
111
|
# raising here, so each caller keeps its own exact refusal
|
|
85
112
|
# wording for "none," "ambiguous," and "declared_by: named the
|
|
86
113
|
# wrong owner" rather than one generic message papering over all
|
|
87
114
|
# three.
|
|
115
|
+
# @param pool [Hash{String => Hash{String => Bluebook::Given, Bluebook::Invariant}}]
|
|
116
|
+
# descriptions mapped to their own candidates, each keyed by declaring owner
|
|
117
|
+
# @param description [String] the rule's own description to find
|
|
118
|
+
# @return [Hash{String => Bluebook::Given, Bluebook::Invariant}] the candidates for
|
|
119
|
+
# `description`, keyed by declaring owner; empty when none exist
|
|
88
120
|
def resolve_owner_keyed(pool, description)
|
|
89
121
|
pool[description] || {}
|
|
90
122
|
end
|
|
91
123
|
|
|
92
|
-
#
|
|
124
|
+
# Primitive 3 — a live scan over already-built sibling objects'
|
|
93
125
|
# own collections, not a separately-maintained pool at all —
|
|
94
126
|
# `ValueObjectBuilder#invariant`'s own shape: every sibling value
|
|
95
|
-
# object on the same aggregate has
|
|
127
|
+
# object on the same aggregate has already been built by the time
|
|
96
128
|
# a later one references back (declaration order, the same
|
|
97
129
|
# constraint every scope in this family carries), so there is
|
|
98
130
|
# nothing to write through — just read their own already-declared
|
|
@@ -100,42 +132,57 @@ module Hecks
|
|
|
100
132
|
# sibling (`:invariants` today; kept a parameter, not hardcoded,
|
|
101
133
|
# since a future sibling-scan scope might reference a different
|
|
102
134
|
# collection).
|
|
135
|
+
# @param siblings [Array<Object>] the already-built sibling objects to scan; each must
|
|
136
|
+
# respond to `reader`
|
|
137
|
+
# @param description [String] the rule's own description to find
|
|
138
|
+
# @param reader [Symbol] the method to call on each sibling to read its own rule
|
|
139
|
+
# collection, such as `:invariants`
|
|
140
|
+
# @return [Bluebook::Given, Bluebook::Invariant, nil] the matching rule, or `nil` if no
|
|
141
|
+
# sibling declares one
|
|
103
142
|
def resolve_sibling_scan(siblings, description, reader:)
|
|
104
143
|
siblings.flat_map { |sibling| sibling.public_send(reader) }
|
|
105
144
|
.find { |rule| rule.description == description }
|
|
106
145
|
end
|
|
107
146
|
|
|
108
|
-
#
|
|
109
|
-
# Hash (that
|
|
147
|
+
# **Which construct uses which primitive** — no longer a Ruby-only
|
|
148
|
+
# Hash (that was this constant's own shape, one round ago): the
|
|
110
149
|
# user's own correction — "my goal is that if they read the same
|
|
111
150
|
# table they behave identically" — means a table only Ruby ever
|
|
112
151
|
# reads cannot deliver that, no matter how faithfully it is
|
|
113
152
|
# cross-checked afterward. `Keyword#resolves_via`/`#disambiguator`
|
|
114
|
-
# (self-hosted, `syntax.bluebook`) is the
|
|
115
|
-
#
|
|
153
|
+
# (self-hosted, `syntax.bluebook`) is the real table now — the
|
|
154
|
+
# same generated data `rust/parser/src/keywords.rs` is generated
|
|
116
155
|
# from (`bin/project_parser_table`). `#lookup` reads it live.
|
|
117
156
|
#
|
|
118
|
-
#
|
|
157
|
+
# **The one unavoidable exception**: the meta-domain's own bootstrap
|
|
119
158
|
# (`MetaValidator.load_grammar_into`) dispatches `given`/
|
|
120
|
-
# `invariant` on
|
|
159
|
+
# `invariant` on itself 61 times while building the very grammar
|
|
121
160
|
# table that would answer "how does given/Aggregate resolve" —
|
|
122
161
|
# `MetaValidator.grammar_registry`/`SyntaxBoot.call` are not
|
|
123
|
-
# ready yet, and cannot be made ready without
|
|
162
|
+
# ready yet, and cannot be made ready without already having
|
|
124
163
|
# resolved a `given` somewhere upstream. `MetaValidator.
|
|
125
|
-
# bootstrapping?` is the
|
|
164
|
+
# bootstrapping?` is the same guard `MetaValidator.call` (the
|
|
126
165
|
# judge) already uses to skip self-judging during this exact
|
|
127
166
|
# window — `#lookup` uses it too, falling back to
|
|
128
|
-
# `BOOTSTRAP_FALLBACK` (below)
|
|
167
|
+
# `BOOTSTRAP_FALLBACK` (below) only while it's true. Every real
|
|
129
168
|
# domain (banking, pizzas, compliance, any future one) boots
|
|
130
|
-
#
|
|
169
|
+
# after `grammar_registry` is fully built and memoized, so reads
|
|
131
170
|
# the real table, every time, no exception.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
171
|
+
#
|
|
172
|
+
# No longer kept in sync by hand — the same `resolves_via`/
|
|
173
|
+
# `disambiguator` columns, projected ahead of time into the
|
|
174
|
+
# committed lib/hecks/bluebook/dsl/bootstrap_table.rb
|
|
175
|
+
# (bin/project_bootstrap_table, pinned by spec/bootstrap_table_spec.rb).
|
|
176
|
+
BOOTSTRAP_FALLBACK = BootstrapTable::RESOLVES
|
|
138
177
|
|
|
178
|
+
# Reads how a (word, context) pair resolves its rule references, off the self-hosted
|
|
179
|
+
# grammar table itself (or, while that table is still booting, the projected fallback).
|
|
180
|
+
#
|
|
181
|
+
# @param word [String] the DSL word, such as `"given"` or `"invariant"`
|
|
182
|
+
# @param context [String] the grammar context, such as `"Aggregate"`
|
|
183
|
+
# @return [Hash{Symbol => String, nil}] `:resolves_via` and `:disambiguator` for this
|
|
184
|
+
# (word, context) pair, `nil`-valued when the row leaves either blank, or `{}` if no row
|
|
185
|
+
# matches at all
|
|
139
186
|
def lookup(word, context)
|
|
140
187
|
if MetaValidator.bootstrapping?
|
|
141
188
|
BOOTSTRAP_FALLBACK[[word, context]] || {}
|
|
@@ -149,17 +196,24 @@ module Hecks
|
|
|
149
196
|
end
|
|
150
197
|
end
|
|
151
198
|
|
|
152
|
-
# A
|
|
199
|
+
# A live cross-check, not a spec-only one — every real domain's
|
|
153
200
|
# own boot (not just `bundle exec rspec`) now genuinely fails
|
|
154
201
|
# loudly if a construct's own hand-written resolution method
|
|
155
202
|
# ever disagrees with what the self-hosted grammar table claims
|
|
156
203
|
# for it. Each of the three `reference_named_*` methods below
|
|
157
|
-
# calls this
|
|
204
|
+
# calls this first, naming the primitive it is about to use —
|
|
158
205
|
# if `syntax.bluebook`'s own `resolves_via` for this exact
|
|
159
206
|
# (word, context) pair ever names something else, this is a
|
|
160
207
|
# real drift between the language's own self-description and
|
|
161
|
-
# its own implementation, caught at the next boot of
|
|
208
|
+
# its own implementation, caught at the next boot of anything,
|
|
162
209
|
# not just the next `rspec` run.
|
|
210
|
+
# @param word [String] the DSL word, such as `"given"` or `"invariant"`
|
|
211
|
+
# @param context [String] the grammar context, such as `"Aggregate"`
|
|
212
|
+
# @param expected_primitive [String] the resolution primitive's own name the caller is
|
|
213
|
+
# about to use, such as `"hash_chain"`, `"owner_keyed"`, or `"sibling_scan"`
|
|
214
|
+
# @return [void]
|
|
215
|
+
# @raise [RuntimeError] if `syntax.bluebook`'s own `resolves_via` for this (word, context)
|
|
216
|
+
# pair names something other than `expected_primitive`
|
|
163
217
|
def verify_resolves_via!(word, context, expected_primitive)
|
|
164
218
|
actual = lookup(word, context)[:resolves_via]
|
|
165
219
|
return if actual == expected_primitive
|
|
@@ -13,6 +13,9 @@ module Hecks
|
|
|
13
13
|
|
|
14
14
|
include WordGate
|
|
15
15
|
|
|
16
|
+
# @param name [String, Symbol] the aggregate's name in the destination era
|
|
17
|
+
# @param was [String, Symbol, nil] the aggregate's earlier name, when renamed
|
|
18
|
+
# @raise [Bluebook::DSL::Malformed] if `name` is empty
|
|
16
19
|
def initialize(name, was: nil)
|
|
17
20
|
raise Malformed, "an aggregate translation needs a name" if name.to_s.empty?
|
|
18
21
|
|
|
@@ -28,14 +31,23 @@ module Hecks
|
|
|
28
31
|
@backfills = []
|
|
29
32
|
end
|
|
30
33
|
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
34
|
+
# Declares a field rename with no other change: same path, new name.
|
|
35
|
+
#
|
|
36
|
+
# Answers the `rename` word (and, via the same table rows, its
|
|
37
|
+
# siblings `move`/`convert`/`retype`/`compute`/`rekey`/`backfill`
|
|
38
|
+
# below) through the table's `calls:` column — item #13's full
|
|
39
|
+
# metaprogrammed dispatch (slice 4c). Each is carried in
|
|
40
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK` like every other
|
|
41
|
+
# `calls:`-routed word, but none of their rows are ever consulted
|
|
42
|
+
# during real bootstrap: `translation.bluebook` describes its own
|
|
43
|
+
# structure with aggregate/entity/attribute, never with these —
|
|
44
|
+
# they're words for real, user-authored `.translation` files
|
|
45
|
+
# only, loaded after the grammar table already exists.
|
|
46
|
+
#
|
|
47
|
+
# @param old_name [Symbol, String] the field's name in the held era
|
|
48
|
+
# @param to [Symbol, String] the field's name in the destination era
|
|
49
|
+
# @return [void]
|
|
50
|
+
# @raise [Bluebook::DSL::Malformed] if `old_name` or `to` is empty
|
|
39
51
|
def rename_impl(old_name, to:)
|
|
40
52
|
raise Malformed, "a rename needs a source name" if old_name.to_s.empty?
|
|
41
53
|
raise Malformed, "a rename needs a destination name (to:)" if to.to_s.empty?
|
|
@@ -43,6 +55,13 @@ module Hecks
|
|
|
43
55
|
@renames[old_name.to_sym] = to.to_sym
|
|
44
56
|
end
|
|
45
57
|
|
|
58
|
+
# Declares a field moved to a different path, name unchanged.
|
|
59
|
+
#
|
|
60
|
+
# @param old_path [String, Symbol] the field's path in the held era; dotted reaches a
|
|
61
|
+
# value-object member
|
|
62
|
+
# @param to [String, Symbol] the field's path in the destination era
|
|
63
|
+
# @return [Array<Bluebook::TranslationMove>] every move declared so far, this one last
|
|
64
|
+
# @raise [Bluebook::DSL::Malformed] if `old_path` or `to` is empty
|
|
46
65
|
def move_impl(old_path, to:)
|
|
47
66
|
raise Malformed, "a move needs a destination path (to:)" if to.to_s.empty?
|
|
48
67
|
raise Malformed, "a move needs a source path" if old_path.to_s.empty?
|
|
@@ -50,10 +69,21 @@ module Hecks
|
|
|
50
69
|
@moves << TranslationMove.new(old_path.to_s, to.to_s)
|
|
51
70
|
end
|
|
52
71
|
|
|
72
|
+
# Declares an exhaustive value-to-value mapping for a field with nothing structural in
|
|
73
|
+
# common with its replacement.
|
|
74
|
+
#
|
|
53
75
|
# A value with nothing structural in common with its replacement
|
|
54
76
|
# — declared as an exhaustive table, not computed, so every value
|
|
55
77
|
# that can appear in old data has a named destination. Paths
|
|
56
78
|
# follow `move`'s convention: dotted reaches a value-object member.
|
|
79
|
+
#
|
|
80
|
+
# @param old_path [String, Symbol] the field's path in the held era
|
|
81
|
+
# @param to [String, Symbol] the field's path in the destination era
|
|
82
|
+
# @param values [Hash] every old value mapped to its destination value
|
|
83
|
+
# @return [Array<Bluebook::TranslationConvert>] every convert declared so far, this one
|
|
84
|
+
# last
|
|
85
|
+
# @raise [Bluebook::DSL::Malformed] if `old_path` or `to` is empty, or `values` is nil
|
|
86
|
+
# or empty
|
|
57
87
|
def convert_impl(old_path, to:, values:)
|
|
58
88
|
raise Malformed, "a convert needs a destination path (to:)" if to.to_s.empty?
|
|
59
89
|
raise Malformed, "a convert needs a source path" if old_path.to_s.empty?
|
|
@@ -70,10 +100,18 @@ module Hecks
|
|
|
70
100
|
# coerce-and-append with nothing else, now executed by
|
|
71
101
|
# `GenericDispatch`.
|
|
72
102
|
|
|
103
|
+
# Declares that a type's name changed while its member structure stayed the same.
|
|
104
|
+
#
|
|
73
105
|
# A value object's or entity's own type name changed, member
|
|
74
106
|
# structure unchanged. The stored data never carries the type
|
|
75
107
|
# name, so nothing moves — this declares that the pair of names
|
|
76
108
|
# means the same shape, which is what lets the era diff accept it.
|
|
109
|
+
#
|
|
110
|
+
# @param old_type [String, Symbol] the type's name in the held era
|
|
111
|
+
# @param to [String, Symbol] the type's name in the destination era
|
|
112
|
+
# @return [Array<Bluebook::TranslationRetype>] every retype declared so far, this one
|
|
113
|
+
# last
|
|
114
|
+
# @raise [Bluebook::DSL::Malformed] if `old_type` or `to` is empty
|
|
77
115
|
def retype_impl(old_type, to:)
|
|
78
116
|
raise Malformed, "a retype needs a source type name" if old_type.to_s.empty?
|
|
79
117
|
raise Malformed, "a retype needs a destination type name (to:)" if to.to_s.empty?
|
|
@@ -81,10 +119,19 @@ module Hecks
|
|
|
81
119
|
@retypes << TranslationRetype.new(old_type.to_s, to.to_s)
|
|
82
120
|
end
|
|
83
121
|
|
|
122
|
+
# Declares a field computed by a hand-written Postgres SQL expression.
|
|
123
|
+
#
|
|
84
124
|
# A computed transform whose only implementation is the SQL
|
|
85
125
|
# expression itself — Postgres-only by construction. The scaffold
|
|
86
126
|
# never proposes one; a human writes it, and the audit's
|
|
87
127
|
# human-sampled review is its only verification.
|
|
128
|
+
#
|
|
129
|
+
# @param old_path [String, Symbol] the source field's path in the held era
|
|
130
|
+
# @param to [String, Symbol] the field's path in the destination era
|
|
131
|
+
# @param sql [String] the Postgres SQL expression computing the destination value
|
|
132
|
+
# @return [Array<Bluebook::TranslationCompute>] every compute declared so far, this one
|
|
133
|
+
# last
|
|
134
|
+
# @raise [Bluebook::DSL::Malformed] if `old_path`, `to`, or `sql` is empty
|
|
88
135
|
def compute_impl(old_path, to:, sql:)
|
|
89
136
|
raise Malformed, "a compute needs a destination path (to:)" if to.to_s.empty?
|
|
90
137
|
raise Malformed, "a compute needs a source path" if old_path.to_s.empty?
|
|
@@ -93,7 +140,10 @@ module Hecks
|
|
|
93
140
|
@computes << TranslationCompute.new(old_path.to_s, to.to_s, sql.to_s)
|
|
94
141
|
end
|
|
95
142
|
|
|
96
|
-
#
|
|
143
|
+
# Declares a hand-written Postgres SQL expression that recomputes the aggregate's own
|
|
144
|
+
# identity.
|
|
145
|
+
#
|
|
146
|
+
# The aggregate's own identity, changing what it's computed from —
|
|
97
147
|
# not a field crossing a boundary (`move`), not a value's own
|
|
98
148
|
# transform (`compute`): the record's key. No path arguments,
|
|
99
149
|
# unlike every rule above — nothing is consumed from or moved into
|
|
@@ -101,14 +151,21 @@ module Hecks
|
|
|
101
151
|
# SQL-only, Postgres-only, human-reviewed-sample-is-the-only-
|
|
102
152
|
# verification shape `compute` already has, and for the same
|
|
103
153
|
# reason: there is nothing in-process to check this against.
|
|
154
|
+
#
|
|
155
|
+
# @param sql [String] the Postgres SQL expression computing the destination identity
|
|
156
|
+
# @return [Array<Bluebook::TranslationRekey>] every rekey declared so far, this one last
|
|
157
|
+
# @raise [Bluebook::DSL::Malformed] if `sql` is empty
|
|
104
158
|
def rekey_impl(sql:)
|
|
105
159
|
raise Malformed, "a rekey needs its sql: expression" if sql.to_s.empty?
|
|
106
160
|
|
|
107
161
|
@rekeys << TranslationRekey.new(sql.to_s)
|
|
108
162
|
end
|
|
109
163
|
|
|
110
|
-
#
|
|
111
|
-
#
|
|
164
|
+
# Declares a newly added, required attribute and the default existing records read
|
|
165
|
+
# until a real value is written.
|
|
166
|
+
#
|
|
167
|
+
# A newly added, required attribute — the addition-side sibling of
|
|
168
|
+
# `drop`. Nothing to rename, move, or convert from, since old data
|
|
112
169
|
# never held this field at all; `default` is what an existing
|
|
113
170
|
# record reads until the next command against it writes a real
|
|
114
171
|
# value. Adapter-agnostic, unlike `compute` — applied the same
|
|
@@ -118,6 +175,12 @@ module Hecks
|
|
|
118
175
|
# `EraGuard.refuse_unsafe_addition!` asks for when a non-optional
|
|
119
176
|
# attribute with no default: could leave an existing record with
|
|
120
177
|
# the field genuinely absent.
|
|
178
|
+
#
|
|
179
|
+
# @param name [String, Symbol] the new attribute's name
|
|
180
|
+
# @param default [Object] the value an existing record reads until it is written for real
|
|
181
|
+
# @return [Array<Bluebook::TranslationBackfill>] every backfill declared so far, this
|
|
182
|
+
# one last
|
|
183
|
+
# @raise [Bluebook::DSL::Malformed] if `name` is empty or `default` is nil
|
|
121
184
|
def backfill_impl(name, default:)
|
|
122
185
|
raise Malformed, "a backfill needs a name" if name.to_s.empty?
|
|
123
186
|
raise Malformed, "a backfill needs a default: value" if default.nil?
|
|
@@ -125,33 +188,46 @@ module Hecks
|
|
|
125
188
|
@backfills << TranslationBackfill.new(name.to_sym, default)
|
|
126
189
|
end
|
|
127
190
|
|
|
191
|
+
# Always refuses to boot: marks a field the scaffold could not decide a rule for.
|
|
192
|
+
#
|
|
128
193
|
# The scaffold writes this where it cannot decide; a file carrying
|
|
129
194
|
# one can only boot into this refusal — never a guess.
|
|
130
195
|
#
|
|
131
|
-
#
|
|
196
|
+
# Answers the `unresolved` word through the table's `calls:`
|
|
197
|
+
# column — item #13's full metaprogrammed
|
|
132
198
|
# dispatch (slice 4). Builds its own message with real branching
|
|
133
199
|
# (empty vs. named candidates, a special :identity case), not a
|
|
134
|
-
# fixed string a boolean `refuses:` flag could express
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
# bootstrap
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
#
|
|
200
|
+
# fixed string a boolean `refuses:` flag could express. Carried in
|
|
201
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK` like every other
|
|
202
|
+
# `calls:`-routed word, but that row is never consulted during
|
|
203
|
+
# real bootstrap (checked directly, not assumed):
|
|
204
|
+
# translation.bluebook (loaded during bootstrap, to describe the
|
|
205
|
+
# translation DSL itself) never writes `unresolved` — that word is
|
|
206
|
+
# only ever used by real, user-authored `.translation` files,
|
|
207
|
+
# loaded well after the grammar table already exists.
|
|
208
|
+
#
|
|
209
|
+
# @param name [String, Symbol] the unresolved field's name, or `:identity` for an
|
|
210
|
+
# unresolved identity change
|
|
211
|
+
# @param candidates [Array<String, Symbol>] paths the scaffold considered but could not
|
|
212
|
+
# choose between; empty when it found none
|
|
213
|
+
# @return [void]
|
|
214
|
+
# @raise [Bluebook::DSL::Malformed] always
|
|
142
215
|
def unresolved_impl(name, candidates: [])
|
|
143
216
|
raise Malformed, unresolved_message(name, candidates)
|
|
144
217
|
end
|
|
145
218
|
|
|
146
|
-
# `method_missing`/`respond_to_missing?`
|
|
147
|
-
#
|
|
148
|
-
#
|
|
149
|
-
#
|
|
150
|
-
#
|
|
151
|
-
#
|
|
152
|
-
# elsewhere in the grammar but not in this context still gets
|
|
153
|
-
# richer
|
|
219
|
+
# `method_missing`/`respond_to_missing?` answer off the self-hosted
|
|
220
|
+
# grammar table (via the `include`d `WordGate`, above), giving a
|
|
221
|
+
# richer, table-driven "must be rename, move, convert, ..." refusal
|
|
222
|
+
# on a genuinely undefined call than a hand-typed list could —
|
|
223
|
+
# the exact "hardcoded legal-word list" this whole arc's item #13
|
|
224
|
+
# exists to close. A word admitted
|
|
225
|
+
# elsewhere in the grammar but not in this context still gets that
|
|
226
|
+
# richer refusal.
|
|
154
227
|
|
|
228
|
+
# Assembles the declared rules into a `TranslationAggregate`.
|
|
229
|
+
#
|
|
230
|
+
# @return [Bluebook::TranslationAggregate] the built per-aggregate translation
|
|
155
231
|
def build
|
|
156
232
|
TranslationAggregate.new(
|
|
157
233
|
name: @name, was: @was, renames: @renames, moves: @moves, converts: @converts,
|
|
@@ -178,7 +254,7 @@ module Hecks
|
|
|
178
254
|
path.to_s.include?(".") ? path.to_s.inspect : ":#{path}"
|
|
179
255
|
end
|
|
180
256
|
|
|
181
|
-
#
|
|
257
|
+
# The scaffold's own hint for the one drift it can detect but never
|
|
182
258
|
# resolve on its own — an aggregate's `identified_by` changed. Not
|
|
183
259
|
# a field to rename/move/drop, so none of the ordinary hints fit;
|
|
184
260
|
# `coverage_check.rb#check_identity_unchanged!` is the real gate,
|
|
@@ -199,6 +275,10 @@ module Hecks
|
|
|
199
275
|
|
|
200
276
|
include WordGate
|
|
201
277
|
|
|
278
|
+
# @param domain [String, Symbol] the domain this translation carries forward
|
|
279
|
+
# @param from [String, Symbol] the origin era
|
|
280
|
+
# @param to [String, Symbol] the destination era
|
|
281
|
+
# @raise [Bluebook::DSL::Malformed] if `domain`, `from`, or `to` is empty
|
|
202
282
|
def initialize(domain, from:, to:)
|
|
203
283
|
raise Malformed, "a translation names no domain" if domain.to_s.empty?
|
|
204
284
|
raise Malformed, "#{domain}'s translation says nothing about its origin era (from:)" if from.to_s.empty?
|
|
@@ -211,38 +291,64 @@ module Hecks
|
|
|
211
291
|
@retired = []
|
|
212
292
|
end
|
|
213
293
|
|
|
214
|
-
#
|
|
294
|
+
# Declares one aggregate's own translation rules.
|
|
295
|
+
#
|
|
296
|
+
# Answers the `aggregate` word through the table's `calls:`
|
|
297
|
+
# column — item #13's full metaprogrammed
|
|
215
298
|
# dispatch (slice 4c). Not bootstrap-reachable — this "Translation"
|
|
216
299
|
# -context `aggregate` (opens a TranslationAggregateBuilder) is a
|
|
217
300
|
# different (context, word) pair than "Bluebook"-context
|
|
218
301
|
# `aggregate` (the one translation.bluebook itself is described
|
|
219
|
-
# with), so it
|
|
302
|
+
# with), so it never describes the language's own
|
|
220
303
|
# translation chapter.
|
|
304
|
+
#
|
|
305
|
+
# @param name [String, Symbol] the aggregate's name in the destination era
|
|
306
|
+
# @param was [String, Symbol, nil] the aggregate's earlier name, when renamed
|
|
307
|
+
# @yield the aggregate's own translation body, evaluated against a
|
|
308
|
+
# `TranslationAggregateBuilder`
|
|
309
|
+
# @return [Array<Bluebook::TranslationAggregate>] every aggregate translation declared
|
|
310
|
+
# so far, this one last
|
|
311
|
+
# @raise [Bluebook::DSL::Malformed] if `name` is empty, or any rule in the body fails
|
|
312
|
+
# its own checks
|
|
221
313
|
def aggregate_impl(name, was: nil, &block)
|
|
222
314
|
builder = TranslationAggregateBuilder.new(name, was: was)
|
|
223
315
|
builder.instance_eval(&block) if block
|
|
224
316
|
@aggregates << builder.build
|
|
225
317
|
end
|
|
226
318
|
|
|
227
|
-
# `retired`
|
|
228
|
-
#
|
|
229
|
-
#
|
|
230
|
-
#
|
|
231
|
-
#
|
|
232
|
-
#
|
|
233
|
-
|
|
234
|
-
# `method_missing`/`respond_to_missing?`
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
# retired aggregates" message.
|
|
239
|
-
|
|
319
|
+
# `retired` (an aggregate that is gone outright, not renamed — the
|
|
320
|
+
# deliberate alternative to a bogus `was:` claim on an unrelated
|
|
321
|
+
# aggregate, the same shape `TranslationAggregateBuilder#drop` is)
|
|
322
|
+
# is executed straight off the grammar table by `GenericDispatch` —
|
|
323
|
+
# item #13's full metaprogrammed dispatch, slice 2 (whole-project
|
|
324
|
+
# table-unification survey) — so no hand-written method answers it here.
|
|
325
|
+
|
|
326
|
+
# `method_missing`/`respond_to_missing?` answer off the self-hosted
|
|
327
|
+
# grammar table (via the `include`d `WordGate`, above, the same
|
|
328
|
+
# mechanism `TranslationAggregateBuilder`'s own comment describes
|
|
329
|
+
# one level up) instead of a hand-typed "it declares aggregate
|
|
330
|
+
# blocks and retired aggregates" message.
|
|
331
|
+
|
|
332
|
+
# Assembles the declared era pair, aggregates and retirements, judged by the translation
|
|
333
|
+
# language.
|
|
334
|
+
#
|
|
335
|
+
# @return [Bluebook::Translation] the translation, returned once the language accepts it
|
|
336
|
+
# @raise [Bluebook::DSL::Malformed] if the translation language refuses the declaration
|
|
240
337
|
def build
|
|
241
338
|
MetaValidator.call_translation(
|
|
242
339
|
Translation.new(domain: @domain, from: @from, to: @to, aggregates: @aggregates, retired: @retired)
|
|
243
340
|
)
|
|
244
341
|
end
|
|
245
342
|
|
|
343
|
+
# Evaluates a `.translation` file's top-level block against a fresh builder.
|
|
344
|
+
#
|
|
345
|
+
# @param domain [String, Symbol] the domain this translation carries forward
|
|
346
|
+
# @param from [String, Symbol] the origin era
|
|
347
|
+
# @param to [String, Symbol] the destination era
|
|
348
|
+
# @yield the translation body, evaluated with the builder as `self`; may be omitted
|
|
349
|
+
# @return [Bluebook::Translation] the judged translation
|
|
350
|
+
# @raise [Bluebook::DSL::Malformed] if the body fails any check, or the translation
|
|
351
|
+
# language refuses the declaration
|
|
246
352
|
def self.build(domain, from:, to:, &block)
|
|
247
353
|
builder = new(domain, from: from, to: to)
|
|
248
354
|
builder.instance_eval(&block) if block
|