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
|
@@ -135,6 +135,47 @@ module Hecks
|
|
|
135
135
|
bluebook_ir.add_port(built)
|
|
136
136
|
end
|
|
137
137
|
|
|
138
|
+
# A TRANSLATION BOUNDARY, NOT A BUSINESS RULE — `translates "Name"
|
|
139
|
+
# do on Foreign::Domain::SomeEvent; trigger Local.Command; end`
|
|
140
|
+
# builds the EXACT SAME `Policy` a `policy` block inside this
|
|
141
|
+
# domain's own `.bluebook` would (same `on_impl`/`trigger_impl`,
|
|
142
|
+
# same `PolicyInterpreter` runtime — reusing `PolicyBuilder`
|
|
143
|
+
# directly, zero new runtime semantics). What's new is only WHERE
|
|
144
|
+
# it can be written: a cross-domain reaction is a wiring/context-
|
|
145
|
+
# mapping decision (this chapter conforming to a foreign chapter's
|
|
146
|
+
# published event), the same kind of decision `port`/
|
|
147
|
+
# `uses_framework` already are — not a fact this domain's own
|
|
148
|
+
# model states about itself, which `policy` (inside the bluebook)
|
|
149
|
+
# remains the right word for.
|
|
150
|
+
#
|
|
151
|
+
# MUST BE A BLOCK, not `translates Event, into: Command` — a flat
|
|
152
|
+
# call's arguments are evaluated eagerly, under THIS builder's own
|
|
153
|
+
# `ConstShim` resolver (`BindingProxy.namespace`, set up by
|
|
154
|
+
# `self.build` below), which mints a `BindingProxy` INSTANCE per
|
|
155
|
+
# segment and cannot answer a further `::` (it is not a Module) —
|
|
156
|
+
# a multi-segment reference like `Deploy::Tenant::TenantProvisioned`
|
|
157
|
+
# would raise before this method ever ran. `port_impl`'s own
|
|
158
|
+
# `ConstShim.with(->(const) { const })` swap only works because
|
|
159
|
+
# its own callers never pass a multi-segment bare constant as an
|
|
160
|
+
# eagerly-evaluated argument — everything of that shape lives
|
|
161
|
+
# inside ITS block, evaluated later, under the swapped resolver.
|
|
162
|
+
# A block defers evaluation the same way; a flat call cannot.
|
|
163
|
+
def translates(name, &block)
|
|
164
|
+
bluebook_ir = Hecks.current_registry.bluebook(@domain) or
|
|
165
|
+
raise Malformed, "#{@domain} declares no such bluebook — translates needs one to attach its reaction to"
|
|
166
|
+
|
|
167
|
+
# SAME RESOLVER BluebookBuilder ITSELF uses for a `policy`
|
|
168
|
+
# block's own `on`/`trigger` (bluebook_builder.rb) — a
|
|
169
|
+
# `ScopedConstant`, not the bare passthrough `port_impl` swaps
|
|
170
|
+
# to, because `on`/`trigger` here take genuinely multi-segment
|
|
171
|
+
# references (`Deploy::Tenant::TenantProvisioned`), not a
|
|
172
|
+
# single-segment type name.
|
|
173
|
+
resolver = ->(const) { ConstShim::ScopedConstant.for(const) }
|
|
174
|
+
built = ConstShim.with(resolver) { PolicyBuilder.build(name, &block) }
|
|
175
|
+
|
|
176
|
+
bluebook_ir.add_policy(built)
|
|
177
|
+
end
|
|
178
|
+
|
|
138
179
|
# Assembles the collected binds, subscriptions and attachments into a `Hecksagon`.
|
|
139
180
|
#
|
|
140
181
|
# No ungoverned-role check here — see
|
|
@@ -23,6 +23,9 @@ module Hecks
|
|
|
23
23
|
# against: AggregateBuilder's own `@value_objects + closed_sets`,
|
|
24
24
|
# or EntityBuilder's owner's, since a piece mints none of its own.
|
|
25
25
|
module IdentityDeclaration
|
|
26
|
+
# Records which of the three live `identified_by` forms a builder declared, deferring
|
|
27
|
+
# resolution until build time.
|
|
28
|
+
#
|
|
26
29
|
# There are three live forms, deliberately distinguishable at the
|
|
27
30
|
# declaration site:
|
|
28
31
|
#
|
|
@@ -33,12 +36,12 @@ module Hecks
|
|
|
33
36
|
# One symbol is retired: it cannot say whether the author means a
|
|
34
37
|
# value concept or a field-shaped database key. Frozen source still
|
|
35
38
|
# reaches the old interpretation through `legacy_identified_by`.
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
39
|
+
# Both `AggregateBuilder` and `EntityBuilder` name this method in their Keyword rows'
|
|
40
|
+
# `calls:` column, so `GenericDispatch` forwards `identified_by` here unchanged, both
|
|
41
|
+
# after boot (reading the live grammar table) and during it
|
|
42
|
+
# (`GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`, which carries every `calls:`-routed row
|
|
43
|
+
# unconditionally, not a hand-picked bootstrap-reachable subset — though this one is
|
|
44
|
+
# exercised during boot too, since every self-hosted aggregate/entity declares an identity).
|
|
42
45
|
# Dispatches across the three live forms documented above (value-
|
|
43
46
|
# object + block, single type target, single/compound field
|
|
44
47
|
# target), each an early return that sets exactly one pending
|
|
@@ -46,6 +49,20 @@ module Hecks
|
|
|
46
49
|
# with-nil semantics and the shared `@name`/`identity_pool`
|
|
47
50
|
# threaded back out as parameters, for no gain beyond what the
|
|
48
51
|
# three-forms comment above already documents.
|
|
52
|
+
#
|
|
53
|
+
# @param targets [Array<Symbol, Module>] zero or more identity targets: a value-object
|
|
54
|
+
# type (a bareword, resolved by `ConstShim` to a `Module`), one or more attribute-name
|
|
55
|
+
# symbols for a field or compound key, or empty when `definition` is given
|
|
56
|
+
# @param as [Symbol, nil] the field name to mint for a value-object-type target; not
|
|
57
|
+
# accepted with a field or compound-key target
|
|
58
|
+
# @yield a bespoke value-object body, `instance_eval`'d by `ValueObjectBuilder.build` to
|
|
59
|
+
# mint the identity's own anonymous value object
|
|
60
|
+
# @return [Array<Symbol>, Array<String>, nil] `targets` when a compound key is declared,
|
|
61
|
+
# the resolved identity paths from `legacy_identified_by` in shadow-parsing mode, or
|
|
62
|
+
# `nil` otherwise; callers reach this through `GenericDispatch`, which discards it
|
|
63
|
+
# @raise [Bluebook::DSL::Malformed] if `identified_by` was already called, combines a
|
|
64
|
+
# block with targets, names no identity, mixes a value-object type with field names,
|
|
65
|
+
# passes `as:` with a field or compound key, or the block declares no attributes
|
|
49
66
|
# rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
50
67
|
def identified_by_impl(*targets, as: nil, &definition)
|
|
51
68
|
return legacy_identified_by(*targets, as: as, &definition) if MetaValidator.shadow_parsing?
|
|
@@ -13,10 +13,13 @@ module Hecks
|
|
|
13
13
|
|
|
14
14
|
include WordGate
|
|
15
15
|
|
|
16
|
+
# @param name [String] the policy's own name, as given to `policy "Name" do ... end`
|
|
16
17
|
def initialize(name)
|
|
17
18
|
@name = name
|
|
18
19
|
end
|
|
19
20
|
|
|
21
|
+
# Records the event this policy reacts to.
|
|
22
|
+
#
|
|
20
23
|
# `on Account::AccountFrozen` — a bare constant accepted (ADR
|
|
21
24
|
# 0025, S6 — "events first-class"), resolved through `ConstShim`
|
|
22
25
|
# the same way `trigger`/`dispatch` already resolve a command
|
|
@@ -28,17 +31,22 @@ module Hecks
|
|
|
28
31
|
# AccountFrozen"` (quoted) and `on Account::AccountFrozen`
|
|
29
32
|
# (bare) stay admitted until a full migration lands.
|
|
30
33
|
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
34
|
+
# Reached through `calls: "on_impl"` rather than `GenericDispatch`'s generic single-fill
|
|
35
|
+
# coercion, the same way `trigger_impl` below overrides its own generic default — `on`
|
|
36
|
+
# admits both a bare constant and quoted text, not one single argument kind.
|
|
37
|
+
#
|
|
38
|
+
# @param event_ref [Symbol, String, Module] the event, as a bare constant (a
|
|
39
|
+
# `ScopedConstant` module `ConstShim` resolves) or quoted text
|
|
40
|
+
# @return [void]
|
|
35
41
|
def on_impl(event_ref)
|
|
36
42
|
@on_event = Naming.event_ref(event_ref)
|
|
37
43
|
end
|
|
38
44
|
|
|
45
|
+
# Records the command this policy dispatches and, optionally, how the event's payload
|
|
46
|
+
# projects onto it.
|
|
47
|
+
#
|
|
39
48
|
# `with:` — what the trigger is given, when the event's own shape
|
|
40
|
-
# is not it. Omitted, the whole event payload forwards verbatim
|
|
41
|
-
# which is what every policy did before this existed.
|
|
49
|
+
# is not it. Omitted, the whole event payload forwards verbatim.
|
|
42
50
|
#
|
|
43
51
|
# Same `key => value` shape a saga's own `dispatch ..., with:`
|
|
44
52
|
# takes, and read the same way at runtime: a Symbol names a field
|
|
@@ -53,21 +61,28 @@ module Hecks
|
|
|
53
61
|
# reactions" — command references become first-class): `trigger
|
|
54
62
|
# Account::Debit`, a bare constant `ConstShim` resolves the same
|
|
55
63
|
# way `reference_to Account` always has, not a quoted verb string.
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
# thing here).
|
|
64
|
+
# Matches the qualified-or-not shape a saga's own `dispatch`
|
|
65
|
+
# command name takes — see `Naming.command_ref`'s own header for
|
|
66
|
+
# how the `::`/`.` rewrite works, and `SagaInterpreter#qualified`
|
|
67
|
+
# for why an unqualified form is always enough (same-domain is
|
|
68
|
+
# the fallback, so `Account::Debit` and `Banking::Account::Debit`
|
|
69
|
+
# mean the same thing here).
|
|
63
70
|
#
|
|
64
71
|
# Legacy under shadow-parsing (S0a's own bridge) — frozen era
|
|
65
72
|
# text still writes the quoted form.
|
|
66
73
|
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
74
|
+
# `["Policy", "trigger"] => :trigger_impl` is in
|
|
75
|
+
# `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`, which carries every `calls:`-routed row
|
|
76
|
+
# unconditionally — this one is also exercised during boot itself, since every
|
|
77
|
+
# self-hosted policy declares a trigger.
|
|
78
|
+
#
|
|
79
|
+
# @param command_ref [Symbol, String, Module] the command, as a bare constant (a
|
|
80
|
+
# `ScopedConstant` module `ConstShim` resolves) or, under shadow-parsing, quoted text
|
|
81
|
+
# @param with [Hash{Symbol => Object}, nil] a projection from the triggering event's payload
|
|
82
|
+
# onto the command's own arguments; a Symbol value names an event field, anything else is
|
|
83
|
+
# a literal; `nil` forwards the whole event payload verbatim
|
|
84
|
+
# @return [void]
|
|
85
|
+
# @raise [Bluebook::DSL::Malformed] if `command_ref` is quoted text outside shadow-parsing
|
|
71
86
|
def trigger_impl(command_ref, with: nil)
|
|
72
87
|
if command_ref.is_a?(::String) && !MetaValidator.shadow_parsing?
|
|
73
88
|
raise Malformed,
|
|
@@ -80,17 +95,28 @@ module Hecks
|
|
|
80
95
|
@projection_declared = !with.nil?
|
|
81
96
|
end
|
|
82
97
|
|
|
98
|
+
# Records the domain this policy's trigger reaches into.
|
|
99
|
+
#
|
|
83
100
|
# `across "Notifications"` names the domain a trigger reaches into.
|
|
84
101
|
# `expect_undelivered: true` declares that this domain expects that
|
|
85
102
|
# target never to be reached (no such domain, on purpose), which
|
|
86
103
|
# `ModelCheck` holds it to in both directions. Reached through
|
|
87
|
-
# `calls
|
|
88
|
-
# coercion
|
|
104
|
+
# `calls: "across_impl"` rather than the generic single-fill
|
|
105
|
+
# coercion, because `expect_undelivered:` is a keyword argument the
|
|
106
|
+
# generic coercion cannot take.
|
|
107
|
+
#
|
|
108
|
+
# @param domain [String, Symbol] the target domain's name
|
|
109
|
+
# @param expect_undelivered [Boolean] whether this domain expects `across`'s target to
|
|
110
|
+
# name no real domain; `ModelCheck` holds it to that in both directions
|
|
111
|
+
# @return [void]
|
|
89
112
|
def across_impl(domain, expect_undelivered: false)
|
|
90
113
|
@target_domain = domain.to_s
|
|
91
114
|
@expect_undelivered = expect_undelivered == true
|
|
92
115
|
end
|
|
93
116
|
|
|
117
|
+
# Records the guard predicate that decides whether this policy applies to a triggering
|
|
118
|
+
# event.
|
|
119
|
+
#
|
|
94
120
|
# **The guard** — same extraction CommandBuilder#given/#ensures already
|
|
95
121
|
# use (Ports::Extraction reads the block's source ; the block itself
|
|
96
122
|
# is never called, here or at runtime — Runtime::PolicyInterpreter
|
|
@@ -105,6 +131,12 @@ module Hecks
|
|
|
105
131
|
# Evaluated against the triggering event's own payload, not a
|
|
106
132
|
# stored record — a policy reacts to what just happened, and has no
|
|
107
133
|
# aggregate instance of its own to read state from.
|
|
134
|
+
#
|
|
135
|
+
# @yield the guard predicate, extracted as source text and evaluated later by
|
|
136
|
+
# `Runtime::PolicyInterpreter` against the triggering event's payload; never called here
|
|
137
|
+
# @return [void]
|
|
138
|
+
# @raise [Bluebook::DSL::Malformed] if the block's source could not be extracted, or if it
|
|
139
|
+
# references a pattern `Expression::AstJson.refuse_unshared_patterns!` does not share
|
|
108
140
|
def where(&predicate)
|
|
109
141
|
canonical = Ports::Extraction.canonical(predicate)
|
|
110
142
|
|
|
@@ -129,6 +161,10 @@ module Hecks
|
|
|
129
161
|
# `for_each` — item #13's full metaprogrammed dispatch, slice 1:
|
|
130
162
|
# same shape as `on`, above.
|
|
131
163
|
|
|
164
|
+
# Builds the `Policy` this builder has accumulated.
|
|
165
|
+
#
|
|
166
|
+
# @return [Bluebook::Policy] the built policy, carrying every field set by `on`,
|
|
167
|
+
# `trigger`, `across`, `where`, `for_each`, and `with:`
|
|
132
168
|
def build
|
|
133
169
|
Policy.new(
|
|
134
170
|
name: @name,
|
|
@@ -142,6 +178,11 @@ module Hecks
|
|
|
142
178
|
).tap { |policy| policy.instance_variable_set(:@projection_declared, !!@projection_declared) }
|
|
143
179
|
end
|
|
144
180
|
|
|
181
|
+
# Builds a `Policy` from a `policy "Name" do ... end` block.
|
|
182
|
+
#
|
|
183
|
+
# @param name [String] the policy's own name
|
|
184
|
+
# @yield the policy's body, `instance_eval`'d against a new builder
|
|
185
|
+
# @return [Bluebook::Policy] the built policy
|
|
145
186
|
def self.build(name, &block)
|
|
146
187
|
builder = new(name)
|
|
147
188
|
builder.instance_eval(&block) if block
|
|
@@ -16,11 +16,14 @@ module Hecks
|
|
|
16
16
|
|
|
17
17
|
include WordGate
|
|
18
18
|
|
|
19
|
+
# @param name [String] the process manager's own name, as written after `process_manager`
|
|
19
20
|
def initialize(name)
|
|
20
21
|
@name = name
|
|
21
22
|
@handlers = []
|
|
22
23
|
end
|
|
23
24
|
|
|
25
|
+
# Records the event that begins a fresh instance of this process manager.
|
|
26
|
+
#
|
|
24
27
|
# `starts_on Transfer::TransferRequested` — bare constant
|
|
25
28
|
# accepted (ADR 0025, S6 — "events first-class"), resolved
|
|
26
29
|
# through `ConstShim` the same way `on_impl`/`transition_impl`
|
|
@@ -31,11 +34,21 @@ module Hecks
|
|
|
31
34
|
# a "." qualified one). A plain String still passes through
|
|
32
35
|
# unchanged, both for `shadow_parse` and for any corpus site a
|
|
33
36
|
# future pass hasn't migrated yet.
|
|
37
|
+
#
|
|
38
|
+
# @param event_ref [Symbol, String, Module] the event, as a bare constant (a
|
|
39
|
+
# `ScopedConstant` module `ConstShim` resolves) or quoted text
|
|
40
|
+
# @return [void]
|
|
34
41
|
def starts_on_impl(event_ref)
|
|
35
42
|
@starts_on = Naming.event_name_ref(event_ref)
|
|
36
43
|
end
|
|
37
44
|
|
|
45
|
+
# Records the event this process manager expects to close out its instance on.
|
|
46
|
+
#
|
|
38
47
|
# `ends_on` — same reasoning as `starts_on_impl`, above.
|
|
48
|
+
#
|
|
49
|
+
# @param event_ref [Symbol, String, Module] the event, as a bare constant (a
|
|
50
|
+
# `ScopedConstant` module `ConstShim` resolves) or quoted text
|
|
51
|
+
# @return [void]
|
|
39
52
|
def ends_on_impl(event_ref)
|
|
40
53
|
@ends_on = Naming.event_name_ref(event_ref)
|
|
41
54
|
end
|
|
@@ -45,15 +58,18 @@ module Hecks
|
|
|
45
58
|
# kind-driven coerce-and-assign with nothing else, executed by
|
|
46
59
|
# `GenericDispatch`.
|
|
47
60
|
#
|
|
48
|
-
# `starts_on`/`ends_on`
|
|
49
|
-
#
|
|
50
|
-
# ArgumentSeed row (`starts_on
|
|
51
|
-
# `GenericDispatch::COERCE_BY_KIND`
|
|
52
|
-
# `"symbol"`, and `shape_for` refuses to pick a
|
|
53
|
-
# shape at all once two Argument rows share a
|
|
54
|
-
# (`arguments.size == 1` below), so both words are
|
|
55
|
-
#
|
|
56
|
-
|
|
61
|
+
# `starts_on`/`ends_on` are hand-written rather than table-driven
|
|
62
|
+
# the same way, because ADR 0025 S6 gives each a second,
|
|
63
|
+
# `kind: "constant"` ArgumentSeed row (`starts_on
|
|
64
|
+
# Transfer::TransferRequested`) — `GenericDispatch::COERCE_BY_KIND`
|
|
65
|
+
# only knows `"text"`/`"symbol"`, and `shape_for` refuses to pick a
|
|
66
|
+
# `single_fill` shape at all once two Argument rows share a
|
|
67
|
+
# `fills:` target (`arguments.size == 1` below), so both words are
|
|
68
|
+
# `calls:`-routed like `transition` already is.
|
|
69
|
+
|
|
70
|
+
# Records one leg of this process manager's state machine: the event that takes it, the
|
|
71
|
+
# state(s) it applies from, and the dispatches (and optional compensation) it fires.
|
|
72
|
+
#
|
|
57
73
|
# One state-machine vocabulary (S7, ADR 0025 — "events and
|
|
58
74
|
# reactions"): the same word `Lifecycle#transition` already
|
|
59
75
|
# carries, one level over — `transition "AccountDebited" =>
|
|
@@ -89,10 +105,18 @@ module Hecks
|
|
|
89
105
|
# persistence/rehydration) needs no change at all: what changed
|
|
90
106
|
# is how the declaration reaches that same shape, not the shape
|
|
91
107
|
# a real run ever sees or persists.
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
108
|
+
# Reached through `calls: "transition_impl"`. Not exercised during boot itself — checked
|
|
109
|
+
# directly, no core/attached chapter declares a `ProcessManager` of its own — though the
|
|
110
|
+
# (context, word) pair is still carried in `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`
|
|
111
|
+
# unconditionally, along with every other `calls:`-routed row.
|
|
112
|
+
#
|
|
113
|
+
# @param mapping [Hash] one `event => target_state` pair plus a required `from:` key
|
|
114
|
+
# naming the source state(s) (`String`, `Symbol`, or `Array<String, Symbol>`) this
|
|
115
|
+
# transition applies from
|
|
116
|
+
# @yield the transition's dispatch body, `instance_eval`'d against a fresh `HandlerBuilder`
|
|
117
|
+
# @return [void]
|
|
118
|
+
# @raise [Bluebook::DSL::ProcessManagerBuilder::InvalidProcessManager] if `mapping` names
|
|
119
|
+
# no `from:`
|
|
96
120
|
def transition_impl(mapping, &block)
|
|
97
121
|
mapping = mapping.dup
|
|
98
122
|
from = mapping.delete(:from)
|
|
@@ -142,6 +166,14 @@ module Hecks
|
|
|
142
166
|
end
|
|
143
167
|
end
|
|
144
168
|
|
|
169
|
+
# Assembles the declared transitions into a `ProcessManager`, after validating them.
|
|
170
|
+
#
|
|
171
|
+
# @return [Bluebook::ProcessManager] the built process manager, with its own `states`
|
|
172
|
+
# derived from the declared transitions
|
|
173
|
+
# @raise [Bluebook::DSL::ProcessManagerBuilder::InvalidProcessManager] if `correlates_by`
|
|
174
|
+
# is undeclared or names a whole field rather than one scalar, if `starts_on` is
|
|
175
|
+
# undeclared, if no transition was declared, or if two transitions answer the same event
|
|
176
|
+
# from the same source state
|
|
145
177
|
def build
|
|
146
178
|
validate!
|
|
147
179
|
|
|
@@ -155,6 +187,13 @@ module Hecks
|
|
|
155
187
|
)
|
|
156
188
|
end
|
|
157
189
|
|
|
190
|
+
# Evaluates a `process_manager` block against a fresh builder and returns what it built.
|
|
191
|
+
#
|
|
192
|
+
# @param name [String] the process manager's own name
|
|
193
|
+
# @yield the process manager's body, `instance_eval`'d against a new builder; may be
|
|
194
|
+
# omitted
|
|
195
|
+
# @return [Bluebook::ProcessManager] the built process manager
|
|
196
|
+
# @raise [Bluebook::DSL::ProcessManagerBuilder::InvalidProcessManager] see `#build`
|
|
158
197
|
def self.build(name, &block)
|
|
159
198
|
builder = new(name)
|
|
160
199
|
builder.instance_eval(&block) if block
|
|
@@ -264,16 +303,19 @@ module Hecks
|
|
|
264
303
|
|
|
265
304
|
include WordGate
|
|
266
305
|
|
|
306
|
+
# Starts this handler's own dispatch list empty.
|
|
267
307
|
def initialize = @dispatches = []
|
|
268
308
|
|
|
309
|
+
# Records one command this transition dispatches, and, if given a block, the
|
|
310
|
+
# compensation that reverses it.
|
|
311
|
+
#
|
|
269
312
|
# The command itself (ADR 0025, "events and reactions" — command
|
|
270
313
|
# references become first-class), same shape and same reasons
|
|
271
314
|
# as `PolicyBuilder#trigger`'s own header — bare constant live,
|
|
272
315
|
# quoted text only under shadow-parsing (S0a's bridge; frozen
|
|
273
316
|
# era text still writes `dispatch "Banking::Account.Debit"`).
|
|
274
317
|
#
|
|
275
|
-
#
|
|
276
|
-
# dispatch (slice 4), same reasoning as trigger_impl above.
|
|
318
|
+
# Reached through `calls: "dispatch_impl"`.
|
|
277
319
|
#
|
|
278
320
|
# An optional block opens `compensates` on this dispatch
|
|
279
321
|
# specifically — per-dispatch saga compensation, replacing a
|
|
@@ -287,6 +329,17 @@ module Hecks
|
|
|
287
329
|
# (`SagaInterpreter`'s own `completed_compensations`) and
|
|
288
330
|
# compensates only those, newest first, instead of trusting an
|
|
289
331
|
# author's static list to be complete and correctly ordered.
|
|
332
|
+
#
|
|
333
|
+
# @param command_ref [Symbol, String, Module] the command, as a bare constant (a
|
|
334
|
+
# `ScopedConstant` module `ConstShim` resolves) or, under shadow-parsing, quoted text
|
|
335
|
+
# @param with [Hash{Symbol => Object}, nil] a projection onto the command's own
|
|
336
|
+
# arguments, the same `key => value` shape a policy's own `trigger ..., with:` takes;
|
|
337
|
+
# `nil` forwards the triggering context verbatim
|
|
338
|
+
# @yield the dispatch's own `compensates` body, `instance_eval`'d against a
|
|
339
|
+
# `DispatchBuilder`
|
|
340
|
+
# @return [Bluebook::DispatchSpec] the dispatch just recorded
|
|
341
|
+
# @raise [Bluebook::DSL::ProcessManagerBuilder::InvalidProcessManager] if `command_ref`
|
|
342
|
+
# is quoted text outside shadow-parsing
|
|
290
343
|
def dispatch_impl(command_ref, with: nil, &block)
|
|
291
344
|
if command_ref.is_a?(::String) && !MetaValidator.shadow_parsing?
|
|
292
345
|
raise InvalidProcessManager,
|
|
@@ -330,6 +383,17 @@ module Hecks
|
|
|
330
383
|
|
|
331
384
|
include WordGate
|
|
332
385
|
|
|
386
|
+
# Records the command that reverses the dispatch this `compensates` block sits inside.
|
|
387
|
+
#
|
|
388
|
+
# @param command_ref [Symbol, String, Module] the compensating command, as a bare
|
|
389
|
+
# constant (a `ScopedConstant` module `ConstShim` resolves) or, under shadow-parsing,
|
|
390
|
+
# quoted text
|
|
391
|
+
# @param with [Hash{Symbol => Object}, nil] a projection onto the command's own
|
|
392
|
+
# arguments, the same shape `HandlerBuilder#dispatch_impl`'s own `with:` takes; `nil`
|
|
393
|
+
# forwards the triggering context verbatim
|
|
394
|
+
# @return [Bluebook::DispatchSpec] the compensating dispatch just recorded
|
|
395
|
+
# @raise [Bluebook::DSL::ProcessManagerBuilder::InvalidProcessManager] if `command_ref`
|
|
396
|
+
# is quoted text outside shadow-parsing
|
|
333
397
|
def compensates_impl(command_ref, with: nil)
|
|
334
398
|
if command_ref.is_a?(::String) && !MetaValidator.shadow_parsing?
|
|
335
399
|
raise InvalidProcessManager,
|
|
@@ -15,28 +15,48 @@ module Hecks
|
|
|
15
15
|
include QuerySpecification::Common::DSL
|
|
16
16
|
include WordGate
|
|
17
17
|
|
|
18
|
+
# @param name [String] the query's name, as written after `query`
|
|
18
19
|
def initialize(name)
|
|
19
20
|
@name = name
|
|
20
21
|
@wheres = []
|
|
21
22
|
end
|
|
22
23
|
|
|
24
|
+
# Sets the human-readable description shown for this query.
|
|
25
|
+
#
|
|
26
|
+
# @param value [String] the description text
|
|
27
|
+
# @return [String] the description as stored
|
|
23
28
|
def description(value) = @description = value
|
|
24
29
|
|
|
30
|
+
# Declares a query parameter that names another aggregate's identity.
|
|
31
|
+
#
|
|
25
32
|
# A query parameter naming another aggregate's own identity
|
|
26
33
|
# (Card.Active's own `Board`, filtering to one board's cards) —
|
|
27
34
|
# just a plain attribute typed as a reference,
|
|
28
|
-
# AttributeCollector#
|
|
35
|
+
# `AttributeCollector#attribute_impl` already handling a Reference
|
|
29
36
|
# exactly like any other. No "acts on itself" case to
|
|
30
37
|
# distinguish here the way a command's own reference_to has —
|
|
31
38
|
# a query has no root of its own to act on, only parameters.
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
39
|
+
#
|
|
40
|
+
# Answers the `reference_to` word through the table's `calls:`
|
|
41
|
+
# column — item #13's full metaprogrammed dispatch (slice 4b).
|
|
42
|
+
# Bootstrap-reachable, in `GenericDispatch::BOOTSTRAP_CALLS_FALLBACK`.
|
|
43
|
+
#
|
|
44
|
+
# @param type [Module, Symbol, String] the referenced aggregate, written as a bare
|
|
45
|
+
# constant
|
|
46
|
+
# @param as [Symbol, nil] the parameter's name; nil derives it from the target
|
|
47
|
+
# @param optional [Boolean] whether the parameter may be omitted when the query runs
|
|
48
|
+
# @return [void]
|
|
49
|
+
# @raise [Bluebook::DSL::Malformed] if `as` (or the derived name) is already declared
|
|
35
50
|
def reference_to_impl(type, as: nil, optional: false)
|
|
36
51
|
target = Naming.demodulise(type)
|
|
37
52
|
attribute_impl(as || default_reference_name(target), Reference.new(target), optional: optional)
|
|
38
53
|
end
|
|
39
54
|
|
|
55
|
+
# Assembles the declared parameters and filtering into a `Query`.
|
|
56
|
+
#
|
|
57
|
+
# @return [Bluebook::Query] the built query
|
|
58
|
+
# @raise [Bluebook::DSL::Malformed] if the body declares `cursor`, which no interpreter
|
|
59
|
+
# implements
|
|
40
60
|
def build
|
|
41
61
|
seal_cursor
|
|
42
62
|
Query.new(
|
|
@@ -54,6 +74,15 @@ module Hecks
|
|
|
54
74
|
)
|
|
55
75
|
end
|
|
56
76
|
|
|
77
|
+
# Evaluates a `query` block against a fresh builder, then fills in owner-derived types.
|
|
78
|
+
#
|
|
79
|
+
# @param name [String] the query's name
|
|
80
|
+
# @param owner_attributes [Array<Bluebook::Attribute>] the enclosing aggregate or
|
|
81
|
+
# entity's own attributes, matched against the block's own parameter names
|
|
82
|
+
# @yield the query body, evaluated with the builder as `self`; may be omitted
|
|
83
|
+
# @return [Bluebook::Query] the built query
|
|
84
|
+
# @raise [Bluebook::DSL::Malformed] if the body declares `cursor`, a duplicate attribute,
|
|
85
|
+
# or a filtering clause the query language refuses
|
|
57
86
|
def self.build(name, owner_attributes: [], &block)
|
|
58
87
|
builder = new(name)
|
|
59
88
|
builder.instance_eval(&block) if block
|