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
|
@@ -5,6 +5,10 @@ module Hecks
|
|
|
5
5
|
# the states and the handlers; the compensation half — `saga` — is
|
|
6
6
|
# derived from the handler that answers a refusal, not declared.
|
|
7
7
|
module ProcessManager
|
|
8
|
+
# The bluebook's name for this construct.
|
|
9
|
+
#
|
|
10
|
+
# @return [String, nil] the process manager's declared name, or `nil` before it
|
|
11
|
+
# is set
|
|
8
12
|
def hecks_name = @name
|
|
9
13
|
|
|
10
14
|
# The leg that answers — selected by (event, current state), C10.3
|
|
@@ -17,12 +21,27 @@ module Hecks
|
|
|
17
21
|
# selection). Build refuses two legs on one (event, state) pair
|
|
18
22
|
# (`ProcessManagerBuilder#validate!`), so a stated lookup is
|
|
19
23
|
# unambiguous by construction.
|
|
24
|
+
#
|
|
25
|
+
# @param event [String, Symbol] the triggering event's name
|
|
26
|
+
# @param state [String, Symbol, nil] the instance's current state; `nil` asks
|
|
27
|
+
# declaratively, ignoring state
|
|
28
|
+
# @return [Bluebook::ProcessManagerHandler, nil] the matching handler row, or `nil`
|
|
29
|
+
# if no declared leg answers this (event, state) pair
|
|
20
30
|
def handler_for(event, state = nil)
|
|
21
31
|
@handlers.find { |h| h.event_type == event.to_s && (state.nil? || h.from_state == state.to_s) }
|
|
22
32
|
end
|
|
23
33
|
|
|
34
|
+
# Finds every declared leg for an event, regardless of source state.
|
|
35
|
+
#
|
|
36
|
+
# @param event [String, Symbol] the triggering event's name
|
|
37
|
+
# @return [Array<Bluebook::ProcessManagerHandler>] every handler row declared for
|
|
38
|
+
# `event`; `[]` if none is declared
|
|
24
39
|
def handlers_for(event) = @handlers.select { |h| h.event_type == event.to_s }
|
|
25
40
|
|
|
41
|
+
# Says whether any declared leg answers an event.
|
|
42
|
+
#
|
|
43
|
+
# @param event [String, Symbol] the triggering event's name
|
|
44
|
+
# @return [Boolean] whether any handler row is declared for `event`
|
|
26
45
|
def handles?(event) = @handlers.any? { |h| h.event_type == event.to_s }
|
|
27
46
|
|
|
28
47
|
# Whether a state is one this procedure declares — asked of a value
|
|
@@ -30,15 +49,24 @@ module Hecks
|
|
|
30
49
|
# state), the way `Lifecycle#states` is asked of an aggregate's
|
|
31
50
|
# resting field. A rehydrated instance in a state no handler could
|
|
32
51
|
# have reached is corruption the durable round-trip introduced.
|
|
52
|
+
#
|
|
53
|
+
# @param state [String, Symbol] the state to check
|
|
54
|
+
# @return [Boolean] whether `state` is one of this procedure's declared states
|
|
33
55
|
def declares_state?(state) = @states.map(&:to_s).include?(state.to_s)
|
|
34
56
|
|
|
57
|
+
# Names the payload field a triggering event's instances are correlated by.
|
|
58
|
+
#
|
|
59
|
+
# @return [Symbol] the head of `correlates_by` — the part before the first `.`
|
|
35
60
|
def correlation_head = @correlates_by.to_s.split(".").first.to_sym
|
|
36
61
|
|
|
37
62
|
# The compensation half of a procedure, read off the handler that
|
|
38
|
-
# answers REFUSED
|
|
63
|
+
# answers `REFUSED`.
|
|
39
64
|
#
|
|
40
65
|
# nil for a procedure with no answer to a refusal, which is a legitimate
|
|
41
66
|
# thing to be — a hiring pipeline cannot un-interview anybody.
|
|
67
|
+
#
|
|
68
|
+
# @return [Bluebook::Saga, nil] the derived saga, or `nil` if no handler answers
|
|
69
|
+
# `REFUSED`
|
|
42
70
|
def saga
|
|
43
71
|
leg = handler_for(Bluebook::ProcessManager::REFUSED)
|
|
44
72
|
return nil unless leg
|
|
@@ -60,6 +88,9 @@ module Hecks
|
|
|
60
88
|
to_state: leg.to_state, compensations: derived + leg.dispatches)
|
|
61
89
|
end
|
|
62
90
|
|
|
91
|
+
# Says whether this procedure declares a handler for `REFUSED`.
|
|
92
|
+
#
|
|
93
|
+
# @return [Boolean] whether this procedure has a derivable saga
|
|
63
94
|
def saga? = !saga.nil?
|
|
64
95
|
end
|
|
65
96
|
end
|
|
@@ -3,6 +3,11 @@ module Hecks
|
|
|
3
3
|
module Behaviour
|
|
4
4
|
# What a query does beyond holding its declared shape.
|
|
5
5
|
module Query
|
|
6
|
+
# Finds a declared result attribute by name.
|
|
7
|
+
#
|
|
8
|
+
# @param named [String, Symbol] the attribute's declared name
|
|
9
|
+
# @return [Bluebook::Attribute, nil] the attribute, or `nil` if none is declared
|
|
10
|
+
# by that name
|
|
6
11
|
def attribute(named) = @attributes.find { |a| a.name == named.to_sym }
|
|
7
12
|
end
|
|
8
13
|
end
|
|
@@ -4,14 +4,22 @@ module Hecks
|
|
|
4
4
|
# **What a read model does**. Its declared half is the gathered heads
|
|
5
5
|
# and the query shape; these are readings taken off them.
|
|
6
6
|
module ReadModel
|
|
7
|
+
# Lists the fields this read model groups rows by.
|
|
8
|
+
#
|
|
9
|
+
# @return [Array<Symbol>] each declared `group_by` field's name, in declaration order
|
|
7
10
|
def group_by_fields = @group_by.map { |row| row[:field].to_sym }
|
|
8
11
|
|
|
9
12
|
# `!!` rather than a bare `@count` — the DSL/reconstruction
|
|
10
13
|
# boundary (ReadModel#initialize) already normalises to
|
|
11
14
|
# `true`/`nil`, so this is belt and braces against a future
|
|
12
15
|
# caller constructing a ReadModel by hand with `count: false`.
|
|
16
|
+
#
|
|
17
|
+
# @return [Boolean] whether this read model reduces to a row count
|
|
13
18
|
def count? = !!@count
|
|
14
19
|
|
|
20
|
+
# Names the verb `Dispatcher#query` looks this read model up by.
|
|
21
|
+
#
|
|
22
|
+
# @return [String] this read model's name in `snake_case`
|
|
15
23
|
def query_name = Naming.snake(@name)
|
|
16
24
|
|
|
17
25
|
# Which gathered heads the filtering applies to (ADR 0055) — plural,
|
|
@@ -22,6 +30,9 @@ module Hecks
|
|
|
22
30
|
# single-head-only — ADR 0055) applies to it, same as before `on:`
|
|
23
31
|
# existed. With several many-side heads, only the ones actually named
|
|
24
32
|
# by a targeted option are eligible.
|
|
33
|
+
#
|
|
34
|
+
# @return [Array<Symbol>] the `:as` name of each many-side head that filtering
|
|
35
|
+
# applies to; `[]` if this read model has no many-side head
|
|
25
36
|
def filtered_head_names
|
|
26
37
|
many = @aggregate_heads.select { |head| head[:many] }
|
|
27
38
|
return [] if many.empty?
|
|
@@ -37,6 +48,11 @@ module Hecks
|
|
|
37
48
|
# `median`, still single-head-only) applies to it — split out only
|
|
38
49
|
# to keep `filtered_head_names` itself under this file's own
|
|
39
50
|
# complexity budget, not because the two questions differ in kind.
|
|
51
|
+
#
|
|
52
|
+
# @param many [Array<Hash{Symbol => Object}>] the read model's many-side
|
|
53
|
+
# `aggregate_heads` rows; must hold exactly one
|
|
54
|
+
# @return [Array<Symbol>] `[the one head's :as name]` if any filtering option is
|
|
55
|
+
# declared, else `[]`
|
|
40
56
|
def single_filtered_head_name(many)
|
|
41
57
|
declared = wheres.any? || order_by || limit || offset || authorization&.tenant ||
|
|
42
58
|
@group_by.any? || count? || @median_field
|
|
@@ -53,6 +69,11 @@ module Hecks
|
|
|
53
69
|
# resolves to `head_as`'s own aggregate.
|
|
54
70
|
FilteredOptions = Struct.new(:wheres, :order_by, :limit, :offset, :null_semantics)
|
|
55
71
|
|
|
72
|
+
# Scopes this read model's filtering options down to one included head.
|
|
73
|
+
#
|
|
74
|
+
# @param head_as [Symbol] the `:as` name of the head to scope filtering to
|
|
75
|
+
# @return [FilteredOptions] the `wheres`/`order_by`/`limit`/`offset` that apply to
|
|
76
|
+
# `head_as`, and this read model's own `null_semantics`
|
|
56
77
|
def options_for(head_as)
|
|
57
78
|
many = @aggregate_heads.select { |head| head[:many] }
|
|
58
79
|
aggregate_name = @aggregate_heads.find { |head| head[:as] == head_as }&.fetch(:aggregate)
|
|
@@ -28,6 +28,10 @@ module Hecks
|
|
|
28
28
|
# no single head, and answering with the first would be a guess ; the
|
|
29
29
|
# readers that need all of them ask for `identity_heads`.
|
|
30
30
|
module Identified
|
|
31
|
+
# Derives `identity_paths`, `identity_heads` and the single-head form of
|
|
32
|
+
# `identified_by` from the declared `identified_by` path(s).
|
|
33
|
+
#
|
|
34
|
+
# @return [void]
|
|
31
35
|
def derive_identity
|
|
32
36
|
@identity_paths = Array(@identified_by).map(&:to_s).reject(&:empty?)
|
|
33
37
|
@identity_heads = @identity_paths.map { |path| path.split(".").first.to_sym }.uniq
|
|
@@ -48,6 +52,10 @@ module Hecks
|
|
|
48
52
|
module Indexed
|
|
49
53
|
# Keyed by symbol — an attribute is asked for by its declared
|
|
50
54
|
# symbol name everywhere in the runtime.
|
|
55
|
+
#
|
|
56
|
+
# @param attributes [Array<Bluebook::Attribute>] the construct's declared
|
|
57
|
+
# attributes
|
|
58
|
+
# @return [void]
|
|
51
59
|
def index_attributes(attributes)
|
|
52
60
|
@attributes_by_name = attributes.to_h { |held| [held.name, held] }
|
|
53
61
|
end
|
|
@@ -56,12 +64,34 @@ module Hecks
|
|
|
56
64
|
# object is a construct whose Ruby `name` is something else
|
|
57
65
|
# entirely (a constant path, or nothing at all for an anonymous
|
|
58
66
|
# class), so the declared name is the only one worth indexing.
|
|
67
|
+
#
|
|
68
|
+
# @param collection [Array<#hecks_name>] declared constructs to index —
|
|
69
|
+
# value objects, commands or queries, each answering `hecks_name`
|
|
70
|
+
# @return [Hash{String => Object}] the collection keyed by each member's
|
|
71
|
+
# `hecks_name`
|
|
59
72
|
def index_by_hecks_name(collection)
|
|
60
73
|
collection.to_h { |held| [held.hecks_name, held] }
|
|
61
74
|
end
|
|
62
75
|
|
|
76
|
+
# Finds a declared attribute by its declared name.
|
|
77
|
+
#
|
|
78
|
+
# @param named [String, Symbol] the attribute's declared name
|
|
79
|
+
# @return [Bluebook::Attribute, nil] the attribute named `named`, or `nil`
|
|
80
|
+
# if none is declared under that name
|
|
63
81
|
def attribute(named) = @attributes_by_name[named.to_sym]
|
|
82
|
+
|
|
83
|
+
# Finds a declared command by its declared name.
|
|
84
|
+
#
|
|
85
|
+
# @param named [String, Symbol] the command's declared name
|
|
86
|
+
# @return [Class, nil] the command class (a `Bluebook::Command` subclass)
|
|
87
|
+
# named `named`, or `nil` if none is declared under that name
|
|
64
88
|
def command(named) = @commands_by_name[named.to_s]
|
|
89
|
+
|
|
90
|
+
# Finds a declared query by its declared name.
|
|
91
|
+
#
|
|
92
|
+
# @param named [String, Symbol] the query's declared name
|
|
93
|
+
# @return [Bluebook::Query, nil] the query named `named`, or `nil` if none
|
|
94
|
+
# is declared under that name
|
|
65
95
|
def query(named) = @queries_by_name[named.to_s]
|
|
66
96
|
end
|
|
67
97
|
|
|
@@ -72,6 +102,12 @@ module Hecks
|
|
|
72
102
|
# is complete is the right stamping point: its declarations are
|
|
73
103
|
# final, and nothing outside needs to remember to stamp them.
|
|
74
104
|
module Owns
|
|
105
|
+
# Stamps each of `children` as owned by this construct.
|
|
106
|
+
#
|
|
107
|
+
# @param children [Array<Object>] the construct's own declared children —
|
|
108
|
+
# arrays or bare objects, flattened before stamping — each answering
|
|
109
|
+
# `hecks_owner=`
|
|
110
|
+
# @return [void]
|
|
75
111
|
def stamp(*children)
|
|
76
112
|
children.flatten.each { |child| child.hecks_owner = self }
|
|
77
113
|
end
|
|
@@ -6,13 +6,22 @@ module Hecks
|
|
|
6
6
|
# behaviour is singleton behaviour, and the holding half's `absorb`
|
|
7
7
|
# is what a generated constructor would be.
|
|
8
8
|
module ValueObject
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
9
|
+
# Recorded as its own fact (`closed_set`), not left to be inferred from
|
|
10
|
+
# `members: []` alone — an empty `one_of` and no `one_of` at all would
|
|
11
|
+
# otherwise be indistinguishable, which would leave the rule about it
|
|
12
|
+
# only livable in the builder. Recording the declaration lets the
|
|
13
|
+
# language judge it instead, the same way an empty attribute name
|
|
14
|
+
# survives into the IR and is judged there.
|
|
15
|
+
#
|
|
16
|
+
# @return [Boolean] whether this value object declares `one_of`, even
|
|
17
|
+
# if left empty
|
|
14
18
|
def closed_set? = @closed_set
|
|
15
19
|
|
|
20
|
+
# Finds a declared attribute by its declared name.
|
|
21
|
+
#
|
|
22
|
+
# @param named [String, Symbol] the attribute's declared name
|
|
23
|
+
# @return [Bluebook::Attribute, nil] the attribute named `named`, or
|
|
24
|
+
# `nil` if none is declared under that name
|
|
16
25
|
def attribute(named) = attributes.find { |held| held.name == named.to_sym }
|
|
17
26
|
|
|
18
27
|
# A single-attribute value object (EmailAddress{address},
|
|
@@ -30,6 +39,9 @@ module Hecks
|
|
|
30
39
|
# `retention_months`/`paper_fee_cents`). `sole_attribute` would
|
|
31
40
|
# return `nil` for that shape and break the discriminant lookup —
|
|
32
41
|
# left as `.first` on purpose, not a missed migration.
|
|
42
|
+
#
|
|
43
|
+
# @return [Bluebook::Attribute, nil] this value object's only
|
|
44
|
+
# attribute, or `nil` when it has zero or more than one
|
|
33
45
|
def sole_attribute
|
|
34
46
|
attributes.first if attributes.size == 1
|
|
35
47
|
end
|
|
@@ -63,6 +63,12 @@ module Hecks
|
|
|
63
63
|
# ("RoleAssignment.Assign"); `Behaviour::Chapter#provided_verb`
|
|
64
64
|
# qualifies it with the chapter's own name.
|
|
65
65
|
Provision = Struct.new(:capability, :key, :verb, keyword_init: true) do
|
|
66
|
+
# Coerces one declared or reconstructed `provides` row into a `Provision`.
|
|
67
|
+
#
|
|
68
|
+
# @param row [Bluebook::Chapter::Provision, #to_h] a `Provision` already, or
|
|
69
|
+
# anything answering `to_h` with `capability`/`key`/`verb` entries
|
|
70
|
+
# @return [Bluebook::Chapter::Provision] `row` itself if it already is one, else
|
|
71
|
+
# a new `Provision` built from its fields
|
|
66
72
|
def self.from(row)
|
|
67
73
|
return row if row.is_a?(self)
|
|
68
74
|
|
|
@@ -74,6 +80,23 @@ module Hecks
|
|
|
74
80
|
attr_reader :name, :version, :vision, :aggregates, :policies, :process_managers,
|
|
75
81
|
:classification, :read_models, :ports, :formerly_known_as, :attaches_to, :provides
|
|
76
82
|
|
|
83
|
+
# @param name [String, Symbol] the chapter's declared name
|
|
84
|
+
# @param version [String, Symbol, nil] the chapter's declared business version,
|
|
85
|
+
# such as `"v1"`, or `nil` if it declares none
|
|
86
|
+
# @param vision [String, nil] the chapter's declared vision statement
|
|
87
|
+
# @param aggregates [Array<Bluebook::Aggregate>] the chapter's declared aggregates
|
|
88
|
+
# @param policies [Array<Bluebook::Policy>] every reaction declared across the
|
|
89
|
+
# chapter's own aggregates, hoisted here
|
|
90
|
+
# @param process_managers [Array<Bluebook::ProcessManager>] the chapter's declared
|
|
91
|
+
# process managers
|
|
92
|
+
# @param classification [String, Symbol, nil] whether this chapter is central to
|
|
93
|
+
# its project's own domain model, or `nil` if undeclared
|
|
94
|
+
# @param read_models [Array<Bluebook::ReadModel>] the chapter's declared read models
|
|
95
|
+
# @param formerly_known_as [String, Symbol, nil] an earlier name this domain was
|
|
96
|
+
# known under, or `nil` if it was never renamed
|
|
97
|
+
# @param attaches_to [Array<String, Symbol>] the contexts this chapter attaches to
|
|
98
|
+
# @param provides [Array<Bluebook::Chapter::Provision, Hash>] the capabilities this
|
|
99
|
+
# chapter declares it provides
|
|
77
100
|
def initialize(name:, version: nil, vision: nil, aggregates: [], policies: [],
|
|
78
101
|
process_managers: [], classification: nil, read_models: [], formerly_known_as: nil,
|
|
79
102
|
attaches_to: [], provides: [])
|
|
@@ -10,14 +10,12 @@ module Hecks
|
|
|
10
10
|
include Hecks::IR
|
|
11
11
|
include Behaviour::Mutation
|
|
12
12
|
|
|
13
|
-
# `sign:` — item #5 of the whole-project table-unification survey
|
|
14
|
-
# `increment`/`decrement`'s own +1/-1
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# (Runtime::CommandRules::Arithmetic::MUTATION_OPS, itself read off
|
|
20
|
-
# the same generated Vocabulary::MutationOp table).
|
|
13
|
+
# `sign:` — item #5 of the whole-project table-unification survey:
|
|
14
|
+
# `increment`/`decrement`'s own +1/-1 comes from `Vocabulary::MutationOp`
|
|
15
|
+
# here, the same table `Runtime::CommandRules::Arithmetic::MUTATION_OPS`
|
|
16
|
+
# already reads on the Ruby runtime side, rather than a ternary on the op
|
|
17
|
+
# name re-deriving it independently in each of two Rust codegen scripts
|
|
18
|
+
# (rust/project/mutations.rb, rust/codegen/src/mutations.rs).
|
|
21
19
|
# Reads `Vocabulary::MutationOp` directly (plain data, no framework
|
|
22
20
|
# dependency — safe during parsing, same reason `RuleReference`'s
|
|
23
21
|
# own bootstrap concerns don't apply here) rather than requiring
|
|
@@ -25,6 +23,11 @@ module Hecks
|
|
|
25
23
|
# real layering inversion (runtime depends on bluebook, not the
|
|
26
24
|
# reverse). "" (not nil) for ops with no sign, matching every other
|
|
27
25
|
# optional IR text field's own absent-is-empty-string convention.
|
|
26
|
+
#
|
|
27
|
+
# @param oper [String, Symbol] the mutation's operation name, such as
|
|
28
|
+
# `"increment"` or `"decrement"`
|
|
29
|
+
# @return [String] the operation's sign from `Vocabulary::MutationOp`
|
|
30
|
+
# (`"+"` or `"-"`), or `""` when the op has none or is not found
|
|
28
31
|
def self.sign_for(oper)
|
|
29
32
|
Hecks::Vocabulary.rows("MutationOp").find { |row| row["name"] == oper.to_s }&.fetch("sign", "") || ""
|
|
30
33
|
end
|
|
@@ -39,6 +42,10 @@ module Hecks
|
|
|
39
42
|
# emission covers the fixed head; `super` supplies it and this adds
|
|
40
43
|
# the tail, which is why a construct with a variable shape needs no
|
|
41
44
|
# new mixin API.
|
|
45
|
+
# @return [Hash{Symbol => Object}] the declared emission — `super`'s fixed
|
|
46
|
+
# head, plus `fields:` (a Hash of bound field values) for an
|
|
47
|
+
# append-shaped op, or `source:` (`classified_source`'s own result) for
|
|
48
|
+
# any other
|
|
42
49
|
def to_h
|
|
43
50
|
return super.merge(fields: appended_fields) if [:append, :delegate, :corrects].include?(op)
|
|
44
51
|
|
|
@@ -95,6 +102,29 @@ module Hecks
|
|
|
95
102
|
attr_reader :role, :goal, :attributes, :givens, :ensures, :mutations, :emits, :references,
|
|
96
103
|
:from, :provenance
|
|
97
104
|
|
|
105
|
+
# Mints one command as its own anonymous class, a subclass of whichever
|
|
106
|
+
# `Command`-descended class `declare` is called on.
|
|
107
|
+
#
|
|
108
|
+
# @param name [String, Symbol] the command's declared name
|
|
109
|
+
# @param role [String, nil] the command's declared role text
|
|
110
|
+
# @param goal [String, nil] the command's declared goal text
|
|
111
|
+
# @param attributes [Array<Bluebook::Attribute>] the command's declared
|
|
112
|
+
# arguments
|
|
113
|
+
# @param givens [Array<Bluebook::Given>] the command's declared
|
|
114
|
+
# preconditions
|
|
115
|
+
# @param ensures [Array<Bluebook::Given>] the command's declared
|
|
116
|
+
# postconditions
|
|
117
|
+
# @param mutations [Array<Bluebook::Mutation>] the state changes this
|
|
118
|
+
# command applies
|
|
119
|
+
# @param emits [Array<String>] the event names this command may emit
|
|
120
|
+
# @param references [String, Symbol, nil] the aggregate name this verb's
|
|
121
|
+
# `reference_to` addresses, or `nil` for a verb declared on the
|
|
122
|
+
# aggregate it acts on
|
|
123
|
+
# @param from [String, Array<String>, nil] the lifecycle state(s) this
|
|
124
|
+
# command is admissible from, or `nil` for no such guard
|
|
125
|
+
# @param provenance [Object, nil] the command's declared canonical
|
|
126
|
+
# source, captured exactly as written, or `nil` if it declares none
|
|
127
|
+
# @return [Class] the new command class
|
|
98
128
|
def declare(name:, role: nil, goal: nil, attributes: [], givens: [], ensures: [],
|
|
99
129
|
mutations: [], emits: [], references: nil, from: nil, provenance: nil)
|
|
100
130
|
verb = Class.new(self)
|
|
@@ -105,6 +135,21 @@ module Hecks
|
|
|
105
135
|
verb
|
|
106
136
|
end
|
|
107
137
|
|
|
138
|
+
# Assigns what the language declares, then hands off to the behaviour's
|
|
139
|
+
# own `settle` — indexing attributes, the one thing not derivable from
|
|
140
|
+
# the declaration.
|
|
141
|
+
#
|
|
142
|
+
# @param role [String, nil] see `declare`
|
|
143
|
+
# @param goal [String, nil] see `declare`
|
|
144
|
+
# @param attributes [Array<Bluebook::Attribute>] see `declare`
|
|
145
|
+
# @param givens [Array<Bluebook::Given>] see `declare`
|
|
146
|
+
# @param ensures [Array<Bluebook::Given>] see `declare`
|
|
147
|
+
# @param mutations [Array<Bluebook::Mutation>] see `declare`
|
|
148
|
+
# @param emits [Array<String>] see `declare`
|
|
149
|
+
# @param references [String, nil] see `declare`
|
|
150
|
+
# @param from [String, Array<String>, nil] see `declare`
|
|
151
|
+
# @param provenance [Object, nil] see `declare`
|
|
152
|
+
# @return [Class] self, once attributes are indexed
|
|
108
153
|
def absorb(role:, goal:, attributes:, givens:, ensures:, mutations:, emits:, references:,
|
|
109
154
|
from: nil, provenance: nil)
|
|
110
155
|
@role = role
|
|
@@ -33,6 +33,19 @@ module Hecks
|
|
|
33
33
|
# what the adapter came back with, `refuses` for what it said instead.
|
|
34
34
|
# Naming only the happy one would put the failure somewhere the model
|
|
35
35
|
# cannot see, which is the whole reason a boundary is worth modelling.
|
|
36
|
+
#
|
|
37
|
+
# @param name [String, Symbol] the operation's declared name
|
|
38
|
+
# @param attributes [Array<Bluebook::Attribute>] the operation's declared payload
|
|
39
|
+
# fields
|
|
40
|
+
# @param emits [Array<String>] the events an inbound operation declares it records
|
|
41
|
+
# @param direction [Symbol, String] `:inbound` for a `tells`/`operation`, `:outbound`
|
|
42
|
+
# for an `asks`
|
|
43
|
+
# @param answers [String, nil] an outbound operation's declared event for what the
|
|
44
|
+
# adapter came back with
|
|
45
|
+
# @param refuses [String, nil] an outbound operation's declared event for what the
|
|
46
|
+
# adapter said instead
|
|
47
|
+
# @param to [String, nil] the aggregate this operation routes to, or `nil` if it
|
|
48
|
+
# declares no routing target
|
|
36
49
|
def initialize(name:, attributes: [], emits: [], direction: :inbound, answers: nil, refuses: nil, to: nil)
|
|
37
50
|
@hecks_name = name.to_s
|
|
38
51
|
@attributes = attributes
|
|
@@ -44,7 +57,14 @@ module Hecks
|
|
|
44
57
|
@attributes_by_name = attributes.to_h { |attribute| [attribute.name, attribute] }
|
|
45
58
|
end
|
|
46
59
|
|
|
60
|
+
# Says whether this operation is the domain asking something of an adapter.
|
|
61
|
+
#
|
|
62
|
+
# @return [Boolean] whether this operation is an `asks`
|
|
47
63
|
def outbound? = @direction == :outbound
|
|
64
|
+
|
|
65
|
+
# Says whether this operation is an adapter telling the domain something.
|
|
66
|
+
#
|
|
67
|
+
# @return [Boolean] whether this operation is a `tells`/`operation`
|
|
48
68
|
def inbound? = @direction == :inbound
|
|
49
69
|
|
|
50
70
|
# No root reference of its own — unlike a command, every attribute
|
|
@@ -72,6 +92,9 @@ module Hecks
|
|
|
72
92
|
# migrated yet keeps the exact prior IR shape, byte for byte,
|
|
73
93
|
# instead of an unconditional new key breaking parser_parity_spec
|
|
74
94
|
# for every domain that never touched this.
|
|
95
|
+
#
|
|
96
|
+
# @return [Hash] the declared emission, plus `direction`/`answers`/`refuses` for an
|
|
97
|
+
# outbound operation and `to` when a routing target is declared
|
|
75
98
|
def to_h
|
|
76
99
|
shape = super
|
|
77
100
|
shape = shape.merge(direction: @direction.to_s, answers: @answers, refuses: @refuses) unless inbound?
|
|
@@ -93,6 +116,8 @@ module Hecks
|
|
|
93
116
|
|
|
94
117
|
attr_reader :name, :operations
|
|
95
118
|
|
|
119
|
+
# @param name [String, Symbol] the port's declared name
|
|
120
|
+
# @param operations [Array<Bluebook::PortOperation>] the port's declared operations
|
|
96
121
|
def initialize(name:, operations: [])
|
|
97
122
|
@name = name.to_s
|
|
98
123
|
@operations = operations
|