Pablo 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.
- data/README +20 -0
- data/lib/pablo.rb +458 -0
- data/tests/help.rb +135 -0
- data/tests/tests.rb +290 -0
- metadata +55 -0
data/README
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
This is Pablo, a Ruby commandline parser that is entirely based on the concept of blocks.
|
3
|
+
Pablo stands for "PArsing BLOcks" (I know, it's a really stupid name).
|
4
|
+
|
5
|
+
_______________________________________________________
|
6
|
+
|
7
|
+
License: Creative Commons Attribution 3.0 Germany
|
8
|
+
For further information regarding this license, visit:
|
9
|
+
http://creativecommons.org/licenses/by/3.0/de/
|
10
|
+
______________________________________________________
|
11
|
+
|
12
|
+
|
13
|
+
Support for Pablo is given at http://pablo.rubyforge.org/ .
|
14
|
+
=============================
|
15
|
+
|
16
|
+
If you have questions, problems, want to report bugs or request features, this is the
|
17
|
+
place for you.
|
18
|
+
|
19
|
+
Have fun with it!
|
20
|
+
|
data/lib/pablo.rb
ADDED
@@ -0,0 +1,458 @@
|
|
1
|
+
# This is Pablo, a commandline parser for Ruby based entirely on the
|
2
|
+
# concept of blocks.
|
3
|
+
#
|
4
|
+
# If you want to find out more or need a tutorial, go to
|
5
|
+
# http://pablo.rubyforge.org/
|
6
|
+
# You'll find a nice wiki there!
|
7
|
+
#
|
8
|
+
# Author:: Fabian Streitel (karottenreibe)
|
9
|
+
# Copyright:: Copyright (c) 2008 Fabian Streitel
|
10
|
+
# License:: Creative Commons Attribution 3.0 Germany
|
11
|
+
# For further information regarding this license, you can go to
|
12
|
+
# http://creativecommons.org/licenses/by/3.0/de/
|
13
|
+
# Homepage:: http://pablo.rubyforge.org/
|
14
|
+
# Git repo:: http://www.github.org/pablo-commandline-parser/
|
15
|
+
#
|
16
|
+
|
17
|
+
module Pablo
|
18
|
+
##
|
19
|
+
# Parses arguments with the help of the tokens defined in the +block+.
|
20
|
+
#
|
21
|
+
# The arguments passed to the parser can be
|
22
|
+
# - one Hash, containing the options for the parsing process and
|
23
|
+
# - one Array, containing the arguments that should be parsed
|
24
|
+
#
|
25
|
+
# Either of the two can be omitted.
|
26
|
+
# If no arguments Array is found, +ARGV+ is parsed.
|
27
|
+
# If no options Hash is found, the defaults are used.
|
28
|
+
#
|
29
|
+
# See _Pablo::Parser#command_ and _Pablo::Parser#option_ for more details.
|
30
|
+
#
|
31
|
+
def self.parse *argsopts, &block
|
32
|
+
# parse arguments and options apart
|
33
|
+
args, opts = nil, {}
|
34
|
+
args = argsopts[1] if argsopts[1].is_a? Array
|
35
|
+
args = argsopts[0] if argsopts[0].is_a? Array
|
36
|
+
opts = argsopts[1] if argsopts[1].is_a? Hash
|
37
|
+
opts = argsopts[0] if argsopts[0].is_a? Hash
|
38
|
+
|
39
|
+
args = ARGV if args.nil?
|
40
|
+
# do under NO circumstances modify the real args
|
41
|
+
args = args.dup
|
42
|
+
|
43
|
+
# create tokens
|
44
|
+
p = Pablo::TopLevel.new opts
|
45
|
+
p.instance_eval &block if block_given?
|
46
|
+
|
47
|
+
# test on a present help command
|
48
|
+
return if p.help? args
|
49
|
+
|
50
|
+
# prepare arguments for marking
|
51
|
+
args.collect! { |a| [ [], a ] }
|
52
|
+
|
53
|
+
# start marking
|
54
|
+
p.mark args
|
55
|
+
|
56
|
+
# start parsing
|
57
|
+
# we process the args until there are no remaining tokens that request parsing
|
58
|
+
empty = !args.any? { |pair| not pair[0].empty? }
|
59
|
+
|
60
|
+
# do the defaults if noone wants parsing
|
61
|
+
return p.do_defaults if empty
|
62
|
+
|
63
|
+
while not empty
|
64
|
+
# get the next token
|
65
|
+
next_token = args.index { |pair| not pair[0].empty? }
|
66
|
+
# parse it
|
67
|
+
args[next_token][0][0].parse args
|
68
|
+
# test on empty again
|
69
|
+
empty = !args.any? { |pair| not pair[0].empty? }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# The base class for all parsing tokens, i.e. Options and Commands.
|
75
|
+
#
|
76
|
+
# Each token is expected to implement a mark and a parse method.
|
77
|
+
# The mark method is expected to mark all the occurrences of the command with a reference to
|
78
|
+
# it's +self+ object.
|
79
|
+
# The parse method will get all the parameters (including subcommands) for consuming.
|
80
|
+
#
|
81
|
+
class Token
|
82
|
+
##
|
83
|
+
# Initializes the structure so it's ready for processing
|
84
|
+
#
|
85
|
+
def initialize toplevel
|
86
|
+
@toplevel = toplevel
|
87
|
+
@toplevel.everyone << self if self != @toplevel
|
88
|
+
@aliases = []
|
89
|
+
@run = lambda {}
|
90
|
+
@requests = []
|
91
|
+
@tokens = []
|
92
|
+
@default = false
|
93
|
+
@sdesc = ""
|
94
|
+
@ldesc = ""
|
95
|
+
end
|
96
|
+
|
97
|
+
attr_reader :tokens
|
98
|
+
|
99
|
+
##
|
100
|
+
# Sets the +block+ to run on discovery of the token.
|
101
|
+
#
|
102
|
+
def run &block
|
103
|
+
@run = block if block_given?
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Adds the alias +a+ to the list of aliases for this token.
|
108
|
+
#
|
109
|
+
def name a
|
110
|
+
@aliases << a.to_s
|
111
|
+
end
|
112
|
+
|
113
|
+
attr_reader :aliases
|
114
|
+
|
115
|
+
##
|
116
|
+
# Marks all occurences of this command's aliases
|
117
|
+
#
|
118
|
+
def mark args
|
119
|
+
args.collect { |pair|
|
120
|
+
@aliases.each { |a| pair[0] << self if pair[1] == a }
|
121
|
+
pair
|
122
|
+
}
|
123
|
+
|
124
|
+
mark_children args
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# A very basic implementation of parse that can be utilized by subclasses to make their
|
129
|
+
# code look tidier.
|
130
|
+
#
|
131
|
+
# Basically goes through the +args+ and removes the consumed arguments if there are any
|
132
|
+
# and calls the +@run+ block if there is any.
|
133
|
+
#
|
134
|
+
# If you supply a +block+ it will be executed instead of the +@run+ block.
|
135
|
+
#
|
136
|
+
def parse args, &block
|
137
|
+
# if no block has been given, we do the default
|
138
|
+
# watch out here: if you use a name that is already present (like args) for
|
139
|
+
# the block argument variable, it will actually use the already present
|
140
|
+
# variable! (not nice!)
|
141
|
+
block = lambda { |argus| @run.call argus } unless block_given?
|
142
|
+
|
143
|
+
return args if ( not @run and not block_given? ) or # no need to parse if we don't run anything...
|
144
|
+
@aliases.empty? # ...or don't have any names anyways.
|
145
|
+
|
146
|
+
# find ourselves
|
147
|
+
myself = args.index { |pair| pair[0].include? self }
|
148
|
+
# if we're not present, we can go
|
149
|
+
return args unless myself
|
150
|
+
|
151
|
+
# where the next mark is
|
152
|
+
stop = args[myself+1..-1].index { |pair| not pair[0].empty? }
|
153
|
+
stop += myself+1 if stop
|
154
|
+
|
155
|
+
# which arguments we will consume
|
156
|
+
myargs = args[myself+1...stop] unless stop.nil?
|
157
|
+
myargs = args[myself+1..-1] if stop.nil?
|
158
|
+
|
159
|
+
# and call the block
|
160
|
+
block.call unmark(myargs)
|
161
|
+
|
162
|
+
# consume the arguments if there are any
|
163
|
+
(0..args.length-1).each { |i|
|
164
|
+
# remove ourselves from our call
|
165
|
+
args[i][0].delete self if myself == i
|
166
|
+
|
167
|
+
# if others have registered for these arguments as well, let them have
|
168
|
+
# their fair share of the cake.
|
169
|
+
if args[myself] and args[myself][0].empty?
|
170
|
+
# remove the whole call if there's noone else requesting it
|
171
|
+
args[i] = nil if myself == i
|
172
|
+
# remove our arguments
|
173
|
+
args[i] = nil if myself < i and ( not stop or i < stop )
|
174
|
+
end
|
175
|
+
}
|
176
|
+
|
177
|
+
args.reject! { |a| a.nil? }
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Goes through +@tokens+ and let's each of them mark the args.
|
182
|
+
#
|
183
|
+
def mark_children args
|
184
|
+
org_args = args.dup
|
185
|
+
@tokens.each { |t| args = t.mark args }
|
186
|
+
args
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# Goes through +@tokens+ and let's each of them parse the args.
|
191
|
+
#
|
192
|
+
def parse_children args
|
193
|
+
@tokens.each { |t| args = t.parse args }
|
194
|
+
args
|
195
|
+
end
|
196
|
+
|
197
|
+
##
|
198
|
+
# Whether or not the current token is a default token.
|
199
|
+
# I.e. it will be called in case no (recognized) arguments were supplied for the parent
|
200
|
+
# command (or the entire argument string if this is a level 1 token).
|
201
|
+
#
|
202
|
+
# _Note:_
|
203
|
+
# The blocks of default tokens' +@run+ blocks will be supplied with an empty array as a
|
204
|
+
# single argument when run as a default.
|
205
|
+
#
|
206
|
+
def default!; @default = true; end
|
207
|
+
attr_accessor :default
|
208
|
+
|
209
|
+
##
|
210
|
+
# Run the associated block explicitly.
|
211
|
+
# Used when running default commands.
|
212
|
+
#
|
213
|
+
def run!
|
214
|
+
@run.call [] if @run
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# Removes all markings from the +args+ so they can be passed to the +@run+ block.
|
219
|
+
#
|
220
|
+
def unmark args
|
221
|
+
args.collect { |pair| pair[1] }
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# Call this to provide a short descriptive text for the +--help/-h/help+ command
|
226
|
+
#
|
227
|
+
def short_desc desc
|
228
|
+
@sdesc = desc
|
229
|
+
end
|
230
|
+
|
231
|
+
attr_reader :sdesc
|
232
|
+
|
233
|
+
##
|
234
|
+
# Call this to provide a longer description of what the token does.
|
235
|
+
# This will be displayed when the user calls +--help/-h/help _command_+.
|
236
|
+
#
|
237
|
+
def long_desc desc
|
238
|
+
@ldesc = desc
|
239
|
+
end
|
240
|
+
|
241
|
+
attr_reader :ldesc
|
242
|
+
end
|
243
|
+
|
244
|
+
##
|
245
|
+
# Keeps and parses an option.
|
246
|
+
# Simple, easy, no children.
|
247
|
+
#
|
248
|
+
class Option < Pablo::Token
|
249
|
+
##
|
250
|
+
# Marks all occurences of this command's aliases
|
251
|
+
#
|
252
|
+
def mark args
|
253
|
+
args.collect { |pair|
|
254
|
+
@aliases.each { |a|
|
255
|
+
if a.length == 2 and '-' == a[0..0]
|
256
|
+
match = /-(.*)/.match pair[1]
|
257
|
+
pair[0] << self if match and match[1].include? a[1..1]
|
258
|
+
else
|
259
|
+
pair[0] << self if pair[1] == a
|
260
|
+
end
|
261
|
+
}
|
262
|
+
pair
|
263
|
+
}
|
264
|
+
|
265
|
+
mark_children args
|
266
|
+
end
|
267
|
+
|
268
|
+
##
|
269
|
+
# Special name function for options. It prepends '--' or '-' to the name passed.
|
270
|
+
# Only exception, if the name already starts with '-' nothing will be changed.
|
271
|
+
#
|
272
|
+
def name a
|
273
|
+
if '-' == a[0..0] then super a
|
274
|
+
elsif 1 == a.length then super '-' + a
|
275
|
+
else super '--' + a
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
##
|
281
|
+
# Keeps and parses a command.
|
282
|
+
# More sophisticated than an option, can have children.
|
283
|
+
#
|
284
|
+
class Command < Pablo::Token
|
285
|
+
##
|
286
|
+
# Adds a subcommand to the current command. The +block+ will be executed in the context of
|
287
|
+
# the new command and can be used to set its aliases, what code to run on discovery etc.
|
288
|
+
#
|
289
|
+
def command &block
|
290
|
+
return unless block_given?
|
291
|
+
c = Pablo::Command.new @toplevel
|
292
|
+
c.instance_eval &block
|
293
|
+
@tokens << c
|
294
|
+
end
|
295
|
+
|
296
|
+
##
|
297
|
+
# Adds an option to the command. The +block+ will be executed in the context of the option
|
298
|
+
# and can be used to set its aliases, what code to run on discovery etc.
|
299
|
+
#
|
300
|
+
def option &block
|
301
|
+
o = Pablo::Option.new @toplevel
|
302
|
+
o.instance_eval &block
|
303
|
+
@tokens << o
|
304
|
+
end
|
305
|
+
|
306
|
+
##
|
307
|
+
# Implementation of parse for commands:
|
308
|
+
# - Let children mark
|
309
|
+
# - Parse ourselves using standard parse implementation
|
310
|
+
# - Let children parse
|
311
|
+
# - Return parsed args
|
312
|
+
#
|
313
|
+
def parse args
|
314
|
+
# will someone just for one second PLEASE think about the children!
|
315
|
+
|
316
|
+
# use the standard implementation to do the dirty job.
|
317
|
+
super args
|
318
|
+
|
319
|
+
# let the children have their fun as well!
|
320
|
+
parse_children args
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
##
|
325
|
+
# Special TopLevel parser.
|
326
|
+
# Only instantiated by +Pablo::parse+. Contains additional commands.
|
327
|
+
#
|
328
|
+
class TopLevel < Pablo::Command
|
329
|
+
def initialize options
|
330
|
+
@everyone = []
|
331
|
+
@options = options
|
332
|
+
@help = false
|
333
|
+
super self # set toplevel
|
334
|
+
end
|
335
|
+
|
336
|
+
attr_accessor :everyone
|
337
|
+
|
338
|
+
##
|
339
|
+
# Will run the default token's run block, if there is one or will try to show
|
340
|
+
# the help otherwise.
|
341
|
+
#
|
342
|
+
def do_defaults
|
343
|
+
done = false
|
344
|
+
@tokens.each { |t|
|
345
|
+
if t.default
|
346
|
+
t.run!
|
347
|
+
done = true
|
348
|
+
end
|
349
|
+
}
|
350
|
+
|
351
|
+
help? [] unless done
|
352
|
+
end
|
353
|
+
|
354
|
+
##
|
355
|
+
# Tests whether a help command is present in +args+ and +help!+ has been called
|
356
|
+
# and if so:
|
357
|
+
# - executes +Pablo::help+
|
358
|
+
# - and returns true
|
359
|
+
# otherwise only returns false.
|
360
|
+
#
|
361
|
+
def help? args
|
362
|
+
return false unless @help
|
363
|
+
found = args.index { |a|
|
364
|
+
@help_aliases.any? { |h| h == a }
|
365
|
+
}
|
366
|
+
found = 0 if args.empty?
|
367
|
+
return false unless found
|
368
|
+
Pablo::help args[found+1..-1], @everyone, @options
|
369
|
+
return true
|
370
|
+
end
|
371
|
+
|
372
|
+
##
|
373
|
+
# Instructs Pablo to display a help message generated from the +short_desc+
|
374
|
+
# and +long_desc+ descriptions of the tokens.
|
375
|
+
#
|
376
|
+
# +alis+ are the aliases the help command should respond to.
|
377
|
+
# By default these are _help_, _--help_ and _-h_.
|
378
|
+
#
|
379
|
+
def help! alis = ['help', '--help', '-h']
|
380
|
+
@help = true
|
381
|
+
@help_aliases = alis
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
##TODO: Version command and version in help command
|
386
|
+
##TODO: caseless aliases!
|
387
|
+
|
388
|
+
##
|
389
|
+
# Displays a help message generated from the +short_desc+ and +long_desc+
|
390
|
+
# specifications of the tokens.
|
391
|
+
#
|
392
|
+
# Output depends on +args+.
|
393
|
+
#
|
394
|
+
# +tokens+ must be a complete list of all tokens defined in the parser.
|
395
|
+
#
|
396
|
+
# +options+ must be a hash of options that will be used to generate the
|
397
|
+
# output, namely +:program+, +:version+, +:usage+.
|
398
|
+
#
|
399
|
+
def self.help args, tokens, options
|
400
|
+
msg = ""
|
401
|
+
aliases = []
|
402
|
+
tokens.each { |t| t.aliases.each { |a| aliases << [a,t] } }
|
403
|
+
|
404
|
+
# whether help for a special command is requested or not
|
405
|
+
found = aliases.find { |a| a[0] == args[0] }
|
406
|
+
found = found[1] if found
|
407
|
+
|
408
|
+
# program name and version
|
409
|
+
puts "#{options[:program]} v#{options[:version].join '.'}" if
|
410
|
+
options[:program] and options[:version]
|
411
|
+
|
412
|
+
# usage
|
413
|
+
puts "Usage: #{options[:usage]}" if options[:usage]
|
414
|
+
|
415
|
+
if found # do long_desc
|
416
|
+
# print long_desc
|
417
|
+
print "Help for "
|
418
|
+
print case found
|
419
|
+
when Pablo::Command then 'command'
|
420
|
+
when Pablo::Option then 'option'
|
421
|
+
else 'Token'
|
422
|
+
end
|
423
|
+
puts " '#{args[0]}':"
|
424
|
+
puts
|
425
|
+
puts found.ldesc
|
426
|
+
|
427
|
+
# print aliases
|
428
|
+
puts
|
429
|
+
puts "::Aliases"
|
430
|
+
puts found.aliases.join ", "
|
431
|
+
|
432
|
+
# print subcommands if any
|
433
|
+
strs = []
|
434
|
+
found.tokens.each { |t| strs << t.aliases[0] }
|
435
|
+
puts "\n::Subcommands" unless strs.empty?
|
436
|
+
puts strs.join " "
|
437
|
+
else # do short_descs
|
438
|
+
# print short_descs
|
439
|
+
puts '::Available commands and options'
|
440
|
+
puts
|
441
|
+
|
442
|
+
strs = []
|
443
|
+
tokens.each { |t| strs << [ t.aliases.join(' '), t.sdesc ] }
|
444
|
+
maxrow = strs.max_by { |x| x[0].length } || ['']
|
445
|
+
maxlen = maxrow[0].length.to_i + 2
|
446
|
+
strs.each { |s| msg += s[0].ljust(maxlen) + '| ' + s[1] + "\n" }
|
447
|
+
puts msg
|
448
|
+
|
449
|
+
# print defaults if any
|
450
|
+
strs = []
|
451
|
+
tokens.each { |t| strs << t.aliases[0] if t.default }
|
452
|
+
puts "\n::Defaults" unless strs.empty?
|
453
|
+
puts strs.join ", "
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
end
|
458
|
+
|
data/tests/help.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'test/unit'
|
3
|
+
require 'lib/pablo'
|
4
|
+
|
5
|
+
def error msg
|
6
|
+
# win does not support ANSI color sequences
|
7
|
+
puts "\e[31m" + msg + "\e[0m" unless PLATFORM =~ /win32/
|
8
|
+
puts msg if PLATFORM =~ /win32/
|
9
|
+
raise msg
|
10
|
+
end
|
11
|
+
|
12
|
+
def message msg
|
13
|
+
# win does not support ANSI color sequences
|
14
|
+
puts "\e[33m" + msg + "\e[0m" unless PLATFORM =~ /win32/
|
15
|
+
puts msg if PLATFORM =~ /win32/
|
16
|
+
end
|
17
|
+
|
18
|
+
class Help < Test::Unit::TestCase
|
19
|
+
def testHelp
|
20
|
+
args = ['help']
|
21
|
+
|
22
|
+
puts
|
23
|
+
message 'Expected output: some stupid help message'
|
24
|
+
message "-----------------------------------------"
|
25
|
+
|
26
|
+
Pablo::parse args, :program => 'stupid', :version => [1,0,5],
|
27
|
+
:usage => 'stupid --null <String>' do
|
28
|
+
option {
|
29
|
+
name 'null'
|
30
|
+
short_desc 'Displays a lot of zeros'
|
31
|
+
run { error "option 'null' incorrectly called!" }
|
32
|
+
long_desc 'Zero Zero Zero Zero Zero Zero\nZero!'
|
33
|
+
}
|
34
|
+
|
35
|
+
command {
|
36
|
+
name 'oneortwo'
|
37
|
+
name 'anotherone'
|
38
|
+
short_desc 'Displays one or two'
|
39
|
+
long_desc 'One two? One two!\noneoneone twotwotwo.'
|
40
|
+
run { error "command 'oneortwo' incorrectly called!" }
|
41
|
+
default!
|
42
|
+
}
|
43
|
+
|
44
|
+
help!
|
45
|
+
end
|
46
|
+
|
47
|
+
message '-----------------------------------------'
|
48
|
+
message 'End of expected output'
|
49
|
+
end
|
50
|
+
|
51
|
+
def testCommandHelp
|
52
|
+
args = ['help', 'oneortwo']
|
53
|
+
|
54
|
+
puts
|
55
|
+
message 'Expected output: some stupid command help'
|
56
|
+
message '-----------------------------------------'
|
57
|
+
|
58
|
+
Pablo::parse args, :program => 'stupid', :version => [1,0,5],
|
59
|
+
:usage => 'stupid --null <String>' do
|
60
|
+
option {
|
61
|
+
name 'n'
|
62
|
+
name 'null'
|
63
|
+
short_desc 'Displays a lot of zeros'
|
64
|
+
long_desc "Zero Zero Zero Zero Zero Zero\nZero!"
|
65
|
+
run { error "option 'null' incorrectly called!" }
|
66
|
+
}
|
67
|
+
|
68
|
+
command {
|
69
|
+
name 'oneortwo'
|
70
|
+
name 'anotherone'
|
71
|
+
short_desc 'Displays one or two'
|
72
|
+
long_desc "One two? One two!\noneoneone twotwotwo."
|
73
|
+
default!
|
74
|
+
run { error "command 'oneortwo' incorrectly called!" }
|
75
|
+
|
76
|
+
command {
|
77
|
+
name 'threeAndFour'
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
help!
|
82
|
+
end
|
83
|
+
|
84
|
+
message '-----------------------------------------'
|
85
|
+
message 'End of expected output'
|
86
|
+
puts
|
87
|
+
end
|
88
|
+
|
89
|
+
def testHelpAliases
|
90
|
+
args = ['huuulpy']
|
91
|
+
|
92
|
+
puts
|
93
|
+
message 'Expected output: the same message again'
|
94
|
+
message "-----------------------------------------"
|
95
|
+
|
96
|
+
Pablo::parse args, :program => 'stupid', :version => [1,0,5],
|
97
|
+
:usage => 'stupid --null <String>' do
|
98
|
+
option {
|
99
|
+
name 'null'
|
100
|
+
short_desc 'Displays a lot of zeros'
|
101
|
+
run { error "option 'null' incorrectly called!" }
|
102
|
+
long_desc 'Zero Zero Zero Zero Zero Zero\nZero!'
|
103
|
+
}
|
104
|
+
|
105
|
+
command {
|
106
|
+
name 'oneortwo'
|
107
|
+
name 'anotherone'
|
108
|
+
short_desc 'Displays one or two'
|
109
|
+
long_desc 'One two? One two!\noneoneone twotwotwo.'
|
110
|
+
run { error "command 'oneortwo' incorrectly called!" }
|
111
|
+
default!
|
112
|
+
}
|
113
|
+
|
114
|
+
help! ['huuulpy', 'foo']
|
115
|
+
end
|
116
|
+
|
117
|
+
message '-----------------------------------------'
|
118
|
+
message 'End of expected output'
|
119
|
+
end
|
120
|
+
|
121
|
+
def testHelpDefault
|
122
|
+
|
123
|
+
puts
|
124
|
+
message 'Expected output: an empty help message'
|
125
|
+
message "-----------------------------------------"
|
126
|
+
|
127
|
+
Pablo::parse [] do
|
128
|
+
help!
|
129
|
+
end
|
130
|
+
|
131
|
+
message '-----------------------------------------'
|
132
|
+
message 'End of expected output'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
data/tests/tests.rb
ADDED
@@ -0,0 +1,290 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'test/unit'
|
3
|
+
require 'lib/pablo'
|
4
|
+
|
5
|
+
class Tests < Test::Unit::TestCase
|
6
|
+
def self.val
|
7
|
+
@@val
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.val= v
|
11
|
+
@@val = v
|
12
|
+
end
|
13
|
+
|
14
|
+
def testSingleCommand
|
15
|
+
args = [ "add", "5" ]
|
16
|
+
Tests.val = 0
|
17
|
+
|
18
|
+
Pablo::parse args do
|
19
|
+
command {
|
20
|
+
name 'add'
|
21
|
+
run { |args| Tests.val += args[0].to_i }
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
assert_equal 5, Tests.val
|
26
|
+
end
|
27
|
+
|
28
|
+
def testSingleCharOption
|
29
|
+
args = [ "-n", "3" ]
|
30
|
+
Tests.val = 0
|
31
|
+
|
32
|
+
Pablo::parse args do
|
33
|
+
option {
|
34
|
+
name 'n'
|
35
|
+
run { |args| Tests.val += args[0].to_i }
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
assert_equal 3, Tests.val
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
def testMultiCharOption
|
44
|
+
args = [ "--nojo", "3" ]
|
45
|
+
Tests.val = 0
|
46
|
+
|
47
|
+
Pablo::parse args do
|
48
|
+
option {
|
49
|
+
name 'nojo'
|
50
|
+
run { |args| Tests.val += args[0].to_i }
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
assert_equal 3, Tests.val
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
def testDashPrependedOption
|
59
|
+
args = [ "-nojo", "3" ]
|
60
|
+
Tests.val = 0
|
61
|
+
|
62
|
+
Pablo::parse args do
|
63
|
+
option {
|
64
|
+
name '-nojo'
|
65
|
+
run { |args| Tests.val += args[0].to_i }
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
assert_equal 3, Tests.val
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
def testOptionMerging
|
74
|
+
args = [ "-no", "3" ]
|
75
|
+
Tests.val = 0
|
76
|
+
|
77
|
+
Pablo::parse args do
|
78
|
+
option {
|
79
|
+
name 'n'
|
80
|
+
run { |args| Tests.val += args[0].to_i }
|
81
|
+
}
|
82
|
+
|
83
|
+
option {
|
84
|
+
name 'o'
|
85
|
+
run { |args| Tests.val += args[0].to_i }
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
assert_equal 6, Tests.val
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
def testUnneccessaryData
|
94
|
+
args = [ "bla", "blu", "-n", "3", "dudu" ]
|
95
|
+
Tests.val = []
|
96
|
+
|
97
|
+
Pablo::parse args do
|
98
|
+
option {
|
99
|
+
name 'n'
|
100
|
+
run { |args|
|
101
|
+
Tests.val << args[0].to_i << args.count }
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
assert_equal 3, Tests.val[0]
|
106
|
+
assert_equal 2, Tests.val[1]
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
def testManyTokens
|
111
|
+
args = [ "add", "5", "add", "3", "-r", "2", "-m", "add", "3", "-m" ]
|
112
|
+
Tests.val = [ [0,0], [0,0], [0,0], 0 ]
|
113
|
+
|
114
|
+
Pablo::parse args do
|
115
|
+
command {
|
116
|
+
name 'add'
|
117
|
+
run { |args|
|
118
|
+
Tests.val[0][0] += 1 #call counter
|
119
|
+
Tests.val[0][1] += args.count #args counter
|
120
|
+
Tests.val[3] += args[0].to_i
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
option {
|
125
|
+
name 'r'
|
126
|
+
run { |args|
|
127
|
+
Tests.val[1][0] += 1
|
128
|
+
Tests.val[1][1] += args.count
|
129
|
+
Tests.val[3] -= args[0].to_i
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
option {
|
134
|
+
name 'm'
|
135
|
+
run { |args|
|
136
|
+
Tests.val[2][0] += 1
|
137
|
+
Tests.val[2][1] += args.count
|
138
|
+
Tests.val[3] += 10
|
139
|
+
}
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
assert_equal [3, 3], Tests.val[0]
|
144
|
+
assert_equal [1, 1], Tests.val[1]
|
145
|
+
assert_equal [2, 0], Tests.val[2]
|
146
|
+
assert_equal 29, Tests.val[3]
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
def testDefault
|
151
|
+
args = []
|
152
|
+
Tests.val= 0
|
153
|
+
|
154
|
+
Pablo::parse args do
|
155
|
+
command {
|
156
|
+
name 'never'
|
157
|
+
run { |args| Tests.val += 1 }
|
158
|
+
}
|
159
|
+
|
160
|
+
command {
|
161
|
+
name 'ever'
|
162
|
+
run { |args| Tests.val += 2 }
|
163
|
+
default!
|
164
|
+
}
|
165
|
+
|
166
|
+
command {
|
167
|
+
name 'java'
|
168
|
+
run { |args| Tests.val += 4 }
|
169
|
+
default!
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
assert_equal 6, Tests.val
|
174
|
+
end
|
175
|
+
|
176
|
+
def testDefaultCluttered
|
177
|
+
args = ['junky', 'bacon']
|
178
|
+
Tests.val= 0
|
179
|
+
|
180
|
+
Pablo::parse args do
|
181
|
+
command {
|
182
|
+
name 'never'
|
183
|
+
run { |args| Tests.val += 1 }
|
184
|
+
}
|
185
|
+
|
186
|
+
command {
|
187
|
+
name 'ever'
|
188
|
+
run { |args| Tests.val += 2 }
|
189
|
+
default!
|
190
|
+
}
|
191
|
+
end
|
192
|
+
|
193
|
+
assert_equal 2, Tests.val
|
194
|
+
end
|
195
|
+
|
196
|
+
##TODO: still failing!
|
197
|
+
=begin
|
198
|
+
def testDefaultLevel2
|
199
|
+
args = ["bla"]
|
200
|
+
Tests.val= 0
|
201
|
+
|
202
|
+
Pablo::parse args do
|
203
|
+
command {
|
204
|
+
name 'never'
|
205
|
+
run { |args| Tests.val += 1 }
|
206
|
+
}
|
207
|
+
|
208
|
+
command {
|
209
|
+
name 'bla'
|
210
|
+
run { |args| Tests.val += 4 }
|
211
|
+
|
212
|
+
option {
|
213
|
+
name 'ever'
|
214
|
+
run { |args| Tests.val += 2 }
|
215
|
+
default!
|
216
|
+
}
|
217
|
+
}
|
218
|
+
end
|
219
|
+
|
220
|
+
assert_equal 6, Tests.val
|
221
|
+
end
|
222
|
+
=end
|
223
|
+
|
224
|
+
##TODO: make code-order possible.
|
225
|
+
def testNaturalOrder
|
226
|
+
args = ["--first", "second"]
|
227
|
+
Tests.val = []
|
228
|
+
|
229
|
+
Pablo::parse args do
|
230
|
+
command {
|
231
|
+
name 'second'
|
232
|
+
run { |args| Tests.val << 2 }
|
233
|
+
}
|
234
|
+
|
235
|
+
option {
|
236
|
+
name 'first'
|
237
|
+
run { |args| Tests.val << 1 }
|
238
|
+
}
|
239
|
+
end
|
240
|
+
|
241
|
+
assert_equal [1,2], Tests.val
|
242
|
+
end
|
243
|
+
|
244
|
+
def testEmptyParser
|
245
|
+
Pablo::parse do
|
246
|
+
end
|
247
|
+
Pablo::parse
|
248
|
+
end
|
249
|
+
|
250
|
+
##TODO: implement children before parent as well
|
251
|
+
##TODO: implement passing of args from children to parents as well
|
252
|
+
def testParentBeforeChildren
|
253
|
+
args = ['add', 5, '--mul', 2]
|
254
|
+
Tests.val = 0
|
255
|
+
|
256
|
+
Pablo::parse args do
|
257
|
+
command {
|
258
|
+
name 'add'
|
259
|
+
run { |args| Tests.val += args[0].to_i }
|
260
|
+
|
261
|
+
option {
|
262
|
+
name 'mul'
|
263
|
+
run { |args| Tests.val *= args[0].to_i }
|
264
|
+
}
|
265
|
+
}
|
266
|
+
end
|
267
|
+
|
268
|
+
assert_equal 10, Tests.val
|
269
|
+
end
|
270
|
+
|
271
|
+
def testMultiOptions
|
272
|
+
args = ['-nu']
|
273
|
+
Tests.val = 0
|
274
|
+
|
275
|
+
Pablo::parse args do
|
276
|
+
option {
|
277
|
+
name 'n'
|
278
|
+
run { |args| Tests.val += 2 }
|
279
|
+
}
|
280
|
+
|
281
|
+
option {
|
282
|
+
name 'u'
|
283
|
+
run { |args| Tests.val += 5 }
|
284
|
+
}
|
285
|
+
end
|
286
|
+
|
287
|
+
assert_equal 7, Tests.val
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: Pablo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Fabian Streitel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-07 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: karottenreibe @nospam@ gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- lib/pablo.rb
|
26
|
+
- README
|
27
|
+
has_rdoc: true
|
28
|
+
homepage: http://pablo.rubyforge.org/
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: "0"
|
39
|
+
version:
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
requirements: []
|
47
|
+
|
48
|
+
rubyforge_project: pablo
|
49
|
+
rubygems_version: 1.3.1
|
50
|
+
signing_key:
|
51
|
+
specification_version: 2
|
52
|
+
summary: A commandline parser for Ruby that is entirely based on blocks.
|
53
|
+
test_files:
|
54
|
+
- tests/help.rb
|
55
|
+
- tests/tests.rb
|