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,409 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
class Assembly
|
|
4
|
+
# ONE TABLE, WHERE THERE WERE FIVE HAND-WRITTEN MIRRORS OF IT.
|
|
5
|
+
#
|
|
6
|
+
# `bluebook.bluebook` already declares what every construct is made of, and
|
|
7
|
+
# `Plan` already reads it — parent, fields, lists, setters. What the language
|
|
8
|
+
# cannot say is the three things Ruby needs to rebuild one, so those are here
|
|
9
|
+
# and ONLY those:
|
|
10
|
+
#
|
|
11
|
+
# holder which class holds it
|
|
12
|
+
# make :declare for a construct that became a class, :new for an
|
|
13
|
+
# instance — the boundary `Query` sits on, since its body is
|
|
14
|
+
# inherited instance methods
|
|
15
|
+
# fields each keyword the constructor takes, as
|
|
16
|
+
# keyword => [key in the declaration, how to read it]
|
|
17
|
+
#
|
|
18
|
+
# A READER is a symbol naming a method on Marks, or :plain for a value that
|
|
19
|
+
# needs no decoding, or [:each, reader] for a list. Nothing here is a rule
|
|
20
|
+
# about whether a declaration is admissible — the language settled that on the
|
|
21
|
+
# way in. This is only how a spelling becomes an object again.
|
|
22
|
+
#
|
|
23
|
+
# WHY A TABLE AND NOT A METHOD PER CATEGORY. The judge used to carry one
|
|
24
|
+
# hand-written branch per category, and the price was fourteen verbs the
|
|
25
|
+
# language declared and the walk never offered — every rule hanging off them
|
|
26
|
+
# decoration, and nothing red, because a branch that does not exist cannot
|
|
27
|
+
# fail. An assembler with a method per category is the same shape. So the
|
|
28
|
+
# table is checked AGAINST the language by spec/assembly_spec: a field the
|
|
29
|
+
# language declares that no contract consumes is a failure, not a silence.
|
|
30
|
+
#
|
|
31
|
+
# The `derived` list is how a field says it needs no assembling — a parent
|
|
32
|
+
# pointer the containment tree already knows, or something computed from what
|
|
33
|
+
# is here (`query_name` is `Naming.snake(name)`, `creates?` is whether a verb
|
|
34
|
+
# names a root). Naming one is a claim, and the coverage gate holds it.
|
|
35
|
+
def self.contract(category) = CONTRACTS.fetch(category.to_s)
|
|
36
|
+
|
|
37
|
+
CONTRACTS = {
|
|
38
|
+
"Bluebook" => Contract.new(
|
|
39
|
+
holder: Chapter, make: :new,
|
|
40
|
+
fields: {
|
|
41
|
+
name: [:name, :plain],
|
|
42
|
+
version: [:version, :plain],
|
|
43
|
+
vision: [:vision, :plain],
|
|
44
|
+
classification: [:classification, :plain],
|
|
45
|
+
formerly_known_as: [:formerly_known_as, :plain],
|
|
46
|
+
attaches_to: [:attaches_to, :plain]
|
|
47
|
+
},
|
|
48
|
+
rows: { normalisations: :normalisation_table },
|
|
49
|
+
derived: { normalisations: :elsewhere }
|
|
50
|
+
),
|
|
51
|
+
|
|
52
|
+
"Aggregate" => Contract.new(
|
|
53
|
+
holder: Aggregate, make: :new,
|
|
54
|
+
fields: {
|
|
55
|
+
name: [:name, :plain],
|
|
56
|
+
description: [:description, :plain],
|
|
57
|
+
identified_by: [:identified_by, :plain],
|
|
58
|
+
attributes: [:attributes, [:each, :attribute]],
|
|
59
|
+
# THE AGGREGATE BOUNDARY, and the precondition a command may
|
|
60
|
+
# reference by name (S10, ADR 0025 — "Rules"). Same reader
|
|
61
|
+
# shapes ValueObject's own `invariants`/Command's own
|
|
62
|
+
# `givens` already use — `invariant` builds an Invariant,
|
|
63
|
+
# `given` a Given, the same struct AggregateBuilder#given
|
|
64
|
+
# hands a referencing command's own resolved list.
|
|
65
|
+
invariants: [:invariants, [:each, :invariant]],
|
|
66
|
+
preconditions: [:preconditions, [:each, :given]],
|
|
67
|
+
# S12, ADR 0025 — the local half of "projects :name, from:
|
|
68
|
+
# :\"reference.remote_field\"", read the same way
|
|
69
|
+
# invariants/preconditions above are: a synthetic command
|
|
70
|
+
# (Aggregate.Projects) the judge dispatches once per
|
|
71
|
+
# declaration, folded into a list on the owning aggregate.
|
|
72
|
+
projected_fields: [:projected_fields, [:each, :projected_field]],
|
|
73
|
+
provenance: [:provenance, :plain]
|
|
74
|
+
},
|
|
75
|
+
rows: { transitions: :transition_rows, value_objects: :value_object_names, identified_by: :identity_rows },
|
|
76
|
+
reads: { identified_by: [:each, :identity_path], attributes: [:each_with_id, :attribute],
|
|
77
|
+
invariants: [:each, :rule], preconditions: [:each, :rule],
|
|
78
|
+
projected_fields: [:each, :projected_field] },
|
|
79
|
+
derived: {
|
|
80
|
+
position: :walk,
|
|
81
|
+
state_field: [:folded, :lifecycle, :field],
|
|
82
|
+
state_start: [:folded, :lifecycle, :default],
|
|
83
|
+
transitions: [:folded, :lifecycle, :transitions],
|
|
84
|
+
value_objects: :children
|
|
85
|
+
}
|
|
86
|
+
),
|
|
87
|
+
|
|
88
|
+
"Command" => Contract.new(
|
|
89
|
+
holder: Command, make: :declare,
|
|
90
|
+
fields: {
|
|
91
|
+
name: [:name, :plain],
|
|
92
|
+
role: [:role, :plain],
|
|
93
|
+
goal: [:goal, :plain],
|
|
94
|
+
references: [:references, :plain],
|
|
95
|
+
attributes: [:attributes, [:each, :shape_field]],
|
|
96
|
+
givens: [:givens, [:each, :given]],
|
|
97
|
+
ensures: [:ensures, [:each, :given]],
|
|
98
|
+
mutations: [:mutations, [:each, :mutation]],
|
|
99
|
+
emits: [:emits, :plain],
|
|
100
|
+
# LIFECYCLE STATE AS A COMMAND GUARD (S10, ADR 0025) — a
|
|
101
|
+
# literal the same way `provenance`/`default:` already are;
|
|
102
|
+
# one state or an array of them, or nil for a command with
|
|
103
|
+
# no such guard.
|
|
104
|
+
from: [:from, :plain],
|
|
105
|
+
provenance: [:provenance, :plain]
|
|
106
|
+
},
|
|
107
|
+
rows: { mutations: :mutation_rows },
|
|
108
|
+
reads: { attributes: [:each, :shape_field], givens: [:each, :rule], ensures: [:each, :rule],
|
|
109
|
+
mutations: [:call, :mutations], emits: :names, provenance: :provenance, from: :from },
|
|
110
|
+
derived: { position: :walk }
|
|
111
|
+
),
|
|
112
|
+
|
|
113
|
+
"ValueObject" => Contract.new(
|
|
114
|
+
holder: ValueObject, make: :declare,
|
|
115
|
+
fields: {
|
|
116
|
+
name: [:name, :plain],
|
|
117
|
+
attributes: [:attributes, [:each, :shape_field]],
|
|
118
|
+
invariants: [:invariants, [:each, :invariant]],
|
|
119
|
+
members: [:members, [:each, :member]],
|
|
120
|
+
closed_set: [:closed_set, :flag]
|
|
121
|
+
},
|
|
122
|
+
# The language counts the admitted rows ; the IR keeps a flag and the rows.
|
|
123
|
+
reads: { attributes: [:each, :shape_field], invariants: [:each, :rule],
|
|
124
|
+
closed_set: [:call, :closed_set_of], members: [:call, :members_row] },
|
|
125
|
+
derived: { position: :walk, rows: [:folded, %i[closed_set members], nil] }
|
|
126
|
+
),
|
|
127
|
+
|
|
128
|
+
"Query" => Contract.new(
|
|
129
|
+
holder: Query, make: :new,
|
|
130
|
+
fields: {
|
|
131
|
+
name: [:name, :plain],
|
|
132
|
+
description: [:description, :plain],
|
|
133
|
+
attributes: [:attributes, [:each, :shape_field]],
|
|
134
|
+
wheres: [:wheres, [:each, :where_clause]],
|
|
135
|
+
order_by: [:order_by, :order_by],
|
|
136
|
+
limit: [:limit, :limit],
|
|
137
|
+
# Held by the language as an OPEN MAP, so every one of these reads the
|
|
138
|
+
# same way and a ninth option needs no new field on either side.
|
|
139
|
+
offset: [:offset, [:option, :offset]],
|
|
140
|
+
cursor: [:cursor, [:option, :cursor]],
|
|
141
|
+
null_semantics: [:null_semantics, [:option, :null_semantics]],
|
|
142
|
+
authorization: [:authorization, [:option, :authorization]],
|
|
143
|
+
inspection: [:inspection, [:option, :inspection]]
|
|
144
|
+
},
|
|
145
|
+
rows: { wheres: :where_rows, options: :option_rows },
|
|
146
|
+
reads: { attributes: [:each, :shape_field], wheres: [:each, :where_clause],
|
|
147
|
+
order_by: [:call, :order_by], limit: [:call, :limit] },
|
|
148
|
+
derived: {
|
|
149
|
+
position: :walk,
|
|
150
|
+
order_field: [:folded, :order_by, :field],
|
|
151
|
+
order_way: [:folded, :order_by, :direction],
|
|
152
|
+
options: [:folded, %i[offset cursor null_semantics authorization inspection], nil]
|
|
153
|
+
}
|
|
154
|
+
),
|
|
155
|
+
|
|
156
|
+
"Entity" => Contract.new(
|
|
157
|
+
holder: Entity, make: :declare,
|
|
158
|
+
fields: {
|
|
159
|
+
name: [:name, :plain],
|
|
160
|
+
description: [:description, :plain],
|
|
161
|
+
# A LIST OF PATHS, exactly as an aggregate's is. The two used to differ
|
|
162
|
+
# — a Symbol here and a String there, which byte equality with `to_h`
|
|
163
|
+
# COULD NOT SEE because both render as a string, so the assembled graph
|
|
164
|
+
# got a String and `element_of` looked up `args["sequence"]` in a
|
|
165
|
+
# symbol-keyed payload and found nothing: "Reverse acts on one
|
|
166
|
+
# LedgerEntry — pass sequence:", while passing sequence. There is one
|
|
167
|
+
# spelling now, and no room left for that difference.
|
|
168
|
+
identified_by: [:identified_by, :plain],
|
|
169
|
+
attributes: [:attributes, [:each, :shape_field]],
|
|
170
|
+
# ADR 0028 — the SAME shape Aggregate's own `preconditions`
|
|
171
|
+
# already carries, one level down: a piece's own named
|
|
172
|
+
# `given`, referenced back by one of its own commands.
|
|
173
|
+
preconditions: [:preconditions, [:each, :given]],
|
|
174
|
+
# Round 7 — the SAME shape Aggregate's own `invariants`
|
|
175
|
+
# already carries, one level down: checked against every
|
|
176
|
+
# instance of this piece, not the aggregate's own flat state.
|
|
177
|
+
invariants: [:invariants, [:each, :invariant]]
|
|
178
|
+
},
|
|
179
|
+
rows: { transitions: :transition_rows, identified_by: :identity_rows },
|
|
180
|
+
reads: { identified_by: [:each, :identity_path], attributes: [:each, :shape_field],
|
|
181
|
+
preconditions: [:each, :rule], invariants: [:each, :rule] },
|
|
182
|
+
derived: {
|
|
183
|
+
position: :walk,
|
|
184
|
+
owner: :parent,
|
|
185
|
+
state_field: [:folded, :lifecycle, :field],
|
|
186
|
+
state_start: [:folded, :lifecycle, :default],
|
|
187
|
+
transitions: [:folded, :lifecycle, :transitions]
|
|
188
|
+
}
|
|
189
|
+
),
|
|
190
|
+
|
|
191
|
+
"Policy" => Contract.new(
|
|
192
|
+
holder: Policy, make: :new,
|
|
193
|
+
fields: {
|
|
194
|
+
name: [:name, :plain],
|
|
195
|
+
aggregate: [:aggregate, :plain],
|
|
196
|
+
on_event: [:on_event, :plain],
|
|
197
|
+
trigger_command: [:trigger_command, :plain],
|
|
198
|
+
target_domain: [:target_domain, :plain],
|
|
199
|
+
where: [:where, :plain],
|
|
200
|
+
for_each: [:for_each, :plain],
|
|
201
|
+
with_spec: [:with_spec, :bindings]
|
|
202
|
+
},
|
|
203
|
+
rows: { with_spec: :with_spec_rows },
|
|
204
|
+
reads: { with_spec: [:from, :with_spec] },
|
|
205
|
+
derived: { position: :walk }
|
|
206
|
+
),
|
|
207
|
+
|
|
208
|
+
"ProcessManager" => Contract.new(
|
|
209
|
+
holder: ProcessManager, make: :new,
|
|
210
|
+
fields: {
|
|
211
|
+
name: [:name, :plain],
|
|
212
|
+
# A SYMBOL. `SagaInterpreter` does `event.payload[pm.correlates_by]` — a
|
|
213
|
+
# hash lookup on a symbol-keyed payload — and `value == pm.correlates_by`
|
|
214
|
+
# when resolving a leg s bindings. A String there finds nothing and
|
|
215
|
+
# resolves to nothing, so the wire never advanced and a drawer that
|
|
216
|
+
# should have held 2500 held 0. to_h could not show it: it stringifies.
|
|
217
|
+
correlates_by: [:correlates_by, :identity],
|
|
218
|
+
starts_on: [:starts_on, :plain],
|
|
219
|
+
ends_on: [:ends_on, :plain],
|
|
220
|
+
states: [:states, :plain]
|
|
221
|
+
},
|
|
222
|
+
reads: { states: :names },
|
|
223
|
+
# S17, ADR 0026 — `handlers` is a REAL feature of the LANGUAGE's
|
|
224
|
+
# own "ProcessManager" declaration now (`attribute :handlers,
|
|
225
|
+
# list_of(Handler)`, reaction.bluebook), consumed by the judge
|
|
226
|
+
# walking `Plan`'s own containment tree (`Handler`'s own
|
|
227
|
+
# `.parent == "ProcessManager"`) rather than by any field this
|
|
228
|
+
# contract itself reads — the same `:children` shape Aggregate's
|
|
229
|
+
# own `value_objects` claim already uses, one level in.
|
|
230
|
+
derived: { position: :walk, handlers: :children }
|
|
231
|
+
),
|
|
232
|
+
|
|
233
|
+
# S17, ADR 0026 — Handler is a genuine entity now, nested under
|
|
234
|
+
# ProcessManager (`entity "Handler"`, reaction.bluebook). Neither
|
|
235
|
+
# `position` nor `handler` is a stored field any more: a saga
|
|
236
|
+
# answers each event ONCE, so `event_type` is Handler's own real,
|
|
237
|
+
# non-positional identity (no walk-minted `position` to derive),
|
|
238
|
+
# and the process manager it belongs to is structural now — which
|
|
239
|
+
# list this element sits in, not a stored field to fold a parent
|
|
240
|
+
# pointer out of.
|
|
241
|
+
"Handler" => Contract.new(
|
|
242
|
+
holder: ProcessManagerHandler, make: :new,
|
|
243
|
+
fields: {
|
|
244
|
+
event_type: [:event_type, :plain],
|
|
245
|
+
from_state: [:from_state, :plain],
|
|
246
|
+
to_state: [:to_state, :plain]
|
|
247
|
+
},
|
|
248
|
+
# `dispatches` — same reason ProcessManager's own `handlers`
|
|
249
|
+
# claim, above, is `:children` : a real feature of the
|
|
250
|
+
# LANGUAGE's own "Handler" declaration (`attribute :dispatches,
|
|
251
|
+
# list_of(Dispatch)`), consumed by the judge walking `Plan`'s
|
|
252
|
+
# own containment tree rather than by any field this contract
|
|
253
|
+
# reads.
|
|
254
|
+
derived: { dispatches: :children }
|
|
255
|
+
),
|
|
256
|
+
|
|
257
|
+
# S17, ADR 0026 — Dispatch is a genuine entity now, nested under
|
|
258
|
+
# Handler (`entity "Dispatch"`, reaction.bluebook) — two levels
|
|
259
|
+
# deep, "no life outside its Handler" (the ADR's own words).
|
|
260
|
+
# `command_name` is Dispatch's own real, non-positional identity
|
|
261
|
+
# — neither `position` nor `handler` is a stored field any more,
|
|
262
|
+
# the same reason Handler's own contract, above, dropped them.
|
|
263
|
+
"Dispatch" => Contract.new(
|
|
264
|
+
holder: DispatchSpec, make: :new,
|
|
265
|
+
fields: {
|
|
266
|
+
command_name: [:command_name, :plain],
|
|
267
|
+
with_spec: [:with_spec, :bindings]
|
|
268
|
+
},
|
|
269
|
+
rows: { with_spec: :with_spec_rows },
|
|
270
|
+
reads: { with_spec: [:from, :with_spec] },
|
|
271
|
+
derived: {}
|
|
272
|
+
),
|
|
273
|
+
|
|
274
|
+
# S14, ADR 0026 — Syntax/Keyword/Argument are never built via
|
|
275
|
+
# `Build`/`Reconstruction`'s own generic path — their own data
|
|
276
|
+
# lives in a dedicated repository `SyntaxBoot` (meta_validator/
|
|
277
|
+
# syntax_boot.rb) reads directly, never through the meta-
|
|
278
|
+
# domain's own reconstruction, the same reason `Member`'s own
|
|
279
|
+
# entry (above) carries `holder: nil, make: nil` too. These
|
|
280
|
+
# entries exist purely so the introspection specs
|
|
281
|
+
# (assembly_spec.rb) can hold them to the same "every field
|
|
282
|
+
# claimed" discipline every other category answers to.
|
|
283
|
+
"Syntax" => Contract.new(
|
|
284
|
+
holder: nil, make: nil,
|
|
285
|
+
fields: { name: [:name, :plain] },
|
|
286
|
+
derived: { keywords: :children, arguments: :children }
|
|
287
|
+
),
|
|
288
|
+
|
|
289
|
+
"Keyword" => Contract.new(
|
|
290
|
+
holder: nil, make: nil,
|
|
291
|
+
fields: {
|
|
292
|
+
word: [:word, :plain],
|
|
293
|
+
context: [:context, :plain],
|
|
294
|
+
body: [:body, :plain],
|
|
295
|
+
inner: [:inner, :plain],
|
|
296
|
+
opens: [:opens, :plain],
|
|
297
|
+
fills: [:fills, :plain],
|
|
298
|
+
was: [:was, :plain],
|
|
299
|
+
resolves_via: [:resolves_via, :plain],
|
|
300
|
+
disambiguator: [:disambiguator, :plain],
|
|
301
|
+
calls: [:calls, :plain]
|
|
302
|
+
},
|
|
303
|
+
derived: { position: :walk }
|
|
304
|
+
),
|
|
305
|
+
|
|
306
|
+
"Argument" => Contract.new(
|
|
307
|
+
holder: nil, make: nil,
|
|
308
|
+
fields: {
|
|
309
|
+
keyword: [:keyword, :plain],
|
|
310
|
+
context: [:context, :plain],
|
|
311
|
+
at: [:at, :plain],
|
|
312
|
+
named: [:named, :plain],
|
|
313
|
+
kind: [:kind, :plain],
|
|
314
|
+
required: [:required, :plain],
|
|
315
|
+
fills: [:fills, :plain],
|
|
316
|
+
selects: [:selects, :plain],
|
|
317
|
+
pair_key_fills: [:pair_key_fills, :plain],
|
|
318
|
+
pair_value_fills: [:pair_value_fills, :plain],
|
|
319
|
+
pairs_shape: [:pairs_shape, :plain],
|
|
320
|
+
variadic: [:variadic, :plain],
|
|
321
|
+
minimum: [:minimum, :plain],
|
|
322
|
+
coerce: [:coerce, :plain],
|
|
323
|
+
blank_message: [:blank_message, :plain]
|
|
324
|
+
},
|
|
325
|
+
derived: { position: :walk }
|
|
326
|
+
),
|
|
327
|
+
|
|
328
|
+
"ReadModel" => Contract.new(
|
|
329
|
+
holder: ReadModel, make: :new,
|
|
330
|
+
fields: {
|
|
331
|
+
name: [:name, :plain],
|
|
332
|
+
description: [:description, :plain],
|
|
333
|
+
reference_name: [:reference_name, :identity],
|
|
334
|
+
reference_target: [:reference_target, :plain],
|
|
335
|
+
aggregate_heads: [:aggregate_heads, [:each, :head]],
|
|
336
|
+
group_by: [:group_by, [:each, :group_by_field]],
|
|
337
|
+
# `count`/`median_field` are `group_by`'s own two siblings —
|
|
338
|
+
# scalars, not lists, so no `[:each, ...]` shape ; `:plain`
|
|
339
|
+
# is what `Assembly::Build` needs (fed the native `to_h`
|
|
340
|
+
# value directly, already `true`/`nil`/a String), and the
|
|
341
|
+
# `reads:` entry below is what `Reconstruction` needs
|
|
342
|
+
# instead (fed the STRINGIFIED meta-domain row).
|
|
343
|
+
count: [:count, :plain],
|
|
344
|
+
median_field: [:median_field, :plain],
|
|
345
|
+
# A read model inherits every option an ask has, so it reads them the
|
|
346
|
+
# same way — see Query.
|
|
347
|
+
wheres: [:wheres, [:each, :where_clause]],
|
|
348
|
+
order_by: [:order_by, :order_by],
|
|
349
|
+
limit: [:limit, :limit],
|
|
350
|
+
offset: [:offset, [:option, :offset]],
|
|
351
|
+
cursor: [:cursor, [:option, :cursor]],
|
|
352
|
+
null_semantics: [:null_semantics, [:option, :null_semantics]],
|
|
353
|
+
authorization: [:authorization, [:option, :authorization]],
|
|
354
|
+
inspection: [:inspection, [:option, :inspection]]
|
|
355
|
+
},
|
|
356
|
+
rows: { options: :read_model_option_rows },
|
|
357
|
+
# `wheres` needs its own reader for the same reason Query's does — a
|
|
358
|
+
# list defaults to `[]`, not the generic `text(row[key])` cell reader's
|
|
359
|
+
# `nil` — even though a read model's row never carries `wheres` as a
|
|
360
|
+
# native field the way Query's does (`[:each, :where_clause]` over an
|
|
361
|
+
# absent `row[:wheres]` is `Array(nil).map { ... }`, i.e. `[]`, always).
|
|
362
|
+
# The REAL values, when a read model declares any, arrive through
|
|
363
|
+
# `options_of(row)`'s merge in `read_model` below (dispatched as
|
|
364
|
+
# generic Option rows, `read_model_option_rows`/`filter_options` — not
|
|
365
|
+
# as dedicated where-clause rows), which overrides this default. Until
|
|
366
|
+
# `ReadModel#to_h` spelled `wheres`/`order_by`/`limit`
|
|
367
|
+
# unconditionally, `round_trip_spec`'s own "SOURCE KEYS ONLY" +compare
|
|
368
|
+
# never asked about this key at all, so the `nil`-vs-`[]` gap between
|
|
369
|
+
# this contract's default and `to_h`'s own `[]` default went unnoticed.
|
|
370
|
+
# `order_by`/`limit` need no matching entry — the generic reader's
|
|
371
|
+
# `nil` already agrees with `to_h`'s own nil-when-undeclared default
|
|
372
|
+
# for those two. `group_by` needs its own reader the same way — a
|
|
373
|
+
# rootless read model's grouping keys, added independently on main.
|
|
374
|
+
reads: { reference_name: :symbol, aggregate_heads: [:each, :head],
|
|
375
|
+
group_by: [:each, :group_by_field], wheres: [:each, :where_clause],
|
|
376
|
+
# `count` needs the boolean coercion `Shapes#read_model_count`
|
|
377
|
+
# gives it (a stringified "true"/absent on the wire, a real
|
|
378
|
+
# `true`/`nil` in `to_h`) — `median_field` needs none: the
|
|
379
|
+
# default `text(row[key])` cell reader already returns the
|
|
380
|
+
# same String-or-nil `ReadModel#to_h`'s own `&.to_s` does.
|
|
381
|
+
count: :read_model_count },
|
|
382
|
+
derived: {
|
|
383
|
+
position: :walk,
|
|
384
|
+
query_name: [:computed, :query_name],
|
|
385
|
+
options: [:folded, %i[offset cursor null_semantics authorization inspection], nil]
|
|
386
|
+
}
|
|
387
|
+
),
|
|
388
|
+
|
|
389
|
+
# A member's pairs are an OPEN MAP, which is why Member is its own root in
|
|
390
|
+
# the language. The IR keeps them as a plain hash on the value object, so
|
|
391
|
+
# they are assembled with their shape rather than as a construct.
|
|
392
|
+
# S17, ADR 0026 — Member is a genuine entity now, nested under
|
|
393
|
+
# ValueObject (`entity "Member"`, shape.bluebook). It no longer
|
|
394
|
+
# holds a `shape` field at all (that was the free-text, un-parsed
|
|
395
|
+
# spelling a standalone root once needed ; an entity's element is
|
|
396
|
+
# never serialized as text) — only `position` (walk-minted, see
|
|
397
|
+
# `entity_own_identity`, judge.rb) and `pairs` (still an open map,
|
|
398
|
+
# still one row per entry, still why a value object cannot hold it
|
|
399
|
+
# directly).
|
|
400
|
+
"Member" => Contract.new(
|
|
401
|
+
holder: nil, make: nil,
|
|
402
|
+
fields: {},
|
|
403
|
+
rows: { pairs: :pair_rows },
|
|
404
|
+
derived: { position: :walk, pairs: [:folded, %i[members], nil] }
|
|
405
|
+
)
|
|
406
|
+
}.freeze
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
class Assembly
|
|
4
|
+
# The leaf shapes an assembly reads, and the encodings it undoes.
|
|
5
|
+
#
|
|
6
|
+
# `to_h` spells things as text so the export stands on its own, and every
|
|
7
|
+
# one of those spellings has to come back apart here. This is the same family
|
|
8
|
+
# of work `MetaValidator::Shapes` does for the reconstruction — the difference
|
|
9
|
+
# is that Shapes rebuilds HASHES and this rebuilds OBJECTS, so it has to
|
|
10
|
+
# recover types rather than just strings.
|
|
11
|
+
#
|
|
12
|
+
# ENCODING LOSSES ARE THE LARGEST FAMILY OF BUG IN THIS CODEBASE, and every
|
|
13
|
+
# member has the same shape: reading an object where `to_h` holds a spelling.
|
|
14
|
+
# So each method below names the spelling it inverts.
|
|
15
|
+
module Marks
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
# `Attribute#to_h` spells a type with `to_s`, so a reference arrives as
|
|
19
|
+
# "Reference<Customer>" and has to become an edge again. Everything else is
|
|
20
|
+
# a name and stays one.
|
|
21
|
+
def attribute(field)
|
|
22
|
+
type = field[:type].to_s
|
|
23
|
+
target = type[/\AReference<(.+)>\z/, 1]
|
|
24
|
+
|
|
25
|
+
Attribute.new(
|
|
26
|
+
name: field[:name],
|
|
27
|
+
type: target ? Reference.new(target) : type,
|
|
28
|
+
list: field[:list] ? true : false,
|
|
29
|
+
default: field[:default],
|
|
30
|
+
# The LAST place optionality can be dropped, and the one that was
|
|
31
|
+
# dropping it. Every bluebook in the registry is the round-trip
|
|
32
|
+
# product — MetaValidator dispatches the declaration in and reads it
|
|
33
|
+
# back — so a fact this constructor does not carry is a fact the
|
|
34
|
+
# language cannot state about itself, however plainly the source
|
|
35
|
+
# wrote it.
|
|
36
|
+
optional: field[:optional] ? true : false,
|
|
37
|
+
pattern: field[:pattern],
|
|
38
|
+
# THE SAME LESSON, ONE FACT LATER. `admits` is not on `to_h` — the
|
|
39
|
+
# wire does not carry it, on purpose — but it must still survive the
|
|
40
|
+
# round trip, because the grammar registry keeps the ASSEMBLED graph
|
|
41
|
+
# and downstream projections read the link off that. Dropped here, the
|
|
42
|
+
# language could not say `admits` about itself no matter how plainly
|
|
43
|
+
# the source wrote it, which is word for word what the note above
|
|
44
|
+
# already learned about `optional`.
|
|
45
|
+
admits: field[:admits],
|
|
46
|
+
relationship: field[:relationship]
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The same shape. Two names because a head's field and a verb's argument are
|
|
51
|
+
# different things in the language even though the IR keeps one class for
|
|
52
|
+
# both — `Aggregate.Attribute` and `Command.Argument` are separate verbs.
|
|
53
|
+
def shape_field(field) = attribute(field)
|
|
54
|
+
|
|
55
|
+
# ONE PART OF AN IDENTITY. It goes in as a row so the language can hold an
|
|
56
|
+
# ordered list of them, and comes back out as the path it always was —
|
|
57
|
+
# a String, because `identity_paths` splits paths and never symbols.
|
|
58
|
+
def identity_path(part) = part[:value].to_s
|
|
59
|
+
|
|
60
|
+
# A member's fields — an OPEN MAP, which is why Member is its own root in
|
|
61
|
+
# the language and why the pairs arrive as a list rather than a value object.
|
|
62
|
+
#
|
|
63
|
+
# The values are UNMARKED, because `ValueObject#to_h` spells them with `to_s`
|
|
64
|
+
# and the language stores them as text: `member code: "JPY", minor_units: 0`
|
|
65
|
+
# came back with a minor_units of "0", and a closed set that admits the string
|
|
66
|
+
# would refuse the number the caller passes.
|
|
67
|
+
def member(pairs)
|
|
68
|
+
pairs.to_h { |key, value| [key.to_sym, unmark_scalar(value)] }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# A read model's gathered head. The keys must be symbols whichever way the
|
|
72
|
+
# declaration arrived, and `as` must be one too: it NAMES the reader the
|
|
73
|
+
# projection answers to, and `ReadModel#to_h` spells it `to_s`.
|
|
74
|
+
def head(row)
|
|
75
|
+
row.to_h { |key, value| [key.to_sym, key.to_sym == :as ? value.to_sym : value] }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# A group_by field's own name — the builder's native shape is already
|
|
79
|
+
# `{field: :symbol}`, so this matches it rather than leaving `field`
|
|
80
|
+
# as the String `Shapes#group_by_field` reads back.
|
|
81
|
+
def group_by_field(row) = { field: row[:field].to_sym }
|
|
82
|
+
|
|
83
|
+
# A scalar that was written as itself rather than inspected — a member's
|
|
84
|
+
# value, where the language holds text and the type has to be read back from
|
|
85
|
+
# the shape of it. Unlike `read`, a bare word stays a String here, because
|
|
86
|
+
# a closed set admits words far more often than symbols.
|
|
87
|
+
def unmark_scalar(value)
|
|
88
|
+
text = value.to_s
|
|
89
|
+
return true if text == "true"
|
|
90
|
+
return false if text == "false"
|
|
91
|
+
return text.to_i if text.match?(/\A-?\d+\z/)
|
|
92
|
+
return text.to_f if text.match?(/\A-?\d+\.\d+\z/)
|
|
93
|
+
|
|
94
|
+
text
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# A saga's argument bindings. Each value rides Literal's spelling, which
|
|
98
|
+
# marks a Symbol with a leading colon — lose it and an argument reads as a
|
|
99
|
+
# string of the same name.
|
|
100
|
+
def bindings(with) = Array(with).to_h { |key, value| [key.to_sym, read(value)] }
|
|
101
|
+
|
|
102
|
+
def invariant(rule)
|
|
103
|
+
Invariant.new(description: rule[:description], canonical: rule[:canonical])
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def given(rule)
|
|
107
|
+
Given.new(description: rule[:description], canonical: rule[:canonical])
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# S12, ADR 0025 — `projects :name, from: :"reference.remote_field"`.
|
|
111
|
+
# All three fields are identifiers, unlike Invariant/Given's own
|
|
112
|
+
# free text, so — like `attribute`'s own `name`/`type` below —
|
|
113
|
+
# they come back as Symbols.
|
|
114
|
+
def projected_field(row)
|
|
115
|
+
ProjectedField.new(name: row[:name].to_sym, reference: row[:reference].to_sym,
|
|
116
|
+
remote_field: row[:remote_field].to_sym)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# `Mutation#to_h` branches on the operation, so this does too.
|
|
120
|
+
#
|
|
121
|
+
# An APPEND binds several fields at once, each either an ARGUMENT (a
|
|
122
|
+
# Symbol, wearing its colon) or a LITERAL — the distinction that is the
|
|
123
|
+
# whole reason `append: { direction: "out" }` was once indistinguishable
|
|
124
|
+
# from an argument named `out`.
|
|
125
|
+
def mutation(change)
|
|
126
|
+
target = change[:target].to_sym
|
|
127
|
+
op = change[:op].to_sym
|
|
128
|
+
|
|
129
|
+
# `:delegate` (CommandBuilder#delegates_to's own comment) rides
|
|
130
|
+
# the SAME multi-binding shape `:append` does.
|
|
131
|
+
return Mutation.new(target: target, op: op, source: appended(change[:fields])) if [:append, :delegate].include?(op)
|
|
132
|
+
|
|
133
|
+
Mutation.new(target: target, op: op, source: classified(change[:source]))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def appended(fields)
|
|
137
|
+
Array(fields).to_h { |field, source| [field.to_sym, read(source)] }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# A SET reads one thing, and `classified_source` said which: an argument by
|
|
141
|
+
# name, or a literal by value.
|
|
142
|
+
def classified(source)
|
|
143
|
+
return nil if source.nil?
|
|
144
|
+
|
|
145
|
+
case source[:kind].to_s
|
|
146
|
+
when "argument" then source[:name].to_sym
|
|
147
|
+
when "state" then StateRef.new(source[:name].to_sym)
|
|
148
|
+
else source[:value]
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# EVERY LITERAL FIELD ON THE WIRE, read back — one spelling, one reader.
|
|
153
|
+
#
|
|
154
|
+
# A where-clause value, a saga's argument bindings, an append binding, a
|
|
155
|
+
# limit: all of them ride Literal's self-describing form, so all of them
|
|
156
|
+
# come back through here. There were two readers (`read` and `unmark`)
|
|
157
|
+
# that disagreed about quoted strings and numbers, and which one a call
|
|
158
|
+
# site got was a coin toss the comments had to keep apologising for.
|
|
159
|
+
#
|
|
160
|
+
# AN OBJECT LITERAL is the one that bit. A saga leg binds `narrative: {
|
|
161
|
+
# text: "transfer out" }` — a value object's fields written inline — and
|
|
162
|
+
# `to_s` on a Hash used to be its inspect form, so it came back as text.
|
|
163
|
+
# Read as a string it reached the runtime as `"{:text=>\"transfer out\"}"`,
|
|
164
|
+
# coercion refused it, the debit leg was never delivered, and the whole
|
|
165
|
+
# settlement wire stopped: banking emitted TransferRequested five times and
|
|
166
|
+
# TransferDebited never. A whole-history replay gate caught what every other
|
|
167
|
+
# gate missed, because a saga that silently does nothing looks exactly like
|
|
168
|
+
# a saga with nothing to do.
|
|
169
|
+
def read(value) = Literal.read(value)
|
|
170
|
+
|
|
171
|
+
def where_clause(clause)
|
|
172
|
+
QuerySpecification::Common::WhereClause.new(
|
|
173
|
+
field: clause[:field], op: clause[:op].to_sym, value: read(clause[:value])
|
|
174
|
+
)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def order_by(declared)
|
|
178
|
+
return nil unless declared
|
|
179
|
+
|
|
180
|
+
QuerySpecification::Common::OrderBy.new(
|
|
181
|
+
field: declared[:field], direction: declared[:direction].to_sym
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def limit(declared)
|
|
186
|
+
return nil unless declared
|
|
187
|
+
|
|
188
|
+
QuerySpecification::Common::LimitSpec.new(value: read(declared[:value]))
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# EVERY OTHER SPECIFICATION OPTION, from one table.
|
|
192
|
+
#
|
|
193
|
+
# Each entry names the struct and which of its members carry a value that
|
|
194
|
+
# rode Literal's spelling rather than plain text. A
|
|
195
|
+
# ninth option is one row here and nothing else — the language already holds
|
|
196
|
+
# it, because it holds options as an open map rather than a field each.
|
|
197
|
+
OPTIONS = {
|
|
198
|
+
offset: [QuerySpecification::Common::OffsetSpec, %i[value]],
|
|
199
|
+
cursor: [QuerySpecification::Common::CursorSpec, %i[value]],
|
|
200
|
+
null_semantics: [QuerySpecification::Common::NullSemantics, []],
|
|
201
|
+
authorization: [QuerySpecification::Common::AuthorizationSpec, %i[policy tenant]],
|
|
202
|
+
inspection: [QuerySpecification::Common::InspectionSpec, []]
|
|
203
|
+
}.freeze
|
|
204
|
+
|
|
205
|
+
# `mode` and `policy` are read as symbols because the DSL declares them that
|
|
206
|
+
# way — `nulls :last`, `authorize :customer_access` — and `to_h` spells them
|
|
207
|
+
# with `to_s`, so the colon is not there to strip.
|
|
208
|
+
SYMBOLIC = %i[mode policy tenant].freeze
|
|
209
|
+
|
|
210
|
+
def option(name, declared)
|
|
211
|
+
return nil if declared.nil?
|
|
212
|
+
|
|
213
|
+
holder, marked = OPTIONS.fetch(name)
|
|
214
|
+
holder.new(**Hash(declared).to_h { |key, value| [key, option_value(key, value, marked)] })
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def option_value(key, value, marked)
|
|
218
|
+
return nil if value.nil?
|
|
219
|
+
return read(value) if marked.include?(key)
|
|
220
|
+
return value.to_sym if SYMBOLIC.include?(key)
|
|
221
|
+
|
|
222
|
+
value
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|