coglius 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Cogfile +15 -0
- data/LICENSE +201 -0
- data/lib/coglius.rb +29 -0
- data/lib/coglius/app.rb +250 -0
- data/lib/coglius/app_support.rb +284 -0
- data/lib/coglius/command.rb +149 -0
- data/lib/coglius/command_line_option.rb +34 -0
- data/lib/coglius/command_line_token.rb +62 -0
- data/lib/coglius/command_support.rb +214 -0
- data/lib/coglius/commands/compound_command.rb +42 -0
- data/lib/coglius/commands/doc.rb +215 -0
- data/lib/coglius/commands/help.rb +73 -0
- data/lib/coglius/commands/help_modules/arg_name_formatter.rb +20 -0
- data/lib/coglius/commands/help_modules/command_finder.rb +60 -0
- data/lib/coglius/commands/help_modules/command_help_format.rb +138 -0
- data/lib/coglius/commands/help_modules/global_help_format.rb +70 -0
- data/lib/coglius/commands/help_modules/help_completion_format.rb +31 -0
- data/lib/coglius/commands/help_modules/list_formatter.rb +23 -0
- data/lib/coglius/commands/help_modules/one_line_wrapper.rb +18 -0
- data/lib/coglius/commands/help_modules/options_formatter.rb +49 -0
- data/lib/coglius/commands/help_modules/text_wrapper.rb +53 -0
- data/lib/coglius/commands/help_modules/tty_only_wrapper.rb +23 -0
- data/lib/coglius/commands/help_modules/verbatim_wrapper.rb +16 -0
- data/lib/coglius/commands/initconfig.rb +69 -0
- data/lib/coglius/commands/rdoc_document_listener.rb +116 -0
- data/lib/coglius/commands/scaffold.rb +401 -0
- data/lib/coglius/copy_options_to_aliases.rb +33 -0
- data/lib/coglius/dsl.rb +221 -0
- data/lib/coglius/exceptions.rb +54 -0
- data/lib/coglius/flag.rb +68 -0
- data/lib/coglius/gli_option_parser.rb +124 -0
- data/lib/coglius/option_parser_factory.rb +45 -0
- data/lib/coglius/options.rb +23 -0
- data/lib/coglius/switch.rb +35 -0
- data/lib/coglius/terminal.rb +94 -0
- data/lib/coglius/version.rb +5 -0
- data/templates/coglius/generator.rb.erb +26 -0
- metadata +208 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'coglius/command'
|
3
|
+
require 'coglius/terminal'
|
4
|
+
require 'coglius/commands/help_modules/list_formatter'
|
5
|
+
require 'coglius/commands/help_modules/text_wrapper'
|
6
|
+
require 'coglius/commands/help_modules/one_line_wrapper'
|
7
|
+
require 'coglius/commands/help_modules/verbatim_wrapper'
|
8
|
+
require 'coglius/commands/help_modules/tty_only_wrapper'
|
9
|
+
require 'coglius/commands/help_modules/options_formatter'
|
10
|
+
require 'coglius/commands/help_modules/global_help_format'
|
11
|
+
require 'coglius/commands/help_modules/command_help_format'
|
12
|
+
require 'coglius/commands/help_modules/help_completion_format'
|
13
|
+
require 'coglius/commands/help_modules/command_finder'
|
14
|
+
require 'coglius/commands/help_modules/arg_name_formatter'
|
15
|
+
|
16
|
+
module Coglius
|
17
|
+
module Commands
|
18
|
+
SORTERS = {
|
19
|
+
:manually => lambda { |list| list },
|
20
|
+
:alpha => lambda { |list| list.sort },
|
21
|
+
}
|
22
|
+
|
23
|
+
WRAPPERS = {
|
24
|
+
:to_terminal => HelpModules::TextWrapper,
|
25
|
+
:never => HelpModules::OneLineWrapper,
|
26
|
+
:one_line => HelpModules::OneLineWrapper,
|
27
|
+
:tty_only => HelpModules::TTYOnlyWrapper,
|
28
|
+
:none => HelpModules::VerbatimWrapper,
|
29
|
+
:verbatim => HelpModules::VerbatimWrapper,
|
30
|
+
}
|
31
|
+
# The help command used for the two-level interactive help system
|
32
|
+
class Help < Command
|
33
|
+
def initialize(app,output=$stdout,error=$stderr)
|
34
|
+
super(:names => :help,
|
35
|
+
:description => 'Shows a list of commands or help for one command',
|
36
|
+
:arguments_name => 'command',
|
37
|
+
:long_desc => 'Gets help for the application or its commands. Can also list the commands in a way helpful to creating a bash-style completion function',
|
38
|
+
:skips_pre => true,
|
39
|
+
:skips_post => true,
|
40
|
+
:skips_around => true)
|
41
|
+
@app = app
|
42
|
+
@sorter = SORTERS[@app.help_sort_type]
|
43
|
+
@text_wrapping_class = WRAPPERS[@app.help_text_wrap_type]
|
44
|
+
|
45
|
+
desc 'List commands one per line, to assist with shell completion'
|
46
|
+
switch :c
|
47
|
+
|
48
|
+
action do |global_options,options,arguments|
|
49
|
+
show_help(global_options,options,arguments,output,error)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def show_help(global_options,options,arguments,out,error)
|
56
|
+
command_finder = HelpModules::CommandFinder.new(@app,arguments,error)
|
57
|
+
if options[:c]
|
58
|
+
help_output = HelpModules::HelpCompletionFormat.new(@app,command_finder,arguments).format
|
59
|
+
out.puts help_output unless help_output.nil?
|
60
|
+
elsif arguments.empty? || options[:c]
|
61
|
+
out.puts HelpModules::GlobalHelpFormat.new(@app,@sorter,@text_wrapping_class).format
|
62
|
+
else
|
63
|
+
name = arguments.shift
|
64
|
+
command = command_finder.find_command(name)
|
65
|
+
unless command.nil?
|
66
|
+
out.puts HelpModules::CommandHelpFormat.new(command,@app,File.basename($0).to_s,@sorter,@text_wrapping_class).format
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Coglius
|
2
|
+
module Commands
|
3
|
+
module HelpModules
|
4
|
+
# Handles wrapping text
|
5
|
+
class ArgNameFormatter
|
6
|
+
def format(arguments_description,arguments_options)
|
7
|
+
return '' if String(arguments_description).strip == ''
|
8
|
+
desc = arguments_description
|
9
|
+
if arguments_options.include? :optional
|
10
|
+
desc = "[#{desc}]"
|
11
|
+
end
|
12
|
+
if arguments_options.include? :multiple
|
13
|
+
desc = "#{desc}[, #{desc}]*"
|
14
|
+
end
|
15
|
+
" " + desc
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Coglius
|
2
|
+
module Commands
|
3
|
+
module HelpModules
|
4
|
+
# Finds commands from the application/command data structures
|
5
|
+
class CommandFinder
|
6
|
+
|
7
|
+
attr_reader :last_unknown_command
|
8
|
+
attr_reader :last_found_command
|
9
|
+
attr_writer :squelch_stderr
|
10
|
+
|
11
|
+
def initialize(app,arguments,error)
|
12
|
+
@app = app
|
13
|
+
@arguments = arguments
|
14
|
+
@error = error
|
15
|
+
@squelch_stderr = false
|
16
|
+
@last_unknown_command = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_command(name)
|
20
|
+
command = find_command_from_base(name,@app)
|
21
|
+
return if unknown_command?(command,name,@error)
|
22
|
+
@last_found_command = command
|
23
|
+
while !@arguments.empty?
|
24
|
+
name = @arguments.shift
|
25
|
+
command = find_command_from_base(name,command)
|
26
|
+
return if unknown_command?(command,name,@error)
|
27
|
+
@last_found_command = command
|
28
|
+
end
|
29
|
+
command
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Given the name of a command to find, and a base, either the app or another command, returns
|
35
|
+
# the command object or nil.
|
36
|
+
def find_command_from_base(command_name,base)
|
37
|
+
base.commands.values.select { |command|
|
38
|
+
if [command.name,Array(command.aliases)].flatten.map(&:to_s).any? { |_| _ == command_name }
|
39
|
+
command
|
40
|
+
end
|
41
|
+
}.first
|
42
|
+
end
|
43
|
+
|
44
|
+
# Checks if the return from find_command was unknown and, if so, prints an error
|
45
|
+
# for the user on the error device, returning true or false if the command was unknown.
|
46
|
+
def unknown_command?(command,name,error)
|
47
|
+
if command.nil?
|
48
|
+
@last_unknown_command = name
|
49
|
+
unless @squelch_stderr
|
50
|
+
error.puts "error: Unknown command '#{name}'. Use '#{File.basename($PROGRAM_NAME)} help' for a list of commands."
|
51
|
+
end
|
52
|
+
true
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Coglius
|
4
|
+
module Commands
|
5
|
+
module HelpModules
|
6
|
+
class CommandHelpFormat
|
7
|
+
def initialize(command,app,basic_invocation,sorter,wrapper_class=TextWrapper)
|
8
|
+
@basic_invocation = basic_invocation
|
9
|
+
@app = app
|
10
|
+
@command = command
|
11
|
+
@sorter = sorter
|
12
|
+
@wrapper_class = wrapper_class
|
13
|
+
end
|
14
|
+
|
15
|
+
def format
|
16
|
+
command_wrapper = @wrapper_class.new(Terminal.instance.size[0],4 + @command.name.to_s.size + 3)
|
17
|
+
wrapper = @wrapper_class.new(Terminal.instance.size[0],4)
|
18
|
+
flags_and_switches = (@command.topmost_ancestor.flags_declaration_order + @command.topmost_ancestor.switches_declaration_order).select { |option| option.associated_command == @command }
|
19
|
+
options_description = OptionsFormatter.new(flags_and_switches,@sorter,@wrapper_class).format
|
20
|
+
commands_description = format_subcommands(@command)
|
21
|
+
|
22
|
+
synopses = []
|
23
|
+
one_line_usage = basic_usage
|
24
|
+
one_line_usage << @command.arguments_description
|
25
|
+
if @command.commands.empty?
|
26
|
+
synopses << one_line_usage
|
27
|
+
else
|
28
|
+
synopses = sorted_synopses
|
29
|
+
if @command.has_action?
|
30
|
+
synopses.unshift(one_line_usage)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
COMMAND_HELP.result(binding)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
COMMAND_HELP = ERB.new(%q(NAME
|
40
|
+
<%= @command.name %> - <%= command_wrapper.wrap(@command.description) %>
|
41
|
+
|
42
|
+
SYNOPSIS
|
43
|
+
<% synopses.each do |s| %>
|
44
|
+
<%= s %>
|
45
|
+
<% end %>
|
46
|
+
<% unless @command.long_description.nil? %>
|
47
|
+
|
48
|
+
DESCRIPTION
|
49
|
+
<%= wrapper.wrap(@command.long_description) %>
|
50
|
+
<% end %>
|
51
|
+
<% if options_description.strip.length != 0 %>
|
52
|
+
|
53
|
+
COMMAND OPTIONS
|
54
|
+
<%= options_description %>
|
55
|
+
<% end %>
|
56
|
+
<% unless @command.commands.empty? %>
|
57
|
+
|
58
|
+
COMMANDS
|
59
|
+
<%= commands_description %>
|
60
|
+
<% end %>),nil,'<>')
|
61
|
+
|
62
|
+
def command_with_subcommand_usage(sub,is_default_command)
|
63
|
+
usage = basic_usage
|
64
|
+
sub_options = @command.flags.merge(@command.switches).select { |_,o| o.associated_command == sub }
|
65
|
+
usage << sub_options.map { |option_name,option|
|
66
|
+
all_names = [option.name,Array(option.aliases)].flatten
|
67
|
+
all_names.map { |_|
|
68
|
+
CommandLineOption.name_as_string(_,false) + (option.kind_of?(Flag) ? " #{option.argument_name }" : '')
|
69
|
+
}.join('|')
|
70
|
+
}.map { |_| "[#{_}]" }.sort.join(' ')
|
71
|
+
usage << ' '
|
72
|
+
if is_default_command
|
73
|
+
usage << "[#{sub.name}]"
|
74
|
+
else
|
75
|
+
usage << sub.name.to_s
|
76
|
+
end
|
77
|
+
usage << ArgNameFormatter.new.format(sub.arguments_description,sub.arguments_options)
|
78
|
+
usage
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def basic_usage
|
83
|
+
usage = @basic_invocation.dup
|
84
|
+
usage << " [global options] #{path_to_command} "
|
85
|
+
usage << "[command options] " unless global_flags_and_switches.empty?
|
86
|
+
usage
|
87
|
+
end
|
88
|
+
|
89
|
+
def path_to_command
|
90
|
+
path = []
|
91
|
+
c = @command
|
92
|
+
while c.kind_of? Command
|
93
|
+
path.unshift(c.name)
|
94
|
+
c = c.parent
|
95
|
+
end
|
96
|
+
path.join(' ')
|
97
|
+
end
|
98
|
+
|
99
|
+
def global_flags_and_switches
|
100
|
+
@app.flags.merge(@app.switches)
|
101
|
+
end
|
102
|
+
|
103
|
+
def format_subcommands(command)
|
104
|
+
commands_array = @sorter.call(command.commands_declaration_order).map { |cmd|
|
105
|
+
if command.get_default_command == cmd.name
|
106
|
+
[cmd.names,cmd.description + " (default)"]
|
107
|
+
else
|
108
|
+
[cmd.names,cmd.description]
|
109
|
+
end
|
110
|
+
}
|
111
|
+
if command.has_action?
|
112
|
+
commands_array.unshift(["<default>",command.default_description])
|
113
|
+
end
|
114
|
+
formatter = ListFormatter.new(commands_array,@wrapper_class)
|
115
|
+
StringIO.new.tap { |io| formatter.output(io) }.string
|
116
|
+
end
|
117
|
+
|
118
|
+
def sorted_synopses
|
119
|
+
synopses_command = {}
|
120
|
+
@command.commands.each do |name,sub|
|
121
|
+
default = @command.get_default_command == name
|
122
|
+
synopsis = command_with_subcommand_usage(sub,default)
|
123
|
+
synopses_command[synopsis] = sub
|
124
|
+
end
|
125
|
+
synopses = synopses_command.keys.sort { |one,two|
|
126
|
+
if synopses_command[one].name == @command.get_default_command
|
127
|
+
-1
|
128
|
+
elsif synopses_command[two].name == @command.get_default_command
|
129
|
+
1
|
130
|
+
else
|
131
|
+
synopses_command[one] <=> synopses_command[two]
|
132
|
+
end
|
133
|
+
}
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Coglius
|
4
|
+
module Commands
|
5
|
+
module HelpModules
|
6
|
+
class GlobalHelpFormat
|
7
|
+
def initialize(app,sorter,wrapper_class)
|
8
|
+
@app = app
|
9
|
+
@sorter = sorter
|
10
|
+
@wrapper_class = wrapper_class
|
11
|
+
end
|
12
|
+
|
13
|
+
def format
|
14
|
+
program_desc = @app.program_desc
|
15
|
+
program_long_desc = @app.program_long_desc
|
16
|
+
if program_long_desc
|
17
|
+
wrapper = @wrapper_class.new(Terminal.instance.size[0],4)
|
18
|
+
program_long_desc = "\n #{wrapper.wrap(program_long_desc)}\n\n" if program_long_desc
|
19
|
+
else
|
20
|
+
program_long_desc = "\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
command_formatter = ListFormatter.new(@sorter.call(@app.commands_declaration_order.reject(&:nodoc)).map { |command|
|
24
|
+
[[command.name,Array(command.aliases)].flatten.join(', '),command.description]
|
25
|
+
}, @wrapper_class)
|
26
|
+
stringio = StringIO.new
|
27
|
+
command_formatter.output(stringio)
|
28
|
+
commands = stringio.string
|
29
|
+
|
30
|
+
global_option_descriptions = OptionsFormatter.new(global_flags_and_switches,@sorter,@wrapper_class).format
|
31
|
+
|
32
|
+
GLOBAL_HELP.result(binding)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
GLOBAL_HELP = ERB.new(%q(NAME
|
38
|
+
<%= File.basename($0) %> - <%= program_desc %>
|
39
|
+
<%= program_long_desc %>
|
40
|
+
SYNOPSIS
|
41
|
+
<%= usage_string %>
|
42
|
+
|
43
|
+
<% unless @app.version_string.nil? %>
|
44
|
+
VERSION
|
45
|
+
<%= @app.version_string %>
|
46
|
+
|
47
|
+
<% end %>
|
48
|
+
<% unless global_flags_and_switches.empty? %>
|
49
|
+
GLOBAL OPTIONS
|
50
|
+
<%= global_option_descriptions %>
|
51
|
+
|
52
|
+
<% end %>
|
53
|
+
COMMANDS
|
54
|
+
<%= commands %>),nil,'<>')
|
55
|
+
|
56
|
+
def global_flags_and_switches
|
57
|
+
@app.flags_declaration_order + @app.switches_declaration_order
|
58
|
+
end
|
59
|
+
|
60
|
+
def usage_string
|
61
|
+
"#{File.basename($0)} ".tap do |string|
|
62
|
+
string << "[global options] " unless global_flags_and_switches.empty?
|
63
|
+
string << "command "
|
64
|
+
string << "[command options] [arguments...]"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Coglius
|
2
|
+
module Commands
|
3
|
+
module HelpModules
|
4
|
+
class HelpCompletionFormat
|
5
|
+
def initialize(app,command_finder,args)
|
6
|
+
@app = app
|
7
|
+
@command_finder = command_finder
|
8
|
+
@command_finder.squelch_stderr = true
|
9
|
+
@args = args
|
10
|
+
end
|
11
|
+
|
12
|
+
def format
|
13
|
+
name = @args.shift
|
14
|
+
|
15
|
+
base = @command_finder.find_command(name)
|
16
|
+
base = @command_finder.last_found_command if base.nil?
|
17
|
+
base = @app if base.nil?
|
18
|
+
|
19
|
+
prefix_to_match = @command_finder.last_unknown_command
|
20
|
+
|
21
|
+
base.commands.values.map { |command|
|
22
|
+
[command.name,command.aliases]
|
23
|
+
}.flatten.compact.map(&:to_s).sort.select { |command_name|
|
24
|
+
prefix_to_match.nil? || command_name =~ /^#{prefix_to_match}/
|
25
|
+
}.join("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Coglius
|
2
|
+
module Commands
|
3
|
+
module HelpModules
|
4
|
+
# Given a list of two-element lists, formats on the terminal
|
5
|
+
class ListFormatter
|
6
|
+
def initialize(list,wrapper_class=TextWrapper)
|
7
|
+
@list = list
|
8
|
+
@wrapper_class = wrapper_class
|
9
|
+
end
|
10
|
+
|
11
|
+
# Output the list to the output_device
|
12
|
+
def output(output_device)
|
13
|
+
return if @list.empty?
|
14
|
+
max_width = @list.map { |_| _[0].length }.max
|
15
|
+
wrapper = @wrapper_class.new(Terminal.instance.size[0],4 + max_width + 3)
|
16
|
+
@list.each do |(name,description)|
|
17
|
+
output_device.printf(" %-#{max_width}s - %s\n",name,wrapper.wrap(String(description).strip))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Coglius
|
2
|
+
module Commands
|
3
|
+
module HelpModules
|
4
|
+
# Formats text in one line, stripping newlines and NOT wrapping
|
5
|
+
class OneLineWrapper
|
6
|
+
# Args are ignored entirely; this keeps it consistent with the TextWrapper interface
|
7
|
+
def initialize(width,indent)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Return a wrapped version of text, assuming that the first line has already been
|
11
|
+
# indented by @indent characters. Resulting text does NOT have a newline in it.
|
12
|
+
def wrap(text)
|
13
|
+
return String(text).gsub(/\n+/,' ').strip
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Coglius
|
2
|
+
module Commands
|
3
|
+
module HelpModules
|
4
|
+
class OptionsFormatter
|
5
|
+
def initialize(flags_and_switches,sorter,wrapper_class)
|
6
|
+
@flags_and_switches = sorter.call(flags_and_switches)
|
7
|
+
@wrapper_class = wrapper_class
|
8
|
+
end
|
9
|
+
|
10
|
+
def format
|
11
|
+
list_formatter = ListFormatter.new(@flags_and_switches.map { |option|
|
12
|
+
if option.respond_to? :argument_name
|
13
|
+
[option_names_for_help_string(option,option.argument_name),description_with_default(option)]
|
14
|
+
else
|
15
|
+
[option_names_for_help_string(option),description_with_default(option)]
|
16
|
+
end
|
17
|
+
},@wrapper_class)
|
18
|
+
stringio = StringIO.new
|
19
|
+
list_formatter.output(stringio)
|
20
|
+
stringio.string
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def description_with_default(option)
|
26
|
+
if option.kind_of? Flag
|
27
|
+
String(option.description) + " (default: #{option.safe_default_value || 'none'})"
|
28
|
+
else
|
29
|
+
String(option.description) + (option.default_value ? " (default: enabled)" : "")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def option_names_for_help_string(option,arg_name=nil)
|
34
|
+
names = [option.name,Array(option.aliases)].flatten
|
35
|
+
names = names.map { |name| CommandLineOption.name_as_string(name,option.kind_of?(Switch) ? option.negatable? : false) }
|
36
|
+
if arg_name.nil?
|
37
|
+
names.join(', ')
|
38
|
+
else
|
39
|
+
if names[-1] =~ /^--/
|
40
|
+
names.join(', ') + "=#{arg_name}"
|
41
|
+
else
|
42
|
+
names.join(', ') + " #{arg_name}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|