hecks 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/hecks/adapters/driven/claude_code.rb +65 -0
- data/lib/hecks/adapters/driven/folder.rb +73 -0
- data/lib/hecks/adapters/driven/google_authentication.rb +25 -4
- data/lib/hecks/adapters/driven/governance_authorization.rb +57 -6
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -0
- data/lib/hecks/adapters/driven/heki/saga_store.rb +53 -7
- data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +93 -2
- data/lib/hecks/adapters/driven/identity_registry.rb +10 -0
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +22 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +35 -7
- data/lib/hecks/adapters/driven/lambda.rb +43 -2
- data/lib/hecks/adapters/driven/local_storage.rb +67 -1
- data/lib/hecks/adapters/driven/memory.rb +13 -13
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +20 -0
- data/lib/hecks/adapters/driven/prism.rb +46 -7
- data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
- data/lib/hecks/adapters/driven/system_clock.rb +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
- data/lib/hecks/adapters/driven.rb +2 -0
- data/lib/hecks/adapters/driving/github_webhook.rb +14 -1
- data/lib/hecks/behaviors/dsl.rb +58 -0
- data/lib/hecks/behaviors/expectations.rb +168 -7
- data/lib/hecks/behaviors/ir.rb +11 -0
- data/lib/hecks/behaviors/rspec.rb +8 -0
- data/lib/hecks/behaviors/runner.rb +19 -0
- data/lib/hecks/behaviors.rb +8 -0
- data/lib/hecks/bluebook/aggregate.rb +30 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +7 -0
- data/lib/hecks/bluebook/assembly/build.rb +25 -4
- data/lib/hecks/bluebook/assembly/contract.rb +66 -14
- data/lib/hecks/bluebook/assembly/contracts.rb +24 -19
- data/lib/hecks/bluebook/assembly/marks.rb +143 -14
- data/lib/hecks/bluebook/assembly/specializer.rb +19 -0
- data/lib/hecks/bluebook/assembly.rb +25 -7
- data/lib/hecks/bluebook/attribute.rb +17 -3
- data/lib/hecks/bluebook/behaviour/aggregate.rb +32 -0
- data/lib/hecks/bluebook/behaviour/attribute.rb +13 -0
- data/lib/hecks/bluebook/behaviour/chapter.rb +55 -2
- data/lib/hecks/bluebook/behaviour/command.rb +33 -3
- data/lib/hecks/bluebook/behaviour/domain_port.rb +20 -0
- data/lib/hecks/bluebook/behaviour/entity.rb +12 -0
- data/lib/hecks/bluebook/behaviour/hexagon.rb +26 -0
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +23 -2
- data/lib/hecks/bluebook/behaviour/policy.rb +34 -9
- data/lib/hecks/bluebook/behaviour/process_manager.rb +32 -1
- data/lib/hecks/bluebook/behaviour/query.rb +5 -0
- data/lib/hecks/bluebook/behaviour/read_model.rb +21 -0
- data/lib/hecks/bluebook/behaviour/traits.rb +36 -0
- data/lib/hecks/bluebook/behaviour/value_object.rb +17 -5
- data/lib/hecks/bluebook/chapter.rb +23 -0
- data/lib/hecks/bluebook/command.rb +53 -8
- data/lib/hecks/bluebook/domain_port.rb +25 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +196 -37
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +79 -42
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +59 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +385 -53
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +135 -21
- data/lib/hecks/bluebook/dsl/command_builder.rb +189 -27
- data/lib/hecks/bluebook/dsl/entity_builder.rb +139 -9
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +41 -0
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +23 -6
- data/lib/hecks/bluebook/dsl/policy_builder.rb +60 -19
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +79 -15
- data/lib/hecks/bluebook/dsl/query_builder.rb +33 -4
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +102 -27
- data/lib/hecks/bluebook/dsl/rule_reference.rb +60 -8
- data/lib/hecks/bluebook/dsl/translation_builder.rb +146 -40
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +55 -7
- data/lib/hecks/bluebook/entity.rb +29 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +115 -23
- data/lib/hecks/bluebook/expression/ast_reader.rb +29 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +48 -9
- data/lib/hecks/bluebook/expression/evaluator.rb +207 -29
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +36 -0
- data/lib/hecks/bluebook/expression/resolver.rb +320 -77
- data/lib/hecks/bluebook/hexagon.rb +34 -0
- data/lib/hecks/bluebook/lifecycle.rb +11 -0
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/judge.rb +30 -26
- data/lib/hecks/bluebook/meta_validator/plan.rb +43 -8
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/readings.rb +157 -7
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -4
- data/lib/hecks/bluebook/meta_validator/shapes.rb +141 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +149 -23
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +5 -4
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator.rb +180 -84
- data/lib/hecks/bluebook/model_check.rb +268 -24
- data/lib/hecks/bluebook/pattern_subset.rb +23 -1
- data/lib/hecks/bluebook/process_manager.rb +13 -0
- data/lib/hecks/bluebook/project_discovery.rb +5 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +44 -0
- data/lib/hecks/bluebook/query.rb +27 -0
- data/lib/hecks/bluebook/read_model.rb +21 -1
- data/lib/hecks/bluebook/reference.rb +21 -8
- data/lib/hecks/bluebook/smoke_test.rb +29 -6
- data/lib/hecks/bluebook/synthesizer.rb +34 -0
- data/lib/hecks/bluebook/translation.rb +30 -1
- data/lib/hecks/bluebook/value_object.rb +23 -5
- data/lib/hecks/bluebook.rb +3 -4
- data/lib/hecks/codemod.rb +107 -20
- data/lib/hecks/construct.rb +15 -1
- data/lib/hecks/corpus.rb +146 -25
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/doc/reference.rb +185 -16
- data/lib/hecks/embryonaut_bluebook.rb +32 -9
- data/lib/hecks/facade/handle.rb +76 -3
- data/lib/hecks/facade/surface/aggregate_door.rb +8 -0
- data/lib/hecks/forms/field_shape.rb +3 -0
- data/lib/hecks/forms/page.rb +14 -0
- data/lib/hecks/forms/port_argument.rb +12 -0
- data/lib/hecks/forms/query_form_renderer.rb +63 -0
- data/lib/hecks/forms/record_renderer.rb +58 -0
- data/lib/hecks/forms/record_table.rb +27 -0
- data/lib/hecks/forms/reference_options.rb +24 -0
- data/lib/hecks/forms/value_object_shape.rb +10 -0
- data/lib/hecks/fqn.rb +58 -0
- data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
- data/lib/hecks/framework/oidc.json +15 -0
- data/lib/hecks/framework.rb +43 -20
- data/lib/hecks/freezer.rb +17 -1
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +159 -24
- data/lib/hecks/fuzzing/combination_miner.rb +59 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +109 -8
- data/lib/hecks/fuzzing/coverage_campaign.rb +56 -13
- data/lib/hecks/fuzzing/differential.rb +34 -0
- data/lib/hecks/fuzzing/domain_generator.rb +188 -11
- data/lib/hecks/fuzzing/era_boundary.rb +45 -15
- data/lib/hecks/fuzzing/form_census.rb +86 -0
- data/lib/hecks/fuzzing/generated_domain_check.rb +76 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +39 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +79 -22
- data/lib/hecks/fuzzing/nondeterministic.rb +13 -1
- data/lib/hecks/fuzzing/persistence_parity.rb +95 -3
- data/lib/hecks/fuzzing/properties/corrections.rb +25 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +158 -14
- data/lib/hecks/fuzzing/properties/guards.rb +44 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +48 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +18 -0
- data/lib/hecks/fuzzing/properties/outbox.rb +49 -11
- data/lib/hecks/fuzzing/properties/querying.rb +68 -14
- data/lib/hecks/fuzzing/properties.rb +24 -15
- data/lib/hecks/fuzzing/qa_settings.rb +12 -0
- data/lib/hecks/fuzzing/replay.rb +137 -29
- data/lib/hecks/fuzzing/rotation_priority.rb +41 -21
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +46 -20
- data/lib/hecks/fuzzing/self_consistency.rb +189 -40
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +12 -6
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +1 -1
- data/lib/hecks/fuzzing/sequence_generator.rb +47 -0
- data/lib/hecks/fuzzing/shrinker.rb +114 -2
- data/lib/hecks/fuzzing/structural_skips.rb +18 -4
- data/lib/hecks/fuzzing/sweep_depth.rb +8 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +61 -14
- data/lib/hecks/fuzzing/value_generator.rb +98 -10
- data/lib/hecks/grammar/evolve.rb +178 -2
- data/lib/hecks/grammar.rb +46 -0
- data/lib/hecks/ir.rb +38 -7
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
- data/lib/hecks/literal.rb +32 -0
- data/lib/hecks/naming.rb +88 -7
- data/lib/hecks/ports/access_control.rb +5 -10
- data/lib/hecks/ports/authorization.rb +3 -6
- data/lib/hecks/ports/identity_assignment.rb +1 -2
- data/lib/hecks/ports/identity_resolution.rb +1 -2
- data/lib/hecks/ports/key_vault.port +6 -0
- data/lib/hecks/ports/key_vault.rb +58 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +6 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +38 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +40 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +92 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +8 -0
- data/lib/hecks/ports/query/in_memory.rb +39 -6
- data/lib/hecks/ports/query/ordering.rb +15 -0
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projections/bootstrap_table.rb +43 -8
- data/lib/hecks/projections/diagrams.rb +243 -7
- data/lib/hecks/projections/glossary/html.rb +88 -0
- data/lib/hecks/projections/glossary/markdown.rb +47 -0
- data/lib/hecks/projections/glossary/mermaid.rb +48 -0
- data/lib/hecks/projections/glossary/sentences.rb +105 -0
- data/lib/hecks/projections/glossary.rb +161 -19
- data/lib/hecks/projections/model/deviations.rb +44 -0
- data/lib/hecks/projections/model.rb +51 -1
- data/lib/hecks/projections/oidc.rb +40 -3
- data/lib/hecks/projections/parser_table.rb +49 -8
- data/lib/hecks/projections/reference.rb +12 -0
- data/lib/hecks/projections/rust_vocabulary.rb +219 -16
- data/lib/hecks/projections/shape.rb +8 -0
- data/lib/hecks/projections/statements.rb +63 -16
- data/lib/hecks/projections/vocabulary.rb +17 -0
- data/lib/hecks/projector/cli_projector.rb +218 -10
- data/lib/hecks/projector/docs_projector.rb +145 -19
- data/lib/hecks/projector/exporter.rb +65 -11
- data/lib/hecks/projector/ir_projector.rb +6 -0
- data/lib/hecks/projector/narrate_projector.rb +136 -15
- data/lib/hecks/projector/target.rb +29 -5
- data/lib/hecks/projector.rb +74 -6
- data/lib/hecks/query_ir.rb +47 -0
- data/lib/hecks/query_specification/common/null_policy.rb +5 -3
- data/lib/hecks/rendering.rb +6 -0
- data/lib/hecks/router/namespace_installer.rb +13 -0
- data/lib/hecks/router.rb +55 -0
- data/lib/hecks/runtime/aggregate_lock.rb +9 -0
- data/lib/hecks/runtime/boot_gates.rb +18 -0
- data/lib/hecks/runtime/caller.rb +32 -0
- data/lib/hecks/runtime/capability_graph.rb +11 -0
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +23 -21
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +14 -15
- data/lib/hecks/runtime/command_interpreter.rb +42 -17
- data/lib/hecks/runtime/command_rules/admissibility.rb +165 -14
- data/lib/hecks/runtime/command_rules/arithmetic.rb +17 -5
- data/lib/hecks/runtime/command_rules/references.rb +118 -28
- data/lib/hecks/runtime/dependency_planning.rb +45 -0
- data/lib/hecks/runtime/dispatcher.rb +28 -50
- data/lib/hecks/runtime/entity_element.rb +161 -8
- data/lib/hecks/runtime/entity_interpreter.rb +44 -9
- data/lib/hecks/runtime/errors.rb +18 -4
- data/lib/hecks/runtime/event.rb +10 -5
- data/lib/hecks/runtime/identity.rb +71 -3
- data/lib/hecks/runtime/instance.rb +67 -7
- data/lib/hecks/runtime/interpreting.rb +13 -5
- data/lib/hecks/runtime/invocation.rb +118 -36
- data/lib/hecks/runtime/loader.rb +94 -8
- data/lib/hecks/runtime/outbox.rb +145 -7
- data/lib/hecks/runtime/policy_interpreter.rb +22 -9
- data/lib/hecks/runtime/port_operation_interpreter.rb +20 -0
- data/lib/hecks/runtime/query_interpreter.rb +40 -12
- data/lib/hecks/runtime/reaction_invocation.rb +53 -8
- data/lib/hecks/runtime/read_model_interpreter.rb +23 -7
- data/lib/hecks/runtime/rebuild_sweep.rb +28 -0
- data/lib/hecks/runtime/reference_hop.rb +42 -0
- data/lib/hecks/runtime/refusal_wording.rb +50 -0
- data/lib/hecks/runtime/registry/saga_persistence.rb +11 -0
- data/lib/hecks/runtime/registry/verification.rb +119 -4
- data/lib/hecks/runtime/registry.rb +157 -4
- data/lib/hecks/runtime/remote_dispatcher.rb +92 -6
- data/lib/hecks/runtime/routing.rb +27 -2
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +10 -12
- data/lib/hecks/runtime/saga_interpreter.rb +27 -13
- data/lib/hecks/runtime/tenant_check.rb +26 -6
- data/lib/hecks/runtime/tenant_scope.rb +18 -0
- data/lib/hecks/runtime/value/coercion.rb +255 -33
- data/lib/hecks/runtime/value/entity_list_coercion.rb +102 -30
- data/lib/hecks/runtime/value.rb +50 -0
- data/lib/hecks/runtime.rb +32 -0
- data/lib/hecks/storehouse.rb +305 -9
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks.rb +79 -1
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -0
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +12 -2
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +29 -5
- metadata +11 -5
- data/lib/hecks/codemod/legacy_dispatch_args.rb +0 -299
- data/lib/hecks/codemod/legacy_dispatch_recorder.rb +0 -186
- data/lib/hecks/deprecation.rb +0 -95
|
@@ -8,6 +8,8 @@ module Hecks
|
|
|
8
8
|
# IR it stores must equal the IR the DSL builder produces". The judge is the
|
|
9
9
|
# first half. This reads the records back and assembles `to_h`.
|
|
10
10
|
#
|
|
11
|
+
# ## One table, both directions
|
|
12
|
+
#
|
|
11
13
|
# It is the inverse of the walk and shares its plan: the walk reads a node's
|
|
12
14
|
# lists through the command that appends to each, and this reads them back out
|
|
13
15
|
# of the rows those commands wrote. The retired `experiment/replay.rb` needed
|
|
@@ -17,6 +19,12 @@ module Hecks
|
|
|
17
19
|
# This file is the traversal. The hashes at its tips, and the encodings they
|
|
18
20
|
# undo, are in Shapes.
|
|
19
21
|
#
|
|
22
|
+
# The parent key of every level is read from the language's own plan, the same
|
|
23
|
+
# Plan the walk dispatches from — so the two directions really are one table,
|
|
24
|
+
# which is what the header above has always claimed.
|
|
25
|
+
#
|
|
26
|
+
# ## Reads level by level, not through the read model
|
|
27
|
+
#
|
|
20
28
|
# It reads level by level, through `DeclaredIn`, and not through the read
|
|
21
29
|
# model — which is the difference between a reconstruction that can be the
|
|
22
30
|
# source and one that can only be a check.
|
|
@@ -31,9 +39,7 @@ module Hecks
|
|
|
31
39
|
# `DeclaredIn` preserves it (spec/executes_spec says so), so this asks each
|
|
32
40
|
# level for its own children rather than filtering one sorted gather.
|
|
33
41
|
#
|
|
34
|
-
#
|
|
35
|
-
# Plan the walk dispatches from — so the two directions really are one table,
|
|
36
|
-
# which is what the header above has always claimed.
|
|
42
|
+
# ## What it cannot rebuild
|
|
37
43
|
#
|
|
38
44
|
# What it cannot rebuild matters as much as what it can, and
|
|
39
45
|
# spec/round_trip_spec pins the difference as an exact set: a field the language
|
|
@@ -43,13 +49,27 @@ module Hecks
|
|
|
43
49
|
include Readings
|
|
44
50
|
include Shapes
|
|
45
51
|
|
|
52
|
+
# Reads one judged chapter back out of `runtime` and assembles it.
|
|
53
|
+
#
|
|
54
|
+
# @param runtime [Runtime::Dispatcher] the dispatcher holding the
|
|
55
|
+
# judge's own records
|
|
56
|
+
# @param chapter [String] the chapter's own `hecks_name`
|
|
57
|
+
# @return [Hash{Symbol => Object}] the reconstructed IR, in the
|
|
58
|
+
# shape the DSL builder's own `to_h` produces
|
|
59
|
+
# @raise [Runtime::NotFound] if `runtime` holds no bluebook named
|
|
60
|
+
# `chapter`
|
|
46
61
|
def self.of(runtime, chapter) = new(runtime, chapter).to_h
|
|
47
62
|
|
|
63
|
+
# @param runtime [Runtime::Dispatcher] the dispatcher holding the
|
|
64
|
+
# judge's own records
|
|
65
|
+
# @param chapter [String] the chapter's own `hecks_name`
|
|
66
|
+
# @raise [Runtime::NotFound] if `runtime` holds no bluebook named
|
|
67
|
+
# `chapter`
|
|
48
68
|
def initialize(runtime, chapter)
|
|
49
69
|
@runtime = runtime
|
|
50
70
|
@plan = Plan.for(MetaValidator.grammar_registry)
|
|
51
71
|
@chapter = runtime.query("Bluebook::Bluebook.Called", name: { value: chapter }).first or
|
|
52
|
-
raise NotFound, "the meta-domain holds no bluebook called #{chapter.inspect}"
|
|
72
|
+
raise Runtime::NotFound, "the meta-domain holds no bluebook called #{chapter.inspect}"
|
|
53
73
|
end
|
|
54
74
|
|
|
55
75
|
def to_h
|
|
@@ -14,6 +14,14 @@ module Hecks
|
|
|
14
14
|
module Shapes
|
|
15
15
|
# The type came in as the id of what it names, so it goes back out as the
|
|
16
16
|
# name — or, for another aggregate's head, as the encoding the IR spells.
|
|
17
|
+
#
|
|
18
|
+
# @param field [Hash{Symbol => Object}] one dispatched attribute row
|
|
19
|
+
# @param aggregate_id [String] the owning aggregate's own id, to
|
|
20
|
+
# strip from an owned value-object type
|
|
21
|
+
# @return [Hash{Symbol => Object}] `:name` (`Symbol`, `nil`),
|
|
22
|
+
# `:type` (`String`), `:list`/`:optional` (`Boolean`), `:default`
|
|
23
|
+
# (decoded literal, `nil` when absent), `:pattern`/`:admits`/
|
|
24
|
+
# `:relationship` (`String`, `nil` when absent)
|
|
17
25
|
def attribute(field, aggregate_id)
|
|
18
26
|
type = text(field[:type]).to_s
|
|
19
27
|
|
|
@@ -39,6 +47,12 @@ module Hecks
|
|
|
39
47
|
# A type this aggregate owns, offered as its id and read back as its name.
|
|
40
48
|
# Prefixed with the owner and the identity join, because that is the value
|
|
41
49
|
# object's identity — the aggregate it belongs to, then its name.
|
|
50
|
+
#
|
|
51
|
+
# @param type [String] the attribute's own dispatched `type` cell
|
|
52
|
+
# @param aggregate_id [String] the owning aggregate's own id
|
|
53
|
+
# @return [String, nil] `type` with the owner's own identity prefix
|
|
54
|
+
# stripped, or `nil` if `type` does not carry that prefix (an
|
|
55
|
+
# ordinary type name, or a cross-aggregate reference)
|
|
42
56
|
def owned_type(type, aggregate_id)
|
|
43
57
|
prefix = Naming.identity([aggregate_id, ""])
|
|
44
58
|
return nil unless type.start_with?(prefix)
|
|
@@ -49,6 +63,9 @@ module Hecks
|
|
|
49
63
|
# One part of an identity, read back as the path it went in as. The inverse
|
|
50
64
|
# of `Marks#identity_path`, and named the same so the two directions read
|
|
51
65
|
# as one table.
|
|
66
|
+
#
|
|
67
|
+
# @param part [Hash{Symbol => Object}] one dispatched identity-part row
|
|
68
|
+
# @return [String] the identity part's own path text
|
|
52
69
|
def identity_path(part) = text(part[:value]).to_s
|
|
53
70
|
|
|
54
71
|
# An argument, a parameter, a piece's attribute — the three places an
|
|
@@ -76,6 +93,15 @@ module Hecks
|
|
|
76
93
|
# ordinary type names something declared beside it (`Money`,
|
|
77
94
|
# `AccountNumber`) and never carries a chapter, while a head's id
|
|
78
95
|
# always does.
|
|
96
|
+
#
|
|
97
|
+
# @param field [Hash{Symbol => Object}] one dispatched attribute,
|
|
98
|
+
# argument, or parameter row
|
|
99
|
+
# @param aggregate_id [String, nil] the piece's own owning
|
|
100
|
+
# aggregate id, passed only for a piece's own attribute
|
|
101
|
+
# @return [Hash{Symbol => Object}] `:name` (`Symbol`, `nil`),
|
|
102
|
+
# `:type` (`String`), `:list`/`:optional` (`Boolean`), `:default`
|
|
103
|
+
# (decoded literal, `nil` when absent), `:pattern`/`:admits`/
|
|
104
|
+
# `:relationship` (`String`, `nil` when absent)
|
|
79
105
|
def shape_field(field, aggregate_id = nil)
|
|
80
106
|
return attribute(field, aggregate_id) if aggregate_id
|
|
81
107
|
|
|
@@ -102,6 +128,11 @@ module Hecks
|
|
|
102
128
|
# is a real regex (it matches everything), so keeping it would turn
|
|
103
129
|
# "no pattern" into "a pattern that always passes" and quietly cost the
|
|
104
130
|
# IR its round trip.
|
|
131
|
+
#
|
|
132
|
+
# @param text [String, nil] a dispatched cell's own already-unwrapped
|
|
133
|
+
# text
|
|
134
|
+
# @return [String, nil] `text`, or `nil` when `text` is `nil` or
|
|
135
|
+
# empty
|
|
105
136
|
def presence(text)
|
|
106
137
|
value = text.to_s
|
|
107
138
|
value.empty? ? nil : value
|
|
@@ -113,13 +144,29 @@ module Hecks
|
|
|
113
144
|
# String, Integer, Float, TrueClass, FalseClass — plus a symbol, and an
|
|
114
145
|
# object literal, which is what `to: { value: "good" }` is: a value object's
|
|
115
146
|
# fields written inline.
|
|
147
|
+
#
|
|
148
|
+
# @param text [String, nil] the self-describing wire spelling
|
|
149
|
+
# `Readings#encode_literal` wrote
|
|
150
|
+
# @return [Object] `nil`, `true`, `false`, `Integer`, `Float`,
|
|
151
|
+
# `Symbol`, `Hecks::StateRef`, `String`, `Hash`, or `Array` — or
|
|
152
|
+
# `text` itself, stripped, when it matches no known spelling
|
|
116
153
|
def decode_literal(text) = Literal.read(text)
|
|
117
154
|
|
|
155
|
+
# Reads a rule's plain description/canonical pair, for a
|
|
156
|
+
# `given`/`invariant`/`ensures`.
|
|
157
|
+
#
|
|
158
|
+
# @param row [Hash{Symbol => Object}] one dispatched rule row
|
|
159
|
+
# @return [Hash{Symbol => String}] `:description` and `:canonical`
|
|
118
160
|
def rule(row) = { description: text(row[:description]), canonical: text(row[:canonical]) }
|
|
119
161
|
|
|
120
162
|
# S12, ADR 0025 — `projects :name, from: :"reference.remote_field"`,
|
|
121
163
|
# read back the same three plain identifiers `rule` above reads
|
|
122
164
|
# description/canonical as.
|
|
165
|
+
#
|
|
166
|
+
# @param row [Hash{Symbol => Object}] one dispatched projected-field
|
|
167
|
+
# row
|
|
168
|
+
# @return [Hash{Symbol => String}] `:name`, `:reference`, and
|
|
169
|
+
# `:remote_field`
|
|
123
170
|
def projected_field(row)
|
|
124
171
|
{ name: text(row[:name]), reference: text(row[:reference]), remote_field: text(row[:remote_field]) }
|
|
125
172
|
end
|
|
@@ -128,16 +175,30 @@ module Hecks
|
|
|
128
175
|
# does — an object literal, self-describing via Hecks::Literal — one
|
|
129
176
|
# level up: a whole keyword's argument rather than an attribute's
|
|
130
177
|
# `default:`.
|
|
178
|
+
#
|
|
179
|
+
# @param row [Hash{Symbol => Object}] a dispatched row carrying a
|
|
180
|
+
# `:provenance` cell
|
|
181
|
+
# @return [Object] the decoded `from:` object literal, or `nil` when
|
|
182
|
+
# absent
|
|
131
183
|
def provenance(row) = decode_literal(text(row[:provenance]))
|
|
132
184
|
|
|
133
185
|
# `command "Debit", from: "open"` — the same literal encoding
|
|
134
186
|
# `provenance`/`default:` already ride (S10, ADR 0025), one
|
|
135
187
|
# state or an array of them, or nil for a command with no
|
|
136
188
|
# lifecycle guard.
|
|
189
|
+
#
|
|
190
|
+
# @param row [Hash{Symbol => Object}] a dispatched row carrying a
|
|
191
|
+
# `:from` cell
|
|
192
|
+
# @return [String, Array<String>, nil] the decoded lifecycle guard
|
|
193
|
+
# state(s), or `nil` for a command with no lifecycle guard
|
|
137
194
|
def from(row) = decode_literal(text(row[:from]))
|
|
138
195
|
|
|
139
196
|
# A flag is held as text ("true"/"false") and emitted as a boolean —
|
|
140
197
|
# `Policy#expect_undelivered` on the wire.
|
|
198
|
+
#
|
|
199
|
+
# @param row [Hash{Symbol => Object}] a dispatched row carrying an
|
|
200
|
+
# `:expect_undelivered` cell
|
|
201
|
+
# @return [Boolean] whether the flag reads `"true"`
|
|
141
202
|
def expect_undelivered?(row) = text(row[:expect_undelivered]).to_s == "true"
|
|
142
203
|
|
|
143
204
|
# The option rows, gathered back into the shapes `extra_options_to_h` spells.
|
|
@@ -146,12 +207,25 @@ module Hecks
|
|
|
146
207
|
# several groups told apart by `at`. Grouping by option name and then by `at`
|
|
147
208
|
# rebuilds both without either knowing which options exist — the whole point
|
|
148
209
|
# of holding them as an open map.
|
|
210
|
+
#
|
|
211
|
+
# @param row [Hash{Symbol => Object}] a dispatched row carrying an
|
|
212
|
+
# `:options` list
|
|
213
|
+
# @return [Hash{Symbol => Object}] one entry per option name; each
|
|
214
|
+
# value is `gathered`'s own return for that option's own parts
|
|
149
215
|
def options_of(row)
|
|
150
216
|
Array(row[:options])
|
|
151
217
|
.group_by { |part| text(part[:option]) }
|
|
152
218
|
.to_h { |option, parts| [option.to_sym, gathered(parts)] }
|
|
153
219
|
end
|
|
154
220
|
|
|
221
|
+
# Groups one option's own dispatched parts back into a single
|
|
222
|
+
# binding, or several `at`-keyed groups for a repeated option.
|
|
223
|
+
#
|
|
224
|
+
# @param parts [Array<Hash{Symbol => Object}>] one option's own
|
|
225
|
+
# dispatched key/value/at rows
|
|
226
|
+
# @return [Hash{Symbol => String}, Array<Hash{Symbol => String}>]
|
|
227
|
+
# a single `key => value` hash when no part carries `at`;
|
|
228
|
+
# otherwise an array of such hashes, one per distinct `at`
|
|
155
229
|
def gathered(parts)
|
|
156
230
|
repeated, single = parts.partition { |part| !text(part[:at]).to_s.empty? }
|
|
157
231
|
return single.to_h { |part| [text(part[:key]).to_sym, text(part[:value])] } if repeated.empty?
|
|
@@ -170,11 +244,20 @@ module Hecks
|
|
|
170
244
|
# back out as the plain string "ceiling", indistinguishable from a
|
|
171
245
|
# literal of the same name. One decode, at the one place that builds
|
|
172
246
|
# the object every comparator actually reads.
|
|
247
|
+
#
|
|
248
|
+
# @param row [Hash{Symbol => Object}] one dispatched where-clause row
|
|
249
|
+
# @return [Hash{Symbol => String}] `:field`, `:op`, and `:value`
|
|
250
|
+
# (raw text, undecoded)
|
|
173
251
|
def where_clause(row)
|
|
174
252
|
{ field: text(row[:field]), op: text(row[:op]), value: text(row[:value]) }
|
|
175
253
|
end
|
|
176
254
|
|
|
177
255
|
# One object in the IR, two fields in the language.
|
|
256
|
+
#
|
|
257
|
+
# @param row [Hash{Symbol => Object}] a dispatched row carrying
|
|
258
|
+
# `:order_field`/`:order_way` cells
|
|
259
|
+
# @return [Hash{Symbol => String}, nil] `:field` and `:direction`,
|
|
260
|
+
# or `nil` when no `order_field` was declared
|
|
178
261
|
def order_by(row)
|
|
179
262
|
field = text(row[:order_field])
|
|
180
263
|
return nil if field.to_s.empty?
|
|
@@ -182,6 +265,12 @@ module Hecks
|
|
|
182
265
|
{ field: field, direction: text(row[:order_way]) }
|
|
183
266
|
end
|
|
184
267
|
|
|
268
|
+
# Reads a read model's own declared row limit.
|
|
269
|
+
#
|
|
270
|
+
# @param row [Hash{Symbol => Object}] a dispatched row carrying a
|
|
271
|
+
# `:limit` cell
|
|
272
|
+
# @return [Hash{Symbol => String}, nil] `{value: ceiling}`, or `nil`
|
|
273
|
+
# when no limit was declared
|
|
185
274
|
def limit(row)
|
|
186
275
|
ceiling = text(row[:limit])
|
|
187
276
|
return nil if ceiling.to_s.empty?
|
|
@@ -189,6 +278,12 @@ module Hecks
|
|
|
189
278
|
{ value: ceiling }
|
|
190
279
|
end
|
|
191
280
|
|
|
281
|
+
# Reads one lifecycle transition row.
|
|
282
|
+
#
|
|
283
|
+
# @param row [Hash{Symbol => Object}] one dispatched lifecycle
|
|
284
|
+
# transition row
|
|
285
|
+
# @return [Hash{Symbol => String}] `:command`, `:from_state`, and
|
|
286
|
+
# `:to_state`
|
|
192
287
|
def transition(row)
|
|
193
288
|
{
|
|
194
289
|
command: text(row[:command]),
|
|
@@ -197,6 +292,13 @@ module Hecks
|
|
|
197
292
|
}
|
|
198
293
|
end
|
|
199
294
|
|
|
295
|
+
# Reads one reference head — a process manager handler's own
|
|
296
|
+
# aggregate binding.
|
|
297
|
+
#
|
|
298
|
+
# @param row [Hash{Symbol => Object}] one dispatched reference-head
|
|
299
|
+
# row
|
|
300
|
+
# @return [Hash{Symbol => Object}] `:aggregate`/`:as` (`String`) and
|
|
301
|
+
# `:many` (`Boolean`)
|
|
200
302
|
def head(row)
|
|
201
303
|
{
|
|
202
304
|
aggregate: text(row[:aggregate]),
|
|
@@ -207,6 +309,11 @@ module Hecks
|
|
|
207
309
|
}
|
|
208
310
|
end
|
|
209
311
|
|
|
312
|
+
# Reads a read model's own `group_by` field name.
|
|
313
|
+
#
|
|
314
|
+
# @param row [Hash{Symbol => Object}] a dispatched row carrying a
|
|
315
|
+
# `:field` cell
|
|
316
|
+
# @return [Hash{Symbol => String}] `{field: ...}`
|
|
210
317
|
def group_by_field(row) = { field: text(row[:field]) }
|
|
211
318
|
|
|
212
319
|
# `count`'s own boolean, read back the same way `head`'s own
|
|
@@ -219,6 +326,11 @@ module Hecks
|
|
|
219
326
|
# `true`/`nil` pair (never `false`) exactly, rather than the
|
|
220
327
|
# unconditional `true`/`false` `head`'s own `many` needs (every
|
|
221
328
|
# head does get a `Gather` dispatch, declared or derived).
|
|
329
|
+
#
|
|
330
|
+
# @param row [Hash{Symbol => Object}] a dispatched row carrying a
|
|
331
|
+
# `:count` cell
|
|
332
|
+
# @return [Boolean, nil] `true` when the flag reads `"true"`, `nil`
|
|
333
|
+
# for an undeclared read model's own `count`
|
|
222
334
|
def read_model_count(row) = (true if text(row[:count]).to_s == "true")
|
|
223
335
|
|
|
224
336
|
# The append flattening, in reverse.
|
|
@@ -227,6 +339,11 @@ module Hecks
|
|
|
227
339
|
# field/kind/source triple, so the walk offers an append once per binding.
|
|
228
340
|
# Rebuilding groups those rows back into the single mutation the IR keeps —
|
|
229
341
|
# the only place here that undoes something rather than simply reading it.
|
|
342
|
+
#
|
|
343
|
+
# @param row [Hash{Symbol => Object}] a dispatched row carrying a
|
|
344
|
+
# `:mutations` list
|
|
345
|
+
# @return [Array<Hash{Symbol => Object}>] one hash per distinct
|
|
346
|
+
# `target`/`op` pair, each `mutation`'s own return
|
|
230
347
|
def mutations(row)
|
|
231
348
|
Array(row[:mutations])
|
|
232
349
|
.group_by { |change| [text(change[:target]), text(change[:op])] }
|
|
@@ -242,6 +359,16 @@ module Hecks
|
|
|
242
359
|
# Mutation's own `to_h` lambda does, rather than leaving the key
|
|
243
360
|
# silently absent (spec/round_trip_spec's whole point: a field the
|
|
244
361
|
# language does not hold is a named gap, not a byte-for-byte one).
|
|
362
|
+
#
|
|
363
|
+
# @param target [String] the mutated field's own name
|
|
364
|
+
# @param oper [String] the mutation's operation name, such as
|
|
365
|
+
# `"set"`, `"append"`, `"delegate"`, or `"corrects"`
|
|
366
|
+
# @param bindings [Array<Hash{Symbol => Object}>] this target/op
|
|
367
|
+
# pair's own dispatched binding rows
|
|
368
|
+
# @return [Hash{Symbol => Object}] `:target`/`:op` (`Symbol`),
|
|
369
|
+
# `:sign` (`String`), plus `:fields` (`appended`'s own return) for
|
|
370
|
+
# `"append"`/`"delegate"`/`"corrects"`, or `:source`
|
|
371
|
+
# (`classified`'s own return) for anything else
|
|
245
372
|
def mutation(target, oper, bindings)
|
|
246
373
|
base = { target: target.to_sym, op: oper.to_sym, sign: Hecks::Bluebook::Mutation.sign_for(oper) }
|
|
247
374
|
# `:delegate`/`:corrects` (CommandBuilder#delegates_to's and
|
|
@@ -252,10 +379,24 @@ module Hecks
|
|
|
252
379
|
base.merge(source: classified(bindings.first))
|
|
253
380
|
end
|
|
254
381
|
|
|
382
|
+
# Rebuilds an append's own field -> source map from its flattened
|
|
383
|
+
# per-binding rows.
|
|
384
|
+
#
|
|
385
|
+
# @param bindings [Array<Hash{Symbol => Object}>] one append's own
|
|
386
|
+
# dispatched binding rows
|
|
387
|
+
# @return [Hash{Symbol => String}] each bound field name mapped to
|
|
388
|
+
# its own source text
|
|
255
389
|
def appended(bindings)
|
|
256
390
|
bindings.to_h { |binding| [text(binding[:field]).to_sym, text(binding[:source])] }
|
|
257
391
|
end
|
|
258
392
|
|
|
393
|
+
# Classifies a single-binding mutation's own source.
|
|
394
|
+
#
|
|
395
|
+
# @param binding [Hash{Symbol => Object}] one dispatched binding row
|
|
396
|
+
# @return [Hash{Symbol => Object}] `{kind: "argument"|"state",
|
|
397
|
+
# name: String}` for an argument or state reference, or
|
|
398
|
+
# `{kind: "literal", value: Object}` (the decoded literal) for
|
|
399
|
+
# anything else
|
|
259
400
|
def classified(binding)
|
|
260
401
|
kind = text(binding[:kind])
|
|
261
402
|
value = text(binding[:source])
|
|
@@ -10,23 +10,27 @@ module Hecks
|
|
|
10
10
|
# coercion/lifecycle-guard door every other command goes through),
|
|
11
11
|
# not merely declared and never exercised.
|
|
12
12
|
#
|
|
13
|
-
#
|
|
14
|
-
# `ArgumentSeed` value objects (still hand-written `member` rows —
|
|
15
|
-
# now beside the concepts they spell) are what gets written ; this
|
|
16
|
-
# is what turns them into what gets read. `Judge`/`Reconstruction`
|
|
17
|
-
# already draw exactly this line everywhere else in the meta-domain
|
|
18
|
-
# (a chapter's own declarations versus what gets dispatched from
|
|
19
|
-
# them) — this runs the same distinction one level further out, for
|
|
20
|
-
# the language's own grammar table.
|
|
13
|
+
# ## The source stays static
|
|
21
14
|
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
15
|
+
# Aggregate-local `KeywordSeed`/`ArgumentSeed` value objects (still
|
|
16
|
+
# hand-written `member` rows — now beside the concepts they spell)
|
|
17
|
+
# are what gets written ; this is what turns them into what gets
|
|
18
|
+
# read. `Judge`/`Reconstruction` already draw exactly this line
|
|
19
|
+
# everywhere else in the meta-domain (a chapter's own declarations
|
|
20
|
+
# versus what gets dispatched from them) — this runs the same
|
|
21
|
+
# distinction one level further out, for the language's own grammar
|
|
22
|
+
# table.
|
|
28
23
|
#
|
|
29
|
-
#
|
|
24
|
+
# ## A dedicated runtime
|
|
25
|
+
#
|
|
26
|
+
# Not `MetaValidator.fresh_runtime`. That one is reserved for
|
|
27
|
+
# `Judge`'s own bootstrap (dispatching a chapter's declarations into
|
|
28
|
+
# the meta-domain's grammar) — a different act from this one
|
|
29
|
+
# (dispatching the meta-domain's own grammar table data into a live
|
|
30
|
+
# "Bluebook" domain instance). Sharing the runtime would let one
|
|
31
|
+
# boot's own repository state leak into the other's.
|
|
32
|
+
#
|
|
33
|
+
# ## Usage
|
|
30
34
|
#
|
|
31
35
|
# MetaValidator.syntax_table # => { keywords: [...], arguments: [...] }
|
|
32
36
|
#
|
|
@@ -76,6 +80,9 @@ module Hecks
|
|
|
76
80
|
# tracked. Holding the chapter objects themselves (not their ids)
|
|
77
81
|
# in the key also means a collected chapter can never hand its id
|
|
78
82
|
# to a newcomer behind this cache's back.
|
|
83
|
+
# @return [Hash{Symbol => Array<Hash{Symbol => String}>}] `:keywords`
|
|
84
|
+
# and `:arguments`, each an array of plain, string-valued row
|
|
85
|
+
# hashes (`status` included)
|
|
79
86
|
def call
|
|
80
87
|
chapters = MetaValidator.grammar_registry.bluebooks.to_a
|
|
81
88
|
return @call if @call && same_chapters?(@call_chapters, chapters)
|
|
@@ -91,6 +98,15 @@ module Hecks
|
|
|
91
98
|
result
|
|
92
99
|
end
|
|
93
100
|
|
|
101
|
+
# Compares the in-process cache key against the registry's current
|
|
102
|
+
# chapters.
|
|
103
|
+
#
|
|
104
|
+
# @param cached [Array<Array(String, Object)>] the `[name, chapter]`
|
|
105
|
+
# pairs `call` last cached against
|
|
106
|
+
# @param current [Array<Array(String, Object)>] the registry's
|
|
107
|
+
# current `[name, chapter]` pairs
|
|
108
|
+
# @return [Boolean] whether `cached` and `current` name the same
|
|
109
|
+
# chapters, by identity, in the same order
|
|
94
110
|
def same_chapters?(cached, current)
|
|
95
111
|
cached.size == current.size &&
|
|
96
112
|
cached.zip(current).all? do |(cached_name, cached_chapter), (name, chapter)|
|
|
@@ -152,8 +168,19 @@ module Hecks
|
|
|
152
168
|
# be ruled out while debugging something else entirely.
|
|
153
169
|
CACHE_DIR = File.expand_path("../../../../tmp/hecks_syntax_boot_cache", __dir__).freeze
|
|
154
170
|
|
|
171
|
+
# Reports whether the cross-process disk cache is turned on.
|
|
172
|
+
#
|
|
173
|
+
# @return [Boolean] whether the cross-process disk cache is turned
|
|
174
|
+
# on (`HECKS_SYNTAX_BOOT_CACHE=off` turns it off)
|
|
155
175
|
def disk_cache_enabled? = ENV["HECKS_SYNTAX_BOOT_CACHE"] != "off"
|
|
156
176
|
|
|
177
|
+
# Reads an already-written cache entry for `chapters`, if any.
|
|
178
|
+
#
|
|
179
|
+
# @param chapters [Array<Array(String, Object)>] the `[name,
|
|
180
|
+
# chapter]` pairs to key the cache entry on
|
|
181
|
+
# @return [Hash{Symbol => Array<Hash{Symbol => String}>}, nil] the
|
|
182
|
+
# cached syntax table, or `nil` if the cache is disabled, no entry
|
|
183
|
+
# exists yet, or the entry could not be read
|
|
157
184
|
def read_disk_cache(chapters)
|
|
158
185
|
return nil unless disk_cache_enabled?
|
|
159
186
|
|
|
@@ -165,6 +192,13 @@ module Hecks
|
|
|
165
192
|
nil
|
|
166
193
|
end
|
|
167
194
|
|
|
195
|
+
# Writes `result` to the disk cache for `chapters`, atomically.
|
|
196
|
+
#
|
|
197
|
+
# @param chapters [Array<Array(String, Object)>] the `[name,
|
|
198
|
+
# chapter]` pairs to key the cache entry on
|
|
199
|
+
# @param result [Hash{Symbol => Array<Hash{Symbol => String}>}] the
|
|
200
|
+
# syntax table to persist
|
|
201
|
+
# @return [void]
|
|
168
202
|
def write_disk_cache(chapters, result)
|
|
169
203
|
return unless disk_cache_enabled?
|
|
170
204
|
|
|
@@ -177,10 +211,21 @@ module Hecks
|
|
|
177
211
|
nil
|
|
178
212
|
end
|
|
179
213
|
|
|
214
|
+
# Builds the disk cache file path for `chapters`.
|
|
215
|
+
#
|
|
216
|
+
# @param chapters [Array<Array(String, Object)>] the `[name,
|
|
217
|
+
# chapter]` pairs to key the cache entry on
|
|
218
|
+
# @return [String] the disk cache file path for `chapters`
|
|
180
219
|
def disk_cache_path(chapters)
|
|
181
220
|
File.join(CACHE_DIR, "#{disk_cache_key(chapters)}.marshal")
|
|
182
221
|
end
|
|
183
222
|
|
|
223
|
+
# Builds the cache key for `chapters`.
|
|
224
|
+
#
|
|
225
|
+
# @param chapters [Array<Array(String, Object)>] the `[name,
|
|
226
|
+
# chapter]` pairs to key the cache entry on
|
|
227
|
+
# @return [String] a SHA-256 digest of the chapter names and the
|
|
228
|
+
# current grammar source
|
|
184
229
|
def disk_cache_key(chapters)
|
|
185
230
|
names = chapters.map { |name, _chapter| name }
|
|
186
231
|
Digest::SHA256.hexdigest("#{names.join(',')}:#{grammar_content_digest}")
|
|
@@ -196,12 +241,20 @@ module Hecks
|
|
|
196
241
|
# chapter it belongs to) — deliberately, since under-covering this
|
|
197
242
|
# set is a correctness bug (a stale table survives a real grammar
|
|
198
243
|
# edit) and over-covering it is only ever a wasted cache miss.
|
|
244
|
+
# @return [String] a SHA-256 digest of every grammar file's own
|
|
245
|
+
# current content, sorted for a stable digest
|
|
199
246
|
def grammar_content_digest
|
|
200
247
|
files = (MetaValidator::GRAMMAR_FILES + MetaValidator::WORLD_GRAMMAR + MetaValidator::HECKSAGON_GRAMMAR +
|
|
201
248
|
Dir.glob(File.join(MetaValidator::ATTACHED_GRAMMAR_DIR, "*.bluebook"))).sort
|
|
202
249
|
Digest::SHA256.hexdigest(files.map { |file| File.read(file) }.join("\0"))
|
|
203
250
|
end
|
|
204
251
|
|
|
252
|
+
# Dispatches every seed row into a fresh "Bluebook" instance and
|
|
253
|
+
# reads the result back.
|
|
254
|
+
#
|
|
255
|
+
# @return [Hash{Symbol => Array<Hash{Symbol => String}>}] `:keywords`
|
|
256
|
+
# and `:arguments`, each an array of plain, string-valued row
|
|
257
|
+
# hashes (`status` included)
|
|
205
258
|
def boot
|
|
206
259
|
bluebook = MetaValidator.grammar_registry.bluebook("Bluebook")
|
|
207
260
|
# `MetaValidator.fresh_runtime`, not a brand-new `Runtime::
|
|
@@ -222,21 +275,35 @@ module Hecks
|
|
|
222
275
|
read_back(runtime, bluebook)
|
|
223
276
|
end
|
|
224
277
|
|
|
278
|
+
# Wraps `text` as a single-field value-object payload, for a
|
|
279
|
+
# dispatch's own `with:`.
|
|
280
|
+
#
|
|
225
281
|
# An Integer stays an Integer — `position` is `Position`-typed
|
|
226
282
|
# (`attribute :value, Integer`), so stringifying it fails the type
|
|
227
283
|
# gate rather than feeding it, the same reading `Judge#v` gives.
|
|
284
|
+
#
|
|
285
|
+
# @param text [String, Integer] the scalar to wrap
|
|
286
|
+
# @return [Hash{Symbol => String, Integer}] `{value: text}`,
|
|
287
|
+
# stringified unless `text` is already an Integer
|
|
228
288
|
def v(text)
|
|
229
289
|
return { value: text } if text.is_a?(Integer)
|
|
230
290
|
|
|
231
291
|
{ value: text.to_s }
|
|
232
292
|
end
|
|
233
293
|
|
|
294
|
+
# Wraps `text` as an `optional: true` command argument's own
|
|
295
|
+
# payload, or omits it.
|
|
296
|
+
#
|
|
234
297
|
# Absent stays absent. A seed row's own optional columns ("was",
|
|
235
298
|
# "at", "named", ...) are empty strings, not nil — `Literal`/CSV-
|
|
236
299
|
# shaped grammar data has no `nil` to write — so this is the one
|
|
237
300
|
# place that decides "" means "not given" for the purpose of an
|
|
238
301
|
# `optional: true` command argument, the same reading `Judge#v`
|
|
239
302
|
# makes for the meta-domain's own dispatches.
|
|
303
|
+
#
|
|
304
|
+
# @param text [String, nil] the seed row's own column value
|
|
305
|
+
# @return [Hash{Symbol => String}, nil] `v(text)`, or `nil` when
|
|
306
|
+
# `text` is `nil` or empty
|
|
240
307
|
def optional(text)
|
|
241
308
|
return nil if text.nil? || text.to_s.empty?
|
|
242
309
|
|
|
@@ -251,6 +318,13 @@ module Hecks
|
|
|
251
318
|
# declared first, named after the real chapter, purely to satisfy
|
|
252
319
|
# the reference — its own vision/classification are never read
|
|
253
320
|
# by anything this boot does.
|
|
321
|
+
#
|
|
322
|
+
# @param runtime [Runtime::Dispatcher] the fresh runtime to
|
|
323
|
+
# dispatch into
|
|
324
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
325
|
+
# "Bluebook" chapter, read for its `hecks_name` and `"Syntax"`
|
|
326
|
+
# aggregate
|
|
327
|
+
# @return [void]
|
|
254
328
|
def declare_syntax(runtime, bluebook)
|
|
255
329
|
syntax = bluebook.aggregate("Syntax")
|
|
256
330
|
runtime.dispatch("Bluebook::Bluebook.Declare",
|
|
@@ -264,14 +338,19 @@ module Hecks
|
|
|
264
338
|
|
|
265
339
|
# `to: "Syntax"` names the record already opened by `declare_syntax`
|
|
266
340
|
# above — an append onto an existing aggregate, not a second creation
|
|
267
|
-
# of it.
|
|
268
|
-
#
|
|
269
|
-
#
|
|
270
|
-
#
|
|
271
|
-
#
|
|
272
|
-
#
|
|
273
|
-
#
|
|
274
|
-
#
|
|
341
|
+
# of it. Smuggling `name: v("Syntax")` into the payload instead — the
|
|
342
|
+
# pre-routing convention `Judge#appends` (the same append shape, for
|
|
343
|
+
# `ValueObject.Member`/`ProcessManager.Handler`) already left behind
|
|
344
|
+
# for `to:`/`with:` — would carry the receiver in the payload,
|
|
345
|
+
# making `Syntax.Keyword`'s own `command.creates?` (true: it
|
|
346
|
+
# declares no `reference_to`) look like a fresh identity to mint,
|
|
347
|
+
# colliding with the very "Syntax" row `declare_syntax` just opened.
|
|
348
|
+
#
|
|
349
|
+
# @param runtime [Runtime::Dispatcher] the fresh runtime to
|
|
350
|
+
# dispatch into
|
|
351
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
352
|
+
# "Bluebook" chapter, read for its `KeywordSeed` rows
|
|
353
|
+
# @return [void]
|
|
275
354
|
def admit_keywords(runtime, bluebook)
|
|
276
355
|
all_rows(bluebook, "KeywordSeed").each_with_index do |row, index|
|
|
277
356
|
runtime.dispatch("Bluebook::Syntax.Keyword", to: "Syntax",
|
|
@@ -289,6 +368,14 @@ module Hecks
|
|
|
289
368
|
end
|
|
290
369
|
end
|
|
291
370
|
|
|
371
|
+
# Admits every `ArgumentSeed` row as a `Syntax.Argument`, the same
|
|
372
|
+
# `to: "Syntax"` append shape `admit_keywords` uses one type over.
|
|
373
|
+
#
|
|
374
|
+
# @param runtime [Runtime::Dispatcher] the fresh runtime to
|
|
375
|
+
# dispatch into
|
|
376
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
377
|
+
# "Bluebook" chapter, read for its `ArgumentSeed` rows
|
|
378
|
+
# @return [void]
|
|
292
379
|
def admit_arguments(runtime, bluebook)
|
|
293
380
|
all_rows(bluebook, "ArgumentSeed").each_with_index do |row, index|
|
|
294
381
|
runtime.dispatch("Bluebook::Syntax.Argument", to: "Syntax",
|
|
@@ -315,6 +402,14 @@ module Hecks
|
|
|
315
402
|
# Port, Adapter, Translation), and attached sub-languages alike.
|
|
316
403
|
# Concatenated into one sequence because `position` is minted from the
|
|
317
404
|
# walk index and must not collide across concepts.
|
|
405
|
+
#
|
|
406
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
407
|
+
# "Bluebook" chapter, read first, before every other loaded
|
|
408
|
+
# chapter
|
|
409
|
+
# @param name [String] the local value-object name to collect rows
|
|
410
|
+
# of, such as `"KeywordSeed"`
|
|
411
|
+
# @return [Array<Hash{Symbol => String}>] every matching row, across
|
|
412
|
+
# every loaded chapter, in chapter order
|
|
318
413
|
def all_rows(bluebook, name)
|
|
319
414
|
seed_chapters(bluebook).flat_map { |chapter| rows(chapter, name) }
|
|
320
415
|
end
|
|
@@ -323,6 +418,11 @@ module Hecks
|
|
|
323
418
|
# sibling/extension. The name rejection avoids reading the same core
|
|
324
419
|
# object twice without relying on object identity across fixpoint
|
|
325
420
|
# assembly.
|
|
421
|
+
#
|
|
422
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
423
|
+
# "Bluebook" chapter
|
|
424
|
+
# @return [Array<Bluebook::Chapter>] `bluebook` first, then every
|
|
425
|
+
# other chapter the grammar registry holds
|
|
326
426
|
def seed_chapters(bluebook)
|
|
327
427
|
[bluebook] + MetaValidator.grammar_registry.bluebooks.values.reject { |chapter| chapter.name == bluebook.name }
|
|
328
428
|
end
|
|
@@ -331,6 +431,13 @@ module Hecks
|
|
|
331
431
|
# every aggregate that owns a same-named local value object. Repeating
|
|
332
432
|
# the value-object shape is deliberate: each concept remains readable
|
|
333
433
|
# by itself, while this discovery is the only grouping mechanism.
|
|
434
|
+
#
|
|
435
|
+
# @param bluebook [Bluebook::Chapter] the chapter to search
|
|
436
|
+
# @param name [String] the local value-object name to collect rows
|
|
437
|
+
# of, such as `"KeywordSeed"`
|
|
438
|
+
# @return [Array<Hash{Symbol => String}>] one hash per member row,
|
|
439
|
+
# `[]` if no aggregate in `bluebook` owns a value object named
|
|
440
|
+
# `name`
|
|
334
441
|
def rows(bluebook, name)
|
|
335
442
|
bluebook.aggregates.flat_map do |aggregate|
|
|
336
443
|
value_object = aggregate.value_objects.find { |vo| vo.hecks_name == name }
|
|
@@ -344,6 +451,14 @@ module Hecks
|
|
|
344
451
|
# above hands the seed data in as — plain hashes, string values,
|
|
345
452
|
# `status` included — so `ParserTable`/`syntax_conformance_spec`
|
|
346
453
|
# need not know or care that a real dispatch happened in between.
|
|
454
|
+
#
|
|
455
|
+
# @param runtime [Runtime::Dispatcher] the runtime `boot` dispatched
|
|
456
|
+
# every seed row into
|
|
457
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
458
|
+
# "Bluebook" chapter, read for its `"Syntax"` aggregate
|
|
459
|
+
# @return [Hash{Symbol => Array<Hash{Symbol => String}>}] `:keywords`
|
|
460
|
+
# and `:arguments`, each an array of plain, string-valued row
|
|
461
|
+
# hashes (`status` included)
|
|
347
462
|
def read_back(runtime, bluebook)
|
|
348
463
|
syntax = bluebook.aggregate("Syntax")
|
|
349
464
|
repository = runtime.registry.repository("Bluebook", syntax)
|
|
@@ -355,12 +470,23 @@ module Hecks
|
|
|
355
470
|
}
|
|
356
471
|
end
|
|
357
472
|
|
|
473
|
+
# Flattens one dispatched record into a plain, string-valued row.
|
|
474
|
+
#
|
|
475
|
+
# @param row [Object] one dispatched keyword or argument record,
|
|
476
|
+
# answering `to_h`
|
|
477
|
+
# @return [Hash{Symbol => String}] `row`'s own fields, each value
|
|
478
|
+
# unwrapped and stringified
|
|
358
479
|
def stringify(row)
|
|
359
480
|
row.to_h.transform_values do |cell|
|
|
360
481
|
scalar(cell).to_s
|
|
361
482
|
end
|
|
362
483
|
end
|
|
363
484
|
|
|
485
|
+
# Unwraps one field's stored value to its bare scalar.
|
|
486
|
+
#
|
|
487
|
+
# @param cell [Object] one field's stored value
|
|
488
|
+
# @return [Object] `cell`'s sole wrapped value, if `cell` is a
|
|
489
|
+
# to_h-able non-String; `cell` unchanged otherwise
|
|
364
490
|
def scalar(cell)
|
|
365
491
|
return cell.to_h.values.first if cell.respond_to?(:to_h) && !cell.is_a?(String)
|
|
366
492
|
|