hecks 3.1.3 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +3 -3
- data/lib/hecks/cli/check_era.rb +3 -39
- data/lib/hecks/deploy/bluebook/deploy.bluebook +105 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +8 -0
- data/lib/hecks/deploy/oidc.json +15 -0
- data/lib/hecks/doors/mcp_door.rb +16 -2
- data/lib/hecks/doors/mcp_door_scope.rb +43 -4
- data/lib/hecks/doors/mcp_guide_cache.rb +75 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +17 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +10 -2
- data/lib/hecks/hecks/adapters/cost_explorer.adapter +7 -0
- data/lib/hecks/hecks/adapters/cost_explorer.rb +102 -0
- data/lib/hecks/hecks/adapters/rust_workspace.rb +1 -1
- data/lib/hecks/hecks/adapters/site_toolchain.rb +21 -11
- data/lib/hecks/hecks/hecks.hecksagon +8 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +7 -7
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +1 -1
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -3
- data/lib/hecks/projections/deploy/box/settings.rb +23 -2
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +3 -1
- data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +17 -0
- data/lib/hecks/projections/deploy/box.rb +13 -2
- data/lib/hecks/projections/site/edge/behaviours.rb +5 -3
- data/lib/hecks/projections/site/edge/checks.rb +31 -5
- data/lib/hecks/projections/site/edge.rb +25 -11
- data/lib/hecks/projections/site/routes_ts.rb +50 -27
- data/lib/hecks/projections/site/site_cdn.rb +7 -4
- data/lib/hecks/projections/site/table/checks.rb +53 -7
- data/lib/hecks/projections/site/table/link_builder.rb +64 -0
- data/lib/hecks/projections/site/table/row_builder.rb +21 -6
- data/lib/hecks/projections/site/table.rb +16 -5
- data/lib/hecks/rust_build/kernel_input.rb +18 -6
- data/lib/hecks/rust_build/project_rust.rb +6 -132
- data/lib/hecks/site/bluebook/site.bluebook +27 -13
- data/lib/hecks/storehouse.rb +35 -1
- data/lib/hecks/tools/comment_style.rb +4 -2
- data/lib/hecks/tools/deploy_recipe.rb +1 -0
- data/lib/hecks/tools/rust_comment_style.rb +1 -1
- data/lib/hecks/tools/site_routes.rb +69 -28
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +1 -1
- data/lib/hecks.rb +1 -0
- data/rust/build/src/lineage_pass.rs +1 -1
- data/rust/build/src/pipeline.rs +1 -1
- data/rust/codegen/src/attr.rs +1 -1
- data/rust/codegen/src/bridging.rs +1 -1
- data/rust/codegen/src/commands.rs +1 -1
- data/rust/codegen/src/constraints.rs +1 -1
- data/rust/codegen/src/dependency_planning.rs +1 -1
- data/rust/codegen/src/domain_generator.rs +2 -2
- data/rust/codegen/src/exemplar.rs +1 -1
- data/rust/codegen/src/expr_emitter.rs +1 -1
- data/rust/codegen/src/fielded.rs +1 -1
- data/rust/codegen/src/json_codec.rs +1 -1
- data/rust/codegen/src/main.rs +3 -4
- data/rust/codegen/src/mutations.rs +1 -1
- data/rust/codegen/src/naming.rs +1 -1
- data/rust/codegen/src/ports.rs +1 -1
- data/rust/codegen/src/queries.rs +3 -3
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/read_models.rs +2 -2
- data/rust/codegen/src/reference_specs.rs +1 -1
- data/rust/codegen/src/registry.rs +4 -4
- data/rust/codegen/src/shared.rs +1 -1
- data/rust/codegen/src/skip_reason.rs +1 -1
- data/rust/codegen/src/types.rs +4 -4
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/auth.rs +25 -1
- data/rust/host/src/needs.rs +94 -27
- data/rust/host/src/web.rs +9 -5
- data/rust/src/exemplar/commands.rs +1 -1
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/fielded.rs +1 -1
- data/rust/src/exemplar/json.rs +1 -1
- data/rust/src/exemplar/mod.rs +1 -1
- data/rust/src/exemplar/mutations.rs +1 -1
- data/rust/src/exemplar/queries.rs +1 -1
- data/rust/src/exemplar/reactions.rs +1 -1
- data/rust/src/exemplar/read_models.rs +1 -1
- data/rust/src/exemplar/registry.rs +1 -1
- data/rust/src/exemplar/types.rs +1 -1
- data/rust/src/kernel/cli.rs +2 -2
- data/rust/src/kernel/read_model.rs +1 -1
- data/rust/src/lib.rs +1 -1
- metadata +5 -23
- data/rust/project/bridging.rb +0 -257
- data/rust/project/commands.rb +0 -877
- data/rust/project/constraints.rb +0 -86
- data/rust/project/dependency_planning.rb +0 -178
- data/rust/project/domain_generator.rb +0 -983
- data/rust/project/exemplar.rb +0 -148
- data/rust/project/expr_emitter.rb +0 -72
- data/rust/project/fielded.rb +0 -141
- data/rust/project/json_codec.rb +0 -641
- data/rust/project/mutations.rb +0 -695
- data/rust/project/naming.rb +0 -223
- data/rust/project/ports.rb +0 -65
- data/rust/project/queries.rb +0 -493
- data/rust/project/reactions.rb +0 -317
- data/rust/project/read_models.rb +0 -690
- data/rust/project/reference_specs.rb +0 -27
- data/rust/project/registry.rb +0 -396
- data/rust/project/skip_reason.rb +0 -29
- data/rust/project/types.rb +0 -225
- data/rust/project/write_if_changed.rb +0 -7
- data/rust/project.rb +0 -30
- data/rust/project_rust_pipeline.rb +0 -391
data/rust/project/reactions.rb
DELETED
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
module RustProjection
|
|
2
|
-
module Projector
|
|
3
|
-
module_function
|
|
4
|
-
|
|
5
|
-
# Re-derives event_qualifier/event_name from on_event the same way
|
|
6
|
-
# Policy#event_qualifier/#event_name do — the wire only carries on_event whole.
|
|
7
|
-
def policy_event_qualifier(on_event)
|
|
8
|
-
text = on_event.to_s
|
|
9
|
-
text.include?(".") ? text.split(".", 2).first : nil
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def policy_event_name(on_event)
|
|
13
|
-
text = on_event.to_s
|
|
14
|
-
text.include?(".") ? text.split(".", 2).last : text
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Emits PolicyRule rows for policies targeting domain_name only; a policy
|
|
18
|
-
# targeting another domain routes through emit_cross_domain_policy_table.
|
|
19
|
-
def emit_policy_table(domain_name, policies, aggregates = [])
|
|
20
|
-
fns = where_fns(policies)
|
|
21
|
-
rows = local_policy_rows(domain_name, policies, aggregates)
|
|
22
|
-
|
|
23
|
-
Exemplar.render(
|
|
24
|
-
"policy_table",
|
|
25
|
-
'crate::kernel::PolicyRule { policy_name: "tmpl_policy_name", event_name: "tmpl_event_name", event_qualifier: None, target_verb: "tmpl_target_verb", for_each: None, for_each_key: None, with_spec: &[], where_expr: None },' => rows.join("\n")
|
|
26
|
-
).then { |table| fns.empty? ? table : "#{table}\n\n#{fns.join("\n\n")}" }
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Merges policy rows across the target domain and every attached chapter;
|
|
30
|
-
# each source is qualified against its own domain_name/aggregates.
|
|
31
|
-
def emit_merged_policy_table(sources)
|
|
32
|
-
rows = sources.flat_map { |source| local_policy_rows(source[:domain_name], source[:policies], source[:aggregates]) }
|
|
33
|
-
fns = sources.flat_map { |source| where_fns(source[:policies]) }
|
|
34
|
-
|
|
35
|
-
Exemplar.render(
|
|
36
|
-
"policy_table",
|
|
37
|
-
'crate::kernel::PolicyRule { policy_name: "tmpl_policy_name", event_name: "tmpl_event_name", event_qualifier: None, target_verb: "tmpl_target_verb", for_each: None, for_each_key: None, with_spec: &[], where_expr: None },' => rows.join("\n")
|
|
38
|
-
).then { |table| fns.empty? ? table : "#{table}\n\n#{fns.join("\n\n")}" }
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def local_policy_rows(domain_name, policies, aggregates = [])
|
|
42
|
-
policies.filter_map do |policy|
|
|
43
|
-
target_domain = policy[:target_domain] || domain_name
|
|
44
|
-
next nil unless target_domain == domain_name
|
|
45
|
-
|
|
46
|
-
event_name = policy_event_name(policy[:on_event])
|
|
47
|
-
qualifier = policy_event_qualifier(policy[:on_event])
|
|
48
|
-
qualifier_expr = qualifier ? "Some(#{qualifier.inspect})" : "None"
|
|
49
|
-
target_verb = "#{target_domain}::#{policy[:trigger_command]}"
|
|
50
|
-
|
|
51
|
-
# policy_name lets orchestrate.rs's reaction_log/saga_log name the
|
|
52
|
-
# policy that fired, matching what CrossDomainPolicyRule already carries.
|
|
53
|
-
" crate::kernel::PolicyRule { policy_name: #{policy[:name].to_s.inspect}, event_name: #{event_name.inspect}, " \
|
|
54
|
-
"event_qualifier: #{qualifier_expr}, target_verb: #{target_verb.inspect}, " \
|
|
55
|
-
"for_each: #{fan_out_verb_expr(domain_name, policy)}, " \
|
|
56
|
-
"for_each_key: #{fan_out_key_expr(domain_name, policy, aggregates)}, " \
|
|
57
|
-
"with_spec: #{with_spec_expr(policy)}, where_expr: #{where_expr(policy)} },"
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# where_expr is a generated function, not a literal: PolicyRule is a const
|
|
62
|
-
# row but Expr owns boxes, so a where { ... } predicate compiles to a callback.
|
|
63
|
-
def where_fn_name(policy) = "where_#{dispatch_fn_name(rust_ident(policy[:name].to_s))}"
|
|
64
|
-
|
|
65
|
-
def where_expr(policy)
|
|
66
|
-
policy[:where].to_s.empty? ? "None" : "Some(#{where_fn_name(policy)})"
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def where_fns(policies)
|
|
70
|
-
policies.reject { |policy| policy[:where].to_s.empty? }.map do |policy|
|
|
71
|
-
"fn #{where_fn_name(policy)}() -> crate::kernel::Expr {\n" \
|
|
72
|
-
" use crate::kernel::Expr;\n" \
|
|
73
|
-
" #{ExprEmitter.emit_ast(policy[:where_ast])}\n" \
|
|
74
|
-
"}"
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Resolves a bare "Aggregate.query" for_each spelling against the policy's
|
|
79
|
-
# own domain here, so the kernel's own lookup stays a plain table hit.
|
|
80
|
-
def fan_out_verb_expr(domain_name, policy)
|
|
81
|
-
for_each = policy[:for_each].to_s
|
|
82
|
-
return "None" if for_each.empty?
|
|
83
|
-
|
|
84
|
-
"Some(#{(for_each.include?("::") ? for_each : "#{domain_name}::#{for_each}").inspect})"
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Addressing key: a command declared on the aggregate it references uses
|
|
88
|
-
# its bare reference key; one merely holding a reference uses that attribute.
|
|
89
|
-
def fan_out_key_expr(domain_name, policy, aggregates)
|
|
90
|
-
for_each = policy[:for_each].to_s
|
|
91
|
-
return "None" if for_each.empty?
|
|
92
|
-
|
|
93
|
-
path, = for_each.split(".", 2)
|
|
94
|
-
row_aggregate = path.to_s.include?("::") ? path.split("::", 2).last : path
|
|
95
|
-
command = target_command_for(policy, aggregates)
|
|
96
|
-
key = command && addressing_key_for(command, row_aggregate)
|
|
97
|
-
key ? "Some(#{key.to_s.inspect})" : "None"
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def target_command_for(policy, aggregates)
|
|
101
|
-
aggregate_name, command_name = policy[:trigger_command].to_s.split(".", 2)
|
|
102
|
-
aggregate = Array(aggregates).find { |candidate| candidate[:name].to_s == aggregate_name.to_s }
|
|
103
|
-
Array(aggregate && aggregate[:commands]).find { |candidate| candidate[:name].to_s == command_name.to_s }
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def addressing_key_for(command, aggregate_name)
|
|
107
|
-
return snake_case(aggregate_name) if command[:references].to_s == aggregate_name.to_s
|
|
108
|
-
|
|
109
|
-
held = Array(command[:attributes]).find { |attribute| attribute[:type].to_s == "Reference<#{aggregate_name}>" }
|
|
110
|
-
held && held[:name]
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def snake_case(name)
|
|
114
|
-
name.to_s.gsub(/([a-z\d])([A-Z])/, '\\1_\\2').gsub(/([A-Z]+)([A-Z][a-z])/, '\\1_\\2').downcase
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
# Each binding rides the wire already rendered (Literal::render, so a
|
|
118
|
-
# Symbol keeps its leading colon and stays distinct from a same-spelled string).
|
|
119
|
-
def with_spec_expr(policy)
|
|
120
|
-
pairs = Array(policy[:with_spec])
|
|
121
|
-
return "&[]" if pairs.empty?
|
|
122
|
-
|
|
123
|
-
"&[#{pairs.map { |key, value| "(#{key.to_s.inspect}, #{value.to_s.inspect})" }.join(', ')}]"
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# target_verb is always fully qualified ("Domain::Aggregate.Command") —
|
|
127
|
-
# the same form dispatch_by_name and every cross-Lambda caller expect.
|
|
128
|
-
def cross_domain_policy_rows(domain_name, policies)
|
|
129
|
-
policies.filter_map do |policy|
|
|
130
|
-
target_domain = policy[:target_domain] || domain_name
|
|
131
|
-
next nil if target_domain == domain_name
|
|
132
|
-
|
|
133
|
-
event_name = policy_event_name(policy[:on_event])
|
|
134
|
-
qualifier = policy_event_qualifier(policy[:on_event])
|
|
135
|
-
qualifier_expr = qualifier ? "Some(#{qualifier.inspect})" : "None"
|
|
136
|
-
target_verb = "#{target_domain}::#{policy[:trigger_command]}"
|
|
137
|
-
|
|
138
|
-
" crate::kernel::CrossDomainPolicyRule { policy_name: #{policy[:name].to_s.inspect}, event_name: #{event_name.inspect}, " \
|
|
139
|
-
"event_qualifier: #{qualifier_expr}, target_domain: #{target_domain.inspect}, target_verb: #{target_verb.inspect}, " \
|
|
140
|
-
"where_expr: #{where_expr(policy)} },"
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def emit_cross_domain_policy_table(domain_name, policies)
|
|
145
|
-
rows = cross_domain_policy_rows(domain_name, policies)
|
|
146
|
-
|
|
147
|
-
puts "cross-domain policy table: #{rows.size} row(s) — delivered by rust/host's lambda_client.rs, not locally dispatched" if rows.any?
|
|
148
|
-
|
|
149
|
-
Exemplar.render(
|
|
150
|
-
"cross_domain_policy_table",
|
|
151
|
-
'crate::kernel::CrossDomainPolicyRule { policy_name: "tmpl_policy_name", event_name: "tmpl_event_name", event_qualifier: None, target_domain: "tmpl_target_domain", target_verb: "tmpl_target_verb", where_expr: None },' =>
|
|
152
|
-
rows.join("\n")
|
|
153
|
-
)
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# Merges cross-domain rows across every attached chapter the same way
|
|
157
|
-
# emit_merged_policy_table merges local ones; aggregates are unused here.
|
|
158
|
-
def emit_merged_cross_domain_policy_table(sources)
|
|
159
|
-
rows = sources.flat_map { |source| cross_domain_policy_rows(source[:domain_name], source[:policies]) }
|
|
160
|
-
|
|
161
|
-
puts "cross-domain policy table: #{rows.size} row(s) — delivered by rust/host's lambda_client.rs, not locally dispatched" if rows.any?
|
|
162
|
-
|
|
163
|
-
Exemplar.render(
|
|
164
|
-
"cross_domain_policy_table",
|
|
165
|
-
'crate::kernel::CrossDomainPolicyRule { policy_name: "tmpl_policy_name", event_name: "tmpl_event_name", event_qualifier: None, target_domain: "tmpl_target_domain", target_verb: "tmpl_target_verb", where_expr: None },' =>
|
|
166
|
-
rows.join("\n")
|
|
167
|
-
)
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# Reuses Marks.read (mutations.rb's append_field_source is the same round
|
|
171
|
-
# trip) rather than re-deriving the with: literal's wire spelling.
|
|
172
|
-
def with_value_parsed(raw)
|
|
173
|
-
Hecks::Bluebook::Assembly::Marks.read(raw)
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# Builds a Json value expression rather than a const literal: Json holds
|
|
177
|
-
# Vec/String, which isn't const-constructible in stable Rust.
|
|
178
|
-
def json_literal_expr(value)
|
|
179
|
-
case value
|
|
180
|
-
when Hash
|
|
181
|
-
fields = value.map { |k, v| "(#{k.to_s.inspect}.to_string(), #{json_literal_expr(v)})" }
|
|
182
|
-
"crate::kernel::Json::Object(vec![#{fields.join(', ')}])"
|
|
183
|
-
when String
|
|
184
|
-
"crate::kernel::Json::Str(#{value.inspect}.to_string())"
|
|
185
|
-
when Integer
|
|
186
|
-
"crate::kernel::Json::int(#{value})"
|
|
187
|
-
when Float
|
|
188
|
-
"crate::kernel::Json::Num(#{value}f64, None)"
|
|
189
|
-
when true, false
|
|
190
|
-
"crate::kernel::Json::Bool(#{value})"
|
|
191
|
-
when nil
|
|
192
|
-
"crate::kernel::Json::Null"
|
|
193
|
-
else
|
|
194
|
-
raise "unsupported with: literal #{value.inspect} — json_literal_expr doesn't cover this shape"
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
# A bare Symbol is a runtime WithValue::Ref; anything else is a literal,
|
|
199
|
-
# emitted as its own fn() -> Json collected into literal_fns.
|
|
200
|
-
def emit_with_value(raw, literal_fns)
|
|
201
|
-
parsed = with_value_parsed(raw)
|
|
202
|
-
return "crate::kernel::WithValue::Ref(#{parsed.to_s.inspect})" if parsed.is_a?(Symbol)
|
|
203
|
-
|
|
204
|
-
fn_name = "pm_literal_#{literal_fns.size}"
|
|
205
|
-
literal_fns << Exemplar.render(
|
|
206
|
-
"with_value_literal_fn",
|
|
207
|
-
"tmpl_literal_fn" => fn_name,
|
|
208
|
-
"tmpl_body_placeholder()" => json_literal_expr(parsed)
|
|
209
|
-
)
|
|
210
|
-
"crate::kernel::WithValue::Literal(#{fn_name})"
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# compensates recurses into itself at most one level: a compensation is
|
|
214
|
-
# never itself compensable.
|
|
215
|
-
def emit_dispatch_spec(spec, literal_fns)
|
|
216
|
-
with_pairs = spec[:with_spec].map { |key, raw| "(#{key.to_s.inspect}, #{emit_with_value(raw, literal_fns)})" }
|
|
217
|
-
compensates = spec[:compensates] ? "Some(&#{emit_dispatch_spec(spec[:compensates], literal_fns)})" : "None"
|
|
218
|
-
"crate::kernel::DispatchSpec { command_name: #{spec[:command_name].inspect}, with: &[#{with_pairs.join(', ')}], compensates: #{compensates} }"
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
def emit_handler(handler, literal_fns)
|
|
222
|
-
dispatches = handler[:dispatches].map { |d| emit_dispatch_spec(d, literal_fns) }
|
|
223
|
-
"crate::kernel::Handler { event_type: #{handler[:event_type].inspect}, from_state: #{handler[:from_state].inspect}, to_state: #{handler[:to_state].inspect}, dispatches: &[#{dispatches.join(', ')}] }"
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
# No cross-domain narrowing needed here the way emit_policy_table needs
|
|
227
|
-
# one: every dispatch's command_name is already domain-qualified on the wire.
|
|
228
|
-
def emit_process_manager_table(process_managers)
|
|
229
|
-
literal_fns = []
|
|
230
|
-
pm_exprs = process_managers.map do |pm|
|
|
231
|
-
handlers = pm[:handlers].map { |h| emit_handler(h, literal_fns) }
|
|
232
|
-
"crate::kernel::ProcessManagerDef { name: #{pm[:name].inspect}, correlates_by: #{pm[:correlates_by].inspect}, " \
|
|
233
|
-
"starts_on: #{pm[:starts_on].inspect}, ends_on: #{pm[:ends_on].inspect}, initial_state: #{pm[:states].first.inspect}, " \
|
|
234
|
-
"handlers: &[#{handlers.join(', ')}] }"
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
Exemplar.render(
|
|
238
|
-
"process_manager_table",
|
|
239
|
-
"fn tmpl_literal_fns_placeholder() {}" => literal_fns.join("\n"),
|
|
240
|
-
' crate::kernel::ProcessManagerDef { name: "tmpl_pm_name", correlates_by: "tmpl_correlates_by", starts_on: "tmpl_starts_on", ends_on: "tmpl_ends_on", initial_state: "tmpl_initial_state", handlers: &[] },' =>
|
|
241
|
-
pm_exprs.map { |e| " #{e}," }.join("\n")
|
|
242
|
-
)
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
# Precomputes Naming.reference_key(event.aggregate) per aggregate for
|
|
246
|
-
# kernel::orchestrate's correlation fallback, keyed by qualified aggregate name.
|
|
247
|
-
def emit_reference_key_table(chapters)
|
|
248
|
-
arms = chapters.flat_map do |domain_name, aggregate_names|
|
|
249
|
-
aggregate_names.map do |name|
|
|
250
|
-
qualified = "#{domain_name}::#{name}"
|
|
251
|
-
key = Hecks::Naming.snake(name)
|
|
252
|
-
" #{qualified.inspect} => Some(#{key.inspect}),"
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
Exemplar.render("reference_key_table", '"tmpl_qualified" => Some("tmpl_key"),' => arms.join("\n"))
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
# Whether a verb's dispatch creates its own record, needed before
|
|
260
|
-
# orchestrate.rs's routing split can promote an addressing key into to:.
|
|
261
|
-
def emit_creates_table(aggregates)
|
|
262
|
-
arms = aggregates.flat_map do |aggregate|
|
|
263
|
-
(Array(aggregate[:commands]).map { |c| [c[:verb], c[:creates]] } +
|
|
264
|
-
Array(aggregate[:entity_commands]).map { |c| [c[:verb], false] }).map do |verb, creates|
|
|
265
|
-
" #{verb.inspect} => #{creates},"
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
Exemplar.render("creates_table", '"tmpl_verb" => true,' => arms.join("\n"))
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
# Single-component identity only; a composite identity (more than one
|
|
273
|
-
# component) is a known gap, skipped rather than guessed at.
|
|
274
|
-
def emit_identity_head_table(aggregates)
|
|
275
|
-
arms = aggregates.filter_map do |aggregate|
|
|
276
|
-
heads = Array(aggregate[:identified_by])
|
|
277
|
-
next if heads.size != 1
|
|
278
|
-
|
|
279
|
-
head = heads.first.to_s.split(".").first
|
|
280
|
-
qualified = "#{aggregate[:domain_name]}::#{aggregate[:name]}"
|
|
281
|
-
" #{qualified.inspect} => Some(#{head.inspect}),"
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
Exemplar.render("identity_head_table", '"tmpl_qualified" => Some("tmpl_head"),' => arms.join("\n"))
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
# Entity-level counterpart to emit_identity_head_table, keyed by
|
|
288
|
-
# "Domain::Aggregate.Entity"; composite identities are out of scope.
|
|
289
|
-
def emit_entity_identity_head_table(aggregates)
|
|
290
|
-
arms = aggregates.flat_map do |aggregate|
|
|
291
|
-
Array(aggregate[:entities]).filter_map do |entity|
|
|
292
|
-
heads = Array(entity[:identified_by])
|
|
293
|
-
next if heads.size != 1
|
|
294
|
-
|
|
295
|
-
head = heads.first.to_s.split(".").first
|
|
296
|
-
qualified = "#{aggregate[:domain_name]}::#{aggregate[:name]}.#{entity[:name]}"
|
|
297
|
-
" #{qualified.inspect} => Some(#{head.inspect}),"
|
|
298
|
-
end
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
Exemplar.render("entity_identity_head_table", '"tmpl_qualified" => Some("tmpl_head"),' => arms.join("\n"))
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
# Declared attribute names per verb, used to filter a policy/process-
|
|
305
|
-
# manager dispatch's with: facts down to what the target command declares.
|
|
306
|
-
def emit_command_attributes_table(aggregates)
|
|
307
|
-
arms = aggregates.flat_map do |aggregate|
|
|
308
|
-
(Array(aggregate[:commands]) + Array(aggregate[:entity_commands])).map do |c|
|
|
309
|
-
names = Array(c[:attributes]).map { |name| name.to_s.inspect }.join(", ")
|
|
310
|
-
" #{c[:verb].inspect} => &[#{names}],"
|
|
311
|
-
end
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
Exemplar.render("command_attributes_table", '"tmpl_verb" => &["tmpl_attr"],' => arms.join("\n"))
|
|
315
|
-
end
|
|
316
|
-
end
|
|
317
|
-
end
|