hecks 2.8.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 +4 -6
- 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 +17 -98
- 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 +5 -48
- 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 +31 -141
- 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 +14 -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 +25 -31
- 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 +16 -69
- 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 -194
- 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 -111
- 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 +53 -267
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +43 -305
- 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 +6 -21
- data/lib/hecks/bluebook/model_check.rb +100 -597
- 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 +4 -23
- data/lib/hecks/cli/document.rb +3 -21
- data/lib/hecks/cli/ir.rb +3 -5
- data/lib/hecks/cli/mcp.rb +2 -3
- data/lib/hecks/cli/mcp_door.rb +19 -114
- data/lib/hecks/cli/model_check.rb +18 -85
- data/lib/hecks/cli/project_cli.rb +7 -40
- data/lib/hecks/cli/project_diagrams.rb +0 -7
- data/lib/hecks/cli/run.rb +12 -58
- data/lib/hecks/cli/smoke_test.rb +2 -16
- data/lib/hecks/cli/stores.rb +1 -2
- data/lib/hecks/cli.rb +18 -24
- 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 +82 -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 +9 -90
- 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 +61 -64
- 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 +6 -25
- 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 +18 -124
- 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 +150 -678
- 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 +2 -15
- 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 +42 -134
- 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 +3 -6
- data/lib/hecks/release/runner/commands.rb +2 -4
- data/lib/hecks/release/runner/gem_publisher.rb +2 -3
- data/lib/hecks/release/runner/npm_publisher.rb +6 -20
- data/lib/hecks/release/runner/preflight.rb +4 -19
- data/lib/hecks/release/runner/tagger.rb +2 -9
- data/lib/hecks/release/runner.rb +3 -32
- 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 +124 -306
- 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 +64 -219
- 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 +117 -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 +99 -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 +141 -568
- 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.rb +26 -81
- 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 +3 -2
data/lib/hecks/projector.rb
CHANGED
|
@@ -1,60 +1,13 @@
|
|
|
1
1
|
module Hecks
|
|
2
|
-
# A named registry of "canonical IR in, external artifact out" tools
|
|
3
|
-
#
|
|
4
|
-
# such tool was its own thing with its own call shape: `Exporter`
|
|
5
|
-
# (registry-wide, consumed directly by bin/ir, bin/project_rust, and
|
|
6
|
-
# translation/audit's approval digest — untouched by this file, still
|
|
7
|
-
# exactly what those three read), and `RustProjection::Projector`
|
|
8
|
-
# (`rust/project.rb`, a whole separate Ruby program under a
|
|
9
|
-
# confusingly-identical module name) are the two that already exist.
|
|
10
|
-
# Neither is registered here — retrofitting either is real, separate
|
|
11
|
-
# work (Rust's generator is a whole second toolchain; a UL/OIDC
|
|
12
|
-
# projector doesn't exist yet at all) — but `:ir` is, as a genuine,
|
|
13
|
-
# working, golden-tested example of the shape every future projector
|
|
14
|
-
# (`:rust`, `:ul`, `:openid`, ...) is meant to follow.
|
|
2
|
+
# A named registry of "canonical IR in, external artifact out" tools, each
|
|
3
|
+
# taking one bluebook's IR and answering a derived artifact (`:ir`, `:docs`, ...).
|
|
15
4
|
#
|
|
16
|
-
#
|
|
17
|
-
# every real projection target (Rust/UL/OIDC all project one domain at
|
|
18
|
-
# a time), and deliberately narrower than `Exporter.call`'s own
|
|
19
|
-
# multi-domain shape.
|
|
5
|
+
# Projector.call(:ir, bluebook: chapter)
|
|
20
6
|
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
# a projection takes a chapter's declaration and answers something
|
|
27
|
-
# that describes the domain: its IR, its storage shape, an OIDC scope
|
|
28
|
-
# manifest, the parser's keyword table, the reference pages. Inert,
|
|
29
|
-
# derived, and runnable against any chapter that carries what it
|
|
30
|
-
# declares it needs. These are what `register` holds.
|
|
31
|
-
#
|
|
32
|
-
# an export takes a declaration and its bindings and answers
|
|
33
|
-
# something that is the domain, running elsewhere — rust/project.rb's
|
|
34
|
-
# generated crate, the WASM artifact, the CloudFormation template
|
|
35
|
-
# `lib/hecks/projections/deploy` renders. It needs the
|
|
36
|
-
# `.world`/`.hecksagon` a projection never looks at, because a
|
|
37
|
-
# running system has to know how it is wired. A target that needs
|
|
38
|
-
# this declares `needs_world: true` (`Target#projects_as`) and reads
|
|
39
|
-
# `options.fetch(:world)`; `Projector.call`'s own `world:` keyword is
|
|
40
|
-
# what carries it, merged into `options` before the target ever runs.
|
|
41
|
-
#
|
|
42
|
-
# a state projection takes records — a domain after dispatch — and is
|
|
43
|
-
# a read-model question wearing the same word.
|
|
44
|
-
# `bin/expression_projection` is the one of these: its operators are
|
|
45
|
-
# not declared anywhere, they are what exists after
|
|
46
|
-
# `Grammar.expression` replays expression_operators.json's ledger of
|
|
47
|
-
# dispatches. Converting it into this registry would be a category
|
|
48
|
-
# error, however much its name suggests otherwise.
|
|
49
|
-
#
|
|
50
|
-
# ## One word, three other meanings
|
|
51
|
-
#
|
|
52
|
-
# Worth naming too, because grepping "projection" turns all of these up
|
|
53
|
-
# and none is the above:
|
|
54
|
-
#
|
|
55
|
-
# Ports::Projection read-model catch-up, events folded into state
|
|
56
|
-
# bin/project forces that catch-up by hand
|
|
57
|
-
# RustProjection rust/project.rb's own separate toolchain
|
|
7
|
+
# A projection is inert and derived from a chapter's declaration. An export
|
|
8
|
+
# (rust crate, WASM, CloudFormation) also needs `.world` bindings and declares
|
|
9
|
+
# `needs_world: true`. State projections (`bin/expression_projection`) and
|
|
10
|
+
# `Ports::Projection` read-model catch-up are different things and not registered.
|
|
58
11
|
module Projector
|
|
59
12
|
class UnknownProjector < StandardError; end
|
|
60
13
|
# A chapter-scoped projector was handed something that is not a chapter.
|
|
@@ -62,14 +15,8 @@ module Hecks
|
|
|
62
15
|
|
|
63
16
|
module_function
|
|
64
17
|
|
|
65
|
-
# `projector` needs only to answer `call(bluebook:, options:)` — a
|
|
66
|
-
# module, a class with a class method, or any object responding to
|
|
67
|
-
# `call` all work. Re-registering a name replaces it outright,
|
|
68
|
-
# deliberately unguarded: a spec re-registering a stub under the same
|
|
69
|
-
# name between examples is the ordinary case, not a footgun to fence
|
|
70
|
-
# against.
|
|
71
18
|
# Adds `projector` to the registry under `name`, replacing anything
|
|
72
|
-
# already registered there.
|
|
19
|
+
# already registered there (deliberately unguarded, so specs can stub).
|
|
73
20
|
#
|
|
74
21
|
# @param name [String, Symbol] the key `projector` is looked up by (converted to a symbol)
|
|
75
22
|
# @param projector [Module, Class, #call] anything answering `call(bluebook:, options:)`
|
|
@@ -81,24 +28,14 @@ module Hecks
|
|
|
81
28
|
# Runs the projector registered under `name` against `bluebook`,
|
|
82
29
|
# after refusing a construct it does not admit.
|
|
83
30
|
#
|
|
84
|
-
# `bluebook:` is kept as the keyword because it is the shipped
|
|
85
|
-
# spelling and every existing caller uses it — but what it accepts is
|
|
86
|
-
# any construct that emits IR, and `admits!` is what decides whether
|
|
87
|
-
# this target can actually take the one handed over.
|
|
88
|
-
#
|
|
89
31
|
# @param name [String, Symbol] the registered projector's key
|
|
90
|
-
# @param bluebook [Bluebook::Behaviour::Chapter, Hecks::IR] the
|
|
91
|
-
# IR-emitting construct to project
|
|
32
|
+
# @param bluebook [Bluebook::Behaviour::Chapter, Hecks::IR] the construct to project
|
|
92
33
|
# @param options [Hash] projector-specific options, passed through unchanged
|
|
93
|
-
# @param world [Bluebook::World, nil]
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
# receives it as a separate argument.
|
|
97
|
-
# @return [Object] whatever the projector's own `call` returns: typically a
|
|
98
|
-
# `Hash`/`String` artifact, or a `Hash{String => String}` file tree
|
|
34
|
+
# @param world [Bluebook::World, nil] bindings an export needs (`needs_world: true`);
|
|
35
|
+
# merged into `options[:world]`
|
|
36
|
+
# @return [Object] whatever the projector's own `call` returns
|
|
99
37
|
# @raise [UnknownProjector] if no projector is registered under `name`
|
|
100
|
-
# @raise [WrongConstruct] if `bluebook` lacks
|
|
101
|
-
# projector requires, or if the projector needs `world:` and none is given
|
|
38
|
+
# @raise [WrongConstruct] if `bluebook` lacks what the projector requires
|
|
102
39
|
def call(name, bluebook:, options: {}, world: nil)
|
|
103
40
|
projector = registry.fetch(name.to_sym) do
|
|
104
41
|
raise UnknownProjector, "no projector registered for #{name.inspect} — registered: #{registered.sort.inspect}"
|
|
@@ -107,31 +44,17 @@ module Hecks
|
|
|
107
44
|
projector.call(bluebook: bluebook, options: world ? options.merge(world: world) : options)
|
|
108
45
|
end
|
|
109
46
|
|
|
110
|
-
# Refuses `construct` if it lacks a capability or declared aggregate
|
|
111
|
-
# `
|
|
112
|
-
# this is what enforces that, before the projector runs.
|
|
113
|
-
#
|
|
114
|
-
# One check covers both shapes. An ordinary construct includes its
|
|
115
|
-
# capabilities and a class-shaped one — Command, Entity, ValueObject
|
|
116
|
-
# — extends them, and `is_a?` consults the singleton chain, so it
|
|
117
|
-
# answers for an extended module as readily as an included one. This
|
|
118
|
-
# started as two checks on the assumption it would not; a spec
|
|
119
|
-
# asserting the assumption failed, which is the only reason the
|
|
120
|
-
# redundant half was noticed.
|
|
47
|
+
# Refuses `construct` if it lacks a capability or declared aggregate `projector` requires.
|
|
48
|
+
# One `is_a?` check covers included and extended capabilities alike.
|
|
121
49
|
#
|
|
122
50
|
# @param name [String, Symbol] the projector's registered key, used in the message
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
# @param construct [Bluebook::Behaviour::Chapter, Hecks::IR] the chapter or
|
|
128
|
-
# IR-emitting construct offered to the projector
|
|
129
|
-
# @param world [Bluebook::World, nil] the `world:` given to `Projector.call`, checked
|
|
130
|
-
# against the target's own `needs_world:` declaration
|
|
51
|
+
# @param projector [Module, Class, #call] the target; consulted for `projection_requires`,
|
|
52
|
+
# `projection_declares` and `projection_needs_world?` when it answers them
|
|
53
|
+
# @param construct [Bluebook::Behaviour::Chapter, Hecks::IR] the construct offered
|
|
54
|
+
# @param world [Bluebook::World, nil] the `world:` given to `Projector.call`
|
|
131
55
|
# @return [void]
|
|
132
|
-
# @raise [WrongConstruct] if
|
|
133
|
-
#
|
|
134
|
-
# and `world` is nil
|
|
56
|
+
# @raise [WrongConstruct] if a required capability or aggregate is missing, or the
|
|
57
|
+
# projector needs `world:` and `world` is nil
|
|
135
58
|
def admits!(name, projector, construct, world = nil)
|
|
136
59
|
needed = projector.respond_to?(:projection_requires) ? projector.projection_requires : []
|
|
137
60
|
missing = needed.reject { |capability| capable?(construct, capability) }
|
|
@@ -190,11 +113,8 @@ module Hecks
|
|
|
190
113
|
@registry ||= {}
|
|
191
114
|
end
|
|
192
115
|
|
|
193
|
-
#
|
|
194
|
-
# (`
|
|
195
|
-
# (`:oidc`). Both resolve here, so the constant form is added
|
|
196
|
-
# surface rather than a replacement — every `Projector.call(:ir, ...)`
|
|
197
|
-
# written before this existed keeps working untouched.
|
|
116
|
+
# Resolves a target given as a constant (`Projections::OIDC`) or a bare
|
|
117
|
+
# key (`:oidc`) to the key it is registered under.
|
|
198
118
|
#
|
|
199
119
|
# @param target [String, Symbol, #projection_key] a registered key, or a target
|
|
200
120
|
# that declared its own key with `Target#projects_as`
|
|
@@ -205,15 +125,10 @@ module Hecks
|
|
|
205
125
|
target
|
|
206
126
|
end
|
|
207
127
|
|
|
208
|
-
#
|
|
209
|
-
# returns an artifact and never touches disk, which is what lets
|
|
210
|
-
# spec/projector_spec.rb compare `:ir`'s output against a golden
|
|
211
|
-
# fixture without a tmpdir. `out:` is the only thing that writes.
|
|
128
|
+
# Writes `artifact` to `out`; a projector itself never touches disk.
|
|
212
129
|
#
|
|
213
|
-
# `as:` comes from the projection's
|
|
214
|
-
#
|
|
215
|
-
# Hash that simply happens to hold strings are the same object to
|
|
216
|
-
# Ruby; only the projection knows which it meant.
|
|
130
|
+
# `as:` comes from the projection's `emits:` declaration, since a file tree
|
|
131
|
+
# and a Hash of strings are the same object to Ruby.
|
|
217
132
|
#
|
|
218
133
|
# @param artifact [Hash, String] the projector's output; a file tree when `as: :files`
|
|
219
134
|
# @param out [String] the path to write to; a directory when `as: :files`
|
|
@@ -226,8 +141,7 @@ module Hecks
|
|
|
226
141
|
out
|
|
227
142
|
end
|
|
228
143
|
|
|
229
|
-
#
|
|
230
|
-
# report what happened without re-deriving it from the tree.
|
|
144
|
+
# Writes each `files` entry under `directory`, answering the paths written.
|
|
231
145
|
#
|
|
232
146
|
# @param files [Hash{String => String}] relative path => contents
|
|
233
147
|
# @param directory [String] the directory to write the tree under
|
|
@@ -249,8 +163,7 @@ require "json"
|
|
|
249
163
|
require_relative "projector/exporter"
|
|
250
164
|
require_relative "projector/ir_projector"
|
|
251
165
|
require_relative "projector/target"
|
|
252
|
-
#
|
|
253
|
-
# else — so unlike a target they are safe to pull in from here.
|
|
166
|
+
# These need only `Naming`, not this file, so they are safe to load from here.
|
|
254
167
|
require_relative "projector/docs_projector"
|
|
255
168
|
require_relative "projector/narrate_projector"
|
|
256
169
|
require_relative "projector/cli_projector"
|
|
@@ -260,9 +173,5 @@ Hecks::Projector.register(:docs, Hecks::Projector::DocsProjector)
|
|
|
260
173
|
Hecks::Projector.register(:narrate, Hecks::Projector::NarrateProjector)
|
|
261
174
|
Hecks::Projector.register(:cli, Hecks::Projector::CliProjector)
|
|
262
175
|
|
|
263
|
-
#
|
|
264
|
-
#
|
|
265
|
-
# needs `Target` and the registry), so requiring them back from here
|
|
266
|
-
# would close a genuine `circular require considered harmful` loop. Ruby
|
|
267
|
-
# tolerates it; the warning is still right, and load order is the kind of
|
|
268
|
-
# thing that only breaks once someone requires a target on its own.
|
|
176
|
+
# Targets are required from lib/hecks.rb, not here: each requires this file, so
|
|
177
|
+
# requiring them back would close a circular require.
|
|
@@ -2,37 +2,16 @@ require_relative "../../literal"
|
|
|
2
2
|
require_relative "../../vocabulary"
|
|
3
3
|
|
|
4
4
|
module Hecks
|
|
5
|
-
#
|
|
6
|
-
# summary) to add Common::COMPARATORS — the vendored comparator
|
|
7
|
-
# vocabulary — and .render_value, the wire-rendering entry point
|
|
8
|
-
# shared by every literal-bearing spec struct.
|
|
5
|
+
# Adds `Common::COMPARATORS` and `.render_value`, the wire renderer shared by spec structs.
|
|
9
6
|
module QuerySpecification
|
|
10
|
-
# The
|
|
11
|
-
# (`WhereClause`, `OrderBy`, `LimitSpec`...), the `DSL` mixin that parses
|
|
12
|
-
# them, and the comparison and null rules every engine answers by. It is
|
|
13
|
-
# its own namespace so a plain `Bluebook::Query` and a
|
|
14
|
-
# `ReadModel::Specification` say one thing the same way rather than each
|
|
15
|
-
# carrying a copy.
|
|
7
|
+
# The clause structs, DSL mixin and comparison rules shared by every query-shaped construct.
|
|
16
8
|
module Common
|
|
17
|
-
# `none_in_state
|
|
18
|
-
#
|
|
19
|
-
# `where ref: { none_in_state: "Claim:held" }` holds true when no
|
|
20
|
-
# record in the named aggregate, keyed by this record's own field
|
|
21
|
-
# value, is currently in the named state. plan.bluebook's own
|
|
22
|
-
# description: "a keyed point lookup (HashMap hit), never a scan" --
|
|
23
|
-
# a real, deliberate, pre-existing feature (WhereOp::NoneInState in
|
|
24
|
-
# the old Rust runtime), not invented here -- see Runtime::
|
|
25
|
-
# QueryInterpreter#holds?'s own comment for the evaluation side.
|
|
9
|
+
# Includes `none_in_state`: `where ref: { none_in_state: "Claim:held" }` holds when no record
|
|
10
|
+
# in the named aggregate, keyed by this field's value, is in that state (a keyed lookup).
|
|
26
11
|
COMPARATORS = Hecks::Vocabulary.symbols("QueryComparator")
|
|
27
12
|
end
|
|
28
13
|
|
|
29
|
-
# Renders a literal
|
|
30
|
-
# wire spelling, for a spec struct's `to_h`.
|
|
31
|
-
#
|
|
32
|
-
# The specification structs' own name for the one wire spelling — see
|
|
33
|
-
# `Hecks::Literal`, which every other `to_h`-bound literal field
|
|
34
|
-
# shares. Kept as a word here because the structs below read better
|
|
35
|
-
# saying what they are doing than naming the module that does it.
|
|
14
|
+
# Renders a query-clause literal as its wire spelling; delegates to `Hecks::Literal.render`.
|
|
36
15
|
#
|
|
37
16
|
# @param value [nil, Symbol, String, StateRef, Boolean, Integer, Float, Hash, Array] the
|
|
38
17
|
# literal as the bluebook author wrote it; Hash values and Array elements are
|
|
@@ -4,44 +4,16 @@ require_relative "../../runtime/value"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module QuerySpecification
|
|
6
6
|
module Common
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# aggregate query actually runs) and `Runtime::QueryInterpreter`
|
|
10
|
-
# (entity/sub-list queries, and any adapter implementing no `:query`)
|
|
11
|
-
# — plus a third partial reading of `comparable` inside
|
|
12
|
-
# `SqlQueryBuilder#query_value`.
|
|
13
|
-
#
|
|
14
|
-
# Two copies of one rule is not a style complaint here; it has cost
|
|
15
|
-
# real bugs twice already, both recorded in the comments those files
|
|
16
|
-
# carried. `none_in_state` was added to one copy only, so an ordinary
|
|
17
|
-
# Memory query's `none_in_state` clause fell to the `else` branch and
|
|
18
|
-
# silently excluded every row. And `comparable` diverged without
|
|
19
|
-
# anyone noticing: one copy took the first numeric member of a value
|
|
20
|
-
# object, the other only unwrapped when there was exactly one — so a
|
|
21
|
-
# value object with two numeric members compared as a number on one
|
|
22
|
-
# path and as a whole Hash on the other.
|
|
23
|
-
#
|
|
24
|
-
# The callers still differ in how they reach a value — one takes a
|
|
25
|
-
# registry argument, the other closes over `@registry`; one digs the
|
|
26
|
-
# field through `FieldPath`, the other is handed it — so they keep
|
|
27
|
-
# their own resolution and share only the comparison itself.
|
|
7
|
+
# The one comparator table shared by `Ports::Query::InMemory` and
|
|
8
|
+
# `Runtime::QueryInterpreter`, so every engine reads a where-clause identically.
|
|
28
9
|
module Comparison
|
|
29
10
|
module_function
|
|
30
11
|
|
|
31
12
|
# Unwraps a value object to the one scalar a comparison can mean.
|
|
32
13
|
#
|
|
33
|
-
#
|
|
34
|
-
# is
|
|
35
|
-
#
|
|
36
|
-
# Anything else is returned unchanged rather than guessed at — two
|
|
37
|
-
# numeric members give no reason to prefer either, and picking the
|
|
38
|
-
# first silently compares a field the author never named.
|
|
39
|
-
#
|
|
40
|
-
# A declaration naming an ambiguous value object is refused when
|
|
41
|
-
# the bluebook loads (`AggregateBuilder`'s own query-field seal),
|
|
42
|
-
# so this branch is a backstop rather than the primary guard: which
|
|
43
|
-
# member is meant is knowable at declaration time, and a refusal
|
|
44
|
-
# naming the candidates is worth more than any runtime reading.
|
|
14
|
+
# Only a sole numeric member, or a sole member of any type, is unambiguous; anything
|
|
15
|
+
# else is returned unchanged rather than guessed at. Ambiguous declarations are refused
|
|
16
|
+
# at load, so this is a backstop.
|
|
45
17
|
#
|
|
46
18
|
# @param value [Runtime::Value, Hash, Object, nil] a held or wanted value; a
|
|
47
19
|
# `Runtime::Value` is read through its `to_h`
|
|
@@ -58,9 +30,8 @@ module Hecks
|
|
|
58
30
|
value
|
|
59
31
|
end
|
|
60
32
|
|
|
61
|
-
# Lists the members a value object offers a scalar comparison, for a
|
|
62
|
-
#
|
|
63
|
-
# unambiguous — nothing to report.
|
|
33
|
+
# Lists the members a value object offers a scalar comparison, for a refusal
|
|
34
|
+
# that can name them. Empty when the value object is unambiguous.
|
|
64
35
|
#
|
|
65
36
|
# @param value_object [Class<Bluebook::ValueObject>] the declared shape (a subclass
|
|
66
37
|
# minted by `Bluebook::ValueObject.declare`, closed sets included) a query field names
|
|
@@ -76,34 +47,19 @@ module Hecks
|
|
|
76
47
|
|
|
77
48
|
NUMERIC_TYPES = %w[Integer Float Numeric].freeze
|
|
78
49
|
|
|
79
|
-
# Decides whether one where-clause comparison holds between the value
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
# A `case` over a closed, declared set (`Vocabulary::QueryComparator`,
|
|
83
|
-
# held equal to this list by spec/vocabulary_table_spec — see the
|
|
84
|
-
# `else` branch's own comment) is the whole point of the one
|
|
85
|
-
# comparator table this file's header describes: one place naming
|
|
86
|
-
# every comparator, not one method per comparator scattered across
|
|
87
|
-
# a module.
|
|
50
|
+
# Decides whether one where-clause comparison holds between the value a record
|
|
51
|
+
# holds and the value the query wants.
|
|
88
52
|
#
|
|
89
|
-
# @param operation [Symbol, String]
|
|
90
|
-
# `
|
|
91
|
-
# @param held [Object, nil] the record's own value for the field
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
# @
|
|
95
|
-
#
|
|
96
|
-
# @param registry [Runtime::Registry, nil] used only by `none_in_state` to look the
|
|
97
|
-
# target aggregate up; `nil` makes that comparator hold
|
|
98
|
-
# @return [Boolean] whether the comparison holds; an ordered comparator over a
|
|
99
|
-
# non-`Numeric` operand is `false`
|
|
100
|
-
# @raise [Runtime::WiringError] if `operation` names no comparator in this table, or
|
|
101
|
-
# `none_in_state`'s target aggregate has no repository that can be wired
|
|
53
|
+
# @param operation [Symbol, String] `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `in`,
|
|
54
|
+
# `contains` or `none_in_state`
|
|
55
|
+
# @param held [Object, nil] the record's own value for the field
|
|
56
|
+
# @param want [Object, nil] the value compared against
|
|
57
|
+
# @param registry [Runtime::Registry, nil] used only by `none_in_state`
|
|
58
|
+
# @return [Boolean] whether the comparison holds
|
|
59
|
+
# @raise [Runtime::WiringError] if `operation` names no comparator in this table
|
|
102
60
|
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
103
61
|
def holds?(operation, held, want, registry: nil)
|
|
104
|
-
# A NULL satisfies no comparison
|
|
105
|
-
# the rule and the reasoning, including why `none_in_state` is
|
|
106
|
-
# exempt from it.
|
|
62
|
+
# A NULL satisfies no comparison (`none_in_state` is exempt); see NullPolicy.
|
|
107
63
|
return false if NullPolicy.unmatchable?(operation, held, want)
|
|
108
64
|
|
|
109
65
|
case operation.to_s
|
|
@@ -117,24 +73,15 @@ module Hecks
|
|
|
117
73
|
when "contains" then contains?(held, want)
|
|
118
74
|
when "none_in_state" then none_in_state?(held, want, registry)
|
|
119
75
|
else
|
|
120
|
-
#
|
|
121
|
-
# QueryComparator, held equal to this list by
|
|
122
|
-
# spec/vocabulary_table_spec) — this is not a real runtime
|
|
123
|
-
# path today, only a backstop against the day a tenth
|
|
124
|
-
# comparator reaches the grammar and this method doesn't
|
|
125
|
-
# grow to match. Silently reading an unrecognized comparator
|
|
126
|
-
# as `eq` is exactly the failure this table already shipped
|
|
127
|
-
# once (see this file's own header) — an operator this
|
|
128
|
-
# method can't evaluate must refuse, not guess.
|
|
76
|
+
# Backstop: an unrecognized comparator must refuse, never read as `eq`.
|
|
129
77
|
raise Runtime::WiringError, "no comparator handles #{operation.to_s.inspect} — add one before declaring it"
|
|
130
78
|
end
|
|
131
79
|
end
|
|
132
80
|
|
|
133
81
|
# Checks that both operands of an ordered comparison are numbers.
|
|
134
82
|
#
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
# predates the extraction (lt was already exactly this permissive).
|
|
83
|
+
# Ordered comparators are numeric-only and silently false otherwise; a where-clause
|
|
84
|
+
# never raises.
|
|
138
85
|
#
|
|
139
86
|
# @param held [Object, nil] the record's own value for the field
|
|
140
87
|
# @param want [Object, nil] the value compared against
|
|
@@ -143,12 +90,8 @@ module Hecks
|
|
|
143
90
|
|
|
144
91
|
# Reads a comparator's list operand as the Strings membership is tested against.
|
|
145
92
|
#
|
|
146
|
-
# `in`
|
|
147
|
-
#
|
|
148
|
-
# serialisation), each element unwrapped the same way a scalar
|
|
149
|
-
# field is. This is `in`'s reading of its argument (a caller may
|
|
150
|
-
# legitimately pass "a,b,c" meaning "any of these") — unrelated to
|
|
151
|
-
# `contains`, which reads the stored field. See `contains?`.
|
|
93
|
+
# `in` accepts an Array or a comma-separated String; `contains` reads the stored
|
|
94
|
+
# field instead (see `contains?`).
|
|
152
95
|
#
|
|
153
96
|
# @param value [Array, String, Object, nil] an Array of elements, or anything whose
|
|
154
97
|
# `to_s` is a comma-separated list such as `"a, b,c"`
|
|
@@ -164,12 +107,6 @@ module Hecks
|
|
|
164
107
|
# Answers `in`: whether the held value, or any element of a held Array,
|
|
165
108
|
# occurs in the wanted list.
|
|
166
109
|
#
|
|
167
|
-
# A folded reference hop asks whether the locally-held identity is
|
|
168
|
-
# among the matching target identities. A has_many relationship holds
|
|
169
|
-
# several identities, so the same question becomes an intersection:
|
|
170
|
-
# does any held identity occur in the wanted set? Scalar `in` retains
|
|
171
|
-
# its existing one-candidate behavior.
|
|
172
|
-
#
|
|
173
110
|
# @param held [Array, Object] the record's own value; an Array contributes each
|
|
174
111
|
# element as a candidate, anything else is the single candidate
|
|
175
112
|
# @param want [Array, String, Object] the wanted set, read through `members`
|
|
@@ -182,19 +119,10 @@ module Hecks
|
|
|
182
119
|
candidates.any? { |candidate| wanted.include?(comparable(candidate).to_s) }
|
|
183
120
|
end
|
|
184
121
|
|
|
185
|
-
# Answers `contains`: element membership for a held Array, substring
|
|
186
|
-
# for anything else.
|
|
122
|
+
# Answers `contains`: element membership for a held Array, substring for anything else.
|
|
187
123
|
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
# arrives already, one element one member, nothing to split), and
|
|
191
|
-
# plain substring for anything else. The scalar case deliberately
|
|
192
|
-
# stays out of `members`' comma-split: that silently reads a
|
|
193
|
-
# free-text field's own comma as a separator — which the SQL
|
|
194
|
-
# side's `instr`/`position` never does, so the two disagree the
|
|
195
|
-
# moment a scalar's real content holds a comma. Matching SQL's
|
|
196
|
-
# substring reading here keeps every engine answering `contains`
|
|
197
|
-
# identically for the same declared field.
|
|
124
|
+
# A scalar is not comma-split, so free text containing a comma matches the way
|
|
125
|
+
# SQL's `instr`/`position` does.
|
|
198
126
|
#
|
|
199
127
|
# @param held [Array, Object] the record's own value; anything but an Array is
|
|
200
128
|
# read through `to_s`
|
|
@@ -206,29 +134,17 @@ module Hecks
|
|
|
206
134
|
held.to_s.include?(want.to_s)
|
|
207
135
|
end
|
|
208
136
|
|
|
209
|
-
# Answers `none_in_state` by looking the held identity up in another
|
|
210
|
-
#
|
|
137
|
+
# Answers `none_in_state` by looking the held identity up in another aggregate's
|
|
138
|
+
# repository and reading that record's state.
|
|
211
139
|
#
|
|
212
|
-
#
|
|
213
|
-
# "
|
|
214
|
-
# keyed by this record's own field value, is in the named state.
|
|
215
|
-
# No registry — no way to look the target up — reads as "not
|
|
216
|
-
# excluded", the same graceful default a missing record already
|
|
217
|
-
# falls back to. The target is searched by bare name across every
|
|
218
|
-
# loaded domain; ambiguity (two domains declaring one name) picks
|
|
219
|
-
# the first match rather than refusing, since a where-clause never
|
|
220
|
-
# raises (see `ordered?`).
|
|
140
|
+
# Holds when no such record is in the named state. No registry, aggregate or record
|
|
141
|
+
# reads as "not excluded". The target is found by bare name; the first match wins.
|
|
221
142
|
#
|
|
222
|
-
# @param held [Object, nil] this record's own field value,
|
|
223
|
-
#
|
|
224
|
-
# @param
|
|
225
|
-
#
|
|
226
|
-
# @
|
|
227
|
-
# aggregate and its repository in
|
|
228
|
-
# @return [Boolean] `false` only when the target record exists and its lifecycle
|
|
229
|
-
# field (or `:state`, absent a lifecycle) equals the named state; `true` when
|
|
230
|
-
# there is no registry, no such aggregate or no such record
|
|
231
|
-
# @raise [Runtime::WiringError] if the target aggregate's repository cannot be wired
|
|
143
|
+
# @param held [Object, nil] this record's own field value, the target record's identity
|
|
144
|
+
# @param want [String, Symbol] `"Aggregate:state"`, split on the first colon
|
|
145
|
+
# @param registry [Runtime::Registry, nil] the booted registry
|
|
146
|
+
# @return [Boolean] `false` only when the target record is in the named state
|
|
147
|
+
# @raise [Runtime::WiringError] if the target's repository cannot be wired
|
|
232
148
|
def none_in_state?(held, want, registry)
|
|
233
149
|
return true unless registry
|
|
234
150
|
|
|
@@ -240,31 +156,7 @@ module Hecks
|
|
|
240
156
|
record = registry.repository(target_domain, target_ir).find(held)
|
|
241
157
|
return true unless record
|
|
242
158
|
|
|
243
|
-
# The
|
|
244
|
-
# declaration — not assumed to be literally named `state`. Every
|
|
245
|
-
# `none_in_state` fixture this comparator originally shipped
|
|
246
|
-
# with (spec/query_none_in_state_*_spec.rb) happens to declare a
|
|
247
|
-
# plain `attribute :state, ...` rather than a real `lifecycle`,
|
|
248
|
-
# which is how the previous hardcoded `record.state[:state]`
|
|
249
|
-
# passed every one of them while being wrong for the shape this
|
|
250
|
-
# whole comparator exists to answer about: a real state machine.
|
|
251
|
-
# `lifecycle :field, default: ... do ... end` stores its state
|
|
252
|
-
# under `field` (`Instance#assign_creation_attributes`'s own
|
|
253
|
-
# `state[aggregate.lifecycle.field.to_sym] = ...`), and this
|
|
254
|
-
# codebase's own convention overwhelmingly names that field
|
|
255
|
-
# `status`, not `state` (`QualityControl::Clearance`'s own
|
|
256
|
-
# `lifecycle :status` among many others) — so the hardcoded key
|
|
257
|
-
# silently read `nil` from every real lifecycle-backed target,
|
|
258
|
-
# comparable(nil) != state was true unconditionally, and
|
|
259
|
-
# `none_in_state` against any lifecycle aggregate answered
|
|
260
|
-
# "not excluded" for every row, always, no matter its actual
|
|
261
|
-
# state. Found chasing `QualityControl::Bug.AwaitingClearance`
|
|
262
|
-
# (qa/bluebook/quality_control.bluebook), which is exactly this
|
|
263
|
-
# shape: `Clearance:green`/`Clearance:red` against a `lifecycle
|
|
264
|
-
# :status` aggregate. Falls back to `:state` when the target
|
|
265
|
-
# declares no lifecycle at all, so every existing fixture (a
|
|
266
|
-
# plain attribute literally named `state`) keeps answering
|
|
267
|
-
# exactly as before.
|
|
159
|
+
# The state lives on the target's lifecycle field, else on `:state`.
|
|
268
160
|
field = target_ir.lifecycle&.field || :state
|
|
269
161
|
comparable(record.state[field]) != state
|
|
270
162
|
end
|
|
@@ -11,33 +11,17 @@ require_relative "where_clause"
|
|
|
11
11
|
module Hecks
|
|
12
12
|
module QuerySpecification
|
|
13
13
|
module Common
|
|
14
|
-
#
|
|
15
|
-
# `
|
|
16
|
-
# bluebook DSL words, mixed into both QueryBuilder and
|
|
17
|
-
# ReadModelBuilder so the two specification kinds parse the same
|
|
18
|
-
# clauses identically rather than each carrying its own copy.
|
|
14
|
+
# Builder methods for the `where`/`order_by`/`limit`/`offset`/`cursor`/`authorize`/
|
|
15
|
+
# `nulls`/`inspect_query` DSL words, shared by QueryBuilder and ReadModelBuilder.
|
|
19
16
|
module DSL
|
|
20
|
-
# Records one `WhereClause` per `field => value` pair
|
|
21
|
-
# `where` DSL word forwards to.
|
|
17
|
+
# Records one `WhereClause` per `field => value` pair (the `where` DSL word).
|
|
22
18
|
#
|
|
23
|
-
# `where`/`order_by
|
|
24
|
-
#
|
|
25
|
-
# the grammar table rather than defined as methods: both the Query
|
|
26
|
-
# and the ReadModel Keyword rows name the `_impl` method in
|
|
27
|
-
# `calls:`, the same shape `attribute_impl` has, and a shared mixin
|
|
28
|
-
# means one method each. `where`/`order_by` are bootstrap-reachable
|
|
29
|
-
# (every core chapter's own `read_model` filters its roster with
|
|
30
|
-
# them), so they must resolve through
|
|
31
|
-
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK` while the grammar
|
|
32
|
-
# table is still being built.
|
|
19
|
+
# `where`/`order_by` resolve through `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`
|
|
20
|
+
# while the grammar table is being built.
|
|
33
21
|
#
|
|
34
|
-
# @param clauses [Hash{Symbol => Object}] field name
|
|
35
|
-
# one
|
|
36
|
-
#
|
|
37
|
-
# value means `eq`
|
|
38
|
-
# @return [void]
|
|
39
|
-
# @raise [ArgumentError] if a Hash value does not have exactly one pair, or names a
|
|
40
|
-
# comparator outside `COMPARATORS`
|
|
22
|
+
# @param clauses [Hash{Symbol => Object}] field name to a literal, a query argument
|
|
23
|
+
# Symbol, or a one-pair Hash such as `{ gte: :minimum }`; a bare value means `eq`
|
|
24
|
+
# @raise [ArgumentError] if a Hash value is not one known `COMPARATORS` pair
|
|
41
25
|
def where_impl(clauses)
|
|
42
26
|
@wheres ||= []
|
|
43
27
|
clauses.each do |field, value|
|