davetron5000-gli 0.1.4

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.
@@ -0,0 +1,133 @@
1
+ This is a command line parser for a git-like command line client.
2
+
3
+ = Use
4
+
5
+ The simplest way to get started is to create a scaffold project
6
+
7
+ gli init my_proj command_name other_command_name
8
+
9
+ This will create a (very) basic scaffold project in <tt>./my_proj</tt>, with a bare-bones
10
+ main file in <tt>./my_proj/bin/my_proj</tt>. This file demonstrates most of what you need
11
+ to describe your command line interface
12
+
13
+ == More Detail
14
+
15
+ This sets you up to use the DSL that GLI defines:
16
+
17
+ #!/usr/bin/ruby
18
+ $: << File.expand_path(File.dirname(__FILE__) + '/../lib')
19
+
20
+ require 'gli'
21
+
22
+ include GLI
23
+
24
+ This describes a command line switch "-n" that is global to all commands and specified before
25
+ the command name on the command line.
26
+
27
+ desc 'Dry run; don\'t change the disk'
28
+ switch :n
29
+
30
+
31
+ This is describes a command line flag that is global and has a default value of <tt>.</tt>. It also
32
+ specified a short description of its argument. This is used to print command line help. Note that we
33
+ have specified two different aliases for this flag. <tt>-r</tt> (because it is listed first) is the default
34
+ one and <tt>--root</tt> is also supported. This means that <tt>-r some_dir</tt> and <tt>--root=some_dir</tt> mean
35
+ the same thing to the application.
36
+
37
+ desc 'Root dir in which to create project'
38
+ default_value '.'
39
+ arg_name 'root_dir'
40
+ flag [:r,:root]
41
+
42
+ Here we specify a command. Inside the block we can use the same sorts of things as we did above to define flags
43
+ and switches specific to the command. These must come after the command name. Also note that we use <tt>arg_name</tt>
44
+ here to describe the arguments this command accepts.
45
+
46
+ desc 'Create a new GLI-based project'
47
+ arg_name 'project_name [command[ command]*]'
48
+ command [:init,:scaffold] do |c|
49
+
50
+ c.desc 'Create an ext dir'
51
+ c.switch [:e,:ext]
52
+
53
+ c.desc 'Overwrite/ignore existing files and directories'
54
+ c.switch [:force]
55
+
56
+ Here we specify the actual actions to take when the command is executed. We define a block that
57
+ will be given the global options (as a Hash), the command-specific options (as a hash) and the command
58
+ line arguments
59
+
60
+ c.action do |global_options,options,args|
61
+ if args.length < 1
62
+ raise(MissingArgumentException,'You must specify the name of your project')
63
+ end
64
+ Scaffold.create_scaffold(g[:r],!o[:notest],o[:e],args[0],args[1..-1],o[:force],g[:n])
65
+ end
66
+ end
67
+
68
+ You can also specify some global code to run before, after and on errors:
69
+
70
+ pre do |global_options,command,options,args|
71
+ puts "After parsing, but before #{command.name} is run"
72
+ end
73
+
74
+ post do |global_options,command,options,args|
75
+ puts "After successful execution of #{command.name}"
76
+ end
77
+
78
+ on_error do |global_options,command,options,args|
79
+ puts "We go an error"
80
+ return true # does the standard error handling code
81
+ # return false # this would skip standard error handling code
82
+ end
83
+
84
+ Now, we run the program using the arguments the user provided on the command line
85
+
86
+ run(ARGV)
87
+
88
+ What this gives you:
89
+
90
+ * A reasonably useful help system. <tt>your_program help</tt> will list all the global options and commands (along with command aliases) and <tt>your_program help command_name</tt> will list help for that given command.
91
+ * Error handling when flags do not receive arguments or unknown flags or switches are given
92
+ * Error handling when an unknown command is specified
93
+ * Default values for flags if they are not specified by the user (switches all default to false)
94
+
95
+ What this doesn't give you:
96
+
97
+ * A way to indicate required flags
98
+ * A way to indicate a require argument or required number of arguments
99
+ * A way to do default switches to 'true' and therefore accept things like <tt>--no-force</tt>
100
+
101
+ = Interface Generated
102
+
103
+ *executable* <i>global options and flags</i> *command* <i>command specific options and flags</i> `arguments`
104
+
105
+ [switch] a command line control string that takes no argument. The <tt>-l</tt> in <tt>ls -l</tt>
106
+ [flag] a switch that takes an argument. The <tt>-d' '</tt> in <tt>cut -d' ' file</tt>
107
+ [command] the command to execute. The <tt>rebase</tt> in <tt>git rebase</tt>
108
+ [arguments] Anything that's not a switch, flag, or command. The <tt>main.c</tt> in <tt>git add main.c</tt>
109
+
110
+ == Switches
111
+
112
+ Switches can be specified one at a time in either a long or short format:
113
+
114
+ git add -i
115
+ git add --interactive
116
+
117
+ Switches can also be combined in their short form:
118
+
119
+ ls -l -a
120
+ ls -la
121
+
122
+ == Flags
123
+
124
+ Flags can be specified in long or short form, and with or without an equals:
125
+
126
+ git merge -s resolve
127
+ git merge --strategy=resolve
128
+
129
+ == Stop Switch
130
+
131
+ A <tt>--</tt> at any time stops processing and sends the rest of the argument to the command as arguments, even if
132
+ they start with a "--"
133
+
data/bin/gli ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/ruby
2
+ $: << File.expand_path(File.dirname(__FILE__) + '/../lib')
3
+
4
+ require 'gli'
5
+ require 'support/scaffold'
6
+
7
+ include GLI
8
+ desc 'Be verbose'
9
+ switch :v
10
+
11
+ desc 'Print version'
12
+ switch :version
13
+
14
+ desc 'Dry run; don\'t change the disk'
15
+ switch :n
16
+
17
+ desc 'Root dir in which to create project'
18
+ default_value '.'
19
+ flag [:r,:root]
20
+
21
+ desc 'Create a new GLI-based project'
22
+ arg_name 'project_name [command[ command]*]'
23
+ command [:init,:scaffold] do |c|
24
+
25
+ c.desc 'Create an ext dir'
26
+ c.switch [:e,:ext]
27
+
28
+ c.desc 'Do not create a test dir'
29
+ c.switch [:notest]
30
+
31
+ c.desc 'Overwrite/ignore existing files and directories'
32
+ c.switch [:force]
33
+
34
+ c.action do |g,o,args|
35
+ if args.length < 1
36
+ raise(MissingArgumentException,'You must specify the name of your project')
37
+ end
38
+ Scaffold.create_scaffold(g[:r],!o[:notest],o[:e],args[0],args[1..-1],o[:force],g[:n])
39
+ end
40
+ end
41
+
42
+ pre do |global,command,options,args|
43
+ if (!command || command.name == :help) && global[:version]
44
+ puts "#{$0} v#{GLI::VERSION}"
45
+ false
46
+ else
47
+ puts "Executing #{command.name}" if global[:v]
48
+ true
49
+ end
50
+ end
51
+
52
+ post do |global,command,options,args|
53
+ puts "Executed #{command.name}" if global[:v]
54
+ end
55
+
56
+ on_error do |global,command,options,args|
57
+ puts "Got an error" if global[:v]
58
+ true
59
+ end
60
+
61
+ run(ARGV)
@@ -0,0 +1,266 @@
1
+ require 'gli/command_line_token.rb'
2
+ require 'gli/command.rb'
3
+ require 'gli/switch.rb'
4
+ require 'gli/flag.rb'
5
+ require 'support/help.rb'
6
+
7
+ # A means to define and parse a command line interface that works as
8
+ # Git's does, in that you specify global options, a command name, command
9
+ # specific options, and then command arguments.
10
+ module GLI
11
+ extend self
12
+
13
+ VERSION = '0.1.4'
14
+
15
+ @@program_name = $0.split(/\//)[-1]
16
+ @@post_block = nil
17
+ @@pre_block = nil
18
+ @@error_block = nil
19
+
20
+ # Reset the GLI module internal data structures; mostly for testing
21
+ def reset
22
+ switches.clear
23
+ flags.clear
24
+ commands.clear
25
+ clear_nexts
26
+ end
27
+
28
+ # describe the next switch, flag, or command
29
+ def desc(description); @@next_desc = description; end
30
+ # describe the argument name of the next flag
31
+ def arg_name(name); @@next_arg_name = name; end
32
+ # set the default value of the next flag
33
+ def default_value(val); @@next_default_value = val; end
34
+
35
+ # Create a flag, which is a switch that takes an argument
36
+ def flag(names)
37
+ flag = Flag.new(names,@@next_desc,@@next_arg_name,@@next_default_value)
38
+ flags[flag.name] = flag
39
+ clear_nexts
40
+ end
41
+
42
+ # Create a switch
43
+ def switch(names)
44
+ switch = Switch.new(names,@@next_desc)
45
+ switches[switch.name] = switch
46
+ clear_nexts
47
+ end
48
+
49
+ # Define a command.
50
+ def command(names)
51
+ command = Command.new(names,@@next_desc,@@next_arg_name)
52
+ commands[command.name] = command
53
+ yield command
54
+ clear_nexts
55
+ end
56
+
57
+ # Define a block to run after command line arguments are parsed
58
+ # but before any command is run. If this block raises an exception
59
+ # the command specified will not be executed.
60
+ # The block will receive the global-options,command,options, and arguments
61
+ # If this block evaluates to true, the program will proceed; otherwise
62
+ # the program will end immediately
63
+ def pre(&a_proc)
64
+ @@pre_block = a_proc
65
+ end
66
+
67
+ # Define a block to run after command hase been executed, only
68
+ # if there was not an error.
69
+ # The block will receive the global-options,command,options, and arguments
70
+ def post(&a_proc)
71
+ @@post_block = a_proc
72
+ end
73
+
74
+ # Define a block to run if an error occurs.
75
+ # The block will receive the global-options,command,options, and arguments
76
+ def on_error(&a_proc)
77
+ @@error_block = a_proc
78
+ end
79
+
80
+ # Runs whatever command is needed based on the arguments.
81
+ def run(args)
82
+ commands[:help] = DefaultHelpCommand.new if !commands[:help]
83
+ begin
84
+ global_options,command,options,arguments = parse_options(args)
85
+ proceed = true
86
+ proceed = @@pre_block.call(global_options,command,options,arguments) if @@pre_block
87
+ if proceed
88
+ command = commands[:help] if !command
89
+ command.execute(global_options,options,arguments)
90
+ @@post_block.call(global_options,command,options,arguments) if @@post_block
91
+ end
92
+ rescue UnknownCommandException, UnknownArgumentException, MissingArgumentException => ex
93
+ regular_error_handling = true
94
+ if @@error_block
95
+ regular_error_handling = @@error_block.call(global_options,command,options,arguments,ex)
96
+ end
97
+
98
+ if regular_error_handling
99
+ puts "error: #{ex}"
100
+ puts
101
+ help = commands[:help]
102
+ help.execute({},{},[])
103
+ end
104
+ end
105
+ end
106
+
107
+ def program_name(override=nil)
108
+ if override
109
+ @@program_name = override
110
+ end
111
+ @@program_name
112
+ end
113
+
114
+ # Returns an array of four values:
115
+ # * global options (as a Hash)
116
+ # * Command
117
+ # * command options (as a Hash)
118
+ # * arguments (as an Array)
119
+ def parse_options(args)
120
+ global_options,command,options,arguments = parse_options_helper(args.clone,Hash.new,nil,Hash.new,Array.new)
121
+ flags.each { |name,flag| global_options[name] = flag.default_value if !global_options[name] }
122
+ command.flags.each { |name,flag| options[name] = flag.default_value if !options[name] }
123
+ return [global_options,command,options,arguments]
124
+ end
125
+
126
+ # Finds the index of the first non-flag
127
+ # argument or -1 if there wasn't one.
128
+ def find_non_flag_index(args)
129
+ args.each_index do |i|
130
+ return i if args[i] =~ /^[^\-]/;
131
+ return i-1 if args[i] =~ /^\-\-$/;
132
+ end
133
+ -1;
134
+ end
135
+
136
+ alias :d :desc
137
+ alias :f :flag
138
+ alias :s :switch
139
+ alias :c :command
140
+
141
+ def clear_nexts
142
+ @@next_desc = nil
143
+ @@next_arg_name = nil
144
+ @@next_default_value = nil
145
+ end
146
+
147
+ clear_nexts
148
+
149
+ def flags; @@flags ||= {}; end
150
+ def switches; @@switches ||= {}; end
151
+ def commands; @@commands ||= {}; end
152
+
153
+ # Recursive helper for parsing command line options
154
+ # [args] the arguments that have yet to be processed
155
+ # [global_options] the global options hash
156
+ # [command] the Command that has been identified (or nil if not identified yet)
157
+ # [command_options] options for Command
158
+ # [arguments] the arguments for Command
159
+ #
160
+ # This works by finding the first non-switch/flag argument, and taking that sublist and trying to pick out
161
+ # flags and switches. After this is done, one of the following is true:
162
+ # * the sublist is empty - in this case, go again, as there might be more flags to parse
163
+ # * the sublist has a flag left in it - unknown flag; we bail
164
+ # * the sublist has a non-flag left in it - this is the command (or the start of the arguments list)
165
+ #
166
+ # This sort does the same thing in two phases; in the first phase, the command hasn't been identified, so
167
+ # we are looking for global switches and flags, ending when we get the command.
168
+ #
169
+ # Once the command has been found, we start looking for command-specific flags and switches.
170
+ # When those have been found, we know the rest of the argument list is arguments for the command
171
+ def parse_options_helper(args,global_options,command,command_options,arguments)
172
+ non_flag_i = find_non_flag_index(args)
173
+ all_flags = false
174
+ if non_flag_i == 0
175
+ # no flags
176
+ if !command
177
+ command_name = args.shift
178
+ command = find_command(command_name)
179
+ raise(UnknownCommandException,"Unknown command '#{command_name}'") if !command
180
+ return parse_options_helper(args,global_options,command,command_options,arguments)
181
+ else
182
+ return global_options,command,command_options,arguments | args
183
+ end
184
+ elsif non_flag_i == -1
185
+ all_flags = true
186
+ end
187
+
188
+ try_me = args[0..non_flag_i]
189
+ rest = args[(non_flag_i+1)..args.length]
190
+ if all_flags
191
+ try_me = args
192
+ rest = []
193
+ end
194
+
195
+ # Suck up whatever options we can
196
+ switch_hash = switches
197
+ flag_hash = flags
198
+ options = global_options
199
+ if command
200
+ switch_hash = command.switches
201
+ flag_hash = command.flags
202
+ options = command_options
203
+ end
204
+
205
+ switch_hash.each do |name,switch|
206
+ value = switch.get_value!(try_me)
207
+ options[name] = value if !options[name]
208
+ end
209
+
210
+ flag_hash.each do |name,flag|
211
+ value = flag.get_value!(try_me)
212
+ options[name] = value if !options[name]
213
+ end
214
+
215
+ if try_me.empty?
216
+ return [global_options,command,command_options,arguments] if rest.empty?
217
+ # If we have no more options we've parsed them all
218
+ # and rest may have more
219
+ return parse_options_helper(rest,global_options,command,command_options,arguments)
220
+ else
221
+ if command
222
+ check = rest
223
+ check = rest | try_me if all_flags
224
+ check.each() do |arg|
225
+ if arg =~ /^\-\-$/
226
+ try_me.delete arg
227
+ break
228
+ end
229
+ raise(UnknownArgumentException,"Unknown argument #{arg}") if arg =~ /^\-/
230
+ end
231
+ return [global_options,command,command_options,try_me | rest]
232
+ else
233
+ # Now we have our command name
234
+ command_name = try_me.shift
235
+ raise(UnknownArgumentException,"Unknown argument #{command_name}") if command_name =~ /^\-/
236
+
237
+ command = find_command(command_name)
238
+ raise(UnknownCommandException,"Unknown command '#{command_name}'") if !command
239
+
240
+ return parse_options_helper(rest,global_options,command,command_options,arguments)
241
+ end
242
+ end
243
+
244
+ end
245
+
246
+ def find_command(name)
247
+ sym = name.to_sym
248
+ return commands[name.to_sym] if commands[sym]
249
+ commands.keys.each do |command_name|
250
+ command = commands[command_name]
251
+ return command if (command.aliases && command.aliases.include?(sym))
252
+ end
253
+ nil
254
+ end
255
+
256
+ # Raise this if you get an argument you were not expecting
257
+ class UnknownArgumentException < Exception
258
+ end
259
+
260
+ class UnknownCommandException < Exception
261
+ end
262
+
263
+ # Raise this if your command doesn't get the number of arguments you were expecting
264
+ class MissingArgumentException < Exception
265
+ end
266
+ end
@@ -0,0 +1,71 @@
1
+ require 'gli/command_line_token.rb'
2
+
3
+ module GLI
4
+ # A command to be run, in context of global flags and switches
5
+ class Command < CommandLineToken
6
+
7
+ # Create a new command
8
+ #
9
+ # [names] the name or names of this command (symbol or Array of symbols)
10
+ # [description] description of this command
11
+ # [arguments_name] description of the arguments, or nil if this command doesn't take arguments
12
+ #
13
+ def initialize(names,description,arguments_name=nil)
14
+ super(names,description)
15
+ @arguments_description = arguments_name || ''
16
+ clear_nexts
17
+ end
18
+
19
+ def names
20
+ all_forms
21
+ end
22
+
23
+ def usage
24
+ usage = name.to_s
25
+ usage += ' [options]' if !flags.empty? || !switches.empty?
26
+ usage += ' ' + @arguments_description if @arguments_description
27
+ usage
28
+ end
29
+
30
+ def flags; @flags ||= {}; end
31
+ def switches; @switches ||= {}; end
32
+
33
+ # describe the next switch or flag
34
+ def desc(description); @next_desc = description; end
35
+ # describe the argument name of the next flag
36
+ def arg_name(name); @next_arg_name = name; end
37
+ # set the default value of the next flag
38
+ def default_value(val); @next_default_value = val; end
39
+
40
+ def flag(names)
41
+ flag = Flag.new(names,@next_desc,@next_arg_name,@next_default_value)
42
+ flags[flag.name] = flag
43
+ clear_nexts
44
+ end
45
+
46
+ # Create a switch
47
+ def switch(names)
48
+ switch = Switch.new(names,@next_desc)
49
+ switches[switch.name] = switch
50
+ clear_nexts
51
+ end
52
+
53
+ def action(&block)
54
+ @action = block
55
+ end
56
+
57
+ def self.name_as_string(name)
58
+ name.to_s
59
+ end
60
+
61
+ def clear_nexts
62
+ @next_desc = nil
63
+ @next_arg_name = nil
64
+ @next_default_value = nil
65
+ end
66
+
67
+ def execute(global_options,options,arguments)
68
+ @action.call(global_options,options,arguments)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,47 @@
1
+ module GLI
2
+ # Logical element of a command line, mostly so that subclasses can have similar
3
+ # initialization and interface
4
+ class CommandLineToken
5
+ attr_reader :name
6
+ attr_reader :aliases
7
+ attr_reader :description
8
+
9
+ def initialize(names,description)
10
+ @description = description
11
+ @name,@aliases,@names = parse_names(names)
12
+ end
13
+
14
+ def usage
15
+ all_forms
16
+ end
17
+
18
+ private
19
+ # Returns a string of all possible forms
20
+ # of this flag. Mostly intended for printing
21
+ # to the user.
22
+ def all_forms(joiner=', ')
23
+ forms = all_forms_a
24
+ forms.join(joiner)
25
+ end
26
+
27
+
28
+ # Handles dealing with the "names" param, parsing
29
+ # it into the primary name and aliases list
30
+ def parse_names(names)
31
+ names_hash = Hash.new
32
+ names = names.is_a?(Array) ? names : [names]
33
+ names.each { |n| names_hash[self.class.name_as_string(n)] = true }
34
+ name = names.shift
35
+ aliases = names.length > 0 ? names : nil
36
+ [name,aliases,names_hash]
37
+ end
38
+
39
+ def all_forms_a
40
+ forms = [self.class.name_as_string(name)]
41
+ if aliases
42
+ forms |= aliases.collect { |a| self.class.name_as_string(a) }.sort { |x,y| y.length <=> x.length }
43
+ end
44
+ forms
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,66 @@
1
+ require 'gli/command_line_token.rb'
2
+
3
+ module GLI
4
+ # Defines a flag, which is to say a switch that takes an argument
5
+ class Flag < Switch
6
+
7
+ attr_reader :default_value
8
+
9
+ def initialize(names,description,argument_name=nil,default=nil)
10
+ super(names,description)
11
+ @argument_name = argument_name || "arg"
12
+ @default_value = default
13
+ end
14
+
15
+ def get_value!(args)
16
+ args.each_index() do |index|
17
+ arg = args[index]
18
+ present,matched,value = find_me(arg)
19
+ if present
20
+ args.delete_at index
21
+ if !value || value == ''
22
+ if args[index]
23
+ value = args[index]
24
+ args.delete_at index
25
+ return value
26
+ else
27
+ raise(MissingArgumentException,"#{matched} requires an argument")
28
+ end
29
+ else
30
+ return value
31
+ end
32
+ end
33
+ end
34
+ return @default_value
35
+ end
36
+
37
+ def find_me(arg)
38
+ if @names[arg]
39
+ return [true,arg,nil] if arg.length == 2
40
+ # This means we matched the long-form, but there's no argument
41
+ raise(MissingArgumentException,"#{arg} requires an argument via #{arg}=argument")
42
+ end
43
+ @names.keys.each() do |name|
44
+ match_string = "^#{name}=(.*)$"
45
+ match_data = arg.match(match_string)
46
+ return [true,name,$1] if match_data;
47
+ end
48
+ [false,nil,nil]
49
+ end
50
+
51
+ # Returns a string of all possible forms
52
+ # of this flag. Mostly intended for printing
53
+ # to the user.
54
+ def all_forms(joiner=', ')
55
+ forms = all_forms_a
56
+ string = forms.join(joiner)
57
+ if forms[-1] =~ /^\-\-/
58
+ string += '='
59
+ else
60
+ string += ' '
61
+ end
62
+ string += @argument_name
63
+ return string
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,56 @@
1
+ require 'gli/command_line_token.rb'
2
+
3
+ module GLI
4
+ # Defines a command line switch
5
+ class Switch < CommandLineToken
6
+
7
+ def initialize(names,description)
8
+ super(names,description)
9
+ end
10
+
11
+ # Given the argument list, scans it looking for this switch
12
+ # returning true if it's in the argumennt list (and removing it from the argument list)
13
+ def get_value!(args)
14
+ idx = -1
15
+ args.each_index do |i|
16
+ result = find_me(args[i])
17
+ if result[0]
18
+ if result[1]
19
+ args[i] = result[1]
20
+ else
21
+ args.delete_at i
22
+ end
23
+ return result[0]
24
+ end
25
+ end
26
+ false
27
+ end
28
+
29
+ # Finds the switch in the given arg, returning the arg to keep.
30
+ # Returns an array of size 2:
31
+ # [0] true or false if the arg was found
32
+ # [1] the remaining arg to keep in the command line or nil to remove it
33
+ def find_me(arg)
34
+ if @names[arg]
35
+ return [true,nil]
36
+ end
37
+ @names.keys.each() do |name|
38
+ if name =~ /^-(\w)$/
39
+ match_string = "^\\-(\\w*)#{$1}(\\w*)$"
40
+ match_data = arg.match(match_string)
41
+ if match_data
42
+ # Note that if [1] and [2] were both empty
43
+ # we'd have returned above
44
+ return [true, "-" + match_data[1] + match_data[2]]
45
+ end
46
+ end
47
+ end
48
+ [false]
49
+ end
50
+
51
+ def self.name_as_string(name)
52
+ string = name.to_s
53
+ string.length == 1 ? "-#{string}" : "--#{string}"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,112 @@
1
+ require 'gli'
2
+ require 'gli/command'
3
+
4
+ module GLI
5
+ class DefaultHelpCommand < Command
6
+ def initialize
7
+ super(:help,'Shows list of commands or help for one command','[command]')
8
+ end
9
+
10
+ def execute(global_options,options,arguments)
11
+ if arguments.empty?
12
+ list_global_flags
13
+ list_commands
14
+ else
15
+ list_one_command_help(arguments[0])
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def list_global_flags
22
+ usage = "usage: #{GLI.program_name} command"
23
+ all_options = GLI.switches.merge(GLI.flags)
24
+ if !all_options.empty?
25
+ usage += ' [options]'
26
+ end
27
+ puts usage
28
+ puts
29
+ puts 'Options:' if !all_options.empty?
30
+ output_command_tokens_for_help(all_options)
31
+ puts if !all_options.empty?
32
+ end
33
+
34
+ def list_commands
35
+ puts 'Commands:'
36
+ output_command_tokens_for_help(GLI.commands,:names)
37
+ end
38
+
39
+ def list_one_command_help(command_name)
40
+ command = GLI.find_command(command_name)
41
+ if command
42
+ puts command.usage
43
+ description = wrap(command.description,4)
44
+ puts " #{description}"
45
+ all_options = command.switches.merge(command.flags)
46
+ if !all_options.empty?
47
+ puts
48
+ puts "Options:"
49
+ output_command_tokens_for_help(all_options)
50
+ end
51
+ else
52
+ puts "No such command #{command_name}"
53
+ end
54
+ end
55
+
56
+ def output_command_tokens_for_help(tokens,usage_name=:usage)
57
+ max = 0
58
+ tokens.values.each do |token|
59
+ len = token.send(usage_name).length
60
+ if len > max
61
+ max = len
62
+ end
63
+ end
64
+ names = tokens.keys.sort { |x,y| x.to_s <=> y.to_s }
65
+ names.each do |name|
66
+ token = tokens[name]
67
+ description = token.description || ''
68
+ if token.kind_of? Flag
69
+ description += " (default: #{token.default_value})" if token.default_value
70
+ end
71
+ description = wrap(description,max+7)
72
+ printf " %-#{max}s - %s\n",token.send(usage_name),description
73
+ end
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ # Wraps the line at the given column length, using the given line padding.
80
+ # Assumes that the first line doesn't need the padding, as its filled
81
+ # up with other stuff
82
+ def wrap(line,pad_length=0,line_length=80)
83
+ line_padding = sprintf("%#{pad_length}s",'')
84
+ words = line.split(/\s+/)
85
+ return line if !words || words.empty?
86
+ wrapped = ''
87
+ while wrapped.length + line_padding.length < line_length
88
+ wrapped += ' ' if wrapped.length > 0
89
+ word = words.shift
90
+ if (wrapped.length + line_padding.length + word.length > line_length)
91
+ words.unshift word
92
+ break;
93
+ end
94
+ wrapped += word
95
+ return wrapped if words.empty?
96
+ end
97
+ wrapped += "\n"
98
+ this_line = line_padding
99
+ words.each do |word|
100
+ if this_line.length + word.length > line_length
101
+ wrapped += this_line
102
+ wrapped += "\n"
103
+ this_line = line_padding + word
104
+ else
105
+ this_line += ' ' if this_line.length > line_padding.length
106
+ this_line += word
107
+ end
108
+ end
109
+ wrapped.chomp!
110
+ wrapped + "\n" + this_line
111
+ end
112
+ end
@@ -0,0 +1,95 @@
1
+ require 'gli'
2
+ require 'fileutils'
3
+
4
+ module GLI
5
+ class Scaffold
6
+
7
+ def self.create_scaffold(root_dir,create_test_dir,create_ext_dir,project_name,commands,force=false,dry_run=false)
8
+ dirs = [File.join(root_dir,project_name,'lib')]
9
+ dirs << File.join(root_dir,project_name,'bin')
10
+ dirs << File.join(root_dir,project_name,'test') if create_test_dir
11
+ dirs << File.join(root_dir,project_name,'ext') if create_ext_dir
12
+
13
+ if mkdirs(dirs,force,dry_run)
14
+ mk_binfile(root_dir,create_ext_dir,force,dry_run,project_name,commands)
15
+ end
16
+ end
17
+
18
+ def self.mk_binfile(root_dir,create_ext_dir,force,dry_run,project_name,commands)
19
+ bin_file = File.join(root_dir,project_name,'bin',project_name)
20
+ if !File.exist?(bin_file) || force
21
+ if !dry_run
22
+ File.open(bin_file,'w') do |file|
23
+ file.puts '#!/usr/bin/ruby'
24
+ file.puts '$: << File.expand_path(File.dirname(__FILE__) + \'/../lib\')'
25
+ file.puts '$: << File.expand_path(File.dirname(__FILE__) + \'/../ext\')' if create_ext_dir
26
+ file.puts <<EOS
27
+ require 'gli'
28
+
29
+ include GLI
30
+
31
+ desc 'Describe some switch here'
32
+ switch [:s,:switch]
33
+
34
+ desc 'Describe some flag here'
35
+ default_value 'the default'
36
+ arg_name 'The name of the argument'
37
+ flag [:f,:flagname]
38
+ EOS
39
+ commands.each do |command|
40
+ file.puts <<EOS
41
+
42
+ desc 'Describe #{command} here'
43
+ arg_name 'Describe arguments to #{command} here'
44
+ command :#{command} do |c|
45
+ c.desc 'Describe a switch to #{command}'
46
+ c.switch :s
47
+
48
+ c.desc 'Describe a flag to #{command}'
49
+ c.default_value 'default'
50
+ c.flag :s
51
+
52
+ c.action do |global_options,options,args|
53
+ # Your command logic here
54
+ end
55
+ end
56
+ EOS
57
+ end
58
+ puts "Create #{bin_file}"
59
+ end
60
+ end
61
+ else
62
+ puts bin_file + " exists; use --force to override"
63
+ return false
64
+ end
65
+ true
66
+ end
67
+
68
+ def self.mkdirs(dirs,force,dry_run)
69
+ exists = false
70
+ if !force
71
+ dirs.each do |dir|
72
+ if File.exist? dir
73
+ puts "#{dir} exists; use --force to override"
74
+ exists = true
75
+ end
76
+ end
77
+ end
78
+ if !exists
79
+ dirs.each do |dir|
80
+ puts "Creating dir #{dir}..."
81
+ if dry_run
82
+ $stderr.puts "dry-run; #{dir} not created"
83
+ else
84
+ FileUtils.mkdir_p dir
85
+ end
86
+ end
87
+ else
88
+ puts "Exiting..."
89
+ return false
90
+ end
91
+ true
92
+ end
93
+
94
+ end
95
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: davetron5000-gli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - David Copeland
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-03 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: davidcopeland@naildrivin5.com
18
+ executables:
19
+ - gli
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - lib/gli/command.rb
26
+ - lib/gli/command_line_token.rb
27
+ - lib/gli/flag.rb
28
+ - lib/gli/switch.rb
29
+ - lib/gli.rb
30
+ - lib/support/help.rb
31
+ - lib/support/scaffold.rb
32
+ - bin/gli
33
+ - README.rdoc
34
+ has_rdoc: true
35
+ homepage: http://davetron5000.github.com/gli
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --title
39
+ - Git Like Interface
40
+ - --main
41
+ - README.rdoc
42
+ - -ri
43
+ require_paths:
44
+ - lib
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.2.0
62
+ signing_key:
63
+ specification_version: 2
64
+ summary: A Git Like Interface for building command line apps
65
+ test_files: []
66
+