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,437 @@
|
|
|
1
|
+
require_relative "../naming"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projector
|
|
5
|
+
# A BLUEBOOK, PROJECTED AS ITS OWN COMMAND-LINE SURFACE.
|
|
6
|
+
#
|
|
7
|
+
# Every verb a domain declares is a subcommand; every argument is an
|
|
8
|
+
# option whose TYPE, whose admitted values and whose required-ness are
|
|
9
|
+
# already stated in the chapter. A hand-written CLI restates all of it and
|
|
10
|
+
# then drifts — and the first thing to drift is the help text, which is the
|
|
11
|
+
# only part anybody reads.
|
|
12
|
+
#
|
|
13
|
+
# WHAT IS PROJECTED, AND WHAT IS NOT. This answers the SURFACE — the verb
|
|
14
|
+
# tree, the argument spec, the usage text — and nothing executes here. One
|
|
15
|
+
# small generic runner (`bin/run`) boots a domain, asks for this, parses
|
|
16
|
+
# against it and dispatches.
|
|
17
|
+
#
|
|
18
|
+
# The alternative was generating an executable per domain, which is what
|
|
19
|
+
# `bin/project_rust` does for a whole runtime and would be the more
|
|
20
|
+
# spectacular version of this. It was not taken: a generated program is a
|
|
21
|
+
# second copy of the dispatch logic, and it needs regenerating on every
|
|
22
|
+
# bluebook edit — a second tax beside the era gate, paid for a file nobody
|
|
23
|
+
# reads. Projecting the surface keeps one dispatcher and a help text that
|
|
24
|
+
# cannot be stale, because it is computed at the moment it is printed.
|
|
25
|
+
#
|
|
26
|
+
# THE TYPING IS THE POINT. A CLI hands everything over as a String.
|
|
27
|
+
# `sequence.value=99` has to become the Integer 99 or the runtime refuses
|
|
28
|
+
# it, and the only honest place to learn that is the value object's own
|
|
29
|
+
# declared field type. A CLI that guessed — "it looks like a number" —
|
|
30
|
+
# would send 99 for a version string of "99" and be wrong in a way nobody
|
|
31
|
+
# could see.
|
|
32
|
+
module CliProjector
|
|
33
|
+
module_function
|
|
34
|
+
|
|
35
|
+
# TWO NAMESPACES, NOT ONE — `{ verbs:, questions:, usage: }`.
|
|
36
|
+
#
|
|
37
|
+
# A chapter may legally declare a command and a query of one name: the
|
|
38
|
+
# language namespaces them and `Banking::Account.Open` is both, in the
|
|
39
|
+
# corpus, today. A single flat list of subcommands has to pick one, and
|
|
40
|
+
# picking silently is how `Ticket.Filed` sat undetected in this
|
|
41
|
+
# repository for a day.
|
|
42
|
+
#
|
|
43
|
+
# So a question is asked with `ask`: `bin/run ask account.open`. It is
|
|
44
|
+
# this codebase own word — `Query::AskOption`, "the ask" — it is
|
|
45
|
+
# shell-safe where a `?` suffix would be eaten by globbing, and it makes
|
|
46
|
+
# the collision impossible rather than detected. It also reads as what it
|
|
47
|
+
# is: everything under `ask` changes nothing.
|
|
48
|
+
def call(bluebook:, options: {})
|
|
49
|
+
verbs = {}
|
|
50
|
+
questions = {}
|
|
51
|
+
|
|
52
|
+
bluebook.aggregates.each do |aggregate|
|
|
53
|
+
aggregate.commands.each { |c| claim(verbs, name_for(aggregate, c), command_spec(bluebook, aggregate, nil, c)) }
|
|
54
|
+
aggregate.queries.each { |q| claim(questions, name_for(aggregate, q), query_spec(bluebook, aggregate, nil, q)) }
|
|
55
|
+
|
|
56
|
+
aggregate.entities.each do |entity|
|
|
57
|
+
entity.commands.each { |c|
|
|
58
|
+
claim(verbs, name_for(aggregate, c, entity), command_spec(bluebook, aggregate, entity, c))
|
|
59
|
+
}
|
|
60
|
+
entity.queries.each { |q|
|
|
61
|
+
claim(questions, name_for(aggregate, q, entity), query_spec(bluebook, aggregate, entity, q))
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# A PORT IS A VERB TOO, and leaving it off the map was a real gap
|
|
66
|
+
# rather than a tasteful omission. The runtime has always dispatched
|
|
67
|
+
# a port operation by exactly the same name as a command — the
|
|
68
|
+
# projection simply never listed one, so `run_specs` and `file`
|
|
69
|
+
# answered "no such verb" while working perfectly through Ruby.
|
|
70
|
+
#
|
|
71
|
+
# It matters most for the caller with no other door. An agent that
|
|
72
|
+
# may not shell out reaches this domain ONLY through the projected
|
|
73
|
+
# CLI, and a port it cannot see is a capability it does not have.
|
|
74
|
+
aggregate.ports.each do |port|
|
|
75
|
+
port.operations.each { |o| claim(verbs, name_for(aggregate, o), port_spec(bluebook, aggregate, port, o)) }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# A REPORT IS A QUESTION TOO, and leaving it off was the same gap the
|
|
80
|
+
# ports had: `Dispatcher#query` has always answered `Domain.ReportName`,
|
|
81
|
+
# the projection simply never listed one — so the composed reads worked
|
|
82
|
+
# from Ruby and did not exist for anybody whose only door is the command
|
|
83
|
+
# line.
|
|
84
|
+
#
|
|
85
|
+
# It matters most for exactly what a report is FOR. Every other question
|
|
86
|
+
# here answers with rows and leaves the arithmetic to the reader; a
|
|
87
|
+
# `group_by` report is the one that counts. An agent that cannot reach
|
|
88
|
+
# it can list bugs all day and never answer "how are we doing".
|
|
89
|
+
#
|
|
90
|
+
# ONE DOT, NOT TWO — a report belongs to the chapter rather than to any
|
|
91
|
+
# aggregate (that is what rootless means), so it is addressed
|
|
92
|
+
# `QualityControl.BugsByStatus` where a query is
|
|
93
|
+
# `QualityControl::Bug.Queue`. `Dispatcher#query` splits on precisely
|
|
94
|
+
# that difference.
|
|
95
|
+
bluebook.read_models.each do |model|
|
|
96
|
+
claim(questions, Naming.snake(model.hecks_name), report_spec(bluebook, model))
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# THE SHORT SPELLING, WHERE IT CANNOT BE AMBIGUOUS. `pizzas
|
|
100
|
+
# create_pizza` rather than `pizzas order.create_pizza` — the
|
|
101
|
+
# aggregate is worth typing only when two of them declare the same
|
|
102
|
+
# verb, and in a one-aggregate domain it never is. Both spellings are
|
|
103
|
+
# always accepted; `names` maps every accepted one to its canonical
|
|
104
|
+
# key, and the display name is the shortest that is unambiguous.
|
|
105
|
+
shorten(verbs)
|
|
106
|
+
shorten(questions)
|
|
107
|
+
|
|
108
|
+
{ verbs: verbs, questions: questions,
|
|
109
|
+
names: { command: aliases(verbs), question: aliases(questions) },
|
|
110
|
+
usage: usage(bluebook, verbs, questions, options) }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# A last segment is claimed only if exactly one verb ends in it. Two
|
|
114
|
+
# aggregates declaring `Close` keep `customer.close` and `account.close`,
|
|
115
|
+
# which is the honest answer — a CLI that picked one would be choosing
|
|
116
|
+
# for the caller.
|
|
117
|
+
def shorten(specs)
|
|
118
|
+
tails = specs.keys.group_by { |name| name.split(".").last }
|
|
119
|
+
specs.each do |name, spec|
|
|
120
|
+
tail = name.split(".").last
|
|
121
|
+
spec[:short] = tails[tail].length == 1 ? tail : name
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def aliases(specs)
|
|
126
|
+
specs.each_with_object({}) do |(name, spec), map|
|
|
127
|
+
map[name] = name
|
|
128
|
+
map[spec[:short]] = name
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# A NAME IS CLAIMED ONCE. A command and a query of one name are legal in
|
|
133
|
+
# a chapter — the language namespaces them — and ambiguous as
|
|
134
|
+
# subcommands. Refused here rather than silently resolving to whichever
|
|
135
|
+
# was walked first, which is how `Ticket.Filed` (a command) and
|
|
136
|
+
# `Ticket.Filed` (a query) sat undetected until something flattened them.
|
|
137
|
+
def claim(verbs, name, spec)
|
|
138
|
+
if verbs.key?(name)
|
|
139
|
+
raise Bluebook::DSL::Malformed,
|
|
140
|
+
"two verbs project to the command-line name #{name.inspect}: " \
|
|
141
|
+
"#{verbs[name][:verb]} and #{spec[:verb]} — rename one"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
verbs[name] = spec
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def name_for(aggregate, verb, entity = nil)
|
|
148
|
+
parts = [Naming.snake(aggregate.hecks_name)]
|
|
149
|
+
parts << Naming.snake(entity.hecks_name) if entity
|
|
150
|
+
parts << Naming.snake(verb.hecks_name)
|
|
151
|
+
parts.join(".")
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def fqn(bluebook, aggregate, verb, entity = nil)
|
|
155
|
+
[bluebook.name, "::", aggregate.hecks_name, ".",
|
|
156
|
+
entity ? "#{entity.hecks_name}." : "", verb.hecks_name].join
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# ── one verb ──────────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
def command_spec(bluebook, aggregate, entity, command)
|
|
162
|
+
holder = entity || aggregate
|
|
163
|
+
arguments = command.attributes.flat_map { |a| options_for(a, holder, aggregate) }
|
|
164
|
+
receiver = if entity
|
|
165
|
+
:entity
|
|
166
|
+
else
|
|
167
|
+
(command.creates? ? nil : :aggregate)
|
|
168
|
+
end
|
|
169
|
+
legacy_arguments = []
|
|
170
|
+
|
|
171
|
+
# THE RECEIVER IS NOT A COMMAND ARGUMENT. An aggregate command names
|
|
172
|
+
# its record through to; an entity command needs both the aggregate
|
|
173
|
+
# record and the entity element within it. Keeping those paths in the
|
|
174
|
+
# projected option list makes the human-facing request complete while
|
|
175
|
+
# CommandRequest can remove them before it builds with: from the
|
|
176
|
+
# command's declared facts.
|
|
177
|
+
#
|
|
178
|
+
# Existing aggregate scripts may still spell the receiver id=... .
|
|
179
|
+
# That alias is deliberately hidden from help and recorded separately
|
|
180
|
+
# as legacy_arguments; new help and examples teach only to=... .
|
|
181
|
+
if entity
|
|
182
|
+
arguments = [
|
|
183
|
+
{ path: "to.aggregate", type: "String", required: true,
|
|
184
|
+
note: "id of the #{aggregate.hecks_name} holding the #{entity.hecks_name}" },
|
|
185
|
+
{ path: "to.entity", type: "String", required: true,
|
|
186
|
+
note: "id of the #{entity.hecks_name} to act on" }
|
|
187
|
+
] + arguments
|
|
188
|
+
elsif receiver == :aggregate
|
|
189
|
+
arguments = [{ path: "to", type: "String", required: true,
|
|
190
|
+
note: "id of the #{aggregate.hecks_name} to act on" }] + arguments
|
|
191
|
+
legacy_arguments = [{ path: "id", type: "String", required: true }]
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
{ verb: fqn(bluebook, aggregate, command, entity), kind: :command,
|
|
195
|
+
summary: command.goal, role: command.role, creates: command.creates?,
|
|
196
|
+
receiver: receiver, legacy_receiver: (receiver == :aggregate ? :id : nil),
|
|
197
|
+
legacy_arguments: legacy_arguments,
|
|
198
|
+
refusals: refusals(command, holder), arguments: arguments }
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# A PORT OPERATION READS AS A VERB BUT REPORTS AS A BOUNDARY.
|
|
202
|
+
#
|
|
203
|
+
# `creates: false` because it makes no record, and `refusals: []`
|
|
204
|
+
# because it has none in the sense every other verb means: a command's
|
|
205
|
+
# refusals are sentences the chapter will say back to you, and an
|
|
206
|
+
# outbound operation's failure is somebody else's sentence, unknowable
|
|
207
|
+
# from here.
|
|
208
|
+
#
|
|
209
|
+
# THE SUMMARY NAMES BOTH ENDINGS, which is the one thing a caller most
|
|
210
|
+
# needs and cannot infer. `run_specs` looks like it either works or
|
|
211
|
+
# errors; what it actually does is answer `SpecsCompleted` even when the
|
|
212
|
+
# suite is red, and refuse only when rspec could not run. Somebody
|
|
213
|
+
# reading `--help` should not have to open the hecksagon to find that
|
|
214
|
+
# out.
|
|
215
|
+
def port_spec(bluebook, aggregate, port, operation)
|
|
216
|
+
arguments = receiver_options(:aggregate, aggregate, nil) +
|
|
217
|
+
operation.attributes.flat_map { |a| options_for(a, aggregate, aggregate) }
|
|
218
|
+
|
|
219
|
+
# THE WIRE NAME CARRIES THE PORT, THE TYPED NAME DOES NOT.
|
|
220
|
+
#
|
|
221
|
+
# `Dispatcher#dispatch` splits a verb into head and sub and looks the
|
|
222
|
+
# head up as a port, so a port operation is addressed
|
|
223
|
+
# `Aggregate.Port.Operation` — three parts where a command has two.
|
|
224
|
+
# But nobody should have to type `sweep.toolchain.run_specs`: which
|
|
225
|
+
# port a verb goes out through is wiring, and the caller's business is
|
|
226
|
+
# what they want done. So the projection spells the verb in full and
|
|
227
|
+
# names it short, which is the same split `shorten` already makes.
|
|
228
|
+
{ verb: [fqn(bluebook, aggregate, operation).sub(/\.[^.]+\z/, ""), port.name, operation.hecks_name].join("."),
|
|
229
|
+
kind: :command, creates: false, receiver: :aggregate, refusals: [],
|
|
230
|
+
role: operation.outbound? ? "#{aggregate.hecks_name} asking #{port.name}" : "#{port.name} telling #{aggregate.hecks_name}",
|
|
231
|
+
summary: port_summary(port, operation), arguments: arguments }
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def port_summary(port, operation)
|
|
235
|
+
return "#{port.name} reports it; emits #{operation.emits.join(', ')}" unless operation.outbound?
|
|
236
|
+
|
|
237
|
+
"Ask #{port.name} — answers #{operation.answers}, refuses #{operation.refuses}"
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# A ROOTLESS REPORT TAKES NOTHING; a rooted one takes the id of the
|
|
241
|
+
# record it is a view of, under the name the model gave that reference.
|
|
242
|
+
def report_spec(bluebook, model)
|
|
243
|
+
arguments =
|
|
244
|
+
if model.reference_target
|
|
245
|
+
[{ path: model.reference_name.to_s, type: "String", required: true,
|
|
246
|
+
note: "id of the #{model.reference_target} this is a view of" }]
|
|
247
|
+
else
|
|
248
|
+
[]
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
{ verb: "#{bluebook.name}.#{model.hecks_name}", kind: :query,
|
|
252
|
+
summary: model.description, arguments: arguments }
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def query_spec(bluebook, aggregate, entity, query)
|
|
256
|
+
arguments = Array(query.to_h[:attributes]).flat_map do |declared|
|
|
257
|
+
attribute = query.attributes.find { |a| a.name.to_s == declared[:name].to_s }
|
|
258
|
+
attribute ? options_for(attribute, entity || aggregate, aggregate) : []
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
{ verb: fqn(bluebook, aggregate, query, entity), kind: :query,
|
|
262
|
+
summary: query.description, arguments: arguments }
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# ── one argument, flattened ───────────────────────────────────────
|
|
266
|
+
|
|
267
|
+
# A VALUE OBJECT BECOMES ONE OPTION PER FIELD, dotted. `commit` typed
|
|
268
|
+
# `CommitRef` is `--commit.value`, because that is the shape the runtime
|
|
269
|
+
# wants and a flat `--commit` would have to guess which field it meant.
|
|
270
|
+
# Single-field value objects — almost all of them — read fine either way,
|
|
271
|
+
# and the runner accepts the short form for exactly those.
|
|
272
|
+
# RECURSIVE, AND IT HAS TO BE. A value object may hold another one —
|
|
273
|
+
# pizzas' `Pizza` holds a `Price` and a `Size` — so stopping after one
|
|
274
|
+
# level produced `pizza.price_cents=1500` and sent the STRING "1500"
|
|
275
|
+
# where `{ cents: 1500 }` belonged.
|
|
276
|
+
#
|
|
277
|
+
# The runtime took it. `qa/FINDINGS.md` #2 is exactly that gap —
|
|
278
|
+
# `Value::Coercion.build` does not validate nested value objects — so a
|
|
279
|
+
# one-level CLI is not merely inconvenient, it is a machine for writing
|
|
280
|
+
# malformed records into a real store, which is what it did on its first
|
|
281
|
+
# run against the pizzas database.
|
|
282
|
+
def options_for(attribute, holder, aggregate, prefix = nil, optional = nil)
|
|
283
|
+
path = [prefix, attribute.name].compact.join(".")
|
|
284
|
+
optional = optional || attribute.optional?
|
|
285
|
+
return [reference_option(attribute)] if attribute.reference?
|
|
286
|
+
|
|
287
|
+
value_object = value_object_for(attribute, holder, aggregate)
|
|
288
|
+
return [scalar_option(path, attribute, optional)] unless value_object
|
|
289
|
+
|
|
290
|
+
# A LIST SAYS SO, ALL THE WAY DOWN TO ITS LEAVES.
|
|
291
|
+
#
|
|
292
|
+
# Without this a `list_of(Tag)` projected exactly like a single Tag:
|
|
293
|
+
# one option, `tags.value`, indistinguishable from a scalar. So the
|
|
294
|
+
# help said to pass one, `CliDoor#bury` overwrote the leaf each time,
|
|
295
|
+
# and passing two tags stored the second and lost the first WITHOUT
|
|
296
|
+
# SAYING ANYTHING. A missing argument is refused loudly; a forgotten
|
|
297
|
+
# one is not, which makes it the more expensive of the two by far.
|
|
298
|
+
#
|
|
299
|
+
# The flag is carried on the leaf rather than kept beside the
|
|
300
|
+
# attribute because the leaf is all `CliDoor` ever sees — it is handed
|
|
301
|
+
# a path and a spec, and reuniting them with the attribute that
|
|
302
|
+
# produced them would be a lookup that exists only to answer this.
|
|
303
|
+
fields = value_object.attributes.flat_map do |field|
|
|
304
|
+
nested = value_object_for(field, value_object, aggregate)
|
|
305
|
+
next options_for(field, value_object, aggregate, path, optional) if nested
|
|
306
|
+
|
|
307
|
+
scalar_option("#{path}.#{field.name}", field, optional || field.optional?,
|
|
308
|
+
enum: closed_members(value_object, field))
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
return fields unless attribute.list?
|
|
312
|
+
|
|
313
|
+
fields.map { |option| option.merge(list: true, note: [option[:note], "repeatable"].compact.join("; ")) }
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def reference_option(attribute)
|
|
317
|
+
{ path: attribute.name.to_s, type: "String", required: !attribute.optional?,
|
|
318
|
+
note: "id of a #{attribute.type.target_name}" }
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def scalar_option(path, field, optional, enum: [])
|
|
322
|
+
option = { path: path, type: field.type.to_s, required: !optional }
|
|
323
|
+
option[:enum] = enum unless enum.empty?
|
|
324
|
+
option[:pattern] = field.pattern if field.respond_to?(:pattern) && field.pattern
|
|
325
|
+
option[:default] = field.default if field.respond_to?(:default) && !field.default.nil?
|
|
326
|
+
option
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def closed_members(value_object, field)
|
|
330
|
+
return [] unless value_object.closed_set?
|
|
331
|
+
|
|
332
|
+
value_object.members.filter_map { |member| member[field.name] }.uniq
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def value_object_for(attribute, holder, aggregate)
|
|
336
|
+
[holder, aggregate].compact.each do |scope|
|
|
337
|
+
next unless scope.respond_to?(:value_objects)
|
|
338
|
+
|
|
339
|
+
found = scope.value_objects.find { |v| v.hecks_name == attribute.type.to_s }
|
|
340
|
+
return found if found
|
|
341
|
+
end
|
|
342
|
+
nil
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Every way this verb can say no, in the chapter's own words — printed
|
|
346
|
+
# by `--help` before the caller spends a dispatch finding out.
|
|
347
|
+
def refusals(command, holder)
|
|
348
|
+
out = []
|
|
349
|
+
lifecycle = holder.lifecycle
|
|
350
|
+
froms = lifecycle && lifecycle.transitions.filter_map do |name, transition|
|
|
351
|
+
Array(transition.from) if name.to_s == command.hecks_name
|
|
352
|
+
end.flatten.uniq
|
|
353
|
+
out << "#{lifecycle.field} is not #{froms.join(' or ')}" if froms && !froms.empty?
|
|
354
|
+
out += command.attributes.select(&:reference?).map { |r| "no #{r.type.target_name} has that #{r.name}" }
|
|
355
|
+
out + command.givens.map(&:description)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# ── the help ──────────────────────────────────────────────────────
|
|
359
|
+
|
|
360
|
+
def usage(bluebook, verbs, questions, options)
|
|
361
|
+
program = options[:program] || "bin/run"
|
|
362
|
+
only = options[:verb]
|
|
363
|
+
|
|
364
|
+
# WHICH NAMESPACE, when both hold the name. `options[:ask]` says so;
|
|
365
|
+
# without it a `--help` for a question would print the command that
|
|
366
|
+
# shares its name, which banking has and which is how this was found.
|
|
367
|
+
if only
|
|
368
|
+
pool = options[:ask] ? questions : verbs
|
|
369
|
+
key = aliases(pool)[only] || (options[:ask] ? nil : aliases(questions)[only])
|
|
370
|
+
spec = pool[key] || questions[key]
|
|
371
|
+
return verb_help(program, spec[:short], spec, ask: options[:ask]) if spec
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
width = (verbs.values + questions.values).map { |spec| spec[:short].length }.max.to_i
|
|
375
|
+
out = ["#{bluebook.name} — #{bluebook.vision}", "",
|
|
376
|
+
" #{program} <verb> [name=value …] do something",
|
|
377
|
+
" #{program} ask <question> [name=value …] read something", ""]
|
|
378
|
+
|
|
379
|
+
out << "verbs:"
|
|
380
|
+
verbs.each_value { |spec| out << " #{spec[:short].ljust(width)} #{spec[:summary]}" }
|
|
381
|
+
out << ""
|
|
382
|
+
out << "questions (nothing here changes anything):"
|
|
383
|
+
questions.each_value { |spec| out << " #{spec[:short].ljust(width)} #{first_sentence(spec[:summary])}" }
|
|
384
|
+
out << ""
|
|
385
|
+
out << " #{program} <verb> --help what one verb wants, and every way it refuses"
|
|
386
|
+
out << " a verb can always be spelled in full — #{example_qualified(verbs)}"
|
|
387
|
+
out.join("\n")
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def example_qualified(verbs)
|
|
391
|
+
name, spec = verbs.find { |key, value| key != value[:short] } || verbs.first
|
|
392
|
+
name ? "#{spec[:short]} is also #{name}" : ""
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# A QUERY's `description` is written as a paragraph — it argues for why
|
|
396
|
+
# the list is worth reading. A verb table wants the first sentence of
|
|
397
|
+
# that argument; `--help` still prints the whole thing.
|
|
398
|
+
def first_sentence(text)
|
|
399
|
+
text.to_s.split(/(?<=\.)\s/).first.to_s
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def verb_help(program, name, spec, ask: false)
|
|
403
|
+
out = ["#{name} — #{spec[:summary]}", ""]
|
|
404
|
+
out << "dispatches #{spec[:verb]}" if spec[:kind] == :command
|
|
405
|
+
out << "reads #{spec[:verb]}" if spec[:kind] == :query
|
|
406
|
+
out << "issued by #{spec[:role]}" if spec[:role]
|
|
407
|
+
out << ""
|
|
408
|
+
invocation = ask ? "#{program} ask #{name}" : "#{program} #{name}"
|
|
409
|
+
out << " #{invocation}#{spec[:arguments].map { |a| " #{a[:path]}=…" }.join}"
|
|
410
|
+
out << ""
|
|
411
|
+
|
|
412
|
+
unless spec[:arguments].empty?
|
|
413
|
+
width = spec[:arguments].map { |a| a[:path].length }.max
|
|
414
|
+
spec[:arguments].each do |argument|
|
|
415
|
+
notes = []
|
|
416
|
+
notes << argument[:type]
|
|
417
|
+
notes << "one of #{argument[:enum].join(', ')}" if argument[:enum]
|
|
418
|
+
notes << "matches #{argument[:pattern]}" if argument[:pattern]
|
|
419
|
+
notes << "defaults to #{argument[:default].inspect}" unless argument[:default].nil?
|
|
420
|
+
notes << argument[:note] if argument[:note]
|
|
421
|
+
notes << "optional" unless argument[:required]
|
|
422
|
+
out << " #{argument[:path].ljust(width)} #{notes.join('; ')}"
|
|
423
|
+
end
|
|
424
|
+
out << ""
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
unless Array(spec[:refusals]).empty?
|
|
428
|
+
out << "refused when:"
|
|
429
|
+
spec[:refusals].each { |refusal| out << " #{refusal}" }
|
|
430
|
+
out << ""
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
out.join("\n")
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
end
|