hecks 1.3.0 → 1.4.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/lib/hecks/adapters/driven/claude_code.rb +7 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +10 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +8 -8
- data/lib/hecks/adapters/driven/governance_authorization.rb +31 -13
- data/lib/hecks/adapters/driven/heki/journal.rb +5 -4
- data/lib/hecks/adapters/driven/heki/saga_store.rb +5 -5
- data/lib/hecks/adapters/driven/heki.rb +13 -7
- data/lib/hecks/adapters/driven/identity_registry.rb +2 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -3
- data/lib/hecks/adapters/driven/lambda/client.rb +34 -9
- data/lib/hecks/adapters/driven/lambda.rb +39 -33
- data/lib/hecks/adapters/driven/local_storage.rb +17 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +1 -1
- data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
- data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
- data/lib/hecks/adapters/driven/postgres.rb +175 -28
- data/lib/hecks/adapters/driven/prism.rb +4 -4
- data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
- data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
- data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
- data/lib/hecks/adapters/driven/sqlite.rb +181 -21
- data/lib/hecks/adapters/driven.rb +4 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +18 -18
- data/lib/hecks/behaviors/dsl.rb +2 -2
- data/lib/hecks/behaviors/expectations.rb +23 -23
- data/lib/hecks/behaviors/ir.rb +1 -1
- data/lib/hecks/behaviors/rspec.rb +1 -1
- data/lib/hecks/behaviors/runner.rb +2 -2
- data/lib/hecks/behaviors.rb +1 -1
- data/lib/hecks/bluebook/aggregate.rb +13 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
- data/lib/hecks/bluebook/assembly/build.rb +1 -1
- data/lib/hecks/bluebook/assembly/contract.rb +39 -16
- data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
- data/lib/hecks/bluebook/assembly/marks.rb +18 -18
- data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
- data/lib/hecks/bluebook/assembly.rb +9 -9
- data/lib/hecks/bluebook/attribute.rb +9 -9
- data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
- data/lib/hecks/bluebook/behaviour/command.rb +23 -23
- data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -7
- data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
- data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
- data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
- data/lib/hecks/bluebook/behaviour/query.rb +1 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +28 -9
- data/lib/hecks/bluebook/command.rb +12 -12
- data/lib/hecks/bluebook/domain_port.rb +9 -9
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
- data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
- data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
- data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
- data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
- data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
- data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
- data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
- data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
- data/lib/hecks/bluebook/entity.rb +11 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
- data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
- data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
- data/lib/hecks/bluebook/expression/resolver.rb +60 -62
- data/lib/hecks/bluebook/hexagon.rb +1 -1
- data/lib/hecks/bluebook/lifecycle.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +101 -87
- data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
- data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
- data/lib/hecks/bluebook/meta_validator.rb +70 -70
- data/lib/hecks/bluebook/model_check.rb +170 -84
- data/lib/hecks/bluebook/pattern_subset.rb +9 -9
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +14 -14
- data/lib/hecks/bluebook/project_register.rb +6 -6
- data/lib/hecks/bluebook/query.rb +4 -4
- data/lib/hecks/bluebook/read_model.rb +14 -14
- data/lib/hecks/bluebook/reference.rb +8 -8
- data/lib/hecks/bluebook/smoke_test.rb +19 -19
- data/lib/hecks/bluebook/synthesizer.rb +12 -12
- data/lib/hecks/bluebook/translation.rb +4 -4
- data/lib/hecks/bluebook/value_object.rb +6 -6
- data/lib/hecks/bluebook.rb +2 -2
- data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
- data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
- data/lib/hecks/codemod.rb +36 -35
- data/lib/hecks/construct.rb +6 -6
- data/lib/hecks/corpus.rb +317 -0
- data/lib/hecks/deprecation.rb +95 -0
- data/lib/hecks/doc/reference.rb +19 -19
- data/lib/hecks/embryonaut_bluebook.rb +11 -11
- data/lib/hecks/facade/cli_door.rb +69 -10
- data/lib/hecks/facade/cli_runner.rb +105 -24
- data/lib/hecks/facade/command_request.rb +23 -0
- data/lib/hecks/facade/handle.rb +79 -32
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
- data/lib/hecks/facade/surface/chapter.rb +26 -17
- data/lib/hecks/facade/surface.rb +16 -3
- data/lib/hecks/facade.rb +15 -4
- data/lib/hecks/forms/app.rb +46 -30
- data/lib/hecks/forms/command_form_renderer.rb +70 -9
- data/lib/hecks/forms/field_renderer.rb +142 -6
- data/lib/hecks/forms/field_shape.rb +180 -19
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +2 -2
- data/lib/hecks/forms/query_form_renderer.rb +2 -2
- data/lib/hecks/forms/record_renderer.rb +2 -2
- data/lib/hecks/forms/record_table.rb +1 -1
- data/lib/hecks/forms/value_object_shape.rb +3 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +1 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework.rb +48 -17
- data/lib/hecks/freezer.rb +11 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
- data/lib/hecks/fuzzing/combination_miner.rb +119 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +140 -45
- data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
- data/lib/hecks/fuzzing/differential.rb +158 -0
- data/lib/hecks/fuzzing/domain_generator.rb +694 -0
- data/lib/hecks/fuzzing/era_boundary.rb +37 -18
- data/lib/hecks/fuzzing/form_census.rb +35 -20
- data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +62 -62
- data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +19 -21
- data/lib/hecks/fuzzing/properties/corrections.rb +9 -9
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +154 -49
- data/lib/hecks/fuzzing/properties/guards.rb +42 -42
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +22 -35
- data/lib/hecks/fuzzing/properties/outbox.rb +24 -24
- data/lib/hecks/fuzzing/properties/querying.rb +17 -17
- data/lib/hecks/fuzzing/properties.rb +64 -40
- data/lib/hecks/fuzzing/qa_settings.rb +152 -0
- data/lib/hecks/fuzzing/replay.rb +114 -114
- data/lib/hecks/fuzzing/rotation_priority.rb +10 -10
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
- data/lib/hecks/fuzzing/self_consistency.rb +141 -112
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +35 -35
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +58 -31
- data/lib/hecks/fuzzing/sequence_generator.rb +111 -23
- data/lib/hecks/fuzzing/shrinker.rb +197 -0
- data/lib/hecks/fuzzing/structural_skips.rb +23 -130
- data/lib/hecks/fuzzing/sweep_depth.rb +4 -4
- data/lib/hecks/fuzzing/target_capabilities.rb +49 -18
- data/lib/hecks/fuzzing/value_generator.rb +14 -14
- data/lib/hecks/fuzzing.rb +5 -0
- data/lib/hecks/grammar/evolve.rb +10 -10
- data/lib/hecks/grammar.rb +7 -7
- data/lib/hecks/ir.rb +13 -13
- data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
- data/lib/hecks/language/bluebook/policy.bluebook +11 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +9 -9
- data/lib/hecks/naming.rb +25 -25
- data/lib/hecks/ports/access_control.rb +58 -2
- data/lib/hecks/ports/agent/answers.rb +83 -6
- data/lib/hecks/ports/agent.rb +119 -35
- data/lib/hecks/ports/authentication.rb +44 -4
- data/lib/hecks/ports/authorization.rb +53 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +24 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +18 -1
- data/lib/hecks/ports/loading.rb +4 -0
- data/lib/hecks/ports/persistence/append_only.rb +172 -8
- data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
- data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
- data/lib/hecks/ports/persistence/execution.rb +4 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
- data/lib/hecks/ports/persistence/plugin.rb +42 -4
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
- data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
- data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
- data/lib/hecks/ports/persistence/state_codec.rb +319 -0
- data/lib/hecks/ports/persistence.rb +36 -1
- data/lib/hecks/ports/projection.rb +61 -7
- data/lib/hecks/ports/query/in_memory.rb +3 -3
- data/lib/hecks/ports/query/ordering.rb +6 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/projections/bootstrap_table.rb +112 -0
- data/lib/hecks/projections/diagrams.rb +75 -75
- data/lib/hecks/projections/glossary/html.rb +3 -3
- data/lib/hecks/projections/glossary/markdown.rb +3 -3
- data/lib/hecks/projections/glossary/mermaid.rb +1 -1
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sentences.rb +5 -5
- data/lib/hecks/projections/glossary.rb +15 -15
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +18 -17
- data/lib/hecks/projections/model.rb +25 -21
- data/lib/hecks/projections/oidc.rb +7 -7
- data/lib/hecks/projections/parser_table.rb +5 -5
- data/lib/hecks/projections/reference.rb +3 -3
- data/lib/hecks/projections/rust_vocabulary.rb +443 -0
- data/lib/hecks/projections/shape.rb +2 -2
- data/lib/hecks/projections/statements.rb +11 -11
- data/lib/hecks/projections/vocabulary.rb +9 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +29 -29
- data/lib/hecks/projector/docs_projector.rb +13 -13
- data/lib/hecks/projector/exporter.rb +42 -21
- data/lib/hecks/projector/ir_projector.rb +1 -1
- data/lib/hecks/projector/narrate_projector.rb +11 -11
- data/lib/hecks/projector/target.rb +13 -13
- data/lib/hecks/projector.rb +15 -15
- data/lib/hecks/query_ir.rb +47 -47
- data/lib/hecks/query_specification/common/comparators.rb +19 -3
- data/lib/hecks/query_specification/common/comparison.rb +109 -27
- data/lib/hecks/query_specification/common/dsl.rb +65 -9
- data/lib/hecks/query_specification/common/null_policy.rb +57 -13
- data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
- data/lib/hecks/query_specification/common/options.rb +25 -0
- data/lib/hecks/query_specification/field_path.rb +69 -15
- data/lib/hecks/query_specification/hop_path.rb +57 -20
- data/lib/hecks/query_specification/read_model/specification.rb +4 -0
- data/lib/hecks/rendering.rb +3 -3
- data/lib/hecks/router/namespace_installer.rb +3 -3
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/aggregate_lock.rb +11 -11
- data/lib/hecks/runtime/boot_gates.rb +3 -3
- data/lib/hecks/runtime/caller.rb +8 -8
- data/lib/hecks/runtime/capability_graph.rb +2 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +42 -63
- data/lib/hecks/runtime/command_interpreter.rb +159 -102
- data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
- data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
- data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
- data/lib/hecks/runtime/command_rules/emission.rb +18 -1
- data/lib/hecks/runtime/command_rules/references.rb +35 -35
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +11 -11
- data/lib/hecks/runtime/dispatcher.rb +283 -90
- data/lib/hecks/runtime/entity_element.rb +151 -67
- data/lib/hecks/runtime/entity_interpreter.rb +108 -82
- data/lib/hecks/runtime/errors.rb +19 -19
- data/lib/hecks/runtime/event.rb +6 -6
- data/lib/hecks/runtime/identity.rb +22 -22
- data/lib/hecks/runtime/instance.rb +39 -14
- data/lib/hecks/runtime/interpreting.rb +12 -12
- data/lib/hecks/runtime/invocation.rb +276 -0
- data/lib/hecks/runtime/loader.rb +14 -14
- data/lib/hecks/runtime/outbox.rb +23 -23
- data/lib/hecks/runtime/policy_interpreter.rb +54 -54
- data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
- data/lib/hecks/runtime/query_interpreter.rb +55 -64
- data/lib/hecks/runtime/reaction_invocation.rb +20 -20
- data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
- data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
- data/lib/hecks/runtime/reference_hop.rb +6 -6
- data/lib/hecks/runtime/refusal_wording.rb +92 -115
- data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
- data/lib/hecks/runtime/registry/verification.rb +36 -26
- data/lib/hecks/runtime/registry.rb +45 -28
- data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
- data/lib/hecks/runtime/routing.rb +10 -153
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
- data/lib/hecks/runtime/saga_interpreter.rb +64 -64
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +9 -9
- data/lib/hecks/runtime/tenant_scope.rb +5 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +318 -127
- data/lib/hecks/runtime/value/entity_list_coercion.rb +34 -34
- data/lib/hecks/runtime/value.rb +21 -21
- data/lib/hecks/runtime.rb +7 -7
- data/lib/hecks/storehouse.rb +64 -64
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +205 -4
- data/lib/hecks.rb +13 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
- metadata +22 -2
|
@@ -8,6 +8,7 @@ module Hecks
|
|
|
8
8
|
# vocabulary of its own; `WorldBuilder#record_binding` reads `#to_h`
|
|
9
9
|
# back out.
|
|
10
10
|
class SettingsCollector
|
|
11
|
+
# Starts with no settings recorded.
|
|
11
12
|
def initialize = @values = {}
|
|
12
13
|
|
|
13
14
|
def method_missing(key, *args, &)
|
|
@@ -19,9 +20,9 @@ module Hecks
|
|
|
19
20
|
def to_h = @values
|
|
20
21
|
end
|
|
21
22
|
|
|
22
|
-
#
|
|
23
|
+
# The aggregate-qualified mirror (#143) — a `.world` file's own
|
|
23
24
|
# `Pizzas::Order.charged_by("Stripe") do ... end` visually mirrors the
|
|
24
|
-
#
|
|
25
|
+
# same bind line the sibling `.hecksagon` file already writes
|
|
25
26
|
# (`HecksagonBuilder`'s own `BindingProxy`), but `IR::World#for_verb`/
|
|
26
27
|
# `#for_binding` key purely by verb and adapter name — the aggregate
|
|
27
28
|
# qualifier is never read back out, it exists only for that visual
|
|
@@ -30,12 +31,19 @@ module Hecks
|
|
|
30
31
|
# not, has to land in the exact same `@settings` write path
|
|
31
32
|
# (`WorldBuilder#record_binding`).
|
|
32
33
|
class WorldConstProxy
|
|
34
|
+
# Mints the stand-in module a bare domain constant resolves to inside a `.world` block.
|
|
35
|
+
#
|
|
36
|
+
# @param builder [Bluebook::DSL::WorldBuilder] the builder every verb called through the
|
|
37
|
+
# module's proxies records its settings on
|
|
38
|
+
# @return [Module] an anonymous module whose `const_missing` answers a `WorldConstProxy`,
|
|
39
|
+
# whatever aggregate name follows `::`
|
|
33
40
|
def self.namespace(builder)
|
|
34
41
|
Module.new do
|
|
35
42
|
define_singleton_method(:const_missing) { |_aggregate| WorldConstProxy.new(builder) }
|
|
36
43
|
end
|
|
37
44
|
end
|
|
38
45
|
|
|
46
|
+
# @param builder [Bluebook::DSL::WorldBuilder] the builder verb calls are recorded on
|
|
39
47
|
def initialize(builder) = @builder = builder
|
|
40
48
|
|
|
41
49
|
def method_missing(verb, *args, **kwargs, &block) = @builder.record_binding(verb, args, kwargs, block)
|
|
@@ -45,7 +53,7 @@ module Hecks
|
|
|
45
53
|
|
|
46
54
|
# Parses a `.world` file's top-level DSL block into a `World` — a
|
|
47
55
|
# domain's own `realm`/`latest` version markers plus its adapter bind
|
|
48
|
-
#
|
|
56
|
+
# settings, one entry per `verb("Adapter") do ... end` call (whether
|
|
49
57
|
# written bare or aggregate-qualified through `WorldConstProxy`'s
|
|
50
58
|
# visual mirror of a sibling `.hecksagon` file's own bind).
|
|
51
59
|
class WorldBuilder
|
|
@@ -53,23 +61,37 @@ module Hecks
|
|
|
53
61
|
|
|
54
62
|
include WordGate
|
|
55
63
|
|
|
64
|
+
# @param domain [String] name of the domain whose world this is
|
|
56
65
|
def initialize(domain)
|
|
57
66
|
@domain = domain
|
|
58
67
|
@settings = {}
|
|
59
68
|
end
|
|
60
69
|
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
70
|
+
# Names the realm this world belongs to, such as `"Examples"` or `"QA"`.
|
|
71
|
+
#
|
|
72
|
+
# `realm_impl`/`latest_impl` answer the `realm`/`latest` words
|
|
73
|
+
# through the table's `calls:` column — item #13's full
|
|
74
|
+
# metaprogrammed dispatch (slice 5). Neither bootstrap-reachable
|
|
75
|
+
# (checked directly). Reached through `WordGate`'s
|
|
64
76
|
# `word_gate_dispatch`, called explicitly below since
|
|
65
77
|
# `WorldBuilder`'s own class-level `method_missing` (the
|
|
66
78
|
# open-verb catch-all beneath this) always wins over the
|
|
67
79
|
# module's — see `word_gate.rb`'s own header for the full
|
|
68
80
|
# mechanism.
|
|
81
|
+
#
|
|
82
|
+
# @param value [String, Symbol] the realm's name; blankness is judged by the world
|
|
83
|
+
# language at `build`, not here
|
|
84
|
+
# @return [String] the realm as stored
|
|
69
85
|
def realm_impl(value)
|
|
70
86
|
@realm = required(value, "realm")
|
|
71
87
|
end
|
|
72
88
|
|
|
89
|
+
# Names the bluebook version this world treats as latest, which `ProjectRegister` compares
|
|
90
|
+
# with the bluebook's own declared `version`.
|
|
91
|
+
#
|
|
92
|
+
# @param value [String, Symbol, Numeric] the version marker; blankness is judged by the
|
|
93
|
+
# world language at `build`, not here
|
|
94
|
+
# @return [String] the version as stored
|
|
73
95
|
def latest_impl(value)
|
|
74
96
|
@latest = required(value, "latest version")
|
|
75
97
|
end
|
|
@@ -83,11 +105,20 @@ module Hecks
|
|
|
83
105
|
|
|
84
106
|
def respond_to_missing?(_name, _include_private = false) = true
|
|
85
107
|
|
|
86
|
-
#
|
|
108
|
+
# Records one bind's settings under both its verb and its `verb:adapter` key.
|
|
109
|
+
#
|
|
110
|
+
# A method of its own, apart from `method_missing` (#143), so
|
|
87
111
|
# `WorldConstProxy`'s own aggregate-qualified verb calls
|
|
88
112
|
# (`Pizzas::Order.charged_by(...)`) write into the exact same
|
|
89
113
|
# place the bare top-level spelling (`charged_by(...)`) already
|
|
90
114
|
# does — one write path, two spellings.
|
|
115
|
+
#
|
|
116
|
+
# @param verb [Symbol, String] the bind verb, such as `:persisted_by`
|
|
117
|
+
# @param args [Array<Object>] the call's positional arguments; the first names the adapter
|
|
118
|
+
# @param kwargs [Hash{Symbol => Object}] settings given inline as keyword arguments
|
|
119
|
+
# @param block [Proc, nil] a settings block, evaluated against a `SettingsCollector`
|
|
120
|
+
# @return [Hash{Symbol => Object}] the settings just recorded: `:adapter` (a String),
|
|
121
|
+
# then the keyword arguments, then the block's settings, later ones winning
|
|
91
122
|
def record_binding(verb, args, kwargs, block)
|
|
92
123
|
collector = SettingsCollector.new
|
|
93
124
|
collector.instance_eval(&block) if block
|
|
@@ -96,18 +127,30 @@ module Hecks
|
|
|
96
127
|
@settings["#{verb}:#{args.first.to_s.downcase}"] = value
|
|
97
128
|
end
|
|
98
129
|
|
|
130
|
+
# Assembles the realm, version marker and bind settings, judged by the world language.
|
|
131
|
+
#
|
|
132
|
+
# @return [Bluebook::World] the world, returned once the language accepts it
|
|
133
|
+
# @raise [Bluebook::DSL::Malformed] if the world language refuses the declaration, such
|
|
134
|
+
# as a blank `realm`
|
|
99
135
|
def build
|
|
100
136
|
MetaValidator.call_world(
|
|
101
137
|
World.new(domain: @domain, realm: @realm, latest: @latest, settings: @settings)
|
|
102
138
|
)
|
|
103
139
|
end
|
|
104
140
|
|
|
141
|
+
# Evaluates a `Hecks.world` block against a fresh builder and returns the world it declared.
|
|
142
|
+
#
|
|
105
143
|
# `ConstShim`'s resolver, the same bridge `HecksagonBuilder`/
|
|
106
144
|
# `BluebookBuilder` already wrap their own `instance_eval` in
|
|
107
|
-
# (#143) — without it, `Pizzas::Order.charged_by(...)`
|
|
145
|
+
# (#143) — without it, `Pizzas::Order.charged_by(...)` raises
|
|
108
146
|
# `NameError: uninitialized constant Pizzas` for every `.world`
|
|
109
147
|
# file using the aggregate-qualified mirror form, since a bare
|
|
110
148
|
# `Pizzas` has no real constant to resolve to.
|
|
149
|
+
#
|
|
150
|
+
# @param domain [String] name of the domain whose world this is
|
|
151
|
+
# @yield the world body, evaluated with the builder as `self`; may be omitted
|
|
152
|
+
# @return [Bluebook::World] the judged world
|
|
153
|
+
# @raise [Bluebook::DSL::Malformed] if the world language refuses the declaration
|
|
111
154
|
def self.build(domain, &block)
|
|
112
155
|
builder = new(domain)
|
|
113
156
|
resolver = ->(_domain) { WorldConstProxy.namespace(builder) }
|
|
@@ -3,10 +3,10 @@ require_relative "expression/ast_json"
|
|
|
3
3
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Bluebook
|
|
6
|
-
# An entity, as a
|
|
6
|
+
# An entity, as a Ruby class — a piece of an aggregate that has an identity
|
|
7
7
|
# of its own.
|
|
8
8
|
#
|
|
9
|
-
# Crossing over closes the
|
|
9
|
+
# Crossing over closes the owner chain. An entity declares commands, and
|
|
10
10
|
# until now those commands had no owner that could state an identity: an
|
|
11
11
|
# entity was an IR object, not a construct, so `Construct#hecks_fqn` refused
|
|
12
12
|
# rather than answering "Deposit" and looking right. Four of banking's
|
|
@@ -14,15 +14,15 @@ module Hecks
|
|
|
14
14
|
# `Banking::Account.Ledger.Deposit` — which is the id the judge already mints
|
|
15
15
|
# for them.
|
|
16
16
|
#
|
|
17
|
-
#
|
|
17
|
+
# Not const_set, for the same reason a command is not: a name inside one
|
|
18
18
|
# aggregate can denote more than one kind of thing, so the constant tree
|
|
19
19
|
# cannot index it.
|
|
20
20
|
#
|
|
21
|
-
# It must stay
|
|
21
|
+
# It must stay structurally interchangeable with an aggregate — the runtime
|
|
22
22
|
# builds `Instance.new(aggregate: entity)` and `CommandRules` takes either as
|
|
23
23
|
# `declaring` — so it answers `hecks_name`, `attributes`, `attribute`,
|
|
24
24
|
# `identified_by` and `lifecycle` exactly as an aggregate does. And it must
|
|
25
|
-
# keep
|
|
25
|
+
# keep not answering `value_object`: `Value.for_attribute` sniffs for that
|
|
26
26
|
# method to tell a piece from a head.
|
|
27
27
|
class Entity
|
|
28
28
|
extend Construct
|
|
@@ -39,26 +39,26 @@ module Hecks
|
|
|
39
39
|
# S17, ADR 0026 — "That is what `entity` is for, and `entity` is
|
|
40
40
|
# declared by the language and used zero times in it" (the ADR's
|
|
41
41
|
# own words). Dispatch nests inside Handler, so an entity's own
|
|
42
|
-
#
|
|
42
|
+
# nested entities are part of its wire shape now, the same way
|
|
43
43
|
# an aggregate's always were — the field the ADR names as
|
|
44
44
|
# declared-but-unused until this slice.
|
|
45
45
|
entities: many(:entities),
|
|
46
|
-
# ADR 0028 — a piece's own `given`, the
|
|
46
|
+
# ADR 0028 — a piece's own `given`, the same shape
|
|
47
47
|
# `Aggregate#preconditions` already carries (its own `emits_ir`
|
|
48
48
|
# row, identical). A precondition shared across this piece's own
|
|
49
49
|
# commands, declared once — a command references it back by
|
|
50
|
-
# name; the resolved text still lands on
|
|
50
|
+
# name; the resolved text still lands on each referencing
|
|
51
51
|
# command's own `givens` either way, so this field is read-only
|
|
52
52
|
# documentation of what the piece itself declared, the same
|
|
53
53
|
# relationship `Aggregate.preconditions` already has to its own
|
|
54
54
|
# commands.
|
|
55
55
|
preconditions: -> { preconditions.map { |rule| Expression::AstJson.rule_row(rule) } },
|
|
56
|
-
# A piece's
|
|
56
|
+
# A piece's own shape rule, checked against every instance of
|
|
57
57
|
# this piece the aggregate holds (Admissibility#enforce_
|
|
58
|
-
# invariants' own recursive walk) — the
|
|
58
|
+
# invariants' own recursive walk) — the same relationship
|
|
59
59
|
# `ValueObject#invariants` already has to its own instances,
|
|
60
60
|
# one level up the construct tree. Not a separate enforcement
|
|
61
|
-
# boundary; still checked at the
|
|
61
|
+
# boundary; still checked at the same two points (after every
|
|
62
62
|
# mutation, before save) the aggregate's own invariants always
|
|
63
63
|
# were — see that method's own comment for why this does not
|
|
64
64
|
# contradict "there is no separate entity invariant."
|
|
@@ -4,17 +4,17 @@ require_relative "resolver"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Bluebook
|
|
6
6
|
module Expression
|
|
7
|
-
# ── AST → JSON — walks the
|
|
7
|
+
# ── AST → JSON — walks the real Evaluator/Resolver AST (the same
|
|
8
8
|
# objects a live dispatch parses `given`/`ensures`/invariant text
|
|
9
9
|
# into — see docs/implemented/guides/running-a-runtime.md's "The
|
|
10
10
|
# expression grammar") and emits plain, JSON-serializable Ruby
|
|
11
11
|
# Hashes, tagged by `"op"`.
|
|
12
12
|
#
|
|
13
|
-
#
|
|
14
|
-
# `emit_bool`/`emit_resolver` already do, over the
|
|
15
|
-
# file's own methods build
|
|
13
|
+
# The same two-method walk `rust/project/expr_emitter.rb`'s own
|
|
14
|
+
# `emit_bool`/`emit_resolver` already do, over the same AST — that
|
|
15
|
+
# file's own methods build Rust source-code strings for `rust/
|
|
16
16
|
# project`'s codegen (`rust/src/kernel::expr::Expr` literals, baked
|
|
17
|
-
# into a generated domain's own compiled binary); this builds
|
|
17
|
+
# into a generated domain's own compiled binary); this builds data
|
|
18
18
|
# instead, for a genuinely different consumer with a genuinely
|
|
19
19
|
# different constraint: `rust/host` can never link the `rust`
|
|
20
20
|
# (kernel) crate at all (a real, load-bearing build constraint —
|
|
@@ -22,25 +22,25 @@ module Hecks
|
|
|
22
22
|
# path dependency would statically bake every domain's generated
|
|
23
23
|
# dispatch code into every Lambda binary), so a value object's own
|
|
24
24
|
# `invariant` predicate has to travel as something `rust/host` can
|
|
25
|
-
# deserialize and interpret itself, at
|
|
25
|
+
# deserialize and interpret itself, at runtime, from `ir.json` — the
|
|
26
26
|
# exact same relationship `rust/project`'s own `Expr` literals
|
|
27
27
|
# already have to the compiled kernel, one layer further out.
|
|
28
28
|
#
|
|
29
|
-
#
|
|
29
|
+
# Lives in core `lib/hecks`, not `rust/project/` — `rust/project.rb`
|
|
30
30
|
# is a separate, downstream toolchain
|
|
31
31
|
# (`lib/hecks/projector.rb`'s own header: "a whole separate Ruby
|
|
32
32
|
# program"), never `require`d by core `lib/hecks/bluebook/*.rb`
|
|
33
33
|
# files (confirmed: no core file does). `value_object.rb`'s own
|
|
34
|
-
# `invariants:` IR emission needs this for
|
|
34
|
+
# `invariants:` IR emission needs this for every domain's ordinary
|
|
35
35
|
# `to_h`/`ir.json` export — golden fixtures, `hecks-parse`'s parity
|
|
36
36
|
# comparisons, and any deploy artifact, not only a `bin/project_rust`
|
|
37
37
|
# run — so it belongs beside `Evaluator`/`Resolver` themselves, not
|
|
38
38
|
# bolted onto a tool that only sometimes runs.
|
|
39
39
|
#
|
|
40
|
-
#
|
|
40
|
+
# Complete, not corpus-scoped: every node this grammar admits gets
|
|
41
41
|
# a real arm, the identical "raise, don't silently drop" discipline
|
|
42
42
|
# `expr_emitter.rb`'s own `emit_bool`/`emit_resolver` already hold
|
|
43
|
-
# to — even though, as of this writing, no real corpus
|
|
43
|
+
# to — even though, as of this writing, no real corpus value object
|
|
44
44
|
# invariant exercises `Include`/`Modulo`/`BlockPredicate`/`Find`/
|
|
45
45
|
# `Array`/`MatchesRegex`/`Presence`/`Assignment`/`Split`/`StartsWith`/
|
|
46
46
|
# `EndsWith`/`First`/`Last` (only `given`/`ensures` clauses do, elsewhere in
|
|
@@ -48,15 +48,15 @@ module Hecks
|
|
|
48
48
|
# `rust/host/src/expr_json.rs`'s own header names exactly which of
|
|
49
49
|
# these its interpreter evaluates for real today versus refuses
|
|
50
50
|
# cleanly — a narrower, deliberate, documented boundary on the
|
|
51
|
-
#
|
|
52
|
-
# to write
|
|
51
|
+
# interpreting side, not on this emitting side: an author is free
|
|
52
|
+
# to write any real expression in a value object's own `invariant`,
|
|
53
53
|
# and this always emits it faithfully; whether `rust/host` can yet
|
|
54
|
-
#
|
|
54
|
+
# check it at mint time is that file's own question to answer, not
|
|
55
55
|
# this one's to pre-empt by refusing to even try.
|
|
56
56
|
module AstJson
|
|
57
57
|
module_function
|
|
58
58
|
|
|
59
|
-
#
|
|
59
|
+
# **The closed op roster** — every `"op"` tag the walkers below can
|
|
60
60
|
# emit, pinned so a reader (or a spec) can refuse a tag it does
|
|
61
61
|
# not know instead of guessing. A new node kind is a new entry
|
|
62
62
|
# here, a new arm below, and a new arm in every reader.
|
|
@@ -68,7 +68,7 @@ module Hecks
|
|
|
68
68
|
matches_regex presence assignment split starts_with ends_with
|
|
69
69
|
].freeze
|
|
70
70
|
|
|
71
|
-
#
|
|
71
|
+
# One rule row, the way every rule site emits it — description and
|
|
72
72
|
# canonical text (what every reader has always had) plus the
|
|
73
73
|
# structured form, derived from the same text. `ast` is a pure
|
|
74
74
|
# function of `canonical`: the IR carries both so a reader that
|
|
@@ -149,9 +149,9 @@ module Hecks
|
|
|
149
149
|
|
|
150
150
|
# The JSON-target sibling of `expr_emitter.rb`'s own
|
|
151
151
|
# `emit_include` — see that method's own comment for the full
|
|
152
|
-
# reasoning (a
|
|
153
|
-
# representable shape on
|
|
154
|
-
# rewrite it identically into an
|
|
152
|
+
# reasoning (a literal array haystack has no `Expr::Include`-
|
|
153
|
+
# representable shape on either target, kernel or host, so both
|
|
154
|
+
# rewrite it identically into an or-of-equalities at emission
|
|
155
155
|
# time rather than carrying a shape neither interpreter could
|
|
156
156
|
# evaluate). A non-literal haystack still emits `include`
|
|
157
157
|
# unchanged.
|
|
@@ -172,7 +172,7 @@ module Hecks
|
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
# One case arm per Resolver node type — the class header above is
|
|
175
|
-
# explicit that this dispatch must stay
|
|
175
|
+
# explicit that this dispatch must stay complete and in one place
|
|
176
176
|
# ("every node this grammar admits gets a real arm"); splitting it
|
|
177
177
|
# into several methods would hide whether the set is still
|
|
178
178
|
# exhaustive instead of making that visible at a glance.
|
|
@@ -184,7 +184,7 @@ module Hecks
|
|
|
184
184
|
when Resolver::StringLiteral then { "op" => "str", "value" => node.value }
|
|
185
185
|
when Resolver::BoolLiteral then { "op" => "bool", "value" => node.value }
|
|
186
186
|
when Resolver::NilLiteral then { "op" => "nil" }
|
|
187
|
-
# `path` is the
|
|
187
|
+
# `path` is the same shape `find.path` already has — segments, not
|
|
188
188
|
# a dotted string a reader would have to split by its own rule.
|
|
189
189
|
when Resolver::Lookup then { "op" => "lookup", "path" => node.path.split(".") }
|
|
190
190
|
when Resolver::Addition then { "op" => "add", "left" => emit_resolver(node.left), "right" => emit_resolver(node.right) }
|
|
@@ -4,8 +4,8 @@ require_relative "resolver"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Bluebook
|
|
6
6
|
module Expression
|
|
7
|
-
#
|
|
8
|
-
# carries as `ast` back into the
|
|
7
|
+
# The inverse of `AstJson` — reads the `"op"`-tagged JSON a rule row
|
|
8
|
+
# carries as `ast` back into the same `Evaluator`/`Resolver` node
|
|
9
9
|
# Structs `Evaluator.parse` builds from `canonical`. This is how the
|
|
10
10
|
# runtime evaluates a rule without re-parsing its text: the one
|
|
11
11
|
# parse happened at DSL-build time, behind `AstJson`; dispatch walks
|
|
@@ -19,7 +19,7 @@ module Hecks
|
|
|
19
19
|
# the spec proved the round trip first, then the runtime adopted it.
|
|
20
20
|
#
|
|
21
21
|
# One deliberate asymmetry, inherited: `AstJson` rewrites a
|
|
22
|
-
#
|
|
22
|
+
# literal-array `.include?` into an or of equalities (see
|
|
23
23
|
# `emit_include`), so reading never produces an `Include` over an
|
|
24
24
|
# `ArrayLiteral`. Evaluation is unchanged by that rewrite, which is
|
|
25
25
|
# exactly what the equivalence spec pins.
|
|
@@ -4,7 +4,7 @@ require_relative "../../vocabulary"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Bluebook
|
|
6
6
|
module Expression
|
|
7
|
-
# Rewrites a predicate's
|
|
7
|
+
# Rewrites a predicate's source text into one canonical spelling
|
|
8
8
|
# before it's ever parsed or hashed — collapsing whitespace and
|
|
9
9
|
# folding admitted synonyms (e.g. `.length` → `.size`) via the
|
|
10
10
|
# `RULES` table projected from the grammar chapter, so two byte-
|
|
@@ -16,7 +16,7 @@ module Hecks
|
|
|
16
16
|
|
|
17
17
|
STRATEGIES = Hecks::Vocabulary.fetch("NormalisationStrategy")
|
|
18
18
|
|
|
19
|
-
#
|
|
19
|
+
# **Read, not restated** — the admitted normalisation rules, projected
|
|
20
20
|
# from the grammar chapter by bin/expression_projection exactly as
|
|
21
21
|
# the evaluator's operator table is. See Evaluator::PROJECTION for
|
|
22
22
|
# why a projection rather than a boot.
|
|
@@ -61,7 +61,7 @@ module Hecks
|
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
# Applies a normalisation rule to the text
|
|
64
|
+
# Applies a normalisation rule to the text outside quoted string
|
|
65
65
|
# literals only, copying every quoted run through byte-for-byte.
|
|
66
66
|
# Every rule here (collapse_whitespace, the `.length`→`.size` fold)
|
|
67
67
|
# used to run quote-blind — `"a b"` collapsed to `"a b"` and
|
|
@@ -69,7 +69,7 @@ module Hecks
|
|
|
69
69
|
# source outside the quotes, silently rewriting what a predicate
|
|
70
70
|
# compares a string attribute against, not merely how the
|
|
71
71
|
# predicate itself is spelled. A canonical string literal's
|
|
72
|
-
#
|
|
72
|
+
# contents are data, never syntax to normalise.
|
|
73
73
|
#
|
|
74
74
|
# Handles both `"` and `'` delimiters (this grammar's own
|
|
75
75
|
# `Resolver.quoted?` admits either), quote-aware exactly the way
|
|
@@ -93,9 +93,9 @@ module Hecks
|
|
|
93
93
|
result << yield(buffer)
|
|
94
94
|
# `char.dup`, not `char.to_s` (a no-op on a String — always
|
|
95
95
|
# returns self, never a copy) and not `+char` either
|
|
96
|
-
# (`String#+@` only dups a
|
|
96
|
+
# (`String#+@` only dups a frozen receiver; `each_char`'s
|
|
97
97
|
# yielded strings aren't frozen, so `+char` is just as
|
|
98
|
-
# much a no-op here). Without a
|
|
98
|
+
# much a no-op here). Without a real copy, `buffer` and
|
|
99
99
|
# `quote` alias the same mutable object: the very next
|
|
100
100
|
# `buffer << char` grows `quote` right along with it, so
|
|
101
101
|
# `char == quote` can only ever compare a single character
|
|
@@ -103,12 +103,12 @@ module Hecks
|
|
|
103
103
|
# literal — everything after a predicate's first quoted
|
|
104
104
|
# string silently skipped normalisation for the rest of
|
|
105
105
|
# the text, undetected because passing text through
|
|
106
|
-
# unnormalised is silent.
|
|
107
|
-
# here, which only checks that quoted
|
|
106
|
+
# unnormalised is silent. Masked by every existing spec
|
|
107
|
+
# here, which only checks that quoted contents survive
|
|
108
108
|
# untouched (the M7 fix this method exists for) — that
|
|
109
109
|
# still holds by accident once the bug makes the "outside"
|
|
110
110
|
# branch unreachable. Found live: a multi-line `given`/
|
|
111
|
-
# `ensures` block whose
|
|
111
|
+
# `ensures` block whose only quoted literal closes before a
|
|
112
112
|
# later line — the newline and that later line's own
|
|
113
113
|
# indentation went uncollapsed, diverging from
|
|
114
114
|
# `hecks-parse`'s own (correct) single-space join.
|
|
@@ -16,9 +16,9 @@ module Hecks
|
|
|
16
16
|
|
|
17
17
|
# Six operators, reduced to two primitives (less_than, equal) combined
|
|
18
18
|
# with a small boolean algebra: compares_less_than/compares_equal choose
|
|
19
|
-
# which primitive(s)
|
|
19
|
+
# which primitive(s) or together, negated inverts the result.
|
|
20
20
|
#
|
|
21
|
-
#
|
|
21
|
+
# **Read, not restated**. This table is the checked-in projection of the
|
|
22
22
|
# grammar chapter's admitted set (bin/expression_projection), joined with
|
|
23
23
|
# the algebra Vocabulary::Comparison declares. The evaluator cannot
|
|
24
24
|
# boot the chapter that configures it — the Prism adapter normalises
|
|
@@ -65,8 +65,8 @@ module Hecks
|
|
|
65
65
|
interpret(ast_cache[expr] ||= parse(expr), state, attrs)
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
#
|
|
69
|
-
# answering `canonical` and `ast`) by walking its
|
|
68
|
+
# The rule-shaped entry — evaluates a Given/Invariant (anything
|
|
69
|
+
# answering `canonical` and `ast`) by walking its structured form,
|
|
70
70
|
# never re-parsing the text: the one parse happened at DSL-build
|
|
71
71
|
# time behind `AstJson`, and `AstReader` turns that tree back into
|
|
72
72
|
# the same nodes `parse` would have built (the equivalence is
|
|
@@ -87,18 +87,18 @@ module Hecks
|
|
|
87
87
|
rule.ast ? AstReader.read_predicate(rule.ast) : parse(rule.canonical)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
# A refused `given`/`ensures`/`invariant` names its own
|
|
90
|
+
# A refused `given`/`ensures`/`invariant` names its own description
|
|
91
91
|
# ("not already superseded") but, on its own, not what the block
|
|
92
92
|
# actually evaluated to — the difference between "the rule is right
|
|
93
93
|
# and my data is wrong" and "the rule is subtly wrong" is often just
|
|
94
94
|
# seeing the two operands. Scoped to the single shape that has one
|
|
95
|
-
# honest answer: `expr`'s own
|
|
95
|
+
# honest answer: `expr`'s own top-level node is a bare `Compare` —
|
|
96
96
|
# not `Or`/`And`/`Not` (which of several sub-comparisons would even
|
|
97
97
|
# be "the" one at fault is genuinely ambiguous), `Include` (no
|
|
98
98
|
# left/right to show), or `Resolve` (a bare boolean read, nothing to
|
|
99
99
|
# compare against). Values are rendered with `Rendering.describe`,
|
|
100
100
|
# the same house style every other refusal already prints a value
|
|
101
|
-
# through. Returns `nil` — not raised — on anything else,
|
|
101
|
+
# through. Returns `nil` — not raised — on anything else, including
|
|
102
102
|
# an operand that itself fails to resolve (`EvaluationError`): a
|
|
103
103
|
# missing diagnostic is a worse debugging experience than none, a
|
|
104
104
|
# crash while building one is worse still.
|
|
@@ -122,8 +122,8 @@ module Hecks
|
|
|
122
122
|
left, right = split_top_level(expr, "&&")
|
|
123
123
|
return And.new(left: parse(left), right: parse(right)) if left
|
|
124
124
|
|
|
125
|
-
# Tried
|
|
126
|
-
# the
|
|
125
|
+
# Tried before `.include?`/comparisons, not after — `!` negates
|
|
126
|
+
# the whole boolean expression that follows it (`!names.include?(x)`
|
|
127
127
|
# means `!(names.include?(x))`, never "call .include? on the negated
|
|
128
128
|
# receiver"), so the leading marker has to be stripped and the
|
|
129
129
|
# remainder re-parsed before anything downstream gets a chance to
|
|
@@ -181,7 +181,7 @@ module Hecks
|
|
|
181
181
|
|
|
182
182
|
# The algebra itself, on values already resolved — split out so a sign
|
|
183
183
|
# test (SignTest#compares_via names an Operator symbol) can apply the
|
|
184
|
-
#
|
|
184
|
+
# same primitives compare() uses against the literal 0, rather than
|
|
185
185
|
# re-deriving positive?/negative?/zero? by hand a second time.
|
|
186
186
|
def apply(comparator, lhs, rhs)
|
|
187
187
|
result = (comparator.compares_less_than && less_than(lhs, rhs)) ||
|
|
@@ -215,13 +215,13 @@ module Hecks
|
|
|
215
215
|
value.nil? ? "nil" : value.class.name
|
|
216
216
|
end
|
|
217
217
|
|
|
218
|
-
#
|
|
218
|
+
# The same mis-split `Resolver.match_call` had (its own comment
|
|
219
219
|
# has the full story), found here too by the same generator: a
|
|
220
|
-
# `.include?` needle can itself be — or contain —
|
|
220
|
+
# `.include?` needle can itself be — or contain — another
|
|
221
221
|
# `.include?` call (`"".include?(arr.all? { |el| "".include?("")
|
|
222
222
|
# }.to_s)`, a String built via `.to_s` off a block predicate
|
|
223
223
|
# whose own body happens to include one) — `rindex` finds the
|
|
224
|
-
#
|
|
224
|
+
# innermost occurrence, not the outermost this split actually
|
|
225
225
|
# needs. Fixed identically: try each occurrence left to right,
|
|
226
226
|
# keep the first whose own balanced-paren match reaches the
|
|
227
227
|
# string's last character — `Resolver.matching_paren` is reused
|
|
@@ -311,13 +311,13 @@ module Hecks
|
|
|
311
311
|
# `{`/`}` depth -- vendored addition, not (yet) upstream
|
|
312
312
|
# hecks (migration plan task 9): this method already
|
|
313
313
|
# treats `(`/`)` as a grouping construct so an operator
|
|
314
|
-
#
|
|
314
|
+
# inside a call's parens is never mistaken for a top-level
|
|
315
315
|
# split point ; `{`/`}` needed the identical treatment the
|
|
316
316
|
# moment `Bluebook::Expression::Resolver` grew block-taking
|
|
317
317
|
# `.all?`/`.any?`/`.none? { |s| PREDICATE }` support (see
|
|
318
318
|
# resolver.rb's own `BlockPredicate` addition) -- without
|
|
319
|
-
# this, an operator
|
|
320
|
-
# `s.length > 0`) reads as a top-level split of the
|
|
319
|
+
# this, an operator inside the block's own predicate (e.g.
|
|
320
|
+
# `s.length > 0`) reads as a top-level split of the whole
|
|
321
321
|
# `value.split("::").all? { |s| s.length > 0 }` expression,
|
|
322
322
|
# confirmed live via `Lexicon::Lexicon.Lookup`/`Query::Query.
|
|
323
323
|
# Run` (the exact `Phrase` invariant this gap was found
|
|
@@ -329,7 +329,7 @@ module Hecks
|
|
|
329
329
|
# a quoted literal either, so this sits beside the existing
|
|
330
330
|
# paren-depth branch, not instead of it.
|
|
331
331
|
#
|
|
332
|
-
# `[`/`]` -- the identical lesson a
|
|
332
|
+
# `[`/`]` -- the identical lesson a third time (found live via
|
|
333
333
|
# the type-directed bounded-exhaustive expression generator,
|
|
334
334
|
# Phase 7 of the equivalence-gap plan): `Resolver::ArrayLiteral`
|
|
335
335
|
# (`[a, b]`) can appear as a general sub-expression, not only
|
|
@@ -337,7 +337,7 @@ module Hecks
|
|
|
337
337
|
# attribute or a synthesized literal is embedded anywhere else
|
|
338
338
|
# -- and an element containing a top-level `+`/comparison of
|
|
339
339
|
# its own (`[0, 0 + 0]`) used to read as a split point for
|
|
340
|
-
#
|
|
340
|
+
# this expression's own boolean/comparison grammar, exactly
|
|
341
341
|
# the way an un-tracked `{`/`}` once did for block predicates.
|
|
342
342
|
elsif ["(", "{", "["].include?(char)
|
|
343
343
|
depth += 1
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# **Hand-written** — the block-predicate/find family of the leaf grammar
|
|
2
2
|
# (`Bluebook::Expression::Resolver`'s own `.all?`/`.any?`/`.none?`/
|
|
3
3
|
# `.find` suffixes), split into this sibling file to keep resolver.rb
|
|
4
|
-
# under Metrics/ModuleLength (350) : this reopens the
|
|
4
|
+
# under Metrics/ModuleLength (350) : this reopens the same `Resolver`
|
|
5
5
|
# module resolver.rb defines, the long nested `module A; module B; ...`
|
|
6
6
|
# form (not the compact `A::B` form) so bare constant/method lookups
|
|
7
7
|
# here (`EvaluationError`, `Evaluator`, `describe`, `unwrap_scalar`,
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
module Hecks
|
|
13
13
|
module Bluebook
|
|
14
14
|
module Expression
|
|
15
|
-
# Reopens the
|
|
15
|
+
# Reopens the same `Resolver` module resolver.rb defines — see this
|
|
16
16
|
# file's own header comment above for why the block-predicate/find
|
|
17
17
|
# family lives here rather than in resolver.rb itself.
|
|
18
18
|
module Resolver
|
|
@@ -23,12 +23,12 @@ module Hecks
|
|
|
23
23
|
# (`value.split("::").all? { |s| s.length > 0 }`). Structurally
|
|
24
24
|
# different from every other addition in this file: every prior
|
|
25
25
|
# suffix is a flat receiver -> scalar transform, but a block
|
|
26
|
-
# predicate needs to evaluate its own sub-expression
|
|
27
|
-
#
|
|
26
|
+
# predicate needs to evaluate its own sub-expression once per
|
|
27
|
+
# element with the block parameter bound to that element. Kept
|
|
28
28
|
# minimal per the migration plan's own instruction -- no
|
|
29
29
|
# persistent iteration-variable concept added to Resolver's
|
|
30
30
|
# state model at all ; `predicate` below is a fully-parsed
|
|
31
|
-
#
|
|
31
|
+
# evaluator ast (not a Resolver ast -- the predicate is a
|
|
32
32
|
# boolean/comparison expression like `s.length > 0`, exactly the
|
|
33
33
|
# grammar `Bluebook::Expression::Evaluator` owns, not this
|
|
34
34
|
# module's own leaf grammar), parsed once at `parse`-time same as
|
|
@@ -78,7 +78,7 @@ module Hecks
|
|
|
78
78
|
|
|
79
79
|
# Every suffix that opens a `{ |x| ... }` block, `.find` included
|
|
80
80
|
# -- shared by `parse_block_opener` below, and by nothing else
|
|
81
|
-
# (this is
|
|
81
|
+
# (this is not `BLOCK_PREDICATE_MODES` — `.find` isn't a mode
|
|
82
82
|
# `evaluate_block_predicate` aggregates through, it builds a
|
|
83
83
|
# `Find` node instead, see below).
|
|
84
84
|
BLOCK_OPENER_SUFFIXES = (BLOCK_PREDICATE_MODES.keys + ["find"]).freeze
|
|
@@ -88,27 +88,27 @@ module Hecks
|
|
|
88
88
|
# last among the suffix rules (right before the `Lookup`
|
|
89
89
|
# catch-all) since a block's own predicate text can itself
|
|
90
90
|
# contain almost anything a leaf expression can, including
|
|
91
|
-
#
|
|
91
|
+
# another block-opening suffix -- letting every more specific
|
|
92
92
|
# rule above try first avoids this one accidentally swallowing a
|
|
93
93
|
# receiver another rule was meant to parse.
|
|
94
94
|
#
|
|
95
|
-
#
|
|
95
|
+
# One combined header regex over all four suffixes together,
|
|
96
96
|
# not `.find` and `.all?/any?/none?` scanned separately (that
|
|
97
97
|
# was this file's own first cut, and it broke the moment a
|
|
98
|
-
# block predicate's own predicate text contained a
|
|
98
|
+
# block predicate's own predicate text contained a different
|
|
99
99
|
# kind of block-opener than the one being scanned for --
|
|
100
100
|
# `legs.any? { |leg| ... legs.find { |o| ... } ... }` : scanning
|
|
101
|
-
# for `.find`
|
|
101
|
+
# for `.find` first found the inner `.find`, not the outer
|
|
102
102
|
# `.any?`, because a non-greedy receiver capture only guarantees
|
|
103
|
-
# the
|
|
104
|
-
# occurrence of
|
|
103
|
+
# the first occurrence of its own suffix, not the first
|
|
104
|
+
# occurrence of any block-opening suffix -- confirmed live via
|
|
105
105
|
# the shipping domain's own re-routing rules, the same
|
|
106
106
|
# "no implicit conversion of Symbol into Integer" signature the
|
|
107
107
|
# original nested-`.any?` bug had, not inferred). Scanning for
|
|
108
|
-
# all four
|
|
109
|
-
# match win fixes both directions (`.find` nested in `.any?`
|
|
110
|
-
# `.any?` nested in `.find`) with the
|
|
111
|
-
# true receiver never itself contains
|
|
108
|
+
# all four at once and letting the regex engine's own leftmost
|
|
109
|
+
# match win fixes both directions (`.find` nested in `.any?` or
|
|
110
|
+
# `.any?` nested in `.find`) with the same one rule, since the
|
|
111
|
+
# true receiver never itself contains any of these four words
|
|
112
112
|
# followed by `{`.
|
|
113
113
|
#
|
|
114
114
|
# `matching_brace` walks forward counting `{`/`}` depth from
|
|
@@ -210,7 +210,7 @@ module Hecks
|
|
|
210
210
|
# comment above. Reuses `interpret_with_element` unchanged
|
|
211
211
|
# (below, shared with `BlockPredicate` — both bind `node.param`
|
|
212
212
|
# to one element and interpret `node.predicate` against it) to
|
|
213
|
-
# find the
|
|
213
|
+
# find the first element the predicate accepts, then projects
|
|
214
214
|
# `node.path` through it via `walk_path`, the same dotted-
|
|
215
215
|
# segment walk `lookup` uses for a plain attribute path. `nil`
|
|
216
216
|
# (no matching element, or a `path` segment that doesn't
|