hecks 0.2.0 → 0.3.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 +403 -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 +62 -0
- data/lib/hecks/adapters/driven/heki/journal.rb +51 -0
- data/lib/hecks/adapters/driven/heki/saga_store.rb +0 -0
- data/lib/hecks/adapters/driven/heki/snapshot.rb +38 -0
- data/lib/hecks/adapters/driven/heki.adapter +4 -0
- data/lib/hecks/adapters/driven/heki.rb +148 -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 +131 -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 +80 -0
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +168 -0
- data/lib/hecks/adapters/driven/postgres.adapter +5 -0
- data/lib/hecks/adapters/driven/postgres.rb +391 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/era_store.rb +171 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/field_cache.rb +190 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/head_compiler.rb +429 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/mint_transaction.rb +166 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/provisioning.rb +286 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/resumable_backfill.rb +168 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/tail_merge.rb +163 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/transform_installer.rb +114 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage.rb +137 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/coverage_check.rb +89 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +82 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/merge_coordinator.rb +43 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/minter.rb +132 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager.rb +74 -0
- data/lib/hecks/adapters/driven/postgres_era.adapter +6 -0
- data/lib/hecks/adapters/driven/postgres_era.rb +671 -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 +63 -0
- data/lib/hecks/adapters/driven/sqlite/projection.rb +105 -0
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +180 -0
- data/lib/hecks/adapters/driven/sqlite.adapter +9 -0
- data/lib/hecks/adapters/driven/sqlite.rb +285 -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 +36 -0
- data/lib/hecks/adapters.rb +6 -0
- data/lib/hecks/behaviors/dsl.rb +96 -0
- data/lib/hecks/behaviors/expectations.rb +280 -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 +409 -0
- data/lib/hecks/bluebook/assembly/marks.rb +227 -0
- data/lib/hecks/bluebook/assembly/specializer.rb +70 -0
- data/lib/hecks/bluebook/assembly.rb +78 -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 +51 -0
- data/lib/hecks/bluebook/behaviour/policy.rb +52 -0
- data/lib/hecks/bluebook/behaviour/process_manager.rb +38 -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 +68 -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 +906 -0
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +327 -0
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +71 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +1018 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +626 -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 +305 -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 +120 -0
- data/lib/hecks/bluebook/dsl/port_builder.rb +30 -0
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +142 -0
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +207 -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 +172 -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 +221 -0
- data/lib/hecks/bluebook/dsl/world_builder.rb +77 -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 +55 -0
- data/lib/hecks/bluebook/expression/evaluator.rb +261 -0
- data/lib/hecks/bluebook/expression/projection.json +170 -0
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +233 -0
- data/lib/hecks/bluebook/expression/resolver.rb +630 -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 +559 -0
- data/lib/hecks/bluebook/meta_validator/plan.rb +332 -0
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +47 -0
- data/lib/hecks/bluebook/meta_validator/readings.rb +350 -0
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +312 -0
- data/lib/hecks/bluebook/meta_validator/shapes.rb +246 -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 +452 -0
- data/lib/hecks/bluebook/model_check.rb +318 -0
- data/lib/hecks/bluebook/pattern_subset.rb +120 -0
- data/lib/hecks/bluebook/policy.rb +41 -0
- data/lib/hecks/bluebook/process_manager.rb +86 -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 +58 -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 +389 -0
- data/lib/hecks/embryonaut_bluebook.rb +75 -0
- data/lib/hecks/facade/cli_door.rb +116 -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 +154 -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 +300 -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 +170 -0
- data/lib/hecks/forms/field_shape.rb +232 -0
- data/lib/hecks/forms/html.rb +53 -0
- data/lib/hecks/forms/index_renderer.rb +35 -0
- data/lib/hecks/forms/page.rb +157 -0
- data/lib/hecks/forms/params.rb +132 -0
- data/lib/hecks/forms/query_form_renderer.rb +114 -0
- data/lib/hecks/forms/record_renderer.rb +115 -0
- data/lib/hecks/forms/record_table.rb +63 -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 +130 -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/invalid_value_generator.rb +86 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +92 -0
- data/lib/hecks/fuzzing/properties.rb +1146 -0
- data/lib/hecks/fuzzing/replay.rb +635 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +93 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +54 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +96 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +185 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +130 -0
- data/lib/hecks/fuzzing/value_generator.rb +177 -0
- data/lib/hecks/fuzzing.rb +12 -0
- data/lib/hecks/grammar/evolve.rb +291 -0
- data/lib/hecks/grammar/expression.bluebook +417 -0
- data/lib/hecks/grammar/expression_operators.json +1152 -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 +437 -0
- data/lib/hecks/language/bluebook/entity.bluebook +316 -0
- data/lib/hecks/language/bluebook/policy.bluebook +172 -0
- data/lib/hecks/language/bluebook/process_manager.bluebook +264 -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 +447 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +364 -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 +313 -0
- data/lib/hecks/language/port.bluebook +92 -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 +121 -0
- data/lib/hecks/ports/access_control.port +4 -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 +4 -0
- data/lib/hecks/ports/agent.rb +167 -0
- data/lib/hecks/ports/authentication.port +4 -0
- data/lib/hecks/ports/authentication.rb +50 -0
- data/lib/hecks/ports/authorization.port +4 -0
- data/lib/hecks/ports/authorization.rb +59 -0
- data/lib/hecks/ports/clock.port +4 -0
- data/lib/hecks/ports/clock.rb +62 -0
- data/lib/hecks/ports/extraction.port +4 -0
- data/lib/hecks/ports/extraction.rb +37 -0
- data/lib/hecks/ports/identity_assignment.port +4 -0
- data/lib/hecks/ports/identity_assignment.rb +45 -0
- data/lib/hecks/ports/identity_generation.port +4 -0
- data/lib/hecks/ports/identity_generation.rb +49 -0
- data/lib/hecks/ports/identity_resolution.port +4 -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 +102 -0
- data/lib/hecks/ports/persistence/binding_policy.rb +56 -0
- data/lib/hecks/ports/persistence/execution.rb +18 -0
- data/lib/hecks/ports/persistence/lineage.rb +277 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +25 -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 +76 -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 +413 -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 +31 -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 +437 -0
- data/lib/hecks/projector/docs_projector.rb +321 -0
- data/lib/hecks/projector/exporter.rb +141 -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 +392 -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 +80 -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 +89 -0
- data/lib/hecks/query_specification/hop_path.rb +130 -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/caller.rb +50 -0
- data/lib/hecks/runtime/capability_graph.rb +44 -0
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +124 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +248 -0
- data/lib/hecks/runtime/command_interpreter.rb +413 -0
- data/lib/hecks/runtime/command_rules/admissibility.rb +307 -0
- data/lib/hecks/runtime/command_rules/arithmetic.rb +254 -0
- data/lib/hecks/runtime/command_rules/authorization.rb +64 -0
- data/lib/hecks/runtime/command_rules/emission.rb +34 -0
- data/lib/hecks/runtime/command_rules/references.rb +181 -0
- data/lib/hecks/runtime/command_rules.rb +28 -0
- data/lib/hecks/runtime/dependency_planning.rb +231 -0
- data/lib/hecks/runtime/dispatcher.rb +286 -0
- data/lib/hecks/runtime/entity_element.rb +253 -0
- data/lib/hecks/runtime/entity_interpreter.rb +228 -0
- data/lib/hecks/runtime/era_check.rb +136 -0
- data/lib/hecks/runtime/era_guard/shape_diff.rb +128 -0
- data/lib/hecks/runtime/era_guard.rb +193 -0
- data/lib/hecks/runtime/era_tamper.rb +61 -0
- data/lib/hecks/runtime/errors.rb +100 -0
- data/lib/hecks/runtime/event.rb +51 -0
- data/lib/hecks/runtime/identity.rb +128 -0
- data/lib/hecks/runtime/instance.rb +120 -0
- data/lib/hecks/runtime/interpreting.rb +69 -0
- data/lib/hecks/runtime/loader.rb +143 -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 +254 -0
- data/lib/hecks/runtime/reaction_invocation.rb +245 -0
- data/lib/hecks/runtime/read_model_interpreter.rb +266 -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 +124 -0
- data/lib/hecks/runtime/registry/saga_persistence.rb +70 -0
- data/lib/hecks/runtime/registry/verification.rb +158 -0
- data/lib/hecks/runtime/registry.rb +218 -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 +301 -0
- data/lib/hecks/runtime/storage_shape.rb +120 -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 +112 -0
- data/lib/hecks/runtime/value/coercion.rb +500 -0
- data/lib/hecks/runtime/value/invariant_violation.rb +5 -0
- data/lib/hecks/runtime/value.rb +125 -0
- data/lib/hecks/runtime.rb +95 -0
- data/lib/hecks/translation/audit/approval_digest.rb +31 -0
- data/lib/hecks/translation/audit/layer_one.rb +37 -0
- data/lib/hecks/translation/audit/layer_two.rb +77 -0
- data/lib/hecks/translation/audit/unfed_report.rb +42 -0
- data/lib/hecks/translation/audit.rb +70 -0
- data/lib/hecks/translation/reattest.rb +72 -0
- data/lib/hecks/translation/rule_compiler.rb +120 -0
- data/lib/hecks/translation/scaffold/differ.rb +183 -0
- data/lib/hecks/translation/scaffold/renderer.rb +41 -0
- data/lib/hecks/translation/scaffold/writer.rb +28 -0
- data/lib/hecks/translation/scaffold.rb +30 -0
- data/lib/hecks/translation.rb +11 -0
- data/lib/hecks/version.rb +16 -0
- data/lib/hecks/vocabulary.rb +213 -0
- data/lib/hecks.rb +118 -6
- metadata +401 -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,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,31 @@
|
|
|
1
|
+
require_relative "../runtime/storage_shape"
|
|
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
|
+
def call(bluebook:, options: {}) = Runtime::StorageShape.project(bluebook)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require_relative "../projector"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
# A DOMAIN'S OWN DECLARED FACTS, PROJECTED AS PLAIN ENGLISH SENTENCES
|
|
6
|
+
# — one atomic, independently-checkable statement per fact, not a
|
|
7
|
+
# flowing document (`Projections::Reference`/`DocsProjector` already
|
|
8
|
+
# do that job well; this is a flat list a caller can iterate, diff,
|
|
9
|
+
# or hand to something else entirely — a review, a test-plan seed,
|
|
10
|
+
# a sanity check that the domain still says what someone thinks it
|
|
11
|
+
# says).
|
|
12
|
+
#
|
|
13
|
+
# NEVER INVENTS A SENTENCE FROM NOTHING — `DocsProjector`'s own
|
|
14
|
+
# discipline, held here too: a relationship's sentence is built
|
|
15
|
+
# MECHANICALLY from its own declared shape (holder, target,
|
|
16
|
+
# relationship kind — the same facts `Projections::Diagrams`'s own
|
|
17
|
+
# relationship_edge already reads), and an invariant's sentence IS
|
|
18
|
+
# the domain author's own `description`, capitalized and
|
|
19
|
+
# punctuated, never paraphrased. If a fact has no author-written
|
|
20
|
+
# description and no unambiguous mechanical phrasing, it doesn't
|
|
21
|
+
# get a sentence here — a wrong sentence is worse than a missing
|
|
22
|
+
# one.
|
|
23
|
+
#
|
|
24
|
+
# REACHABLE THE SAME WAY EVERY PROJECTION ALREADY IS, no new facade
|
|
25
|
+
# wiring needed: `Pizzas.project(Projections::Statements)` —
|
|
26
|
+
# `Facade::Surface::Chapter#project`'s own comment already settled
|
|
27
|
+
# this ("anything genuinely needing the graph is a projector, and a
|
|
28
|
+
# projector is given it"). `bin/statements` is a thin, optional
|
|
29
|
+
# convenience for reaching the same call from a shell.
|
|
30
|
+
#
|
|
31
|
+
# MVP SCOPE: "has many"/"has a"/"belongs to"/"references" sentences
|
|
32
|
+
# for every list or relationship attribute, and every invariant's
|
|
33
|
+
# own description (aggregate-level and every nested value object's),
|
|
34
|
+
# verbatim. Lifecycle transitions and command given/ensures read the
|
|
35
|
+
# same way and are the natural next sentences — not built yet.
|
|
36
|
+
module Statements
|
|
37
|
+
extend Hecks::Projector::Target
|
|
38
|
+
|
|
39
|
+
projects_as :statements
|
|
40
|
+
|
|
41
|
+
module_function
|
|
42
|
+
|
|
43
|
+
def call(bluebook:, options: {})
|
|
44
|
+
holders(bluebook).flat_map { |holder| statements_for(holder) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def holders(bluebook) = bluebook.aggregates.flat_map { |aggregate| [aggregate, *aggregate.entities] }
|
|
48
|
+
|
|
49
|
+
def statements_for(holder)
|
|
50
|
+
attribute_statements(holder) + invariant_statements(holder)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# "MANY" READS THE SAME WAY REGARDLESS OF WHAT'S BEHIND IT — a
|
|
54
|
+
# `has_many` relationship to another aggregate and a plain
|
|
55
|
+
# `list_of(Topping)` value-object attribute are the same idea to
|
|
56
|
+
# someone reading the domain in English ("an Order has many
|
|
57
|
+
# toppings" is true either way), even though they're two
|
|
58
|
+
# different IR shapes (a `Reference` versus an ordinary type).
|
|
59
|
+
# `attribute.name` carries the noun, not the target's own class
|
|
60
|
+
# name, because the field's own name is what the domain author
|
|
61
|
+
# actually chose to call the collection — "toppings", not
|
|
62
|
+
# "Topping".
|
|
63
|
+
def attribute_statements(holder)
|
|
64
|
+
holder.attributes.filter_map { |attribute| attribute_statement(holder, attribute) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def attribute_statement(holder, attribute)
|
|
68
|
+
subject = "#{article(holder.hecks_name)} #{holder.hecks_name}"
|
|
69
|
+
return "#{subject} has many #{attribute.name}." if attribute.list?
|
|
70
|
+
|
|
71
|
+
target = attribute.relationship && attribute.type.target_name
|
|
72
|
+
target_phrase = target && "#{article(target).downcase} #{target}"
|
|
73
|
+
case attribute.relationship
|
|
74
|
+
when "has_one" then "#{subject} has #{target_phrase}."
|
|
75
|
+
when "belongs_to" then "#{subject} belongs to #{target_phrase}."
|
|
76
|
+
when "reference_to" then "#{subject} references #{target_phrase}."
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# "AN ACCOUNT", NOT "A ACCOUNT" — every subject/object noun here is
|
|
81
|
+
# a bare construct name (`ATMCard`, `ExternalTransfer`, `Account`),
|
|
82
|
+
# never free text, so the ordinary "starts with a vowel LETTER"
|
|
83
|
+
# heuristic is safe: this language's own naming never produces the
|
|
84
|
+
# English exceptions that heuristic gets wrong (an "hour", a
|
|
85
|
+
# "university") because a construct name is always spelled as a
|
|
86
|
+
# plain word, never an abbreviation read letter-by-letter or a
|
|
87
|
+
# word starting with a consonant LETTER but a vowel SOUND.
|
|
88
|
+
def article(word) = word.to_s.match?(/\A[AEIOUaeiou]/) ? "An" : "A"
|
|
89
|
+
|
|
90
|
+
# INVARIANTS LIVE IN TWO PLACES — directly on the holder (an
|
|
91
|
+
# aggregate-level rule, checked after every command) and on every
|
|
92
|
+
# value object nested inside it — `DocsProjector#rules_of`'s own
|
|
93
|
+
# `value_object_for` lookup is the precedent for walking both.
|
|
94
|
+
def invariant_statements(holder)
|
|
95
|
+
own = Array(holder.respond_to?(:invariants) ? holder.invariants : [])
|
|
96
|
+
nested = Array(holder.respond_to?(:value_objects) ? holder.value_objects : []).flat_map(&:invariants)
|
|
97
|
+
(own + nested).map { |invariant| invariant_statement(invariant) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# THE DOMAIN AUTHOR'S OWN WORDS, CAPITALIZED AND PUNCTUATED —
|
|
101
|
+
# nothing else. `invariant("a pizza is named")` already reads as
|
|
102
|
+
# a sentence; this is the entire transformation.
|
|
103
|
+
def invariant_statement(invariant)
|
|
104
|
+
text = invariant.description.to_s.strip
|
|
105
|
+
text = "#{text[0].upcase}#{text[1..]}" if text[0]
|
|
106
|
+
text.end_with?(".", "!", "?") ? text : "#{text}."
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
require_relative "../projector"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
# THE CLOSED SETS, PROJECTED — lib/hecks/vocabulary.rb rendered
|
|
6
|
+
# from whichever chapter declares a `Vocabulary` aggregate, which in
|
|
7
|
+
# practice is the language projecting its own tables.
|
|
8
|
+
#
|
|
9
|
+
# Projector.call(:vocabulary, bluebook: <the Bluebook chapter>)
|
|
10
|
+
#
|
|
11
|
+
# A PROJECTION RATHER THAN A BIN/ SCRIPT, and that correction is the
|
|
12
|
+
# point of it. This was first written as its own program with its own
|
|
13
|
+
# call shape, beside `bin/project_parser_table`, `bin/reference` and
|
|
14
|
+
# `bin/expression_projection` — four tools doing "canonical IR in,
|
|
15
|
+
# external artifact out" four different ways, which is the exact
|
|
16
|
+
# thing the projector registry exists to stop.
|
|
17
|
+
#
|
|
18
|
+
# Reads the chapter's JUDGED IR, so what is written out is what the
|
|
19
|
+
# language actually holds rather than what a builder happened to
|
|
20
|
+
# produce.
|
|
21
|
+
module Vocabulary
|
|
22
|
+
extend Projector::Target
|
|
23
|
+
|
|
24
|
+
projects_as :vocabulary, declares: "Vocabulary"
|
|
25
|
+
|
|
26
|
+
HEADER = <<~RUBY.freeze
|
|
27
|
+
# GENERATED — projected from the language's own Vocabulary aggregate
|
|
28
|
+
# (lib/hecks/language/bluebook/vocabulary.bluebook).
|
|
29
|
+
#
|
|
30
|
+
# DO NOT EDIT. spec/vocabulary_table_spec.rb re-projects this in memory
|
|
31
|
+
# and refuses a diff, so an edit here fails the ordinary suite.
|
|
32
|
+
#
|
|
33
|
+
# Plain data on purpose — no requires, no dependency on the model —
|
|
34
|
+
# because several of these sets are read while a bluebook is still
|
|
35
|
+
# being parsed. A table that needed the framework to load could not
|
|
36
|
+
# be the table the framework loads with.
|
|
37
|
+
RUBY
|
|
38
|
+
|
|
39
|
+
module_function
|
|
40
|
+
|
|
41
|
+
# The projector protocol. `options` is unused: a vocabulary table
|
|
42
|
+
# has nothing to vary.
|
|
43
|
+
def call(bluebook:, options: {}) = render(bluebook)
|
|
44
|
+
|
|
45
|
+
# FULL ROWS, not just the first field of each.
|
|
46
|
+
#
|
|
47
|
+
# Most vocabularies ARE one-field lists and the field is the term.
|
|
48
|
+
# Several are not: `Comparison` declares the algebra each operator
|
|
49
|
+
# computes with, `RefusalTemplate` an error plus its key and text.
|
|
50
|
+
# Taking the first field of those produced a list of thirty-nine
|
|
51
|
+
# duplicated error names — well-formed and meaningless. So rows are
|
|
52
|
+
# carried whole, and the terms are derived from them.
|
|
53
|
+
def tables(bluebook)
|
|
54
|
+
bluebook.aggregate("Vocabulary").value_objects.to_h do |vo|
|
|
55
|
+
[vo.hecks_name, vo.members.map { |row| row.to_h.transform_keys(&:to_s).transform_values(&:to_s) }]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def render(bluebook)
|
|
60
|
+
rows = tables(bluebook).sort_by(&:first).map do |name, members|
|
|
61
|
+
" #{name.inspect} => [\n" +
|
|
62
|
+
members.map { |row| " #{row.inspect}.freeze" }.join(",\n") +
|
|
63
|
+
"\n ].freeze"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
<<~RUBY
|
|
67
|
+
#{HEADER}
|
|
68
|
+
module Hecks
|
|
69
|
+
module Vocabulary
|
|
70
|
+
TABLES = {
|
|
71
|
+
#{rows.join(",\n")}
|
|
72
|
+
}.freeze
|
|
73
|
+
|
|
74
|
+
# THE TERMS — the first field of each row, which for a
|
|
75
|
+
# one-field vocabulary is the whole of it. Derived ONCE and
|
|
76
|
+
# frozen rather than mapped per call: these are constant
|
|
77
|
+
# tables, and a constant that allocates a new array every
|
|
78
|
+
# time it is read is not one.
|
|
79
|
+
TERMS = TABLES.transform_values { |rows| rows.map { |row| row.values.first }.freeze }.freeze
|
|
80
|
+
|
|
81
|
+
module_function
|
|
82
|
+
|
|
83
|
+
# Refuses an unknown name rather than answering nil: a set
|
|
84
|
+
# the language does not declare is a typo, not an empty set.
|
|
85
|
+
def fetch(name) = TERMS.fetch(name)
|
|
86
|
+
|
|
87
|
+
# The rows whole, for the vocabularies that carry more than
|
|
88
|
+
# a term — Comparison's own algebra, RefusalTemplate's text.
|
|
89
|
+
def rows(name) = TABLES.fetch(name)
|
|
90
|
+
|
|
91
|
+
def symbols(name) = fetch(name).map(&:to_sym)
|
|
92
|
+
|
|
93
|
+
def names = TABLES.keys
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
RUBY
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require_relative "projector"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
# THE TARGETS A DOMAIN CAN BE PROJECTED INTO, as constants rather than
|
|
5
|
+
# bare symbols — `Pizzas.project(Projections::OIDC)`.
|
|
6
|
+
#
|
|
7
|
+
# A constant is worth the namespace for two reasons a Symbol cannot
|
|
8
|
+
# give: a typo raises `NameError` at the call site instead of
|
|
9
|
+
# `UnknownProjector` at dispatch time, and each target has somewhere to
|
|
10
|
+
# carry its own documentation and defaults.
|
|
11
|
+
#
|
|
12
|
+
# Namespaced rather than top-level because `IR` is already taken —
|
|
13
|
+
# `Hecks::Bluebook` is the MODEL (`Bluebook::Command`, and the
|
|
14
|
+
# chapter class itself).
|
|
15
|
+
# `Projections::IR` is the projection OF that model, a different thing
|
|
16
|
+
# that would be genuinely confusing under the same bare name.
|
|
17
|
+
#
|
|
18
|
+
# `include Hecks::Projections` gets the short spelling where the
|
|
19
|
+
# extra qualification is noise (`bin/` scripts, a console session).
|
|
20
|
+
module Projections
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
require_relative "projections/ir"
|
|
25
|
+
require_relative "projections/shape"
|
|
26
|
+
require_relative "projections/oidc"
|
|
27
|
+
require_relative "projections/vocabulary"
|
|
28
|
+
require_relative "projections/parser_table"
|
|
29
|
+
require_relative "projections/reference"
|
|
30
|
+
require_relative "projections/model"
|
|
31
|
+
require_relative "projections/diagrams"
|
|
32
|
+
require_relative "projections/statements"
|