walheim 0.2.0 → 0.3.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: 8d19e8685c0c52cb39ba23885c62d508846a85bb92b378634ccf577f3f8b3cd9
4
- data.tar.gz: da82158c9db6c6c4a34633d10ca7c0c532ad3ead820dd244339d64ebd77182be
3
+ metadata.gz: c859436cfcc9582a4a875cf547855472d592b37e391f58abadd0003943272d1d
4
+ data.tar.gz: 15b5ad58f1c94475882e31c41c203f6a8446a63629ed7543957fc3244282be2b
5
5
  SHA512:
6
- metadata.gz: b995e6d2017d058690016d58881af050cd1f0c629c6a38ff741f29d11df27ffc86050f8ea17e0a1c83964f29a6c87a7a003e94b6f2fac9ce593d6c08b53d68bc
7
- data.tar.gz: cce38837eb9ba755d59463334c03b8e383f6cf9484b0062625695af198ac637bb648e1c3e93d28f42b30966edfa04b8b04f028d793d0befc60c9f11a4363fbc1
6
+ metadata.gz: b4c2b0a0ff5d4e6ca42d2ac2159c85689fc7faca2f633061d11b8d3cb8a2cf3bf2d4e9d07c35ca6d2abfb9768fbdcaacd49a0ccb673ba0b0cab55da3ec80c5ba
7
+ data.tar.gz: 2c1c9369e7372cf6c21d1771870e805e43f10a8a082dc886d4a18e6f7ad70bd272e50bb3463c6cafeb8a37acf5a351b0d7efe5ba12b070c6ae45092d92e4d375
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Walheim
2
2
 
3
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/walheimlab/walheim-rb)
4
+
3
5
  A kubectl-style CLI for managing Docker-based homelab infrastructure across multiple machines.
4
6
 
5
7
  ## Overview
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'helpers'
3
+ require_relative "helpers"
4
4
 
5
5
  module Walheim
6
6
  module BaseCommand
@@ -9,35 +9,39 @@ module Walheim
9
9
  handler_info = Walheim::HandlerRegistry.get(kind)
10
10
  unless handler_info
11
11
  warn "Error: unknown kind '#{kind}'"
12
- warn ''
13
- warn 'Available kinds:'
12
+ warn ""
13
+ warn "Available kinds:"
14
14
  Walheim::HandlerRegistry.all_visible.each { |h| warn " #{h[:name]}" }
15
15
  exit 1
16
16
  end
17
17
 
18
18
  # 2. Check operation support
19
+ handler_class = handler_info[:handler]
19
20
  unless Walheim::HandlerRegistry.supports_operation?(kind, operation)
20
21
  warn "Error: #{operation} not supported for #{kind}"
21
22
  exit 1
22
23
  end
23
24
 
24
- # 3. Resolve data directory from context
25
+ # 3. Get operation metadata
26
+ op_metadata = handler_class.operation_info[operation]
27
+ unless op_metadata
28
+ warn "Error: operation metadata missing for #{operation}"
29
+ exit 1
30
+ end
31
+
32
+ # 4. Resolve data directory from context
25
33
  data_dir = resolve_data_dir(options, parent_options)
26
34
 
27
- # 4. Initialize handler
28
- handler = handler_info[:handler].new(data_dir: data_dir)
35
+ # 5. Initialize handler
36
+ handler = handler_class.new(data_dir: data_dir)
29
37
 
30
- # 5. Validate namespace requirements
31
- if handler.is_a?(Walheim::NamespacedResource)
32
- validate_namespace_options!(operation, kind, name, options)
33
- end
38
+ # 6. Validate namespace requirements
39
+ validate_namespace_requirements!(operation, kind, name, options, op_metadata) if handler.is_a?(Walheim::NamespacedResource)
34
40
 
35
- # 6. Dispatch to handler
36
- dispatch_to_handler(handler, operation, name, options, handler_info)
41
+ # 7. Dispatch to handler using metadata
42
+ dispatch_operation(handler, operation, name, options, handler_info, op_metadata)
37
43
  end
38
44
 
39
- private
40
-
41
45
  def self.resolve_data_dir(options, parent_options)
42
46
  # Merge options
43
47
  all_options = parent_options.merge(options)
@@ -51,125 +55,120 @@ module Walheim
51
55
  if context_name
52
56
  config.data_dir(context_name)
53
57
  elsif all_options[:data_dir]
54
- warn 'Warning: --data-dir is deprecated. Use contexts.'
58
+ warn "Warning: --data-dir is deprecated. Use contexts."
55
59
  all_options[:data_dir]
