main 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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