clive 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,8 @@
1
- class Clive
2
-
1
+ module Clive
2
+
3
3
  # A switch which can be triggered with either --no-[name] and --[name].
4
- # The 'truthness' of this is then passed to the block.
4
+ # The 'truth' of this is then passed to the block.
5
+ #
5
6
  class Bool < Option
6
7
  attr_accessor :truth
7
8
 
@@ -13,27 +14,27 @@ class Clive
13
14
  # +short+ and/or +desc+ can be omitted when creating a Boolean, all
14
15
  # other arguments must be present.
15
16
  #
16
- # @overload initialize(short, long, desc, truth, &block)
17
- # Creates a new boolean switch
18
- # @param [Symbol] short single character to use
19
- # @param [Symbol] long word/longer name for boolean switch
20
- # @param [String] desc description of use/purpose
17
+ # @param names [Array[Symbol]]
18
+ # Names that the boolean switch can be called with, must include
19
+ # a long name (eg. 2 or more characters) so that the --no- can
20
+ # be prefixed.
21
+ #
22
+ # @param desc [String]
23
+ # A description of the bool.
24
+ #
25
+ # @param truth [true, false]
26
+ # Truth of the switch to create.
21
27
  #
22
- # @yield [Boolean] A block to be run when the switch is triggered
23
- # @raise [MissingLongName] raises when a long name is not given
28
+ # @yield [true, false] A block to be run when the switch is triggered
29
+ # @raise [MissingLongName] Raised when a long name is not given
24
30
  #
25
- def initialize(*args, truth, &block)
31
+ def initialize(names, desc, truth, &block)
26
32
  @names = []
27
- args.each do |i|
28
- case i
29
- when Symbol
30
- if truth
31
- @names << i.to_s
32
- else
33
- @names << "no-#{i.to_s}" if i.length > 1
34
- end
35
- when String
36
- @desc = i
33
+ names.each do |i|
34
+ if truth
35
+ @names << i.to_s
36
+ else
37
+ @names << "no-#{i.to_s}" if i.length > 1
37
38
  end
38
39
  end
39
40
 
@@ -42,27 +43,24 @@ class Clive
42
43
  raise MissingLongName, @names[0]
43
44
  end
44
45
 
46
+ @desc = desc
45
47
  @truth = truth
46
48
  @block = block
47
49
  end
48
50
 
49
- # Run the block with +@truth+
51
+ # Run the block with the switches truth.
50
52
  def run
51
53
  @block.call(@truth)
52
54
  end
53
55
 
54
- # @param [Integer] width the total ideal width of help
55
- # @param [Integer] prepend the number of spaces to add before each line
56
- # @return [String] summary for help or nil if +@truth = false+
57
- def summary(width=30, prepend=5)
58
- return nil unless @truth
59
-
60
- n = names_to_strings(true).join(', ')
61
- spaces = width-n.length
62
- spaces = 1 if spaces < 1
63
- s = spaces(spaces)
64
- p = spaces(prepend)
65
- "#{p}#{n}#{s}#{@desc}"
56
+ # Should only return a hash when this is the 'true' switch.
57
+ # @see Clive::Option#to_h
58
+ def to_h
59
+ if @truth
60
+ {'names' => names_to_strings(true), 'desc' => @desc}
61
+ else
62
+ nil
63
+ end
66
64
  end
67
65
 
68
66
  end
@@ -1,4 +1,4 @@
1
- class Clive
1
+ module Clive
2
2
 
3
3
  # A string which describes the command to execute
4
4
  # eg. git add
@@ -8,18 +8,18 @@ class Clive
8
8
 
9
9
  attr_accessor :options, :commands
10
10
  attr_accessor :argv, :base
11
- attr_accessor :header, :footer
11
+ attr_reader :names, :current_desc
12
12
 
13
13
  # Create a new Command instance
14
14
  #
15
15
  # @overload initialize(base, &block)
16
16
  # Creates a new base Command to house everything else
