commander-openflighthpc 1.0.0.pre.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.rubocop.yml +44 -0
- data/.rubocop_todo.yml +77 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +3 -0
- data/LICENSE +51 -0
- data/Manifest +38 -0
- data/README.md +492 -0
- data/Rakefile +13 -0
- data/bin/commander +104 -0
- data/commander-openflighthpc.gemspec +32 -0
- data/lib/commander.rb +36 -0
- data/lib/commander/blank.rb +7 -0
- data/lib/commander/command.rb +263 -0
- data/lib/commander/configure.rb +14 -0
- data/lib/commander/core_ext.rb +2 -0
- data/lib/commander/core_ext/array.rb +24 -0
- data/lib/commander/core_ext/object.rb +8 -0
- data/lib/commander/delegates.rb +27 -0
- data/lib/commander/help_formatters.rb +52 -0
- data/lib/commander/help_formatters/base.rb +24 -0
- data/lib/commander/help_formatters/terminal.rb +24 -0
- data/lib/commander/help_formatters/terminal/command_help.erb +35 -0
- data/lib/commander/help_formatters/terminal/help.erb +36 -0
- data/lib/commander/help_formatters/terminal/subcommand_help.erb +23 -0
- data/lib/commander/help_formatters/terminal_compact.rb +11 -0
- data/lib/commander/help_formatters/terminal_compact/command_help.erb +26 -0
- data/lib/commander/help_formatters/terminal_compact/help.erb +29 -0
- data/lib/commander/help_formatters/terminal_compact/subcommand_help.erb +15 -0
- data/lib/commander/import.rb +5 -0
- data/lib/commander/methods.rb +11 -0
- data/lib/commander/patches/decimal-integer.rb +17 -0
- data/lib/commander/patches/help_formatter_binding.rb +15 -0
- data/lib/commander/patches/implicit-short-tags.rb +75 -0
- data/lib/commander/patches/option_defaults.rb +23 -0
- data/lib/commander/patches/validate_inputs.rb +76 -0
- data/lib/commander/platform.rb +7 -0
- data/lib/commander/runner.rb +493 -0
- data/lib/commander/user_interaction.rb +551 -0
- data/lib/commander/version.rb +3 -0
- data/spec/command_spec.rb +157 -0
- data/spec/configure_spec.rb +37 -0
- data/spec/core_ext/array_spec.rb +18 -0
- data/spec/core_ext/object_spec.rb +19 -0
- data/spec/help_formatters/terminal_compact_spec.rb +69 -0
- data/spec/help_formatters/terminal_spec.rb +67 -0
- data/spec/methods_spec.rb +61 -0
- data/spec/patches/validate_inputs_spec.rb +84 -0
- data/spec/runner_spec.rb +672 -0
- data/spec/spec_helper.rb +79 -0
- data/spec/ui_spec.rb +30 -0
- metadata +183 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
module Commander
|
2
|
+
def configure(*configuration_opts, &configuration_block)
|
3
|
+
configuration_module = Module.new
|
4
|
+
configuration_module.extend Commander::Methods
|
5
|
+
|
6
|
+
configuration_module.class_exec(*configuration_opts, &configuration_block)
|
7
|
+
|
8
|
+
configuration_module.class_exec do
|
9
|
+
run!
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module_function :configure
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Array
|
2
|
+
##
|
3
|
+
# Split _string_ into an array. Used in
|
4
|
+
# conjunction with Highline's #ask, or #ask_for_array
|
5
|
+
# methods, which must respond to #parse.
|
6
|
+
#
|
7
|
+
# This method allows escaping of whitespace. For example
|
8
|
+
# the arguments foo bar\ baz will become ['foo', 'bar baz']
|
9
|
+
#
|
10
|
+
# === Example
|
11
|
+
#
|
12
|
+
# # ask invokes Array#parse
|
13
|
+
# list = ask 'Favorite cookies:', Array
|
14
|
+
#
|
15
|
+
# # or use ask_for_CLASS
|
16
|
+
# list = ask_for_array 'Favorite cookies: '
|
17
|
+
#
|
18
|
+
|
19
|
+
def self.parse(string)
|
20
|
+
# Using reverse + lookahead to work around Ruby 1.8's lack of lookbehind
|
21
|
+
# TODO: simplify now that we don't support Ruby 1.8
|
22
|
+
string.reverse.split(/\s(?!\\)/).reverse.map { |s| s.reverse.gsub('\\ ', ' ') }
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Commander
|
2
|
+
module Delegates
|
3
|
+
%w(
|
4
|
+
add_command
|
5
|
+
command
|
6
|
+
program
|
7
|
+
error_handler
|
8
|
+
run!
|
9
|
+
global_option
|
10
|
+
alias_command
|
11
|
+
default_command
|
12
|
+
always_trace!
|
13
|
+
never_trace!
|
14
|
+
silent_trace!
|
15
|
+
).each do |meth|
|
16
|
+
eval <<-END, binding, __FILE__, __LINE__
|
17
|
+
def #{meth}(*args, &block)
|
18
|
+
::Commander::Runner.instance.#{meth}(*args, &block)
|
19
|
+
end
|
20
|
+
END
|
21
|
+
end
|
22
|
+
|
23
|
+
def defined_commands(*args, &block)
|
24
|
+
::Commander::Runner.instance.commands(*args, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Commander
|
2
|
+
module HelpFormatter
|
3
|
+
autoload :Base, 'commander/help_formatters/base'
|
4
|
+
autoload :Terminal, 'commander/help_formatters/terminal'
|
5
|
+
autoload :TerminalCompact, 'commander/help_formatters/terminal_compact'
|
6
|
+
|
7
|
+
class Context
|
8
|
+
def initialize(target)
|
9
|
+
@target = target
|
10
|
+
end
|
11
|
+
|
12
|
+
# NOTE: `get_binding` has been stubbed! This version will be ignored
|
13
|
+
# See patch for actual version
|
14
|
+
prepend Patches::HelpFormatterBinding
|
15
|
+
def get_binding
|
16
|
+
@target.instance_eval { binding }.tap do |bind|
|
17
|
+
decorate_binding(bind)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# No-op, override in subclasses.
|
22
|
+
def decorate_binding(_bind)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ProgramContext < Context
|
27
|
+
def decorate_binding(bind)
|
28
|
+
bind.eval("max_command_length = #{max_command_length(bind)}")
|
29
|
+
bind.eval("max_aliases_length = #{max_aliases_length(bind)}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def max_command_length(bind)
|
33
|
+
max_key_length(bind.eval('@commands'))
|
34
|
+
end
|
35
|
+
|
36
|
+
def max_aliases_length(bind)
|
37
|
+
max_key_length(bind.eval('@aliases'))
|
38
|
+
end
|
39
|
+
|
40
|
+
def max_key_length(hash, default = 20)
|
41
|
+
longest = hash.keys.max_by(&:size)
|
42
|
+
longest ? longest.size : default
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module_function
|
47
|
+
|
48
|
+
def indent(amount, text)
|
49
|
+
text.to_s.gsub("\n", "\n" + (' ' * amount))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Commander
|
2
|
+
##
|
3
|
+
# = Help Formatter
|
4
|
+
#
|
5
|
+
# Commander's help formatters control the output when
|
6
|
+
# either the help command, or --help switch are called.
|
7
|
+
# The default formatter is Commander::HelpFormatter::Terminal.
|
8
|
+
|
9
|
+
module HelpFormatter
|
10
|
+
class Base
|
11
|
+
def initialize(runner)
|
12
|
+
@runner = runner
|
13
|
+
end
|
14
|
+
|
15
|
+
def render
|
16
|
+
'Implement global help here'
|
17
|
+
end
|
18
|
+
|
19
|
+
def render_command(command)
|
20
|
+
"Implement help for #{command.name} here"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Commander
|
4
|
+
module HelpFormatter
|
5
|
+
class Terminal < Base
|
6
|
+
def render
|
7
|
+
template(:help).result(ProgramContext.new(@runner).get_binding)
|
8
|
+
end
|
9
|
+
|
10
|
+
def render_command(command)
|
11
|
+
template(:command_help).result(Context.new(command).get_binding)
|
12
|
+
end
|
13
|
+
|
14
|
+
def render_subcommand(command)
|
15
|
+
bind = ProgramContext.new(@runner).get_binding({cmd: command})
|
16
|
+
template(:subcommand_help).result(bind)
|
17
|
+
end
|
18
|
+
|
19
|
+
def template(name)
|
20
|
+
ERB.new(File.read(File.join(File.dirname(__FILE__), 'terminal', "#{name}.erb")), nil, '-')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<% if !@syntax -%>
|
2
|
+
<%= $terminal.color "NAME", :bold %>:
|
3
|
+
|
4
|
+
<%= @name %>
|
5
|
+
<% else -%>
|
6
|
+
<%= $terminal.color "SYNOPSIS", :bold %>:
|
7
|
+
|
8
|
+
<%= @syntax -%>
|
9
|
+
|
10
|
+
<% end -%>
|
11
|
+
|
12
|
+
<%= $terminal.color "DESCRIPTION", :bold %>:
|
13
|
+
|
14
|
+
<%= Commander::HelpFormatter.indent 4, (@description || @summary || 'No description.') -%>
|
15
|
+
|
16
|
+
<% unless @examples.empty? -%>
|
17
|
+
|
18
|
+
<%= $terminal.color "EXAMPLES", :bold %>:
|
19
|
+
<% for description, command in @examples -%>
|
20
|
+
|
21
|
+
# <%= description %>
|
22
|
+
<%= command %>
|
23
|
+
<% end -%>
|
24
|
+
<% end -%>
|
25
|
+
<% unless @options.empty? -%>
|
26
|
+
|
27
|
+
<%= $terminal.color "OPTIONS", :bold %>:
|
28
|
+
<% for option in @options -%>
|
29
|
+
|
30
|
+
<%= option[:switches].join ', ' %>
|
31
|
+
<%= Commander::HelpFormatter.indent 8, option[:description] %><% if option[:default] %>
|
32
|
+
<%= $terminal.color "Default", :bold %>: <%= option[:default] %><% end %>
|
33
|
+
<% end -%>
|
34
|
+
<% end -%>
|
35
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<%= $terminal.color "NAME", :bold %>:
|
2
|
+
|
3
|
+
<%= program :name %>
|
4
|
+
|
5
|
+
<%= $terminal.color "DESCRIPTION", :bold %>:
|
6
|
+
|
7
|
+
<%= Commander::HelpFormatter.indent 4, program(:description) %>
|
8
|
+
|
9
|
+
<%= $terminal.color "COMMANDS", :bold %>:
|
10
|
+
<% for name, command in @help_commands.sort -%>
|
11
|
+
<% unless alias? name %>
|
12
|
+
<%= "%-#{max_command_length}s %s" % [command.name, command.summary || command.description] -%>
|
13
|
+
<% end -%>
|
14
|
+
<% end %>
|
15
|
+
<% unless @aliases.empty? %>
|
16
|
+
<%= $terminal.color "ALIASES", :bold %>:
|
17
|
+
<% for alias_name, args in @aliases.sort %>
|
18
|
+
<%= "%-#{max_aliases_length}s %s %s" % [alias_name, command(alias_name).name, args.join(' ')] -%>
|
19
|
+
<% end %>
|
20
|
+
<% end %>
|
21
|
+
<% unless @help_options.empty? -%>
|
22
|
+
<%= $terminal.color "GLOBAL OPTIONS", :bold %>:
|
23
|
+
<% for option in @help_options -%>
|
24
|
+
|
25
|
+
<%= option[:switches].join ', ' %>
|
26
|
+
<%= option[:description] %>
|
27
|
+
<% end -%>
|
28
|
+
<% end -%>
|
29
|
+
<% if program :help -%>
|
30
|
+
<% for title, body in program(:help) %>
|
31
|
+
<%= $terminal.color title.to_s.upcase, :bold %>:
|
32
|
+
|
33
|
+
<%= body %>
|
34
|
+
<% end -%>
|
35
|
+
<% end -%>
|
36
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<% if !cmd.syntax %>
|
2
|
+
<%= $terminal.color "NAME", :bold %>:
|
3
|
+
|
4
|
+
<%= cmd.name %>
|
5
|
+
<% else -%>
|
6
|
+
<%= $terminal.color "SYNOPSIS", :bold %>:
|
7
|
+
|
8
|
+
<%= cmd.syntax -%>
|
9
|
+
|
10
|
+
<% end -%>
|
11
|
+
|
12
|
+
<%= $terminal.color "DESCRIPTION", :bold %>:
|
13
|
+
|
14
|
+
<%= Commander::HelpFormatter.indent 4, (cmd.description || cmd.summary || 'No description.') -%>
|
15
|
+
|
16
|
+
|
17
|
+
<%= $terminal.color "SUBCOMMANDS", :bold %>:
|
18
|
+
<% for name, command in @commands.sort -%>
|
19
|
+
<% unless alias? name %>
|
20
|
+
<%= "%-#{max_command_length}s %s" % [command.name, command.summary || command.description] -%>
|
21
|
+
<% end -%>
|
22
|
+
<% end %>
|
23
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<%= @name %>
|
2
|
+
<% if @syntax -%>
|
3
|
+
|
4
|
+
Usage: <%= @syntax %>
|
5
|
+
<% end -%>
|
6
|
+
<% if @description || @summary -%>
|
7
|
+
|
8
|
+
<%= @description || @summary %>
|
9
|
+
<% end -%>
|
10
|
+
<% unless @examples.empty? -%>
|
11
|
+
|
12
|
+
Examples:
|
13
|
+
<% for description, command in @examples -%>
|
14
|
+
|
15
|
+
# <%= description %>
|
16
|
+
<%= command %>
|
17
|
+
<% end -%>
|
18
|
+
<% end -%>
|
19
|
+
<% unless @options.empty? -%>
|
20
|
+
|
21
|
+
Options:
|
22
|
+
<% for option in @options -%>
|
23
|
+
<%= "%-20s %s" % [option[:switches].join(', '), option[:description]] %><% if option[:default] %> <%= option[:default] %><% end %>
|
24
|
+
<% end -%>
|
25
|
+
<% end -%>
|
26
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<%= program :name %>
|
2
|
+
|
3
|
+
<%= program :description %>
|
4
|
+
|
5
|
+
Commands:
|
6
|
+
<% for name, command in @help_commands.sort -%>
|
7
|
+
<% unless alias? name -%>
|
8
|
+
<%= "%-#{max_command_length}s %s" % [command.name, command.summary || command.description] %>
|
9
|
+
<% end -%>
|
10
|
+
<% end -%>
|
11
|
+
<% unless @aliases.empty? %>
|
12
|
+
Aliases:
|
13
|
+
<% for alias_name, args in @aliases.sort -%>
|
14
|
+
<%= "%-#{max_aliases_length}s %s %s" % [alias_name, command(alias_name).name, args.join(' ')] %>
|
15
|
+
<% end -%>
|
16
|
+
<% end %>
|
17
|
+
<% unless @help_options.empty? -%>
|
18
|
+
Global Options:
|
19
|
+
<% for option in @help_options -%>
|
20
|
+
<%= "%-20s %s" % [option[:switches].join(', '), option[:description]] -%>
|
21
|
+
<% end -%>
|
22
|
+
<% end -%>
|
23
|
+
<% if program :help -%>
|
24
|
+
<% for title, body in program(:help) %>
|
25
|
+
<%= title %>:
|
26
|
+
<%= body %>
|
27
|
+
<% end %>
|
28
|
+
<% end -%>
|
29
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<%= cmd.name %>
|
2
|
+
<% if cmd.syntax -%>
|
3
|
+
|
4
|
+
Usage: <%= cmd.syntax %>
|
5
|
+
<% end -%>
|
6
|
+
<% if cmd.description || cmd.summary -%>
|
7
|
+
|
8
|
+
<%= cmd.description || cmd.summary %>
|
9
|
+
<% end -%>
|
10
|
+
|
11
|
+
<% for name, command in @commands.sort -%>
|
12
|
+
<% unless alias? name -%>
|
13
|
+
<%= "%-#{max_command_length}s %s" % [command.name, command.summary || command.description] %>
|
14
|
+
<% end -%>
|
15
|
+
<% end -%>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Patches the underling OptionParser DecimalInteger type so that it doesn't
|
2
|
+
# convert numbers starting with a '0' as if they are an Octal number
|
3
|
+
module Commander
|
4
|
+
module Patches
|
5
|
+
module DecimalInteger
|
6
|
+
decimal = '\d+(?:_\d+)*'
|
7
|
+
DecimalInteger = /\A[-+]?#{decimal}\z/io
|
8
|
+
::OptionParser::accept(DecimalInteger, DecimalInteger) {|s,|
|
9
|
+
begin
|
10
|
+
Integer(s, 10)
|
11
|
+
rescue ArgumentError
|
12
|
+
raise ::OptionParser::InvalidArgument, s
|
13
|
+
end if s
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Commander
|
2
|
+
module Patches
|
3
|
+
module HelpFormatterBinding
|
4
|
+
def get_binding(additional = {})
|
5
|
+
bnd = @target.instance_eval { binding }.tap do |bind|
|
6
|
+
decorate_binding(bind)
|
7
|
+
end
|
8
|
+
additional.each do |k, v|
|
9
|
+
bnd.local_variable_set(k, v)
|
10
|
+
end
|
11
|
+
bnd
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Patches the underling ruby OptionParser to prevent it from automatically
|
2
|
+
# generating short tags for options
|
3
|
+
module Commander
|
4
|
+
module Patches
|
5
|
+
module ImplicitShortTags
|
6
|
+
def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
|
7
|
+
opt, arg, val, rest = nil
|
8
|
+
nonopt ||= proc {|a| throw :terminate, a}
|
9
|
+
argv.unshift(arg) if arg = catch(:terminate) {
|
10
|
+
while arg = argv.shift
|
11
|
+
case arg
|
12
|
+
# long option
|
13
|
+
when /\A--([^=]*)(?:=(.*))?/m
|
14
|
+
opt, rest = $1, $2
|
15
|
+
opt.tr!('_', '-')
|
16
|
+
begin
|
17
|
+
sw, = complete(:long, opt, true)
|
18
|
+
rescue ::OptionParser::ParseError
|
19
|
+
raise $!.set_option(arg, true)
|
20
|
+
end
|
21
|
+
begin
|
22
|
+
opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
|
23
|
+
val = cb.call(val) if cb
|
24
|
+
setter.call(sw.switch_name, val) if setter
|
25
|
+
rescue ::OptionParser::ParseError
|
26
|
+
raise $!.set_option(arg, rest)
|
27
|
+
end
|
28
|
+
|
29
|
+
# short option
|
30
|
+
when /\A-(.)((=).*|.+)?/m
|
31
|
+
eq, rest, opt = $3, $2, $1
|
32
|
+
has_arg, val = eq, rest
|
33
|
+
begin
|
34
|
+
sw, = search(:short, opt)
|
35
|
+
unless sw
|
36
|
+
sw, = complete(:short, opt)
|
37
|
+
# short option matched.
|
38
|
+
val = arg.sub(/\A-/, '')
|
39
|
+
has_arg = true
|
40
|
+
end
|
41
|
+
rescue ::OptionParser::ParseError
|
42
|
+
raise $!.set_option(arg, true)
|
43
|
+
end
|
44
|
+
begin
|
45
|
+
opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
|
46
|
+
raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
|
47
|
+
argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-')
|
48
|
+
val = cb.call(val) if cb
|
49
|
+
setter.call(sw.switch_name, val) if setter
|
50
|
+
rescue ::OptionParser::ParseError
|
51
|
+
raise $!.set_option(arg, arg.length > 2)
|
52
|
+
end
|
53
|
+
|
54
|
+
# non-option argument
|
55
|
+
else
|
56
|
+
catch(:prune) do
|
57
|
+
visit(:each_option) do |sw0|
|
58
|
+
sw = sw0
|
59
|
+
sw.block.call(arg) if ::OptionParser::Switch === sw and sw.match_nonswitch?(arg)
|
60
|
+
end
|
61
|
+
nonopt.call(arg)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
nil
|
67
|
+
}
|
68
|
+
|
69
|
+
visit(:search, :short, nil) {|sw| sw.block.call(*argv) if !sw.pattern}
|
70
|
+
|
71
|
+
argv
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|