crishoj-commander 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,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