assistant 1.0.0.rc1 → 1.0.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/.github/workflows/docs.yml +9 -19
- data/.gitignore +0 -3
- data/CHANGELOG.md +8 -31
- data/Gemfile +6 -9
- data/Gemfile.lock +3 -104
- data/README.md +8 -6
- data/Rakefile +17 -11
- data/Steepfile +18 -3
- data/assistant.gemspec +1 -1
- data/docs/.nojekyll +0 -0
- data/docs/404.html +292 -0
- data/docs/_media/apple-touch-icon.png +0 -0
- data/docs/_media/favicon.png +0 -0
- data/docs/_media/home-logo.svg +8 -0
- data/docs/_media/repo-card.png +0 -0
- data/docs/_sidebar.md +29 -0
- data/docs/api-reference.md +22 -11
- data/docs/changelog.md +0 -5
- data/docs/deprecations.md +4 -9
- data/docs/examples/{index.md → README.md} +7 -17
- data/docs/examples/cli-handler.md +39 -13
- data/docs/examples/composing-services.md +45 -13
- data/docs/examples/execute-callbacks.md +49 -13
- data/docs/examples/instrumentation-notifier.md +48 -13
- data/docs/examples/rails-service.md +40 -13
- data/docs/examples/rbs-generator.md +90 -13
- data/docs/examples/sidekiq-worker.md +33 -13
- data/docs/getting-started.md +11 -5
- data/docs/guides/{index.md → README.md} +1 -8
- data/docs/guides/composing-services.md +6 -12
- data/docs/guides/inputs.md +6 -12
- data/docs/guides/logging-and-results.md +9 -15
- data/docs/guides/rbs-and-types.md +151 -13
- data/docs/guides/validation.md +29 -11
- data/docs/index.html +291 -0
- data/docs/index.md +67 -24
- data/docs/roadmap.md +8 -13
- data/lib/assistant/version.rb +1 -1
- metadata +12 -5
- data/_config.yml +0 -87
data/docs/404.html
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
6
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
7
|
+
|
|
8
|
+
<title>Assistant — tiny, dependency-free soft-fail service objects for Ruby</title>
|
|
9
|
+
<meta name="description"
|
|
10
|
+
content="Assistant is a tiny, dependency-free Ruby gem for soft-fail service objects with a uniform result shape, structured log items, and first-class RBS/Steep support.">
|
|
11
|
+
<meta name="theme-color" content="#2f4f5a">
|
|
12
|
+
|
|
13
|
+
<link rel="icon" type="image/svg+xml" href="/assistant/_media/assistant_icon.svg">
|
|
14
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/assistant/_media/apple-touch-icon.png">
|
|
15
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
16
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
17
|
+
<link href="https://fonts.googleapis.com/css2?family=MuseoModerno:ital,wght@0,800;1,800&display=swap" rel="stylesheet">
|
|
18
|
+
|
|
19
|
+
<!-- Open Graph / Twitter card -->
|
|
20
|
+
<meta property="og:title" content="Assistant">
|
|
21
|
+
<meta property="og:description" content="Tiny, dependency-free soft-fail service objects for Ruby.">
|
|
22
|
+
<meta property="og:image" content="https://ramongr.github.io/assistant/_media/repo-card.png">
|
|
23
|
+
<meta property="og:image:width" content="1200">
|
|
24
|
+
<meta property="og:image:height" content="630">
|
|
25
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
26
|
+
<meta name="twitter:image" content="https://ramongr.github.io/assistant/_media/repo-card.png">
|
|
27
|
+
|
|
28
|
+
<!-- Docsify default theme + dark/light theme overlay -->
|
|
29
|
+
<link rel="stylesheet"
|
|
30
|
+
href="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css"
|
|
31
|
+
title="docsify-darklight-theme"
|
|
32
|
+
type="text/css" />
|
|
33
|
+
|
|
34
|
+
<!-- Prism syntax-highlighting theme (light by default; swapped on dark toggle) -->
|
|
35
|
+
<link rel="stylesheet"
|
|
36
|
+
href="//cdn.jsdelivr.net/npm/prism-themes/themes/prism-material-light.min.css"
|
|
37
|
+
id="prism-theme"
|
|
38
|
+
type="text/css" />
|
|
39
|
+
|
|
40
|
+
<style>
|
|
41
|
+
:root {
|
|
42
|
+
/* Brand palette
|
|
43
|
+
#22223b — Space Cadet (dark text / dark bg)
|
|
44
|
+
#a07178 — Rose Taupe (secondary accent / success)
|
|
45
|
+
#4d7c8a — Steel Teal (dark-mode highlight; brand asset color)
|
|
46
|
+
#2f4f5a — Steel Teal Dark (light-mode links / inline code /
|
|
47
|
+
theme color — 7.87:1 on #f4f2f3,
|
|
48
|
+
8.30:1 on #ffffff, both AAA)
|
|
49
|
+
#f4f2f3 — Cultured (light background)
|
|
50
|
+
#ffd166 — Naples Yellow (primary accent) */
|
|
51
|
+
--theme-color: #2f4f5a;
|
|
52
|
+
--assistant-space-cadet: #22223b;
|
|
53
|
+
--assistant-naples-yellow: #ffd166;
|
|
54
|
+
}
|
|
55
|
+
.app-name-link {
|
|
56
|
+
align-items: center;
|
|
57
|
+
display: flex;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
padding: 0.75rem 0 1rem;
|
|
60
|
+
}
|
|
61
|
+
.assistant-app-name {
|
|
62
|
+
display: block;
|
|
63
|
+
font-family: 'MuseoModerno', 'PT Sans', system-ui, sans-serif;
|
|
64
|
+
font-size: 5.5rem;
|
|
65
|
+
font-style: normal;
|
|
66
|
+
font-weight: 800;
|
|
67
|
+
letter-spacing: -0.09em;
|
|
68
|
+
line-height: 0.82;
|
|
69
|
+
}
|
|
70
|
+
.assistant-home-wordmark {
|
|
71
|
+
color: #22223b;
|
|
72
|
+
font-family: 'MuseoModerno', 'PT Sans', system-ui, sans-serif;
|
|
73
|
+
font-size: clamp(4.25rem, 16vw, 16rem);
|
|
74
|
+
font-style: normal;
|
|
75
|
+
font-weight: 800;
|
|
76
|
+
letter-spacing: -0.09em;
|
|
77
|
+
line-height: 0.82;
|
|
78
|
+
margin: 0 0 1rem;
|
|
79
|
+
text-align: center;
|
|
80
|
+
}
|
|
81
|
+
body.assistant-sidebar-shell .sidebar {
|
|
82
|
+
padding-top: 4rem;
|
|
83
|
+
}
|
|
84
|
+
body.assistant-sidebar-shell .sidebar-toggle {
|
|
85
|
+
align-items: center;
|
|
86
|
+
background: var(--assistant-space-cadet);
|
|
87
|
+
border: 1px solid rgba(255, 209, 102, 0.42);
|
|
88
|
+
border-radius: 999px;
|
|
89
|
+
bottom: auto;
|
|
90
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.24);
|
|
91
|
+
color: var(--assistant-naples-yellow);
|
|
92
|
+
display: flex;
|
|
93
|
+
height: 2.25rem;
|
|
94
|
+
justify-content: center;
|
|
95
|
+
left: 0.875rem;
|
|
96
|
+
padding: 0;
|
|
97
|
+
top: 0.875rem;
|
|
98
|
+
transition: background-color 0.2s ease, border-color 0.2s ease,
|
|
99
|
+
color 0.2s ease, transform 0.2s ease;
|
|
100
|
+
width: 2.25rem;
|
|
101
|
+
z-index: 60;
|
|
102
|
+
}
|
|
103
|
+
body.assistant-sidebar-shell .sidebar-toggle:hover {
|
|
104
|
+
background: var(--assistant-naples-yellow);
|
|
105
|
+
border-color: var(--assistant-naples-yellow);
|
|
106
|
+
color: var(--assistant-space-cadet);
|
|
107
|
+
transform: translateY(-1px);
|
|
108
|
+
}
|
|
109
|
+
body.assistant-sidebar-shell .sidebar-toggle span {
|
|
110
|
+
display: none;
|
|
111
|
+
}
|
|
112
|
+
body.assistant-sidebar-shell .sidebar-toggle::before {
|
|
113
|
+
content: "\00d7";
|
|
114
|
+
font-size: 1.75rem;
|
|
115
|
+
font-weight: 700;
|
|
116
|
+
line-height: 1;
|
|
117
|
+
transform: translateY(-0.08em);
|
|
118
|
+
}
|
|
119
|
+
body.assistant-sidebar-shell.close .sidebar-toggle {
|
|
120
|
+
background: var(--assistant-naples-yellow);
|
|
121
|
+
border-color: var(--assistant-naples-yellow);
|
|
122
|
+
color: var(--assistant-space-cadet);
|
|
123
|
+
}
|
|
124
|
+
body.assistant-sidebar-shell.close .sidebar-toggle:hover {
|
|
125
|
+
background: var(--assistant-space-cadet);
|
|
126
|
+
border-color: var(--assistant-space-cadet);
|
|
127
|
+
color: var(--assistant-naples-yellow);
|
|
128
|
+
}
|
|
129
|
+
body.assistant-sidebar-shell.close .sidebar-toggle::before {
|
|
130
|
+
content: "\2630";
|
|
131
|
+
font-size: 1.25rem;
|
|
132
|
+
transform: none;
|
|
133
|
+
}
|
|
134
|
+
</style>
|
|
135
|
+
</head>
|
|
136
|
+
<body class="assistant-sidebar-shell">
|
|
137
|
+
<div id="app">Loading documentation…</div>
|
|
138
|
+
|
|
139
|
+
<script>
|
|
140
|
+
// Re-applies the matching Prism theme stylesheet whenever the
|
|
141
|
+
// docsify-darklight-theme toggle is clicked. Without this, switching to
|
|
142
|
+
// dark mode keeps the light Prism palette on code blocks.
|
|
143
|
+
const prismThemeSwitcher = function (hook) {
|
|
144
|
+
hook.doneEach(function () {
|
|
145
|
+
const toggle = document.querySelector('.docsify-darklight-theme');
|
|
146
|
+
if (!toggle || toggle.dataset.bound === '1') return;
|
|
147
|
+
toggle.dataset.bound = '1';
|
|
148
|
+
toggle.addEventListener('click', function () {
|
|
149
|
+
const link = document.getElementById('prism-theme');
|
|
150
|
+
if (!link) return;
|
|
151
|
+
const dark = link.href.includes('prism-material-dark');
|
|
152
|
+
link.href = dark
|
|
153
|
+
? '//cdn.jsdelivr.net/npm/prism-themes/themes/prism-material-light.min.css'
|
|
154
|
+
: '//cdn.jsdelivr.net/npm/prism-themes/themes/prism-material-dark.min.css';
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// Page footer rendered by docsify after each markdown body.
|
|
160
|
+
const pageFooter = function (hook) {
|
|
161
|
+
const footer = [
|
|
162
|
+
'<hr/>',
|
|
163
|
+
'<footer style="text-align:center; opacity:.7; font-size:.9em; padding:1rem 0;">',
|
|
164
|
+
' <span>Assistant · MIT-licensed · ',
|
|
165
|
+
' <a href="https://github.com/ramongr/assistant" target="_blank" rel="noopener">GitHub</a> · ',
|
|
166
|
+
' <a href="https://rubygems.org/gems/assistant" target="_blank" rel="noopener">RubyGems</a>',
|
|
167
|
+
' </span>',
|
|
168
|
+
'</footer>'
|
|
169
|
+
].join('');
|
|
170
|
+
hook.afterEach(function (html) { return html + footer; });
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
window.$docsify = {
|
|
174
|
+
name: '<span class="assistant-app-name" aria-label="Assistant">a</span>',
|
|
175
|
+
nameLink: '#/',
|
|
176
|
+
repo: 'ramongr/assistant',
|
|
177
|
+
basePath: '/assistant/',
|
|
178
|
+
homepage: 'index.md',
|
|
179
|
+
coverpage: false,
|
|
180
|
+
loadSidebar: true,
|
|
181
|
+
alias: {
|
|
182
|
+
'/.*/_sidebar.md': '/_sidebar.md'
|
|
183
|
+
},
|
|
184
|
+
loadNavbar: false,
|
|
185
|
+
auto2top: true,
|
|
186
|
+
subMaxLevel: 3,
|
|
187
|
+
maxLevel: 4,
|
|
188
|
+
relativePath: true,
|
|
189
|
+
notFoundPage: true,
|
|
190
|
+
executeScript: true,
|
|
191
|
+
themeColor: '#2f4f5a',
|
|
192
|
+
// Hash-routed by default (`/assistant/#/getting-started`). A
|
|
193
|
+
// history-mode experiment was tried but every clean URL like
|
|
194
|
+
// `/assistant/getting-started` returned HTTP 404 on GitHub Pages — Pages
|
|
195
|
+
// serves `docs/404.html` as the SPA shell for unknown paths, but always
|
|
196
|
+
// with a 404 status code, which breaks link previews, link checkers,
|
|
197
|
+
// and search crawlers. Hash routing keeps every navigable URL on a
|
|
198
|
+
// genuine 200 response.
|
|
199
|
+
search: {
|
|
200
|
+
maxAge: 86400000,
|
|
201
|
+
paths: 'auto',
|
|
202
|
+
placeholder: 'Search the docs …',
|
|
203
|
+
noData: 'No results.',
|
|
204
|
+
depth: 3,
|
|
205
|
+
hideOtherSidebarContent: false
|
|
206
|
+
},
|
|
207
|
+
copyCode: {
|
|
208
|
+
buttonText: 'Copy',
|
|
209
|
+
errorText: 'Error',
|
|
210
|
+
successText: 'Copied'
|
|
211
|
+
},
|
|
212
|
+
mermaidConfig: {
|
|
213
|
+
querySelector: '.mermaid'
|
|
214
|
+
},
|
|
215
|
+
darklightTheme: {
|
|
216
|
+
siteFont: 'PT Sans, system-ui, sans-serif',
|
|
217
|
+
defaultTheme: 'light',
|
|
218
|
+
codeFontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
|
219
|
+
bodyFontSize: '17px',
|
|
220
|
+
dark: {
|
|
221
|
+
accent: '#ffd166',
|
|
222
|
+
toggleBackground: '#22223b',
|
|
223
|
+
background: '#22223b',
|
|
224
|
+
textColor: '#f4f2f3',
|
|
225
|
+
codeTextColor: '#f4f2f3',
|
|
226
|
+
codeBackgroundColor: '#2c2c4a',
|
|
227
|
+
borderColor: '#3a3a5c',
|
|
228
|
+
blockQuoteColor: '#a07178',
|
|
229
|
+
highlightColor: '#4d7c8a',
|
|
230
|
+
sidebarSublink: '#a07178',
|
|
231
|
+
codeTypeColor: '#4d7c8a',
|
|
232
|
+
coverBackground: 'linear-gradient(to left bottom, #22223b 0%, #ffd166 100%)'
|
|
233
|
+
},
|
|
234
|
+
light: {
|
|
235
|
+
accent: '#2f4f5a',
|
|
236
|
+
toggleBackground: '#f4f2f3',
|
|
237
|
+
background: '#f4f2f3',
|
|
238
|
+
textColor: '#22223b',
|
|
239
|
+
codeTextColor: '#22223b',
|
|
240
|
+
codeBackgroundColor: '#ffffff',
|
|
241
|
+
borderColor: 'rgba(34,34,59,0.12)',
|
|
242
|
+
blockQuoteColor: '#a07178',
|
|
243
|
+
highlightColor: '#2f4f5a',
|
|
244
|
+
sidebarSublink: '#22223b',
|
|
245
|
+
codeTypeColor: '#2f4f5a',
|
|
246
|
+
coverBackground: 'linear-gradient(to left bottom, #f4f2f3 0%, #a07178 100%)'
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
plugins: [
|
|
250
|
+
prismThemeSwitcher,
|
|
251
|
+
pageFooter,
|
|
252
|
+
function (hook, vm) {
|
|
253
|
+
if (typeof EditOnGithubPlugin !== 'undefined') {
|
|
254
|
+
EditOnGithubPlugin.create(
|
|
255
|
+
'https://github.com/ramongr/assistant/blob/main/docs/',
|
|
256
|
+
null,
|
|
257
|
+
'Edit this page on GitHub'
|
|
258
|
+
)(hook, vm);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
};
|
|
263
|
+
</script>
|
|
264
|
+
|
|
265
|
+
<!-- Docsify core -->
|
|
266
|
+
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
|
267
|
+
|
|
268
|
+
<!-- Dark/light theme toggle -->
|
|
269
|
+
<script src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"
|
|
270
|
+
type="text/javascript"></script>
|
|
271
|
+
|
|
272
|
+
<!-- Plugins -->
|
|
273
|
+
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
|
274
|
+
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code@2"></script>
|
|
275
|
+
<script src="//cdn.jsdelivr.net/npm/docsify-edit-on-github"></script>
|
|
276
|
+
<script src="//cdn.jsdelivr.net/npm/docsify-tabs@1"></script>
|
|
277
|
+
|
|
278
|
+
<!-- Mermaid (diagrams) — load the UMD bundle from /dist/, not the
|
|
279
|
+
package root, which jsDelivr resolves to an ES-module file that the
|
|
280
|
+
browser refuses to execute as a classic script. -->
|
|
281
|
+
<script src="//cdn.jsdelivr.net/npm/mermaid@10.9.1/dist/mermaid.min.js"></script>
|
|
282
|
+
<script src="//cdn.jsdelivr.net/npm/docsify-mermaid@2.0.1/dist/docsify-mermaid.js"></script>
|
|
283
|
+
<script>mermaid.initialize({ startOnLoad: false, theme: 'default' });</script>
|
|
284
|
+
|
|
285
|
+
<!-- Prism syntax highlighting: Ruby + autoloader for everything else -->
|
|
286
|
+
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-ruby.min.js"></script>
|
|
287
|
+
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-bash.min.js"></script>
|
|
288
|
+
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-yaml.min.js"></script>
|
|
289
|
+
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-json.min.js"></script>
|
|
290
|
+
<script src="//cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js"></script>
|
|
291
|
+
</body>
|
|
292
|
+
</html>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Assistant">
|
|
3
|
+
<title>Assistant</title>
|
|
4
|
+
<rect width="64" height="64" rx="12" fill="#22223b"/>
|
|
5
|
+
<text x="32" y="44" text-anchor="middle"
|
|
6
|
+
font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
|
|
7
|
+
font-size="38" font-weight="700" fill="#ffd166">A</text>
|
|
8
|
+
</svg>
|
|
Binary file
|
data/docs/_sidebar.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<!-- Sidebar (Docsify reads this on every route).
|
|
2
|
+
Order: Home, Getting started, Guides, API reference, Deprecations,
|
|
3
|
+
Examples. -->
|
|
4
|
+
|
|
5
|
+
- [Home](/)
|
|
6
|
+
- [Getting started](/getting-started.md)
|
|
7
|
+
|
|
8
|
+
- [Guides](/guides/)
|
|
9
|
+
- [Inputs](/guides/inputs.md)
|
|
10
|
+
- [Validation](/guides/validation.md)
|
|
11
|
+
- [Logging and results](/guides/logging-and-results.md)
|
|
12
|
+
- [Composing services](/guides/composing-services.md)
|
|
13
|
+
- [RBS and types](/guides/rbs-and-types.md)
|
|
14
|
+
|
|
15
|
+
- [API reference](/api-reference.md)
|
|
16
|
+
- [Deprecations](/deprecations.md)
|
|
17
|
+
|
|
18
|
+
- [Examples](/examples/)
|
|
19
|
+
- [Rails service](/examples/rails-service.md)
|
|
20
|
+
- [CLI handler](/examples/cli-handler.md)
|
|
21
|
+
- [Sidekiq worker](/examples/sidekiq-worker.md)
|
|
22
|
+
- [Composing services](/examples/composing-services.md)
|
|
23
|
+
- [Execute callbacks](/examples/execute-callbacks.md)
|
|
24
|
+
- [Instrumentation notifier](/examples/instrumentation-notifier.md)
|
|
25
|
+
- [RBS generator](/examples/rbs-generator.md)
|
|
26
|
+
|
|
27
|
+
- **Links**
|
|
28
|
+
- [GitHub](https://github.com/ramongr/assistant)
|
|
29
|
+
- [RubyGems](https://rubygems.org/gems/assistant)
|
data/docs/api-reference.md
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: API reference
|
|
3
|
-
nav_order: 3
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
<!-- markdownlint-disable MD013 MD024 -->
|
|
7
2
|
# API reference
|
|
8
3
|
|
|
@@ -25,13 +20,13 @@ without a major version bump.
|
|
|
25
20
|
## Table of contents
|
|
26
21
|
|
|
27
22
|
- [`Assistant` module](#assistant-module)
|
|
28
|
-
- [`Assistant::Service`](#
|
|
23
|
+
- [`Assistant::Service`](#assistant-service)
|
|
29
24
|
- [Class methods](#class-methods)
|
|
30
25
|
- [Instance methods](#instance-methods)
|
|
31
26
|
- [Generated per-input methods](#generated-per-input-methods)
|
|
32
27
|
- [Result shape](#result-shape)
|
|
33
|
-
- [`Assistant::LogItem`](#
|
|
34
|
-
- [`Assistant::LogList`](#
|
|
28
|
+
- [`Assistant::LogItem`](#assistant-logitem)
|
|
29
|
+
- [`Assistant::LogList`](#assistant-loglist)
|
|
35
30
|
- [Execute callbacks](#execute-callbacks)
|
|
36
31
|
- [Service composition](#service-composition)
|
|
37
32
|
- [Instrumentation notifier](#instrumentation-notifier)
|
|
@@ -72,7 +67,7 @@ you.
|
|
|
72
67
|
| `Service.around_execute(&block)` | Frozen | Hook block is `instance_exec`'d with an inner block argument that yields to the next layer. |
|
|
73
68
|
| `Service.input_snapshot_class -> Class` | Frozen *(new in 1.0)* | Memoised `Data.define(*declared_input_names)` class used by `#input_snapshot`. |
|
|
74
69
|
|
|
75
|
-
> **
|
|
70
|
+
> **Keyword-only DSL.** Every other public DSL helper (e.g.
|
|
76
71
|
> `merge_logs`, all `InputBuilder` internals) takes keyword arguments
|
|
77
72
|
> only. The two exemptions above — `Service.input` and
|
|
78
73
|
> `Service.inputs` — keep their leading positional `name` / `names`
|
|
@@ -130,11 +125,27 @@ hash shapes:
|
|
|
130
125
|
The status enum is exhaustively `:ok`, `:with_warnings`, `:with_errors`.
|
|
131
126
|
No new status values may be added in 1.x without a deprecation cycle.
|
|
132
127
|
|
|
128
|
+
The status flag is derived purely from what the service logged during
|
|
129
|
+
`#run`:
|
|
130
|
+
|
|
131
|
+
```mermaid
|
|
132
|
+
flowchart TD
|
|
133
|
+
Start([Service#run]) --> Errors{Any LogItem with<br/>level: :error?}
|
|
134
|
+
Errors -- Yes --> Failed["{ result: nil,<br/>status: :with_errors,<br/>errors: [...] }"]
|
|
135
|
+
Errors -- No --> Warnings{Any LogItem with<br/>level: :warning?}
|
|
136
|
+
Warnings -- Yes --> WithWarnings["{ result: ...,<br/>status: :with_warnings,<br/>warnings: [...] }"]
|
|
137
|
+
Warnings -- No --> Ok["{ result: ...,<br/>status: :ok,<br/>warnings: [] }"]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`#execute` is **skipped** entirely when any declarative or custom
|
|
141
|
+
`#validate` check has already logged an error — see the
|
|
142
|
+
[Validation guide](guides/validation.md) for the full lifecycle.
|
|
143
|
+
|
|
133
144
|
---
|
|
134
145
|
|
|
135
146
|
## `Assistant::LogItem`
|
|
136
147
|
|
|
137
|
-
> **Breaking change in 1.0
|
|
148
|
+
> **Breaking change in 1.0.** `LogItem.new` now raises
|
|
138
149
|
> `ArgumentError` when any required attribute is invalid. The
|
|
139
150
|
> `#valid?` family is **retained** for introspection, but in normal
|
|
140
151
|
> flows it always returns `true` after a successful `new`.
|
|
@@ -231,7 +242,7 @@ return values whose `class` is `equal?`. See
|
|
|
231
242
|
|
|
232
243
|
## `assistant-rbs` CLI
|
|
233
244
|
|
|
234
|
-
Bundled executable shipped at `exe/assistant-rbs
|
|
245
|
+
Bundled executable shipped at `exe/assistant-rbs`. Generates
|
|
235
246
|
per-class RBS signatures for `Assistant::Service` subclasses so Steep
|
|
236
247
|
can type-check user code.
|
|
237
248
|
|
data/docs/changelog.md
CHANGED
data/docs/deprecations.md
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Deprecations
|
|
3
|
-
nav_order: 4
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# Deprecations
|
|
7
2
|
|
|
8
3
|
This page tracks every public symbol that is **deprecated** in a 1.x release
|
|
@@ -16,14 +11,14 @@ The deprecation policy is one full minor cycle: anything marked here in
|
|
|
16
11
|
|
|
17
12
|
| Symbol | Replacement | Deprecated in | Removed in |
|
|
18
13
|
|---------------------------------------------------------|-------------------------------------------------------------------|---------------|------------|
|
|
19
|
-
| `Assistant::Service#valid_require_<name>?` | `Assistant::Service#valid_required_<name>?` | `1.0.0`
|
|
20
|
-
| `Assistant::Service#valid_require_conditional_<name>?` | `Assistant::Service#valid_required_conditional_<name>?` | `1.0.0`
|
|
14
|
+
| `Assistant::Service#valid_require_<name>?` | `Assistant::Service#valid_required_<name>?` | `1.0.0` | `2.0.0` |
|
|
15
|
+
| `Assistant::Service#valid_require_conditional_<name>?` | `Assistant::Service#valid_required_conditional_<name>?` | `1.0.0` | `2.0.0` |
|
|
21
16
|
|
|
22
17
|
---
|
|
23
18
|
|
|
24
19
|
## `valid_require_<name>?` → `valid_required_<name>?`
|
|
25
20
|
|
|
26
|
-
**Deprecated in**: `1.0.0
|
|
21
|
+
**Deprecated in**: `1.0.0`. **Removed in**: `2.0.0`.
|
|
27
22
|
|
|
28
23
|
### What changed
|
|
29
24
|
|
|
@@ -82,5 +77,5 @@ was decided in favour of Option B: the new names read better, match
|
|
|
82
77
|
standard English, and are easier to grep for. The old names live one
|
|
83
78
|
minor cycle to give downstream services an upgrade window.
|
|
84
79
|
|
|
85
|
-
See [`docs/v1/02-features.md`](https://github.com/ramongr/assistant/blob/main/docs/v1/02-features.md)
|
|
80
|
+
See [`docs/v1/02-features.md`](https://github.com/ramongr/assistant/blob/main/docs/v1/02-features.md) for the
|
|
86
81
|
implementation plan and acceptance criteria.
|
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
title: Examples
|
|
3
|
-
nav_order: 5
|
|
4
|
-
has_children: true
|
|
5
|
-
permalink: /examples/
|
|
6
|
-
---
|
|
1
|
+
# Examples <!-- {docsify-ignore-all} -->
|
|
7
2
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
> [P6–P12 of the GitHub Pages plan](https://github.com/ramongr/assistant/blob/main/docs/v1/08-github-pages.md#p6p12-examples-one-pr-per-example)
|
|
14
|
-
> for the per-example schedule.
|
|
3
|
+
Each entry below shows a small, real-world wiring pattern with a
|
|
4
|
+
callout-sized code snippet. Runnable scripts live under
|
|
5
|
+
[`examples/<slug>/`](https://github.com/ramongr/assistant/tree/main/examples)
|
|
6
|
+
and their regression tests under
|
|
7
|
+
[`test/examples/`](https://github.com/ramongr/assistant/tree/main/test/examples).
|
|
15
8
|
|
|
16
9
|
| Example | Demonstrates |
|
|
17
10
|
| --- | --- |
|
|
@@ -21,9 +14,6 @@ permalink: /examples/
|
|
|
21
14
|
| [Composing services](composing-services.md) | Outer service uses `call_service` to chain two inner services; log timeline merging. |
|
|
22
15
|
| [Execute callbacks](execute-callbacks.md) | `before_execute` audit logger; `around_execute` timing wrapper; failure cases. |
|
|
23
16
|
| [Instrumentation notifier](instrumentation-notifier.md) | `Assistant.notifier=` wired to a fake `ActiveSupport::Notifications`-shaped sink. |
|
|
24
|
-
| [RBS generator](rbs-generator.md) | Service definition → `bin/assistant-rbs --output sig` → Steep proving
|
|
17
|
+
| [RBS generator](rbs-generator.md) | Service definition → `bin/assistant-rbs --output sig` → Steep proving generated input reader types. |
|
|
25
18
|
|
|
26
19
|
Each example is intentionally small enough to be read in one sitting.
|
|
27
|
-
Source for every script lives under
|
|
28
|
-
[`examples/`](https://github.com/ramongr/assistant/tree/main/examples)
|
|
29
|
-
in the repository.
|
|
@@ -1,17 +1,43 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: CLI handler
|
|
3
|
-
parent: Examples
|
|
4
|
-
nav_order: 2
|
|
5
|
-
---
|
|
6
|
-
|
|
7
1
|
# CLI handler
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
An `OptionParser`-driven script that runs a service and derives the
|
|
4
|
+
process exit code from `#status`:
|
|
5
|
+
|
|
6
|
+
```ruby
|
|
7
|
+
#!/usr/bin/env ruby
|
|
8
|
+
# frozen_string_literal: true
|
|
9
|
+
|
|
10
|
+
require 'optparse'
|
|
11
|
+
require 'assistant'
|
|
12
|
+
require_relative 'create_user'
|
|
13
|
+
|
|
14
|
+
options = {}
|
|
15
|
+
OptionParser.new do |opts|
|
|
16
|
+
opts.banner = 'Usage: create_user --email EMAIL --name NAME'
|
|
17
|
+
opts.on('-eEMAIL', '--email=EMAIL') { |v| options[:email] = v }
|
|
18
|
+
opts.on('-nNAME', '--name=NAME') { |v| options[:name] = v }
|
|
19
|
+
end.parse!
|
|
20
|
+
|
|
21
|
+
case CreateUser.run(**options)
|
|
22
|
+
in { result:, status: :ok }
|
|
23
|
+
warn "ok: #{result.inspect}"
|
|
24
|
+
exit 0
|
|
25
|
+
in { result:, status: :with_warnings, warnings: }
|
|
26
|
+
warn "ok (with warnings): #{result.inspect}"
|
|
27
|
+
warnings.each { |w| warn " warning: #{w.message}" }
|
|
28
|
+
exit 0
|
|
29
|
+
in { errors:, status: :with_errors }
|
|
30
|
+
errors.each { |e| warn "error: #{e.message}" }
|
|
31
|
+
exit 1
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Notes:
|
|
12
36
|
|
|
13
|
-
|
|
37
|
+
* `:ok` and `:with_warnings` both exit 0 — warnings are still a
|
|
38
|
+
successful run. Only `:with_errors` exits 1.
|
|
39
|
+
* Print to `$stderr` (`warn`) so the script can be piped without log
|
|
40
|
+
noise leaking into stdout.
|
|
14
41
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
prose stays in lockstep with the code.
|
|
42
|
+
> Source: [`examples/cli_handler/`](https://github.com/ramongr/assistant/tree/main/examples/cli_handler) ·
|
|
43
|
+
> Test: [`test/examples/cli_handler_example_test.rb`](https://github.com/ramongr/assistant/blob/main/test/examples/cli_handler_example_test.rb)
|
|
@@ -1,17 +1,49 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Composing services
|
|
3
|
-
parent: Examples
|
|
4
|
-
nav_order: 4
|
|
5
|
-
---
|
|
6
|
-
|
|
7
1
|
# Composing services
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
`#call_service` runs a sibling service from inside `#execute` and
|
|
4
|
+
merges its log timeline into the outer service automatically — see the
|
|
5
|
+
[Composing services guide](../guides/composing-services.md) for the
|
|
6
|
+
full contract.
|
|
7
|
+
|
|
8
|
+
A two-step signup, where `CreateUser` looks up or creates the user
|
|
9
|
+
and `SendWelcomeEmail` queues the welcome:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
class SignUpUser < Assistant::Service
|
|
13
|
+
input :email, type: String, required: true
|
|
14
|
+
input :name, type: String, required: true
|
|
15
|
+
|
|
16
|
+
def execute
|
|
17
|
+
user = call_service(CreateUser, email:, name:)
|
|
18
|
+
return if user.failure?
|
|
19
|
+
|
|
20
|
+
call_service(SendWelcomeEmail, user_id: user.result.id)
|
|
21
|
+
|
|
22
|
+
user.result
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
What the merged timeline looks like:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
SignUpUser.run(email: 'a@b.com', name: 'Alice')
|
|
31
|
+
# => { result: <User>, status: :with_warnings,
|
|
32
|
+
# warnings: [
|
|
33
|
+
# #<LogItem :email_normalized 'normalized to a@b.com'>, # from CreateUser
|
|
34
|
+
# #<LogItem :throttled 'welcome queued for 1s delay'> # from SendWelcomeEmail
|
|
35
|
+
# ] }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Notes:
|
|
12
39
|
|
|
13
|
-
|
|
40
|
+
* If `CreateUser` returns `:with_errors`, `user.failure?` is true and
|
|
41
|
+
the early-return propagates: the outer status downgrades to
|
|
42
|
+
`:with_errors` automatically because the inner errors were merged
|
|
43
|
+
via `merge_logs`.
|
|
44
|
+
* Use `call_service` (not `CreateUser.run(...)`) so the inner service's
|
|
45
|
+
logs join the outer timeline. Calling `.run` directly silently
|
|
46
|
+
discards them.
|
|
14
47
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
prose stays in lockstep with the code.
|
|
48
|
+
> Source: [`examples/composing_services/`](https://github.com/ramongr/assistant/tree/main/examples/composing_services) ·
|
|
49
|
+
> Test: [`test/examples/composing_services_example_test.rb`](https://github.com/ramongr/assistant/blob/main/test/examples/composing_services_example_test.rb)
|