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,13 @@ module Hecks
|
|
|
8
8
|
# everything ambiguous left unresolved for a human.
|
|
9
9
|
module Differ
|
|
10
10
|
# Diff two bluebook IRs into the edge's declarations.
|
|
11
|
+
#
|
|
12
|
+
# @param held_bluebook [Bluebook::Chapter] the era being translated from
|
|
13
|
+
# @param current_bluebook [Bluebook::Chapter] the era being translated to
|
|
14
|
+
# @return [Hash{Symbol => Object}] `:aggregates` (Array<Scaffold::ScaffoldedAggregate>,
|
|
15
|
+
# one per matched aggregate carrying a rename or a rule), `:retired` (Array<String>,
|
|
16
|
+
# vanished aggregate names with no successor), `:unclaimed` (Array<String>, vanished
|
|
17
|
+
# aggregate names left ambiguous)
|
|
11
18
|
def diff(held_bluebook, current_bluebook)
|
|
12
19
|
held_shapes = projections(held_bluebook)
|
|
13
20
|
current_shapes = projections(current_bluebook)
|
|
@@ -27,17 +34,31 @@ module Hecks
|
|
|
27
34
|
{ aggregates: aggregates, retired: matched[:retired], unclaimed: matched[:unclaimed] }
|
|
28
35
|
end
|
|
29
36
|
|
|
37
|
+
# Projects a bluebook's storage shape and indexes its aggregates by name.
|
|
38
|
+
#
|
|
39
|
+
# @param bluebook [Bluebook::Chapter] the bluebook to project
|
|
40
|
+
# @return [Hash{String => Hash}] the bluebook's storage-shape aggregates (see
|
|
41
|
+
# `Runtime::StorageShape.project`), keyed by aggregate name
|
|
30
42
|
def projections(bluebook)
|
|
31
43
|
Runtime::StorageShape.project(bluebook)["aggregates"].to_h { |shape| [shape["name"], shape] }
|
|
32
44
|
end
|
|
33
45
|
|
|
34
46
|
# A vanished aggregate whose full shape reappears under exactly one
|
|
35
|
-
# new name
|
|
47
|
+
# new name counts as a rename. `retired` is only confident when nothing
|
|
36
48
|
# remains it could plausibly have become — a vanished aggregate
|
|
37
49
|
# beside an unmatched new one might be a rename-plus-reshape, and
|
|
38
50
|
# writing `retired` there would be a guess that strands data.
|
|
39
51
|
# Anything ambiguous stays unclaimed, and the coverage gate names
|
|
40
52
|
# it until a human decides.
|
|
53
|
+
#
|
|
54
|
+
# @param held_shapes [Hash{String => Hash}] the held era's aggregate shapes, keyed by
|
|
55
|
+
# name (see `projections`)
|
|
56
|
+
# @param current_shapes [Hash{String => Hash}] the current era's aggregate shapes,
|
|
57
|
+
# keyed by name
|
|
58
|
+
# @return [Hash{Symbol => Object}] `:pairs` (Hash{String => String, nil}, each current
|
|
59
|
+
# name to its matched held name, or nil when unmatched), `:retired` (Array<String>,
|
|
60
|
+
# vanished names with no successor), `:unclaimed` (Array<String>, vanished names
|
|
61
|
+
# left ambiguous)
|
|
41
62
|
def match_aggregates(held_shapes, current_shapes)
|
|
42
63
|
pairs = {}
|
|
43
64
|
current_shapes.each_key { |name| pairs[name] = held_shapes.key?(name) ? name : nil }
|
|
@@ -67,6 +88,13 @@ module Hecks
|
|
|
67
88
|
# new type name: a retype. Anything else: unresolved, carrying its
|
|
68
89
|
# type-compatible candidates (an empty list is the arrow toward
|
|
69
90
|
# compute, or drop).
|
|
91
|
+
#
|
|
92
|
+
# @param held_shape [Hash{String => Object}] the held era's aggregate shape (see
|
|
93
|
+
# `projections`)
|
|
94
|
+
# @param current_shape [Hash{String => Object}] the current era's aggregate shape
|
|
95
|
+
# @return [Array<Hash{Symbol => Object}>] one rule Hash per changed path — `:kind`
|
|
96
|
+
# plus `:from`/`:to` for `:rename`/`:move`/`:retype`, or `:from`/`:candidates` for
|
|
97
|
+
# `:unresolved` (see `Renderer#render_rule`); `[]` when nothing changed
|
|
70
98
|
def attribute_rules(held_shape, current_shape)
|
|
71
99
|
rules = []
|
|
72
100
|
rules << identity_hint(held_shape, current_shape)
|
|
@@ -93,6 +121,13 @@ module Hecks
|
|
|
93
121
|
|
|
94
122
|
# retype pass: same attribute name, same member structure, the
|
|
95
123
|
# type's own name changed
|
|
124
|
+
#
|
|
125
|
+
# @param held_attrs [Hash{String => Hash}] the held era's attributes, keyed by name
|
|
126
|
+
# @param current_attrs [Hash{String => Hash}] the current era's attributes, keyed by
|
|
127
|
+
# name
|
|
128
|
+
# @return [Array<Hash{Symbol => String}>] one `{kind: :retype, from:, to:}` rule per
|
|
129
|
+
# attribute whose members are unchanged but whose type name changed; `[]` when none
|
|
130
|
+
# match
|
|
96
131
|
def retype_rules(held_attrs, current_attrs)
|
|
97
132
|
(held_attrs.keys & current_attrs.keys).filter_map do |name|
|
|
98
133
|
held = held_attrs[name]
|
|
@@ -105,6 +140,16 @@ module Hecks
|
|
|
105
140
|
end
|
|
106
141
|
end
|
|
107
142
|
|
|
143
|
+
# Resolves each vanished path into a rename, move or unresolved rule, appended to
|
|
144
|
+
# `rules` in place.
|
|
145
|
+
#
|
|
146
|
+
# @param rules [Array<Hash{Symbol => Object}>] the rule list to append to; also read
|
|
147
|
+
# for `:rename`/`:move` targets an earlier iteration already claimed
|
|
148
|
+
# @param vanished [Hash{String => Object}] vanished paths to type signature (see
|
|
149
|
+
# `vanished_paths`)
|
|
150
|
+
# @param appeared [Hash{String => Object}] appeared paths to type signature (see
|
|
151
|
+
# `appeared_paths`)
|
|
152
|
+
# @return [void]
|
|
108
153
|
def resolve_vanished_rules!(rules, vanished, appeared)
|
|
109
154
|
vanished.each do |path, signature|
|
|
110
155
|
matches = appeared.select { |_, candidate| candidate == signature }.keys
|
|
@@ -125,6 +170,14 @@ module Hecks
|
|
|
125
170
|
# and members that vanished (or changed type) inside a kept
|
|
126
171
|
# attribute — mirroring exactly what EraGuard will demand coverage
|
|
127
172
|
# for.
|
|
173
|
+
#
|
|
174
|
+
# @param held_attrs [Hash{String => Hash}] the held era's attributes, keyed by name
|
|
175
|
+
# @param current_attrs [Hash{String => Hash}] the current era's attributes, keyed by
|
|
176
|
+
# name
|
|
177
|
+
# @param retyped [Array<String>] type names already claimed by a `:retype` rule,
|
|
178
|
+
# excluded here
|
|
179
|
+
# @return [Hash{String => Object}] each vanished or changed path, dotted for a member,
|
|
180
|
+
# valued by that path's type signature
|
|
128
181
|
def vanished_paths(held_attrs, current_attrs, retyped)
|
|
129
182
|
paths = {}
|
|
130
183
|
held_attrs.each do |name, held|
|
|
@@ -146,6 +199,17 @@ module Hecks
|
|
|
146
199
|
paths
|
|
147
200
|
end
|
|
148
201
|
|
|
202
|
+
# Current paths that need explaining: whole attributes that appeared, and members
|
|
203
|
+
# that appeared (or changed type) inside a kept attribute — the mirror of
|
|
204
|
+
# `vanished_paths`.
|
|
205
|
+
#
|
|
206
|
+
# @param held_attrs [Hash{String => Hash}] the held era's attributes, keyed by name
|
|
207
|
+
# @param current_attrs [Hash{String => Hash}] the current era's attributes, keyed by
|
|
208
|
+
# name
|
|
209
|
+
# @param retyped [Array<String>] type names already claimed by a `:retype` rule,
|
|
210
|
+
# excluded here
|
|
211
|
+
# @return [Hash{String => Object}] each appeared or changed path, dotted for a
|
|
212
|
+
# member, valued by that path's type signature
|
|
149
213
|
def appeared_paths(held_attrs, current_attrs, retyped)
|
|
150
214
|
paths = {}
|
|
151
215
|
current_attrs.each do |name, current|
|
|
@@ -173,24 +237,51 @@ module Hecks
|
|
|
173
237
|
# `check_identity_unchanged!` is the real gate this only hints
|
|
174
238
|
# toward, the same "tool proactively guides you" pattern the
|
|
175
239
|
# generic unresolved message already gives unfed fields.
|
|
240
|
+
#
|
|
241
|
+
# @param held_shape [Hash{String => Object}] the held era's aggregate shape
|
|
242
|
+
# @param current_shape [Hash{String => Object}] the current era's aggregate shape
|
|
243
|
+
# @return [Hash{Symbol => Object}, nil] `{kind: :unresolved, from: :identity,
|
|
244
|
+
# candidates: []}` when the identity paths differ; nil when they match
|
|
176
245
|
def identity_hint(held_shape, current_shape)
|
|
177
246
|
return if held_shape["identity"] == current_shape["identity"]
|
|
178
247
|
|
|
179
248
|
{ kind: :unresolved, from: :identity, candidates: [] }
|
|
180
249
|
end
|
|
181
250
|
|
|
251
|
+
# Finds appeared paths type-compatible with a vanished path's own signature.
|
|
252
|
+
#
|
|
253
|
+
# @param appeared [Hash{String => Object}] appeared paths to type signature (see
|
|
254
|
+
# `appeared_paths`)
|
|
255
|
+
# @param signature [Object] the vanished path's own type signature to match against
|
|
256
|
+
# @return [Array<String>] appeared paths whose scalar signature (see `scalar_of`)
|
|
257
|
+
# matches `signature`'s
|
|
182
258
|
def compatible_candidates(appeared, signature)
|
|
183
259
|
appeared.select { |_, candidate| scalar_of(candidate) == scalar_of(signature) }.keys
|
|
184
260
|
end
|
|
185
261
|
|
|
262
|
+
# Reduces a type signature to the shape `compatible_candidates` compares by.
|
|
263
|
+
#
|
|
264
|
+
# @param signature [Object, Hash] a path's type signature
|
|
265
|
+
# @return [Object] `signature["members"]` for a container Hash, else `signature`
|
|
266
|
+
# unchanged
|
|
186
267
|
def scalar_of(signature) = signature.is_a?(Hash) ? signature["members"] : signature
|
|
187
268
|
|
|
269
|
+
# Reads a container attribute's own members, keyed by name.
|
|
270
|
+
#
|
|
271
|
+
# @param attribute [Hash{String => Object}] one path's type signature
|
|
272
|
+
# @return [Hash{String => Object}] each member's name to its type; `{}` when
|
|
273
|
+
# `attribute` is not a container
|
|
188
274
|
def members_of(attribute)
|
|
189
275
|
return {} unless container?(attribute)
|
|
190
276
|
|
|
191
277
|
attribute["type"]["members"].to_h { |member| [member["name"], member["type"]] }
|
|
192
278
|
end
|
|
193
279
|
|
|
280
|
+
# Reports whether a type signature is a container (value object or entity), not a
|
|
281
|
+
# scalar.
|
|
282
|
+
#
|
|
283
|
+
# @param attribute [Hash{String => Object}] one path's type signature
|
|
284
|
+
# @return [Boolean] true if `attribute["type"]` is a Hash, false for a scalar
|
|
194
285
|
def container?(attribute) = attribute["type"].is_a?(Hash)
|
|
195
286
|
end
|
|
196
287
|
end
|
|
@@ -5,6 +5,10 @@ module Hecks
|
|
|
5
5
|
# ambiguities as parse-refusing `unresolved` constructs, never
|
|
6
6
|
# comments, so an unresolved file can only boot into a refusal.
|
|
7
7
|
module Renderer
|
|
8
|
+
# Renders a scaffolded edge as loadable `.bluebook` translation source.
|
|
9
|
+
#
|
|
10
|
+
# @param edge [Scaffold::Edge] the edge to render
|
|
11
|
+
# @return [String] the edge's `.bluebook` text, ending in a newline
|
|
8
12
|
def render(edge)
|
|
9
13
|
lines = ["Hecks.data_translation #{edge.domain.inspect}, from: #{edge.from.inspect}, to: #{edge.to.inspect} do"]
|
|
10
14
|
edge.aggregates.each do |aggregate|
|
|
@@ -19,6 +23,12 @@ module Hecks
|
|
|
19
23
|
"#{lines.join("\n")}\n"
|
|
20
24
|
end
|
|
21
25
|
|
|
26
|
+
# Renders one scaffolded rule as a line of `.bluebook` source.
|
|
27
|
+
#
|
|
28
|
+
# @param rule [Hash{Symbol => Object}] a rule Hash as `Differ#attribute_rules` builds
|
|
29
|
+
# it: `:kind` plus `:from`/`:to`, or `:from`/`:candidates` for `:unresolved`
|
|
30
|
+
# @return [String, nil] the rendered line; nil if `rule[:kind]` is none of `:rename`,
|
|
31
|
+
# `:move`, `:retype` or `:unresolved`
|
|
22
32
|
def render_rule(rule)
|
|
23
33
|
case rule[:kind]
|
|
24
34
|
when :rename then "rename :#{rule[:from]}, to: :#{rule[:to]}"
|
|
@@ -30,6 +40,11 @@ module Hecks
|
|
|
30
40
|
end
|
|
31
41
|
end
|
|
32
42
|
|
|
43
|
+
# Renders a path as bluebook source: a Symbol literal when bare, a String literal
|
|
44
|
+
# when dotted.
|
|
45
|
+
#
|
|
46
|
+
# @param path [String, Symbol] a bare or dotted path
|
|
47
|
+
# @return [String] `":name"` for a bare path, `path.inspect` for a dotted one
|
|
33
48
|
def render_path(path) = path.to_s.include?(".") ? path.to_s.inspect : ":#{path}"
|
|
34
49
|
end
|
|
35
50
|
end
|
|
@@ -6,10 +6,18 @@ module Hecks
|
|
|
6
6
|
# Put the rendered edge on disk, regenerating in place when a file
|
|
7
7
|
# for the same shape pair already exists.
|
|
8
8
|
module Writer
|
|
9
|
+
# Renders an edge and writes it under `directory/translations`, regenerating in
|
|
10
|
+
# place when a file for the same shape pair already exists.
|
|
11
|
+
#
|
|
9
12
|
# The edge file, regenerated in place when one for the same shape
|
|
10
13
|
# pair already exists (matched textually — an unresolved file
|
|
11
14
|
# cannot be loaded to ask, that being the whole point of
|
|
12
15
|
# unresolved).
|
|
16
|
+
#
|
|
17
|
+
# @param directory [String] the domain's root directory
|
|
18
|
+
# @param edge [Scaffold::Edge] the edge to render and write
|
|
19
|
+
# @return [String] the path written, either the matched existing file or a new
|
|
20
|
+
# `<ordinal>-<label>.bluebook`
|
|
13
21
|
def write!(directory, edge)
|
|
14
22
|
translations_dir = File.join(directory, "translations")
|
|
15
23
|
FileUtils.mkdir_p(translations_dir)
|
|
@@ -18,6 +18,19 @@ module Hecks
|
|
|
18
18
|
|
|
19
19
|
module_function
|
|
20
20
|
|
|
21
|
+
# Filters, orders and pages `records` against a declared query specification.
|
|
22
|
+
#
|
|
23
|
+
# @param records [Array<Runtime::Instance, Hash>] the candidate records to filter —
|
|
24
|
+
# an Instance, a Value, or a plain row hash per record (see `FieldPath.dig`)
|
|
25
|
+
# @param declared [QuerySpecification::Common::Options,
|
|
26
|
+
# Bluebook::Behaviour::ReadModel::FilteredOptions] the specification providing
|
|
27
|
+
# `wheres`, `order_by`, `offset` and `limit`
|
|
28
|
+
# @param args [Hash{Symbol => Object}] bound values for any Symbol placeholder in a
|
|
29
|
+
# where clause, `offset`, or `limit`
|
|
30
|
+
# @param registry [Runtime::Registry, nil] the booted registry, passed through to a
|
|
31
|
+
# registry-aware comparison; nil when none is available
|
|
32
|
+
# @return [Array<Runtime::Instance, Hash>] the matching records, ordered and paged;
|
|
33
|
+
# the same element type as `records`
|
|
21
34
|
def execute(records, declared, args = {}, registry: nil)
|
|
22
35
|
matched = records.select do |record|
|
|
23
36
|
declared.wheres.all? do |clause|
|
|
@@ -47,18 +60,38 @@ module Hecks
|
|
|
47
60
|
end
|
|
48
61
|
|
|
49
62
|
# The comparator table itself lives in
|
|
50
|
-
# QuerySpecification::Common::Comparison
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
# stays here is how a value is reached
|
|
54
|
-
# arrives as an argument rather than
|
|
55
|
-
# field is dug through FieldPath
|
|
63
|
+
# QuerySpecification::Common::Comparison, not duplicated per
|
|
64
|
+
# caller — this module and Runtime::QueryInterpreter drifted
|
|
65
|
+
# when each carried its own copy (see that file's own comment
|
|
66
|
+
# for what it cost). What stays here is how a value is reached
|
|
67
|
+
# for this path: a registry arrives as an argument rather than
|
|
68
|
+
# as instance state, and the field is dug through FieldPath
|
|
69
|
+
# before it arrives.
|
|
70
|
+
# @param clause [QuerySpecification::Common::WhereClause] the declared comparison
|
|
71
|
+
# @param held [Object, nil] the record's own value for `clause.field`
|
|
72
|
+
# @param args [Hash{Symbol => Object}] bound values for a Symbol-named `clause.value`
|
|
73
|
+
# @param registry [Runtime::Registry, nil] used only by `none_in_state`; see
|
|
74
|
+
# `Comparison.holds?`
|
|
75
|
+
# @return [Boolean] whether the comparison holds
|
|
76
|
+
# @raise [Runtime::WiringError] if `clause.op` names no comparator in
|
|
77
|
+
# `Comparison`'s table, or `none_in_state`'s target aggregate has no wired repository
|
|
56
78
|
def holds?(clause, held, args, registry: nil)
|
|
57
79
|
Comparison.holds?(clause.op, held, comparable(resolve(clause.value, args)), registry: registry)
|
|
58
80
|
end
|
|
59
81
|
|
|
82
|
+
# Resolves a where-clause value, looking up a Symbol placeholder in `args`.
|
|
83
|
+
#
|
|
84
|
+
# @param value [Object] a literal value, or a Symbol naming a key in `args`
|
|
85
|
+
# @param args [Hash{Symbol => Object}] bound argument values
|
|
86
|
+
# @return [Object] `args[value]` when `value` is a Symbol, else `value` unchanged
|
|
60
87
|
def resolve(value, args) = value.is_a?(Symbol) ? args[value] : value
|
|
61
88
|
|
|
89
|
+
# Normalises a value into the shape `Comparison` compares against.
|
|
90
|
+
#
|
|
91
|
+
# @param value [Runtime::Value, Hash, Object, nil] a held or wanted value; a
|
|
92
|
+
# `Runtime::Value` is read through its `to_h`
|
|
93
|
+
# @return [Object, nil] the sole numeric member, or the sole member, of a Hash-shaped
|
|
94
|
+
# value; otherwise `value` unchanged
|
|
62
95
|
def comparable(value) = Comparison.comparable(value)
|
|
63
96
|
end
|
|
64
97
|
end
|
|
@@ -24,6 +24,21 @@ module Hecks
|
|
|
24
24
|
module Ordering
|
|
25
25
|
module_function
|
|
26
26
|
|
|
27
|
+
# Orders an Array of rows by the declared order, with identity as the total-order
|
|
28
|
+
# tiebreaker.
|
|
29
|
+
#
|
|
30
|
+
# @param rows [Array<Object>] the rows to order; any shape `identity` and the block
|
|
31
|
+
# can read (a `Runtime::Instance`, a plain Hash row, …)
|
|
32
|
+
# @param order_by [QuerySpecification::Common::OrderBy, nil] the declared order;
|
|
33
|
+
# nil orders by identity alone
|
|
34
|
+
# @param null_semantics [QuerySpecification::Common::NullSemantics, nil] where a
|
|
35
|
+
# nil-valued row sorts, passed to `NullPolicy.order`; nil uses its native default
|
|
36
|
+
# @param identity [Proc] yields a row, returns its comparable identity value, used
|
|
37
|
+
# as the stable tiebreaker
|
|
38
|
+
# @yieldparam row [Object] one row being ordered
|
|
39
|
+
# @yieldreturn [Object, nil] the row's comparable value for `order_by`'s field
|
|
40
|
+
# @return [Array<Object>] `rows`, ordered by `order_by` then identity (identity
|
|
41
|
+
# alone when `order_by` is nil)
|
|
27
42
|
def apply(rows, order_by, null_semantics = nil, identity:, &value_of)
|
|
28
43
|
# Stable, because sort_by is not : two rows whose identity ties would
|
|
29
44
|
# otherwise swap arbitrarily, and a tier meant to remove store-dependence
|
data/lib/hecks/ports.rb
CHANGED
|
@@ -20,6 +20,7 @@ require_relative "ports/query"
|
|
|
20
20
|
require_relative "ports/projection"
|
|
21
21
|
require_relative "ports/extraction"
|
|
22
22
|
require_relative "ports/identity_generation"
|
|
23
|
+
require_relative "ports/key_vault"
|
|
23
24
|
require_relative "ports/authorization"
|
|
24
25
|
require_relative "ports/identity_resolution"
|
|
25
26
|
require_relative "ports/authentication"
|
|
@@ -9,14 +9,16 @@ module Hecks
|
|
|
9
9
|
#
|
|
10
10
|
# While `MetaValidator.bootstrapping?` the grammar table does not exist
|
|
11
11
|
# yet, so `WordGate#method_missing` and `RuleReference#lookup` cannot
|
|
12
|
-
# read `calls:`/`resolves_via:`/`disambiguator:` off it
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
12
|
+
# read `calls:`/`resolves_via:`/`disambiguator:` off it — this table is
|
|
13
|
+
# what they read instead.
|
|
14
|
+
#
|
|
15
|
+
# A partition, not a filter: every live row lands in the table, not only
|
|
16
|
+
# the ones a bootstrap chapter happens to call. Picking a subset by
|
|
17
|
+
# grepping which words the core chapters use during bootstrap would risk
|
|
18
|
+
# quietly dropping any word a chapter starts using later. Including
|
|
19
|
+
# every row instead costs nothing: a builder with its own `def` never
|
|
20
|
+
# reaches `method_missing` at all, so a row no bootstrap chapter calls
|
|
21
|
+
# is simply never read.
|
|
20
22
|
#
|
|
21
23
|
# The table cannot be built at boot for the same reason it exists: it
|
|
22
24
|
# is read before the grammar it comes from has been assembled. So it is
|
|
@@ -43,11 +45,24 @@ module Hecks
|
|
|
43
45
|
|
|
44
46
|
module_function
|
|
45
47
|
|
|
48
|
+
# Projects the bootstrap fallback table — the only real work this projector
|
|
49
|
+
# does, `bluebook` unused because the table is the whole language's grammar,
|
|
50
|
+
# not any one chapter's.
|
|
51
|
+
#
|
|
52
|
+
# @param bluebook [Bluebook::Chapter] ignored; present to satisfy the
|
|
53
|
+
# `Projector::Target` calling convention
|
|
54
|
+
# @param options [Hash{Symbol => Object}] ignored; present to satisfy the
|
|
55
|
+
# `Projector::Target` calling convention
|
|
56
|
+
# @return [String] the rendered `lib/hecks/bluebook/dsl/bootstrap_table.rb` source
|
|
46
57
|
def call(bluebook:, options: {}) = render(bluebook)
|
|
47
58
|
|
|
48
59
|
# Retired rows are out of the language; admitted and deprecated rows
|
|
49
60
|
# still dispatch — the same `status != "retired"` reading
|
|
50
61
|
# `GenericDispatch.shape_for` gives the live table.
|
|
62
|
+
#
|
|
63
|
+
# @return [Array<Hash{Symbol => String}>] every non-retired keyword row, each with
|
|
64
|
+
# at least `:context`, `:word`, `:status`, `:calls`, `:resolves_via` and
|
|
65
|
+
# `:disambiguator`, values stringified
|
|
51
66
|
def live_keywords
|
|
52
67
|
Bluebook::MetaValidator::SyntaxBoot.call[:keywords].reject { |row| row[:status] == "retired" }
|
|
53
68
|
end
|
|
@@ -57,6 +72,13 @@ module Hecks
|
|
|
57
72
|
# An overloaded word has one row per argument shape, and they all
|
|
58
73
|
# name the same method. Two that don't would make the table keep
|
|
59
74
|
# whichever came last, so that is refused instead.
|
|
75
|
+
#
|
|
76
|
+
# @param rows [Array<Hash{Symbol => String}>] the keyword rows to build from,
|
|
77
|
+
# defaulting to every live one
|
|
78
|
+
# @return [Hash{Array(String, String) => Symbol}] each `calls:`-declaring row's
|
|
79
|
+
# `[context, word]` mapped to the method name it dispatches to
|
|
80
|
+
# @raise [Projections::BootstrapTable::Conflict] if two rows sharing a
|
|
81
|
+
# `[context, word]` name different methods
|
|
60
82
|
def calls(rows = live_keywords)
|
|
61
83
|
rows.reject { |row| row[:calls].to_s.empty? }
|
|
62
84
|
.group_by { |row| [row[:context], row[:word]] }
|
|
@@ -71,6 +93,12 @@ module Hecks
|
|
|
71
93
|
# `[word, context] => { resolves_via:, disambiguator: }` —
|
|
72
94
|
# RuleReference's own key order, blank columns omitted, the same
|
|
73
95
|
# shape its live `lookup` answers.
|
|
96
|
+
#
|
|
97
|
+
# @param rows [Array<Hash{Symbol => String}>] the keyword rows to build from,
|
|
98
|
+
# defaulting to every live one
|
|
99
|
+
# @return [Hash{Array(String, String) => Hash{Symbol => String}}] each
|
|
100
|
+
# `resolves_via:`-declaring row's `[word, context]` mapped to its rule Hash,
|
|
101
|
+
# with any blank `resolves_via`/`disambiguator` column omitted
|
|
74
102
|
def resolves(rows = live_keywords)
|
|
75
103
|
rows.reject { |row| row[:resolves_via].to_s.empty? }.to_h do |row|
|
|
76
104
|
rule = { resolves_via: row[:resolves_via], disambiguator: row[:disambiguator] }
|
|
@@ -78,6 +106,13 @@ module Hecks
|
|
|
78
106
|
end
|
|
79
107
|
end
|
|
80
108
|
|
|
109
|
+
# Renders the bootstrap table as committable Ruby source — `CALLS` and
|
|
110
|
+
# `RESOLVES` frozen Hash literals under the generated-file header.
|
|
111
|
+
#
|
|
112
|
+
# @param _bluebook [Bluebook::Chapter] ignored; the table draws from the whole
|
|
113
|
+
# language's grammar, not this argument
|
|
114
|
+
# @return [String] the full `lib/hecks/bluebook/dsl/bootstrap_table.rb` source,
|
|
115
|
+
# ready to write to disk
|
|
81
116
|
def render(_bluebook)
|
|
82
117
|
rows = live_keywords
|
|
83
118
|
calls_lines = calls(rows).map { |key, target| " #{key.inspect} => #{target.inspect}" }
|