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,92 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
# One field crossing a value-object boundary: a scalar becoming a VO
|
|
4
|
+
# member, a VO member becoming a scalar, or a rename across the move.
|
|
5
|
+
# Paths are dotted ("price.cents") for a VO member, bare otherwise.
|
|
6
|
+
TranslationMove = Struct.new(:from, :to)
|
|
7
|
+
|
|
8
|
+
# One field whose VALUE, not just its name or position, changed —
|
|
9
|
+
# an old value has nothing in common with a new one, so the only
|
|
10
|
+
# honest way to bridge it is a declared, exhaustive lookup table.
|
|
11
|
+
# Paths follow `TranslationMove`'s convention (dotted reaches a VO
|
|
12
|
+
# member); `values` maps every old-era value that can appear to its
|
|
13
|
+
# new-era replacement.
|
|
14
|
+
TranslationConvert = Struct.new(:from, :to, :values)
|
|
15
|
+
|
|
16
|
+
# A value object's or entity's own TYPE NAME changed with its member
|
|
17
|
+
# structure unchanged — the one drift `rename`/`move` cannot express,
|
|
18
|
+
# because the attribute kept its name and only the type it points at
|
|
19
|
+
# was renamed. Mirrors `was:` one level deeper.
|
|
20
|
+
TranslationRetype = Struct.new(:from, :to)
|
|
21
|
+
|
|
22
|
+
# A computed transform — rescale, reformat, split, merge — whose only
|
|
23
|
+
# implementation is the SQL expression itself, evaluated exclusively
|
|
24
|
+
# inside the compiled Postgres head. No in-process reference exists
|
|
25
|
+
# to check it against; any aggregate carrying one refuses to boot on
|
|
26
|
+
# every other adapter.
|
|
27
|
+
TranslationCompute = Struct.new(:from, :to, :sql)
|
|
28
|
+
|
|
29
|
+
# The aggregate's own IDENTITY changed what it's computed from — not
|
|
30
|
+
# a field crossing a boundary (that's `move`), a value objects's type
|
|
31
|
+
# name (`retype`), or a value transform (`compute`): the record's own
|
|
32
|
+
# key. No `from:`/`to:` path, unlike every other rule here, because
|
|
33
|
+
# nothing is being consumed from or moved into `state` — the state
|
|
34
|
+
# a record already holds is untouched; only what identifies it is
|
|
35
|
+
# recomputed. Same SQL-only, Postgres-only, no-in-process-reference
|
|
36
|
+
# shape `compute` already has, and for the same reason: there is
|
|
37
|
+
# nothing to check this against outside the compiled head.
|
|
38
|
+
TranslationRekey = Struct.new(:sql)
|
|
39
|
+
|
|
40
|
+
# A newly added, required attribute with no source in old data at
|
|
41
|
+
# all — not a rename, move, or convert, all of which need a FROM
|
|
42
|
+
# path in the old shape. `default` is the value an existing record
|
|
43
|
+
# reads until the next command against it writes a real one; unlike
|
|
44
|
+
# `compute`, this is adapter-agnostic — applied in-process by
|
|
45
|
+
# `Lineage#translate`, the same as rename/move/drop, because there is
|
|
46
|
+
# nothing to compute, only a value to declare.
|
|
47
|
+
TranslationBackfill = Struct.new(:name, :default)
|
|
48
|
+
|
|
49
|
+
# One aggregate's part of a translation: which attributes were
|
|
50
|
+
# renamed, moved, converted, or deliberately dropped on the way from
|
|
51
|
+
# the old era to the new one. `drops` exists so data loss is a
|
|
52
|
+
# declared decision, not a field that quietly stopped being
|
|
53
|
+
# explained.
|
|
54
|
+
class TranslationAggregate
|
|
55
|
+
attr_reader :name, :was, :renames, :moves, :converts, :drops, :retypes, :computes, :rekeys, :backfills
|
|
56
|
+
|
|
57
|
+
def initialize(name:, was: nil, renames: {}, moves: [], converts: [], drops: [], retypes: [],
|
|
58
|
+
computes: [], rekeys: [], backfills: [])
|
|
59
|
+
@name = name.to_s
|
|
60
|
+
@was = was&.to_s
|
|
61
|
+
@renames = renames
|
|
62
|
+
@moves = moves
|
|
63
|
+
@converts = converts
|
|
64
|
+
@drops = drops
|
|
65
|
+
@retypes = retypes
|
|
66
|
+
@computes = computes
|
|
67
|
+
@rekeys = rekeys
|
|
68
|
+
@backfills = backfills
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# A declared map from one era of a domain's storage shape to the
|
|
73
|
+
# next. Renames and moves — the smallest slice that proves the
|
|
74
|
+
# concept: old records are translated when they are replayed, never
|
|
75
|
+
# rewritten. `retired` acknowledges aggregates that are gone
|
|
76
|
+
# outright — not renamed — so their disappearance is a decision,
|
|
77
|
+
# not an accident.
|
|
78
|
+
class Translation
|
|
79
|
+
attr_reader :domain, :from, :to, :aggregates, :retired
|
|
80
|
+
|
|
81
|
+
def initialize(domain:, from:, to:, aggregates: [], retired: [])
|
|
82
|
+
@domain = domain.to_s
|
|
83
|
+
@from = from
|
|
84
|
+
@to = to
|
|
85
|
+
@aggregates = aggregates
|
|
86
|
+
@retired = retired
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def for_aggregate(name) = @aggregates.find { |aggregate| aggregate.name == name.to_s }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require_relative "behaviour/value_object"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Bluebook
|
|
5
|
+
Invariant = Struct.new(:description, :canonical, :predicate, keyword_init: true)
|
|
6
|
+
|
|
7
|
+
# A value object — a DECLARATION HOLDER, never instantiated.
|
|
8
|
+
#
|
|
9
|
+
# `ValueObjectBuilder` returns an anonymous subclass whose singleton
|
|
10
|
+
# carries the declaration : attributes, invariants, members. Nothing ever
|
|
11
|
+
# calls `.new` on one — coercion builds `Runtime::Value` wrappers, and
|
|
12
|
+
# invariants run as canonical text through the Evaluator. The runtime
|
|
13
|
+
# reaches a shape through `aggregate.value_object(name)`, the IR's own
|
|
14
|
+
# finder ; there is no constant nesting and no second index.
|
|
15
|
+
#
|
|
16
|
+
# `to_h` does not move. It is a byte-for-byte contract pinned by the
|
|
17
|
+
# golden fixtures, so it keeps spelling the short declared name, which
|
|
18
|
+
# is what `hecks_name` carries. DECLARATIONS IN THE GRAPH, STRINGS IN THE
|
|
19
|
+
# EXPORT.
|
|
20
|
+
class ValueObject
|
|
21
|
+
extend Construct
|
|
22
|
+
# EXTENDED, not included — this construct is a class, so its
|
|
23
|
+
# emission is a class method. See Hecks::IR's own note on
|
|
24
|
+
# the two shapes.
|
|
25
|
+
extend Hecks::IR
|
|
26
|
+
extend Behaviour::ValueObject
|
|
27
|
+
|
|
28
|
+
emits_ir(
|
|
29
|
+
name: :hecks_name,
|
|
30
|
+
attributes: many(:attributes),
|
|
31
|
+
invariants: -> { invariants.map { |rule| { description: rule.description, canonical: rule.canonical } } },
|
|
32
|
+
closed_set: :closed_set?,
|
|
33
|
+
members: -> { members.map { |member| member.map { |field, value| [field.to_s, value.to_s] } } }
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
class << self
|
|
37
|
+
attr_reader :attributes, :invariants, :members
|
|
38
|
+
|
|
39
|
+
# One declared shape — a subclass rather than an instance, so the thing
|
|
40
|
+
# the bluebook declares and the thing Ruby holds are one object.
|
|
41
|
+
def declare(name:, attributes: [], invariants: [], members: [], closed_set: !members.empty?)
|
|
42
|
+
shape = Class.new(self)
|
|
43
|
+
shape.hecks_name = name.to_s
|
|
44
|
+
shape.absorb(attributes: attributes, invariants: invariants,
|
|
45
|
+
members: members, closed_set: closed_set)
|
|
46
|
+
shape
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def absorb(attributes:, invariants:, members:, closed_set:)
|
|
50
|
+
@attributes = attributes
|
|
51
|
+
@invariants = invariants
|
|
52
|
+
@members = members
|
|
53
|
+
@closed_set = closed_set
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Hecks::Bluebook
|
|
2
|
+
#
|
|
3
|
+
# Everything a .bluebook file becomes on its way to running: the expression
|
|
4
|
+
# language (expression/), the model itself (the chapter class plus
|
|
5
|
+
# everything it declares), the assembly that turns
|
|
6
|
+
# declarations back into that graph (assembly.rb + assembly/), the authoring
|
|
7
|
+
# DSL (dsl/), and the meta-validator that judges a chapter against the
|
|
8
|
+
# language's own grammar (meta_validator.rb + meta_validator/).
|
|
9
|
+
#
|
|
10
|
+
# The require order below preserves the boot order the flat list in
|
|
11
|
+
# lib/hecks.rb used to spell: expression and IR first (pure
|
|
12
|
+
# declarations), assembly's collaborators before its face, the DSL before
|
|
13
|
+
# the meta-validator that its builders call at build time.
|
|
14
|
+
|
|
15
|
+
module Hecks
|
|
16
|
+
# Declared as a CLASS, not a module — `Hecks::Bluebook` IS a
|
|
17
|
+
# chapter (bluebook/chapter.rb carries its body). Everything a chapter
|
|
18
|
+
# declares nests under it, as do the ways to build one (`DSL`) and to
|
|
19
|
+
# judge one (`MetaValidator`). Reopening this anywhere must say `class`.
|
|
20
|
+
module Bluebook
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# What the chapter's own files lean on at class-body level (`extend
|
|
25
|
+
# Construct`, the assembly's QuerySpecification marks) — required here
|
|
26
|
+
# because those files' contents are frozen and cannot say so themselves.
|
|
27
|
+
require_relative "construct"
|
|
28
|
+
require_relative "literal"
|
|
29
|
+
require_relative "query_specification"
|
|
30
|
+
|
|
31
|
+
require_relative "bluebook/expression"
|
|
32
|
+
# THE MODEL ITSELF — the chapter class's own body first, then everything
|
|
33
|
+
# a chapter declares. Order matters only for reading: each is a bag of
|
|
34
|
+
# declarations with no load-time cross-references.
|
|
35
|
+
require_relative "bluebook/chapter"
|
|
36
|
+
require_relative "bluebook/reference"
|
|
37
|
+
require_relative "bluebook/attribute"
|
|
38
|
+
require_relative "bluebook/value_object"
|
|
39
|
+
require_relative "bluebook/command"
|
|
40
|
+
require_relative "bluebook/lifecycle"
|
|
41
|
+
require_relative "bluebook/query"
|
|
42
|
+
require_relative "bluebook/read_model"
|
|
43
|
+
require_relative "bluebook/entity"
|
|
44
|
+
require_relative "bluebook/domain_port"
|
|
45
|
+
require_relative "bluebook/policy"
|
|
46
|
+
require_relative "bluebook/process_manager"
|
|
47
|
+
require_relative "bluebook/aggregate"
|
|
48
|
+
require_relative "bluebook/hexagon"
|
|
49
|
+
require_relative "bluebook/translation"
|
|
50
|
+
|
|
51
|
+
require_relative "bluebook/assembly/contract"
|
|
52
|
+
require_relative "bluebook/assembly/contracts"
|
|
53
|
+
require_relative "bluebook/assembly/specializer"
|
|
54
|
+
require_relative "bluebook/assembly/build"
|
|
55
|
+
require_relative "bluebook/assembly/marks"
|
|
56
|
+
require_relative "bluebook/assembly/aggregate_assembly"
|
|
57
|
+
require_relative "bluebook/assembly"
|
|
58
|
+
|
|
59
|
+
require_relative "bluebook/project_loader"
|
|
60
|
+
|
|
61
|
+
require_relative "bluebook/dsl"
|
|
62
|
+
require_relative "bluebook/pattern_subset"
|
|
63
|
+
|
|
64
|
+
require_relative "bluebook/meta_validator"
|
|
65
|
+
require_relative "bluebook/meta_validator/plan"
|
|
66
|
+
require_relative "bluebook/meta_validator/readings"
|
|
67
|
+
require_relative "bluebook/meta_validator/judge"
|
|
68
|
+
require_relative "bluebook/meta_validator/shapes"
|
|
69
|
+
require_relative "bluebook/meta_validator/reconstruction"
|
|
70
|
+
require_relative "bluebook/meta_validator/world_judge"
|
|
71
|
+
require_relative "bluebook/meta_validator/port_judge"
|
|
72
|
+
require_relative "bluebook/meta_validator/adapter_judge"
|
|
73
|
+
require_relative "bluebook/meta_validator/translation_judge"
|
|
74
|
+
require_relative "bluebook/meta_validator/syntax_boot"
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
require_relative "bluebook/meta_validator"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
# SHARED MACHINERY for a codemod that migrates real `.bluebook` source
|
|
5
|
+
# once a DSL builder change makes some previously-required declaration
|
|
6
|
+
# optional/redundant — pulled out of `bin/codemod_implicit_append_fields`
|
|
7
|
+
# (the first one built), which needed three real, hard-won fixes before
|
|
8
|
+
# it could be trusted: a process-lifetime AST cache with no
|
|
9
|
+
# invalidation, a batch-revert granularity that let one unsafe
|
|
10
|
+
# candidate sink every other safe one sharing its boot, and (in the
|
|
11
|
+
# SPINE the codemod migrates FOR, not here) an append-at-end insertion
|
|
12
|
+
# that only round-tripped correctly for whichever field happened to be
|
|
13
|
+
# last. None of those are guessable in advance; they only surface by
|
|
14
|
+
# actually running a real edit against real self-hosted code. This
|
|
15
|
+
# module is that lesson, kept — the NEXT codemod plugs in two rule-
|
|
16
|
+
# specific procs (`find_candidates`, `apply_candidate`) and inherits
|
|
17
|
+
# the boot/safety-net machinery rather than rediscovering it.
|
|
18
|
+
#
|
|
19
|
+
# A CODEMOD IS NOT PATTERN-MATCHING ALONE. Deciding "is this line safe
|
|
20
|
+
# to delete" means knowing what the RUNTIME would resolve it to — so
|
|
21
|
+
# every codemod built on this module follows the same three steps:
|
|
22
|
+
# 1. Boot the real domain (or the self-hosted meta-domain) and read
|
|
23
|
+
# its IR to find CANDIDATES — provided by the caller's own
|
|
24
|
+
# `find_candidates`, since the actual redundancy rule is specific
|
|
25
|
+
# to whichever spine change this migration serves.
|
|
26
|
+
# 2. Locate and remove each candidate's own source text — the
|
|
27
|
+
# caller's own `apply_candidate`.
|
|
28
|
+
# 3. Re-boot from the edited text and diff the full IR export
|
|
29
|
+
# against the pre-edit export. Byte-identical -> keep. Anything
|
|
30
|
+
# else (including a RAISED exception — the self-hosted
|
|
31
|
+
# meta-domain DISPATCHES itself into being, S14, so a bad edit
|
|
32
|
+
# can surface as a runtime refusal, not just a differing export)
|
|
33
|
+
# -> revert and report SKIPPED, never silently guessed past.
|
|
34
|
+
#
|
|
35
|
+
# DESIGN-TIME CHECKLIST for the SPINE change a future codemod migrates
|
|
36
|
+
# corpus text for — both items below are real bugs THIS module's own
|
|
37
|
+
# first use found, not hypothetical:
|
|
38
|
+
# - Does the resolved value get inserted into an ORDER-SENSITIVE
|
|
39
|
+
# list (the exported IR is array-order-sensitive throughout)? If
|
|
40
|
+
# so, the spine's own insertion must preserve the ORIGINAL
|
|
41
|
+
# position, not just append — an append-at-end insertion only
|
|
42
|
+
# round-trips correctly for a value that already happened to be
|
|
43
|
+
# last.
|
|
44
|
+
# - Does resolution depend on ANOTHER construct already being
|
|
45
|
+
# declared (the aggregate/entity a `sets`/`append:` field resolves
|
|
46
|
+
# against)? If a creator command can be declared BEFORE the
|
|
47
|
+
# construct it creates (real, live: `command "Handler"` before
|
|
48
|
+
# `entity "Handler"`, one file down), one-pass resolution
|
|
49
|
+
# genuinely cannot see it yet — not a codemod bug, a structural
|
|
50
|
+
# limit worth naming rather than working around.
|
|
51
|
+
module Codemod
|
|
52
|
+
ROOT = File.expand_path("../..", __dir__)
|
|
53
|
+
|
|
54
|
+
EXAMPLE_ROOTS = Dir.glob(File.join(ROOT, "examples", "*")).select { |p| File.directory?(p) }.sort
|
|
55
|
+
META_FILES = (Dir.glob(File.join(ROOT, "lib/hecks/grammar/*.bluebook")) +
|
|
56
|
+
Dir.glob(File.join(ROOT, "lib/hecks/framework/bluebook/*.bluebook")) +
|
|
57
|
+
Dir.glob(File.join(ROOT, "lib/hecks/language/bluebook/**/*.bluebook"))).sort
|
|
58
|
+
|
|
59
|
+
# THE SAME LIGHTWEIGHT PATH `spec/spec_helper.rb`'s own
|
|
60
|
+
# `boot_in_memory` uses — `Hecks.with_registry` satisfies
|
|
61
|
+
# `Hecks.bluebook`'s own `collect`'s "loaded outside a boot" check
|
|
62
|
+
# without `Hecks.boot`'s full era-check/adapter-wiring path, which
|
|
63
|
+
# needs a LIVE Postgres connection for `compliance` and would make
|
|
64
|
+
# every codemod depend on a database it has no reason to touch — a
|
|
65
|
+
# codemod only ever reads a chapter's own declared IR, never a
|
|
66
|
+
# stored record.
|
|
67
|
+
PERSISTENCE_PORT = File.join(ROOT, "lib/hecks/ports/persistence.port")
|
|
68
|
+
EXTRACTION_PORT = File.join(ROOT, "lib/hecks/ports/extraction.port")
|
|
69
|
+
MEMORY_ADAPTER = File.join(ROOT, "lib/hecks/adapters/driven/memory.adapter")
|
|
70
|
+
PRISM_ADAPTER = File.join(ROOT, "lib/hecks/adapters/driven/prism.adapter")
|
|
71
|
+
|
|
72
|
+
def self.export_json(registry) = Hecks::Projector::Exporter.json(registry)
|
|
73
|
+
|
|
74
|
+
# `Hecks::Adapters::Prism` caches a file's parsed AST for the
|
|
75
|
+
# life of the PROCESS, keyed by path — fine for every existing
|
|
76
|
+
# caller (a file loads once per process: one `bin/ir` run, one
|
|
77
|
+
# rspec worker), but a codemod legitimately reloads the SAME path
|
|
78
|
+
# after editing it, and a stale cached tree reports a
|
|
79
|
+
# `given`/`ensures` block at its OLD line number, which no longer
|
|
80
|
+
# matches the freshly re-executed file's own `block.source_location`
|
|
81
|
+
# — surfacing as "did not survive extraction" on a perfectly valid
|
|
82
|
+
# file. `Prism.forget` is the real invalidation API this module's
|
|
83
|
+
# own first use motivated (found here, fixed at the source rather
|
|
84
|
+
# than left as a private `TREES.clear` poke from outside).
|
|
85
|
+
def self.load_bluebook(path)
|
|
86
|
+
paths = if path.is_a?(Array)
|
|
87
|
+
path
|
|
88
|
+
elsif File.directory?(path)
|
|
89
|
+
Dir.glob(File.join(path, "*.bluebook"))
|
|
90
|
+
else
|
|
91
|
+
[path]
|
|
92
|
+
end
|
|
93
|
+
paths.each { |file| Hecks::Adapters::Prism.forget(file) }
|
|
94
|
+
|
|
95
|
+
registry = Hecks::Runtime::Registry.new
|
|
96
|
+
Hecks.with_registry(registry) do
|
|
97
|
+
Kernel.load(PERSISTENCE_PORT)
|
|
98
|
+
Kernel.load(EXTRACTION_PORT)
|
|
99
|
+
Kernel.load(MEMORY_ADAPTER)
|
|
100
|
+
Kernel.load(PRISM_ADAPTER)
|
|
101
|
+
Hecks::Bluebook::MetaValidator.defer { paths.each { |file| Kernel.load(file) } }
|
|
102
|
+
Hecks::Bluebook::MetaValidator.judge_deferred!(registry)
|
|
103
|
+
end
|
|
104
|
+
registry
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# `forget_all`, not a single `forget` — the meta-domain is NINE
|
|
108
|
+
# files (`MetaValidator::GRAMMAR_FILES`) merged into one registry,
|
|
109
|
+
# and a caller here (the codemod runner) doesn't generally know in
|
|
110
|
+
# advance which ONE it just edited.
|
|
111
|
+
def self.boot_meta
|
|
112
|
+
Hecks::Adapters::Prism.forget_all
|
|
113
|
+
Hecks::Bluebook::MetaValidator.instance_variable_set(:@grammar_registry, nil)
|
|
114
|
+
export_json(Hecks::Bluebook::MetaValidator.grammar_registry)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self.meta_registry
|
|
118
|
+
Hecks::Adapters::Prism.forget_all
|
|
119
|
+
Hecks::Bluebook::MetaValidator.instance_variable_set(:@grammar_registry, nil)
|
|
120
|
+
Hecks::Bluebook::MetaValidator.grammar_registry
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Walks every aggregate (and every nested entity, recursively)
|
|
124
|
+
# across every chapter in a booted registry, yielding [owning
|
|
125
|
+
# construct, command] pairs — `construct` is whichever
|
|
126
|
+
# Aggregate/Entity actually OWNS the command, the same distinction
|
|
127
|
+
# `AggregateBuilder#command` vs `EntityBuilder#command` already
|
|
128
|
+
# draws. Generic enough for any rule that needs to walk real
|
|
129
|
+
# commands, not specific to the attribute-redundancy rule.
|
|
130
|
+
def self.each_command(registry)
|
|
131
|
+
registry.bluebooks.each_value do |chapter|
|
|
132
|
+
chapter.aggregates.each do |aggregate|
|
|
133
|
+
walk = lambda do |construct|
|
|
134
|
+
construct.commands.each { |command| yield construct, command }
|
|
135
|
+
construct.entities.each(&walk) if construct.respond_to?(:entities)
|
|
136
|
+
end
|
|
137
|
+
walk.call(aggregate)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def self.owner_attribute(construct, name)
|
|
143
|
+
construct.attributes.find { |attr| attr.name.to_s == name.to_s }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# A LIST attribute's own element construct — the value object or
|
|
147
|
+
# entity `list_of(...)` names, resolved by `hecks_name` the same way
|
|
148
|
+
# `AttributeCollector#resolve_identity_field!` already does. Shared
|
|
149
|
+
# because "what does this list actually hold" is a question any
|
|
150
|
+
# append-shaped rule needs answered, not just this one.
|
|
151
|
+
def self.element_construct_for(construct, list_field)
|
|
152
|
+
list_attr = owner_attribute(construct, list_field)
|
|
153
|
+
return nil unless list_attr&.list?
|
|
154
|
+
|
|
155
|
+
pool = construct.respond_to?(:value_objects) ? construct.value_objects.dup : []
|
|
156
|
+
pool.concat(construct.entities) if construct.respond_to?(:entities)
|
|
157
|
+
pool.find { |c| c.hecks_name.to_s == list_attr.type.to_s }
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Either a raised exception OR a differing export counts as unsafe
|
|
161
|
+
# — see the module header on why the meta-domain specifically can
|
|
162
|
+
# raise. Returns [value_or_nil, error_message_or_nil].
|
|
163
|
+
def self.safely
|
|
164
|
+
[yield, nil]
|
|
165
|
+
rescue StandardError => e
|
|
166
|
+
[nil, "#{e.class}: #{e.message}"]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# THE REUSABLE RUNNER — every real bug fix this module carries lives
|
|
170
|
+
# here, not in a caller's own script. A caller supplies:
|
|
171
|
+
#
|
|
172
|
+
# find_candidates: ->(registry) { [...] }
|
|
173
|
+
# Given a booted registry, return every CANDIDATE this rule
|
|
174
|
+
# could migrate. A candidate is whatever shape the caller wants
|
|
175
|
+
# — `apply_candidate` is the only other thing that reads it.
|
|
176
|
+
#
|
|
177
|
+
# apply_candidate: ->(text, candidate) { [new_text, changed_bool] }
|
|
178
|
+
# Given one file's current text and one candidate, return the
|
|
179
|
+
# edited text and whether a match was actually found/removed —
|
|
180
|
+
# `false` (text unchanged) when the candidate doesn't apply to
|
|
181
|
+
# THIS file, which is how the meta-domain's multi-file search
|
|
182
|
+
# below finds the right one without the caller needing to know
|
|
183
|
+
# which file a candidate lives in ahead of time.
|
|
184
|
+
#
|
|
185
|
+
# label: ->(candidate) { "..." }
|
|
186
|
+
# One-line description for the results report.
|
|
187
|
+
class Runner
|
|
188
|
+
def initialize(find_candidates:, apply_candidate:, label:)
|
|
189
|
+
@find_candidates = find_candidates
|
|
190
|
+
@apply_candidate = apply_candidate
|
|
191
|
+
@label = label
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def run(dry_run: false)
|
|
195
|
+
results = { applied: [], skipped: [], clean: [] }
|
|
196
|
+
run_example_domains(results, dry_run)
|
|
197
|
+
run_meta_domain(results, dry_run)
|
|
198
|
+
results
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def report(results, dry_run:)
|
|
202
|
+
puts "== results (#{dry_run ? 'DRY RUN — nothing written' : 'applied'}) =="
|
|
203
|
+
puts "clean (no candidates): #{results[:clean].join(', ')}" unless results[:clean].empty?
|
|
204
|
+
results[:applied].each { |r| puts "APPLIED #{r[:file]}: #{r[:candidates].join(', ')}" }
|
|
205
|
+
results[:skipped].each { |r| puts "SKIPPED #{r[:file]} (#{r[:reason]}): #{Array(r[:candidates]).join(', ')}" }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
private
|
|
209
|
+
|
|
210
|
+
def apply_many(text, candidates)
|
|
211
|
+
applied = []
|
|
212
|
+
candidates.each do |c|
|
|
213
|
+
text, changed = @apply_candidate.call(text, c)
|
|
214
|
+
applied << c if changed
|
|
215
|
+
end
|
|
216
|
+
[text, applied]
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# The per-candidate write/verify/revert sequence below is one
|
|
220
|
+
# coherent unit (see the comment ahead of the candidates.each
|
|
221
|
+
# loop for why it can't batch) — splitting it across methods just
|
|
222
|
+
# to satisfy the line count would scatter state four ways for no
|
|
223
|
+
# readability gain.
|
|
224
|
+
# rubocop:disable-next Metrics/BlockLength
|
|
225
|
+
def run_example_domains(results, dry_run)
|
|
226
|
+
Codemod::EXAMPLE_ROOTS.each do |domain_dir|
|
|
227
|
+
bluebook_files = Dir.glob(File.join(domain_dir, "bluebook", "*.bluebook"))
|
|
228
|
+
next if bluebook_files.empty?
|
|
229
|
+
|
|
230
|
+
before_json = Codemod.export_json(Codemod.load_bluebook(bluebook_files))
|
|
231
|
+
candidates = @find_candidates.call(Codemod.load_bluebook(bluebook_files))
|
|
232
|
+
|
|
233
|
+
if candidates.empty?
|
|
234
|
+
results[:clean] << domain_dir
|
|
235
|
+
next
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
live = bluebook_files.to_h { |file| [file, File.read(file)] }
|
|
239
|
+
applied_by_file = Hash.new { |hash, file| hash[file] = [] }
|
|
240
|
+
|
|
241
|
+
candidates.each do |candidate|
|
|
242
|
+
target_file = bluebook_files.find { |file| apply_many(live[file], [candidate]).last.any? }
|
|
243
|
+
unless target_file
|
|
244
|
+
results[:skipped] << { file: domain_dir, reason: "no matching source line", candidates: [@label.call(candidate)] }
|
|
245
|
+
next
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
original_text = live[target_file]
|
|
249
|
+
text, = apply_many(original_text, [candidate])
|
|
250
|
+
live[target_file] = text
|
|
251
|
+
File.write(target_file, text)
|
|
252
|
+
after_json, error = Codemod.safely do
|
|
253
|
+
Codemod.export_json(Codemod.load_bluebook(bluebook_files))
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
if after_json == before_json && !dry_run
|
|
257
|
+
applied_by_file[target_file] << candidate
|
|
258
|
+
elsif after_json == before_json
|
|
259
|
+
live[target_file] = original_text
|
|
260
|
+
File.write(target_file, original_text)
|
|
261
|
+
Codemod.safely { Codemod.load_bluebook(bluebook_files) }
|
|
262
|
+
applied_by_file[target_file] << candidate
|
|
263
|
+
else
|
|
264
|
+
live[target_file] = original_text
|
|
265
|
+
File.write(target_file, original_text)
|
|
266
|
+
Codemod.safely { Codemod.load_bluebook(bluebook_files) }
|
|
267
|
+
reason = error ? "reboot raised after edit (#{error}) — reverted" : "IR changed after edit — reverted"
|
|
268
|
+
results[:skipped] << { file: target_file, reason: reason, candidates: [@label.call(candidate)] }
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
applied_by_file.each do |file, applied|
|
|
273
|
+
results[:applied] << { file: file, candidates: applied.map(&@label) }
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# PER-CANDIDATE, not one batched write-then-verify — the
|
|
279
|
+
# meta-domain is ONE shared registry (SyntaxBoot DISPATCHES it
|
|
280
|
+
# into itself, S14), so a single unsafe candidate among many would
|
|
281
|
+
# otherwise sink every OTHER, genuinely safe candidate in the same
|
|
282
|
+
# run: this module's first real run found exactly that (25
|
|
283
|
+
# candidates, one dispatch-time break, all 25 reverted as a batch
|
|
284
|
+
# before this per-candidate loop existed). `before_meta` stays the
|
|
285
|
+
# fixed reference throughout — a truly safe candidate never
|
|
286
|
+
# changes the IR, so the running state's own export should always
|
|
287
|
+
# still equal the pristine original after each kept edit, by
|
|
288
|
+
# definition, no moving target needed.
|
|
289
|
+
def run_meta_domain(results, dry_run)
|
|
290
|
+
before_meta = Codemod.boot_meta
|
|
291
|
+
meta_candidates = @find_candidates.call(Codemod.meta_registry)
|
|
292
|
+
|
|
293
|
+
if meta_candidates.empty?
|
|
294
|
+
results[:clean] << "meta-domain"
|
|
295
|
+
return
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
live_meta = Codemod::META_FILES.to_h { |f| [f, File.read(f)] }
|
|
299
|
+
applied_by_file = Hash.new { |h, k| h[k] = [] }
|
|
300
|
+
|
|
301
|
+
meta_candidates.each do |candidate|
|
|
302
|
+
target_file = Codemod::META_FILES.find { |f| apply_many(live_meta[f], [candidate]).last.any? }
|
|
303
|
+
unless target_file
|
|
304
|
+
results[:skipped] << { file: "meta-domain", reason: "no matching source line", candidates: [@label.call(candidate)] }
|
|
305
|
+
next
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
text, = apply_many(live_meta[target_file], [candidate])
|
|
309
|
+
original_text = live_meta[target_file]
|
|
310
|
+
live_meta[target_file] = text
|
|
311
|
+
Codemod::META_FILES.each { |f| File.write(f, live_meta[f]) }
|
|
312
|
+
|
|
313
|
+
# DRY RUN STILL VERIFIES — see run_example_domains' own
|
|
314
|
+
# comment; the edit is always written and rebooted for real,
|
|
315
|
+
# then always reverted afterward when dry-run (whether or not
|
|
316
|
+
# it was safe) so the next candidate is judged against the
|
|
317
|
+
# true original state, never an accumulated hypothetical.
|
|
318
|
+
after_meta, error = Codemod.safely { Codemod.boot_meta }
|
|
319
|
+
|
|
320
|
+
if after_meta == before_meta && !dry_run
|
|
321
|
+
applied_by_file[target_file] << candidate
|
|
322
|
+
elsif after_meta == before_meta # dry-run, safe — revert, but count as applied
|
|
323
|
+
live_meta[target_file] = original_text
|
|
324
|
+
File.write(target_file, original_text)
|
|
325
|
+
Codemod.safely { Codemod.boot_meta }
|
|
326
|
+
applied_by_file[target_file] << candidate
|
|
327
|
+
else
|
|
328
|
+
live_meta[target_file] = original_text
|
|
329
|
+
File.write(target_file, original_text)
|
|
330
|
+
Codemod.safely { Codemod.boot_meta } # restore cache/memoized state to the reverted text
|
|
331
|
+
reason = error ? "reboot raised (#{error})" : "IR changed"
|
|
332
|
+
results[:skipped] << { file: "meta-domain", reason: reason, candidates: [@label.call(candidate)] }
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
applied_by_file.each do |file, candidates|
|
|
337
|
+
results[:applied] << { file: file, candidates: candidates.map(&@label) }
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
# The invisible field a built construct carries.
|
|
3
|
+
#
|
|
4
|
+
# A construct is a RECORD WITH AN OWNER CHAIN — the chapter (Bluebook)
|
|
5
|
+
# owns its aggregates, an aggregate owns everything declared on it — and the
|
|
6
|
+
# bluebook identity is carried in its own field, under a `hecks_` prefix that
|
|
7
|
+
# no domain attribute can collide with. Invisible means exactly that: not an
|
|
8
|
+
# attribute, not a key in `to_h`, not a reader on instances. Framework
|
|
9
|
+
# metadata about the construct, not part of the domain it describes.
|
|
10
|
+
#
|
|
11
|
+
# The identity is COMPUTED by walking owners rather than stamped, so nothing
|
|
12
|
+
# has to be re-stamped when a chapter is assembled after its aggregates:
|
|
13
|
+
#
|
|
14
|
+
# Pizzas the chapter — no owner
|
|
15
|
+
# Pizzas::Pizza an aggregate joins its chapter with ::
|
|
16
|
+
# Pizzas::Pizza.Price everything else joins its owner with .
|
|
17
|
+
#
|
|
18
|
+
# That spelling is not invented here. It is the id `MetaValidator::Judge`
|
|
19
|
+
# already mints in `#identify`, so a construct and the meta-domain's
|
|
20
|
+
# record OF that construct carry the same identity, and there is no
|
|
21
|
+
# translation table between them to be quietly wrong in.
|
|
22
|
+
#
|
|
23
|
+
# Usage:
|
|
24
|
+
#
|
|
25
|
+
# price = Class.new(ValueObject) # a declaration holder
|
|
26
|
+
# price.hecks_name = "Price"
|
|
27
|
+
# price.hecks_owner = pizza_ir # the Aggregate that declares it
|
|
28
|
+
# price.hecks_fqn # => "Pizzas::Pizza.Price"
|
|
29
|
+
module Construct
|
|
30
|
+
# A construct asked for an identity it cannot compute.
|
|
31
|
+
class Unowned < StandardError; end
|
|
32
|
+
|
|
33
|
+
# What declares this one — the chapter above an aggregate, the aggregate
|
|
34
|
+
# above a value object. nil for a chapter, which is the top.
|
|
35
|
+
attr_accessor :hecks_owner
|
|
36
|
+
|
|
37
|
+
attr_writer :hecks_name
|
|
38
|
+
|
|
39
|
+
# A chapter is the only construct that legitimately has no owner. Everything
|
|
40
|
+
# else is DECLARED IN something, so a missing owner is an unstamped construct
|
|
41
|
+
# rather than a top — see hecks_fqn.
|
|
42
|
+
attr_writer :hecks_root
|
|
43
|
+
|
|
44
|
+
def hecks_root? = @hecks_root ? true : false
|
|
45
|
+
|
|
46
|
+
# The name as the bluebook declares it, never the constant path.
|
|
47
|
+
def hecks_name = @hecks_name
|
|
48
|
+
|
|
49
|
+
# How this construct joins its owner. An aggregate is a member of its
|
|
50
|
+
# chapter's namespace (`::`) ; everything else is declared ON its owner
|
|
51
|
+
# (`.`). Overridden by Aggregate, defaulted here for every other construct.
|
|
52
|
+
def hecks_separator = "."
|
|
53
|
+
|
|
54
|
+
# REFUSES rather than guesses. A construct with no owner and no claim to be a
|
|
55
|
+
# chapter has simply not been stamped yet — entity commands are in that state
|
|
56
|
+
# while entities are still IR objects — and answering the bare name would be a
|
|
57
|
+
# plausible half-truth that no test would notice. That shape of falsehood is
|
|
58
|
+
# what this repo keeps finding, so it goes red instead.
|
|
59
|
+
def hecks_fqn
|
|
60
|
+
return hecks_name.to_s if hecks_root?
|
|
61
|
+
|
|
62
|
+
unless hecks_owner
|
|
63
|
+
raise Construct::Unowned,
|
|
64
|
+
"#{hecks_name.inspect} cannot say what declares it, so it has no " \
|
|
65
|
+
"identity — it was never stamped with an owner"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
"#{hecks_owner.hecks_fqn}#{hecks_separator}#{hecks_name}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|