hecks 1.4.0 → 1.5.1
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.bluebook +105 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/deploy/oidc.json +5 -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/compliance.bluebook +221 -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 +85 -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/deploy/fargate.rb +666 -0
- data/lib/hecks/projections/deploy/lambda.rb +2423 -0
- data/lib/hecks/projections/deploy/shared.rb +624 -0
- 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/projections.rb +3 -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 +47 -10
- data/lib/hecks/projector.rb +101 -19
- 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 +14 -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
- data/lib/hecks/framework/bluebook/compliance.bluebook +0 -1
|
@@ -14,14 +14,31 @@ module Hecks
|
|
|
14
14
|
|
|
15
15
|
attr_reader :root, :discovery, :register
|
|
16
16
|
|
|
17
|
+
# Discovers and loads every domain under `root` in one call.
|
|
18
|
+
#
|
|
19
|
+
# @param root [String] the directory to search under
|
|
20
|
+
# @return [Bluebook::ProjectLoader] the loader, with every discovered domain
|
|
21
|
+
# booted and registered
|
|
17
22
|
def self.load(root) = new(root).load
|
|
18
23
|
|
|
24
|
+
# @param root [String] the directory to search under
|
|
25
|
+
# @param discovery [Bluebook::ProjectDiscovery] the discovery this loader walks
|
|
26
|
+
# `root` with
|
|
27
|
+
# @param register [Bluebook::ProjectRegister] the register booted domains are
|
|
28
|
+
# fed into
|
|
19
29
|
def initialize(root, discovery: ProjectDiscovery.new(root), register: ProjectRegister.new)
|
|
20
30
|
@root = File.expand_path(root)
|
|
21
31
|
@discovery = discovery
|
|
22
32
|
@register = register
|
|
23
33
|
end
|
|
24
34
|
|
|
35
|
+
# Boots every domain `discovery` finds under `root` and registers its declarations.
|
|
36
|
+
#
|
|
37
|
+
# @return [Bluebook::ProjectLoader] self
|
|
38
|
+
# @raise [Bluebook::ProjectRegister::MissingRealm] see `ProjectRegister#register`
|
|
39
|
+
# @raise [Bluebook::ProjectRegister::LatestMismatch] see `ProjectRegister#register`
|
|
40
|
+
# @raise [Bluebook::ProjectRegister::DuplicateFqn] see `ProjectRegister#register`
|
|
41
|
+
# @raise [Runtime::WiringError] see `ProjectRegister#register`
|
|
25
42
|
def load
|
|
26
43
|
discovery.bluebook_directories.each do |directory|
|
|
27
44
|
runtime = Runtime.boot(directory)
|
|
@@ -30,10 +47,33 @@ module Hecks
|
|
|
30
47
|
self
|
|
31
48
|
end
|
|
32
49
|
|
|
50
|
+
# Lists every registered entry.
|
|
51
|
+
#
|
|
52
|
+
# @return [Hash{String => Bluebook::ProjectRegister::Entry}] every registered
|
|
53
|
+
# entry, keyed by its FQN
|
|
33
54
|
def entries = register.entries
|
|
55
|
+
|
|
56
|
+
# Finds a registered entry by its fully-qualified verb.
|
|
57
|
+
#
|
|
58
|
+
# @param address [String, #to_s] the FQN to look up
|
|
59
|
+
# @return [Bluebook::ProjectRegister::Entry] the entry registered under `address`
|
|
60
|
+
# @raise [KeyError] if no entry is registered under `address`
|
|
34
61
|
def fetch(address) = register.fetch(address)
|
|
62
|
+
|
|
63
|
+
# Says whether an entry is registered under an address.
|
|
64
|
+
#
|
|
65
|
+
# @param address [String, #to_s] the FQN to check
|
|
66
|
+
# @return [Boolean] whether an entry is registered under `address`
|
|
35
67
|
def include?(address) = register.include?(address)
|
|
68
|
+
|
|
69
|
+
# Lists every registered command entry.
|
|
70
|
+
#
|
|
71
|
+
# @return [Array<Bluebook::ProjectRegister::Entry>] every registered command entry
|
|
36
72
|
def commands = register.commands
|
|
73
|
+
|
|
74
|
+
# Lists every registered query entry.
|
|
75
|
+
#
|
|
76
|
+
# @return [Array<Bluebook::ProjectRegister::Entry>] every registered query entry
|
|
37
77
|
def queries = register.queries
|
|
38
78
|
end
|
|
39
79
|
end
|
|
@@ -4,7 +4,14 @@ module Hecks
|
|
|
4
4
|
# declarations become public FQNs, but never searches or boots folders.
|
|
5
5
|
class ProjectRegister
|
|
6
6
|
Entry = Struct.new(:fqn, :source_directory, :dispatcher, :declared_verb, :domain_version, keyword_init: true) do
|
|
7
|
+
# Says whether this entry addresses a command.
|
|
8
|
+
#
|
|
9
|
+
# @return [Boolean] whether this entry's `fqn` addresses a command
|
|
7
10
|
def command? = fqn.command?
|
|
11
|
+
|
|
12
|
+
# Says whether this entry addresses a query.
|
|
13
|
+
#
|
|
14
|
+
# @return [Boolean] whether this entry's `fqn` addresses a query
|
|
8
15
|
def query? = fqn.query?
|
|
9
16
|
end
|
|
10
17
|
|
|
@@ -14,16 +21,53 @@ module Hecks
|
|
|
14
21
|
|
|
15
22
|
attr_reader :entries
|
|
16
23
|
|
|
24
|
+
# Starts an empty register, with no domains loaded yet.
|
|
17
25
|
def initialize
|
|
18
26
|
@entries = {}
|
|
19
27
|
@tenant_directories = Hash.new { |hash, key| hash[key] = [] }
|
|
20
28
|
end
|
|
21
29
|
|
|
30
|
+
# Finds a registered entry by its fully-qualified verb.
|
|
31
|
+
#
|
|
32
|
+
# @param address [String, #to_s] the FQN to look up, such as `"Pizzas.Pizza.Order"`
|
|
33
|
+
# @return [Bluebook::ProjectRegister::Entry] the entry registered under `address`
|
|
34
|
+
# @raise [KeyError] if no entry is registered under `address`
|
|
22
35
|
def fetch(address) = entries.fetch(address.to_s)
|
|
36
|
+
|
|
37
|
+
# Says whether an entry is registered under an address.
|
|
38
|
+
#
|
|
39
|
+
# @param address [String, #to_s] the FQN to check
|
|
40
|
+
# @return [Boolean] whether an entry is registered under `address`
|
|
23
41
|
def include?(address) = entries.key?(address.to_s)
|
|
42
|
+
|
|
43
|
+
# Lists every registered command entry.
|
|
44
|
+
#
|
|
45
|
+
# @return [Array<Bluebook::ProjectRegister::Entry>] every registered command entry
|
|
24
46
|
def commands = entries.values.select(&:command?)
|
|
47
|
+
|
|
48
|
+
# Lists every registered query entry.
|
|
49
|
+
#
|
|
50
|
+
# @return [Array<Bluebook::ProjectRegister::Entry>] every registered query entry
|
|
25
51
|
def queries = entries.values.select(&:query?)
|
|
26
52
|
|
|
53
|
+
# Registers every aggregate command/query and read model query a booted chapter
|
|
54
|
+
# declares, under its versioned and (if current) unversioned FQNs.
|
|
55
|
+
#
|
|
56
|
+
# @param bluebooks [Array<Bluebook::Chapter>] the booted chapters to register
|
|
57
|
+
# @param registry [Runtime::Registry] the registry `bluebooks` booted into
|
|
58
|
+
# @param dispatcher [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted
|
|
59
|
+
# dispatcher entries dispatch through
|
|
60
|
+
# @param directory [String] the source directory `bluebooks` was discovered under
|
|
61
|
+
# @return [Bluebook::ProjectRegister] self
|
|
62
|
+
# @raise [Bluebook::ProjectRegister::MissingRealm] if a bluebook's world declares no
|
|
63
|
+
# realm
|
|
64
|
+
# @raise [Bluebook::ProjectRegister::LatestMismatch] if a world's `latest` disagrees
|
|
65
|
+
# with its bluebook's own version
|
|
66
|
+
# @raise [Bluebook::ProjectRegister::DuplicateFqn] if an FQN this call would register
|
|
67
|
+
# is already registered
|
|
68
|
+
# @raise [Runtime::WiringError] if a second tenant registers the same on-disk
|
|
69
|
+
# directory while any of its aggregates binds to an adapter that is not
|
|
70
|
+
# `tenant_capable?`
|
|
27
71
|
def register(bluebooks, registry, dispatcher, directory)
|
|
28
72
|
bluebooks.each do |bluebook|
|
|
29
73
|
refuse_unless_safe_for_second_tenant!(bluebook, registry, directory)
|
data/lib/hecks/bluebook/query.rb
CHANGED
|
@@ -6,6 +6,11 @@ module Hecks
|
|
|
6
6
|
# throughout this chapter reach for. See lib/hecks/bluebook.rb's own
|
|
7
7
|
# header for what `Bluebook` is as a whole.
|
|
8
8
|
module Bluebook
|
|
9
|
+
# Renders a captured Ruby value to its self-describing wire spelling.
|
|
10
|
+
#
|
|
11
|
+
# @param value [Object] any value an `emits_ir` field carries, such as a Symbol,
|
|
12
|
+
# String, Hash, Array, or literal
|
|
13
|
+
# @return [String] the value rendered through `Hecks::Literal.render`
|
|
9
14
|
def self.render_value(value) = Literal.render(value)
|
|
10
15
|
|
|
11
16
|
# A query — an ask, declared on an aggregate or on one of its entities.
|
|
@@ -40,6 +45,25 @@ module Hecks
|
|
|
40
45
|
|
|
41
46
|
attr_reader :name, :description, :attributes
|
|
42
47
|
|
|
48
|
+
# @param name [String, Symbol] the query's declared name
|
|
49
|
+
# @param description [String, nil] the query's declared prose description
|
|
50
|
+
# @param attributes [Array<Bluebook::Attribute>] the query's declared result fields
|
|
51
|
+
# @param wheres [Array<QuerySpecification::Common::WhereClause>] the declared filter
|
|
52
|
+
# conditions
|
|
53
|
+
# @param order_by [QuerySpecification::Common::OrderBy, nil] the declared sort field
|
|
54
|
+
# and direction, or `nil` for no explicit ordering
|
|
55
|
+
# @param limit [QuerySpecification::Common::LimitSpec, nil] the declared row limit,
|
|
56
|
+
# or `nil` for none
|
|
57
|
+
# @param offset [QuerySpecification::Common::OffsetSpec, nil] the declared row offset,
|
|
58
|
+
# or `nil` for none
|
|
59
|
+
# @param cursor [QuerySpecification::Common::CursorSpec, nil] the declared pagination
|
|
60
|
+
# cursor, or `nil` for none
|
|
61
|
+
# @param authorization [QuerySpecification::Common::AuthorizationSpec, nil] the declared
|
|
62
|
+
# authorization policy, or `nil` for none
|
|
63
|
+
# @param null_semantics [QuerySpecification::Common::NullSemantics, nil] how this query
|
|
64
|
+
# orders `nil` values; defaults to `NullSemantics.default` when omitted
|
|
65
|
+
# @param inspection [QuerySpecification::Common::InspectionSpec, nil] the declared
|
|
66
|
+
# inspection mode, or `nil` for none
|
|
43
67
|
def initialize(name:, description: nil, attributes: [], wheres: [],
|
|
44
68
|
order_by: nil, limit: nil, offset: nil, cursor: nil,
|
|
45
69
|
authorization: nil, null_semantics: nil,
|
|
@@ -59,6 +83,9 @@ module Hecks
|
|
|
59
83
|
# group_by, scope_to). Declared emission covers the settled part
|
|
60
84
|
# and `super` hands it over; the tail stays dynamic, which is the
|
|
61
85
|
# honest description of it.
|
|
86
|
+
#
|
|
87
|
+
# @return [Hash] the declared emission, merged with whatever `extra_options_to_h`
|
|
88
|
+
# the specification layer currently carries
|
|
62
89
|
def to_h = super.merge(extra_options_to_h)
|
|
63
90
|
end
|
|
64
91
|
end
|
|
@@ -31,9 +31,25 @@ module Hecks
|
|
|
31
31
|
|
|
32
32
|
# `reference_name:`/`reference_target:` are nil for a rootless read
|
|
33
33
|
# model (no `reference_to` declared) — `&.` throughout, rather than
|
|
34
|
-
#
|
|
34
|
+
# calling `.to_s`/`.to_sym` unconditionally, so
|
|
35
35
|
# `reference_target.nil?` stays a real, checkable fact for the
|
|
36
36
|
# interpreter instead of silently becoming `""`.
|
|
37
|
+
#
|
|
38
|
+
# @param name [String, Symbol] the read model's declared name
|
|
39
|
+
# @param description [String, nil] the read model's declared prose description
|
|
40
|
+
# @param reference_name [Symbol, String, nil] the local reference attribute this
|
|
41
|
+
# read model roots at, or `nil` for a rootless read model
|
|
42
|
+
# @param reference_target [String, Symbol, nil] the rooted aggregate's name, or
|
|
43
|
+
# `nil` for a rootless read model
|
|
44
|
+
# @param aggregate_heads [Array<Hash{Symbol => Object}>] the included aggregates, each
|
|
45
|
+
# a row with `:aggregate` (the aggregate's name), `:as` (the output field name) and
|
|
46
|
+
# `:many` (whether this head is a many-side join)
|
|
47
|
+
# @param group_by [Array<Hash{field: Symbol}>] the declared group-by fields, one row
|
|
48
|
+
# per field
|
|
49
|
+
# @param count [Boolean, nil] whether this read model reduces to a row count; `nil`
|
|
50
|
+
# when undeclared
|
|
51
|
+
# @param median_field [Symbol, nil] the field this read model reduces to the median
|
|
52
|
+
# of, or `nil` when undeclared
|
|
37
53
|
def initialize(name:, description: nil, reference_name: nil, reference_target: nil, aggregate_heads: [],
|
|
38
54
|
group_by: [], count: nil, median_field: nil, **)
|
|
39
55
|
super(joins: aggregate_heads, **)
|
|
@@ -94,6 +110,10 @@ module Hecks
|
|
|
94
110
|
# `WholeBluebook`, ...) for a fact nothing about them changed —
|
|
95
111
|
# the exact `extra_options_to_h` reads for `cursor`/`offset`/etc,
|
|
96
112
|
# applied here for the same reason.
|
|
113
|
+
#
|
|
114
|
+
# @return [Hash] the declared emission, with `aggregate_heads`/`group_by` rows
|
|
115
|
+
# stringified, `count`/`median_field` merged in only when declared, and
|
|
116
|
+
# `extra_options_to_h`'s own dynamic tail merged last
|
|
97
117
|
def to_h
|
|
98
118
|
reductions = {}
|
|
99
119
|
reductions[:count] = true if @count
|
|
@@ -2,15 +2,16 @@ module Hecks
|
|
|
2
2
|
module Bluebook
|
|
3
3
|
# An attribute that points at another aggregate's head.
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
5
|
+
# Holds the target directly, and answers `resolve` with the target's
|
|
6
|
+
# Aggregate, rather than being spelled as the string `"Reference<Customer>"`
|
|
7
|
+
# that `AggregateBuilder#reference_to` and `CommandBuilder#cross_reference`
|
|
8
|
+
# would otherwise mint from the real constant just handed in. That string
|
|
9
|
+
# would need five separate readers to parse back apart — a regex in the
|
|
10
|
+
# command interpreter, string equality in the read-model interpreter and
|
|
11
|
+
# the SQLite adapter, and `delete_prefix` in the bluebook builder — one
|
|
12
|
+
# writer inventing a spelling, five readers reparsing it.
|
|
11
13
|
#
|
|
12
|
-
#
|
|
13
|
-
# Aggregate. Resolution is lazy and deliberately so: `reference_to
|
|
14
|
+
# Resolution is lazy and deliberately so: `reference_to
|
|
14
15
|
# Customer` may name an aggregate declared lower in the file — banking's
|
|
15
16
|
# Account points at Customer and survives only because Customer happens to
|
|
16
17
|
# be written above — so the edge cannot be resolved at declaration time.
|
|
@@ -29,6 +30,8 @@ module Hecks
|
|
|
29
30
|
# up to the chapter, stamped once every sibling has been read.
|
|
30
31
|
attr_accessor :declared_in
|
|
31
32
|
|
|
33
|
+
# @param target_name [Module, String, Symbol] the aggregate constant this
|
|
34
|
+
# reference points at, or its already-spelled name
|
|
32
35
|
def initialize(target_name)
|
|
33
36
|
@target_name = Naming.demodulise(target_name).to_s
|
|
34
37
|
end
|
|
@@ -36,6 +39,11 @@ module Hecks
|
|
|
36
39
|
# The Aggregate this points at, or nil when the target belongs to
|
|
37
40
|
# another domain — a cross-domain target may legitimately not be loaded,
|
|
38
41
|
# the same reading `across` policies get.
|
|
42
|
+
#
|
|
43
|
+
# @return [Bluebook::Aggregate, nil] the target aggregate, or `nil` when
|
|
44
|
+
# `declared_in`'s own chapter does not declare one by this name
|
|
45
|
+
# @raise [DSL::Malformed] if `declared_in` is unset, so there is no
|
|
46
|
+
# chapter to resolve the target against
|
|
39
47
|
def resolve
|
|
40
48
|
unless declared_in
|
|
41
49
|
raise DSL::Malformed,
|
|
@@ -48,6 +56,8 @@ module Hecks
|
|
|
48
56
|
end
|
|
49
57
|
|
|
50
58
|
# The IR spelling, the one the export carries.
|
|
59
|
+
#
|
|
60
|
+
# @return [String] `"Reference<TargetName>"`
|
|
51
61
|
def to_s = "Reference<#{@target_name}>"
|
|
52
62
|
def inspect = "#<Reference #{@target_name}>"
|
|
53
63
|
|
|
@@ -66,6 +76,9 @@ module Hecks
|
|
|
66
76
|
# (if same-shaped) owning aggregates, and `declared_in` is a
|
|
67
77
|
# cross-reference for `resolve`, not part of what this attribute
|
|
68
78
|
# itself is.
|
|
79
|
+
#
|
|
80
|
+
# @param other [Object] the value to compare against
|
|
81
|
+
# @return [Boolean] whether `other` is a `Reference` to the same target
|
|
69
82
|
def ==(other) = other.is_a?(Reference) && target_name == other.target_name
|
|
70
83
|
alias eql? ==
|
|
71
84
|
def hash = [self.class, target_name].hash
|
|
@@ -22,11 +22,15 @@ module Hecks
|
|
|
22
22
|
# report, per created root; every failure is collected and reported
|
|
23
23
|
# together rather than stopping at the first.
|
|
24
24
|
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
25
|
+
# ## What this cannot catch
|
|
26
|
+
#
|
|
27
|
+
# A call that dispatches cleanly but answers wrong — the read-model join
|
|
28
|
+
# bug this exact tool's own first user hit is the textbook case, a query
|
|
29
|
+
# that silently returned an empty array rather than raising.
|
|
30
|
+
# Smoke-testing proves nothing crashes; it does not prove the answer is
|
|
31
|
+
# the right one.
|
|
32
|
+
#
|
|
33
|
+
# ## False positives from a naive synthesizer
|
|
30
34
|
#
|
|
31
35
|
# A failure here is not necessarily a domain bug, either — it may
|
|
32
36
|
# just be `Synthesizer`'s own naive values (0, an empty list,
|
|
@@ -82,6 +86,11 @@ module Hecks
|
|
|
82
86
|
# measured, not hypothetical: it once left a stale `Widget` constant
|
|
83
87
|
# that corrupted an unrelated spec's own unrelated use of the same
|
|
84
88
|
# bare name.
|
|
89
|
+
#
|
|
90
|
+
# @param dir [String] path to a bootable hecks app directory — a saved
|
|
91
|
+
# domain, or a throwaway one rendered into a temp dir for this purpose
|
|
92
|
+
# @return [Array<Failure>] every dispatch or query failure collected,
|
|
93
|
+
# empty when `dir` declares no bluebook or nothing failed
|
|
85
94
|
def call(dir)
|
|
86
95
|
Dir.mktmpdir("hecks-smoke-") do |scratch|
|
|
87
96
|
isolate!(dir, scratch)
|
|
@@ -105,6 +114,11 @@ module Hecks
|
|
|
105
114
|
# excluded — those are earlier, superseded versions of the same
|
|
106
115
|
# domain; smoke-testing the current declaration is the point, not
|
|
107
116
|
# its whole history.
|
|
117
|
+
#
|
|
118
|
+
# @param dir [String] path to the source hecks app directory to isolate
|
|
119
|
+
# @param scratch [String] path to the throwaway directory to copy the
|
|
120
|
+
# `.bluebook` files into
|
|
121
|
+
# @return [void]
|
|
108
122
|
def isolate!(dir, scratch)
|
|
109
123
|
source = Adapters::Folder.new.bluebook_directory(dir)
|
|
110
124
|
target = File.join(scratch, "bluebook")
|
|
@@ -123,6 +137,14 @@ module Hecks
|
|
|
123
137
|
# documents; splitting would turn that ordering into an implicit contract on
|
|
124
138
|
# parameter/return passing instead of one visible sequence.
|
|
125
139
|
# rubocop:disable-next Metrics/AbcSize
|
|
140
|
+
#
|
|
141
|
+
# @param dispatcher [Runtime::Dispatcher, Runtime::RemoteDispatcher] the
|
|
142
|
+
# booted dispatcher to dispatch synthesized commands and queries through
|
|
143
|
+
# @param domain [String] the domain name to smoke-test, a key of
|
|
144
|
+
# `dispatcher.registry.bluebooks`
|
|
145
|
+
# @return [Array<Failure>] every dispatch or query failure collected, in
|
|
146
|
+
# declaration order; empty when `domain` names no loaded chapter or
|
|
147
|
+
# nothing failed
|
|
126
148
|
def smoke_domain(dispatcher, domain)
|
|
127
149
|
chapter = dispatcher.registry.bluebook(domain)
|
|
128
150
|
return [] unless chapter
|
|
@@ -156,7 +178,8 @@ module Hecks
|
|
|
156
178
|
|
|
157
179
|
chapter.read_models.each do |model|
|
|
158
180
|
root_id = created[model.reference_target]
|
|
159
|
-
|
|
181
|
+
# Nothing was created for this root — nothing to smoke-test yet, not a failure.
|
|
182
|
+
next unless root_id
|
|
160
183
|
|
|
161
184
|
dispatcher.query("#{domain}.#{model.query_name}", model.reference_name => root_id)
|
|
162
185
|
rescue StandardError => e
|
|
@@ -42,6 +42,17 @@ module Hecks
|
|
|
42
42
|
# the same run; a reference whose target isn't in there yet gets
|
|
43
43
|
# a placeholder instead of failing outright, since the caller may
|
|
44
44
|
# not care about that particular argument's real value.
|
|
45
|
+
#
|
|
46
|
+
# @param chapter [Bluebook::Chapter] the chapter the command's aggregate
|
|
47
|
+
# belongs to, needed to resolve a value object type name back to its
|
|
48
|
+
# own declared shape
|
|
49
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate `command` belongs to
|
|
50
|
+
# @param command [Class] the command class (a `Bluebook::Command`
|
|
51
|
+
# subclass) to synthesize arguments for
|
|
52
|
+
# @param created [Hash{String => Object}] aggregate name mapped to a real
|
|
53
|
+
# id already minted for it earlier in this same run
|
|
54
|
+
# @return [Hash{Symbol => Object}] one synthesized value per declared
|
|
55
|
+
# attribute, keyed by attribute name
|
|
45
56
|
def args_for(chapter, aggregate, command, created = {})
|
|
46
57
|
command.attributes.to_h do |attribute|
|
|
47
58
|
if attribute.reference?
|
|
@@ -59,6 +70,16 @@ module Hecks
|
|
|
59
70
|
# same tolerance `bin/interview shape`'s own lookup already
|
|
60
71
|
# needed: a value object referenced by name doesn't have to be
|
|
61
72
|
# declared on the same aggregate using it.
|
|
73
|
+
#
|
|
74
|
+
# @param chapter [Bluebook::Chapter] the chapter to search when
|
|
75
|
+
# `type_name` is not declared on `aggregate` itself
|
|
76
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate `type_name` is
|
|
77
|
+
# looked up on first
|
|
78
|
+
# @param type_name [String] the value object's declared type name
|
|
79
|
+
# @return [String, Hash{Symbol => Object}] the string `"smoke-test"` when
|
|
80
|
+
# no such value object is declared; otherwise a Hash of one value per
|
|
81
|
+
# field — the closed set's own first admitted member's fields, or one
|
|
82
|
+
# freshly synthesized scalar per declared field
|
|
62
83
|
def value_for(chapter, aggregate, type_name)
|
|
63
84
|
value_object = aggregate.value_object(type_name) ||
|
|
64
85
|
chapter.aggregates.filter_map { |a| a.value_object(type_name) }.first
|
|
@@ -75,6 +96,13 @@ module Hecks
|
|
|
75
96
|
# another value object, resolved the identical way `value_for`
|
|
76
97
|
# resolves any other one (its own closed set or its own nested
|
|
77
98
|
# fields, however deep that nesting actually goes).
|
|
99
|
+
#
|
|
100
|
+
# @param chapter [Bluebook::Chapter] see `value_for`
|
|
101
|
+
# @param aggregate [Bluebook::Aggregate] see `value_for`
|
|
102
|
+
# @param type_name [String] the field's declared type name
|
|
103
|
+
# @return [Integer, Float, true, false, String, Hash{Symbol => Object}] a
|
|
104
|
+
# bare scalar when `type_name` is a true primitive (see `scalar_for`),
|
|
105
|
+
# otherwise `value_for`'s own result for the nested value object it names
|
|
78
106
|
def field_value_for(chapter, aggregate, type_name)
|
|
79
107
|
PRIMITIVES.include?(type_name.to_s) ? scalar_for(type_name) : value_for(chapter, aggregate, type_name)
|
|
80
108
|
end
|
|
@@ -83,6 +111,12 @@ module Hecks
|
|
|
83
111
|
# closed set's own field (that's `value_for`'s job, reading the
|
|
84
112
|
# set's real first member) — only for a plain scalar field with
|
|
85
113
|
# no declared vocabulary to respect.
|
|
114
|
+
#
|
|
115
|
+
# @param primitive [String, Symbol] the primitive type name, such as
|
|
116
|
+
# `"Integer"` or `"String"`
|
|
117
|
+
# @return [Integer, Float, false, String] `0` for `"Integer"`, `0.0` for
|
|
118
|
+
# `"Float"`, `false` for `"TrueClass"`/`"FalseClass"`, or the string
|
|
119
|
+
# `"smoke-test"` for anything else
|
|
86
120
|
def scalar_for(primitive)
|
|
87
121
|
case primitive.to_s
|
|
88
122
|
when "Integer" then 0
|
|
@@ -19,7 +19,7 @@ module Hecks
|
|
|
19
19
|
# A value object's or entity's own type name changed with its member
|
|
20
20
|
# structure unchanged — the one drift `rename`/`move` cannot express,
|
|
21
21
|
# because the attribute kept its name and only the type it points at
|
|
22
|
-
#
|
|
22
|
+
# changed name. Mirrors `was:` one level deeper.
|
|
23
23
|
TranslationRetype = Struct.new(:from, :to)
|
|
24
24
|
|
|
25
25
|
# A computed transform — rescale, reformat, split, merge — whose only
|
|
@@ -57,6 +57,23 @@ module Hecks
|
|
|
57
57
|
class TranslationAggregate
|
|
58
58
|
attr_reader :name, :was, :renames, :moves, :converts, :drops, :retypes, :computes, :rekeys, :backfills
|
|
59
59
|
|
|
60
|
+
# @param name [String, Symbol] the aggregate's name in the destination era
|
|
61
|
+
# @param was [String, Symbol, nil] the aggregate's name in the origin era, or `nil`
|
|
62
|
+
# if it was not renamed
|
|
63
|
+
# @param renames [Hash{Symbol => Symbol}] each renamed field, old name to new name
|
|
64
|
+
# @param moves [Array<Bluebook::TranslationMove>] fields crossing a value-object
|
|
65
|
+
# boundary
|
|
66
|
+
# @param converts [Array<Bluebook::TranslationConvert>] fields whose value is
|
|
67
|
+
# remapped through a declared lookup table
|
|
68
|
+
# @param drops [Array<Symbol>] fields deliberately not carried forward
|
|
69
|
+
# @param retypes [Array<Bluebook::TranslationRetype>] value object or entity type
|
|
70
|
+
# renames with their member structure unchanged
|
|
71
|
+
# @param computes [Array<Bluebook::TranslationCompute>] fields computed by a SQL
|
|
72
|
+
# expression evaluated only inside the compiled Postgres head
|
|
73
|
+
# @param rekeys [Array<Bluebook::TranslationRekey>] SQL expressions that recompute
|
|
74
|
+
# the aggregate's own identity
|
|
75
|
+
# @param backfills [Array<Bluebook::TranslationBackfill>] newly added required
|
|
76
|
+
# fields with no source in old data
|
|
60
77
|
def initialize(name:, was: nil, renames: {}, moves: [], converts: [], drops: [], retypes: [],
|
|
61
78
|
computes: [], rekeys: [], backfills: [])
|
|
62
79
|
@name = name.to_s
|
|
@@ -81,6 +98,13 @@ module Hecks
|
|
|
81
98
|
class Translation
|
|
82
99
|
attr_reader :domain, :from, :to, :aggregates, :retired
|
|
83
100
|
|
|
101
|
+
# @param domain [String, Symbol] the domain this translation carries forward
|
|
102
|
+
# @param from [String, Symbol] the origin era
|
|
103
|
+
# @param to [String, Symbol] the destination era
|
|
104
|
+
# @param aggregates [Array<Bluebook::TranslationAggregate>] each aggregate's own
|
|
105
|
+
# translation rules
|
|
106
|
+
# @param retired [Array<String>] the names of aggregates gone outright in the
|
|
107
|
+
# destination era, rather than renamed
|
|
84
108
|
def initialize(domain:, from:, to:, aggregates: [], retired: [])
|
|
85
109
|
@domain = domain.to_s
|
|
86
110
|
@from = from
|
|
@@ -89,6 +113,11 @@ module Hecks
|
|
|
89
113
|
@retired = retired
|
|
90
114
|
end
|
|
91
115
|
|
|
116
|
+
# Finds one aggregate's own translation rules by its destination-era name.
|
|
117
|
+
#
|
|
118
|
+
# @param name [String, Symbol] the aggregate's name in the destination era
|
|
119
|
+
# @return [Bluebook::TranslationAggregate, nil] the aggregate's translation, or
|
|
120
|
+
# `nil` if `name` carries no translation rules
|
|
92
121
|
def for_aggregate(name) = @aggregates.find { |aggregate| aggregate.name == name.to_s }
|
|
93
122
|
end
|
|
94
123
|
end
|
|
@@ -42,11 +42,11 @@ module Hecks
|
|
|
42
42
|
# The field name is stringified, never the value. A `member` row can
|
|
43
43
|
# hold any of the scalar types an attribute declares — `Integer 84`
|
|
44
44
|
# (`StatementFrequency#retention_months`, statements.bluebook), not
|
|
45
|
-
# only `String` — and
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
45
|
+
# only `String` — and stringifying it on the way out would erase
|
|
46
|
+
# that: `84` and `"84"` (a member some other row might legitimately
|
|
47
|
+
# spell as text) would become indistinguishable once they reached
|
|
48
|
+
# `to_h`. The declared name still moves (`field.to_s`) — that half
|
|
49
|
+
# was never a Ruby object with a type to lose.
|
|
50
50
|
members: -> { members.map { |member| member.map { |field, value| [field.to_s, value] } } }
|
|
51
51
|
)
|
|
52
52
|
|
|
@@ -55,6 +55,17 @@ module Hecks
|
|
|
55
55
|
|
|
56
56
|
# One declared shape — a subclass rather than an instance, so the thing
|
|
57
57
|
# the bluebook declares and the thing Ruby holds are one object.
|
|
58
|
+
#
|
|
59
|
+
# @param name [String, Symbol] the value object's declared type name
|
|
60
|
+
# @param attributes [Array<Bluebook::Attribute>] the value object's declared
|
|
61
|
+
# fields
|
|
62
|
+
# @param invariants [Array<Bluebook::Invariant>] the rules checked against every
|
|
63
|
+
# instance of this value object
|
|
64
|
+
# @param members [Array<Hash{Symbol => Object}>] the declared `one_of` members, one
|
|
65
|
+
# row of field values per member
|
|
66
|
+
# @param closed_set [Boolean] whether a `one_of` was declared, even with no
|
|
67
|
+
# members; defaults to whether `members` is non-empty
|
|
68
|
+
# @return [Class] the minted shape class (a `Bluebook::ValueObject` subclass)
|
|
58
69
|
def declare(name:, attributes: [], invariants: [], members: [], closed_set: !members.empty?)
|
|
59
70
|
shape = Class.new(self)
|
|
60
71
|
shape.hecks_name = name.to_s
|
|
@@ -63,6 +74,13 @@ module Hecks
|
|
|
63
74
|
shape
|
|
64
75
|
end
|
|
65
76
|
|
|
77
|
+
# Assigns what the language declares onto this shape class.
|
|
78
|
+
#
|
|
79
|
+
# @param attributes [Array<Bluebook::Attribute>] see `declare`
|
|
80
|
+
# @param invariants [Array<Bluebook::Invariant>] see `declare`
|
|
81
|
+
# @param members [Array<Hash{Symbol => Object}>] see `declare`
|
|
82
|
+
# @param closed_set [Boolean] see `declare`
|
|
83
|
+
# @return [void]
|
|
66
84
|
def absorb(attributes:, invariants:, members:, closed_set:)
|
|
67
85
|
@attributes = attributes
|
|
68
86
|
@invariants = invariants
|
data/lib/hecks/bluebook.rb
CHANGED
|
@@ -7,10 +7,9 @@
|
|
|
7
7
|
# DSL (dsl/), and the meta-validator that judges a chapter against the
|
|
8
8
|
# language's own grammar (meta_validator.rb + meta_validator/).
|
|
9
9
|
#
|
|
10
|
-
# The require order below
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# the meta-validator that its builders call at build time.
|
|
10
|
+
# The require order below matters: expression and IR load first (pure
|
|
11
|
+
# declarations), assembly's collaborators load before its face, and the DSL
|
|
12
|
+
# loads before the meta-validator that its builders call at build time.
|
|
14
13
|
|
|
15
14
|
module Hecks
|
|
16
15
|
# Declared as a class, not a module — `Hecks::Bluebook` is a
|