hecks 2.7.0 → 2.9.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/exe/hecks +11 -0
- data/lib/hecks/adapters/driven/claude_code.rb +4 -104
- data/lib/hecks/adapters/driven/d1.rb +44 -237
- data/lib/hecks/adapters/driven/folder.rb +17 -76
- data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
- data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
- data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
- data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
- data/lib/hecks/adapters/driven/heki.rb +13 -42
- data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
- data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
- data/lib/hecks/adapters/driven/lambda.rb +10 -84
- data/lib/hecks/adapters/driven/local_storage.rb +5 -40
- data/lib/hecks/adapters/driven/memory.rb +18 -99
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
- data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
- data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +4 -30
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
- data/lib/hecks/adapters/driven/postgres.rb +145 -134
- data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
- data/lib/hecks/adapters/driven/prism.rb +7 -30
- data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
- data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
- data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
- data/lib/hecks/adapters/driven/sqlite.rb +128 -65
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +15 -37
- data/lib/hecks/adapters/driven.rb +5 -36
- data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
- data/lib/hecks/behaviors/dsl.rb +7 -25
- data/lib/hecks/behaviors/expectations.rb +32 -142
- data/lib/hecks/behaviors/ir.rb +7 -20
- data/lib/hecks/behaviors/rspec.rb +6 -17
- data/lib/hecks/behaviors/runner.rb +10 -34
- data/lib/hecks/behaviors.rb +4 -8
- data/lib/hecks/bluebook/aggregate.rb +9 -75
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
- data/lib/hecks/bluebook/assembly/build.rb +4 -15
- data/lib/hecks/bluebook/assembly/contract.rb +15 -136
- data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
- data/lib/hecks/bluebook/assembly/marks.rb +31 -223
- data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
- data/lib/hecks/bluebook/assembly.rb +6 -45
- data/lib/hecks/bluebook/attribute.rb +12 -61
- data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
- data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
- data/lib/hecks/bluebook/behaviour/chapter.rb +17 -42
- data/lib/hecks/bluebook/behaviour/command.rb +14 -66
- data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
- data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
- data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
- data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
- data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
- data/lib/hecks/bluebook/behaviour/read_model.rb +38 -26
- data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
- data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
- data/lib/hecks/bluebook/capabilities.rb +17 -49
- data/lib/hecks/bluebook/chapter.rb +10 -48
- data/lib/hecks/bluebook/command.rb +23 -81
- data/lib/hecks/bluebook/domain_port.rb +14 -63
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +17 -65
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
- data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
- data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -189
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
- data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
- data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
- data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
- data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
- data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
- data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
- data/lib/hecks/bluebook/dsl/world_builder.rb +19 -106
- data/lib/hecks/bluebook/entity.rb +13 -71
- data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
- data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
- data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
- data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
- data/lib/hecks/bluebook/expression/resolver.rb +37 -660
- data/lib/hecks/bluebook/hexagon.rb +5 -28
- data/lib/hecks/bluebook/lifecycle.rb +5 -16
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
- data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
- data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
- data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
- data/lib/hecks/bluebook/meta_validator/shapes.rb +54 -268
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
- data/lib/hecks/bluebook/meta_validator.rb +79 -349
- data/lib/hecks/bluebook/model_check/client_profile.rb +10 -91
- data/lib/hecks/bluebook/model_check.rb +102 -600
- data/lib/hecks/bluebook/pattern_subset.rb +11 -69
- data/lib/hecks/bluebook/process_manager.rb +13 -83
- data/lib/hecks/bluebook/project_loader.rb +3 -39
- data/lib/hecks/bluebook/project_register.rb +12 -60
- data/lib/hecks/bluebook/query.rb +9 -48
- data/lib/hecks/bluebook/read_model.rb +57 -81
- data/lib/hecks/bluebook/reference.rb +7 -41
- data/lib/hecks/bluebook/smoke_test.rb +8 -98
- data/lib/hecks/bluebook/synthesizer.rb +13 -62
- data/lib/hecks/bluebook/translation.rb +26 -62
- data/lib/hecks/bluebook/value_object.rb +11 -36
- data/lib/hecks/bluebook.rb +5 -22
- data/lib/hecks/cache_dir.rb +84 -0
- data/lib/hecks/cli/document.rb +86 -0
- data/lib/hecks/cli/ir.rb +45 -0
- data/lib/hecks/cli/mcp.rb +31 -0
- data/lib/hecks/cli/mcp_door.rb +366 -0
- data/lib/hecks/cli/model_check.rb +203 -0
- data/lib/hecks/cli/project_cli.rb +90 -0
- data/lib/hecks/cli/project_diagrams.rb +36 -0
- data/lib/hecks/cli/run.rb +173 -0
- data/lib/hecks/cli/smoke_test.rb +45 -0
- data/lib/hecks/cli/stores.rb +53 -0
- data/lib/hecks/cli.rb +160 -0
- data/lib/hecks/construct.rb +8 -34
- data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
- data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
- data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
- data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
- data/lib/hecks/embryonaut_bluebook.rb +4 -71
- data/lib/hecks/facade/cli_door.rb +24 -106
- data/lib/hecks/facade/cli_runner.rb +28 -172
- data/lib/hecks/facade/command_request.rb +13 -33
- data/lib/hecks/facade/handle.rb +34 -121
- data/lib/hecks/facade/json_door.rb +31 -146
- data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
- data/lib/hecks/facade/surface/chapter.rb +10 -54
- data/lib/hecks/facade/surface.rb +8 -23
- data/lib/hecks/facade.rb +10 -25
- data/lib/hecks/forms/app.rb +15 -70
- data/lib/hecks/forms/command_form_renderer.rb +10 -29
- data/lib/hecks/forms/field_renderer.rb +17 -156
- data/lib/hecks/forms/field_shape.rb +30 -222
- data/lib/hecks/forms/html.rb +12 -47
- data/lib/hecks/forms/index_renderer.rb +1 -4
- data/lib/hecks/forms/page.rb +3 -9
- data/lib/hecks/forms/params.rb +14 -60
- data/lib/hecks/forms/port_argument.rb +2 -19
- data/lib/hecks/forms/query_form_renderer.rb +4 -79
- data/lib/hecks/forms/record_renderer.rb +8 -75
- data/lib/hecks/forms/record_table.rb +7 -50
- data/lib/hecks/forms/reference_options.rb +3 -30
- data/lib/hecks/forms/value_object_shape.rb +8 -35
- data/lib/hecks/forms.rb +5 -33
- data/lib/hecks/fqn.rb +5 -12
- data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
- data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
- data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
- data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
- data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
- data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
- data/lib/hecks/framework.rb +11 -80
- data/lib/hecks/freezer.rb +5 -39
- data/lib/hecks/grammar/expression.bluebook +15 -40
- data/lib/hecks/grammar/translation.bluebook +3 -6
- data/lib/hecks/grammar.rb +16 -41
- data/lib/hecks/ir.rb +14 -83
- data/lib/hecks/language/adapter.bluebook +2 -7
- data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
- data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
- data/lib/hecks/language/bluebook/command.bluebook +27 -144
- data/lib/hecks/language/bluebook/entity.bluebook +24 -97
- data/lib/hecks/language/bluebook/policy.bluebook +16 -67
- data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
- data/lib/hecks/language/bluebook/projection.bluebook +126 -60
- data/lib/hecks/language/bluebook/query.bluebook +12 -43
- data/lib/hecks/language/bluebook/shape.bluebook +23 -73
- data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
- data/lib/hecks/language/bluebook/vocabulary.bluebook +98 -197
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
- data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
- data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
- data/lib/hecks/language/oidc.json +35 -0
- data/lib/hecks/language/port.bluebook +4 -17
- data/lib/hecks/language/translation/translation.bluebook +3 -9
- data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
- data/lib/hecks/language/world/wiring.bluebook +1 -3
- data/lib/hecks/language/world/world.bluebook +5 -7
- data/lib/hecks/literal.rb +8 -50
- data/lib/hecks/mcp_door_scope.rb +95 -0
- data/lib/hecks/mcp_stdio_guard.rb +4 -41
- data/lib/hecks/naming.rb +29 -137
- data/lib/hecks/ports/access_control.rb +7 -23
- data/lib/hecks/ports/agent/answers.rb +5 -23
- data/lib/hecks/ports/agent.rb +47 -172
- data/lib/hecks/ports/authentication.rb +12 -39
- data/lib/hecks/ports/authorization.rb +16 -43
- data/lib/hecks/ports/clock.rb +5 -45
- data/lib/hecks/ports/extraction.rb +7 -12
- data/lib/hecks/ports/identity_assignment.rb +8 -29
- data/lib/hecks/ports/identity_generation.rb +6 -29
- data/lib/hecks/ports/identity_resolution.rb +2 -10
- data/lib/hecks/ports/key_vault.rb +9 -45
- data/lib/hecks/ports/persistence/append_only.rb +62 -65
- data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
- data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
- data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
- data/lib/hecks/ports/persistence/plugin.rb +13 -42
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +7 -26
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +22 -122
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
- data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
- data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
- data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
- data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
- data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
- data/lib/hecks/ports/persistence/state_codec.rb +23 -103
- data/lib/hecks/ports/persistence.rb +7 -14
- data/lib/hecks/ports/projection.rb +42 -84
- data/lib/hecks/ports/query/in_memory.rb +10 -58
- data/lib/hecks/ports/query/ordering.rb +11 -32
- data/lib/hecks/ports/query.rb +14 -37
- data/lib/hecks/ports.rb +2 -8
- data/lib/hecks/projections/bootstrap_table.rb +11 -43
- data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
- data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
- data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
- data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
- data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
- data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
- data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
- data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
- data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
- data/lib/hecks/projections/deploy/fargate.rb +35 -202
- data/lib/hecks/projections/deploy/lambda.rb +151 -679
- data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
- data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
- data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
- data/lib/hecks/projections/deploy/preview/script.rb +2 -14
- data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
- data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
- data/lib/hecks/projections/deploy/preview/template.rb +2 -58
- data/lib/hecks/projections/deploy/preview.rb +2 -67
- data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
- data/lib/hecks/projections/deploy/scripts.rb +4 -57
- data/lib/hecks/projections/deploy/shared.rb +47 -146
- data/lib/hecks/projections/deploy/smoke.rb +4 -61
- data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
- data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
- data/lib/hecks/projections/deploy/template_diff.rb +5 -57
- data/lib/hecks/projections/diagrams.rb +20 -582
- data/lib/hecks/projections/glossary/html.rb +14 -130
- data/lib/hecks/projections/glossary/markdown.rb +8 -69
- data/lib/hecks/projections/glossary/mermaid.rb +9 -66
- data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
- data/lib/hecks/projections/glossary/sentences.rb +14 -159
- data/lib/hecks/projections/glossary.rb +30 -222
- data/lib/hecks/projections/ir.rb +3 -10
- data/lib/hecks/projections/model/deviations.rb +15 -42
- data/lib/hecks/projections/model.rb +12 -51
- data/lib/hecks/projections/oidc.rb +12 -56
- data/lib/hecks/projections/parser_table.rb +9 -28
- data/lib/hecks/projections/reference.rb +9 -16
- data/lib/hecks/projections/rust_vocabulary.rb +12 -250
- data/lib/hecks/projections/shape.rb +3 -29
- data/lib/hecks/projections/statements.rb +7 -86
- data/lib/hecks/projections/vocabulary.rb +6 -22
- data/lib/hecks/projections.rb +2 -16
- data/lib/hecks/projector/cli_projector.rb +60 -375
- data/lib/hecks/projector/docs_projector.rb +10 -201
- data/lib/hecks/projector/exporter.rb +46 -138
- data/lib/hecks/projector/ir_projector.rb +2 -9
- data/lib/hecks/projector/narrate_projector.rb +15 -189
- data/lib/hecks/projector/target.rb +13 -76
- data/lib/hecks/projector.rb +30 -121
- data/lib/hecks/query_specification/common/comparators.rb +5 -26
- data/lib/hecks/query_specification/common/comparison.rb +35 -143
- data/lib/hecks/query_specification/common/dsl.rb +8 -24
- data/lib/hecks/query_specification/common/null_policy.rb +20 -70
- data/lib/hecks/query_specification/common/options.rb +2 -6
- data/lib/hecks/query_specification/common/where_clause.rb +2 -10
- data/lib/hecks/query_specification/field_path.rb +37 -77
- data/lib/hecks/query_specification/hop_path.rb +12 -76
- data/lib/hecks/release/gem_pin.rb +2 -26
- data/lib/hecks/release/runner/ci_publisher.rb +93 -0
- data/lib/hecks/release/runner/commands.rb +66 -0
- data/lib/hecks/release/runner/console.rb +52 -0
- data/lib/hecks/release/runner/gem_publisher.rb +43 -0
- data/lib/hecks/release/runner/git.rb +48 -0
- data/lib/hecks/release/runner/npm_publisher.rb +75 -0
- data/lib/hecks/release/runner/preflight.rb +104 -0
- data/lib/hecks/release/runner/published.rb +55 -0
- data/lib/hecks/release/runner/tagger.rb +80 -0
- data/lib/hecks/release/runner.rb +215 -0
- data/lib/hecks/rendering.rb +4 -26
- data/lib/hecks/router/namespace_installer.rb +12 -36
- data/lib/hecks/router.rb +4 -8
- data/lib/hecks/runtime/aggregate_lock.rb +8 -33
- data/lib/hecks/runtime/boot_gates.rb +3 -18
- data/lib/hecks/runtime/caller.rb +19 -59
- data/lib/hecks/runtime/capability_graph.rb +5 -21
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
- data/lib/hecks/runtime/command_interpreter.rb +125 -307
- data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
- data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
- data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
- data/lib/hecks/runtime/command_rules/emission.rb +8 -21
- data/lib/hecks/runtime/command_rules/references.rb +23 -202
- data/lib/hecks/runtime/dependency_planning.rb +30 -102
- data/lib/hecks/runtime/dispatcher.rb +82 -241
- data/lib/hecks/runtime/entity_element.rb +40 -462
- data/lib/hecks/runtime/entity_interpreter.rb +65 -220
- data/lib/hecks/runtime/errors.rb +32 -115
- data/lib/hecks/runtime/event.rb +3 -25
- data/lib/hecks/runtime/identity.rb +18 -154
- data/lib/hecks/runtime/instance.rb +18 -150
- data/lib/hecks/runtime/interpreting.rb +11 -70
- data/lib/hecks/runtime/invocation.rb +28 -164
- data/lib/hecks/runtime/loader.rb +48 -186
- data/lib/hecks/runtime/outbox.rb +38 -261
- data/lib/hecks/runtime/policy_interpreter.rb +38 -200
- data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
- data/lib/hecks/runtime/query_interpreter.rb +45 -187
- data/lib/hecks/runtime/reaction_invocation.rb +36 -138
- data/lib/hecks/runtime/read_model_interpreter.rb +139 -208
- data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
- data/lib/hecks/runtime/reference_hop.rb +8 -81
- data/lib/hecks/runtime/refusal_wording.rb +8 -89
- data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
- data/lib/hecks/runtime/registry/verification.rb +52 -225
- data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
- data/lib/hecks/runtime/registry.rb +103 -244
- data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
- data/lib/hecks/runtime/routing.rb +4 -15
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
- data/lib/hecks/runtime/saga_interpreter.rb +79 -263
- data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
- data/lib/hecks/runtime/tenant_check.rb +6 -68
- data/lib/hecks/runtime/tenant_scope.rb +3 -28
- data/lib/hecks/runtime/value/admission.rb +16 -67
- data/lib/hecks/runtime/value/coercion.rb +155 -641
- data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
- data/lib/hecks/runtime/value.rb +12 -84
- data/lib/hecks/runtime.rb +17 -65
- data/lib/hecks/storehouse.rb +146 -567
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
- data/lib/hecks/vendoring/git_environment.rb +2 -8
- data/lib/hecks/vendoring/git_source.rb +4 -10
- data/lib/hecks/vendoring.rb +7 -46
- data/lib/hecks/version.rb +3 -14
- data/lib/hecks/vocabulary.rb +5 -0
- data/lib/hecks.rb +32 -82
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
- metadata +30 -59
- data/lib/hecks/bench/cli.rb +0 -74
- data/lib/hecks/bench/environment.rb +0 -100
- data/lib/hecks/bench/postgres_probe.rb +0 -41
- data/lib/hecks/bench/report.rb +0 -116
- data/lib/hecks/bench/ruby_runner.rb +0 -151
- data/lib/hecks/bench/run.rb +0 -55
- data/lib/hecks/bench/rust_runner.rb +0 -165
- data/lib/hecks/bench/stats.rb +0 -75
- data/lib/hecks/bench/suite.rb +0 -172
- data/lib/hecks/bench/workload.rb +0 -137
- data/lib/hecks/bench.rb +0 -28
- data/lib/hecks/codemod.rb +0 -443
- data/lib/hecks/corpus.rb +0 -479
- data/lib/hecks/doc/reference.rb +0 -585
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
- data/lib/hecks/fuzzing/combination_miner.rb +0 -178
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
- data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
- data/lib/hecks/fuzzing/differential.rb +0 -192
- data/lib/hecks/fuzzing/domain_generator.rb +0 -871
- data/lib/hecks/fuzzing/era_boundary.rb +0 -154
- data/lib/hecks/fuzzing/form_census.rb +0 -285
- data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
- data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
- data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
- data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
- data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
- data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
- data/lib/hecks/fuzzing/properties/guards.rb +0 -398
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
- data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
- data/lib/hecks/fuzzing/properties/querying.rb +0 -260
- data/lib/hecks/fuzzing/properties.rb +0 -267
- data/lib/hecks/fuzzing/qa_settings.rb +0 -164
- data/lib/hecks/fuzzing/replay.rb +0 -930
- data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
- data/lib/hecks/fuzzing/self_consistency.rb +0 -825
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
- data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
- data/lib/hecks/fuzzing/shrinker.rb +0 -309
- data/lib/hecks/fuzzing/structural_skips.rb +0 -53
- data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
- data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
- data/lib/hecks/fuzzing/value_generator.rb +0 -340
- data/lib/hecks/fuzzing.rb +0 -23
- data/lib/hecks/grammar/evolve.rb +0 -524
- data/lib/hecks/query_ir.rb +0 -482
|
@@ -2,12 +2,8 @@ require_relative "word_gate"
|
|
|
2
2
|
module Hecks
|
|
3
3
|
module Bluebook
|
|
4
4
|
module DSL
|
|
5
|
-
# Parses a `read_model "Name" do ... end` block into a `ReadModel
|
|
6
|
-
#
|
|
7
|
-
# root (omitted, it's a rootless "bulk" read model) plus one or more
|
|
8
|
-
# `include`d aggregate heads, with `where`/`order_by`/`limit`/etc, and
|
|
9
|
-
# at most one of `group_by`, `count`, or `median` as its single
|
|
10
|
-
# reduction over the one eligible many-side collection.
|
|
5
|
+
# Parses a `read_model "Name" do ... end` block into a `ReadModel`: a cross-aggregate
|
|
6
|
+
# projection over an optional `reference_to` root and its `include`d aggregate heads.
|
|
11
7
|
class ReadModelBuilder
|
|
12
8
|
GRAMMAR_CONTEXT = "ReadModel".freeze
|
|
13
9
|
|
|
@@ -24,16 +20,10 @@ module Hecks
|
|
|
24
20
|
# @param value [String] the description text
|
|
25
21
|
# @return [String] the description as stored
|
|
26
22
|
def description(value)
|
|
27
|
-
# moved to the language: ProjectionText / purpose, on Projection.Declare
|
|
28
23
|
@description = value
|
|
29
24
|
end
|
|
30
25
|
|
|
31
|
-
# Declares the read model's own "one" side
|
|
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.
|
|
26
|
+
# Declares the read model's own "one" side, the aggregate head every row projects around.
|
|
37
27
|
#
|
|
38
28
|
# @param type [Module, Symbol, String] the referenced aggregate, written as a bare
|
|
39
29
|
# constant
|
|
@@ -50,21 +40,8 @@ module Hecks
|
|
|
50
40
|
|
|
51
41
|
# Adds one aggregate head this read model projects.
|
|
52
42
|
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
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.
|
|
43
|
+
# `many:` is decided at build time against the reference target, so an include declared
|
|
44
|
+
# before `reference_to` resolves like one declared after it.
|
|
68
45
|
#
|
|
69
46
|
# @param type [Module, Symbol, String] the included aggregate, written as a bare constant
|
|
70
47
|
# @param as [Symbol, nil] the field name to project this head's rows under; `nil` derives
|
|
@@ -75,64 +52,21 @@ module Hecks
|
|
|
75
52
|
@includes << [Naming.demodulise(type), as]
|
|
76
53
|
end
|
|
77
54
|
|
|
78
|
-
# `on:`
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
# `
|
|
83
|
-
# ignored no-op rather than a real answer. Overriding only here
|
|
84
|
-
# means a `Query`'s own `where(..., on: X)` gets Ruby's own loud
|
|
85
|
-
# `unknown keyword: :on` instead of quietly doing nothing.
|
|
86
|
-
#
|
|
87
|
-
# `on:` names the target by type (`on: Character`), resolved the
|
|
88
|
-
# same way `reference_to`/`include` already resolve their own type
|
|
89
|
-
# argument (`Naming.demodulise`) — not by the include's own `as:`
|
|
90
|
-
# alias. A read model that `include`s the same type twice under two
|
|
91
|
-
# different `as:` has no way to say which one `on:` means today; no
|
|
92
|
-
# real corpus read model does this, so it's a real, deliberate scope
|
|
93
|
-
# limit (see ADR 0055), not an oversight.
|
|
94
|
-
#
|
|
95
|
-
# `*positional, on:, **rest` rather than a plain `(clauses, on: nil)`
|
|
96
|
-
# — found necessary, not stylistic, by reproducing the failure
|
|
97
|
-
# directly: `where(status: "disputed")` reaches here with its
|
|
98
|
-
# `status: "disputed"` captured as `**kwargs` (GenericDispatch's own
|
|
99
|
-
# `builder.send(calls, *args, **kwargs, &block)`), and Ruby stops
|
|
100
|
-
# auto-converting a bare `**hash` call into a plain positional Hash
|
|
101
|
-
# the moment a method declares any real keyword parameter — so a
|
|
102
|
-
# `(clauses, on: nil)` signature raised "wrong number of arguments
|
|
103
|
-
# (given 0, expected 1)" on every ordinary `where(field: value)`
|
|
104
|
-
# call, never reaching `on:` at all. `**rest` sidesteps this: Ruby
|
|
105
|
-
# still auto-splits `on:` into the declared keyword and gathers
|
|
106
|
-
# every other key into `rest` regardless of how the caller wrote it.
|
|
107
|
-
#
|
|
108
|
-
# `QuerySpecification::Common::WhereClause` etc — fully qualified,
|
|
109
|
-
# not the bare names `dsl.rb`'s own shared `where_impl` gets away
|
|
110
|
-
# with. That file is lexically nested inside `Common` itself, so
|
|
111
|
-
# `WhereClause` resolves directly; this class is nested inside
|
|
112
|
-
# `Bluebook::DSL`, which has no lexical or ancestor path to
|
|
113
|
-
# `QuerySpecification::Common` at all — a bare `WhereClause` here
|
|
114
|
-
# falls through to `const_missing` and, mid-bluebook-load, that's
|
|
115
|
-
# `ConstShim`, which resolves it against the self-hosted grammar
|
|
116
|
-
# domain's own unrelated `WhereClause` construct instead (a `Module`,
|
|
117
|
-
# not this `Struct`) — found directly by reproducing "undefined
|
|
118
|
-
# method `new' for module WhereClause" against a real corpus load,
|
|
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.
|
|
55
|
+
# The `on:` overrides of the shared where/order_by/limit/offset are kept off the shared
|
|
56
|
+
# module so a plain `query` still raises `unknown keyword: :on`. `on:` names its target
|
|
57
|
+
# by type, not by an include's `as:` alias (ADR 0055).
|
|
58
|
+
|
|
59
|
+
# Records one `WhereClause` per `field => value` pair, optionally targeted with `on:`.
|
|
123
60
|
#
|
|
124
|
-
# @param positional [Array<Hash
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
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`
|
|
61
|
+
# @param positional [Array<Hash>] at most one bare Hash of clauses; normally empty
|
|
62
|
+
# @param on [Module, Symbol, String, nil] the many-side include the clauses apply to
|
|
63
|
+
# @param rest [Hash{Symbol => Object}] `field => value`; a value is a literal, a Symbol
|
|
64
|
+
# naming a query argument, or a one-pair `{ comparator => operand }` Hash
|
|
132
65
|
# @return [void]
|
|
133
|
-
# @raise [ArgumentError]
|
|
134
|
-
# does not have exactly one pair, or names a comparator outside `COMPARATORS`
|
|
66
|
+
# @raise [ArgumentError] on more than one positional argument or a malformed comparator
|
|
135
67
|
def where_impl(*positional, on: nil, **rest)
|
|
68
|
+
# `**rest` because a declared keyword stops Ruby turning a bare `where(status: "x")`
|
|
69
|
+
# into a positional Hash.
|
|
136
70
|
raise ArgumentError, "wrong number of arguments (given #{positional.size}, expected 1)" if positional.size > 1
|
|
137
71
|
|
|
138
72
|
@wheres ||= []
|
|
@@ -140,6 +74,8 @@ module Hecks
|
|
|
140
74
|
clauses = (positional.first || {}).merge(rest)
|
|
141
75
|
clauses.each do |field, value|
|
|
142
76
|
op, operand = split_comparator(value)
|
|
77
|
+
# Fully qualified: a bare `WhereClause` reaches `ConstShim` and resolves to the
|
|
78
|
+
# grammar domain's unrelated module.
|
|
143
79
|
@wheres << QuerySpecification::Common::WhereClause.new(field: field, op: op, value: operand, target: target)
|
|
144
80
|
end
|
|
145
81
|
end
|
|
@@ -178,74 +114,50 @@ module Hecks
|
|
|
178
114
|
@offset = QuerySpecification::Common::OffsetSpec.new(value: value, target: resolve_target(on))
|
|
179
115
|
end
|
|
180
116
|
|
|
181
|
-
# Names which of the eligible head's own fields to nest its rows
|
|
182
|
-
#
|
|
183
|
-
#
|
|
184
|
-
# reached it). The same "exactly one many-side head" rule
|
|
185
|
-
# `seal_query_options` already enforces for where/order_by/etc
|
|
186
|
-
# applies here too (`seal_group_by`) — grouping is a question
|
|
187
|
-
# about one collection's own rows, same as those are.
|
|
117
|
+
# Names which of the eligible head's own fields to nest its rows under, one level per
|
|
118
|
+
# field; the leaf is the row with those fields removed.
|
|
119
|
+
#
|
|
188
120
|
# @param fields [Array<Symbol>] the eligible many-side head's own fields to nest its rows
|
|
189
121
|
# under, one level per field
|
|
190
122
|
# @return [void]
|
|
191
123
|
def group_by_impl(*fields)
|
|
192
|
-
# Hash rows
|
|
193
|
-
# `
|
|
194
|
-
# does: the language's own self-hosted grammar (`projection
|
|
195
|
-
# .bluebook`'s `GroupByField`) has to have something to read a
|
|
196
|
-
# `field:` off of when `Judge` walks this list generically: a
|
|
197
|
-
# bare `Symbol` has no attribute of its own to read.
|
|
124
|
+
# Hash rows rather than bare symbols: the self-hosted `GroupByField` grammar reads
|
|
125
|
+
# `field:` off each entry when `Judge` walks the list.
|
|
198
126
|
@group_by = fields.map { |field| { field: field.to_sym } }
|
|
199
127
|
end
|
|
200
128
|
|
|
201
|
-
# `
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
|
|
205
|
-
#
|
|
206
|
-
#
|
|
207
|
-
#
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
#
|
|
211
|
-
#
|
|
212
|
-
#
|
|
213
|
-
# `
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
# Runtime::ReadModelInterpreter#median for where that lands and
|
|
223
|
-
# is documented for a caller. `field` must name a numeric
|
|
224
|
-
# attribute (a bare numeric primitive, or a value object
|
|
225
|
-
# carrying one) -- checked once, at read time, by
|
|
226
|
-
# ReadModelInterpreter#aggregation_target, the same place
|
|
227
|
-
# `group_by`'s own field names are checked.
|
|
228
|
-
# `median` — item #13's full metaprogrammed dispatch, slice 1:
|
|
229
|
-
# same shape as `count`, above (a bare, kind-driven coerce-and-
|
|
230
|
-
# assign).
|
|
129
|
+
# `sum`/`avg`/`min`/`max`/`any`/`all` need no method of their own: each is one
|
|
130
|
+
# required positional symbol, the same shape as `median`, which `GenericDispatch`
|
|
131
|
+
# already handles generically off the grammar table (ADR 0078).
|
|
132
|
+
|
|
133
|
+
# The field's value at one interpolated rank (`0.0`..`1.0`) across the eligible
|
|
134
|
+
# many-side head's own rows; `median` is the fixed `at: 0.5` case (ADR 0078). Takes
|
|
135
|
+
# a named argument alongside its field, which is outside `GenericDispatch`'s
|
|
136
|
+
# one-positional-argument shape, so it needs a method of its own.
|
|
137
|
+
#
|
|
138
|
+
# @param field [Symbol] the eligible many-side head's own numeric field
|
|
139
|
+
# @param at [Float] the rank to interpolate, `0.0` (the minimum) through `1.0` (the maximum)
|
|
140
|
+
# @return [void]
|
|
141
|
+
# @raise [ArgumentError] if `at` is not a `Float` in `0.0..1.0`
|
|
142
|
+
def percentile_impl(field, at:)
|
|
143
|
+
unless at.is_a?(Float) && (0.0..1.0).cover?(at)
|
|
144
|
+
raise ArgumentError, "percentile's at: must be a Float between 0.0 and 1.0 (given #{at.inspect})"
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
@percentile_field = field.to_sym
|
|
148
|
+
@percentile_at = at
|
|
149
|
+
end
|
|
231
150
|
|
|
232
151
|
# Assembles the declared references, includes and clauses into a `ReadModel`, after
|
|
233
152
|
# validating them.
|
|
234
153
|
#
|
|
235
|
-
# `reference_to` is optional
|
|
236
|
-
#
|
|
237
|
-
# (no FK match against a root that doesn't exist), and dispatch
|
|
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
|
-
#
|
|
154
|
+
# `reference_to` is optional: a rootless read model is a bulk one and takes no id.
|
|
155
|
+
# Zero includes and no reference is refused.
|
|
244
156
|
# @return [Bluebook::ReadModel] the built read model
|
|
245
157
|
# @raise [Bluebook::DSL::Malformed] if neither `reference_to` nor any `include` is
|
|
246
|
-
# declared, if `where`/`order_by`/`limit`/`offset`/`group_by
|
|
158
|
+
# declared, if `where`/`order_by`/`limit`/`offset`/`group_by` or a reduction name an
|
|
247
159
|
# `on:` that isn't a many-side include or are left untargeted with more than one
|
|
248
|
-
# many-side head, if
|
|
160
|
+
# many-side head, if more than one reduction is declared or one is combined with
|
|
249
161
|
# `group_by`, or if `cursor` is declared
|
|
250
162
|
def build
|
|
251
163
|
if !@reference_target && Array(@includes).empty?
|
|
@@ -266,7 +178,10 @@ module Hecks
|
|
|
266
178
|
cursor: @cursor,
|
|
267
179
|
authorization: @authorization, null_semantics: @null_semantics,
|
|
268
180
|
inspection: @inspection, group_by: @group_by || [],
|
|
269
|
-
count: @count, median_field: @median_field
|
|
181
|
+
count: @count, median_field: @median_field,
|
|
182
|
+
sum_field: @sum_field, avg_field: @avg_field, min_field: @min_field,
|
|
183
|
+
max_field: @max_field, percentile_field: @percentile_field,
|
|
184
|
+
percentile_at: @percentile_at, any_field: @any_field, all_field: @all_field)
|
|
270
185
|
end
|
|
271
186
|
|
|
272
187
|
# Evaluates a `read_model` block against a fresh builder and returns what it built.
|
|
@@ -283,30 +198,9 @@ module Hecks
|
|
|
283
198
|
|
|
284
199
|
private
|
|
285
200
|
|
|
286
|
-
# where/order_by/limit/offset/authorize's tenant
|
|
287
|
-
#
|
|
288
|
-
#
|
|
289
|
-
# ordering, paging, or tenant-scoping one row means nothing). ADR
|
|
290
|
-
# 0055 gave `where`/`order_by`/`limit`/`offset` an `on:` to name
|
|
291
|
-
# which many-side collection they mean, so this asks two questions
|
|
292
|
-
# now instead of one:
|
|
293
|
-
#
|
|
294
|
-
# 1. Does every declared `on:` actually name a many-side included
|
|
295
|
-
# aggregate? Checked regardless of how many many-side heads
|
|
296
|
-
# exist — a typo refuses immediately, not only once ambiguity
|
|
297
|
-
# would otherwise bite.
|
|
298
|
-
# 2. Is there still an untargeted option declared (including
|
|
299
|
-
# `authorize`'s own `tenant:`, which has no `on:` of its own —
|
|
300
|
-
# a real, deliberate scope limit, see ADR 0055)? An untargeted
|
|
301
|
-
# option still needs exactly one many-side head to mean
|
|
302
|
-
# anything unambiguous — the original rule, unchanged, and
|
|
303
|
-
# still worded the same way (`spec/runtime/
|
|
304
|
-
# read_model_interpreter_spec.rb`'s existing refusal regex
|
|
305
|
-
# still matches).
|
|
306
|
-
#
|
|
307
|
-
# A read model with several many-side heads is legal precisely when
|
|
308
|
-
# every declared option names one; a read model with a single
|
|
309
|
-
# many-side head is unaffected either way, `on:` or not.
|
|
201
|
+
# where/order_by/limit/offset/authorize's tenant apply to the many-side collections;
|
|
202
|
+
# the reference target is a single row. Every `on:` must name a many-side include, and
|
|
203
|
+
# any untargeted option needs exactly one many-side head (ADR 0055).
|
|
310
204
|
def seal_query_options
|
|
311
205
|
many = Array(@aggregate_heads).select { |head| head[:many] }
|
|
312
206
|
|
|
@@ -320,10 +214,7 @@ module Hecks
|
|
|
320
214
|
"name which one with `on:` (e.g. `where(field: value, on: Character)`), or drop the options"
|
|
321
215
|
end
|
|
322
216
|
|
|
323
|
-
#
|
|
324
|
-
# both under the same "one job per method" shape every other seal in
|
|
325
|
-
# this file already holds to (each raises its own one Malformed, for
|
|
326
|
-
# its own one reason).
|
|
217
|
+
# Refuses an `on:` that does not name one of the many-side includes.
|
|
327
218
|
def validate_declared_targets!(many)
|
|
328
219
|
many_by_aggregate = many.to_h { |head| [head[:aggregate], head] }
|
|
329
220
|
declared_targets = Array(@wheres).map(&:target) + [@order_by&.target, @limit&.target, @offset&.target]
|
|
@@ -338,9 +229,7 @@ module Hecks
|
|
|
338
229
|
end
|
|
339
230
|
end
|
|
340
231
|
|
|
341
|
-
#
|
|
342
|
-
# header. `authorize`'s own `tenant:` has no `on:` at all (ADR 0055's
|
|
343
|
-
# own documented scope limit), so it always counts as untargeted.
|
|
232
|
+
# `authorize`'s `tenant:` has no `on:`, so it always counts as untargeted.
|
|
344
233
|
def untargeted_option_declared?
|
|
345
234
|
Array(@wheres).any? { |where| where.target.nil? } ||
|
|
346
235
|
(@order_by && @order_by.target.nil?) ||
|
|
@@ -349,9 +238,7 @@ module Hecks
|
|
|
349
238
|
@authorization&.tenant
|
|
350
239
|
end
|
|
351
240
|
|
|
352
|
-
# Same
|
|
353
|
-
# answers a question about one collection's own rows, so zero or
|
|
354
|
-
# several many-side heads leaves it with no unambiguous target.
|
|
241
|
+
# Same rule as `seal_query_options`: `group_by` needs exactly one many-side head.
|
|
355
242
|
def seal_group_by
|
|
356
243
|
return unless @group_by&.any?
|
|
357
244
|
|
|
@@ -364,45 +251,35 @@ module Hecks
|
|
|
364
251
|
"own rows; name which one by including only it"
|
|
365
252
|
end
|
|
366
253
|
|
|
367
|
-
# `
|
|
368
|
-
#
|
|
369
|
-
#
|
|
370
|
-
# `seal_group_by` doesn't need: a read model reports one shape,
|
|
371
|
-
# so `count` and `median` cannot both be declared, and neither
|
|
372
|
-
# may combine with `group_by` (nesting rows and reducing them to
|
|
373
|
-
# a scalar are answers to different questions ; a caller asking
|
|
374
|
-
# "how many, nested by state" is not a shape this read model
|
|
375
|
-
# produces today — a real, deliberate scope limit, not an
|
|
376
|
-
# oversight, the same discipline the rootless model's own
|
|
377
|
-
# "each head reads independently" limit already documents).
|
|
254
|
+
# Every reduction (`Behaviour::ReadModel::REDUCTION_FIELDS`) needs exactly one
|
|
255
|
+
# many-side head, cannot be declared alongside another, and cannot combine with
|
|
256
|
+
# `group_by`: a read model reports one shape.
|
|
378
257
|
def seal_aggregation
|
|
379
|
-
|
|
258
|
+
declared = Behaviour::ReadModel::REDUCTION_FIELDS
|
|
259
|
+
.select { |ivar, _| instance_variable_get(:"@#{ivar}") }.values
|
|
260
|
+
return if declared.empty?
|
|
380
261
|
|
|
381
|
-
if @count && @median_field
|
|
382
|
-
raise Malformed,
|
|
383
|
-
"#{@name} declares both count and median — a read model reports " \
|
|
384
|
-
"one shape; choose one"
|
|
385
|
-
end
|
|
386
262
|
if @group_by&.any?
|
|
387
263
|
raise Malformed,
|
|
388
|
-
"#{@name} declares
|
|
264
|
+
"#{@name} declares #{declared.join('/')} together with group_by — a read " \
|
|
389
265
|
"model reports one shape; choose one"
|
|
390
266
|
end
|
|
267
|
+
if declared.size > 1
|
|
268
|
+
joiner = declared.size == 2 ? "both #{declared.join(' and ')}" : declared.join(", ")
|
|
269
|
+
raise Malformed, "#{@name} declares #{joiner} — a read model reports one shape; choose one"
|
|
270
|
+
end
|
|
391
271
|
|
|
392
272
|
many = Array(@aggregate_heads).count { |head| head[:many] }
|
|
393
273
|
return if many == 1
|
|
394
274
|
|
|
395
275
|
raise Malformed,
|
|
396
|
-
"#{@name} declares
|
|
397
|
-
"aggregates, not exactly one —
|
|
276
|
+
"#{@name} declares #{declared.first} but includes #{many} many-side " \
|
|
277
|
+
"aggregates, not exactly one — #{declared.first} reduces a single " \
|
|
398
278
|
"collection's own rows; name which one by including only it"
|
|
399
279
|
end
|
|
400
280
|
|
|
401
|
-
# `cursor`
|
|
402
|
-
#
|
|
403
|
-
# it here, rather than deleting the word, keeps the declared syntax
|
|
404
|
-
# honest (the language still knows the shape) while refusing to let a
|
|
405
|
-
# bluebook author believe cursor-based pagination actually happens.
|
|
281
|
+
# `cursor` is parsed but no interpreter applies it, so it is refused rather than
|
|
282
|
+
# letting an author believe cursor pagination happens.
|
|
406
283
|
def seal_cursor
|
|
407
284
|
return unless @cursor
|
|
408
285
|
|
|
@@ -411,10 +288,7 @@ module Hecks
|
|
|
411
288
|
"pagination — use limit/offset instead"
|
|
412
289
|
end
|
|
413
290
|
|
|
414
|
-
# `on:`
|
|
415
|
-
# `include` already use for their own type argument. `nil` when `on:`
|
|
416
|
-
# is omitted, matching every other optional field's "absent, not
|
|
417
|
-
# false" reading in this file.
|
|
291
|
+
# Resolves `on:` to a type name; `nil` when omitted.
|
|
418
292
|
def resolve_target(on) = on && Naming.demodulise(on)
|
|
419
293
|
|
|
420
294
|
def add_aggregate_head(type, name, many:)
|
|
@@ -3,76 +3,16 @@ require_relative "bootstrap_table"
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Bluebook
|
|
5
5
|
module DSL
|
|
6
|
-
# The three resolution primitives
|
|
7
|
-
#
|
|
8
|
-
# reduces to, at every scope this language has grown one so far
|
|
9
|
-
# (a command referencing its owner or a sibling piece's entity-wide
|
|
10
|
-
# pool; an aggregate referencing another aggregate chapter-wide; a
|
|
11
|
-
# value object referencing a sibling value object on the same
|
|
12
|
-
# aggregate) — extracted here, once, so the next scope this family
|
|
13
|
-
# widens to (there will be one — see docs/resolution-rules/
|
|
14
|
-
# chapter-given.md's own "Known limitations" for two already named)
|
|
15
|
-
# reuses one of these three shapes instead of a fourth hand-written
|
|
16
|
-
# near-duplicate resolver.
|
|
17
|
-
#
|
|
18
|
-
# ## Not one unified algorithm
|
|
19
|
-
#
|
|
20
|
-
# A real design question this file answers directly: the three existing
|
|
21
|
-
# resolvers are not superficially different, they are structurally different (a
|
|
22
|
-
# multi-pool fallback chain; one pool keyed by declaring owner,
|
|
23
|
-
# needing disambiguation; a live scan over already-built sibling
|
|
24
|
-
# objects with no separate pool at all) — forcing them into one
|
|
25
|
-
# shape would be a real behavior change (see `#lookup`, below, for
|
|
26
|
-
# which construct uses which), not the pure internal refactor this
|
|
27
|
-
# module is. `build_rule` is the one piece that was genuinely
|
|
28
|
-
# identical across all 7 declaring methods (`given`×3,
|
|
29
|
-
# `invariant`×3, `ensures`×1) before this file existed — extract
|
|
30
|
-
# predicate source, refuse if extraction failed, build the struct.
|
|
31
|
-
#
|
|
32
|
-
# ## Resolution rules read off the grammar table, not a Ruby-only mirror
|
|
33
|
-
#
|
|
34
|
-
# `#lookup`/`#verify_resolves_via!` read which construct uses which
|
|
35
|
-
# primitive off the self-hosted grammar table itself
|
|
36
|
-
# (`Keyword#resolves_via`, `syntax.bluebook`), so a real domain's own boot, not just
|
|
37
|
-
# `bundle exec rspec`, fails loudly the moment the table and this file's own
|
|
38
|
-
# hand-written resolution methods disagree.
|
|
6
|
+
# The three "declared once, referenced by name" resolution primitives shared by
|
|
7
|
+
# every given/invariant/ensures scope in this DSL (ADR 0025).
|
|
39
8
|
module RuleReference
|
|
40
9
|
module_function
|
|
41
10
|
|
|
42
|
-
# Extracts a predicate
|
|
11
|
+
# Extracts a predicate's source and builds the rule struct (Given or Invariant),
|
|
43
12
|
# refusing when the source could not be read.
|
|
44
13
|
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
# `Given` lives in command.rb, `Invariant` in value_object.rb).
|
|
48
|
-
# `owner_name`/`word` are only for the refusal message's own
|
|
49
|
-
# wording. `extraction_failure` is the tail of that same
|
|
50
|
-
# message, and stays a required parameter rather than one
|
|
51
|
-
# hardcoded string on purpose — `given` ("its source could not
|
|
52
|
-
# be read, so no other runtime could ever evaluate it"),
|
|
53
|
-
# `invariant` ("it would be a rule the IR cannot carry"), and
|
|
54
|
-
# `ensures` ("a postcondition is carried as text, and this one
|
|
55
|
-
# has none") each already had their own exact wording before
|
|
56
|
-
# this method existed; unifying them into one generic sentence
|
|
57
|
-
# would be a real (if small) behavior change this refactor is
|
|
58
|
-
# not making.
|
|
59
|
-
#
|
|
60
|
-
# @param struct_class [Class] the rule struct to build — `Bluebook::Given` or
|
|
61
|
-
# `Bluebook::Invariant`
|
|
62
|
-
# @param description [String] the rule's own description, as declared by the caller's
|
|
63
|
-
# own `given`/`invariant`/`ensures` word
|
|
64
|
-
# @param predicate [Proc] the rule's own body block, never called here — only its
|
|
65
|
-
# extracted source is used
|
|
66
|
-
# @param owner_name [String] the declaring construct's own name, for the refusal message
|
|
67
|
-
# @param word [String] the declaring word (`"given"`, `"invariant"`, or `"ensures"`),
|
|
68
|
-
# for the refusal message
|
|
69
|
-
# @param extraction_failure [String] the refusal message's own tail, naming what an
|
|
70
|
-
# unreadable predicate would mean for this particular word
|
|
71
|
-
# @return [Bluebook::Given, Bluebook::Invariant] the built rule struct, an instance of
|
|
72
|
-
# `struct_class`
|
|
73
|
-
# @raise [Bluebook::DSL::Malformed] if `predicate`'s source could not be extracted, or
|
|
74
|
-
# it matches against a pattern construct `Expression::AstJson::PatternSubset` refuses, or
|
|
75
|
-
# it calls a method the expression language does not have
|
|
14
|
+
# extraction_failure is required, not hardcoded, since given/invariant/ensures
|
|
15
|
+
# each need their own exact refusal wording.
|
|
76
16
|
def build_rule(struct_class, description, predicate, owner_name:, word:, extraction_failure:)
|
|
77
17
|
canonical = Ports::Extraction.canonical(predicate)
|
|
78
18
|
|
|
@@ -89,103 +29,37 @@ module Hecks
|
|
|
89
29
|
struct_class.new(description: description, canonical: canonical, predicate: predicate, ast: ast)
|
|
90
30
|
end
|
|
91
31
|
|
|
92
|
-
#
|
|
93
|
-
# Rule` pools, first match wins. `CommandBuilder#given`'s own
|
|
94
|
-
# two-pool shape (its own owner's `named_givens`, then a sibling
|
|
95
|
-
# piece's entity-wide pool) is this with a 2-element chain — a
|
|
96
|
-
# future single-pool bare reference is the same primitive with a
|
|
97
|
-
# 1-element chain, not a separate "just look in one hash" method.
|
|
98
|
-
# @param pools [Array<Hash{String => Bluebook::Given, Bluebook::Invariant}>] pools to
|
|
99
|
-
# search in order; the first pool holding `description` wins
|
|
100
|
-
# @param description [String] the rule's own description to find
|
|
101
|
-
# @return [Bluebook::Given, Bluebook::Invariant, nil] the matching rule, or `nil` if no
|
|
102
|
-
# pool has one
|
|
32
|
+
# Resolution primitive 1: first pool (in order) whose hash has this description wins.
|
|
103
33
|
def resolve_hash_chain(pools, description)
|
|
104
34
|
pools.each { |pool| return pool[description] if pool.key?(description) }
|
|
105
35
|
nil
|
|
106
36
|
end
|
|
107
37
|
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
# same description can mean two genuinely different predicates
|
|
112
|
-
# (docs/implemented/resolution-rules/chapter-given.md). Returns the full
|
|
113
|
-
# candidates Hash (0, 1, or many entries) — deliberately not
|
|
114
|
-
# raising here, so each caller keeps its own exact refusal
|
|
115
|
-
# wording for "none," "ambiguous," and "declared_by: named the
|
|
116
|
-
# wrong owner" rather than one generic message papering over all
|
|
117
|
-
# three.
|
|
118
|
-
# @param pool [Hash{String => Hash{String => Bluebook::Given, Bluebook::Invariant}}]
|
|
119
|
-
# descriptions mapped to their own candidates, each keyed by declaring owner
|
|
120
|
-
# @param description [String] the rule's own description to find
|
|
121
|
-
# @return [Hash{String => Bluebook::Given, Bluebook::Invariant}] the candidates for
|
|
122
|
-
# `description`, keyed by declaring owner; empty when none exist
|
|
38
|
+
# Resolution primitive 2: one pool keyed by declaring owner. Returns every candidate
|
|
39
|
+
# rather than raising, so each caller keeps its own "none"/"ambiguous"/"wrong owner"
|
|
40
|
+
# wording.
|
|
123
41
|
def resolve_owner_keyed(pool, description)
|
|
124
42
|
pool[description] || {}
|
|
125
43
|
end
|
|
126
44
|
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# object on the same aggregate has already been built by the time
|
|
131
|
-
# a later one references back (declaration order, the same
|
|
132
|
-
# constraint every scope in this family carries), so there is
|
|
133
|
-
# nothing to write through — just read their own already-declared
|
|
134
|
-
# rules directly. `reader` is the method name to call on each
|
|
135
|
-
# sibling (`:invariants` today; kept a parameter, not hardcoded,
|
|
136
|
-
# since a future sibling-scan scope might reference a different
|
|
137
|
-
# collection).
|
|
138
|
-
# @param siblings [Array<Object>] the already-built sibling objects to scan; each must
|
|
139
|
-
# respond to `reader`
|
|
140
|
-
# @param description [String] the rule's own description to find
|
|
141
|
-
# @param reader [Symbol] the method to call on each sibling to read its own rule
|
|
142
|
-
# collection, such as `:invariants`
|
|
143
|
-
# @return [Bluebook::Given, Bluebook::Invariant, nil] the matching rule, or `nil` if no
|
|
144
|
-
# sibling declares one
|
|
45
|
+
# Resolution primitive 3: scans already-built siblings' own rule collections directly
|
|
46
|
+
# instead of a separately-maintained pool, since declaration order guarantees every
|
|
47
|
+
# sibling exists by the time a later one references it.
|
|
145
48
|
def resolve_sibling_scan(siblings, description, reader:)
|
|
146
49
|
siblings.flat_map { |sibling| sibling.public_send(reader) }
|
|
147
50
|
.find { |rule| rule.description == description }
|
|
148
51
|
end
|
|
149
52
|
|
|
150
|
-
#
|
|
151
|
-
#
|
|
152
|
-
# user's own correction — "my goal is that if they read the same
|
|
153
|
-
# table they behave identically" — means a table only Ruby ever
|
|
154
|
-
# reads cannot deliver that, no matter how faithfully it is
|
|
155
|
-
# cross-checked afterward. `Keyword#resolves_via`/`#disambiguator`
|
|
156
|
-
# (self-hosted, `syntax.bluebook`) is the real table now — the
|
|
157
|
-
# same generated data `rust/parser/src/keywords.rs` is generated
|
|
158
|
-
# from (`bin/project_parser_table`). `#lookup` reads it live.
|
|
159
|
-
#
|
|
160
|
-
# **The one unavoidable exception**: the meta-domain's own bootstrap
|
|
161
|
-
# (`MetaValidator.load_grammar_into`) dispatches `given`/
|
|
162
|
-
# `invariant` on itself 61 times while building the very grammar
|
|
163
|
-
# table that would answer "how does given/Aggregate resolve" —
|
|
164
|
-
# `MetaValidator.grammar_registry`/`SyntaxBoot.call` are not
|
|
165
|
-
# ready yet, and cannot be made ready without already having
|
|
166
|
-
# resolved a `given` somewhere upstream. `MetaValidator.
|
|
167
|
-
# bootstrapping?` is the same guard `MetaValidator.call` (the
|
|
168
|
-
# judge) already uses to skip self-judging during this exact
|
|
169
|
-
# window — `#lookup` uses it too, falling back to
|
|
170
|
-
# `BOOTSTRAP_FALLBACK` (below) only while it's true. Every real
|
|
171
|
-
# domain (banking, pizzas, compliance, any future one) boots
|
|
172
|
-
# after `grammar_registry` is fully built and memoized, so reads
|
|
173
|
-
# the real table, every time, no exception.
|
|
53
|
+
# Which (word, context) pair resolves via which primitive, read from the self-hosted
|
|
54
|
+
# grammar table (Keyword#resolves_via in syntax.bluebook) once it exists.
|
|
174
55
|
#
|
|
175
|
-
#
|
|
176
|
-
#
|
|
177
|
-
#
|
|
178
|
-
# (bin/project_bootstrap_table, pinned by spec/bootstrap_table_spec.rb).
|
|
56
|
+
# Projected ahead of time into bootstrap_table.rb for the bootstrap window before
|
|
57
|
+
# the grammar table itself is built (MetaValidator.bootstrapping?); kept in sync by
|
|
58
|
+
# bin/project_bootstrap_table, pinned by spec/bootstrap_table_spec.rb.
|
|
179
59
|
BOOTSTRAP_FALLBACK = BootstrapTable::RESOLVES
|
|
180
60
|
|
|
181
|
-
# Reads how a (word, context) pair resolves
|
|
182
|
-
# grammar table
|
|
183
|
-
#
|
|
184
|
-
# @param word [String] the DSL word, such as `"given"` or `"invariant"`
|
|
185
|
-
# @param context [String] the grammar context, such as `"Aggregate"`
|
|
186
|
-
# @return [Hash{Symbol => String, nil}] `:resolves_via` and `:disambiguator` for this
|
|
187
|
-
# (word, context) pair, `nil`-valued when the row leaves either blank, or `{}` if no row
|
|
188
|
-
# matches at all
|
|
61
|
+
# Reads how a (word, context) pair resolves rule references, off the self-hosted
|
|
62
|
+
# grammar table (or, during bootstrap, the projected fallback).
|
|
189
63
|
def lookup(word, context)
|
|
190
64
|
if MetaValidator.bootstrapping?
|
|
191
65
|
BOOTSTRAP_FALLBACK[[word, context]] || {}
|
|
@@ -199,24 +73,9 @@ module Hecks
|
|
|
199
73
|
end
|
|
200
74
|
end
|
|
201
75
|
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
# ever disagrees with what the self-hosted grammar table claims
|
|
206
|
-
# for it. Each of the three `reference_named_*` methods below
|
|
207
|
-
# calls this first, naming the primitive it is about to use —
|
|
208
|
-
# if `syntax.bluebook`'s own `resolves_via` for this exact
|
|
209
|
-
# (word, context) pair ever names something else, this is a
|
|
210
|
-
# real drift between the language's own self-description and
|
|
211
|
-
# its own implementation, caught at the next boot of anything,
|
|
212
|
-
# not just the next `rspec` run.
|
|
213
|
-
# @param word [String] the DSL word, such as `"given"` or `"invariant"`
|
|
214
|
-
# @param context [String] the grammar context, such as `"Aggregate"`
|
|
215
|
-
# @param expected_primitive [String] the resolution primitive's own name the caller is
|
|
216
|
-
# about to use, such as `"hash_chain"`, `"owner_keyed"`, or `"sibling_scan"`
|
|
217
|
-
# @return [void]
|
|
218
|
-
# @raise [RuntimeError] if `syntax.bluebook`'s own `resolves_via` for this (word, context)
|
|
219
|
-
# pair names something other than `expected_primitive`
|
|
76
|
+
# Raises if syntax.bluebook's own resolves_via for (word, context) disagrees with
|
|
77
|
+
# the primitive the caller is about to use — catches drift between the grammar
|
|
78
|
+
# table and its Ruby implementation at boot, not just in spec.
|
|
220
79
|
def verify_resolves_via!(word, context, expected_primitive)
|
|
221
80
|
actual = lookup(word, context)[:resolves_via]
|
|
222
81
|
return if actual == expected_primitive
|