Class: Dispatcher
- Inherits:
-
Object
- Object
- Dispatcher
- Defined in:
- lib/cli-dispatcher.rb
Overview
Constructs a program that can operate a number of user-provided commands. To use this class, subclass it and define methods of the form:
def cmd_name(args...)
Then create an instance of the class and call one of the dispatch methods, typically:
[DispatcherSubclass].new.dispatch_argv
To provide help and/or a category for a command, define the methods
def help_name; [string] end
def cat_name; [string] end
For the help command, the first line should be a short description of the command, which will be used in a summary table describing the command. For the category, all commands with the same category will be grouped together in the help summary display.
This class incorporates optparse, providing the commands setup_options and add_options to pass through options specifications.
Class Method Summary collapse
-
.add_structured_commands ⇒ Object
Adds commands relevant when this dispatcher uses Structured data inputs.
Instance Method Summary collapse
-
#add_options(opts) ⇒ Object
Given an OptionParser object, add options.
- #cmd_explain(class_name) ⇒ Object
- #cmd_help(cmd = nil, all: true) ⇒ Object
-
#cmd_interactive ⇒ Object
Runs the dispatcher in interactive mode, in which command lines are read from a prompt.
- #cmd_template(class_name) ⇒ Object
-
#dispatch(cmd, *args) ⇒ Object
Dispatches a single command with given arguments.
-
#dispatch_argv(default_interactive = false) ⇒ Object
Reads ARGV and dispatches a command.
- #help_cmds(cmds) ⇒ Object
- #help_explain ⇒ Object
- #help_help ⇒ Object
- #help_interactive ⇒ Object
- #help_string(cmd, all: true) ⇒ Object
- #help_template ⇒ Object
-
#interactive_prompt ⇒ Object
Returns the string for the interactive prompt.
-
#setup_options ⇒ Object
Receives options, passing them to OptionParser.
- #signature_string(cmd) ⇒ Object
Class Method Details
.add_structured_commands ⇒ Object
Adds commands relevant when this dispatcher uses Structured data inputs.
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/cli-dispatcher.rb', line 197 def self.add_structured_commands def help_explain return <<~EOF Displays an explanation of a Structured class. Use this to assist in generating or checking a Rubric file. EOF end def cmd_explain(class_name) c = Object.const_get(class_name) unless c.is_a?(Class) && c.include?(Structured) raise "Invalid class #{class_name}" end c.explain end def help_template return <<~EOF Produces a template for the given Structured class. EOF end def cmd_template(class_name) c = Object.const_get(class_name) unless c.is_a?(Class) && c.include?(Structured) raise("Invalid class #{class_name}") end puts c.template end end |
Instance Method Details
#add_options(opts) ⇒ Object
Given an OptionParser object, add options. By default, this method does nothing. The usage of this method, in contrast to #setup_options, is to override this method, invoking calls to the opts argument to add options. The method will be called automatically when the Dispatcher is invoked.
249 250 |
# File 'lib/cli-dispatcher.rb', line 249 def (opts) end |
#cmd_explain(class_name) ⇒ Object
206 207 208 209 210 211 212 |
# File 'lib/cli-dispatcher.rb', line 206 def cmd_explain(class_name) c = Object.const_get(class_name) unless c.is_a?(Class) && c.include?(Structured) raise "Invalid class #{class_name}" end c.explain end |
#cmd_help(cmd = nil, all: true) ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/cli-dispatcher.rb', line 121 def cmd_help(cmd = nil, all: true) if cmd warn("") warn(help_string(cmd)) warn("") exit(1) end warn("Run 'help [command]' for further help on that command.") warn("") grouped_methods = methods.map { |m| s = m.to_s s.start_with?("cmd_") ? s.delete_prefix("cmd_") : nil }.compact.group_by { |m| cat_m = "cat_#{m}".to_sym respond_to?(cat_m) ? send(cat_m) : nil } help_cmds(grouped_methods.delete(nil)) if grouped_methods.include?(nil) grouped_methods.sort.each do |cat, cmds| warn("\n#{cat}\n\n") help_cmds(cmds) end end |
#cmd_interactive ⇒ Object
Runs the dispatcher in interactive mode, in which command lines are read from a prompt.
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/cli-dispatcher.rb', line 166 def cmd_interactive stty_save = `stty -g`.chomp loop do begin buf = Reline.readline(interactive_prompt, true) exit unless buf args = buf.shellsplit next if args.empty? exit if args.first == 'exit' dispatch(*args) rescue Interrupt system("stty", stty_save) exit rescue STDERR.puts $!. end end end |
#cmd_template(class_name) ⇒ Object
220 221 222 223 224 225 226 |
# File 'lib/cli-dispatcher.rb', line 220 def cmd_template(class_name) c = Object.const_get(class_name) unless c.is_a?(Class) && c.include?(Structured) raise("Invalid class #{class_name}") end puts c.template end |
#dispatch(cmd, *args) ⇒ Object
Dispatches a single command with given arguments. If the command is not found, then issues a help warning. Returns true or false depending on whether the command executed successfully.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/cli-dispatcher.rb', line 68 def dispatch(cmd, *args) cmd_sym = "cmd_#{cmd}".to_sym begin if respond_to?(cmd_sym) send(cmd_sym, *args) return true else warn("Unknown command #{cmd_sym}. Run 'help' for a list of commands.") return false end rescue ArgumentError if $!.backtrace_locations.first.base_label == cmd_sym.to_s warn("#{cmd}: wrong number of arguments.") warn("Usage: #{signature_string(cmd)}") else raise end return false end end |
#dispatch_argv(default_interactive = false) ⇒ Object
Reads ARGV and dispatches a command. If no arguments are given, an appropriate warning is issued and the program terminates.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/cli-dispatcher.rb', line 36 def dispatch_argv(default_interactive = false) @option_parser ||= OptionParser.new (@option_parser) @option_parser. = <<~EOF Usage: #{File.basename($0)} [options] command [arguments...] Run '#{File.basename($0)} help' for a list of commands. Options: EOF @option_parser.on_tail('-h', '--help', 'Show this help') do warn(@option_parser) warn("\nCommands:") cmd_help exit 1 end @option_parser.parse! if !ARGV.empty? exit dispatch(*ARGV) ? 0 : 1 elsif default_interactive cmd_interactive else STDERR.puts(@option_parser) exit 1 end end |
#help_cmds(cmds) ⇒ Object
148 149 150 151 152 153 154 155 156 |
# File 'lib/cli-dispatcher.rb', line 148 def help_cmds(cmds) cmds.sort.each do |cmd| warn(TextTools.line_break( help_string(cmd, all: false), prefix: " " * 12, first_prefix: cmd.ljust(11) + ' ', )) end end |
#help_explain ⇒ Object
198 199 200 201 202 203 204 |
# File 'lib/cli-dispatcher.rb', line 198 def help_explain return <<~EOF Displays an explanation of a Structured class. Use this to assist in generating or checking a Rubric file. EOF end |
#help_help ⇒ Object
113 114 115 116 117 118 119 |
# File 'lib/cli-dispatcher.rb', line 113 def help_help return <<~EOF Displays help on commands. Run 'help [command]' for further help on that command. EOF end |
#help_interactive ⇒ Object
158 159 160 |
# File 'lib/cli-dispatcher.rb', line 158 def help_interactive return "Start an interactive shell for entering commands." end |
#help_string(cmd, all: true) ⇒ Object
89 90 91 92 93 94 95 96 97 |
# File 'lib/cli-dispatcher.rb', line 89 def help_string(cmd, all: true) cmd_sym = "help_#{cmd}".to_sym return signature_string(cmd) unless respond_to?(cmd_sym) if all return $0 + " " + signature_string(cmd) + "\n\n" + send(cmd_sym) else return send(cmd_sym).to_s.split("\n", 2).first end end |
#help_template ⇒ Object
214 215 216 217 218 |
# File 'lib/cli-dispatcher.rb', line 214 def help_template return <<~EOF Produces a template for the given Structured class. EOF end |
#interactive_prompt ⇒ Object
Returns the string for the interactive prompt. Subclasses can override this method to offer more detailed prompts.
189 190 191 |
# File 'lib/cli-dispatcher.rb', line 189 def interactive_prompt return "#{File.basename($0)}> " end |
#setup_options ⇒ Object
Receives options, passing them to OptionParser. The options are processed when dispatch_argv is called. The usage of this method is that after the Dispatcher object is created, this method is called to instantiate the options for the class. See #add_options for another way of doing this.
The banner and -h/–help options will be added automatically.
237 238 239 240 241 |
# File 'lib/cli-dispatcher.rb', line 237 def @option_parser = OptionParser.new do |opts| yield(opts) end end |
#signature_string(cmd) ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/cli-dispatcher.rb', line 99 def signature_string(cmd) cmd_sym = "cmd_#{cmd}".to_sym raise "No such command" unless respond_to?(cmd_sym) return cmd + " " + method(cmd_sym).parameters.map { |type, name| case type when :req then name.to_s when :opt then "[#{name}]" when :rest then "*#{name}" when :keyreq, :key, :keyrest, :block then nil else raise "Unknown parameter type #{type}" end }.compact.join(" ") end |