hecks 3.9.1 → 3.11.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 +4 -4
- data/examples/pizzas/bluebook/pizzas.bluebook +29 -0
- data/examples/pizzas/bluebook/pizzas.hecksagon +7 -0
- data/exe/hecks +4 -4
- data/lib/hecks/adapters/driven/d1.rb +2 -10
- data/lib/hecks/adapters/driven/heki.adapter +1 -1
- data/lib/hecks/adapters/driven/heki.rb +2 -12
- data/lib/hecks/adapters/driven/lambda.adapter +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +3 -18
- data/lib/hecks/adapters/driven/postgres.rb +4 -11
- data/lib/hecks/adapters/driven/sqlite.adapter +1 -1
- data/lib/hecks/adapters/driven/sqlite.rb +2 -10
- data/lib/hecks/adapters/driving/admission.rb +46 -0
- data/lib/hecks/adapters/driving/cli/nesting.rb +36 -0
- data/lib/hecks/adapters/driving/cli/shorthand.rb +88 -0
- data/lib/hecks/adapters/driving/cli.rb +127 -0
- data/lib/hecks/adapters/driving/cli_runner/answers.rb +160 -0
- data/lib/hecks/adapters/driving/cli_runner/resolution.rb +155 -0
- data/lib/hecks/adapters/driving/cli_runner/settling.rb +120 -0
- data/lib/hecks/adapters/driving/cli_runner/streaming.rb +93 -0
- data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +47 -0
- data/lib/hecks/adapters/driving/cli_runner.rb +69 -0
- data/lib/hecks/adapters/driving/command_request.rb +121 -0
- data/lib/hecks/adapters/driving/handle.rb +214 -0
- data/lib/hecks/adapters/driving/json.rb +136 -0
- data/lib/hecks/adapters/driving/launcher_options.rb +267 -0
- data/lib/hecks/adapters/driving/mcp/properties.rb +49 -0
- data/lib/hecks/adapters/driving/mcp/tool_calls.rb +116 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/auditing.rb +53 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/calling.rb +71 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/reading.rb +99 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions.rb +18 -0
- data/lib/hecks/adapters/driving/mcp.rb +159 -0
- data/lib/hecks/adapters/driving/mcp_guide_cache.rb +78 -0
- data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +97 -0
- data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +69 -0
- data/lib/hecks/adapters/driving/mcp_scope/presenter.rb +83 -0
- data/lib/hecks/adapters/driving/mcp_scope.rb +213 -0
- data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
- data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
- data/lib/hecks/adapters/driving/ruby.rb +83 -0
- data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
- data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
- data/lib/hecks/behaviors/expectations/boot.rb +1 -1
- data/lib/hecks/bench/ruby_runner.rb +1 -1
- data/lib/hecks/bluebook/ask_resolution.rb +130 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
- data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
- data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
- data/lib/hecks/bluebook/assembly/marks.rb +1 -0
- data/lib/hecks/bluebook/attribute.rb +1 -1
- data/lib/hecks/bluebook/behaviour/command.rb +13 -8
- data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
- data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
- data/lib/hecks/bluebook/capabilities.rb +91 -9
- data/lib/hecks/bluebook/domain_port.rb +16 -4
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
- data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
- data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
- data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
- data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
- data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
- data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
- data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
- data/lib/hecks/bluebook/expression/projection.json +36 -0
- data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
- data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
- data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
- data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
- data/lib/hecks/bluebook/expression/resolver.rb +20 -1
- data/lib/hecks/bluebook/hecksagon.rb +23 -3
- data/lib/hecks/bluebook/lifecycle.rb +22 -3
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
- data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/readings.rb +18 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
- data/lib/hecks/bluebook/meta_validator.rb +12 -12
- data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
- data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
- data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
- data/lib/hecks/bluebook/model_check.rb +8 -2
- data/lib/hecks/bluebook/policy.rb +4 -2
- data/lib/hecks/bluebook/smoke_test.rb +1 -1
- data/lib/hecks/bluebook/synthesizer.rb +2 -0
- data/lib/hecks/bluebook.rb +1 -0
- data/lib/hecks/cli/command_table.rb +1 -1
- data/lib/hecks/cli/console.rb +2 -1
- data/lib/hecks/cli/document.rb +1 -1
- data/lib/hecks/cli/launcher_words.rb +4 -4
- data/lib/hecks/cli/mcp.rb +4 -4
- data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
- data/lib/hecks/cli/project_cli/templates.rb +5 -5
- data/lib/hecks/cli/project_cli.rb +9 -6
- data/lib/hecks/cli/run.rb +2 -2
- data/lib/hecks/cli/smoke_http/checks.rb +4 -1
- data/lib/hecks/cli/smoke_http/transport.rb +3 -3
- data/lib/hecks/cli/smoke_http.rb +3 -0
- data/lib/hecks/codemod.rb +5 -2
- data/lib/hecks/corpus/routes.rb +7 -0
- data/lib/hecks/corpus/rust.rb +3 -2
- data/lib/hecks/deploy/bluebook/deploy.bluebook +124 -2
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +8 -0
- data/lib/hecks/deploy/oidc.json +16 -3
- data/lib/hecks/doc/reference/rendering.rb +6 -3
- data/lib/hecks/forms/app/commands.rb +8 -7
- data/lib/hecks/forms/app/queries.rb +2 -1
- data/lib/hecks/forms/app.rb +1 -1
- data/lib/hecks/forms/record_renderer.rb +1 -1
- data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
- data/lib/hecks/fuzzing/failure_triage.rb +2 -1
- data/lib/hecks/fuzzing/mutation/run.rb +11 -4
- data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
- data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
- data/lib/hecks/fuzzing/properties/guards.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency.rb +6 -2
- data/lib/hecks/fuzzing/shrinker.rb +3 -1
- data/lib/hecks/fuzzing/value_generator.rb +19 -5
- data/lib/hecks/gate/stages.yml +4 -10
- data/lib/hecks/grammar/evolve.rb +1 -1
- data/lib/hecks/grammar/expression_operators.json +372 -0
- data/lib/hecks/grammar/operators.rb +3 -1
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
- data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
- data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
- data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
- data/lib/hecks/hecks/adapters/deploy_toolchain/clearing.rb +51 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +2 -0
- data/lib/hecks/hecks/adapters/finding_github.rb +13 -6
- data/lib/hecks/hecks/adapters/first_admin.rb +2 -2
- data/lib/hecks/hecks/adapters/github_rulesets.rb +6 -3
- data/lib/hecks/hecks/adapters/in_process_boot.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +10 -20
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +28 -5
- data/lib/hecks/hecks/adapters/local_files.rb +2 -2
- data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +107 -0
- data/lib/hecks/hecks/adapters/rehearsal/census.rb +71 -0
- data/lib/hecks/hecks/adapters/rehearsal/declaration.rb +133 -0
- data/lib/hecks/hecks/adapters/rehearsal/environment.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +142 -0
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +96 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +100 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +69 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +129 -0
- data/lib/hecks/hecks/adapters/rehearsal/rows.rb +41 -0
- data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/subject.rb +104 -0
- data/lib/hecks/hecks/adapters/rehearsal.rb +54 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.rb +81 -0
- data/lib/hecks/hecks/adapters/site_toolchain/role_prober.rb +78 -0
- data/lib/hecks/hecks/adapters/site_toolchain.rb +29 -4
- data/lib/hecks/hecks/adapters/terminal.rb +13 -11
- data/lib/hecks/hecks/adapters/world_clearing.rb +65 -0
- data/lib/hecks/hecks/codebase.behaviors +13 -1
- data/lib/hecks/hecks/codebase.bluebook +239 -5
- data/lib/hecks/hecks/custodian.behaviors +24 -6
- data/lib/hecks/hecks/custodian.bluebook +6 -1227
- data/lib/hecks/hecks/deploy.behaviors +1 -1
- data/lib/hecks/hecks/deploy.world +1 -3
- data/lib/hecks/hecks/hecks.behaviors +1 -1
- data/lib/hecks/hecks/hecks.hecksagon +61 -18
- data/lib/hecks/hecks/hecks.world +11 -10
- data/lib/hecks/hecks/rehearsal.bluebook +477 -0
- data/lib/hecks/hecks/site.behaviors +1 -1
- data/lib/hecks/hecks/site.world +0 -1
- data/lib/hecks/hecks/tooling.bluebook +1238 -0
- data/lib/hecks/indifferent_key.rb +20 -0
- data/lib/hecks/language/adapter.bluebook +13 -3
- data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
- data/lib/hecks/language/bluebook/entity.bluebook +18 -0
- data/lib/hecks/language/bluebook/policy.bluebook +9 -1
- data/lib/hecks/language/bluebook/query.bluebook +1 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +11 -4
- data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
- data/lib/hecks/language/oidc.json +10 -0
- data/lib/hecks/language/port.bluebook +4 -4
- data/lib/hecks/literal.rb +12 -3
- data/lib/hecks/ports/key_vault.rb +7 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
- data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +29 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/fargate/settings/tuning.rb +15 -0
- data/lib/hecks/projections/deploy/fargate/settings.rb +3 -1
- data/lib/hecks/projections/deploy/fargate/stack/readers.rb +1 -0
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/shared.rb +5 -2
- data/lib/hecks/projections/deploy/templates/fargate/Makefile.tmpl +7 -4
- data/lib/hecks/projections/model/deviations.rb +2 -2
- data/lib/hecks/projections/model.rb +2 -2
- data/lib/hecks/projections/oidc.rb +13 -1
- data/lib/hecks/projections/shape.rb +5 -2
- data/lib/hecks/projections/site/cms_editor/attributes.rb +95 -0
- data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
- data/lib/hecks/projections/site/cms_editor/checks.rb +77 -0
- data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
- data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
- data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
- data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
- data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
- data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
- data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
- data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
- data/lib/hecks/projections/site/cms_editor/media.rb +85 -0
- data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
- data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
- data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
- data/lib/hecks/projections/site/cms_editor/pretty.rb +40 -0
- data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
- data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
- data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
- data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
- data/lib/hecks/projections/site/cms_editor/schema.rb +161 -0
- data/lib/hecks/projections/site/cms_editor/setting.rb +130 -0
- data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +370 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +87 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/session.ts.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/sso.ts.tmpl +20 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +133 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +29 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +43 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +177 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/multipart.ts.tmpl +53 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/sniff.ts.tmpl +94 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/storage.ts.tmpl +50 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +214 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/server.ts.tmpl +65 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +123 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_parse.js.tmpl +320 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +275 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +61 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +92 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +63 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +213 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
- data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +17 -0
- data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
- data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
- data/lib/hecks/projections/site/cms_editor.rb +133 -0
- data/lib/hecks/projections/site/role_probe.rb +74 -0
- data/lib/hecks/projector/cli_audience.rb +2 -1
- data/lib/hecks/projector/cli_projector/listing.rb +1 -1
- data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
- data/lib/hecks/projector/docs_projector/closing.rb +1 -1
- data/lib/hecks/projector/docs_projector.rb +1 -1
- data/lib/hecks/projector/exporter/bindings.rb +40 -10
- data/lib/hecks/projector/exporter/capability_values.rb +55 -0
- data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
- data/lib/hecks/projector/target.rb +11 -0
- data/lib/hecks/projector.rb +32 -5
- data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
- data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
- data/lib/hecks/quality_control/quality_control.bluebook +6 -2
- data/lib/hecks/release/approval.rb +64 -0
- data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
- data/lib/hecks/router/namespace_installer.rb +16 -16
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/best_effort.rb +22 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
- data/lib/hecks/runtime/command_interpreter.rb +3 -2
- data/lib/hecks/runtime/command_rules/emission.rb +1 -1
- data/lib/hecks/runtime/dispatcher.rb +4 -4
- data/lib/hecks/runtime/entity_element/location.rb +1 -1
- data/lib/hecks/runtime/event.rb +5 -0
- data/lib/hecks/runtime/identity.rb +7 -6
- data/lib/hecks/runtime/interpreting.rb +2 -1
- data/lib/hecks/runtime/invocation_builder.rb +1 -1
- data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
- data/lib/hecks/runtime/loader.rb +10 -10
- data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
- data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
- data/lib/hecks/runtime/policy_interpreter.rb +6 -6
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
- data/lib/hecks/runtime/reaction_invocation.rb +1 -1
- data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
- data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
- data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
- data/lib/hecks/runtime/registry/merging.rb +1 -1
- data/lib/hecks/runtime/registry/providers.rb +16 -0
- data/lib/hecks/runtime/registry/read_repository.rb +6 -4
- data/lib/hecks/runtime/registry/verification.rb +4 -1
- data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
- data/lib/hecks/runtime/registry.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
- data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter.rb +3 -3
- data/lib/hecks/runtime/tenant_check.rb +5 -4
- data/lib/hecks/runtime/value/coercion.rb +2 -2
- data/lib/hecks/runtime/value/validation.rb +16 -4
- data/lib/hecks/runtime.rb +9 -9
- data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
- data/lib/hecks/rust_build/wasm.rb +1 -1
- data/lib/hecks/rust_build/wasm_browser.rb +1 -1
- data/lib/hecks/rust_build.rb +11 -0
- data/lib/hecks/served_options.rb +53 -0
- data/lib/hecks/site/bluebook/site.bluebook +29 -1
- data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -0
- data/lib/hecks/storehouse/audit.rb +1 -1
- data/lib/hecks/storehouse/dispatching.rb +8 -6
- data/lib/hecks/storehouse/introspection.rb +2 -2
- data/lib/hecks/storehouse/querying.rb +6 -4
- data/lib/hecks/storehouse.rb +4 -4
- data/lib/hecks/tools/comment_style/source_file.rb +4 -1
- data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
- data/lib/hecks/tools/lanes/rows.rb +6 -0
- data/lib/hecks/tools/oidc_manifests.rb +4 -5
- data/lib/hecks/tools/regeneration_run.rb +11 -4
- data/lib/hecks/tools/site_routes/cli.rb +1 -1
- data/lib/hecks/tools/site_routes/editor_files.rb +45 -0
- data/lib/hecks/tools/site_routes.rb +14 -7
- data/lib/hecks/tools/tenant_provisioning.rb +2 -2
- data/lib/hecks/tools/tools_doc.rb +4 -4
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +4 -4
- data/lib/hecks.rb +15 -15
- data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
- data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
- data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
- data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
- data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
- data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
- data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
- data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
- data/prebuilt/05c358d38d661a3774f813d49c66f5b68939af94432d66257a56f49ee2598994.marshal +0 -0
- data/prebuilt/26c40c61f09b09b11651c64668eda3ceb180f83cf2a3802d3033d2fd99ac841b.marshal +0 -0
- data/prebuilt/verdicts-230ac18c25abfd3d92ede032bee2661a4c2cd2773ab0cf0e2ae52546ba2f7af9.json +1 -0
- data/rust/.gitignore +6 -0
- data/rust/Cargo.toml +9 -1
- data/rust/build/src/binding_pass.rs +103 -0
- data/rust/build/src/cargo_sync.rs +6 -0
- data/rust/build/src/lineage_pass.rs +3 -3
- data/rust/build/src/main.rs +17 -5
- data/rust/build/src/pipeline.rs +48 -5
- data/rust/build/src/seams_pass.rs +123 -0
- data/rust/build/src/translation_pass.rs +167 -0
- data/rust/codegen/src/asks.rs +103 -0
- data/rust/codegen/src/bridging.rs +1 -1
- data/rust/codegen/src/commands.rs +13 -8
- data/rust/codegen/src/constraints.rs +25 -0
- data/rust/codegen/src/domain_generator.rs +11 -9
- data/rust/codegen/src/expr_emitter.rs +13 -0
- data/rust/codegen/src/json_codec.rs +54 -15
- data/rust/codegen/src/main.rs +2 -1
- data/rust/codegen/src/mutations.rs +174 -4
- data/rust/codegen/src/naming.rs +30 -0
- data/rust/codegen/src/queries.rs +11 -2
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/types.rs +166 -8
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +4 -4
- data/rust/host/src/bin/validate_harness.rs +43 -0
- data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
- data/rust/host/src/boundary_fuzz/ir.rs +1 -0
- data/rust/host/src/boundary_fuzz/server.rs +1 -1
- data/rust/host/src/checkout.rs +35 -19
- data/rust/host/src/commerce_ir.rs +408 -2
- data/rust/host/src/expr_json.rs +118 -0
- data/rust/host/src/mint.rs +1 -1
- data/rust/host/src/payments/tests.rs +27 -1
- data/rust/host/src/payments.rs +12 -4
- data/rust/host/src/query_step.rs +22 -4
- data/rust/host/src/reference_validate.rs +653 -108
- data/rust/host/src/resend.rs +2 -0
- data/rust/host/src/server.rs +19 -6
- data/rust/host/src/ui_schema.rs +132 -5
- data/rust/host/src/web/newsletter.rs +7 -8
- data/rust/host/src/web/newsletter_send.rs +38 -13
- data/rust/host/src/web/registrations.rs +229 -26
- data/rust/host/src/web.rs +3 -3
- data/rust/parser/src/emit.rs +38 -6
- data/rust/parser/src/expr/ast_json.rs +7 -1
- data/rust/parser/src/expr/resolver.rs +171 -6
- data/rust/parser/src/ir.rs +7 -0
- data/rust/parser/src/keywords.rs +24 -0
- data/rust/parser/src/main.rs +3 -0
- data/rust/parser/src/parse/aggregate.rs +2 -1
- data/rust/parser/src/parse/chapter.rs +2 -2
- data/rust/parser/src/parse/command.rs +2 -0
- data/rust/parser/src/parse/hecksagon.rs +5 -2
- data/rust/parser/src/parse/lifecycle.rs +81 -0
- data/rust/parser/src/parse/mod.rs +15 -1
- data/rust/parser/src/parse/policy.rs +13 -2
- data/rust/parser/src/parse/value_object.rs +13 -8
- data/rust/parser/src/ruby_value.rs +1 -1
- data/rust/src/exemplar/commands.rs +4 -4
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/json.rs +43 -1
- data/rust/src/kernel/cli.rs +7 -5
- data/rust/src/kernel/dispatch.rs +2 -2
- data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
- data/rust/src/kernel/expr.rs +73 -4
- data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
- data/rust/src/kernel/expression_operators/presence.rs +41 -3
- data/rust/src/kernel/expression_operators/text.rs +26 -2
- data/rust/src/kernel/json.rs +15 -0
- data/rust/src/kernel/mod.rs +1 -1
- data/rust/src/kernel/orchestrate.rs +54 -5
- data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
- data/rust/src/kernel/pattern.rs +60 -0
- data/rust/src/kernel/reference_lookup.rs +90 -7
- metadata +174 -36
- data/lib/hecks/doors/cli_door/nesting.rb +0 -34
- data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
- data/lib/hecks/doors/cli_door.rb +0 -124
- data/lib/hecks/doors/cli_runner/answers.rb +0 -138
- data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
- data/lib/hecks/doors/cli_runner/settling.rb +0 -115
- data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
- data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
- data/lib/hecks/doors/cli_runner.rb +0 -66
- data/lib/hecks/doors/command_request.rb +0 -119
- data/lib/hecks/doors/handle.rb +0 -208
- data/lib/hecks/doors/json_door.rb +0 -132
- data/lib/hecks/doors/launcher_options.rb +0 -247
- data/lib/hecks/doors/mcp_door/properties.rb +0 -47
- data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
- data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
- data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
- data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
- data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
- data/lib/hecks/doors/mcp_door.rb +0 -156
- data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
- data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
- data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
- data/lib/hecks/doors/mcp_door_scope.rb +0 -201
- data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
- data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
- data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
- data/lib/hecks/doors/ruby_door.rb +0 -80
- data/lib/hecks/doors/usage_cache.rb +0 -139
- data/prebuilt/0ee7f83cba59ce7d1c45c65ba166b071779f70143449f584f33ff7989f64eccb.marshal +0 -0
- data/prebuilt/6e4a6bcf0aa880e2e6b056e1846b70e5a768090b9dc998bbadeaf5d6bcfd3db8.marshal +0 -0
- data/prebuilt/verdicts-91aaf93ce8981bd9a754948f075d95650dc2ec6435015376f5c60992b36a24b1.json +0 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require_relative "capability_values"
|
|
2
|
+
|
|
1
3
|
module Hecks
|
|
2
4
|
module Projector
|
|
3
5
|
module Exporter
|
|
@@ -5,6 +7,8 @@ module Hecks
|
|
|
5
7
|
# membership, identity, newsletter, registrations, payments): a per-deployment binding fact,
|
|
6
8
|
# not part of the declared IR. Each answers `{}` when nothing the domain attaches provides it.
|
|
7
9
|
module Bindings
|
|
10
|
+
include CapabilityValues
|
|
11
|
+
|
|
8
12
|
# Which chapter this domain's role checks resolve against
|
|
9
13
|
# (`Registry#authorization_provider_for`); `{}` if none does.
|
|
10
14
|
#
|
|
@@ -70,16 +74,20 @@ module Hecks
|
|
|
70
74
|
# @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
|
|
71
75
|
# @param domain_name [String] the domain to export the newsletter binding for
|
|
72
76
|
# @return [Hash{Symbol => String, nil}] `:provider` (name), `:subscribe`, `:add_name`,
|
|
73
|
-
# `:confirm`, `:unsubscribe` (qualified verbs),
|
|
74
|
-
# leading qualified aggregate name)
|
|
75
|
-
#
|
|
77
|
+
# `:confirm`, `:unsubscribe` (qualified verbs), `:aggregate` (`:subscribe`'s own
|
|
78
|
+
# leading qualified aggregate name) and, when declared, `:awaiting_confirmation`,
|
|
79
|
+
# `:receives_issues` and `:left` (the states of the lifecycle marks they name) and
|
|
80
|
+
# `:confirm_window` and `:unsubscribe_window` (seconds); `{}` if nothing this domain
|
|
81
|
+
# attaches provides newsletter
|
|
76
82
|
def newsletter(registry, domain_name)
|
|
77
83
|
provider = registry.newsletter_provider_for(domain_name)
|
|
78
84
|
return {} unless provider
|
|
79
85
|
|
|
80
86
|
verbs = verbs_of(provider, Bluebook::Capabilities::NEWSLETTER,
|
|
81
87
|
:subscribe, :add_name, :confirm, :unsubscribe)
|
|
82
|
-
{ provider: provider.name, **verbs, aggregate: aggregate_of(verbs[:subscribe])
|
|
88
|
+
{ provider: provider.name, **verbs, aggregate: aggregate_of(verbs[:subscribe]),
|
|
89
|
+
**all_marked_states(provider, Bluebook::Capabilities::NEWSLETTER),
|
|
90
|
+
**all_durations(provider, Bluebook::Capabilities::NEWSLETTER) }
|
|
83
91
|
end
|
|
84
92
|
|
|
85
93
|
# Which chapter answers sending a newsletter issue
|
|
@@ -107,15 +115,18 @@ module Hecks
|
|
|
107
115
|
# @param domain_name [String] the domain to export the registrations binding for
|
|
108
116
|
# @return [Hash{Symbol => String, nil}] `:provider` (name), `:schedule`, `:request`
|
|
109
117
|
# (qualified verbs), `:event_aggregate` and `:registration_aggregate` (each verb's own
|
|
110
|
-
# leading qualified aggregate name)
|
|
111
|
-
#
|
|
118
|
+
# leading qualified aggregate name) and, when declared, `:frees_seat` (the states of the
|
|
119
|
+
# lifecycle mark it names) and `:registered_at` (the attribute that stamps a request);
|
|
120
|
+
# `{}` if nothing this domain attaches provides registrations
|
|
112
121
|
def registrations(registry, domain_name)
|
|
113
122
|
provider = registry.registrations_provider_for(domain_name)
|
|
114
123
|
return {} unless provider
|
|
115
124
|
|
|
116
125
|
verbs = verbs_of(provider, Bluebook::Capabilities::REGISTRATIONS, :schedule, :request)
|
|
117
126
|
{ provider: provider.name, **verbs,
|
|
118
|
-
**aggregates_of(verbs, event_aggregate: :schedule, registration_aggregate: :request)
|
|
127
|
+
**aggregates_of(verbs, event_aggregate: :schedule, registration_aggregate: :request),
|
|
128
|
+
**all_marked_states(provider, Bluebook::Capabilities::REGISTRATIONS),
|
|
129
|
+
**all_words(provider, Bluebook::Capabilities::REGISTRATIONS) }
|
|
119
130
|
end
|
|
120
131
|
|
|
121
132
|
# Which chapter owns the payment-processor connection
|
|
@@ -142,14 +153,33 @@ module Hecks
|
|
|
142
153
|
# @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
|
|
143
154
|
# @param domain_name [String] the domain to export the payments binding for
|
|
144
155
|
# @return [Hash{Symbol => String, nil}] `:provider` (name), `:initiate`, `:succeeded`,
|
|
145
|
-
# `:failed` (qualified verbs),
|
|
146
|
-
# aggregate name)
|
|
156
|
+
# `:failed` (qualified verbs), `:aggregate` (`:initiate`'s own leading qualified
|
|
157
|
+
# aggregate name) and, when declared, `:holds_seat` (the states of the lifecycle mark
|
|
158
|
+
# it names) and `:lapse_reason` (the word a lapsed hold
|
|
159
|
+
# records); `{}` if nothing this domain attaches provides payments
|
|
147
160
|
def payments(registry, domain_name)
|
|
148
161
|
provider = registry.payments_provider_for(domain_name)
|
|
149
162
|
return {} unless provider
|
|
150
163
|
|
|
151
164
|
verbs = verbs_of(provider, Bluebook::Capabilities::PAYMENTS, :initiate, :succeeded, :failed)
|
|
152
|
-
{ provider: provider.name, **verbs, aggregate: aggregate_of(verbs[:initiate])
|
|
165
|
+
{ provider: provider.name, **verbs, aggregate: aggregate_of(verbs[:initiate]),
|
|
166
|
+
**marked_states(provider, Bluebook::Capabilities::PAYMENTS, :holds_seat),
|
|
167
|
+
**all_words(provider, Bluebook::Capabilities::PAYMENTS) }
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Which chapter owns the checkout boundary (`Registry#checkout_provider_for`); `{}` if
|
|
171
|
+
# none does.
|
|
172
|
+
#
|
|
173
|
+
# @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
|
|
174
|
+
# @param domain_name [String] the domain to export the checkout binding for
|
|
175
|
+
# @return [Hash{Symbol => Object}] `:provider` (name) and, when declared,
|
|
176
|
+
# `:webhook_tolerance` and `:session_hold` (whole seconds); `{}` if nothing this
|
|
177
|
+
# domain attaches provides checkout
|
|
178
|
+
def checkout(registry, domain_name)
|
|
179
|
+
provider = registry.checkout_provider_for(domain_name)
|
|
180
|
+
return {} unless provider
|
|
181
|
+
|
|
182
|
+
{ provider: provider.name, **all_durations(provider, Bluebook::Capabilities::CHECKOUT) }
|
|
153
183
|
end
|
|
154
184
|
|
|
155
185
|
private
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Projector
|
|
3
|
+
module Exporter
|
|
4
|
+
# The values a capability's optional contract entries resolve to in the exported fact: the
|
|
5
|
+
# states of a lifecycle `:mark` (ADR 0097), the whole seconds of a `:duration` (ADR 0098), and
|
|
6
|
+
# the word of a `:text` or `:attribute` entry (ADR 0099).
|
|
7
|
+
module CapabilityValues
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# `{ key => states }` for each optional `:mark` entry `provider` declares under
|
|
11
|
+
# `capability`.
|
|
12
|
+
def all_marked_states(provider, capability)
|
|
13
|
+
mark_keys = declared_keys(capability, :mark)
|
|
14
|
+
mark_keys.reduce({}) { |marks, key| marks.merge(marked_states(provider, capability, key)) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# `{ key => seconds }` for each optional `:duration` entry `provider` declares under
|
|
18
|
+
# `capability`: the whole-seconds default of the attribute it names.
|
|
19
|
+
def all_durations(provider, capability)
|
|
20
|
+
declared_keys(capability, :duration).each_with_object({}) do |key, durations|
|
|
21
|
+
verb = provider.provision(capability)&.fetch(key, nil)
|
|
22
|
+
durations[key] = Bluebook::Capabilities.duration_of(provider, verb) if verb
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# `{ key => word }` for each optional `:text` entry and `{ key => attribute_name }` for each
|
|
27
|
+
# optional `:attribute` entry `provider` declares under `capability` (ADR 0099).
|
|
28
|
+
def all_words(provider, capability)
|
|
29
|
+
words = { text: :text_of, attribute: :attribute_of }.flat_map do |kind, resolver|
|
|
30
|
+
declared_keys(capability, kind).filter_map do |key|
|
|
31
|
+
verb = provider.provision(capability)&.fetch(key, nil)
|
|
32
|
+
[key, Bluebook::Capabilities.public_send(resolver, provider, verb)] if verb
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
words.to_h
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# `{ key => states }` for an optional `:mark` entry `provider` declares under `capability`:
|
|
39
|
+
# the states of the named aggregate's lifecycle mark. `{}` when the entry is not declared.
|
|
40
|
+
def marked_states(provider, capability, key)
|
|
41
|
+
verb = provider.provision(capability)&.fetch(key, nil)
|
|
42
|
+
return {} unless verb
|
|
43
|
+
|
|
44
|
+
aggregate_name, mark = verb.split(".", 2)
|
|
45
|
+
{ key => provider.aggregate(aggregate_name).lifecycle.marked(mark) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The contract keys of `capability` whose kind is `kind`.
|
|
49
|
+
def declared_keys(capability, kind)
|
|
50
|
+
Bluebook::Capabilities::CONTRACTS.fetch(capability).select { |_, entry| entry == kind }.keys
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -16,7 +16,7 @@ module Hecks
|
|
|
16
16
|
# A policy as the sentence saying it fires on its own.
|
|
17
17
|
def policy_sentence(policy)
|
|
18
18
|
elsewhere = policy.target_domain ? " in #{policy.target_domain}" : ""
|
|
19
|
-
"Whenever `#{policy.on_event}` happens, `#{policy.
|
|
19
|
+
"Whenever `#{policy.on_event}` happens, `#{policy.reaches}` fires on its own#{elsewhere} — " \
|
|
20
20
|
"nobody has to ask for it."
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -58,6 +58,17 @@ module Hecks
|
|
|
58
58
|
# before `projects_as` is called
|
|
59
59
|
def projection_needs_world? = @projection_needs_world || false
|
|
60
60
|
|
|
61
|
+
# Derives what kind of target this is from flags it already declares,
|
|
62
|
+
# so there is no second spelling to drift.
|
|
63
|
+
#
|
|
64
|
+
# @return [Symbol] `:export` if it needs a world, `:meta` if it needs a
|
|
65
|
+
# chapter declaring particular aggregates, otherwise `:projection`
|
|
66
|
+
def projection_category
|
|
67
|
+
return :export if projection_needs_world?
|
|
68
|
+
|
|
69
|
+
projection_declares.empty? ? :projection : :meta
|
|
70
|
+
end
|
|
71
|
+
|
|
61
72
|
# Names the capability module(s) a construct must satisfy, defaulting
|
|
62
73
|
# to plain chapter-hood when `projects_as` named none.
|
|
63
74
|
#
|
data/lib/hecks/projector.rb
CHANGED
|
@@ -4,10 +4,13 @@ module Hecks
|
|
|
4
4
|
#
|
|
5
5
|
# Projector.call(:ir, bluebook: chapter)
|
|
6
6
|
#
|
|
7
|
-
# A
|
|
8
|
-
#
|
|
9
|
-
# `
|
|
10
|
-
# `
|
|
7
|
+
# A target's category is derived from the flags it declares (`category`):
|
|
8
|
+
#
|
|
9
|
+
# - `:projection` is inert and derived from any chapter's declaration.
|
|
10
|
+
# - `:meta` also needs a chapter declaring particular aggregates (`declares:`),
|
|
11
|
+
# so it only fits hecks's own grammar chapters.
|
|
12
|
+
# - `:export` (CloudFormation, ...) also needs `.world` bindings and declares
|
|
13
|
+
# `needs_world: true`; that is adapter wiring, not a projection of the domain.
|
|
11
14
|
module Projector
|
|
12
15
|
class UnknownProjector < StandardError; end
|
|
13
16
|
# A chapter-scoped projector was handed something that is not a chapter.
|
|
@@ -38,7 +41,9 @@ module Hecks
|
|
|
38
41
|
# @raise [WrongConstruct] if `bluebook` lacks what the projector requires
|
|
39
42
|
def call(name, bluebook:, options: {}, world: nil)
|
|
40
43
|
projector = registry.fetch(name.to_sym) do
|
|
41
|
-
raise UnknownProjector,
|
|
44
|
+
raise UnknownProjector,
|
|
45
|
+
"no projector registered for #{name.inspect} — registered: " \
|
|
46
|
+
"#{registered_by_category.map { |kind, keys| "#{kind} #{keys.inspect}" }.join(", ")}"
|
|
42
47
|
end
|
|
43
48
|
admits!(name, projector, bluebook, world)
|
|
44
49
|
projector.call(bluebook: bluebook, options: world ? options.merge(world: world) : options)
|
|
@@ -114,6 +119,28 @@ module Hecks
|
|
|
114
119
|
# @return [Array<Symbol>] every key currently registered
|
|
115
120
|
def registered = registry.keys
|
|
116
121
|
|
|
122
|
+
# Gives the category of a registered target, derived from its flags.
|
|
123
|
+
# A target that answers no `projection_category` (the built-ins, which do
|
|
124
|
+
# not extend `Target`) is a plain projection.
|
|
125
|
+
#
|
|
126
|
+
# @param name [String, Symbol] the registered projector's key
|
|
127
|
+
# @return [Symbol] `:projection`, `:meta` or `:export` (`:projection` for an
|
|
128
|
+
# unregistered `name`)
|
|
129
|
+
def category(name)
|
|
130
|
+
projector = registry.fetch(name.to_sym) { return :projection }
|
|
131
|
+
projector.respond_to?(:projection_category) ? projector.projection_category : :projection
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Lists every registered key under its category.
|
|
135
|
+
#
|
|
136
|
+
# @return [Hash{Symbol => Array<Symbol>}] `:projection`, `:meta` and `:export`
|
|
137
|
+
# (always all three), each with its sorted keys
|
|
138
|
+
def registered_by_category
|
|
139
|
+
grouped = { projection: [], meta: [], export: [] }
|
|
140
|
+
registered.sort.each { |key| grouped[category(key)] << key }
|
|
141
|
+
grouped
|
|
142
|
+
end
|
|
143
|
+
|
|
117
144
|
# Gives the live registry, initializing it on first use.
|
|
118
145
|
#
|
|
119
146
|
# @return [Hash{Symbol => Module, Class, #call}] the live key => projector registry
|
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
require "open3"
|
|
4
4
|
require "json"
|
|
5
|
+
require_relative "../../indifferent_key"
|
|
5
6
|
|
|
6
7
|
module Hecks
|
|
7
8
|
module Adapters
|
|
8
9
|
# The `CI` port adapter: answers `Clearance.CI.Run` from GitHub's check runs for a commit.
|
|
9
10
|
# Raises when no checks exist, any are still running, or any failed.
|
|
10
11
|
class GithubChecks
|
|
12
|
+
# Raised when check runs cannot be read or have not settled.
|
|
13
|
+
class Error < RuntimeError; end
|
|
14
|
+
|
|
11
15
|
# All keywords are unused; the adapter takes no per-boot configuration.
|
|
12
16
|
def initialize(aggregate: nil, settings: {}, root: nil); end
|
|
13
17
|
|
|
@@ -29,7 +33,7 @@ module Hecks
|
|
|
29
33
|
#
|
|
30
34
|
# @param commit [Hash, String] a materialized value object (`{value: sha}`) or a bare sha
|
|
31
35
|
# @return [Hash] `{ summary: { value: ... } }` when every check is green
|
|
32
|
-
# @raise [
|
|
36
|
+
# @raise [Error] when the sha is malformed, `gh` is missing or fails, no checks
|
|
33
37
|
# exist, any are incomplete, or any failed
|
|
34
38
|
def run(commit:, **)
|
|
35
39
|
sha = valid_sha(commit)
|
|
@@ -49,7 +53,7 @@ module Hecks
|
|
|
49
53
|
# @param names [Array<String>] the checks to look up
|
|
50
54
|
# @return [Hash{String => Symbol}] each name to `:passed`, `:failed`, `:pending` (reported but
|
|
51
55
|
# not completed) or `:missing` (not reported at all)
|
|
52
|
-
# @raise [
|
|
56
|
+
# @raise [Error] when the sha is malformed or `gh` is missing or fails
|
|
53
57
|
def states(commit:, names:)
|
|
54
58
|
reported = check_runs(valid_sha(commit)).select { |run| actions?(run) }.group_by { |run| run["name"] }
|
|
55
59
|
latest = reported.transform_values { |runs| runs.max_by { |run| run["id"].to_i } }
|
|
@@ -62,7 +66,7 @@ module Hecks
|
|
|
62
66
|
|
|
63
67
|
def valid_sha(commit)
|
|
64
68
|
sha = sha_of(commit)
|
|
65
|
-
raise "not a commit sha: #{sha.inspect}" unless sha.match?(SHA_PATTERN)
|
|
69
|
+
raise Error, "not a commit sha: #{sha.inspect}" unless sha.match?(SHA_PATTERN)
|
|
66
70
|
|
|
67
71
|
sha
|
|
68
72
|
end
|
|
@@ -70,11 +74,11 @@ module Hecks
|
|
|
70
74
|
# The check runs against `sha`, once none is still running.
|
|
71
75
|
def settled_runs(sha)
|
|
72
76
|
runs = check_runs(sha)
|
|
73
|
-
raise "gh reports no checks at all against #{sha}" if runs.empty?
|
|
77
|
+
raise Error, "gh reports no checks at all against #{sha}" if runs.empty?
|
|
74
78
|
|
|
75
79
|
# Refuse rather than answer on an incomplete run; asking again once it settles is safe.
|
|
76
80
|
incomplete = runs.reject { |run| run["status"] == "completed" }
|
|
77
|
-
raise "checks against #{sha} are still running — asked before they settled" if incomplete.any?
|
|
81
|
+
raise Error, "checks against #{sha} are still running — asked before they settled" if incomplete.any?
|
|
78
82
|
|
|
79
83
|
runs
|
|
80
84
|
end
|
|
@@ -95,7 +99,7 @@ module Hecks
|
|
|
95
99
|
def sha_of(commit)
|
|
96
100
|
return commit if commit.is_a?(String)
|
|
97
101
|
|
|
98
|
-
|
|
102
|
+
IndifferentKey.read(commit, :value).to_s
|
|
99
103
|
end
|
|
100
104
|
|
|
101
105
|
# Every page of check runs: a red run past the first page must still be seen.
|
|
@@ -115,11 +119,11 @@ module Hecks
|
|
|
115
119
|
def check_run_page(sha, page)
|
|
116
120
|
path = "repos/{owner}/{repo}/commits/#{sha}/check-runs?per_page=#{PER_PAGE}&page=#{page}"
|
|
117
121
|
out, err, status = Open3.capture3("gh", "api", path)
|
|
118
|
-
raise "gh api check-runs failed for #{sha}: #{err.strip.empty? ? out.strip : err.strip}" unless status.success?
|
|
122
|
+
raise Error, "gh api check-runs failed for #{sha}: #{err.strip.empty? ? out.strip : err.strip}" unless status.success?
|
|
119
123
|
|
|
120
124
|
JSON.parse(out)
|
|
121
125
|
rescue Errno::ENOENT
|
|
122
|
-
raise "gh is not installed or not on PATH — cannot read check runs for #{sha}"
|
|
126
|
+
raise Error, "gh is not installed or not on PATH — cannot read check runs for #{sha}"
|
|
123
127
|
end
|
|
124
128
|
end
|
|
125
129
|
end
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "open3"
|
|
4
|
+
require_relative "../../indifferent_key"
|
|
4
5
|
|
|
5
6
|
module Hecks
|
|
6
7
|
module Adapters
|
|
7
8
|
# The `IssueTracker` port's adapter: files a `QualityControl::Ticket` as a GitHub issue.
|
|
8
9
|
# Raises when `gh` refuses, which the runtime records as the ticket's refusal.
|
|
9
10
|
class GithubIssues
|
|
11
|
+
# Raised when `gh` cannot file the issue.
|
|
12
|
+
class Error < RuntimeError; end
|
|
13
|
+
|
|
10
14
|
# @param aggregate [Object, nil] unused
|
|
11
15
|
# @param settings [Hash] unused
|
|
12
16
|
# @param root [String, nil] unused
|
|
@@ -18,7 +22,7 @@ module Hecks
|
|
|
18
22
|
# value objects (`{value: ...}`) or bare strings, and `pull_request` names a proposed fix
|
|
19
23
|
# @return [Hash{Symbol => Hash}] `number:` and `url:` of the issue, in the shape the
|
|
20
24
|
# ticket's `Filed` command takes
|
|
21
|
-
# @raise [
|
|
25
|
+
# @raise [Error] when `gh` exits non-zero or prints no issue URL
|
|
22
26
|
def file(**held)
|
|
23
27
|
out = create_issue(plain(held[:repository]), plain(held[:title]).to_s, issue_body(held))
|
|
24
28
|
url, number = issue_url_and_number(out)
|
|
@@ -37,7 +41,7 @@ module Hecks
|
|
|
37
41
|
def issue_url_and_number(out)
|
|
38
42
|
url = out.strip.lines.last.to_s.strip
|
|
39
43
|
number = url[%r{/issues/(\d+)\z}, 1]
|
|
40
|
-
raise "gh issue create printed no issue URL: #{out.strip.inspect}" unless number
|
|
44
|
+
raise Error, "gh issue create printed no issue URL: #{out.strip.inspect}" unless number
|
|
41
45
|
|
|
42
46
|
[url, number]
|
|
43
47
|
end
|
|
@@ -45,7 +49,7 @@ module Hecks
|
|
|
45
49
|
def create_issue(repository, title, body)
|
|
46
50
|
out, err, status = Open3.capture3("gh", "issue", "create", "--repo", repository,
|
|
47
51
|
"--title", title, "--body", body)
|
|
48
|
-
raise "gh issue create failed — #{err.strip.empty? ? out.strip : err.strip}" unless status.success?
|
|
52
|
+
raise Error, "gh issue create failed — #{err.strip.empty? ? out.strip : err.strip}" unless status.success?
|
|
49
53
|
|
|
50
54
|
out
|
|
51
55
|
end
|
|
@@ -54,7 +58,7 @@ module Hecks
|
|
|
54
58
|
def plain(field)
|
|
55
59
|
return field unless field.is_a?(Hash)
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
IndifferentKey.read(field, :value)
|
|
58
62
|
end
|
|
59
63
|
end
|
|
60
64
|
end
|
|
@@ -11,8 +11,8 @@ require_relative "qa_open_pr/recording"
|
|
|
11
11
|
module Hecks
|
|
12
12
|
module QualityControlCli
|
|
13
13
|
# The command behind `hecks quality_control patch.open`: opens a PR and records it in the
|
|
14
|
-
# QualityControl ledger in one step. It is the only
|
|
15
|
-
# `QualityControl::Improvement.Open`.
|
|
14
|
+
# QualityControl ledger in one step. It is the only entry point into
|
|
15
|
+
# `QualityControl::Patch.Open` and `QualityControl::Improvement.Open`.
|
|
16
16
|
#
|
|
17
17
|
# It refuses (exit 1, nothing opened or recorded) unless the ledger's own `Open` command would
|
|
18
18
|
# take the record and the `GitPr` adapter agrees. The command's `given`s are the rules, asked
|
|
@@ -10,7 +10,7 @@ module Hecks
|
|
|
10
10
|
private
|
|
11
11
|
|
|
12
12
|
# Re-runs only the comparison that surprised; `seat` matters only for `self_consistency`,
|
|
13
|
-
# which reads the Rust binary's rehydration
|
|
13
|
+
# which reads the Rust binary's rehydration entry point under a differential seat.
|
|
14
14
|
def candidate_divergences(check_mode, seat, steps)
|
|
15
15
|
case check_mode
|
|
16
16
|
when :differential
|
|
@@ -37,7 +37,7 @@ module Hecks
|
|
|
37
37
|
|
|
38
38
|
def print_finding_instructions
|
|
39
39
|
puts "No Bug was logged (that needs a real failing-test demonstration, which"
|
|
40
|
-
puts "needs judgment this script does not have — hecks quality_control log is the
|
|
40
|
+
puts "needs judgment this script does not have — hecks quality_control log is the entry point)"
|
|
41
41
|
puts "and the sweep was not concluded. The ledger's own SuspendOnSurprise"
|
|
42
42
|
puts "policy took the target out of the rotation; it stays out until a PERSON"
|
|
43
43
|
puts "runs: hecks quality_control ask run target=#{@target_reference} " \
|
|
@@ -130,7 +130,7 @@ module Hecks
|
|
|
130
130
|
@trace_last = now
|
|
131
131
|
end
|
|
132
132
|
|
|
133
|
-
# Default `
|
|
133
|
+
# Default `install_driving` so `QualityControl::Target` and friends are top-level constants.
|
|
134
134
|
def boot_ledger
|
|
135
135
|
Hecks.boot(@domain_dir)
|
|
136
136
|
rescue StandardError => e
|
|
@@ -164,7 +164,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
164
164
|
role "QA engineer"
|
|
165
165
|
goal "Hand a chapter back to the rotation, stamped with when, what this pass was worth, and how the clean streak moves"
|
|
166
166
|
|
|
167
|
-
# `now` is the argument and `last_swept` the field, so the
|
|
167
|
+
# `now` is the argument and `last_swept` the field, so the entry point can fill the clock.
|
|
168
168
|
#
|
|
169
169
|
# `next_streak` is the caller's to compute (`clean_streak + 1` if every check held and none
|
|
170
170
|
# was ever surprised, else 0): `Target` cannot read `Sweep` state and no policy payload can
|
|
@@ -530,6 +530,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
530
530
|
end
|
|
531
531
|
|
|
532
532
|
lifecycle :status, default: "sweeping" do
|
|
533
|
+
mark :failure, "abandoned"
|
|
533
534
|
transition "Check" => "sweeping", from: "sweeping"
|
|
534
535
|
transition "Waive" => "sweeping", from: "sweeping"
|
|
535
536
|
transition "Conclude" => "concluded", from: "sweeping"
|
|
@@ -778,7 +779,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
778
779
|
invariant("a bug sequence is positive") { value.positive? }
|
|
779
780
|
end
|
|
780
781
|
|
|
781
|
-
# Prefixed on purpose: `
|
|
782
|
+
# Prefixed on purpose: `Adapters::Driving::Ruby.install` puts every aggregate name on Object, so a
|
|
782
783
|
# value object sharing a word with any aggregate resolves to that class once the other domain
|
|
783
784
|
# boots.
|
|
784
785
|
value_object "BugTitle" do
|
|
@@ -1456,6 +1457,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
1456
1457
|
# filing. `Submit` is non-creating, so `TicketSubmitted` carries `{ id: ... }`, which a
|
|
1457
1458
|
# reference slot takes.
|
|
1458
1459
|
lifecycle :status, default: "raised" do
|
|
1460
|
+
mark :failure, "refused", "abandoned"
|
|
1459
1461
|
transition "Submit" => "submitting", from: ["raised", "refused"]
|
|
1460
1462
|
transition "Filed" => "filed", from: "submitting"
|
|
1461
1463
|
transition "Refused" => "refused", from: "submitting"
|
|
@@ -1866,6 +1868,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
1866
1868
|
# proof-only PR can be closed while still "opened" (`retire_if_settled?` hit
|
|
1867
1869
|
# `LifecycleRefused`).
|
|
1868
1870
|
lifecycle :status, default: "opened" do
|
|
1871
|
+
mark :failure, "needs_fix"
|
|
1869
1872
|
transition "Land" => "landed", from: ["opened", "needs_fix"]
|
|
1870
1873
|
transition "Regress" => "needs_fix", from: "landed"
|
|
1871
1874
|
transition "Merge" => "merged", from: ["opened", "landed", "needs_fix"]
|
|
@@ -1983,6 +1986,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
1983
1986
|
end
|
|
1984
1987
|
|
|
1985
1988
|
lifecycle :status, default: "running" do
|
|
1989
|
+
mark :failure, "red"
|
|
1986
1990
|
transition "Passed" => "green", from: "running"
|
|
1987
1991
|
transition "Failed" => "red", from: "running"
|
|
1988
1992
|
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hecks/vocabulary"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Release
|
|
7
|
+
# Whether the commit that set the version carries the owner's approval of the release.
|
|
8
|
+
#
|
|
9
|
+
# Releases are the owner's decision. A bump on `main` reaches `stable` by itself, so the bump
|
|
10
|
+
# commit must say, in its message, that a release is wanted: a trailer line named by the
|
|
11
|
+
# `release_trailer` of the `Lane` row a release is cut from, `Release-Approved-By: <name>`. The
|
|
12
|
+
# decision is read from git history alone, so it can be made again on any later run. It reads
|
|
13
|
+
# only the Vocabulary table, so `release.yml` can run it with the runner's own Ruby.
|
|
14
|
+
module Approval
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
# @return [String] the trailer key, read from the `Lane` row that feeds a tag
|
|
18
|
+
# @raise [KeyError] when that row names no trailer
|
|
19
|
+
def trailer
|
|
20
|
+
row = Hecks::Vocabulary.rows("Lane").find { |lane| !lane["feeds"].to_s.empty? }
|
|
21
|
+
key = row && row["release_trailer"].to_s
|
|
22
|
+
raise KeyError, "the Lane row a release is cut from names no release_trailer" if key.nil? || key.empty?
|
|
23
|
+
|
|
24
|
+
key
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param message [String] a commit message
|
|
28
|
+
# @return [String, nil] who approved, from the first trailer line with a name, or nil
|
|
29
|
+
def approver(message)
|
|
30
|
+
line = message.to_s.lines.map(&:strip).find { |text| text.match?(/\A#{Regexp.escape(trailer)}:\s*\S/) }
|
|
31
|
+
line&.sub(/\A[^:]*:\s*/, "")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param message [String] the message of the commit that set the version
|
|
35
|
+
# @param tag_exists [Boolean] whether the release's tag already stands on origin: a release
|
|
36
|
+
# that was begun was approved when it began, so finishing it asks again for nothing
|
|
37
|
+
# @param explicit [String, nil] a name the owner passed to a by-hand run
|
|
38
|
+
# @return [Array(Boolean, String)] whether the release may go ahead, and the words to print
|
|
39
|
+
def verdict(message:, tag_exists: false, explicit: nil)
|
|
40
|
+
return [true, "approved by #{explicit.strip} (by-hand run)"] unless explicit.to_s.strip.empty?
|
|
41
|
+
return [true, "the tag already stands; finishing a release that was begun"] if tag_exists
|
|
42
|
+
|
|
43
|
+
who = approver(message)
|
|
44
|
+
return [true, "approved by #{who} (#{trailer} trailer)"] if who
|
|
45
|
+
|
|
46
|
+
[false, "the commit that set the version has no '#{trailer}: <name>' line in its message, so it " \
|
|
47
|
+
"is not a release the owner asked for. Nothing was tagged or pushed. To release it, " \
|
|
48
|
+
"re-run by hand with the approval: gh workflow run release.yml -f tag=vX.Y.Z -f approved_by=<name>"]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Reads the decision's inputs from the environment `release.yml` gives it.
|
|
52
|
+
#
|
|
53
|
+
# @param env [#[]] `COMMIT_MESSAGE`, `TAG_EXISTS` (true/false), `APPROVED_BY`
|
|
54
|
+
# @param out [#puts] where the words go
|
|
55
|
+
# @return [Integer] the exit status: 0 to go ahead, 1 to refuse
|
|
56
|
+
def check(env: ENV, out: $stdout)
|
|
57
|
+
ok, words = verdict(message: env["COMMIT_MESSAGE"], tag_exists: env["TAG_EXISTS"] == "true",
|
|
58
|
+
explicit: env["APPROVED_BY"])
|
|
59
|
+
out.puts(ok ? "release approval: #{words}" : "::error::#{words}")
|
|
60
|
+
ok ? 0 : 1
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
require_relative "../
|
|
1
|
+
require_relative "../adapters/driving/handle"
|
|
2
2
|
|
|
3
3
|
module Hecks
|
|
4
4
|
class Router
|
|
5
5
|
class NamespaceInstaller
|
|
6
6
|
# One aggregate's repository and event log, read through its dispatcher.
|
|
7
|
-
|
|
7
|
+
AggregateReader = Struct.new(:dispatcher, :domain, :aggregate) do
|
|
8
8
|
# @param entry [Router::Entry] a router entry
|
|
9
|
-
# @return [
|
|
9
|
+
# @return [AggregateReader, nil] the reader for the entry's aggregate; nil for a
|
|
10
|
+
# query/command-only entry
|
|
10
11
|
# whose owner isn't a real aggregate root
|
|
11
12
|
def self.for(entry)
|
|
12
13
|
domain = entry.fqn.domain
|
|
@@ -29,7 +30,7 @@ module Hecks
|
|
|
29
30
|
def all = repository.all.map { |instance| handle(instance) }
|
|
30
31
|
|
|
31
32
|
def handle(instance)
|
|
32
|
-
|
|
33
|
+
Adapters::Driving::Handle.new(dispatcher: dispatcher, domain: domain, aggregate: aggregate, instance: instance)
|
|
33
34
|
end
|
|
34
35
|
end
|
|
35
36
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require_relative "../fqn"
|
|
2
2
|
require_relative "options_proxy"
|
|
3
|
-
require_relative "
|
|
3
|
+
require_relative "aggregate_reader"
|
|
4
4
|
|
|
5
5
|
module Hecks
|
|
6
6
|
class Router
|
|
@@ -12,11 +12,11 @@ module Hecks
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
# Installs every current-version route as a namespace constant/method, plus
|
|
15
|
-
# the aggregate CRUD
|
|
15
|
+
# the aggregate CRUD module and short `Aggregate.verb` shortcuts.
|
|
16
16
|
def install!
|
|
17
17
|
current_entries.each { |entry| install_namespace_entry(entry) }
|
|
18
18
|
install_shortcuts!
|
|
19
|
-
|
|
19
|
+
install_aggregate_modules!
|
|
20
20
|
self
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -48,26 +48,26 @@ module Hecks
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
# `.find`/`.all`/`.count`/`.events`/`.repository`, matching `Hecks.boot`'s
|
|
51
|
-
#
|
|
52
|
-
def
|
|
51
|
+
# module; grouped by (realm, domain, aggregate) since the five don't vary by verb.
|
|
52
|
+
def install_aggregate_modules!
|
|
53
53
|
current_entries.reject { |entry| entry.fqn.aggregate.nil? }
|
|
54
54
|
.group_by { |entry| [entry.fqn.realm, entry.fqn.domain, entry.fqn.aggregate] }
|
|
55
|
-
.each_value { |entries|
|
|
55
|
+
.each_value { |entries| install_aggregate_module(entries.first) }
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
return unless
|
|
58
|
+
def install_aggregate_module(entry)
|
|
59
|
+
reader = AggregateReader.for(entry)
|
|
60
|
+
return unless reader
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
define_module_methods(namespace_for(entry.fqn.realm, entry.fqn.domain, entry.fqn.aggregate), reader)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
def
|
|
66
|
-
target.define_singleton_method(:repository) {
|
|
67
|
-
target.define_singleton_method(:count) {
|
|
68
|
-
target.define_singleton_method(:events) {
|
|
69
|
-
target.define_singleton_method(:find) { |id|
|
|
70
|
-
target.define_singleton_method(:all) {
|
|
65
|
+
def define_module_methods(target, reader)
|
|
66
|
+
target.define_singleton_method(:repository) { reader.repository }
|
|
67
|
+
target.define_singleton_method(:count) { reader.repository.count }
|
|
68
|
+
target.define_singleton_method(:events) { reader.events }
|
|
69
|
+
target.define_singleton_method(:find) { |id| reader.find(id) }
|
|
70
|
+
target.define_singleton_method(:all) { reader.all }
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def install_shortcuts!
|
data/lib/hecks/router.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module Hecks
|
|
2
|
-
# The public, project-wide dispatch
|
|
2
|
+
# The public, project-wide dispatch entry point. It owns address resolution only;
|
|
3
3
|
# each discovered Bluebook keeps its own runtime and persistence bindings.
|
|
4
4
|
class Router
|
|
5
5
|
class UnknownAddress < StandardError; end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Runtime
|
|
3
|
+
# The one place the runtime answers a default when a read raises, so a deliberate guard is
|
|
4
|
+
# named at its call site and `Hecks/SwallowedRescue` can refuse every other silent swallow.
|
|
5
|
+
#
|
|
6
|
+
# BestEffort.call(false) { adapter_class.tenant_capable? } # => false when the lookup raises
|
|
7
|
+
module BestEffort
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# Runs the block and answers its value, or `default` when it raises a StandardError.
|
|
11
|
+
#
|
|
12
|
+
# @param default [Object] what to answer when the block raises
|
|
13
|
+
# @yield the read that may raise
|
|
14
|
+
# @return [Object] the block's value, or `default`
|
|
15
|
+
def call(default)
|
|
16
|
+
yield
|
|
17
|
+
rescue StandardError
|
|
18
|
+
default
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|