Module: Jazari::Operations
- Defined in:
- lib/jazari/operations.rb
Overview
The runbook half: resolving a subject's operating truth and customizing it.
Reading a default writes NOTHING — no row, no anchor, no audit noise. The first customization materializes a record; reset destroys it and reveals the current canon again, so a recipe correction reaches every subject that never overrode it.
Constant Summary collapse
- MAX_TOPIC =
120- MAX_DESCRIPTION =
20_000- KEEP_ORIGIN =
origindistinguishes REWRITING the procedure from PERFORMING it, so the sentinel is not cosmetic. Rewriting it (customize) restates why the row exists, and passing nil there is an operator claiming it as their own. Ticking an item is doing the work the row already describes, and must leave that claim alone — otherwise the first person to check a box silently converts a migration artifact into a deliberate divergence. :keep
Class Method Summary collapse
- .add_item(target:, expected_revision:, text:, required: true) ⇒ Object
- .check_item(target:, expected_revision:, item_id:, done:) ⇒ Object
-
.customize(target:, expected_revision:, topic:, description:, checklist:, origin: nil) ⇒ Object
originrecords WHY this row exists, for hosts that materialize runbooks themselves — a backfill, an import, a template. -
.forget_subject(subject) ⇒ Object
A host calls this from its own after-commit when a subject is destroyed.
- .remove_item(target:, expected_revision:, item_id:) ⇒ Object
-
.reset(target:, expected_revision:) ⇒ Object
Destroys the customization and reveals the current canon.
- .resolve(target:) ⇒ Object
Class Method Details
.add_item(target:, expected_revision:, text:, required: true) ⇒ Object
38 39 40 41 42 43 44 45 46 47 |
# File 'lib/jazari/operations.rb', line 38 def add_item(target:, expected_revision:, text:, required: true) writable!(target) write(target, expected_revision) do |current| items = current[:checklist] + [ { id: Checklist.generate_id, text: text.to_s, done: false, required: required == true } ] Checklist.validate!(items) current.merge(checklist: items) end end |
.check_item(target:, expected_revision:, item_id:, done:) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/jazari/operations.rb', line 59 def check_item(target:, expected_revision:, item_id:, done:) writable!(target) write(target, expected_revision) do |current| found = false items = current[:checklist].map do |item| next item unless item[:id] == item_id.to_s found = true item.merge(done: done == true) end raise ItemNotFound, "unknown checklist item #{item_id}" unless found current.merge(checklist: items) end end |
.customize(target:, expected_revision:, topic:, description:, checklist:, origin: nil) ⇒ Object
origin records WHY this row exists, for hosts that materialize runbooks
themselves — a backfill, an import, a template. Leave it nil for an operator
edit, which is the default because that is the ordinary case.
29 30 31 32 33 34 35 36 |
# File 'lib/jazari/operations.rb', line 29 def customize(target:, expected_revision:, topic:, description:, checklist:, origin: nil) writable!(target) validate_document!(topic, description) items = Checklist.normalize(checklist) write(target, expected_revision, origin: origin) do |current| current.merge(topic: topic, description: description, checklist: items) end end |
.forget_subject(subject) ⇒ Object
A host calls this from its own after-commit when a subject is destroyed.
Jazari cannot hook the host's model itself: the subject may live in a different logical database, so no cross-database foreign key is claimed and no cascade exists. The host calls in; the gem cleans up.
Runs are deliberately PRESERVED. A run is an audit record of something that actually happened, and deleting the subject does not un-happen it. Their subject columns keep pointing at the departed record — which is sound precisely because no FK was ever claimed.
101 102 103 104 105 106 107 |
# File 'lib/jazari/operations.rb', line 101 def forget_subject(subject) runbook = Runbook.find_by(runbookable: subject) runbook&.destroy! subject.destroy! if subject.is_a?(Anchor) && subject.persisted? Jazari.config.on_subject_destroyed&.call(subject) true end |
.remove_item(target:, expected_revision:, item_id:) ⇒ Object
49 50 51 52 53 54 55 56 57 |
# File 'lib/jazari/operations.rb', line 49 def remove_item(target:, expected_revision:, item_id:) writable!(target) write(target, expected_revision) do |current| remaining = current[:checklist].reject { |item| item[:id] == item_id.to_s } raise ItemNotFound, "unknown checklist item #{item_id}" if remaining.length == current[:checklist].length current.merge(checklist: remaining) end end |
.reset(target:, expected_revision:) ⇒ Object
Destroys the customization and reveals the current canon. Idempotent on an already-default target only while the supplied default revision matches.
77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/jazari/operations.rb', line 77 def reset(target:, expected_revision:) writable!(target) record = find_runbook(target) if record record.with_lock do verify_custom_revision!(record, expected_revision) destroy_with_anchor(record, target) end else verify_default_revision!(target, expected_revision) end resolve(target: target) end |
.resolve(target:) ⇒ Object
16 17 18 19 20 21 22 23 24 |
# File 'lib/jazari/operations.rb', line 16 def resolve(target:) recipe = RecipeRegistry.fetch(target.recipe_id) record = find_runbook(target) last = Runs.last(target: target) return custom_value(record, target, recipe, last) if record default_value(recipe, target, last) end |