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,183 @@
|
|
|
1
|
+
require_relative "../../runtime/storage_shape"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Translation
|
|
5
|
+
module Scaffold
|
|
6
|
+
# Diff two eras' storage-shape projections into the edge's
|
|
7
|
+
# declarations — confident rules by unique signature pairing,
|
|
8
|
+
# everything ambiguous left unresolved for a human.
|
|
9
|
+
module Differ
|
|
10
|
+
# Diff two bluebook IRs into the edge's declarations.
|
|
11
|
+
def diff(held_bluebook, current_bluebook)
|
|
12
|
+
held_shapes = projections(held_bluebook)
|
|
13
|
+
current_shapes = projections(current_bluebook)
|
|
14
|
+
|
|
15
|
+
matched = match_aggregates(held_shapes, current_shapes)
|
|
16
|
+
aggregates = current_bluebook.aggregates.filter_map do |aggregate|
|
|
17
|
+
held_name = matched[:pairs][aggregate.name]
|
|
18
|
+
next unless held_name
|
|
19
|
+
|
|
20
|
+
rules = attribute_rules(held_shapes[held_name], current_shapes[aggregate.name])
|
|
21
|
+
was = held_name == aggregate.name ? nil : held_name
|
|
22
|
+
next if rules.empty? && was.nil?
|
|
23
|
+
|
|
24
|
+
ScaffoldedAggregate.new(name: aggregate.name, was: was, rules: rules)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
{ aggregates: aggregates, retired: matched[:retired], unclaimed: matched[:unclaimed] }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def projections(bluebook)
|
|
31
|
+
Runtime::StorageShape.project(bluebook)["aggregates"].to_h { |shape| [shape["name"], shape] }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# A vanished aggregate whose full shape reappears under exactly one
|
|
35
|
+
# new name was renamed. `retired` is only confident when NOTHING
|
|
36
|
+
# remains it could plausibly have become — a vanished aggregate
|
|
37
|
+
# beside an unmatched new one might be a rename-plus-reshape, and
|
|
38
|
+
# writing `retired` there would be a guess that strands data.
|
|
39
|
+
# Anything ambiguous stays unclaimed, and the coverage gate names
|
|
40
|
+
# it until a human decides.
|
|
41
|
+
def match_aggregates(held_shapes, current_shapes)
|
|
42
|
+
pairs = {}
|
|
43
|
+
current_shapes.each_key { |name| pairs[name] = held_shapes.key?(name) ? name : nil }
|
|
44
|
+
|
|
45
|
+
vanished = held_shapes.keys - current_shapes.keys
|
|
46
|
+
appeared = current_shapes.keys.select { |name| pairs[name].nil? }
|
|
47
|
+
unclaimed = []
|
|
48
|
+
vanished.each do |old_name|
|
|
49
|
+
old_shape = held_shapes[old_name].merge("name" => nil)
|
|
50
|
+
candidates = appeared.select { |name| current_shapes[name].merge("name" => nil) == old_shape }
|
|
51
|
+
if candidates.size == 1
|
|
52
|
+
pairs[candidates.first] = old_name
|
|
53
|
+
appeared -= candidates
|
|
54
|
+
else
|
|
55
|
+
unclaimed << old_name
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
retired = appeared.empty? ? unclaimed : []
|
|
60
|
+
{ pairs: pairs, retired: retired, unclaimed: retired.empty? ? unclaimed : [] }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Every changed path in one aggregate, resolved into rules by
|
|
64
|
+
# signature matching over the FULL path set — top-level names and
|
|
65
|
+
# dotted members alike. Unique signature pair: a rename (both
|
|
66
|
+
# top-level) or a move (any dotted end). Same path, same members,
|
|
67
|
+
# new type name: a retype. Anything else: unresolved, carrying its
|
|
68
|
+
# type-compatible candidates (an empty list is the arrow toward
|
|
69
|
+
# compute, or drop).
|
|
70
|
+
def attribute_rules(held_shape, current_shape)
|
|
71
|
+
rules = []
|
|
72
|
+
rules << identity_hint(held_shape, current_shape)
|
|
73
|
+
rules.compact!
|
|
74
|
+
held_attrs = (held_shape["attributes"] || []).to_h { |attribute| [attribute["name"], attribute] }
|
|
75
|
+
current_attrs = (current_shape["attributes"] || []).to_h { |attribute| [attribute["name"], attribute] }
|
|
76
|
+
|
|
77
|
+
# retype pass: same attribute name, same member structure, the
|
|
78
|
+
# TYPE's own name changed
|
|
79
|
+
(held_attrs.keys & current_attrs.keys).each do |name|
|
|
80
|
+
held = held_attrs[name]
|
|
81
|
+
current = current_attrs[name]
|
|
82
|
+
next if held == current
|
|
83
|
+
next unless container?(held) && container?(current)
|
|
84
|
+
next unless held["type"]["members"] == current["type"]["members"] && held["list"] == current["list"]
|
|
85
|
+
|
|
86
|
+
rules << { kind: :retype, from: held["type"]["type"], to: current["type"]["type"] }
|
|
87
|
+
end
|
|
88
|
+
retyped = rules.filter_map { |rule| rule[:from] if rule[:kind] == :retype }
|
|
89
|
+
|
|
90
|
+
vanished = vanished_paths(held_attrs, current_attrs, retyped)
|
|
91
|
+
appeared = appeared_paths(held_attrs, current_attrs, retyped)
|
|
92
|
+
|
|
93
|
+
vanished.each do |path, signature|
|
|
94
|
+
matches = appeared.select { |_, candidate| candidate == signature }.keys
|
|
95
|
+
taken = rules.filter_map { |rule| rule[:to] if %i[rename move].include?(rule[:kind]) }
|
|
96
|
+
matches -= taken
|
|
97
|
+
if matches.size == 1 && vanished.count { |_, other| other == signature } == 1
|
|
98
|
+
target = matches.first
|
|
99
|
+
kind = path.include?(".") || target.include?(".") ? :move : :rename
|
|
100
|
+
rules << { kind: kind, from: path, to: target }
|
|
101
|
+
else
|
|
102
|
+
candidates = matches.empty? ? compatible_candidates(appeared, signature) : matches
|
|
103
|
+
rules << { kind: :unresolved, from: path, candidates: candidates }
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
rules
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Held paths that need explaining: whole attributes that vanished,
|
|
110
|
+
# and members that vanished (or changed type) inside a kept
|
|
111
|
+
# attribute — mirroring exactly what EraGuard will demand coverage
|
|
112
|
+
# for.
|
|
113
|
+
def vanished_paths(held_attrs, current_attrs, retyped)
|
|
114
|
+
paths = {}
|
|
115
|
+
held_attrs.each do |name, held|
|
|
116
|
+
next if retyped.include?(container?(held) ? held["type"]["type"] : nil)
|
|
117
|
+
|
|
118
|
+
current = current_attrs[name]
|
|
119
|
+
if current.nil?
|
|
120
|
+
paths[name] = held["type"]
|
|
121
|
+
next
|
|
122
|
+
end
|
|
123
|
+
next if held == current
|
|
124
|
+
|
|
125
|
+
held_members = members_of(held)
|
|
126
|
+
current_members = members_of(current)
|
|
127
|
+
held_members.each do |member, signature|
|
|
128
|
+
paths["#{name}.#{member}"] = signature if current_members[member] != signature
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
paths
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def appeared_paths(held_attrs, current_attrs, retyped)
|
|
135
|
+
paths = {}
|
|
136
|
+
current_attrs.each do |name, current|
|
|
137
|
+
next if retyped.include?(container?(current) ? current["type"]["type"] : nil)
|
|
138
|
+
|
|
139
|
+
held = held_attrs[name]
|
|
140
|
+
if held.nil?
|
|
141
|
+
paths[name] = current["type"]
|
|
142
|
+
next
|
|
143
|
+
end
|
|
144
|
+
next if held == current
|
|
145
|
+
|
|
146
|
+
held_members = members_of(held)
|
|
147
|
+
members_of(current).each do |member, signature|
|
|
148
|
+
paths["#{name}.#{member}"] = signature if held_members[member] != signature
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
paths
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# THE ONE THING `attribute_rules` COULD NOT SEE BEFORE — the shape
|
|
155
|
+
# projection already carries `"identity"` (`StorageShape
|
|
156
|
+
# .project_aggregate`), it was simply never read here. An
|
|
157
|
+
# unresolved placeholder, not a guess: `coverage_check.rb`'s own
|
|
158
|
+
# `check_identity_unchanged!` is the real gate this only hints
|
|
159
|
+
# toward, the same "tool proactively guides you" pattern the
|
|
160
|
+
# generic unresolved message already gives unfed fields.
|
|
161
|
+
def identity_hint(held_shape, current_shape)
|
|
162
|
+
return if held_shape["identity"] == current_shape["identity"]
|
|
163
|
+
|
|
164
|
+
{ kind: :unresolved, from: :identity, candidates: [] }
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def compatible_candidates(appeared, signature)
|
|
168
|
+
appeared.select { |_, candidate| scalar_of(candidate) == scalar_of(signature) }.keys
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def scalar_of(signature) = signature.is_a?(Hash) ? signature["members"] : signature
|
|
172
|
+
|
|
173
|
+
def members_of(attribute)
|
|
174
|
+
return {} unless container?(attribute)
|
|
175
|
+
|
|
176
|
+
attribute["type"]["members"].to_h { |member| [member["name"], member["type"]] }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def container?(attribute) = attribute["type"].is_a?(Hash)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Translation
|
|
3
|
+
module Scaffold
|
|
4
|
+
# Render an edge into .bluebook text — confident rules inline,
|
|
5
|
+
# ambiguities as parse-refusing `unresolved` constructs, never
|
|
6
|
+
# comments, so an unresolved file can only boot into a refusal.
|
|
7
|
+
module Renderer
|
|
8
|
+
def render(edge)
|
|
9
|
+
lines = ["Hecks.data_translation #{edge.domain.inspect}, from: #{edge.from.inspect}, to: #{edge.to.inspect} do"]
|
|
10
|
+
edge.aggregates.each do |aggregate|
|
|
11
|
+
header = " aggregate #{aggregate.name.inspect}"
|
|
12
|
+
header += ", was: #{aggregate.was.inspect}" if aggregate.was
|
|
13
|
+
if aggregate.rules.empty?
|
|
14
|
+
lines << "#{header} do"
|
|
15
|
+
else
|
|
16
|
+
lines << "#{header} do"
|
|
17
|
+
aggregate.rules.each { |rule| lines << " #{render_rule(rule)}" }
|
|
18
|
+
end
|
|
19
|
+
lines << " end"
|
|
20
|
+
end
|
|
21
|
+
edge.retired.each { |name| lines << " retired #{name.inspect}" }
|
|
22
|
+
lines << "end"
|
|
23
|
+
"#{lines.join("\n")}\n"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def render_rule(rule)
|
|
27
|
+
case rule[:kind]
|
|
28
|
+
when :rename then "rename :#{rule[:from]}, to: :#{rule[:to]}"
|
|
29
|
+
when :move then "move #{rule[:from].inspect}, to: #{rule[:to].inspect}"
|
|
30
|
+
when :retype then "retype #{rule[:from].inspect}, to: #{rule[:to].inspect}"
|
|
31
|
+
when :unresolved
|
|
32
|
+
candidates = rule[:candidates].map { |candidate| render_path(candidate) }.join(", ")
|
|
33
|
+
"unresolved #{render_path(rule[:from])}, candidates: [#{candidates}]"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def render_path(path) = path.to_s.include?(".") ? path.to_s.inspect : ":#{path}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Translation
|
|
5
|
+
module Scaffold
|
|
6
|
+
# Put the rendered edge on disk, regenerating in place when a file
|
|
7
|
+
# for the same shape pair already exists.
|
|
8
|
+
module Writer
|
|
9
|
+
# The edge file, regenerated in place when one for the same shape
|
|
10
|
+
# pair already exists (matched textually — an unresolved file
|
|
11
|
+
# cannot be LOADED to ask, that being the whole point of
|
|
12
|
+
# unresolved).
|
|
13
|
+
def write!(directory, edge)
|
|
14
|
+
translations_dir = File.join(directory, "translations")
|
|
15
|
+
FileUtils.mkdir_p(translations_dir)
|
|
16
|
+
|
|
17
|
+
existing = Dir[File.join(translations_dir, "*.bluebook")].find do |path|
|
|
18
|
+
text = File.read(path)
|
|
19
|
+
text.include?("from: #{edge.from.inspect}") && text.include?("to: #{edge.to.inspect}")
|
|
20
|
+
end
|
|
21
|
+
path = existing || File.join(translations_dir, "#{edge.ordinal}-#{edge.label}.bluebook")
|
|
22
|
+
File.write(path, render(edge))
|
|
23
|
+
path
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require_relative "scaffold/differ"
|
|
2
|
+
require_relative "scaffold/renderer"
|
|
3
|
+
require_relative "scaffold/writer"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Translation
|
|
7
|
+
# The scaffold writes translations; humans resolve ambiguity. It
|
|
8
|
+
# diffs the held era's storage-shape projection against the current
|
|
9
|
+
# one (scaffold/differ.rb) and WRITES the edge file
|
|
10
|
+
# (scaffold/renderer.rb, scaffold/writer.rb): confident rules inline
|
|
11
|
+
# (unique signature pairs → rename/move, aggregate renames → `was:`,
|
|
12
|
+
# type renames with identical members → retype, aggregates gone
|
|
13
|
+
# without a successor → retired), ambiguities as parse-refusing
|
|
14
|
+
# `unresolved` constructs — never comments, so an unresolved file can
|
|
15
|
+
# only boot into a refusal, never a guess. It NEVER proposes a
|
|
16
|
+
# `compute` (a computation takes domain judgment no mechanical diff
|
|
17
|
+
# can infer) — an empty-candidate `unresolved` is what points the
|
|
18
|
+
# author there. It never proposes a `drop` either: data loss is a
|
|
19
|
+
# decision, not a default. Re-running regenerates the file, matched
|
|
20
|
+
# by shape pair.
|
|
21
|
+
module Scaffold
|
|
22
|
+
Edge = Struct.new(:domain, :from, :to, :ordinal, :label, :aggregates, :retired, keyword_init: true)
|
|
23
|
+
ScaffoldedAggregate = Struct.new(:name, :was, :rules, keyword_init: true)
|
|
24
|
+
|
|
25
|
+
extend Differ
|
|
26
|
+
extend Renderer
|
|
27
|
+
extend Writer
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
# The era-translation toolchain: scaffold drafts a translation from two
|
|
3
|
+
# eras' shapes, audit judges a written one, reattest re-seals it.
|
|
4
|
+
module Translation
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
require_relative "translation/rule_compiler"
|
|
9
|
+
require_relative "translation/scaffold"
|
|
10
|
+
require_relative "translation/audit"
|
|
11
|
+
require_relative "translation/reattest"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
# ISOLATED, ON PURPOSE — the gemspec reads this file directly rather
|
|
3
|
+
# than `require_relative "lib/hecks"` (the whole framework),
|
|
4
|
+
# specifically so evaluating the gemspec never triggers ANY of this
|
|
5
|
+
# gem's own dependencies (prism among them) before Bundler has even
|
|
6
|
+
# resolved what to install. A real, live chicken-and-egg bug caught
|
|
7
|
+
# deploying to a Ruby version that doesn't bundle prism for free
|
|
8
|
+
# (AWS Lambda's ruby3.2): the gemspec's own `require_relative
|
|
9
|
+
# "lib/hecks"` pulled in adapters/driven/prism.rb's
|
|
10
|
+
# unconditional `require "prism"`, which failed before Bundler ever
|
|
11
|
+
# read what the gemspec itself declared as a dependency — declaring
|
|
12
|
+
# it there, or even in the consuming Gemfile, never closed the gap,
|
|
13
|
+
# because gemspec evaluation happens before anything Bundler
|
|
14
|
+
# resolves is actually loadable yet.
|
|
15
|
+
VERSION = "0.3.0"
|
|
16
|
+
end
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# GENERATED — projected from the language's own Vocabulary aggregate
|
|
2
|
+
# (lib/hecks/language/bluebook/vocabulary.bluebook).
|
|
3
|
+
#
|
|
4
|
+
# DO NOT EDIT. spec/vocabulary_table_spec.rb re-projects this in memory
|
|
5
|
+
# and refuses a diff, so an edit here fails the ordinary suite.
|
|
6
|
+
#
|
|
7
|
+
# Plain data on purpose — no requires, no dependency on the model —
|
|
8
|
+
# because several of these sets are read while a bluebook is still
|
|
9
|
+
# being parsed. A table that needed the framework to load could not
|
|
10
|
+
# be the table the framework loads with.
|
|
11
|
+
|
|
12
|
+
module Hecks
|
|
13
|
+
module Vocabulary
|
|
14
|
+
TABLES = {
|
|
15
|
+
"AggregateDispatchOrder" => [
|
|
16
|
+
{"step"=>"refuse_unknown_arguments"}.freeze,
|
|
17
|
+
{"step"=>"refuse_absent_arguments"}.freeze,
|
|
18
|
+
{"step"=>"normalize_args"}.freeze,
|
|
19
|
+
{"step"=>"refuse_role_mismatch"}.freeze,
|
|
20
|
+
{"step"=>"resolve_references"}.freeze,
|
|
21
|
+
{"step"=>"hydrate"}.freeze,
|
|
22
|
+
{"step"=>"enforce_givens"}.freeze,
|
|
23
|
+
{"step"=>"admissible_transition"}.freeze,
|
|
24
|
+
{"step"=>"assign_creation_attributes"}.freeze,
|
|
25
|
+
{"step"=>"apply_mutations"}.freeze,
|
|
26
|
+
{"step"=>"advance_lifecycle"}.freeze,
|
|
27
|
+
{"step"=>"delegate_to_entity"}.freeze,
|
|
28
|
+
{"step"=>"enforce_ensures"}.freeze,
|
|
29
|
+
{"step"=>"enforce_invariants"}.freeze,
|
|
30
|
+
{"step"=>"save"}.freeze,
|
|
31
|
+
{"step"=>"emit"}.freeze
|
|
32
|
+
].freeze,
|
|
33
|
+
"Comparison" => [
|
|
34
|
+
{"symbol"=>">=", "compares_less_than"=>"true", "compares_equal"=>"false", "negated"=>"true"}.freeze,
|
|
35
|
+
{"symbol"=>"<=", "compares_less_than"=>"true", "compares_equal"=>"true", "negated"=>"false"}.freeze,
|
|
36
|
+
{"symbol"=>"<", "compares_less_than"=>"true", "compares_equal"=>"false", "negated"=>"false"}.freeze,
|
|
37
|
+
{"symbol"=>">", "compares_less_than"=>"true", "compares_equal"=>"true", "negated"=>"true"}.freeze,
|
|
38
|
+
{"symbol"=>"==", "compares_less_than"=>"false", "compares_equal"=>"true", "negated"=>"false"}.freeze,
|
|
39
|
+
{"symbol"=>"!=", "compares_less_than"=>"false", "compares_equal"=>"true", "negated"=>"true"}.freeze
|
|
40
|
+
].freeze,
|
|
41
|
+
"DomainRefusal" => [
|
|
42
|
+
{"name"=>"AbsentArgument"}.freeze,
|
|
43
|
+
{"name"=>"AlreadyExists"}.freeze,
|
|
44
|
+
{"name"=>"AttributeAbsent"}.freeze,
|
|
45
|
+
{"name"=>"EnsuresNotMet"}.freeze,
|
|
46
|
+
{"name"=>"GivenNotMet"}.freeze,
|
|
47
|
+
{"name"=>"InvariantViolation"}.freeze,
|
|
48
|
+
{"name"=>"LifecycleRefused"}.freeze,
|
|
49
|
+
{"name"=>"NotFound"}.freeze,
|
|
50
|
+
{"name"=>"ProjectionAbsent"}.freeze,
|
|
51
|
+
{"name"=>"RemoteRefusal"}.freeze,
|
|
52
|
+
{"name"=>"TypeMismatch"}.freeze,
|
|
53
|
+
{"name"=>"Unauthorized"}.freeze,
|
|
54
|
+
{"name"=>"UnknownArgument"}.freeze,
|
|
55
|
+
{"name"=>"UnknownVerb"}.freeze
|
|
56
|
+
].freeze,
|
|
57
|
+
"EntityDispatchOrder" => [
|
|
58
|
+
{"step"=>"normalize_args"}.freeze,
|
|
59
|
+
{"step"=>"refuse_role_mismatch"}.freeze,
|
|
60
|
+
{"step"=>"resolve_references"}.freeze,
|
|
61
|
+
{"step"=>"hydrate_parent"}.freeze,
|
|
62
|
+
{"step"=>"locate_element"}.freeze,
|
|
63
|
+
{"step"=>"enforce_givens"}.freeze,
|
|
64
|
+
{"step"=>"admissible_transition"}.freeze,
|
|
65
|
+
{"step"=>"apply_mutations"}.freeze,
|
|
66
|
+
{"step"=>"advance_lifecycle"}.freeze,
|
|
67
|
+
{"step"=>"enforce_ensures"}.freeze,
|
|
68
|
+
{"step"=>"enforce_invariants"}.freeze,
|
|
69
|
+
{"step"=>"save"}.freeze,
|
|
70
|
+
{"step"=>"emit"}.freeze
|
|
71
|
+
].freeze,
|
|
72
|
+
"FieldHint" => [
|
|
73
|
+
{"name"=>"email", "pattern"=>"email", "resolves_to"=>"html_type"}.freeze,
|
|
74
|
+
{"name"=>"url", "pattern"=>"\\b(url|uri|website|link)\\b", "resolves_to"=>"html_type"}.freeze,
|
|
75
|
+
{"name"=>"tel", "pattern"=>"phone|\\btel(ephone)?\\b", "resolves_to"=>"html_type"}.freeze,
|
|
76
|
+
{"name"=>"textarea", "pattern"=>"\\b(text|body|note|notes|description|message|comment)\\b", "resolves_to"=>"kind"}.freeze
|
|
77
|
+
].freeze,
|
|
78
|
+
"IncludeHaystack" => [
|
|
79
|
+
{"type"=>"Array", "strategy"=>"membership"}.freeze,
|
|
80
|
+
{"type"=>"String", "strategy"=>"substring"}.freeze
|
|
81
|
+
].freeze,
|
|
82
|
+
"LoadOrder" => [
|
|
83
|
+
{"glob"=>"*.port"}.freeze,
|
|
84
|
+
{"glob"=>"*.adapter"}.freeze,
|
|
85
|
+
{"glob"=>"*.bluebook"}.freeze,
|
|
86
|
+
{"glob"=>"translations/*.bluebook"}.freeze,
|
|
87
|
+
{"glob"=>"*.hecksagon"}.freeze,
|
|
88
|
+
{"glob"=>"*.world"}.freeze
|
|
89
|
+
].freeze,
|
|
90
|
+
"MutationOp" => [
|
|
91
|
+
{"name"=>"set", "sign"=>""}.freeze,
|
|
92
|
+
{"name"=>"append", "sign"=>""}.freeze,
|
|
93
|
+
{"name"=>"increment", "sign"=>"1"}.freeze,
|
|
94
|
+
{"name"=>"decrement", "sign"=>"-1"}.freeze,
|
|
95
|
+
{"name"=>"multiply", "sign"=>""}.freeze,
|
|
96
|
+
{"name"=>"clamp", "sign"=>""}.freeze,
|
|
97
|
+
{"name"=>"remove", "sign"=>""}.freeze,
|
|
98
|
+
{"name"=>"delegate", "sign"=>""}.freeze
|
|
99
|
+
].freeze,
|
|
100
|
+
"NormalisationStrategy" => [
|
|
101
|
+
{"name"=>"collapse_whitespace"}.freeze,
|
|
102
|
+
{"name"=>"replace"}.freeze
|
|
103
|
+
].freeze,
|
|
104
|
+
"Primitive" => [
|
|
105
|
+
{"name"=>"String"}.freeze,
|
|
106
|
+
{"name"=>"Integer"}.freeze,
|
|
107
|
+
{"name"=>"Float"}.freeze,
|
|
108
|
+
{"name"=>"TrueClass"}.freeze,
|
|
109
|
+
{"name"=>"FalseClass"}.freeze
|
|
110
|
+
].freeze,
|
|
111
|
+
"QueryComparator" => [
|
|
112
|
+
{"name"=>"eq"}.freeze,
|
|
113
|
+
{"name"=>"ne"}.freeze,
|
|
114
|
+
{"name"=>"gt"}.freeze,
|
|
115
|
+
{"name"=>"gte"}.freeze,
|
|
116
|
+
{"name"=>"lt"}.freeze,
|
|
117
|
+
{"name"=>"lte"}.freeze,
|
|
118
|
+
{"name"=>"in"}.freeze,
|
|
119
|
+
{"name"=>"contains"}.freeze,
|
|
120
|
+
{"name"=>"none_in_state"}.freeze
|
|
121
|
+
].freeze,
|
|
122
|
+
"RefusalTemplate" => [
|
|
123
|
+
{"refusal"=>"NotFound", "site"=>"creating_no_identity", "template"=>"{command} creates a {aggregate} — pass {identity}:"}.freeze,
|
|
124
|
+
{"refusal"=>"AlreadyExists", "site"=>"creating_duplicate", "template"=>"{command} creates a {aggregate} that already exists — {identity} {offered}"}.freeze,
|
|
125
|
+
{"refusal"=>"AlreadyExists", "site"=>"entity_duplicate", "template"=>"a {entity} already exists on {aggregate} — {identity} {offered}"}.freeze,
|
|
126
|
+
{"refusal"=>"NotFound", "site"=>"acting_no_identity", "template"=>"{command} acts on an existing {aggregate} — pass {identity}:"}.freeze,
|
|
127
|
+
{"refusal"=>"NotFound", "site"=>"record_missing", "template"=>"no {aggregate} with {identity} {offered}"}.freeze,
|
|
128
|
+
{"refusal"=>"NotFound", "site"=>"entity_parent_no_identity", "template"=>"{command} acts on a {aggregate}'s {entity} — pass {identity}:"}.freeze,
|
|
129
|
+
{"refusal"=>"UnknownVerb", "site"=>"entity_unknown", "template"=>"{aggregate} has no entity {entity}"}.freeze,
|
|
130
|
+
{"refusal"=>"NotFound", "site"=>"entity_element_no_identity", "template"=>"{command} acts on one {entity} — pass {identity}:"}.freeze,
|
|
131
|
+
{"refusal"=>"NotFound", "site"=>"entity_element_missing", "template"=>"no {entity} with {identity} {wants} on {aggregate} {parent_id}"}.freeze,
|
|
132
|
+
{"refusal"=>"NotFound", "site"=>"reference_target_missing", "template"=>"no {target} with {heads} {key}"}.freeze,
|
|
133
|
+
{"refusal"=>"NotFound", "site"=>"read_model_reference_missing", "template"=>"no {aggregate} with reference {offered}"}.freeze,
|
|
134
|
+
{"refusal"=>"TypeMismatch", "site"=>"read_model_object_reference", "template"=>"{query} refused — a reference is an id, and {field} arrived as an object"}.freeze,
|
|
135
|
+
{"refusal"=>"UnknownVerb", "site"=>"no_query", "template"=>"{aggregate} has no query {query}"}.freeze,
|
|
136
|
+
{"refusal"=>"UnknownVerb", "site"=>"entity_query_missing", "template"=>"{entity} has no query {query}"}.freeze,
|
|
137
|
+
{"refusal"=>"UnknownVerb", "site"=>"entity_holds_no_list", "template"=>"{aggregate} holds no list of {entity}"}.freeze,
|
|
138
|
+
{"refusal"=>"UnknownVerb", "site"=>"entity_no_command", "template"=>"{entity} has no command {command}"}.freeze,
|
|
139
|
+
{"refusal"=>"UnknownVerb", "site"=>"aggregate_no_command", "template"=>"{aggregate} has no command {command}"}.freeze,
|
|
140
|
+
{"refusal"=>"UnknownVerb", "site"=>"port_no_operation", "template"=>"{port} has no operation {operation}"}.freeze,
|
|
141
|
+
{"refusal"=>"UnknownVerb", "site"=>"no_domain", "template"=>"no domain {domain} loaded (verb {verb})"}.freeze,
|
|
142
|
+
{"refusal"=>"UnknownVerb", "site"=>"no_read_model", "template"=>"{domain} has no read model {query}"}.freeze,
|
|
143
|
+
{"refusal"=>"UnknownVerb", "site"=>"not_fully_qualified", "template"=>"{verb} is not a fully-qualified verb (Domain::Aggregate.Command)"}.freeze,
|
|
144
|
+
{"refusal"=>"UnknownVerb", "site"=>"no_aggregate", "template"=>"{domain} has no aggregate {aggregate}"}.freeze,
|
|
145
|
+
{"refusal"=>"LifecycleRefused", "site"=>"transition_blocked", "template"=>"{command} refused — {field} is {current}, and {command} moves it only from {allowed}"}.freeze,
|
|
146
|
+
{"refusal"=>"TypeMismatch", "site"=>"value_object_shape", "template"=>"{name} is a {type} — pass its fields as an object, not {offered}"}.freeze,
|
|
147
|
+
{"refusal"=>"TypeMismatch", "site"=>"reference_as_object", "template"=>"{command} refused — a reference is an id, and {attribute} arrived as an object{known_by}"}.freeze,
|
|
148
|
+
{"refusal"=>"TypeMismatch", "site"=>"multi_field_scalar", "template"=>"{type} has multiple fields and cannot stand in for a scalar"}.freeze,
|
|
149
|
+
{"refusal"=>"TypeMismatch", "site"=>"composite_identity", "template"=>"{type} is a composite identity — an identity must have exactly one field"}.freeze,
|
|
150
|
+
{"refusal"=>"TypeMismatch", "site"=>"numeric_field", "template"=>"{type}.{field} expects {expected}, got {offered}"}.freeze,
|
|
151
|
+
{"refusal"=>"TypeMismatch", "site"=>"pattern_mismatch", "template"=>"{type}.{field} must match {pattern}, got {offered}"}.freeze,
|
|
152
|
+
{"refusal"=>"TypeMismatch", "site"=>"arithmetic_amount", "template"=>"{op} of {target} needs an Integer, got {offered}"}.freeze,
|
|
153
|
+
{"refusal"=>"TypeMismatch", "site"=>"arithmetic_current", "template"=>"{op} of {target} needs an Integer {target}, got {offered}"}.freeze,
|
|
154
|
+
{"refusal"=>"TypeMismatch", "site"=>"arithmetic_shared_field", "template"=>"{op} of {target} needs a value object with one shared Integer field"}.freeze,
|
|
155
|
+
{"refusal"=>"UnknownArgument", "site"=>"unknown_args", "template"=>"{command} does not declare {unknown} — it takes {declared}"}.freeze,
|
|
156
|
+
{"refusal"=>"AbsentArgument", "site"=>"absent_args", "template"=>"{command} was not given {absent} — it takes {declared}"}.freeze,
|
|
157
|
+
{"refusal"=>"InvariantViolation", "site"=>"closed_set_member", "template"=>"{type} admits {admitted} — got {offered}"}.freeze,
|
|
158
|
+
{"refusal"=>"InvariantViolation", "site"=>"value_object_invariant", "template"=>"{name} invariant violated — {description} (given {offered})"}.freeze,
|
|
159
|
+
{"refusal"=>"InvariantViolation", "site"=>"admits_declared_set", "template"=>"{name} admits {admits} — {admitted} — got {offered}"}.freeze,
|
|
160
|
+
{"refusal"=>"InvariantViolation", "site"=>"undeclared_set", "template"=>"{name} admits {admits}, which this chapter does not declare — a closed set is named Aggregate::SetName, and it must be one the bluebook actually holds"}.freeze,
|
|
161
|
+
{"refusal"=>"Unauthorized", "site"=>"tenant_required", "template"=>"{query} declares authorize with tenant: {field} — pass {field}: to name which {field} this ask is scoped to"}.freeze,
|
|
162
|
+
{"refusal"=>"Unauthorized", "site"=>"role_mismatch", "template"=>"{command} refused — role: {role}, and the caller stated {caller_role}"}.freeze,
|
|
163
|
+
{"refusal"=>"AttributeAbsent", "site"=>"absent_read", "template"=>"{aggregate} {field} is absent on this record — declared, not optional, and added since it was written. Backfill it in a translation (backfill :{field}, default: ...), or declare it optional: true"}.freeze,
|
|
164
|
+
{"refusal"=>"ProjectionAbsent", "site"=>"absent_read", "template"=>"{aggregate} {field} is not yet projected on this record — declared via projects :{field}, but no rebuild sweep has populated it. Run the sweep, or read {reference}.{remote_field} directly if this rule cannot wait"}.freeze
|
|
165
|
+
].freeze,
|
|
166
|
+
"SignTest" => [
|
|
167
|
+
{"name"=>"positive?", "compares_via"=>">"}.freeze,
|
|
168
|
+
{"name"=>"negative?", "compares_via"=>"<"}.freeze,
|
|
169
|
+
{"name"=>"zero?", "compares_via"=>"=="}.freeze
|
|
170
|
+
].freeze,
|
|
171
|
+
"SizedType" => [
|
|
172
|
+
{"type"=>"Array"}.freeze,
|
|
173
|
+
{"type"=>"String"}.freeze,
|
|
174
|
+
{"type"=>"Hash"}.freeze
|
|
175
|
+
].freeze,
|
|
176
|
+
"ToStringType" => [
|
|
177
|
+
{"type"=>"String"}.freeze,
|
|
178
|
+
{"type"=>"Integer"}.freeze,
|
|
179
|
+
{"type"=>"Float"}.freeze,
|
|
180
|
+
{"type"=>"TrueClass"}.freeze,
|
|
181
|
+
{"type"=>"FalseClass"}.freeze,
|
|
182
|
+
{"type"=>"NilClass"}.freeze
|
|
183
|
+
].freeze,
|
|
184
|
+
"Trigger" => [
|
|
185
|
+
{"name"=>"refused"}.freeze
|
|
186
|
+
].freeze,
|
|
187
|
+
"VocabularyName" => [
|
|
188
|
+
|
|
189
|
+
].freeze
|
|
190
|
+
}.freeze
|
|
191
|
+
|
|
192
|
+
# THE TERMS — the first field of each row, which for a
|
|
193
|
+
# one-field vocabulary is the whole of it. Derived ONCE and
|
|
194
|
+
# frozen rather than mapped per call: these are constant
|
|
195
|
+
# tables, and a constant that allocates a new array every
|
|
196
|
+
# time it is read is not one.
|
|
197
|
+
TERMS = TABLES.transform_values { |rows| rows.map { |row| row.values.first }.freeze }.freeze
|
|
198
|
+
|
|
199
|
+
module_function
|
|
200
|
+
|
|
201
|
+
# Refuses an unknown name rather than answering nil: a set
|
|
202
|
+
# the language does not declare is a typo, not an empty set.
|
|
203
|
+
def fetch(name) = TERMS.fetch(name)
|
|
204
|
+
|
|
205
|
+
# The rows whole, for the vocabularies that carry more than
|
|
206
|
+
# a term — Comparison's own algebra, RefusalTemplate's text.
|
|
207
|
+
def rows(name) = TABLES.fetch(name)
|
|
208
|
+
|
|
209
|
+
def symbols(name) = fetch(name).map(&:to_sym)
|
|
210
|
+
|
|
211
|
+
def names = TABLES.keys
|
|
212
|
+
end
|
|
213
|
+
end
|
data/lib/hecks.rb
CHANGED
|
@@ -1,6 +1,118 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative
|
|
1
|
+
require_relative "hecks/version"
|
|
2
|
+
# The closed sets the runtime computes with, generated from
|
|
3
|
+
# vocabulary.bluebook. Plain data, required first, because some of
|
|
4
|
+
# them are read while a bluebook is still being parsed.
|
|
5
|
+
require_relative "hecks/vocabulary"
|
|
6
|
+
require_relative "hecks/rendering"
|
|
7
|
+
require_relative "hecks/naming"
|
|
8
|
+
require_relative "hecks/fqn"
|
|
9
|
+
require_relative "hecks/freezer"
|
|
10
|
+
require_relative "hecks/construct"
|
|
11
|
+
# Before `bluebook` — every construct under `Bluebook` includes or
|
|
12
|
+
# extends this to declare what it emits.
|
|
13
|
+
require_relative "hecks/ir"
|
|
14
|
+
require_relative "hecks/literal"
|
|
15
|
+
require_relative "hecks/facade"
|
|
16
|
+
require_relative "hecks/query_specification"
|
|
17
|
+
|
|
18
|
+
require_relative "hecks/ports"
|
|
19
|
+
require_relative "hecks/bluebook"
|
|
20
|
+
require_relative "hecks/router"
|
|
21
|
+
|
|
22
|
+
require_relative "hecks/runtime"
|
|
23
|
+
require_relative "hecks/translation"
|
|
24
|
+
require_relative "hecks/adapters"
|
|
25
|
+
require_relative "hecks/projector"
|
|
26
|
+
# AFTER the projector registry and its `Target` mixin are both real —
|
|
27
|
+
# every target registers itself as it loads, so this require IS the
|
|
28
|
+
# installation of them.
|
|
29
|
+
require_relative "hecks/projections"
|
|
30
|
+
# AFTER `Projector` (dispatches against the `:cli` projection) and
|
|
31
|
+
# `Ports::Clock` (fills a staleness rule's `now` at the door) both exist.
|
|
32
|
+
require_relative "hecks/facade/cli_door"
|
|
33
|
+
require_relative "hecks/facade/cli_runner"
|
|
34
|
+
require_relative "hecks/framework"
|
|
35
|
+
require_relative "hecks/embryonaut_bluebook"
|
|
36
|
+
|
|
37
|
+
module Hecks
|
|
38
|
+
class LoadOutsideBoot < StandardError; end
|
|
39
|
+
|
|
40
|
+
class << self
|
|
41
|
+
attr_reader :current_registry
|
|
42
|
+
|
|
43
|
+
# The loading words below collect into the registry the RUNTIME is
|
|
44
|
+
# holding open ; booting and that ambient state belong to the runtime
|
|
45
|
+
# layer, so this module is their facade and Hecks::Runtime is where
|
|
46
|
+
# they live.
|
|
47
|
+
# `install_facade:` — see Runtime::Loader.boot. Defaults on; a caller
|
|
48
|
+
# that only dispatches by FQN string can skip the global sugar.
|
|
49
|
+
#
|
|
50
|
+
# `environment:` — RECOVERED, not new: this parameter (and the
|
|
51
|
+
# `environments/<name>.hecksagon` / `.world` overlay it loads —
|
|
52
|
+
# see Adapters::Folder#load_domain) existed on a prior commit of
|
|
53
|
+
# this repo (933d1dd), was vendored out to a real consumer
|
|
54
|
+
# (lifeadelics/domain), and was then lost from this repo's own
|
|
55
|
+
# history (no branch here reaches that commit). Ported forward
|
|
56
|
+
# from the consumer's vendor snapshot — the only surviving copy —
|
|
57
|
+
# and generalized: the original only loaded a `.hecksagon`
|
|
58
|
+
# overlay; this also loads a same-named `.world` overlay, both
|
|
59
|
+
# merged into the base rather than replacing it (Registry#add_hecksagon
|
|
60
|
+
# / #add_world). hecks never reads ENV itself — a caller
|
|
61
|
+
# resolves its own env var name and passes the resulting string
|
|
62
|
+
# straight through, e.g. `Hecks.boot(path, environment:
|
|
63
|
+
# ENV.fetch("MYAPP_ENV", "development"))`.
|
|
64
|
+
def boot(path, shared: nil, install_facade: true, environment: nil)
|
|
65
|
+
Runtime.boot(path, shared: shared, install_facade: install_facade, environment: environment)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# `paths` — an explicit list of files to boot (a `.bluebook`, its
|
|
69
|
+
# `.hecksagon`, optionally a `.world`), loaded in place from wherever
|
|
70
|
+
# they actually live — see Runtime::Loader.boot_files's own header for
|
|
71
|
+
# why this exists beside `boot` rather than as a special case of it.
|
|
72
|
+
def boot_files(paths, shared: nil, install_facade: true, environment: nil)
|
|
73
|
+
Runtime.boot_files(paths, shared: shared, install_facade: install_facade, environment: environment)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def with_registry(registry, &block) = Runtime.with_registry(registry, &block)
|
|
77
|
+
|
|
78
|
+
def current_registry = Runtime.current_registry
|
|
79
|
+
|
|
80
|
+
# Bind who is dispatching for the duration of the block — checked
|
|
81
|
+
# against a command's declared `role`, if it has one. Unbound (the
|
|
82
|
+
# default), a command's role stays exactly what it is without this:
|
|
83
|
+
# decoration.
|
|
84
|
+
#
|
|
85
|
+
# `actor_id` is OPTIONAL — a caller naming only a role is checked by
|
|
86
|
+
# string equality against the command's own `role`, exactly as
|
|
87
|
+
# before. A caller that also names WHO it is lets the check run
|
|
88
|
+
# against a real Governance `RoleAssignment` instead, once the
|
|
89
|
+
# command's domain has Governance attached — see
|
|
90
|
+
# `CommandRules::Authorization`'s own header.
|
|
91
|
+
def as_caller(role:, actor_id: nil, &block) = Runtime.as_caller(role: role, actor_id: actor_id, &block)
|
|
92
|
+
|
|
93
|
+
def bluebook(name, version: nil, &block)
|
|
94
|
+
collect(:add_bluebook, Bluebook::DSL::BluebookBuilder.build(name, version: version, &block))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def hecksagon(name, &block) = collect(:add_hecksagon, Bluebook::DSL::HecksagonBuilder.build(name, &block))
|
|
98
|
+
def port(name, &block) = collect(:add_port, Bluebook::DSL::PortBuilder.build(name, &block))
|
|
99
|
+
def adapter(name, &block) = collect(:add_adapter, Bluebook::DSL::AdapterBuilder.build(name, &block))
|
|
100
|
+
def world(name, &block) = collect(:add_world, Bluebook::DSL::WorldBuilder.build(name, &block))
|
|
101
|
+
|
|
102
|
+
def data_translation(name, from:, to:, &block)
|
|
103
|
+
collect(:add_translation, Bluebook::DSL::TranslationBuilder.build(name, from: from, to: to, &block))
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
def collect(method, item)
|
|
109
|
+
unless Runtime.current_registry
|
|
110
|
+
raise LoadOutsideBoot,
|
|
111
|
+
"declaration loaded outside a boot — use Hecks.boot(path) rather than requiring the file directly"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
Runtime.current_registry.public_send(method, item)
|
|
115
|
+
item
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|