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
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Adapters
|
|
7
|
+
# Everything a rehearsal does on a client's domain: where its data copy comes from, how the
|
|
8
|
+
# mint's audit is run over it, and what is reported. The two port adapters (`RehearsalStore`
|
|
9
|
+
# and `RehearsalSubject`) are thin; the work lives here.
|
|
10
|
+
module Rehearsal
|
|
11
|
+
# A rehearsal that was not admitted or could not be carried on: the port answers it as a
|
|
12
|
+
# refusal, which the run keeps as `refusal`.
|
|
13
|
+
class Refused < StandardError; end
|
|
14
|
+
|
|
15
|
+
# What a subject's own `.world` says about the copy of its data, read from its
|
|
16
|
+
# `rehearsed_by` block and checked before anything is touched.
|
|
17
|
+
#
|
|
18
|
+
# A connection is only ever named by an environment variable (`connection_env`); a literal
|
|
19
|
+
# connection string, user or password in the world is refused, so the file holds no secret.
|
|
20
|
+
class Declaration
|
|
21
|
+
KINDS = %w[memory postgres].freeze
|
|
22
|
+
MODES = %w[empty dump readonly_copy].freeze
|
|
23
|
+
LITERALS = %i[database url connection password user].freeze
|
|
24
|
+
LOCAL_HOSTS = [nil, "", "localhost", "127.0.0.1", "::1", "[::1]"].freeze
|
|
25
|
+
|
|
26
|
+
NAME = /\A[A-Za-z0-9_-]+\z/
|
|
27
|
+
|
|
28
|
+
attr_reader :kind, :mode, :connection_env, :dump, :rows, :drop_after, :allow_remote, :schema, :environment
|
|
29
|
+
|
|
30
|
+
# @param world [#for_verb, nil] the subject's world, as the registry holds it
|
|
31
|
+
# @param directory [String] the domain's bluebook directory, which a relative file is under
|
|
32
|
+
# @return [Declaration] the checked declaration
|
|
33
|
+
# @raise [Refused] if the world declares no `rehearsed_by`, or declares it wrongly
|
|
34
|
+
def self.from_world(world, directory)
|
|
35
|
+
settings = world&.for_verb("rehearsed_by") || {}
|
|
36
|
+
raise Refused, "the domain's world declares no `rehearsed_by`: nothing says where its data copy is" if settings.empty?
|
|
37
|
+
|
|
38
|
+
new(settings, directory).tap(&:check!)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @param settings [Hash] the block's settings, `adapter:` among them
|
|
42
|
+
# @param directory [String] where a relative `dump` or `rows` is found
|
|
43
|
+
def initialize(settings, directory)
|
|
44
|
+
@kind = settings[:adapter].to_s.downcase
|
|
45
|
+
@connection_env = settings[:connection_env]&.to_s
|
|
46
|
+
@dump = expand(settings[:dump], directory)
|
|
47
|
+
@rows = expand(settings[:rows], directory)
|
|
48
|
+
@mode = settings.fetch(:mode) { default_mode }.to_s
|
|
49
|
+
read_switches(settings)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [void]
|
|
53
|
+
# @raise [Refused] naming what is wrong with the declaration
|
|
54
|
+
def check!
|
|
55
|
+
check_names!
|
|
56
|
+
raise Refused, literal_wording unless @literals.empty?
|
|
57
|
+
|
|
58
|
+
kind == "postgres" ? check_postgres! : check_memory!
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [String] the connection the environment holds
|
|
62
|
+
# @raise [Refused] if the variable the world names is unset or empty, or names a server
|
|
63
|
+
# that is not this machine and the world did not say `allow_remote true`
|
|
64
|
+
def connection
|
|
65
|
+
value = ENV.fetch(connection_env, "")
|
|
66
|
+
raise Refused, "the environment variable #{connection_env} is not set" if value.strip.empty?
|
|
67
|
+
|
|
68
|
+
refuse_remote!(value)
|
|
69
|
+
value
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def refuse_remote!(value)
|
|
75
|
+
host = URI.parse(value).host
|
|
76
|
+
return if allow_remote || LOCAL_HOSTS.include?(host)
|
|
77
|
+
|
|
78
|
+
raise Refused, "#{connection_env} names the server #{host.inspect}, which is not this machine: " \
|
|
79
|
+
"a rehearsal reads a local copy; say `allow_remote true` in rehearsed_by to point it elsewhere"
|
|
80
|
+
rescue URI::InvalidURIError
|
|
81
|
+
nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def read_switches(settings)
|
|
85
|
+
@drop_after = settings.fetch(:drop_after, true) != false
|
|
86
|
+
@allow_remote = settings.fetch(:allow_remote, false) == true
|
|
87
|
+
@literals = LITERALS & settings.keys.map(&:to_sym)
|
|
88
|
+
@schema = settings[:schema]&.to_s
|
|
89
|
+
@environment = settings[:environment]&.to_s
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def default_mode = dump || rows ? "dump" : "empty"
|
|
93
|
+
|
|
94
|
+
def check_names!
|
|
95
|
+
raise Refused, "rehearsed_by names #{kind.inspect}; the stores are #{KINDS.join(" and ")}" unless KINDS.include?(kind)
|
|
96
|
+
|
|
97
|
+
raise Refused, "rehearsed_by mode #{mode.inspect} is not one of #{MODES.join(", ")}" unless MODES.include?(mode)
|
|
98
|
+
|
|
99
|
+
check_label!("schema", schema)
|
|
100
|
+
check_label!("environment", environment)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def check_label!(word, value)
|
|
104
|
+
return if value.nil? || NAME.match?(value)
|
|
105
|
+
|
|
106
|
+
raise Refused, "rehearsed_by #{word} #{value.inspect} is not a name (letters, digits, `_`, `-`)"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def expand(path, directory)
|
|
110
|
+
path && File.expand_path(path.to_s, directory)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def literal_wording
|
|
114
|
+
"rehearsed_by holds #{@literals.join(", ")}: name the environment variable in `connection_env`, " \
|
|
115
|
+
"never the value, so the world file holds no secret"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def check_postgres!
|
|
119
|
+
unless connection_env
|
|
120
|
+
raise Refused,
|
|
121
|
+
"a postgres rehearsal needs `connection_env`, the name of the variable holding the server"
|
|
122
|
+
end
|
|
123
|
+
raise Refused, "mode dump needs `dump`, the file to restore" if mode == "dump" && !dump
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def check_memory!
|
|
127
|
+
raise Refused, "mode readonly_copy is a postgres mode: a memory store has no copy to read" if mode == "readonly_copy"
|
|
128
|
+
raise Refused, "mode dump needs `rows`, the JSON file the memory store holds" if mode == "dump" && !rows
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Adapters
|
|
5
|
+
module Rehearsal
|
|
6
|
+
# Which environment overlay (`environments/<name>.hecksagon` and `.world`) a rehearsal loads
|
|
7
|
+
# the subject under, and where that was decided.
|
|
8
|
+
#
|
|
9
|
+
# A rehearsal is about the era the production binds will mint, so it loads `production` when
|
|
10
|
+
# the domain has `environments/production.hecksagon`, the same default the Rust host build
|
|
11
|
+
# takes (`ProjectRust::DomainLoader#load_environment`). Most domains bind their aggregates
|
|
12
|
+
# to a database only in that overlay, so the base files alone have no `persisted_by` bind.
|
|
13
|
+
# The choice is a reported fact (`environment_loaded`), never a silent one.
|
|
14
|
+
class Environment
|
|
15
|
+
# Where the name came from, in the order they are tried.
|
|
16
|
+
SOURCES = %w[world HECKS_PROJECT_ENVIRONMENT default none].freeze
|
|
17
|
+
|
|
18
|
+
NONE_WORDS = "no environment overlay (none named in rehearsed_by or HECKS_PROJECT_ENVIRONMENT, " \
|
|
19
|
+
"and no environments/production.hecksagon)"
|
|
20
|
+
|
|
21
|
+
# @!attribute [r] name
|
|
22
|
+
# @return [String] the overlay's name, or `""` when none was loaded
|
|
23
|
+
# @!attribute [r] source
|
|
24
|
+
# @return [String] one of `SOURCES`
|
|
25
|
+
# @!attribute [r] directory
|
|
26
|
+
# @return [String] the bluebook directory the overlay is looked for under
|
|
27
|
+
attr_reader :name, :source, :directory
|
|
28
|
+
|
|
29
|
+
# @param declared [String, nil] what the world's `rehearsed_by` names (`environment "..."`)
|
|
30
|
+
# @param directory [String] the domain's bluebook directory
|
|
31
|
+
# @return [Environment] the choice: the world's, else `HECKS_PROJECT_ENVIRONMENT`, else
|
|
32
|
+
# `production` when its overlay exists, else none
|
|
33
|
+
def self.resolve(declared, directory)
|
|
34
|
+
env = ENV.fetch("HECKS_PROJECT_ENVIRONMENT", "")
|
|
35
|
+
return new(declared, "world", directory) if declared && !declared.empty?
|
|
36
|
+
return new(env, "HECKS_PROJECT_ENVIRONMENT", directory) unless env.empty?
|
|
37
|
+
return new("production", "default", directory) if File.exist?(File.join(directory, "environments",
|
|
38
|
+
"production.hecksagon"))
|
|
39
|
+
|
|
40
|
+
new("", "none", directory)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param name [String] the overlay's name
|
|
44
|
+
# @param source [String] one of `SOURCES`
|
|
45
|
+
# @param directory [String] the bluebook directory
|
|
46
|
+
def initialize(name, source, directory)
|
|
47
|
+
@name = name
|
|
48
|
+
@source = source
|
|
49
|
+
@directory = directory
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [String, nil] the name to load, nil when no overlay is wanted
|
|
53
|
+
def overlay = name.empty? ? nil : name
|
|
54
|
+
|
|
55
|
+
# @return [Boolean] whether the hecksagon file of this overlay exists
|
|
56
|
+
def overlay_file? = File.exist?(file)
|
|
57
|
+
|
|
58
|
+
# @return [String] the hecksagon file the overlay is looked for in
|
|
59
|
+
def file = File.join(directory, "environments", "#{name}.hecksagon")
|
|
60
|
+
|
|
61
|
+
# @return [Hash] the report fact, as the `Environment` value object holds it
|
|
62
|
+
def facts = { name: name, source: source, file: name.empty? ? "" : file }
|
|
63
|
+
|
|
64
|
+
# @return [String] what the overlay choice was, in words
|
|
65
|
+
def to_s
|
|
66
|
+
return NONE_WORDS if name.empty?
|
|
67
|
+
|
|
68
|
+
"environment #{name.inspect} (#{source}), file #{file} #{overlay_file? ? "found" : "NOT FOUND"}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# The overlays the directory holds, so a refusal can say what could be chosen.
|
|
72
|
+
#
|
|
73
|
+
# @return [Array<String>] the names of `environments/*.hecksagon`
|
|
74
|
+
def available
|
|
75
|
+
Dir.glob(File.join(directory, "environments", "*.hecksagon")).map { |path| File.basename(path, ".hecksagon") }.sort
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @return [String] the overlays the directory holds, comma separated, or `none`
|
|
79
|
+
def available_words = available.empty? ? "none" : available.join(", ")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "subject"
|
|
4
|
+
require_relative "memory_store"
|
|
5
|
+
require_relative "postgres_store"
|
|
6
|
+
require_relative "rust_audit"
|
|
7
|
+
require_relative "audit_report"
|
|
8
|
+
|
|
9
|
+
module Hecks
|
|
10
|
+
module Adapters
|
|
11
|
+
module Rehearsal
|
|
12
|
+
# The two questions a rehearsal asks of a subject and its data copy: where the shape stands
|
|
13
|
+
# (`survey`) and what the mint's audit would refuse (`examine`). Each answer is the facts, as
|
|
14
|
+
# the `RehearsalRun` commands take them.
|
|
15
|
+
class Inquiry
|
|
16
|
+
# What every rehearsal says it did not check.
|
|
17
|
+
ENGINE_LIMIT = { what: "the Rust host's mint audit",
|
|
18
|
+
why: "no validate_harness was built, so the Ruby audit alone ran over the rows" }.freeze
|
|
19
|
+
|
|
20
|
+
ALWAYS = [
|
|
21
|
+
{ what: "the mint itself",
|
|
22
|
+
why: "no era was written: the edge, its approval and the audit were evaluated, nothing minted" },
|
|
23
|
+
{ what: "an edge's coverage of every change in the shape", why: "the mint checks it; the rehearsal does not yet" },
|
|
24
|
+
{ what: "commands, policies and requests", why: "no traffic was run: only the stored data and the shape were read" },
|
|
25
|
+
{ what: "data written after the copy was made", why: "a copy is a moment; the mint sees the data of its own" }
|
|
26
|
+
].freeze
|
|
27
|
+
|
|
28
|
+
# @param subject [Subject] the loaded domain
|
|
29
|
+
# @param store [MemoryStore, PostgresStore] its prepared data copy
|
|
30
|
+
def initialize(subject, store)
|
|
31
|
+
@subject = subject
|
|
32
|
+
@store = store
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @return [Hash] `booted`, `host_version`, `store`, `mode`, `before`, `after`, `minting`,
|
|
36
|
+
# the schema and environment that were read, and what the copy holds
|
|
37
|
+
def survey
|
|
38
|
+
shape.merge(environment_loaded: @subject.environment.facts, copy_note: plain(copy_note)).merge(copy_facts)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [Hash] the edge facts, every refused row and its total, what was read, the parity
|
|
42
|
+
# of the two engines, and what was not checked
|
|
43
|
+
def examine
|
|
44
|
+
edge = minting == "next" ? @subject.edge_from(before) : nil
|
|
45
|
+
refusals, after = edge ? @store.audit(@subject, edge) : [[], {}]
|
|
46
|
+
rust = RustAudit.call(@subject, after)
|
|
47
|
+
report = AuditReport.new(minting: minting, edge: edge, states: after, ruby: refusals, rust: rust)
|
|
48
|
+
answer(edge, report, (refusals + rust.violations).uniq, limits(edge, rust, report))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def plain(value) = { value: value }
|
|
54
|
+
|
|
55
|
+
def shape
|
|
56
|
+
{ booted: flag(true), host_version: plain(Rehearsal.host_version), store: plain(@subject.declaration.kind),
|
|
57
|
+
mode: plain(@subject.declaration.mode), before: plain(before), after: plain(@subject.label),
|
|
58
|
+
minting: plain(minting) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def answer(edge, report, refused, unchecked)
|
|
62
|
+
rows, total = Rows.from(refused)
|
|
63
|
+
edge_facts(edge).merge(refused_rows: rows, refusal_total: plain(total), parity: report.parity,
|
|
64
|
+
unchecked: unchecked).merge(report.facts)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def flag(value) = plain(value)
|
|
68
|
+
|
|
69
|
+
def census = @census ||= @store.census(@subject)
|
|
70
|
+
|
|
71
|
+
def copy_note
|
|
72
|
+
domain = @subject.bluebook.name
|
|
73
|
+
copy_empty? ? census.refusal(domain) : census.summary(domain)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def copy_empty?
|
|
77
|
+
census.empty? && @subject.declaration.mode != "empty" && @subject.bluebook.aggregates.any?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# The copy is empty when a dump or a readonly copy was asked for and it holds no row of a
|
|
81
|
+
# domain that declares aggregates; a mode of `empty` is asked to hold nothing.
|
|
82
|
+
def copy_facts = census.facts.merge(copy_empty: flag(copy_empty?))
|
|
83
|
+
|
|
84
|
+
def edge_facts(edge)
|
|
85
|
+
{ minting: plain(minting), edge_present: flag(!edge.nil?), edge_approved: flag(approved?(edge)) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def eras = @eras ||= @store.eras(@subject)
|
|
89
|
+
|
|
90
|
+
def latest = @latest ||= eras.last && @subject.held.named(eras.last)
|
|
91
|
+
|
|
92
|
+
def before = latest ? latest[:label] : "none"
|
|
93
|
+
|
|
94
|
+
def minting
|
|
95
|
+
return "first" unless latest
|
|
96
|
+
|
|
97
|
+
before == @subject.label ? "none" : "next"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def approved?(edge)
|
|
101
|
+
return false unless edge
|
|
102
|
+
|
|
103
|
+
!Translation::ApprovalFile.needs_rehearsal?(edge) ||
|
|
104
|
+
!Translation::ApprovalFile.applicable(@subject.directory, edge).nil?
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def limits(edge, rust, report)
|
|
108
|
+
engine = rust.ran ? [] : [ENGINE_LIMIT]
|
|
109
|
+
engine + report.limits + ALWAYS + @store.limits(edge) + [journal_limit] + attached_limits +
|
|
110
|
+
(@subject.held.capable? ? [] : [binding_note])
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# The audit reads the journal's latest entry of each record, never the head tables.
|
|
114
|
+
def journal_limit
|
|
115
|
+
{ what: "the head tables",
|
|
116
|
+
why: "the audit reads each record's latest journal entry (hecks_journal_<domain>_era_N), not the head " \
|
|
117
|
+
"tables, so an edit to a head table is not seen. The copy holds #{census.head_rows} head rows " \
|
|
118
|
+
"and #{census.journal_rows} journal records#{divergence}" }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def divergence
|
|
122
|
+
census.head_rows == census.journal_rows ? "." : ": they differ, and only the journal's are audited."
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The chapters loaded beside the domain's own (a vendored package): the host holds eras
|
|
126
|
+
# for the domain's own aggregates only, so theirs can neither mint nor be refused.
|
|
127
|
+
def attached_limits
|
|
128
|
+
names = @subject.held.registry.bluebooks.values.map(&:name) - [@subject.bluebook.name]
|
|
129
|
+
return [] if names.empty?
|
|
130
|
+
|
|
131
|
+
[{ what: "the shape of the attached chapters (#{names.sort.join(", ")})",
|
|
132
|
+
why: "their aggregates have no era row of their own, so a change to them is not a mint" }]
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def binding_note
|
|
136
|
+
{ what: "that a boot would mint", why: "the domain is bound to #{@subject.held.adapter_name}, which holds no eras " \
|
|
137
|
+
"in this environment" }
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "rows"
|
|
5
|
+
require_relative "census"
|
|
6
|
+
|
|
7
|
+
module Hecks
|
|
8
|
+
module Adapters
|
|
9
|
+
module Rehearsal
|
|
10
|
+
# The data copy as a JSON document the world names (`rows`): the eras held and the records
|
|
11
|
+
# stored under the latest, in that era's shape. Nothing is created or dropped, so specs and
|
|
12
|
+
# fixtures rehearse without a database.
|
|
13
|
+
#
|
|
14
|
+
# The document is `{"eras": [{"ordinal": 1, "held_text": "<bluebook text>"}], "records":
|
|
15
|
+
# {"Aggregate": {"id": {"field": ...}}}}`.
|
|
16
|
+
class MemoryStore
|
|
17
|
+
# @param declaration [Declaration] a `memory` declaration
|
|
18
|
+
def initialize(declaration)
|
|
19
|
+
path = declaration.rows
|
|
20
|
+
@document = path ? JSON.parse(File.read(path)) : {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [void]
|
|
24
|
+
def prepare! = nil
|
|
25
|
+
|
|
26
|
+
# @return [void] there is nothing to drop
|
|
27
|
+
def drop! = nil
|
|
28
|
+
|
|
29
|
+
# @param _subject [Subject] unused: the document holds its own eras
|
|
30
|
+
# @return [Array<Hash>] the eras, `ordinal` and `held_text`
|
|
31
|
+
def eras(_subject)
|
|
32
|
+
Array(@document["eras"]).map { |era| { ordinal: era["ordinal"], held_text: era["held_text"] } }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# What the document holds: it keeps the latest state of each record only, so the head and
|
|
36
|
+
# the journal counts are the same number.
|
|
37
|
+
#
|
|
38
|
+
# @param subject [Subject] the loaded domain
|
|
39
|
+
# @return [Census] the counts
|
|
40
|
+
def census(subject)
|
|
41
|
+
counts = subject.bluebook.aggregates.map do |aggregate|
|
|
42
|
+
held = records(aggregate.name).size
|
|
43
|
+
Census::Count.new(aggregate: aggregate.name, head: held, journal: held)
|
|
44
|
+
end
|
|
45
|
+
Census.new(schema: "(memory document)", source: "memory", present: true, eras: eras(subject).size,
|
|
46
|
+
journal_entries: counts.sum(&:journal), counts: counts)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Runs the mint's audit over the stored records, translated through the edge in process.
|
|
50
|
+
#
|
|
51
|
+
# @param subject [Subject] the loaded domain
|
|
52
|
+
# @param edge [Bluebook::Translation] the edge leading to the shape
|
|
53
|
+
# @return [Array(Array<String>, Hash)] the audit's refusals, and the translated records
|
|
54
|
+
# by aggregate name and id
|
|
55
|
+
def audit(subject, edge)
|
|
56
|
+
refusals = []
|
|
57
|
+
after = subject.bluebook.aggregates.to_h do |aggregate|
|
|
58
|
+
declared = edge.for_aggregate(aggregate.name)
|
|
59
|
+
before = records(declared&.was || aggregate.name)
|
|
60
|
+
translated = translate(aggregate, declared, before)
|
|
61
|
+
refusals.concat(Translation::Audit.check(aggregate: aggregate, declared: declared, before: before,
|
|
62
|
+
after: translated).violations)
|
|
63
|
+
[aggregate.name, translated]
|
|
64
|
+
end
|
|
65
|
+
[refusals, after]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @param edge [Bluebook::Translation] the edge
|
|
69
|
+
# @return [Array<Hash>] what memory cannot apply, as report limits
|
|
70
|
+
def limits(edge)
|
|
71
|
+
return [] unless edge && Translation::ApprovalFile.needs_rehearsal?(edge)
|
|
72
|
+
|
|
73
|
+
[{ what: "the edge's compute and rekey rules",
|
|
74
|
+
why: "they are SQL, which only a postgres copy runs; memory leaves those fields as stored" }]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def records(name) = @document.dig("records", name.to_s) || {}
|
|
80
|
+
|
|
81
|
+
def translate(aggregate, declared, before)
|
|
82
|
+
lineage = Ports::Persistence::Lineage.from_declared(declared, aggregate.name)
|
|
83
|
+
return before unless lineage
|
|
84
|
+
|
|
85
|
+
before.to_h { |id, state| [id, lineage.translate(entry(id, state)).state] }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def entry(id, state)
|
|
89
|
+
Ports::Persistence::Entry.new(operation: "save", id: id, state: symbolise(state), mirrors: {})
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def symbolise(state) = JSON.parse(JSON.generate(state), symbolize_names: true)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "census"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Adapters
|
|
7
|
+
module Rehearsal
|
|
8
|
+
# Counts what a Postgres copy holds for the domain without creating anything: a plain
|
|
9
|
+
# connection, the schemas looked up before one is chosen, and counts over the relations that
|
|
10
|
+
# exist. A schema that is not in the copy is reported, never created.
|
|
11
|
+
class PostgresCensus
|
|
12
|
+
JOURNAL_LATEST = <<~SQL
|
|
13
|
+
SELECT aggregate, count(*) AS live FROM (
|
|
14
|
+
SELECT DISTINCT ON (aggregate, aggregate_id) aggregate, operation
|
|
15
|
+
FROM %<journal>s ORDER BY aggregate, aggregate_id, ordinal DESC
|
|
16
|
+
) latest WHERE operation = 'save' GROUP BY aggregate
|
|
17
|
+
SQL
|
|
18
|
+
|
|
19
|
+
# @param url [String] the copy's database URL
|
|
20
|
+
# @param subject [Subject] the loaded domain
|
|
21
|
+
# @param candidates [Array<Array(String, String)>] schema and source, in the order tried
|
|
22
|
+
def initialize(url, subject, candidates)
|
|
23
|
+
@url = url
|
|
24
|
+
@subject = subject
|
|
25
|
+
@candidates = candidates
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Census] the counts, in the first candidate schema that holds a lineage, else the
|
|
29
|
+
# first that exists, else the first named
|
|
30
|
+
def read
|
|
31
|
+
db = PG.connect(@url)
|
|
32
|
+
present = schemas(db)
|
|
33
|
+
lineage = holders(db)
|
|
34
|
+
name, source = choose(present, lineage)
|
|
35
|
+
return absent(name, source, lineage) unless present.include?(name)
|
|
36
|
+
|
|
37
|
+
db.exec("SET search_path TO #{db.quote_ident(name)}")
|
|
38
|
+
counted(db, name, source, lineage - [name])
|
|
39
|
+
ensure
|
|
40
|
+
db&.close
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def schemas(db) = db.exec("SELECT schema_name FROM information_schema.schemata").map { |row| row["schema_name"] }
|
|
46
|
+
|
|
47
|
+
def holders(db)
|
|
48
|
+
sql = "SELECT table_schema FROM information_schema.tables WHERE table_name = 'hecks_eras'"
|
|
49
|
+
db.exec(sql).map { |row| row["table_schema"] }.sort
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def choose(present, lineage)
|
|
53
|
+
@candidates.find { |name, _| lineage.include?(name) } || @candidates.find { |name, _| present.include?(name) } ||
|
|
54
|
+
@candidates.first
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def absent(name, source, lineage)
|
|
58
|
+
Census.new(schema: name, source: source, present: false, eras: 0, journal_entries: 0,
|
|
59
|
+
counts: empty_counts, elsewhere: lineage)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def empty_counts = @subject.bluebook.aggregates.map { |a| Census::Count.new(aggregate: a.name, head: 0, journal: 0) }
|
|
63
|
+
|
|
64
|
+
def counted(db, name, source, elsewhere)
|
|
65
|
+
lineage = PostgresEra::Lineage.new(db, @subject.bluebook.name)
|
|
66
|
+
Census.new(schema: name, source: source, present: true, eras: eras(db), counts: counts(db, lineage),
|
|
67
|
+
journal_entries: rows(db, lineage.journal), elsewhere: elsewhere)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def counts(db, lineage)
|
|
71
|
+
live = latest(db, lineage)
|
|
72
|
+
@subject.bluebook.aggregates.map do |aggregate|
|
|
73
|
+
Census::Count.new(aggregate: aggregate.name, head: rows(db, lineage.head_view(aggregate.storage_name)),
|
|
74
|
+
journal: live.fetch(aggregate.storage_name, 0))
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def eras(db)
|
|
79
|
+
return 0 unless relation?(db, "hecks_eras")
|
|
80
|
+
|
|
81
|
+
db.exec_params("SELECT count(*) FROM hecks_eras WHERE domain = $1", [@subject.bluebook.name])[0]["count"].to_i
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def latest(db, lineage)
|
|
85
|
+
return {} unless relation?(db, lineage.journal)
|
|
86
|
+
|
|
87
|
+
db.exec(format(JOURNAL_LATEST, journal: db.quote_ident(lineage.journal))).to_h { |r| [r["aggregate"], r["live"].to_i] }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def rows(db, name)
|
|
91
|
+
relation?(db, name) ? db.exec("SELECT count(*) FROM #{db.quote_ident(name)}")[0]["count"].to_i : 0
|
|
92
|
+
rescue PG::Error
|
|
93
|
+
0
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def relation?(db, name) = !db.exec_params("SELECT to_regclass($1) AS found", [db.quote_ident(name)])[0]["found"].nil?
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "postgres_census"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Adapters
|
|
7
|
+
module Rehearsal
|
|
8
|
+
# What a `PostgresStore` reads from its database: the eras it holds and the mint's audit over
|
|
9
|
+
# the records under them. No table is provisioned and no era written; a copy somebody else
|
|
10
|
+
# made is read in a read-only session, and the database a run made is its own to touch.
|
|
11
|
+
#
|
|
12
|
+
# The schema is the world's `schema`, else `HECKS_SCHEMA`, else the one the domain's name
|
|
13
|
+
# gives (`Shop` is `shop`), else `public` when that holds the lineage. It is looked up
|
|
14
|
+
# before it is used, so a schema the copy does not have is reported and never created.
|
|
15
|
+
module PostgresReading
|
|
16
|
+
# @param subject [Subject] the loaded domain
|
|
17
|
+
# @return [Census] what the copy holds for the domain, counted once
|
|
18
|
+
def census(subject)
|
|
19
|
+
@census ||= PostgresCensus.new(url, subject, schema_candidates(subject)).read.tap do |found|
|
|
20
|
+
subject.held.read_schema(found.schema)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @param subject [Subject] the loaded domain
|
|
25
|
+
# @return [Array<Hash>] the eras the database holds, none when it holds no lineage
|
|
26
|
+
def eras(subject)
|
|
27
|
+
return [] unless census(subject).present
|
|
28
|
+
|
|
29
|
+
reading(subject) { |lineage| subject.held.held_eras(lineage) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @param subject [Subject] the loaded domain
|
|
33
|
+
# @param _edge [Bluebook::Translation] the edge leading to the shape
|
|
34
|
+
# @return [Array(Array<String>, Hash)] what the audit's layers refuse over the stored
|
|
35
|
+
# records, and the records as translated, by aggregate name and id
|
|
36
|
+
def audit(subject, _edge)
|
|
37
|
+
return [[], {}] unless census(subject).present
|
|
38
|
+
|
|
39
|
+
reading(subject) do |lineage|
|
|
40
|
+
finding = JournalStore::EdgeAudit.call(subject.held, lineage, subject.held.held_eras(lineage))
|
|
41
|
+
finding ? [finding.refusals, finding.after] : [[], {}]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @param _edge [Bluebook::Translation] unused
|
|
46
|
+
# @return [Array<Hash>] none: a postgres copy runs the compiled rules itself
|
|
47
|
+
def limits(_edge) = []
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def schema_candidates(subject)
|
|
52
|
+
return [[@declaration.schema, "world"]] if @declaration.schema
|
|
53
|
+
return [[ENV.fetch("HECKS_SCHEMA"), "HECKS_SCHEMA"]] if ENV.fetch("HECKS_SCHEMA", "") != ""
|
|
54
|
+
|
|
55
|
+
[[Naming.snake(subject.bluebook.name), "derived"], %w[public default]]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def reading(subject)
|
|
59
|
+
subject.held.read_from(url)
|
|
60
|
+
db = PostgresEra.connect_for(subject.bluebook.name, subject.held.settings)
|
|
61
|
+
db.exec("SET default_transaction_read_only = on") if @declaration.mode == "readonly_copy"
|
|
62
|
+
yield PostgresEra::Lineage.new(db, subject.bluebook.name)
|
|
63
|
+
ensure
|
|
64
|
+
db&.close
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|