ktec-commander 3.3.1

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.
@@ -0,0 +1,8 @@
1
+
2
+ module Commander
3
+ module HelpFormatter
4
+ autoload :Base, 'commander/help_formatters/base'
5
+ autoload :Terminal, 'commander/help_formatters/terminal'
6
+ autoload :TerminalCompact, 'commander/help_formatters/terminal_compact'
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+
2
+ module Commander
3
+
4
+ ##
5
+ # = Help Formatter
6
+ #
7
+ # Commander's help formatters control the output when
8
+ # either the help command, or --help switch are called.
9
+ # The default formatter is Commander::HelpFormatter::Terminal.
10
+
11
+ module HelpFormatter
12
+ class Base
13
+ def initialize runner; @runner = runner end
14
+ def render; 'Implement global help here' end
15
+ def render_command command; "Implement help for #{command.name} here" end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+
2
+ require 'erb'
3
+
4
+ module Commander
5
+ module HelpFormatter
6
+ class Terminal < Base
7
+ def render
8
+ template(:help).result @runner.get_binding
9
+ end
10
+
11
+ def render_command command
12
+ template(:command_help).result command.get_binding
13
+ end
14
+
15
+ def template name
16
+ ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal', "#{name}.erb")), nil, '-')
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+
2
+ <%= $terminal.color "NAME", :bold %>:
3
+
4
+ <%= @name %>
5
+
6
+ <%= $terminal.color "DESCRIPTION", :bold %>:
7
+
8
+ <%= @description || @summary || 'No description.' -%>
9
+
10
+ <% if @syntax -%>
11
+
12
+ <%= $terminal.color "SYNOPSIS", :bold %>:
13
+
14
+ <%= @syntax -%>
15
+
16
+ <% end -%>
17
+ <% unless @examples.empty? -%>
18
+
19
+ <%= $terminal.color "EXAMPLES", :bold %>:
20
+ <% for description, command in @examples -%>
21
+
22
+ # <%= description %>
23
+ <%= command %>
24
+ <% end -%>
25
+ <% end -%>
26
+ <% unless @options.empty? -%>
27
+
28
+ <%= $terminal.color "OPTIONS", :bold %>:
29
+ <% for option in @options -%>
30
+
31
+ <%= option[:switches].join ', ' %>
32
+ <%= option[:description] %>
33
+ <% end -%>
34
+ <% end -%>
35
+
@@ -0,0 +1,37 @@
1
+
2
+ <%= $terminal.color "NAME", :bold %>:
3
+
4
+ <%= program :name %>
5
+
6
+ <%= $terminal.color "DESCRIPTION", :bold %>:
7
+
8
+ <%= program :description %>
9
+
10
+ <%= $terminal.color "COMMANDS", :bold %>:
11
+ <% for name, command in @commands -%>
12
+ <% unless alias? name %>
13
+ <%= "%-20s %s" % [command.name, command.summary || command.description] -%>
14
+ <% end -%>
15
+ <% end %>
16
+ <% unless @aliases.empty? %>
17
+ <%= $terminal.color "ALIASES", :bold %>:
18
+ <% for alias_name, args in @aliases %>
19
+ <%= "%-20s %s %s" % [alias_name, command(alias_name).name, args.join(' ')] -%>
20
+ <% end %>
21
+ <% end %>
22
+ <% unless @options.empty? -%>
23
+ <%= $terminal.color "GLOBAL OPTIONS", :bold %>:
24
+ <% for option in @options -%>
25
+
26
+ <%= option[:switches].join ', ' %>
27
+ <%= option[:description] %>
28
+ <% end -%>
29
+ <% end -%>
30
+ <% if program :help -%>
31
+ <% for title, body in program(:help) %>
32
+ <%= $terminal.color title.to_s.upcase, :bold %>:
33
+
34
+ <%= body %>
35
+ <% end -%>
36
+ <% end -%>
37
+
@@ -0,0 +1,12 @@
1
+
2
+ require 'erb'
3
+
4
+ module Commander
5
+ module HelpFormatter
6
+ class TerminalCompact < Terminal
7
+ def template name
8
+ ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal_compact', "#{name}.erb")), nil, '-')
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+
2
+ <%= @name %>
3
+ <% if @description || @summary -%>
4
+
5
+ <%= @description || @summary %>
6
+ <% end -%>
7
+ <% if @syntax -%>
8
+
9
+ Usage: <%= @syntax %>
10
+ <% end -%>
11
+ <% unless @examples.empty? -%>
12
+
13
+ Examples:
14
+ <% for description, command in @examples -%>
15
+
16
+ # <%= description %>
17
+ <%= command %>
18
+ <% end -%>
19
+ <% end -%>
20
+ <% unless @options.empty? -%>
21
+
22
+ Options:
23
+ <% for option in @options -%>
24
+ <%= "%-20s %s" % [option[:switches].join(', '), option[:description]] %>
25
+ <% end -%>
26
+ <% end -%>
27
+
@@ -0,0 +1,30 @@
1
+
2
+ <%= program :name %>
3
+
4
+ <%= program :description %>
5
+
6
+ Commands:
7
+ <% for name, command in @commands -%>
8
+ <% unless alias? name -%>
9
+ <%= "%-20s %s" % [command.name, command.summary || command.description] %>
10
+ <% end -%>
11
+ <% end -%>
12
+ <% unless @aliases.empty? %>
13
+ Aliases:
14
+ <% for alias_name, args in @aliases -%>
15
+ <%= "%-20s %s %s" % [alias_name, command(alias_name).name, args.join(' ')] %>
16
+ <% end -%>
17
+ <% end %>
18
+ <% unless @options.empty? -%>
19
+ Global Options:
20
+ <% for option in @options -%>
21
+ <%= "%-20s %s" % [option[:switches].join(', '), option[:description]] -%>
22
+ <% end -%>
23
+ <% end -%>
24
+ <% if program :help -%>
25
+ <% for title, body in program(:help) %>
26
+ <%= title %>:
27
+ <%= body %>
28
+ <% end %>
29
+ <% end -%>
30
+
@@ -0,0 +1,10 @@
1
+
2
+ require 'commander'
3
+ require 'commander/delegates'
4
+
5
+ include Commander::UI
6
+ include Commander::UI::AskForClass
7
+ include Commander::Delegates
8
+
9
+ $terminal.wrap_at = HighLine::SystemExtensions.terminal_size.first - 5 rescue 80 if $stdin.tty?
10
+ at_exit { run! }
@@ -0,0 +1,387 @@
1
+
2
+ require 'optparse'
3
+
4
+ module Commander
5
+ class Runner
6
+
7
+ #--
8
+ # Exceptions
9
+ #++
10
+
11
+ class CommandError < StandardError; end
12
+ class InvalidCommandError < CommandError; end
13
+
14
+ ##
15
+ # Array of commands.
16
+
17
+ attr_reader :commands
18
+
19
+ ##
20
+ # Global options.
21
+
22
+ attr_reader :options
23
+
24
+ ##
25
+ # Hash of help formatter aliases.
26
+
27
+ attr_reader :help_formatter_aliases
28
+
29
+ ##
30
+ # Initialize a new command runner. Optionally
31
+ # supplying _args_ for mocking, or arbitrary usage.
32
+
33
+ def initialize args = ARGV
34
+ @args, @commands, @aliases, @options = args, {}, {}, []
35
+ @help_formatter_aliases = help_formatter_alias_defaults
36
+ @program = program_defaults
37
+ create_default_commands
38
+ end
39
+
40
+ ##
41
+ # Return singleton Runner instance.
42
+
43
+ def self.instance
44
+ @singleton ||= new
45
+ end
46
+
47
+ ##
48
+ # Run command parsing and execution process.
49
+
50
+ def run!
51
+ trace = false
52
+ require_program :version, :description
53
+ trap('INT') { abort program(:int_message) }
54
+ global_option('-h', '--help', 'Display help documentation') { command(:help).run *@args[1..-1]; return }
55
+ global_option('-v', '--version', 'Display version information') { say version; return }
56
+ global_option('-t', '--trace', 'Display backtrace when an error occurs') { trace = true }
57
+ parse_global_options
58
+ remove_global_options options, @args
59
+ unless trace
60
+ begin
61
+ run_active_command
62
+ rescue InvalidCommandError => e
63
+ abort "#{e}. Use --help for more information"
64
+ rescue \
65
+ OptionParser::InvalidOption,
66
+ OptionParser::InvalidArgument,
67
+ OptionParser::MissingArgument => e
68
+ abort e
69
+ rescue => e
70
+ abort "error: #{e}. Use --trace to view backtrace"
71
+ end
72
+ else
73
+ run_active_command
74
+ end
75
+ end
76
+
77
+ ##
78
+ # Return program version.
79
+
80
+ def version
81
+ '%s %s' % [program(:name), program(:version)]
82
+ end
83
+
84
+ ##
85
+ # Assign program information.
86
+ #
87
+ # === Examples
88
+ #
89
+ # # Set data
90
+ # program :name, 'Commander'
91
+ # program :version, Commander::VERSION
92
+ # program :description, 'Commander utility program.'
93
+ # program :help, 'Copyright', '2008 TJ Holowaychuk'
94
+ # program :help, 'Anything', 'You want'
95
+ # program :int_message 'Bye bye!'
96
+ # program :help_formatter, :compact
97
+ # program :help_formatter, Commander::HelpFormatter::TerminalCompact
98
+ #
99
+ # # Get data
100
+ # program :name # => 'Commander'
101
+ #
102
+ # === Keys
103
+ #
104
+ # :version (required) Program version triple, ex: '0.0.1'
105
+ # :description (required) Program description
106
+ # :name Program name, defaults to basename of executable
107
+ # :help_formatter Defaults to Commander::HelpFormatter::Terminal
108
+ # :help Allows addition of arbitrary global help blocks
109
+ # :int_message Message to display when interrupted (CTRL + C)
110
+ #
111
+
112
+ def program key, *args
113
+ if key == :help and !args.empty?
114
+ @program[:help] ||= {}
115
+ @program[:help][args.first] = args.at(1)
116
+ elsif key == :help_formatter && !args.empty?
117
+ @program[key] = (@help_formatter_aliases[args.first] || args.first)
118
+ else
119
+ @program[key] = *args unless args.empty?
120
+ @program[key]
121
+ end
122
+ end
123
+
124
+ ##
125
+ # Creates and yields a command instance when a block is passed.
126
+ # Otherwise attempts to return the command, raising InvalidCommandError when
127
+ # it does not exist.
128
+ #
129
+ # === Examples
130
+ #
131
+ # command :my_command do |c|
132
+ # c.when_called do |args|
133
+ # # Code
134
+ # end
135
+ # end
136
+ #
137
+
138
+ def command name, &block
139
+ yield add_command(Commander::Command.new(name)) if block
140
+ @commands[name.to_s]
141
+ end
142
+
143
+ ##
144
+ # Add a global option; follows the same syntax as Command#option
145
+ # This would be used for switches such as --version, --trace, etc.
146
+
147
+ def global_option *args, &block
148
+ switches, description = Runner.separate_switches_from_description *args
149
+ @options << {
150
+ :args => args,
151
+ :proc => block,
152
+ :switches => switches,
153
+ :description => description,
154
+ }
155
+ end
156
+
157
+ ##
158
+ # Alias command _name_ with _alias_name_. Optionally _args_ may be passed
159
+ # as if they were being passed straight to the original command via the command-line.
160
+
161
+ def alias_command alias_name, name, *args
162
+ @commands[alias_name.to_s] = command name
163
+ @aliases[alias_name.to_s] = args
164
+ end
165
+
166
+ ##
167
+ # Default command _name_ to be used when no other
168
+ # command is found in the arguments.
169
+
170
+ def default_command name
171
+ @default_command = name
172
+ end
173
+
174
+ ##
175
+ # Add a command object to this runner.
176
+
177
+ def add_command command
178
+ @commands[command.name] = command
179
+ end
180
+
181
+ ##
182
+ # Check if command _name_ is an alias.
183
+
184
+ def alias? name
185
+ @aliases.include? name.to_s
186
+ end
187
+
188
+ ##
189
+ # Check if a command _name_ exists.
190
+
191
+ def command_exists? name
192
+ @commands[name.to_s]
193
+ end
194
+
195
+ #:stopdoc:
196
+
197
+ ##
198
+ # Get active command within arguments passed to this runner.
199
+
200
+ def active_command
201
+ @__active_command ||= command(command_name_from_args)
202
+ end
203
+
204
+ ##
205
+ # Attempts to locate a command name from within the arguments.
206
+ # Supports multi-word commands, using the largest possible match.
207
+
208
+ def command_name_from_args
209
+ @__command_name_from_args ||= (valid_command_names_from(*@args.dup).sort.last || @default_command)
210
+ end
211
+
212
+ ##
213
+ # Returns array of valid command names found within _args_.
214
+
215
+ def valid_command_names_from *args
216
+ arg_string = args.delete_if { |value| value =~ /^-/ }.join ' '
217
+ commands.keys.find_all { |name| name if /^#{name}/.match arg_string }
218
+ end
219
+
220
+ ##
221
+ # Help formatter instance.
222
+
223
+ def help_formatter
224
+ @__help_formatter ||= program(:help_formatter).new self
225
+ end
226
+
227
+ ##
228
+ # Return arguments without the command name.
229
+
230
+ def args_without_command_name
231
+ removed = []
232
+ parts = command_name_from_args.split rescue []
233
+ @args.dup.delete_if do |arg|
234
+ removed << arg if parts.include?(arg) and not removed.include?(arg)
235
+ end
236
+ end
237
+
238
+ ##
239
+ # Returns hash of help formatter alias defaults.
240
+
241
+ def help_formatter_alias_defaults
242
+ return :compact => HelpFormatter::TerminalCompact
243
+ end
244
+
245
+ ##
246
+ # Returns hash of program defaults.
247
+
248
+ def program_defaults
249
+ return :help_formatter => HelpFormatter::Terminal,
250
+ :int_message => "\nProcess interrupted",
251
+ :name => File.basename($0)
252
+ end
253
+
254
+ ##
255
+ # Creates default commands such as 'help' which is
256
+ # essentially the same as using the --help switch.
257
+
258
+ def create_default_commands
259
+ command :help do |c|
260
+ c.syntax = 'commander help [command]'
261
+ c.description = 'Display global or [command] help documentation.'
262
+ c.example 'Display global help', 'command help'
263
+ c.example "Display help for 'foo'", 'command help foo'
264
+ c.when_called do |args, options|
265
+ enable_paging
266
+ if args.empty?
267
+ say help_formatter.render
268
+ else
269
+ command = command args.join(' ')
270
+ require_valid_command command
271
+ say help_formatter.render_command(command)
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ ##
278
+ # Raises InvalidCommandError when a _command_ is not found.
279
+
280
+ def require_valid_command command = active_command
281
+ raise InvalidCommandError, 'invalid command', caller if command.nil?
282
+ end
283
+
284
+ ##
285
+ # Removes global _options_ from _args_. This prevents an invalid
286
+ # option error from occurring when options are parsed
287
+ # again for the command.
288
+
289
+ def remove_global_options options, args
290
+ # TODO: refactor with flipflop, please TJ ! have time to refactor me !
291
+ options.each do |option|
292
+ switches = option[:switches]
293
+ past_switch, arg_removed = false, false
294
+ args.delete_if do |arg|
295
+ # TODO: clean this up, no rescuing ;)
296
+ if switches.any? { |switch| switch.match(/^#{arg}/) rescue false }
297
+ past_switch, arg_removed = true, false
298
+ true
299
+ elsif past_switch && !arg_removed && arg !~ /^-/
300
+ arg_removed = true
301
+ else
302
+ arg_removed = true
303
+ false
304
+ end
305
+ end
306
+ end
307
+ end
308
+
309
+ ##
310
+ # Parse global command options.
311
+
312
+ def parse_global_options
313
+ options.inject OptionParser.new do |options, option|
314
+ options.on *option[:args], &global_option_proc(option[:switches], &option[:proc])
315
+ end.parse! @args.dup
316
+ rescue OptionParser::InvalidOption
317
+ # Ignore invalid options since options will be further
318
+ # parsed by our sub commands.
319
+ end
320
+
321
+ ##
322
+ # Returns a proc allowing for commands to inherit global options.
323
+ # This functionality works whether a block is present for the global
324
+ # option or not, so simple switches such as --verbose can be used
325
+ # without a block, and used throughout all commands.
326
+
327
+ def global_option_proc switches, &block
328
+ lambda do |value|
329
+ unless active_command.nil?
330
+ active_command.proxy_options << [Runner.switch_to_sym(switches.last), value]
331
+ end
332
+ yield value if block and !value.nil?
333
+ end
334
+ end
335
+
336
+ ##
337
+ # Raises a CommandError when the program any of the _keys_ are not present, or empty.
338
+
339
+ def require_program *keys
340
+ keys.each do |key|
341
+ raise CommandError, "program #{key} required" if program(key).nil? or program(key).empty?
342
+ end
343
+ end
344
+
345
+ ##
346
+ # Return switches and description separated from the _args_ passed.
347
+
348
+ def self.separate_switches_from_description *args
349
+ switches = args.find_all { |arg| arg.to_s =~ /^-/ }
350
+ description = args.last unless !args.last.is_a? String or args.last.match(/^-/)
351
+ return switches, description
352
+ end
353
+
354
+ ##
355
+ # Attempts to generate a method name symbol from +switch+.
356
+ # For example:
357
+ #
358
+ # -h # => :h
359
+ # --trace # => :trace
360
+ # --some-switch # => :some_switch
361
+ # --[no-]feature # => :feature
362
+ # --file FILE # => :file
363
+ # --list of,things # => :list
364
+ #
365
+
366
+ def self.switch_to_sym switch
367
+ switch.scan(/[\-\]](\w+)/).join('_').to_sym rescue nil
368
+ end
369
+
370
+ ##
371
+ # Run the active command.
372
+
373
+ def run_active_command
374
+ require_valid_command
375
+ if alias? command_name_from_args
376
+ active_command.run *(@aliases[command_name_from_args.to_s] + args_without_command_name)
377
+ else
378
+ active_command.run *args_without_command_name
379
+ end
380
+ end
381
+
382
+ def say *args #:nodoc:
383
+ $terminal.say *args
384
+ end
385
+
386
+ end
387
+ end