jazari 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -0
- data/README.md +21 -28
- data/app/models/jazari/anchor.rb +8 -1
- data/app/models/jazari/recipe_record.rb +8 -1
- data/app/models/jazari/run.rb +8 -1
- data/app/models/jazari/runbook.rb +8 -1
- data/lib/jazari/mcp/actions.rb +124 -0
- data/lib/jazari/mcp/handler.rb +9 -13
- data/lib/jazari/version.rb +1 -1
- data/lib/jazari.rb +8 -18
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7725f32aead889e14acc5cb3df2f91b8add8e478248244b1dc8b7893565d8b34
|
|
4
|
+
data.tar.gz: 746f4d57b3e11aaa9252e089568981baa90ddab26fd41d27757a7db3f987227d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b6ece9105ac8612a32c329d2581590132874a558f502580dc6d62af6df050c258468d011b7be99b9cf5abc1b7a56561ecd383993fe91f4467d15d211086fe927
|
|
7
|
+
data.tar.gz: 84ac29347f6349d21e102ff66acf21387de68a95f149adf7267ccc77a2416fe3b1d7534fe0242b80cc8aec6aff36cf94f849597e12bd802aa18c59135144d66f
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,41 @@ codes, the resolved-value shape, how revisions are computed, and the schema the
|
|
|
8
8
|
generator emits — changes to any of those are breaking even when the method
|
|
9
9
|
signatures do not move.
|
|
10
10
|
|
|
11
|
+
## [0.2.1] - 2026-08-10
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **Table names are resolved lazily, not assigned at class-definition time.**
|
|
16
|
+
The binding previously depended on load order: under Zeitwerk a host's
|
|
17
|
+
`Jazari.configure` runs before the model constants autoload, so the
|
|
18
|
+
assignment never happened and the models silently kept the gem's default
|
|
19
|
+
names. A host that had adopted *existing* tables then queried tables that did
|
|
20
|
+
not exist. Found by a third host adoption.
|
|
21
|
+
|
|
22
|
+
## [0.2.0] - 2026-08-10
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **`Jazari::Mcp::Actions`** — every action declared as data (`scope`, `effect`,
|
|
27
|
+
`confirm`, parameter schemas, summary), with `schema_fragment` for merging into
|
|
28
|
+
a host's own MCP tool. A host with an existing MCP surface keeps its tool name,
|
|
29
|
+
envelope, dispatch, and permissions, and calls `Jazari.*` directly — no
|
|
30
|
+
coupling to this gem's handler or reply shape.
|
|
31
|
+
- `Jazari::Mcp::Actions.summaries` for a tool description or paired skill.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **The MCP layer is now genuinely optional.** Descriptors load with the gem;
|
|
36
|
+
the dispatcher does not. `require "jazari/mcp/handler"` to opt in.
|
|
37
|
+
- `Mcp::Handler` validates and scopes against the same declarations, so a
|
|
38
|
+
published schema and the implemented behaviour cannot drift. A test asserts the
|
|
39
|
+
handler dispatches every declared action.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- `mcp/handler.rb` now requires `mcp/actions`. With the layer opt-in, a host
|
|
44
|
+
requiring the handler alone would have raised `NameError`.
|
|
45
|
+
|
|
11
46
|
## [0.1.0] - 2026-08-10
|
|
12
47
|
|
|
13
48
|
First release. Proven against one host adoption.
|
data/README.md
CHANGED
|
@@ -10,6 +10,14 @@ Requires Ruby 3.2+, Rails 7.1+, and PostgreSQL.
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## Guides
|
|
14
|
+
|
|
15
|
+
The README is the pitch; the [guides](guide/) are the working documents —
|
|
16
|
+
[concepts](guide/01-concepts.md) (start here: one word means something
|
|
17
|
+
different than you expect), [adoption](guide/02-adoption.md),
|
|
18
|
+
[anchors](guide/03-anchors.md), [runs and evidence](guide/04-runs.md),
|
|
19
|
+
[MCP](guide/05-mcp.md), and [migrating an existing checklist](guide/06-migrating.md).
|
|
20
|
+
|
|
13
21
|
## The problem
|
|
14
22
|
|
|
15
23
|
You have a procedure. Verify a backup by restoring it. Provision a server.
|
|
@@ -221,34 +229,19 @@ out of coverage.
|
|
|
221
229
|
## What this is not
|
|
222
230
|
|
|
223
231
|
Not an execution framework. Jazari holds the *state* of a procedure — the canon,
|
|
224
|
-
the overrides, the runs, the evidence. It does not SSH anywhere
|
|
225
|
-
commands.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
That is not why the gem carries his name.
|
|
238
|
-
|
|
239
|
-
He also wrote *The Book of Knowledge of Ingenious Mechanical Devices*, finished
|
|
240
|
-
the year he died: fifty machines, each with numbered construction steps and
|
|
241
|
-
drawings detailed enough that a stranger who had never met him could rebuild the
|
|
242
|
-
device. He wrote down the *procedure*, not just the result — including, in his
|
|
243
|
-
own words, the steps he had gotten wrong first.
|
|
244
|
-
|
|
245
|
-
Eight hundred years later people have built working machines from those pages.
|
|
246
|
-
|
|
247
|
-
That is the whole idea here. A procedure is not lore in someone's head or prose
|
|
248
|
-
in a document nobody opens. It is a written, versioned, checkable artefact that
|
|
249
|
-
somebody else can execute and prove they executed.
|
|
250
|
-
|
|
251
|
-
`runbook` was taken on RubyGems — by the execution framework above, fittingly.
|
|
232
|
+
the overrides, the runs, the evidence. It does not SSH anywhere, shell out, or
|
|
233
|
+
run your commands.
|
|
234
|
+
|
|
235
|
+
That boundary is deliberate. Execution is already well served by whatever you
|
|
236
|
+
have — CI, a rake task, Ansible, a deploy tool — and those differ per shop.
|
|
237
|
+
What none of them keep is a durable, checkable, addressable record of *which
|
|
238
|
+
procedure* was run, by whom, and what came back. Jazari keeps that, and stays
|
|
239
|
+
out of the way of however you actually run things.
|
|
240
|
+
|
|
241
|
+
(The name `runbook` was already taken on RubyGems, by a DSL for executing
|
|
242
|
+
operational procedures. That gem's last release was 2021 and its last commit
|
|
243
|
+
2022, so it is not a dependency worth taking — but the name is still occupied,
|
|
244
|
+
which is one reason this gem is called jazari.)
|
|
252
245
|
|
|
253
246
|
## License
|
|
254
247
|
|
data/app/models/jazari/anchor.rb
CHANGED
|
@@ -5,7 +5,14 @@ module Jazari
|
|
|
5
5
|
# a JSON-tree node, a file path, a DNS zone, a document. The scope is
|
|
6
6
|
# host-registered; the gem ships no default scope naming any real model.
|
|
7
7
|
class Anchor < ApplicationRecord
|
|
8
|
-
|
|
8
|
+
# Resolved on EVERY call, not assigned at class-definition time.
|
|
9
|
+
#
|
|
10
|
+
# Assigning it eagerly made the binding depend on load order: under Zeitwerk
|
|
11
|
+
# a host's `configure` runs before these constants autoload, so the
|
|
12
|
+
# assignment never happened and the model silently kept the gem's default
|
|
13
|
+
# name. A host adopting existing tables then queried tables that do not
|
|
14
|
+
# exist. Reading config here removes the ordering question entirely.
|
|
15
|
+
def self.table_name = Jazari.table_name_for(:anchors)
|
|
9
16
|
|
|
10
17
|
has_one :runbook, as: :runbookable, dependent: :destroy
|
|
11
18
|
|
|
@@ -5,7 +5,14 @@ module Jazari
|
|
|
5
5
|
# is the immutable value the domain passes around, and letting an ActiveRecord
|
|
6
6
|
# object wear that name is how unsaved records leak into return values.
|
|
7
7
|
class RecipeRecord < ApplicationRecord
|
|
8
|
-
|
|
8
|
+
# Resolved on EVERY call, not assigned at class-definition time.
|
|
9
|
+
#
|
|
10
|
+
# Assigning it eagerly made the binding depend on load order: under Zeitwerk
|
|
11
|
+
# a host's `configure` runs before these constants autoload, so the
|
|
12
|
+
# assignment never happened and the model silently kept the gem's default
|
|
13
|
+
# name. A host adopting existing tables then queried tables that do not
|
|
14
|
+
# exist. Reading config here removes the ordering question entirely.
|
|
15
|
+
def self.table_name = Jazari.table_name_for(:recipes)
|
|
9
16
|
|
|
10
17
|
POLICIES = Jazari::RunPolicy::ALL
|
|
11
18
|
|
data/app/models/jazari/run.rb
CHANGED
|
@@ -8,7 +8,14 @@ module Jazari
|
|
|
8
8
|
# that separation is the whole point: `reset` on a runbook must not be able to
|
|
9
9
|
# destroy the record that the ritual ever ran.
|
|
10
10
|
class Run < ApplicationRecord
|
|
11
|
-
|
|
11
|
+
# Resolved on EVERY call, not assigned at class-definition time.
|
|
12
|
+
#
|
|
13
|
+
# Assigning it eagerly made the binding depend on load order: under Zeitwerk
|
|
14
|
+
# a host's `configure` runs before these constants autoload, so the
|
|
15
|
+
# assignment never happened and the model silently kept the gem's default
|
|
16
|
+
# name. A host adopting existing tables then queried tables that do not
|
|
17
|
+
# exist. Reading config here removes the ordering question entirely.
|
|
18
|
+
def self.table_name = Jazari.table_name_for(:runs)
|
|
12
19
|
|
|
13
20
|
belongs_to :subject, polymorphic: true, optional: true
|
|
14
21
|
|
|
@@ -4,7 +4,14 @@ module Jazari
|
|
|
4
4
|
# One subject's override of the canon. Materialized on first customization —
|
|
5
5
|
# never on read.
|
|
6
6
|
class Runbook < ApplicationRecord
|
|
7
|
-
|
|
7
|
+
# Resolved on EVERY call, not assigned at class-definition time.
|
|
8
|
+
#
|
|
9
|
+
# Assigning it eagerly made the binding depend on load order: under Zeitwerk
|
|
10
|
+
# a host's `configure` runs before these constants autoload, so the
|
|
11
|
+
# assignment never happened and the model silently kept the gem's default
|
|
12
|
+
# name. A host adopting existing tables then queried tables that do not
|
|
13
|
+
# exist. Reading config here removes the ordering question entirely.
|
|
14
|
+
def self.table_name = Jazari.table_name_for(:runbooks)
|
|
8
15
|
|
|
9
16
|
belongs_to :runbookable, polymorphic: true
|
|
10
17
|
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jazari
|
|
4
|
+
module Mcp
|
|
5
|
+
# Declarative descriptors for every action, so a host can absorb jazari into
|
|
6
|
+
# ITS OWN tool instead of adopting this gem's handler.
|
|
7
|
+
#
|
|
8
|
+
# Without these, a host has two bad choices: take `Mcp::Handler`'s reply
|
|
9
|
+
# shape wholesale, or hand-write the action list and let it drift from the
|
|
10
|
+
# gem it describes. Neither is acceptable for a host that already has an
|
|
11
|
+
# MCP surface with its own envelope, naming, and permission model.
|
|
12
|
+
#
|
|
13
|
+
# These descriptors are the single source of truth: `Handler` validates and
|
|
14
|
+
# scopes against them too, so the schema a host publishes and the behaviour
|
|
15
|
+
# the gem implements cannot diverge.
|
|
16
|
+
module Actions
|
|
17
|
+
# effect drives how a host should annotate the action to its clients:
|
|
18
|
+
# :read — no writes
|
|
19
|
+
# :additive — writes, but cannot destroy prior state
|
|
20
|
+
# :overwrite — may replace an operator's content; annotate cautiously
|
|
21
|
+
# :destructive — removes something; gate it
|
|
22
|
+
Action = Data.define(:name, :scope, :effect, :summary, :params, :confirm) do
|
|
23
|
+
def read? = scope == :read
|
|
24
|
+
def confirm? = confirm == true
|
|
25
|
+
def to_h = { name: name, scope: scope, effect: effect, summary: summary,
|
|
26
|
+
params: params, confirm: confirm }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
REVISION = { type: "string",
|
|
30
|
+
description: "The revision from the read immediately before this call." }.freeze
|
|
31
|
+
ITEM_ID = { type: "string", description: "Opaque checklist item id." }.freeze
|
|
32
|
+
RUN_ID = { type: "integer", description: "The run returned by start." }.freeze
|
|
33
|
+
ACTOR = { type: "string", description: "Opaque identity of who is acting." }.freeze
|
|
34
|
+
|
|
35
|
+
ALL = [
|
|
36
|
+
Action.new(name: "get", scope: :read, effect: :read, confirm: false,
|
|
37
|
+
summary: "Resolve the operating procedure for a target, with progress and last run.",
|
|
38
|
+
params: {}),
|
|
39
|
+
Action.new(name: "last_run", scope: :read, effect: :read, confirm: false,
|
|
40
|
+
summary: "The most recent run for a target — answers whether the ritual actually happened.",
|
|
41
|
+
params: {}),
|
|
42
|
+
Action.new(name: "set", scope: :write, effect: :overwrite, confirm: false,
|
|
43
|
+
summary: "Replace this subject's procedure. Materialises an override on first use.",
|
|
44
|
+
params: { expected_revision: REVISION,
|
|
45
|
+
topic: { type: "string", description: "Short title." },
|
|
46
|
+
description: { type: "string", description: "Markdown body." },
|
|
47
|
+
checklist: { type: "array", description: "Full checklist; replaces the existing one." } }),
|
|
48
|
+
Action.new(name: "add_item", scope: :write, effect: :additive, confirm: false,
|
|
49
|
+
summary: "Append one checklist step.",
|
|
50
|
+
params: { expected_revision: REVISION,
|
|
51
|
+
text: { type: "string", description: "The step." },
|
|
52
|
+
required: { type: "boolean", description: "Whether the step is required. Default true." } }),
|
|
53
|
+
Action.new(name: "remove_item", scope: :write, effect: :destructive, confirm: false,
|
|
54
|
+
summary: "Delete one checklist step.",
|
|
55
|
+
params: { expected_revision: REVISION, item_id: ITEM_ID }),
|
|
56
|
+
Action.new(name: "check_item", scope: :write, effect: :additive, confirm: false,
|
|
57
|
+
summary: "Mark a step done or not done.",
|
|
58
|
+
params: { expected_revision: REVISION, item_id: ITEM_ID,
|
|
59
|
+
done: { type: "boolean", description: "Default true." } }),
|
|
60
|
+
Action.new(name: "reset", scope: :write, effect: :destructive, confirm: true,
|
|
61
|
+
summary: "Discard this subject's override and reveal the current canon.",
|
|
62
|
+
params: { expected_revision: REVISION,
|
|
63
|
+
confirm: { type: "boolean", description: "Must be true — this discards operator content." } }),
|
|
64
|
+
Action.new(name: "start", scope: :write, effect: :additive, confirm: false,
|
|
65
|
+
summary: "Open a run. Under a once-per-day recipe this returns the existing run instead of erroring.",
|
|
66
|
+
params: { actor_ref: ACTOR }),
|
|
67
|
+
Action.new(name: "tick", scope: :write, effect: :additive, confirm: false,
|
|
68
|
+
summary: "Record a step done within a run. Does not touch the subject's own checklist.",
|
|
69
|
+
params: { run_id: RUN_ID, expected_revision: REVISION, item_id: ITEM_ID,
|
|
70
|
+
done: { type: "boolean", description: "Default true." },
|
|
71
|
+
actor_ref: ACTOR,
|
|
72
|
+
note: { type: "string", description: "Optional free text." } }),
|
|
73
|
+
Action.new(name: "evidence", scope: :write, effect: :additive, confirm: false,
|
|
74
|
+
summary: "Attach evidence to a run: output, url, sha, count, or note.",
|
|
75
|
+
params: { run_id: RUN_ID, expected_revision: REVISION, item_id: ITEM_ID,
|
|
76
|
+
kind: { type: "string", description: "One of: output, url, sha, count, note." },
|
|
77
|
+
value: { type: "string", description: "The evidence itself." } }),
|
|
78
|
+
Action.new(name: "finish", scope: :write, effect: :additive, confirm: false,
|
|
79
|
+
summary: "Close a run with an outcome: completed, abandoned, or failed.",
|
|
80
|
+
params: { run_id: RUN_ID, expected_revision: REVISION,
|
|
81
|
+
outcome: { type: "string", description: "completed | abandoned | failed" } })
|
|
82
|
+
].freeze
|
|
83
|
+
|
|
84
|
+
NAMES = ALL.map(&:name).freeze
|
|
85
|
+
|
|
86
|
+
module_function
|
|
87
|
+
|
|
88
|
+
# scope: :read advertises only the read-only subset. A read-scoped
|
|
89
|
+
# connection should not SEE mutations in its tool list — offering them and
|
|
90
|
+
# then refusing is a worse experience than not offering them.
|
|
91
|
+
def all(scope: :write)
|
|
92
|
+
scope.to_s == "read" ? ALL.select(&:read?) : ALL
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def names(scope: :write) = all(scope: scope).map(&:name)
|
|
96
|
+
|
|
97
|
+
def fetch(name)
|
|
98
|
+
ALL.find { |action| action.name == name.to_s } or
|
|
99
|
+
raise ArgumentError, "unknown runbook action #{name.inspect}"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# A fragment a host merges into its OWN tool's input schema. It deliberately
|
|
103
|
+
# returns only the action enum and the parameter properties — the host owns
|
|
104
|
+
# the tool name, the description, its own target params, and its reply
|
|
105
|
+
# envelope. Nothing here presumes this gem's handler is in the loop.
|
|
106
|
+
#
|
|
107
|
+
# frag = Jazari::Mcp::Actions.schema_fragment
|
|
108
|
+
# MY_TOOL[:input_schema][:properties].merge!(frag[:properties])
|
|
109
|
+
# MY_TOOL[:input_schema][:properties][:action][:enum] += frag[:enum]
|
|
110
|
+
def schema_fragment(scope: :write)
|
|
111
|
+
actions = all(scope: scope)
|
|
112
|
+
properties = actions.each_with_object({}) do |action, acc|
|
|
113
|
+
action.params.each { |key, spec| acc[key] ||= spec }
|
|
114
|
+
end
|
|
115
|
+
{ enum: actions.map(&:name), properties: properties }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Human-readable action list for a tool description or a paired skill.
|
|
119
|
+
def summaries(scope: :write)
|
|
120
|
+
all(scope: scope).map { |a| "#{a.name} — #{a.summary}" }
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
data/lib/jazari/mcp/handler.rb
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# The handler dispatches against Mcp::Actions, so it must require it: a host may
|
|
4
|
+
# `require "jazari/mcp/handler"` on its own now that this layer is opt-in.
|
|
5
|
+
require "jazari/mcp/actions"
|
|
6
|
+
|
|
3
7
|
module Jazari
|
|
4
8
|
module Mcp
|
|
5
9
|
# Transport-neutral adapter between an MCP action name and the domain.
|
|
@@ -13,23 +17,15 @@ module Jazari
|
|
|
13
17
|
# That split is deliberate: two products sharing this handler still present
|
|
14
18
|
# their own tool, their own subject vocabulary, and their own permissions.
|
|
15
19
|
class Handler
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# Advertise only what the granted scope may call. A read-scoped connection
|
|
21
|
-
# should not see mutations in its tool list at all — hiding them is not
|
|
22
|
-
# security, but offering them and refusing is a worse experience.
|
|
23
|
-
def self.actions_for(scope)
|
|
24
|
-
scope.to_s == "read" ? READ_ACTIONS : ACTIONS
|
|
25
|
-
end
|
|
20
|
+
# Actions are declared once, in Mcp::Actions, so the schema a host
|
|
21
|
+
# publishes and the behaviour implemented here cannot drift apart.
|
|
22
|
+
def self.actions_for(scope) = Actions.names(scope: scope)
|
|
26
23
|
|
|
27
24
|
def call(action:, target:, arguments: {})
|
|
28
25
|
args = symbolize(arguments)
|
|
29
|
-
|
|
30
|
-
raise ArgumentError, "unknown runbook action #{action.inspect}" unless ACTIONS.include?(name)
|
|
26
|
+
descriptor = Actions.fetch(action) # raises on an unknown action
|
|
31
27
|
|
|
32
|
-
reply(public_send(:"handle_#{name}", target, args))
|
|
28
|
+
reply(public_send(:"handle_#{descriptor.name}", target, args))
|
|
33
29
|
rescue Jazari::Error => error
|
|
34
30
|
# The closed taxonomy crosses the wire as a code, never as a message
|
|
35
31
|
# that could disclose a record, a query, or whether a target exists.
|
data/lib/jazari/version.rb
CHANGED
data/lib/jazari.rb
CHANGED
|
@@ -10,7 +10,11 @@ require "jazari/resolved_runbook"
|
|
|
10
10
|
require "jazari/recipe_registry"
|
|
11
11
|
require "jazari/runs"
|
|
12
12
|
require "jazari/operations"
|
|
13
|
-
|
|
13
|
+
# The MCP layer is OPTIONAL. Descriptors are cheap and a host may want them to
|
|
14
|
+
# build its own tool, so they load; the handler does not, because a host with
|
|
15
|
+
# its own MCP surface never calls it.
|
|
16
|
+
# require "jazari/mcp/handler" # only if you want the ready-made dispatcher
|
|
17
|
+
require "jazari/mcp/actions"
|
|
14
18
|
require "jazari/railtie" if defined?(::Rails::Railtie)
|
|
15
19
|
|
|
16
20
|
# Addressable operating procedures.
|
|
@@ -66,25 +70,11 @@ module Jazari
|
|
|
66
70
|
config.table_names[key]&.to_s || "#{config.table_prefix}#{TABLES.fetch(key)}"
|
|
67
71
|
end
|
|
68
72
|
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
# CONSTRAINT: ActiveRecord table names are process-global class state, so the
|
|
73
|
-
# prefix is a BOOT-TIME setting for the whole process. It is not per-request,
|
|
74
|
-
# per-thread, or per-tenant, and two hosts in one process cannot hold
|
|
75
|
-
# different prefixes. Call `configure` once, at boot, after the models are
|
|
76
|
-
# loaded; `models_loaded?` reports whether the binding actually took effect
|
|
77
|
-
# so a host can assert it instead of silently running on default names.
|
|
73
|
+
# Kept for hosts that call it explicitly; the models now resolve their own
|
|
74
|
+
# names on every call, so nothing depends on this having run.
|
|
78
75
|
def self.models_loaded? = const_defined?(:RecipeRecord)
|
|
79
76
|
|
|
80
|
-
def self.apply_table_names!
|
|
81
|
-
return false unless models_loaded?
|
|
82
|
-
|
|
83
|
-
{ RecipeRecord: :recipes, Runbook: :runbooks, Anchor: :anchors, Run: :runs }.each do |klass, key|
|
|
84
|
-
const_get(klass).table_name = table_name_for(key)
|
|
85
|
-
end
|
|
86
|
-
true
|
|
87
|
-
end
|
|
77
|
+
def self.apply_table_names! = models_loaded?
|
|
88
78
|
|
|
89
79
|
def self.config = configuration || configure
|
|
90
80
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jazari
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nauman Tariq
|
|
@@ -60,6 +60,7 @@ files:
|
|
|
60
60
|
- lib/jazari/anchors.rb
|
|
61
61
|
- lib/jazari/checklist.rb
|
|
62
62
|
- lib/jazari/errors.rb
|
|
63
|
+
- lib/jazari/mcp/actions.rb
|
|
63
64
|
- lib/jazari/mcp/handler.rb
|
|
64
65
|
- lib/jazari/operations.rb
|
|
65
66
|
- lib/jazari/railtie.rb
|