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
|
@@ -29,6 +29,19 @@ module Hecks
|
|
|
29
29
|
UNSET = Object.new.freeze
|
|
30
30
|
private_constant :UNSET
|
|
31
31
|
|
|
32
|
+
# @param name [String] the command's name, as written after `command`
|
|
33
|
+
# @param owner [String, nil] name of the aggregate or entity this command belongs to
|
|
34
|
+
# @param from [String, Symbol, Array<String, Symbol>, nil] the lifecycle state(s) this
|
|
35
|
+
# command guards from; nil admits from any state
|
|
36
|
+
# @param named_givens [Hash{String => Bluebook::Given}] the owner's own given pool, for
|
|
37
|
+
# bare `given` references to resolve against
|
|
38
|
+
# @param owner_attributes [Array<Bluebook::Attribute>] the owner's own attributes, for
|
|
39
|
+
# implicit `sets`/`state` resolution
|
|
40
|
+
# @param owner_constructs [Array<Bluebook::ValueObject, Bluebook::Entity>] the owner's
|
|
41
|
+
# own value objects and entities, for an `append:` element type to resolve against
|
|
42
|
+
# @param entity_shared_givens [Hash{String => Bluebook::Given}] a sibling piece's own
|
|
43
|
+
# entity-level given pool, for a piece-owned command's bare reference; empty for an
|
|
44
|
+
# aggregate-owned command
|
|
32
45
|
def initialize(name, owner: nil, from: nil, named_givens: {}, owner_attributes: [], owner_constructs: [],
|
|
33
46
|
entity_shared_givens: {})
|
|
34
47
|
@name = name
|
|
@@ -63,7 +76,10 @@ module Hecks
|
|
|
63
76
|
# exactly the failure mode `reference_to`'s own duplicate guard
|
|
64
77
|
# (below) already exists to prevent for a command's root.
|
|
65
78
|
#
|
|
66
|
-
#
|
|
79
|
+
# Sets the command's one responsibility role, refusing a second declaration.
|
|
80
|
+
#
|
|
81
|
+
# Answers the `role` word through the table's `calls:` column —
|
|
82
|
+
# item #13's full metaprogrammed dispatch
|
|
67
83
|
# (slice 4). This is a uniqueness gate on prior state (`@role`
|
|
68
84
|
# already set), not a pure function of the argument's own value —
|
|
69
85
|
# a genuinely different shape than a plain fill, so it stays
|
|
@@ -71,6 +87,10 @@ module Hecks
|
|
|
71
87
|
# was in slice 3. Bootstrap-reachable (every self-hosted command
|
|
72
88
|
# declares a role), so also named in
|
|
73
89
|
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
90
|
+
#
|
|
91
|
+
# @param value [String, Symbol] the role's name
|
|
92
|
+
# @return [Object] `value` as stored
|
|
93
|
+
# @raise [Bluebook::DSL::Malformed] if a role is already declared
|
|
74
94
|
def role_impl(value)
|
|
75
95
|
if @role
|
|
76
96
|
raise Malformed,
|
|
@@ -82,23 +102,49 @@ module Hecks
|
|
|
82
102
|
@role = value
|
|
83
103
|
end
|
|
84
104
|
|
|
105
|
+
# Sets the human-readable description shown for this command.
|
|
106
|
+
#
|
|
107
|
+
# @param value [String] the description text
|
|
108
|
+
# @return [String] the description as stored
|
|
85
109
|
def goal(value) = @goal = value
|
|
86
110
|
|
|
87
|
-
#
|
|
111
|
+
# Names where a concept adopted from a canonical source came from.
|
|
112
|
+
#
|
|
113
|
+
# See `AggregateBuilder#provenance_impl`'s own comment — identical shape,
|
|
88
114
|
# one level down.
|
|
89
|
-
#
|
|
115
|
+
#
|
|
116
|
+
# Answers the `provenance` word through the table's `calls:`
|
|
117
|
+
# column — item #13's full metaprogrammed
|
|
90
118
|
# dispatch (slice 4c). Bootstrap-reachable, in
|
|
91
|
-
# GenericDispatch::BOOTSTRAP_CALLS_FALLBACK
|
|
119
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
120
|
+
#
|
|
121
|
+
# @param from [Object] the canonical source, captured exactly as written
|
|
122
|
+
# @return [Object] `from` as stored
|
|
92
123
|
def provenance_impl(from:) = @provenance = from
|
|
93
124
|
|
|
125
|
+
# Declares the aggregate this command acts on (with no `as:`), or a cross-reference to
|
|
126
|
+
# another aggregate (with `as:`).
|
|
127
|
+
#
|
|
94
128
|
# `optional:` rides here as well as on a plain attribute : `as:` makes a
|
|
95
129
|
# reference into a named argument, and a named argument is exactly the kind
|
|
96
130
|
# of fact that may or may not be given. The meta-domain's Verb.Declare
|
|
97
131
|
# points at the Entity a command belongs to — and most commands belong to no
|
|
98
132
|
# entity at all.
|
|
99
|
-
#
|
|
133
|
+
#
|
|
134
|
+
# Answers the `reference_to` word through the table's `calls:`
|
|
135
|
+
# column — item #13's full metaprogrammed
|
|
100
136
|
# dispatch (slice 4b). Bootstrap-reachable, in
|
|
101
|
-
# GenericDispatch::BOOTSTRAP_CALLS_FALLBACK
|
|
137
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
138
|
+
#
|
|
139
|
+
# @param type [Module, Symbol, String] the referenced aggregate, written as a bare
|
|
140
|
+
# constant
|
|
141
|
+
# @param as [Symbol, nil] the attribute's name for a cross-reference; nil declares the
|
|
142
|
+
# root this command acts on instead, unless `type` is a different aggregate than the
|
|
143
|
+
# owner
|
|
144
|
+
# @param optional [Boolean] whether the reference may be absent
|
|
145
|
+
# @return [void]
|
|
146
|
+
# @raise [Bluebook::DSL::Malformed] if the command already acts on a root, or (for a
|
|
147
|
+
# cross-reference) `as` is already declared
|
|
102
148
|
def reference_to_impl(type, as: nil, optional: false)
|
|
103
149
|
demodulised = Naming.demodulise(type)
|
|
104
150
|
# moved to the language: given "a command names what it acts on", on Verb.ActsOn
|
|
@@ -131,19 +177,32 @@ module Hecks
|
|
|
131
177
|
|
|
132
178
|
public
|
|
133
179
|
|
|
180
|
+
# Declares a precondition this command requires, or references one the owning aggregate
|
|
181
|
+
# (or a sibling piece) already declared.
|
|
182
|
+
#
|
|
134
183
|
# No block is a reference, not a fresh declaration (S10, ADR
|
|
135
184
|
# 0025 — "a precondition shared across commands is declared
|
|
136
185
|
# once. An aggregate declares it by name and commands
|
|
137
186
|
# reference it"): the same word, the same shape
|
|
138
|
-
# (`AggregateBuilder#
|
|
187
|
+
# (`AggregateBuilder#given_impl`, block required there), so naming a
|
|
139
188
|
# precondition back is spelled exactly like declaring one would
|
|
140
189
|
# be, minus the block — one idea, one word, never a second
|
|
141
190
|
# spelling ("requires"/"precondition") for "use the one already
|
|
142
191
|
# named". Resolved against whatever the owning aggregate has
|
|
143
|
-
# declared so far — see `AggregateBuilder#
|
|
192
|
+
# declared so far — see `AggregateBuilder#command_impl`'s own
|
|
144
193
|
# comment on why that means declaration order matters here.
|
|
145
|
-
#
|
|
146
|
-
#
|
|
194
|
+
#
|
|
195
|
+
# Answers the `given` word through the table's `calls:` column —
|
|
196
|
+
# item #13's full metaprogrammed dispatch
|
|
197
|
+
# (slice 4b), same reasoning as `reference_to_impl` above.
|
|
198
|
+
#
|
|
199
|
+
# @param description [String] the rule's description; also the name the owning
|
|
200
|
+
# aggregate's rule is referenced by when no block is given
|
|
201
|
+
# @yield the predicate body; evaluated for its extracted source, never called directly
|
|
202
|
+
# @return [void]
|
|
203
|
+
# @raise [Bluebook::DSL::Malformed] if given a block whose source cannot be extracted, or
|
|
204
|
+
# given no block and the description names no precondition the owner (or a sibling
|
|
205
|
+
# piece under the same aggregate) declares
|
|
147
206
|
def given_impl(description, &predicate)
|
|
148
207
|
return reference_named_given(description) unless predicate
|
|
149
208
|
|
|
@@ -179,11 +238,19 @@ module Hecks
|
|
|
179
238
|
|
|
180
239
|
public
|
|
181
240
|
|
|
241
|
+
# Declares a postcondition, checked against the settled record after the command's own
|
|
242
|
+
# mutations apply.
|
|
243
|
+
#
|
|
182
244
|
# The postcondition — a given for the far side of the mutations,
|
|
183
245
|
# evaluated against the settled record with `old` naming the state
|
|
184
246
|
# as it stood before them: `ensures("...") { old.balance.cents ==
|
|
185
247
|
# balance.cents + amount.cents }`. Same extraction, same Rule
|
|
186
|
-
# shape, same refusal form; EnsuresNotMet instead of GivenNotMet
|
|
248
|
+
# shape, same refusal form; `EnsuresNotMet` instead of `GivenNotMet`.
|
|
249
|
+
#
|
|
250
|
+
# @param description [String] the rule's description
|
|
251
|
+
# @yield the predicate body; evaluated for its extracted source, never called directly
|
|
252
|
+
# @return [void]
|
|
253
|
+
# @raise [Bluebook::DSL::Malformed] if the block's source could not be extracted
|
|
187
254
|
def ensures(description, &predicate)
|
|
188
255
|
@ensures << build_rule(Given, description, predicate, owner_name: @name, word: "ensures",
|
|
189
256
|
extraction_failure: "a postcondition is carried as text, and this one has none")
|
|
@@ -205,8 +272,8 @@ module Hecks
|
|
|
205
272
|
# Vendored addition, not (yet) upstream hecks (migration plan
|
|
206
273
|
# task 4, i106 in-DSL math): `multiply:`/`clamp:` -- per-tick organ
|
|
207
274
|
# math (miette's body/organs/bluebook: strength decays ×0.98,
|
|
208
|
-
# weight/strength clamp to [0, 1])
|
|
209
|
-
#
|
|
275
|
+
# weight/strength clamp to [0, 1]), moved into the bluebook itself
|
|
276
|
+
# from shell-side awk. `multiply:` mirrors
|
|
210
277
|
# increment/decrement's shape exactly (a Numeric amount, applied
|
|
211
278
|
# by CommandRules::Arithmetic -- see that file's own comment on
|
|
212
279
|
# the matching Float-support widening this required). `clamp:`
|
|
@@ -235,7 +302,7 @@ module Hecks
|
|
|
235
302
|
# boolean occurrences of the bare-positional-second-arg shape
|
|
236
303
|
# anywhere in the corpus). Folded into `to:` itself rather than
|
|
237
304
|
# given its own mutation op -- semantically identical, same
|
|
238
|
-
# UNSET
|
|
305
|
+
# `UNSET`-sentinel discipline the `to: false` fix already
|
|
239
306
|
# established (a positional `false` must read as "set to
|
|
240
307
|
# false," not "absent," same as the keyword form). Only applied
|
|
241
308
|
# when `to:` itself was not also given, so an explicit `to:`
|
|
@@ -262,15 +329,38 @@ module Hecks
|
|
|
262
329
|
KWARG_TO_OP = { to: :set, append: :append, increment: :increment, decrement: :decrement,
|
|
263
330
|
multiply: :multiply, clamp: :clamp, remove: :remove }.freeze
|
|
264
331
|
|
|
265
|
-
#
|
|
332
|
+
# Declares one mutation this command applies to `target`, its op selected by whichever
|
|
333
|
+
# single keyword (or the omitted, self-referential form) names a source.
|
|
334
|
+
#
|
|
335
|
+
# Answers the `sets` word through the table's `calls:` column —
|
|
336
|
+
# item #13's full metaprogrammed dispatch
|
|
266
337
|
# (slice 4c). The `KWARG_TO_OP` op-selection mapping is already
|
|
267
|
-
# table-verified (`Argument#selects`), but the REST (UNSET
|
|
338
|
+
# table-verified (`Argument#selects`), but the REST (`UNSET`-
|
|
268
339
|
# sentinel discipline, redundant-spelling refusal, omittable-
|
|
269
340
|
# `to:` fallback, one-mutation-only refusal, the position-
|
|
270
341
|
# preserving `resolve_*!` reinsertion) is keyed off runtime
|
|
271
342
|
# state, not a pure function of a static row — stays hand-
|
|
272
343
|
# written, reached through `calls:` like everything else here.
|
|
273
|
-
# Bootstrap-reachable, in BOOTSTRAP_CALLS_FALLBACK
|
|
344
|
+
# Bootstrap-reachable, in `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
345
|
+
#
|
|
346
|
+
# @param target [Symbol, String] the field this mutation writes
|
|
347
|
+
# @param positional_to [Object] a bare positional value, folded into `to:` when `to:`
|
|
348
|
+
# itself is not given; the corpus's only real use is a boolean shorthand
|
|
349
|
+
# (`sets :deployed, true`)
|
|
350
|
+
# @param to [Object] the value or field reference to set `target` to; a Symbol equal to
|
|
351
|
+
# `target` is redundant and refused, and omitting `to:` entirely means the same thing
|
|
352
|
+
# @param append [Object] a Hash of fields for a new list element, or a bare value for the
|
|
353
|
+
# one-field shorthand
|
|
354
|
+
# @param increment [Object] the amount to add to `target`'s current value
|
|
355
|
+
# @param decrement [Object] the amount to subtract from `target`'s current value
|
|
356
|
+
# @param multiply [Object] the amount to multiply `target`'s current value by
|
|
357
|
+
# @param clamp [Array(Object, Object)] the `[min, max]` pair to bound `target`'s current
|
|
358
|
+
# value to
|
|
359
|
+
# @param remove [Object] the value to remove from `target`'s own list, by equality
|
|
360
|
+
# @return [void]
|
|
361
|
+
# @raise [Bluebook::DSL::Malformed] if `to:` redundantly repeats `target`, or more than
|
|
362
|
+
# one of `to:`/`append:`/`increment:`/`decrement:`/`multiply:`/`clamp:`/`remove:` (or
|
|
363
|
+
# the folded `positional_to`) is given
|
|
274
364
|
def sets_impl(target, positional_to = UNSET, to: UNSET, append: UNSET,
|
|
275
365
|
increment: UNSET, decrement: UNSET, multiply: UNSET, clamp: UNSET, remove: UNSET)
|
|
276
366
|
# moved to the language: given "a mutation names a target", on Verb.Change
|
|
@@ -307,20 +397,34 @@ module Hecks
|
|
|
307
397
|
@mutations << Mutation.new(target: target.to_sym, op: op, source: normalize_append_source(op, source))
|
|
308
398
|
end
|
|
309
399
|
|
|
310
|
-
#
|
|
311
|
-
#
|
|
400
|
+
# Refuses the retired `then_set` spelling, except while shadow-parsing frozen era text,
|
|
401
|
+
# where it is read as the legacy mutation shape.
|
|
402
|
+
#
|
|
403
|
+
# Legacy under shadow-parsing (S0a's own bridge) — frozen era
|
|
404
|
+
# text still parses under it; live source refuses it,
|
|
312
405
|
# naming the replacement.
|
|
313
|
-
#
|
|
314
|
-
#
|
|
406
|
+
#
|
|
407
|
+
# Answers the `then_set` word — item #13's full metaprogrammed
|
|
408
|
+
# dispatch (slice 5). Not bootstrap-reachable. Has its own
|
|
315
409
|
# dedicated, `status: "deprecated"` Keyword row (syntax.bluebook)
|
|
316
410
|
# rather than living only as `sets`'s own `was:` — see that
|
|
317
411
|
# row's own comment for why.
|
|
412
|
+
#
|
|
413
|
+
# @param target [Symbol, String] the field this legacy mutation writes
|
|
414
|
+
# @param positional_to [Object] the bare second positional argument; `UNSET` when
|
|
415
|
+
# omitted, forwarded to `legacy_then_set` verbatim under shadow-parsing, along with
|
|
416
|
+
# every keyword argument this call received
|
|
417
|
+
# @return [void]
|
|
418
|
+
# @raise [Bluebook::DSL::Malformed] outside shadow-parsing, always; under
|
|
419
|
+
# shadow-parsing, if `legacy_then_set` names no operation or more than one
|
|
318
420
|
def then_set_impl(target, positional_to = UNSET, **)
|
|
319
421
|
return legacy_then_set(target, positional_to, **) if MetaValidator.shadow_parsing?
|
|
320
422
|
|
|
321
423
|
raise Malformed, "#{@name}'s then_set is gone — sets is the word now"
|
|
322
424
|
end
|
|
323
425
|
|
|
426
|
+
# Declares one event this command announces to the outside.
|
|
427
|
+
#
|
|
324
428
|
# No raise here. "an event is named" is declared in the language itself —
|
|
325
429
|
# language/bluebook/behavior.bluebook, on Command.Announce — and MetaValidator is what
|
|
326
430
|
# enforces it. This is the first rule to move across rather than be
|
|
@@ -341,6 +445,10 @@ module Hecks
|
|
|
341
445
|
# are accepted in live source until a full corpus migration
|
|
342
446
|
# lands and the same refusal this file's `reference_to`/
|
|
343
447
|
# `trigger_impl` already carry can be added here safely.
|
|
448
|
+
#
|
|
449
|
+
# @param event_name [String, Symbol, Module] the event, quoted text or a bare constant
|
|
450
|
+
# such as `Account::AccountFrozen`
|
|
451
|
+
# @return [Array<String>] every event declared so far, this one last
|
|
344
452
|
def emits(event_name)
|
|
345
453
|
@emits << Naming.event_ref(event_name)
|
|
346
454
|
end
|
|
@@ -349,11 +457,21 @@ module Hecks
|
|
|
349
457
|
# append: { ply: state(:ply), knights: state(:knights) }` copies
|
|
350
458
|
# what the record holds now into the new element; `sets :last,
|
|
351
459
|
# to: state(:current)` copies one field onto another. A bare
|
|
352
|
-
# Symbol always names an argument (
|
|
460
|
+
# Symbol always names an argument (`resolve_append_fields!`'s own
|
|
461
|
+
# comment has the full account),
|
|
353
462
|
# so without this a command could not snapshot its own state at
|
|
354
463
|
# all. `Literal::StateRef`'s own comment has the wire spelling.
|
|
464
|
+
#
|
|
465
|
+
# References the record's own current field as a mutation source, as opposed to a
|
|
466
|
+
# bare Symbol, which names an argument.
|
|
467
|
+
#
|
|
468
|
+
# @param name [Symbol, String] the field to read from the pre-dispatch record
|
|
469
|
+
# @return [Literal::StateRef] the wrapped reference
|
|
355
470
|
def state(name) = StateRef.new(name.to_sym)
|
|
356
471
|
|
|
472
|
+
# Declares a synchronous, atomic delegation of this command's own dispatch to one
|
|
473
|
+
# nested entity command.
|
|
474
|
+
#
|
|
357
475
|
# The synchronous cousin of `trigger` — an aggregate-level command
|
|
358
476
|
# that hands its own dispatch to one nested entity command, checked
|
|
359
477
|
# and applied within the same atomic dispatch rather than a second
|
|
@@ -417,12 +535,19 @@ module Hecks
|
|
|
417
535
|
# `meta_validator/shapes.rb#mutation`, `assembly/marks.rb#mutation`)
|
|
418
536
|
# now check for `:delegate` alongside it, each with a comment
|
|
419
537
|
# pointing back here.
|
|
420
|
-
#
|
|
421
|
-
# — matches `sets_impl`/`given_impl`/`reference_to_impl`'s own
|
|
538
|
+
# Answers the `delegates_to` word through the table's `calls:`
|
|
539
|
+
# column — matches `sets_impl`/`given_impl`/`reference_to_impl`'s own
|
|
422
540
|
# convention (language/bluebook/syntax.bluebook's own Keyword row
|
|
423
541
|
# for this word names `calls: "delegates_to_impl"`), the same
|
|
424
542
|
# `word`-vs-`_impl` split every hand-written (not yet item-#13-
|
|
425
543
|
# generic-dispatch-migrated) DSL word here already follows.
|
|
544
|
+
#
|
|
545
|
+
# @param target [String, Symbol] the delegated command, dotted `"Entity.Command"`
|
|
546
|
+
# @param with [Hash{Symbol => Symbol, Object}] projects this command's own arguments
|
|
547
|
+
# onto the target's; a Symbol value names one of this command's own arguments,
|
|
548
|
+
# anything else is a literal
|
|
549
|
+
# @return [Array<Bluebook::Mutation>] every mutation declared so far, this one last
|
|
550
|
+
# @raise [Bluebook::DSL::Malformed] if `target` is not `"Entity.Command"` shaped
|
|
426
551
|
def delegates_to_impl(target, with: {})
|
|
427
552
|
entity_name, _dot, command_name = target.to_s.rpartition(".")
|
|
428
553
|
if entity_name.empty? || command_name.empty?
|
|
@@ -434,6 +559,8 @@ module Hecks
|
|
|
434
559
|
@mutations << Mutation.new(target: target.to_s, op: :delegate, source: with)
|
|
435
560
|
end
|
|
436
561
|
|
|
562
|
+
# Declares that this command amends a past event, rather than rewriting it.
|
|
563
|
+
#
|
|
437
564
|
# A command declaring what past fact it amends — the append-only
|
|
438
565
|
# answer to "what if this record's history turns out to have been
|
|
439
566
|
# wrong": never rewrite the original event (the log stays exactly
|
|
@@ -473,6 +600,18 @@ module Hecks
|
|
|
473
600
|
# a real runtime consumer exists) — so coercing it to a String here
|
|
474
601
|
# keeps it out of that machinery entirely rather than silently
|
|
475
602
|
# miscategorised as an unresolvable argument reference.
|
|
603
|
+
#
|
|
604
|
+
# @param event [String, Symbol, Module] the event this command corrects, quoted text or
|
|
605
|
+
# a bare constant
|
|
606
|
+
# @param as [Symbol, nil] binds the located instance under this label, for a
|
|
607
|
+
# `given`/`ensures` to reference; nil declares no binding
|
|
608
|
+
# @param reason [String, nil] why the correction is being made, carried as audit data;
|
|
609
|
+
# required, non-blank
|
|
610
|
+
# @param reverses [Boolean] true to auto-derive the corrective `sets` from the original
|
|
611
|
+
# event's own mutations, checked once every sibling command is known; mutually
|
|
612
|
+
# exclusive with an explicit `sets` on the same command
|
|
613
|
+
# @return [Array<Bluebook::Mutation>] every mutation declared so far, this one last
|
|
614
|
+
# @raise [Bluebook::DSL::Malformed] if `reason` is nil or blank
|
|
476
615
|
def corrects_impl(event, as: nil, reason: nil, reverses: false)
|
|
477
616
|
if reason.to_s.strip.empty?
|
|
478
617
|
raise Malformed,
|
|
@@ -489,6 +628,13 @@ module Hecks
|
|
|
489
628
|
# `corrects` name a command and an event, never a field.
|
|
490
629
|
FIELD_EFFECTS = %i[set append remove increment decrement multiply clamp].freeze
|
|
491
630
|
|
|
631
|
+
# Resolves implicit attributes, refuses conflicting mutations, and assembles the
|
|
632
|
+
# declared rules and effects into a `Command`.
|
|
633
|
+
#
|
|
634
|
+
# @return [Bluebook::Command] the built command
|
|
635
|
+
# @raise [Bluebook::DSL::Malformed] if the same field is written twice, an argument or
|
|
636
|
+
# state source names an undeclared field, or a delegating command also declares its
|
|
637
|
+
# own mutations or events
|
|
492
638
|
def build
|
|
493
639
|
resolve_implicit_attributes!
|
|
494
640
|
refuse_duplicate_targets!
|
|
@@ -516,6 +662,21 @@ module Hecks
|
|
|
516
662
|
)
|
|
517
663
|
end
|
|
518
664
|
|
|
665
|
+
# Evaluates a `command` block against a fresh builder and returns what it built.
|
|
666
|
+
#
|
|
667
|
+
# @param name [String] the command's name
|
|
668
|
+
# @param owner [String, nil] name of the aggregate or entity this command belongs to
|
|
669
|
+
# @param from [String, Symbol, Array<String, Symbol>, nil] the lifecycle state(s) this
|
|
670
|
+
# command guards from
|
|
671
|
+
# @param named_givens [Hash{String => Bluebook::Given}] the owner's own given pool
|
|
672
|
+
# @param owner_attributes [Array<Bluebook::Attribute>] the owner's own attributes
|
|
673
|
+
# @param owner_constructs [Array<Bluebook::ValueObject, Bluebook::Entity>] the owner's
|
|
674
|
+
# own value objects and entities
|
|
675
|
+
# @param entity_shared_givens [Hash{String => Bluebook::Given}] a sibling piece's own
|
|
676
|
+
# entity-level given pool
|
|
677
|
+
# @yield the command body, evaluated with the builder as `self`; may be omitted
|
|
678
|
+
# @return [Bluebook::Command] the built command
|
|
679
|
+
# @raise [Bluebook::DSL::Malformed] if the body fails any check `#build` raises
|
|
519
680
|
def self.build(name, owner: nil, from: nil, named_givens: {}, owner_attributes: [], owner_constructs: [],
|
|
520
681
|
entity_shared_givens: {}, &block)
|
|
521
682
|
builder = new(name, owner: owner, from: from, named_givens: named_givens,
|
|
@@ -660,8 +821,8 @@ module Hecks
|
|
|
660
821
|
# A symbol naming its own target — never a literal that merely
|
|
661
822
|
# spells the same word. `sets :moved, to: "moved"` (a chess rook
|
|
662
823
|
# recording that it has moved, into a closed set whose member is
|
|
663
|
-
# literally "moved")
|
|
664
|
-
# the owner's `moved` attribute onto the command — a phantom
|
|
824
|
+
# literally "moved") must not read as the shorthand and import
|
|
825
|
+
# the owner's `moved` attribute onto the command — that would add a phantom
|
|
665
826
|
# argument nothing ever passes, harmless at runtime only
|
|
666
827
|
# because the owner's default filled it, and a real, silent
|
|
667
828
|
# divergence for every projection that reads the command's
|
|
@@ -724,7 +885,8 @@ module Hecks
|
|
|
724
885
|
return if self_ref_fields.empty?
|
|
725
886
|
|
|
726
887
|
present = self_ref_fields.filter_map { |field| attributes.find { |attr| attr.name == field } }
|
|
727
|
-
|
|
888
|
+
# Already fully declared — nothing to resolve.
|
|
889
|
+
return if present.size == self_ref_fields.size
|
|
728
890
|
|
|
729
891
|
anchor = present.empty? ? attributes.length : present.map { |attr| attributes.index(attr) }.min
|
|
730
892
|
attributes.reject! { |attr| present.include?(attr) }
|
|
@@ -18,6 +18,22 @@ module Hecks
|
|
|
18
18
|
include RuleReference
|
|
19
19
|
include WordGate
|
|
20
20
|
|
|
21
|
+
# @param name [String] the entity's name, as written after `entity`
|
|
22
|
+
# @param owner_value_objects [Array<Bluebook::ValueObject>] the owning aggregate's value
|
|
23
|
+
# objects, threaded through unchanged for this piece's `identified_by` to resolve
|
|
24
|
+
# against
|
|
25
|
+
# @param owner_named_givens [Hash{String => Bluebook::Given}] the aggregate-wide,
|
|
26
|
+
# cross-entity given pool, shared and written through by `given_impl`
|
|
27
|
+
# @param identity_name_prefix [String, nil] the prefix a synthesized identity value
|
|
28
|
+
# object's name takes; nil derives it from `name`
|
|
29
|
+
# @param identity_value_object_installer [#call, nil] called with a synthesized value
|
|
30
|
+
# object to install it onto the owning aggregate; nil when there is none
|
|
31
|
+
# @param aggregate_name [String, nil] the root aggregate's name, for chapter-wide given
|
|
32
|
+
# keys; nil derives it from `name`
|
|
33
|
+
# @param chapter_entity_named_givens [Hash{String => Hash{String => Bluebook::Given}}]
|
|
34
|
+
# the chapter-wide, entity-scoped given pool, shared and written through
|
|
35
|
+
# @param chapter_entity_pending_givens [Array<Hash>] unresolved chapter-wide bare given
|
|
36
|
+
# references, appended to when this piece's own reference cannot resolve yet
|
|
21
37
|
def initialize(name, owner_value_objects: [], owner_named_givens: {},
|
|
22
38
|
identity_name_prefix: nil, identity_value_object_installer: nil,
|
|
23
39
|
aggregate_name: nil, chapter_entity_named_givens: {}, chapter_entity_pending_givens: [])
|
|
@@ -61,16 +77,31 @@ module Hecks
|
|
|
61
77
|
@pending_queries = []
|
|
62
78
|
end
|
|
63
79
|
|
|
80
|
+
# Sets the human-readable description shown for this entity.
|
|
81
|
+
#
|
|
82
|
+
# @param value [String] the description text
|
|
83
|
+
# @return [String] the description as stored
|
|
64
84
|
def description(value) = @description = value
|
|
65
85
|
|
|
86
|
+
# Declares a reference from this piece to another aggregate's identity.
|
|
87
|
+
#
|
|
66
88
|
# The same field AggregateBuilder's own reference_to builds — a
|
|
67
89
|
# piece can hold a reference to another root exactly the way its
|
|
68
90
|
# own head can (Card.assignee_id, a Team's own id), just never
|
|
69
91
|
# to another piece, since there's no cross-piece addressing
|
|
70
92
|
# anywhere in this language to resolve one against.
|
|
71
|
-
#
|
|
93
|
+
#
|
|
94
|
+
# Answers the `reference_to` word through the table's `calls:`
|
|
95
|
+
# column — item #13's full metaprogrammed
|
|
72
96
|
# dispatch (slice 4b). Bootstrap-reachable, in
|
|
73
|
-
# GenericDispatch::BOOTSTRAP_CALLS_FALLBACK
|
|
97
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
98
|
+
#
|
|
99
|
+
# @param type [Module, Symbol, String] the referenced aggregate, written as a bare
|
|
100
|
+
# constant
|
|
101
|
+
# @param as [Symbol, nil] the attribute's name; nil derives it from `type`
|
|
102
|
+
# @param optional [Boolean] whether the reference may be absent
|
|
103
|
+
# @return [void]
|
|
104
|
+
# @raise [Bluebook::DSL::Malformed] if `as` (or the derived name) is already declared
|
|
74
105
|
def reference_to_impl(type, as: nil, optional: false)
|
|
75
106
|
target = Naming.demodulise(type)
|
|
76
107
|
relationship_attribute(target, :reference_to,
|
|
@@ -86,6 +117,14 @@ module Hecks
|
|
|
86
117
|
# `*_impl` to match AggregateBuilder's own siblings, item #13's
|
|
87
118
|
# full metaprogrammed dispatch convention.
|
|
88
119
|
# rubocop:disable Naming/PredicatePrefix
|
|
120
|
+
# Declares a list-typed relationship to another entity, referenced by its plural name.
|
|
121
|
+
#
|
|
122
|
+
# @param type [Module, Symbol, String] the related entity's plural name, a bare constant
|
|
123
|
+
# @param as [Symbol, nil] the attribute's name; nil derives it from `type`
|
|
124
|
+
# @param options [Hash] must be empty; kept only to name which unsupported keyword was
|
|
125
|
+
# given in the refusal message
|
|
126
|
+
# @return [void]
|
|
127
|
+
# @raise [Bluebook::DSL::Malformed] if any keyword argument is given
|
|
89
128
|
def has_many_impl(type, as: nil, **options)
|
|
90
129
|
unless options.empty?
|
|
91
130
|
raise Malformed,
|
|
@@ -97,12 +136,24 @@ module Hecks
|
|
|
97
136
|
as || Naming.snake(plural).to_sym, list: true)
|
|
98
137
|
end
|
|
99
138
|
|
|
139
|
+
# Declares a single-valued relationship to another entity.
|
|
140
|
+
#
|
|
141
|
+
# @param type [Module, Symbol, String] the related entity, a bare constant
|
|
142
|
+
# @param as [Symbol, nil] the attribute's name; nil derives it from `type`
|
|
143
|
+
# @param optional [Boolean] whether the relationship may be absent
|
|
144
|
+
# @return [void]
|
|
100
145
|
def has_one_impl(type, as: nil, optional: false)
|
|
101
146
|
target = Naming.demodulise(type)
|
|
102
147
|
relationship_attribute(target, :has_one, as || Naming.snake(target).to_sym, optional: optional)
|
|
103
148
|
end
|
|
104
149
|
# rubocop:enable Naming/PredicatePrefix
|
|
105
150
|
|
|
151
|
+
# Declares a single-valued relationship to the entity that owns this one.
|
|
152
|
+
#
|
|
153
|
+
# @param type [Module, Symbol, String] the owning entity, a bare constant
|
|
154
|
+
# @param as [Symbol, nil] the attribute's name; nil derives it from `type`
|
|
155
|
+
# @param optional [Boolean] whether the relationship may be absent
|
|
156
|
+
# @return [void]
|
|
106
157
|
def belongs_to_impl(type, as: nil, optional: false)
|
|
107
158
|
target = Naming.demodulise(type)
|
|
108
159
|
relationship_attribute(target, :belongs_to, as || Naming.snake(target).to_sym, optional: optional)
|
|
@@ -117,22 +168,39 @@ module Hecks
|
|
|
117
168
|
# (`identified_by :branch_code, :box_number`), which a piece may
|
|
118
169
|
# declare for the same reason a head may.
|
|
119
170
|
|
|
171
|
+
# Queues a command declared on this piece, built later once every sibling has been seen.
|
|
172
|
+
#
|
|
120
173
|
# `from:` — see `AggregateBuilder#command`'s own comment; the
|
|
121
174
|
# same guard, checked against this piece's own lifecycle field
|
|
122
175
|
# (S10, ADR 0025 — a piece's own state machine is checkable the
|
|
123
176
|
# same way a head's is).
|
|
124
|
-
#
|
|
125
|
-
#
|
|
177
|
+
#
|
|
178
|
+
# Answers the `command` word through the table's `calls:` column,
|
|
179
|
+
# along with its siblings `query`/`entity`/`lifecycle` below —
|
|
180
|
+
# item #13's full metaprogrammed dispatch (slice 4c), same
|
|
126
181
|
# reasoning as AggregateBuilder's own siblings: bootstrap-
|
|
127
|
-
# reachable, in BOOTSTRAP_CALLS_FALLBACK
|
|
182
|
+
# reachable, in `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
183
|
+
#
|
|
184
|
+
# @param name [String] the command's name
|
|
185
|
+
# @param from [String, Symbol, Array<String, Symbol>, nil] the lifecycle state(s) this
|
|
186
|
+
# command guards from; nil admits from any state
|
|
187
|
+
# @yield the command body, evaluated against a `CommandBuilder` once drained
|
|
188
|
+
# @return [Array<Array>] every pending command queued so far, this one last
|
|
128
189
|
def command_impl(name, from: nil, &block)
|
|
129
190
|
@pending_commands << [name, from, block]
|
|
130
191
|
end
|
|
131
192
|
|
|
193
|
+
# Queues a query declared on this piece, built later once every sibling has been seen.
|
|
194
|
+
#
|
|
195
|
+
# @param name [String] the query's name
|
|
196
|
+
# @yield the query body, evaluated against a `QueryBuilder` once drained
|
|
197
|
+
# @return [Array<Array>] every pending query queued so far, this one last
|
|
132
198
|
def query_impl(name, &block)
|
|
133
199
|
@pending_queries << [name, block]
|
|
134
200
|
end
|
|
135
201
|
|
|
202
|
+
# Queues a piece nested inside this one, built later once every sibling has been seen.
|
|
203
|
+
#
|
|
136
204
|
# S17, ADR 0026 — a piece nested inside a piece. "A `Dispatch`
|
|
137
205
|
# [has] no life outside its `Handler`" (the ADR's own words) —
|
|
138
206
|
# the same reason `Member` nests inside `ValueObject`, one level
|
|
@@ -144,10 +212,20 @@ module Hecks
|
|
|
144
212
|
# piece's already does (`AggregateBuilder#entity`'s own comment
|
|
145
213
|
# names this pool ; there is exactly one of them, however deep
|
|
146
214
|
# the nesting goes).
|
|
215
|
+
# @param name [String] the nested piece's name
|
|
216
|
+
# @yield the piece body, evaluated against an `EntityBuilder` once drained
|
|
217
|
+
# @return [Array<Array>] every pending piece queued so far, this one last
|
|
147
218
|
def entity_impl(name, &block)
|
|
148
219
|
@pending_entities << [name, block]
|
|
149
220
|
end
|
|
150
221
|
|
|
222
|
+
# Declares this piece's own state machine.
|
|
223
|
+
#
|
|
224
|
+
# @param field [Symbol, String] the attribute the state machine lives on
|
|
225
|
+
# @param default [String, Symbol] the state a new record starts in
|
|
226
|
+
# @yield the lifecycle body of `transition` rows, evaluated against a `LifecycleBuilder`
|
|
227
|
+
# @return [Bluebook::Lifecycle] the built state machine
|
|
228
|
+
# @raise [Bluebook::DSL::Malformed] if two transitions for one command overlap
|
|
151
229
|
def lifecycle_impl(field, default:, &)
|
|
152
230
|
@lifecycle = LifecycleBuilder.build(field, default: default, &)
|
|
153
231
|
end
|
|
@@ -170,8 +248,12 @@ module Hecks
|
|
|
170
248
|
# (`CommandBuilder#reference_named_given`) still reads whatever
|
|
171
249
|
# `@named_givens` holds at the command's own build time, not by
|
|
172
250
|
# magic.
|
|
173
|
-
#
|
|
174
|
-
#
|
|
251
|
+
# Declares a rule this piece's own commands must satisfy, or references one a sibling
|
|
252
|
+
# piece anywhere in the chapter already declared.
|
|
253
|
+
#
|
|
254
|
+
# Answers the `given` word through the table's `calls:` column —
|
|
255
|
+
# item #13's full metaprogrammed dispatch
|
|
256
|
+
# (slice 4b), same reasoning as `reference_to_impl` above.
|
|
175
257
|
#
|
|
176
258
|
# Bare — no block — references another piece's own declaration,
|
|
177
259
|
# anywhere in the chapter, not just a sibling under this same
|
|
@@ -203,6 +285,19 @@ module Hecks
|
|
|
203
285
|
# way `admits:` shipped textual before its own constant-bridge
|
|
204
286
|
# existed, revisited only if a genuine, separate need for
|
|
205
287
|
# constant-addressed pieces shows up later.
|
|
288
|
+
#
|
|
289
|
+
# @param description [String] the rule's description; also the name a sibling piece
|
|
290
|
+
# references it by when no block is given
|
|
291
|
+
# @param declared_by [String, nil] disambiguates which piece's own rule to reference,
|
|
292
|
+
# as `"Aggregate.Entity"`, when more than one shares `description`; only meaningful
|
|
293
|
+
# with no block
|
|
294
|
+
# @yield the predicate body; evaluated for its extracted source, never called directly
|
|
295
|
+
# @return [void]
|
|
296
|
+
# @raise [Bluebook::DSL::Malformed] if given a block whose source cannot be extracted;
|
|
297
|
+
# given no block, the description is immediately ambiguous between more than one
|
|
298
|
+
# already-loaded piece with no `declared_by` to disambiguate; an unresolved reference
|
|
299
|
+
# defers instead, and may still raise once the whole chapter has loaded, if it then
|
|
300
|
+
# resolves to none or more than one candidate
|
|
206
301
|
def given_impl(description, declared_by: nil, &predicate)
|
|
207
302
|
return reference_named_chapter_entity_given(description, declared_by: declared_by) unless predicate
|
|
208
303
|
|
|
@@ -242,13 +337,28 @@ module Hecks
|
|
|
242
337
|
# piece yet; if that need shows up, it is `given`'s own
|
|
243
338
|
# cross-entity write-through pattern to extend, not a reason to
|
|
244
339
|
# invent a second one here speculatively.
|
|
245
|
-
#
|
|
246
|
-
#
|
|
340
|
+
# Declares a rule every instance of this piece must satisfy.
|
|
341
|
+
#
|
|
342
|
+
# Answers the `invariant` word through the table's `calls:`
|
|
343
|
+
# column — item #13's full metaprogrammed
|
|
344
|
+
# dispatch (slice 4b), same reasoning as `given_impl` above.
|
|
345
|
+
#
|
|
346
|
+
# @param description [String] the rule's description
|
|
347
|
+
# @yield the predicate body; evaluated for its extracted source, never called directly
|
|
348
|
+
# @return [void]
|
|
349
|
+
# @raise [Bluebook::DSL::Malformed] if the block's source could not be extracted
|
|
247
350
|
def invariant_impl(description, &predicate)
|
|
248
351
|
@invariants << build_rule(Invariant, description, predicate, owner_name: @name, word: "invariant",
|
|
249
352
|
extraction_failure: "it would be a rule the IR cannot carry")
|
|
250
353
|
end
|
|
251
354
|
|
|
355
|
+
# Assembles the declared attributes, relationships, nested constructs and rules into an
|
|
356
|
+
# `Entity`.
|
|
357
|
+
#
|
|
358
|
+
# @return [Bluebook::Entity] the built piece
|
|
359
|
+
# @raise [Bluebook::DSL::Malformed] if identity resolution, a lifecycle guard on a
|
|
360
|
+
# command with no lifecycle, a lifecycle-field mutation outside a transition, or a
|
|
361
|
+
# synthesized closed set colliding with one the aggregate already holds fails
|
|
252
362
|
def build
|
|
253
363
|
drain_pending!
|
|
254
364
|
resolve_pending_identity!
|
|
@@ -268,6 +378,26 @@ module Hecks
|
|
|
268
378
|
)
|
|
269
379
|
end
|
|
270
380
|
|
|
381
|
+
# Evaluates an `entity` block against a fresh builder and returns what it built.
|
|
382
|
+
#
|
|
383
|
+
# @param name [String] the entity's name
|
|
384
|
+
# @param owner_value_objects [Array<Bluebook::ValueObject>] the owning aggregate's value
|
|
385
|
+
# objects, for `identified_by` to resolve against
|
|
386
|
+
# @param owner_named_givens [Hash{String => Bluebook::Given}] the aggregate-wide,
|
|
387
|
+
# cross-entity given pool
|
|
388
|
+
# @param identity_name_prefix [String, nil] the prefix a synthesized identity value
|
|
389
|
+
# object's name takes
|
|
390
|
+
# @param identity_value_object_installer [#call, nil] called to install a synthesized
|
|
391
|
+
# value object onto the owning aggregate
|
|
392
|
+
# @param aggregate_name [String, nil] the root aggregate's name, for chapter-wide given
|
|
393
|
+
# keys
|
|
394
|
+
# @param chapter_entity_named_givens [Hash{String => Hash{String => Bluebook::Given}}]
|
|
395
|
+
# the chapter-wide, entity-scoped given pool
|
|
396
|
+
# @param chapter_entity_pending_givens [Array<Hash>] unresolved chapter-wide bare given
|
|
397
|
+
# references
|
|
398
|
+
# @yield the entity body, evaluated with the builder as `self`; may be omitted
|
|
399
|
+
# @return [Bluebook::Entity] the built piece
|
|
400
|
+
# @raise [Bluebook::DSL::Malformed] if the body fails any check `#build` raises
|
|
271
401
|
def self.build(name, owner_value_objects: [], owner_named_givens: {},
|
|
272
402
|
identity_name_prefix: nil, identity_value_object_installer: nil,
|
|
273
403
|
aggregate_name: nil, chapter_entity_named_givens: {}, chapter_entity_pending_givens: [], &block)
|