hecks 1.0.2 → 1.1.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 +3 -3
- data/lib/hecks/adapters/driven/d1.rb +48 -28
- data/lib/hecks/adapters/driven/folder.rb +10 -3
- data/lib/hecks/adapters/driven/google_authentication.rb +2 -4
- data/lib/hecks/adapters/driven/heki/journal.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +3 -5
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +4 -1
- data/lib/hecks/adapters/driven/lambda.rb +2 -2
- data/lib/hecks/adapters/driven/local_storage.adapter +3 -0
- data/lib/hecks/adapters/driven/local_storage.rb +120 -0
- data/lib/hecks/adapters/driven/memory.rb +48 -3
- data/lib/hecks/adapters/driven/postgres/codec.rb +3 -1
- data/lib/hecks/adapters/driven/postgres/outbox.rb +97 -0
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +3 -2
- data/lib/hecks/adapters/driven/postgres.rb +25 -14
- data/lib/hecks/adapters/driven/prism.rb +11 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +45 -20
- data/lib/hecks/adapters/driven/sqlite/codec.rb +3 -1
- data/lib/hecks/adapters/driven/sqlite/projection.rb +17 -1
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +23 -1
- data/lib/hecks/adapters/driven/sqlite.rb +87 -8
- data/lib/hecks/adapters/driven.rb +4 -0
- data/lib/hecks/adapters.rb +4 -0
- data/lib/hecks/behaviors/dsl.rb +7 -0
- data/lib/hecks/behaviors/expectations.rb +32 -4
- data/lib/hecks/behaviors/rspec.rb +2 -0
- data/lib/hecks/behaviors/runner.rb +4 -0
- data/lib/hecks/bluebook/aggregate.rb +6 -5
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +4 -4
- data/lib/hecks/bluebook/assembly/build.rb +6 -3
- data/lib/hecks/bluebook/assembly/contract.rb +5 -1
- data/lib/hecks/bluebook/assembly/contracts.rb +4 -0
- data/lib/hecks/bluebook/assembly/marks.rb +2 -1
- data/lib/hecks/bluebook/assembly.rb +1 -3
- data/lib/hecks/bluebook/attribute.rb +6 -0
- data/lib/hecks/bluebook/behaviour/policy.rb +18 -0
- data/lib/hecks/bluebook/behaviour/process_manager.rb +17 -1
- data/lib/hecks/bluebook/behaviour/traits.rb +1 -1
- data/lib/hecks/bluebook/command.rb +6 -5
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +4 -1
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +524 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +19 -472
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +14 -6
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +5 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +848 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +25 -784
- data/lib/hecks/bluebook/dsl/command_builder.rb +53 -13
- data/lib/hecks/bluebook/dsl/const_shim.rb +6 -0
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +20 -7
- data/lib/hecks/bluebook/dsl/entity_builder.rb +27 -6
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +12 -2
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +8 -1
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +24 -12
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +40 -1
- data/lib/hecks/bluebook/dsl/policy_builder.rb +10 -1
- data/lib/hecks/bluebook/dsl/port_builder.rb +6 -1
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +33 -12
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +56 -12
- data/lib/hecks/bluebook/dsl/query_builder.rb +7 -1
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +11 -3
- data/lib/hecks/bluebook/dsl/rule_reference.rb +3 -1
- data/lib/hecks/bluebook/dsl/translation_builder.rb +13 -2
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +8 -1
- data/lib/hecks/bluebook/dsl/word_gate.rb +5 -0
- data/lib/hecks/bluebook/dsl/world_builder.rb +13 -3
- data/lib/hecks/bluebook/entity.rb +5 -3
- data/lib/hecks/bluebook/expression/ast_json.rb +88 -9
- data/lib/hecks/bluebook/expression/ast_reader.rb +107 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +7 -0
- data/lib/hecks/bluebook/expression/evaluator.rb +64 -11
- data/lib/hecks/bluebook/expression/projection.json +12 -0
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +4 -3
- data/lib/hecks/bluebook/expression/resolver.rb +83 -11
- data/lib/hecks/bluebook/expression.rb +1 -0
- data/lib/hecks/bluebook/hexagon.rb +10 -0
- data/lib/hecks/bluebook/lifecycle.rb +9 -0
- data/lib/hecks/bluebook/meta_validator/judge.rb +10 -4
- data/lib/hecks/bluebook/meta_validator/plan.rb +4 -0
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/shapes.rb +3 -3
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +7 -4
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +42 -3
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +1 -1
- data/lib/hecks/bluebook/meta_validator.rb +5 -5
- data/lib/hecks/bluebook/model_check.rb +119 -82
- data/lib/hecks/bluebook/pattern_subset.rb +9 -0
- data/lib/hecks/bluebook/policy.rb +2 -1
- data/lib/hecks/bluebook/process_manager.rb +6 -0
- data/lib/hecks/bluebook/project_register.rb +45 -25
- data/lib/hecks/bluebook/query.rb +4 -0
- data/lib/hecks/bluebook/read_model.rb +2 -2
- data/lib/hecks/bluebook/smoke_test.rb +5 -0
- data/lib/hecks/bluebook/synthesizer.rb +2 -1
- data/lib/hecks/bluebook/translation.rb +3 -0
- data/lib/hecks/bluebook/value_object.rb +2 -2
- data/lib/hecks/codemod.rb +13 -0
- data/lib/hecks/doc/reference.rb +17 -11
- data/lib/hecks/facade/cli_runner.rb +2 -2
- data/lib/hecks/facade/command_request.rb +11 -11
- data/lib/hecks/facade/handle.rb +3 -3
- data/lib/hecks/facade/surface/aggregate_door.rb +52 -38
- data/lib/hecks/facade.rb +4 -0
- data/lib/hecks/forms/app.rb +10 -6
- data/lib/hecks/forms/command_form_renderer.rb +3 -3
- data/lib/hecks/forms/field_renderer.rb +14 -9
- data/lib/hecks/forms/field_shape.rb +7 -2
- data/lib/hecks/forms/html.rb +5 -2
- data/lib/hecks/forms/index_renderer.rb +1 -1
- data/lib/hecks/forms/page.rb +7 -3
- data/lib/hecks/forms/query_form_renderer.rb +3 -2
- data/lib/hecks/forms/record_renderer.rb +22 -16
- data/lib/hecks/forms.rb +4 -0
- data/lib/hecks/fqn.rb +21 -9
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +30 -8
- data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -1
- data/lib/hecks/fuzzing/isolated_boot.rb +14 -6
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +264 -0
- data/lib/hecks/fuzzing/properties/guards.rb +251 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +351 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +99 -0
- data/lib/hecks/fuzzing/properties/querying.rb +206 -0
- data/lib/hecks/fuzzing/properties.rb +41 -1065
- data/lib/hecks/fuzzing/replay.rb +60 -16
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +6 -2
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +2 -1
- data/lib/hecks/fuzzing/sequence_generator.rb +0 -1
- data/lib/hecks/fuzzing/value_generator.rb +2 -2
- data/lib/hecks/grammar/evolve.rb +34 -10
- data/lib/hecks/grammar/expression_operators.json +124 -0
- data/lib/hecks/grammar.rb +15 -5
- data/lib/hecks/ir.rb +9 -0
- data/lib/hecks/language/bluebook/aggregate.bluebook +4 -2
- data/lib/hecks/language/bluebook/command.bluebook +2 -1
- data/lib/hecks/language/bluebook/entity.bluebook +2 -1
- data/lib/hecks/language/bluebook/projection.bluebook +2 -1
- data/lib/hecks/language/bluebook/query.bluebook +4 -2
- data/lib/hecks/language/bluebook/shape.bluebook +2 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -0
- data/lib/hecks/literal.rb +18 -8
- data/lib/hecks/naming.rb +6 -3
- data/lib/hecks/ports/access_control.rb +1 -1
- data/lib/hecks/ports/agent.rb +5 -3
- data/lib/hecks/ports/authentication.rb +3 -2
- data/lib/hecks/ports/authorization.rb +1 -1
- data/lib/hecks/ports/clock.rb +1 -1
- data/lib/hecks/ports/extraction.rb +5 -1
- data/lib/hecks/ports/identity_assignment.rb +1 -1
- data/lib/hecks/ports/identity_generation.rb +1 -1
- data/lib/hecks/ports/identity_resolution.rb +1 -1
- data/lib/hecks/ports/loading.rb +4 -1
- data/lib/hecks/ports/persistence/append_only.rb +40 -4
- data/lib/hecks/ports/persistence/binding_policy.rb +1 -1
- data/lib/hecks/ports/persistence/plugin.rb +3 -0
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -1
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +5 -5
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +14 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +4 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +80 -16
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +23 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +43 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +25 -4
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +49 -9
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +10 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +49 -38
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +23 -13
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +19 -21
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +27 -9
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +9 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +25 -10
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +2 -6
- data/lib/hecks/ports/persistence/plugins/era.rb +3 -1
- data/lib/hecks/ports/persistence/remote_runtime.rb +1 -1
- data/lib/hecks/ports/persistence.rb +7 -3
- data/lib/hecks/ports/projection.rb +13 -5
- data/lib/hecks/ports/query/in_memory.rb +8 -2
- data/lib/hecks/ports/query.rb +1 -3
- data/lib/hecks/ports.rb +2 -0
- data/lib/hecks/projections/diagrams.rb +11 -3
- data/lib/hecks/projections/ir.rb +2 -0
- data/lib/hecks/projections/model/deviations.rb +2 -1
- data/lib/hecks/projections/model.rb +15 -10
- data/lib/hecks/projections/vocabulary.rb +1 -3
- data/lib/hecks/projector/cli_projector.rb +49 -29
- data/lib/hecks/projector/docs_projector.rb +21 -12
- data/lib/hecks/projector/exporter.rb +10 -1
- data/lib/hecks/projector/narrate_projector.rb +83 -25
- data/lib/hecks/projector.rb +2 -2
- data/lib/hecks/query_ir.rb +60 -36
- data/lib/hecks/query_specification/common/authorization_spec.rb +1 -1
- data/lib/hecks/query_specification/common/comparators.rb +4 -0
- data/lib/hecks/query_specification/common/comparison.rb +8 -1
- data/lib/hecks/query_specification/common/dsl.rb +5 -0
- data/lib/hecks/query_specification/common/null_policy.rb +6 -2
- data/lib/hecks/query_specification/common/options.rb +17 -5
- data/lib/hecks/query_specification/field_path.rb +4 -4
- data/lib/hecks/query_specification/read_model/specification.rb +5 -2
- data/lib/hecks/router/namespace_installer.rb +22 -10
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +57 -22
- data/lib/hecks/runtime/command_interpreter.rb +104 -65
- data/lib/hecks/runtime/command_rules/admissibility.rb +26 -8
- data/lib/hecks/runtime/command_rules/arithmetic.rb +35 -14
- data/lib/hecks/runtime/dependency_planning.rb +33 -21
- data/lib/hecks/runtime/dispatcher.rb +41 -7
- data/lib/hecks/runtime/entity_element.rb +25 -6
- data/lib/hecks/runtime/entity_interpreter.rb +28 -15
- data/lib/hecks/runtime/errors.rb +28 -2
- data/lib/hecks/runtime/instance.rb +9 -3
- data/lib/hecks/runtime/interpreting.rb +29 -2
- data/lib/hecks/runtime/loader.rb +21 -0
- data/lib/hecks/runtime/outbox.rb +365 -0
- data/lib/hecks/runtime/policy_interpreter.rb +32 -17
- data/lib/hecks/runtime/query_interpreter.rb +36 -16
- data/lib/hecks/runtime/reaction_invocation.rb +35 -22
- data/lib/hecks/runtime/read_model_interpreter.rb +26 -3
- data/lib/hecks/runtime/reference_hop.rb +1 -1
- data/lib/hecks/runtime/refusal_wording.rb +55 -49
- data/lib/hecks/runtime/registry/saga_persistence.rb +3 -1
- data/lib/hecks/runtime/registry/verification.rb +39 -2
- data/lib/hecks/runtime/registry.rb +21 -11
- data/lib/hecks/runtime/remote_dispatcher.rb +3 -1
- data/lib/hecks/runtime/routing.rb +52 -39
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +7 -7
- data/lib/hecks/runtime/saga_interpreter.rb +119 -90
- data/lib/hecks/runtime/saga_pending_dispatch.rb +6 -3
- data/lib/hecks/runtime/tenant_scope.rb +7 -0
- data/lib/hecks/runtime/value/coercion.rb +207 -48
- data/lib/hecks/runtime/value.rb +1 -1
- data/lib/hecks/runtime.rb +7 -2
- data/lib/hecks/storehouse.rb +15 -8
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +1 -0
- data/lib/hecks.rb +17 -13
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +1 -1
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +1 -1
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +1 -1
- metadata +14 -2
data/lib/hecks/fuzzing/replay.rb
CHANGED
|
@@ -57,6 +57,20 @@ module Hecks
|
|
|
57
57
|
# DISPATCH_ORDER.
|
|
58
58
|
GUARD_REFUSAL_CLASSES = [Runtime::GivenNotMet, Runtime::LifecycleRefused].freeze
|
|
59
59
|
|
|
60
|
+
# One tightly ordered loop over steps, with several "oracle" snapshots
|
|
61
|
+
# (reaction_mark, fan_out_snapshot, guard_check, mutation_trace) that
|
|
62
|
+
# must be taken at very specific points RELATIVE TO dispatch — see
|
|
63
|
+
# fan_out_snapshot's own comment above for the real, previously-
|
|
64
|
+
# shipped bug this exact before/after ordering fixes. Splitting this
|
|
65
|
+
# into smaller methods would mean threading five-plus oracle-state
|
|
66
|
+
# locals across method boundaries as parameters/return values, and
|
|
67
|
+
# would let a future editor silently reorder a snapshot relative to
|
|
68
|
+
# `dispatch` without any single method looking wrong — the ordering
|
|
69
|
+
# invariant is only visible with the whole sequence in one place.
|
|
70
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
71
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
72
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
73
|
+
# rubocop:disable-next Metrics/PerceivedComplexity
|
|
60
74
|
def call(domain_path, steps, adapter: :memory)
|
|
61
75
|
# See isolated_boot.rb's own header: resets data/ AND rebinds
|
|
62
76
|
# persistence to the chosen adapter (Memory by default), since a
|
|
@@ -106,8 +120,8 @@ module Hecks
|
|
|
106
120
|
begin
|
|
107
121
|
rows = run_filter(runtime, question)
|
|
108
122
|
queries << { query: question, rows: rows, instances_at: snapshot_instances(runtime) }
|
|
109
|
-
rescue => e
|
|
110
|
-
refusals << { verb: filter_label(question), error: e.message }
|
|
123
|
+
rescue StandardError => e
|
|
124
|
+
refusals << { verb: filter_label(question), error: e.message, kind: refusal_kind(e) }
|
|
111
125
|
end
|
|
112
126
|
next
|
|
113
127
|
end
|
|
@@ -160,7 +174,7 @@ module Hecks
|
|
|
160
174
|
end
|
|
161
175
|
queries << entry
|
|
162
176
|
|
|
163
|
-
refusals << { verb: question, error: native_error.message, kind: native_error
|
|
177
|
+
refusals << { verb: question, error: native_error.message, kind: refusal_kind(native_error) } if native_error
|
|
164
178
|
next
|
|
165
179
|
end
|
|
166
180
|
|
|
@@ -256,9 +270,7 @@ module Hecks
|
|
|
256
270
|
# so there is no "after" to compare (and #build_mutation_
|
|
257
271
|
# trace already skipped anything with no mutations to
|
|
258
272
|
# trace in the first place).
|
|
259
|
-
if mutation_trace
|
|
260
|
-
mutation_traces << mutation_trace.merge(after: read_mutation_after(runtime, mutation_trace))
|
|
261
|
-
end
|
|
273
|
+
mutation_traces << mutation_trace.merge(after: read_mutation_after(runtime, mutation_trace)) if mutation_trace
|
|
262
274
|
rescue *Runtime::DOMAIN_REFUSALS, Bluebook::Expression::EvaluationError => e
|
|
263
275
|
# `kind:` — the RAISED CLASS, not re-derived from the message.
|
|
264
276
|
# `GivenNotMet`/`EnsuresNotMet` share their exact wording
|
|
@@ -269,7 +281,7 @@ module Hecks
|
|
|
269
281
|
# would misread every one of those as a guard, or a guard as
|
|
270
282
|
# one of those. The class is unambiguous where the string
|
|
271
283
|
# is not.
|
|
272
|
-
refusals << { verb: step["verb"], error: e.message, kind: e
|
|
284
|
+
refusals << { verb: step["verb"], error: e.message, kind: refusal_kind(e) }
|
|
273
285
|
# ONLY a refusal raised BY THE GUARD ITSELF counts here —
|
|
274
286
|
# measured, not assumed: a step whose args were simply
|
|
275
287
|
# incomplete (AbsentArgument, from normalize_args — which
|
|
@@ -287,16 +299,17 @@ module Hecks
|
|
|
287
299
|
# two guard classes is left OUT of guard_checks entirely —
|
|
288
300
|
# inconclusive, not a claimed pass.
|
|
289
301
|
if guard_check && GUARD_REFUSAL_CLASSES.include?(e.class)
|
|
290
|
-
guard_checks << guard_check.merge(actual_refused: true,
|
|
302
|
+
guard_checks << guard_check.merge(actual_refused: true,
|
|
303
|
+
actual_kind: e.class.name)
|
|
291
304
|
end
|
|
292
305
|
end
|
|
293
306
|
end
|
|
294
307
|
|
|
295
308
|
instances = snapshot_instances(runtime)
|
|
296
309
|
|
|
297
|
-
events = runtime.events.map
|
|
310
|
+
events = runtime.events.map do |event|
|
|
298
311
|
{ name: event.name, aggregate: event.aggregate, id: event.id, payload: event.payload }
|
|
299
|
-
|
|
312
|
+
end
|
|
300
313
|
|
|
301
314
|
# THE LIVE PROCESS-MANAGER STORE, materialised to inert data —
|
|
302
315
|
# `{ pm_name => { correlation => { state:, memory: } } }`, the
|
|
@@ -397,6 +410,16 @@ module Hecks
|
|
|
397
410
|
instances
|
|
398
411
|
end
|
|
399
412
|
|
|
413
|
+
# One early-return chain resolving "which record, if any" (see the
|
|
414
|
+
# long comment above), feeding a two-stage guard replay
|
|
415
|
+
# (enforce_givens then admissible_transition) that must run in that
|
|
416
|
+
# order to mirror the real DISPATCH_ORDER. Splitting the resolution
|
|
417
|
+
# out would still require passing domain_name/aggregate_name/
|
|
418
|
+
# aggregate/command/record back in, for no gain — every early
|
|
419
|
+
# return already reads as "nothing to check" at its own site.
|
|
420
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
421
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
422
|
+
# rubocop:disable-next Metrics/PerceivedComplexity
|
|
400
423
|
def build_guard_check(runtime, verb, args)
|
|
401
424
|
domain_name, aggregate_name, command_name = Naming.split_verb(verb)
|
|
402
425
|
return nil unless command_name && !command_name.include?(".")
|
|
@@ -412,7 +435,7 @@ module Hecks
|
|
|
412
435
|
# counts as something to check now that `admissible_transition`
|
|
413
436
|
# is reproduced below; skipping it here would just move the
|
|
414
437
|
# exact gap that call was added to close one line earlier.
|
|
415
|
-
has_transition = aggregate.lifecycle
|
|
438
|
+
has_transition = aggregate.lifecycle&.transitions_for(command.hecks_name)&.any?
|
|
416
439
|
return nil if command.givens.empty? && !command.from && !has_transition
|
|
417
440
|
|
|
418
441
|
reference_key = command.references.to_s.empty? ? nil : Naming.reference_key(command.references)
|
|
@@ -477,9 +500,17 @@ module Hecks
|
|
|
477
500
|
# `nil` for anything out of scope: an aggregate-level command, an
|
|
478
501
|
# entity command with no mutations at all, or one whose identity
|
|
479
502
|
# args (parent OR element) don't resolve.
|
|
503
|
+
# Same shape as build_guard_check just above: one early-return chain
|
|
504
|
+
# resolving the entity/element this step's args address (see the
|
|
505
|
+
# comment above), each step depending on the previous one's
|
|
506
|
+
# resolved value. Threading those locals out to smaller methods
|
|
507
|
+
# would cost more than the cop gains.
|
|
508
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
509
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
510
|
+
# rubocop:disable-next Metrics/PerceivedComplexity
|
|
480
511
|
def build_mutation_trace(runtime, verb, args)
|
|
481
512
|
domain_name, aggregate_name, command_name = Naming.split_verb(verb)
|
|
482
|
-
return nil unless command_name
|
|
513
|
+
return nil unless command_name&.include?(".")
|
|
483
514
|
|
|
484
515
|
aggregate = runtime.registry.bluebook(domain_name)&.aggregate(aggregate_name)
|
|
485
516
|
return nil unless aggregate
|
|
@@ -487,7 +518,7 @@ module Hecks
|
|
|
487
518
|
entity_name, entity_command_name = command_name.split(".", 2)
|
|
488
519
|
entity = aggregate.entities.find { |candidate| candidate.hecks_name == entity_name }
|
|
489
520
|
command = entity&.command(entity_command_name)
|
|
490
|
-
return nil unless command
|
|
521
|
+
return nil unless command&.mutations&.any?
|
|
491
522
|
|
|
492
523
|
reference_key = command.references.to_s.empty? ? nil : Naming.reference_key(command.references)
|
|
493
524
|
parent_id = Runtime::Identity.of(aggregate, args) ||
|
|
@@ -634,17 +665,30 @@ module Hecks
|
|
|
634
665
|
# Sorted by id ascending regardless — `Ports::Query::Ordering`'s own
|
|
635
666
|
# header explains why an ask with no declared order still needs
|
|
636
667
|
# this tier ("the identity tier is what makes an ask total").
|
|
668
|
+
# THE OUTCOME CLASS a recorded refusal row names (C8.2/C8.3,
|
|
669
|
+
# docs/semantics/bluebook-semantics.md): a domain refusal is its own
|
|
670
|
+
# class; an evaluation FAULT — the language refusing to interpret a
|
|
671
|
+
# broken rule or input — is `"Fault"`, the same word the Rust kernel
|
|
672
|
+
# emits (`Refusal::Fault`), never a refusal class and never a raw
|
|
673
|
+
# Ruby exception name.
|
|
674
|
+
def refusal_kind(error)
|
|
675
|
+
error.is_a?(Bluebook::Expression::EvaluationError) ? "Fault" : error.class.name
|
|
676
|
+
end
|
|
677
|
+
|
|
637
678
|
def run_filter(runtime, filter)
|
|
638
679
|
aggregate_ref = filter["aggregate"].to_s
|
|
639
680
|
field = filter["field"].to_s
|
|
640
681
|
op = filter["op"].to_s
|
|
641
682
|
value = filter["value"]
|
|
642
683
|
|
|
643
|
-
|
|
684
|
+
# A malformed ad-hoc ask is a FAULT (C8.3), not a bare RuntimeError.
|
|
685
|
+
unless FILTER_COMPARATORS.include?(op)
|
|
686
|
+
raise Bluebook::Expression::EvaluationError, "unknown query comparator #{op.inspect}"
|
|
687
|
+
end
|
|
644
688
|
|
|
645
689
|
domain_name, aggregate_name = aggregate_ref.split("::", 2)
|
|
646
690
|
aggregate = runtime.registry.bluebook(domain_name)&.aggregate(aggregate_name)
|
|
647
|
-
raise "unknown aggregate #{aggregate_ref.inspect}" unless aggregate
|
|
691
|
+
raise Bluebook::Expression::EvaluationError, "unknown aggregate #{aggregate_ref.inspect}" unless aggregate
|
|
648
692
|
|
|
649
693
|
clause = QuerySpecification::Common::WhereClause.new(field: field, op: op, value: value)
|
|
650
694
|
records = runtime.registry.repository(domain_name, aggregate).all
|
|
@@ -662,7 +706,7 @@ module Hecks
|
|
|
662
706
|
# own `filter_label` builds from the same three raw fields, tolerant
|
|
663
707
|
# of any of them being missing (Ruby's own nil-to-"" interpolation)
|
|
664
708
|
# the same way that Rust port is.
|
|
665
|
-
def filter_label(filter) = "filter #{filter[
|
|
709
|
+
def filter_label(filter) = "filter #{filter['aggregate']}.#{filter['field']} #{filter['op']}"
|
|
666
710
|
end
|
|
667
711
|
end
|
|
668
712
|
end
|
|
@@ -8,7 +8,11 @@ module Hecks
|
|
|
8
8
|
private
|
|
9
9
|
|
|
10
10
|
def build_catalog(runtime)
|
|
11
|
-
creating
|
|
11
|
+
creating = []
|
|
12
|
+
instance = []
|
|
13
|
+
entity_commands = []
|
|
14
|
+
queries = []
|
|
15
|
+
entity_queries = []
|
|
12
16
|
read_models = []
|
|
13
17
|
|
|
14
18
|
runtime.registry.bluebooks.each do |domain_name, bluebook|
|
|
@@ -54,7 +58,7 @@ module Hecks
|
|
|
54
58
|
populators: populators(runtime),
|
|
55
59
|
# Which aggregates this corpus can actually make one of — the ones
|
|
56
60
|
# `satisfiable?` is entitled to wait for.
|
|
57
|
-
creatable: creating.
|
|
61
|
+
creatable: creating.to_set { |entry| entry[:aggregate].hecks_name } }
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
# Which command, on which aggregate, appends to which entity list — so a
|
|
@@ -175,7 +175,8 @@ module Hecks
|
|
|
175
175
|
# `:id` the runtime itself never declared as an attribute
|
|
176
176
|
# either), which still needs exactly the old minting behavior.
|
|
177
177
|
unless composite_identity?(aggregate)
|
|
178
|
-
args[parent_key] ||= identity_shaped(aggregate, aggregate.identified_by, ValueGenerator.random_id(@random),
|
|
178
|
+
args[parent_key] ||= identity_shaped(aggregate, aggregate.identified_by, ValueGenerator.random_id(@random),
|
|
179
|
+
aggregate)
|
|
179
180
|
end
|
|
180
181
|
else
|
|
181
182
|
scalar = pick_known(aggregate.hecks_name)
|
|
@@ -128,9 +128,9 @@ module Hecks
|
|
|
128
128
|
# admitted rows — deliberately, to exercise the refusal a `one_of`
|
|
129
129
|
# exists to enforce, not just its happy path.
|
|
130
130
|
def invalid_member(value_object, random:)
|
|
131
|
-
value_object.attributes.to_h
|
|
131
|
+
value_object.attributes.to_h do |field|
|
|
132
132
|
[field.name.to_s, primitive(field.type.to_s, random: random, name: field.name.to_s)]
|
|
133
|
-
|
|
133
|
+
end
|
|
134
134
|
end
|
|
135
135
|
|
|
136
136
|
# THE ID ITSELF. This minted `{"value" => id}` back when a reference was
|
data/lib/hecks/grammar/evolve.rb
CHANGED
|
@@ -83,7 +83,12 @@ module Hecks
|
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def propose(word:, context:, body: "none", inner: "", opens: "", fills: "", path: nil)
|
|
86
|
-
|
|
86
|
+
if keyword_rows(path).any? do |row|
|
|
87
|
+
row[:word] == word && row[:context] == context
|
|
88
|
+
end
|
|
89
|
+
raise Refusal,
|
|
90
|
+
"#{context}.#{word} is already declared — one row per (word, context, form)"
|
|
91
|
+
end
|
|
87
92
|
|
|
88
93
|
path = owner_path(context: context, word: word, opens: opens, paths: paths_for(path))
|
|
89
94
|
source = File.read(path)
|
|
@@ -101,7 +106,8 @@ module Hecks
|
|
|
101
106
|
end
|
|
102
107
|
|
|
103
108
|
def set_status(word:, context:, to:, path: nil)
|
|
104
|
-
raise Refusal, "#{to.inspect} is not a station a word's life admits" unless %w[proposed admitted deprecated
|
|
109
|
+
raise Refusal, "#{to.inspect} is not a station a word's life admits" unless %w[proposed admitted deprecated
|
|
110
|
+
retired].include?(to)
|
|
105
111
|
|
|
106
112
|
path = path_holding_keyword(word, context, paths_for(path))
|
|
107
113
|
source = File.read(path)
|
|
@@ -131,7 +137,12 @@ module Hecks
|
|
|
131
137
|
row = keyword_rows(path).find { |r| r[:word] == word && r[:context] == context }
|
|
132
138
|
raise Refusal, "#{context}.#{word} is not declared" unless row
|
|
133
139
|
raise Refusal, "#{context}.#{word} was already #{row[:was]} — one rename hop, then eras" if row[:was]
|
|
134
|
-
|
|
140
|
+
if keyword_rows(path).any? do |r|
|
|
141
|
+
r[:word] == to && r[:context] == context
|
|
142
|
+
end
|
|
143
|
+
raise Refusal,
|
|
144
|
+
"#{context}.#{to} is already declared — a rename cannot land on a living word"
|
|
145
|
+
end
|
|
135
146
|
|
|
136
147
|
paths = paths_for(path)
|
|
137
148
|
path = path_holding_keyword(word, context, paths)
|
|
@@ -161,7 +172,11 @@ module Hecks
|
|
|
161
172
|
|
|
162
173
|
def path_holding_argument(keyword, context, at, named, paths = syntax_paths)
|
|
163
174
|
paths.find do |candidate|
|
|
164
|
-
argument_blocks(File.read(candidate)).any?
|
|
175
|
+
argument_blocks(File.read(candidate)).any? do |block|
|
|
176
|
+
block.lines.any? do |line|
|
|
177
|
+
argument_row?(line, keyword, context, at, named)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
165
180
|
end || raise(Refusal, "#{context}.#{keyword}'s argument at #{at.inspect}/named #{named.inspect} is not declared")
|
|
166
181
|
end
|
|
167
182
|
|
|
@@ -251,7 +266,9 @@ module Hecks
|
|
|
251
266
|
|
|
252
267
|
path = owner_path(context: context, word: keyword, paths: paths_for(path))
|
|
253
268
|
source = File.read(path)
|
|
254
|
-
block = argument_blocks(source).find
|
|
269
|
+
block = argument_blocks(source).find do |candidate|
|
|
270
|
+
candidate.include?(%(context: "#{context}"))
|
|
271
|
+
end || argument_block(source)
|
|
255
272
|
indent = block[/^(\s*)member /, 1] || " "
|
|
256
273
|
shape = pairs_shape.to_s.empty? ? "" : %(pairs_shape: "#{pairs_shape}", )
|
|
257
274
|
row = %(#{indent}member keyword: "#{keyword}", context: "#{context}", at: "#{at}", ) +
|
|
@@ -264,14 +281,21 @@ module Hecks
|
|
|
264
281
|
end
|
|
265
282
|
|
|
266
283
|
def set_argument_status(keyword:, context:, to:, at: "", named: "", path: nil)
|
|
267
|
-
raise Refusal, "#{to.inspect} is not a station an argument's life admits" unless %w[proposed admitted deprecated
|
|
284
|
+
raise Refusal, "#{to.inspect} is not a station an argument's life admits" unless %w[proposed admitted deprecated
|
|
285
|
+
retired].include?(to)
|
|
268
286
|
|
|
269
287
|
path = path_holding_argument(keyword, context, at, named, paths_for(path))
|
|
270
288
|
source = File.read(path)
|
|
271
|
-
block = argument_blocks(source).find
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
289
|
+
block = argument_blocks(source).find do |candidate|
|
|
290
|
+
candidate.lines.any? do |line|
|
|
291
|
+
argument_row?(line, keyword, context, at, named)
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
rows = block.lines.select { |line| argument_row?(line, keyword, context, at, named) }
|
|
295
|
+
if rows.empty?
|
|
296
|
+
raise Refusal, "#{context}.#{keyword}'s argument at #{at.inspect}/named #{named.inspect} is not " \
|
|
297
|
+
"declared"
|
|
298
|
+
end
|
|
275
299
|
|
|
276
300
|
updated = block.lines.map do |line|
|
|
277
301
|
next line unless argument_row?(line, keyword, context, at, named)
|
|
@@ -1643,6 +1643,130 @@
|
|
|
1643
1643
|
"value": "length_to_size"
|
|
1644
1644
|
}
|
|
1645
1645
|
}
|
|
1646
|
+
},
|
|
1647
|
+
{
|
|
1648
|
+
"verb": "Expression::Operator.Propose",
|
|
1649
|
+
"args": {
|
|
1650
|
+
"symbol": {
|
|
1651
|
+
"value": ".set?"
|
|
1652
|
+
},
|
|
1653
|
+
"category": {
|
|
1654
|
+
"value": "presence"
|
|
1655
|
+
},
|
|
1656
|
+
"precedence": {
|
|
1657
|
+
"value": 7
|
|
1658
|
+
},
|
|
1659
|
+
"arity": {
|
|
1660
|
+
"value": 1
|
|
1661
|
+
},
|
|
1662
|
+
"grammar": {
|
|
1663
|
+
"value": "inner"
|
|
1664
|
+
},
|
|
1665
|
+
"strategy": {
|
|
1666
|
+
"value": "suffix_match"
|
|
1667
|
+
},
|
|
1668
|
+
"position": {
|
|
1669
|
+
"value": 21
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
},
|
|
1673
|
+
{
|
|
1674
|
+
"verb": "Expression::Operator.Render",
|
|
1675
|
+
"args": {
|
|
1676
|
+
"symbol": {
|
|
1677
|
+
"value": ".set?"
|
|
1678
|
+
},
|
|
1679
|
+
"target": {
|
|
1680
|
+
"value": "ruby"
|
|
1681
|
+
},
|
|
1682
|
+
"form": {
|
|
1683
|
+
"value": "a.set?"
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
"verb": "Expression::Operator.Render",
|
|
1689
|
+
"args": {
|
|
1690
|
+
"symbol": {
|
|
1691
|
+
"value": ".set?"
|
|
1692
|
+
},
|
|
1693
|
+
"target": {
|
|
1694
|
+
"value": "rust"
|
|
1695
|
+
},
|
|
1696
|
+
"form": {
|
|
1697
|
+
"value": "a.set? -> !nil?(a)"
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
"verb": "Expression::Operator.Admit",
|
|
1703
|
+
"args": {
|
|
1704
|
+
"symbol": {
|
|
1705
|
+
"value": ".set?"
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
},
|
|
1709
|
+
{
|
|
1710
|
+
"verb": "Expression::Operator.Propose",
|
|
1711
|
+
"args": {
|
|
1712
|
+
"symbol": {
|
|
1713
|
+
"value": ".unset?"
|
|
1714
|
+
},
|
|
1715
|
+
"category": {
|
|
1716
|
+
"value": "presence"
|
|
1717
|
+
},
|
|
1718
|
+
"precedence": {
|
|
1719
|
+
"value": 7
|
|
1720
|
+
},
|
|
1721
|
+
"arity": {
|
|
1722
|
+
"value": 1
|
|
1723
|
+
},
|
|
1724
|
+
"grammar": {
|
|
1725
|
+
"value": "inner"
|
|
1726
|
+
},
|
|
1727
|
+
"strategy": {
|
|
1728
|
+
"value": "suffix_match"
|
|
1729
|
+
},
|
|
1730
|
+
"position": {
|
|
1731
|
+
"value": 22
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
},
|
|
1735
|
+
{
|
|
1736
|
+
"verb": "Expression::Operator.Render",
|
|
1737
|
+
"args": {
|
|
1738
|
+
"symbol": {
|
|
1739
|
+
"value": ".unset?"
|
|
1740
|
+
},
|
|
1741
|
+
"target": {
|
|
1742
|
+
"value": "ruby"
|
|
1743
|
+
},
|
|
1744
|
+
"form": {
|
|
1745
|
+
"value": "a.unset?"
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
},
|
|
1749
|
+
{
|
|
1750
|
+
"verb": "Expression::Operator.Render",
|
|
1751
|
+
"args": {
|
|
1752
|
+
"symbol": {
|
|
1753
|
+
"value": ".unset?"
|
|
1754
|
+
},
|
|
1755
|
+
"target": {
|
|
1756
|
+
"value": "rust"
|
|
1757
|
+
},
|
|
1758
|
+
"form": {
|
|
1759
|
+
"value": "a.unset? -> nil?(a)"
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
"verb": "Expression::Operator.Admit",
|
|
1765
|
+
"args": {
|
|
1766
|
+
"symbol": {
|
|
1767
|
+
"value": ".unset?"
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1646
1770
|
}
|
|
1647
1771
|
]
|
|
1648
1772
|
}
|
data/lib/hecks/grammar.rb
CHANGED
|
@@ -43,9 +43,9 @@ module Hecks
|
|
|
43
43
|
args = symbolize(step.fetch("args"))
|
|
44
44
|
begin
|
|
45
45
|
dispatcher.dispatch(step.fetch("verb"), **args)
|
|
46
|
-
rescue *Runtime::DOMAIN_REFUSALS =>
|
|
46
|
+
rescue *Runtime::DOMAIN_REFUSALS => e
|
|
47
47
|
raise Runtime::WiringError,
|
|
48
|
-
"the admission ledger refused at #{step['verb']} #{step['args']} — #{
|
|
48
|
+
"the admission ledger refused at #{step['verb']} #{step['args']} — #{e.message}"
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
@@ -119,7 +119,7 @@ module Hecks
|
|
|
119
119
|
# Every grammar/*.bluebook chapter, booted the same way the corpus
|
|
120
120
|
# boots them — each alone, in a scratch registry.
|
|
121
121
|
def grammar_chapters
|
|
122
|
-
Dir[File.join(DIR, "*.bluebook")].
|
|
122
|
+
Dir[File.join(DIR, "*.bluebook")].map do |chapter|
|
|
123
123
|
registry = Runtime::Registry.new
|
|
124
124
|
root = File.expand_path("../..", __dir__)
|
|
125
125
|
Hecks.with_registry(registry) do
|
|
@@ -138,14 +138,24 @@ module Hecks
|
|
|
138
138
|
# walked for the resolver's arithmetic.
|
|
139
139
|
def operators_in(canonical)
|
|
140
140
|
evaluator = Bluebook::Expression::Evaluator
|
|
141
|
-
|
|
142
|
-
evaluator.parse(canonical)
|
|
141
|
+
begin
|
|
142
|
+
node = evaluator.parse(canonical)
|
|
143
143
|
rescue StandardError
|
|
144
144
|
return []
|
|
145
145
|
end
|
|
146
146
|
walk_operators(node, evaluator).uniq
|
|
147
147
|
end
|
|
148
148
|
|
|
149
|
+
# A recursive descent over a CLOSED, declared set of AST node types
|
|
150
|
+
# (Evaluator's boolean/compare/include nodes, Resolver's arithmetic
|
|
151
|
+
# nodes, and the generic Struct fallback) — each branch does the
|
|
152
|
+
# same one thing (name the node's own operator, recurse into its
|
|
153
|
+
# children) and the branches don't interact, but the ABC score adds
|
|
154
|
+
# every branch's cost together. Splitting the case out per node type
|
|
155
|
+
# would trade one place that shows the whole operator vocabulary for
|
|
156
|
+
# several that each show a fragment, with no reduction in real
|
|
157
|
+
# complexity.
|
|
158
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
149
159
|
def walk_operators(node, evaluator)
|
|
150
160
|
resolver = Bluebook::Expression::Resolver
|
|
151
161
|
case node
|
data/lib/hecks/ir.rb
CHANGED
|
@@ -73,6 +73,11 @@ module Hecks
|
|
|
73
73
|
# crash on an undeclared lifecycle.
|
|
74
74
|
One = Struct.new(:source)
|
|
75
75
|
|
|
76
|
+
# The declaration side: `emits_ir(**spec)` records a construct's field ->
|
|
77
|
+
# rule map, `many`/`one` wrap a source name so a field can recurse into a
|
|
78
|
+
# list or a single nested construct, and `ir_spec` reads the declaration
|
|
79
|
+
# back — walking the superclass chain so an anonymous `Class.new(base)`
|
|
80
|
+
# inherits its base's shape instead of redeclaring it.
|
|
76
81
|
module Declares
|
|
77
82
|
def emits_ir(**spec)
|
|
78
83
|
@ir_spec = spec
|
|
@@ -92,6 +97,10 @@ module Hecks
|
|
|
92
97
|
end
|
|
93
98
|
end
|
|
94
99
|
|
|
100
|
+
# The emission side: `to_h` reads the declaring construct's `ir_spec` and
|
|
101
|
+
# applies each field's rule (`Many#to_h`, `One&.to_h`, a plain Symbol
|
|
102
|
+
# send, or an instance-`exec`'d Proc) in declaration order to build the
|
|
103
|
+
# actual Hash.
|
|
95
104
|
module Emits
|
|
96
105
|
def to_h
|
|
97
106
|
spec = ir_spec_for(self)
|
|
@@ -83,7 +83,8 @@ Hecks.bluebook "Bluebook" do
|
|
|
83
83
|
# Without this an aggregate attribute's default was simply dropped, and the
|
|
84
84
|
# reconstruction hardcoded nil, which happened to match every corpus member
|
|
85
85
|
# and would have lied about the first one that declared a default.
|
|
86
|
-
|
|
86
|
+
# optional: a declaration with no default has none (C3.7 — a value object's fields arrive or it refuses)
|
|
87
|
+
attribute :default, String, optional: true
|
|
87
88
|
# THE CLOSED SET THIS VALUE MUST BELONG TO, named where it was already
|
|
88
89
|
# declared — `Vocabulary::QueryComparator`. Qualified by the aggregate that
|
|
89
90
|
# holds it, because a closed set is a value object INSIDE an aggregate and
|
|
@@ -103,7 +104,8 @@ Hecks.bluebook "Bluebook" do
|
|
|
103
104
|
|
|
104
105
|
value_object "Transition" do
|
|
105
106
|
attribute :command, String
|
|
106
|
-
|
|
107
|
+
# optional: a transition declared with no `from:` matches any state (C3.7)
|
|
108
|
+
attribute :from_state, String, optional: true
|
|
107
109
|
attribute :to_state, String
|
|
108
110
|
end
|
|
109
111
|
|
|
@@ -82,7 +82,8 @@ Hecks.bluebook "Bluebook" do
|
|
|
82
82
|
attribute :list, String
|
|
83
83
|
attribute :optional, String, optional: true
|
|
84
84
|
attribute :pattern, String, optional: true
|
|
85
|
-
|
|
85
|
+
# optional: a declaration with no default has none (C3.7 — a value object's fields arrive or it refuses)
|
|
86
|
+
attribute :default, String, optional: true
|
|
86
87
|
# The closed set this value must belong to — see Aggregate::Field, which is
|
|
87
88
|
# the same declaration, spelled for a command's arguments.
|
|
88
89
|
attribute :admits, String, optional: true
|
|
@@ -61,7 +61,8 @@ Hecks.bluebook "Bluebook" do
|
|
|
61
61
|
attribute :list, String
|
|
62
62
|
attribute :optional, String, optional: true
|
|
63
63
|
attribute :pattern, String, optional: true
|
|
64
|
-
|
|
64
|
+
# optional: a declaration with no default has none (C3.7 — a value object's fields arrive or it refuses)
|
|
65
|
+
attribute :default, String, optional: true
|
|
65
66
|
# The closed set this value must belong to — see Aggregate::Field.
|
|
66
67
|
attribute :admits, String, optional: true
|
|
67
68
|
attribute :relationship, String, optional: true
|
|
@@ -60,7 +60,8 @@ Hecks.bluebook "Bluebook" do
|
|
|
60
60
|
attribute :option, String
|
|
61
61
|
attribute :key, String
|
|
62
62
|
attribute :value, String
|
|
63
|
-
|
|
63
|
+
# optional: not every projection option carries an `at` (C3.7)
|
|
64
|
+
attribute :at, String, optional: true
|
|
64
65
|
end
|
|
65
66
|
|
|
66
67
|
value_object "GroupByField" do
|
|
@@ -59,7 +59,8 @@ Hecks.bluebook "Bluebook" do
|
|
|
59
59
|
attribute :list, String
|
|
60
60
|
attribute :optional, String, optional: true
|
|
61
61
|
attribute :pattern, String, optional: true
|
|
62
|
-
|
|
62
|
+
# optional: a declaration with no default has none (C3.7 — a value object's fields arrive or it refuses)
|
|
63
|
+
attribute :default, String, optional: true
|
|
63
64
|
# The closed set this value must belong to — see Aggregate::Field.
|
|
64
65
|
attribute :admits, String, optional: true
|
|
65
66
|
attribute :relationship, String, optional: true
|
|
@@ -74,7 +75,8 @@ Hecks.bluebook "Bluebook" do
|
|
|
74
75
|
attribute :option, String
|
|
75
76
|
attribute :key, String
|
|
76
77
|
attribute :value, String
|
|
77
|
-
|
|
78
|
+
# optional: only a repeated option (an index hint) carries an `at` (C3.7)
|
|
79
|
+
attribute :at, String, optional: true
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
# The way back: everything DECLARED IN the aggregate above. The parent
|
|
@@ -34,7 +34,8 @@ Hecks.bluebook "Bluebook" do
|
|
|
34
34
|
attribute :list, String, pattern: '[^ \t\n\r]'
|
|
35
35
|
attribute :optional, String, optional: true
|
|
36
36
|
attribute :pattern, String, optional: true
|
|
37
|
-
|
|
37
|
+
# optional: a declaration with no default has none (C3.7 — a value object's fields arrive or it refuses)
|
|
38
|
+
attribute :default, String, optional: true
|
|
38
39
|
# The closed set this value must belong to — see Aggregate::Field.
|
|
39
40
|
attribute :admits, String, optional: true
|
|
40
41
|
attribute :relationship, String, optional: true
|
|
@@ -294,6 +294,8 @@ Hecks.bluebook "Bluebook" do
|
|
|
294
294
|
template: "{type}.{field} expects {expected}, got {offered}"
|
|
295
295
|
member refusal: "TypeMismatch", site: "non_finite_field",
|
|
296
296
|
template: "{type}.{field} must be a finite number, got {offered}"
|
|
297
|
+
member refusal: "TypeMismatch", site: "integer_range",
|
|
298
|
+
template: "{type}.{field} must fit in a 64-bit integer, got {offered}"
|
|
297
299
|
member refusal: "TypeMismatch", site: "pattern_mismatch",
|
|
298
300
|
template: "{type}.{field} must match {pattern}, got {offered}"
|
|
299
301
|
member refusal: "TypeMismatch", site: "arithmetic_amount",
|