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
|
@@ -16,6 +16,10 @@ module Hecks
|
|
|
16
16
|
include RuleReference
|
|
17
17
|
include WordGate
|
|
18
18
|
|
|
19
|
+
# @param name [String] the value object's name, as written after `value_object`
|
|
20
|
+
# @param owner_value_objects [Array<Bluebook::ValueObject>] the owning aggregate's other
|
|
21
|
+
# value objects, built so far, that this one's `invariant` references may resolve
|
|
22
|
+
# against
|
|
19
23
|
def initialize(name, owner_value_objects: [])
|
|
20
24
|
@name = name
|
|
21
25
|
@invariants = []
|
|
@@ -23,7 +27,7 @@ module Hecks
|
|
|
23
27
|
@owner_value_objects = owner_value_objects
|
|
24
28
|
end
|
|
25
29
|
|
|
26
|
-
#
|
|
30
|
+
# The wrapper block is gone (ADR 0025, "Attributes" — "closed sets
|
|
27
31
|
# lose the wrapper block"). `member` lines are bare now, written
|
|
28
32
|
# directly in the `value_object` body with no `one_of do ... end`
|
|
29
33
|
# around them — `build`'s own `closed_set: @closed_set ||
|
|
@@ -39,24 +43,35 @@ module Hecks
|
|
|
39
43
|
# use the inline form on one of its own attributes. One `one_of`
|
|
40
44
|
# method now, not two.
|
|
41
45
|
#
|
|
42
|
-
#
|
|
46
|
+
# Legacy under shadow-parsing (S0a's own bridge) — frozen era text
|
|
43
47
|
# still writes the block form (2 locations in
|
|
44
48
|
# `examples/banking/data/eras/banking/1.bluebook`, duplicated once
|
|
45
49
|
# more in that era's own archive copy), so `EraGuard.shadow_parse`
|
|
46
50
|
# still needs to read it. `block_given?` is what tells the two
|
|
47
51
|
# calling shapes apart: the type-position form
|
|
48
52
|
# (`one_of("a", "b")`) never passes a block, only the wrapper does.
|
|
49
|
-
#
|
|
50
|
-
#
|
|
53
|
+
# Declares this value object as a closed set, either from bare values or (under
|
|
54
|
+
# shadow-parsing) a legacy `do ... end` wrapper of `member` lines.
|
|
55
|
+
#
|
|
56
|
+
# Answers the `one_of` word through the table's `calls:` column —
|
|
57
|
+
# item #13's full metaprogrammed dispatch
|
|
58
|
+
# (slice 5). Same name as `AttributeCollector#one_of_impl`'s own
|
|
51
59
|
# — required for the `super(*values)` call below to keep
|
|
52
60
|
# resolving; see that method's own comment. Reached directly
|
|
53
61
|
# through its own "ValueObject"-context Keyword row (the
|
|
54
62
|
# block-wrapper form has its own row, distinct from "Type"'s),
|
|
55
|
-
# not through the Type-position fallback this word's
|
|
63
|
+
# not through the Type-position fallback this word's other
|
|
56
64
|
# context uses.
|
|
65
|
+
#
|
|
66
|
+
# @param values [Array<String, Symbol>] permitted values, given bare (no block); at
|
|
67
|
+
# least one is required
|
|
68
|
+
# @yield a legacy `member` wrapper body; only read under shadow-parsing
|
|
69
|
+
# @return [void]
|
|
70
|
+
# @raise [Bluebook::DSL::Malformed] if given no values and no block, or a block outside
|
|
71
|
+
# shadow-parsing
|
|
57
72
|
def one_of_impl(*values, &block)
|
|
58
73
|
unless block
|
|
59
|
-
#
|
|
74
|
+
# No values, no block is the scalar spelling — nonsensical, not
|
|
60
75
|
# merely inert: it names a closed set with nothing in it. The
|
|
61
76
|
# original block form caught this by a side effect (`@closed_set
|
|
62
77
|
# = true` ran unconditionally, before the `if block`), and this
|
|
@@ -83,20 +98,27 @@ module Hecks
|
|
|
83
98
|
instance_eval(&block)
|
|
84
99
|
end
|
|
85
100
|
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
101
|
+
# Declares one member row of a closed set, one value per attribute.
|
|
102
|
+
#
|
|
103
|
+
# Answers the `member` word through the table's `calls:` column —
|
|
104
|
+
# item #13's full metaprogrammed dispatch (slice 4c). Bootstrap-reachable, in
|
|
105
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
106
|
+
#
|
|
107
|
+
# @param fields [Hash{Symbol => Object}] each declared attribute's name mapped to this
|
|
108
|
+
# member's value for it
|
|
109
|
+
# @return [Array<Hash{Symbol => Object}>] every member declared so far, this one last
|
|
110
|
+
# @raise [Bluebook::DSL::Malformed] if `fields` is empty
|
|
89
111
|
def member_impl(**fields)
|
|
90
112
|
raise Malformed, "#{@name} declared an empty member" if fields.empty?
|
|
91
113
|
|
|
92
114
|
@members << fields
|
|
93
115
|
end
|
|
94
116
|
|
|
95
|
-
#
|
|
117
|
+
# No block is a reference, not a fresh declaration — the same
|
|
96
118
|
# move S10 already made for `CommandBuilder#given` (ADR 0025,
|
|
97
119
|
# "a precondition shared across commands is declared once... a
|
|
98
120
|
# command references it by name"), one level over: a rule
|
|
99
|
-
# shared across
|
|
121
|
+
# shared across sibling value objects on the same aggregate,
|
|
100
122
|
# declared once, on the first one to need it. Real, live
|
|
101
123
|
# redundancy this closes: `Account`'s own `Money`/`PositiveMoney`
|
|
102
124
|
# both declared `invariant("a currency is a three-letter code")
|
|
@@ -105,9 +127,20 @@ module Hecks
|
|
|
105
127
|
# the referencing value object's own build time, against
|
|
106
128
|
# whatever sibling value objects the aggregate has already
|
|
107
129
|
# built, the same ordering rule `given` carries.
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
130
|
+
# Declares a rule this value object's own value must satisfy, or references one a sibling
|
|
131
|
+
# value object on the same aggregate already declared.
|
|
132
|
+
#
|
|
133
|
+
# Answers the `invariant` word through the table's `calls:` column —
|
|
134
|
+
# item #13's full metaprogrammed dispatch (slice 4b). Bootstrap-reachable, in
|
|
135
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
136
|
+
#
|
|
137
|
+
# @param description [String] the rule's description; also the name a sibling value
|
|
138
|
+
# object references it by when no block is given
|
|
139
|
+
# @yield the predicate body; evaluated for its extracted source, never called directly
|
|
140
|
+
# @return [void]
|
|
141
|
+
# @raise [Bluebook::DSL::Malformed] if given a block whose source cannot be extracted or
|
|
142
|
+
# uses a pattern construct engines disagree on, or given no block and no sibling value
|
|
143
|
+
# object on this aggregate declares a rule with this description
|
|
111
144
|
def invariant_impl(description, &predicate)
|
|
112
145
|
return reference_named_invariant(description) unless predicate
|
|
113
146
|
|
|
@@ -119,7 +152,7 @@ module Hecks
|
|
|
119
152
|
|
|
120
153
|
private
|
|
121
154
|
|
|
122
|
-
#
|
|
155
|
+
# Primitive 3 (RuleReference#resolve_sibling_scan) — see that
|
|
123
156
|
# method's own comment for why this is a live scan, not a pool.
|
|
124
157
|
def reference_named_invariant(description)
|
|
125
158
|
verify_resolves_via!("invariant", "ValueObject", "sibling_scan")
|
|
@@ -134,6 +167,12 @@ module Hecks
|
|
|
134
167
|
|
|
135
168
|
public
|
|
136
169
|
|
|
170
|
+
# Assembles the declared attributes, invariants and members into a `ValueObject`.
|
|
171
|
+
#
|
|
172
|
+
# @return [Bluebook::ValueObject] the built value object; `closed_set` is true when any
|
|
173
|
+
# member was declared
|
|
174
|
+
# @raise [Bluebook::DSL::Malformed] if `one_of:` was declared on an attribute alongside
|
|
175
|
+
# other attributes on the same value object
|
|
137
176
|
def build
|
|
138
177
|
if @inline_closed_set_field && attributes.size > 1
|
|
139
178
|
raise Malformed,
|
|
@@ -149,6 +188,15 @@ module Hecks
|
|
|
149
188
|
)
|
|
150
189
|
end
|
|
151
190
|
|
|
191
|
+
# Evaluates a `value_object` block against a fresh builder and returns what it built.
|
|
192
|
+
#
|
|
193
|
+
# @param name [String] the value object's name
|
|
194
|
+
# @param owner_value_objects [Array<Bluebook::ValueObject>] the owning aggregate's other
|
|
195
|
+
# value objects, for `invariant` references to resolve against
|
|
196
|
+
# @yield the value object body, evaluated with the builder as `self`; may be omitted
|
|
197
|
+
# @return [Bluebook::ValueObject] the built value object
|
|
198
|
+
# @raise [Bluebook::DSL::Malformed] if the body declares a rule extraction cannot read,
|
|
199
|
+
# `one_of:` on more than one attribute, or uses a word the grammar does not admit
|
|
152
200
|
def self.build(name, owner_value_objects: [], &block)
|
|
153
201
|
builder = new(name, owner_value_objects: owner_value_objects)
|
|
154
202
|
builder.instance_eval(&block) if block
|
|
@@ -157,15 +205,15 @@ module Hecks
|
|
|
157
205
|
|
|
158
206
|
private
|
|
159
207
|
|
|
160
|
-
#
|
|
161
|
-
# overriding `AttributeCollector`'s own refusal (every
|
|
162
|
-
# includer has no meaningful use for this).
|
|
208
|
+
# **The new spelling** — `attribute :name, String, one_of: %w[...]`,
|
|
209
|
+
# overriding `AttributeCollector`'s own refusal (every other
|
|
210
|
+
# includer has no meaningful use for this). Private, like the
|
|
163
211
|
# module's own version it overrides — it is a callback `attribute`
|
|
164
212
|
# invokes on itself, never a word a bluebook author calls by name.
|
|
165
|
-
# Refuses a
|
|
213
|
+
# Refuses a second attribute naming one_of: on the same value
|
|
166
214
|
# object outright — a single-field set names exactly one field, by
|
|
167
215
|
# construction; two would be structurally ambiguous about which
|
|
168
|
-
# field each member line belongs to. `build` refuses the
|
|
216
|
+
# field each member line belongs to. `build` refuses the other
|
|
169
217
|
# half of that same rule (this attribute coexisting with unrelated
|
|
170
218
|
# ones on a multi-field object).
|
|
171
219
|
def install_inline_closed_set(field, values)
|
|
@@ -2,46 +2,49 @@ require_relative "generic_dispatch"
|
|
|
2
2
|
module Hecks
|
|
3
3
|
module Bluebook
|
|
4
4
|
module DSL
|
|
5
|
-
#
|
|
5
|
+
# The Ruby-side `word_gate` (`rust/parser/src/parse/mod.rs`'s own,
|
|
6
6
|
# read there first — this is the same job, one level up: Rust's own
|
|
7
|
-
# gate refuses a mistyped/inadmissible
|
|
8
|
-
# moment it reads a line of `.bluebook`
|
|
7
|
+
# gate refuses a mistyped/inadmissible word directly, at the
|
|
8
|
+
# moment it reads a line of `.bluebook` source text, before any
|
|
9
9
|
# per-construct parsing runs. Ruby never lexes source text — a
|
|
10
|
-
# `.bluebook` file
|
|
10
|
+
# `.bluebook` file is Ruby, so `given("x")` is already a real
|
|
11
11
|
# method call by the time any of this code runs, and Ruby's own
|
|
12
|
-
# method dispatch already refuses an
|
|
13
|
-
#
|
|
14
|
-
# is consult the self-hosted grammar table
|
|
12
|
+
# method dispatch already refuses an arity mismatch on an
|
|
13
|
+
# existing method for free. What Ruby's own dispatch does not do
|
|
14
|
+
# is consult the self-hosted grammar table at all — a mistyped
|
|
15
15
|
# word (`giv3n("x")`) or a word used in the wrong context
|
|
16
16
|
# (`identified_by` inside a `command` block) just raises Ruby's
|
|
17
17
|
# own generic `NoMethodError`, naming nothing about what the
|
|
18
18
|
# language actually admits. This module closes that gap.
|
|
19
19
|
#
|
|
20
|
-
#
|
|
20
|
+
# ## What reaches this module
|
|
21
|
+
#
|
|
22
|
+
# `method_missing`/`respond_to_missing?` only — a word this
|
|
21
23
|
# builder class already answers with an ordinary `def` (or a
|
|
22
|
-
# shared mixin
|
|
24
|
+
# method a shared mixin such as `AttributeCollector` defines) never
|
|
23
25
|
# reaches this module at all; Ruby's own method lookup finds it
|
|
24
|
-
# first, and
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# item #13's full metaprogrammed dispatch
|
|
28
|
-
# table-unification survey)
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
# is genuinely unchanged.
|
|
26
|
+
# first, and not every word has had its hand-written method
|
|
27
|
+
# removed. A word the grammar admits whose builder has no
|
|
28
|
+
# hand-written method executes for real here, via `GenericDispatch`
|
|
29
|
+
# (item #13's full metaprogrammed dispatch, whole-project
|
|
30
|
+
# table-unification survey) — see that module's own header for
|
|
31
|
+
# exactly which ones, and the full account of what was verified
|
|
32
|
+
# before each was migrated. A mistyped or wrongly-contexted word
|
|
33
|
+
# gets the same real, helpful, table-driven refusal Rust's own
|
|
34
|
+
# `word_gate` already gives, instead of Ruby's own generic
|
|
35
|
+
# `NoMethodError`.
|
|
36
|
+
#
|
|
37
|
+
# ## `GRAMMAR_CONTEXT`
|
|
37
38
|
#
|
|
38
39
|
# `self.class::GRAMMAR_CONTEXT` — each including class names which
|
|
39
40
|
# row of the self-hosted `Context` closed set it corresponds to
|
|
40
41
|
# (`AggregateBuilder::GRAMMAR_CONTEXT = "Aggregate"`, etc.) — the
|
|
41
|
-
#
|
|
42
|
+
# same string `word_gate`'s own `context` parameter already is on
|
|
42
43
|
# the Rust side, read off the identical table.
|
|
43
44
|
#
|
|
44
|
-
#
|
|
45
|
+
# ## Bootstrapping
|
|
46
|
+
#
|
|
47
|
+
# Bootstrapping gated, the same reason `RuleReference#lookup`
|
|
45
48
|
# already is (`rule_reference.rb`'s own comment has the full
|
|
46
49
|
# story) — the meta-domain's own bootstrap calls dozens of
|
|
47
50
|
# keywords on itself before its own grammar table exists to
|
|
@@ -52,28 +55,31 @@ module Hecks
|
|
|
52
55
|
# is deliberately no fallback covering the whole ~200-row table —
|
|
53
56
|
# that would defeat the entire point.
|
|
54
57
|
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`,
|
|
58
|
-
#
|
|
59
|
-
# `calls:` column
|
|
60
|
-
#
|
|
61
|
-
#
|
|
58
|
+
# One narrow exception (item #13's full metaprogrammed dispatch,
|
|
59
|
+
# slice 3, whole-project table-unification survey):
|
|
60
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`, an explicit table
|
|
61
|
+
# naming the same (context, word) -> method pairs the real
|
|
62
|
+
# `calls:` column carries, projected ahead of time into the
|
|
63
|
+
# committed `bootstrap_table.rb` — it matters only for words both
|
|
64
|
+
# migrated to the `calls:` shape and bootstrap-reachable
|
|
65
|
+
# (`attribute`, for one). This is the one place in this whole arc a
|
|
62
66
|
# bootstrap fallback was worth building despite `RuleReference`'s
|
|
63
|
-
# own precedent against it: it duplicates
|
|
64
|
-
#
|
|
67
|
+
# own precedent against it: it duplicates no logic, only a method
|
|
68
|
+
# name — `attribute_impl`'s own real, hand-written body is called
|
|
65
69
|
# either way, bootstrap or not, so there is nothing here that can
|
|
66
70
|
# drift the way a full behavioral duplicate could.
|
|
67
71
|
#
|
|
68
|
-
#
|
|
72
|
+
# ## `word_gate_dispatch` and the type-position fallback
|
|
73
|
+
#
|
|
74
|
+
# Two further pieces, both slice 5:
|
|
69
75
|
# - `word_gate_dispatch` is the same admission+dispatch logic
|
|
70
76
|
# `method_missing` below runs, factored out so a class with
|
|
71
|
-
# its
|
|
77
|
+
# its own class-level `method_missing` (`HecksagonBuilder`'s/
|
|
72
78
|
# `WorldBuilder`'s genuinely open-ended verb vocabulary) can
|
|
73
79
|
# call it directly and fall back to its own open-verb handling
|
|
74
80
|
# only when this returns `NOT_ADMITTED` — a class-level `def`
|
|
75
81
|
# always wins over an included module's in Ruby's own method
|
|
76
|
-
# resolution, so their
|
|
82
|
+
# resolution, so their own `method_missing` is the only one
|
|
77
83
|
# that ever runs for them, and this is what lets a real,
|
|
78
84
|
# closed-set word (`port`/`realm`/`latest`) still reach
|
|
79
85
|
# `GenericDispatch` despite that.
|
|
@@ -84,7 +90,7 @@ module Hecks
|
|
|
84
90
|
# "Type" builder `self.class::GRAMMAR_CONTEXT` could ever
|
|
85
91
|
# name. See that method's own comment.
|
|
86
92
|
module WordGate
|
|
87
|
-
# A caller with its
|
|
93
|
+
# A caller with its own class-level `method_missing`
|
|
88
94
|
# (`HecksagonBuilder`/`WorldBuilder` — see `word_gate_dispatch`'s
|
|
89
95
|
# own header) checks for this to tell "not a word the grammar
|
|
90
96
|
# admits at all" apart from every other outcome below (a real
|
|
@@ -92,7 +98,7 @@ module Hecks
|
|
|
92
98
|
# caller can fall through to its own open-ended handling instead.
|
|
93
99
|
NOT_ADMITTED = Object.new.freeze
|
|
94
100
|
|
|
95
|
-
#
|
|
101
|
+
# Private, matching Ruby's own convention for both (`Object`
|
|
96
102
|
# defines them private too) — and load-bearing here, not just
|
|
97
103
|
# style: `spec/syntax_conformance_spec.rb`'s "declares every
|
|
98
104
|
# word X answers" check walks `public_instance_methods`, and a
|
|
@@ -129,23 +135,23 @@ module Hecks
|
|
|
129
135
|
result
|
|
130
136
|
end
|
|
131
137
|
|
|
132
|
-
#
|
|
138
|
+
# The core admission+dispatch logic, factored out of
|
|
133
139
|
# `method_missing` — item #13's full metaprogrammed dispatch
|
|
134
140
|
# (slice 5, whole-project table-unification survey) — so a
|
|
135
|
-
# class with its
|
|
141
|
+
# class with its own class-level `method_missing`
|
|
136
142
|
# (`HecksagonBuilder`'s/`WorldBuilder`'s genuinely open-ended
|
|
137
143
|
# verb vocabulary, `persisted_by "Heki"`/`posted_by "Carrier"`,
|
|
138
|
-
# can never
|
|
139
|
-
#
|
|
144
|
+
# can never be a closed table) can still route a word the
|
|
145
|
+
# grammar admits through here first, falling back to its own
|
|
140
146
|
# open-verb handling only for what this doesn't recognize at
|
|
141
147
|
# all — unblocking `Hecksagon#port`/`World#realm`/`World#latest`,
|
|
142
148
|
# each a real, closed-set word that happened to sit on a class
|
|
143
149
|
# whose own `method_missing` a class-level `def` always wins
|
|
144
150
|
# over Ruby's own module-inclusion order.
|
|
145
151
|
#
|
|
146
|
-
#
|
|
152
|
+
# Never raises "not admitted" — returns `NOT_ADMITTED` instead,
|
|
147
153
|
# so the caller (this module's own `method_missing`, or one of
|
|
148
|
-
# the two above) decides what that means for it.
|
|
154
|
+
# the two above) decides what that means for it. Does still
|
|
149
155
|
# raise the richer, table-driven refusals below once a word is
|
|
150
156
|
# admitted-but-unimplemented, or admitted-somewhere-else-only —
|
|
151
157
|
# those are real, useful refusals regardless of which
|
|
@@ -161,16 +167,16 @@ module Hecks
|
|
|
161
167
|
keywords = rows[:keywords]
|
|
162
168
|
admitted = keywords.select { |row| row[:context] == context && (row[:word] == word.to_s || row[:was] == word.to_s) }
|
|
163
169
|
|
|
164
|
-
#
|
|
170
|
+
# **The type-position fallback** — item #13's full metaprogrammed
|
|
165
171
|
# dispatch (slice 5). `one_of`/`list_of`, called inside an
|
|
166
172
|
# attribute's own type argument (`attribute :x,
|
|
167
173
|
# one_of("a","b")`), run with `self` as whatever builder is
|
|
168
|
-
#
|
|
174
|
+
# currently instance_eval'ing — there is no dedicated "Type"
|
|
169
175
|
# builder of its own; every attribute()-taking builder answers
|
|
170
176
|
# these identically, through the shared `AttributeCollector`
|
|
171
|
-
# mixin. `self.class::GRAMMAR_CONTEXT` can never actually
|
|
177
|
+
# mixin. `self.class::GRAMMAR_CONTEXT` can never actually be
|
|
172
178
|
# "Type", so a word admitted only there would otherwise look
|
|
173
|
-
# inadmissible everywhere. Checked only once
|
|
179
|
+
# inadmissible everywhere. Checked only once this context has
|
|
174
180
|
# already come up empty, so a context with its own same-named
|
|
175
181
|
# row (`ValueObject`'s own `one_of`, the block-wrapper form)
|
|
176
182
|
# keeps using that instead, unaffected.
|
|
@@ -190,11 +196,11 @@ module Hecks
|
|
|
190
196
|
"'#{word}' is not a word #{context} admits — legal words here: #{legal.join(', ')}"
|
|
191
197
|
end
|
|
192
198
|
|
|
193
|
-
#
|
|
199
|
+
# Item #13's full metaprogrammed dispatch — the word is
|
|
194
200
|
# admitted here, and this builder has no hand-written method
|
|
195
201
|
# left for it; before falling through to the "not yet
|
|
196
202
|
# implemented" refusal every word without one still gets,
|
|
197
|
-
# offer it to GenericDispatch — the
|
|
203
|
+
# offer it to GenericDispatch — the safe, verified subset of
|
|
198
204
|
# words whose whole behavior is now executed off this same
|
|
199
205
|
# table, not just checked against it.
|
|
200
206
|
dispatched = GenericDispatch.try(self, context, word.to_s, args, kwargs, block, rows)
|
|
@@ -215,13 +221,13 @@ module Hecks
|
|
|
215
221
|
super
|
|
216
222
|
end
|
|
217
223
|
|
|
218
|
-
# A word admitted
|
|
224
|
+
# A word admitted somewhere, just not in this context, still
|
|
219
225
|
# falls through to Ruby's own `NoMethodError` rather than this
|
|
220
226
|
# module's own richer refusal — `method_missing` fires for
|
|
221
227
|
# every typo in the whole codebase (this class's own genuinely
|
|
222
228
|
# private helper methods included), not just DSL keyword calls;
|
|
223
|
-
# only raise the
|
|
224
|
-
# least
|
|
229
|
+
# only raise the rich, table-driven message when the word is at
|
|
230
|
+
# least something the grammar knows about, anywhere, so an
|
|
225
231
|
# unrelated Ruby-level typo inside a builder's own private code
|
|
226
232
|
# keeps its own ordinary, unconfusing `NoMethodError`.
|
|
227
233
|
def admitted_anywhere?(rows, word)
|
|
@@ -8,6 +8,7 @@ module Hecks
|
|
|
8
8
|
# vocabulary of its own; `WorldBuilder#record_binding` reads `#to_h`
|
|
9
9
|
# back out.
|
|
10
10
|
class SettingsCollector
|
|
11
|
+
# Starts with no settings recorded.
|
|
11
12
|
def initialize = @values = {}
|
|
12
13
|
|
|
13
14
|
def method_missing(key, *args, &)
|
|
@@ -19,9 +20,9 @@ module Hecks
|
|
|
19
20
|
def to_h = @values
|
|
20
21
|
end
|
|
21
22
|
|
|
22
|
-
#
|
|
23
|
+
# The aggregate-qualified mirror (#143) — a `.world` file's own
|
|
23
24
|
# `Pizzas::Order.charged_by("Stripe") do ... end` visually mirrors the
|
|
24
|
-
#
|
|
25
|
+
# same bind line the sibling `.hecksagon` file already writes
|
|
25
26
|
# (`HecksagonBuilder`'s own `BindingProxy`), but `IR::World#for_verb`/
|
|
26
27
|
# `#for_binding` key purely by verb and adapter name — the aggregate
|
|
27
28
|
# qualifier is never read back out, it exists only for that visual
|
|
@@ -30,12 +31,19 @@ module Hecks
|
|
|
30
31
|
# not, has to land in the exact same `@settings` write path
|
|
31
32
|
# (`WorldBuilder#record_binding`).
|
|
32
33
|
class WorldConstProxy
|
|
34
|
+
# Mints the stand-in module a bare domain constant resolves to inside a `.world` block.
|
|
35
|
+
#
|
|
36
|
+
# @param builder [Bluebook::DSL::WorldBuilder] the builder every verb called through the
|
|
37
|
+
# module's proxies records its settings on
|
|
38
|
+
# @return [Module] an anonymous module whose `const_missing` answers a `WorldConstProxy`,
|
|
39
|
+
# whatever aggregate name follows `::`
|
|
33
40
|
def self.namespace(builder)
|
|
34
41
|
Module.new do
|
|
35
42
|
define_singleton_method(:const_missing) { |_aggregate| WorldConstProxy.new(builder) }
|
|
36
43
|
end
|
|
37
44
|
end
|
|
38
45
|
|
|
46
|
+
# @param builder [Bluebook::DSL::WorldBuilder] the builder verb calls are recorded on
|
|
39
47
|
def initialize(builder) = @builder = builder
|
|
40
48
|
|
|
41
49
|
def method_missing(verb, *args, **kwargs, &block) = @builder.record_binding(verb, args, kwargs, block)
|
|
@@ -45,7 +53,7 @@ module Hecks
|
|
|
45
53
|
|
|
46
54
|
# Parses a `.world` file's top-level DSL block into a `World` — a
|
|
47
55
|
# domain's own `realm`/`latest` version markers plus its adapter bind
|
|
48
|
-
#
|
|
56
|
+
# settings, one entry per `verb("Adapter") do ... end` call (whether
|
|
49
57
|
# written bare or aggregate-qualified through `WorldConstProxy`'s
|
|
50
58
|
# visual mirror of a sibling `.hecksagon` file's own bind).
|
|
51
59
|
class WorldBuilder
|
|
@@ -53,23 +61,37 @@ module Hecks
|
|
|
53
61
|
|
|
54
62
|
include WordGate
|
|
55
63
|
|
|
64
|
+
# @param domain [String] name of the domain whose world this is
|
|
56
65
|
def initialize(domain)
|
|
57
66
|
@domain = domain
|
|
58
67
|
@settings = {}
|
|
59
68
|
end
|
|
60
69
|
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
70
|
+
# Names the realm this world belongs to, such as `"Examples"` or `"QA"`.
|
|
71
|
+
#
|
|
72
|
+
# `realm_impl`/`latest_impl` answer the `realm`/`latest` words
|
|
73
|
+
# through the table's `calls:` column — item #13's full
|
|
74
|
+
# metaprogrammed dispatch (slice 5). Neither bootstrap-reachable
|
|
75
|
+
# (checked directly). Reached through `WordGate`'s
|
|
64
76
|
# `word_gate_dispatch`, called explicitly below since
|
|
65
77
|
# `WorldBuilder`'s own class-level `method_missing` (the
|
|
66
78
|
# open-verb catch-all beneath this) always wins over the
|
|
67
79
|
# module's — see `word_gate.rb`'s own header for the full
|
|
68
80
|
# mechanism.
|
|
81
|
+
#
|
|
82
|
+
# @param value [String, Symbol] the realm's name; blankness is judged by the world
|
|
83
|
+
# language at `build`, not here
|
|
84
|
+
# @return [String] the realm as stored
|
|
69
85
|
def realm_impl(value)
|
|
70
86
|
@realm = required(value, "realm")
|
|
71
87
|
end
|
|
72
88
|
|
|
89
|
+
# Names the bluebook version this world treats as latest, which `ProjectRegister` compares
|
|
90
|
+
# with the bluebook's own declared `version`.
|
|
91
|
+
#
|
|
92
|
+
# @param value [String, Symbol, Numeric] the version marker; blankness is judged by the
|
|
93
|
+
# world language at `build`, not here
|
|
94
|
+
# @return [String] the version as stored
|
|
73
95
|
def latest_impl(value)
|
|
74
96
|
@latest = required(value, "latest version")
|
|
75
97
|
end
|
|
@@ -83,11 +105,20 @@ module Hecks
|
|
|
83
105
|
|
|
84
106
|
def respond_to_missing?(_name, _include_private = false) = true
|
|
85
107
|
|
|
86
|
-
#
|
|
108
|
+
# Records one bind's settings under both its verb and its `verb:adapter` key.
|
|
109
|
+
#
|
|
110
|
+
# A method of its own, apart from `method_missing` (#143), so
|
|
87
111
|
# `WorldConstProxy`'s own aggregate-qualified verb calls
|
|
88
112
|
# (`Pizzas::Order.charged_by(...)`) write into the exact same
|
|
89
113
|
# place the bare top-level spelling (`charged_by(...)`) already
|
|
90
114
|
# does — one write path, two spellings.
|
|
115
|
+
#
|
|
116
|
+
# @param verb [Symbol, String] the bind verb, such as `:persisted_by`
|
|
117
|
+
# @param args [Array<Object>] the call's positional arguments; the first names the adapter
|
|
118
|
+
# @param kwargs [Hash{Symbol => Object}] settings given inline as keyword arguments
|
|
119
|
+
# @param block [Proc, nil] a settings block, evaluated against a `SettingsCollector`
|
|
120
|
+
# @return [Hash{Symbol => Object}] the settings just recorded: `:adapter` (a String),
|
|
121
|
+
# then the keyword arguments, then the block's settings, later ones winning
|
|
91
122
|
def record_binding(verb, args, kwargs, block)
|
|
92
123
|
collector = SettingsCollector.new
|
|
93
124
|
collector.instance_eval(&block) if block
|
|
@@ -96,18 +127,30 @@ module Hecks
|
|
|
96
127
|
@settings["#{verb}:#{args.first.to_s.downcase}"] = value
|
|
97
128
|
end
|
|
98
129
|
|
|
130
|
+
# Assembles the realm, version marker and bind settings, judged by the world language.
|
|
131
|
+
#
|
|
132
|
+
# @return [Bluebook::World] the world, returned once the language accepts it
|
|
133
|
+
# @raise [Bluebook::DSL::Malformed] if the world language refuses the declaration, such
|
|
134
|
+
# as a blank `realm`
|
|
99
135
|
def build
|
|
100
136
|
MetaValidator.call_world(
|
|
101
137
|
World.new(domain: @domain, realm: @realm, latest: @latest, settings: @settings)
|
|
102
138
|
)
|
|
103
139
|
end
|
|
104
140
|
|
|
141
|
+
# Evaluates a `Hecks.world` block against a fresh builder and returns the world it declared.
|
|
142
|
+
#
|
|
105
143
|
# `ConstShim`'s resolver, the same bridge `HecksagonBuilder`/
|
|
106
144
|
# `BluebookBuilder` already wrap their own `instance_eval` in
|
|
107
|
-
# (#143) — without it, `Pizzas::Order.charged_by(...)`
|
|
145
|
+
# (#143) — without it, `Pizzas::Order.charged_by(...)` raises
|
|
108
146
|
# `NameError: uninitialized constant Pizzas` for every `.world`
|
|
109
147
|
# file using the aggregate-qualified mirror form, since a bare
|
|
110
148
|
# `Pizzas` has no real constant to resolve to.
|
|
149
|
+
#
|
|
150
|
+
# @param domain [String] name of the domain whose world this is
|
|
151
|
+
# @yield the world body, evaluated with the builder as `self`; may be omitted
|
|
152
|
+
# @return [Bluebook::World] the judged world
|
|
153
|
+
# @raise [Bluebook::DSL::Malformed] if the world language refuses the declaration
|
|
111
154
|
def self.build(domain, &block)
|
|
112
155
|
builder = new(domain)
|
|
113
156
|
resolver = ->(_domain) { WorldConstProxy.namespace(builder) }
|
|
@@ -3,10 +3,10 @@ require_relative "expression/ast_json"
|
|
|
3
3
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Bluebook
|
|
6
|
-
# An entity, as a
|
|
6
|
+
# An entity, as a Ruby class — a piece of an aggregate that has an identity
|
|
7
7
|
# of its own.
|
|
8
8
|
#
|
|
9
|
-
# Crossing over closes the
|
|
9
|
+
# Crossing over closes the owner chain. An entity declares commands, and
|
|
10
10
|
# until now those commands had no owner that could state an identity: an
|
|
11
11
|
# entity was an IR object, not a construct, so `Construct#hecks_fqn` refused
|
|
12
12
|
# rather than answering "Deposit" and looking right. Four of banking's
|
|
@@ -14,15 +14,15 @@ module Hecks
|
|
|
14
14
|
# `Banking::Account.Ledger.Deposit` — which is the id the judge already mints
|
|
15
15
|
# for them.
|
|
16
16
|
#
|
|
17
|
-
#
|
|
17
|
+
# Not const_set, for the same reason a command is not: a name inside one
|
|
18
18
|
# aggregate can denote more than one kind of thing, so the constant tree
|
|
19
19
|
# cannot index it.
|
|
20
20
|
#
|
|
21
|
-
# It must stay
|
|
21
|
+
# It must stay structurally interchangeable with an aggregate — the runtime
|
|
22
22
|
# builds `Instance.new(aggregate: entity)` and `CommandRules` takes either as
|
|
23
23
|
# `declaring` — so it answers `hecks_name`, `attributes`, `attribute`,
|
|
24
24
|
# `identified_by` and `lifecycle` exactly as an aggregate does. And it must
|
|
25
|
-
# keep
|
|
25
|
+
# keep not answering `value_object`: `Value.for_attribute` sniffs for that
|
|
26
26
|
# method to tell a piece from a head.
|
|
27
27
|
class Entity
|
|
28
28
|
extend Construct
|
|
@@ -39,26 +39,26 @@ module Hecks
|
|
|
39
39
|
# S17, ADR 0026 — "That is what `entity` is for, and `entity` is
|
|
40
40
|
# declared by the language and used zero times in it" (the ADR's
|
|
41
41
|
# own words). Dispatch nests inside Handler, so an entity's own
|
|
42
|
-
#
|
|
42
|
+
# nested entities are part of its wire shape now, the same way
|
|
43
43
|
# an aggregate's always were — the field the ADR names as
|
|
44
44
|
# declared-but-unused until this slice.
|
|
45
45
|
entities: many(:entities),
|
|
46
|
-
# ADR 0028 — a piece's own `given`, the
|
|
46
|
+
# ADR 0028 — a piece's own `given`, the same shape
|
|
47
47
|
# `Aggregate#preconditions` already carries (its own `emits_ir`
|
|
48
48
|
# row, identical). A precondition shared across this piece's own
|
|
49
49
|
# commands, declared once — a command references it back by
|
|
50
|
-
# name; the resolved text still lands on
|
|
50
|
+
# name; the resolved text still lands on each referencing
|
|
51
51
|
# command's own `givens` either way, so this field is read-only
|
|
52
52
|
# documentation of what the piece itself declared, the same
|
|
53
53
|
# relationship `Aggregate.preconditions` already has to its own
|
|
54
54
|
# commands.
|
|
55
55
|
preconditions: -> { preconditions.map { |rule| Expression::AstJson.rule_row(rule) } },
|
|
56
|
-
# A piece's
|
|
56
|
+
# A piece's own shape rule, checked against every instance of
|
|
57
57
|
# this piece the aggregate holds (Admissibility#enforce_
|
|
58
|
-
# invariants' own recursive walk) — the
|
|
58
|
+
# invariants' own recursive walk) — the same relationship
|
|
59
59
|
# `ValueObject#invariants` already has to its own instances,
|
|
60
60
|
# one level up the construct tree. Not a separate enforcement
|
|
61
|
-
# boundary; still checked at the
|
|
61
|
+
# boundary; still checked at the same two points (after every
|
|
62
62
|
# mutation, before save) the aggregate's own invariants always
|
|
63
63
|
# were — see that method's own comment for why this does not
|
|
64
64
|
# contradict "there is no separate entity invariant."
|
|
@@ -70,6 +70,24 @@ module Hecks
|
|
|
70
70
|
attr_reader :description, :identified_by, :identity_paths, :identity_heads,
|
|
71
71
|
:attributes, :commands, :queries, :entities, :preconditions, :invariants, :lifecycle
|
|
72
72
|
|
|
73
|
+
# Mints a new piece class for one declared entity and absorbs its fields into it.
|
|
74
|
+
#
|
|
75
|
+
# @param name [String, Symbol] the entity's declared name
|
|
76
|
+
# @param description [String, nil] the entity's declared prose description
|
|
77
|
+
# @param identified_by [String, Symbol, Array<String, Symbol>, nil] the identity
|
|
78
|
+
# path(s) this entity is addressed by
|
|
79
|
+
# @param attributes [Array<Bluebook::Attribute>] the entity's declared fields
|
|
80
|
+
# @param commands [Array<Class>] the command classes (`Bluebook::Command` subclasses)
|
|
81
|
+
# declared on this entity
|
|
82
|
+
# @param queries [Array<Bluebook::Query>] the queries declared on this entity
|
|
83
|
+
# @param entities [Array<Class>] the entity classes (`Bluebook::Entity` subclasses)
|
|
84
|
+
# nested directly under this entity
|
|
85
|
+
# @param preconditions [Array<Bluebook::Given>] this entity's own named `given`s
|
|
86
|
+
# @param invariants [Array<Bluebook::Invariant>] the rules checked against every
|
|
87
|
+
# instance of this entity
|
|
88
|
+
# @param lifecycle [Bluebook::Lifecycle, nil] the entity's declared state machine,
|
|
89
|
+
# or `nil` if it declares none
|
|
90
|
+
# @return [Class] the minted piece class (a `Bluebook::Entity` subclass)
|
|
73
91
|
def declare(name:, description: nil, identified_by: nil, attributes: [],
|
|
74
92
|
commands: [], queries: [], entities: [], preconditions: [], invariants: [], lifecycle: nil)
|
|
75
93
|
piece = Class.new(self)
|
|
@@ -85,6 +103,17 @@ module Hecks
|
|
|
85
103
|
# Assigns what the language declares, then hands off to the
|
|
86
104
|
# behaviour's own `settle` — derived identity and the name
|
|
87
105
|
# indexes, neither of which the declaration states.
|
|
106
|
+
#
|
|
107
|
+
# @param description [String, nil] see `declare`
|
|
108
|
+
# @param identified_by [String, Symbol, Array<String, Symbol>, nil] see `declare`
|
|
109
|
+
# @param attributes [Array<Bluebook::Attribute>] see `declare`
|
|
110
|
+
# @param commands [Array<Class>] see `declare`
|
|
111
|
+
# @param queries [Array<Bluebook::Query>] see `declare`
|
|
112
|
+
# @param entities [Array<Class>] see `declare`
|
|
113
|
+
# @param preconditions [Array<Bluebook::Given>] see `declare`
|
|
114
|
+
# @param invariants [Array<Bluebook::Invariant>] see `declare`
|
|
115
|
+
# @param lifecycle [Bluebook::Lifecycle, nil] see `declare`
|
|
116
|
+
# @return [Class] this piece's own class, self, once identity and indexes are derived
|
|
88
117
|
def absorb(description:, identified_by:, attributes:, commands:, queries:, entities:, preconditions:, invariants:,
|
|
89
118
|
lifecycle:)
|
|
90
119
|
@description = description
|