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,284 @@
1
+ module Coglius
2
+ # Internals for make App work
3
+ module AppSupport
4
+ # Override the device of stderr; exposed only for testing
5
+ def error_device=(e) #:nodoc:
6
+ @stderr = e
7
+ end
8
+
9
+ def context_description
10
+ "in global context"
11
+ end
12
+
13
+ # Reset the Coglius module internal data structures; mostly useful for testing
14
+ def reset # :nodoc:
15
+ switches.clear
16
+ flags.clear
17
+ @commands = nil
18
+ @commands_declaration_order = []
19
+ @flags_declaration_order = []
20
+ @switches_declaration_order = []
21
+ @version = nil
22
+ @config_file = nil
23
+ @use_openstruct = false
24
+ @prog_desc = nil
25
+ @error_block = false
26
+ @pre_block = false
27
+ @post_block = false
28
+ @default_command = :help
29
+ @around_block = nil
30
+ clear_nexts
31
+ end
32
+
33
+ # Get an array of commands, ordered by when they were declared
34
+ def commands_declaration_order # :nodoc:
35
+ @commands_declaration_order
36
+ end
37
+
38
+ # Get the version string
39
+ def version_string #:nodoc:
40
+ @version
41
+ end
42
+
43
+ # Get the default command for the entire app
44
+ def get_default_command
45
+ @default_command
46
+ end
47
+
48
+ # Runs whatever command is needed based on the arguments.
49
+ #
50
+ # +args+:: the command line ARGV array
51
+ #
52
+ # Returns a number that would be a reasonable exit code
53
+ def run(args) #:nodoc:
54
+ args = args.dup if @preserve_argv
55
+ command = nil
56
+ begin
57
+ override_defaults_based_on_config(parse_config)
58
+
59
+ add_help_switch_if_needed(switches)
60
+
61
+ global_options,command,options,arguments = CogliusOptionParser.new(commands,flags,switches,accepts,@default_command).parse_options(args)
62
+
63
+ copy_options_to_aliased_versions(global_options,command,options)
64
+
65
+ global_options = convert_to_openstruct_if_needed(global_options)
66
+ options = convert_to_openstruct_if_needed(options)
67
+
68
+ if proceed?(global_options,command,options,arguments)
69
+ call_command(command,global_options,options,arguments)
70
+ end
71
+ 0
72
+ rescue Exception => ex
73
+ handle_exception(ex,command)
74
+ end
75
+ end
76
+
77
+
78
+ # Return the name of the config file; mostly useful for generating help docs
79
+ def config_file_name #:nodoc:
80
+ @config_file
81
+ end
82
+
83
+ def accepts #:nodoc:
84
+ @accepts ||= {}
85
+ end
86
+
87
+ # Copies all options in both global_options and options to keys for the aliases of those flags.
88
+ # For example, if a flag works with either -f or --flag, this will copy the value from [:f] to [:flag]
89
+ # to allow the user to access the options by any alias
90
+ def copy_options_to_aliased_versions(global_options,command,options) # :nodoc:
91
+ copy_options_to_aliases(global_options)
92
+ command.copy_options_to_aliases(options)
93
+ end
94
+
95
+ def parse_config # :nodoc:
96
+ config = {
97
+ 'commands' => {},
98
+ }
99
+ if @config_file && File.exist?(@config_file)
100
+ require 'yaml'
101
+ config.merge!(File.open(@config_file) { |file| YAML::load(file) })
102
+ end
103
+ config
104
+ end
105
+
106
+ def clear_nexts # :nodoc:
107
+ super
108
+ @skips_post = false
109
+ @skips_pre = false
110
+ @skips_around = false
111
+ end
112
+
113
+ def stderr
114
+ @stderr ||= STDERR
115
+ end
116
+
117
+ def self.included(klass)
118
+ @stderr = $stderr
119
+ end
120
+
121
+ def flags # :nodoc:
122
+ @flags ||= {}
123
+ end
124
+
125
+ def switches # :nodoc:
126
+ @switches ||= {}
127
+ end
128
+
129
+ def commands # :nodoc:
130
+ if !@commands
131
+ @commands = { :help => Coglius::Commands::Help.new(self), :_doc => Coglius::Commands::Doc.new(self) }
132
+ @commands_declaration_order ||= []
133
+ @commands_declaration_order << @commands[:help]
134
+ @commands_declaration_order << @commands[:_doc]
135
+ end
136
+ @commands
137
+ end
138
+
139
+ def pre_block
140
+ @pre_block ||= Proc.new do
141
+ true
142
+ end
143
+ end
144
+
145
+ def post_block
146
+ @post_block ||= Proc.new do
147
+ end
148
+ end
149
+
150
+ def around_blocks
151
+ @around_blocks || []
152
+ end
153
+
154
+ def help_sort_type
155
+ @help_sort_type || :alpha
156
+ end
157
+
158
+ def help_text_wrap_type
159
+ @help_text_wrap_type || :to_terminal
160
+ end
161
+
162
+ # Sets the default values for flags based on the configuration
163
+ def override_defaults_based_on_config(config)
164
+ override_default(flags,config)
165
+ override_default(switches,config)
166
+
167
+ override_command_defaults(commands,config)
168
+ end
169
+
170
+ def override_command_defaults(command_list,config)
171
+ command_list.each do |command_name,command|
172
+ next if command_name == :initconfig || command.nil?
173
+ command_config = (config['commands'] || {})[command_name] || {}
174
+
175
+ override_default(command.topmost_ancestor.flags,command_config)
176
+ override_default(command.topmost_ancestor.switches,command_config)
177
+
178
+ override_command_defaults(command.commands,command_config)
179
+ end
180
+ end
181
+
182
+ def override_default(tokens,config)
183
+ tokens.each do |name,token|
184
+ token.default_value=config[name] if config[name]
185
+ end
186
+ end
187
+
188
+ private
189
+
190
+ def handle_exception(ex,command)
191
+ if regular_error_handling?(ex)
192
+ output_error_message(ex)
193
+ if ex.kind_of?(OptionParser::ParseError) || ex.kind_of?(BadCommandLine)
194
+ commands[:help] and commands[:help].execute({},{},command.nil? ? [] : [command.name.to_s])
195
+ end
196
+ end
197
+
198
+ raise ex if ENV['Coglius_DEBUG'] == 'true'
199
+
200
+ ex.extend(Coglius::StandardException)
201
+ ex.exit_code
202
+ end
203
+
204
+ def output_error_message(ex)
205
+ stderr.puts error_message(ex) unless no_message_given?(ex)
206
+ if ex.kind_of?(OptionParser::ParseError) || ex.kind_of?(BadCommandLine)
207
+ stderr.puts unless no_message_given?(ex)
208
+ end
209
+ end
210
+
211
+ def no_message_given?(ex)
212
+ ex.message == ex.class.name
213
+ end
214
+
215
+ # Possibly returns a copy of the passed-in Hash as an instance of Coglius::Option.
216
+ # By default, it will *not*. However by putting <tt>use_openstruct true</tt>
217
+ # in your CLI definition, it will
218
+ def convert_to_openstruct_if_needed(options) # :nodoc:
219
+ @use_openstruct ? Options.new(options) : options
220
+ end
221
+
222
+ def add_help_switch_if_needed(switches)
223
+ help_switch_exists = switches.values.find { |switch|
224
+ (Array(switch.aliases) + [switch.name]).find { |an_alias|
225
+ an_alias.to_s == 'help'
226
+ }
227
+ }
228
+ unless help_switch_exists
229
+ desc 'Show this message'
230
+ switch :help, :negatable => false
231
+ end
232
+ end
233
+
234
+ # True if we should proceed with executing the command; this calls
235
+ # the pre block if it's defined
236
+ def proceed?(global_options,command,options,arguments) #:nodoc:
237
+ if command && command.skips_pre
238
+ true
239
+ else
240
+ pre_block.call(global_options,command,options,arguments)
241
+ end
242
+ end
243
+
244
+ # Returns true if we should proceed with Coglius's basic error handling.
245
+ # This calls the error block if the user provided one
246
+ def regular_error_handling?(ex) #:nodoc:
247
+ if @error_block
248
+ @error_block.call(ex)
249
+ else
250
+ true
251
+ end
252
+ end
253
+
254
+ # Returns a String of the error message to show the user
255
+ # +ex+:: The exception we caught that launched the error handling routines
256
+ def error_message(ex) #:nodoc:
257
+ "error: #{ex.message}"
258
+ end
259
+
260
+ def call_command(command,global_options,options,arguments)
261
+ arguments = arguments.map { |arg| arg.dup } # unfreeze
262
+ code = lambda { command.execute(global_options,options,arguments) }
263
+ nested_arounds = unless command.skips_around
264
+ around_blocks.inject do |outer_around, inner_around|
265
+ lambda { |go,c,o,a, code1|
266
+ inner = lambda { inner_around.call(go,c,o,a, code1) }
267
+ outer_around.call(go,c,o,a, inner)
268
+ }
269
+ end
270
+ end
271
+
272
+ if nested_arounds
273
+ nested_arounds.call(global_options,command, options, arguments, code)
274
+ else
275
+ code.call
276
+ end
277
+
278
+ unless command.skips_post
279
+ post_block.call(global_options,command,options,arguments)
280
+ end
281
+ end
282
+
283
+ end
284
+ end
@@ -0,0 +1,149 @@
1
+ require 'coglius/command_line_token.rb'
2
+ require 'coglius/copy_options_to_aliases.rb'
3
+ require 'coglius/dsl.rb'
4
+
5
+ module Coglius
6
+ # A command to be run, in context of global flags and switches. You are given an instance of this class
7
+ # to the block you use for Coglius::DSL#command. This class mixes in Coglius::DSL so all of those methods are available
8
+ # to describe the command, in addition to the methods documented here, most importantly
9
+ # #action.
10
+ #
11
+ # Example:
12
+ #
13
+ # command :list do |c| # <- c is an instance of Coglius::Command
14
+ # c.desc 'use long form'
15
+ # c.switch :l
16
+ #
17
+ # c.action do |global,options,args|
18
+ # # list things here
19
+ # end
20
+ #
21
+ # c.command :tasks do |t| # <- t is an instance of Coglius::Command
22
+ # # this is a "subcommand" of list
23
+ #
24
+ # t.action do |global,options,args|
25
+ # # do whatever list tasks should do
26
+ # end
27
+ # end
28
+ # end
29
+ #
30
+ class Command < CommandLineToken
31
+ include CopyOptionsToAliases
32
+ include DSL
33
+ include CommandSupport
34
+
35
+ # Create a new command.
36
+ #
37
+ # options:: Keys should be:
38
+ # +names+:: A String, Symbol, or Array of String or Symbol that represents the name(s) of this command (required).
39
+ # +description+:: short description of this command as a String
40
+ # +arguments_name+:: description of the arguments as a String, or nil if this command doesn't take arguments
41
+ # +long_desc+:: a longer description of the command, possibly with multiple lines. A double line-break is treated
42
+ # as a paragraph break. No other formatting is respected, though inner whitespace is maintained.
43
+ # +skips_pre+:: if true, this command advertises that it doesn't want the pre block called first
44
+ # +skips_post+:: if true, this command advertises that it doesn't want the post block called after it
45
+ # +skips_around+:: if true, this command advertises that it doesn't want the around block called
46
+ def initialize(options)
47
+ super(options[:names],options[:description],options[:long_desc])
48
+ @arguments_description = options[:arguments_name] || ''
49
+ @arguments_options = Array(options[:arguments_options]).flatten
50
+ @skips_pre = options[:skips_pre]
51
+ @skips_post = options[:skips_post]
52
+ @skips_around = options[:skips_around]
53
+ @commands_declaration_order = []
54
+ @flags_declaration_order = []
55
+ @switches_declaration_order = []
56
+ clear_nexts
57
+ end
58
+
59
+ # Set the default command if this command has subcommands and the user doesn't
60
+ # provide a subcommand when invoking THIS command. When nil, this will show an error and the help
61
+ # for this command; when set, the command with this name will be executed.
62
+ #
63
+ # +command_name+:: The primary name of the subcommand of this command that should be run by default as a String or Symbol.
64
+ def default_command(command_name)
65
+ @default_command = command_name
66
+ end
67
+
68
+ # Define the action to take when the user executes this command. Every command should either define this
69
+ # action block, or have subcommands (or both).
70
+ #
71
+ # +block+:: A block of code to execute. The block will be given 3 arguments:
72
+ # +global_options+:: A Hash of the _global_ options specified
73
+ # by the user, with defaults set and config file values used (if using a config file, see
74
+ # Coglius::App#config_file)
75
+ # +options+:: A Hash of the command-specific options specified by the
76
+ # user, with defaults set and config file values used (if using a config file, see
77
+ # Coglius::App#config_file).
78
+ # +arguments+:: An Array of Strings representing the unparsed command line arguments
79
+ # The block's result value is not used; raise an exception or use Coglius#exit_now! if you need an early exit based
80
+ # on an error condition
81
+ #
82
+ def action(&block)
83
+ @action = block
84
+ end
85
+
86
+ # Describes this commands action block when it *also* has subcommands.
87
+ # In this case, the Coglius::DSL#desc value is the general description of the commands
88
+ # that this command groups, and the value for *this* method documents what
89
+ # will happen if you omit a subcommand.
90
+ #
91
+ # Note that if you omit the action block and specify a subcommand, that subcommand's
92
+ # description will be used to describe what happens by default.
93
+ #
94
+ # desc:: the description of what this command's action block does.
95
+ #
96
+ # Example
97
+ #
98
+ # desc 'list things'
99
+ # command :list do |c|
100
+ #
101
+ # c.desc 'list tasks'
102
+ # c.command :tasks do |t|
103
+ # t.action do |global,options,args|
104
+ # end
105
+ # end
106
+ #
107
+ # c.desc 'list contexts'
108
+ # c.command :contexts do |t|
109
+ # t.action do |global,options,args|
110
+ # end
111
+ # end
112
+ #
113
+ # c.default_desc 'list both tasks and contexts'
114
+ # c.action do |global,options,args|
115
+ # # list everything
116
+ # end
117
+ # end
118
+ #
119
+ #
120
+ # > todo help list
121
+ # NAME
122
+ # list - List things
123
+ #
124
+ # SYNOPSIS
125
+ # todo [global options] list [command options]
126
+ # todo [global options] list [command options] tasks
127
+ # todo [global options] list [command options] contexts
128
+ #
129
+ # COMMANDS
130
+ # <default> - list both tasks and contexts
131
+ # tasks - list tasks
132
+ # contexts - list contexts
133
+ #
134
+ def default_desc(desc)
135
+ @default_desc = desc
136
+ end
137
+
138
+ # Returns true if this command has the given option defined
139
+ def has_option?(option) #:nodoc:
140
+ option = option.gsub(/^\-+/,'')
141
+ ((flags.values.map { |_| [_.name,_.aliases] }) +
142
+ (switches.values.map { |_| [_.name,_.aliases] })).flatten.map(&:to_s).include?(option)
143
+ end
144
+
145
+ def self.name_as_string(name,negatable=false) #:nodoc:
146
+ name.to_s
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,34 @@
1
+ require 'coglius/command_line_token.rb'
2
+
3
+ module Coglius
4
+ # An option, not a command or argument, on the command line
5
+ class CommandLineOption < CommandLineToken #:nodoc:
6
+
7
+ attr_accessor :default_value
8
+ # Command to which this option "belongs", nil if it's a global option
9
+ attr_accessor :associated_command
10
+
11
+ # Creates a new option
12
+ #
13
+ # names - Array of symbols or strings representing the names of this switch
14
+ # options - hash of options:
15
+ # :desc - the short description
16
+ # :long_desc - the long description
17
+ # :default_value - the default value of this option
18
+ def initialize(names,options = {})
19
+ super(names,options[:desc],options[:long_desc])
20
+ @default_value = options[:default_value]
21
+ end
22
+
23
+ def self.name_as_string(name,negatable=true)
24
+ string = name.to_s
25
+ if string.length == 1
26
+ "-#{string}"
27
+ elsif negatable
28
+ "--[no-]#{string}"
29
+ else
30
+ "--#{string}"
31
+ end
32
+ end
33
+ end
34
+ end