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,98 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Projections
|
|
3
|
+
module Model
|
|
4
|
+
# HOW THE MODEL'S SHAPE DIFFERS FROM THE LANGUAGE'S, and why.
|
|
5
|
+
#
|
|
6
|
+
# Every construct's `emits_ir` restates what the grammar declares —
|
|
7
|
+
# and the two legitimately differ, in seven ways. These lived only
|
|
8
|
+
# as prose in Ruby comments until spec/model_shape_conformance_spec
|
|
9
|
+
# made them checkable; they live HERE so the generator and the gate
|
|
10
|
+
# read one source rather than two that must agree.
|
|
11
|
+
#
|
|
12
|
+
# A reason is carried beside each entry because the generated file
|
|
13
|
+
# will CARRY it: a deviation is exactly the kind of thing whose
|
|
14
|
+
# explanation must survive regeneration, and the only way it can is
|
|
15
|
+
# to be emitted rather than typed into the output.
|
|
16
|
+
module Deviations
|
|
17
|
+
# The grammar is relational — a Command points UP at its
|
|
18
|
+
# Aggregate — where the model composes. An explicit `as:` still
|
|
19
|
+
# keeps its `_id` (Command's own `entity_id`, kept as data); the
|
|
20
|
+
# parent link itself mints bare now (ADR 0025) — `aggregate` or
|
|
21
|
+
# `bluebook`, whichever this category's creating command declares
|
|
22
|
+
# first — so both spellings are checked. Entity spells its own
|
|
23
|
+
# (separate, non-colliding) text twin of the parent link `owner`.
|
|
24
|
+
PARENT_REF = ->(field) { field.to_s.match?(/_id\z/) || %i[owner aggregate bluebook].include?(field) }
|
|
25
|
+
|
|
26
|
+
# The judge's own field, never the model's — contracts.rb already
|
|
27
|
+
# says so with `derived: { position: :walk }`.
|
|
28
|
+
JUDGE_ONLY = %i[position].freeze
|
|
29
|
+
|
|
30
|
+
# What the model holds that the grammar declares elsewhere: the
|
|
31
|
+
# containment edges, stated in syntax.bluebook's Keyword rows as
|
|
32
|
+
# `context` -> `opens`.
|
|
33
|
+
CONTAINED = {
|
|
34
|
+
"Bluebook" => %i[aggregates read_models policies process_managers],
|
|
35
|
+
"Aggregate" => %i[commands entities queries value_objects],
|
|
36
|
+
# S17, ADR 0026 — an entity may nest further entities now
|
|
37
|
+
# (`Dispatch`, inside `Handler`) — same containment edge as
|
|
38
|
+
# Aggregate's own `entities`, one level down.
|
|
39
|
+
"Entity" => %i[commands entities queries],
|
|
40
|
+
"ValueObject" => %i[members],
|
|
41
|
+
"ProcessManager" => %i[handlers]
|
|
42
|
+
}.freeze
|
|
43
|
+
|
|
44
|
+
# One model field gathered from several declared ones —
|
|
45
|
+
# contracts.rb's own `[:folded, ...]` shape, as the pair it is.
|
|
46
|
+
FOLDED = {
|
|
47
|
+
"Aggregate" => { lifecycle: %i[state_field state_start transitions] },
|
|
48
|
+
"Entity" => { lifecycle: %i[state_field state_start transitions] },
|
|
49
|
+
"Query" => { order_by: %i[order_field order_way] }
|
|
50
|
+
}.freeze
|
|
51
|
+
|
|
52
|
+
# The inverse of a fold, and it had no name at all: one declared
|
|
53
|
+
# field opening into several the model holds apart.
|
|
54
|
+
UNPACKED = {
|
|
55
|
+
"ReadModel" => { options: %i[wheres order_by limit] }
|
|
56
|
+
}.freeze
|
|
57
|
+
|
|
58
|
+
# Model-only, each for its own reason rather than by oversight.
|
|
59
|
+
COMPUTED = {
|
|
60
|
+
"Bluebook" => { ir_version: "the EMISSION's own version, not the domain's",
|
|
61
|
+
canonical_form: "the normalisation table every reader needs beside the IR" },
|
|
62
|
+
"ValueObject" => { closed_set: "an empty one_of and no one_of are otherwise indistinguishable" },
|
|
63
|
+
"Aggregate" => { ports: "declared in the hecksagon, attached after the aggregate exists" }
|
|
64
|
+
}.freeze
|
|
65
|
+
|
|
66
|
+
# DECLARED, AND DELIBERATELY NOT EMITTED. The category that had no
|
|
67
|
+
# home anywhere before — each of these was a comment and nothing
|
|
68
|
+
# more.
|
|
69
|
+
OFF_THE_WIRE = {
|
|
70
|
+
"Policy" => { aggregate: "the wire format is a pinned contract, and it does not carry " \
|
|
71
|
+
"where a policy was written before the builder hoisted it" },
|
|
72
|
+
"Bluebook" => { formerly_known_as: "a rename's old name is a fact about the source",
|
|
73
|
+
normalisations: "the normalisation table rides on canonical_form instead" },
|
|
74
|
+
"ValueObject" => { rows: "the language's name for a closed set's members; emitted as `members`" }
|
|
75
|
+
}.freeze
|
|
76
|
+
|
|
77
|
+
# Emitted by `to_h`'s own merge rather than by `emits_ir` — the
|
|
78
|
+
# two constructs whose shape is genuinely not fixed, because the
|
|
79
|
+
# query specification layer grew options after them.
|
|
80
|
+
DYNAMIC_TAIL = {
|
|
81
|
+
"Query" => %i[options],
|
|
82
|
+
"ReadModel" => %i[options group_by aggregate_heads count median_field]
|
|
83
|
+
}.freeze
|
|
84
|
+
|
|
85
|
+
module_function
|
|
86
|
+
|
|
87
|
+
# The tables that carry a reason answer with names only when the
|
|
88
|
+
# caller wants the set rather than the explanations.
|
|
89
|
+
def off_the_wire(name) = OFF_THE_WIRE.fetch(name, {}).keys
|
|
90
|
+
def computed(name) = COMPUTED.fetch(name, {}).keys
|
|
91
|
+
def contained(name) = CONTAINED.fetch(name, [])
|
|
92
|
+
def folded(name) = FOLDED.fetch(name, {})
|
|
93
|
+
def unpacked(name) = UNPACKED.fetch(name, {})
|
|
94
|
+
def dynamic_tail(name) = DYNAMIC_TAIL.fetch(name, [])
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
require_relative "model/deviations"
|
|
2
|
+
require_relative "../projector"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Projections
|
|
6
|
+
# THE MODEL CLASSES, PROJECTED FROM THE LANGUAGE THAT DECLARES THEM.
|
|
7
|
+
#
|
|
8
|
+
# A construct's HOLDING half — its readers, its emission, a
|
|
9
|
+
# constructor that assigns declared fields and hands off — restates
|
|
10
|
+
# what `bluebook.bluebook` already says, three times over in Ruby.
|
|
11
|
+
# This renders it instead.
|
|
12
|
+
#
|
|
13
|
+
# ONLY THE HOLDING HALF. `Behaviour::X` is hand-written and permanent,
|
|
14
|
+
# and `settle` is the seam: everything a declaration cannot state
|
|
15
|
+
# lives behind it, so regenerating can never be lossy. That property
|
|
16
|
+
# was established construct by construct before any of this was
|
|
17
|
+
# written — the chapter looked generatable and was not, until its
|
|
18
|
+
# `@hecks_root`, ports table and child stamping moved behind `settle`.
|
|
19
|
+
#
|
|
20
|
+
# THE HOST MANIFEST IS THE HONEST PART. The grammar states the fields;
|
|
21
|
+
# it does not state which constructs are Ruby CLASSES rather than
|
|
22
|
+
# instances, what a constructor's defaults are, or how a value is
|
|
23
|
+
# coerced on the way in. Those are facts about Ruby, not about
|
|
24
|
+
# bluebooks, so they are declared here rather than pretended into the
|
|
25
|
+
# language — and keeping them in one table is what would let a second
|
|
26
|
+
# host swap this file rather than edit thirteen.
|
|
27
|
+
module Model
|
|
28
|
+
extend Projector::Target
|
|
29
|
+
|
|
30
|
+
projects_as :model, declares: "Bluebook", emits: :files
|
|
31
|
+
|
|
32
|
+
# PER-CONSTRUCT RUBY FACTS. `coerce` is the only fiddly column: a
|
|
33
|
+
# declared field arrives as whatever the builder handed over, and
|
|
34
|
+
# each construct has always normalised its own on the way in.
|
|
35
|
+
HOST = {
|
|
36
|
+
"Policy" => {
|
|
37
|
+
construct: "Policy",
|
|
38
|
+
file: "policy.rb",
|
|
39
|
+
behaviour: "Behaviour::Policy",
|
|
40
|
+
readers: %i[name on_event trigger_command target_domain where for_each with_spec],
|
|
41
|
+
accessors: %i[aggregate],
|
|
42
|
+
defaults: { name: nil, on_event: "nil", trigger_command: "nil",
|
|
43
|
+
target_domain: "nil", where: "nil", for_each: "nil",
|
|
44
|
+
with_spec: "[]", aggregate: "nil" },
|
|
45
|
+
coerce: { name: ".to_s", aggregate: "&.to_s" },
|
|
46
|
+
# A LIST OF BINDINGS IS NOT A SCALAR ON THE WIRE. Every other
|
|
47
|
+
# field emits as itself; this one has to render the way
|
|
48
|
+
# `DispatchSpec`'s own `with_spec` does — keys to strings, and
|
|
49
|
+
# `render_value` KEEPING the leading colon on a Symbol, because
|
|
50
|
+
# a binding that reads an event field and one that supplies a
|
|
51
|
+
# literal string are otherwise indistinguishable once written
|
|
52
|
+
# down (see `MetaValidator::Readings`' own note on exactly that).
|
|
53
|
+
renders: { with_spec: "-> { with_spec.map { |key, value| [key.to_s, Bluebook.render_value(value)] } }" },
|
|
54
|
+
settles: false
|
|
55
|
+
}
|
|
56
|
+
}.freeze
|
|
57
|
+
|
|
58
|
+
module_function
|
|
59
|
+
|
|
60
|
+
def call(bluebook:, options: {})
|
|
61
|
+
HOST.to_h { |name, host| [host.fetch(:file), render(bluebook, name, host)] }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def render(bluebook, name, host)
|
|
65
|
+
<<~RUBY
|
|
66
|
+
# GENERATED — projected from the language's own #{name} aggregate.
|
|
67
|
+
# DO NOT EDIT: the holding half is rendered, and #{host.fetch(:behaviour)}
|
|
68
|
+
# is where anything hand-written belongs.
|
|
69
|
+
require_relative "behaviour/#{File.basename(host.fetch(:file), ".rb")}"
|
|
70
|
+
|
|
71
|
+
module Hecks
|
|
72
|
+
module Bluebook
|
|
73
|
+
class #{name}
|
|
74
|
+
include Hecks::IR
|
|
75
|
+
include #{host.fetch(:behaviour)}
|
|
76
|
+
|
|
77
|
+
#{indent(emits(bluebook, name), 6)}
|
|
78
|
+
|
|
79
|
+
#{indent(readers(host), 6)}
|
|
80
|
+
|
|
81
|
+
#{indent(constructor(host), 6)}
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
RUBY
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# The emission, keyed as the model spells it and sourced as the
|
|
89
|
+
# language declares it.
|
|
90
|
+
def emits(bluebook, name)
|
|
91
|
+
fields = emitted_fields(bluebook, name)
|
|
92
|
+
renders = HOST.fetch(name).fetch(:renders, {})
|
|
93
|
+
width = fields.map { |f| f.to_s.length }.max.to_i
|
|
94
|
+
|
|
95
|
+
"emits_ir(\n" +
|
|
96
|
+
fields.map { |f| " #{"#{f}:".ljust(width + 1)} #{renders.fetch(f, ":#{f}")}" }.join(",\n") +
|
|
97
|
+
"\n)"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# WHAT THE CONSTRUCT EMITS: what the language declares, less every
|
|
101
|
+
# deviation the tables account for. The generator and
|
|
102
|
+
# spec/model_shape_conformance_spec compute this the same way, from
|
|
103
|
+
# the same tables, which is the point of the tables being in lib.
|
|
104
|
+
def emitted_fields(bluebook, name)
|
|
105
|
+
bluebook.aggregate(name).attributes.map(&:name)
|
|
106
|
+
.reject { |f| Deviations::PARENT_REF.call(f) } -
|
|
107
|
+
Deviations::JUDGE_ONLY -
|
|
108
|
+
Deviations.off_the_wire(name) -
|
|
109
|
+
Deviations.dynamic_tail(name) -
|
|
110
|
+
Deviations.folded(name).values.flatten -
|
|
111
|
+
Deviations.unpacked(name).keys
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def readers(host)
|
|
115
|
+
lines = ["attr_reader #{host.fetch(:readers).map { |r| ":#{r}" }.join(', ')}"]
|
|
116
|
+
accessors = host.fetch(:accessors, [])
|
|
117
|
+
return lines.join("\n") if accessors.empty?
|
|
118
|
+
|
|
119
|
+
# A declared field the model deliberately does not emit still
|
|
120
|
+
# needs a reader, and the REASON it is off the wire is carried
|
|
121
|
+
# here rather than typed in — a comment that survives
|
|
122
|
+
# regeneration is one the generator writes.
|
|
123
|
+
reasons = Deviations::OFF_THE_WIRE.fetch(host.fetch(:construct, ""), {})
|
|
124
|
+
(lines + accessors.map { |a|
|
|
125
|
+
why = reasons[a]
|
|
126
|
+
(why ? "\n# #{a.upcase}, DECLARED AND DELIBERATELY OFF THE WIRE\n# #{wrap(why)}\n" : "") +
|
|
127
|
+
"attr_accessor :#{a}"
|
|
128
|
+
}).join("\n")
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def constructor(host)
|
|
132
|
+
args = host.fetch(:defaults).map { |f, d| d ? "#{f}: #{d}" : "#{f}:" }.join(", ")
|
|
133
|
+
body = host.fetch(:defaults).keys.map { |f|
|
|
134
|
+
" @#{f} = #{f}#{host.fetch(:coerce, {})[f]}"
|
|
135
|
+
}
|
|
136
|
+
body << "\n settle" if host.fetch(:settles, true)
|
|
137
|
+
|
|
138
|
+
"def initialize(#{args})\n#{body.join("\n")}\nend"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def indent(text, by) = text.lines.map { |l| l.strip.empty? ? l : (" " * by) + l }.join
|
|
142
|
+
def wrap(text) = text.scan(/.{1,62}(?:\s|$)/).map(&:strip).join("\n# ")
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require_relative "../naming"
|
|
2
|
+
require_relative "../projector"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Projections
|
|
6
|
+
# AN OIDC CLIENT/SCOPE MANIFEST derived from a domain's own IR: what
|
|
7
|
+
# an identity provider has to know about this domain before it can
|
|
8
|
+
# issue a token that means anything here.
|
|
9
|
+
#
|
|
10
|
+
# THE ARTIFACT HALF OF SOMETHING ALREADY HALF-BUILT.
|
|
11
|
+
# `spec/oidc_projection_spec.rb` covers the INTEGRATION half — verified
|
|
12
|
+
# claims in, `IdentityResolution.resolve` → `Authorization.holds_role?`
|
|
13
|
+
# → a dispatch scoped by `Hecks.as_caller(role:)`. That half enforces
|
|
14
|
+
# a role per command. It just had no way to say, up front and as data,
|
|
15
|
+
# WHICH role each command wants — every answer came from asking the
|
|
16
|
+
# live runtime one dispatch at a time.
|
|
17
|
+
#
|
|
18
|
+
# This is that catalogue, and the two are checked against each other
|
|
19
|
+
# rather than merely coexisting: `verb` here is spelled exactly as
|
|
20
|
+
# `Runtime::Dispatcher#dispatch` takes it and exactly as
|
|
21
|
+
# `Bluebook#verbs` lists it, so a scope can never name a command
|
|
22
|
+
# the domain does not have (spec/projections/oidc_projection_spec.rb
|
|
23
|
+
# holds the two lists equal), and `role` is the same string
|
|
24
|
+
# `Ports::Authorization.holds_role?` compares against a real
|
|
25
|
+
# `Governance::RoleAssignment`.
|
|
26
|
+
#
|
|
27
|
+
# ROLES COME FROM THE COMMANDS, NOT FROM GOVERNANCE. A command's own
|
|
28
|
+
# `role "Compliance officer"` is in the bluebook IR
|
|
29
|
+
# (`Command#role`), whereas `uses_framework "Governance"` is
|
|
30
|
+
# declared in the `.hecksagon` — which `call(bluebook:, options:)`
|
|
31
|
+
# cannot see at all. Reading the commands is both the only thing
|
|
32
|
+
# available here AND the more accurate source: it says what each
|
|
33
|
+
# command actually demands, not merely which role vocabulary the
|
|
34
|
+
# application happened to mount.
|
|
35
|
+
#
|
|
36
|
+
# A command with no declared role projects `"role" => nil` rather
|
|
37
|
+
# than being dropped — an unguarded command is a real fact about the
|
|
38
|
+
# domain, and silently omitting it would make the manifest read as
|
|
39
|
+
# though the command did not exist.
|
|
40
|
+
module OIDC
|
|
41
|
+
extend Projector::Target
|
|
42
|
+
|
|
43
|
+
projects_as :oidc
|
|
44
|
+
|
|
45
|
+
module_function
|
|
46
|
+
|
|
47
|
+
# `audience:` overrides the domain name, for the ordinary case
|
|
48
|
+
# where the IdP's registered audience is a URL rather than a bare
|
|
49
|
+
# chapter name.
|
|
50
|
+
def call(bluebook:, options: {})
|
|
51
|
+
scopes = scopes_for(bluebook)
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
"audience" => (options[:audience] || bluebook.name).to_s,
|
|
55
|
+
"scopes" => scopes,
|
|
56
|
+
"roles" => scopes.map { |scope| scope["role"] }.compact.uniq.sort
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Sorted by scope, not left in declaration order — a manifest is
|
|
61
|
+
# something two versions of get diffed, and the same precedent
|
|
62
|
+
# `StorageShape.project` sets for its own aggregate list.
|
|
63
|
+
#
|
|
64
|
+
# Recurses into entities, not just an aggregate's own direct
|
|
65
|
+
# commands — the exact same gap `Bluebook#verbs` closed for the
|
|
66
|
+
# same reason (`chapter.rb`'s own comment): a command reached
|
|
67
|
+
# through `Dispatcher#dispatch`'s dotted `Entity.Command` routing
|
|
68
|
+
# is real and callable, so a manifest that never names it can
|
|
69
|
+
# never grant a client a scope for it either.
|
|
70
|
+
def scopes_for(bluebook)
|
|
71
|
+
bluebook.aggregates.flat_map { |aggregate| aggregate_scopes(bluebook, aggregate) }
|
|
72
|
+
.sort_by { |scope| scope["scope"] }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def aggregate_scopes(bluebook, aggregate)
|
|
76
|
+
verb_prefix = "#{bluebook.name}::#{aggregate.hecks_name}"
|
|
77
|
+
scope_prefix = "#{Naming.snake(bluebook.name)}:#{Naming.snake(aggregate.hecks_name)}"
|
|
78
|
+
|
|
79
|
+
command_scopes(aggregate.commands, verb_prefix, scope_prefix) +
|
|
80
|
+
aggregate.entities.flat_map { |entity| entity_scopes(entity, verb_prefix, scope_prefix) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# S17, ADR 0026 — an entity can nest further entities (`Dispatch`,
|
|
84
|
+
# inside `Handler`), so this recurses the same way `Chapter#verbs`
|
|
85
|
+
# now does. The verb and scope prefixes grow in lockstep, each
|
|
86
|
+
# `.`-joined the same way its own kind already was.
|
|
87
|
+
def entity_scopes(entity, verb_prefix, scope_prefix)
|
|
88
|
+
verb_prefix = "#{verb_prefix}.#{entity.hecks_name}"
|
|
89
|
+
scope_prefix = "#{scope_prefix}.#{Naming.snake(entity.hecks_name)}"
|
|
90
|
+
|
|
91
|
+
command_scopes(entity.commands, verb_prefix, scope_prefix) +
|
|
92
|
+
entity.entities.flat_map { |piece| entity_scopes(piece, verb_prefix, scope_prefix) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# `banking:account.open` — the shape an OIDC scope is conventionally
|
|
96
|
+
# spelled in, and snake_cased through the SAME `Naming.snake` the
|
|
97
|
+
# facade uses to name a command's own door method, so a scope and
|
|
98
|
+
# the Ruby call that satisfies it cannot drift apart.
|
|
99
|
+
def command_scopes(commands, verb_prefix, scope_prefix)
|
|
100
|
+
commands.map do |command|
|
|
101
|
+
{
|
|
102
|
+
"scope" => "#{scope_prefix}.#{Naming.snake(command.hecks_name)}",
|
|
103
|
+
"verb" => "#{verb_prefix}.#{command.hecks_name}",
|
|
104
|
+
"role" => command.role
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
require_relative "../projector"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
# THE RUST PARSER'S KEYWORD TABLE, projected from the chapter's own
|
|
6
|
+
# Syntax aggregate — "the parser's grammar knowledge is DERIVED from
|
|
7
|
+
# hecks's self-description, not hand-typed a second time", which
|
|
8
|
+
# is the anti-drift idea the whole Rust-parser plan rests on.
|
|
9
|
+
#
|
|
10
|
+
# A REGISTERED TARGET NOW, rather than a module living inside its own
|
|
11
|
+
# bin/ script. It was already a projection in everything but call
|
|
12
|
+
# shape; this only stops it being a fifth way of spelling one.
|
|
13
|
+
module ParserTable
|
|
14
|
+
extend Projector::Target
|
|
15
|
+
projects_as :parser_table, declares: "Syntax"
|
|
16
|
+
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
def call(bluebook:, options: {}) = render(bluebook)
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
# THE CHAPTER IS HANDED OVER, not reached for. This used to open the
|
|
24
|
+
# grammar registry itself, which meant the projection could only ever
|
|
25
|
+
# project one chapter — the language's own. Taking it as an argument is
|
|
26
|
+
# what the projector protocol asks for, and it costs nothing.
|
|
27
|
+
def syntax(bluebook) = bluebook.aggregate("Syntax")
|
|
28
|
+
|
|
29
|
+
# Every cell as text — exactly spec/syntax_conformance_spec.rb's own
|
|
30
|
+
# `rows` helper, reused rather than re-derived: a member's fields decode
|
|
31
|
+
# back through typed literal decoding on the way out of reconstruction,
|
|
32
|
+
# and this reads it back as what was written.
|
|
33
|
+
def rows(bluebook, name)
|
|
34
|
+
syntax(bluebook).value_objects.find { |vo| vo.hecks_name == name }
|
|
35
|
+
.members.map { |row| row.to_h.transform_values(&:to_s) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def closed_set_members(bluebook, name) = rows(bluebook, name).map { |row| row[:name] }
|
|
39
|
+
|
|
40
|
+
KEYWORD_FIELDS = %i[word context body inner opens fills status was resolves_via disambiguator].freeze
|
|
41
|
+
ARGUMENT_FIELDS = %i[keyword context at named kind required fills selects
|
|
42
|
+
pair_key_fills pair_value_fills pairs_shape status variadic minimum].freeze
|
|
43
|
+
|
|
44
|
+
# A Rust string literal for one field's value — every field here is
|
|
45
|
+
# plain ASCII (a word, a context name, a digit, "true"/"false"), so this
|
|
46
|
+
# only has to be safe against the two characters Rust string literals
|
|
47
|
+
# themselves reserve.
|
|
48
|
+
def rust_string(value) = "\"#{value.to_s.gsub('\\', '\\\\\\\\').gsub('"', '\\"')}\""
|
|
49
|
+
|
|
50
|
+
def keyword_row(row)
|
|
51
|
+
fields = KEYWORD_FIELDS.map { |field| rust_string(row[field]) }
|
|
52
|
+
" KeywordRow { #{KEYWORD_FIELDS.zip(fields).map { |name, value| "#{name}: #{value}" }.join(', ')} },"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def argument_row(row)
|
|
56
|
+
fields = ARGUMENT_FIELDS.map { |field| rust_string(row[field]) }
|
|
57
|
+
" ArgumentRow { #{ARGUMENT_FIELDS.zip(fields).map { |name, value| "#{name}: #{value}" }.join(', ')} },"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def const_str_array(name, values)
|
|
61
|
+
lines = values.map { |value| " #{rust_string(value)}," }
|
|
62
|
+
"pub static #{name}: &[&str] = &[\n#{lines.join("\n")}\n];\n"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# S14, ADR 0026 — Keyword/Argument are genuine entities of Syntax
|
|
66
|
+
# now, dispatched (not merely declared) so their own `status`
|
|
67
|
+
# really is a lifecycle. `SyntaxBoot.call` reads the STILL-STATIC
|
|
68
|
+
# seed rows (`KeywordSeed`/`ArgumentSeed`), dispatches each one
|
|
69
|
+
# through the real admission/lifecycle door, and hands back the
|
|
70
|
+
# exact same shape `rows` used to read straight off the closed set
|
|
71
|
+
# — symbol keys, string values, `status` included — so nothing
|
|
72
|
+
# else in this file needed to change.
|
|
73
|
+
def render(bluebook)
|
|
74
|
+
table = Hecks::Bluebook::MetaValidator::SyntaxBoot.call
|
|
75
|
+
keywords = table[:keywords]
|
|
76
|
+
arguments = table[:arguments]
|
|
77
|
+
|
|
78
|
+
<<~RUST
|
|
79
|
+
// GENERATED by bin/project_parser_table from
|
|
80
|
+
// aggregate-local syntax tables under lib/hecks/language/ — DO NOT EDIT BY HAND.
|
|
81
|
+
//
|
|
82
|
+
// Re-run `bin/project_parser_table` after any syntax-table change.
|
|
83
|
+
// spec/parser_table_spec.rb regenerates this into memory and fails the
|
|
84
|
+
// normal `bundle exec rspec` suite the moment this file drifts from what
|
|
85
|
+
// the language currently declares — see that spec and
|
|
86
|
+
// bin/project_parser_table's own header for why this file is never
|
|
87
|
+
// hand-edited.
|
|
88
|
+
|
|
89
|
+
#[derive(Debug, Clone, Copy)]
|
|
90
|
+
pub struct KeywordRow {
|
|
91
|
+
pub word: &'static str,
|
|
92
|
+
pub context: &'static str,
|
|
93
|
+
pub body: &'static str,
|
|
94
|
+
pub inner: &'static str,
|
|
95
|
+
pub opens: &'static str,
|
|
96
|
+
pub fills: &'static str,
|
|
97
|
+
pub status: &'static str,
|
|
98
|
+
pub was: &'static str,
|
|
99
|
+
/// Which shared Hecks::Bluebook::DSL::RuleReference
|
|
100
|
+
/// primitive this word's own bare form resolves through,
|
|
101
|
+
/// once it has one ("hash_chain" / "owner_keyed" /
|
|
102
|
+
/// "sibling_scan") — "" for every word that only ever
|
|
103
|
+
/// declares.
|
|
104
|
+
pub resolves_via: &'static str,
|
|
105
|
+
/// Which disambiguator keyword argument a bare reference
|
|
106
|
+
/// may supply — "declared_by" today, "" otherwise.
|
|
107
|
+
pub disambiguator: &'static str,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#[derive(Debug, Clone, Copy)]
|
|
111
|
+
pub struct ArgumentRow {
|
|
112
|
+
pub keyword: &'static str,
|
|
113
|
+
pub context: &'static str,
|
|
114
|
+
pub at: &'static str,
|
|
115
|
+
pub named: &'static str,
|
|
116
|
+
pub kind: &'static str,
|
|
117
|
+
pub required: &'static str,
|
|
118
|
+
pub fills: &'static str,
|
|
119
|
+
pub selects: &'static str,
|
|
120
|
+
pub pair_key_fills: &'static str,
|
|
121
|
+
pub pair_value_fills: &'static str,
|
|
122
|
+
pub pairs_shape: &'static str,
|
|
123
|
+
pub status: &'static str,
|
|
124
|
+
pub variadic: &'static str,
|
|
125
|
+
pub minimum: &'static str,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
impl KeywordRow {
|
|
129
|
+
/// An absent status reads as admitted — the grown-column convention
|
|
130
|
+
/// syntax_conformance_spec.rb's own `status_of`/`live?` already use.
|
|
131
|
+
pub fn live(&self) -> bool {
|
|
132
|
+
matches!(self.status, "" | "admitted" | "deprecated")
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
impl ArgumentRow {
|
|
137
|
+
pub fn live(&self) -> bool {
|
|
138
|
+
matches!(self.status, "" | "admitted" | "deprecated")
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
pub static KEYWORDS: &[KeywordRow] = &[
|
|
143
|
+
#{keywords.map { |row| keyword_row(row) }.join("\n")}
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
pub static ARGUMENTS: &[ArgumentRow] = &[
|
|
147
|
+
#{arguments.map { |row| argument_row(row) }.join("\n")}
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
#{const_str_array('CONTEXTS', closed_set_members(bluebook, 'Context'))}
|
|
151
|
+
#{const_str_array('BODIES', closed_set_members(bluebook, 'Body'))}
|
|
152
|
+
#{const_str_array('ARGUMENT_KINDS', closed_set_members(bluebook, 'ArgumentKind'))}
|
|
153
|
+
#{const_str_array('PAIRS_SHAPES', closed_set_members(bluebook, 'PairsShape'))}
|
|
154
|
+
#{const_str_array('STATUSES', closed_set_members(bluebook, 'Status'))}
|
|
155
|
+
RUST
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require_relative "../doc/reference"
|
|
2
|
+
require_relative "../projector"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Projections
|
|
6
|
+
# THE DSL REFERENCE PAGES, projected from the chapter's own Syntax
|
|
7
|
+
# aggregate — the tables come from the declaration, the prose is
|
|
8
|
+
# preserved from whatever is already committed.
|
|
9
|
+
#
|
|
10
|
+
# THE FIRST PROJECTION THAT EMITS A TREE. `emits: :files` says so,
|
|
11
|
+
# and the framework writes the map rather than inferring one: a Hash
|
|
12
|
+
# of path => contents and a Hash that merely holds strings are the
|
|
13
|
+
# same object to Ruby, so only the projection can know which it
|
|
14
|
+
# meant. This is the case the output contract was designed for and
|
|
15
|
+
# deliberately left unimplemented until something real needed it.
|
|
16
|
+
#
|
|
17
|
+
# NOT PURE, and it cannot be. `Doc::Reference.pages` READS the
|
|
18
|
+
# committed pages to harvest their prose, so the existing directory
|
|
19
|
+
# is an input — passed as `from:` rather than assumed, so the
|
|
20
|
+
# projection never reaches for a path of its own choosing.
|
|
21
|
+
module Reference
|
|
22
|
+
extend Projector::Target
|
|
23
|
+
|
|
24
|
+
projects_as :reference, declares: "Syntax", emits: :files
|
|
25
|
+
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
def call(bluebook:, options: {})
|
|
29
|
+
from = options[:from] or
|
|
30
|
+
raise ArgumentError,
|
|
31
|
+
":reference harvests prose from the committed pages, so it needs " \
|
|
32
|
+
"`from:` — the directory they live in"
|
|
33
|
+
|
|
34
|
+
Doc::Reference.pages(from)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require_relative "../ports/persistence"
|
|
2
|
+
require_relative "../projector"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Projections
|
|
6
|
+
# THE STORAGE SHAPE of one bluebook — the same structural form
|
|
7
|
+
# `StorageShape.mint_hash` hashes to name an era, which is what makes
|
|
8
|
+
# a bump/no-bump question answerable by diffing two of these.
|
|
9
|
+
#
|
|
10
|
+
# THE RETROFIT, and the point of it: `Runtime::StorageShape.project`
|
|
11
|
+
# was written long before this framework existed, and it already
|
|
12
|
+
# takes exactly one bluebook, already returns a plain Hash, already
|
|
13
|
+
# touches no disk and no live runtime — it even already uses the
|
|
14
|
+
# word. Nothing about it was shaped to fit `call(bluebook:, options:)`.
|
|
15
|
+
#
|
|
16
|
+
# That it fits anyway, by a two-line keyword adapter and no change to
|
|
17
|
+
# `StorageShape` itself, is the evidence that the protocol is a real
|
|
18
|
+
# description of what projections do here rather than a shape fitted
|
|
19
|
+
# around its own first example (`:ir`, whose `call` is `to_h`, proves
|
|
20
|
+
# much less on its own).
|
|
21
|
+
module Shape
|
|
22
|
+
extend Projector::Target
|
|
23
|
+
|
|
24
|
+
projects_as :shape
|
|
25
|
+
|
|
26
|
+
module_function
|
|
27
|
+
|
|
28
|
+
# ADR 0033 — `Runtime::StorageShape` lives in the era persistence
|
|
29
|
+
# plugin now; the `:shape` projection itself stays registered
|
|
30
|
+
# unconditionally (the seam `spec/projector_seam_spec.rb` enforces
|
|
31
|
+
# is "every file here registers," not "every projection always
|
|
32
|
+
# succeeds") but refuses clearly if asked to run with the plugin
|
|
33
|
+
# unloaded, rather than raising on an undefined constant.
|
|
34
|
+
def call(bluebook:, options: {})
|
|
35
|
+
unless Ports::Persistence.plugin?(:era)
|
|
36
|
+
raise "the :shape projection needs the era persistence plugin loaded " \
|
|
37
|
+
"(require \"hecks/ports/persistence/plugins/era\")"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Runtime::StorageShape.project(bluebook)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|