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,50 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
module Behaviour
|
|
4
|
+
# WHAT A HECKSAGON DOES — the lookups over its declared binds.
|
|
5
|
+
module Hecksagon
|
|
6
|
+
# Aggregate-specific bind wins when one was declared; otherwise
|
|
7
|
+
# falls back to a domain-level default (`b.aggregate.nil?` — see
|
|
8
|
+
# `HecksagonBuilder#method_missing`). Checked directly rather than
|
|
9
|
+
# via `aggregate_name`/`Naming.demodulise`, which would need its own
|
|
10
|
+
# nil-handling for the default row.
|
|
11
|
+
def bind_for(aggregate_name, verb)
|
|
12
|
+
@binds.find { |b| b.aggregate_name == aggregate_name.to_s && b.verb.to_s == verb.to_s } ||
|
|
13
|
+
@binds.find { |b| b.aggregate.nil? && b.verb.to_s == verb.to_s }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def binds_for(aggregate_name, verb)
|
|
17
|
+
specific = @binds.select { |b| b.aggregate_name == aggregate_name.to_s && b.verb.to_s == verb.to_s }
|
|
18
|
+
return specific if specific.any?
|
|
19
|
+
|
|
20
|
+
@binds.select { |b| b.aggregate.nil? && b.verb.to_s == verb.to_s }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# WHAT A WORLD DOES — settings lookup, with the adapter-specific
|
|
25
|
+
# entry falling back to the verb's own.
|
|
26
|
+
module World
|
|
27
|
+
def for_verb(verb) = @settings.fetch(verb.to_s, {})
|
|
28
|
+
|
|
29
|
+
# The generic `verb` entry (`persisted_by("Heki") do dir :default
|
|
30
|
+
# end`) only answers for the adapter it actually names — falling
|
|
31
|
+
# back to it unconditionally applies one adapter's settings to an
|
|
32
|
+
# unrelated one. Real, corpus-caught bug: a hecksagon binding two
|
|
33
|
+
# aggregates to two different adapters under the SAME verb (one to
|
|
34
|
+
# Heki, one to Memory) sent Memory's lookup down Heki's generic
|
|
35
|
+
# entry, then failed `check_settings` with "Memory does not
|
|
36
|
+
# declare :dir" — the generic entry's own `settings[:adapter]`
|
|
37
|
+
# names Heki, not Memory, so the fallback was never actually for
|
|
38
|
+
# this bind. `{}` is exactly right when nothing was configured for
|
|
39
|
+
# THIS adapter — Memory, which takes no values at all.
|
|
40
|
+
def for_binding(verb, adapter)
|
|
41
|
+
qualified = @settings["#{verb}:#{adapter.to_s.downcase}"]
|
|
42
|
+
return qualified if qualified
|
|
43
|
+
|
|
44
|
+
generic = for_verb(verb)
|
|
45
|
+
generic[:adapter].to_s.downcase == adapter.to_s.downcase ? generic : {}
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
module Behaviour
|
|
4
|
+
# WHAT A LIFECYCLE DOES. Its declared half is a field, a starting
|
|
5
|
+
# state and a transition list. Everything here READS that — which
|
|
6
|
+
# states exist, which transition a command takes, and how one
|
|
7
|
+
# declared transition expands into the several rows the emission
|
|
8
|
+
# carries when `from` names more than one source state.
|
|
9
|
+
module Lifecycle
|
|
10
|
+
def states
|
|
11
|
+
([default] + transitions.map { |_command, t| t.target }).uniq
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def transitions_for(command)
|
|
15
|
+
transitions.select { |name, _| name == command.to_s }.map { |_, t| t }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def target_for(command, current_state = nil)
|
|
19
|
+
match_transition(command, current_state)&.target
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# ONE DECLARED TRANSITION IS SEVERAL ROWS when `from` names more
|
|
25
|
+
# than one source state — the emission carries them flat, so the
|
|
26
|
+
# fan-out happens here rather than in whatever reads it.
|
|
27
|
+
def expand(command, transition)
|
|
28
|
+
sources = transition.from.nil? ? [nil] : Array(transition.from)
|
|
29
|
+
|
|
30
|
+
sources.map do |source|
|
|
31
|
+
{ command: command, to_state: transition.target, from_state: source }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def match_transition(command, current_state)
|
|
36
|
+
matches = transitions_for(command)
|
|
37
|
+
return nil if matches.empty?
|
|
38
|
+
return matches.first unless current_state
|
|
39
|
+
|
|
40
|
+
matches.find { |t| applies_from?(t, current_state) } || matches.first
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def applies_from?(transition, current)
|
|
44
|
+
return true unless transition.from
|
|
45
|
+
|
|
46
|
+
Array(transition.from).map(&:to_s).include?(current.to_s)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
module Behaviour
|
|
4
|
+
# WHAT A POLICY DOES. Its declared half is four plain fields; these
|
|
5
|
+
# are the readings taken off them.
|
|
6
|
+
module Policy
|
|
7
|
+
# The BLUEBOOK's name for this construct, asked the same way of a class
|
|
8
|
+
# that has crossed over and of an IR object that has not. Collapses into
|
|
9
|
+
# Construct when this one crosses.
|
|
10
|
+
def hecks_name = @name
|
|
11
|
+
|
|
12
|
+
def event_qualifier = Naming.qualifier(@on_event)
|
|
13
|
+
|
|
14
|
+
def event_name = Naming.unqualified(@on_event)
|
|
15
|
+
|
|
16
|
+
# WHETHER THIS POLICY FANS OUT — `for_each` names a query, and a
|
|
17
|
+
# non-empty one turns a single reaction into one dispatch per row
|
|
18
|
+
# the query answers. Read by the interpreter that runs the fan-out
|
|
19
|
+
# and by the property that checks it dispatched once per row.
|
|
20
|
+
def fans_out? = !@for_each.to_s.empty?
|
|
21
|
+
|
|
22
|
+
# WHETHER THIS POLICY IS GUARDED — a non-empty `where` decides
|
|
23
|
+
# whether the policy fires at all, read against the triggering
|
|
24
|
+
# event's own payload.
|
|
25
|
+
def guarded? = !@where.to_s.empty?
|
|
26
|
+
|
|
27
|
+
# THE FAN-OUT QUERY'S ROUTE, split the way the runtime runs it:
|
|
28
|
+
# `[query_domain, aggregate_name, query_name]`. The query runs
|
|
29
|
+
# against the triggering event's OWN domain unless `for_each`
|
|
30
|
+
# names one ("Domain::Aggregate.query"). Deliberately independent
|
|
31
|
+
# of `across`/`target_domain`, which name where `trigger` fires,
|
|
32
|
+
# not where the fan-out's own query runs.
|
|
33
|
+
def for_each_route(default_domain)
|
|
34
|
+
path, query_name = @for_each.to_s.split(".", 2)
|
|
35
|
+
domain, aggregate = path.to_s.include?("::") ? path.split("::", 2) : [default_domain.to_s, path]
|
|
36
|
+
[domain, aggregate, query_name]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# THE ARGUMENT NAME A FAN-OUT DISPATCH mints each matched row's id
|
|
40
|
+
# under used to be minted HERE, unconditionally, as `<aggregate>
|
|
41
|
+
# _id` — a real bug: a target command declared ON the very
|
|
42
|
+
# aggregate it self-references (`Account.Freeze`) is addressed by
|
|
43
|
+
# its OWN identity field's name, not a synthetic foreign key, and
|
|
44
|
+
# every such dispatch refused. That question is not this policy's
|
|
45
|
+
# to answer at all — it depends on the TARGET COMMAND'S OWN
|
|
46
|
+
# declared shape, not on the aggregate name alone — so it now
|
|
47
|
+
# lives on `Behaviour::Command#addressing_key_for`, asked of the
|
|
48
|
+
# resolved target command by `PolicyInterpreter#addressing_key_for`.
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
module Behaviour
|
|
4
|
+
# WHAT A PROCESS MANAGER DOES. Its declared half is the trigger,
|
|
5
|
+
# the states and the handlers; the compensation half — `saga` — is
|
|
6
|
+
# DERIVED from the handler that answers a refusal, not declared.
|
|
7
|
+
module ProcessManager
|
|
8
|
+
def hecks_name = @name
|
|
9
|
+
|
|
10
|
+
def handler_for(event) = @handlers.find { |h| h.event_type == event.to_s }
|
|
11
|
+
|
|
12
|
+
# WHETHER A STATE IS ONE THIS PROCEDURE DECLARES — asked of a value
|
|
13
|
+
# a real run left a saga instance holding (its live or rehydrated
|
|
14
|
+
# state), the way `Lifecycle#states` is asked of an aggregate's
|
|
15
|
+
# resting field. A rehydrated instance in a state no handler could
|
|
16
|
+
# have reached is corruption the durable round-trip introduced.
|
|
17
|
+
def declares_state?(state) = @states.map(&:to_s).include?(state.to_s)
|
|
18
|
+
|
|
19
|
+
def correlation_head = @correlates_by.to_s.split(".").first.to_sym
|
|
20
|
+
|
|
21
|
+
# The compensation half of a procedure, read off the handler that
|
|
22
|
+
# answers REFUSED.
|
|
23
|
+
#
|
|
24
|
+
# nil for a procedure with no answer to a refusal, which is a legitimate
|
|
25
|
+
# thing to be — a hiring pipeline cannot un-interview anybody.
|
|
26
|
+
def saga
|
|
27
|
+
leg = handler_for(Bluebook::ProcessManager::REFUSED)
|
|
28
|
+
return nil unless leg
|
|
29
|
+
|
|
30
|
+
Bluebook::Saga.new(trigger: Bluebook::ProcessManager::REFUSED, from_state: leg.from_state,
|
|
31
|
+
to_state: leg.to_state, reversals: leg.dispatches)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def saga? = !saga.nil?
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
module Behaviour
|
|
4
|
+
# WHAT A READ MODEL DOES. Its declared half is the gathered heads
|
|
5
|
+
# and the query shape; these are readings taken off them.
|
|
6
|
+
module ReadModel
|
|
7
|
+
def group_by_fields = @group_by.map { |row| row[:field].to_sym }
|
|
8
|
+
|
|
9
|
+
# `!!` rather than a bare `@count` — the DSL/reconstruction
|
|
10
|
+
# boundary (ReadModel#initialize) already normalises to
|
|
11
|
+
# `true`/`nil`, so this is belt and braces against a future
|
|
12
|
+
# caller constructing a ReadModel by hand with `count: false`.
|
|
13
|
+
def count? = !!@count
|
|
14
|
+
|
|
15
|
+
def query_name = Naming.snake(@name)
|
|
16
|
+
|
|
17
|
+
# WHICH GATHERED HEAD THE FILTERING APPLIES TO, so the read-model
|
|
18
|
+
# interpreter can ask this directly rather than re-deriving or
|
|
19
|
+
# re-checking it.
|
|
20
|
+
def filtered_head_name
|
|
21
|
+
return nil unless wheres.any? || order_by || limit || offset || authorization&.tenant ||
|
|
22
|
+
@group_by.any? || count? || @median_field
|
|
23
|
+
|
|
24
|
+
@aggregate_heads.find { |head| head[:many] }&.fetch(:as)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
module Behaviour
|
|
4
|
+
# THE BEHAVIOUR MORE THAN ONE CONSTRUCT SHARES.
|
|
5
|
+
#
|
|
6
|
+
# None of this is new duplication. `attribute(named)` was written
|
|
7
|
+
# out four separate times — on Aggregate, Entity, Command and
|
|
8
|
+
# PortOperation — and the identity derivation twice, byte for byte,
|
|
9
|
+
# for as long as those constructs have existed. Splitting holding
|
|
10
|
+
# from doing is what made them visible: the same method in four
|
|
11
|
+
# files reads as four methods, and the same method in four modules
|
|
12
|
+
# named `Behaviour::*` reads as one repeated.
|
|
13
|
+
#
|
|
14
|
+
# Written to work whether the construct is INCLUDED into (an
|
|
15
|
+
# ordinary object like Aggregate) or EXTENDED into (a class-shaped
|
|
16
|
+
# one like Command), because every one of them reads instance
|
|
17
|
+
# variables and nothing else.
|
|
18
|
+
|
|
19
|
+
# A CONSTRUCT WHOSE IDENTITY IS A JOIN OF DECLARED PATHS.
|
|
20
|
+
#
|
|
21
|
+
# THE PATHS, IN DECLARATION ORDER, because the identity IS their join.
|
|
22
|
+
# "number.value" says which field carries the identity ; several paths
|
|
23
|
+
# say the identity is made of several facts, which is what anything
|
|
24
|
+
# named beneath another thing needs. `identity_heads` are the attributes
|
|
25
|
+
# those paths start at — what every reader that looks up or coerces an
|
|
26
|
+
# attribute actually wants — and `identified_by` is the single head,
|
|
27
|
+
# offered ONLY when there is one path to have a head of. A composite has
|
|
28
|
+
# no single head, and answering with the first would be a guess ; the
|
|
29
|
+
# readers that need all of them ask for `identity_heads`.
|
|
30
|
+
module Identified
|
|
31
|
+
def derive_identity
|
|
32
|
+
@identity_paths = Array(@identified_by).map { |path| path.to_s }.reject(&:empty?)
|
|
33
|
+
@identity_heads = @identity_paths.map { |path| path.split(".").first.to_sym }.uniq
|
|
34
|
+
@identified_by = @identity_heads.size == 1 ? @identity_heads.first : nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# A CONSTRUCT THAT ANSWERS FOR ITS OWN DECLARATIONS BY NAME.
|
|
39
|
+
#
|
|
40
|
+
# Indexed once, since the declarations are final by the time the
|
|
41
|
+
# construct exists — every dispatch asks these finders by name, and a
|
|
42
|
+
# linear scan repeated on every call was doing work the declared shape
|
|
43
|
+
# had already settled at boot.
|
|
44
|
+
#
|
|
45
|
+
# `index_by_name` takes the collections to index rather than naming
|
|
46
|
+
# them, because WHICH collections a construct has is exactly the part
|
|
47
|
+
# that differs: an aggregate has five, an entity three, a command one.
|
|
48
|
+
module Indexed
|
|
49
|
+
# Keyed by SYMBOL — an attribute is asked for by its declared
|
|
50
|
+
# symbol name everywhere in the runtime.
|
|
51
|
+
def index_attributes(attributes)
|
|
52
|
+
@attributes_by_name = attributes.to_h { |held| [held.name, held] }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Keyed by STRING and by `hecks_name` — a command, query or value
|
|
56
|
+
# object is a construct whose Ruby `name` is something else
|
|
57
|
+
# entirely (a constant path, or nothing at all for an anonymous
|
|
58
|
+
# class), so the DECLARED name is the only one worth indexing.
|
|
59
|
+
def index_by_hecks_name(collection)
|
|
60
|
+
collection.to_h { |held| [held.hecks_name, held] }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def attribute(named) = @attributes_by_name[named.to_sym]
|
|
64
|
+
def command(named) = @commands_by_name[named.to_s]
|
|
65
|
+
def query(named) = @queries_by_name[named.to_s]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# A CONSTRUCT THAT OWNS WHAT IT DECLARES.
|
|
69
|
+
#
|
|
70
|
+
# Owner links are only ever read lazily — hecks_fqn at ask time,
|
|
71
|
+
# Reference#resolve at dispatch time — so the moment the construct
|
|
72
|
+
# is complete is the right stamping point: its declarations are
|
|
73
|
+
# final, and nothing outside needs to remember to stamp them.
|
|
74
|
+
module Owns
|
|
75
|
+
def stamp(*children)
|
|
76
|
+
children.flatten.each { |child| child.hecks_owner = self }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
module Behaviour
|
|
4
|
+
# WHAT A VALUE OBJECT DOES. EXTENDED, not included — a value object
|
|
5
|
+
# is a CLASS (`Class.new(self)`, one per declared shape), so its
|
|
6
|
+
# behaviour is singleton behaviour, and the holding half's `absorb`
|
|
7
|
+
# is what a generated constructor would be.
|
|
8
|
+
module ValueObject
|
|
9
|
+
# A one_of DECLARED but left empty used to be indistinguishable from no
|
|
10
|
+
# one_of at all — both are `members: []` — so the rule about it could
|
|
11
|
+
# only live in the builder. Recording the declaration lets the language
|
|
12
|
+
# judge it, the same way an empty attribute NAME survives into the IR
|
|
13
|
+
# and is judged there.
|
|
14
|
+
def closed_set? = @closed_set
|
|
15
|
+
|
|
16
|
+
def attribute(named) = attributes.find { |held| held.name == named.to_sym }
|
|
17
|
+
|
|
18
|
+
# A single-attribute value object (EmailAddress{address},
|
|
19
|
+
# CustomerNumber{value}) is a NAME for a scalar, not a genuine
|
|
20
|
+
# group — [[feedback_name_the_scalar_field]]. Four call sites
|
|
21
|
+
# already inline this exact check (`attributes.size == 1` /
|
|
22
|
+
# `attributes.first`) — `forms/field_shape.rb` (twice),
|
|
23
|
+
# `adapters/driven/sql_query_builder.rb`,
|
|
24
|
+
# `fuzzing/invalid_value_generator.rb` — kept as-is for now
|
|
25
|
+
# (not migrated to this reader), but any NEW caller should
|
|
26
|
+
# reach for this rather than add a fifth inline copy.
|
|
27
|
+
def sole_attribute
|
|
28
|
+
attributes.first if attributes.size == 1
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require_relative "behaviour/chapter"
|
|
2
|
+
require_relative "../ir"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Bluebook
|
|
6
|
+
# THE CHAPTER, and no longer the namespace it lives in.
|
|
7
|
+
#
|
|
8
|
+
# `Hecks::Bluebook` was briefly a CLASS, because dropping the
|
|
9
|
+
# old `IR::` segment would otherwise have produced
|
|
10
|
+
# `Bluebook::Bluebook` — a class shadowing its own enclosing module.
|
|
11
|
+
# Naming the construct what the code already called it everywhere
|
|
12
|
+
# (a chapter; this file was already chapter.rb) removes the clash
|
|
13
|
+
# at the source, so the namespace goes back to being a module.
|
|
14
|
+
#
|
|
15
|
+
# Measured before changing: `Bluebook` was used AS a class in five
|
|
16
|
+
# places and as a namespace in seventy-seven files.
|
|
17
|
+
class Chapter
|
|
18
|
+
include Construct
|
|
19
|
+
include Behaviour::Chapter
|
|
20
|
+
include Hecks::IR
|
|
21
|
+
|
|
22
|
+
# THE SCHEMA'S OWN VERSION — not a domain's `version:` (Banking's
|
|
23
|
+
# "v1", a business fact the AUTHOR chose), but the shape `to_h`
|
|
24
|
+
# itself emits. A consumer reading exported IR with no Ruby DSL to
|
|
25
|
+
# cross-check against (a build-time generator, a stored snapshot)
|
|
26
|
+
# needs to know WHICH shape it's holding before it can safely
|
|
27
|
+
# interpret any of the rest of it. Bump this when `to_h`'s own
|
|
28
|
+
# shape changes in a way a consumer would need to know about —
|
|
29
|
+
# not when a domain's own declarations change.
|
|
30
|
+
IR_VERSION = 1
|
|
31
|
+
|
|
32
|
+
emits_ir(
|
|
33
|
+
ir_version: -> { IR_VERSION },
|
|
34
|
+
name: :name,
|
|
35
|
+
version: :version,
|
|
36
|
+
vision: :vision,
|
|
37
|
+
classification: :classification,
|
|
38
|
+
aggregates: many(:aggregates),
|
|
39
|
+
read_models: many(:read_models),
|
|
40
|
+
policies: many(:policies),
|
|
41
|
+
process_managers: many(:process_managers),
|
|
42
|
+
attaches_to: :attaches_to,
|
|
43
|
+
canonical_form: -> { Expression::CanonicalForm.table }
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
attr_reader :name, :version, :vision, :aggregates, :policies, :process_managers,
|
|
47
|
+
:classification, :read_models, :ports, :formerly_known_as, :attaches_to
|
|
48
|
+
|
|
49
|
+
def initialize(name:, version: nil, vision: nil, aggregates: [], policies: [],
|
|
50
|
+
process_managers: [], classification: nil, read_models: [], formerly_known_as: nil,
|
|
51
|
+
attaches_to: [])
|
|
52
|
+
@policies = policies
|
|
53
|
+
@process_managers = process_managers
|
|
54
|
+
@name = name.to_s
|
|
55
|
+
@hecks_name = @name
|
|
56
|
+
@hecks_root = true
|
|
57
|
+
@version = version&.to_s
|
|
58
|
+
@vision = vision
|
|
59
|
+
@aggregates = aggregates
|
|
60
|
+
@read_models = read_models
|
|
61
|
+
@classification = classification&.to_s
|
|
62
|
+
@formerly_known_as = formerly_known_as&.to_s
|
|
63
|
+
@attaches_to = Array(attaches_to).map(&:to_s)
|
|
64
|
+
settle
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
require_relative "behaviour/command"
|
|
2
|
+
require_relative "../vocabulary"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Bluebook
|
|
6
|
+
Given = Struct.new(:description, :canonical, :predicate, keyword_init: true)
|
|
7
|
+
|
|
8
|
+
Mutation = Struct.new(:target, :op, :source, keyword_init: true) do
|
|
9
|
+
include Hecks::IR
|
|
10
|
+
include Behaviour::Mutation
|
|
11
|
+
|
|
12
|
+
# `sign:` — item #5 of the whole-project table-unification survey.
|
|
13
|
+
# `increment`/`decrement`'s own +1/-1 used to be re-derived from the
|
|
14
|
+
# op NAME by two independent Rust codegen scripts (rust/project/
|
|
15
|
+
# mutations.rb, rust/codegen/src/mutations.rs — a ternary on
|
|
16
|
+
# `op == "increment"` in each), even though the fact was already
|
|
17
|
+
# table-driven on the Ruby runtime side
|
|
18
|
+
# (Runtime::CommandRules::Arithmetic::MUTATION_OPS, held equal to
|
|
19
|
+
# Vocabulary::MutationOp by spec/vocabulary_conformance_spec.rb).
|
|
20
|
+
# Reads `Vocabulary::MutationOp` directly (plain data, no framework
|
|
21
|
+
# dependency — safe during parsing, same reason `RuleReference`'s
|
|
22
|
+
# own bootstrap concerns don't apply here) rather than requiring
|
|
23
|
+
# Runtime::CommandRules from the bluebook/IR layer, which would be a
|
|
24
|
+
# real layering inversion (runtime depends on bluebook, not the
|
|
25
|
+
# reverse). "" (not nil) for ops with no sign, matching every other
|
|
26
|
+
# optional IR text field's own absent-is-empty-string convention.
|
|
27
|
+
def self.sign_for(op)
|
|
28
|
+
Hecks::Vocabulary.rows("MutationOp").find { |row| row["name"] == op.to_s }&.fetch("sign", "") || ""
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
emits_ir(target: :target, op: :op, sign: -> { Mutation.sign_for(op) })
|
|
32
|
+
|
|
33
|
+
# THE ONE GENUINELY BRANCHING EMISSION in the model: an append (or a
|
|
34
|
+
# DELEGATE — `CommandBuilder#delegates_to`'s own comment gives the
|
|
35
|
+
# full reasoning for reusing this exact wire shape rather than
|
|
36
|
+
# inventing a parallel one) binds several fields at once and carries
|
|
37
|
+
# `fields:`, everything else carries a single `source:`. Declared
|
|
38
|
+
# emission covers the fixed head; `super` supplies it and this adds
|
|
39
|
+
# the tail, which is why a construct with a variable shape needs no
|
|
40
|
+
# new mixin API.
|
|
41
|
+
def to_h
|
|
42
|
+
return super.merge(fields: appended_fields) if [:append, :delegate].include?(op)
|
|
43
|
+
|
|
44
|
+
super.merge(source: classified_source)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# A command, as a RUBY CLASS.
|
|
49
|
+
#
|
|
50
|
+
# NOT nested as a constant, and that is a finding rather than a shortcut. A
|
|
51
|
+
# command and a value object may legitimately share a name inside one
|
|
52
|
+
# aggregate — the language does it six times, and means it: the command
|
|
53
|
+
# `Argument` is the verb that appends to the `arguments` list whose element
|
|
54
|
+
# type is the value object `Argument`, and `Plan` reads exactly that pairing.
|
|
55
|
+
# So `Bluebook::Command::Argument` cannot be both, and a single constant
|
|
56
|
+
# namespace cannot index a kind-ambiguous name. The same follows for
|
|
57
|
+
# `hecks_fqn` : `Bluebook::Command.Argument` names both, which is why the judge's
|
|
58
|
+
# ids only work per-category, each in its own repository. IDENTITY IS
|
|
59
|
+
# (KIND, FQN), not fqn.
|
|
60
|
+
#
|
|
61
|
+
# It is a declaration holder anyway, because that is where the EDGES live.
|
|
62
|
+
# `acts_on` answers with the owning construct itself — the Aggregate,
|
|
63
|
+
# or the entity holder for a piece's verb — rather than the name of one.
|
|
64
|
+
# The invocation door (`pizza.add_topping`) is the facade's business, a
|
|
65
|
+
# per-boot projection ; no verb method is defined here.
|
|
66
|
+
class Command
|
|
67
|
+
extend Construct
|
|
68
|
+
extend Hecks::IR
|
|
69
|
+
extend Behaviour::Command
|
|
70
|
+
|
|
71
|
+
emits_ir(
|
|
72
|
+
name: :hecks_name,
|
|
73
|
+
role: :role,
|
|
74
|
+
goal: :goal,
|
|
75
|
+
references: :references,
|
|
76
|
+
attributes: many(:attributes),
|
|
77
|
+
givens: -> { givens.map { |rule| { description: rule.description, canonical: rule.canonical } } },
|
|
78
|
+
ensures: -> { ensures.map { |rule| { description: rule.description, canonical: rule.canonical } } },
|
|
79
|
+
mutations: many(:mutations),
|
|
80
|
+
emits: :emits,
|
|
81
|
+
# THE LIFECYCLE STATE THIS COMMAND IS ADMISSIBLE FROM (S10, ADR
|
|
82
|
+
# 0025 — "lifecycle state becomes a command guard") — a GUARD,
|
|
83
|
+
# not a transition: `command "Debit", from: "open"` replaces
|
|
84
|
+
# `given("account is open") { status == "open" }`, checked
|
|
85
|
+
# against the owning construct's own lifecycle field the same
|
|
86
|
+
# way `admissible_transition` already checks a real state
|
|
87
|
+
# change, but names no target state and moves nothing. Nil for
|
|
88
|
+
# a command with no such guard — most commands.
|
|
89
|
+
from: -> { from },
|
|
90
|
+
provenance: :provenance
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
class << self
|
|
94
|
+
attr_reader :role, :goal, :attributes, :givens, :ensures, :mutations, :emits, :references,
|
|
95
|
+
:from, :provenance
|
|
96
|
+
|
|
97
|
+
def declare(name:, role: nil, goal: nil, attributes: [], givens: [], ensures: [],
|
|
98
|
+
mutations: [], emits: [], references: nil, from: nil, provenance: nil)
|
|
99
|
+
verb = Class.new(self)
|
|
100
|
+
verb.hecks_name = name.to_s
|
|
101
|
+
verb.absorb(role: role, goal: goal, attributes: attributes, givens: givens,
|
|
102
|
+
ensures: ensures, mutations: mutations, emits: emits, references: references&.to_s,
|
|
103
|
+
from: from, provenance: provenance)
|
|
104
|
+
verb
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def absorb(role:, goal:, attributes:, givens:, ensures:, mutations:, emits:, references:,
|
|
108
|
+
from: nil, provenance: nil)
|
|
109
|
+
@role = role
|
|
110
|
+
@goal = goal
|
|
111
|
+
@attributes = attributes
|
|
112
|
+
@givens = givens
|
|
113
|
+
@ensures = ensures
|
|
114
|
+
@mutations = mutations
|
|
115
|
+
@emits = emits
|
|
116
|
+
@references = references
|
|
117
|
+
@from = from
|
|
118
|
+
@provenance = provenance
|
|
119
|
+
settle
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require_relative "behaviour/domain_port"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Bluebook
|
|
5
|
+
# THE PRIMARY/DRIVING HALF OF HEXAGONAL ARCHITECTURE (Cockburn) — called
|
|
6
|
+
# BY an adapter living outside the bluebook entirely, never by the
|
|
7
|
+
# domain calling out. That is already `Hecks.port` (persistence,
|
|
8
|
+
# projection, extraction, loading) plus `Ports::*` : the secondary/
|
|
9
|
+
# driven half, unchanged by this.
|
|
10
|
+
#
|
|
11
|
+
# An operation carries no `given`/`ensures`/`then_set` — a port is the
|
|
12
|
+
# anti-corruption boundary that turns an external call into a fact in
|
|
13
|
+
# this domain's own vocabulary, not a second place business rules live.
|
|
14
|
+
# Those stay on whatever command a `policy` triggers in reaction to the
|
|
15
|
+
# event an operation emits.
|
|
16
|
+
class PortOperation
|
|
17
|
+
include Hecks::IR
|
|
18
|
+
include Behaviour::PortOperation
|
|
19
|
+
|
|
20
|
+
emits_ir(name: :hecks_name, attributes: many(:attributes), emits: :emits)
|
|
21
|
+
|
|
22
|
+
attr_reader :hecks_name, :attributes, :emits, :direction, :answers, :refuses, :to
|
|
23
|
+
|
|
24
|
+
# TWO DIRECTIONS THROUGH ONE DOOR.
|
|
25
|
+
#
|
|
26
|
+
# `:inbound` is what this class has always been — `tells`, spelled
|
|
27
|
+
# `operation` before it had a twin: an external fact arriving, turned
|
|
28
|
+
# into this domain's own event. It emits and that is all; there is no
|
|
29
|
+
# channel back to whoever called.
|
|
30
|
+
#
|
|
31
|
+
# `:outbound` is `asks` — the domain wanting something from outside
|
|
32
|
+
# and having to live with either answer. It names BOTH: `answers` for
|
|
33
|
+
# what the adapter came back with, `refuses` for what it said instead.
|
|
34
|
+
# Naming only the happy one would put the failure somewhere the model
|
|
35
|
+
# cannot see, which is the whole reason a boundary is worth modelling.
|
|
36
|
+
def initialize(name:, attributes: [], emits: [], direction: :inbound, answers: nil, refuses: nil, to: nil)
|
|
37
|
+
@hecks_name = name.to_s
|
|
38
|
+
@attributes = attributes
|
|
39
|
+
@emits = emits
|
|
40
|
+
@direction = direction.to_sym
|
|
41
|
+
@answers = answers
|
|
42
|
+
@refuses = refuses
|
|
43
|
+
@to = to
|
|
44
|
+
@attributes_by_name = attributes.to_h { |attribute| [attribute.name, attribute] }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def outbound? = @direction == :outbound
|
|
48
|
+
def inbound? = @direction == :inbound
|
|
49
|
+
|
|
50
|
+
# No root reference of its own — unlike a command, every attribute
|
|
51
|
+
# EQUALLY describes the payload, including whichever one identifies
|
|
52
|
+
# the record its emitted event belongs to. Kept only so
|
|
53
|
+
# CommandInterpreter::ArgumentGate's `reference_key` can ask for it
|
|
54
|
+
# without learning this isn't a command.
|
|
55
|
+
|
|
56
|
+
# `direction`/`answers`/`refuses` are deliberately OUTSIDE `emits_ir`'s
|
|
57
|
+
# declared shape and added here only for an outbound operation — an
|
|
58
|
+
# ordinary inbound one (`tells`, still spelled `operation` everywhere
|
|
59
|
+
# in the existing corpus) keeps the EXACT prior IR shape, byte for
|
|
60
|
+
# byte. Pizzas' `PaymentGateway` port is inbound-only and is checked
|
|
61
|
+
# against `hecks-parse`'s own Rust output for byte-identity
|
|
62
|
+
# (parser_parity_spec.rb) — the Rust side has no notion of `asks` yet,
|
|
63
|
+
# so an unconditional new key here would break that parity for a
|
|
64
|
+
# domain that never asked for the feature. Only a chapter that
|
|
65
|
+
# actually declares `asks` (this extraction's own QualityControl
|
|
66
|
+
# ledger, not yet in any Rust-parity corpus) pays for it.
|
|
67
|
+
# `to` — SAME "deliberately outside emits_ir, merged in only when
|
|
68
|
+
# present" treatment as direction/answers/refuses just above, and
|
|
69
|
+
# for the identical reason: an operation still spelled the old way
|
|
70
|
+
# (`reference_to` inside the block, shadow-parsing only — see
|
|
71
|
+
# reference_to_impl's own comment) or one that simply hasn't
|
|
72
|
+
# migrated yet keeps the EXACT prior IR shape, byte for byte,
|
|
73
|
+
# instead of an unconditional new key breaking parser_parity_spec
|
|
74
|
+
# for every domain that never touched this.
|
|
75
|
+
def to_h
|
|
76
|
+
shape = super
|
|
77
|
+
shape = shape.merge(direction: @direction.to_s, answers: @answers, refuses: @refuses) unless inbound?
|
|
78
|
+
shape = shape.merge(to: @to) if @to
|
|
79
|
+
shape
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# A named group of operations an aggregate (or, later, a chapter)
|
|
84
|
+
# exposes to whatever adapter calls in — the contract that today lives
|
|
85
|
+
# only as a bare `verb`/`signal` pair in a standalone `.port` file.
|
|
86
|
+
# Superseding those is the goal ; for now they keep working untouched,
|
|
87
|
+
# and this is additive.
|
|
88
|
+
class DomainPort
|
|
89
|
+
include Hecks::IR
|
|
90
|
+
include Behaviour::DomainPort
|
|
91
|
+
|
|
92
|
+
emits_ir(name: :name, operations: many(:operations))
|
|
93
|
+
|
|
94
|
+
attr_reader :name, :operations
|
|
95
|
+
|
|
96
|
+
def initialize(name:, operations: [])
|
|
97
|
+
@name = name.to_s
|
|
98
|
+
@operations = operations
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|