hecks 3.10.0 → 3.12.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 +158 -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 +70 -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/newcomer.rb +50 -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/read_models.rb +1 -1
- 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 +4 -15
- data/lib/hecks/bluebook/assembly/scalars.rb +39 -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 +24 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +6 -3
- 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 +2 -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 +34 -4
- 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 +33 -7
- 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/statuses.rb +2 -1
- 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 +133 -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/terminal.rb +13 -11
- 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 +53 -18
- data/lib/hecks/hecks/hecks.world +13 -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.behaviors +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.behaviors +19 -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 +63 -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/projection_files/expression_tables.rb +7 -2
- data/lib/hecks/projections/deploy/box/compose.rb +6 -4
- data/lib/hecks/projections/deploy/box/edge.rb +62 -0
- data/lib/hecks/projections/deploy/box/logging.rb +99 -0
- data/lib/hecks/projections/deploy/box/roll_scripts.rb +16 -8
- data/lib/hecks/projections/deploy/box/settings/cdn.rb +40 -0
- data/lib/hecks/projections/deploy/box/settings/logs.rb +40 -0
- data/lib/hecks/projections/deploy/box/settings/patterns.rb +11 -0
- data/lib/hecks/projections/deploy/box/settings/types.rb +16 -1
- data/lib/hecks/projections/deploy/box/settings.rb +6 -2
- data/lib/hecks/projections/deploy/box/stack_yaml.rb +5 -2
- data/lib/hecks/projections/deploy/box/templates/Makefile.tmpl +16 -0
- data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +8 -4
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +32 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/box/templates/edge.mk.tmpl +17 -0
- data/lib/hecks/projections/deploy/box/templates/edge.yaml.tmpl +65 -0
- data/lib/hecks/projections/deploy/box/templates/log-group.yaml.tmpl +12 -0
- data/lib/hecks/projections/deploy/box/templates/log-policy.yaml.tmpl +7 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +5 -4
- data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +5 -4
- data/lib/hecks/projections/deploy/box.rb +15 -3
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/shared.rb +5 -2
- 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 +26 -6
- data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
- data/lib/hecks/projections/site/cms_editor/checks.rb +14 -1
- 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 +1 -1
- 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/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 +96 -19
- data/lib/hecks/projections/site/cms_editor/setting.rb +52 -9
- data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +18 -1
- data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +251 -57
- 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 +32 -24
- 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 +78 -24
- data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +6 -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 +19 -2
- data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +31 -8
- 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 +131 -2
- 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 +8 -5
- 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 +229 -34
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +40 -81
- 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 +29 -12
- 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 +53 -17
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +178 -88
- 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 +2 -1
- 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 +49 -23
- data/lib/hecks/projections/site/role_probe.rb +2 -0
- data/lib/hecks/projections/site/site_root/workflow.rb +5 -0
- data/lib/hecks/projector/cli_audience.rb +22 -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/cli_projector/usage.rb +15 -4
- 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/host.rb +11 -4
- data/lib/hecks/rust_build/prebuilt_binary.rb +101 -0
- 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 +1 -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/ci_gate_decision.rb +35 -0
- data/lib/hecks/tools/ci_gates.rb +20 -1
- 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/files.rb +8 -0
- data/lib/hecks/tools/lanes/prebuilt_workflow.rb +81 -0
- data/lib/hecks/tools/lanes/rows.rb +6 -0
- data/lib/hecks/tools/lanes.rb +1 -0
- data/lib/hecks/tools/oidc_manifests.rb +4 -5
- data/lib/hecks/tools/regeneration_run.rb +11 -4
- data/lib/hecks/tools/release_only.rb +111 -0
- data/lib/hecks/tools/site_routes/editor_files.rb +12 -5
- 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 +19 -5
- 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/9ee54e2f0d4d6d3fe046827779b221e02de665fba911a048ae5dd8b8fbfb7d90.marshal +0 -0
- data/prebuilt/d74a00e996222c1d8b676d5959092effc46b4dc59e0555f3bf9554ac58e3094a.marshal +0 -0
- data/prebuilt/verdicts-df19f35868d3284a70f55e42dcef87ef4e65e6a678e5135f72e1f6cff4c1357b.json +1 -0
- data/rust/.gitignore +6 -0
- data/rust/Cargo.toml +6 -0
- 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/commands.rs +13 -8
- data/rust/codegen/src/constraints.rs +25 -0
- data/rust/codegen/src/domain_generator.rs +2 -0
- data/rust/codegen/src/expr_emitter.rs +13 -0
- data/rust/codegen/src/json_codec.rs +54 -15
- data/rust/codegen/src/main.rs +1 -0
- data/rust/codegen/src/mutations.rs +128 -3
- 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/extension.rs +13 -1
- data/rust/host/src/lib.rs +1 -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/platform_signin/attack_tests.rs +1100 -0
- data/rust/host/src/platform_signin/config.rs +512 -0
- data/rust/host/src/platform_signin/cookie.rs +237 -0
- data/rust/host/src/platform_signin/extension.rs +190 -0
- data/rust/host/src/platform_signin/fetcher.rs +322 -0
- data/rust/host/src/platform_signin/handler.rs +251 -0
- data/rust/host/src/platform_signin/handler_tests.rs +554 -0
- data/rust/host/src/platform_signin/jti.rs +196 -0
- data/rust/host/src/platform_signin/jwks.rs +542 -0
- data/rust/host/src/platform_signin/jws.rs +249 -0
- data/rust/host/src/platform_signin/known_answer_tests.rs +237 -0
- data/rust/host/src/platform_signin/mapping_tests.rs +254 -0
- data/rust/host/src/platform_signin/mod.rs +75 -0
- data/rust/host/src/platform_signin/mode_tests.rs +382 -0
- data/rust/host/src/platform_signin/pages.rs +204 -0
- data/rust/host/src/platform_signin/query.rs +233 -0
- data/rust/host/src/platform_signin/session.rs +151 -0
- data/rust/host/src/platform_signin/settings.rs +385 -0
- data/rust/host/src/platform_signin/strict_json.rs +351 -0
- data/rust/host/src/platform_signin/test_support.rs +235 -0
- data/rust/host/src/platform_signin/ttl_tests.rs +94 -0
- data/rust/host/src/platform_signin/verify.rs +604 -0
- data/rust/host/src/platform_signin/wiring_support.rs +319 -0
- 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 +53 -9
- 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 +34 -17
- 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 +66 -3
- data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
- 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 +178 -38
- 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/lib/hecks/projections/site/cms_editor/templates/src/ui/body_widget.js.tmpl +0 -282
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/media_picker.js.tmpl +0 -109
- data/prebuilt/c28d6d9db14eea3e17161e9d9c262efee30b256cf97693a7cfd552bce24b2f7f.marshal +0 -0
- data/prebuilt/cb375cba61ca0dbe8ddb9739c306510fe05e5f4b7a9b6955caff8224c63ea0e0.marshal +0 -0
- data/prebuilt/verdicts-d6d584ad43b5f3cebf88aafbbdbf024c38574acf18e06adffbc555f7786b1c2f.json +0 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
require "digest"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Adapters
|
|
7
|
+
module Driving
|
|
8
|
+
# Remembers the help a launcher prints, so a repeat `hecks` need not read the domain again.
|
|
9
|
+
#
|
|
10
|
+
# The help is a pure function of the domain's declarations and of the gem that projects them,
|
|
11
|
+
# and reading those is most of what a launcher costs. The answer is kept in a file named for a
|
|
12
|
+
# digest of everything it depends on (the domain's declaration files, the gem's judging code
|
|
13
|
+
# and adapters, the hecks and Ruby versions, the environment overlay, the command line and
|
|
14
|
+
# the program name),
|
|
15
|
+
# so any edit to one of them is simply a different entry: nothing is ever stale, only unused.
|
|
16
|
+
# Only an answer that ended well is kept, and any trouble with the cache itself is ignored
|
|
17
|
+
# and the help is worked out as if it did not exist.
|
|
18
|
+
#
|
|
19
|
+
# `HECKS_NO_USAGE_CACHE=1` turns it off; `HECKS_CACHE_DIR` moves it.
|
|
20
|
+
module UsageCache
|
|
21
|
+
# The variable that turns the cache off when set to anything but empty or `0`.
|
|
22
|
+
DISABLE_VARIABLE = "HECKS_NO_USAGE_CACHE".freeze
|
|
23
|
+
# The variable that names the directory the cache lives in.
|
|
24
|
+
DIRECTORY_VARIABLE = "HECKS_CACHE_DIR".freeze
|
|
25
|
+
# The declaration files whose contents shape a help text.
|
|
26
|
+
DECLARATIONS = "*.{bluebook,world,hecksagon,rb}".freeze
|
|
27
|
+
# How long an unused entry stays before the next write sweeps it away.
|
|
28
|
+
KEEP_SECONDS = 14 * 24 * 60 * 60
|
|
29
|
+
|
|
30
|
+
module_function
|
|
31
|
+
|
|
32
|
+
# Answers the cached help for this command line, or works it out with the block and
|
|
33
|
+
# remembers it.
|
|
34
|
+
#
|
|
35
|
+
# @param runtime [#directory] what `Hecks.describe` answered; anything else bypasses the
|
|
36
|
+
# cache
|
|
37
|
+
# @param argv [Array<String>] the command line
|
|
38
|
+
# @param program [String] how the caller was invoked
|
|
39
|
+
# @param audience [String] who the help is for, since the same line reads differently to
|
|
40
|
+
# each
|
|
41
|
+
# @yield works the answer out, loading the domain
|
|
42
|
+
# @yieldreturn [Array(String, Integer), nil] the text and status; nil if it runs a command
|
|
43
|
+
# @return [Array(String, Integer), nil] the block's answer, or the remembered one
|
|
44
|
+
def fetch(runtime, argv, program, audience: "")
|
|
45
|
+
directory = runtime.respond_to?(:directory) ? runtime.directory : nil
|
|
46
|
+
return yield unless directory && enabled?
|
|
47
|
+
|
|
48
|
+
file = entry_path(directory, argv, program, audience)
|
|
49
|
+
remembered = file && recall(file)
|
|
50
|
+
return remembered if remembered
|
|
51
|
+
|
|
52
|
+
answer = yield
|
|
53
|
+
remember(file, answer) if worth_keeping?(file, answer)
|
|
54
|
+
answer
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Whether an answer that ended well can be written to the entry's file.
|
|
58
|
+
def worth_keeping?(file, answer)
|
|
59
|
+
file && answer&.last&.zero?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [Boolean] whether the cache is on
|
|
63
|
+
def enabled?
|
|
64
|
+
off = ENV[DISABLE_VARIABLE].to_s
|
|
65
|
+
off.empty? || off == "0"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @return [String] the directory entries live in
|
|
69
|
+
def directory
|
|
70
|
+
ENV[DIRECTORY_VARIABLE] || File.join(ENV["XDG_CACHE_HOME"] || File.join(Dir.home, ".cache"), "hecks")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# The file that holds the answer for this command line, named by what it depends on.
|
|
74
|
+
#
|
|
75
|
+
# @return [String, nil] nil when the digest cannot be taken
|
|
76
|
+
def entry_path(domain, argv, program, audience = "")
|
|
77
|
+
digest = Digest::SHA256.new
|
|
78
|
+
key_parts(program, audience, argv).each { |part| digest << part.to_s << "\0" }
|
|
79
|
+
fingerprint_code(domain, digest)
|
|
80
|
+
File.join(directory, "usage-#{digest.hexdigest}.json")
|
|
81
|
+
rescue SystemCallError
|
|
82
|
+
nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @return [Array<String>] what the answer depends on besides the files: the gem and Ruby
|
|
86
|
+
# series, the environment overlay, the program and audience and the command line
|
|
87
|
+
def key_parts(program, audience, argv)
|
|
88
|
+
[Hecks::VERSION, Hecks::Bluebook::MetaValidator::VerdictCache.ruby_series,
|
|
89
|
+
ENV["HECKS_ENVIRONMENT"].to_s, program, audience, argv.join("\0")]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Feeds the digest every declaration file under `root`: its path below `root` and its bytes,
|
|
93
|
+
# so a touch or a checkout elsewhere keeps the entry and an edit does not.
|
|
94
|
+
def fingerprint(root, digest)
|
|
95
|
+
Dir.glob(File.join(root, "**", DECLARATIONS)).each do |file|
|
|
96
|
+
digest << file.delete_prefix(root) << "\0" << File.binread(file) << "\0"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Feeds the digest what shapes a help text: the domain's declarations, the judging trees of
|
|
101
|
+
# the gem (the same set the verdict cache keys on) and the adapters that render the text. An
|
|
102
|
+
# edit to the gem's projections, deploy or release code cannot change a help line.
|
|
103
|
+
def fingerprint_code(domain, digest)
|
|
104
|
+
fingerprint(domain, digest)
|
|
105
|
+
digest << Hecks::Bluebook::MetaValidator::VerdictCache.code_digest << "\0"
|
|
106
|
+
fingerprint(__dir__, digest)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Reads the remembered answer and marks the entry as used, so a sweep keeps what is read.
|
|
110
|
+
#
|
|
111
|
+
# @return [Array(String, Integer), nil] the remembered answer, or nil when none can be
|
|
112
|
+
# trusted
|
|
113
|
+
def recall(file)
|
|
114
|
+
record = JSON.parse(File.read(file))
|
|
115
|
+
File.utime(nil, nil, file)
|
|
116
|
+
[record.fetch("text"), record.fetch("status")]
|
|
117
|
+
rescue SystemCallError, JSON::ParserError, KeyError
|
|
118
|
+
nil
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Writes the answer beside its name and sweeps entries nobody has read for a while.
|
|
122
|
+
def remember(file, answer)
|
|
123
|
+
FileUtils.mkdir_p(File.dirname(file))
|
|
124
|
+
scratch = "#{file}.#{Process.pid}"
|
|
125
|
+
File.write(scratch, JSON.generate("text" => answer.first, "status" => answer.last))
|
|
126
|
+
File.rename(scratch, file)
|
|
127
|
+
sweep(File.dirname(file))
|
|
128
|
+
rescue SystemCallError
|
|
129
|
+
nil
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Removes the entries older than `KEEP_SECONDS`.
|
|
133
|
+
def sweep(dir)
|
|
134
|
+
cutoff = Time.now - KEEP_SECONDS
|
|
135
|
+
Dir.glob(File.join(dir, "usage-*.json")).each do |entry|
|
|
136
|
+
File.delete(entry) if File.mtime(entry) < cutoff
|
|
137
|
+
end
|
|
138
|
+
rescue SystemCallError
|
|
139
|
+
nil
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
module Hecks
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
module Adapters
|
|
3
|
+
# Driving adapters: how an outside caller reaches in. The Ruby one is the class-free public
|
|
4
|
+
# surface, installed per boot by `Runtime::Loader.bind_runtime`.
|
|
5
|
+
module Driving
|
|
6
|
+
end
|
|
4
7
|
end
|
|
5
8
|
|
|
6
9
|
# Installs facade constants at top level, replacing only what it installed itself.
|
|
@@ -44,7 +47,8 @@ module Hecks
|
|
|
44
47
|
end
|
|
45
48
|
end
|
|
46
49
|
|
|
47
|
-
require_relative "
|
|
48
|
-
require_relative "
|
|
49
|
-
require_relative "
|
|
50
|
-
require_relative "
|
|
50
|
+
require_relative "driving/admission"
|
|
51
|
+
require_relative "driving/handle"
|
|
52
|
+
require_relative "driving/ruby"
|
|
53
|
+
require_relative "driving/command_request"
|
|
54
|
+
require_relative "driving/json"
|
|
@@ -43,7 +43,7 @@ module Hecks
|
|
|
43
43
|
# Memory-only-guarded runtime
|
|
44
44
|
# @raise [Malformed] if any aggregate is bound to a non-Memory adapter
|
|
45
45
|
def boot_and_guard(files)
|
|
46
|
-
runtime = Hecks::Runtime::Loader.boot_files(files,
|
|
46
|
+
runtime = Hecks::Runtime::Loader.boot_files(files, install_driving: false)
|
|
47
47
|
guard_memory_only!(runtime)
|
|
48
48
|
runtime
|
|
49
49
|
end
|
|
@@ -35,7 +35,7 @@ module Hecks
|
|
|
35
35
|
def boot(workload, adapter, schema)
|
|
36
36
|
Hecks::Fuzzing::IsolatedBoot.call(workload.domain_path, adapter: adapter, database: DATABASE,
|
|
37
37
|
schema: schema, scratch: { database: DATABASE, schema: schema }) do |copy|
|
|
38
|
-
yield Hecks.boot(copy,
|
|
38
|
+
yield Hecks.boot(copy, install_driving: false)
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
require_relative "../naming"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Bluebook
|
|
5
|
+
# Maps a policy's `ask :name` onto the port operation the hecksagon declared for it.
|
|
6
|
+
#
|
|
7
|
+
# The match is by the event's aggregate and the ask's name against that aggregate's declared
|
|
8
|
+
# `asks`. When one aggregate has the same ask name on two ports, the operation the hecksagon
|
|
9
|
+
# marked with `ask_via` is the one; otherwise the ask is ambiguous.
|
|
10
|
+
module AskResolution
|
|
11
|
+
# One ask's outcome: the verb it resolves to, or why it resolves to none.
|
|
12
|
+
#
|
|
13
|
+
# @!attribute [r] verb
|
|
14
|
+
# @return [String, nil] `Aggregate::Port.Operation` when resolved, spelled as the
|
|
15
|
+
# `trigger Aggregate::Port::Operation` it stands for is
|
|
16
|
+
# @!attribute [r] problem
|
|
17
|
+
# @return [Symbol, nil] `:no_aggregate`, `:unmatched` or `:ambiguous` when not
|
|
18
|
+
# @!attribute [r] detail
|
|
19
|
+
# @return [String, nil] what the refusal says, naming the aggregate and candidates
|
|
20
|
+
Outcome = Struct.new(:verb, :problem, :detail, keyword_init: true) do
|
|
21
|
+
# @return [Boolean] whether the ask resolved to one operation
|
|
22
|
+
def resolved? = problem.nil?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
# Resolves one ask policy against a chapter's aggregates and their declared ports.
|
|
28
|
+
#
|
|
29
|
+
# @param chapter [Bluebook::Chapter] the chapter the policy is declared in
|
|
30
|
+
# @param policy [Bluebook::Policy] a policy that `asks?`
|
|
31
|
+
# @return [Outcome] the verb, or the problem
|
|
32
|
+
def call(chapter, policy)
|
|
33
|
+
aggregate = event_aggregate(chapter, policy)
|
|
34
|
+
return no_aggregate(policy) unless aggregate
|
|
35
|
+
|
|
36
|
+
found = picked(candidates(aggregate, policy.ask))
|
|
37
|
+
return unmatched(aggregate, policy) if found.empty?
|
|
38
|
+
return ambiguous(aggregate, policy, found) if found.size > 1
|
|
39
|
+
|
|
40
|
+
port, operation = found.first
|
|
41
|
+
Outcome.new(verb: "#{aggregate.hecks_name}::#{port.name}.#{operation.hecks_name}")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The operations a tie leaves once `ask_via` has spoken: the chosen ones, or all of them
|
|
45
|
+
# when the hecksagon chose none.
|
|
46
|
+
#
|
|
47
|
+
# @return [Array<Array(Bluebook::DomainPort, Bluebook::PortOperation)>]
|
|
48
|
+
def picked(found)
|
|
49
|
+
chosen = found.select { |_port, operation| operation.chosen? }
|
|
50
|
+
found.size > 1 && !chosen.empty? ? chosen : found
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Binds every ask policy of a chapter that resolves, leaving the rest for the boot gate and
|
|
54
|
+
# `model_check` to name.
|
|
55
|
+
#
|
|
56
|
+
# @param chapter [Bluebook::Chapter] the chapter whose policies to resolve
|
|
57
|
+
# @return [void]
|
|
58
|
+
def bind_resolved(chapter)
|
|
59
|
+
chapter.policies.select(&:asks?).each do |policy|
|
|
60
|
+
outcome = call(chapter, policy)
|
|
61
|
+
policy.resolve_ask!(outcome.verb) if outcome.resolved?
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Marks the named ask on the named port as the one policies reach, for `ask_via`, and
|
|
66
|
+
# re-binds every ask the pick settles.
|
|
67
|
+
#
|
|
68
|
+
# @param chapter [Bluebook::Chapter, nil] the chapter the aggregate belongs to
|
|
69
|
+
# @param aggregate_name [String] the aggregate declaring the port
|
|
70
|
+
# @param name [String, Symbol] the ask's declared name
|
|
71
|
+
# @param port [String, Symbol] the port whose ask is picked
|
|
72
|
+
# @return [void]
|
|
73
|
+
# @raise [Bluebook::DSL::Malformed] if no such ask is declared on that port yet
|
|
74
|
+
def pick!(chapter, aggregate_name, name, port)
|
|
75
|
+
held = chapter&.aggregate(aggregate_name)&.port(port)
|
|
76
|
+
operation = held&.operation(name)
|
|
77
|
+
unless operation
|
|
78
|
+
raise DSL::Malformed, "#{aggregate_name}.ask_via #{name.to_s.inspect}, port: #{port.to_s.inspect} " \
|
|
79
|
+
"names no declared ask — declare the port's `asks` before picking it"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
operation.choose!
|
|
83
|
+
bind_resolved(chapter)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# The aggregate whose event the policy reacts to: the one the event is qualified with, or
|
|
87
|
+
# the one whose commands emit it when the event is written bare.
|
|
88
|
+
#
|
|
89
|
+
# @return [Bluebook::Aggregate, nil]
|
|
90
|
+
def event_aggregate(chapter, policy)
|
|
91
|
+
qualifier = policy.event_qualifier
|
|
92
|
+
return chapter.aggregate(Naming.demodulise(qualifier)) if qualifier
|
|
93
|
+
|
|
94
|
+
chapter.aggregates.find do |aggregate|
|
|
95
|
+
aggregate.commands.any? { |command| Array(command.emits).include?(policy.event_name) }
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @return [Array<Array(Bluebook::DomainPort, Bluebook::PortOperation)>] each outbound
|
|
100
|
+
# operation on the aggregate's ports the ask name spells
|
|
101
|
+
def candidates(aggregate, ask)
|
|
102
|
+
aggregate.ports.flat_map do |port|
|
|
103
|
+
port.operations.select { |op| op.outbound? && Naming.snake(op.hecks_name) == ask.to_s }
|
|
104
|
+
.map { |operation| [port, operation] }
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def no_aggregate(policy)
|
|
109
|
+
Outcome.new(problem: :no_aggregate,
|
|
110
|
+
detail: "#{policy.name}'s ask #{policy.ask.to_sym.inspect} reacts to " \
|
|
111
|
+
"#{policy.on_event.inspect}, whose aggregate this chapter does not declare")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def unmatched(aggregate, policy)
|
|
115
|
+
Outcome.new(problem: :unmatched,
|
|
116
|
+
detail: "#{policy.name}'s ask #{policy.ask.to_sym.inspect} matches no `asks` declared on " \
|
|
117
|
+
"#{aggregate.hecks_name} — declare `asks \"#{Naming.pascal(policy.ask)}\"` " \
|
|
118
|
+
"on one of its ports in the hecksagon")
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def ambiguous(aggregate, policy, found)
|
|
122
|
+
ports = found.map { |port, _operation| port.name.inspect }.join(" and ")
|
|
123
|
+
Outcome.new(problem: :ambiguous,
|
|
124
|
+
detail: "#{policy.name}'s ask #{policy.ask.to_sym.inspect} is asked on #{ports} of " \
|
|
125
|
+
"#{aggregate.hecks_name} — pick one in the hecksagon with " \
|
|
126
|
+
"`#{aggregate.hecks_name}.ask_via \"#{Naming.pascal(policy.ask)}\", port: \"...\"`")
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -75,7 +75,7 @@ module Hecks
|
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
# Folds `state_field`, `state_start` and `
|
|
78
|
+
# Folds `state_field`, `state_start`, `transitions` and `marks` into the IR's one Lifecycle.
|
|
79
79
|
def lifecycle_of(row)
|
|
80
80
|
declared = row[:lifecycle]
|
|
81
81
|
return nil unless declared
|
|
@@ -83,7 +83,8 @@ module Hecks
|
|
|
83
83
|
Lifecycle.new(
|
|
84
84
|
field: declared[:field],
|
|
85
85
|
default: declared[:default],
|
|
86
|
-
transitions: transitions(Array(declared[:transitions]))
|
|
86
|
+
transitions: transitions(Array(declared[:transitions])),
|
|
87
|
+
marks: marks(Array(declared[:marks]))
|
|
87
88
|
)
|
|
88
89
|
end
|
|
89
90
|
|
|
@@ -97,6 +98,11 @@ module Hecks
|
|
|
97
98
|
end
|
|
98
99
|
end
|
|
99
100
|
|
|
101
|
+
# Regroups the one-row-per-state marks back into `name => states`, in declared order.
|
|
102
|
+
def marks(rows)
|
|
103
|
+
rows.group_by { |held| held[:name].to_s }.transform_values { |held| held.map { |row| row[:state].to_s } }
|
|
104
|
+
end
|
|
105
|
+
|
|
100
106
|
def from_of(froms)
|
|
101
107
|
return nil if froms.empty?
|
|
102
108
|
return froms.first if froms.size == 1
|
|
@@ -49,7 +49,7 @@ module Hecks
|
|
|
49
49
|
# this entity rather than the aggregate's flat state.
|
|
50
50
|
invariants: [:invariants, [:each, :invariant]]
|
|
51
51
|
},
|
|
52
|
-
rows: { transitions: :transition_rows, identified_by: :identity_rows },
|
|
52
|
+
rows: { transitions: :transition_rows, marks: :mark_rows, identified_by: :identity_rows },
|
|
53
53
|
reads: { identified_by: [:each, :identity_path], attributes: [:each, :shape_field],
|
|
54
54
|
preconditions: [:each, :rule], invariants: [:each, :rule] },
|
|
55
55
|
derived: {
|
|
@@ -57,7 +57,8 @@ module Hecks
|
|
|
57
57
|
owner: :parent,
|
|
58
58
|
state_field: [:folded, :lifecycle, :field],
|
|
59
59
|
state_start: [:folded, :lifecycle, :default],
|
|
60
|
-
transitions: [:folded, :lifecycle, :transitions]
|
|
60
|
+
transitions: [:folded, :lifecycle, :transitions],
|
|
61
|
+
marks: [:folded, :lifecycle, :marks]
|
|
61
62
|
}
|
|
62
63
|
),
|
|
63
64
|
|
|
@@ -68,6 +69,7 @@ module Hecks
|
|
|
68
69
|
aggregate: [:aggregate, :plain],
|
|
69
70
|
on_event: [:on_event, :plain],
|
|
70
71
|
trigger_command: [:trigger_command, :plain],
|
|
72
|
+
ask: [:ask, :plain],
|
|
71
73
|
target_domain: [:target_domain, :plain],
|
|
72
74
|
expect_undelivered: [:expect_undelivered, :plain],
|
|
73
75
|
where: [:where, :plain],
|
|
@@ -34,7 +34,8 @@ module Hecks
|
|
|
34
34
|
projected_fields: [:projected_fields, [:each, :projected_field]],
|
|
35
35
|
provenance: [:provenance, :plain]
|
|
36
36
|
},
|
|
37
|
-
rows: { transitions: :transition_rows,
|
|
37
|
+
rows: { transitions: :transition_rows, marks: :mark_rows, value_objects: :value_object_names,
|
|
38
|
+
identified_by: :identity_rows },
|
|
38
39
|
reads: { identified_by: [:each, :identity_path], attributes: [:each_with_id, :attribute],
|
|
39
40
|
invariants: [:each, :rule], preconditions: [:each, :rule],
|
|
40
41
|
projected_fields: [:each, :projected_field] },
|
|
@@ -43,6 +44,7 @@ module Hecks
|
|
|
43
44
|
state_field: [:folded, :lifecycle, :field],
|
|
44
45
|
state_start: [:folded, :lifecycle, :default],
|
|
45
46
|
transitions: [:folded, :lifecycle, :transitions],
|
|
47
|
+
marks: [:folded, :lifecycle, :marks],
|
|
46
48
|
value_objects: :children
|
|
47
49
|
}
|
|
48
50
|
),
|
|
@@ -35,10 +35,10 @@ module Hecks
|
|
|
35
35
|
# One identity part, as the String path `identity_paths` splits.
|
|
36
36
|
def identity_path(part) = part[:value].to_s
|
|
37
37
|
|
|
38
|
-
# A member's fields, an open map, so the pairs arrive as a list.
|
|
39
|
-
#
|
|
38
|
+
# A member's fields, an open map, so the pairs arrive as a list. Each value is already
|
|
39
|
+
# typed: `Reconstruction` reads a stored cell with `Scalars.unmark` before it gets here.
|
|
40
40
|
def member(pairs)
|
|
41
|
-
pairs.to_h { |key, value| [key.to_sym,
|
|
41
|
+
pairs.to_h { |key, value| [key.to_sym, value] }
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
# A read model's gathered head; `as` is a Symbol because `ReadModel#to_h` spells it `to_s`.
|
|
@@ -49,18 +49,6 @@ module Hecks
|
|
|
49
49
|
# Matches the builder's native `{field: :symbol}` shape.
|
|
50
50
|
def group_by_field(row) = { field: row[:field].to_sym }
|
|
51
51
|
|
|
52
|
-
# Reads a member's scalar back from text. Unlike `read`, a bare word stays a
|
|
53
|
-
# String, because a closed set admits words far more often than symbols.
|
|
54
|
-
def unmark_scalar(value)
|
|
55
|
-
text = value.to_s
|
|
56
|
-
return true if text == "true"
|
|
57
|
-
return false if text == "false"
|
|
58
|
-
return text.to_i if text.match?(/\A-?\d+\z/)
|
|
59
|
-
return text.to_f if text.match?(/\A-?\d+\.\d+\z/)
|
|
60
|
-
|
|
61
|
-
text
|
|
62
|
-
end
|
|
63
|
-
|
|
64
52
|
# A saga's argument bindings; Literal marks a Symbol with a leading colon.
|
|
65
53
|
def bindings(with) = Array(with).to_h { |key, value| [key.to_sym, read(value)] }
|
|
66
54
|
|
|
@@ -110,6 +98,7 @@ module Hecks
|
|
|
110
98
|
case source[:kind].to_s
|
|
111
99
|
when "argument" then source[:name].to_sym
|
|
112
100
|
when "state" then StateRef.new(source[:name].to_sym)
|
|
101
|
+
when "expression" then Computed.new(source[:text])
|
|
113
102
|
else source[:value]
|
|
114
103
|
end
|
|
115
104
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
class Assembly
|
|
4
|
+
# How a `member` cell is spelled as stored text, and read back. The meta-domain holds a cell
|
|
5
|
+
# as text, so a quoted `"true"` and a bare `true` need different spellings: a boolean or
|
|
6
|
+
# number is its own spelling, and a String that would read back as one, or as quoted text,
|
|
7
|
+
# is quoted so it stays a String. The Rust parser reads a cell the same way (`cell_value`
|
|
8
|
+
# in `parse/value_object.rs`).
|
|
9
|
+
module Scalars
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
# @param value [Object] a cell as the builder holds it
|
|
13
|
+
# @return [Object] `value` itself unless it is a String that reads back as another type, in
|
|
14
|
+
# which case its quoted spelling
|
|
15
|
+
def mark(value)
|
|
16
|
+
return value unless value.is_a?(String)
|
|
17
|
+
|
|
18
|
+
unmark(value) == value ? value : Literal.quote(value)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Unlike `Literal.read`, a bare word stays a String, because a closed set admits words far
|
|
22
|
+
# more often than symbols; quoted text is a String whatever it spells.
|
|
23
|
+
#
|
|
24
|
+
# @param value [#to_s] a cell's stored text
|
|
25
|
+
# @return [Object] the String, boolean or number the text spells
|
|
26
|
+
def unmark(value)
|
|
27
|
+
text = value.to_s
|
|
28
|
+
return Literal.unquote(text) if Literal.quoted?(text)
|
|
29
|
+
return true if text == "true"
|
|
30
|
+
return false if text == "false"
|
|
31
|
+
return text.to_i if text.match?(/\A-?\d+\z/)
|
|
32
|
+
return text.to_f if text.match?(/\A-?\d+\.\d+\z/)
|
|
33
|
+
|
|
34
|
+
text
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -52,7 +52,7 @@ module Hecks
|
|
|
52
52
|
|
|
53
53
|
# A bare constant in a bluebook is a name, even when Ruby has heard of it.
|
|
54
54
|
#
|
|
55
|
-
# `
|
|
55
|
+
# `Adapters::Driving::Ruby` installs aggregate names as top-level constants, which would win
|
|
56
56
|
# over the `const_missing` resolver and silently rebind the attribute. Demodulising
|
|
57
57
|
# spells `:Target` and `QualityControl::Target` the same.
|
|
58
58
|
def spell(type)
|
|
@@ -78,15 +78,20 @@ module Hecks
|
|
|
78
78
|
#
|
|
79
79
|
# @return [Hash{Symbol => Object}] `{kind: "argument", name:}` for a
|
|
80
80
|
# command argument, `{kind: "state", name:}` for a state
|
|
81
|
-
# self-reference,
|
|
81
|
+
# self-reference, `{kind: "expression", text:, ast:}` for a computed value, or
|
|
82
|
+
# `{kind: "literal", value:}` for a literal value
|
|
82
83
|
def classified_source
|
|
83
|
-
if source.is_a?(Symbol)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
return { kind: "argument", name: source.to_s } if source.is_a?(Symbol)
|
|
85
|
+
return { kind: "state", name: source.name.to_s } if source.is_a?(StateRef)
|
|
86
|
+
return computed_source if source.is_a?(Computed)
|
|
87
|
+
|
|
88
|
+
{ kind: "literal", value: source }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# A computed value on the wire: its canonical text, and the AST the Rust side reads.
|
|
92
|
+
def computed_source
|
|
93
|
+
tree = Expression::Resolver.parse(source.text)
|
|
94
|
+
{ kind: "expression", text: source.text, ast: Expression::AstJson.emit_resolver(tree) }
|
|
90
95
|
end
|
|
91
96
|
end
|
|
92
97
|
end
|
|
@@ -18,6 +18,36 @@ module Hecks
|
|
|
18
18
|
# @return [String]
|
|
19
19
|
def event_name = Naming.unqualified(@on_event)
|
|
20
20
|
|
|
21
|
+
# Whether this policy puts a need to the outside by name rather than triggering a command.
|
|
22
|
+
#
|
|
23
|
+
# @return [Boolean]
|
|
24
|
+
def asks? = !@ask.to_s.empty?
|
|
25
|
+
|
|
26
|
+
# What the policy reaches, as a reader names it: the trigger, or the ask while no
|
|
27
|
+
# hecksagon has bound it to a port operation.
|
|
28
|
+
#
|
|
29
|
+
# @return [String]
|
|
30
|
+
def reaches = @trigger_command.to_s.empty? && asks? ? "ask :#{@ask}" : @trigger_command.to_s
|
|
31
|
+
|
|
32
|
+
# Binds an `ask` to the verb the hecksagon's declared ask resolves to, so everything
|
|
33
|
+
# that reads `trigger_command` reads the port operation the old spelling named.
|
|
34
|
+
#
|
|
35
|
+
# @param verb [String] the resolved `Aggregate::Port.Operation`
|
|
36
|
+
# @return [String] the verb
|
|
37
|
+
def resolve_ask!(verb) = @trigger_command = verb
|
|
38
|
+
|
|
39
|
+
# The wire form: an ask policy carries `ask` where a trigger policy carries
|
|
40
|
+
# `trigger_command`, and a trigger policy carries no `ask` key at all, so the pinned
|
|
41
|
+
# shape of every existing policy does not move.
|
|
42
|
+
#
|
|
43
|
+
# @return [Hash] the policy's IR
|
|
44
|
+
def to_h
|
|
45
|
+
shape = super
|
|
46
|
+
return shape.except(:ask) unless asks?
|
|
47
|
+
|
|
48
|
+
shape.except(:ask).to_h { |key, value| key == :trigger_command ? [:ask, @ask] : [key, value] }
|
|
49
|
+
end
|
|
50
|
+
|
|
21
51
|
# Whether `for_each` names a query, turning one reaction into a dispatch per row.
|
|
22
52
|
#
|
|
23
53
|
# @return [Boolean]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
module Capabilities
|
|
4
|
+
# Reads the value a capability entry names ("Aggregate.attribute"): whole seconds, text,
|
|
5
|
+
# or the attribute's own name. Extended onto {Capabilities}, whose singleton methods
|
|
6
|
+
# these become.
|
|
7
|
+
module AttributeValues
|
|
8
|
+
# The whole seconds a duration attribute's `default:` holds: a bare integer, or the
|
|
9
|
+
# `{ value: N }` fill of a one-field value object (how a bluebook types a number).
|
|
10
|
+
#
|
|
11
|
+
# @param default [Object] an attribute's `default:`
|
|
12
|
+
# @return [Integer, nil] the positive whole seconds, or `nil` when it is not that
|
|
13
|
+
def duration_seconds(default)
|
|
14
|
+
seconds = filled_value(default)
|
|
15
|
+
seconds if seconds.is_a?(Integer) && seconds.positive?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# The seconds a `:duration` verb ("Aggregate.attribute") resolves to in `chapter`.
|
|
19
|
+
#
|
|
20
|
+
# @param chapter [Bluebook::Chapter] the chapter that declares the aggregate
|
|
21
|
+
# @param verb [String] the entry, spelled "Aggregate.attribute"
|
|
22
|
+
# @return [Integer, nil] the positive whole seconds, or `nil` when the verb names no such
|
|
23
|
+
# attribute or its default is not whole seconds
|
|
24
|
+
def duration_of(chapter, verb)
|
|
25
|
+
duration_seconds(attribute_default(chapter, verb))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# The `default:` of the attribute a verb ("Aggregate.attribute") names in `chapter`.
|
|
29
|
+
#
|
|
30
|
+
# @param chapter [Bluebook::Chapter] the chapter that declares the aggregate
|
|
31
|
+
# @param verb [String] the entry, spelled "Aggregate.attribute"
|
|
32
|
+
# @return [Object, nil] that attribute's `default:`, or `nil` when there is none
|
|
33
|
+
def attribute_default(chapter, verb)
|
|
34
|
+
aggregate_name, attribute_name = verb.split(".", 2)
|
|
35
|
+
chapter.aggregate(aggregate_name)&.attributes&.find { |a| a.name.to_s == attribute_name }&.default
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# A default's value: the bare value, or the `{ value: x }` fill of a one-field value object.
|
|
39
|
+
#
|
|
40
|
+
# @param default [Object] an attribute's `default:`
|
|
41
|
+
# @return [Object] the bare value
|
|
42
|
+
def filled_value(default)
|
|
43
|
+
default.is_a?(Hash) ? default.fetch(:value) { default["value"] } : default
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The string a `:text` verb ("Aggregate.attribute") resolves to in `chapter`: the
|
|
47
|
+
# attribute's `default:`, bare or as the `{ value: "..." }` fill of a one-field value
|
|
48
|
+
# object (ADR 0099).
|
|
49
|
+
#
|
|
50
|
+
# @param chapter [Bluebook::Chapter] the chapter that declares the aggregate
|
|
51
|
+
# @param verb [String] the entry, spelled "Aggregate.attribute"
|
|
52
|
+
# @return [String, nil] the non-empty text, or `nil` when the verb names no such attribute
|
|
53
|
+
# or its default is not text
|
|
54
|
+
def text_of(chapter, verb)
|
|
55
|
+
text = filled_value(attribute_default(chapter, verb))
|
|
56
|
+
text if text.is_a?(String) && !text.empty?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# The attribute name an `:attribute` verb ("Aggregate.attribute") resolves to in `chapter`.
|
|
60
|
+
#
|
|
61
|
+
# @param chapter [Bluebook::Chapter] the chapter that declares the aggregate
|
|
62
|
+
# @param verb [String] the entry, spelled "Aggregate.attribute"
|
|
63
|
+
# @return [String, nil] the attribute's name, or `nil` when the aggregate lacks it
|
|
64
|
+
def attribute_of(chapter, verb)
|
|
65
|
+
aggregate_name, attribute_name = verb.split(".", 2)
|
|
66
|
+
found = chapter.aggregate(aggregate_name)&.attributes&.any? { |a| a.name.to_s == attribute_name }
|
|
67
|
+
attribute_name if found
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|