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