main 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 +343 -0
- data/README.tmpl +154 -0
- data/a.rb +33 -0
- data/gemspec.rb +27 -0
- data/gen_readme.rb +38 -0
- data/install.rb +210 -0
- data/lib/main.rb +25 -0
- data/lib/main/base.rb +150 -0
- data/lib/main/cast.rb +74 -0
- data/lib/main/factories.rb +15 -0
- data/lib/main/getoptlong.rb +470 -0
- data/lib/main/parameter.rb +496 -0
- data/lib/main/usage.rb +138 -0
- data/lib/main/util.rb +91 -0
- data/main-0.0.1.gem +0 -0
- data/samples/a.rb +17 -0
- data/samples/b.rb +17 -0
- data/samples/c.rb +21 -0
- data/samples/d.rb +26 -0
- data/test/main.rb +523 -0
- metadata +69 -0
@@ -0,0 +1,496 @@
|
|
1
|
+
module Main
|
2
|
+
class Parameter
|
3
|
+
class Error < StandardError; attribute 'wrapped'; end
|
4
|
+
class NotGiven < Error; end
|
5
|
+
class Arity < Error; end
|
6
|
+
class InValid < Error; end
|
7
|
+
class NoneSuch < Error; end
|
8
|
+
class AmbigousOption < Error; end
|
9
|
+
class NeedlessArgument < Error; end
|
10
|
+
class MissingArgument < Error; end
|
11
|
+
class InvalidOption < Error; end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def new *a, &b
|
15
|
+
raise
|
16
|
+
end
|
17
|
+
|
18
|
+
def wrapped_error w
|
19
|
+
e = Error.new "(#{ w.message } (#{ w.class }))"
|
20
|
+
e.wrapped = w
|
21
|
+
e.set_backtrace(w.backtrace || [])
|
22
|
+
e
|
23
|
+
end
|
24
|
+
|
25
|
+
def wrap_errors
|
26
|
+
begin
|
27
|
+
yield
|
28
|
+
rescue => e
|
29
|
+
raise wrapped_error(e)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Types = []
|
34
|
+
def inherited other
|
35
|
+
Types << other
|
36
|
+
end
|
37
|
+
|
38
|
+
def sym
|
39
|
+
@sym ||= name.split(%r/::/).last.downcase.to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
def class_for type
|
43
|
+
sym = type.to_s.downcase.to_sym
|
44
|
+
c = Types.detect{|t| t.sym == sym}
|
45
|
+
raise ArgumentError, type.inspect unless c
|
46
|
+
c
|
47
|
+
end
|
48
|
+
|
49
|
+
def create type, *a, &b
|
50
|
+
c = class_for type
|
51
|
+
obj = c.allocate
|
52
|
+
obj.type = c.sym
|
53
|
+
obj.instance_eval{ initialize *a, &b }
|
54
|
+
obj
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
attribute 'type'
|
59
|
+
attribute 'names'
|
60
|
+
attribute 'abbreviations'
|
61
|
+
attribute 'argument'
|
62
|
+
attribute 'given'
|
63
|
+
attribute 'cast'
|
64
|
+
attribute 'validate'
|
65
|
+
attribute 'description'
|
66
|
+
attribute 'synopsis'
|
67
|
+
attribute('values'){ [] }
|
68
|
+
attribute('defaults'){ [] }
|
69
|
+
|
70
|
+
attribute 'arity' => 1
|
71
|
+
attribute 'required' => false
|
72
|
+
|
73
|
+
def initialize name, *names, &block
|
74
|
+
@names = Cast.list_of_string name, *names
|
75
|
+
@names.map! do |name|
|
76
|
+
if name =~ %r/^-+/
|
77
|
+
name.gsub! %r/^-+/, ''
|
78
|
+
end
|
79
|
+
|
80
|
+
if name =~ %r/=.*$/
|
81
|
+
argument( name =~ %r/=\s*\[.*$/ ? :optional : :required )
|
82
|
+
name.gsub! %r/=.*$/, ''
|
83
|
+
end
|
84
|
+
|
85
|
+
name
|
86
|
+
end
|
87
|
+
@names = @names.sort.reverse
|
88
|
+
@names[1..-1].each do |name|
|
89
|
+
raise ArgumentError, "only one long name allowed (#{ @names.inspect })" if
|
90
|
+
name.size > 1
|
91
|
+
end
|
92
|
+
|
93
|
+
DSL.evaluate(self, &block) if block
|
94
|
+
end
|
95
|
+
|
96
|
+
def name
|
97
|
+
names.first
|
98
|
+
end
|
99
|
+
|
100
|
+
def default
|
101
|
+
defaults.first
|
102
|
+
end
|
103
|
+
|
104
|
+
def typename
|
105
|
+
prefix = '--' if type.to_s =~ %r/option/
|
106
|
+
"#{ type }(#{ prefix }#{ name })"
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_value value
|
110
|
+
given true
|
111
|
+
values << value
|
112
|
+
end
|
113
|
+
|
114
|
+
def value
|
115
|
+
values.first
|
116
|
+
end
|
117
|
+
|
118
|
+
def argument_none?
|
119
|
+
argument.nil?
|
120
|
+
end
|
121
|
+
|
122
|
+
def argument_required?
|
123
|
+
argument and
|
124
|
+
argument.to_s.downcase.to_sym == :required
|
125
|
+
end
|
126
|
+
def argument_optional?
|
127
|
+
argument and
|
128
|
+
argument.to_s.downcase.to_sym == :optional
|
129
|
+
end
|
130
|
+
|
131
|
+
def optional?
|
132
|
+
not required?
|
133
|
+
end
|
134
|
+
def optional= bool
|
135
|
+
self.required !bool
|
136
|
+
end
|
137
|
+
|
138
|
+
def setup!
|
139
|
+
return false unless given?
|
140
|
+
validate_arity!
|
141
|
+
cast!
|
142
|
+
validate!
|
143
|
+
true
|
144
|
+
end
|
145
|
+
|
146
|
+
def validate_arity!
|
147
|
+
raise Arity, "#{ typename })" if values.size.zero? and argument_required?
|
148
|
+
if values.size < arity
|
149
|
+
if argument_required?
|
150
|
+
raise Arity, "#{ typename }) #{ values.size }/#{ arity }" if(values.size < arity)
|
151
|
+
elsif argument_optional?
|
152
|
+
raise Arity, "#{ typename }) #{ values.size }/#{ arity }" if(values.size < arity and values.size > 0)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def validate!
|
158
|
+
if validate?
|
159
|
+
values.each do |value|
|
160
|
+
validate[value] or
|
161
|
+
raise InValid, "#{ typename }=#{ value.inspect }"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def cast!
|
167
|
+
if cast?
|
168
|
+
op = cast.respond_to?('call') ? cast : Cast[cast]
|
169
|
+
values.map! do |val|
|
170
|
+
Parameter.wrap_errors{ op.call val }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class Argument < Parameter
|
176
|
+
attribute 'required' => true
|
177
|
+
|
178
|
+
attribute 'synopsis' do
|
179
|
+
label = name
|
180
|
+
op = required ? "->" : "~>"
|
181
|
+
value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
|
182
|
+
value = "#{ cast }(#{ value })" if cast
|
183
|
+
"#{ label } [ #{ arity } #{ op } #{ value } ]"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
class Option < Parameter
|
188
|
+
attribute 'required' => false
|
189
|
+
|
190
|
+
attribute 'synopsis' do
|
191
|
+
long, *short = names
|
192
|
+
value = cast || name
|
193
|
+
rhs = argument ? (argument == :required ? "=#{ name }" : "=[#{ name }]") : nil
|
194
|
+
label = ["--#{ long }#{ rhs }", short.map{|s| "-#{ s }"}].flatten.join(", ")
|
195
|
+
unless argument_none?
|
196
|
+
op = required ? "->" : "~>"
|
197
|
+
value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
|
198
|
+
value = "#{ cast }(#{ value })" if cast
|
199
|
+
"#{ label } [ #{ arity } #{ op } #{ value } ]"
|
200
|
+
else
|
201
|
+
label
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class Keyword < Parameter
|
207
|
+
attribute 'required' => false
|
208
|
+
attribute 'argument' => :required
|
209
|
+
|
210
|
+
attribute 'synopsis' do
|
211
|
+
label = "#{ name }=#{ name }"
|
212
|
+
op = required ? "->" : "~>"
|
213
|
+
value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
|
214
|
+
value = "#{ cast }(#{ value })" if cast
|
215
|
+
"#{ label } [ #{ arity } #{ op } #{ value } ]"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
class Environment < Parameter
|
220
|
+
attribute 'argument' => :required
|
221
|
+
|
222
|
+
attribute 'synopsis' do
|
223
|
+
label = "env[#{ name }]=#{ name }"
|
224
|
+
op = required ? "->" : "~>"
|
225
|
+
value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
|
226
|
+
value = "#{ cast }(#{ value })" if cast
|
227
|
+
"#{ label } [ #{ arity } #{ op } #{ value } ]"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
class List < ::Array
|
232
|
+
def parse argv, env
|
233
|
+
parse_options argv
|
234
|
+
return 'help' if detect{|p| p.name.to_s == 'help' and p.given?}
|
235
|
+
parse_keywords argv
|
236
|
+
parse_arguments argv
|
237
|
+
parse_environment env
|
238
|
+
defaults!
|
239
|
+
validate!
|
240
|
+
self
|
241
|
+
end
|
242
|
+
|
243
|
+
def parse_options argv, params = nil
|
244
|
+
params ||= select{|p| p.type == :option}
|
245
|
+
|
246
|
+
spec, h, s = [], {}, {}
|
247
|
+
|
248
|
+
params.each do |p|
|
249
|
+
head, *tail = p.names
|
250
|
+
long = "--#{ head }"
|
251
|
+
shorts = tail.map{|t| "-#{ t }"}
|
252
|
+
type =
|
253
|
+
if p.argument_required? then GetoptLong::REQUIRED_ARGUMENT
|
254
|
+
elsif p.argument_optional? then GetoptLong::OPTIONAL_ARGUMENT
|
255
|
+
else GetoptLong::NO_ARGUMENT
|
256
|
+
end
|
257
|
+
a = [ long, shorts, type ].flatten
|
258
|
+
spec << a
|
259
|
+
h[long] = p
|
260
|
+
s[long] = a
|
261
|
+
end
|
262
|
+
|
263
|
+
begin
|
264
|
+
GetoptLong.new(argv, *spec).each do |long, value|
|
265
|
+
value =
|
266
|
+
case s[long].last
|
267
|
+
when GetoptLong::NO_ARGUMENT
|
268
|
+
value.empty? ? true : value
|
269
|
+
when GetoptLong::OPTIONAL_ARGUMENT
|
270
|
+
value.empty? ? true : value
|
271
|
+
when GetoptLong::REQUIRED_ARGUMENT
|
272
|
+
value
|
273
|
+
end
|
274
|
+
p = h[long]
|
275
|
+
p.add_value value
|
276
|
+
end
|
277
|
+
rescue GetoptLong::AmbigousOption, GetoptLong::NeedlessArgument,
|
278
|
+
GetoptLong::MissingArgument, GetoptLong::InvalidOption => e
|
279
|
+
c = Parameter.const_get e.class.name.split(/::/).last
|
280
|
+
ex = c.new e.message
|
281
|
+
ex.set_backtrace e.message
|
282
|
+
raise ex
|
283
|
+
end
|
284
|
+
|
285
|
+
params.each do |p|
|
286
|
+
p.setup!
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def parse_arguments argv, params=nil
|
291
|
+
params ||= select{|p| p.type == :argument}
|
292
|
+
|
293
|
+
params.each do |p|
|
294
|
+
p.arity.times do
|
295
|
+
break if argv.empty?
|
296
|
+
value = argv.shift
|
297
|
+
p.add_value value
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
params.each do |p|
|
302
|
+
p.setup!
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def parse_keywords argv, params=nil
|
307
|
+
params ||= select{|p| p.type == :keyword}
|
308
|
+
|
309
|
+
replacements = {}
|
310
|
+
|
311
|
+
params.each do |p|
|
312
|
+
names = p.names
|
313
|
+
name = names.first
|
314
|
+
|
315
|
+
kre = %r/^\s*(#{ names.join '|' })\s*=/
|
316
|
+
opt = "--#{ name }"
|
317
|
+
i = 0
|
318
|
+
|
319
|
+
argv.each_with_index do |a, idx|
|
320
|
+
b = argv[idx + 1]
|
321
|
+
m, key, *ignored = kre.match("#{ a }#{ b }").to_a
|
322
|
+
if m
|
323
|
+
replacements[i] ||= a.gsub %r/^\s*#{ key }/, opt
|
324
|
+
end
|
325
|
+
i += 1
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
replacements.each do |i, r|
|
330
|
+
argv[i] = r
|
331
|
+
end
|
332
|
+
|
333
|
+
parse_options argv, params
|
334
|
+
end
|
335
|
+
|
336
|
+
def parse_environment env, params=nil
|
337
|
+
params ||= select{|p| p.type == :environment}
|
338
|
+
|
339
|
+
params.each do |p|
|
340
|
+
names = p.names
|
341
|
+
name = names.first
|
342
|
+
value = env[name]
|
343
|
+
next unless value
|
344
|
+
p.add_value value
|
345
|
+
end
|
346
|
+
|
347
|
+
params.each do |p|
|
348
|
+
p.setup!
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def defaults!
|
353
|
+
each do |p|
|
354
|
+
#p.add_value p.default if(p.default? and (not p.given?))
|
355
|
+
if(p.defaults? and (not p.given?))
|
356
|
+
p.defaults.each do |default|
|
357
|
+
p.add_value default
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def validate!
|
364
|
+
each do |p|
|
365
|
+
raise NotGiven, p.typename if(p.required? and (not p.given?))
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
class DSL
|
371
|
+
def self.evaluate param, &block
|
372
|
+
new(param).evaluate(&block)
|
373
|
+
end
|
374
|
+
attr 'p'
|
375
|
+
alias_method 'evaluate', 'instance_eval'
|
376
|
+
def initialize param
|
377
|
+
@p = param
|
378
|
+
end
|
379
|
+
|
380
|
+
def type sym
|
381
|
+
p.type = sym
|
382
|
+
end
|
383
|
+
def type?
|
384
|
+
p.type?
|
385
|
+
end
|
386
|
+
|
387
|
+
def synopsis arg
|
388
|
+
p.synopsis arg
|
389
|
+
end
|
390
|
+
|
391
|
+
def argument arg
|
392
|
+
p.argument arg
|
393
|
+
end
|
394
|
+
def argument_required bool = true
|
395
|
+
if bool
|
396
|
+
p.argument :required
|
397
|
+
else
|
398
|
+
p.argument false
|
399
|
+
end
|
400
|
+
end
|
401
|
+
def argument_required?
|
402
|
+
p.argument_required?
|
403
|
+
end
|
404
|
+
|
405
|
+
def argument_optional bool = true
|
406
|
+
if bool
|
407
|
+
p.argument :optional
|
408
|
+
else
|
409
|
+
p.argument false
|
410
|
+
end
|
411
|
+
end
|
412
|
+
def argument_optional?
|
413
|
+
p.argument_optional?
|
414
|
+
end
|
415
|
+
|
416
|
+
def required bool = true
|
417
|
+
p.required = bool
|
418
|
+
end
|
419
|
+
def required?
|
420
|
+
p.required?
|
421
|
+
end
|
422
|
+
|
423
|
+
def optional bool = true
|
424
|
+
if bool
|
425
|
+
p.required !bool
|
426
|
+
else
|
427
|
+
p.required bool
|
428
|
+
end
|
429
|
+
end
|
430
|
+
def optional?
|
431
|
+
p.optional?
|
432
|
+
end
|
433
|
+
|
434
|
+
def cast sym=nil, &b
|
435
|
+
p.cast = sym || b
|
436
|
+
end
|
437
|
+
def cast?
|
438
|
+
p.cast?
|
439
|
+
end
|
440
|
+
|
441
|
+
def validate sym=nil, &b
|
442
|
+
p.validate = sym || b
|
443
|
+
end
|
444
|
+
def validate?
|
445
|
+
p.validate?
|
446
|
+
end
|
447
|
+
|
448
|
+
def description s
|
449
|
+
p.description = s.to_s
|
450
|
+
end
|
451
|
+
def description?
|
452
|
+
p.description?
|
453
|
+
end
|
454
|
+
alias_method 'desc', 'description'
|
455
|
+
|
456
|
+
def default value, *values
|
457
|
+
p.defaults.push value
|
458
|
+
p.defaults.push *values
|
459
|
+
p.defaults
|
460
|
+
end
|
461
|
+
def defaults?
|
462
|
+
p.defaults?
|
463
|
+
end
|
464
|
+
def defaults value, *values
|
465
|
+
p.defaults.push value
|
466
|
+
p.defaults.push *values
|
467
|
+
p.defaults
|
468
|
+
end
|
469
|
+
def defaults?
|
470
|
+
p.defaults?
|
471
|
+
end
|
472
|
+
|
473
|
+
def arity value
|
474
|
+
p.arity = Integer value
|
475
|
+
end
|
476
|
+
def arity?
|
477
|
+
p.arity?
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
class Table < ::Array
|
482
|
+
def initialize
|
483
|
+
super()
|
484
|
+
self.fields = []
|
485
|
+
extend BoundsCheck
|
486
|
+
end
|
487
|
+
module BoundsCheck
|
488
|
+
def [] *a, &b
|
489
|
+
p = super
|
490
|
+
ensure
|
491
|
+
raise NoneSuch, a.join(',') unless p
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|