hecks 0.2.0 → 1.0.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 +5 -5
- data/lib/hecks/adapters/driven/claude_code.adapter +3 -0
- data/lib/hecks/adapters/driven/claude_code.rb +127 -0
- data/lib/hecks/adapters/driven/d1.adapter +12 -0
- data/lib/hecks/adapters/driven/d1.rb +427 -0
- data/lib/hecks/adapters/driven/folder.adapter +3 -0
- data/lib/hecks/adapters/driven/folder.rb +199 -0
- data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
- data/lib/hecks/adapters/driven/google_authentication.rb +100 -0
- data/lib/hecks/adapters/driven/governance_authorization.adapter +3 -0
- data/lib/hecks/adapters/driven/governance_authorization.rb +101 -0
- data/lib/hecks/adapters/driven/heki/journal.rb +56 -0
- data/lib/hecks/adapters/driven/heki/saga_store.rb +99 -0
- data/lib/hecks/adapters/driven/heki/snapshot.rb +65 -0
- data/lib/hecks/adapters/driven/heki.adapter +4 -0
- data/lib/hecks/adapters/driven/heki.rb +179 -0
- data/lib/hecks/adapters/driven/identity_registry.adapter +3 -0
- data/lib/hecks/adapters/driven/identity_registry.rb +26 -0
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +51 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +63 -0
- data/lib/hecks/adapters/driven/lambda.adapter +4 -0
- data/lib/hecks/adapters/driven/lambda.rb +145 -0
- data/lib/hecks/adapters/driven/memory.adapter +3 -0
- data/lib/hecks/adapters/driven/memory.rb +103 -0
- data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +3 -0
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +28 -0
- data/lib/hecks/adapters/driven/postgres/codec.rb +88 -0
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +207 -0
- data/lib/hecks/adapters/driven/postgres.adapter +5 -0
- data/lib/hecks/adapters/driven/postgres.rb +438 -0
- data/lib/hecks/adapters/driven/postgres_era.adapter +17 -0
- data/lib/hecks/adapters/driven/prism.adapter +3 -0
- data/lib/hecks/adapters/driven/prism.rb +80 -0
- data/lib/hecks/adapters/driven/secure_random_identity.adapter +3 -0
- data/lib/hecks/adapters/driven/secure_random_identity.rb +14 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +221 -0
- data/lib/hecks/adapters/driven/sqlite/codec.rb +80 -0
- data/lib/hecks/adapters/driven/sqlite/projection.rb +172 -0
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +192 -0
- data/lib/hecks/adapters/driven/sqlite.adapter +9 -0
- data/lib/hecks/adapters/driven/sqlite.rb +306 -0
- data/lib/hecks/adapters/driven/system_clock.adapter +3 -0
- data/lib/hecks/adapters/driven/system_clock.rb +14 -0
- data/lib/hecks/adapters/driven.rb +54 -0
- data/lib/hecks/adapters.rb +6 -0
- data/lib/hecks/behaviors/dsl.rb +125 -0
- data/lib/hecks/behaviors/expectations.rb +340 -0
- data/lib/hecks/behaviors/ir.rb +31 -0
- data/lib/hecks/behaviors/rspec.rb +42 -0
- data/lib/hecks/behaviors/runner.rb +96 -0
- data/lib/hecks/behaviors.rb +25 -0
- data/lib/hecks/bluebook/aggregate.rb +108 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +134 -0
- data/lib/hecks/bluebook/assembly/build.rb +48 -0
- data/lib/hecks/bluebook/assembly/contract.rb +112 -0
- data/lib/hecks/bluebook/assembly/contracts.rb +438 -0
- data/lib/hecks/bluebook/assembly/marks.rb +228 -0
- data/lib/hecks/bluebook/assembly/specializer.rb +70 -0
- data/lib/hecks/bluebook/assembly.rb +91 -0
- data/lib/hecks/bluebook/attribute.rb +96 -0
- data/lib/hecks/bluebook/behaviour/aggregate.rb +83 -0
- data/lib/hecks/bluebook/behaviour/attribute.rb +27 -0
- data/lib/hecks/bluebook/behaviour/chapter.rb +72 -0
- data/lib/hecks/bluebook/behaviour/command.rb +116 -0
- data/lib/hecks/bluebook/behaviour/domain_port.rb +25 -0
- data/lib/hecks/bluebook/behaviour/entity.rb +59 -0
- data/lib/hecks/bluebook/behaviour/hexagon.rb +50 -0
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +68 -0
- data/lib/hecks/bluebook/behaviour/policy.rb +52 -0
- data/lib/hecks/bluebook/behaviour/process_manager.rb +51 -0
- data/lib/hecks/bluebook/behaviour/query.rb +10 -0
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -0
- data/lib/hecks/bluebook/behaviour/traits.rb +81 -0
- data/lib/hecks/bluebook/behaviour/value_object.rb +33 -0
- data/lib/hecks/bluebook/chapter.rb +78 -0
- data/lib/hecks/bluebook/command.rb +124 -0
- data/lib/hecks/bluebook/domain_port.rb +102 -0
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +34 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +1018 -0
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +348 -0
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +71 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +1087 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +714 -0
- data/lib/hecks/bluebook/dsl/const_shim.rb +81 -0
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +79 -0
- data/lib/hecks/bluebook/dsl/entity_builder.rb +430 -0
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +366 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +163 -0
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +191 -0
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +44 -0
- data/lib/hecks/bluebook/dsl/malformed.rb +7 -0
- data/lib/hecks/bluebook/dsl/policy_builder.rb +135 -0
- data/lib/hecks/bluebook/dsl/port_builder.rb +39 -0
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +142 -0
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +306 -0
- data/lib/hecks/bluebook/dsl/query_builder.rb +113 -0
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +233 -0
- data/lib/hecks/bluebook/dsl/rule_reference.rb +173 -0
- data/lib/hecks/bluebook/dsl/translation_builder.rb +243 -0
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +178 -0
- data/lib/hecks/bluebook/dsl/word_gate.rb +228 -0
- data/lib/hecks/bluebook/dsl/world_builder.rb +117 -0
- data/lib/hecks/bluebook/dsl.rb +45 -0
- data/lib/hecks/bluebook/entity.rb +103 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +123 -0
- data/lib/hecks/bluebook/expression/evaluator.rb +304 -0
- data/lib/hecks/bluebook/expression/projection.json +218 -0
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +233 -0
- data/lib/hecks/bluebook/expression/resolver.rb +781 -0
- data/lib/hecks/bluebook/expression.rb +13 -0
- data/lib/hecks/bluebook/hexagon.rb +60 -0
- data/lib/hecks/bluebook/lifecycle.rb +42 -0
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +54 -0
- data/lib/hecks/bluebook/meta_validator/judge.rb +621 -0
- data/lib/hecks/bluebook/meta_validator/plan.rb +332 -0
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +51 -0
- data/lib/hecks/bluebook/meta_validator/readings.rb +360 -0
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +351 -0
- data/lib/hecks/bluebook/meta_validator/shapes.rb +266 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +255 -0
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +138 -0
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +78 -0
- data/lib/hecks/bluebook/meta_validator.rb +542 -0
- data/lib/hecks/bluebook/model_check.rb +445 -0
- data/lib/hecks/bluebook/pattern_subset.rb +184 -0
- data/lib/hecks/bluebook/policy.rb +41 -0
- data/lib/hecks/bluebook/process_manager.rb +128 -0
- data/lib/hecks/bluebook/project_discovery.rb +30 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +82 -0
- data/lib/hecks/bluebook/query.rb +61 -0
- data/lib/hecks/bluebook/read_model.rb +109 -0
- data/lib/hecks/bluebook/reference.rb +74 -0
- data/lib/hecks/bluebook/smoke_test.rb +166 -0
- data/lib/hecks/bluebook/synthesizer.rb +95 -0
- data/lib/hecks/bluebook/translation.rb +92 -0
- data/lib/hecks/bluebook/value_object.rb +66 -0
- data/lib/hecks/bluebook.rb +74 -0
- data/lib/hecks/codemod.rb +342 -0
- data/lib/hecks/construct.rb +71 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +219 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +4 -0
- data/lib/hecks/deploy/oidc.json +18 -0
- data/lib/hecks/doc/reference.rb +410 -0
- data/lib/hecks/embryonaut_bluebook.rb +75 -0
- data/lib/hecks/facade/cli_door.rb +119 -0
- data/lib/hecks/facade/cli_runner.rb +190 -0
- data/lib/hecks/facade/command_request.rb +105 -0
- data/lib/hecks/facade/handle.rb +173 -0
- data/lib/hecks/facade/json_door.rb +166 -0
- data/lib/hecks/facade/surface/aggregate_door.rb +185 -0
- data/lib/hecks/facade/surface/chapter.rb +107 -0
- data/lib/hecks/facade/surface.rb +48 -0
- data/lib/hecks/facade.rb +44 -0
- data/lib/hecks/forms/app.rb +341 -0
- data/lib/hecks/forms/command_form_renderer.rb +113 -0
- data/lib/hecks/forms/examples/banking_console.bluebook +3 -0
- data/lib/hecks/forms/field_renderer.rb +177 -0
- data/lib/hecks/forms/field_shape.rb +232 -0
- data/lib/hecks/forms/html.rb +84 -0
- data/lib/hecks/forms/index_renderer.rb +35 -0
- data/lib/hecks/forms/page.rb +157 -0
- data/lib/hecks/forms/params.rb +161 -0
- data/lib/hecks/forms/port_argument.rb +46 -0
- data/lib/hecks/forms/query_form_renderer.rb +114 -0
- data/lib/hecks/forms/record_renderer.rb +119 -0
- data/lib/hecks/forms/record_table.rb +68 -0
- data/lib/hecks/forms/reference_options.rb +30 -0
- data/lib/hecks/forms/value_object_shape.rb +46 -0
- data/lib/hecks/forms.rb +54 -0
- data/lib/hecks/fqn.rb +94 -0
- data/lib/hecks/framework/bluebook/compliance.bluebook +1 -0
- data/lib/hecks/framework/bluebook/console_settings.bluebook +489 -0
- data/lib/hecks/framework/bluebook/framework.hecksagon +32 -0
- data/lib/hecks/framework/bluebook/governance.bluebook +145 -0
- data/lib/hecks/framework/bluebook/identity.bluebook +90 -0
- data/lib/hecks/framework/oidc.json +39 -0
- data/lib/hecks/framework.rb +90 -0
- data/lib/hecks/freezer.rb +67 -0
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +527 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +86 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +286 -0
- data/lib/hecks/fuzzing/properties.rb +1192 -0
- data/lib/hecks/fuzzing/replay.rb +668 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +93 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +80 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +96 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +250 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +131 -0
- data/lib/hecks/fuzzing/value_generator.rb +200 -0
- data/lib/hecks/fuzzing.rb +12 -0
- data/lib/hecks/grammar/evolve.rb +324 -0
- data/lib/hecks/grammar/expression.bluebook +420 -0
- data/lib/hecks/grammar/expression_operators.json +1648 -0
- data/lib/hecks/grammar/grammar.hecksagon +20 -0
- data/lib/hecks/grammar/oidc.json +69 -0
- data/lib/hecks/grammar/translation.bluebook +203 -0
- data/lib/hecks/grammar.rb +178 -0
- data/lib/hecks/ir.rb +126 -0
- data/lib/hecks/language/adapter.bluebook +116 -0
- data/lib/hecks/language/bluebook/aggregate.bluebook +581 -0
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +76 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +256 -0
- data/lib/hecks/language/bluebook/bluebook.hecksagon +24 -0
- data/lib/hecks/language/bluebook/command.bluebook +471 -0
- data/lib/hecks/language/bluebook/entity.bluebook +392 -0
- data/lib/hecks/language/bluebook/policy.bluebook +189 -0
- data/lib/hecks/language/bluebook/process_manager.bluebook +381 -0
- data/lib/hecks/language/bluebook/projection.bluebook +267 -0
- data/lib/hecks/language/bluebook/query.bluebook +245 -0
- data/lib/hecks/language/bluebook/shape.bluebook +292 -0
- data/lib/hecks/language/bluebook/syntax.bluebook +448 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +379 -0
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +51 -0
- data/lib/hecks/language/hecksagon/domain_port.bluebook +76 -0
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +131 -0
- data/lib/hecks/language/hecksagon/port_operation.bluebook +102 -0
- data/lib/hecks/language/oidc.json +333 -0
- data/lib/hecks/language/port.bluebook +120 -0
- data/lib/hecks/language/translation/translation.bluebook +110 -0
- data/lib/hecks/language/translation/translation_aggregate.bluebook +267 -0
- data/lib/hecks/language/world/wiring.bluebook +62 -0
- data/lib/hecks/language/world/world.bluebook +84 -0
- data/lib/hecks/literal.rb +125 -0
- data/lib/hecks/naming.rb +174 -0
- data/lib/hecks/ports/access_control.port +9 -0
- data/lib/hecks/ports/access_control.rb +62 -0
- data/lib/hecks/ports/agent/answers.rb +104 -0
- data/lib/hecks/ports/agent.port +8 -0
- data/lib/hecks/ports/agent.rb +167 -0
- data/lib/hecks/ports/authentication.port +6 -0
- data/lib/hecks/ports/authentication.rb +50 -0
- data/lib/hecks/ports/authorization.port +7 -0
- data/lib/hecks/ports/authorization.rb +62 -0
- data/lib/hecks/ports/clock.port +5 -0
- data/lib/hecks/ports/clock.rb +62 -0
- data/lib/hecks/ports/extraction.port +5 -0
- data/lib/hecks/ports/extraction.rb +37 -0
- data/lib/hecks/ports/identity_assignment.port +5 -0
- data/lib/hecks/ports/identity_assignment.rb +45 -0
- data/lib/hecks/ports/identity_generation.port +5 -0
- data/lib/hecks/ports/identity_generation.rb +49 -0
- data/lib/hecks/ports/identity_resolution.port +5 -0
- data/lib/hecks/ports/identity_resolution.rb +40 -0
- data/lib/hecks/ports/loading.port +4 -0
- data/lib/hecks/ports/loading.rb +13 -0
- data/lib/hecks/ports/persistence/append_only.rb +138 -0
- data/lib/hecks/ports/persistence/binding_policy.rb +56 -0
- data/lib/hecks/ports/persistence/execution.rb +23 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +25 -0
- data/lib/hecks/ports/persistence/plugin.rb +54 -0
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +169 -0
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +128 -0
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +161 -0
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +61 -0
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +304 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +171 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +190 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +478 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +166 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +314 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +168 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +170 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +134 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +137 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +89 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +85 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +43 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +132 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +74 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +758 -0
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +120 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +31 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +46 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +102 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +46 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +70 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +72 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +120 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +183 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +41 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +28 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +30 -0
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +11 -0
- data/lib/hecks/ports/persistence/plugins/era.rb +48 -0
- data/lib/hecks/ports/persistence/remote_runtime.rb +42 -0
- data/lib/hecks/ports/persistence/repository_factory.rb +30 -0
- data/lib/hecks/ports/persistence.port +4 -0
- data/lib/hecks/ports/persistence.rb +39 -0
- data/lib/hecks/ports/projection.port +4 -0
- data/lib/hecks/ports/projection.rb +96 -0
- data/lib/hecks/ports/query/in_memory.rb +60 -0
- data/lib/hecks/ports/query/ordering.rb +41 -0
- data/lib/hecks/ports/query.rb +36 -0
- data/lib/hecks/ports.rb +27 -0
- data/lib/hecks/projections/diagrams.rb +642 -0
- data/lib/hecks/projections/ir.rb +18 -0
- data/lib/hecks/projections/model/deviations.rb +98 -0
- data/lib/hecks/projections/model.rb +145 -0
- data/lib/hecks/projections/oidc.rb +110 -0
- data/lib/hecks/projections/parser_table.rb +159 -0
- data/lib/hecks/projections/reference.rb +38 -0
- data/lib/hecks/projections/shape.rb +44 -0
- data/lib/hecks/projections/statements.rb +110 -0
- data/lib/hecks/projections/vocabulary.rb +100 -0
- data/lib/hecks/projections.rb +32 -0
- data/lib/hecks/projector/cli_projector.rb +446 -0
- data/lib/hecks/projector/docs_projector.rb +321 -0
- data/lib/hecks/projector/exporter.rb +158 -0
- data/lib/hecks/projector/ir_projector.rb +18 -0
- data/lib/hecks/projector/narrate_projector.rb +243 -0
- data/lib/hecks/projector/target.rb +97 -0
- data/lib/hecks/projector.rb +186 -0
- data/lib/hecks/query_ir.rb +411 -0
- data/lib/hecks/query_specification/common/authorization_spec.rb +9 -0
- data/lib/hecks/query_specification/common/comparators.rb +25 -0
- data/lib/hecks/query_specification/common/comparison.rb +174 -0
- data/lib/hecks/query_specification/common/cursor_spec.rb +9 -0
- data/lib/hecks/query_specification/common/dsl.rb +58 -0
- data/lib/hecks/query_specification/common/inspection_spec.rb +9 -0
- data/lib/hecks/query_specification/common/limit_spec.rb +9 -0
- data/lib/hecks/query_specification/common/null_policy.rb +92 -0
- data/lib/hecks/query_specification/common/null_semantics.rb +11 -0
- data/lib/hecks/query_specification/common/offset_spec.rb +9 -0
- data/lib/hecks/query_specification/common/options.rb +32 -0
- data/lib/hecks/query_specification/common/order_by.rb +9 -0
- data/lib/hecks/query_specification/common/specification.rb +10 -0
- data/lib/hecks/query_specification/common/where_clause.rb +9 -0
- data/lib/hecks/query_specification/field_path.rb +107 -0
- data/lib/hecks/query_specification/hop_path.rb +132 -0
- data/lib/hecks/query_specification/read_model/specification.rb +18 -0
- data/lib/hecks/query_specification.rb +14 -0
- data/lib/hecks/rendering.rb +48 -0
- data/lib/hecks/router/namespace_installer.rb +157 -0
- data/lib/hecks/router.rb +70 -0
- data/lib/hecks/runtime/aggregate_lock.rb +45 -0
- data/lib/hecks/runtime/boot_gates.rb +41 -0
- data/lib/hecks/runtime/caller.rb +66 -0
- data/lib/hecks/runtime/capability_graph.rb +44 -0
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +135 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +260 -0
- data/lib/hecks/runtime/command_interpreter.rb +497 -0
- data/lib/hecks/runtime/command_rules/admissibility.rb +357 -0
- data/lib/hecks/runtime/command_rules/arithmetic.rb +260 -0
- data/lib/hecks/runtime/command_rules/authorization.rb +65 -0
- data/lib/hecks/runtime/command_rules/emission.rb +34 -0
- data/lib/hecks/runtime/command_rules/references.rb +189 -0
- data/lib/hecks/runtime/command_rules.rb +28 -0
- data/lib/hecks/runtime/dependency_planning.rb +245 -0
- data/lib/hecks/runtime/dispatcher.rb +301 -0
- data/lib/hecks/runtime/entity_element.rb +253 -0
- data/lib/hecks/runtime/entity_interpreter.rb +299 -0
- data/lib/hecks/runtime/errors.rb +122 -0
- data/lib/hecks/runtime/event.rb +51 -0
- data/lib/hecks/runtime/identity.rb +156 -0
- data/lib/hecks/runtime/instance.rb +175 -0
- data/lib/hecks/runtime/interpreting.rb +90 -0
- data/lib/hecks/runtime/loader.rb +184 -0
- data/lib/hecks/runtime/policy_interpreter.rb +366 -0
- data/lib/hecks/runtime/port_operation_interpreter.rb +210 -0
- data/lib/hecks/runtime/query_interpreter.rb +286 -0
- data/lib/hecks/runtime/reaction_invocation.rb +253 -0
- data/lib/hecks/runtime/read_model_interpreter.rb +341 -0
- data/lib/hecks/runtime/rebuild_sweep.rb +74 -0
- data/lib/hecks/runtime/reference_hop.rb +99 -0
- data/lib/hecks/runtime/refusal_wording.rb +126 -0
- data/lib/hecks/runtime/registry/saga_persistence.rb +142 -0
- data/lib/hecks/runtime/registry/verification.rb +246 -0
- data/lib/hecks/runtime/registry.rb +279 -0
- data/lib/hecks/runtime/remote_dispatcher.rb +143 -0
- data/lib/hecks/runtime/routing.rb +96 -0
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +97 -0
- data/lib/hecks/runtime/saga_interpreter.rb +503 -0
- data/lib/hecks/runtime/saga_pending_dispatch.rb +45 -0
- data/lib/hecks/runtime/tenant_check.rb +84 -0
- data/lib/hecks/runtime/tenant_scope.rb +55 -0
- data/lib/hecks/runtime/value/admission.rb +130 -0
- data/lib/hecks/runtime/value/coercion.rb +565 -0
- data/lib/hecks/runtime/value/invariant_violation.rb +5 -0
- data/lib/hecks/runtime/value.rb +125 -0
- data/lib/hecks/runtime.rb +107 -0
- data/lib/hecks/storehouse.rb +632 -0
- data/lib/hecks/version.rb +16 -0
- data/lib/hecks/vocabulary.rb +218 -0
- data/lib/hecks.rb +123 -6
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +90 -0
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +128 -0
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +160 -0
- metadata +412 -222
- data/bin/hecks +0 -7
- data/bin/hecks-package +0 -65
- data/bin/hecks_console +0 -12
- data/bin/hecks_serverless +0 -6
- data/lib/cli/build.rb +0 -14
- data/lib/cli/command_runner.rb +0 -28
- data/lib/cli/console.rb +0 -10
- data/lib/cli/generate.rb +0 -37
- data/lib/cli/hecks-cli.rb +0 -27
- data/lib/cli/test.rb +0 -57
- data/lib/console/commands.rb +0 -8
- data/lib/console/hecks-console.rb +0 -1
- data/lib/packager/README.md +0 -0
- data/lib/packager/app_runner.rb +0 -21
- data/lib/packager/args.rb +0 -26
- data/lib/packager/compatibility/fixnum.rb +0 -6
- data/lib/packager/hecks.rb +0 -39
- data/lib/packager/query_runner.rb +0 -21
- data/lib/packager/resources/Dockerfile +0 -11
- data/lib/packager/resources/app_binary +0 -7
- data/lib/packager/resources/bundle_config +0 -3
- data/lib/packager/resources/traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz +0 -0
- data/lib/packager/resources/traveling-ruby-20150715-2.2.2-osx.tar.gz +0 -0
- data/lib/serverless/Domain +0 -32
- data/lib/serverless/cli.rb +0 -75
- data/lib/serverless/resources/command_name.js +0 -5
- data/lib/serverless/resources/environment.js +0 -7
- data/lib/serverless/resources/handler.js.tt +0 -28
- data/lib/serverless/resources/run_binary.js +0 -22
- data/lib/serverless/resources/serverless.yml +0 -20
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
require_relative "behaviour/process_manager"
|
|
2
|
+
require_relative "../ir"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Bluebook
|
|
6
|
+
# `compensates` — a SECOND `DispatchSpec`, shape-identical to this
|
|
7
|
+
# one, naming the command that undoes THIS dispatch specifically
|
|
8
|
+
# (see `ProcessManagerBuilder::HandlerBuilder#dispatch_impl`'s own
|
|
9
|
+
# comment). `nil` for a dispatch with nothing to undo (a pure
|
|
10
|
+
# bookkeeping mark, or one whose own effect is superseded by a later
|
|
11
|
+
# command rather than needing its own compensation). Never nested
|
|
12
|
+
# further — a compensation is not itself compensable; no known
|
|
13
|
+
# corpus need, and ADR 0025's own "a word earns its place by being
|
|
14
|
+
# used" bar would refuse a second level speculatively.
|
|
15
|
+
DispatchSpec = Struct.new(:command_name, :with_spec, :compensates, keyword_init: true) do
|
|
16
|
+
# A Struct already answers to_h; including the mixin puts the
|
|
17
|
+
# DECLARED emission ahead of Struct's own in the ancestry, which
|
|
18
|
+
# is what makes the shape data rather than a method body.
|
|
19
|
+
include Hecks::IR
|
|
20
|
+
|
|
21
|
+
emits_ir(
|
|
22
|
+
command_name: -> { command_name.to_s },
|
|
23
|
+
with_spec: -> { with_spec.map { |key, value| [key.to_s, Bluebook.render_value(value)] } },
|
|
24
|
+
compensates: one(:compensates)
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
ProcessManagerHandler = Struct.new(:event_type, :from_state, :to_state,
|
|
29
|
+
:dispatches, keyword_init: true) do
|
|
30
|
+
include Hecks::IR
|
|
31
|
+
|
|
32
|
+
emits_ir(
|
|
33
|
+
event_type: -> { event_type.to_s },
|
|
34
|
+
from_state: -> { from_state.to_s },
|
|
35
|
+
to_state: -> { to_state.to_s },
|
|
36
|
+
dispatches: many(:dispatches)
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The compensation half of a procedure, as its own thing.
|
|
41
|
+
#
|
|
42
|
+
# A PROCESS MANAGER coordinates: legs, states, an opinion about who goes
|
|
43
|
+
# next. A SAGA undoes: what makes the world good again when a leg it
|
|
44
|
+
# dispatched is refused. Two concepts, and the industry slurs them into one
|
|
45
|
+
# word — so here they are two objects, and a procedure either has a saga or
|
|
46
|
+
# does not.
|
|
47
|
+
#
|
|
48
|
+
# `undoes` is the ordered list of commands the compensation sends — a
|
|
49
|
+
# STATIC PREVIEW, declaration order (`Behaviour::ProcessManager#saga`),
|
|
50
|
+
# not one instance's own runtime history. Per-dispatch compensation
|
|
51
|
+
# (`compensates`, on the step it compensates for) moved most of what
|
|
52
|
+
# a saga undoes off this leg's own hand-written body and onto
|
|
53
|
+
# whichever forward dispatch each one undoes — this reads every
|
|
54
|
+
# declared `compensates` across the WHOLE saga first, then whatever
|
|
55
|
+
# this leg's own hand-written body still lists, for compensation
|
|
56
|
+
# that isn't expressible as "undo command X." WHICH of a declared
|
|
57
|
+
# `compensates` actually fires for one instance, and in what order
|
|
58
|
+
# (newest-first, completed-legs-only), is `SagaInterpreter`'s own
|
|
59
|
+
# dynamic `completed_compensations` — a per-instance runtime fact
|
|
60
|
+
# this declaration-only object could never hold.
|
|
61
|
+
#
|
|
62
|
+
# NAMING COLLISION, ONCE FLAGGED, NOW RESOLVED — `command`'s own
|
|
63
|
+
# `corrects event, reverses: true` (docs/implemented/decisions/0036-
|
|
64
|
+
# corrects-is-an-appended-fact-not-a-rewrite.md) already claimed
|
|
65
|
+
# `reverses` for a different meaning: auto-deriving a command's OWN
|
|
66
|
+
# corrective mutation from a past EVENT, not a saga's own
|
|
67
|
+
# compensating leg from a past DISPATCH. This feature keeps
|
|
68
|
+
# `reverses` reserved for `corrects` and uses `compensates` for
|
|
69
|
+
# per-dispatch saga compensation instead — a deliberate choice, not
|
|
70
|
+
# an accidental collision.
|
|
71
|
+
Saga = Struct.new(:trigger, :from_state, :to_state, :compensations, keyword_init: true) do
|
|
72
|
+
def undoes = compensations.map(&:command_name)
|
|
73
|
+
|
|
74
|
+
def to_s = "#{trigger} → #{to_state} (#{undoes.join(', ')})"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class ProcessManager
|
|
78
|
+
# The BLUEBOOK's name for this construct, asked the same way of a class
|
|
79
|
+
# that has crossed over and of an IR object that has not. Collapses into
|
|
80
|
+
# Construct when this one crosses.
|
|
81
|
+
# The trigger of a compensating leg. Not an event name — no aggregate
|
|
82
|
+
# announces that a leg the procedure dispatched was declined — so it lives
|
|
83
|
+
# here beside the thing it triggers rather than in the runtime that
|
|
84
|
+
# notices it. Declared in the language's Trigger vocabulary, which
|
|
85
|
+
# spec/vocabulary_conformance_spec holds to this constant.
|
|
86
|
+
REFUSED = Hecks::Vocabulary.fetch("Trigger").first
|
|
87
|
+
|
|
88
|
+
include Hecks::IR
|
|
89
|
+
include Behaviour::ProcessManager
|
|
90
|
+
|
|
91
|
+
emits_ir(
|
|
92
|
+
name: :name,
|
|
93
|
+
# M11 — `&.`, not `.`: a DSL-built process manager always carries
|
|
94
|
+
# a real `correlates_by` (`ProcessManagerBuilder#build` refuses to
|
|
95
|
+
# mint one without it), but the IR class itself defaults it to
|
|
96
|
+
# `nil` and is what `Assembly::Build`'s `:identity` reader
|
|
97
|
+
# (`value&.to_sym`) round-trips against. A bare `.to_s` mapped
|
|
98
|
+
# that absent case to `""`, indistinguishable on the wire from a
|
|
99
|
+
# real empty name and read back as the wrong, non-nil `:""`
|
|
100
|
+
# instead of `nil` — the same nil-erasure S1 fixed for
|
|
101
|
+
# `render_value`, one field over. `correlates_by` is always a
|
|
102
|
+
# bare Symbol (`SagaInterpreter` hash-looks-up a payload by it),
|
|
103
|
+
# never a `Literal`-encoded polymorphic value, so this stays a
|
|
104
|
+
# local `&.` rather than routing through `Literal.render` — that
|
|
105
|
+
# would wrap a real value in a leading `:` and break both the
|
|
106
|
+
# `:identity` reader's plain `to_sym` and the pinned golden IR
|
|
107
|
+
# fixtures' bare-string spelling (`"reference.value"`).
|
|
108
|
+
correlates_by: -> { correlates_by&.to_s },
|
|
109
|
+
starts_on: :starts_on,
|
|
110
|
+
ends_on: :ends_on,
|
|
111
|
+
states: :states,
|
|
112
|
+
handlers: many(:handlers)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
attr_reader :name, :correlates_by, :starts_on, :ends_on, :states, :handlers
|
|
116
|
+
|
|
117
|
+
def initialize(name:, correlates_by: nil, starts_on: nil, ends_on: nil,
|
|
118
|
+
states: [], handlers: [])
|
|
119
|
+
@name = name.to_s
|
|
120
|
+
@correlates_by = correlates_by
|
|
121
|
+
@starts_on = starts_on
|
|
122
|
+
@ends_on = ends_on
|
|
123
|
+
@states = states
|
|
124
|
+
@handlers = handlers
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "find"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Bluebook
|
|
5
|
+
# Finds domain declaration folders without loading or executing them.
|
|
6
|
+
class ProjectDiscovery
|
|
7
|
+
SKIPPED_DIRECTORIES = %w[.git .bundle data node_modules target tmp vendor generated coverage].freeze
|
|
8
|
+
|
|
9
|
+
attr_reader :root
|
|
10
|
+
|
|
11
|
+
def initialize(root)
|
|
12
|
+
@root = File.expand_path(root)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def bluebook_directories
|
|
16
|
+
found = []
|
|
17
|
+
Find.find(root) do |path|
|
|
18
|
+
if File.directory?(path)
|
|
19
|
+
if SKIPPED_DIRECTORIES.include?(File.basename(path))
|
|
20
|
+
Find.prune
|
|
21
|
+
elsif File.basename(path) == "bluebook" && Dir.glob(File.join(path, "*.bluebook")).any?
|
|
22
|
+
found << path
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
found.sort
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative "project_discovery"
|
|
2
|
+
require_relative "project_register"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Bluebook
|
|
6
|
+
# Boots each discovered directory and feeds its declarations into the
|
|
7
|
+
# deterministic project register. Discovery and FQN registration live in
|
|
8
|
+
# their own objects so either can be used without executing domains.
|
|
9
|
+
class ProjectLoader
|
|
10
|
+
Entry = ProjectRegister::Entry
|
|
11
|
+
DuplicateFqn = ProjectRegister::DuplicateFqn
|
|
12
|
+
MissingRealm = ProjectRegister::MissingRealm
|
|
13
|
+
LatestMismatch = ProjectRegister::LatestMismatch
|
|
14
|
+
|
|
15
|
+
attr_reader :root, :discovery, :register
|
|
16
|
+
|
|
17
|
+
def self.load(root) = new(root).load
|
|
18
|
+
|
|
19
|
+
def initialize(root, discovery: ProjectDiscovery.new(root), register: ProjectRegister.new)
|
|
20
|
+
@root = File.expand_path(root)
|
|
21
|
+
@discovery = discovery
|
|
22
|
+
@register = register
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def load
|
|
26
|
+
discovery.bluebook_directories.each do |directory|
|
|
27
|
+
runtime = Runtime.boot(directory)
|
|
28
|
+
register.register(runtime.registry.bluebooks.values, runtime.registry, runtime, directory)
|
|
29
|
+
end
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def entries = register.entries
|
|
34
|
+
def fetch(address) = register.fetch(address)
|
|
35
|
+
def include?(address) = register.include?(address)
|
|
36
|
+
def commands = register.commands
|
|
37
|
+
def queries = register.queries
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
# The callable catalogue for a discovered project. It knows how Bluebook
|
|
4
|
+
# declarations become public FQNs, but never searches or boots folders.
|
|
5
|
+
class ProjectRegister
|
|
6
|
+
Entry = Struct.new(:fqn, :source_directory, :dispatcher, :declared_verb, :domain_version, keyword_init: true) do
|
|
7
|
+
def command? = fqn.command?
|
|
8
|
+
def query? = fqn.query?
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class DuplicateFqn < StandardError; end
|
|
12
|
+
class MissingRealm < StandardError; end
|
|
13
|
+
class LatestMismatch < StandardError; end
|
|
14
|
+
|
|
15
|
+
attr_reader :entries
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@entries = {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def fetch(address) = entries.fetch(address.to_s)
|
|
22
|
+
def include?(address) = entries.key?(address.to_s)
|
|
23
|
+
def commands = entries.values.select(&:command?)
|
|
24
|
+
def queries = entries.values.select(&:query?)
|
|
25
|
+
|
|
26
|
+
def register(bluebooks, registry, dispatcher, directory)
|
|
27
|
+
bluebooks.each do |bluebook|
|
|
28
|
+
world = registry.world(bluebook.name)
|
|
29
|
+
realm = world&.realm
|
|
30
|
+
raise MissingRealm, "#{bluebook.name} in #{directory} has no world realm" if realm.to_s.empty?
|
|
31
|
+
if world.latest && bluebook.version && world.latest != bluebook.version
|
|
32
|
+
raise LatestMismatch,
|
|
33
|
+
"#{bluebook.name} in #{directory} declares latest #{world.latest.inspect}, not #{bluebook.version.inspect}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
bluebook.aggregates.each do |aggregate|
|
|
37
|
+
aggregate.commands.each do |command|
|
|
38
|
+
add(Fqn.command(realm: realm, domain: bluebook.name, version: bluebook.version,
|
|
39
|
+
aggregate: aggregate.hecks_name, command: command.hecks_name), directory, dispatcher, command.hecks_name, bluebook.version) if bluebook.version
|
|
40
|
+
add(Fqn.command(realm: realm, domain: bluebook.name, aggregate: aggregate.hecks_name,
|
|
41
|
+
command: command.hecks_name), directory, dispatcher, command.hecks_name, bluebook.version) if current?(
|
|
42
|
+
bluebook, world
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
aggregate.queries.each do |query|
|
|
46
|
+
name = Naming.snake(query.name)
|
|
47
|
+
add(Fqn.query(realm: realm, domain: bluebook.name, version: bluebook.version,
|
|
48
|
+
aggregate: aggregate.hecks_name, query: name), directory, dispatcher, query.name, bluebook.version) if bluebook.version
|
|
49
|
+
add(Fqn.query(realm: realm, domain: bluebook.name, aggregate: aggregate.hecks_name,
|
|
50
|
+
query: name), directory, dispatcher, query.name, bluebook.version) if current?(bluebook, world)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
bluebook.read_models.each do |model|
|
|
54
|
+
add(Fqn.query(realm: realm, domain: bluebook.name, version: bluebook.version,
|
|
55
|
+
query: model.query_name), directory, dispatcher, model.query_name, bluebook.version) if bluebook.version
|
|
56
|
+
add(Fqn.query(realm: realm, domain: bluebook.name, query: model.query_name),
|
|
57
|
+
directory, dispatcher, model.query_name, bluebook.version) if current?(bluebook, world)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
self
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def current?(bluebook, world)
|
|
66
|
+
bluebook.version.nil? || world.latest == bluebook.version
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def add(fqn, directory, dispatcher, declared_verb, domain_version)
|
|
70
|
+
existing = entries[fqn.to_s]
|
|
71
|
+
if existing
|
|
72
|
+
raise DuplicateFqn, "#{fqn} is declared in both #{existing.source_directory} and #{directory}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
entries[fqn.to_s] = Entry.new(
|
|
76
|
+
fqn: fqn, source_directory: directory, dispatcher: dispatcher,
|
|
77
|
+
declared_verb: declared_verb, domain_version: domain_version
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require_relative "behaviour/query"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Bluebook
|
|
5
|
+
def self.render_value(value) = Literal.render(value)
|
|
6
|
+
|
|
7
|
+
# A query — an ASK, declared on an aggregate or on one of its entities.
|
|
8
|
+
#
|
|
9
|
+
# It crosses over as an INSTANCE rather than a class, and the reason is worth
|
|
10
|
+
# stating because it is the boundary of the pattern. A query inherits its
|
|
11
|
+
# whole body from `QuerySpecification::Common::Options` — `wheres`,
|
|
12
|
+
# `order_by`, `limit`, `offset`, `cursor`,
|
|
13
|
+
# `authorization`, `null_semantics`, `inspection` — and those
|
|
14
|
+
# are INSTANCE methods that the runtime and the SQLite adapter both read.
|
|
15
|
+
# Hoisting the declaration onto a metaclass would put the identity and the
|
|
16
|
+
# specification on opposite sides of the object.
|
|
17
|
+
#
|
|
18
|
+
# And nothing is lost, because `Class#name` was the only reason a construct
|
|
19
|
+
# ever needed a second word for its own name. An instance answers `name`
|
|
20
|
+
# truthfully. So a query gains an owner and an identity — which is what the
|
|
21
|
+
# graph is for — and keeps the name it always had.
|
|
22
|
+
class Query < QuerySpecification::Common::Options
|
|
23
|
+
include Construct
|
|
24
|
+
|
|
25
|
+
include Hecks::IR
|
|
26
|
+
include Behaviour::Query
|
|
27
|
+
|
|
28
|
+
emits_ir(
|
|
29
|
+
name: :name,
|
|
30
|
+
description: :description,
|
|
31
|
+
attributes: many(:attributes),
|
|
32
|
+
wheres: many(:wheres),
|
|
33
|
+
order_by: one(:order_by),
|
|
34
|
+
limit: one(:limit)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
attr_reader :name, :description, :attributes
|
|
38
|
+
|
|
39
|
+
def initialize(name:, description: nil, attributes: [], wheres: [],
|
|
40
|
+
order_by: nil, limit: nil, offset: nil, cursor: nil,
|
|
41
|
+
authorization: nil, null_semantics: nil,
|
|
42
|
+
inspection: nil)
|
|
43
|
+
null_semantics ||= QuerySpecification::Common::NullSemantics.default
|
|
44
|
+
super(wheres: wheres, order_by: order_by, limit: limit, offset: offset, cursor: cursor,
|
|
45
|
+
authorization: authorization,
|
|
46
|
+
null_semantics: null_semantics, inspection: inspection)
|
|
47
|
+
@name = name.to_s
|
|
48
|
+
@hecks_name = @name
|
|
49
|
+
@description = description
|
|
50
|
+
@attributes = attributes
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# A query's shape is NOT fixed — `extra_options_to_h` carries
|
|
54
|
+
# whatever options the specification layer grew (count, median,
|
|
55
|
+
# group_by, scope_to). Declared emission covers the settled part
|
|
56
|
+
# and `super` hands it over; the tail stays dynamic, which is the
|
|
57
|
+
# honest description of it.
|
|
58
|
+
def to_h = super.merge(extra_options_to_h)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require_relative "behaviour/read_model"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Bluebook
|
|
5
|
+
# A read model — an ask that gathers heads from more than one aggregate.
|
|
6
|
+
#
|
|
7
|
+
# Like a query it crosses over as an INSTANCE, and for the same reason: its
|
|
8
|
+
# body is inherited from `QuerySpecification::ReadModel::Specification`, whose
|
|
9
|
+
# readers the runtime and the SQLite adapter both call on the object. It gains
|
|
10
|
+
# an identity and an owner ; it keeps the name it always had, because only a
|
|
11
|
+
# CLASS ever had a competing answer for `name`.
|
|
12
|
+
class ReadModel < QuerySpecification::ReadModel::Specification
|
|
13
|
+
include Construct
|
|
14
|
+
|
|
15
|
+
include Hecks::IR
|
|
16
|
+
include Behaviour::ReadModel
|
|
17
|
+
|
|
18
|
+
emits_ir(
|
|
19
|
+
name: :name,
|
|
20
|
+
description: :description,
|
|
21
|
+
reference_name: :reference_name,
|
|
22
|
+
reference_target: :reference_target,
|
|
23
|
+
query_name: :query_name,
|
|
24
|
+
wheres: many(:wheres),
|
|
25
|
+
order_by: one(:order_by),
|
|
26
|
+
limit: one(:limit)
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
attr_reader :name, :description, :reference_name, :reference_target, :aggregate_heads, :group_by,
|
|
30
|
+
:count, :median_field
|
|
31
|
+
|
|
32
|
+
# `reference_name:`/`reference_target:` are nil for a ROOTLESS read
|
|
33
|
+
# model (no `reference_to` declared) — `&.` throughout, rather than
|
|
34
|
+
# the `.to_s`/`.to_sym` this used to require unconditionally, so
|
|
35
|
+
# `reference_target.nil?` stays a real, checkable fact for the
|
|
36
|
+
# interpreter instead of silently becoming `""`.
|
|
37
|
+
def initialize(name:, description: nil, reference_name: nil, reference_target: nil, aggregate_heads: [],
|
|
38
|
+
group_by: [], count: nil, median_field: nil, **options)
|
|
39
|
+
super(joins: aggregate_heads, **options)
|
|
40
|
+
@name = name.to_s
|
|
41
|
+
@hecks_name = @name
|
|
42
|
+
@description = description
|
|
43
|
+
@reference_name = reference_name&.to_sym
|
|
44
|
+
@reference_target = reference_target&.to_s
|
|
45
|
+
@aggregate_heads = aggregate_heads
|
|
46
|
+
# Hash rows (`{field: :agg}`), same shape as `aggregate_heads` —
|
|
47
|
+
# `group_by_fields` is the convenience reader everything but
|
|
48
|
+
# `to_h`/the Judge's own generic walk actually wants.
|
|
49
|
+
@group_by = group_by
|
|
50
|
+
# ABSENT IS NOT FALSE. `@count` stays nil rather than becoming
|
|
51
|
+
# `false` when undeclared — the same "if you declare it, declare
|
|
52
|
+
# something" reading `Lifecycle`'s own optional fields rely on
|
|
53
|
+
# (MetaValidator::Judge#setters skips a setter whose every
|
|
54
|
+
# source is `nil`, so an ALWAYS-false `@count` would dispatch
|
|
55
|
+
# `ReadModel.Count` on every read model that never wrote the
|
|
56
|
+
# word at all). `!!count` collapses a truthy DSL `true` (or a
|
|
57
|
+
# reconstructed `true`) to the same `true`/`nil` pair either path
|
|
58
|
+
# produces — never `false`.
|
|
59
|
+
@count = count ? true : nil
|
|
60
|
+
@median_field = median_field&.to_sym
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# `.to_sym` regardless of source — the DIRECT builder path stores
|
|
64
|
+
# symbols, but `Reconstruction::Shapes#group_by_field` (the
|
|
65
|
+
# replayed-from-the-meta-domain path every REAL boot actually
|
|
66
|
+
# goes through) reads the field back as a String, the same way
|
|
67
|
+
# `aggregate_heads`' own `:as` does. Row hash KEYS built by
|
|
68
|
+
# `Value.materialize_unwrapped` are symbols (`attr.name`), so
|
|
69
|
+
# this has to be too, or `row[field]` in `nest` silently misses.
|
|
70
|
+
|
|
71
|
+
# `wheres`/`order_by`/`limit` are spelled explicitly here, the SAME
|
|
72
|
+
# mechanism `Query#to_h` already uses (query.rb, read directly
|
|
73
|
+
# before this was written) — always present, `wheres` a (possibly
|
|
74
|
+
# empty) array and `order_by`/`limit` nil when undeclared, exactly
|
|
75
|
+
# like a Query's. `extra_options_to_h` still excludes these three by
|
|
76
|
+
# name (options.rb), so nothing here double-spells them; the two
|
|
77
|
+
# constructs now share one encoding for the same three words rather
|
|
78
|
+
# than a reader needing to learn a second one. See this file's own
|
|
79
|
+
# `filtered_head_name` and language/bluebook/syntax.bluebook's
|
|
80
|
+
# `ReadModel` `where`/`order_by`/`limit` member rows for the history
|
|
81
|
+
# of why this WAS narrower, and 2026-08-11's read-model where/
|
|
82
|
+
# order_by/limit task for why it stopped being.
|
|
83
|
+
# Same dynamic tail as a Query's, plus two collections whose
|
|
84
|
+
# ROWS are plain hashes rather than constructs — they are
|
|
85
|
+
# normalised here rather than by `many`, which recurses through
|
|
86
|
+
# `to_h` and would have nothing to call.
|
|
87
|
+
# `count`/`median_field` are ABSENT (no key at all), not `nil`,
|
|
88
|
+
# when undeclared — unlike `group_by` (always a `[]`, never
|
|
89
|
+
# missing) they are the FIRST fields this construct has ever
|
|
90
|
+
# added that no OLDER real corpus member could possibly carry, so
|
|
91
|
+
# merging them unconditionally would have put a `null` onto every
|
|
92
|
+
# existing read model's own wire shape (banking's
|
|
93
|
+
# `ComplianceDashboard`/`CustomerPortfolio`, the language's own
|
|
94
|
+
# `WholeBluebook`, ...) for a fact nothing about them changed —
|
|
95
|
+
# the exact `extra_options_to_h` reads for `cursor`/`offset`/etc,
|
|
96
|
+
# applied here for the same reason.
|
|
97
|
+
def to_h
|
|
98
|
+
reductions = {}
|
|
99
|
+
reductions[:count] = true if @count
|
|
100
|
+
reductions[:median_field] = @median_field.to_s if @median_field
|
|
101
|
+
super
|
|
102
|
+
.merge(aggregate_heads: @aggregate_heads.map { |head| head.merge(as: head[:as].to_s) })
|
|
103
|
+
.merge(group_by: @group_by.map { |row| row.merge(field: row[:field].to_s) })
|
|
104
|
+
.merge(reductions)
|
|
105
|
+
.merge(extra_options_to_h)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
# An attribute that points at another aggregate's head.
|
|
4
|
+
#
|
|
5
|
+
# This used to be the STRING `"Reference<Customer>"`, minted by
|
|
6
|
+
# `AggregateBuilder#reference_to` and `CommandBuilder#cross_reference` from
|
|
7
|
+
# a real constant that had just been handed in, and then parsed back apart
|
|
8
|
+
# by a regex in the command interpreter, by string equality in the read-model
|
|
9
|
+
# interpreter and the SQLite adapter, and by `delete_prefix` in the bluebook
|
|
10
|
+
# builder. Five readers of a spelling one writer invented.
|
|
11
|
+
#
|
|
12
|
+
# It holds the TARGET instead, and answers `resolve` with the target's
|
|
13
|
+
# Aggregate. Resolution is LAZY and deliberately so: `reference_to
|
|
14
|
+
# Customer` may name an aggregate declared lower in the file — banking's
|
|
15
|
+
# Account points at Customer and survives only because Customer happens to
|
|
16
|
+
# be written above — so the edge cannot be resolved at declaration time.
|
|
17
|
+
# It resolves through the chapter's own IR (`Bluebook#aggregate`),
|
|
18
|
+
# which is scoped by construction : the lookup cannot walk anywhere but
|
|
19
|
+
# this chapter's declared heads, so a same-named aggregate in another
|
|
20
|
+
# loaded domain is unreachable rather than defended against.
|
|
21
|
+
#
|
|
22
|
+
# `to_s` still spells `"Reference<Customer>"`, because `Attribute#to_h` is
|
|
23
|
+
# part of the pinned byte-for-byte export contract. IR objects in
|
|
24
|
+
# the graph, strings in the export.
|
|
25
|
+
class Reference
|
|
26
|
+
attr_reader :target_name
|
|
27
|
+
|
|
28
|
+
# The Aggregate whose declaration carries this reference — the way
|
|
29
|
+
# up to the chapter, stamped once every sibling has been read.
|
|
30
|
+
attr_accessor :declared_in
|
|
31
|
+
|
|
32
|
+
def initialize(target_name)
|
|
33
|
+
@target_name = Naming.demodulise(target_name).to_s
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# The Aggregate this points at, or nil when the target belongs to
|
|
37
|
+
# ANOTHER domain — a cross-domain target may legitimately not be loaded,
|
|
38
|
+
# the same reading `across` policies get.
|
|
39
|
+
def resolve
|
|
40
|
+
unless declared_in
|
|
41
|
+
raise DSL::Malformed,
|
|
42
|
+
"#{self} cannot say which aggregate declares it, so it cannot " \
|
|
43
|
+
"resolve — a reference that resolves to nothing is checked " \
|
|
44
|
+
"against nothing"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
declared_in.hecks_owner&.aggregate(@target_name)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The IR spelling, the one the export carries.
|
|
51
|
+
def to_s = "Reference<#{@target_name}>"
|
|
52
|
+
def inspect = "#<Reference #{@target_name}>"
|
|
53
|
+
|
|
54
|
+
# VALUE EQUALITY, not identity — without this, two references to
|
|
55
|
+
# the SAME target, parsed from two SEPARATE bluebook reads (era
|
|
56
|
+
# N's own boot and a held era's own shadow reconstruction,
|
|
57
|
+
# coverage_check.rb's own comparison), are different objects and
|
|
58
|
+
# compare unequal by Ruby's default `==`. `EraGuard::ShapeDiff
|
|
59
|
+
# #diff_type`'s `held_type != current_type` check then reads as
|
|
60
|
+
# true for EVERY reference_to attribute on EVERY mint, regardless
|
|
61
|
+
# of whether the reference actually changed — a real refusal for
|
|
62
|
+
# attributes nothing about. `declared_in` (which aggregate carries
|
|
63
|
+
# this reference) is deliberately excluded: the same reference
|
|
64
|
+
# attribute exists once, but its `held_type`/`current_type` come
|
|
65
|
+
# from separately-parsed bluebooks with structurally different
|
|
66
|
+
# (if same-shaped) owning aggregates, and `declared_in` is a
|
|
67
|
+
# cross-reference for `resolve`, not part of what this attribute
|
|
68
|
+
# itself IS.
|
|
69
|
+
def ==(other) = other.is_a?(Reference) && target_name == other.target_name
|
|
70
|
+
alias eql? ==
|
|
71
|
+
def hash = [self.class, target_name].hash
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|