ahoward-main 2.9.0

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.
@@ -0,0 +1,51 @@
1
+ module Main
2
+ ### because active_record fubars the hell out of Logger ;-(
3
+ class Logger < ::Logger
4
+ def self.new *a, &b
5
+ super(*a, &b).instance_eval{ @default_formatter = @formatter = Formatter.new; self }
6
+ end
7
+ def format_message(severity, datetime, progname, msg)
8
+ (@formatter || @default_formatter).call(severity, datetime, progname, msg)
9
+ end
10
+
11
+ def device
12
+ @logdev.instance_eval{ @dev }
13
+ end
14
+
15
+ def tty?
16
+ device.respond_to?('tty?') and device.tty?
17
+ end
18
+
19
+ def turn which
20
+ @logdev.extend OnOff unless OnOff === @logdev
21
+ @logdev.turn which
22
+ end
23
+
24
+ def on
25
+ turn :on
26
+ end
27
+ alias_method "on!", "on"
28
+ def self.on *a, &b
29
+ new(*a, &b).instance_eval{ turn :on; self }
30
+ end
31
+
32
+ def off
33
+ turn :off
34
+ end
35
+ alias_method "off!", "off"
36
+ def self.off *a, &b
37
+ new(*a, &b).instance_eval{ turn :off; self }
38
+ end
39
+
40
+ module OnOff
41
+ def turn which
42
+ @turned = which.to_s =~ %r/on/i ? :on : :off
43
+ end
44
+
45
+ def write message
46
+ return message.to_s.size if @turned == :off
47
+ super
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,42 @@
1
+ class Mode < ::String
2
+ class Error < ::StandardError; end
3
+ class Duplicate < Error; end
4
+ class Ambiguous < Error
5
+ include Main::Softspoken
6
+ end
7
+
8
+ class List < ::Array
9
+ def initialize *a, &b
10
+ super
11
+ ensure
12
+ self.fields = []
13
+ end
14
+ def add klass
15
+ mode_name = Mode.new klass.mode_name
16
+ raise Duplicate, mode_name if has_key? mode_name
17
+ self[mode_name] = klass
18
+ end
19
+ def find_by_mode m, options = {}
20
+ quiet = options['quiet'] || options[:quiet]
21
+ each_pair do |mode, klass|
22
+ return mode if mode == m
23
+ end
24
+ candidates = []
25
+ each_pair do |mode, klass|
26
+ candidates << mode if mode.index(m) == 0
27
+ end
28
+ case candidates.size
29
+ when 0
30
+ nil
31
+ when 1
32
+ candidates.first
33
+ else
34
+ raise Ambiguous, "ambiguous mode: #{ m } = (#{ candidates.sort.join ' or ' })?"
35
+ end
36
+ end
37
+ end
38
+
39
+ def self.list *a, &b
40
+ List.new *a, &b
41
+ end
42
+ end
@@ -0,0 +1,699 @@
1
+ module Main
2
+ class Parameter
3
+ class Error < StandardError
4
+ include Softspoken
5
+ fattr 'wrapped'
6
+ end
7
+ class Arity < Error; end
8
+ class NotGiven < Arity; end
9
+ class InValid < Error; end
10
+ class NoneSuch < Error; end
11
+ class AmbigousOption < Error; end
12
+ class NeedlessArgument < Error; end
13
+ class MissingArgument < Error; end
14
+ class InvalidOption < Error; end
15
+
16
+ class << self
17
+ def wrapped_error w
18
+ e = Error.new "(#{ w.message } (#{ w.class }))"
19
+ e.wrapped = w
20
+ e.set_backtrace(w.backtrace || [])
21
+ e
22
+ end
23
+
24
+ def wrap_errors
25
+ begin
26
+ yield
27
+ rescue => e
28
+ raise wrapped_error(e)
29
+ end
30
+ end
31
+
32
+ Types = [ Parameter ]
33
+ def inherited other
34
+ Types << other
35
+ end
36
+
37
+ def sym
38
+ @sym ||= name.split(%r/::/).last.downcase.to_sym
39
+ end
40
+
41
+ def class_for type
42
+ sym = type.to_s.downcase.to_sym
43
+ c = Types.detect{|t| t.sym == sym}
44
+ raise ArgumentError, type.inspect unless c
45
+ c
46
+ end
47
+
48
+ def create type, *a, &b
49
+ c = class_for type
50
+ obj = c.allocate
51
+ obj.type = c.sym
52
+ obj.instance_eval{ initialize *a, &b }
53
+ obj
54
+ end
55
+ end
56
+
57
+ fattr 'type'
58
+ fattr 'names'
59
+ fattr 'abbreviations'
60
+ fattr 'argument'
61
+ fattr 'given'
62
+ fattr 'cast'
63
+ fattr 'validate'
64
+ fattr 'description'
65
+ fattr 'synopsis'
66
+ fattr('values'){ [] }
67
+ fattr('defaults'){ [] }
68
+ fattr('examples'){ [] }
69
+
70
+ fattr 'arity' => 1
71
+ fattr 'required' => false
72
+
73
+ fattr 'error_handler_before'
74
+ fattr 'error_handler_instead'
75
+ fattr 'error_handler_after'
76
+
77
+ def initialize name, *names, &block
78
+ @names = Cast.list_of_string name, *names
79
+ @names.map! do |name|
80
+ if name =~ %r/^-+/
81
+ name.gsub! %r/^-+/, ''
82
+ end
83
+
84
+ if name =~ %r/=.*$/
85
+ argument( name =~ %r/=\s*\[.*$/ ? :optional : :required )
86
+ name.gsub! %r/=.*$/, ''
87
+ end
88
+
89
+ name
90
+ end
91
+ @names = @names.sort_by{|name| name.size}.reverse
92
+ @names[1..-1].each do |name|
93
+ raise ArgumentError, "only one long name allowed (#{ @names.inspect })" if
94
+ name.size > 1
95
+ end
96
+
97
+ DSL.evaluate(self, &block) if block
98
+ sanity_check!
99
+ end
100
+
101
+ def sanity_check!
102
+ raise Arity, "#{ name } with arity -1 (zero or more args) cannot be required" if(arity == -1 and required?)
103
+ end
104
+
105
+ def name
106
+ names.first
107
+ end
108
+
109
+ def default
110
+ defaults.first
111
+ end
112
+
113
+ def typename
114
+ prefix = '--' if type.to_s =~ %r/option/
115
+ "#{ type }(#{ prefix }#{ name })"
116
+ end
117
+
118
+ def add_value value
119
+ given true
120
+ values << value
121
+ end
122
+
123
+ def value
124
+ values.first
125
+ end
126
+
127
+ def argument_none?
128
+ argument.nil?
129
+ end
130
+
131
+ def argument_required?
132
+ argument and
133
+ argument.to_s.downcase.to_sym == :required
134
+ end
135
+ def argument_optional?
136
+ argument and
137
+ argument.to_s.downcase.to_sym == :optional
138
+ end
139
+
140
+ def optional?
141
+ not required?
142
+ end
143
+ def optional= bool
144
+ self.required !bool
145
+ end
146
+
147
+ =begin
148
+ def setup!
149
+ return false unless given?
150
+ adding_handlers do
151
+ check_arity
152
+ apply_casting
153
+ check_validation
154
+ end
155
+ true
156
+ end
157
+ =end
158
+
159
+ def setup!
160
+ adding_handlers do
161
+ check_arity
162
+ apply_casting
163
+ check_validation
164
+ end
165
+ end
166
+
167
+ def check_arity
168
+ return true if not given? and optional?
169
+
170
+ ex = values.size == 0 ? NotGiven : Arity
171
+
172
+ (raise ex, "#{ typename })" if values.size.zero? and argument_required?) unless arity == -1
173
+
174
+ if arity >= 0
175
+ min = arity
176
+ sign = ''
177
+ else
178
+ min = arity.abs - 1
179
+ sign = '-'
180
+ end
181
+
182
+ arity = min
183
+
184
+ =begin
185
+ puts
186
+ p :values => values
187
+ p :arity => arity
188
+ p :argument_required => argument_required?
189
+ p :argument_none => argument_none?
190
+ puts
191
+ =end
192
+ if values.size < arity
193
+ if argument_optional?
194
+ raise ex, "#{ typename }) #{ values.size }/#{ sign }#{ arity }" if(values.size < arity and values.size > 0)
195
+ elsif argument_required? or argument_none?
196
+ raise ex, "#{ typename }) #{ values.size }/#{ sign }#{ arity }" if(values.size < arity)
197
+ end
198
+ end
199
+ end
200
+
201
+ def apply_casting
202
+ if cast?
203
+ op = cast.respond_to?('call') ? cast : Cast[cast]
204
+ values.map! do |val|
205
+ Parameter.wrap_errors{ op.call val }
206
+ end
207
+ end
208
+ end
209
+
210
+ def check_validation
211
+ if validate?
212
+ values.each do |value|
213
+ validate[value] or
214
+ raise InValid, "invalid: #{ typename }=#{ value.inspect }"
215
+ end
216
+ end
217
+ end
218
+
219
+ def add_handlers e
220
+ esc =
221
+ class << e
222
+ self
223
+ end
224
+
225
+ this = self
226
+
227
+ %w[ before instead after ].each do |which|
228
+ getter = "error_handler_#{ which }"
229
+ query = "error_handler_#{ which }?"
230
+ if send(query)
231
+ handler = send getter
232
+ esc.module_eval do
233
+ define_method(getter) do |main|
234
+ main.instance_eval_block self, &handler
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
240
+
241
+ def adding_handlers
242
+ begin
243
+ yield
244
+ rescue Exception => e
245
+ add_handlers e
246
+ raise
247
+ end
248
+ end
249
+
250
+ class Argument < Parameter
251
+ fattr 'required' => true
252
+
253
+ fattr 'synopsis' do
254
+ label = name
255
+ op = required ? "->" : "~>"
256
+ value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
257
+ value = "#{ cast }(#{ value })" if(cast and not cast.respond_to?(:call))
258
+ "#{ label } (#{ arity } #{ op } #{ value })"
259
+ end
260
+ end
261
+
262
+ class Option < Parameter
263
+ fattr 'required' => false
264
+ fattr 'arity' => 0
265
+
266
+ fattr 'synopsis' do
267
+ long, *short = names
268
+ value = cast || name
269
+ rhs = argument ? (argument == :required ? "=#{ name }" : "=[#{ name }]") : nil
270
+ label = ["--#{ long }#{ rhs }", short.map{|s| "-#{ s }"}].flatten.join(", ")
271
+ unless argument_none?
272
+ op = required ? "->" : "~>"
273
+ value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
274
+ value = "#{ cast }(#{ value })" if(cast and not cast.respond_to?(:call))
275
+ "#{ label } (#{ arity } #{ op } #{ value })"
276
+ else
277
+ label
278
+ end
279
+ end
280
+ end
281
+
282
+ class Keyword < Parameter
283
+ fattr 'required' => false
284
+ fattr 'argument' => :required
285
+
286
+ fattr 'synopsis' do
287
+ label = "#{ name }=#{ name }"
288
+ op = required ? "->" : "~>"
289
+ value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
290
+ value = "#{ cast }(#{ value })" if(cast and not cast.respond_to?(:call))
291
+ "#{ label } (#{ arity } #{ op } #{ value })"
292
+ end
293
+ end
294
+
295
+ class Environment < Parameter
296
+ fattr 'argument' => :required
297
+
298
+ fattr 'synopsis' do
299
+ label = "env[#{ name }]=#{ name }"
300
+ op = required ? "->" : "~>"
301
+ value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
302
+ value = "#{ cast }(#{ value })" if(cast and not cast.respond_to?(:call))
303
+ "#{ label } (#{ arity } #{ op } #{ value })"
304
+ end
305
+ end
306
+
307
+ class List < ::Array
308
+ fattr :main
309
+ fattr :argv
310
+ fattr :env
311
+
312
+ def parse main
313
+ @main, @argv, @env = main, main.argv, main.env
314
+
315
+ ignore, stop = [], argv.index('--')
316
+
317
+ if stop
318
+ ignore = argv[stop .. -1]
319
+ (argv.size - stop).times{ argv.pop }
320
+ end
321
+
322
+ parse_options argv
323
+
324
+ return 'help' if detect{|p| p.name.to_s == 'help' and p.given?}
325
+
326
+ parse_keywords argv
327
+ parse_arguments argv
328
+ parse_environment env
329
+
330
+ defaults!
331
+ validate!
332
+
333
+ argv.push *ignore[1 .. -1] unless ignore.empty?
334
+
335
+ return self
336
+ ensure
337
+ @main, @argv, @env = nil
338
+ end
339
+
340
+ def parse_options argv, params = nil
341
+ params ||= options
342
+
343
+ spec, h, s = [], {}, {}
344
+
345
+ params.each do |p|
346
+ head, *tail = p.names
347
+ long = "--#{ head }"
348
+ shorts = tail.map{|t| "-#{ t }"}
349
+ type =
350
+ if p.argument_required? then GetoptLong::REQUIRED_ARGUMENT
351
+ elsif p.argument_optional? then GetoptLong::OPTIONAL_ARGUMENT
352
+ else GetoptLong::NO_ARGUMENT
353
+ end
354
+ a = [ long, shorts, type ].flatten
355
+ spec << a
356
+ h[long] = p
357
+ s[long] = a
358
+ end
359
+
360
+ begin
361
+ GetoptLong.new(argv, *spec).each do |long, value|
362
+ value =
363
+ case s[long].last
364
+ when GetoptLong::NO_ARGUMENT
365
+ value.empty? ? true : value
366
+ when GetoptLong::OPTIONAL_ARGUMENT
367
+ value.empty? ? true : value
368
+ when GetoptLong::REQUIRED_ARGUMENT
369
+ value
370
+ end
371
+ p = h[long]
372
+ p.add_value value
373
+ end
374
+ rescue GetoptLong::AmbigousOption, GetoptLong::NeedlessArgument,
375
+ GetoptLong::MissingArgument, GetoptLong::InvalidOption => e
376
+ c = Parameter.const_get e.class.name.split(/::/).last
377
+ ex = c.new e.message
378
+ ex.set_backtrace e.message
379
+ ex.extend Softspoken
380
+ raise ex
381
+ end
382
+
383
+ =begin
384
+ params.each do |p|
385
+ p.setup!
386
+ end
387
+ =end
388
+ end
389
+
390
+ def parse_arguments argv, params=nil
391
+ params ||= select{|p| p.type == :argument}
392
+
393
+ params.each do |p|
394
+ if p.arity >= 0
395
+ p.arity.times do
396
+ break if argv.empty?
397
+ value = argv.shift
398
+ p.add_value value
399
+ end
400
+ else
401
+ arity = p.arity.abs - 1
402
+ arity.times do
403
+ break if argv.empty?
404
+ value = argv.shift
405
+ p.add_value value
406
+ end
407
+ argv.size.times do
408
+ value = argv.shift
409
+ p.add_value value
410
+ end
411
+ end
412
+ end
413
+
414
+ =begin
415
+ params.each do |p|
416
+ p.setup!
417
+ end
418
+ =end
419
+ end
420
+
421
+ def parse_keywords argv, params=nil
422
+ params ||= select{|p| p.type == :keyword}
423
+
424
+ replacements = {}
425
+
426
+ params.each do |p|
427
+ names = p.names
428
+ name = names.sort_by{|n| [n.size,n]}.last
429
+
430
+ kre = %r/^\s*(#{ names.join '|' })\s*=/
431
+ opt = "--#{ name }"
432
+ i = -1
433
+
434
+ argv.each_with_index do |a, idx|
435
+ i += 1
436
+ b = argv[idx + 1]
437
+ s = "#{ a }#{ b }"
438
+ m, key, *ignored = kre.match(s).to_a
439
+ if m
440
+ replacements[i] ||= a.gsub %r/^\s*#{ key }/, opt
441
+ next
442
+ end
443
+ =begin
444
+ abbrev = name.index(key) == 0
445
+ if abbrev
446
+ replacements[i] ||= a.gsub %r/^\s*#{ key }/, opt
447
+ end
448
+ =end
449
+ end
450
+ end
451
+
452
+ replacements.each do |i, r|
453
+ argv[i] = r
454
+ end
455
+
456
+ parse_options argv, params
457
+ end
458
+
459
+ def parse_environment env, params=nil
460
+ params ||= select{|p| p.type == :environment}
461
+
462
+ params.each do |p|
463
+ names = p.names
464
+ name = names.first
465
+ value = env[name]
466
+ next unless value
467
+ p.add_value value
468
+ end
469
+
470
+ =begin
471
+ params.each do |p|
472
+ p.setup!
473
+ end
474
+ =end
475
+ end
476
+
477
+ def defaults!
478
+ each do |p|
479
+ if(p.defaults? and (not p.given?))
480
+ p.defaults.each do |default|
481
+ p.values << (default.respond_to?('to_proc') ? main.instance_eval(&default) : default) # so as NOT to set 'given?'
482
+ end
483
+ end
484
+ end
485
+ end
486
+
487
+ def validate!
488
+ each do |p|
489
+ #p.adding_handlers do
490
+ #next if p.arity == -1
491
+ #raise NotGiven, "#{ p.typename } not given" if(p.required? and (not p.given?))
492
+ #end
493
+ p.setup!
494
+ end
495
+ end
496
+
497
+ [:parameter, :option, :argument, :keyword, :environment].each do |m|
498
+ define_method("#{ m }s"){ select{|p| p.type == m or m == :parameter} }
499
+
500
+ define_method("has_#{ m }?") do |name, *names|
501
+ catch :has do
502
+ names = Cast.list_of_string name, *names
503
+ send("#{ m }s").each do |param|
504
+ common = Cast.list_of_string(param.names) & names
505
+ throw :has, true unless common.empty?
506
+ end
507
+ false
508
+ end
509
+ end
510
+ end
511
+
512
+ def delete name, *names
513
+ name, *names = name.names if Parameter === name
514
+ names = Cast.list_of_string name, *names
515
+ keep = []
516
+ each do |param|
517
+ common = Cast.list_of_string(param.names) & names
518
+ keep << param if common.empty?
519
+ end
520
+ replace keep
521
+ end
522
+
523
+ def << *a
524
+ delete *a
525
+ super
526
+ end
527
+ end
528
+
529
+ class DSL
530
+ def self.evaluate param, &block
531
+ new(param).instance_eval(&block)
532
+ end
533
+
534
+ attr 'param'
535
+
536
+ def initialize param
537
+ @param = param
538
+ end
539
+
540
+ def fattr a = nil, &block
541
+ name = param.name
542
+ a ||= name
543
+ b = fattr_block_for name, &block
544
+ Main.current.module_eval{ fattr *a, &b }
545
+ end
546
+ alias_method 'attribute', 'fattr'
547
+
548
+ def fattr_block_for name, &block
549
+ block ||= lambda{|param| [0,1].include?(param.arity) ? param.value : param.values }
550
+ lambda{ block.call self.param[name] }
551
+ end
552
+
553
+ def attr *a, &b
554
+ fattr *a, &b
555
+ end
556
+
557
+ def example *list
558
+ list.flatten.compact.each do |elem|
559
+ param.examples << elem.to_s
560
+ end
561
+ end
562
+ alias_method "examples", "example"
563
+
564
+
565
+ def type *sym
566
+ sym.size == 0 ? param.type : (param.type = sym.first)
567
+ end
568
+ def type?
569
+ param.type?
570
+ end
571
+
572
+ def synopsis *arg
573
+ arg.size == 0 ? param.synopsis : (param.synopsis arg.first)
574
+ end
575
+
576
+ def argument arg
577
+ param.argument arg
578
+ end
579
+ def argument_required bool = true
580
+ if bool
581
+ param.argument :required
582
+ else
583
+ param.argument false
584
+ end
585
+ end
586
+ def argument_required?
587
+ param.argument_required?
588
+ end
589
+
590
+ def argument_optional bool = true
591
+ if bool
592
+ param.argument :optional
593
+ else
594
+ param.argument false
595
+ end
596
+ end
597
+ def argument_optional?
598
+ param.argument_optional?
599
+ end
600
+
601
+ def required bool = true
602
+ param.required = bool
603
+ end
604
+ def required?
605
+ param.required?
606
+ end
607
+
608
+ def optional bool = true
609
+ if bool
610
+ param.required !bool
611
+ else
612
+ param.required bool
613
+ end
614
+ end
615
+ def optional?
616
+ param.optional?
617
+ end
618
+
619
+ def cast sym=nil, &b
620
+ param.cast = sym || b
621
+ end
622
+ def cast?
623
+ param.cast?
624
+ end
625
+
626
+ def validate sym=nil, &b
627
+ param.validate = sym || b
628
+ end
629
+ def validate?
630
+ param.validate?
631
+ end
632
+
633
+ def description s
634
+ param.description = s.to_s
635
+ end
636
+ def description?
637
+ param.description?
638
+ end
639
+ alias_method 'desc', 'description'
640
+
641
+ def default *values, &block
642
+ if block.nil? and values.empty?
643
+ raise ArgumentError, 'no default'
644
+ end
645
+ unless values.empty?
646
+ param.defaults.push *values
647
+ end
648
+ unless block.nil?
649
+ param.defaults.push block
650
+ end
651
+ param.defaults
652
+ end
653
+ alias_method 'defaults', 'default'
654
+ def defaults?
655
+ param.defaults?
656
+ end
657
+
658
+ def arity value
659
+ raise Arity if value.nil?
660
+ value = -1 if value.to_s == '*'
661
+ value = Integer value
662
+ raise Arity if value.zero?
663
+ param.arity = value
664
+ if param.arity == -1
665
+ optional true
666
+ end
667
+ value
668
+ end
669
+ def arity?
670
+ param.arity?
671
+ end
672
+
673
+ def error which = :instead, &block
674
+ param.send "error_handler_#{ which }=", block
675
+ end
676
+ end
677
+
678
+ class Table < ::Array
679
+ def initialize
680
+ super()
681
+ self.fields = []
682
+ extend BoundsCheck
683
+ end
684
+
685
+ def to_options
686
+ (hash = self.to_hash ).keys.each { |key| hash[key] = hash[key].value }
687
+ return hash
688
+ end
689
+
690
+ module BoundsCheck
691
+ def [] *a, &b
692
+ p = super
693
+ ensure
694
+ raise NoneSuch, a.join(',') unless p
695
+ end
696
+ end
697
+ end
698
+ end
699
+ end