17
- # @param [Boolean] base whether the command is the base
17
+ # @param base [Boolean] whether the command is the base
18
18
  #
19
- # @overload initialize(name, desc, &block)
19
+ # @overload initialize(names, desc, &block)
20
20
  # Creates a new Command as part of the base Command
21
- # @param [Symbol] name the name of the command
22
- # @param [String] desc the description of the command
21
+ # @param names [Symbol] the name of the command
22
+ # @param desc [String] the description of the command
23
23
  #
24
24
  # @yield A block to run, containing switches, flags and commands
25
25
  #
@@ -47,10 +47,15 @@ class Clive
47
47
  @block = block
48
48
  end
49
49
 
50
- @header = "Usage: #{File.basename($0, '.*')} "
51
- @header << (@base ? "[commands]" : @names.join(', '))
52
- @header << " [options]"
50
+ # Create basic header "Usage: filename [command] [options]
51
+ # or "Usage: filename commandname(s) [options]
52
+ @header = "Usage: #{File.basename($0, '.*')} " <<
53
+ (@base ? "[command]" : @names.join(', ')) <<
54
+ " [options]"
55
+
53
56
  @footer = nil
57
+ @current_desc = ""
58
+ help_formatter :default
54
59
 
55
60
  self.build_help
56
61
  end
@@ -146,7 +151,7 @@ class Clive
146
151
  pre_command << i
147
152
  end
148
153
  end
149
-
154
+
150
155
  post_command = Tokens.new(tokens.array - pre_command - [command])
151
156
  pre_command_tokens = parse(pre_command)
152
157
  r = pre_command_tokens
@@ -179,7 +184,7 @@ class Clive
179
184
  else
180
185
  if k == :word
181
186
  # add to last flag?
182
- if r.last && r.last[0] == :flag && r.last.size - 2 < r.last[1].args.size
187
+ if r.last && r.last[0] == :flag && r.last.size - 2 < r.last[1].arg_size
183
188
  r.last.push(v)
184
189
  else
185
190
  r << [:argument, v]
@@ -192,13 +197,16 @@ class Clive
192
197
  r
193
198
  end
194
199
 
195
- # @group Missing Helpers
200
+ def to_h
201
+ {
202
+ 'names' => @names,
203
+ 'desc' => @desc
204
+ }
205
+ end
196
206
 
197
- def option_missing(&block)
198
- @option_missing = block
199
- end
207
+
200
208
 
201
- # @group Creators
209
+ # @group DSL
202
210
 
203
211
  # Add a new command to +@commands+
204
212
  #
@@ -211,19 +219,35 @@ class Clive
211
219
  # and flags
212
220
  #
213
221
  def command(*args, &block)
214
- @commands << Command.new(*args, &block)
222
+ @commands << Command.new(*args, @current_desc, &block)
223
+ @current_desc = ""
215
224
  end
216
225
 
217
226
  # Add a new switch to +@switches+
218
227
  # @see Switch#initialize
219
228
  def switch(*args, &block)
220
- @options << Switch.new(*args, &block)
229
+ @options << Switch.new(args, @current_desc, &block)
230
+ @current_desc = ""
221
231
  end
222
232
 
223
233
  # Adds a new flag to +@flags+
224
234
  # @see Flag#initialize
225
235
  def flag(*args, &block)
226
- @options << Flag.new(*args, &block)
236
+ names = []
237
+ arg = []
238
+ args.each do |i|
239
+ if i.is_a? Symbol
240
+ names << i
241
+ else
242
+ if i[:arg]
243
+ arg << i[:arg]
244
+ else
245
+ arg << i[:args]
246
+ end
247
+ end
248
+ end
249
+ @options << Flag.new(names, @current_desc, arg, &block)
250
+ @current_desc = ""
227
251
  end
228
252
 
229
253
  # Creates a boolean switch. This is done by adding two switches of
@@ -232,21 +256,48 @@ class Clive
232
256
  #
233
257
  # @see Bool#initialize
