cli-dispatcher 1.2.4 → 1.2.6

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: 3f480b01f8af6a9af5c131e3e25081edd7e69fff4caf3daa22ea10efbc49b4f4
4
- data.tar.gz: 2eea3e4b6791f4babe182b56c15607b7fa5eb532106c00505fb26b8b0ee0f0d9
3
+ metadata.gz: be7440d7ccb3b1dfd9b90eae4b32d62597a864e4dd6676deaf9dc36e08d08470
4
+ data.tar.gz: 3338fab7655d4074368786f822ffac62c9b12d3e74ea98ef7bc554f44b248e93
5
5
  SHA512:
6
- metadata.gz: b4ea432b818502f65c048a0911aef575b42ed17d2cff7e42f97382f5b3f6ce77e8cf52964f9ca43eb85b467c4f3a67bcc957a14d51b82b5fe37ff120524f03a9
7
- data.tar.gz: 9e5dbff959cc60f44ebadf445cb3257fea82403c2db4f053a7f241deb0f7a290cc5b5f02e3810e8ca48ba5ea16893765cfbe8e73c8b7f73b12c7462004b56ef7
6
+ metadata.gz: 16e45a2046ebbd9679789549b53e15afb4b4cd556bf1857feb25e61c4d90f55a4bb4d555f242fc66af926550566aa4e75fcf5bd693133475c00070ffbc102b91
7
+ data.tar.gz: 2f09de799fcca6242233880aa6452abf600e3d179f6ddd7b9b3d03747980d327e68e56cb8609264c3ac7b29397f5285d2264c4b96ef55c5d1eb4c74e05a26cb0
@@ -1,5 +1,7 @@
1
1
  require 'optparse'
2
2
  require 'texttools'
3
+ require 'reline'
4
+ require 'shellwords'
3
5
 
4
6
  #
5
7
  # Constructs a program that can operate a number of user-provided commands. To
@@ -31,12 +33,12 @@ class Dispatcher
31
33
  # Reads ARGV and dispatches a command. If no arguments are given, an
32
34
  # appropriate warning is issued and the program terminates.
33
35
  #
34
- def dispatch_argv
36
+ def dispatch_argv(default_interactive = false)
35
37
  @option_parser ||= OptionParser.new
36
38
  add_options(@option_parser)
37
39
  @option_parser.banner = <<~EOF
38
- Usage: #$0 [options] command [arguments...]
39
- Run '#$0 help' for a list of commands.
40
+ Usage: #{File.basename($0)} [options] command [arguments...]
41
+ Run '#{File.basename($0)} help' for a list of commands.
40
42
 
41
43
  Options:
42
44
  EOF
@@ -48,35 +50,39 @@ class Dispatcher
48
50
  end
49
51
 
50
52
  @option_parser.parse!
51
- if ARGV.empty?
53
+ if !ARGV.empty?
54
+ exit dispatch(*ARGV) ? 0 : 1
55
+ elsif default_interactive
56
+ cmd_interactive
57
+ else
52
58
  STDERR.puts(@option_parser)
53
59
  exit 1
54
60
  end
55
- dispatch(*ARGV)
56
61
  end
57
62
 
58
63
  #
59
64
  # Dispatches a single command with given arguments. If the command is not
60
- # found, then issues a help warning.
65
+ # found, then issues a help warning. Returns true or false depending on
66
+ # whether the command executed successfully.
61
67
  #
62
68
  def dispatch(cmd, *args)
63
69
  cmd_sym = "cmd_#{cmd}".to_sym
64
70
  begin
65
71
  if respond_to?(cmd_sym)
66
72
  send(cmd_sym, *args)
73
+ return true
67
74
  else
68
- warn("Usage: #$0 [options] command [arguments...]")
69
- warn("Run '#$0 help' for a list of commands.")
70
- exit(1)
75
+ warn("Unknown command #{cmd_sym}. Run 'help' for a list of commands.")
76
+ return false
71
77
  end
72
78
  rescue ArgumentError
73
79
  if $!.backtrace_locations.first.base_label == cmd_sym.to_s
74
- warn("#{cmd}: wrong number of arguments")
80
+ warn("#{cmd}: wrong number of arguments.")
75
81
  warn("Usage: #{signature_string(cmd)}")
76
- exit(1)
77
82
  else
