cli_class_tool 0.2.1 → 0.4.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: 641f72caf2c39cac1c5a222756c481f0de86b5fd7db14893001a64773400d894
4
- data.tar.gz: 612b9cca3cbd1b157780c490093657e77ba021bb973ca38e76e63f19de1e730e
3
+ metadata.gz: bad275062a9f72ff201812eb6cd7871b9321b80b57f379f817cab483de42b738
4
+ data.tar.gz: e3cb70b12fe4831a4fdd194eaa4c67118efcfd9ffc0f1903524d74bdaf805e1a
5
5
  SHA512:
6
- metadata.gz: f2ceb59e563ebe911176786f1e4ffdd3762c2ef0b9a62acad6323896c68741228468f3b21e53331f4b22774b4bd0ac8746048330408d84366b6fa4426a546ff6
7
- data.tar.gz: 3d848b3725fd1e80e061d04e358bbb71821708fc2ed490612ad4fe66b2ffc038396605cda7dfa28f89d7bdaaf703f6b038a0b66a68f18d18c12a2a12742667ab
6
+ metadata.gz: 43cc9f0156398b92a5d1c7370c6c8c7dcf61d80e4ad9b18bec8a4a5bedceec7aa9e9af40e5d801554c7369aa746bac98c204ad949d2d27b381c4e14cbec93312
7
+ data.tar.gz: '0948e6f13057dd174140d84a7845c33a25e758a9ef018e10afb9c75bb734e87dffaf191e81091e01fe8c1dc9ff66437672364343d7ffc054f76cf0d591ebec1d'
data/CHANGELOG CHANGED
@@ -1,7 +1,24 @@
1
+ ------------------
2
+ 0.4.0 (2026-06-05)
3
+ ------------------
4
+
5
+ * Add basic initializer for Common
6
+ * Add Common::run() that will auto spawn a Common object and run the command with it.
7
+ * log function can now be used in alternate object or class by including/extending CLIClassTool::Logger
8
+
9
+ ------------------
10
+ 0.3.0 (2026-06-02)
11
+ ------------------
12
+
13
+ * Enforce module/class namespaces for all action classes (potentially breaking change)
14
+ * Support automatic project-specific `RunError` generation with customized base error inheritance
15
+
1
16
  ------------------
2
17
  0.2.1 (2026-06-01)
3
18
  ------------------
4
19
 
20
+ * Fix issue with conflicting addon files
21
+
5
22
  ------------------
6
23
  0.2.0 (2026-06-01)
7
24
  ------------------
data/README.md CHANGED
@@ -112,6 +112,61 @@ exit MyProject.execAction(opts, opts[:action])
112
112
 
113
113
  ---
114
114
 
115
+ ## Namespace Requirement & Error Handling
116
+
117
+ ### 1. Module Namespace Requirement
118
+
119
+ To ensure clean encapsulation and safe error resolution, all action classes and standard subclasses inheriting from `CLIClassTool::Common` **must** be defined within a named module/class namespace (such as `MyProject`):
120
+
121
+ ```ruby
122
+ # Correct: Action class is scoped under MyProject namespace
123
+ module MyProject
124
+ class Suse < CLIClassTool::Common
125
+ # ...
126
+ end
127
+ end
128
+
129
+ # Incorrect: Defining action classes directly in the global namespace is forbidden
130
+ # and will raise an error at load-time:
131
+ class Suse < CLIClassTool::Common; end
132
+ # => "CLIClassTool action classes must be defined within a named module/class namespace"
133
+ ```
134
+
135
+ ### 2. Automated `RunError` Generation & Customized Inheritance
136
+
137
+ When your project's parent module extends `CLIClassTool::Utils`, `CLIClassTool` automatically generates a nested `RunError` class (i.e. `MyProject::RunError`) specifically for your project on load.
138
+
139
+ By default, this generated error inherits from `CLIClassTool::RunError`. However, if you have a binary-specific or project-wide base error class defined, `CLIClassTool` will **automatically detect it** and make `RunError` inherit from it instead!
140
+
141
+ It looks for existing error classes in this order:
142
+ 1. `#{base_name}Error` (e.g., `MyProjectError` in the global scope)
143
+ 2. `#{base_name}::Error` (e.g., `MyProject::Error` within your namespace)
144
+ 3. Falls back to `CLIClassTool::RunError`
145
+
146
+ #### Why is this useful?
147
+ This allows you to catch all potential CLI errors (including system command failures from `run`, `runGit`, etc.) in a single rescue block at the entry point of your binary:
148
+
149
+ ```ruby
150
+ # Define your binary-specific error class
151
+ class MyProjectError < StandardError; end
152
+
153
+ module MyProject
154
+ extend CLIClassTool::Utils
155
+ # CLIClassTool automatically defines MyProject::RunError inheriting from MyProjectError!
156
+ end
157
+
158
+ # In your executable (bin/mytool)
159
+ begin
160
+ MyProject.run_cli
161
+ rescue MyProjectError => e
162
+ # This cleanly catches MyProject::RunError, as well as any other custom MyProjectError!
163
+ STDERR.puts "# ERROR: #{e.message}"
164
+ exit e.respond_to?(:err_code) ? e.err_code : 1
165
+ end
166
+ ```
167
+
168
+ ---
169
+
115
170
  ## Logging Levels