234
258
  def bool(*args, &block)
235
- @options << Bool.new(*args, true, &block)
236
- @options << Bool.new(*args, false, &block)
259
+ @options << Bool.new(args, @current_desc, true, &block)
260
+ @options << Bool.new(args, @current_desc, false, &block)
261
+ @current_desc= ""
237
262
  end
238
263
 
239
- # @group Help
240
-
241
- # This actually creates a switch with "-h" and "--help" that controls
242
- # the help on this command.
243
- def build_help
244
- @options << Switch.new(:h, :help, "Display help") do
245
- puts self.help
246
- exit 0
264
+ # Add a description for the next option in the class. Or acts as an
265
+ # accessor for @desc.
266
+ #
267
+ # @example
268
+ #
269
+ # class CLI
270
+ # include Clive::Parser
271
+ #
272
+ # desc 'Force build docs'
273
+ # switch :force do
274
+ # # code
275
+ # end
276
+ # end
277
+ #
278
+ def desc(str=nil)
279
+ if str
280
+ @current_desc = str
281
+ else
282
+ @desc
247
283
  end
248
284
  end
249
285
 
286
+ # Define a block to execute when the option to execute cannot be found.
287
+ #
288
+ # @example
289
+ #
290
+ # class CLI
291
+ # include Clive::Parser
292
+ #
293
+ # option_missing do |name|
294
+ # puts "#{name} couldn't be found"
295
+ # end
296
+ #
297
+ def option_missing(&block)
298
+ @option_missing = block
299
+ end
300
+
250
301
  # Set the header
251
302
  def header(val)
252
303
  @header = val
@@ -257,40 +308,106 @@ class Clive
257
308
  @footer = val
258
309
  end
259
310
 
260
- def summary(width=30, prepend=5)
261
- a = @names.sort.join(', ')
262
- b = @desc
263
- s = spaces(width-a.length)
264
- p = spaces(prepend)
265
- "#{p}#{a}#{s}#{b}"
266
- end
311
+ # @group Help
267
312
 
313
+ # This actually creates a switch with "-h" and "--help" that controls
314
+ # the help on this command.
315
+ def build_help
316
+ @options << Switch.new([:h, :help], "Display help") do
317
+ puts self.help
318
+ exit 0
319
+ end
320
+ end
321
+
268
322
  # Generate the summary for help, show all flags and switches, but do not
269
323
  # show the flags and switches within each command. Should also prepend the
270
324
  # header and append the footer if set.
271
- def help(width=30, prepend=5)
272
- summary = "#{@header}\n"
325
+ def help
326
+ @formatter.format(@header, @footer, @commands, @options)
327
+ end
328
+
329
+ # This allows you to define how the output from #help looks.
330
+ #
331
+ # For this you have access to several tokens which are evaluated in an object
332
+ # with the correct values, this means you are able to use #join on arrays or
333
+ # prepend, etc. The variables (tokens) are:
334
+ #
335
+ # * prepend - a string of spaces as specified when #help_formatter is called
336
+ # * names - an array of names for the option
337
+ # * spaces - a string of spaces to align the descriptions properly
338
+ # * desc - a string of the description for the option
339
+ #
340
+ # And for flags you have access to:
341
+ #
342
+ # * args - an array of arguments for the flag
343
+ # * options - an array of options to choose from
344
+ #
345
+ #
346
+ # @overload help_formatter(args, &block)
347
+ # Create a new help formatter to use.
348
+ # @param args [Hash]
349
+ # @option args [Integer] :width Width before flexible spaces
350
+ # @option args [Integer] :prepend Width of spaces to prepend with
351
+ #
352
+ # @overload help_formatter(name)
353
+ # Use an existing help formatter.
354
+ # @param name [Symbol] name of the formatter (either +:default+ or +:white+)
355
+ #
356
+ #
357
+ # @example
358
+ #
359
+ # CLI.help_formatter do |h|
360
+ #
361
+ # h.switch "{prepend}{names.join(', ')} {spaces}{desc.grey}"
362
+ # h.bool "{prepend}{names.join(', ')} {spaces}{desc.grey}"
363
+ # h.flag "{prepend}{names.join(', ')} {args.join(' ')} {spaces}{desc.grey}"
364
+ # h.command "{prepend}{names.join(', ')} {spaces}{desc.grey}"
365
+ #
366
+ # end
367
+ #
368
+ #
369
+ def help_formatter(*args, &block)
370
+ if block_given?
371
+ width = 30
372
+ prepend = 5
273
373
 