56
60
  else
57
- warn 'Error: No Walheim configuration found.'
58
- warn ''
59
- warn 'Create your first context:'
60
- warn ' whctl context new <name> --data-dir <path>'
61
+ warn "Error: No Walheim configuration found."
62
+ warn ""
63
+ warn "Create your first context:"
64
+ warn " whctl context new <name> --data-dir <path>"
61
65
  exit 1
62
66
  end
63
67
  rescue Walheim::Config::ConfigError, Walheim::Config::ValidationError
64
68
  if all_options[:data_dir]
65
- warn 'Warning: --data-dir is deprecated.'
69
+ warn "Warning: --data-dir is deprecated."
66
70
  all_options[:data_dir]
67
71
  else
68
- warn 'Error: No Walheim configuration found.'
69
- warn ''
70
- warn 'Create your first context:'
71
- warn ' whctl context new <name> --data-dir <path>'
72
+ warn "Error: No Walheim configuration found."
73
+ warn ""
74
+ warn "Create your first context:"
75
+ warn " whctl context new <name> --data-dir <path>"
72
76
  exit 1
73
77
  end
74
78
  end
75
79
  end
76
80
 
77
- def self.validate_namespace_options!(operation, kind, name, options)
78
- # Operations that require namespace or --all
79
- requires_namespace = [:get, :apply, :delete, :start, :pause, :stop, :logs, :import]
80
- return unless requires_namespace.include?(operation)
81
+ def self.validate_namespace_requirements!(operation, kind, _name, options, op_metadata)
82
+ dispatch_meta = op_metadata[:dispatch] || {}
83
+ namespace_handling = dispatch_meta[:namespace_handling]
81
84
 
82
- # get can use --all
83
- if operation == :get
85
+ case namespace_handling
86
+ when :optional_with_all
87
+ # Operations like get can use --all or -n
84
88
  return if options[:all] || options[:namespace]
85
- warn 'Error: either -n {namespace} or --all/-A flag is required'
86
- warn "Usage: whctl get #{kind} -n {namespace}"
87
- warn "Usage: whctl get #{kind} --all"
89
+
90
+ warn "Error: either -n {namespace} or --all/-A flag is required"
91
+ warn "Usage: whctl #{operation} #{kind} -n {namespace}"
92
+ warn "Usage: whctl #{operation} #{kind} --all"
88
93
  exit 1
89
- end
94
+ when :required
95
+ # Operations require namespace
96
+ return if options[:namespace]
90
97
 
91
- # Other operations require namespace
92
- unless options[:namespace]
93
- warn 'Error: -n {namespace} is required'
98
+ warn "Error: -n {namespace} is required"
94
99
  warn "Usage: whctl #{operation} #{kind} {name} -n {namespace}"
95
100
  exit 1
96
- end
97
- end
98
-
99
- def self.dispatch_to_handler(handler, operation, name, options, handler_info)
100
- case operation
101
- when :get
102
- dispatch_get(handler, name, options, handler_info)
103
- when :apply
104
- dispatch_apply(handler, name, options, handler_info)
105
- when :delete
106
- handler.delete(namespace: options[:namespace], name: name)
107
- when :create
108
- # Special case: create namespace
109
- handler.create(name: name, username: options[:username], hostname: options[:hostname])
110
- when :import
111
- # Special case: import app
112
- compose_manifest = Walheim::Helpers.read_yaml_input(options[:file])
113
- handler.import(namespace: options[:namespace], name: name, compose_manifest: compose_manifest)
114
- when :start, :pause, :stop
115
- handler.send(operation, namespace: options[:namespace], name: name)
116
- when :logs
117
- log_opts = {}
118
- log_opts[:follow] = options[:follow] if options[:follow]
119
- log_opts[:tail] = options[:tail] if options[:tail]
120
- log_opts[:timestamps] = options[:timestamps] if options[:timestamps]
121
- handler.logs(namespace: options[:namespace], name: name, **log_opts)
101
+ when nil
102
+ # No namespace validation needed
103
+ nil
122
104
  else
123
- warn "Error: operation #{operation} not implemented"
105
+ warn "Error: unknown namespace_handling: #{namespace_handling}"
124
106
  exit 1
125
107
  end
126
108
  end
127
109
 
