gum 0.3.0-aarch64-linux
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +460 -0
- data/exe/aarch64-linux/gum +0 -0
- data/exe/gum +11 -0
- data/gum.gemspec +35 -0
- data/lib/gum/command.rb +159 -0
- data/lib/gum/commands/choose.rb +95 -0
- data/lib/gum/commands/confirm.rb +57 -0
- data/lib/gum/commands/file.rb +84 -0
- data/lib/gum/commands/filter.rb +119 -0
- data/lib/gum/commands/format.rb +74 -0
- data/lib/gum/commands/input.rb +68 -0
- data/lib/gum/commands/join.rb +41 -0
- data/lib/gum/commands/log.rb +98 -0
- data/lib/gum/commands/pager.rb +55 -0
- data/lib/gum/commands/spin.rb +167 -0
- data/lib/gum/commands/style.rb +93 -0
- data/lib/gum/commands/table.rb +93 -0
- data/lib/gum/commands/write.rb +84 -0
- data/lib/gum/upstream.rb +17 -0
- data/lib/gum/version.rb +7 -0
- data/lib/gum.rb +174 -0
- data/sig/gum/command.rbs +23 -0
- data/sig/gum/commands/choose.rbs +42 -0
- data/sig/gum/commands/confirm.rbs +30 -0
- data/sig/gum/commands/file.rbs +38 -0
- data/sig/gum/commands/filter.rbs +48 -0
- data/sig/gum/commands/format.rbs +47 -0
- data/sig/gum/commands/input.rbs +32 -0
- data/sig/gum/commands/join.rbs +24 -0
- data/sig/gum/commands/log.rbs +56 -0
- data/sig/gum/commands/pager.rbs +28 -0
- data/sig/gum/commands/spin.rbs +55 -0
- data/sig/gum/commands/style.rbs +44 -0
- data/sig/gum/commands/table.rbs +35 -0
- data/sig/gum/commands/write.rbs +35 -0
- data/sig/gum/upstream.rbs +9 -0
- data/sig/gum/version.rbs +5 -0
- data/sig/gum.rbs +79 -0
- metadata +84 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e1c0d54e51e7ef16f4908c751206dc343faf4962231740715144b980d9c6856a
|
|
4
|
+
data.tar.gz: 1c4c8fa4b186e7ca4011c3058416b3ec8823b7d49aa61d991a254c2daf14424d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c1a8f4d70e804bd9ebbf9c3129e3883265d9cda77930e9940e2e237fdd49a13c5d9b758d7031a3c9f4844cdb77dbe7aa468d37bb7678e3bcbb6b0ac4f8aad98e
|
|
7
|
+
data.tar.gz: 4aa981718e0713e632fe387c1e3354dbab196e88aadc41efe736363750628ab2b0a5cb7df20b85902591deec520c1821025f3cd72b73f1708b8a9170e220d289
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Marco Roth
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
# Gum for Ruby
|
|
2
|
+
|
|
3
|
+
<p>
|
|
4
|
+
<a href="https://github.com/marcoroth/gum-ruby" target="_blank"><img src="assets/logo.png" alt="Gum Image" width="450" /></a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
Ruby wrapper for [Charm's Gum](https://github.com/charmbracelet/gum). A tool for glamorous scripts.
|
|
8
|
+
|
|
9
|
+
This gem bundles the `gum` binary and provides an idiomatic Ruby API for all gum commands.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
**Add to your Gemfile:**
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bundle add gum
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Or install directly:**
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gem install gum
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
require "gum"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Input
|
|
32
|
+
|
|
33
|
+
**Prompt for single-line input:**
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
name = Gum.input(placeholder: "Enter your name")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Password input (masked):**
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
password = Gum.input(password: true)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**With default value and custom prompt:**
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
email = Gum.input(value: "user@", prompt: "> ", placeholder: "email")
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**With character limit:**
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
code = Gum.input(placeholder: "Enter code", char_limit: 6)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Write
|
|
58
|
+
|
|
59
|
+
**Prompt for multi-line text input (Ctrl+D to submit):**
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
description = Gum.write(placeholder: "Enter description...")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**With dimensions:**
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
notes = Gum.write(width: 80, height: 10, header: "Notes")
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**With line numbers:**
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
content = Gum.write(show_line_numbers: true)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Choose
|
|
78
|
+
|
|
79
|
+
**Single selection (array):**
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
color = Gum.choose(["red", "green", "blue"])
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Single selection (splat):**
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
color = Gum.choose("red", "green", "blue")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Multiple selection with limit:**
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
colors = Gum.choose(["red", "green", "blue"], limit: 2)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Unlimited selection:**
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
colors = Gum.choose(["red", "green", "blue"], no_limit: true)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**With header and custom height:**
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
choice = Gum.choose(options, header: "Pick one:", height: 10)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Pre-selected items:**
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
choice = Gum.choose(["a", "b", "c"], selected: ["b"])
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Filter
|
|
116
|
+
|
|
117
|
+
**Single selection (array):**
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
file = Gum.filter(Dir.glob("**/*.rb"))
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Single selection (splat):**
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
file = Gum.filter("file1.rb", "file2.rb", "file3.rb")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Multiple selection:**
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
files = Gum.filter(Dir.glob("*"), limit: 5)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Unlimited selection:**
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
files = Gum.filter(items, no_limit: true)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**With placeholder and height:**
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
selection = Gum.filter(items, placeholder: "Search...", height: 20)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Disable fuzzy matching for exact search:**
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
result = Gum.filter(items, fuzzy: false)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Confirm
|
|
154
|
+
|
|
155
|
+
**Ask for yes/no confirmation:**
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
if Gum.confirm("Delete file?")
|
|
159
|
+
File.delete(path)
|
|
160
|
+
end
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**With default value:**
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
proceed = Gum.confirm("Continue?", default: true)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Custom button labels:**
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
Gum.confirm("Save changes?", affirmative: "Save", negative: "Discard")
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### File
|
|
176
|
+
|
|
177
|
+
**Start from current directory:**
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
path = Gum.file
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Start from specific directory:**
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
path = Gum.file("~/Documents")
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Show hidden files:**
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
path = Gum.file(all: true)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Only show directories:**
|
|
196
|
+
|
|
197
|
+
```ruby
|
|
198
|
+
dir = Gum.file(directory_only: true)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Pager
|
|
202
|
+
|
|
203
|
+
**Scroll through content:**
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
Gum.pager(File.read("README.md"))
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**With line numbers:**
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
Gum.pager(content, show_line_numbers: true)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Soft wrap long lines:**
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
Gum.pager(content, soft_wrap: true)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Spin
|
|
222
|
+
|
|
223
|
+
**With shell command:**
|
|
224
|
+
|
|
225
|
+
```ruby
|
|
226
|
+
Gum.spin("Installing...", command: "npm install")
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**With Ruby block:**
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
result = Gum.spin("Processing...") do
|
|
233
|
+
expensive_computation
|
|
234
|
+
end
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Custom spinner type:**
|
|
238
|
+
|
|
239
|
+
```ruby
|
|
240
|
+
Gum.spin("Loading...", spinner: :dot, command: "sleep 5")
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Available spinner types: `:line`, `:dot`, `:minidot`, `:jump`, `:pulse`, `:points`, `:globe`, `:moon`, `:monkey`, `:meter`, `:hamburger`
|
|
244
|
+
|
|
245
|
+
### Style
|
|
246
|
+
|
|
247
|
+
**Basic styling:**
|
|
248
|
+
|
|
249
|
+
```ruby
|
|
250
|
+
styled = Gum.style("Hello", foreground: "212", bold: true)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**With border:**
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
box = Gum.style("Content", border: :double, padding: "1 2")
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Multiple lines with alignment:**
|
|
260
|
+
|
|
261
|
+
```ruby
|
|
262
|
+
styled = Gum.style("Line 1", "Line 2", align: :center, width: 50)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Full styling example:**
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
styled = Gum.style(
|
|
269
|
+
"Bubble Gum",
|
|
270
|
+
foreground: "212",
|
|
271
|
+
border: :double,
|
|
272
|
+
border_foreground: "212",
|
|
273
|
+
align: :center,
|
|
274
|
+
width: 50,
|
|
275
|
+
margin: "1 2",
|
|
276
|
+
padding: "2 4"
|
|
277
|
+
)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Available border types: `:none`, `:hidden`, `:rounded`, `:double`, `:thick`, `:normal`
|
|
281
|
+
|
|
282
|
+
### Join
|
|
283
|
+
|
|
284
|
+
Join text blocks horizontally or vertically:
|
|
285
|
+
|
|
286
|
+
```ruby
|
|
287
|
+
box1 = Gum.style("A", border: :rounded, padding: "1 3")
|
|
288
|
+
box2 = Gum.style("B", border: :rounded, padding: "1 3")
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Horizontal join (default):**
|
|
292
|
+
|
|
293
|
+
```ruby
|
|
294
|
+
combined = Gum.join(box1, box2)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Vertical join:**
|
|
298
|
+
|
|
299
|
+
```ruby
|
|
300
|
+
stacked = Gum.join(box1, box2, vertical: true)
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**With alignment:**
|
|
304
|
+
|
|
305
|
+
```ruby
|
|
306
|
+
aligned = Gum.join(box1, box2, vertical: true, align: :center)
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Format
|
|
310
|
+
|
|
311
|
+
**Markdown:**
|
|
312
|
+
|
|
313
|
+
```ruby
|
|
314
|
+
Gum.format("# Hello\n- Item 1\n- Item 2", type: :markdown)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Template (see Termenv docs for helpers):**
|
|
318
|
+
|
|
319
|
+
```ruby
|
|
320
|
+
Gum.format('{{ Bold "Hello" }} {{ Color "99" "0" " World " }}', type: :template)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Emoji:**
|
|
324
|
+
|
|
325
|
+
```ruby
|
|
326
|
+
Gum.format("I :heart: Ruby :gem:", type: :emoji)
|
|
327
|
+
# => "I ❤️ Ruby 💎"
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Shorthand methods:**
|
|
331
|
+
|
|
332
|
+
```ruby
|
|
333
|
+
Gum::Format.markdown("# Hello")
|
|
334
|
+
Gum::Format.emoji("I :heart: Ruby")
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Table
|
|
338
|
+
|
|
339
|
+
**From array of arrays:**
|
|
340
|
+
|
|
341
|
+
```ruby
|
|
342
|
+
data = [["Alice", "30"], ["Bob", "25"]]
|
|
343
|
+
selection = Gum.table(data, columns: %w[Name Age])
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Just print (no selection):**
|
|
347
|
+
|
|
348
|
+
```ruby
|
|
349
|
+
Gum.table(data, columns: %w[Name Age], print: true)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**From CSV string:**
|
|
353
|
+
|
|
354
|
+
```ruby
|
|
355
|
+
Gum.table(File.read("data.csv"))
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**With custom border:**
|
|
359
|
+
|
|
360
|
+
```ruby
|
|
361
|
+
Gum.table(data, columns: %w[Name Age], border: :rounded)
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Log
|
|
365
|
+
|
|
366
|
+
**Basic logging:**
|
|
367
|
+
|
|
368
|
+
```ruby
|
|
369
|
+
Gum.log("Application started", level: :info)
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**Structured logging with key-value pairs:**
|
|
373
|
+
|
|
374
|
+
```ruby
|
|
375
|
+
Gum.log("User created", level: :info, user_id: 123, email: "user@example.com")
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**With timestamp:**
|
|
379
|
+
|
|
380
|
+
```ruby
|
|
381
|
+
Gum.log("Error occurred", level: :error, time: :rfc822)
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Shorthand methods:**
|
|
385
|
+
|
|
386
|
+
```ruby
|
|
387
|
+
Gum::Log.debug("Debug message")
|
|
388
|
+
Gum::Log.info("Info message")
|
|
389
|
+
Gum::Log.warn("Warning message")
|
|
390
|
+
Gum::Log.error("Error message", code: 500)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Available log levels: `:debug`, `:info`, `:warn`, `:error`, `:fatal`
|
|
394
|
+
|
|
395
|
+
## Styling Options
|
|
396
|
+
|
|
397
|
+
Most commands support styling options as hashes. Common style properties include:
|
|
398
|
+
|
|
399
|
+
```ruby
|
|
400
|
+
{
|
|
401
|
+
foreground: "212", # ANSI color code, hex (#ff0000), or name
|
|
402
|
+
background: "0",
|
|
403
|
+
bold: true,
|
|
404
|
+
italic: true,
|
|
405
|
+
underline: true,
|
|
406
|
+
strikethrough: true,
|
|
407
|
+
faint: true,
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Example with input styling:**
|
|
412
|
+
|
|
413
|
+
```ruby
|
|
414
|
+
Gum.input(
|
|
415
|
+
placeholder: "Enter name",
|
|
416
|
+
cursor: { foreground: "#FF0" },
|
|
417
|
+
prompt_style: { foreground: "#0FF" }
|
|
418
|
+
)
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Environment Variables
|
|
422
|
+
|
|
423
|
+
- `GUM_INSTALL_DIR` - Override the path to the gum binary
|
|
424
|
+
|
|
425
|
+
## Raw Command Execution
|
|
426
|
+
|
|
427
|
+
For commands not covered by the Ruby API, you can execute gum directly:
|
|
428
|
+
|
|
429
|
+
```ruby
|
|
430
|
+
Gum.execute("choose", "a", "b", "c", "--limit", "2")
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## Development
|
|
434
|
+
|
|
435
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
436
|
+
|
|
437
|
+
### Building Platform Gems
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
rake gem:ruby # Build pure Ruby gem (no binary)
|
|
441
|
+
rake gem:arm64-darwin # Build macOS ARM64 gem
|
|
442
|
+
rake gem:x86_64-darwin # Build macOS Intel gem
|
|
443
|
+
rake gem:arm64-linux # Build Linux ARM64 gem
|
|
444
|
+
rake gem:x86_64-linux # Build Linux x86_64 gem
|
|
445
|
+
rake package # Build all platform gems
|
|
446
|
+
rake download # Download all binaries
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## Contributing
|
|
450
|
+
|
|
451
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/marcoroth/gum-ruby.
|
|
452
|
+
|
|
453
|
+
## License
|
|
454
|
+
|
|
455
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
456
|
+
|
|
457
|
+
## Acknowledgements
|
|
458
|
+
|
|
459
|
+
- [Charm](https://charm.sh/) for creating the amazing [gum](https://github.com/charmbracelet/gum) tool
|
|
460
|
+
- Inspired by [`litestream-ruby`](https://github.com/fractaledmind/litestream-ruby) for the native binary packaging approach
|
|
Binary file
|
data/exe/gum
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Because rubygems shims assume a gem's executables are Ruby scripts,
|
|
5
|
+
# we need this wrapper to find and exec the native gum binary.
|
|
6
|
+
|
|
7
|
+
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
|
8
|
+
|
|
9
|
+
require "gum"
|
|
10
|
+
|
|
11
|
+
exec(Gum.executable, *ARGV)
|
data/gum.gemspec
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/gum/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "gum"
|
|
7
|
+
spec.version = Gum::VERSION
|
|
8
|
+
spec.authors = ["Marco Roth"]
|
|
9
|
+
spec.email = ["marco.roth@intergga.ch"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Ruby wrapper for Charm's gum CLI tool."
|
|
12
|
+
spec.description = "Integrate Charm's gum with the RubyGems infrastructure."
|
|
13
|
+
spec.homepage = "https://github.com/marcoroth/gum-ruby"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 3.2.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata = {
|
|
18
|
+
"homepage_uri" => spec.homepage,
|
|
19
|
+
"source_code_uri" => "https://github.com/marcoroth/gum-ruby",
|
|
20
|
+
"changelog_uri" => "https://github.com/marcoroth/gum-ruby/releases",
|
|
21
|
+
"rubygems_mfa_required" => "true",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
spec.files = Dir[
|
|
25
|
+
"gum.gemspec",
|
|
26
|
+
"lib/**/*",
|
|
27
|
+
"sig/**/*",
|
|
28
|
+
"LICENSE.txt",
|
|
29
|
+
"README.md"
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
spec.bindir = "exe"
|
|
33
|
+
spec.executables = ["gum"]
|
|
34
|
+
spec.require_paths = ["lib"]
|
|
35
|
+
end
|
data/lib/gum/command.rb
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
5
|
+
require "English"
|
|
6
|
+
require "open3"
|
|
7
|
+
|
|
8
|
+
module Gum
|
|
9
|
+
module Command
|
|
10
|
+
def self.run(*, input: nil, interactive: true)
|
|
11
|
+
if input && interactive
|
|
12
|
+
run_interactive_with_input(*, input: input)
|
|
13
|
+
elsif input
|
|
14
|
+
run_non_interactive(*, input: input)
|
|
15
|
+
else
|
|
16
|
+
run_interactive(*)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.run_non_interactive(*args, input:)
|
|
21
|
+
stdout, stderr, status = Open3.capture3(Gum.executable, *args.map(&:to_s), stdin_data: input)
|
|
22
|
+
|
|
23
|
+
unless status.success?
|
|
24
|
+
return nil if status.exitstatus == 130 # User cancelled (Ctrl+C)
|
|
25
|
+
|
|
26
|
+
raise Error, "gum #{args.first} failed: #{stderr}" unless stderr.empty?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
stdout.chomp
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.run_interactive(*args)
|
|
33
|
+
tty = File.open("/dev/tty", "r+")
|
|
34
|
+
|
|
35
|
+
stdout, wait_thread = Open3.pipeline_r(
|
|
36
|
+
[Gum.executable, *args.map(&:to_s)],
|
|
37
|
+
in: tty,
|
|
38
|
+
err: tty
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
output = stdout.read.chomp
|
|
42
|
+
stdout.close
|
|
43
|
+
tty.close
|
|
44
|
+
|
|
45
|
+
status = wait_thread.last.value
|
|
46
|
+
return nil if status.exitstatus == 130 # User cancelled
|
|
47
|
+
return nil unless status.success?
|
|
48
|
+
|
|
49
|
+
output
|
|
50
|
+
rescue Errno::ENOENT, Errno::ENXIO, Errno::EIO
|
|
51
|
+
stdout, stderr, status = Open3.capture3(Gum.executable, *args.map(&:to_s))
|
|
52
|
+
|
|
53
|
+
unless status.success?
|
|
54
|
+
return nil if status.exitstatus == 130
|
|
55
|
+
raise Error, "gum #{args.first} failed: #{stderr}" unless stderr.empty?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
stdout.chomp
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.run_interactive_with_input(*args, input:)
|
|
62
|
+
tty = File.open("/dev/tty", "r+")
|
|
63
|
+
stdin_read, stdin_write = IO.pipe
|
|
64
|
+
stdout_read, stdout_write = IO.pipe
|
|
65
|
+
|
|
66
|
+
pid = Process.spawn(
|
|
67
|
+
Gum.executable, *args.map(&:to_s),
|
|
68
|
+
in: stdin_read,
|
|
69
|
+
out: stdout_write,
|
|
70
|
+
err: tty
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
stdin_read.close
|
|
74
|
+
stdout_write.close
|
|
75
|
+
|
|
76
|
+
stdin_write.write(input)
|
|
77
|
+
stdin_write.close
|
|
78
|
+
|
|
79
|
+
output = stdout_read.read.chomp
|
|
80
|
+
stdout_read.close
|
|
81
|
+
|
|
82
|
+
_, status = Process.wait2(pid)
|
|
83
|
+
tty.close
|
|
84
|
+
|
|
85
|
+
return nil if status.exitstatus == 130 # User cancelled
|
|
86
|
+
return nil unless status.success?
|
|
87
|
+
|
|
88
|
+
output
|
|
89
|
+
rescue Errno::ENOENT, Errno::ENXIO, Errno::EIO
|
|
90
|
+
run_non_interactive(*args, input: input)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.run_display_only(*args, input:)
|
|
94
|
+
IO.popen([Gum.executable, *args.map(&:to_s)], "w") do |io|
|
|
95
|
+
io.write(input)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
$CHILD_STATUS.success? || nil
|
|
99
|
+
rescue Errno::ENOENT
|
|
100
|
+
raise Error, "gum executable not found"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def self.run_with_status(*args, input: nil)
|
|
104
|
+
if input
|
|
105
|
+
_stdout, _stderr, status = Open3.capture3(Gum.executable, *args.map(&:to_s), stdin_data: input)
|
|
106
|
+
status.success?
|
|
107
|
+
else
|
|
108
|
+
system(Gum.executable, *args.map(&:to_s))
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def self.build_args(command, *positional, **options)
|
|
113
|
+
args = [command]
|
|
114
|
+
args.concat(positional.flatten.compact)
|
|
115
|
+
|
|
116
|
+
options.each do |key, value|
|
|
117
|
+
next if value.nil?
|
|
118
|
+
|
|
119
|
+
flag = key.to_s.tr("_", "-")
|
|
120
|
+
|
|
121
|
+
case value
|
|
122
|
+
when true
|
|
123
|
+
args << "--#{flag}"
|
|
124
|
+
when false
|
|
125
|
+
args << "--no-#{flag}" if flag_supports_negation?(command, flag)
|
|
126
|
+
when Array
|
|
127
|
+
value.each { |v| args << "--#{flag}=#{v}" }
|
|
128
|
+
when Hash
|
|
129
|
+
value.each { |k, v| args << "--#{flag}.#{k}=#{v}" }
|
|
130
|
+
else
|
|
131
|
+
args << "--#{flag}=#{value}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
args
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def self.add_style_args(args, flag, style_hash)
|
|
139
|
+
return unless style_hash
|
|
140
|
+
|
|
141
|
+
style_hash.each do |key, value|
|
|
142
|
+
args << "--#{flag}.#{key}" << value.to_s
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def self.flag_supports_negation?(command, flag)
|
|
147
|
+
negatable = {
|
|
148
|
+
"filter" => ["fuzzy", "sort", "strict", "reverse", "indicator"],
|
|
149
|
+
"choose" => ["limit"],
|
|
150
|
+
"input" => ["echo-mode"],
|
|
151
|
+
"file" => ["all", "file", "directory"],
|
|
152
|
+
"pager" => ["soft-wrap"],
|
|
153
|
+
"table" => ["border", "print"],
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
negatable.fetch(command, []).include?(flag)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|