274
- if @options.length > 0
275
- summary << "\n Options:\n"
276
- @options.sort.each do |i|
277
- next if i.names.include?("help")
278
- summary << i.summary(width, prepend) << "\n" if i.summary
374
+ unless args.empty?
375
+ args[0].each do |k,v|
376
+ case k
377
+ when :width
378
+ width = v
379
+ when :prepend
380
+ prepend = v
381
+ end
382
+ end
279
383
  end
280
- end
281
-
282
- if @commands.length > 0
283
- summary << "\n Commands:\n"
284
- @commands.sort.each do |i|
285
- summary << i.summary(width, prepend) << "\n"
384
+
385
+ @formatter = Formatter.new(width, prepend)
386
+ block.call(@formatter)
387
+ @formatter
388
+ else
389
+ case args[0]
390
+ when :default
391
+ help_formatter do |h|
392
+ h.switch "{prepend}{names.join(', ')} {spaces}{desc.grey}"
393
+ h.bool "{prepend}{names.join(', ')} {spaces}{desc.grey}"
394
+ h.flag "{prepend}{names.join(', ')} {args.join(' ')} {spaces}" <<
395
+ "{desc.grey} {options.join('(', ', ', ')').blue.bold}"
396
+ h.command "{prepend}{names.join(', ')} {spaces}{desc.grey}"
397
+ end
398
+
399
+ when :white
400
+ help_formatter do |h|
401
+ h.switch "{prepend}{names.join(', ')} {spaces}{desc}"
402
+ h.bool "{prepend}{names.join(', ')} {spaces}{desc}"
403
+ h.flag "{prepend}{names.join(', ')} {args.join(' ')} {spaces}" <<
404
+ "{desc} {options.join('(', ', ', ')').bold}"
405
+ h.command "{prepend}{names.join(', ')} {spaces}{desc}"
406
+ end
407
+
286
408
  end
287
409
  end
288
-
289
- summary << "\n#{@footer}\n" if @footer
290
-
291
- summary
292
410
  end
293
411
 
294
-
295
412
  end
296
413
  end
@@ -1,4 +1,4 @@
1
- class Clive
1
+ module Clive
2
2
 
3
3
  # General problem
4
4
  class CliveError < StandardError
@@ -23,7 +23,7 @@ class Clive
23
23
  self.reason + ': ' + args.join(' ')
24
24
  end
25
25
  alias_method :to_s, :message
26
-
26
+
27
27
  end
28
28
 
29
29
  # General problem with input
@@ -1,5 +1,4 @@
1
- class Clive
2
-
1
+ module Clive
3
2
  class Array < ::Array
4
3
 
5
4
  # If passed a Symbol or String will get the option or command with that name.
@@ -49,5 +48,24 @@ class Clive
49
48
  result
50
49
  end
51
50
 
51
+ alias_method :_join, :join
52
+
53
+ def join(*args)
54
+ case args.size
55
+ when 1
56
+ self._join(args[0])
57
+
58
+ when 2 # use second for last eg. 1, 2 and 3
59
+ self[0..-2]._join(args[0]) << args[1] << self[-1]
60
+
61
+ when 3 # prepend and append 1st and 3rd eg. (1, 2, 3)
62
+ if self[0] != ""
63
+ args[0] << self._join(args[1]) << args[2]
64
+ else
65
+ ""
66
+ end
67
+ end
68
+ end
69
+
52
70
  end
53
- end
71
+ end