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
|
@@ -15,28 +15,48 @@ module Hecks
|
|
|
15
15
|
include QuerySpecification::Common::DSL
|
|
16
16
|
include WordGate
|
|
17
17
|
|
|
18
|
+
# @param name [String] the query's name, as written after `query`
|
|
18
19
|
def initialize(name)
|
|
19
20
|
@name = name
|
|
20
21
|
@wheres = []
|
|
21
22
|
end
|
|
22
23
|
|
|
24
|
+
# Sets the human-readable description shown for this query.
|
|
25
|
+
#
|
|
26
|
+
# @param value [String] the description text
|
|
27
|
+
# @return [String] the description as stored
|
|
23
28
|
def description(value) = @description = value
|
|
24
29
|
|
|
30
|
+
# Declares a query parameter that names another aggregate's identity.
|
|
31
|
+
#
|
|
25
32
|
# A query parameter naming another aggregate's own identity
|
|
26
33
|
# (Card.Active's own `Board`, filtering to one board's cards) —
|
|
27
34
|
# just a plain attribute typed as a reference,
|
|
28
|
-
# AttributeCollector#
|
|
35
|
+
# `AttributeCollector#attribute_impl` already handling a Reference
|
|
29
36
|
# exactly like any other. No "acts on itself" case to
|
|
30
37
|
# distinguish here the way a command's own reference_to has —
|
|
31
38
|
# a query has no root of its own to act on, only parameters.
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
39
|
+
#
|
|
40
|
+
# Answers the `reference_to` word through the table's `calls:`
|
|
41
|
+
# column — item #13's full metaprogrammed dispatch (slice 4b).
|
|
42
|
+
# Bootstrap-reachable, in `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
43
|
+
#
|
|
44
|
+
# @param type [Module, Symbol, String] the referenced aggregate, written as a bare
|
|
45
|
+
# constant
|
|
46
|
+
# @param as [Symbol, nil] the parameter's name; nil derives it from the target
|
|
47
|
+
# @param optional [Boolean] whether the parameter may be omitted when the query runs
|
|
48
|
+
# @return [void]
|
|
49
|
+
# @raise [Bluebook::DSL::Malformed] if `as` (or the derived name) is already declared
|
|
35
50
|
def reference_to_impl(type, as: nil, optional: false)
|
|
36
51
|
target = Naming.demodulise(type)
|
|
37
52
|
attribute_impl(as || default_reference_name(target), Reference.new(target), optional: optional)
|
|
38
53
|
end
|
|
39
54
|
|
|
55
|
+
# Assembles the declared parameters and filtering into a `Query`.
|
|
56
|
+
#
|
|
57
|
+
# @return [Bluebook::Query] the built query
|
|
58
|
+
# @raise [Bluebook::DSL::Malformed] if the body declares `cursor`, which no interpreter
|
|
59
|
+
# implements
|
|
40
60
|
def build
|
|
41
61
|
seal_cursor
|
|
42
62
|
Query.new(
|
|
@@ -54,6 +74,15 @@ module Hecks
|
|
|
54
74
|
)
|
|
55
75
|
end
|
|
56
76
|
|
|
77
|
+
# Evaluates a `query` block against a fresh builder, then fills in owner-derived types.
|
|
78
|
+
#
|
|
79
|
+
# @param name [String] the query's name
|
|
80
|
+
# @param owner_attributes [Array<Bluebook::Attribute>] the enclosing aggregate or
|
|
81
|
+
# entity's own attributes, matched against the block's own parameter names
|
|
82
|
+
# @yield the query body, evaluated with the builder as `self`; may be omitted
|
|
83
|
+
# @return [Bluebook::Query] the built query
|
|
84
|
+
# @raise [Bluebook::DSL::Malformed] if the body declares `cursor`, a duplicate attribute,
|
|
85
|
+
# or a filtering clause the query language refuses
|
|
57
86
|
def self.build(name, owner_attributes: [], &block)
|
|
58
87
|
builder = new(name)
|
|
59
88
|
builder.instance_eval(&block) if block
|
|
@@ -69,14 +98,14 @@ module Hecks
|
|
|
69
98
|
|
|
70
99
|
private
|
|
71
100
|
|
|
72
|
-
# A block parameter names one of the
|
|
101
|
+
# A block parameter names one of the owner's (the aggregate or entity
|
|
73
102
|
# this query is declared on) own already-declared attributes —
|
|
74
103
|
# `query "ForDecision" do |decision| where decision: :decision end`
|
|
75
104
|
# on Submission, whose own `attribute :decision, DecisionRef` already
|
|
76
105
|
# says what `decision` is. Restating `attribute :decision, DecisionRef`
|
|
77
106
|
# a second time inside the query was pure duplication; this derives
|
|
78
107
|
# the same type from the owner instead. Only fills in a name the block
|
|
79
|
-
# body did
|
|
108
|
+
# body did not already declare explicitly (checked after instance_eval
|
|
80
109
|
# runs, so an existing bluebook still spelling it out both ways keeps
|
|
81
110
|
# working unchanged — this only removes the need to, never refuses
|
|
82
111
|
# the choice to). A block parameter matching nothing on the owner is
|
|
@@ -90,8 +119,8 @@ module Hecks
|
|
|
90
119
|
owner_attr = owner_attributes.find { |a| a.name == param_name }
|
|
91
120
|
next unless owner_attr
|
|
92
121
|
|
|
93
|
-
# `Attribute.new`
|
|
94
|
-
# entry — `owner_attr.type` is already
|
|
122
|
+
# `Attribute.new` directly, not the public `attribute(...)` DSL
|
|
123
|
+
# entry — `owner_attr.type` is already spelled (a demodulised
|
|
95
124
|
# String, `Attribute#spell`'s own doing), not a bareword the
|
|
96
125
|
# bluebook author typed, so it must not run through
|
|
97
126
|
# `AttributeCollector#attribute`'s quoted-type refusal (ADR
|
|
@@ -14,18 +14,33 @@ module Hecks
|
|
|
14
14
|
include QuerySpecification::Common::DSL
|
|
15
15
|
include WordGate
|
|
16
16
|
|
|
17
|
+
# @param name [String] the read model's own name, as written after `read_model`
|
|
17
18
|
def initialize(name)
|
|
18
19
|
@name = name
|
|
19
20
|
end
|
|
20
21
|
|
|
22
|
+
# Sets the human-readable description shown for this read model.
|
|
23
|
+
#
|
|
24
|
+
# @param value [String] the description text
|
|
25
|
+
# @return [String] the description as stored
|
|
21
26
|
def description(value)
|
|
22
27
|
# moved to the language: ProjectionText / purpose, on Projection.Declare
|
|
23
28
|
@description = value
|
|
24
29
|
end
|
|
25
30
|
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
31
|
+
# Declares the read model's own "one" side — the aggregate head every row projects
|
|
32
|
+
# around, keyed by its identity.
|
|
33
|
+
#
|
|
34
|
+
# Reached through `calls: "reference_to_impl"`. Bootstrap-reachable — the self-hosted
|
|
35
|
+
# `Bluebook.WholeBluebook` read model declares one — so it's exercised while
|
|
36
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK` is still in play, not only afterwards.
|
|
37
|
+
#
|
|
38
|
+
# @param type [Module, Symbol, String] the referenced aggregate, written as a bare
|
|
39
|
+
# constant
|
|
40
|
+
# @param as [Symbol, nil] the field name to project the reference under; `nil` derives it
|
|
41
|
+
# from the target's own name
|
|
42
|
+
# @return [void]
|
|
43
|
+
# @raise [Bluebook::DSL::Malformed] if `reference_to` was already declared
|
|
29
44
|
def reference_to_impl(type, as: nil)
|
|
30
45
|
raise Malformed, "#{@name} already has a projection reference" if @reference_target
|
|
31
46
|
|
|
@@ -33,28 +48,34 @@ module Hecks
|
|
|
33
48
|
@reference_name = (as || Naming.snake(@reference_target)).to_sym
|
|
34
49
|
end
|
|
35
50
|
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
# groups). The class-level
|
|
50
|
-
# class body uses is
|
|
51
|
-
#
|
|
51
|
+
# Adds one aggregate head this read model projects.
|
|
52
|
+
#
|
|
53
|
+
# Order-independent: `many:` is decided by comparing the included type
|
|
54
|
+
# against the reference target, resolved at build time (when the
|
|
55
|
+
# reference is known), not at the moment `include` itself runs — so
|
|
56
|
+
# an include declared before `reference_to` resolves exactly like one
|
|
57
|
+
# declared after it.
|
|
58
|
+
#
|
|
59
|
+
# Reached through `calls: "include_impl"`. Bootstrap-reachable —
|
|
60
|
+
# every core chapter's own `read_model` names which aggregates it
|
|
61
|
+
# includes with it — so it's exercised while
|
|
62
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK` is still in play.
|
|
63
|
+
# `group_by_impl`, below, is table-routed the same way but is not
|
|
64
|
+
# bootstrap-reachable (no core read_model groups). The class-level
|
|
65
|
+
# `include WordGate` this file's own class body uses is
|
|
66
|
+
# `Module#include`, a different receiver, unrelated to this
|
|
67
|
+
# instance method sharing its name.
|
|
68
|
+
#
|
|
69
|
+
# @param type [Module, Symbol, String] the included aggregate, written as a bare constant
|
|
70
|
+
# @param as [Symbol, nil] the field name to project this head's rows under; `nil` derives
|
|
71
|
+
# it from the target's own name (pluralized for a many-side head)
|
|
72
|
+
# @return [void]
|
|
52
73
|
def include_impl(type, as: nil)
|
|
53
74
|
@includes ||= []
|
|
54
75
|
@includes << [Naming.demodulise(type), as]
|
|
55
76
|
end
|
|
56
77
|
|
|
57
|
-
# `on:` (ADR 0055) —
|
|
78
|
+
# `on:` (ADR 0055) — overrides of `QuerySpecification::Common::DSL`'s
|
|
58
79
|
# shared `where_impl`/`order_by_impl`/`limit_impl`/`offset_impl`,
|
|
59
80
|
# scoped to `ReadModelBuilder` alone rather than added to the shared
|
|
60
81
|
# module `Query` also mixes in: a plain `query` has no
|
|
@@ -63,10 +84,10 @@ module Hecks
|
|
|
63
84
|
# means a `Query`'s own `where(..., on: X)` gets Ruby's own loud
|
|
64
85
|
# `unknown keyword: :on` instead of quietly doing nothing.
|
|
65
86
|
#
|
|
66
|
-
# `on:` names the target by
|
|
87
|
+
# `on:` names the target by type (`on: Character`), resolved the
|
|
67
88
|
# same way `reference_to`/`include` already resolve their own type
|
|
68
89
|
# argument (`Naming.demodulise`) — not by the include's own `as:`
|
|
69
|
-
# alias. A read model that `include`s the
|
|
90
|
+
# alias. A read model that `include`s the same type twice under two
|
|
70
91
|
# different `as:` has no way to say which one `on:` means today; no
|
|
71
92
|
# real corpus read model does this, so it's a real, deliberate scope
|
|
72
93
|
# limit (see ADR 0055), not an oversight.
|
|
@@ -77,14 +98,14 @@ module Hecks
|
|
|
77
98
|
# `status: "disputed"` captured as `**kwargs` (GenericDispatch's own
|
|
78
99
|
# `builder.send(calls, *args, **kwargs, &block)`), and Ruby stops
|
|
79
100
|
# auto-converting a bare `**hash` call into a plain positional Hash
|
|
80
|
-
#
|
|
101
|
+
# the moment a method declares any real keyword parameter — so a
|
|
81
102
|
# `(clauses, on: nil)` signature raised "wrong number of arguments
|
|
82
103
|
# (given 0, expected 1)" on every ordinary `where(field: value)`
|
|
83
104
|
# call, never reaching `on:` at all. `**rest` sidesteps this: Ruby
|
|
84
105
|
# still auto-splits `on:` into the declared keyword and gathers
|
|
85
|
-
# every
|
|
106
|
+
# every other key into `rest` regardless of how the caller wrote it.
|
|
86
107
|
#
|
|
87
|
-
# `QuerySpecification::Common::WhereClause` etc —
|
|
108
|
+
# `QuerySpecification::Common::WhereClause` etc — fully qualified,
|
|
88
109
|
# not the bare names `dsl.rb`'s own shared `where_impl` gets away
|
|
89
110
|
# with. That file is lexically nested inside `Common` itself, so
|
|
90
111
|
# `WhereClause` resolves directly; this class is nested inside
|
|
@@ -92,10 +113,25 @@ module Hecks
|
|
|
92
113
|
# `QuerySpecification::Common` at all — a bare `WhereClause` here
|
|
93
114
|
# falls through to `const_missing` and, mid-bluebook-load, that's
|
|
94
115
|
# `ConstShim`, which resolves it against the self-hosted grammar
|
|
95
|
-
# domain's
|
|
116
|
+
# domain's own unrelated `WhereClause` construct instead (a `Module`,
|
|
96
117
|
# not this `Struct`) — found directly by reproducing "undefined
|
|
97
118
|
# method `new' for module WhereClause" against a real corpus load,
|
|
98
119
|
# not guessed.
|
|
120
|
+
#
|
|
121
|
+
# Records one `WhereClause` per `field => value` pair, targeted at a named many-side
|
|
122
|
+
# include when this read model has more than one.
|
|
123
|
+
#
|
|
124
|
+
# @param positional [Array<Hash{Symbol => Object}>] at most one bare Hash of where
|
|
125
|
+
# clauses; in practice always empty, since Ruby routes an ordinary
|
|
126
|
+
# `where(field: value)` call entirely into `rest` (see the comment above)
|
|
127
|
+
# @param on [Module, Symbol, String, nil] the many-side included aggregate these clauses
|
|
128
|
+
# apply to; `nil` when this read model has at most one many-side head
|
|
129
|
+
# @param rest [Hash{Symbol => Object}] `field => value` where clauses; a value is either
|
|
130
|
+
# a literal, a Symbol naming a query argument, or a one-pair Hash `{ comparator =>
|
|
131
|
+
# operand }` such as `{ gte: :minimum }` — a bare value means `eq`
|
|
132
|
+
# @return [void]
|
|
133
|
+
# @raise [ArgumentError] if more than one positional argument is given, or a Hash value
|
|
134
|
+
# does not have exactly one pair, or names a comparator outside `COMPARATORS`
|
|
99
135
|
def where_impl(*positional, on: nil, **rest)
|
|
100
136
|
raise ArgumentError, "wrong number of arguments (given #{positional.size}, expected 1)" if positional.size > 1
|
|
101
137
|
|
|
@@ -108,30 +144,55 @@ module Hecks
|
|
|
108
144
|
end
|
|
109
145
|
end
|
|
110
146
|
|
|
147
|
+
# Records the read model's single ordering, replacing any declared earlier.
|
|
148
|
+
#
|
|
149
|
+
# @param field [Symbol, String] the field to order by; a dotted path such as
|
|
150
|
+
# `:"order.value"` reaches a value object's member
|
|
151
|
+
# @param direction [Symbol, String] `:asc` or `:desc`
|
|
152
|
+
# @param on [Module, Symbol, String, nil] the many-side included aggregate this ordering
|
|
153
|
+
# applies to; `nil` when this read model has at most one many-side head
|
|
154
|
+
# @return [QuerySpecification::Common::OrderBy] the ordering just recorded
|
|
111
155
|
def order_by_impl(field, direction = :asc, on: nil)
|
|
112
156
|
@order_by = QuerySpecification::Common::OrderBy.new(field: field, direction: direction, target: resolve_target(on))
|
|
113
157
|
end
|
|
114
158
|
|
|
159
|
+
# Records the most rows the targeted collection returns.
|
|
160
|
+
#
|
|
161
|
+
# @param value [Integer, Symbol] a literal row count, or a Symbol naming the query
|
|
162
|
+
# argument that supplies it
|
|
163
|
+
# @param on [Module, Symbol, String, nil] the many-side included aggregate this limit
|
|
164
|
+
# applies to; `nil` when this read model has at most one many-side head
|
|
165
|
+
# @return [QuerySpecification::Common::LimitSpec] the limit just recorded
|
|
115
166
|
def limit_impl(value, on: nil)
|
|
116
167
|
@limit = QuerySpecification::Common::LimitSpec.new(value: value, target: resolve_target(on))
|
|
117
168
|
end
|
|
118
169
|
|
|
170
|
+
# Records how many matched rows the targeted collection skips before the limit applies.
|
|
171
|
+
#
|
|
172
|
+
# @param value [Integer, Symbol] a literal row count, or a Symbol naming the query
|
|
173
|
+
# argument that supplies it
|
|
174
|
+
# @param on [Module, Symbol, String, nil] the many-side included aggregate this offset
|
|
175
|
+
# applies to; `nil` when this read model has at most one many-side head
|
|
176
|
+
# @return [QuerySpecification::Common::OffsetSpec] the offset just recorded
|
|
119
177
|
def offset_impl(value, on: nil)
|
|
120
178
|
@offset = QuerySpecification::Common::OffsetSpec.new(value: value, target: resolve_target(on))
|
|
121
179
|
end
|
|
122
180
|
|
|
123
|
-
#
|
|
181
|
+
# Names which of the eligible head's own fields to nest its rows
|
|
124
182
|
# under — one level per field, the leaf being that row with the
|
|
125
183
|
# named fields removed (they're already spent, as the keys that
|
|
126
184
|
# reached it). The same "exactly one many-side head" rule
|
|
127
185
|
# `seal_query_options` already enforces for where/order_by/etc
|
|
128
186
|
# applies here too (`seal_group_by`) — grouping is a question
|
|
129
|
-
# about
|
|
187
|
+
# about one collection's own rows, same as those are.
|
|
188
|
+
# @param fields [Array<Symbol>] the eligible many-side head's own fields to nest its rows
|
|
189
|
+
# under, one level per field
|
|
190
|
+
# @return [void]
|
|
130
191
|
def group_by_impl(*fields)
|
|
131
192
|
# Hash rows, `{field:}`, not bare symbols — same shape
|
|
132
193
|
# `aggregate_heads` already uses for exactly the reason it
|
|
133
194
|
# does: the language's own self-hosted grammar (`projection
|
|
134
|
-
# .bluebook`'s `GroupByField`) has to have
|
|
195
|
+
# .bluebook`'s `GroupByField`) has to have something to read a
|
|
135
196
|
# `field:` off of when `Judge` walks this list generically: a
|
|
136
197
|
# bare `Symbol` has no attribute of its own to read.
|
|
137
198
|
@group_by = fields.map { |field| { field: field.to_sym } }
|
|
@@ -139,23 +200,23 @@ module Hecks
|
|
|
139
200
|
|
|
140
201
|
# `count` -- a bare row count over the eligible many-side head's
|
|
141
202
|
# own rows (after `where`/`order_by`/`limit`/`offset` apply, the
|
|
142
|
-
# same rows `group_by` itself would nest) --
|
|
143
|
-
# match", not "which ones". A sibling
|
|
203
|
+
# same rows `group_by` itself would nest) -- answers "how many
|
|
204
|
+
# match", not "which ones". A sibling reduction to `group_by`,
|
|
144
205
|
# not a filter: `seal_aggregation` refuses combining it with
|
|
145
206
|
# `group_by` or with `median`, the same "exactly one many-side
|
|
146
207
|
# head" rule `seal_group_by` already enforces for the same
|
|
147
208
|
# reason -- a bare marker, so `@count` is left unset (nil, not
|
|
148
|
-
# false) rather than defaulted, matching the "
|
|
149
|
-
#
|
|
209
|
+
# false) rather than defaulted, matching the "absent is not
|
|
210
|
+
# empty" reading `Lifecycle`'s own optional fields already rely
|
|
150
211
|
# on for the Judge's setter dispatch (Behaviour::ReadModel#
|
|
151
212
|
# count?, ReadModelInterpreter#aggregation_target).
|
|
152
213
|
# `count` — item #13's full metaprogrammed dispatch, slice 1
|
|
153
|
-
# (whole-project table-unification survey): the
|
|
214
|
+
# (whole-project table-unification survey): the only Keyword row
|
|
154
215
|
# filling `count` — a bare marker, now stored as literal `true`
|
|
155
216
|
# by `GenericDispatch` off that same table fact.
|
|
156
217
|
|
|
157
|
-
# `median(field)` -- the median
|
|
158
|
-
# across the eligible many-side head's own rows.
|
|
218
|
+
# `median(field)` -- the median value of one numeric field
|
|
219
|
+
# across the eligible many-side head's own rows. Even count: the
|
|
159
220
|
# average of its two middle values (the standard definition,
|
|
160
221
|
# not "the lower of the two") -- see
|
|
161
222
|
# Runtime::ReadModelInterpreter#median for where that lands and
|
|
@@ -168,16 +229,24 @@ module Hecks
|
|
|
168
229
|
# same shape as `count`, above (a bare, kind-driven coerce-and-
|
|
169
230
|
# assign).
|
|
170
231
|
|
|
171
|
-
#
|
|
172
|
-
#
|
|
232
|
+
# Assembles the declared references, includes and clauses into a `ReadModel`, after
|
|
233
|
+
# validating them.
|
|
234
|
+
#
|
|
235
|
+
# `reference_to` is optional — a read model with no root is a
|
|
236
|
+
# bulk one: every `include`d head reads its own aggregate whole
|
|
173
237
|
# (no FK match against a root that doesn't exist), and dispatch
|
|
174
|
-
# takes no id argument at all.
|
|
175
|
-
#
|
|
176
|
-
#
|
|
177
|
-
#
|
|
178
|
-
#
|
|
179
|
-
#
|
|
180
|
-
#
|
|
238
|
+
# takes no id argument at all. Requiring one would refuse a real,
|
|
239
|
+
# legitimate shape: `group_by`'s own real use (nesting an
|
|
240
|
+
# aggregate's own whole table by its own field values) has no root
|
|
241
|
+
# to speak of. Still needs to describe something — zero includes
|
|
242
|
+
# and no reference is refused.
|
|
243
|
+
#
|
|
244
|
+
# @return [Bluebook::ReadModel] the built read model
|
|
245
|
+
# @raise [Bluebook::DSL::Malformed] if neither `reference_to` nor any `include` is
|
|
246
|
+
# declared, if `where`/`order_by`/`limit`/`offset`/`group_by`/`count`/`median` name an
|
|
247
|
+
# `on:` that isn't a many-side include or are left untargeted with more than one
|
|
248
|
+
# many-side head, if `count` and `median` are both declared or combined with
|
|
249
|
+
# `group_by`, or if `cursor` is declared
|
|
181
250
|
def build
|
|
182
251
|
if !@reference_target && Array(@includes).empty?
|
|
183
252
|
raise Malformed,
|
|
@@ -200,6 +269,12 @@ module Hecks
|
|
|
200
269
|
count: @count, median_field: @median_field)
|
|
201
270
|
end
|
|
202
271
|
|
|
272
|
+
# Evaluates a `read_model` block against a fresh builder and returns what it built.
|
|
273
|
+
#
|
|
274
|
+
# @param name [String] the read model's own name
|
|
275
|
+
# @yield the read model's body, `instance_eval`'d against a new builder
|
|
276
|
+
# @return [Bluebook::ReadModel] the built read model
|
|
277
|
+
# @raise [Bluebook::DSL::Malformed] see `#build`
|
|
203
278
|
def self.build(name, &block)
|
|
204
279
|
builder = new(name)
|
|
205
280
|
builder.instance_eval(&block) if block
|
|
@@ -213,18 +288,18 @@ module Hecks
|
|
|
213
288
|
# (the "one" side, the reference target itself, is a single row;
|
|
214
289
|
# ordering, paging, or tenant-scoping one row means nothing). ADR
|
|
215
290
|
# 0055 gave `where`/`order_by`/`limit`/`offset` an `on:` to name
|
|
216
|
-
#
|
|
291
|
+
# which many-side collection they mean, so this asks two questions
|
|
217
292
|
# now instead of one:
|
|
218
293
|
#
|
|
219
294
|
# 1. Does every declared `on:` actually name a many-side included
|
|
220
295
|
# aggregate? Checked regardless of how many many-side heads
|
|
221
296
|
# exist — a typo refuses immediately, not only once ambiguity
|
|
222
297
|
# would otherwise bite.
|
|
223
|
-
# 2. Is there still an
|
|
298
|
+
# 2. Is there still an untargeted option declared (including
|
|
224
299
|
# `authorize`'s own `tenant:`, which has no `on:` of its own —
|
|
225
300
|
# a real, deliberate scope limit, see ADR 0055)? An untargeted
|
|
226
301
|
# option still needs exactly one many-side head to mean
|
|
227
|
-
# anything unambiguous — the
|
|
302
|
+
# anything unambiguous — the original rule, unchanged, and
|
|
228
303
|
# still worded the same way (`spec/runtime/
|
|
229
304
|
# read_model_interpreter_spec.rb`'s existing refusal regex
|
|
230
305
|
# still matches).
|
|
@@ -246,7 +321,7 @@ module Hecks
|
|
|
246
321
|
end
|
|
247
322
|
|
|
248
323
|
# Question 1 of `seal_query_options`'s own two, split out to keep
|
|
249
|
-
# both under the same "one job per method" shape every
|
|
324
|
+
# both under the same "one job per method" shape every other seal in
|
|
250
325
|
# this file already holds to (each raises its own one Malformed, for
|
|
251
326
|
# its own one reason).
|
|
252
327
|
def validate_declared_targets!(many)
|
|
@@ -275,7 +350,7 @@ module Hecks
|
|
|
275
350
|
end
|
|
276
351
|
|
|
277
352
|
# Same shape as `seal_query_options`, same reason — `group_by`
|
|
278
|
-
# answers a question about
|
|
353
|
+
# answers a question about one collection's own rows, so zero or
|
|
279
354
|
# several many-side heads leaves it with no unambiguous target.
|
|
280
355
|
def seal_group_by
|
|
281
356
|
return unless @group_by&.any?
|
|
@@ -289,10 +364,10 @@ module Hecks
|
|
|
289
364
|
"own rows; name which one by including only it"
|
|
290
365
|
end
|
|
291
366
|
|
|
292
|
-
# `count`/`median` are the
|
|
367
|
+
# `count`/`median` are the other two reductions a read model may
|
|
293
368
|
# declare over its one eligible collection — same "exactly one
|
|
294
369
|
# many-side head" rule as `seal_group_by`, plus a rule
|
|
295
|
-
# `seal_group_by` doesn't need: a read model reports
|
|
370
|
+
# `seal_group_by` doesn't need: a read model reports one shape,
|
|
296
371
|
# so `count` and `median` cannot both be declared, and neither
|
|
297
372
|
# may combine with `group_by` (nesting rows and reducing them to
|
|
298
373
|
# a scalar are answers to different questions ; a caller asking
|