coglius 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/Cogfile +15 -0
  2. data/LICENSE +201 -0
  3. data/lib/coglius.rb +29 -0
  4. data/lib/coglius/app.rb +250 -0
  5. data/lib/coglius/app_support.rb +284 -0
  6. data/lib/coglius/command.rb +149 -0
  7. data/lib/coglius/command_line_option.rb +34 -0
  8. data/lib/coglius/command_line_token.rb +62 -0
  9. data/lib/coglius/command_support.rb +214 -0
  10. data/lib/coglius/commands/compound_command.rb +42 -0
  11. data/lib/coglius/commands/doc.rb +215 -0
  12. data/lib/coglius/commands/help.rb +73 -0
  13. data/lib/coglius/commands/help_modules/arg_name_formatter.rb +20 -0
  14. data/lib/coglius/commands/help_modules/command_finder.rb +60 -0
  15. data/lib/coglius/commands/help_modules/command_help_format.rb +138 -0
  16. data/lib/coglius/commands/help_modules/global_help_format.rb +70 -0
  17. data/lib/coglius/commands/help_modules/help_completion_format.rb +31 -0
  18. data/lib/coglius/commands/help_modules/list_formatter.rb +23 -0
  19. data/lib/coglius/commands/help_modules/one_line_wrapper.rb +18 -0
  20. data/lib/coglius/commands/help_modules/options_formatter.rb +49 -0
  21. data/lib/coglius/commands/help_modules/text_wrapper.rb +53 -0
  22. data/lib/coglius/commands/help_modules/tty_only_wrapper.rb +23 -0
  23. data/lib/coglius/commands/help_modules/verbatim_wrapper.rb +16 -0
  24. data/lib/coglius/commands/initconfig.rb +69 -0
  25. data/lib/coglius/commands/rdoc_document_listener.rb +116 -0
  26. data/lib/coglius/commands/scaffold.rb +401 -0
  27. data/lib/coglius/copy_options_to_aliases.rb +33 -0
  28. data/lib/coglius/dsl.rb +221 -0
  29. data/lib/coglius/exceptions.rb +54 -0
  30. data/lib/coglius/flag.rb +68 -0
  31. data/lib/coglius/gli_option_parser.rb +124 -0
  32. data/lib/coglius/option_parser_factory.rb +45 -0
  33. data/lib/coglius/options.rb +23 -0
  34. data/lib/coglius/switch.rb +35 -0
  35. data/lib/coglius/terminal.rb +94 -0
  36. data/lib/coglius/version.rb +5 -0
  37. data/templates/coglius/generator.rb.erb +26 -0
  38. 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