pablo 1.0.3
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.tar.gz.sig +0 -0
- data/History.txt +4 -0
- data/LICENSE.txt +22 -0
- data/Manifest.txt +26 -0
- data/README.txt +61 -0
- data/lib/pablo.rb +298 -0
- data/lib/pablo/always.rb +44 -0
- data/lib/pablo/arguments.rb +76 -0
- data/lib/pablo/color.rb +57 -0
- data/lib/pablo/command.rb +71 -0
- data/lib/pablo/errors.rb +68 -0
- data/lib/pablo/expansion.rb +58 -0
- data/lib/pablo/help.rb +191 -0
- data/lib/pablo/helpers.rb +46 -0
- data/lib/pablo/option.rb +89 -0
- data/lib/pablo/parser.rb +192 -0
- data/lib/pablo/token.rb +66 -0
- data/lib/pablo/version.rb +29 -0
- data/test/fixtures/license.txt +22 -0
- data/test/fixtures/load_yaml.yml +37 -0
- data/test/fixtures/output.yml +13 -0
- data/test/help_fixtures.rb +47 -0
- data/test/test_arguments.rb +56 -0
- data/test/test_expansion.rb +62 -0
- data/test/test_output.rb +202 -0
- data/test/test_pablo.rb +115 -0
- data/test/test_parsers.rb +470 -0
- metadata +117 -0
- metadata.gz.sig +0 -0
data.tar.gz.sig
ADDED
Binary file
|
data/History.txt
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
/*---- DON'T PANIC License 1.1 -----------
|
2
|
+
|
3
|
+
Don't panic, this piece of software is
|
4
|
+
free, i.e. you can do with it whatever
|
5
|
+
you like, including, but not limited to:
|
6
|
+
|
7
|
+
* using it
|
8
|
+
* copying it
|
9
|
+
* (re)distributing it
|
10
|
+
* burning/burying/shredding it
|
11
|
+
* eating it
|
12
|
+
* using it to obtain world domination
|
13
|
+
* and ignoring it
|
14
|
+
|
15
|
+
Under the sole condition that you
|
16
|
+
|
17
|
+
* CONSIDER buying the author a strong
|
18
|
+
brownian motion producer, say a nice
|
19
|
+
hot cup of tea, should you ever meet
|
20
|
+
him in person.
|
21
|
+
|
22
|
+
----------------------------------------*/
|
data/Manifest.txt
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
lib/pablo.rb
|
6
|
+
lib/pablo/always.rb
|
7
|
+
lib/pablo/arguments.rb
|
8
|
+
lib/pablo/color.rb
|
9
|
+
lib/pablo/command.rb
|
10
|
+
lib/pablo/errors.rb
|
11
|
+
lib/pablo/expansion.rb
|
12
|
+
lib/pablo/help.rb
|
13
|
+
lib/pablo/helpers.rb
|
14
|
+
lib/pablo/option.rb
|
15
|
+
lib/pablo/parser.rb
|
16
|
+
lib/pablo/token.rb
|
17
|
+
lib/pablo/version.rb
|
18
|
+
test/fixtures/license.txt
|
19
|
+
test/fixtures/load_yaml.yml
|
20
|
+
test/fixtures/output.yml
|
21
|
+
test/help_fixtures.rb
|
22
|
+
test/test_arguments.rb
|
23
|
+
test/test_expansion.rb
|
24
|
+
test/test_output.rb
|
25
|
+
test/test_pablo.rb
|
26
|
+
test/test_parsers.rb
|
data/README.txt
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
= pablo
|
2
|
+
|
3
|
+
Support for pablo is given at http://pablo.rubyforge.org/.
|
4
|
+
If you have questions, problems, want to report bugs or request features, this is the
|
5
|
+
place for you.
|
6
|
+
Have fun with it!
|
7
|
+
|
8
|
+
== DESCRIPTION
|
9
|
+
|
10
|
+
This is pablo, a Ruby commandline parser that follows the command/option paradigm.
|
11
|
+
pablo stands for "PArsing BLOcks" (I know, it's a really stupid name).
|
12
|
+
|
13
|
+
== FEATURES/PROBLEMS:
|
14
|
+
|
15
|
+
* Commandline parsing following the well-known command/option paradigm (like `gem` does).
|
16
|
+
* Easy declaration and description of commands/options via YAML syntax
|
17
|
+
* Auto-generated `help`, `version` and `license` commands
|
18
|
+
* Optional expansion of commands and options and detection of ambiguities
|
19
|
+
|
20
|
+
== SYNOPSIS:
|
21
|
+
|
22
|
+
pablo ARGV, :program => 'test', :desc => 'a test program',
|
23
|
+
:version => '1.0', :license => 'GPL or something' do
|
24
|
+
|
25
|
+
desc('prints foo')
|
26
|
+
option :foo do
|
27
|
+
puts 'foo'
|
28
|
+
end
|
29
|
+
|
30
|
+
help_command
|
31
|
+
version_command
|
32
|
+
license_command
|
33
|
+
ambiguity_command
|
34
|
+
|
35
|
+
command :bar do |args, pablo|
|
36
|
+
option :goo || missing_argument('goo')
|
37
|
+
|
38
|
+
if pablo.options[:goo]
|
39
|
+
puts 'goo'
|
40
|
+
else
|
41
|
+
puts 'bar'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
== REQUIREMENTS:
|
47
|
+
|
48
|
+
* none so far
|
49
|
+
|
50
|
+
== INSTALL:
|
51
|
+
|
52
|
+
* On Linux:
|
53
|
+
sudo gem install -t pablo
|
54
|
+
* On Windows:
|
55
|
+
gem install -t pablo
|
56
|
+
|
57
|
+
== LICENSE:
|
58
|
+
|
59
|
+
DON'T PANIC License v1.1
|
60
|
+
See LICENSE.txt for full license text
|
61
|
+
|
data/lib/pablo.rb
ADDED
@@ -0,0 +1,298 @@
|
|
1
|
+
###### DON'T PANIC License 1.1 ###########
|
2
|
+
#
|
3
|
+
# Don't panic, this piece of software is
|
4
|
+
# free, i.e. you can do with it whatever
|
5
|
+
# you like, including, but not limited to:
|
6
|
+
#
|
7
|
+
# * using it
|
8
|
+
# * copying it
|
9
|
+
# * (re)distributing it
|
10
|
+
# * burning/burying/shredding it
|
11
|
+
# * eating it
|
12
|
+
# * using it to obtain world domination
|
13
|
+
# * and ignoring it
|
14
|
+
#
|
15
|
+
# Under the sole condition that you
|
16
|
+
#
|
17
|
+
# * CONSIDER buying the author a strong
|
18
|
+
# brownian motion producer, say a nice
|
19
|
+
# hot cup of tea, should you ever meet
|
20
|
+
# him in person.
|
21
|
+
#
|
22
|
+
##########################################
|
23
|
+
|
24
|
+
# other libs
|
25
|
+
require 'yaml'
|
26
|
+
|
27
|
+
# global helpers
|
28
|
+
require 'pablo/helpers'
|
29
|
+
|
30
|
+
# standard equipment
|
31
|
+
require 'pablo/arguments'
|
32
|
+
require 'pablo/parser'
|
33
|
+
require 'pablo/command'
|
34
|
+
require 'pablo/option'
|
35
|
+
require 'pablo/token'
|
36
|
+
require 'pablo/always'
|
37
|
+
|
38
|
+
# additional gadgets
|
39
|
+
require 'pablo/color'
|
40
|
+
require 'pablo/errors'
|
41
|
+
require 'pablo/expansion'
|
42
|
+
require 'pablo/help'
|
43
|
+
|
44
|
+
#
|
45
|
+
# Shortcut. Creates a Pablo instance, instance_evals the
|
46
|
+
# given block on it an calls Pablo#parse.
|
47
|
+
# Also, if +args+ is empty, +ARGV+ will be used.
|
48
|
+
def pablo args, opts = Hash.new, &block
|
49
|
+
raise 'pablo needs a block to do something senisble' unless block_given?
|
50
|
+
args = [ARGV] if args.empty?
|
51
|
+
p = Pablo.new opts
|
52
|
+
p.instance_eval(&block)
|
53
|
+
p.parse args
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Does the heavy lifting.
|
58
|
+
class Pablo
|
59
|
+
|
60
|
+
include Pablo::Helpers
|
61
|
+
|
62
|
+
attr_accessor :args
|
63
|
+
attr_accessor :options, :commands, :tokens
|
64
|
+
attr_accessor :registered, :expand
|
65
|
+
|
66
|
+
#
|
67
|
+
# * +opts+: options for Pablo
|
68
|
+
# * :command_consumes_all => true|false
|
69
|
+
# Forces toplevel commands (not) to consume all arguments after
|
70
|
+
# being applied.
|
71
|
+
# I'm still looking for a better name for that option, so it may
|
72
|
+
# be subject to change.
|
73
|
+
# * :expand => true|false|Array
|
74
|
+
# Turns on the automatic abbreviation expansion mechanism for
|
75
|
+
# * all parsers (true)
|
76
|
+
# * no parsers (false)
|
77
|
+
# * a specific parser (Array containing :commands and/or :options)
|
78
|
+
# * :program => String
|
79
|
+
# The name of the Program to be displayed by +help+ and +version+.
|
80
|
+
# * :version => String
|
81
|
+
# The version of the Program to be displayed by +version+ and +help+.
|
82
|
+
# * :usage => String
|
83
|
+
# The usage information of the Program to be displayed by +help+, e.g.
|
84
|
+
# [<command>] [<options>]
|
85
|
+
def initialize opts = Hash.new
|
86
|
+
@opts = {
|
87
|
+
:command_consumes_all => true,
|
88
|
+
:expand => false
|
89
|
+
}.merge(opts)
|
90
|
+
|
91
|
+
check_options()
|
92
|
+
|
93
|
+
@finalize = @colorize = @cur = nil
|
94
|
+
@toplevel, @args, @pending = true, nil, Hash.new
|
95
|
+
@expand, @ambiguity, @registered = Array.new, nil, Array.new
|
96
|
+
@options, @commands, @tokens = Hash.new, Hash.new, Hash.new
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Starts the parsing process on the given +args+,
|
101
|
+
# according to the registered parsers.
|
102
|
+
def parse args
|
103
|
+
@toplevel, @args = false, Pablo::Arguments.new(args)
|
104
|
+
|
105
|
+
catch :abort do
|
106
|
+
begin
|
107
|
+
@registered.each { |parser|
|
108
|
+
@cur = parser
|
109
|
+
parser.parse(@args)
|
110
|
+
}
|
111
|
+
true
|
112
|
+
rescue MissingArgumentError, WrongArgumentError => e
|
113
|
+
$stderr.puts e.message
|
114
|
+
false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# Define a command.
|
121
|
+
# +args+ must be:
|
122
|
+
# * at least one name for the command
|
123
|
+
# * a hash of options:
|
124
|
+
# * :default
|
125
|
+
# The default value to be set if the command is not recognized.
|
126
|
+
# * :consume_all => true|false
|
127
|
+
# Whether (or not) the command should consume_all remaining
|
128
|
+
# arguments after it has been recognized
|
129
|
+
# * :consume => Fixnum|String|Regexp|Array
|
130
|
+
# * Fixnum: consume the next n arguments
|
131
|
+
# * String: consume if the next argument equals that String. Else
|
132
|
+
# the option will not match.
|
133
|
+
# * Regexp: consume if the next argument matches that Regexp. Else
|
134
|
+
# the option will not match.
|
135
|
+
# * Array: consume next +arr.length+ arguments if they equal those
|
136
|
+
# given in the Array.
|
137
|
+
# * :expand => Proc
|
138
|
+
# A Proc that will be passed a single argument String and a single
|
139
|
+
# name of the token as parameters and has to return a Boolean indicating
|
140
|
+
# whether or not the String is an abbreviation of that name.
|
141
|
+
def command *args, &block
|
142
|
+
exec(Pablo::Command, *args, &block)
|
143
|
+
end
|
144
|
+
alias_method :cmd, :command
|
145
|
+
|
146
|
+
#
|
147
|
+
# Define an option.
|
148
|
+
# +args+ must be:
|
149
|
+
# * at least one name for the command
|
150
|
+
# * a hash of options:
|
151
|
+
# * :default
|
152
|
+
# The default value to be set if the option is not recognized.
|
153
|
+
# * :negative => true|false|String
|
154
|
+
# If +true+, the option will have a counterpart named +--no<name>+.
|
155
|
+
# If a String, the option will have a counterpart named
|
156
|
+
# +--<string><name>+.
|
157
|
+
# * :consume => Fixnum|String|Regexp|Array
|
158
|
+
# * Fixnum: consume the next n arguments
|
159
|
+
# * String: consume if the next argument equals that String. Else
|
160
|
+
# the option will not match.
|
161
|
+
# * Regexp: consume if the next argument matches that Regexp. Else
|
162
|
+
# the option will not match.
|
163
|
+
# * Array: consume next +arr.length+ arguments if they equal those
|
164
|
+
# given in the Array.
|
165
|
+
# * :expand => Proc
|
166
|
+
# A Proc that will be passed a single argument String and a single
|
167
|
+
# name of the token as parameters and has to return a Boolean indicating
|
168
|
+
# whether or not the String is an abbreviation of that name.
|
169
|
+
def option *args, &block
|
170
|
+
exec(Pablo::Option, *args, &block)
|
171
|
+
end
|
172
|
+
alias_method :opt, :option
|
173
|
+
|
174
|
+
#
|
175
|
+
# Define a token.
|
176
|
+
# +args+ must be:
|
177
|
+
# * at least one name for the command
|
178
|
+
# * a hash of options:
|
179
|
+
# * :default
|
180
|
+
# The default value to be set if the token is not recognized.
|
181
|
+
# * :consume => Fixnum|String|Regexp|Array
|
182
|
+
# * Fixnum: consume the next n arguments
|
183
|
+
# * String: consume if the next argument equals that String. Else
|
184
|
+
# the option will not match.
|
185
|
+
# * Regexp: consume if the next argument matches that Regexp. Else
|
186
|
+
# the option will not match.
|
187
|
+
# * Array: consume next +arr.length+ arguments if they equal those
|
188
|
+
# given in the Array.
|
189
|
+
def token *args, &block
|
190
|
+
exec(Pablo::Token, *args, &block)
|
191
|
+
end
|
192
|
+
alias_method :tok, :token
|
193
|
+
|
194
|
+
#
|
195
|
+
# Define arbitrary code to always run at that stage of parsing.
|
196
|
+
def always &block
|
197
|
+
exec(Pablo::Always, &block)
|
198
|
+
end
|
199
|
+
|
200
|
+
#
|
201
|
+
# Set the short description of the next parser.
|
202
|
+
def desc d
|
203
|
+
@pending[:desc] = d
|
204
|
+
end
|
205
|
+
|
206
|
+
#
|
207
|
+
# Set the long description of the next parser.
|
208
|
+
def longdesc l
|
209
|
+
@pending[:longdesc] = l
|
210
|
+
end
|
211
|
+
|
212
|
+
#
|
213
|
+
# Set the usage information of the next parser.
|
214
|
+
def usage u
|
215
|
+
@pending[:usage] = u
|
216
|
+
end
|
217
|
+
|
218
|
+
#
|
219
|
+
# Loads whole parsers and global options from a YAML source.
|
220
|
+
# +yml+ can either be a String of YAML or the path of a YAML file.
|
221
|
+
def load_yaml yml
|
222
|
+
yml = open(yml).read() if File.exist?(yml)
|
223
|
+
opts = YAML::load(yml)
|
224
|
+
|
225
|
+
parsers = opts.delete(:parsers)
|
226
|
+
@opts.merge!(opts)
|
227
|
+
|
228
|
+
parsers.each do |p|
|
229
|
+
type = [:command, :token, :option, :help_command,
|
230
|
+
:ambiguity_command, :license_command, :version_command].find { |s| !p[s].nil? }
|
231
|
+
|
232
|
+
unless type.nil?
|
233
|
+
args = p.delete(type)
|
234
|
+
|
235
|
+
if args.is_a? Array then args << p
|
236
|
+
else args = [p]
|
237
|
+
end
|
238
|
+
|
239
|
+
type == :ambiguity_command ?
|
240
|
+
self.send(type) : self.send(type, *args)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
#
|
246
|
+
# Whether or not we dwell at toplevel currently.
|
247
|
+
# Only valid within a Command, Option, Token etc,
|
248
|
+
# but NOT within this class.
|
249
|
+
def toplevel?
|
250
|
+
@toplevel
|
251
|
+
end
|
252
|
+
|
253
|
+
#
|
254
|
+
# Sets the run object for this pablo instance.
|
255
|
+
def run= object
|
256
|
+
@opts[:run] = object
|
257
|
+
end
|
258
|
+
|
259
|
+
#
|
260
|
+
# Returns the object which keeps all the instance methods corresponding
|
261
|
+
# to the defined parsers - or +nil+ if none was given
|
262
|
+
def run_object
|
263
|
+
@opts[:run]
|
264
|
+
end
|
265
|
+
|
266
|
+
#
|
267
|
+
# Whether or not commands should consume all arguments after they
|
268
|
+
# parsed something.
|
269
|
+
def consume_all?
|
270
|
+
@opts[:command_consumes_all]
|
271
|
+
end
|
272
|
+
|
273
|
+
private
|
274
|
+
|
275
|
+
def check_options
|
276
|
+
raise ":expand expects any of true, false, :commands, :options" unless
|
277
|
+
[true, false, :commands, :options].include? @opts[:expand]
|
278
|
+
%w{program version usage}.each { |sym|
|
279
|
+
raise ":#{sym} expects a String" unless
|
280
|
+
@opts[sym.to_sym].nil? or @opts[sym.to_sym].respond_to? :to_str
|
281
|
+
}
|
282
|
+
end
|
283
|
+
|
284
|
+
#
|
285
|
+
# Generalization of command, option etc. methods.
|
286
|
+
def exec klass, *args, &block
|
287
|
+
opts = args[-1].respond_to?(:to_hash) ?
|
288
|
+
args.delete_at(-1).to_hash : Hash.new
|
289
|
+
opts = @pending.merge(opts)
|
290
|
+
@pending.clear
|
291
|
+
args << opts
|
292
|
+
|
293
|
+
e = klass.new(self, *args, &block)
|
294
|
+
e.parse(@args) unless @toplevel
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
|
data/lib/pablo/always.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
###### DON'T PANIC License 1.1 ###########
|
2
|
+
#
|
3
|
+
# Don't panic, this piece of software is
|
4
|
+
# free, i.e. you can do with it whatever
|
5
|
+
# you like, including, but not limited to:
|
6
|
+
#
|
7
|
+
# * using it
|
8
|
+
# * copying it
|
9
|
+
# * (re)distributing it
|
10
|
+
# * burning/burying/shredding it
|
11
|
+
# * eating it
|
12
|
+
# * using it to obtain world domination
|
13
|
+
# * and ignoring it
|
14
|
+
#
|
15
|
+
# Under the sole condition that you
|
16
|
+
#
|
17
|
+
# * CONSIDER buying the author a strong
|
18
|
+
# brownian motion producer, say a nice
|
19
|
+
# hot cup of tea, should you ever meet
|
20
|
+
# him in person.
|
21
|
+
#
|
22
|
+
##########################################
|
23
|
+
|
24
|
+
class Pablo
|
25
|
+
|
26
|
+
class Always
|
27
|
+
|
28
|
+
def initialize pablo, *args, &block
|
29
|
+
@pablo, @block = pablo, block
|
30
|
+
@pablo.registered << self
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse args
|
34
|
+
@block.call(args, @pablo)
|
35
|
+
end
|
36
|
+
|
37
|
+
def names_to_user; ''; end
|
38
|
+
def names_to_rex; nil; end
|
39
|
+
def klass_to_sym; :always; end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|