philiprehberger-cli_kit 0.3.1 → 0.5.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/CHANGELOG.md +14 -1
- data/README.md +29 -0
- data/lib/philiprehberger/cli_kit/colorize.rb +52 -0
- data/lib/philiprehberger/cli_kit/parser.rb +22 -4
- data/lib/philiprehberger/cli_kit/version.rb +1 -1
- data/lib/philiprehberger/cli_kit.rb +22 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 895e4a8ff9edcfa13591e60cd91454c81840404c61b724e3394d234b71f8e496
|
|
4
|
+
data.tar.gz: b789d3b41ab39145500841661603728c8edccc438ab577319c6166bdf27230cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6b708dc9eadf82852b3d683f32dd4adeeaae2bc0f3985ce2c338930a58cc1cb8bbe2cff17a929578907ffe68ae1c63a38ff115129fefdb68a531a18c467d0650
|
|
7
|
+
data.tar.gz: 94e8d68950132d59d2ed894d356374269989d5f73db2837897f957cc3fd70011f5f1b4c96f8ba75554b7069c65900c1385bf357885ba925346009da154ba011f
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.0] - 2026-04-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- ANSI color helpers via `CliKit.color`, `CliKit.bold`, `CliKit.dim` and the `CliKit::Colorize` module
|
|
14
|
+
- Automatic disable when stdout is not a TTY or `NO_COLOR` is set
|
|
15
|
+
|
|
16
|
+
## [0.4.0] - 2026-04-18
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `option :name, required: true` — DSL flag that raises `CliKit::Error` at parse time when the option is omitted; help output appends `(required)` to the option's description
|
|
20
|
+
|
|
10
21
|
## [0.3.1] - 2026-04-15
|
|
11
22
|
|
|
12
23
|
### Changed
|
|
@@ -63,7 +74,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
63
74
|
- Animated spinner for long-running operations
|
|
64
75
|
- Positional argument collection
|
|
65
76
|
|
|
66
|
-
[Unreleased]: https://github.com/philiprehberger/rb-cli-kit/compare/v0.
|
|
77
|
+
[Unreleased]: https://github.com/philiprehberger/rb-cli-kit/compare/v0.5.0...HEAD
|
|
78
|
+
[0.5.0]: https://github.com/philiprehberger/rb-cli-kit/compare/v0.4.0...v0.5.0
|
|
79
|
+
[0.4.0]: https://github.com/philiprehberger/rb-cli-kit/compare/v0.3.1...v0.4.0
|
|
67
80
|
[0.3.1]: https://github.com/philiprehberger/rb-cli-kit/compare/v0.3.0...v0.3.1
|
|
68
81
|
[0.3.0]: https://github.com/philiprehberger/rb-cli-kit/compare/v0.2.1...v0.3.0
|
|
69
82
|
[0.2.1]: https://github.com/philiprehberger/rb-cli-kit/compare/v0.2.0...v0.2.1
|
data/README.md
CHANGED
|
@@ -105,6 +105,20 @@ port = Philiprehberger::CliKit.ask('Port:', error: 'Must be a number') do |answe
|
|
|
105
105
|
end
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
### Required options
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
result = Philiprehberger::CliKit.parse(ARGV) do
|
|
112
|
+
option :env, short: :e, required: true, desc: 'Target environment'
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Invoked without --env raises Philiprehberger::CliKit::Error:
|
|
116
|
+
# Missing required option(s): --env
|
|
117
|
+
#
|
|
118
|
+
# Help text appends "(required)" to the option's description:
|
|
119
|
+
# -e, --env VALUE Target environment (required)
|
|
120
|
+
```
|
|
121
|
+
|
|
108
122
|
### Repeatable Options
|
|
109
123
|
|
|
110
124
|
```ruby
|
|
@@ -163,6 +177,17 @@ data = Philiprehberger::CliKit.spinner('Loading data...') do
|
|
|
163
177
|
end
|
|
164
178
|
```
|
|
165
179
|
|
|
180
|
+
### Color Output
|
|
181
|
+
|
|
182
|
+
Colors are auto-disabled when stdout is not a TTY or the `NO_COLOR` environment variable is set.
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
require "philiprehberger/cli_kit"
|
|
186
|
+
|
|
187
|
+
puts Philiprehberger::CliKit.color('OK', :green)
|
|
188
|
+
puts Philiprehberger::CliKit.bold('Important')
|
|
189
|
+
```
|
|
190
|
+
|
|
166
191
|
## API
|
|
167
192
|
|
|
168
193
|
| Method | Description |
|
|
@@ -175,7 +200,11 @@ end
|
|
|
175
200
|
| `.select(message, choices)` | Present numbered menu and return one selection |
|
|
176
201
|
| `.multi_select(message, choices, defaults:)` | Present numbered menu and return multiple selections |
|
|
177
202
|
| `.spinner(message) { ... }` | Show spinner during block execution |
|
|
203
|
+
| `.color(text, name)` | Wrap text in ANSI color (no-op when not a TTY or NO_COLOR set) |
|
|
204
|
+
| `.bold(text)` | Wrap text in ANSI bold |
|
|
205
|
+
| `.dim(text)` | Wrap text in ANSI dim |
|
|
178
206
|
| `Parser#option(name, multi: true)` | Collect repeated option values into an array |
|
|
207
|
+
| `Parser#option(name, required: true)` | Raise `CliKit::Error` at parse time when the option is omitted |
|
|
179
208
|
| `Parser#flags` | Hash of boolean flag values |
|
|
180
209
|
| `Parser#options` | Hash of option values (arrays when `multi: true`) |
|
|
181
210
|
| `Parser#arguments` | Array of positional arguments |
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Philiprehberger
|
|
4
|
+
module CliKit
|
|
5
|
+
# ANSI color and style helpers. Output is auto-disabled when stdout is not a
|
|
6
|
+
# TTY or the NO_COLOR environment variable is set (per https://no-color.org).
|
|
7
|
+
module Colorize
|
|
8
|
+
CODES = {
|
|
9
|
+
red: 31, green: 32, yellow: 33, blue: 34,
|
|
10
|
+
magenta: 35, cyan: 36, white: 37, gray: 90
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
# @return [Boolean] true when ANSI escape codes should be emitted
|
|
16
|
+
def enabled?
|
|
17
|
+
return false if ENV.key?('NO_COLOR')
|
|
18
|
+
|
|
19
|
+
$stdout.tty?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Wraps text in an ANSI color escape, or returns it untouched when colors
|
|
23
|
+
# are disabled.
|
|
24
|
+
#
|
|
25
|
+
# @param text [String]
|
|
26
|
+
# @param name [Symbol] one of :red, :green, :yellow, :blue, :magenta, :cyan, :white, :gray
|
|
27
|
+
# @return [String]
|
|
28
|
+
def color(text, name)
|
|
29
|
+
return text unless enabled?
|
|
30
|
+
|
|
31
|
+
code = CODES.fetch(name) { raise ArgumentError, "unknown color: #{name.inspect}" }
|
|
32
|
+
"\e[#{code}m#{text}\e[0m"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @param text [String]
|
|
36
|
+
# @return [String]
|
|
37
|
+
def bold(text)
|
|
38
|
+
return text unless enabled?
|
|
39
|
+
|
|
40
|
+
"\e[1m#{text}\e[0m"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param text [String]
|
|
44
|
+
# @return [String]
|
|
45
|
+
def dim(text)
|
|
46
|
+
return text unless enabled?
|
|
47
|
+
|
|
48
|
+
"\e[2m#{text}\e[0m"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -13,6 +13,7 @@ module Philiprehberger
|
|
|
13
13
|
@flags = {}
|
|
14
14
|
@options = {}
|
|
15
15
|
@arguments = []
|
|
16
|
+
@supplied_options = []
|
|
16
17
|
@command_name = nil
|
|
17
18
|
@program_name = nil
|
|
18
19
|
end
|
|
@@ -35,9 +36,12 @@ module Philiprehberger
|
|
|
35
36
|
# @param default [Object, nil] default value
|
|
36
37
|
# @param desc [String, nil] description for help text
|
|
37
38
|
# @param multi [Boolean] when true, collect repeated values into an array
|
|
39
|
+
# @param required [Boolean] when true, raise if the option is not supplied at parse time
|
|
38
40
|
# @return [void]
|
|
39
|
-
def option(name, short: nil, default: nil, desc: nil, multi: false)
|
|
40
|
-
@option_definitions[name] = {
|
|
41
|
+
def option(name, short: nil, default: nil, desc: nil, multi: false, required: false)
|
|
42
|
+
@option_definitions[name] = {
|
|
43
|
+
short: short, default: default, desc: desc, multi: multi, required: required
|
|
44
|
+
}
|
|
41
45
|
@options[name] = multi ? [] : default
|
|
42
46
|
end
|
|
43
47
|
|
|
@@ -131,6 +135,7 @@ module Philiprehberger
|
|
|
131
135
|
@arguments << arg
|
|
132
136
|
end
|
|
133
137
|
end
|
|
138
|
+
validate_required_options!
|
|
134
139
|
self
|
|
135
140
|
end
|
|
136
141
|
|
|
@@ -172,6 +177,7 @@ module Philiprehberger
|
|
|
172
177
|
|
|
173
178
|
def assign_option(name, value)
|
|
174
179
|
defn = @option_definitions[name]
|
|
180
|
+
@supplied_options << name
|
|
175
181
|
if defn && defn[:multi]
|
|
176
182
|
@options[name] = [] unless @options[name].is_a?(Array)
|
|
177
183
|
@options[name] << value unless value.nil?
|
|
@@ -180,6 +186,16 @@ module Philiprehberger
|
|
|
180
186
|
end
|
|
181
187
|
end
|
|
182
188
|
|
|
189
|
+
def validate_required_options!
|
|
190
|
+
missing = @option_definitions.each_with_object([]) do |(name, defn), acc|
|
|
191
|
+
acc << name if defn[:required] && !@supplied_options.include?(name)
|
|
192
|
+
end
|
|
193
|
+
return if missing.empty?
|
|
194
|
+
|
|
195
|
+
names = missing.map { |n| "--#{n.to_s.tr('_', '-')}" }.join(', ')
|
|
196
|
+
raise Error, "Missing required option(s): #{names}"
|
|
197
|
+
end
|
|
198
|
+
|
|
183
199
|
def format_flag_help(name, defn)
|
|
184
200
|
long = "--#{name.to_s.tr('_', '-')}"
|
|
185
201
|
if defn[:short]
|
|
@@ -204,8 +220,10 @@ module Philiprehberger
|
|
|
204
220
|
else
|
|
205
221
|
label = " #{long}"
|
|
206
222
|
end
|
|
207
|
-
|
|
208
|
-
|
|
223
|
+
desc = defn[:desc]
|
|
224
|
+
desc = desc ? "#{desc} (required)" : '(required)' if defn[:required]
|
|
225
|
+
if desc
|
|
226
|
+
"#{label.ljust(24)}#{desc}"
|
|
209
227
|
else
|
|
210
228
|
label
|
|
211
229
|
end
|
|
@@ -5,6 +5,7 @@ require_relative 'cli_kit/parser'
|
|
|
5
5
|
require_relative 'cli_kit/prompt'
|
|
6
6
|
require_relative 'cli_kit/spinner'
|
|
7
7
|
require_relative 'cli_kit/menu'
|
|
8
|
+
require_relative 'cli_kit/colorize'
|
|
8
9
|
|
|
9
10
|
module Philiprehberger
|
|
10
11
|
module CliKit
|
|
@@ -105,5 +106,26 @@ module Philiprehberger
|
|
|
105
106
|
def self.multi_select(message, choices, defaults: [], input: $stdin, output: $stdout)
|
|
106
107
|
Menu.multi_select(message, choices, defaults: defaults, input: input, output: output)
|
|
107
108
|
end
|
|
109
|
+
|
|
110
|
+
# Wraps text in ANSI color (auto-disabled when not a TTY or NO_COLOR is set).
|
|
111
|
+
#
|
|
112
|
+
# @param text [String]
|
|
113
|
+
# @param name [Symbol]
|
|
114
|
+
# @return [String]
|
|
115
|
+
def self.color(text, name)
|
|
116
|
+
Colorize.color(text, name)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @param text [String]
|
|
120
|
+
# @return [String]
|
|
121
|
+
def self.bold(text)
|
|
122
|
+
Colorize.bold(text)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @param text [String]
|
|
126
|
+
# @return [String]
|
|
127
|
+
def self.dim(text)
|
|
128
|
+
Colorize.dim(text)
|
|
129
|
+
end
|
|
108
130
|
end
|
|
109
131
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-cli_kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Lightweight CLI toolkit combining argument parsing with flags and options,
|
|
14
14
|
interactive prompts with confirmation, and animated spinners for long-running operations.
|
|
@@ -22,6 +22,7 @@ files:
|
|
|
22
22
|
- LICENSE
|
|
23
23
|
- README.md
|
|
24
24
|
- lib/philiprehberger/cli_kit.rb
|
|
25
|
+
- lib/philiprehberger/cli_kit/colorize.rb
|
|
25
26
|
- lib/philiprehberger/cli_kit/menu.rb
|
|
26
27
|
- lib/philiprehberger/cli_kit/parser.rb
|
|
27
28
|
- lib/philiprehberger/cli_kit/prompt.rb
|