cli_class_tool 1.1.0 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 645eabaadb2f9f7b6497f524dc2e847bbe6fa2b3bd0085449e7a5c6e49e6d943
4
- data.tar.gz: 94cb2bfe852dce7a0f1a55a7888a813e4beea307f0a41c056c69d26f0914e4e7
3
+ metadata.gz: 89e584d14bafdaf918d35406f8bf1dd1b548147cf65ff7e8a51f0f687add515c
4
+ data.tar.gz: 4dc4ed8c9a27d01fdf8b3cc408532b42e0c7ab2c80b6e75d92b35c1a200be4bb
5
5
  SHA512:
6
- metadata.gz: 576fc426eedf2ed6578dce762fae533432cee75c1dce31d31ebf1c44b2b6c82b0a6418d74670fb5b6a6812b278c0444cc8295adb79d5fa2be4507cbbc9136d16
7
- data.tar.gz: 4c6544ef6d4eecb54d5f2e7de75106e33936523d3e2bcea82ad729e0a388d9e33b193727b67637ef9901f29c23fccfa5ee9caed484043c4b2e7e8f60da7ca7db
6
+ metadata.gz: 52e76102055a0d40a7ef754232f30619a882b906200d29e7a31eafe87e24304225bc022877c3a504957389064874046ee522aba7d7e96d3dee46697d6e7afdaf
7
+ data.tar.gz: be038285b90077978d9b4f72150d66aee2d994f9dffa6b709b59c49b1be8a645a0fcfe1b9f6ca2e1cf9ffe372c1ad9832be7aa7ea026353c33084797e1bd1f3e
data/CHANGELOG CHANGED
@@ -1,3 +1,19 @@
1
+ ------------------
2
+ 2.1.0 (2026-09-25)
3
+ ------------------
4
+
5
+ * Add `String#hyperlink` method for OSC 8 terminal hyperlinks
6
+ * Add `String#visible_length` method to compute visible string length stripped of ANSI escapes, control characters, and hyperlinks
7
+
8
+ ------------------
9
+ 2.0.0 (2026-09-18)
10
+ ------------------
11
+
12
+ * Refactor Common.run, runSystem, runGit, and runGitInteractive to accept optional named arguments (env:, catch_err:, silent_err:)
13
+ * Replace check_err positional boolean with catch_err: keyword argument (default: false) across all run methods
14
+ * Replace opts[:env] with env: keyword argument across all run methods
15
+ * Add silent_err: keyword argument to redirect stderr to /dev/null
16
+
1
17
  ------------------
2
18
  1.1.0 (2026-09-09)
3
19
  ------------------
data/README.md CHANGED
@@ -207,6 +207,48 @@ confirm(opts, "format the disk", ignored_default: true)
207
207
 
208
208
  ---
209
209
 
210
+ ## Command Execution (`run`, `runSystem`, `runGit`, `runGitInteractive`)
211
+
212
+ `CLIClassTool::Common` provides shell and git execution methods that run within the context of the instance's target `@path`.
213
+
214
+ ### Available Methods
215
+
216
+ - `run(cmd, env: nil, catch_err: false, silent_err: false)`: Executes a command in a subshell, returning the stripped stdout (`String`).
217
+ - `CLIClassTool::Common.run(path, cmd, env: nil, catch_err: false, silent_err: false)`: Class method convenience helper that instantiates a `Common` object for `path` and runs `cmd`.
218
+ - `runSystem(cmd, env: nil, catch_err: false, silent_err: false)`: Executes a command interactively using `system()`, returning `true` on success and `false` on failure.
219
+ - `runGit(cmd, env: nil, catch_err: false, silent_err: false)`: Runs `git #{cmd}` in a subshell, returning the stripped stdout (`String`).
220
+ - `runGitInteractive(cmd, env: nil, catch_err: false, silent_err: false)`: Runs `git #{cmd}` interactively using `system()`, returning `true` on success and `false` on failure.
221
+
222
+ ### Optional Named Arguments
223
+
224
+ - `env` (`String`, default: `nil`): Optional environment variable prefix prepended to the command (e.g. `env: "FOO=bar"` or `env: "GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=user.name GIT_CONFIG_VALUE_0=TestRunner"`).
225
+ - `catch_err` (`Boolean`, default: `false`): Controls error handling on non-zero exit codes:
226
+ - `catch_err: false` (default): Command failures abort and raise a project-specific `RunError`.
227
+ - `catch_err: true`: Suppresses raising `RunError`, returning the command output (or `false` for `system` methods).
228
+ - `silent_err` (`Boolean`, default: `false`): When `true`, redirects stderr output to `/dev/null` (`2>/dev/null`).
229
+
230
+ ### Examples
231
+
232
+ ```ruby
233
+ # Basic execution (raises RunError if command exits non-zero)
234
+ branch = run("git rev-parse --abbrev-ref HEAD")
235
+
236
+ # Running with environment variables
237
+ run("make", env: "CC=clang CFLAGS='-O2'")
238
+
239
+ # Suppressing errors on expected failures
240
+ output = run("grep -r 'pattern' .", catch_err: true)
241
+
242
+ # Suppressing error output (stderr redirected to /dev/null)
243
+ runGit("rev-parse --verify non_existing_ref", catch_err: true, silent_err: true)
244
+
245
+ # Interactive execution via system()
246
+ runSystem("make menuconfig")
247
+ runGitInteractive("rebase -i origin/main")
248
+ ```
249
+
250
+ ---
251
+
210
252
  ## Dynamic Class Overrides (Addons)
