coglius 0.0.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.
- 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
|