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,413 @@
|
|
|
1
|
+
require_relative "interpreting"
|
|
2
|
+
require_relative "command_interpreter/argument_gate"
|
|
3
|
+
require_relative "command_interpreter/mutation_applier"
|
|
4
|
+
require_relative "../rendering"
|
|
5
|
+
require_relative "errors"
|
|
6
|
+
require_relative "identity"
|
|
7
|
+
require_relative "dependency_planning"
|
|
8
|
+
require_relative "../ports/persistence/execution"
|
|
9
|
+
require_relative "instance"
|
|
10
|
+
require_relative "refusal_wording"
|
|
11
|
+
require_relative "entity_element"
|
|
12
|
+
|
|
13
|
+
module Hecks
|
|
14
|
+
module Runtime
|
|
15
|
+
# The dispatch pipeline for a command on an aggregate head. The payload
|
|
16
|
+
# gate lives in command_interpreter/argument_gate.rb, the mutation walk
|
|
17
|
+
# in command_interpreter/mutation_applier.rb; what stays here is the
|
|
18
|
+
# order of the steps and how a record is addressed.
|
|
19
|
+
class CommandInterpreter
|
|
20
|
+
include Interpreting
|
|
21
|
+
include ArgumentGate
|
|
22
|
+
include MutationApplier
|
|
23
|
+
|
|
24
|
+
attr_reader :registry
|
|
25
|
+
|
|
26
|
+
# THE DECLARED ORDER, HAND-TYPED — mirrors Vocabulary::AggregateDispatchOrder
|
|
27
|
+
# (language/bluebook/vocabulary.bluebook:188-205), held equal to it by
|
|
28
|
+
# spec/vocabulary_conformance_spec.rb the same way every other vocabulary
|
|
29
|
+
# in that file is (RefusalWording::TEMPLATES, CommandRules::MUTATION_OPS,
|
|
30
|
+
# ...) rather than read live off the meta-domain at every dispatch —
|
|
31
|
+
# Runtime::RefusalWording's own doc comment gives the same reason.
|
|
32
|
+
DISPATCH_ORDER = Hecks::Vocabulary.symbols("AggregateDispatchOrder")
|
|
33
|
+
|
|
34
|
+
# EVERY CROSS-STEP LOCAL `call` used to thread through its own literal
|
|
35
|
+
# sequence, held in one place now that the sequence is data-driven —
|
|
36
|
+
# `result` and `transition`/`old_state` default to nil until the step
|
|
37
|
+
# that sets them runs, same as they were unset locals before that point.
|
|
38
|
+
Context = Struct.new(:domain, :aggregate, :command, :args, :repository, :instance, :transition, :old_state,
|
|
39
|
+
:result, :correlation, :route, :plan, :strategy, :persistence_outcome, :delegated_events,
|
|
40
|
+
:dry_run)
|
|
41
|
+
|
|
42
|
+
def initialize(registry, rules:)
|
|
43
|
+
@registry = registry
|
|
44
|
+
@rules = rules
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# `dry_run:` — Dispatcher#dry_run?'s own entry point. Every step up
|
|
48
|
+
# through enforce_ensures/enforce_invariants runs exactly as a real
|
|
49
|
+
# dispatch would (givens checked, mutations applied to `ctx.instance`
|
|
50
|
+
# in memory); `step_save`/`step_emit` are the only two that read this
|
|
51
|
+
# flag, each skipping its own real work — see their own comments.
|
|
52
|
+
def call(domain, aggregate, command, args, correlation = nil, route: nil, dry_run: false)
|
|
53
|
+
ctx = Context.new(domain, aggregate, command, args)
|
|
54
|
+
ctx.correlation = correlation
|
|
55
|
+
ctx.route = route
|
|
56
|
+
ctx.dry_run = dry_run
|
|
57
|
+
ctx.plan = DependencyPlanning::Analyzer.call(aggregate: aggregate, command: command)
|
|
58
|
+
run_dispatch_order(DISPATCH_ORDER, ctx)
|
|
59
|
+
[ctx.instance, ctx.result, ctx.plan, ctx.persistence_outcome]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def step_refuse_unknown_arguments(ctx)
|
|
65
|
+
step(:refuse_unknown_arguments) { refuse_unknown_arguments(ctx.domain, ctx.aggregate, ctx.command, ctx.args) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def step_refuse_absent_arguments(ctx)
|
|
69
|
+
step(:refuse_absent_arguments) { refuse_absent_arguments(ctx.command, ctx.args) }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def step_normalize_args(ctx)
|
|
73
|
+
ctx.args = step(:normalize_args) { normalize_args(ctx.aggregate, ctx.command, ctx.args) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def step_refuse_role_mismatch(ctx)
|
|
77
|
+
step(:refuse_role_mismatch) { @rules.refuse_role_mismatch(ctx.command, ctx.domain) }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def step_resolve_references(ctx)
|
|
81
|
+
step(:resolve_references) { @rules.resolve_references(ctx.domain, ctx.command, ctx.args) }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def step_hydrate(ctx)
|
|
85
|
+
ctx.repository = @registry.repository(ctx.domain, ctx.aggregate)
|
|
86
|
+
ctx.strategy = ctx.plan.strategy_for(capabilities: ctx.repository.capabilities)
|
|
87
|
+
ctx.instance = step(:hydrate) {
|
|
88
|
+
if ctx.plan.complete_state? && ctx.plan.state_independent?
|
|
89
|
+
hydrate_complete_state(ctx.repository, ctx.aggregate, ctx.command, ctx.args, ctx.route, ctx.strategy)
|
|
90
|
+
elsif ctx.plan.complete_state?
|
|
91
|
+
hydrate_prior_or_initial(ctx.repository, ctx.aggregate, ctx.command, ctx.args, ctx.route)
|
|
92
|
+
elsif legacy_implicit_creation?(ctx)
|
|
93
|
+
hydrate_legacy_creation(ctx.repository, ctx.aggregate, ctx.command, ctx.args)
|
|
94
|
+
else
|
|
95
|
+
hydrate_existing(ctx.repository, ctx.aggregate, ctx.command, ctx.args, ctx.route)
|
|
96
|
+
end
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def step_enforce_givens(ctx)
|
|
101
|
+
step(:enforce_givens) {
|
|
102
|
+
@rules.enforce_givens(ctx.instance, ctx.command, ctx.args, domain: ctx.domain,
|
|
103
|
+
declaring: ctx.aggregate, parent: ctx.instance)
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def step_admissible_transition(ctx)
|
|
108
|
+
ctx.transition = step(:admissible_transition) { @rules.admissible_transition(ctx.aggregate, ctx.command, ctx.instance) }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def step_assign_creation_attributes(ctx)
|
|
112
|
+
return unless legacy_implicit_creation?(ctx)
|
|
113
|
+
|
|
114
|
+
step(:assign_creation_attributes) { assign_creation_attributes(ctx.instance, ctx.aggregate, ctx.command, ctx.args) }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def step_apply_mutations(ctx)
|
|
118
|
+
# The state as the givens saw it — what `old` names inside an
|
|
119
|
+
# ensures. A shallow dup suffices: mutations REPLACE fields (set,
|
|
120
|
+
# arithmetic via Value#with, append builds a new array), never
|
|
121
|
+
# edit a held value in place.
|
|
122
|
+
ctx.old_state = ctx.instance.state.dup unless ctx.command.ensures.empty?
|
|
123
|
+
step(:apply_mutations) {
|
|
124
|
+
ctx.command.mutations.each { |mutation|
|
|
125
|
+
apply(ctx.instance, ctx.aggregate, mutation, ctx.args)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def step_advance_lifecycle(ctx)
|
|
131
|
+
return unless ctx.transition
|
|
132
|
+
|
|
133
|
+
step(:advance_lifecycle) { ctx.instance[ctx.aggregate.lifecycle.field] = ctx.transition.target }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# THE SYNCHRONOUS COUSIN OF A POLICY'S OWN `trigger` — see
|
|
137
|
+
# `CommandBuilder#delegates_to`'s own comment for the full reasoning.
|
|
138
|
+
# Runs AFTER this command's own mutations/lifecycle (so a delegating
|
|
139
|
+
# command could in principle still guard with its own `given`s first,
|
|
140
|
+
# though the real use in `domain/chess` declares none) and BEFORE
|
|
141
|
+
# `enforce_ensures`/`enforce_invariants`/`save`, so a refusal here
|
|
142
|
+
# raises a real, unrescued exception and NOTHING from either side —
|
|
143
|
+
# this command's own state, the target element's — has been saved
|
|
144
|
+
# yet. `ctx.instance` is the SAME in-memory record `step_hydrate`
|
|
145
|
+
# loaded and `step_save` will persist; `EntityElement.locate_chain`
|
|
146
|
+
# mutates it in place exactly the way `EntityInterpreter`'s own
|
|
147
|
+
# `step_locate_element`/`step_apply_mutations` mutate their OWN
|
|
148
|
+
# freshly-loaded copy — the only difference is WHICH already-in-
|
|
149
|
+
# memory record gets handed in.
|
|
150
|
+
def step_delegate_to_entity(ctx)
|
|
151
|
+
delegation = ctx.command.mutations.find { |mutation| mutation.op == :delegate }
|
|
152
|
+
return unless delegation
|
|
153
|
+
|
|
154
|
+
step(:delegate_to_entity) {
|
|
155
|
+
entity_name, _dot, command_name = delegation.target.to_s.rpartition(".")
|
|
156
|
+
entity = ctx.aggregate.entities.find { |e| e.hecks_name == entity_name } ||
|
|
157
|
+
raise(WiringError, "#{ctx.command.hecks_name} delegates_to #{entity_name}." \
|
|
158
|
+
"#{command_name}, but #{ctx.aggregate.hecks_name} has no " \
|
|
159
|
+
"entity named #{entity_name.inspect}")
|
|
160
|
+
target_command = entity.command(command_name) ||
|
|
161
|
+
raise(WiringError, "#{ctx.command.hecks_name} delegates_to " \
|
|
162
|
+
"#{entity_name}.#{command_name}, which " \
|
|
163
|
+
"#{entity_name} declares no such command")
|
|
164
|
+
|
|
165
|
+
# `with:` REMAPS, it does not ENUMERATE — starting from a copy
|
|
166
|
+
# of THIS command's own already-resolved args (`ctx.args`) and
|
|
167
|
+
# overlaying the explicit mapping on top means ambient context
|
|
168
|
+
# the caller never had to think about (the aggregate's own
|
|
169
|
+
# identity, addressed the ordinary way to reach `MoveKnight` at
|
|
170
|
+
# all) flows through to the target untouched, the same as it
|
|
171
|
+
# always would for a caller dispatching the entity command
|
|
172
|
+
# directly. Confirmed necessary, not a defensive guess: a real
|
|
173
|
+
# downstream domain's own AdvancePly-on-Moved policy silently
|
|
174
|
+
# failed to re-locate its OWN aggregate (`reaction_log`: "no
|
|
175
|
+
# Game with label.value ..."), because the emitted event's
|
|
176
|
+
# payload — built from `target_args` alone — never carried
|
|
177
|
+
# `label` at all when `with:` named only `id`/`to`.
|
|
178
|
+
target_args = ctx.args.merge(
|
|
179
|
+
delegation.source.to_h { |target_key, source_key| [target_key.to_sym, ctx.args[source_key]] }
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
element = EntityElement.locate_chain(ctx.aggregate, [entity], ctx.instance, target_args, command_name)
|
|
183
|
+
view = Instance.new(aggregate: entity, id: EntityElement.element_identity(entity, element).to_s, state: element)
|
|
184
|
+
|
|
185
|
+
@rules.enforce_givens(view, target_command, target_args, domain: ctx.domain, declaring: entity, parent: ctx.instance)
|
|
186
|
+
transition = @rules.admissible_transition(entity, target_command, view)
|
|
187
|
+
|
|
188
|
+
old_element = target_command.ensures.empty? ? nil : element.dup
|
|
189
|
+
target_command.mutations.each { |mutation|
|
|
190
|
+
EntityElement.apply_to_element(@rules, ctx.aggregate, entity, element, mutation, target_args)
|
|
191
|
+
}
|
|
192
|
+
element[entity.lifecycle.field] = transition.target if transition
|
|
193
|
+
|
|
194
|
+
settled = Instance.new(aggregate: entity, id: view.id, state: element)
|
|
195
|
+
@rules.enforce_ensures(settled, target_command, target_args, old: old_element, domain: ctx.domain, parent: ctx.instance)
|
|
196
|
+
|
|
197
|
+
ctx.delegated_events = @rules.emit(target_command, ctx.domain, ctx.aggregate, ctx.instance, target_args, ctx.repository)
|
|
198
|
+
}
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def step_enforce_ensures(ctx)
|
|
202
|
+
step(:enforce_ensures) {
|
|
203
|
+
@rules.enforce_ensures(ctx.instance, ctx.command, ctx.args, old: ctx.old_state,
|
|
204
|
+
domain: ctx.domain, parent: ctx.instance)
|
|
205
|
+
}
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def step_enforce_invariants(ctx)
|
|
209
|
+
step(:enforce_invariants) { @rules.enforce_invariants(ctx.instance, ctx.aggregate, domain: ctx.domain) }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# `dry_run:` skips this — see Dispatcher#dry_run?'s own comment. The
|
|
213
|
+
# same conditional-skip shape `step_assign_creation_attributes`
|
|
214
|
+
# already has (`return unless ctx.command.creates?`), not a new
|
|
215
|
+
# pattern: a step that does not apply this time traces nothing,
|
|
216
|
+
# rather than a caller having to branch around it.
|
|
217
|
+
def step_save(ctx)
|
|
218
|
+
return if ctx.dry_run
|
|
219
|
+
|
|
220
|
+
step(:save) do
|
|
221
|
+
@rules.resolve_state_references(ctx.domain, ctx.aggregate, ctx.instance.state)
|
|
222
|
+
ctx.persistence_outcome = if ctx.strategy == DependencyPlanning::ATOMIC_PUT
|
|
223
|
+
# A SECOND CREATION IS NOT A FRESH ONE — see
|
|
224
|
+
# hydrate_legacy_creation's own comment; the
|
|
225
|
+
# same refusal, on the same terms, for the
|
|
226
|
+
# complete-state path. `insert_only:` asks the
|
|
227
|
+
# ADAPTER to decide and refuse ATOMICALLY
|
|
228
|
+
# (never writing a `creates?` command over an
|
|
229
|
+
# identity that already names a record) rather
|
|
230
|
+
# than this interpreter reading the record
|
|
231
|
+
# first to check — a `repository.find` before
|
|
232
|
+
# every atomic_put would be exactly the read
|
|
233
|
+
# this strategy exists to skip.
|
|
234
|
+
ctx.repository.atomic_put(ctx.instance, insert_only: ctx.command.creates?)
|
|
235
|
+
else
|
|
236
|
+
ctx.repository.save(ctx.instance)
|
|
237
|
+
Ports::Persistence::Outcome.new(status: :saved, instance: ctx.instance)
|
|
238
|
+
end
|
|
239
|
+
if ctx.persistence_outcome.status == :conflicted
|
|
240
|
+
raise(AlreadyExists, RefusalWording.render("AlreadyExists", "creating_duplicate",
|
|
241
|
+
command: ctx.command.hecks_name, aggregate: ctx.aggregate.hecks_name,
|
|
242
|
+
identity: identity_reading(ctx.aggregate),
|
|
243
|
+
offered: Rendering.describe(ctx.instance.id)))
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# A DELEGATING COMMAND EMITS NOTHING OF ITS OWN (`CommandBuilder#build`'s
|
|
249
|
+
# own guard refuses declaring `emits` alongside `delegates_to`) — its
|
|
250
|
+
# result IS whatever `step_delegate_to_entity` already collected from
|
|
251
|
+
# the target entity command's own `emits`, not a second, empty call
|
|
252
|
+
# into `@rules.emit` for a command with no announced events at all.
|
|
253
|
+
#
|
|
254
|
+
# `dry_run:` skips this too, same reasoning as `step_save` — nothing
|
|
255
|
+
# was committed, so `ctx.result` stays nil and `Dispatcher#dry_run?`
|
|
256
|
+
# never reads it (its own return value is just whether this raised).
|
|
257
|
+
def step_emit(ctx)
|
|
258
|
+
return if ctx.dry_run
|
|
259
|
+
|
|
260
|
+
ctx.result = step(:emit) {
|
|
261
|
+
# `ctx.delegated_events` is only ever set by `step_delegate_to_entity`,
|
|
262
|
+
# and only when this command carries a `:delegate` mutation — an
|
|
263
|
+
# empty Array (the target genuinely emitted nothing) is still
|
|
264
|
+
# truthy in Ruby, so this reads correctly either way.
|
|
265
|
+
next ctx.delegated_events if ctx.delegated_events
|
|
266
|
+
|
|
267
|
+
@rules.emit(ctx.command, ctx.domain, ctx.aggregate, ctx.instance, ctx.args, ctx.repository, ctx.correlation)
|
|
268
|
+
}
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def hydrate_existing(repository, aggregate, command, args, route = nil)
|
|
272
|
+
if route
|
|
273
|
+
found = repository.find(route.aggregate) ||
|
|
274
|
+
raise(NotFound, RefusalWording.render("NotFound", "record_missing",
|
|
275
|
+
aggregate: aggregate.hecks_name,
|
|
276
|
+
identity: identity_reading(aggregate),
|
|
277
|
+
offered: Rendering.describe(route.aggregate)))
|
|
278
|
+
return found.dup
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
id = identity_of(aggregate, args) ||
|
|
282
|
+
identity_from(aggregate, args, :id) ||
|
|
283
|
+
identity_from(aggregate, args, reference_key(command)) ||
|
|
284
|
+
raise(NotFound, RefusalWording.render("NotFound", "acting_no_identity",
|
|
285
|
+
command: command.hecks_name, aggregate: aggregate.hecks_name,
|
|
286
|
+
identity: identity_reading(aggregate)))
|
|
287
|
+
found = repository.find(id) ||
|
|
288
|
+
raise(NotFound, RefusalWording.render("NotFound", "record_missing",
|
|
289
|
+
aggregate: aggregate.hecks_name,
|
|
290
|
+
identity: identity_reading(aggregate),
|
|
291
|
+
offered: Rendering.describe(id)))
|
|
292
|
+
found.dup
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Transitional compatibility for live source that has not yet acquired
|
|
296
|
+
# explicit effects. It is deliberately isolated from the normal routing
|
|
297
|
+
# and planning path so `reference_to` no longer chooses how a migrated
|
|
298
|
+
# command hydrates or persists. Wave 8 removes this after the inventory is
|
|
299
|
+
# empty; frozen eras retain their own shadow parser.
|
|
300
|
+
#
|
|
301
|
+
# `ctx.plan.complete_state?` already claimed every command whose plan is
|
|
302
|
+
# fully resolved in the two branches above `step_hydrate` tries first, so
|
|
303
|
+
# reaching this check already means the plan is incomplete — an
|
|
304
|
+
# un-migrated command still routes here on `creates?` alone, the same as
|
|
305
|
+
# the old `hydrate` did, regardless of whether it happens to have any
|
|
306
|
+
# mutations (`write_set`). Requiring an EMPTY write_set here refused
|
|
307
|
+
# every un-migrated creating command that sets even one field.
|
|
308
|
+
def legacy_implicit_creation?(ctx)
|
|
309
|
+
ctx.route.nil? && ctx.command.creates?
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def hydrate_legacy_creation(repository, aggregate, command, args)
|
|
313
|
+
id = identity_of(aggregate, args) ||
|
|
314
|
+
raise(NotFound, RefusalWording.render("NotFound", "creating_no_identity",
|
|
315
|
+
command: command.hecks_name, aggregate: aggregate.hecks_name,
|
|
316
|
+
identity: identity_reading(aggregate)))
|
|
317
|
+
if repository.find(id)
|
|
318
|
+
raise(AlreadyExists, RefusalWording.render("AlreadyExists", "creating_duplicate",
|
|
319
|
+
command: command.hecks_name, aggregate: aggregate.hecks_name,
|
|
320
|
+
identity: identity_reading(aggregate),
|
|
321
|
+
offered: Rendering.describe(id)))
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
Instance.new(aggregate: aggregate, id: id)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def hydrate_complete_state(repository, aggregate, command, args, route, strategy)
|
|
328
|
+
derived = identity_of(aggregate, args)
|
|
329
|
+
if route && derived && route.aggregate.to_s != derived.to_s
|
|
330
|
+
raise TypeMismatch,
|
|
331
|
+
"#{command.hecks_name} routes to #{route.aggregate.inspect}, but its identity facts name #{derived.inspect}"
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
id = route&.aggregate || derived ||
|
|
335
|
+
raise(NotFound, RefusalWording.render("NotFound", "creating_no_identity",
|
|
336
|
+
command: command.hecks_name,
|
|
337
|
+
aggregate: aggregate.hecks_name,
|
|
338
|
+
identity: identity_reading(aggregate)))
|
|
339
|
+
|
|
340
|
+
# A SECOND CREATION IS NOT A FRESH ONE — see hydrate_legacy_creation's
|
|
341
|
+
# own comment; the same refusal, on the same terms, for the
|
|
342
|
+
# complete-state path. ONLY when `strategy` will NOT be ATOMIC_PUT:
|
|
343
|
+
# an atomic-put-capable adapter enforces this itself, atomically,
|
|
344
|
+
# via `insert_only:` in step_save (no read here, no race with the
|
|
345
|
+
# write) — but `strategy_for` already fell back to a plain `save`
|
|
346
|
+
# for an adapter with no atomic_put capability at all (Heki, today),
|
|
347
|
+
# and a plain `save` never refuses an overwrite on its own. Skipping
|
|
348
|
+
# this check for THAT case silently dropped the refusal instead of
|
|
349
|
+
# deferring it — the very "no such call — no such check" gap Wave
|
|
350
|
+
# 8 exists to close everywhere else.
|
|
351
|
+
if command.creates? && strategy != DependencyPlanning::ATOMIC_PUT && repository.find(id)
|
|
352
|
+
raise(AlreadyExists, RefusalWording.render("AlreadyExists", "creating_duplicate",
|
|
353
|
+
command: command.hecks_name, aggregate: aggregate.hecks_name,
|
|
354
|
+
identity: identity_reading(aggregate),
|
|
355
|
+
offered: Rendering.describe(id)))
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
Instance.new(aggregate: aggregate, id: id)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# A complete command may still depend on prior state: lifecycle guards
|
|
362
|
+
# are the common case. Read once so an existing record is checked against
|
|
363
|
+
# its real state; when no record exists, the aggregate's declared defaults
|
|
364
|
+
# are the prior state. Completeness, not a reference marker, proves that
|
|
365
|
+
# initializing that missing record is safe.
|
|
366
|
+
def hydrate_prior_or_initial(repository, aggregate, command, args, route)
|
|
367
|
+
derived = identity_of(aggregate, args)
|
|
368
|
+
if route && derived && route.aggregate.to_s != derived.to_s
|
|
369
|
+
raise TypeMismatch,
|
|
370
|
+
"#{command.hecks_name} routes to #{route.aggregate.inspect}, but its identity facts name #{derived.inspect}"
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
id = route&.aggregate || derived ||
|
|
374
|
+
raise(NotFound, RefusalWording.render("NotFound", "creating_no_identity",
|
|
375
|
+
command: command.hecks_name,
|
|
376
|
+
aggregate: aggregate.hecks_name,
|
|
377
|
+
identity: identity_reading(aggregate)))
|
|
378
|
+
found = repository.find(id)
|
|
379
|
+
|
|
380
|
+
# A SECOND CREATION IS NOT A FRESH ONE — see hydrate_legacy_
|
|
381
|
+
# creation's own comment; the same refusal, on the same terms, for
|
|
382
|
+
# a complete-but-state-dependent command (one with a `given`
|
|
383
|
+
# reading its own prior state, which is what routes here instead
|
|
384
|
+
# of hydrate_complete_state). Gated on `command.creates?`: a
|
|
385
|
+
# NON-creating command's own complete payload legitimately means
|
|
386
|
+
# "act on whatever this identity already holds" (`found` IS the
|
|
387
|
+
# prior state this branch exists to supply), so only a genuine
|
|
388
|
+
# creation reusing an already-occupied identity is a duplicate.
|
|
389
|
+
# `SafeDepositBox.Rent` is exactly this shape — `given("box is
|
|
390
|
+
# vacant")` makes it state-dependent, so a second Rent used to
|
|
391
|
+
# silently hydrate the existing box as "prior state" and refuse
|
|
392
|
+
# for the wrong reason (not vacant) instead of the right one
|
|
393
|
+
# (already exists).
|
|
394
|
+
if found && command.creates?
|
|
395
|
+
raise(AlreadyExists, RefusalWording.render("AlreadyExists", "creating_duplicate",
|
|
396
|
+
command: command.hecks_name, aggregate: aggregate.hecks_name,
|
|
397
|
+
identity: identity_reading(aggregate),
|
|
398
|
+
offered: Rendering.describe(id)))
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
found ? found.dup : Instance.new(aggregate: aggregate, id: id)
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# THE JOIN, THE DIG, AND THE READING — all shared with `EntityInterpreter`
|
|
405
|
+
# now, in `Runtime::Identity`, rather than kept as two copies that could
|
|
406
|
+
# only ever drift. See that module for the reasoning ; these three stay
|
|
407
|
+
# here, at the old names, purely so nothing below has to change.
|
|
408
|
+
def identity_of(aggregate, args) = Identity.of(aggregate, args)
|
|
409
|
+
def identity_from(aggregate, args, key) = Identity.from(aggregate, args, key)
|
|
410
|
+
def identity_reading(construct) = Identity.reading(construct)
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
end
|