128
- def self.dispatch_get(handler, name, options, handler_info)
129
- if handler.is_a?(Walheim::ClusterResource)
130
- result = handler.get(name: name)
131
- Walheim::Helpers.print_cluster_resources_table(result, handler_info[:name])
132
- else
133
- result = if options[:all]
134
- handler.get(namespace: nil, name: nil)
110
+ def self.dispatch_operation(handler, operation, name, options, handler_info, op_metadata)
111
+ dispatch_meta = op_metadata[:dispatch] || {}
112
+ method_name = dispatch_meta[:method] || operation
113
+
114
+ # Build method parameters
115
+ params = build_method_params(name, options, dispatch_meta)
116
+
117
+ # Call handler method
118
+ result = handler.send(method_name, **params)
119
+
120
+ # Handle output formatting
121
+ handle_output(result, handler, handler_info, options, dispatch_meta)
122
+ end
123
+
124
+ def self.build_method_params(name, options, dispatch_meta)
125
+ params = {}
126
+
127
+ # Add positional params (like :name)
128
+ positional_params = dispatch_meta[:params] || []
129
+ positional_params.each do |param_name|
130
+ case param_name
131
+ when :name
132
+ params[:name] = name
135
133
  else
136
- handler.get(namespace: options[:namespace], name: name)
134
+ params[param_name] = options[param_name]
137
135
  end
138
- Walheim::Helpers.print_resources_table(result, options[:all], handler_info[:name])
139
136
  end
140
- end
141
137
 
142
- def self.dispatch_apply(handler, name, options, handler_info)
143
- # Extract from manifest if -f provided
144
- if options[:file]
145
- manifest_data = Walheim::Helpers.read_yaml_input(options[:file])
138
+ # Add named params from options
139
+ named_params = dispatch_meta[:named_params] || {}
140
+ named_params.each do |param_name, option_key|
141
+ option_value = options[option_key]
146
142
 
147
- if handler.is_a?(Walheim::NamespacedResource)
148
- namespace = manifest_data['metadata']['namespace']
149
- name = manifest_data['metadata']['name']
143
+ # Handle file readers
144
+ if dispatch_meta[:file_reader] == param_name && option_value
145
+ option_value = Walheim::Helpers.read_yaml_input(option_value)
146
+ end
150
147
 
151
- unless namespace && name
152
- warn 'Error: Manifest must contain metadata.namespace and metadata.name'
153
- exit 1
154
- end
148
+ # Always include declared parameters (handlers expect them as keyword args)
149
+ params[param_name] = option_value
150
+ end
155
151
 
156
- handler.apply(namespace: namespace, name: name, manifest_source: options[:file])
152
+ params
153
+ end
154
+
155
+ def self.handle_output(result, handler, handler_info, options, dispatch_meta)
156
+ output_type = dispatch_meta[:output]
157
+
158
+ case output_type
159
+ when :table
160
+ # Table output for get operations
161
+ if handler.is_a?(Walheim::ClusterResource)
162
+ Walheim::Helpers.print_cluster_resources_table(result, handler_info[:name])
157
163
  else
158
- # Cluster resource
159
- name = manifest_data['metadata']['name']
160
- unless name
161
- warn 'Error: Manifest must contain metadata.name'
162
- exit 1
163
- end
164
- handler.apply(name: name, manifest_source: options[:file])
164
+ Walheim::Helpers.print_resources_table(result, options[:all], handler_info[:name])
165
165
  end
166
+ when nil
167
+ # No output handling needed (handler prints directly)
168
+ nil
166
169
  else
167
- # Apply from existing manifest in data dir
168
- if handler.is_a?(Walheim::NamespacedResource)
169
- handler.apply(namespace: options[:namespace], name: name)
170
- else
171
- handler.apply(name: name)
172
- end
170
+ warn "Error: unknown output type: #{output_type}"
171
+ exit 1
173
172
  end
174
173
  end
175
174
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'terminal-table'
4
- require 'yaml'
3
+ require "terminal-table"
4
+ require "yaml"
5
5
 
6
6
  module Walheim
7
7
  module Helpers
@@ -22,31 +22,31 @@ module Walheim
22
22
  summary_fields = result.first[:summary].keys
23
23
 
24
24
  # Build header row
25
- if all_namespaces
26
- headers = ['NAMESPACE', 'NAME'] + summary_fields.map(&:to_s).map(&:upcase)
25
+ headers = if all_namespaces
26
+ %w[NAMESPACE NAME] + summary_fields.map(&:to_s).map(&:upcase)
27
27
  else
28
- headers = ['NAME'] + summary_fields.map(&:to_s).map(&:upcase)
28
+ [ "NAME" ] + summary_fields.map(&:to_s).map(&:upcase)
29
29
  end
