hecks 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/hecks/adapters/driven/claude_code.adapter +3 -0
- data/lib/hecks/adapters/driven/claude_code.rb +127 -0
- data/lib/hecks/adapters/driven/d1.adapter +12 -0
- data/lib/hecks/adapters/driven/d1.rb +403 -0
- data/lib/hecks/adapters/driven/folder.adapter +3 -0
- data/lib/hecks/adapters/driven/folder.rb +199 -0
- data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
- data/lib/hecks/adapters/driven/google_authentication.rb +100 -0
- data/lib/hecks/adapters/driven/governance_authorization.adapter +3 -0
- data/lib/hecks/adapters/driven/governance_authorization.rb +62 -0
- data/lib/hecks/adapters/driven/heki/journal.rb +51 -0
- data/lib/hecks/adapters/driven/heki/saga_store.rb +0 -0
- data/lib/hecks/adapters/driven/heki/snapshot.rb +38 -0
- data/lib/hecks/adapters/driven/heki.adapter +4 -0
- data/lib/hecks/adapters/driven/heki.rb +148 -0
- data/lib/hecks/adapters/driven/identity_registry.adapter +3 -0
- data/lib/hecks/adapters/driven/identity_registry.rb +26 -0
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +51 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +63 -0
- data/lib/hecks/adapters/driven/lambda.adapter +4 -0
- data/lib/hecks/adapters/driven/lambda.rb +131 -0
- data/lib/hecks/adapters/driven/memory.adapter +3 -0
- data/lib/hecks/adapters/driven/memory.rb +103 -0
- data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +3 -0
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +28 -0
- data/lib/hecks/adapters/driven/postgres/codec.rb +80 -0
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +168 -0
- data/lib/hecks/adapters/driven/postgres.adapter +5 -0
- data/lib/hecks/adapters/driven/postgres.rb +391 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/era_store.rb +171 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/field_cache.rb +190 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/head_compiler.rb +429 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/mint_transaction.rb +166 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/provisioning.rb +286 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/resumable_backfill.rb +168 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/tail_merge.rb +163 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/transform_installer.rb +114 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage.rb +137 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/coverage_check.rb +89 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +82 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/merge_coordinator.rb +43 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/minter.rb +132 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager.rb +74 -0
- data/lib/hecks/adapters/driven/postgres_era.adapter +6 -0
- data/lib/hecks/adapters/driven/postgres_era.rb +671 -0
- data/lib/hecks/adapters/driven/prism.adapter +3 -0
- data/lib/hecks/adapters/driven/prism.rb +80 -0
- data/lib/hecks/adapters/driven/secure_random_identity.adapter +3 -0
- data/lib/hecks/adapters/driven/secure_random_identity.rb +14 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +221 -0
- data/lib/hecks/adapters/driven/sqlite/codec.rb +63 -0
- data/lib/hecks/adapters/driven/sqlite/projection.rb +105 -0
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +180 -0
- data/lib/hecks/adapters/driven/sqlite.adapter +9 -0
- data/lib/hecks/adapters/driven/sqlite.rb +285 -0
- data/lib/hecks/adapters/driven/system_clock.adapter +3 -0
- data/lib/hecks/adapters/driven/system_clock.rb +14 -0
- data/lib/hecks/adapters/driven.rb +36 -0
- data/lib/hecks/adapters.rb +6 -0
- data/lib/hecks/behaviors/dsl.rb +96 -0
- data/lib/hecks/behaviors/expectations.rb +280 -0
- data/lib/hecks/behaviors/ir.rb +31 -0
- data/lib/hecks/behaviors/rspec.rb +42 -0
- data/lib/hecks/behaviors/runner.rb +96 -0
- data/lib/hecks/behaviors.rb +25 -0
- data/lib/hecks/bluebook/aggregate.rb +108 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +134 -0
- data/lib/hecks/bluebook/assembly/build.rb +48 -0
- data/lib/hecks/bluebook/assembly/contract.rb +112 -0
- data/lib/hecks/bluebook/assembly/contracts.rb +409 -0
- data/lib/hecks/bluebook/assembly/marks.rb +227 -0
- data/lib/hecks/bluebook/assembly/specializer.rb +70 -0
- data/lib/hecks/bluebook/assembly.rb +78 -0
- data/lib/hecks/bluebook/attribute.rb +96 -0
- data/lib/hecks/bluebook/behaviour/aggregate.rb +83 -0
- data/lib/hecks/bluebook/behaviour/attribute.rb +27 -0
- data/lib/hecks/bluebook/behaviour/chapter.rb +72 -0
- data/lib/hecks/bluebook/behaviour/command.rb +116 -0
- data/lib/hecks/bluebook/behaviour/domain_port.rb +25 -0
- data/lib/hecks/bluebook/behaviour/entity.rb +59 -0
- data/lib/hecks/bluebook/behaviour/hexagon.rb +50 -0
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +51 -0
- data/lib/hecks/bluebook/behaviour/policy.rb +52 -0
- data/lib/hecks/bluebook/behaviour/process_manager.rb +38 -0
- data/lib/hecks/bluebook/behaviour/query.rb +10 -0
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -0
- data/lib/hecks/bluebook/behaviour/traits.rb +81 -0
- data/lib/hecks/bluebook/behaviour/value_object.rb +33 -0
- data/lib/hecks/bluebook/chapter.rb +68 -0
- data/lib/hecks/bluebook/command.rb +124 -0
- data/lib/hecks/bluebook/domain_port.rb +102 -0
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +34 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +906 -0
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +327 -0
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +71 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +1018 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +626 -0
- data/lib/hecks/bluebook/dsl/const_shim.rb +81 -0
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +79 -0
- data/lib/hecks/bluebook/dsl/entity_builder.rb +305 -0
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +366 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +163 -0
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +191 -0
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +44 -0
- data/lib/hecks/bluebook/dsl/malformed.rb +7 -0
- data/lib/hecks/bluebook/dsl/policy_builder.rb +120 -0
- data/lib/hecks/bluebook/dsl/port_builder.rb +30 -0
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +142 -0
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +207 -0
- data/lib/hecks/bluebook/dsl/query_builder.rb +113 -0
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +233 -0
- data/lib/hecks/bluebook/dsl/rule_reference.rb +172 -0
- data/lib/hecks/bluebook/dsl/translation_builder.rb +243 -0
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +178 -0
- data/lib/hecks/bluebook/dsl/word_gate.rb +221 -0
- data/lib/hecks/bluebook/dsl/world_builder.rb +77 -0
- data/lib/hecks/bluebook/dsl.rb +45 -0
- data/lib/hecks/bluebook/entity.rb +103 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +55 -0
- data/lib/hecks/bluebook/expression/evaluator.rb +261 -0
- data/lib/hecks/bluebook/expression/projection.json +170 -0
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +233 -0
- data/lib/hecks/bluebook/expression/resolver.rb +630 -0
- data/lib/hecks/bluebook/expression.rb +13 -0
- data/lib/hecks/bluebook/hexagon.rb +60 -0
- data/lib/hecks/bluebook/lifecycle.rb +42 -0
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +54 -0
- data/lib/hecks/bluebook/meta_validator/judge.rb +559 -0
- data/lib/hecks/bluebook/meta_validator/plan.rb +332 -0
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +47 -0
- data/lib/hecks/bluebook/meta_validator/readings.rb +350 -0
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +312 -0
- data/lib/hecks/bluebook/meta_validator/shapes.rb +246 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +255 -0
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +138 -0
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +78 -0
- data/lib/hecks/bluebook/meta_validator.rb +452 -0
- data/lib/hecks/bluebook/model_check.rb +318 -0
- data/lib/hecks/bluebook/pattern_subset.rb +120 -0
- data/lib/hecks/bluebook/policy.rb +41 -0
- data/lib/hecks/bluebook/process_manager.rb +86 -0
- data/lib/hecks/bluebook/project_discovery.rb +30 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +82 -0
- data/lib/hecks/bluebook/query.rb +61 -0
- data/lib/hecks/bluebook/read_model.rb +109 -0
- data/lib/hecks/bluebook/reference.rb +74 -0
- data/lib/hecks/bluebook/smoke_test.rb +166 -0
- data/lib/hecks/bluebook/synthesizer.rb +95 -0
- data/lib/hecks/bluebook/translation.rb +92 -0
- data/lib/hecks/bluebook/value_object.rb +58 -0
- data/lib/hecks/bluebook.rb +74 -0
- data/lib/hecks/codemod.rb +342 -0
- data/lib/hecks/construct.rb +71 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +219 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +4 -0
- data/lib/hecks/deploy/oidc.json +18 -0
- data/lib/hecks/doc/reference.rb +389 -0
- data/lib/hecks/embryonaut_bluebook.rb +75 -0
- data/lib/hecks/facade/cli_door.rb +116 -0
- data/lib/hecks/facade/cli_runner.rb +190 -0
- data/lib/hecks/facade/command_request.rb +105 -0
- data/lib/hecks/facade/handle.rb +173 -0
- data/lib/hecks/facade/json_door.rb +154 -0
- data/lib/hecks/facade/surface/aggregate_door.rb +185 -0
- data/lib/hecks/facade/surface/chapter.rb +107 -0
- data/lib/hecks/facade/surface.rb +48 -0
- data/lib/hecks/facade.rb +44 -0
- data/lib/hecks/forms/app.rb +300 -0
- data/lib/hecks/forms/command_form_renderer.rb +113 -0
- data/lib/hecks/forms/examples/banking_console.bluebook +3 -0
- data/lib/hecks/forms/field_renderer.rb +170 -0
- data/lib/hecks/forms/field_shape.rb +232 -0
- data/lib/hecks/forms/html.rb +53 -0
- data/lib/hecks/forms/index_renderer.rb +35 -0
- data/lib/hecks/forms/page.rb +157 -0
- data/lib/hecks/forms/params.rb +132 -0
- data/lib/hecks/forms/query_form_renderer.rb +114 -0
- data/lib/hecks/forms/record_renderer.rb +115 -0
- data/lib/hecks/forms/record_table.rb +63 -0
- data/lib/hecks/forms/reference_options.rb +30 -0
- data/lib/hecks/forms/value_object_shape.rb +46 -0
- data/lib/hecks/forms.rb +54 -0
- data/lib/hecks/fqn.rb +94 -0
- data/lib/hecks/framework/bluebook/compliance.bluebook +1 -0
- data/lib/hecks/framework/bluebook/console_settings.bluebook +489 -0
- data/lib/hecks/framework/bluebook/framework.hecksagon +32 -0
- data/lib/hecks/framework/bluebook/governance.bluebook +130 -0
- data/lib/hecks/framework/bluebook/identity.bluebook +90 -0
- data/lib/hecks/framework/oidc.json +39 -0
- data/lib/hecks/framework.rb +90 -0
- data/lib/hecks/freezer.rb +67 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +86 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +92 -0
- data/lib/hecks/fuzzing/properties.rb +1146 -0
- data/lib/hecks/fuzzing/replay.rb +635 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +93 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +54 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +96 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +185 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +130 -0
- data/lib/hecks/fuzzing/value_generator.rb +177 -0
- data/lib/hecks/fuzzing.rb +12 -0
- data/lib/hecks/grammar/evolve.rb +291 -0
- data/lib/hecks/grammar/expression.bluebook +417 -0
- data/lib/hecks/grammar/expression_operators.json +1152 -0
- data/lib/hecks/grammar/grammar.hecksagon +20 -0
- data/lib/hecks/grammar/oidc.json +69 -0
- data/lib/hecks/grammar/translation.bluebook +203 -0
- data/lib/hecks/grammar.rb +178 -0
- data/lib/hecks/ir.rb +126 -0
- data/lib/hecks/language/adapter.bluebook +116 -0
- data/lib/hecks/language/bluebook/aggregate.bluebook +581 -0
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +76 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +256 -0
- data/lib/hecks/language/bluebook/bluebook.hecksagon +24 -0
- data/lib/hecks/language/bluebook/command.bluebook +437 -0
- data/lib/hecks/language/bluebook/entity.bluebook +316 -0
- data/lib/hecks/language/bluebook/policy.bluebook +172 -0
- data/lib/hecks/language/bluebook/process_manager.bluebook +264 -0
- data/lib/hecks/language/bluebook/projection.bluebook +267 -0
- data/lib/hecks/language/bluebook/query.bluebook +245 -0
- data/lib/hecks/language/bluebook/shape.bluebook +292 -0
- data/lib/hecks/language/bluebook/syntax.bluebook +447 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +364 -0
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +51 -0
- data/lib/hecks/language/hecksagon/domain_port.bluebook +76 -0
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +131 -0
- data/lib/hecks/language/hecksagon/port_operation.bluebook +102 -0
- data/lib/hecks/language/oidc.json +313 -0
- data/lib/hecks/language/port.bluebook +92 -0
- data/lib/hecks/language/translation/translation.bluebook +110 -0
- data/lib/hecks/language/translation/translation_aggregate.bluebook +267 -0
- data/lib/hecks/language/world/wiring.bluebook +62 -0
- data/lib/hecks/language/world/world.bluebook +84 -0
- data/lib/hecks/literal.rb +125 -0
- data/lib/hecks/naming.rb +121 -0
- data/lib/hecks/ports/access_control.port +4 -0
- data/lib/hecks/ports/access_control.rb +62 -0
- data/lib/hecks/ports/agent/answers.rb +104 -0
- data/lib/hecks/ports/agent.port +4 -0
- data/lib/hecks/ports/agent.rb +167 -0
- data/lib/hecks/ports/authentication.port +4 -0
- data/lib/hecks/ports/authentication.rb +50 -0
- data/lib/hecks/ports/authorization.port +4 -0
- data/lib/hecks/ports/authorization.rb +59 -0
- data/lib/hecks/ports/clock.port +4 -0
- data/lib/hecks/ports/clock.rb +62 -0
- data/lib/hecks/ports/extraction.port +4 -0
- data/lib/hecks/ports/extraction.rb +37 -0
- data/lib/hecks/ports/identity_assignment.port +4 -0
- data/lib/hecks/ports/identity_assignment.rb +45 -0
- data/lib/hecks/ports/identity_generation.port +4 -0
- data/lib/hecks/ports/identity_generation.rb +49 -0
- data/lib/hecks/ports/identity_resolution.port +4 -0
- data/lib/hecks/ports/identity_resolution.rb +40 -0
- data/lib/hecks/ports/loading.port +4 -0
- data/lib/hecks/ports/loading.rb +13 -0
- data/lib/hecks/ports/persistence/append_only.rb +102 -0
- data/lib/hecks/ports/persistence/binding_policy.rb +56 -0
- data/lib/hecks/ports/persistence/execution.rb +18 -0
- data/lib/hecks/ports/persistence/lineage.rb +277 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +25 -0
- data/lib/hecks/ports/persistence/remote_runtime.rb +42 -0
- data/lib/hecks/ports/persistence/repository_factory.rb +30 -0
- data/lib/hecks/ports/persistence.port +4 -0
- data/lib/hecks/ports/persistence.rb +39 -0
- data/lib/hecks/ports/projection.port +4 -0
- data/lib/hecks/ports/projection.rb +76 -0
- data/lib/hecks/ports/query/in_memory.rb +60 -0
- data/lib/hecks/ports/query/ordering.rb +41 -0
- data/lib/hecks/ports/query.rb +36 -0
- data/lib/hecks/ports.rb +27 -0
- data/lib/hecks/projections/diagrams.rb +413 -0
- data/lib/hecks/projections/ir.rb +18 -0
- data/lib/hecks/projections/model/deviations.rb +98 -0
- data/lib/hecks/projections/model.rb +145 -0
- data/lib/hecks/projections/oidc.rb +110 -0
- data/lib/hecks/projections/parser_table.rb +159 -0
- data/lib/hecks/projections/reference.rb +38 -0
- data/lib/hecks/projections/shape.rb +31 -0
- data/lib/hecks/projections/statements.rb +110 -0
- data/lib/hecks/projections/vocabulary.rb +100 -0
- data/lib/hecks/projections.rb +32 -0
- data/lib/hecks/projector/cli_projector.rb +437 -0
- data/lib/hecks/projector/docs_projector.rb +321 -0
- data/lib/hecks/projector/exporter.rb +141 -0
- data/lib/hecks/projector/ir_projector.rb +18 -0
- data/lib/hecks/projector/narrate_projector.rb +243 -0
- data/lib/hecks/projector/target.rb +97 -0
- data/lib/hecks/projector.rb +186 -0
- data/lib/hecks/query_ir.rb +392 -0
- data/lib/hecks/query_specification/common/authorization_spec.rb +9 -0
- data/lib/hecks/query_specification/common/comparators.rb +25 -0
- data/lib/hecks/query_specification/common/comparison.rb +174 -0
- data/lib/hecks/query_specification/common/cursor_spec.rb +9 -0
- data/lib/hecks/query_specification/common/dsl.rb +58 -0
- data/lib/hecks/query_specification/common/inspection_spec.rb +9 -0
- data/lib/hecks/query_specification/common/limit_spec.rb +9 -0
- data/lib/hecks/query_specification/common/null_policy.rb +80 -0
- data/lib/hecks/query_specification/common/null_semantics.rb +11 -0
- data/lib/hecks/query_specification/common/offset_spec.rb +9 -0
- data/lib/hecks/query_specification/common/options.rb +32 -0
- data/lib/hecks/query_specification/common/order_by.rb +9 -0
- data/lib/hecks/query_specification/common/specification.rb +10 -0
- data/lib/hecks/query_specification/common/where_clause.rb +9 -0
- data/lib/hecks/query_specification/field_path.rb +89 -0
- data/lib/hecks/query_specification/hop_path.rb +130 -0
- data/lib/hecks/query_specification/read_model/specification.rb +18 -0
- data/lib/hecks/query_specification.rb +14 -0
- data/lib/hecks/rendering.rb +48 -0
- data/lib/hecks/router/namespace_installer.rb +157 -0
- data/lib/hecks/router.rb +70 -0
- data/lib/hecks/runtime/caller.rb +50 -0
- data/lib/hecks/runtime/capability_graph.rb +44 -0
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +124 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +248 -0
- data/lib/hecks/runtime/command_interpreter.rb +413 -0
- data/lib/hecks/runtime/command_rules/admissibility.rb +307 -0
- data/lib/hecks/runtime/command_rules/arithmetic.rb +254 -0
- data/lib/hecks/runtime/command_rules/authorization.rb +64 -0
- data/lib/hecks/runtime/command_rules/emission.rb +34 -0
- data/lib/hecks/runtime/command_rules/references.rb +181 -0
- data/lib/hecks/runtime/command_rules.rb +28 -0
- data/lib/hecks/runtime/dependency_planning.rb +231 -0
- data/lib/hecks/runtime/dispatcher.rb +286 -0
- data/lib/hecks/runtime/entity_element.rb +253 -0
- data/lib/hecks/runtime/entity_interpreter.rb +228 -0
- data/lib/hecks/runtime/era_check.rb +136 -0
- data/lib/hecks/runtime/era_guard/shape_diff.rb +128 -0
- data/lib/hecks/runtime/era_guard.rb +193 -0
- data/lib/hecks/runtime/era_tamper.rb +61 -0
- data/lib/hecks/runtime/errors.rb +100 -0
- data/lib/hecks/runtime/event.rb +51 -0
- data/lib/hecks/runtime/identity.rb +128 -0
- data/lib/hecks/runtime/instance.rb +120 -0
- data/lib/hecks/runtime/interpreting.rb +69 -0
- data/lib/hecks/runtime/loader.rb +143 -0
- data/lib/hecks/runtime/policy_interpreter.rb +366 -0
- data/lib/hecks/runtime/port_operation_interpreter.rb +210 -0
- data/lib/hecks/runtime/query_interpreter.rb +254 -0
- data/lib/hecks/runtime/reaction_invocation.rb +245 -0
- data/lib/hecks/runtime/read_model_interpreter.rb +266 -0
- data/lib/hecks/runtime/rebuild_sweep.rb +74 -0
- data/lib/hecks/runtime/reference_hop.rb +99 -0
- data/lib/hecks/runtime/refusal_wording.rb +124 -0
- data/lib/hecks/runtime/registry/saga_persistence.rb +70 -0
- data/lib/hecks/runtime/registry/verification.rb +158 -0
- data/lib/hecks/runtime/registry.rb +218 -0
- data/lib/hecks/runtime/remote_dispatcher.rb +143 -0
- data/lib/hecks/runtime/routing.rb +96 -0
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +97 -0
- data/lib/hecks/runtime/saga_interpreter.rb +301 -0
- data/lib/hecks/runtime/storage_shape.rb +120 -0
- data/lib/hecks/runtime/tenant_check.rb +84 -0
- data/lib/hecks/runtime/tenant_scope.rb +55 -0
- data/lib/hecks/runtime/value/admission.rb +112 -0
- data/lib/hecks/runtime/value/coercion.rb +500 -0
- data/lib/hecks/runtime/value/invariant_violation.rb +5 -0
- data/lib/hecks/runtime/value.rb +125 -0
- data/lib/hecks/runtime.rb +95 -0
- data/lib/hecks/translation/audit/approval_digest.rb +31 -0
- data/lib/hecks/translation/audit/layer_one.rb +37 -0
- data/lib/hecks/translation/audit/layer_two.rb +77 -0
- data/lib/hecks/translation/audit/unfed_report.rb +42 -0
- data/lib/hecks/translation/audit.rb +70 -0
- data/lib/hecks/translation/reattest.rb +72 -0
- data/lib/hecks/translation/rule_compiler.rb +120 -0
- data/lib/hecks/translation/scaffold/differ.rb +183 -0
- data/lib/hecks/translation/scaffold/renderer.rb +41 -0
- data/lib/hecks/translation/scaffold/writer.rb +28 -0
- data/lib/hecks/translation/scaffold.rb +30 -0
- data/lib/hecks/translation.rb +11 -0
- data/lib/hecks/version.rb +16 -0
- data/lib/hecks/vocabulary.rb +213 -0
- data/lib/hecks.rb +118 -6
- metadata +401 -222
- data/bin/hecks +0 -7
- data/bin/hecks-package +0 -65
- data/bin/hecks_console +0 -12
- data/bin/hecks_serverless +0 -6
- data/lib/cli/build.rb +0 -14
- data/lib/cli/command_runner.rb +0 -28
- data/lib/cli/console.rb +0 -10
- data/lib/cli/generate.rb +0 -37
- data/lib/cli/hecks-cli.rb +0 -27
- data/lib/cli/test.rb +0 -57
- data/lib/console/commands.rb +0 -8
- data/lib/console/hecks-console.rb +0 -1
- data/lib/packager/README.md +0 -0
- data/lib/packager/app_runner.rb +0 -21
- data/lib/packager/args.rb +0 -26
- data/lib/packager/compatibility/fixnum.rb +0 -6
- data/lib/packager/hecks.rb +0 -39
- data/lib/packager/query_runner.rb +0 -21
- data/lib/packager/resources/Dockerfile +0 -11
- data/lib/packager/resources/app_binary +0 -7
- data/lib/packager/resources/bundle_config +0 -3
- data/lib/packager/resources/traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz +0 -0
- data/lib/packager/resources/traveling-ruby-20150715-2.2.2-osx.tar.gz +0 -0
- data/lib/serverless/Domain +0 -32
- data/lib/serverless/cli.rb +0 -75
- data/lib/serverless/resources/command_name.js +0 -5
- data/lib/serverless/resources/environment.js +0 -7
- data/lib/serverless/resources/handler.js.tt +0 -28
- data/lib/serverless/resources/run_binary.js +0 -22
- data/lib/serverless/resources/serverless.yml +0 -20
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "../../vocabulary"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Bluebook
|
|
6
|
+
module Expression
|
|
7
|
+
module Evaluator
|
|
8
|
+
Operator = Struct.new(:symbol, :compares_less_than, :compares_equal, :negated, keyword_init: true)
|
|
9
|
+
|
|
10
|
+
# Six operators, reduced to two primitives (less_than, equal) combined
|
|
11
|
+
# with a small boolean algebra: compares_less_than/compares_equal choose
|
|
12
|
+
# which primitive(s) OR together, negated inverts the result.
|
|
13
|
+
#
|
|
14
|
+
# READ, NOT RESTATED. This table is the checked-in projection of the
|
|
15
|
+
# grammar chapter's admitted set (bin/expression_projection), joined with
|
|
16
|
+
# the algebra Vocabulary::Comparison declares. The evaluator cannot
|
|
17
|
+
# boot the chapter that configures it — the Prism adapter normalises
|
|
18
|
+
# every predicate through CanonicalForm while a bluebook loads — so
|
|
19
|
+
# the projection is how the domain reaches here: regenerated when the
|
|
20
|
+
# ledger changes, held fresh by spec/operators_export_spec.rb, and
|
|
21
|
+
# held to the live machinery by spec/operator_conformance_spec.rb.
|
|
22
|
+
PROJECTION = JSON.parse(
|
|
23
|
+
File.read(File.join(__dir__, "projection.json")), symbolize_names: true
|
|
24
|
+
).freeze
|
|
25
|
+
|
|
26
|
+
OPERATORS = PROJECTION.fetch(:operators)
|
|
27
|
+
.select { |row| row[:category] == "comparison" }
|
|
28
|
+
.map { |row| Operator.new(**row.slice(:symbol, :compares_less_than, :compares_equal, :negated)) }
|
|
29
|
+
.freeze
|
|
30
|
+
|
|
31
|
+
COMPARISONS = OPERATORS.map(&:symbol).freeze
|
|
32
|
+
|
|
33
|
+
# The boolean/comparison grammar an expression parses into. Leaf nodes
|
|
34
|
+
# (Compare/Include/Resolve) hold already-parsed Resolver ASTs, not raw
|
|
35
|
+
# strings — which branch a leaf's grammar takes is, like the boolean/
|
|
36
|
+
# comparison grammar above it, a pure function of the string. Only the
|
|
37
|
+
# final state/attrs dictionary read, inside Resolver.interpret, varies
|
|
38
|
+
# per call. Parsing the whole tree once and caching it (ast_cache,
|
|
39
|
+
# below) is what makes that safe.
|
|
40
|
+
Or = Struct.new(:left, :right, keyword_init: true)
|
|
41
|
+
And = Struct.new(:left, :right, keyword_init: true)
|
|
42
|
+
Not = Struct.new(:node, keyword_init: true)
|
|
43
|
+
Compare = Struct.new(:operator, :left, :right, keyword_init: true)
|
|
44
|
+
Include = Struct.new(:haystack, :needle, keyword_init: true)
|
|
45
|
+
Resolve = Struct.new(:expr, keyword_init: true)
|
|
46
|
+
|
|
47
|
+
module_function
|
|
48
|
+
|
|
49
|
+
# Keyed by the exact string `call` receives. Canonical text is already
|
|
50
|
+
# normalised at DSL-build time, so the same given/invariant's text is
|
|
51
|
+
# byte-identical across every dispatch that evaluates it — parsed once
|
|
52
|
+
# here, interpreted fresh against each call's own state/attrs. Matches
|
|
53
|
+
# MetaValidator.verdicts' unsynchronized `||= {}` idiom : redundant
|
|
54
|
+
# parse work under real parallelism, never corruption.
|
|
55
|
+
def ast_cache = @ast_cache ||= {}
|
|
56
|
+
|
|
57
|
+
def call(expr, state, attrs = {})
|
|
58
|
+
interpret(ast_cache[expr] ||= parse(expr), state, attrs)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def parse(expr)
|
|
62
|
+
expr = strip_parens(expr.to_s.strip)
|
|
63
|
+
|
|
64
|
+
left, right = split_top_level(expr, "||")
|
|
65
|
+
return Or.new(left: parse(left), right: parse(right)) if left
|
|
66
|
+
|
|
67
|
+
left, right = split_top_level(expr, "&&")
|
|
68
|
+
return And.new(left: parse(left), right: parse(right)) if left
|
|
69
|
+
|
|
70
|
+
membership = match_include(expr)
|
|
71
|
+
return Include.new(haystack: Resolver.parse(membership[0]), needle: Resolver.parse(membership[1])) if membership
|
|
72
|
+
|
|
73
|
+
OPERATORS.each do |op|
|
|
74
|
+
left, right = split_comparison(expr, op.symbol)
|
|
75
|
+
return Compare.new(operator: op, left: Resolver.parse(left), right: Resolver.parse(right)) if left
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
return Not.new(node: parse(Regexp.last_match(1))) if expr =~ /\A!(.+)\z/
|
|
79
|
+
|
|
80
|
+
Resolve.new(expr: Resolver.parse(expr))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def interpret(node, state, attrs)
|
|
84
|
+
case node
|
|
85
|
+
when Or then interpret(node.left, state, attrs) || interpret(node.right, state, attrs)
|
|
86
|
+
when And then interpret(node.left, state, attrs) && interpret(node.right, state, attrs)
|
|
87
|
+
when Not then !interpret(node.node, state, attrs)
|
|
88
|
+
when Compare then compare(node.operator, node.left, node.right, state, attrs)
|
|
89
|
+
when Include then includes?([node.haystack, node.needle], state, attrs)
|
|
90
|
+
when Resolve then truthy?(Resolver.interpret(node.expr, state, attrs))
|
|
91
|
+
else
|
|
92
|
+
# Every node `parse` can produce has a `when` above — a
|
|
93
|
+
# backstop against the day this grammar grows a new node
|
|
94
|
+
# type and `interpret` doesn't grow to match it. A missing
|
|
95
|
+
# arm here used to return bare `nil`, and `Or`/`And` fold
|
|
96
|
+
# that straight into the boolean algebra as ordinary falsy
|
|
97
|
+
# — reading exactly like "the rule legitimately does not
|
|
98
|
+
# hold" rather than "the runtime cannot evaluate this rule
|
|
99
|
+
# at all", the one silent no-op this language otherwise
|
|
100
|
+
# refuses.
|
|
101
|
+
raise EvaluationError, "no interpreter handles #{node.class} — add a case before parse can produce it"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def compare(op, left, right, state, attrs)
|
|
106
|
+
lhs = Resolver.interpret(left, state, attrs)
|
|
107
|
+
rhs = Resolver.interpret(right, state, attrs)
|
|
108
|
+
|
|
109
|
+
apply(op, lhs, rhs)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# The algebra itself, on values already resolved — split out so a sign
|
|
113
|
+
# test (SignTest#compares_via names an Operator symbol) can apply the
|
|
114
|
+
# SAME primitives compare() uses against the literal 0, rather than
|
|
115
|
+
# re-deriving positive?/negative?/zero? by hand a second time.
|
|
116
|
+
def apply(op, lhs, rhs)
|
|
117
|
+
result = (op.compares_less_than && less_than(lhs, rhs)) ||
|
|
118
|
+
(op.compares_equal && equal?(lhs, rhs))
|
|
119
|
+
op.negated ? !result : result
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def less_than(lhs, rhs)
|
|
123
|
+
left = Resolver.numeric(lhs)
|
|
124
|
+
right = Resolver.numeric(rhs)
|
|
125
|
+
return left < right if left && right
|
|
126
|
+
return lhs < rhs if lhs.is_a?(String) && rhs.is_a?(String)
|
|
127
|
+
|
|
128
|
+
raise EvaluationError,
|
|
129
|
+
"comparison of #{class_of(lhs)} with #{Resolver.describe(rhs)} failed"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def equal?(lhs, rhs)
|
|
133
|
+
left = Resolver.numeric(lhs)
|
|
134
|
+
right = Resolver.numeric(rhs)
|
|
135
|
+
return left == right if left && right
|
|
136
|
+
|
|
137
|
+
lhs == rhs
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def truthy?(value)
|
|
141
|
+
!value.nil? && value != false
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def class_of(value)
|
|
145
|
+
value.nil? ? "nil" : value.class.name
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def match_include(expr)
|
|
149
|
+
index = expr.rindex(".include?(")
|
|
150
|
+
return nil unless index && expr.end_with?(")")
|
|
151
|
+
|
|
152
|
+
[expr[0...index], expr[(index + ".include?(".length)...-1]]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Declared the same way in Vocabulary::IncludeHaystack
|
|
156
|
+
# (language/bluebook/vocabulary.bluebook) — spec/vocabulary_conformance_spec
|
|
157
|
+
# holds this equal to the language, so the set of haystack types
|
|
158
|
+
# `.include?` supports cannot drift from what the language says it
|
|
159
|
+
# does.
|
|
160
|
+
INCLUDE_HAYSTACKS = Hecks::Vocabulary.fetch("IncludeHaystack")
|
|
161
|
+
|
|
162
|
+
def includes?(parts, state, attrs)
|
|
163
|
+
haystack, needle = parts
|
|
164
|
+
wanted = Resolver.interpret(needle, state, attrs)
|
|
165
|
+
|
|
166
|
+
case (found = Resolver.interpret(haystack, state, attrs))
|
|
167
|
+
when Array then found.any? { |item| equal?(item, wanted) }
|
|
168
|
+
when String
|
|
169
|
+
unless wanted.is_a?(String)
|
|
170
|
+
raise EvaluationError, "no implicit conversion of #{class_of(wanted)} into String"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
found.include?(wanted)
|
|
174
|
+
else false
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def strip_parens(expr)
|
|
179
|
+
return expr unless expr.start_with?("(") && expr.end_with?(")")
|
|
180
|
+
|
|
181
|
+
depth = 0
|
|
182
|
+
expr.each_char.with_index do |char, index|
|
|
183
|
+
depth += 1 if char == "("
|
|
184
|
+
depth -= 1 if char == ")"
|
|
185
|
+
return expr if depth.zero? && index < expr.length - 1
|
|
186
|
+
end
|
|
187
|
+
strip_parens(expr[1..-2].strip)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def split_top_level(expr, operator)
|
|
191
|
+
index = top_level_index(expr, operator)
|
|
192
|
+
return nil unless index
|
|
193
|
+
|
|
194
|
+
[expr[0...index].strip, expr[(index + operator.length)..].strip]
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def split_comparison(expr, operator)
|
|
198
|
+
index = top_level_index(expr, operator) { |at| !part_of_longer?(expr, at, operator) }
|
|
199
|
+
return nil unless index
|
|
200
|
+
|
|
201
|
+
[expr[0...index].strip, expr[(index + operator.length)..].strip]
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def part_of_longer?(expr, index, operator)
|
|
205
|
+
after = expr[index + operator.length]
|
|
206
|
+
before = index.positive? ? expr[index - 1] : nil
|
|
207
|
+
|
|
208
|
+
return true if after == "=" && !operator.end_with?("=")
|
|
209
|
+
return true if ["<", ">", "!", "="].include?(before) && operator.start_with?("=")
|
|
210
|
+
|
|
211
|
+
false
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def top_level_index(expr, operator)
|
|
215
|
+
depth = 0
|
|
216
|
+
quote = nil
|
|
217
|
+
index = 0
|
|
218
|
+
|
|
219
|
+
while index < expr.length
|
|
220
|
+
char = expr[index]
|
|
221
|
+
|
|
222
|
+
if quote
|
|
223
|
+
quote = nil if char == quote
|
|
224
|
+
elsif ['"', "'"].include?(char)
|
|
225
|
+
quote = char
|
|
226
|
+
# `{`/`}` depth -- vendored addition, not (yet) upstream
|
|
227
|
+
# hecks (migration plan task 9): this method already
|
|
228
|
+
# treats `(`/`)` as a grouping construct so an operator
|
|
229
|
+
# INSIDE a call's parens is never mistaken for a top-level
|
|
230
|
+
# split point ; `{`/`}` needed the identical treatment the
|
|
231
|
+
# moment `Bluebook::Expression::Resolver` grew block-taking
|
|
232
|
+
# `.all?`/`.any?`/`.none? { |s| PREDICATE }` support (see
|
|
233
|
+
# resolver.rb's own `BlockPredicate` addition) -- without
|
|
234
|
+
# this, an operator INSIDE the block's own predicate (e.g.
|
|
235
|
+
# `s.length > 0`) reads as a top-level split of the WHOLE
|
|
236
|
+
# `value.split("::").all? { |s| s.length > 0 }` expression,
|
|
237
|
+
# confirmed live via `Lexicon::Lexicon.Lookup`/`Query::Query.
|
|
238
|
+
# Run` (the exact `Phrase` invariant this gap was found
|
|
239
|
+
# against) : the stray `>` split the expression in half
|
|
240
|
+
# before `Resolver.parse` ever saw the block as one atomic
|
|
241
|
+
# leaf, and the two halves then failed independently with
|
|
242
|
+
# the same raw `TypeError` the block-predicate fix was
|
|
243
|
+
# built to close. `{`/`}` cannot legitimately appear inside
|
|
244
|
+
# a quoted literal either, so this sits beside the existing
|
|
245
|
+
# paren-depth branch, not instead of it.
|
|
246
|
+
elsif char == "(" || char == "{"
|
|
247
|
+
depth += 1
|
|
248
|
+
elsif char == ")" || char == "}"
|
|
249
|
+
depth -= 1
|
|
250
|
+
elsif depth.zero? && expr[index, operator.length] == operator
|
|
251
|
+
return index if !block_given? || yield(index)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
index += 1
|
|
255
|
+
end
|
|
256
|
+
nil
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
{
|
|
2
|
+
"operators": [
|
|
3
|
+
{
|
|
4
|
+
"symbol": "||",
|
|
5
|
+
"category": "logical",
|
|
6
|
+
"precedence": 1,
|
|
7
|
+
"arity": 2
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"symbol": "&&",
|
|
11
|
+
"category": "logical",
|
|
12
|
+
"precedence": 2,
|
|
13
|
+
"arity": 2
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"symbol": ".include?",
|
|
17
|
+
"category": "membership",
|
|
18
|
+
"precedence": 3,
|
|
19
|
+
"arity": 2
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"symbol": ">=",
|
|
23
|
+
"category": "comparison",
|
|
24
|
+
"precedence": 4,
|
|
25
|
+
"arity": 2,
|
|
26
|
+
"compares_less_than": true,
|
|
27
|
+
"compares_equal": false,
|
|
28
|
+
"negated": true
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"symbol": "<=",
|
|
32
|
+
"category": "comparison",
|
|
33
|
+
"precedence": 4,
|
|
34
|
+
"arity": 2,
|
|
35
|
+
"compares_less_than": true,
|
|
36
|
+
"compares_equal": true,
|
|
37
|
+
"negated": false
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"symbol": "<",
|
|
41
|
+
"category": "comparison",
|
|
42
|
+
"precedence": 4,
|
|
43
|
+
"arity": 2,
|
|
44
|
+
"compares_less_than": true,
|
|
45
|
+
"compares_equal": false,
|
|
46
|
+
"negated": false
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"symbol": ">",
|
|
50
|
+
"category": "comparison",
|
|
51
|
+
"precedence": 4,
|
|
52
|
+
"arity": 2,
|
|
53
|
+
"compares_less_than": true,
|
|
54
|
+
"compares_equal": true,
|
|
55
|
+
"negated": true
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"symbol": "==",
|
|
59
|
+
"category": "comparison",
|
|
60
|
+
"precedence": 4,
|
|
61
|
+
"arity": 2,
|
|
62
|
+
"compares_less_than": false,
|
|
63
|
+
"compares_equal": true,
|
|
64
|
+
"negated": false
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"symbol": "!=",
|
|
68
|
+
"category": "comparison",
|
|
69
|
+
"precedence": 4,
|
|
70
|
+
"arity": 2,
|
|
71
|
+
"compares_less_than": false,
|
|
72
|
+
"compares_equal": true,
|
|
73
|
+
"negated": true
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"symbol": "!",
|
|
77
|
+
"category": "logical",
|
|
78
|
+
"precedence": 5,
|
|
79
|
+
"arity": 1
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"symbol": "+",
|
|
83
|
+
"category": "arithmetic",
|
|
84
|
+
"precedence": 6,
|
|
85
|
+
"arity": 2
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"symbol": ".modulo",
|
|
89
|
+
"category": "arithmetic",
|
|
90
|
+
"precedence": 6,
|
|
91
|
+
"arity": 2
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"symbol": ".positive?",
|
|
95
|
+
"category": "sign_test",
|
|
96
|
+
"precedence": 7,
|
|
97
|
+
"arity": 1
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"symbol": ".negative?",
|
|
101
|
+
"category": "sign_test",
|
|
102
|
+
"precedence": 7,
|
|
103
|
+
"arity": 1
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"symbol": ".zero?",
|
|
107
|
+
"category": "sign_test",
|
|
108
|
+
"precedence": 7,
|
|
109
|
+
"arity": 1
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"symbol": ".empty?",
|
|
113
|
+
"category": "sized",
|
|
114
|
+
"precedence": 8,
|
|
115
|
+
"arity": 1
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"symbol": ".to_s",
|
|
119
|
+
"category": "to_string",
|
|
120
|
+
"precedence": 9,
|
|
121
|
+
"arity": 1
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"symbol": ".size",
|
|
125
|
+
"category": "sized",
|
|
126
|
+
"precedence": 8,
|
|
127
|
+
"arity": 1
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"symbol": ".any?",
|
|
131
|
+
"category": "enumeration",
|
|
132
|
+
"precedence": 10,
|
|
133
|
+
"arity": 2
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"symbol": ".none?",
|
|
137
|
+
"category": "enumeration",
|
|
138
|
+
"precedence": 10,
|
|
139
|
+
"arity": 2
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"symbol": ".all?",
|
|
143
|
+
"category": "enumeration",
|
|
144
|
+
"precedence": 10,
|
|
145
|
+
"arity": 2
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"symbol": ".find",
|
|
149
|
+
"category": "enumeration",
|
|
150
|
+
"precedence": 10,
|
|
151
|
+
"arity": 2
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
"normalisations": [
|
|
155
|
+
{
|
|
156
|
+
"strategy": "collapse_whitespace",
|
|
157
|
+
"source_token": "",
|
|
158
|
+
"replacement": "",
|
|
159
|
+
"boundary": "none",
|
|
160
|
+
"position": 1
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"strategy": "replace",
|
|
164
|
+
"source_token": ".length",
|
|
165
|
+
"replacement": ".size",
|
|
166
|
+
"boundary": "word",
|
|
167
|
+
"position": 2
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# HAND-WRITTEN — the block-predicate/find family of the leaf grammar
|
|
2
|
+
# (`Bluebook::Expression::Resolver`'s own `.all?`/`.any?`/`.none?`/
|
|
3
|
+
# `.find` suffixes), split into this sibling file to keep resolver.rb
|
|
4
|
+
# under Metrics/ModuleLength (350) : this reopens the SAME `Resolver`
|
|
5
|
+
# module resolver.rb defines, the long nested `module A; module B; ...`
|
|
6
|
+
# form (not the compact `A::B` form) so bare constant/method lookups
|
|
7
|
+
# here (`EvaluationError`, `Evaluator`, `describe`, `unwrap_scalar`,
|
|
8
|
+
# `walk_path`) resolve exactly the way they do inside resolver.rb
|
|
9
|
+
# itself — this is one module, split across two files, not two
|
|
10
|
+
# modules. See resolver.rb's own header for the grammar this belongs
|
|
11
|
+
# to; see each struct/method below for its own "why".
|
|
12
|
+
module Hecks
|
|
13
|
+
module Bluebook
|
|
14
|
+
module Expression
|
|
15
|
+
module Resolver
|
|
16
|
+
# `receiver.all? { |x| PREDICATE }` / `.any? { ... }` / `.none? {
|
|
17
|
+
# ... }` -- vendored addition, not (yet) upstream hecks
|
|
18
|
+
# (migration plan task 9), completing the same `Phrase`
|
|
19
|
+
# four-segment invariant the `Split` node above was built for
|
|
20
|
+
# (`value.split("::").all? { |s| s.length > 0 }`). Structurally
|
|
21
|
+
# different from every other addition in this file: every prior
|
|
22
|
+
# suffix is a flat receiver -> scalar transform, but a block
|
|
23
|
+
# predicate needs to evaluate its own sub-expression ONCE PER
|
|
24
|
+
# ELEMENT with the block parameter bound to that element. Kept
|
|
25
|
+
# minimal per the migration plan's own instruction -- no
|
|
26
|
+
# persistent iteration-variable concept added to Resolver's
|
|
27
|
+
# state model at all ; `predicate` below is a fully-parsed
|
|
28
|
+
# EVALUATOR ast (not a Resolver ast -- the predicate is a
|
|
29
|
+
# boolean/comparison expression like `s.length > 0`, exactly the
|
|
30
|
+
# grammar `Bluebook::Expression::Evaluator` owns, not this
|
|
31
|
+
# module's own leaf grammar), parsed once at `parse`-time same as
|
|
32
|
+
# every sibling node's sub-expressions are. `mode` distinguishes
|
|
33
|
+
# all?/any?/none? without three duplicated node types, since
|
|
34
|
+
# their only difference is which Array predicate aggregates the
|
|
35
|
+
# per-element results (interpret_with_element, below, is the
|
|
36
|
+
# "smallest correct thing" the plan asked for -- it threads the
|
|
37
|
+
# element binding through a temporarily-extended `attrs` hash for
|
|
38
|
+
# that one predicate's evaluation only, never touching
|
|
39
|
+
# `interpret`'s own signature or any other node's call sites).
|
|
40
|
+
# `Resolver` already calls into `Evaluator` elsewhere in this
|
|
41
|
+
# file (`sign_test_node`/`apply_sign_test` call `Evaluator.
|
|
42
|
+
# apply`/`Evaluator::OPERATORS` directly) -- this is the same
|
|
43
|
+
# precedented cross-reference, not a new coupling.
|
|
44
|
+
BlockPredicate = Struct.new(:mode, :receiver, :param, :predicate, keyword_init: true)
|
|
45
|
+
|
|
46
|
+
# Which Array method each block-predicate suffix maps to, and
|
|
47
|
+
# which Ruby Enumerable method decides the aggregate result --
|
|
48
|
+
# declared as data, not a three-way `case`, the same shape
|
|
49
|
+
# SIGN_TEST_OPERATORS above already uses for its own suffix
|
|
50
|
+
# family.
|
|
51
|
+
BLOCK_PREDICATE_MODES = {
|
|
52
|
+
"all?" => :all,
|
|
53
|
+
"any?" => :any,
|
|
54
|
+
"none?" => :none
|
|
55
|
+
}.freeze
|
|
56
|
+
|
|
57
|
+
# `receiver.find { |x| PREDICATE }` / `.find { ... }.a.b` -- the
|
|
58
|
+
# "find-then-project" shape the shipping domain's re-routing
|
|
59
|
+
# rules kept reaching for by hand (a caller-precomputed
|
|
60
|
+
# `next_load_location` field standing in for "the leg after this
|
|
61
|
+
# one", because `BlockPredicate` above can only answer yes/no,
|
|
62
|
+
# never hand back the element it found). `path` holds the
|
|
63
|
+
# dotted segments walked past the closing `}`, e.g. the `["
|
|
64
|
+
# next_load_location"]` in `legs.find { |l| l.load_location ==
|
|
65
|
+
# x }.next_load_location` -- empty when `.find { ... }` is used
|
|
66
|
+
# bare (its own found-element-or-nil is the value, same as any
|
|
67
|
+
# other leaf, e.g. composed with `.present?` the same way every
|
|
68
|
+
# other receiver already composes with it). Reuses `param`/
|
|
69
|
+
# `predicate`'s exact field names from `BlockPredicate` on
|
|
70
|
+
# purpose: `interpret_with_element` below binds by those two
|
|
71
|
+
# names and is shared unchanged between both node types.
|
|
72
|
+
Find = Struct.new(:receiver, :param, :predicate, :path, keyword_init: true)
|
|
73
|
+
|
|
74
|
+
module_function
|
|
75
|
+
|
|
76
|
+
# Every suffix that opens a `{ |x| ... }` block, `.find` included
|
|
77
|
+
# -- shared by `parse_block_opener` below, and by nothing else
|
|
78
|
+
# (this is NOT `BLOCK_PREDICATE_MODES` — `.find` isn't a mode
|
|
79
|
+
# `evaluate_block_predicate` aggregates through, it builds a
|
|
80
|
+
# `Find` node instead, see below).
|
|
81
|
+
BLOCK_OPENER_SUFFIXES = (BLOCK_PREDICATE_MODES.keys + ["find"]).freeze
|
|
82
|
+
|
|
83
|
+
# `.all?`/`.any?`/`.none?`/`.find` -- vendored addition, see the
|
|
84
|
+
# `BlockPredicate`/`Find` structs' own comments above. Matched
|
|
85
|
+
# last among the suffix rules (right before the `Lookup`
|
|
86
|
+
# catch-all) since a block's own predicate text can itself
|
|
87
|
+
# contain almost anything a leaf expression can, including
|
|
88
|
+
# ANOTHER block-opening suffix -- letting every more specific
|
|
89
|
+
# rule above try first avoids this one accidentally swallowing a
|
|
90
|
+
# receiver another rule was meant to parse.
|
|
91
|
+
#
|
|
92
|
+
# ONE combined header regex over ALL FOUR suffixes together,
|
|
93
|
+
# not `.find` and `.all?/any?/none?` scanned separately (that
|
|
94
|
+
# was this file's own first cut, and it broke the moment a
|
|
95
|
+
# block predicate's own predicate text contained a DIFFERENT
|
|
96
|
+
# kind of block-opener than the one being scanned for --
|
|
97
|
+
# `legs.any? { |leg| ... legs.find { |o| ... } ... }` : scanning
|
|
98
|
+
# for `.find` FIRST found the INNER `.find`, not the outer
|
|
99
|
+
# `.any?`, because a non-greedy receiver capture only guarantees
|
|
100
|
+
# the FIRST occurrence of ITS OWN suffix, not the first
|
|
101
|
+
# occurrence of ANY block-opening suffix -- confirmed live via
|
|
102
|
+
# the shipping domain's own re-routing rules, the same
|
|
103
|
+
# "no implicit conversion of Symbol into Integer" signature the
|
|
104
|
+
# original nested-`.any?` bug had, not inferred). Scanning for
|
|
105
|
+
# all four AT ONCE and letting the regex engine's own leftmost
|
|
106
|
+
# match win fixes both directions (`.find` nested in `.any?` OR
|
|
107
|
+
# `.any?` nested in `.find`) with the SAME one rule, since the
|
|
108
|
+
# true receiver never itself contains ANY of these four words
|
|
109
|
+
# followed by `{`.
|
|
110
|
+
#
|
|
111
|
+
# `matching_brace` walks forward counting `{`/`}` depth from
|
|
112
|
+
# there, the same quote-aware, depth-aware discipline
|
|
113
|
+
# `split_addition`/`array_elements` already apply, so a `}`
|
|
114
|
+
# inside a nested block (or a quoted `start_with?` substring)
|
|
115
|
+
# never miscounts. `.find`'s own trailing dotted path (`path`)
|
|
116
|
+
# is captured from whatever follows the closing brace ; every
|
|
117
|
+
# other suffix instead requires nothing follow it at all (the
|
|
118
|
+
# `BlockPredicate` shape, unchanged from before this rewrite).
|
|
119
|
+
def parse_block_opener(expr)
|
|
120
|
+
pattern = /\A(.+?)\.(#{BLOCK_OPENER_SUFFIXES.map { |suffix| Regexp.escape(suffix) }.join('|')})\s*\{\s*\|(\w+)\|\s*/m
|
|
121
|
+
header = expr.match(pattern)
|
|
122
|
+
return nil unless header
|
|
123
|
+
|
|
124
|
+
receiver_text = header[1]
|
|
125
|
+
suffix = header[2]
|
|
126
|
+
param = header[3]
|
|
127
|
+
body_start = header.end(0)
|
|
128
|
+
body_end = matching_brace(expr, body_start)
|
|
129
|
+
return nil unless body_end
|
|
130
|
+
|
|
131
|
+
predicate = Evaluator.parse(expr[body_start...body_end].strip)
|
|
132
|
+
|
|
133
|
+
if suffix == "find"
|
|
134
|
+
trailing = expr[(body_end + 1)..].strip
|
|
135
|
+
return nil unless trailing.empty? || trailing.start_with?(".")
|
|
136
|
+
|
|
137
|
+
Find.new(receiver: parse(receiver_text), param: param, predicate: predicate,
|
|
138
|
+
path: trailing.empty? ? [] : trailing[1..].split("."))
|
|
139
|
+
else
|
|
140
|
+
return nil unless expr[(body_end + 1)..].strip.empty?
|
|
141
|
+
|
|
142
|
+
BlockPredicate.new(mode: BLOCK_PREDICATE_MODES.fetch(suffix), receiver: parse(receiver_text),
|
|
143
|
+
param: param, predicate: predicate)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# The index of the `}` that closes the `{` implicitly opened
|
|
148
|
+
# just before `start` (the caller's own header match already
|
|
149
|
+
# consumed that opening brace, so depth begins at 1) -- nil if
|
|
150
|
+
# the string runs out before depth returns to 0 (a caller-error
|
|
151
|
+
# shape, not a valid expression). Quote-aware so a `}` inside a
|
|
152
|
+
# quoted substring (`.start_with?("}")`) never miscounts, the
|
|
153
|
+
# same discipline `split_addition`/`array_elements` already
|
|
154
|
+
# apply for their own depth tracking.
|
|
155
|
+
def matching_brace(expr, start)
|
|
156
|
+
depth = 1
|
|
157
|
+
quote = nil
|
|
158
|
+
index = start
|
|
159
|
+
while index < expr.length
|
|
160
|
+
char = expr[index]
|
|
161
|
+
if quote
|
|
162
|
+
quote = nil if char == quote
|
|
163
|
+
elsif ['"', "'"].include?(char)
|
|
164
|
+
quote = char
|
|
165
|
+
elsif char == "{"
|
|
166
|
+
depth += 1
|
|
167
|
+
elsif char == "}"
|
|
168
|
+
depth -= 1
|
|
169
|
+
return index if depth.zero?
|
|
170
|
+
end
|
|
171
|
+
index += 1
|
|
172
|
+
end
|
|
173
|
+
nil
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# `.all?`/`.any?`/`.none?` -- vendored addition, see the
|
|
177
|
+
# `BlockPredicate` struct's own comment above. `collection` is
|
|
178
|
+
# already-interpreted (a real Array, produced by whatever
|
|
179
|
+
# receiver expression came before it -- typically `Split`'s
|
|
180
|
+
# output), so this only has to run the per-element predicate and
|
|
181
|
+
# aggregate. `interpret_with_element` is the "smallest correct
|
|
182
|
+
# thing" the migration plan asked for : no persistent iteration-
|
|
183
|
+
# variable concept added anywhere else in Resolver's state model,
|
|
184
|
+
# just `attrs` extended with the bound name for the span of that
|
|
185
|
+
# one predicate evaluation, discarded immediately after.
|
|
186
|
+
def evaluate_block_predicate(node, collection, state, attrs)
|
|
187
|
+
unless collection.is_a?(Array)
|
|
188
|
+
raise EvaluationError, "#{node.mode}? expects a list, got #{describe(collection)}"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
outcomes = collection.map { |element| interpret_with_element(node, element, state, attrs) }
|
|
192
|
+
|
|
193
|
+
case node.mode
|
|
194
|
+
when :all then outcomes.all?
|
|
195
|
+
when :any then outcomes.any?
|
|
196
|
+
when :none then outcomes.none?
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Binds the block parameter for exactly one element's predicate
|
|
201
|
+
# evaluation -- `attrs` wins over `state` in `fetch` (see below),
|
|
202
|
+
# so the bound name shadows any same-named state/attrs field for
|
|
203
|
+
# the span of this one call only ; nothing persists past it.
|
|
204
|
+
def interpret_with_element(node, element, state, attrs)
|
|
205
|
+
Evaluator.interpret(node.predicate, state, attrs.merge(node.param.to_sym => element))
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# `.find { |x| PREDICATE }` -- see the `Find` struct's own
|
|
209
|
+
# comment above. Reuses `interpret_with_element` unchanged
|
|
210
|
+
# (below, shared with `BlockPredicate` — both bind `node.param`
|
|
211
|
+
# to one element and interpret `node.predicate` against it) to
|
|
212
|
+
# find the FIRST element the predicate accepts, then projects
|
|
213
|
+
# `node.path` through it via `walk_path`, the same dotted-
|
|
214
|
+
# segment walk `lookup` uses for a plain attribute path. `nil`
|
|
215
|
+
# (no matching element, or a `path` segment that doesn't
|
|
216
|
+
# resolve) flows through rather than raising — the same "a
|
|
217
|
+
# dispatch-time given just refuses" shape every other missing-
|
|
218
|
+
# value case in this grammar already has, and the one a re-
|
|
219
|
+
# routing check like "is there a leg after this one" needs :
|
|
220
|
+
# not finding one is a normal outcome, not an error.
|
|
221
|
+
def found_of(node, collection, state, attrs)
|
|
222
|
+
raise EvaluationError, "find expects a list, got #{describe(collection)}" unless collection.is_a?(Array)
|
|
223
|
+
|
|
224
|
+
found = collection.find { |element| interpret_with_element(node, element, state, attrs) }
|
|
225
|
+
return unwrap_scalar(found) if node.path.empty?
|
|
226
|
+
return nil if found.nil?
|
|
227
|
+
|
|
228
|
+
unwrap_scalar(walk_path(found, node.path))
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|