211
253
 
212
254
  `CLIClassTool` natively supports dynamic class overrides (addons). This allows projects to load repository-specific or custom subclasses that extend or override base action behaviors without modifying the core codebase.
@@ -446,3 +488,22 @@ puts "System Message".bold # Bold text using default terminal color
446
488
  ```
447
489
 
448
490
  These methods automatically check if `$stdout` is a TTY and safely fall back to standard, unformatted strings if output is piped or redirected (non-TTY mode).
491
+
492
+ ### Terminal Hyperlinks (`hyperlink`)
493
+
494
+ You can create clickable OSC 8 terminal hyperlinks using the `.hyperlink(url)` method. When `$stdout` is a TTY and a target URL is provided, it formats the string with OSC 8 escape sequences; otherwise, it returns the original string:
495
+
496
+ ```ruby
497
+ puts "Visit Website".hyperlink("https://example.com")
498
+ puts "Source Code".blue.hyperlink("https://github.com")
499
+ ```
500
+
501
+ ### Visible String Length (`visible_length`)
502
+
503
+ To compute the display length of a string without ANSI color escape codes, embedded hyperlinks, or residual control characters, use `.visible_length`:
504
+
505
+ ```ruby
506
+ str = "Click here".bold.red.hyperlink("https://example.com")
507
+ str.length # Raw string length including escape sequences
508
+ str.visible_length # 10 (visible characters count)
509
+ ```
@@ -136,12 +136,12 @@ module CLIClassTool
136
136
 
137
137
  private
138
138
  # Raise error if system command failed
139
- # @param check_err [Boolean] Whether to check for errors
139
+ # @param catch_err [Boolean] Whether to catch (suppress raising) errors
140
140
  # @param sysret [Process::Status] System return status
141
141
  # @param ret [String, nil] Optional return message
142
- # @raise [StandardError] If command failed
143
- def abort_if_err(check_err, sysret, ret = nil)
144
- if sysret.exitstatus != 0 && check_err == true
142
+ # @raise [StandardError] If command failed and catch_err is false
143
+ def abort_if_err(catch_err, sysret, ret = nil)
144
+ if sysret.exitstatus != 0 && !catch_err
145
145
  unless parent_module.const_defined?(:RunError)
146
146
  raise "CLIClassTool parent module #{parent_module} must extend CLIClassTool::Utils to define RunError"
147
147
  end
@@ -177,58 +177,73 @@ module CLIClassTool
177
177
  # Run a shell command
178
178
  #
179
179
  # @param cmd [String] Command to run
180
- # @param check_err [Boolean] Raise error on failure
180
+ # @param env [String, nil] Environment variable prefix (e.g., "FOO=bar")
181
+ # @param catch_err [Boolean] If true, do not raise error on failure
182
+ # @param silent_err [Boolean] If true, redirect stderr to /dev/null
181
183
  # @return [String] Command output
182
- # @raise [StandardError] If command fails and check_err is true
183
- def run(cmd, check_err = true)
184
+ # @raise [StandardError] If command fails and catch_err is false
185
+ def run(cmd, env: nil, catch_err: false, silent_err: false)
184
186
  cmd_debug('', cmd)
185
- ret = `cd #{@path} && #{cmd}`.chomp()
186
- abort_if_err(check_err, $?, ret)
187
+ env_prefix = env ? "#{env} " : ""
188
+ redirect = silent_err ? " 2>/dev/null" : ""
189
+ ret = `cd #{@path} && #{env_prefix}#{cmd}#{redirect}`.chomp()
190
+ abort_if_err(catch_err, $?, ret)
187
191
  return ret
188
192
  end
189
193
 
190
- def self.run(path, cmd, check_err = true)
194
+ def self.run(path, cmd, env: nil, catch_err: false, silent_err: false)
191
195
  obj = Common.new(path, self)
192
- return obj.run(cmd, check_err)
196
+ return obj.run(cmd, env: env, catch_err: catch_err, silent_err: silent_err)
193
197
  end
198
+
194
199
  # Run a shell command using system() (interactive)
195
200
  #
196
201
  # @param cmd [String] Command to run
197
- # @param check_err [Boolean] Raise error on failure
202
+ # @param env [String, nil] Environment variable prefix (e.g., "FOO=bar")
203
+ # @param catch_err [Boolean] If true, do not raise error on failure
204
+ # @param silent_err [Boolean] If true, redirect stderr to /dev/null
198
205
  # @return [Boolean] Command success status
199
- # @raise [StandardError] If command fails and check_err is true
200
- def runSystem(cmd, check_err = true)
206
+ # @raise [StandardError] If command fails and catch_err is false
207
+ def runSystem(cmd, env: nil, catch_err: false, silent_err: false)
201
208
  cmd_debug('interactive', cmd)
