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,154 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "handle"
|
|
3
|
+
require_relative "command_request"
|
|
4
|
+
require_relative "../naming"
|
|
5
|
+
require_relative "../runtime/errors"
|
|
6
|
+
require_relative "../runtime/value"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module Facade
|
|
10
|
+
# THE JSON DOOR — WHERE Facade MEETS BODY-IN/BODY-OUT CALLERS.
|
|
11
|
+
#
|
|
12
|
+
# `Handle`/`Surface` are Ruby sugar over the dispatcher for a Ruby caller
|
|
13
|
+
# holding real objects — a symbol verb name, a `**kwargs` payload, a
|
|
14
|
+
# `Handle` back in hand. A REST-ish JSON API is a caller holding STRINGS
|
|
15
|
+
# instead: a URL segment naming a collection, a URL segment naming a
|
|
16
|
+
# record or a verb, a parsed request body whose every key arrived as a
|
|
17
|
+
# String because that is all JSON ever gives. Every app that wants to put
|
|
18
|
+
# a JSON API in front of a booted domain has to do that translation —
|
|
19
|
+
# name to class, string to symbol, nested `Runtime::Value` back to plain
|
|
20
|
+
# data — and one sibling app had already hand-written it once, bespoke,
|
|
21
|
+
# against its own routes, before this existed. This is that translation
|
|
22
|
+
# pulled out, generic, reading the SAME IR the rest of the facade already
|
|
23
|
+
# reads rather than re-deriving "how do I find an aggregate by name".
|
|
24
|
+
#
|
|
25
|
+
# NO HTTP LIVES HERE. Same discipline `Router` and `Surface` already
|
|
26
|
+
# hold: this module never sees a request object, never picks a status
|
|
27
|
+
# code, never calls `halt`. Every method here takes plain Ruby values in
|
|
28
|
+
# — a raw JSON String is the one exception, see `.parse` below, every
|
|
29
|
+
# other input is already-parsed data — and returns plain Ruby values
|
|
30
|
+
# out, or raises. Turning a raw request body into that input, and
|
|
31
|
+
# turning a raised exception into an HTTP status, both stay the calling
|
|
32
|
+
# app's job, exactly the way they already are for `Router#dispatch`.
|
|
33
|
+
#
|
|
34
|
+
# EVERY "THAT DOESN'T EXIST" CASE RAISES `Runtime::NotFound` — THE SAME
|
|
35
|
+
# CLASS EVERY TIME, NOT A NEW ONE PER CALLER. `Runtime::NotFound` already
|
|
36
|
+
# sits in `Runtime::DOMAIN_REFUSALS` (runtime/errors.rb) — the family a
|
|
37
|
+
# booted app already has, or trivially can have, ONE generic `error`
|
|
38
|
+
# handler for, mapping the whole family to a status code without a
|
|
39
|
+
# special case per refusal. A bespoke `JsonDoor::CollectionNotFound` (or
|
|
40
|
+
# three of those, one per flavor of "not found") would just force every
|
|
41
|
+
# app that adopts this door to widen its rescue clause to match it — the
|
|
42
|
+
# opposite of what a shared refusal family is for. So "no such
|
|
43
|
+
# aggregate", "no creating command", "no such command", and "no record
|
|
44
|
+
# with that id" all raise the one class, distinguished only by message.
|
|
45
|
+
module JsonDoor
|
|
46
|
+
module_function
|
|
47
|
+
|
|
48
|
+
# "Banking", "Customer" -> the `Banking::Customer` class `.find` /
|
|
49
|
+
# `.create_...` / etc already answer for — the same class
|
|
50
|
+
# `Facade::Handle`'s own reference accessors reach with
|
|
51
|
+
# `Object.const_get` (see handle.rb's `define_reference_accessors`).
|
|
52
|
+
#
|
|
53
|
+
# Checked against the CURRENT boot's IR first, not against Ruby's
|
|
54
|
+
# constant table directly — a name that names nothing in this
|
|
55
|
+
# registry should refuse before ever asking Ruby whether some
|
|
56
|
+
# same-named constant happens to exist (possibly a stale one, left
|
|
57
|
+
# over from an earlier boot in this same process — the exact hazard
|
|
58
|
+
# `AggregateDoor#port`'s own comment describes at length). Only once
|
|
59
|
+
# the IR confirms the aggregate is real does this read the constant
|
|
60
|
+
# the current boot's `Surface.install` actually minted for it.
|
|
61
|
+
def aggregate(dispatcher, domain, name)
|
|
62
|
+
ir = dispatcher.registry.bluebook(domain)&.aggregate(name)
|
|
63
|
+
raise Runtime::NotFound, "#{domain} declares no aggregate named #{name.inspect}" unless ir
|
|
64
|
+
|
|
65
|
+
Object.const_get("#{domain}::#{ir.hecks_name}")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# The one command a POST to a bare collection URL means — "make one of
|
|
69
|
+
# these". `AggregateDoor` already enforces exactly one creating
|
|
70
|
+
# command per aggregate (`Command#creates?`, true exactly when the
|
|
71
|
+
# command declares no `references`); this just names it the same
|
|
72
|
+
# snake_case-plus-bang a Ruby caller would already ask the door for
|
|
73
|
+
# (`Naming.snake`, the identical call `AggregateDoor` itself makes
|
|
74
|
+
# when it defines that singleton method in the first place — `!`
|
|
75
|
+
# because every command does now, door and Handle alike).
|
|
76
|
+
def creating_command(klass)
|
|
77
|
+
creating = klass.ir.commands.find(&:creates?)
|
|
78
|
+
raise Runtime::NotFound, "#{klass.ir.hecks_name} declares no creating command" unless creating
|
|
79
|
+
|
|
80
|
+
"#{Naming.snake(creating.hecks_name)}!"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# A URL segment or a JSON body's "command" field, checked against what
|
|
84
|
+
# the aggregate actually declares. `klass.commands` is the same
|
|
85
|
+
# sorted, snake_cased list `AggregateDoor` built for its own door, so
|
|
86
|
+
# a name this accepts is a name a `Handle` can actually dispatch.
|
|
87
|
+
def validate_command!(klass, name)
|
|
88
|
+
wanted = name.to_s
|
|
89
|
+
return wanted if klass.commands.include?(wanted)
|
|
90
|
+
|
|
91
|
+
raise Runtime::NotFound, "#{klass.ir.hecks_name} declares no command named #{wanted.inspect}"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# `klass.find` already answers nil-on-miss — the right shape for a
|
|
95
|
+
# Ruby caller that means to check for itself. A JSON caller asking for
|
|
96
|
+
# one record by id off a URL means to HAVE it, or answer 404 — this is
|
|
97
|
+
# that stricter wrapper, raising the same `Runtime::NotFound` the rest
|
|
98
|
+
# of this door raises rather than handing back nil for the caller to
|
|
99
|
+
# remember to check.
|
|
100
|
+
def find!(klass, id)
|
|
101
|
+
klass.find(id) or raise Runtime::NotFound, "no #{klass.ir.hecks_name} found for id #{id.inspect}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# JSON only ever hands back String keys. A command's args, and every
|
|
105
|
+
# nested value-object literal inside them, need symbol keys before
|
|
106
|
+
# `Handle`/`Dispatcher` will accept them at all — this is that
|
|
107
|
+
# recursive conversion, blind to how deep a body nests.
|
|
108
|
+
def deep_symbolize(value)
|
|
109
|
+
case value
|
|
110
|
+
when Hash then value.to_h { |k, v| [k.to_sym, deep_symbolize(v)] }
|
|
111
|
+
when Array then value.map { |item| deep_symbolize(item) }
|
|
112
|
+
else value
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# The other direction: a `Handle`, or a query row's plain state hash,
|
|
117
|
+
# carrying a `Runtime::Value` at every level a value object sits at —
|
|
118
|
+
# down to plain Ruby a JSON encoder can walk without knowing what a
|
|
119
|
+
# `Runtime::Value` is.
|
|
120
|
+
#
|
|
121
|
+
# `Runtime::Value.materialize` (runtime/value.rb) already does the
|
|
122
|
+
# actual recursion — its `Hash` branch calls itself on every value,
|
|
123
|
+
# its `Value` branch unwraps through `#to_h`, which itself calls
|
|
124
|
+
# `materialize` on every field, so a value object three levels deep
|
|
125
|
+
# unwraps three levels deep for free. This is not a second unwrapper
|
|
126
|
+
# sitting next to it: `materialize` covers `Hash`/`Array`/`Value`
|
|
127
|
+
# already, and a `Handle` is none of those three, so the one thing
|
|
128
|
+
# this adds is `#to_h`'ing a `Handle` first so `materialize`'s own
|
|
129
|
+
# `Hash` case can take it from there.
|
|
130
|
+
def materialize(value)
|
|
131
|
+
value = value.to_h if value.is_a?(Handle)
|
|
132
|
+
Runtime::Value.materialize(value)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Parsed JSON and raw JSON text cross the same receiver/payload boundary
|
|
136
|
+
# as CLI and forms. The result is ready to splat into Dispatcher#dispatch
|
|
137
|
+
# and contains no loose routing fields.
|
|
138
|
+
def command_request(body, receiver:, legacy_receiver: nil)
|
|
139
|
+
input = body.is_a?(String) ? parse(body) : body
|
|
140
|
+
CommandRequest.normalize(input, receiver: receiver, legacy_receiver: legacy_receiver)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# The one place a raw JSON string is legitimate input for this door —
|
|
144
|
+
# a POST body, still text at the point a generic, HTTP-blind layer can
|
|
145
|
+
# see it. `JSON::ParserError` already names "this wasn't JSON" exactly
|
|
146
|
+
# right ; wrapping it in a bespoke `JsonDoor`-specific class would
|
|
147
|
+
# only be a second name for the same fact, so it propagates exactly as
|
|
148
|
+
# `JSON.parse` raises it — a calling app catches it the same standard
|
|
149
|
+
# way it would catch any other malformed-input error, no new class to
|
|
150
|
+
# learn.
|
|
151
|
+
def parse(raw_json) = JSON.parse(raw_json)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
require_relative "../../bluebook/dsl/hecksagon_builder"
|
|
2
|
+
require_relative "../../bluebook/dsl/domain_port_builder"
|
|
3
|
+
require_relative "../../bluebook/dsl/const_shim"
|
|
4
|
+
require_relative "../../bluebook/hexagon"
|
|
5
|
+
require_relative "../handle"
|
|
6
|
+
require_relative "../../naming"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module Facade
|
|
10
|
+
module Surface
|
|
11
|
+
# One aggregate's door: creating verbs as module methods returning
|
|
12
|
+
# the record in hand, CRUD delegation to the repository, the
|
|
13
|
+
# `persisted_by`-style binding collector a `.hecksagon` lands on, and
|
|
14
|
+
# the aggregate-scoped `port` a `.hecksagon` lands on beside it.
|
|
15
|
+
module AggregateDoor
|
|
16
|
+
def aggregate_module(dispatcher, domain, ir)
|
|
17
|
+
fqn = "#{domain}::#{ir.hecks_name}"
|
|
18
|
+
door = Module.new
|
|
19
|
+
|
|
20
|
+
ir.attributes.each do |attribute|
|
|
21
|
+
next unless RESERVED.include?(attribute.name.to_sym)
|
|
22
|
+
|
|
23
|
+
warn "[hecks] #{ir.hecks_name}##{attribute.name} shadows a built-in — no reader defined"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# A creating verb is a MODULE method returning the new record in hand ;
|
|
27
|
+
# a verb that reaches an existing record lives on the Handle. `!` —
|
|
28
|
+
# a command DOES something (mutates, may refuse), Ruby's own
|
|
29
|
+
# convention for that ; `Naming.snake` alone used to leave a
|
|
30
|
+
# creating command's bare name claiming the exact spelling a
|
|
31
|
+
# QUERY of the SAME business name also wants (`Account`'s own
|
|
32
|
+
# "Open" — the creating command AND a query listing open
|
|
33
|
+
# accounts, a real same-aggregate collision this corpus already
|
|
34
|
+
# has) — the suffix is what makes both nameable at all, not
|
|
35
|
+
# merely a style choice.
|
|
36
|
+
ir.commands.select(&:creates?).each do |command|
|
|
37
|
+
door.define_singleton_method("#{Naming.snake(command.hecks_name)}!") do |**args|
|
|
38
|
+
Handle.new(dispatcher: dispatcher, domain: domain, ir: ir,
|
|
39
|
+
instance: dispatcher.dispatch("#{fqn}.#{command.hecks_name}", with: args).instance)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# A query is a MODULE method too — same level as a creating
|
|
44
|
+
# command, since neither needs an existing record in hand — but
|
|
45
|
+
# bare: a query reads and returns, nothing to warn a caller
|
|
46
|
+
# about the way `!` does for a command. Answers the raw row
|
|
47
|
+
# array `dispatcher.query` itself answers, the same shape
|
|
48
|
+
# `runtime.query("#{fqn}.Name")` already gave.
|
|
49
|
+
ir.queries.each do |query|
|
|
50
|
+
door.define_singleton_method(Naming.snake(query.hecks_name)) do |**args|
|
|
51
|
+
dispatcher.query("#{fqn}.#{query.hecks_name}", **args)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
door.define_singleton_method(:fqn) { fqn }
|
|
56
|
+
door.define_singleton_method(:ir) { ir }
|
|
57
|
+
|
|
58
|
+
# THE SAME VERB THE CHAPTER ANSWERS, one level down. Every
|
|
59
|
+
# construct emits its own IR (Hecks::IR), so an aggregate
|
|
60
|
+
# is a legitimate thing to project — `Pizzas::Order.project(
|
|
61
|
+
# Projections::IR)` is this aggregate's IR, not the chapter's.
|
|
62
|
+
#
|
|
63
|
+
# A chapter-scoped target refuses here rather than inventing an
|
|
64
|
+
# answer: `Projector.admits!` is what tells `Projections::IR`
|
|
65
|
+
# (`from: :any`) apart from `Projections::Shape`
|
|
66
|
+
# (`from: :chapter`), which used to return a confidently empty
|
|
67
|
+
# `{"aggregates" => []}` for an aggregate.
|
|
68
|
+
door.define_singleton_method(:project) do |target, out: nil, **options|
|
|
69
|
+
key = Projector.key_for(target)
|
|
70
|
+
artifact = Projector.call(key, bluebook: ir, options: options)
|
|
71
|
+
return artifact unless out
|
|
72
|
+
|
|
73
|
+
Projector.write(artifact, out, as: Projector.emits_for(key))
|
|
74
|
+
end
|
|
75
|
+
door.define_singleton_method(:repository) { dispatcher.registry.repository(domain, ir) }
|
|
76
|
+
door.define_singleton_method(:commands) { ir.commands.map { |c| "#{Naming.snake(c.hecks_name)}!" }.sort }
|
|
77
|
+
door.define_singleton_method(:queries) { ir.queries.map { |q| Naming.snake(q.hecks_name) }.sort }
|
|
78
|
+
# ONE AGGREGATE'S USAGE DOCUMENT — the same projection the chapter
|
|
79
|
+
# answers with, narrowed to this head. `commands` above already
|
|
80
|
+
# answers "what can I call"; this answers "and what does each one
|
|
81
|
+
# want, refuse, and guarantee", which is the rest of the question.
|
|
82
|
+
door.define_singleton_method(:docs) do |**options|
|
|
83
|
+
Projector.call(:docs, bluebook: dispatcher.registry.bluebook(domain),
|
|
84
|
+
options: options.merge(aggregate: ir.hecks_name))
|
|
85
|
+
end
|
|
86
|
+
# ONE AGGREGATE, READ BACK IN ENGLISH — the same narrowing `:docs`
|
|
87
|
+
# takes, aimed at `:narrate` instead.
|
|
88
|
+
door.define_singleton_method(:narrate) do |**options|
|
|
89
|
+
Projector.call(:narrate, bluebook: dispatcher.registry.bluebook(domain),
|
|
90
|
+
options: options.merge(aggregate: ir.hecks_name))
|
|
91
|
+
end
|
|
92
|
+
door.define_singleton_method(:count) { dispatcher.registry.repository(domain, ir).count }
|
|
93
|
+
door.define_singleton_method(:events) { dispatcher.events.select { |event| event.aggregate == fqn } }
|
|
94
|
+
|
|
95
|
+
door.define_singleton_method(:find) do |id|
|
|
96
|
+
found = dispatcher.registry.repository(domain, ir).find(id)
|
|
97
|
+
found && Handle.new(dispatcher: dispatcher, domain: domain, ir: ir, instance: found)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
door.define_singleton_method(:all) do |**opts|
|
|
101
|
+
dispatcher.registry.repository(domain, ir).all(**opts).map do |instance|
|
|
102
|
+
Handle.new(dispatcher: dispatcher, domain: domain, ir: ir, instance: instance)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# THE SAME REASON `method_missing` BELOW EXISTS AT ALL — a facade
|
|
107
|
+
# left over from a PREVIOUS boot in this process shadows the fresh
|
|
108
|
+
# `BindingProxy` a `.hecksagon` would otherwise reach through
|
|
109
|
+
# `ConstShim`/`const_missing`, so `Pizzas::Pizza.port(...)` lands
|
|
110
|
+
# HERE instead once any boot has run before.
|
|
111
|
+
#
|
|
112
|
+
# RE-RESOLVED, NOT THE CLOSED-OVER `ir` — this door can be a STALE
|
|
113
|
+
# one, built by a boot from earlier in this same process, sitting
|
|
114
|
+
# on the `Pizzas`/`Pizza` constants only because nothing has
|
|
115
|
+
# re-installed them since. Attaching to this door's OWN `ir` would
|
|
116
|
+
# attach the port to a discarded aggregate from that old boot,
|
|
117
|
+
# invisible to the CURRENT one actually being loaded — silently,
|
|
118
|
+
# the exact way `method_missing` below already has to avoid it for
|
|
119
|
+
# a plain bind, via `HecksagonBuilder.collector` rather than
|
|
120
|
+
# anything this door closes over. `Hecks.current_registry` is
|
|
121
|
+
# the same "whichever boot is actually in progress" indirection,
|
|
122
|
+
# and `BindingProxy#port` already re-resolves through it the same
|
|
123
|
+
# way — this is that door's fallback twin, not a shortcut past it.
|
|
124
|
+
door.define_singleton_method(:port) do |name, &block|
|
|
125
|
+
current = Hecks.current_registry&.bluebook(domain)&.aggregate(ir.hecks_name) or
|
|
126
|
+
raise Bluebook::DSL::Malformed, "#{fqn}.port(#{name.inspect}) called outside a boot"
|
|
127
|
+
|
|
128
|
+
domain_port = Bluebook::DSL::ConstShim.with(->(const) { const }) do
|
|
129
|
+
Bluebook::DSL::DomainPortBuilder.build(name, owner: current.hecks_name, &block)
|
|
130
|
+
end
|
|
131
|
+
domain_port.operations.each do |operation|
|
|
132
|
+
operation.attributes.select(&:reference?).each { |attribute| attribute.type.declared_in = current }
|
|
133
|
+
end
|
|
134
|
+
current.add_port(domain_port)
|
|
135
|
+
self
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
door.define_singleton_method(:method_missing) do |verb, *args, **kwargs, &block|
|
|
139
|
+
collector = Bluebook::DSL::HecksagonBuilder.collector
|
|
140
|
+
return super(verb, *args, **kwargs, &block) unless collector
|
|
141
|
+
|
|
142
|
+
collector << Bluebook::Bind.new(
|
|
143
|
+
aggregate: fqn,
|
|
144
|
+
verb: verb.to_s,
|
|
145
|
+
adapter: args.first.to_s,
|
|
146
|
+
role: kwargs[:role]&.to_s
|
|
147
|
+
)
|
|
148
|
+
block&.call
|
|
149
|
+
self
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
door.define_singleton_method(:respond_to_missing?) do |name, include_private = false|
|
|
153
|
+
!Bluebook::DSL::HecksagonBuilder.collector.nil? || super(name, include_private)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# THE SAME STALE-FACADE HAZARD `method_missing`/`port` ABOVE ALREADY
|
|
157
|
+
# DOCUMENT, one door lower — `Surface.install` installs an aggregate's
|
|
158
|
+
# OWN name as a bare TOP-LEVEL constant too (`Namespace.install(Object,
|
|
159
|
+
# aggregate.hecks_name, ...)`, surface.rb's own `install`), not only
|
|
160
|
+
# nested under its chapter. So once ANY domain has booted once in this
|
|
161
|
+
# process, `Account::Debit` written while declaring some OTHER
|
|
162
|
+
# bluebook — same domain or a different one — reaches THIS door's
|
|
163
|
+
# const_missing directly, never `Object.const_missing`/`ConstShim::
|
|
164
|
+
# Hook` at all: real modules resolve without ever calling that.
|
|
165
|
+
#
|
|
166
|
+
# `ir.hecks_name`, NOT the qualified `fqn` — a scoped reference has to
|
|
167
|
+
# read the SAME either way, whether or not a stale door happens to be
|
|
168
|
+
# sitting on this process from an earlier boot; qualifying it here
|
|
169
|
+
# would make `Account::Debit`'s own meaning depend on incidental
|
|
170
|
+
# process history, which is the exact instability S0b exists to
|
|
171
|
+
# remove (docs/dsl-work-slices.md — "two domains in one registry" is
|
|
172
|
+
# this file's own reason for being owned by that slice).
|
|
173
|
+
door.define_singleton_method(:const_missing) do |name|
|
|
174
|
+
resolver = Bluebook::DSL::ConstShim.resolver
|
|
175
|
+
return resolver.call("#{ir.hecks_name}::#{name}") if resolver
|
|
176
|
+
|
|
177
|
+
super(name)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
door
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
require_relative "../../bluebook/dsl/binding_proxy"
|
|
2
|
+
require_relative "../../bluebook/dsl/const_shim"
|
|
3
|
+
require_relative "../../bluebook/dsl/hecksagon_builder"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Facade
|
|
7
|
+
module Surface
|
|
8
|
+
# One chapter's module: vision and aggregate roll-call on the
|
|
9
|
+
# singleton, one aggregate door per declared head, and the
|
|
10
|
+
# declaration hook for names the door does not carry.
|
|
11
|
+
module Chapter
|
|
12
|
+
def chapter_module(dispatcher, bluebook)
|
|
13
|
+
chapter = Module.new
|
|
14
|
+
chapter.define_singleton_method(:vision) { bluebook.vision }
|
|
15
|
+
chapter.define_singleton_method(:aggregates) { bluebook.aggregates.map(&:name).sort }
|
|
16
|
+
|
|
17
|
+
# THE CHAPTER, EXPLAINING ITSELF. `Projector::DocsProjector` reads
|
|
18
|
+
# nothing but this bluebook's own IR, so the document cannot drift
|
|
19
|
+
# from the domain — the same guarantee `bin/reference` gives the DSL
|
|
20
|
+
# reference by generating it from the Syntax chapter.
|
|
21
|
+
#
|
|
22
|
+
# A METHOD RATHER THAN ONLY A SCRIPT because that is what gets it
|
|
23
|
+
# read: `QualityControl.docs` in a console, beside `vision` and
|
|
24
|
+
# `aggregates`, at the moment somebody is wondering what a verb
|
|
25
|
+
# wants. Projected on each call rather than memoised — it is a pure
|
|
26
|
+
# read of the IR, and a stale document is the entire thing this
|
|
27
|
+
# exists to prevent.
|
|
28
|
+
chapter.define_singleton_method(:docs) do |**options|
|
|
29
|
+
Projector.call(:docs, bluebook: bluebook, options: options)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# THE CHAPTER, READ BACK IN ENGLISH — `Projector::NarrateProjector`
|
|
33
|
+
# beside `:docs`, for the reader who needs to confirm the domain is
|
|
34
|
+
# right rather than call it: `QualityControl.narrate` in a console.
|
|
35
|
+
chapter.define_singleton_method(:narrate) do |**options|
|
|
36
|
+
Projector.call(:narrate, bluebook: bluebook, options: options)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# THE DOMAIN'S OWN IR, PROJECTED. `Projector` has taken
|
|
40
|
+
# `call(name, bluebook:, options:)` since §30, but nothing could
|
|
41
|
+
# reach it from a booted domain — this module already closes
|
|
42
|
+
# over the one `Bluebook` every projector wants and simply
|
|
43
|
+
# never handed it out, so every bin/ projector rebuilt a
|
|
44
|
+
# Registry and reloaded the files to recover what was already
|
|
45
|
+
# sitting here.
|
|
46
|
+
#
|
|
47
|
+
# Pizzas.project(Projections::OIDC)
|
|
48
|
+
# Pizzas.project(Projections::Shape, out: "shape.json")
|
|
49
|
+
#
|
|
50
|
+
# No `to_ir` companion: `project(Projections::IR)` already
|
|
51
|
+
# answers with the canonical serialized form, and handing out
|
|
52
|
+
# the live graph as well would be a second way to say the same
|
|
53
|
+
# thing. Anything genuinely needing the graph is a projector,
|
|
54
|
+
# and a projector is given it.
|
|
55
|
+
#
|
|
56
|
+
# `out:` writes and everything else is passed through to the
|
|
57
|
+
# target — so `options` stays the projector's own vocabulary
|
|
58
|
+
# (`audience:` for OIDC) rather than a fixed set this method
|
|
59
|
+
# would have to know about.
|
|
60
|
+
chapter.define_singleton_method(:project) do |target, out: nil, **options|
|
|
61
|
+
key = Projector.key_for(target)
|
|
62
|
+
artifact = Projector.call(key, bluebook: bluebook, options: options)
|
|
63
|
+
return artifact unless out
|
|
64
|
+
|
|
65
|
+
Projector.write(artifact, out, as: Projector.emits_for(key))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
bluebook.aggregates.each do |aggregate|
|
|
69
|
+
chapter.const_set(aggregate.hecks_name, aggregate_module(dispatcher, bluebook.name, aggregate))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# INSIDE A HECKSAGON, A NAME IS A DECLARATION, NOT A LOOKUP. A
|
|
73
|
+
# `.hecksagon` may name an aggregate this door does not carry — a STALE
|
|
74
|
+
# door from an earlier boot resolving another registry's chapter, the
|
|
75
|
+
# exact hazard the constant tree used to hide by reinstalling on every
|
|
76
|
+
# load. With a collector open, the name becomes a `BindingProxy`
|
|
77
|
+
# recording the same bind the aggregate module would ; without one, it
|
|
78
|
+
# is a genuine NameError, exactly as before.
|
|
79
|
+
#
|
|
80
|
+
# THE OTHER READER OF A MISS is S0b's own bridge (docs/dsl-work-
|
|
81
|
+
# slices.md, const_shim.rb's `ScopedConstant`): a bluebook still being
|
|
82
|
+
# DECLARED that names `#{bluebook.name}::Something` — an event, a
|
|
83
|
+
# command reference, an `admits:` — reaches HERE, not
|
|
84
|
+
# `Object.const_missing`, the moment this chapter's own facade already
|
|
85
|
+
# exists (a previous boot in the same process, or this same chapter
|
|
86
|
+
# re-entering its own name). `ConstShim.active?` is exactly as true
|
|
87
|
+
# here as it is at the top level, mid-declaration, and consulting the
|
|
88
|
+
# SAME resolver is what makes a scoped reference resolve identically
|
|
89
|
+
# whether or not a facade happens to be built yet — the earlier
|
|
90
|
+
# attempt's own failure mode (this file's own history) was a bridge
|
|
91
|
+
# that worked only BEFORE any facade existed.
|
|
92
|
+
chapter.define_singleton_method(:const_missing) do |name|
|
|
93
|
+
collector = Bluebook::DSL::HecksagonBuilder.collector
|
|
94
|
+
return Bluebook::DSL::BindingProxy.new("#{bluebook.name}::#{name}", collector) if collector
|
|
95
|
+
|
|
96
|
+
resolver = Bluebook::DSL::ConstShim.resolver
|
|
97
|
+
return resolver.call("#{bluebook.name}::#{name}") if resolver
|
|
98
|
+
|
|
99
|
+
super(name)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
chapter
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require_relative "surface/chapter"
|
|
2
|
+
require_relative "surface/aggregate_door"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Facade
|
|
6
|
+
# THE DOOR, WITHOUT THE CLASSES.
|
|
7
|
+
#
|
|
8
|
+
# `Pizzas::Pizza.create_pizza(...)` is the public surface (HANDOVER rule 3),
|
|
9
|
+
# and this is what serves it now : anonymous per-boot modules whose
|
|
10
|
+
# singleton methods close over the dispatcher and dispatch by FQN — the
|
|
11
|
+
# same shape `Router::NamespaceInstaller` proved. Nothing here is a domain
|
|
12
|
+
# class ; the IR is the only graph, and the door is a projection of it,
|
|
13
|
+
# installed by `Loader.bind_runtime` and re-installed whole on the next
|
|
14
|
+
# boot. How a chapter's module is built is surface/chapter.rb; how one
|
|
15
|
+
# aggregate's door is built is surface/aggregate_door.rb.
|
|
16
|
+
#
|
|
17
|
+
# The hexagon-binding door rides along : `Pizzas::Pizza.persisted_by("Heki")`
|
|
18
|
+
# in a `.hecksagon` file lands on the module's `method_missing`, which
|
|
19
|
+
# records an `Bind` into whatever `HecksagonBuilder.collector` is open
|
|
20
|
+
# AT CALL TIME — so even a facade left over from a previous boot records
|
|
21
|
+
# into the current builder, and a chapter with no constant at all falls
|
|
22
|
+
# through to `ConstShim` → `BindingProxy`, which mints byte-identical binds.
|
|
23
|
+
module Surface
|
|
24
|
+
# These would shadow the machinery a Handle runs on, so a field named one
|
|
25
|
+
# of them gets no reader and says so — the same warning the class door gave.
|
|
26
|
+
RESERVED = %i[id state events reload inspect to_h hash class].freeze
|
|
27
|
+
|
|
28
|
+
extend Chapter
|
|
29
|
+
extend AggregateDoor
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
def install(dispatcher)
|
|
34
|
+
dispatcher.registry.bluebooks.each_value do |bluebook|
|
|
35
|
+
chapter = chapter_module(dispatcher, bluebook)
|
|
36
|
+
Namespace.install(Object, bluebook.name, chapter)
|
|
37
|
+
|
|
38
|
+
bluebook.aggregates.each do |aggregate|
|
|
39
|
+
next if aggregate.hecks_name == bluebook.name
|
|
40
|
+
|
|
41
|
+
Namespace.install(Object, aggregate.hecks_name, chapter.const_get(aggregate.hecks_name, false))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
dispatcher
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/hecks/facade.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
# The class-free public surface — see facade/surface.rb for the door and
|
|
3
|
+
# facade/handle.rb for the record in hand. Installed per boot by
|
|
4
|
+
# `Runtime::Loader.bind_runtime`.
|
|
5
|
+
module Facade
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# The one careful way to put something at top level.
|
|
9
|
+
#
|
|
10
|
+
# Overwrites only what it itself installed (tracked in GENERATED), refuses —
|
|
11
|
+
# with a warning, never a raise — to clobber a constant belonging to user
|
|
12
|
+
# code or the stdlib. Which is why a chapter named `Set` never becomes a
|
|
13
|
+
# constant, and why `Registry` keeps its own table of chapters rather than
|
|
14
|
+
# trusting Ruby's. Each re-install REPLACES the previous boot's entry, so
|
|
15
|
+
# what GENERATED retains is one small facade module per name, not a graph
|
|
16
|
+
# per boot.
|
|
17
|
+
module Namespace
|
|
18
|
+
GENERATED = {}
|
|
19
|
+
|
|
20
|
+
module_function
|
|
21
|
+
|
|
22
|
+
def install(container, name, value)
|
|
23
|
+
name = name.to_s
|
|
24
|
+
|
|
25
|
+
if container.const_defined?(name, false)
|
|
26
|
+
current = container.const_get(name)
|
|
27
|
+
unless GENERATED[[container, name]].equal?(current)
|
|
28
|
+
warn "[hecks] #{name} is already defined — leaving it alone"
|
|
29
|
+
return current
|
|
30
|
+
end
|
|
31
|
+
container.send(:remove_const, name)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
container.const_set(name, value)
|
|
35
|
+
GENERATED[[container, name]] = value
|
|
36
|
+
value
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
require_relative "facade/handle"
|
|
42
|
+
require_relative "facade/surface"
|
|
43
|
+
require_relative "facade/command_request"
|
|
44
|
+
require_relative "facade/json_door"
|