116
171
  By inheriting from `CLIClassTool::Common`, your action classes have access to a rich `log` helper supporting several standard output and color levels:
117
172
 
@@ -1,22 +1,9 @@
1
1
  # Main module for generic CLI class-based tools and utilities
2
2
  module CLIClassTool
3
3
 
4
- # Common utility class providing logging, configuration, and shell execution methods
5
- class Common
6
- # List of available actions for this class
7
- ACTION_LIST = [ :list_actions ]
8
- # Help text for actions
9
- ACTION_HELP = {}
10
-
4
+ # Logger for CLIClassTool::Common
5
+ module Logger
11
6
  private
12
- # Get the parent module of this class (e.g. KernelWork or XXX)
13
- def parent_module
14
- @parent_module ||= begin
15
- parts = self.class.name.split('::')
16
- parts.size > 1 ? Object.const_get(parts[0...-1].join('::')) : Object
17
- end
18
- end
19
-
20
7
  # Internal log method
21
8
  # @param lvl [String] Log level string (colored)
22
9
  # @param str [String] Message
@@ -33,26 +20,33 @@ module CLIClassTool
33
20
  out.print("# " + lvl.to_s() + ": " + str + "\r")
34
21
  end
35
22
 
36
- # Raise error if system command failed
37
- # @param check_err [Boolean] Whether to check for errors
38
- # @param sysret [Process::Status] System return status
39
- # @param ret [String, nil] Optional return message
23
+ # Compute the parent module of an object or a class
24
+ #
25
+ # @param obj [Object,Class] Object or class to get the Module from
40
26
  # @raise [StandardError] If command failed
41
- def abort_if_err(check_err, sysret, ret = nil)
42
- if sysret.exitstatus != 0 && check_err == true
43
- run_error_class = parent_module.const_defined?(:RunError) ? parent_module::RunError : RuntimeError
44
- raise(run_error_class.new(sysret.exitstatus, ret))
27
+ def obj_to_parent_mod(obj)
28
+ return obj if obj.is_a?(Module)
29
+ theClass = obj.is_a?(Class) ? obj : obj.class
30
+ if theClass.name.nil?
31
+ return Object
32
+ else
33
+ parts = theClass.name.split('::')
34
+ if parts.size <= 1
35
+ raise "CLIClassTool action classes must be defined within a named module/class namespace"
36
+ end
37
+ return Object.const_get(parts[0...-1].join('::'))
45
38
  end
46
39
  end
47
40
 
48
- # Debug command execution
49
- # @param cmd_type [String] Type of command (e.g., 'git')
50
- # @param cmd [String] The command string
51
- def cmd_debug(cmd_type, cmd)
52
- log(:DEBUG, "Called from #{caller[1]}")
53
- log(:DEBUG, "Running #{cmd_type} command '#{cmd}'")
41
+ # Get the parent module of this class (e.g. KernelWork or XXX)
42
+ def parent_module
43
+ return @parent_module if @parent_module != nil
44
+
45
+ @parent_module = obj_to_parent_mod(self)
46
+ return @parent_module
54
47
  end
55
- protected
48
+
49
+ public
56
50
  # Log a message with a specific level
57
51
  #
58
52
  # @param lvl [Symbol] Log level (:DEBUG, :INFO, :WARNING, :ERROR, etc.)
@@ -102,8 +96,61 @@ module CLIClassTool
102
96
  end
103
97
  return rep
104
98
  end
