hecks 1.4.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/hecks/adapters/driven/claude_code.rb +65 -0
- data/lib/hecks/adapters/driven/folder.rb +73 -0
- data/lib/hecks/adapters/driven/google_authentication.rb +25 -4
- data/lib/hecks/adapters/driven/governance_authorization.rb +57 -6
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -0
- data/lib/hecks/adapters/driven/heki/saga_store.rb +53 -7
- data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +93 -2
- data/lib/hecks/adapters/driven/identity_registry.rb +10 -0
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +22 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +35 -7
- data/lib/hecks/adapters/driven/lambda.rb +43 -2
- data/lib/hecks/adapters/driven/local_storage.rb +67 -1
- data/lib/hecks/adapters/driven/memory.rb +13 -13
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +20 -0
- data/lib/hecks/adapters/driven/prism.rb +46 -7
- data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
- data/lib/hecks/adapters/driven/system_clock.rb +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
- data/lib/hecks/adapters/driven.rb +2 -0
- data/lib/hecks/adapters/driving/github_webhook.rb +14 -1
- data/lib/hecks/behaviors/dsl.rb +58 -0
- data/lib/hecks/behaviors/expectations.rb +168 -7
- data/lib/hecks/behaviors/ir.rb +11 -0
- data/lib/hecks/behaviors/rspec.rb +8 -0
- data/lib/hecks/behaviors/runner.rb +19 -0
- data/lib/hecks/behaviors.rb +8 -0
- data/lib/hecks/bluebook/aggregate.rb +30 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +7 -0
- data/lib/hecks/bluebook/assembly/build.rb +25 -4
- data/lib/hecks/bluebook/assembly/contract.rb +66 -14
- data/lib/hecks/bluebook/assembly/contracts.rb +24 -19
- data/lib/hecks/bluebook/assembly/marks.rb +143 -14
- data/lib/hecks/bluebook/assembly/specializer.rb +19 -0
- data/lib/hecks/bluebook/assembly.rb +25 -7
- data/lib/hecks/bluebook/attribute.rb +17 -3
- data/lib/hecks/bluebook/behaviour/aggregate.rb +32 -0
- data/lib/hecks/bluebook/behaviour/attribute.rb +13 -0
- data/lib/hecks/bluebook/behaviour/chapter.rb +55 -2
- data/lib/hecks/bluebook/behaviour/command.rb +33 -3
- data/lib/hecks/bluebook/behaviour/domain_port.rb +20 -0
- data/lib/hecks/bluebook/behaviour/entity.rb +12 -0
- data/lib/hecks/bluebook/behaviour/hexagon.rb +26 -0
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +23 -2
- data/lib/hecks/bluebook/behaviour/policy.rb +34 -9
- data/lib/hecks/bluebook/behaviour/process_manager.rb +32 -1
- data/lib/hecks/bluebook/behaviour/query.rb +5 -0
- data/lib/hecks/bluebook/behaviour/read_model.rb +21 -0
- data/lib/hecks/bluebook/behaviour/traits.rb +36 -0
- data/lib/hecks/bluebook/behaviour/value_object.rb +17 -5
- data/lib/hecks/bluebook/chapter.rb +23 -0
- data/lib/hecks/bluebook/command.rb +53 -8
- data/lib/hecks/bluebook/domain_port.rb +25 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +196 -37
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +79 -42
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +59 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +385 -53
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +135 -21
- data/lib/hecks/bluebook/dsl/command_builder.rb +189 -27
- data/lib/hecks/bluebook/dsl/entity_builder.rb +139 -9
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +41 -0
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +23 -6
- data/lib/hecks/bluebook/dsl/policy_builder.rb +60 -19
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +79 -15
- data/lib/hecks/bluebook/dsl/query_builder.rb +33 -4
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +102 -27
- data/lib/hecks/bluebook/dsl/rule_reference.rb +60 -8
- data/lib/hecks/bluebook/dsl/translation_builder.rb +146 -40
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +55 -7
- data/lib/hecks/bluebook/entity.rb +29 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +115 -23
- data/lib/hecks/bluebook/expression/ast_reader.rb +29 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +48 -9
- data/lib/hecks/bluebook/expression/evaluator.rb +207 -29
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +36 -0
- data/lib/hecks/bluebook/expression/resolver.rb +320 -77
- data/lib/hecks/bluebook/hexagon.rb +34 -0
- data/lib/hecks/bluebook/lifecycle.rb +11 -0
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/judge.rb +30 -26
- data/lib/hecks/bluebook/meta_validator/plan.rb +43 -8
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/readings.rb +157 -7
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -4
- data/lib/hecks/bluebook/meta_validator/shapes.rb +141 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +149 -23
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +5 -4
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +1 -0
- data/lib/hecks/bluebook/meta_validator.rb +180 -84
- data/lib/hecks/bluebook/model_check.rb +268 -24
- data/lib/hecks/bluebook/pattern_subset.rb +23 -1
- data/lib/hecks/bluebook/process_manager.rb +13 -0
- data/lib/hecks/bluebook/project_discovery.rb +5 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +44 -0
- data/lib/hecks/bluebook/query.rb +27 -0
- data/lib/hecks/bluebook/read_model.rb +21 -1
- data/lib/hecks/bluebook/reference.rb +21 -8
- data/lib/hecks/bluebook/smoke_test.rb +29 -6
- data/lib/hecks/bluebook/synthesizer.rb +34 -0
- data/lib/hecks/bluebook/translation.rb +30 -1
- data/lib/hecks/bluebook/value_object.rb +23 -5
- data/lib/hecks/bluebook.rb +3 -4
- data/lib/hecks/codemod.rb +107 -20
- data/lib/hecks/construct.rb +15 -1
- data/lib/hecks/corpus.rb +146 -25
- data/lib/hecks/deploy/bluebook/deploy.bluebook +105 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/deploy/oidc.json +5 -0
- data/lib/hecks/doc/reference.rb +185 -16
- data/lib/hecks/embryonaut_bluebook.rb +32 -9
- data/lib/hecks/facade/handle.rb +76 -3
- data/lib/hecks/facade/surface/aggregate_door.rb +8 -0
- data/lib/hecks/forms/field_shape.rb +3 -0
- data/lib/hecks/forms/page.rb +14 -0
- data/lib/hecks/forms/port_argument.rb +12 -0
- data/lib/hecks/forms/query_form_renderer.rb +63 -0
- data/lib/hecks/forms/record_renderer.rb +58 -0
- data/lib/hecks/forms/record_table.rb +27 -0
- data/lib/hecks/forms/reference_options.rb +24 -0
- data/lib/hecks/forms/value_object_shape.rb +10 -0
- data/lib/hecks/fqn.rb +58 -0
- data/lib/hecks/framework/bluebook/compliance.bluebook +221 -0
- data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
- data/lib/hecks/framework/oidc.json +15 -0
- data/lib/hecks/framework.rb +43 -20
- data/lib/hecks/freezer.rb +17 -1
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +159 -24
- data/lib/hecks/fuzzing/combination_miner.rb +59 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +109 -8
- data/lib/hecks/fuzzing/coverage_campaign.rb +56 -13
- data/lib/hecks/fuzzing/differential.rb +34 -0
- data/lib/hecks/fuzzing/domain_generator.rb +188 -11
- data/lib/hecks/fuzzing/era_boundary.rb +45 -15
- data/lib/hecks/fuzzing/form_census.rb +86 -0
- data/lib/hecks/fuzzing/generated_domain_check.rb +76 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +39 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +79 -22
- data/lib/hecks/fuzzing/nondeterministic.rb +13 -1
- data/lib/hecks/fuzzing/persistence_parity.rb +95 -3
- data/lib/hecks/fuzzing/properties/corrections.rb +25 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +158 -14
- data/lib/hecks/fuzzing/properties/guards.rb +44 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +48 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +18 -0
- data/lib/hecks/fuzzing/properties/outbox.rb +49 -11
- data/lib/hecks/fuzzing/properties/querying.rb +68 -14
- data/lib/hecks/fuzzing/properties.rb +24 -15
- data/lib/hecks/fuzzing/qa_settings.rb +12 -0
- data/lib/hecks/fuzzing/replay.rb +137 -29
- data/lib/hecks/fuzzing/rotation_priority.rb +41 -21
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +46 -20
- data/lib/hecks/fuzzing/self_consistency.rb +189 -40
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +12 -6
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +1 -1
- data/lib/hecks/fuzzing/sequence_generator.rb +47 -0
- data/lib/hecks/fuzzing/shrinker.rb +114 -2
- data/lib/hecks/fuzzing/structural_skips.rb +18 -4
- data/lib/hecks/fuzzing/sweep_depth.rb +8 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +61 -14
- data/lib/hecks/fuzzing/value_generator.rb +98 -10
- data/lib/hecks/grammar/evolve.rb +178 -2
- data/lib/hecks/grammar.rb +46 -0
- data/lib/hecks/ir.rb +38 -7
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
- data/lib/hecks/literal.rb +32 -0
- data/lib/hecks/naming.rb +88 -7
- data/lib/hecks/ports/access_control.rb +5 -10
- data/lib/hecks/ports/authorization.rb +3 -6
- data/lib/hecks/ports/identity_assignment.rb +1 -2
- data/lib/hecks/ports/identity_resolution.rb +1 -2
- data/lib/hecks/ports/key_vault.port +6 -0
- data/lib/hecks/ports/key_vault.rb +85 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +6 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +38 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +40 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +92 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +8 -0
- data/lib/hecks/ports/query/in_memory.rb +39 -6
- data/lib/hecks/ports/query/ordering.rb +15 -0
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projections/bootstrap_table.rb +43 -8
- data/lib/hecks/projections/deploy/fargate.rb +666 -0
- data/lib/hecks/projections/deploy/lambda.rb +2423 -0
- data/lib/hecks/projections/deploy/shared.rb +624 -0
- data/lib/hecks/projections/diagrams.rb +243 -7
- data/lib/hecks/projections/glossary/html.rb +88 -0
- data/lib/hecks/projections/glossary/markdown.rb +47 -0
- data/lib/hecks/projections/glossary/mermaid.rb +48 -0
- data/lib/hecks/projections/glossary/sentences.rb +105 -0
- data/lib/hecks/projections/glossary.rb +161 -19
- data/lib/hecks/projections/model/deviations.rb +44 -0
- data/lib/hecks/projections/model.rb +51 -1
- data/lib/hecks/projections/oidc.rb +40 -3
- data/lib/hecks/projections/parser_table.rb +49 -8
- data/lib/hecks/projections/reference.rb +12 -0
- data/lib/hecks/projections/rust_vocabulary.rb +219 -16
- data/lib/hecks/projections/shape.rb +8 -0
- data/lib/hecks/projections/statements.rb +63 -16
- data/lib/hecks/projections/vocabulary.rb +17 -0
- data/lib/hecks/projections.rb +3 -0
- data/lib/hecks/projector/cli_projector.rb +218 -10
- data/lib/hecks/projector/docs_projector.rb +145 -19
- data/lib/hecks/projector/exporter.rb +65 -11
- data/lib/hecks/projector/ir_projector.rb +6 -0
- data/lib/hecks/projector/narrate_projector.rb +136 -15
- data/lib/hecks/projector/target.rb +47 -10
- data/lib/hecks/projector.rb +101 -19
- data/lib/hecks/query_ir.rb +47 -0
- data/lib/hecks/query_specification/common/null_policy.rb +5 -3
- data/lib/hecks/rendering.rb +6 -0
- data/lib/hecks/router/namespace_installer.rb +13 -0
- data/lib/hecks/router.rb +55 -0
- data/lib/hecks/runtime/aggregate_lock.rb +9 -0
- data/lib/hecks/runtime/boot_gates.rb +18 -0
- data/lib/hecks/runtime/caller.rb +32 -0
- data/lib/hecks/runtime/capability_graph.rb +11 -0
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +23 -21
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +14 -15
- data/lib/hecks/runtime/command_interpreter.rb +42 -17
- data/lib/hecks/runtime/command_rules/admissibility.rb +165 -14
- data/lib/hecks/runtime/command_rules/arithmetic.rb +17 -5
- data/lib/hecks/runtime/command_rules/references.rb +118 -28
- data/lib/hecks/runtime/dependency_planning.rb +45 -0
- data/lib/hecks/runtime/dispatcher.rb +28 -50
- data/lib/hecks/runtime/entity_element.rb +161 -8
- data/lib/hecks/runtime/entity_interpreter.rb +44 -9
- data/lib/hecks/runtime/errors.rb +18 -4
- data/lib/hecks/runtime/event.rb +10 -5
- data/lib/hecks/runtime/identity.rb +71 -3
- data/lib/hecks/runtime/instance.rb +67 -7
- data/lib/hecks/runtime/interpreting.rb +13 -5
- data/lib/hecks/runtime/invocation.rb +118 -36
- data/lib/hecks/runtime/loader.rb +94 -8
- data/lib/hecks/runtime/outbox.rb +145 -7
- data/lib/hecks/runtime/policy_interpreter.rb +22 -9
- data/lib/hecks/runtime/port_operation_interpreter.rb +20 -0
- data/lib/hecks/runtime/query_interpreter.rb +40 -12
- data/lib/hecks/runtime/reaction_invocation.rb +53 -8
- data/lib/hecks/runtime/read_model_interpreter.rb +23 -7
- data/lib/hecks/runtime/rebuild_sweep.rb +28 -0
- data/lib/hecks/runtime/reference_hop.rb +42 -0
- data/lib/hecks/runtime/refusal_wording.rb +50 -0
- data/lib/hecks/runtime/registry/saga_persistence.rb +11 -0
- data/lib/hecks/runtime/registry/verification.rb +119 -4
- data/lib/hecks/runtime/registry.rb +157 -4
- data/lib/hecks/runtime/remote_dispatcher.rb +92 -6
- data/lib/hecks/runtime/routing.rb +27 -2
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +10 -12
- data/lib/hecks/runtime/saga_interpreter.rb +27 -13
- data/lib/hecks/runtime/tenant_check.rb +26 -6
- data/lib/hecks/runtime/tenant_scope.rb +18 -0
- data/lib/hecks/runtime/value/coercion.rb +255 -33
- data/lib/hecks/runtime/value/entity_list_coercion.rb +102 -30
- data/lib/hecks/runtime/value.rb +50 -0
- data/lib/hecks/runtime.rb +32 -0
- data/lib/hecks/storehouse.rb +305 -9
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks.rb +79 -1
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -0
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +12 -2
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +29 -5
- metadata +14 -5
- data/lib/hecks/codemod/legacy_dispatch_args.rb +0 -299
- data/lib/hecks/codemod/legacy_dispatch_recorder.rb +0 -186
- data/lib/hecks/deprecation.rb +0 -95
- data/lib/hecks/framework/bluebook/compliance.bluebook +0 -1
|
@@ -133,6 +133,111 @@ Hecks.bluebook "Deploy" do
|
|
|
133
133
|
end
|
|
134
134
|
end
|
|
135
135
|
|
|
136
|
+
# A sibling of `LambdaTarget`, not a shared shape with it — value objects
|
|
137
|
+
# are not shared across aggregates in this DSL (`Tenant`'s own
|
|
138
|
+
# `DomainName`/`Realm`/`Schema` below already make the same point), so
|
|
139
|
+
# this aggregate carries its own copies of `DomainName`/`Region`/
|
|
140
|
+
# `Database`/`Web` rather than reaching into `LambdaTarget`'s.
|
|
141
|
+
aggregate "FargateTarget" do
|
|
142
|
+
description "One domain's validated AWS Fargate deploy target — what deployed_to(\"AwsFargate\") must resolve to before bin/project_deploy generates anything from it."
|
|
143
|
+
|
|
144
|
+
identified_by :domain
|
|
145
|
+
|
|
146
|
+
attribute :domain, DomainName
|
|
147
|
+
attribute :region, Region
|
|
148
|
+
attribute :cpu, Cpu
|
|
149
|
+
attribute :memory, Memory
|
|
150
|
+
attribute :database, Database
|
|
151
|
+
attribute :web, Web
|
|
152
|
+
attribute :port, Port
|
|
153
|
+
|
|
154
|
+
value_object "DomainName" do
|
|
155
|
+
attribute :value, String
|
|
156
|
+
invariant("a domain is named") { !value.to_s.empty? }
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
value_object "Region" do
|
|
160
|
+
attribute :value, String
|
|
161
|
+
invariant("a region is named") { !value.to_s.empty? }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# A positive integer, nothing more — AWS's real cpu/memory
|
|
165
|
+
# combinations (256 cpu units needs 512-2048 MB of `memory`, say)
|
|
166
|
+
# are a pairing `ECS` itself enforces at deploy time; encoding that
|
|
167
|
+
# whole table here would duplicate AWS's own rules for no benefit
|
|
168
|
+
# this generator needs. This only catches a typo (zero, negative)
|
|
169
|
+
# before a single file is generated, the same floor `Memory`/`Port`
|
|
170
|
+
# below hold for their own ranges.
|
|
171
|
+
value_object "Cpu" do
|
|
172
|
+
attribute :value, Integer
|
|
173
|
+
invariant("cpu is positive") { value.positive? }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Fargate's own valid range, not Lambda's 128-10240 MB —
|
|
177
|
+
# `LambdaTarget`'s own `Memory` value object (above) is scoped to
|
|
178
|
+
# that aggregate alone, so this is a distinct value object under the
|
|
179
|
+
# same name rather than a shared one. A plain sanity floor and
|
|
180
|
+
# ceiling, not AWS's own cpu-paired table (see `Cpu`'s own comment).
|
|
181
|
+
value_object "Memory" do
|
|
182
|
+
attribute :value, Integer
|
|
183
|
+
invariant("memory is positive") { value.positive? }
|
|
184
|
+
invariant("memory is at most 122880 MB") { value <= 122880 }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Same shape and reasoning as `LambdaTarget`'s own `Database` —
|
|
188
|
+
# "Postgres"/"Aurora" provision this domain's own RDS instance;
|
|
189
|
+
# "Shared" borrows another already-deployed domain's instance,
|
|
190
|
+
# isolated by a native Postgres schema. See that value object's own
|
|
191
|
+
# comment for the full reasoning; it applies unchanged here.
|
|
192
|
+
value_object "Database" do
|
|
193
|
+
attribute :value, String, one_of: ["Postgres", "Aurora", "Shared"]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Same shape and reasoning as `LambdaTarget`'s own `Web` — "Rust"
|
|
197
|
+
# means the domain's own task also serves its public web UI
|
|
198
|
+
# in-process, over the port this aggregate's own `Port` attribute
|
|
199
|
+
# declares.
|
|
200
|
+
value_object "Web" do
|
|
201
|
+
attribute :value, String, one_of: ["None", "Rust"]
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# The container port the task listens on and the target group
|
|
205
|
+
# routes to — a real TCP port, not an arbitrary integer.
|
|
206
|
+
value_object "Port" do
|
|
207
|
+
attribute :value, Integer
|
|
208
|
+
invariant("a port is at least 1") { value >= 1 }
|
|
209
|
+
invariant("a port is at most 65535") { value <= 65535 }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# All seven required, for the identical reason `LambdaTarget.Declare`
|
|
213
|
+
# requires all of its own attributes — see that command's own
|
|
214
|
+
# comment on `normalize_args`/`coerce_declared_arguments`. This
|
|
215
|
+
# validates a fully-resolved target, after `bin/project_deploy`'s own
|
|
216
|
+
# defaulting (`cpu`/`memory`/`port` each fall back to a Ruby-side
|
|
217
|
+
# default before this ever dispatches), not instead of it.
|
|
218
|
+
command "Declare" do
|
|
219
|
+
goal "Validate one domain's AwsFargate deploy target before anything is generated from it"
|
|
220
|
+
|
|
221
|
+
attribute :domain, DomainName
|
|
222
|
+
attribute :region, Region
|
|
223
|
+
attribute :cpu, Cpu
|
|
224
|
+
attribute :memory, Memory
|
|
225
|
+
attribute :database, Database
|
|
226
|
+
attribute :web, Web
|
|
227
|
+
attribute :port, Port
|
|
228
|
+
|
|
229
|
+
sets :domain
|
|
230
|
+
sets :region
|
|
231
|
+
sets :cpu
|
|
232
|
+
sets :memory
|
|
233
|
+
sets :database
|
|
234
|
+
sets :web
|
|
235
|
+
sets :port
|
|
236
|
+
|
|
237
|
+
emits "FargateTargetDeclared"
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
136
241
|
# `bin/project_tenant`'S OWN VALIDATED SHAPE — same split LambdaTarget
|
|
137
242
|
# draws with `bin/project_deploy`: this aggregate validates, that
|
|
138
243
|
# script GENERATES/ACTS (writes `environments/<slug>.world`, ensures
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
# The Deploy domain, bound so it boots standing alone.
|
|
2
2
|
Hecks.hecksagon "Deploy" do
|
|
3
3
|
persisted_by "Memory"
|
|
4
|
+
|
|
5
|
+
# THE IMPERATIVE HALF, BEHIND A REAL PORT — `bin/project_tenant`
|
|
6
|
+
# dispatches this `asks` once `Tenant.Declare` has already validated
|
|
7
|
+
# the request; `TenantProvisioner` (lib/hecks/adapters/driven/
|
|
8
|
+
# tenant_provisioner.rb) writes the overlay file and answers or
|
|
9
|
+
# refuses. `database`/`adapter`/`directory` are declared here, not on
|
|
10
|
+
# `Tenant` itself — CLI-only facts this validated request never
|
|
11
|
+
# needed to hold as its own state. `directory` is the real
|
|
12
|
+
# filesystem path being provisioned — NOT re-derivable from `Tenant`'s
|
|
13
|
+
# own `domain` (its declared NAME, e.g. "Scratch", never a path).
|
|
14
|
+
Deploy::Tenant.port "TenantProvisioning" do
|
|
15
|
+
asks "Provision", to: Tenant do
|
|
16
|
+
attribute :database, DatabaseName
|
|
17
|
+
attribute :adapter, AdapterName
|
|
18
|
+
attribute :directory, DirectoryPath
|
|
19
|
+
answers "TenantProvisioned"
|
|
20
|
+
refuses "ProvisioningRefused"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
4
23
|
end
|
data/lib/hecks/deploy/oidc.json
CHANGED
data/lib/hecks/doc/reference.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Hecks
|
|
|
19
19
|
GENERATED_END = "<!-- generated:end -->".freeze
|
|
20
20
|
TODO_SENTINEL = "<!-- TODO: document this word -->".freeze
|
|
21
21
|
|
|
22
|
-
# A
|
|
22
|
+
# A page's own hand-written opening, harvested under a key no word
|
|
23
23
|
# can ever collide with (words are strings off the Syntax chapter;
|
|
24
24
|
# this is a Symbol). It exists so a page can boot once — load a real
|
|
25
25
|
# corpus chapter, wire its hexagon — and have every word's example
|
|
@@ -31,18 +31,36 @@ module Hecks
|
|
|
31
31
|
|
|
32
32
|
module_function
|
|
33
33
|
|
|
34
|
+
# The marker opening one word's generated region.
|
|
35
|
+
#
|
|
36
|
+
# @param word [String, Symbol, #to_s] the word this section documents
|
|
37
|
+
# @return [String] the HTML comment marking that word's generated region open
|
|
34
38
|
def generated_begin(word) = "<!-- generated:begin word=#{word} -->"
|
|
35
39
|
|
|
36
40
|
# Keyed by region rather than by word — the same marker convention,
|
|
37
41
|
# used for the parts of a page that are not about one word: a
|
|
38
42
|
# page's generated lede here, README's generated indexes below.
|
|
43
|
+
#
|
|
44
|
+
# @param id [String, Symbol, #to_s] the region's id, such as `"page"` or `"tools"`
|
|
45
|
+
# @return [String] the HTML comment marking that region's generated region open
|
|
39
46
|
def region_begin(id) = "<!-- generated:begin id=#{id} -->"
|
|
40
47
|
|
|
48
|
+
# The language's own Syntax aggregate, read off the judged grammar chapter.
|
|
49
|
+
#
|
|
50
|
+
# @return [Bluebook::Aggregate, nil] the Syntax aggregate, or nil if the
|
|
51
|
+
# grammar's Bluebook chapter declares none by that name
|
|
41
52
|
def syntax
|
|
42
53
|
meta = Bluebook::MetaValidator.grammar_registry.bluebook("Bluebook")
|
|
43
54
|
meta.aggregates.find { |aggregate| aggregate.hecks_name == "Syntax" }
|
|
44
55
|
end
|
|
45
56
|
|
|
57
|
+
# Reads one closed-set value object's declared members off the Syntax
|
|
58
|
+
# aggregate, as string-valued Hashes.
|
|
59
|
+
#
|
|
60
|
+
# @param name [String, Symbol, #to_s] the value object's `hecks_name`,
|
|
61
|
+
# such as `"Keyword"`
|
|
62
|
+
# @return [Array<Hash{Symbol => String}>] each declared member's fields,
|
|
63
|
+
# values stringified
|
|
46
64
|
def rows(name)
|
|
47
65
|
syntax.value_objects.find { |vo| vo.hecks_name == name }
|
|
48
66
|
.members.map { |row| row.to_h.transform_values(&:to_s) }
|
|
@@ -53,31 +71,60 @@ module Hecks
|
|
|
53
71
|
# really is a lifecycle. `SyntaxBoot.call` discovers the static
|
|
54
72
|
# aggregate-local seed rows (`KeywordSeed`/`ArgumentSeed`), dispatches each
|
|
55
73
|
# one through the real admission/lifecycle door, and hands back the
|
|
56
|
-
# same shape `rows
|
|
57
|
-
# change.
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
74
|
+
# same shape `rows`, above, already produces — nothing below this
|
|
75
|
+
# needed to change.
|
|
76
|
+
#
|
|
77
|
+
# This module holds no separate `@keywords ||=` memo of its own —
|
|
78
|
+
# delegates straight through to `SyntaxBoot.call`'s own cache
|
|
79
|
+
# (keyed on the grammar registry's own chapter set — see its
|
|
80
|
+
# comment) rather than shadowing it. A second, local cache on top
|
|
81
|
+
# of that one would double-cache with no way to invalidate either
|
|
82
|
+
# half: whichever call happened to land first would lock in
|
|
83
|
+
# forever, even one caught mid-build missing every Paging-attached
|
|
84
|
+
# word (limit/offset/cursor/nulls).
|
|
85
|
+
#
|
|
86
|
+
# @return [Array<Hash{Symbol => String}>] every declared Keyword row
|
|
68
87
|
def keywords = Bluebook::MetaValidator::SyntaxBoot.call[:keywords]
|
|
88
|
+
|
|
89
|
+
# Every declared Argument row.
|
|
90
|
+
#
|
|
91
|
+
# @return [Array<Hash{Symbol => String}>] every declared Argument row
|
|
69
92
|
def arguments = Bluebook::MetaValidator::SyntaxBoot.call[:arguments]
|
|
70
93
|
|
|
94
|
+
# Reads a row's declared status, defaulting when it declared none.
|
|
95
|
+
#
|
|
96
|
+
# @param row [Hash{Symbol => String}] a Keyword or Argument row
|
|
97
|
+
# @return [String] the row's declared status, defaulting to `"admitted"`
|
|
98
|
+
# when it declared none
|
|
71
99
|
def status_of(row) = row[:status].to_s.empty? ? "admitted" : row[:status].to_s
|
|
100
|
+
|
|
101
|
+
# Whether a row is still current enough to appear in the reference.
|
|
102
|
+
#
|
|
103
|
+
# @param row [Hash{Symbol => String}] a Keyword or Argument row
|
|
104
|
+
# @return [Boolean] true if the row's status is `"admitted"` or `"deprecated"`
|
|
72
105
|
def live?(row) = %w[admitted deprecated].include?(status_of(row))
|
|
73
106
|
|
|
107
|
+
# Every distinct context a keyword is declared in.
|
|
108
|
+
#
|
|
109
|
+
# @return [Array<String>] every distinct context a keyword is declared in
|
|
74
110
|
def contexts = keywords.map { |row| row[:context] }.uniq
|
|
75
111
|
|
|
112
|
+
# Derives a context's reference page filename.
|
|
113
|
+
#
|
|
114
|
+
# @param context [String, Symbol, #to_s] a context name, such as `"File"`
|
|
115
|
+
# @return [String] the reference page's filename for that context
|
|
76
116
|
def page_name(context) = "#{Naming.snake(context)}.md"
|
|
77
117
|
|
|
78
118
|
# Every reference page, rendered fresh — prose carried over from
|
|
79
119
|
# the committed pages, new words seeded with the sentinel, orphaned
|
|
80
120
|
# prose refused.
|
|
121
|
+
#
|
|
122
|
+
# @param directory [String] path to the directory holding the committed
|
|
123
|
+
# reference pages, read for their hand-written prose
|
|
124
|
+
# @return [Hash{String => String}] every page's filename (plus `"index.md"`)
|
|
125
|
+
# mapped to its freshly rendered Markdown content
|
|
126
|
+
# @raise [RuntimeError] if a committed page carries prose for a word the
|
|
127
|
+
# language no longer declares in that context
|
|
81
128
|
def pages(directory)
|
|
82
129
|
contexts.each_with_object({}) do |context, pages|
|
|
83
130
|
path = File.join(directory, page_name(context))
|
|
@@ -86,13 +133,22 @@ module Hecks
|
|
|
86
133
|
end.merge("index.md" => render_index)
|
|
87
134
|
end
|
|
88
135
|
|
|
89
|
-
# A
|
|
136
|
+
# A word admitting two forms has two rows — syntax.bluebook's own
|
|
90
137
|
# stated rule, and `identified_by` (a block, or a bare argument and
|
|
91
138
|
# none) is the case that made it real again. One section per word all
|
|
92
139
|
# the same: the prose is the word's rather than the form's, and the
|
|
93
140
|
# argument rows join by (word, context) and so already cover every
|
|
94
141
|
# form. Grouped rather than rendered per row, or a reader would meet
|
|
95
142
|
# the same heading and the same paragraph twice.
|
|
143
|
+
#
|
|
144
|
+
# @param context [String] the context name, such as `"File"`
|
|
145
|
+
# @param prose [Hash{String, Symbol => String}] hand-written prose harvested
|
|
146
|
+
# from the committed page, keyed by word (or `PREAMBLE` for the page's lede)
|
|
147
|
+
# @param path [String] the page's file path, used only in the orphan-refusal
|
|
148
|
+
# message below
|
|
149
|
+
# @return [String] the page's full rendered Markdown
|
|
150
|
+
# @raise [RuntimeError] if `prose` carries a key for a word the language no
|
|
151
|
+
# longer declares in `context`
|
|
96
152
|
def render_page(context, prose, path)
|
|
97
153
|
words = keywords.select { |row| row[:context] == context }.group_by { |row| row[:word] }
|
|
98
154
|
orphans = prose.keys - words.keys - [PREAMBLE]
|
|
@@ -119,6 +175,10 @@ module Hecks
|
|
|
119
175
|
PAGE
|
|
120
176
|
end
|
|
121
177
|
|
|
178
|
+
# The one-line description of where a context's words are typed.
|
|
179
|
+
#
|
|
180
|
+
# @param context [String] the context name, such as `"File"` or `"Command"`
|
|
181
|
+
# @return [String] a sentence naming where words in this context are typed
|
|
122
182
|
def context_lede(context)
|
|
123
183
|
openers = keywords.select { |row| row[:opens] == context }
|
|
124
184
|
return "Words available at the top of a file." if context == "File"
|
|
@@ -131,6 +191,12 @@ module Hecks
|
|
|
131
191
|
# One spelling per form, everything else off the first row — the
|
|
132
192
|
# columns that differ between two forms of one word are `body` (which
|
|
133
193
|
# is what the spelling shows) and nothing else.
|
|
194
|
+
#
|
|
195
|
+
# @param forms [Array<Hash{Symbol => String}>] one word's Keyword rows, one
|
|
196
|
+
# per admitted form
|
|
197
|
+
# @param prose [String, nil] the word's hand-written prose, or nil if none
|
|
198
|
+
# was harvested
|
|
199
|
+
# @return [String] the word's rendered section, generated table plus prose
|
|
134
200
|
def render_word(forms, prose)
|
|
135
201
|
row = forms.first
|
|
136
202
|
table = argument_table(row)
|
|
@@ -152,15 +218,30 @@ module Hecks
|
|
|
152
218
|
WORD
|
|
153
219
|
end
|
|
154
220
|
|
|
221
|
+
# Falls back to the TODO sentinel when a word has no prose yet.
|
|
222
|
+
#
|
|
223
|
+
# @param prose [String, nil] a word's hand-written prose, or nil if none
|
|
224
|
+
# was harvested
|
|
225
|
+
# @return [String] `prose` stripped, or the TODO sentinel if it is blank
|
|
155
226
|
def prose_or_sentinel(prose)
|
|
156
227
|
text = prose.to_s.strip
|
|
157
228
|
text.empty? ? TODO_SENTINEL : text
|
|
158
229
|
end
|
|
159
230
|
|
|
231
|
+
# Finds the Argument rows declared for one Keyword row's word.
|
|
232
|
+
#
|
|
233
|
+
# @param row [Hash{Symbol => String}] a Keyword row
|
|
234
|
+
# @return [Array<Hash{Symbol => String}>] the Argument rows declared for
|
|
235
|
+
# this row's word, in this row's context
|
|
160
236
|
def word_arguments(row)
|
|
161
237
|
arguments.select { |arg| arg[:keyword] == row[:word] && arg[:context] == row[:context] }
|
|
162
238
|
end
|
|
163
239
|
|
|
240
|
+
# Builds the call spelling shown for one word.
|
|
241
|
+
#
|
|
242
|
+
# @param row [Hash{Symbol => String}] a Keyword row
|
|
243
|
+
# @return [String] the word's call spelling, positional arguments then
|
|
244
|
+
# named ones, with a trailing `do ... end` unless its body is `"none"`
|
|
164
245
|
def signature(row)
|
|
165
246
|
positional = word_arguments(row).reject { |arg| arg[:at].to_s.empty? }
|
|
166
247
|
.sort_by { |arg| arg[:at].to_i }
|
|
@@ -172,6 +253,11 @@ module Hecks
|
|
|
172
253
|
row[:body].to_s == "none" ? base : "#{base} do ... end"
|
|
173
254
|
end
|
|
174
255
|
|
|
256
|
+
# Renders one word's arguments as a Markdown table.
|
|
257
|
+
#
|
|
258
|
+
# @param row [Hash{Symbol => String}] a Keyword row
|
|
259
|
+
# @return [String] a Markdown table of the row's arguments, or `""` if it
|
|
260
|
+
# declares none
|
|
175
261
|
def argument_table(row)
|
|
176
262
|
args = word_arguments(row)
|
|
177
263
|
return "" if args.empty?
|
|
@@ -184,6 +270,10 @@ module Hecks
|
|
|
184
270
|
"#{lines.join("\n")}\n"
|
|
185
271
|
end
|
|
186
272
|
|
|
273
|
+
# Renders the reference index page.
|
|
274
|
+
#
|
|
275
|
+
# @return [String] the reference index page's rendered Markdown, one
|
|
276
|
+
# linked entry per context
|
|
187
277
|
def render_index
|
|
188
278
|
listed = contexts.map do |context|
|
|
189
279
|
count = keywords.select { |row| row[:context] == context }.map { |row| row[:word] }.uniq.size
|
|
@@ -204,7 +294,7 @@ module Hecks
|
|
|
204
294
|
# Prose keyed by word: everything between a section's generated
|
|
205
295
|
# region and the next `## ` heading (or end of file).
|
|
206
296
|
#
|
|
207
|
-
# Starts on PREAMBLE rather than nil so the text between the
|
|
297
|
+
# Starts on `PREAMBLE` rather than nil so the text between the page's
|
|
208
298
|
# own generated lede and its first word heading is carried over too
|
|
209
299
|
# instead of being silently dropped. A page written before that
|
|
210
300
|
# region existed has no generated marker ahead of its first `## `,
|
|
@@ -214,6 +304,11 @@ module Hecks
|
|
|
214
304
|
# buffer/in_fence) — each branch mutates shared local state that
|
|
215
305
|
# carries into the next iteration, so splitting per branch would
|
|
216
306
|
# mean passing all four back and forth by reference every line.
|
|
307
|
+
#
|
|
308
|
+
# @param text [String] a committed reference page's full Markdown source
|
|
309
|
+
# @return [Hash{String, Symbol => String}] hand-written prose, keyed by
|
|
310
|
+
# word (or `PREAMBLE` for the page's lede); empty and TODO-sentinel-only
|
|
311
|
+
# entries are dropped
|
|
217
312
|
# rubocop:disable-next Metrics/PerceivedComplexity
|
|
218
313
|
def harvest(text)
|
|
219
314
|
prose = {}
|
|
@@ -246,6 +341,13 @@ module Hecks
|
|
|
246
341
|
prose.reject { |_word, text_| text_.empty? || text_ == TODO_SENTINEL }
|
|
247
342
|
end
|
|
248
343
|
|
|
344
|
+
# Renders every reference page and writes each to `directory`.
|
|
345
|
+
#
|
|
346
|
+
# @param directory [String] path to the directory to write pages into,
|
|
347
|
+
# created if it does not exist
|
|
348
|
+
# @return [void]
|
|
349
|
+
# @raise [RuntimeError] if a committed page carries prose for a word the
|
|
350
|
+
# language no longer declares in that context
|
|
249
351
|
def write!(directory)
|
|
250
352
|
FileUtils.mkdir_p(directory)
|
|
251
353
|
pages(directory).each do |name, content|
|
|
@@ -257,6 +359,10 @@ module Hecks
|
|
|
257
359
|
# reference page, keyed by region id instead of a word, so the
|
|
258
360
|
# index a reader lands on first can't drift from what actually
|
|
259
361
|
# exists on disk either.
|
|
362
|
+
#
|
|
363
|
+
# @param root [String] the repository root
|
|
364
|
+
# @return [Hash{String => String}] each region id mapped to its freshly
|
|
365
|
+
# rendered content
|
|
260
366
|
def readme_regions(root)
|
|
261
367
|
{
|
|
262
368
|
"guides" => guide_index(root),
|
|
@@ -267,6 +373,11 @@ module Hecks
|
|
|
267
373
|
}
|
|
268
374
|
end
|
|
269
375
|
|
|
376
|
+
# Lists every committed guide, linked and titled by its own heading.
|
|
377
|
+
#
|
|
378
|
+
# @param root [String] the repository root
|
|
379
|
+
# @return [String] a Markdown list linking every committed guide, titled
|
|
380
|
+
# by its own `# ` heading
|
|
270
381
|
def guide_index(root)
|
|
271
382
|
paths = Dir.glob(File.join(root, "docs/implemented/guides/*.md"))
|
|
272
383
|
.reject { |p| %w[AUTHORING.md].include?(File.basename(p)) }
|
|
@@ -278,6 +389,11 @@ module Hecks
|
|
|
278
389
|
lines.join("\n")
|
|
279
390
|
end
|
|
280
391
|
|
|
392
|
+
# Links the reference index, with its context count.
|
|
393
|
+
#
|
|
394
|
+
# @param _root [String] unused; kept for the shape `readme_regions` calls
|
|
395
|
+
# every region renderer with
|
|
396
|
+
# @return [String] one line linking the reference index, with its context count
|
|
281
397
|
def reference_index(_root)
|
|
282
398
|
count = contexts.size
|
|
283
399
|
"[The DSL reference](docs/implemented/reference/index.md) — #{count} contexts, generated from " \
|
|
@@ -285,6 +401,11 @@ module Hecks
|
|
|
285
401
|
"`spec/reference_golden_spec.rb`."
|
|
286
402
|
end
|
|
287
403
|
|
|
404
|
+
# Lists every `bin/` script that opens with a comment, one row each.
|
|
405
|
+
#
|
|
406
|
+
# @param root [String] the repository root
|
|
407
|
+
# @return [String] a Markdown table of every `bin/` script with an opening
|
|
408
|
+
# comment, one row each
|
|
288
409
|
def tool_table(root)
|
|
289
410
|
scripts = Dir.glob(File.join(root, "bin/*")).select { |p| File.file?(p) }.sort
|
|
290
411
|
rows = scripts.filter_map { |path| [path, tool_summary(path)] }.select { |_, desc| desc }
|
|
@@ -298,6 +419,10 @@ module Hecks
|
|
|
298
419
|
# little long and says "...". A code-bearing comment (`field.name`,
|
|
299
420
|
# `pattern:`) makes naive sentence-splitting on "." or ":" cut in
|
|
300
421
|
# the wrong place, so this truncates on length alone.
|
|
422
|
+
#
|
|
423
|
+
# @param path [String] a `bin/` script's path
|
|
424
|
+
# @return [String, nil] its opening comment paragraph, truncated to 140
|
|
425
|
+
# characters, or nil if the script opens with no comment
|
|
301
426
|
def tool_summary(path)
|
|
302
427
|
comment_lines = []
|
|
303
428
|
started = false
|
|
@@ -324,6 +449,10 @@ module Hecks
|
|
|
324
449
|
# own drift check; this just quotes its own output, so the two
|
|
325
450
|
# can't independently drift from each other either — a stale
|
|
326
451
|
# Order_lifecycle.mmd fails that spec long before this one runs.
|
|
452
|
+
#
|
|
453
|
+
# @param root [String] the repository root
|
|
454
|
+
# @return [String] a Markdown section showcasing the generated diagram
|
|
455
|
+
# tooling, quoting `examples/pizzas`'s own committed Order lifecycle diagram
|
|
327
456
|
def diagram_showcase(root)
|
|
328
457
|
lifecycle = File.read(File.join(root, "docs/generated/diagrams/pizzas/Order_lifecycle.mmd")).strip
|
|
329
458
|
<<~MARKDOWN.strip
|
|
@@ -337,6 +466,11 @@ module Hecks
|
|
|
337
466
|
MARKDOWN
|
|
338
467
|
end
|
|
339
468
|
|
|
469
|
+
# Lists every example domain, with its own declared vision.
|
|
470
|
+
#
|
|
471
|
+
# @param root [String] the repository root
|
|
472
|
+
# @return [String] a Markdown list of every example domain with a
|
|
473
|
+
# `.bluebook` file, each with its own declared `vision` text
|
|
340
474
|
def corpus_roster(root)
|
|
341
475
|
dirs = Dir.glob(File.join(root, "examples/*/"))
|
|
342
476
|
lines = dirs.filter_map do |dir|
|
|
@@ -351,6 +485,12 @@ module Hecks
|
|
|
351
485
|
lines.join("\n")
|
|
352
486
|
end
|
|
353
487
|
|
|
488
|
+
# Replaces every generated region inside `text` with its freshly rendered
|
|
489
|
+
# content, leaving the hand-written parts of README untouched.
|
|
490
|
+
#
|
|
491
|
+
# @param root [String] the repository root
|
|
492
|
+
# @param text [String] the README's current full text
|
|
493
|
+
# @return [String] the README's text, with each generated region refreshed
|
|
354
494
|
def render_readme(root, text)
|
|
355
495
|
readme_regions(root).reduce(text) do |current, (id, content)|
|
|
356
496
|
pattern = /#{Regexp.escape(region_begin(id))}.*?#{Regexp.escape(GENERATED_END)}/m
|
|
@@ -358,6 +498,10 @@ module Hecks
|
|
|
358
498
|
end
|
|
359
499
|
end
|
|
360
500
|
|
|
501
|
+
# Regenerates README's generated regions in place.
|
|
502
|
+
#
|
|
503
|
+
# @param root [String] the repository root
|
|
504
|
+
# @return [void]
|
|
361
505
|
def write_readme!(root)
|
|
362
506
|
path = File.join(root, "README.md")
|
|
363
507
|
File.write(path, render_readme(root, File.read(path)))
|
|
@@ -371,9 +515,14 @@ module Hecks
|
|
|
371
515
|
# gate exists to refuse.
|
|
372
516
|
EXAMPLE_FENCE = /^```ruby(?: bluebook| boot)?[ \t]*$/
|
|
373
517
|
|
|
518
|
+
# Whether a word's prose carries a runnable example.
|
|
519
|
+
#
|
|
520
|
+
# @param prose [String, nil] a word's hand-written prose, or nil
|
|
521
|
+
# @return [Boolean] true if `prose` contains a runnable `ruby` or
|
|
522
|
+
# `ruby bluebook`/`ruby boot` fenced example
|
|
374
523
|
def exemplified?(prose) = prose.to_s.match?(EXAMPLE_FENCE)
|
|
375
524
|
|
|
376
|
-
# Every live
|
|
525
|
+
# Every live word, paired with its prose. Both coverage gates ask a
|
|
377
526
|
# question about this same walk and differ only in what they ask of
|
|
378
527
|
# the prose, so they share it rather than each re-deriving the page
|
|
379
528
|
# set — the two are meant to move together, and one drifting past
|
|
@@ -382,6 +531,12 @@ module Hecks
|
|
|
382
531
|
#
|
|
383
532
|
# `harvest` already rejects empty prose and the TODO sentinel, so a
|
|
384
533
|
# word with nothing written for it arrives here with a nil.
|
|
534
|
+
#
|
|
535
|
+
# @param directory [String] path to the directory holding the committed
|
|
536
|
+
# reference pages
|
|
537
|
+
# @return [Array(String, String, String), Array(String, String, nil)]
|
|
538
|
+
# `[word, context, prose]` for every live (admitted or deprecated) word,
|
|
539
|
+
# `prose` nil if none was harvested
|
|
385
540
|
def live_words(directory)
|
|
386
541
|
rows = contexts.flat_map do |context|
|
|
387
542
|
path = File.join(directory, page_name(context))
|
|
@@ -392,9 +547,18 @@ module Hecks
|
|
|
392
547
|
rows.uniq { |word, context, _| [word, context] }
|
|
393
548
|
end
|
|
394
549
|
|
|
550
|
+
# Disambiguates a word by the context it is declared in.
|
|
551
|
+
#
|
|
552
|
+
# @param word [String] a word
|
|
553
|
+
# @param context [String] the context it is declared in
|
|
554
|
+
# @return [String] the word, disambiguated by its context
|
|
395
555
|
def name_of(word, context) = "#{word} (#{context})"
|
|
396
556
|
|
|
397
557
|
# The coverage gate's question: every live word with no prose yet.
|
|
558
|
+
#
|
|
559
|
+
# @param directory [String] path to the directory holding the committed
|
|
560
|
+
# reference pages
|
|
561
|
+
# @return [Array<String>] each undocumented live word, named by `name_of`
|
|
398
562
|
def undocumented(directory)
|
|
399
563
|
live_words(directory).reject { |_word, _context, prose| prose }
|
|
400
564
|
.map { |word, context, _| name_of(word, context) }
|
|
@@ -407,6 +571,11 @@ module Hecks
|
|
|
407
571
|
# shipped twice (`read_model`'s where/order_by/limit/offset, and
|
|
408
572
|
# `role`/`goal` on a command). An example that runs is the only
|
|
409
573
|
# documentation that can go red.
|
|
574
|
+
#
|
|
575
|
+
# @param directory [String] path to the directory holding the committed
|
|
576
|
+
# reference pages
|
|
577
|
+
# @return [Array<String>] each live word with prose but no runnable
|
|
578
|
+
# example, named by `name_of`
|
|
410
579
|
def unexemplified(directory)
|
|
411
580
|
live_words(directory).reject { |_word, _context, prose| exemplified?(prose) }
|
|
412
581
|
.map { |word, context, _| name_of(word, context) }
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
module Hecks
|
|
2
|
-
#
|
|
3
|
-
# for members that don't ship inside hecks's own lib/ at all: a
|
|
4
|
-
# separate, independently-versioned package
|
|
5
|
-
# (github.com/chrisyoung/embryonaut_bluebooks) that a consuming project
|
|
6
|
-
# vendors into its own checkout, the same way a project already vendors
|
|
7
|
-
# hecks itself (bin/vendor_hecks, vendor/hecks/).
|
|
2
|
+
# A vendored, external bluebook.
|
|
8
3
|
#
|
|
9
|
-
#
|
|
4
|
+
# ## What it is
|
|
5
|
+
#
|
|
6
|
+
# Same shape as Framework (framework.rb), for members that don't ship
|
|
7
|
+
# inside hecks's own lib/ at all: a separate, independently-versioned
|
|
8
|
+
# package (github.com/chrisyoung/embryonaut_bluebooks) that a consuming
|
|
9
|
+
# project vendors into its own checkout, the same way a project already
|
|
10
|
+
# vendors hecks itself (bin/vendor_hecks, vendor/hecks/).
|
|
11
|
+
#
|
|
12
|
+
# ## Recovered, not rebuilt
|
|
13
|
+
#
|
|
14
|
+
# This module and its `uses_embryonaut_bluebook`
|
|
10
15
|
# DSL word (hecksagon_builder.rb) were built on a prior commit of this
|
|
11
16
|
# repo (933d1dd), vendored out to a real consumer (lifeadelics/domain,
|
|
12
17
|
# for embryonaut_bluebooks/payments), and then lost from this repo's own
|
|
@@ -17,6 +22,8 @@ module Hecks
|
|
|
17
22
|
# conventions rather than copied wholesale, since the two trees had
|
|
18
23
|
# otherwise diverged for weeks in both directions.
|
|
19
24
|
#
|
|
25
|
+
# ## Resolution
|
|
26
|
+
#
|
|
20
27
|
# Resolved from the consuming registry's own root, not this gem's
|
|
21
28
|
# __dir__ — Framework::ROOT can be a fixed, `__dir__`-relative constant
|
|
22
29
|
# because framework members ship inside this gem; an embryonaut bluebook
|
|
@@ -27,6 +34,8 @@ module Hecks
|
|
|
27
34
|
# itself only runs at hecksagon-build time, when a real registry is
|
|
28
35
|
# current.
|
|
29
36
|
#
|
|
37
|
+
# ## Load order
|
|
38
|
+
#
|
|
30
39
|
# Every `.bluebook` file in the package, sorted — not just one. Unlike a
|
|
31
40
|
# framework member (one file, named by its own stem), a vendored package
|
|
32
41
|
# can span several bluebook files that reopen the same `Hecks.bluebook`
|
|
@@ -37,7 +46,9 @@ module Hecks
|
|
|
37
46
|
# payment < payments < policies, the same reason that package's own
|
|
38
47
|
# files are named to fall in that order in the first place.
|
|
39
48
|
#
|
|
40
|
-
#
|
|
49
|
+
# ## Scope
|
|
50
|
+
#
|
|
51
|
+
# Only the bluebook files — same restriction Framework draws, same
|
|
41
52
|
# reason: a `.hecksagon`/`.port`/`.adapter` is a wiring decision
|
|
42
53
|
# (persistence, which processor adapter is bound) that belongs to
|
|
43
54
|
# whoever is deploying, never baked into the vendored package itself.
|
|
@@ -46,12 +57,24 @@ module Hecks
|
|
|
46
57
|
# `Hecks.hecksagon "Payments" do ... end` to bind real storage/adapters
|
|
47
58
|
# — see Framework's own comment for the fuller reasoning, identical here.
|
|
48
59
|
#
|
|
49
|
-
#
|
|
60
|
+
# ## Idempotency
|
|
61
|
+
#
|
|
62
|
+
# Idempotent the same way `Framework.load!` is — checked against the
|
|
50
63
|
# bluebook this package actually declares (`Naming.pascal("payments")`
|
|
51
64
|
# => "Payments"), not a separate ledger. A vendored package's directory
|
|
52
65
|
# name and its declared `Hecks.bluebook` name are the one convention
|
|
53
66
|
# this reuses from Framework rather than reinventing.
|
|
54
67
|
module EmbryonautBluebook
|
|
68
|
+
# Loads a vendored embryonaut bluebook package's `.bluebook` files, once
|
|
69
|
+
# per registry.
|
|
70
|
+
#
|
|
71
|
+
# @param name [String, Symbol] the vendored package's directory name, such
|
|
72
|
+
# as `"payments"`
|
|
73
|
+
# @param registry [Runtime::Registry, nil] the registry to vendor into and
|
|
74
|
+
# check for an existing load; defaults to the current boot registry
|
|
75
|
+
# @return [void]
|
|
76
|
+
# @raise [Runtime::WiringError] if `registry` has no root, or no vendored
|
|
77
|
+
# package named `name` is checked out
|
|
55
78
|
def self.load!(name, registry: Hecks.current_registry)
|
|
56
79
|
unless registry&.root
|
|
57
80
|
raise Runtime::WiringError,
|