78
- raise $!
83
+ raise
79
84
  end
85
+ return false
80
86
  end
81
87
  end
82
88
 
@@ -143,12 +149,48 @@ class Dispatcher
143
149
  cmds.sort.each do |cmd|
144
150
  warn(TextTools.line_break(
145
151
  help_string(cmd, all: false),
146
- prefix: " " * 11,
147
- first_prefix: cmd.ljust(10) + ' ',
152
+ prefix: " " * 12,
153
+ first_prefix: cmd.ljust(11) + ' ',
148
154
  ))
149
155
  end
150
156
  end
151
157
 
158
+ def help_interactive
159
+ return "Start an interactive shell for entering commands."
160
+ end
161
+
162
+ #
163
+ # Runs the dispatcher in interactive mode, in which command lines are read
164
+ # from a prompt.
165
+ #
166
+ def cmd_interactive
167
+ stty_save = `stty -g`.chomp
168
+ loop do
169
+ begin
170
+ buf = Reline.readline(interactive_prompt, true)
171
+ exit unless buf
172
+ args = buf.shellsplit
173
+ next if args.empty?
174
+ exit if args.first == 'exit'
175
+ dispatch(*args)
176
+ rescue Interrupt
177
+ system("stty", stty_save)
178
+ exit
179
+ rescue
180
+ STDERR.puts $!.full_message
181
+ end
182
+ end
183
+ end
184
+
185
+ #
186
+ # Returns the string for the interactive prompt. Subclasses can override this
187
+ # method to offer more detailed prompts.
188
+ #
189
+ def interactive_prompt
190
+ return "#{File.basename($0)}> "
191
+ end
192
+
193
+
152
194
  #
153
195
  # Adds commands relevant when this dispatcher uses Structured data inputs.
154
196
  #
@@ -185,12 +227,16 @@ class Dispatcher
185
227
  end
186
228
 
187
229
 
188
- # Receives options, passing them to OptionParser. The options are processed
189
- # when dispatch_argv is called. The usage of this method is that after the
190
- # Dispatcher object is created, this method is called to instantiate the
191
- # options for the class. See #add_options for another way of doing this.
192
230
  #
193
- # The banner and -h/--help options will be added automatically.
231
+ # Creates an OptionParser object for this Dispatcher. The options for the
232
+ # OptionParser are defined in a block passed to this method. The block
233
+ # receives one argument, which is the OptionParser object being created.
234
+ #
235
+ # The banner and -h/--help options will be added automatically to the created
236
+ # OptionParser object.
237
+ #
238
+ # For a slightly simpler way to set up options for this Dispatcher object, see
239
+ # the add_options method.
194
240
  #
195
241
  def setup_options
196
242
  @option_parser = OptionParser.new do |opts|
@@ -199,10 +245,14 @@ class Dispatcher
199
245
  end
200
246
 
201
247
  #
202
- # Given an OptionParser object, add options. By default, this method does
203
- # nothing. The usage of this method, in contrast to #setup_options, is to
204
- # override this method, invoking calls to the +opts+ argument to add options.
205
- # The method will be called automatically when the Dispatcher is invoked.
248
+ # Adds command-line options for this class. By default, this method does
249
+ # nothing. Subclasses may override this method to add options. The method will
250
+ # be automatically invoked during a dispatch_argv call, thereby constructing
251
+ # an OptionParser object to handle the command-line arguments.
252
+ #
253
+ # The argument to this method is an OptionParser object, to which the desired
254
+ # options may be added. The banner and -h/--help options will be added
255
+ # automatically to the OptionParser object.
206
256
  #
207
257
  def add_options(opts)
208
258
  end
data/lib/structured.rb CHANGED
@@ -461,7 +461,7 @@ module Structured
461
461
  Structured.trace(key.to_s) do
462
462
  val = hash[key] || hash[key.to_s]
463
463
  cval = process_value(obj, val, data)
464
- apply_val(obj, key, cval) if cval
464
+ apply_val(obj, key, cval) unless cval.nil?
465
465
  end
466
466
  end
467
467
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cli-dispatcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
4
+ version: 1.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Duan
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-05-20 00:00:00.000000000 Z
10
+ date: 2025-12-04 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: |
13
13
  Library for creating command-line programs that accept commands. Also