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
|
@@ -1,53 +1,36 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
5
|
-
# are the readings taken off them.
|
|
4
|
+
# Readings over a policy's declared fields.
|
|
6
5
|
module Policy
|
|
7
|
-
# The bluebook's name for this construct
|
|
8
|
-
# that has crossed over and of an IR object that has not. Collapses into
|
|
9
|
-
# Construct when this one crosses.
|
|
6
|
+
# The bluebook's name for this construct.
|
|
10
7
|
#
|
|
11
|
-
# @return [String]
|
|
8
|
+
# @return [String]
|
|
12
9
|
def hecks_name = @name
|
|
13
10
|
|
|
14
11
|
# The domain-and-aggregate prefix of the triggering event's name.
|
|
15
12
|
#
|
|
16
|
-
# @return [String, nil] the part of `on_event` before its first
|
|
17
|
-
# (the `"Domain::Aggregate"` prefix), or `nil` when `on_event`
|
|
18
|
-
# carries no dot
|
|
13
|
+
# @return [String, nil] the part of `on_event` before its first `.`, or `nil` if none
|
|
19
14
|
def event_qualifier = Naming.qualifier(@on_event)
|
|
20
15
|
|
|
21
|
-
# The bare name
|
|
22
|
-
# prefix.
|
|
16
|
+
# The triggering event's bare name, without its prefix.
|
|
23
17
|
#
|
|
24
|
-
# @return [String]
|
|
25
|
-
# bare event name), or the whole string when it carries no dot
|
|
18
|
+
# @return [String]
|
|
26
19
|
def event_name = Naming.unqualified(@on_event)
|
|
27
20
|
|
|
28
|
-
# Whether
|
|
29
|
-
# non-empty one turns a single reaction into one dispatch per row
|
|
30
|
-
# the query answers. Read by the interpreter that runs the fan-out
|
|
31
|
-
# and by the property that checks it dispatched once per row.
|
|
21
|
+
# Whether `for_each` names a query, turning one reaction into a dispatch per row.
|
|
32
22
|
#
|
|
33
|
-
# @return [Boolean]
|
|
23
|
+
# @return [Boolean]
|
|
34
24
|
def fans_out? = !@for_each.to_s.empty?
|
|
35
25
|
|
|
36
|
-
# Whether
|
|
37
|
-
# whether the policy fires at all, read against the triggering
|
|
38
|
-
# event's own payload.
|
|
26
|
+
# Whether a non-empty `where` decides if the policy fires at all.
|
|
39
27
|
#
|
|
40
|
-
# @return [Boolean]
|
|
28
|
+
# @return [Boolean]
|
|
41
29
|
def guarded? = !@where.to_s.empty?
|
|
42
30
|
|
|
43
|
-
# The structured form of `where`,
|
|
44
|
-
# `AstJson.emit_predicate` spells for every rule row, memoized
|
|
45
|
-
# here because a policy is consulted once per event, not once per
|
|
46
|
-
# boot. Nil when there is no `where`, exactly as the wire carries
|
|
47
|
-
# it.
|
|
31
|
+
# The structured form of `where`, memoized because a policy is consulted once per event.
|
|
48
32
|
#
|
|
49
|
-
# @return [Hash, nil]
|
|
50
|
-
# `nil` when this policy is not guarded
|
|
33
|
+
# @return [Hash, nil] `nil` when the policy is not guarded
|
|
51
34
|
def where_ast
|
|
52
35
|
if defined?(@where_ast)
|
|
53
36
|
@where_ast
|
|
@@ -56,39 +39,27 @@ module Hecks
|
|
|
56
39
|
end
|
|
57
40
|
end
|
|
58
41
|
|
|
59
|
-
# The
|
|
60
|
-
#
|
|
61
|
-
# it; an unmet where is a silent skip).
|
|
42
|
+
# The guard as a rule for `Evaluator.call_rule`; it has no description because an
|
|
43
|
+
# unmet `where` is a silent skip.
|
|
62
44
|
#
|
|
63
|
-
# @return [Bluebook::Given]
|
|
64
|
-
# and its `ast` already resolved via `where_ast`
|
|
45
|
+
# @return [Bluebook::Given]
|
|
65
46
|
def where_rule = @where_rule ||= Given.new(description: nil, canonical: @where, ast: where_ast)
|
|
66
47
|
|
|
67
|
-
# The fan-out query's route, split the way the runtime runs it
|
|
68
|
-
# `[query_domain, aggregate_name, query_name]`. The query runs
|
|
69
|
-
# against the triggering event's own domain unless `for_each`
|
|
70
|
-
# names one ("Domain::Aggregate.query"). Deliberately independent
|
|
71
|
-
# of `across`/`target_domain`, which name where `trigger` fires,
|
|
72
|
-
# not where the fan-out's own query runs.
|
|
48
|
+
# The fan-out query's route, split the way the runtime runs it.
|
|
73
49
|
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
50
|
+
# The query runs in the triggering event's domain unless `for_each` names one
|
|
51
|
+
# ("Domain::Aggregate.query"); it is independent of `across`/`target_domain`.
|
|
52
|
+
#
|
|
53
|
+
# @param default_domain [String, Symbol] the triggering event's domain
|
|
54
|
+
# @return [Array(String, String, String)] `[domain, aggregate_name, query_name]`
|
|
78
55
|
def for_each_route(default_domain)
|
|
79
56
|
path, query_name = @for_each.to_s.split(".", 2)
|
|
80
57
|
domain, aggregate = path.to_s.include?("::") ? path.split("::", 2) : [default_domain.to_s, path]
|
|
81
58
|
[domain, aggregate, query_name]
|
|
82
59
|
end
|
|
83
60
|
|
|
84
|
-
# The argument name
|
|
85
|
-
#
|
|
86
|
-
# declared shape (a target command declared on the very aggregate it
|
|
87
|
-
# self-references, such as `Account.Freeze`, is addressed by its own
|
|
88
|
-
# identity field's name, not a synthetic foreign key), not on the
|
|
89
|
-
# aggregate name alone, so it lives on
|
|
90
|
-
# `Behaviour::Command#addressing_key_for`, asked of the resolved
|
|
91
|
-
# target command by `PolicyInterpreter#addressing_key_for`.
|
|
61
|
+
# The argument name for each fan-out row's id is not minted here: it depends on the
|
|
62
|
+
# target command's declared shape, so it lives on `Behaviour::Command#addressing_key_for`.
|
|
92
63
|
end
|
|
93
64
|
end
|
|
94
65
|
end
|
|
@@ -1,96 +1,69 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# derived from the handler that answers a refusal, not declared.
|
|
4
|
+
# Readings over a process manager's trigger, states and handlers.
|
|
5
|
+
# Its `saga` is derived from the handler that answers a refusal, not declared.
|
|
7
6
|
module ProcessManager
|
|
8
7
|
# The bluebook's name for this construct.
|
|
9
8
|
#
|
|
10
|
-
# @return [String, nil]
|
|
11
|
-
# is set
|
|
9
|
+
# @return [String, nil]
|
|
12
10
|
def hecks_name = @name
|
|
13
11
|
|
|
14
|
-
# The leg that answers
|
|
15
|
-
# (docs/semantics/bluebook-semantics.md). Two legs may answer the
|
|
16
|
-
# same event from different states; which one runs is decided by
|
|
17
|
-
# the state the instance is in, never by declaration order. With
|
|
18
|
-
# no `state` the reading is declarative — "does any leg answer this
|
|
19
|
-
# event, and what is it" — for callers that ask about the
|
|
20
|
-
# procedure rather than an instance (`saga`, the outbox's consumer
|
|
21
|
-
# selection). Build refuses two legs on one (event, state) pair
|
|
22
|
-
# (`ProcessManagerBuilder#validate!`), so a stated lookup is
|
|
23
|
-
# unambiguous by construction.
|
|
12
|
+
# The leg that answers an event from a state (C10.3, docs/semantics/bluebook-semantics.md).
|
|
24
13
|
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# @
|
|
29
|
-
#
|
|
14
|
+
# State, never declaration order, picks between legs; build refuses two legs on one
|
|
15
|
+
# (event, state) pair. With no `state` the lookup is declarative: does any leg answer.
|
|
16
|
+
#
|
|
17
|
+
# @param event [String, Symbol]
|
|
18
|
+
# @param state [String, Symbol, nil] the instance's current state; `nil` ignores state
|
|
19
|
+
# @return [Bluebook::ProcessManagerHandler, nil]
|
|
30
20
|
def handler_for(event, state = nil)
|
|
31
21
|
@handlers.find { |h| h.event_type == event.to_s && (state.nil? || h.from_state == state.to_s) }
|
|
32
22
|
end
|
|
33
23
|
|
|
34
|
-
#
|
|
24
|
+
# Every declared leg for an event, regardless of source state.
|
|
35
25
|
#
|
|
36
|
-
# @param event [String, Symbol]
|
|
37
|
-
# @return [Array<Bluebook::ProcessManagerHandler>]
|
|
38
|
-
# `event`; `[]` if none is declared
|
|
26
|
+
# @param event [String, Symbol]
|
|
27
|
+
# @return [Array<Bluebook::ProcessManagerHandler>]
|
|
39
28
|
def handlers_for(event) = @handlers.select { |h| h.event_type == event.to_s }
|
|
40
29
|
|
|
41
|
-
#
|
|
30
|
+
# Whether any declared leg answers an event.
|
|
42
31
|
#
|
|
43
|
-
# @param event [String, Symbol]
|
|
44
|
-
# @return [Boolean]
|
|
32
|
+
# @param event [String, Symbol]
|
|
33
|
+
# @return [Boolean]
|
|
45
34
|
def handles?(event) = @handlers.any? { |h| h.event_type == event.to_s }
|
|
46
35
|
|
|
47
|
-
# Whether a state is one this procedure declares
|
|
48
|
-
#
|
|
49
|
-
# state), the way `Lifecycle#states` is asked of an aggregate's
|
|
50
|
-
# resting field. A rehydrated instance in a state no handler could
|
|
51
|
-
# have reached is corruption the durable round-trip introduced.
|
|
36
|
+
# Whether a state is one this procedure declares. A rehydrated instance in an
|
|
37
|
+
# undeclared state is corruption from the durable round-trip.
|
|
52
38
|
#
|
|
53
|
-
# @param state [String, Symbol]
|
|
54
|
-
# @return [Boolean]
|
|
39
|
+
# @param state [String, Symbol]
|
|
40
|
+
# @return [Boolean]
|
|
55
41
|
def declares_state?(state) = @states.map(&:to_s).include?(state.to_s)
|
|
56
42
|
|
|
57
|
-
#
|
|
43
|
+
# The payload field a triggering event's instances are correlated by.
|
|
58
44
|
#
|
|
59
|
-
# @return [Symbol] the
|
|
45
|
+
# @return [Symbol] the part of `correlates_by` before the first `.`
|
|
60
46
|
def correlation_head = @correlates_by.to_s.split(".").first.to_sym
|
|
61
47
|
|
|
62
|
-
# The compensation half
|
|
63
|
-
# answers `REFUSED`.
|
|
64
|
-
#
|
|
65
|
-
# nil for a procedure with no answer to a refusal, which is a legitimate
|
|
66
|
-
# thing to be — a hiring pipeline cannot un-interview anybody.
|
|
48
|
+
# The compensation half, read off the handler that answers `REFUSED`.
|
|
67
49
|
#
|
|
68
|
-
# @return [Bluebook::Saga, nil]
|
|
69
|
-
#
|
|
50
|
+
# @return [Bluebook::Saga, nil] `nil` when no handler answers a refusal, which is
|
|
51
|
+
# legitimate: a hiring pipeline cannot un-interview anybody
|
|
70
52
|
def saga
|
|
71
53
|
leg = handler_for(Bluebook::ProcessManager::REFUSED)
|
|
72
54
|
return nil unless leg
|
|
73
55
|
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
# `SagaInterpreter`'s own `completed_compensations`, not a
|
|
78
|
-
# fact `Saga` — a pure declaration reading — could ever hold).
|
|
79
|
-
# Every `compensates` any handler's own dispatch declares,
|
|
80
|
-
# forward declaration order, then whatever this leg's own
|
|
81
|
-
# hand-written body still lists — coexistence, not replacement
|
|
82
|
-
# (`ProcessManagerBuilder::HandlerBuilder#dispatch_impl`'s own
|
|
83
|
-
# comment): a saga can derive some of its compensation and
|
|
84
|
-
# still hand-write the rest for what isn't expressible as
|
|
85
|
-
# "undo command X".
|
|
56
|
+
# A static preview in declaration order, not one instance's runtime history.
|
|
57
|
+
# Derived `compensates` come first, then the leg's hand-written body, so a saga
|
|
58
|
+
# can derive some compensation and hand-write the rest.
|
|
86
59
|
derived = handlers.flat_map { |handler| handler.dispatches.filter_map(&:compensates) }
|
|
87
60
|
Bluebook::Saga.new(trigger: Bluebook::ProcessManager::REFUSED, from_state: leg.from_state,
|
|
88
61
|
to_state: leg.to_state, compensations: derived + leg.dispatches)
|
|
89
62
|
end
|
|
90
63
|
|
|
91
|
-
#
|
|
64
|
+
# Whether this procedure declares a handler for `REFUSED`.
|
|
92
65
|
#
|
|
93
|
-
# @return [Boolean]
|
|
66
|
+
# @return [Boolean]
|
|
94
67
|
def saga? = !saga.nil?
|
|
95
68
|
end
|
|
96
69
|
end
|
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
4
|
+
# What a read model does. Its declared half is the gathered heads
|
|
5
5
|
# and the query shape; these are readings taken off them.
|
|
6
6
|
module ReadModel
|
|
7
|
+
# Every reduction a read model may declare over its one eligible collection, each
|
|
8
|
+
# naming the ivar that holds its declared value and the word it reads back as in a
|
|
9
|
+
# refusal message. A read model reports one shape, so at most one of these is ever set.
|
|
10
|
+
REDUCTION_FIELDS = {
|
|
11
|
+
count: "count", median_field: "median", sum_field: "sum", avg_field: "avg",
|
|
12
|
+
min_field: "min", max_field: "max", percentile_field: "percentile",
|
|
13
|
+
any_field: "any", all_field: "all"
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
# @return [Boolean] whether this read model declares any reduction (`REDUCTION_FIELDS`)
|
|
17
|
+
def reducing? = REDUCTION_FIELDS.each_key.any? { |ivar| instance_variable_get(:"@#{ivar}") }
|
|
18
|
+
|
|
7
19
|
# Lists the fields this read model groups rows by.
|
|
8
20
|
#
|
|
9
21
|
# @return [Array<Symbol>] each declared `group_by` field's name, in declaration order
|
|
10
22
|
def group_by_fields = @group_by.map { |row| row[:field].to_sym }
|
|
11
23
|
|
|
12
|
-
# Reports whether this read model's `group_by` names every identity
|
|
13
|
-
# of the aggregate it groups (ADR 0061
|
|
24
|
+
# Reports whether this read model's `group_by` names every identity
|
|
25
|
+
# head of the aggregate it groups (ADR 0061).
|
|
14
26
|
#
|
|
15
|
-
# Such a key path
|
|
16
|
-
# declaration alone
|
|
17
|
-
# other key path is checked per request.
|
|
27
|
+
# Such a key path can't be shared by two rows, so uniqueness is accepted
|
|
28
|
+
# from the declaration alone rather than checked per request.
|
|
18
29
|
#
|
|
19
30
|
# @param aggregate [Bluebook::Aggregate, nil] the aggregate of this read
|
|
20
31
|
# model's one many-side head
|
|
@@ -27,10 +38,8 @@ module Hecks
|
|
|
27
38
|
!identity.empty? && (identity - group_by_fields).empty?
|
|
28
39
|
end
|
|
29
40
|
|
|
30
|
-
# `!!`
|
|
31
|
-
#
|
|
32
|
-
# `true`/`nil`, so this is belt and braces against a future
|
|
33
|
-
# caller constructing a ReadModel by hand with `count: false`.
|
|
41
|
+
# `!!` guards against a future caller constructing a ReadModel by
|
|
42
|
+
# hand with `count: false` rather than the normalised `nil`.
|
|
34
43
|
#
|
|
35
44
|
# @return [Boolean] whether this read model reduces to a row count
|
|
36
45
|
def count? = !!@count
|
|
@@ -40,14 +49,9 @@ module Hecks
|
|
|
40
49
|
# @return [String] this read model's name in `snake_case`
|
|
41
50
|
def query_name = Naming.snake(@name)
|
|
42
51
|
|
|
43
|
-
# Which gathered heads the filtering applies to (ADR 0055)
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
# model with a single many-side head keeps the old reading: every
|
|
47
|
-
# untargeted option (plus `group_by`/`count`/`median`, still
|
|
48
|
-
# single-head-only — ADR 0055) applies to it, same as before `on:`
|
|
49
|
-
# existed. With several many-side heads, only the ones actually named
|
|
50
|
-
# by a targeted option are eligible.
|
|
52
|
+
# Which gathered heads the filtering applies to (ADR 0055): with one
|
|
53
|
+
# many-side head every untargeted option applies to it; with several,
|
|
54
|
+
# only the ones a targeted option names via `on:` are eligible.
|
|
51
55
|
#
|
|
52
56
|
# @return [Array<Symbol>] the `:as` name of each many-side head that filtering
|
|
53
57
|
# applies to; `[]` if this read model has no many-side head
|
|
@@ -61,10 +65,7 @@ module Hecks
|
|
|
61
65
|
targets.filter_map { |target| many.find { |head| head[:aggregate] == target.to_s } }.map { |head| head[:as] }
|
|
62
66
|
end
|
|
63
67
|
|
|
64
|
-
#
|
|
65
|
-
# many-side head, every untargeted option (plus `group_by`/`count`/
|
|
66
|
-
# `median`, still single-head-only) applies to it — split out only
|
|
67
|
-
# to keep `filtered_head_names` itself under this file's own
|
|
68
|
+
# Split out only to keep `filtered_head_names` under this file's own
|
|
68
69
|
# complexity budget, not because the two questions differ in kind.
|
|
69
70
|
#
|
|
70
71
|
# @param many [Array<Hash{Symbol => Object}>] the read model's many-side
|
|
@@ -73,18 +74,11 @@ module Hecks
|
|
|
73
74
|
# declared, else `[]`
|
|
74
75
|
def single_filtered_head_name(many)
|
|
75
76
|
declared = wheres.any? || order_by || limit || offset || authorization&.tenant ||
|
|
76
|
-
@group_by.any? ||
|
|
77
|
+
@group_by.any? || reducing?
|
|
77
78
|
declared ? [many.first[:as]] : []
|
|
78
79
|
end
|
|
79
80
|
|
|
80
|
-
#
|
|
81
|
-
# (ADR 0055) — a small view `Ports::Query::InMemory.execute` reads
|
|
82
|
-
# exactly the way it already reads a whole `Query`/`ReadModel`
|
|
83
|
-
# (`.wheres`/`.order_by`/`.limit`/`.offset`/`.null_semantics`), scoped
|
|
84
|
-
# to `head_as`'s own aggregate: an untargeted option applies when
|
|
85
|
-
# `head_as` is the read model's one many-side head (the pre-`on:`
|
|
86
|
-
# reading, unchanged) ; a targeted one applies when its `target`
|
|
87
|
-
# resolves to `head_as`'s own aggregate.
|
|
81
|
+
# Filtering options scoped to one included aggregate head (ADR 0055).
|
|
88
82
|
FilteredOptions = Struct.new(:wheres, :order_by, :limit, :offset, :null_semantics)
|
|
89
83
|
|
|
90
84
|
# Scopes this read model's filtering options down to one included head.
|
|
@@ -1,35 +1,17 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# out four separate times — on Aggregate, Entity, Command and
|
|
8
|
-
# PortOperation — and the identity derivation twice, byte for byte,
|
|
9
|
-
# for as long as those constructs have existed. Splitting holding
|
|
10
|
-
# from doing is what made them visible: the same method in four
|
|
11
|
-
# files reads as four methods, and the same method in four modules
|
|
12
|
-
# named `Behaviour::*` reads as one repeated.
|
|
13
|
-
#
|
|
14
|
-
# Written to work whether the construct is included into (an
|
|
15
|
-
# ordinary object like Aggregate) or extended into (a class-shaped
|
|
16
|
-
# one like Command), because every one of them reads instance
|
|
17
|
-
# variables and nothing else.
|
|
4
|
+
# Behaviour shared by more than one construct. Each module works whether it is included
|
|
5
|
+
# (an ordinary object like Aggregate) or extended (a class-shaped one like Command),
|
|
6
|
+
# because it reads only instance variables.
|
|
18
7
|
|
|
19
8
|
# A construct whose identity is a join of declared paths.
|
|
20
9
|
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# named beneath another thing needs. `identity_heads` are the attributes
|
|
25
|
-
# those paths start at — what every reader that looks up or coerces an
|
|
26
|
-
# attribute actually wants — and `identified_by` is the single head,
|
|
27
|
-
# offered only when there is one path to have a head of. A composite has
|
|
28
|
-
# no single head, and answering with the first would be a guess ; the
|
|
29
|
-
# readers that need all of them ask for `identity_heads`.
|
|
10
|
+
# Several paths mean the identity is made of several facts. `identity_heads` are the
|
|
11
|
+
# attributes those paths start at; `identified_by` is the single head, set only when there
|
|
12
|
+
# is exactly one, since a composite has no single head and the first would be a guess.
|
|
30
13
|
module Identified
|
|
31
|
-
# Derives `identity_paths`, `identity_heads` and the single-head
|
|
32
|
-
# `identified_by` from the declared `identified_by` path(s).
|
|
14
|
+
# Derives `identity_paths`, `identity_heads` and the single-head `identified_by`.
|
|
33
15
|
#
|
|
34
16
|
# @return [void]
|
|
35
17
|
def derive_identity
|
|
@@ -41,72 +23,53 @@ module Hecks
|
|
|
41
23
|
|
|
42
24
|
# A construct that answers for its own declarations by name.
|
|
43
25
|
#
|
|
44
|
-
# Indexed once
|
|
45
|
-
#
|
|
46
|
-
# linear scan repeated on every call was doing work the declared shape
|
|
47
|
-
# had already settled at boot.
|
|
48
|
-
#
|
|
49
|
-
# `index_by_name` takes the collections to index rather than naming
|
|
50
|
-
# them, because which collections a construct has is exactly the part
|
|
51
|
-
# that differs: an aggregate has five, an entity three, a command one.
|
|
26
|
+
# Indexed once because declarations are final when the construct exists. `index_by_name`
|
|
27
|
+
# takes the collections to index since which ones a construct has differs per construct.
|
|
52
28
|
module Indexed
|
|
53
|
-
# Keyed by symbol
|
|
54
|
-
# symbol name everywhere in the runtime.
|
|
29
|
+
# Keyed by symbol, the way the runtime asks for an attribute.
|
|
55
30
|
#
|
|
56
|
-
# @param attributes [Array<Bluebook::Attribute>]
|
|
57
|
-
# attributes
|
|
31
|
+
# @param attributes [Array<Bluebook::Attribute>]
|
|
58
32
|
# @return [void]
|
|
59
33
|
def index_attributes(attributes)
|
|
60
34
|
@attributes_by_name = attributes.to_h { |held| [held.name, held] }
|
|
61
35
|
end
|
|
62
36
|
|
|
63
|
-
# Keyed by string and by `hecks_name
|
|
64
|
-
#
|
|
65
|
-
# entirely (a constant path, or nothing at all for an anonymous
|
|
66
|
-
# class), so the declared name is the only one worth indexing.
|
|
37
|
+
# Keyed by string and by `hecks_name`, since such a construct's Ruby `name` is a
|
|
38
|
+
# constant path or nothing at all for an anonymous class.
|
|
67
39
|
#
|
|
68
|
-
# @param collection [Array<#hecks_name>]
|
|
69
|
-
#
|
|
70
|
-
# @return [Hash{String => Object}] the collection keyed by each member's
|
|
71
|
-
# `hecks_name`
|
|
40
|
+
# @param collection [Array<#hecks_name>] value objects, commands or queries
|
|
41
|
+
# @return [Hash{String => Object}]
|
|
72
42
|
def index_by_hecks_name(collection)
|
|
73
43
|
collection.to_h { |held| [held.hecks_name, held] }
|
|
74
44
|
end
|
|
75
45
|
|
|
76
|
-
# Finds a declared attribute by
|
|
46
|
+
# Finds a declared attribute by name.
|
|
77
47
|
#
|
|
78
|
-
# @param named [String, Symbol]
|
|
79
|
-
# @return [Bluebook::Attribute, nil]
|
|
80
|
-
# if none is declared under that name
|
|
48
|
+
# @param named [String, Symbol]
|
|
49
|
+
# @return [Bluebook::Attribute, nil]
|
|
81
50
|
def attribute(named) = @attributes_by_name[named.to_sym]
|
|
82
51
|
|
|
83
|
-
# Finds a declared command by
|
|
52
|
+
# Finds a declared command by name.
|
|
84
53
|
#
|
|
85
|
-
# @param named [String, Symbol]
|
|
86
|
-
# @return [Class, nil]
|
|
87
|
-
# named `named`, or `nil` if none is declared under that name
|
|
54
|
+
# @param named [String, Symbol]
|
|
55
|
+
# @return [Class, nil] a `Bluebook::Command` subclass
|
|
88
56
|
def command(named) = @commands_by_name[named.to_s]
|
|
89
57
|
|
|
90
|
-
# Finds a declared query by
|
|
58
|
+
# Finds a declared query by name.
|
|
91
59
|
#
|
|
92
|
-
# @param named [String, Symbol]
|
|
93
|
-
# @return [Bluebook::Query, nil]
|
|
94
|
-
# is declared under that name
|
|
60
|
+
# @param named [String, Symbol]
|
|
61
|
+
# @return [Bluebook::Query, nil]
|
|
95
62
|
def query(named) = @queries_by_name[named.to_s]
|
|
96
63
|
end
|
|
97
64
|
|
|
98
65
|
# A construct that owns what it declares.
|
|
99
66
|
#
|
|
100
|
-
# Owner links are
|
|
101
|
-
#
|
|
102
|
-
# is complete is the right stamping point: its declarations are
|
|
103
|
-
# final, and nothing outside needs to remember to stamp them.
|
|
67
|
+
# Owner links are read lazily (hecks_fqn, Reference#resolve), so stamping when the
|
|
68
|
+
# construct is complete is early enough and needs no help from outside.
|
|
104
69
|
module Owns
|
|
105
70
|
# Stamps each of `children` as owned by this construct.
|
|
106
71
|
#
|
|
107
|
-
# @param children [Array
|
|
108
|
-
# arrays or bare objects, flattened before stamping — each answering
|
|
109
|
-
# `hecks_owner=`
|
|
72
|
+
# @param children [Array<#hecks_owner=>] arrays or bare objects, flattened first
|
|
110
73
|
# @return [void]
|
|
111
74
|
def stamp(*children)
|
|
112
75
|
children.flatten.each { |child| child.hecks_owner = self }
|
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
3
|
module Behaviour
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# behaviour is singleton behaviour, and the holding half's `absorb`
|
|
7
|
-
# is what a generated constructor would be.
|
|
4
|
+
# Behaviour of a value object; extended rather than included, since each shape is
|
|
5
|
+
# its own class.
|
|
8
6
|
module ValueObject
|
|
9
|
-
# Recorded as its own fact
|
|
10
|
-
# `members: []` alone — an empty `one_of` and no `one_of` at all would
|
|
11
|
-
# otherwise be indistinguishable, which would leave the rule about it
|
|
12
|
-
# only livable in the builder. Recording the declaration lets the
|
|
13
|
-
# language judge it instead, the same way an empty attribute name
|
|
14
|
-
# survives into the IR and is judged there.
|
|
7
|
+
# Recorded as its own fact so an empty `one_of` differs from no `one_of` at all.
|
|
15
8
|
#
|
|
16
9
|
# @return [Boolean] whether this value object declares `one_of`, even
|
|
17
10
|
# if left empty
|
|
@@ -24,21 +17,9 @@ module Hecks
|
|
|
24
17
|
# `nil` if none is declared under that name
|
|
25
18
|
def attribute(named) = attributes.find { |held| held.name == named.to_sym }
|
|
26
19
|
|
|
27
|
-
# A single-attribute value object (EmailAddress{address},
|
|
28
|
-
# CustomerNumber{value}) is a name for a scalar, not a genuine
|
|
29
|
-
# group — [[feedback_name_the_scalar_field]]. `adapters/driven/
|
|
30
|
-
# sql_query_builder.rb` and `fuzzing/invalid_value_generator.rb`
|
|
31
|
-
# now read through this rather than inlining the check.
|
|
20
|
+
# A single-attribute value object (EmailAddress{address}) names a scalar, not a group.
|
|
32
21
|
#
|
|
33
|
-
#
|
|
34
|
-
# (`closed_set_options`/`closed_set_field`) look identical but are
|
|
35
|
-
# not the same question — they pick a closed set's discriminant
|
|
36
|
-
# column, and a closed set can be genuinely multi-attribute
|
|
37
|
-
# (`Runtime::Value::Admission#member_matches?`'s own comment
|
|
38
|
-
# names a real one: `StatementFrequency`'s `cadence`/
|
|
39
|
-
# `retention_months`/`paper_fee_cents`). `sole_attribute` would
|
|
40
|
-
# return `nil` for that shape and break the discriminant lookup —
|
|
41
|
-
# left as `.first` on purpose, not a missed migration.
|
|
22
|
+
# A closed set's discriminant column is a different question and may span attributes.
|
|
42
23
|
#
|
|
43
24
|
# @return [Bluebook::Attribute, nil] this value object's only
|
|
44
25
|
# attribute, or `nil` when it has zero or more than one
|
|
@@ -1,65 +1,33 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Bluebook
|
|
3
|
-
# What a chapter may declare it provides
|
|
4
|
-
#
|
|
5
|
-
# place of a check for the literal name "Governance" — by the role
|
|
6
|
-
# check at dispatch, the ungoverned-role refusal at boot, the
|
|
7
|
-
# authorization adapter and the fuzzer — so the declaration has to be
|
|
8
|
-
# complete and point at real verbs. `BluebookBuilder::Validation
|
|
9
|
-
# #validate_provisions!` holds every `provides` to this table.
|
|
10
|
-
#
|
|
11
|
-
# key => :command | :query | :port_operation the kind of verb that key must name
|
|
12
|
-
#
|
|
13
|
-
# A `:port_operation` verb is spelled `"Aggregate.Port.Operation"` and names
|
|
14
|
-
# an operation the chapter's hecksagon declares on one of its aggregates.
|
|
15
|
-
# The hecksagon attaches after the chapter is built, so the chapter itself
|
|
16
|
-
# only checks the spelling; `Registry#verify!` checks the operation exists.
|
|
3
|
+
# What a chapter may declare it provides — trusted in place of a literal
|
|
4
|
+
# chapter-name check by role checks, adapters and the fuzzer.
|
|
17
5
|
module Capabilities
|
|
18
6
|
AUTHORIZATION = "authorization".freeze
|
|
19
|
-
# Who may sign in
|
|
20
|
-
#
|
|
21
|
-
# the aggregate (HECKS_MEMBERSHIP_AGGREGATE). Same declared-
|
|
22
|
-
# not-named shape `AUTHORIZATION` already is for Governance.
|
|
7
|
+
# Who may sign in and with what role, resolved by declaration rather
|
|
8
|
+
# than an env var naming the aggregate.
|
|
23
9
|
MEMBERSHIP = "membership".freeze
|
|
24
|
-
# A stable organizational identity,
|
|
25
|
-
#
|
|
26
|
-
# "Identity". Same declared-not-named shape `AUTHORIZATION` already
|
|
27
|
-
# is. rust/host does not build this chapter's payloads; any field
|
|
28
|
-
# mapping lives on the consuming hecksagon's `translates` ACL.
|
|
10
|
+
# A stable organizational identity, recognised by declaration rather
|
|
11
|
+
# than the literal name "Identity".
|
|
29
12
|
IDENTITY = "identity".freeze
|
|
30
|
-
# Guest
|
|
31
|
-
#
|
|
32
|
-
# "Newsletter". rust/host's newsletter routes dispatch these verbs
|
|
33
|
-
# and read the subscribing aggregate's instances off `subscribe`.
|
|
13
|
+
# Guest newsletter signups, recognised by declaration rather than the
|
|
14
|
+
# literal chapter name "Newsletter".
|
|
34
15
|
NEWSLETTER = "newsletter".freeze
|
|
35
|
-
# Sending an issue to
|
|
36
|
-
#
|
|
37
|
-
# capability from `NEWSLETTER` so a chapter that only takes signups
|
|
38
|
-
# declares nothing more; rust/host serves the send route only when a
|
|
39
|
-
# chapter declares this one as well.
|
|
16
|
+
# Sending an issue to confirmed subscribers; kept separate from
|
|
17
|
+
# `NEWSLETTER` so a signup-only chapter declares nothing more.
|
|
40
18
|
NEWSLETTER_ISSUES = "newsletter_issues".freeze
|
|
41
19
|
|
|
42
|
-
# Taking a payment through an external processor
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# routes dispatch these verbs and read the paying aggregate's
|
|
46
|
-
# instances off `initiate`. The two verdicts are hecksagon port
|
|
47
|
-
# operations (the processor's webhook, translated into this chapter's
|
|
48
|
-
# vocabulary), not commands.
|
|
20
|
+
# Taking a payment through an external processor, recognised by
|
|
21
|
+
# declaration rather than the literal name "Payments"; the two
|
|
22
|
+
# verdicts are port operations, not commands.
|
|
49
23
|
PAYMENTS = "payments".freeze
|
|
50
24
|
|
|
51
|
-
# Scheduling sessions and taking registrations
|
|
52
|
-
#
|
|
53
|
-
# "Registration". rust/host's event and registration routes dispatch
|
|
54
|
-
# these verbs; the aggregates they name are the event and the
|
|
55
|
-
# registration.
|
|
25
|
+
# Scheduling sessions and taking registrations, recognised by
|
|
26
|
+
# declaration rather than the literal aggregate names.
|
|
56
27
|
REGISTRATIONS = "registrations".freeze
|
|
57
28
|
|
|
58
|
-
# A business's connection to its payment processor
|
|
59
|
-
#
|
|
60
|
-
# declaration, not the literal name "PaymentConnection". rust/host's
|
|
61
|
-
# payments routes dispatch these verbs; the connection aggregate is
|
|
62
|
-
# the one `connect` names.
|
|
29
|
+
# A business's connection to its payment processor, recognised by
|
|
30
|
+
# declaration rather than the literal name "PaymentConnection".
|
|
63
31
|
PAYMENT_CONNECTION = "payment_connection".freeze
|
|
64
32
|
|
|
65
33
|
CONTRACTS = {
|