202
- ret = system("cd #{@path} && #{cmd}")
203
- abort_if_err(check_err, $?)
209
+ env_prefix = env ? "#{env} " : ""
210
+ redirect = silent_err ? " 2>/dev/null" : ""
211
+ ret = system("cd #{@path} && #{env_prefix}#{cmd}#{redirect}")
212
+ abort_if_err(catch_err, $?)
204
213
  return ret
205
214
  end
206
215
 
207
216
  # Run a git command
208
217
  #
209
218
  # @param cmd [String] Git command arguments
210
- # @param opts [Hash] Options (e.g., :env)
211
- # @param check_err [Boolean] Raise error on failure
219
+ # @param env [String, nil] Environment variable prefix (e.g., "FOO=bar")
220
+ # @param catch_err [Boolean] If true, do not raise error on failure
221
+ # @param silent_err [Boolean] If true, redirect stderr to /dev/null
212
222
  # @return [String] Command output
213
- # @raise [StandardError] If command fails and check_err is true
214
- def runGit(cmd, opts={}, check_err = true)
223
+ # @raise [StandardError] If command fails and catch_err is false
224
+ def runGit(cmd, env: nil, catch_err: false, silent_err: false)
215
225
  cmd_debug('git', cmd)
216
- ret = `cd #{@path} && #{opts[:env]} git #{cmd}`.chomp()
217
- abort_if_err(check_err, $?, ret)
226
+ env_prefix = env ? "#{env} " : ""
227
+ redirect = silent_err ? " 2>/dev/null" : ""
228
+ ret = `cd #{@path} && #{env_prefix}git #{cmd}#{redirect}`.chomp()
229
+ abort_if_err(catch_err, $?, ret)
218
230
  return ret
219
231
  end
220
232
 
221
233
  # Run a git command interactively
222
234
  #
223
235
  # @param cmd [String] Git command arguments
224
- # @param opts [Hash] Options (e.g., :env)
225
- # @param check_err [Boolean] Raise error on failure
236
+ # @param env [String, nil] Environment variable prefix (e.g., "FOO=bar")
237
+ # @param catch_err [Boolean] If true, do not raise error on failure
238
+ # @param silent_err [Boolean] If true, redirect stderr to /dev/null
226
239
  # @return [Boolean] Command success status
227
- # @raise [StandardError] If command fails and check_err is true
228
- def runGitInteractive(cmd, opts={}, check_err = true)
240
+ # @raise [StandardError] If command fails and catch_err is false
241
+ def runGitInteractive(cmd, env: nil, catch_err: false, silent_err: false)
229
242
  cmd_debug('git interactive', cmd)
230
- ret = system("cd #{@path} && #{opts[:env]} git #{cmd}")
231
- abort_if_err(check_err, $?)
243
+ env_prefix = env ? "#{env} " : ""
244
+ redirect = silent_err ? " 2>/dev/null" : ""
245
+ ret = system("cd #{@path} && #{env_prefix}git #{cmd}#{redirect}")
246
+ abort_if_err(catch_err, $?)
232
247
  return ret
233
248
  end
234
249
 
@@ -139,4 +139,28 @@ class String
139
139
  def light_white
140
140
  colorize(97)
141
141
  end
142
+
143
+ # Wrap the string in an OSC 8 terminal hyperlink if stdout is a TTY.
144
+ #
145
+ # @param url [String, #to_s, nil] Target URL for the hyperlink.
146
+ # @return [String] The formatted terminal hyperlink, or original string if not a TTY or URL is nil/empty.
147
+ def hyperlink(url)
148
+ @@is_a_tty = $stdout.isatty() if @@is_a_tty == nil
149
+ if @@is_a_tty && url && !url.to_s.empty?
150
+ "\e]8;;#{url}\e\\#{self}\e]8;;\e\\"
151
+ else
152
+ self
153
+ end
154
+ end
155
+
156
+ # Compute the visible length of the string
157
+ # Without any of ANSI/ASCII control characters nor embedded hyperlinks
158
+ #
159
+ # @return [Integer] Visible string length
160
+ def visible_length()
161
+ return self.gsub(/\e\]8;.*?(?:\e\\|\a)/, "") # Removes opening and closing hyperlink tags
162
+ .gsub(/\e\[[0-9;?]*[a-zA-Z]/, "") # Removes ANSI escape codes (colors, cursor control)
163
+ .gsub(/[[:cntrl:]]/, "") # Removes any residual control characters
164
+ .length
165
+ end
142
166
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cli_class_tool
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicolas Morey
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-09-09 00:00:00.000000000 Z
10
+ date: 2026-09-25 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rake
@@ -83,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
83
  - !ruby/object:Gem::Version
84
84
  version: '0'
85
85
  requirements: []
86
- rubygems_version: 4.0.16
86
+ rubygems_version: 4.0.20
87
87
  specification_version: 4
88
88
  summary: A lightweight object-oriented framework for class-based command-line interface
89
89
  (CLI) applications.