99
+ end
100
+
101
+ # Common utility class providing logging, configuration, and shell execution methods
102
+ class Common
103
+ # Hook to enforce namespace loading at load time
104
+ def self.inherited(subclass)
105
+ if subclass.name
106
+ parts = subclass.name.to_s.split('::')
107
+ if parts.size <= 1
108
+ raise "CLIClassTool action classes must be defined within a named module/class namespace"
109
+ end
110
+ end
111
+ end
112
+
113
+ # List of available actions for this class
114
+ ACTION_LIST = [ :list_actions ]
115
+ # Help text for actions
116
+ ACTION_HELP = {}
117
+
118
+ # Give the Logger mathods to Common
119
+ include CLIClassTool::Logger
120
+
121
+ private
122
+ # Raise error if system command failed
123
+ # @param check_err [Boolean] Whether to check for errors
124
+ # @param sysret [Process::Status] System return status
125
+ # @param ret [String, nil] Optional return message
126
+ # @raise [StandardError] If command failed
127
+ def abort_if_err(check_err, sysret, ret = nil)
128
+ if sysret.exitstatus != 0 && check_err == true
129
+ unless parent_module.const_defined?(:RunError)
130
+ raise "CLIClassTool parent module #{parent_module} must extend CLIClassTool::Utils to define RunError"
131
+ end
132
+ raise(parent_module::RunError.new(sysret.exitstatus, ret))
133
+ end
134
+ end
135
+
136
+ # Debug command execution
137
+ # @param cmd_type [String] Type of command (e.g., 'git')
138
+ # @param cmd [String] The command string
139
+ def cmd_debug(cmd_type, cmd)
140
+ log(:DEBUG, "Called from #{caller[1]}")
141
+ log(:DEBUG, "Running #{cmd_type} command '#{cmd}'")
142
+ end
143
+
105
144
 
106
145
  public
146
+ # Simple initializer for a Common object
147
+ #
148
+ # @param path [String] Path to run commands from
149
+ def initialize(path=".", caller_obj=self)
150
+ @path = path
151
+ @parent_module = obj_to_parent_mod(caller_obj)
152
+ end
153
+
107
154
  # Run a shell command
108
155
  #
109
156
  # @param cmd [String] Command to run
@@ -117,6 +164,10 @@ module CLIClassTool
117
164
  return ret
118
165
  end
119
166
 
167
+ def self.run(path, cmd, check_err = true)
168
+ obj = Common.new(path, self)
169
+ return obj.run(cmd, check_err)
170
+ end
120
171
  # Run a shell command using system() (interactive)
121
172
  #
122
173
  # @param cmd [String] Command to run
@@ -2,6 +2,21 @@ module CLIClassTool
2
2
  # Generic utilities for CLI class-based actions
3
3
  module Utils
4
4
 
5
+ # Hook called when a module extends CLIClassTool::Utils
6
+ def self.extended(base)
7
+ possible_name = base.name ? "#{base.name}Error" : nil
8
+
9
+ superclass = if possible_name && Object.const_defined?(possible_name)
10
+ Object.const_get(possible_name)
11
+ elsif base.const_defined?(:Error)
12
+ base.const_get(:Error)
13
+ else
14
+ CLIClassTool::RunError
15
+ end
16
+
17
+ CLIClassTool.define_run_error(base, superclass)
18
+ end
19
+
5
20
  # Convert a string to an action symbol, validating it against available actions
6
21
  #
7
22
  # @param str [String] Action name
@@ -186,7 +201,6 @@ module CLIClassTool
186
201
  def run_cli(opts = {}, argv = ARGV)
187
202
  # Fetch actions and action helps
188
203
  action_helps = self.getActionAttr("ACTION_HELP")
189
- action_list = self.getActionAttr("ACTION_LIST")
190
204
 
191
205
  # 1. Action Parser Setup
192
206
  action_parser = OptionParser.new(nil, 60)
@@ -1,3 +1,33 @@
1
+ module CLIClassTool
2
+ class RunError < StandardError
3
+ attr_reader :err_code, :output
4
+
5
+ def initialize(err_code, output = nil)
6
+ @err_code = err_code
7
+ @output = output
8
+ super("Command failed with exit status #{err_code}#{output ? "\n#{output}" : ''}")
9
+ end
10
+ end
11
+
12
+ def self.define_run_error(parent_module, superclass = CLIClassTool::RunError)
13
+ klass = Class.new(superclass)
14
+
15
+ if !(superclass <= CLIClassTool::RunError)
16
+ klass.class_eval do
17
+ attr_reader :err_code, :output
18
+
19
+ def initialize(err_code, output = nil)
20
+ @err_code = err_code
21
+ @output = output
22
+ super("Command failed with exit status #{err_code}#{output ? "\n#{output}" : ''}")
23
+ end
24
+ end
25
+ end
26
+
27
+ parent_module.const_set(:RunError, klass)
28
+ end
29
+ end
30
+
1
31
  require_relative 'cli_class_tool/string'
2
32
  require_relative 'cli_class_tool/common'
3
33
  require_relative 'cli_class_tool/utils'
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: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicolas Morey
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-06-01 00:00:00.000000000 Z
10
+ date: 2026-06-05 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rake