30
30
 
31
31
  # Build data rows
32
32
  rows = result.map do |resource|
33
33
  row = if all_namespaces
34
- [resource[:namespace], resource[:name]]
34
+ [ resource[:namespace], resource[:name] ]
35
35
  else
36
- [resource[:name]]
36
+ [ resource[:name] ]
37
37
  end
38
38
 
39
39
  # Add summary field values
40
- summary_values = summary_fields.map { |field| resource[:summary][field] || 'N/A' }
40
+ summary_values = summary_fields.map { |field| resource[:summary][field] || "N/A" }
41
41
  row + summary_values
42
42
  end
43
43
 
44
- all_rows = [headers] + rows
44
+ all_rows = [ headers ] + rows
45
45
 
46
46
  table = Terminal::Table.new do |t|
47
47
  t.rows = all_rows
48
48
  t.style = {
49
- border_x: '', border_y: '', border_i: '',
49
+ border_x: "", border_y: "", border_i: "",
50
50
  padding_left: 0, padding_right: 3,
51
51
  border_top: false, border_bottom: false,
52
52
  all_separators: false
@@ -73,23 +73,23 @@ module Walheim
73
73
  summary_fields = result.first[:summary].keys
74
74
 
75
75
  # Build header row (no NAMESPACE column for cluster resources)
76
- headers = ['NAME'] + summary_fields.map(&:to_s).map(&:upcase)
76
+ headers = [ "NAME" ] + summary_fields.map(&:to_s).map(&:upcase)
77
77
 
78
78
  # Build data rows
79
79
  rows = result.map do |resource|
80
- row = [resource[:name]]
80
+ row = [ resource[:name] ]
81
81
 
82
82
  # Add summary field values
83
- summary_values = summary_fields.map { |field| resource[:summary][field] || 'N/A' }
83
+ summary_values = summary_fields.map { |field| resource[:summary][field] || "N/A" }
84
84
  row + summary_values
85
85
  end
86
86
 
87
- all_rows = [headers] + rows
87
+ all_rows = [ headers ] + rows
88
88
 
89
89
  table = Terminal::Table.new do |t|
90
90
  t.rows = all_rows
91
91
  t.style = {
92
- border_x: '', border_y: '', border_i: '',
92
+ border_x: "", border_y: "", border_i: "",
93
93
  padding_left: 0, padding_right: 3,
94
94
  border_top: false, border_bottom: false,
95
95
  all_separators: false
@@ -101,9 +101,9 @@ module Walheim
101
101
 
102
102
  # Read YAML from file or stdin
103
103
  def self.read_yaml_input(file_path)
104
- if file_path == '-'
104
+ if file_path == "-"
105
105
  # Read from stdin
106
- YAML.load(STDIN.read)
106
+ YAML.safe_load($stdin.read)
107
107
  else
108
108
  # Read from file
109
109
  YAML.load_file(file_path)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'optparse'
4
- require 'terminal-table'
3
+ require "optparse"
4
+ require "terminal-table"
5
5
 
6
6
  module Walheim
7
7
  module LegacyContext
@@ -9,12 +9,12 @@ module Walheim
9
9
  options = {}
10
10
 
11
11
  # Remove 'context' from argv since it's already been identified
12
- argv.shift if argv[0] == 'context'
12
+ argv.shift if argv[0] == "context"
13
13
 
14
14
  # Parse global flags
15
15
  global_parser = OptionParser.new do |opts|
16
- opts.on('--whconfig PATH', 'Alternate config file path') { |v| options[:whconfig] = v }
17
- opts.on('-d DIR', '--data-dir DIR', 'Data directory') { |v| options[:data_dir] = v }
16
+ opts.on("--whconfig PATH", "Alternate config file path") { |v| options[:whconfig] = v }
17
+ opts.on("-d DIR", "--data-dir DIR", "Data directory") { |v| options[:data_dir] = v }
18
18
  end
19
19
 
20
20
  # Parse global flags (use parse! to consume all flags)
@@ -30,17 +30,17 @@ module Walheim
30
30
  context_name = argv[1]
31
31
 
32
32
  case subcommand
33
- when 'new'
33
+ when "new"
34
34
  # whctl context new {name} --data-dir {path}
35
35
  unless context_name
36
- warn 'Error: context name is required'
37
- warn 'Usage: whctl context new {name} --data-dir {path}'
36
+ warn "Error: context name is required"
37
+ warn "Usage: whctl context new {name} --data-dir {path}"
38
38
  exit 1
39
39
  end
40
40
 
41
41
  unless options[:data_dir]
42
- warn 'Error: --data-dir flag is required'
43
- warn 'Usage: whctl context new {name} --data-dir {path}'
42
+ warn "Error: --data-dir flag is required"
43
+ warn "Usage: whctl context new {name} --data-dir {path}"
44
44
  exit 1
45
45
  end
46
46
 
@@ -48,19 +48,19 @@ module Walheim
48
48
  expanded_data_dir = File.expand_path(options[:data_dir])
49
49
  unless Dir.exist?(expanded_data_dir)
50
50
  warn "Error: data directory '#{expanded_data_dir}' does not exist"
51
- warn ''
52
- warn 'Please create the directory first or provide a valid path'
51
+ warn ""
52
+ warn "Please create the directory first or provide a valid path"
53
53
  exit 1
54
54
  end
55
55
 
56
56
  # Check for namespaces subdirectory
57
- namespaces_path = File.join(expanded_data_dir, 'namespaces')
57
+ namespaces_path = File.join(expanded_data_dir, "namespaces")
58
58
  unless Dir.exist?(namespaces_path)
59
59
  warn "Warning: data directory does not contain 'namespaces' subdirectory"
60
60
  warn "Expected path: #{namespaces_path}"
61
- warn ''
62
- warn 'This directory should contain your homelab configuration.'
63
- warn 'Creating namespaces directory...'
61
+ warn ""
62
+ warn "This directory should contain your homelab configuration."
63
+ warn "Creating namespaces directory..."
64
64
  Dir.mkdir(namespaces_path)
65
65
  end
66
66
 
@@ -81,34 +81,34 @@ module Walheim
81
81
  exit 1
82
82
  end
83
83
 
84
- when 'list'
84
+ when "list"
85
85
  # whctl context list
86
86
  config = Walheim::Config.new(config_path: options[:whconfig])
87
87
 
88
88
  unless Walheim::Config.exists?(config_path: options[:whconfig])
89
- warn 'No Walheim configuration found.'
90
- warn ''
91
- warn 'Create your first context:'
92
- warn ' whctl context new <name> --data-dir <path>'
89
+ warn "No Walheim configuration found."
90
+ warn ""
91
+ warn "Create your first context:"
92
+ warn " whctl context new <name> --data-dir <path>"
93
93
  exit 1
94
94
  end
95
95
 
96
96
  contexts = config.list_contexts
97
97
  if contexts.empty?
98
- puts 'No contexts configured.'
98
+ puts "No contexts configured."
99
99
  exit 0
100
100
  end
101
101
 
102
102
  rows = contexts.map do |ctx|
103
- active_marker = ctx['active'] ? '*' : ''
104
- [active_marker, ctx['name'], ctx['dataDir']]
103
+ active_marker = ctx["active"] ? "*" : ""
104
+ [ active_marker, ctx["name"], ctx["dataDir"] ]
105
105
  end
106
106
 
107
107
  table = Terminal::Table.new do |t|
108
- t.headings = ['CURRENT', 'NAME', 'DATA DIRECTORY']
108
+ t.headings = [ "CURRENT", "NAME", "DATA DIRECTORY" ]
109
109
  t.rows = rows
110
110
  t.style = {
111
- border_x: '', border_y: '', border_i: '',
111
+ border_x: "", border_y: "", border_i: "",
112
112
  padding_left: 0, padding_right: 3,
113
113
  border_top: false, border_bottom: false,
114
114
  all_separators: false
@@ -117,11 +117,11 @@ module Walheim
117
117
 
118
118
  puts table
119
119
 
120
- when 'use'
120
+ when "use"
121
121
  # whctl context use {name}
122
122
  unless context_name
123
- warn 'Error: context name is required'
124
- warn 'Usage: whctl context use {name}'
123
+ warn "Error: context name is required"
124
+ warn "Usage: whctl context use {name}"
125
125
  exit 1
126
126
  end
127
127
 
@@ -136,15 +136,15 @@ module Walheim
136
136
  exit 1
137
137
  end
138
138
 
139
- when 'current'
139
+ when "current"
140
140
  # whctl context current
141
141
  config = Walheim::Config.new(config_path: options[:whconfig])
142
142
 
143
143
  unless Walheim::Config.exists?(config_path: options[:whconfig])
144
- warn 'No Walheim configuration found.'
145
- warn ''
146
- warn 'Create your first context:'
147
- warn ' whctl context new <name> --data-dir <path>'
144
+ warn "No Walheim configuration found."
145
+ warn ""
146
+ warn "Create your first context:"
147
+ warn " whctl context new <name> --data-dir <path>"
148
148
  exit 1
149
149
  end
150
150
 
@@ -152,22 +152,22 @@ module Walheim
152
152
  puts "Current context: #{config.current_context}"
153
153
  puts "Data directory: #{config.data_dir}"
154
154
  else
155
- puts 'No active context selected.'
156
- puts ''
157
- puts 'Available contexts:'
155
+ puts "No active context selected."
156
+ puts ""
157
+ puts "Available contexts:"
158
158
  config.list_contexts.each do |ctx|
159
159
  puts " - #{ctx['name']}"
160
160
  end
161
- puts ''
162
- puts 'Select a context:'
163
- puts ' whctl context use <context-name>'
161
+ puts ""
162
+ puts "Select a context:"
163
+ puts " whctl context use <context-name>"
164
164
  end
165
165
 
166
- when 'delete'
166
+ when "delete"
167
167
  # whctl context delete {name}
168
168
  unless context_name
169
- warn 'Error: context name is required'
170
- warn 'Usage: whctl context delete {name}'
169
+ warn "Error: context name is required"
170
+ warn "Usage: whctl context delete {name}"
171
171
  exit 1
172
172
  end
173
173
 
@@ -180,10 +180,10 @@ module Walheim
180
180
 
181
181
  # Show warning if this was the active context
182
182
  unless config.current_context
183
- puts ''
184
- puts 'Note: This was your active context.'
185
- puts 'Select a new context with:'
186
- puts ' whctl context use <context-name>'
183
+ puts ""
184
+ puts "Note: This was your active context."
185
+ puts "Select a new context with:"
186
+ puts " whctl context use <context-name>"
187
187
  end
188
188
  rescue Walheim::Config::ConfigError => e
189
189
  warn "Error: #{e.message}"
@@ -192,13 +192,13 @@ module Walheim
192
192
 
193
193
  else
194
194
  warn "Error: unknown context subcommand '#{subcommand}'"
195
- warn ''
196
- warn 'Available context commands:'
197
- warn ' whctl context new {name} --data-dir {path}'
198
- warn ' whctl context list'
199
- warn ' whctl context use {name}'
200
- warn ' whctl context current'
201
- warn ' whctl context delete {name}'
195
+ warn ""
196
+ warn "Available context commands:"
197
+ warn " whctl context new {name} --data-dir {path}"
198
+ warn " whctl context list"
199
+ warn " whctl context use {name}"
200
+ warn " whctl context current"
201
+ warn " whctl context delete {name}"
202
202
  exit 1
203
203
  end
204
204
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'base_command'
3
+ require_relative "base_command"
4
4
 
5
5
  module Walheim
6
6
  module ResourceCommand
@@ -10,7 +10,7 @@ module Walheim
10
10
  return if handlers.empty?
11
11
 
12
12
  # Build command description
13
- descriptions = handlers.map { |h| h[:name] }.join(', ')
13
+ descriptions = handlers.map { |h| h[:name] }.join(", ")
14
14
  desc_text = "#{operation.to_s.capitalize} resources (#{descriptions})"
15
15
 
16
16
  # Define Thor command
@@ -21,12 +21,10 @@ module Walheim
21
21
  all_options = {}
22
22
  handlers.each do |handler_info|
23
23
  handler_class = handler_info[:handler]
24
- if handler_class.respond_to?(:operation_info)
25
- op_metadata = handler_class.operation_info[operation]
26
- if op_metadata && op_metadata[:options]
27
- all_options.merge!(op_metadata[:options])
28
- end
29
- end
24
+ next unless handler_class.respond_to?(:operation_info)
25
+
26
+ op_metadata = handler_class.operation_info[operation]
27
+ all_options.merge!(op_metadata[:options]) if op_metadata && op_metadata[:options]
30
28
  end
31
29
 
32
30
  # Register options with Thor
@@ -43,7 +41,11 @@ module Walheim
43
41
  kind: kind,
44
42
  name: name,
45
43
  options: options,
46
- parent_options: self.class.class_options.transform_keys(&:to_sym).transform_values { |v| options[v.name] rescue nil }
44
+ parent_options: self.class.class_options.transform_keys(&:to_sym).transform_values do |v|
45
+ options[v.name]
46
+ rescue StandardError
47
+ nil
48
+ end
47
49
  )
48
50
  end
49
51
  end