alib 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1029 @@
1
+ #
2
+ # template main building block classes
3
+ #
4
+
5
+
6
+ module ALib
7
+
8
+ class AbstractMain
9
+ #--{{{
10
+ include ALib
11
+ include ALib::Logging
12
+
13
+ module Constants
14
+ EXIT_SUCCESS = 0
15
+ EXIT_PSEUDO_SUCCESS = 42
16
+ EXIT_OK = 42
17
+ EXIT_FAILURE = 1
18
+ end
19
+ include Constants
20
+
21
+ def self.exit_success() EXIT_SUCCESS end
22
+ def exit_success() EXIT_SUCCESS end
23
+ def self.status_success() EXIT_SUCCESS end
24
+ def status_success() EXIT_SUCCESS end
25
+
26
+ def self.exit_pseudo_success() EXIT_PSEUDO_SUCCESS end
27
+ def exit_pseudo_success() EXIT_PSEUDO_SUCCESS end
28
+ def self.status_pseudo_success() EXIT_PSEUDO_SUCCESS end
29
+ def status_pseudo_success() EXIT_PSEUDO_SUCCESS end
30
+
31
+ def self.exit_ok() EXIT_OK end
32
+ def exit_ok() EXIT_OK end
33
+ def self.status_ok() EXIT_OK end
34
+ def status_ok() EXIT_OK end
35
+
36
+ def self.exit_failure() EXIT_FAILURE end
37
+ def exit_failure() EXIT_FAILURE end
38
+ def self.status_failure() EXIT_FAILURE end
39
+ def status_failure() EXIT_FAILURE end
40
+
41
+ class << self
42
+ #--{{{
43
+ def stringlist(*list)
44
+ #--{{{
45
+ list.flatten.compact.map{|item| "#{ item }".strip}
46
+ #--}}}
47
+ end
48
+ def instance_attributes(*names)
49
+ #--{{{
50
+ names = stringlist names
51
+ names.each do |name|
52
+ getter = "#{ name }"
53
+ setter = "#{ name }="
54
+ code = <<-code
55
+ def #{ name }(*a)
56
+ unless a.empty?
57
+ self.#{ name }= a.shift
58
+ else
59
+ @#{ name }
60
+ end
61
+ end
62
+ def #{ name }= value
63
+ @#{ name } = value
64
+ end
65
+ alias #{ name }? #{ name }
66
+ code
67
+ module_eval code
68
+ end
69
+ #--}}}
70
+ end
71
+ alias instance_attribute instance_attributes
72
+ alias i_attrs instance_attributes
73
+ alias i_attr instance_attributes
74
+ alias attribute instance_attributes
75
+ alias attributes instance_attributes
76
+
77
+ def class_attributes(*names)
78
+ #--{{{
79
+ names = stringlist names
80
+ names.each do |name|
81
+ getter = "#{ name }"
82
+ setter = "#{ name }="
83
+ code = <<-code
84
+ class << self
85
+ def #{ name }(*a)
86
+ unless a.empty?
87
+ self.#{ name }= a.shift
88
+ else
89
+ @#{ name }
90
+ end
91
+ end
92
+ def #{ name }= value
93
+ @#{ name } = value
94
+ end
95
+ alias #{ name }? #{ name }
96
+ end
97
+ code
98
+ module_eval code
99
+ end
100
+ #--}}}
101
+ end
102
+ alias class_attribute class_attributes
103
+ alias c_attrs class_attributes
104
+ alias c_attr class_attributes
105
+
106
+ %w( version author program optspec name examples synopsis ).each do |meth|
107
+ #--{{{
108
+ module_eval <<-code
109
+ def #{ meth }(*a)
110
+ unless a.empty?
111
+ self.#{ meth }= a.shift
112
+ else
113
+ @#{ meth }
114
+ end
115
+ end
116
+ def #{ meth }= value
117
+ @#{ meth } = value
118
+ end
119
+ def #{ meth }?
120
+ defined? @#{ meth } and @#{ meth }
121
+ end
122
+ code
123
+ #--}}}
124
+ end
125
+ alias prognam program
126
+ alias progname program
127
+ alias prognam= program=
128
+ alias prognam? program?
129
+
130
+ =begin
131
+ %w( usage examples ).each do |meth|
132
+ #--{{{
133
+ module_eval <<-code
134
+ def #{ meth }(*a)
135
+ unless a.empty?
136
+ self.#{ meth }= a.shift
137
+ else
138
+ @#{ meth }
139
+ end
140
+ end
141
+ def #{ meth }= msg
142
+ @#{ meth } = unindent_block msg.to_s
143
+ end
144
+ def #{ meth }?
145
+ defined? @#{ meth } and @#{ meth }
146
+ end
147
+ code
148
+ #--}}}
149
+ end
150
+ =end
151
+
152
+ def usage arg = nil
153
+ #--{{{
154
+ if arg
155
+ @usage = arg.to_s
156
+ else
157
+ if defined? @usage
158
+ @usage
159
+ else
160
+ s = ""
161
+
162
+ unless name.to_s.empty?
163
+ #s << "\n"
164
+ s << "NAME\n"
165
+ s << indent_block("#{ name }\n", 2)
166
+ end
167
+
168
+ unless synopsis.to_s.empty?
169
+ s << "\n"
170
+ s << "SYNOPSIS\n"
171
+ s << indent_block("#{ synopsis }\n", 2)
172
+ end
173
+
174
+ unless examples.to_s.empty?
175
+ s << "\n"
176
+ s << "EXAMPLES\n"
177
+ s << indent_block("#{ examples }\n", 2)
178
+ end
179
+
180
+ s
181
+ end
182
+ end
183
+ #--}}}
184
+ end
185
+
186
+ =begin
187
+ usage <<-usage
188
+ NAME
189
+ #{ program } v#{ version }
190
+
191
+ SYNOPSIS
192
+ #{ program } [options]* [file]*
193
+ usage
194
+ =end
195
+
196
+
197
+ def unindent_block buf
198
+ #--{{{
199
+ buf = "#{ buf }"
200
+ lines = buf.to_a
201
+ indent_pat = %r/^\s*[\s|]/
202
+ indent = lines.first[ indent_pat ] rescue nil
203
+ if indent
204
+ lines.map do |line|
205
+ line[ indent.size..-1 ] || "\n"
206
+ end.join
207
+ else
208
+ buf
209
+ end
210
+ #--}}}
211
+ end
212
+
213
+ def indent_block buf, n = 2
214
+ #--{{{
215
+ space = ' ' * n.to_i
216
+ unindent_block(buf).gsub %r/^/, space
217
+ #--}}}
218
+ end
219
+
220
+ def options(*list)
221
+ #--{{{
222
+ @optspec ||= []
223
+ return @optspec if list.empty?
224
+ list = [list] unless Array === list.first
225
+ list.each{|spec| (@optspec ||= []) << spec}
226
+ #--}}}
227
+ end
228
+ alias option options
229
+
230
+ def required_arguments(*list)
231
+ #--{{{
232
+ @required_arguments ||= []
233
+ list.flatten.each do |arg|
234
+ return(optional_argument(arg)) if Hash === arg
235
+ unless instance_methods.include? "#{ arg }"
236
+ module_eval <<-code
237
+ def #{ arg }(*__list)
238
+ if __list.empty?
239
+ @#{ arg }
240
+ else
241
+ send('#{ arg }=', *__list)
242
+ end
243
+ end
244
+ def #{ arg }=(__arg, *__list)
245
+ if __list.empty?
246
+ @#{ arg } = __arg
247
+ else
248
+ @#{ arg } = ([__arg] + __list)
249
+ end
250
+ end
251
+ def #{ arg }?
252
+ defined? @#{ arg } and @#{ arg }
253
+ end
254
+ code
255
+ end
256
+ @required_arguments << "#{ arg }"
257
+ end
258
+ @required_arguments
259
+ #--}}}
260
+ end
261
+ alias required_argument required_arguments
262
+ alias arguments required_arguments
263
+ alias argument required_arguments
264
+
265
+ def optional_arguments(*list)
266
+ #--{{{
267
+ @optional_arguments ||= []
268
+ list.flatten.each do |arg|
269
+ arg, default =
270
+ case arg
271
+ when Hash
272
+ arg.to_a.first
273
+ else
274
+ [arg, nil]
275
+ end
276
+ @do_not_gc ||= []
277
+ @do_not_gc << default unless @do_not_gc.include? default
278
+ unless instance_methods.include? "#{ arg }"
279
+ module_eval <<-code
280
+ def #{ arg }(*__list)
281
+ unless @#{ arg }
282
+ @#{ arg } = ObjectSpace::_id2ref #{ default.object_id }
283
+ end
284
+ if __list.empty?
285
+ @#{ arg }
286
+ else
287
+ send('#{ arg }=', *__list)
288
+ end
289
+ end
290
+ def #{ arg }=(__arg, *__list)
291
+ if __list.empty?
292
+ @#{ arg } = __arg
293
+ else
294
+ @#{ arg } = ([__arg] + __list)
295
+ end
296
+ end
297
+ def #{ arg }?
298
+ defined? @#{ arg } and @#{ arg }
299
+ end
300
+ code
301
+ end
302
+ @optional_arguments << "#{ arg }"
303
+ end
304
+ @optional_arguments
305
+ #--}}}
306
+ end
307
+ alias optional_argument optional_arguments
308
+
309
+ def class_initialize
310
+ #--{{{
311
+ version '0.0.0'
312
+
313
+ author 'ara.t.howard@noaa.gov'
314
+
315
+ program File.basename($0)
316
+
317
+ name "#{ program } v#{ version }"
318
+
319
+ optspec [
320
+ [ '--help', '-h', 'this message' ],
321
+ [ '--log=path','-l', 'set log file - (default stderr)' ],
322
+ [ '--verbosity=verbostiy', '-v', '0|fatal < 1|error < 2|warn < 3|info < 4|debug - (default info)' ],
323
+ ]
324
+
325
+ =begin
326
+ usage <<-usage
327
+ NAME
328
+ #{ program } v#{ version }
329
+
330
+ SYNOPSIS
331
+ #{ program } [options]* [file]*
332
+ usage
333
+ =end
334
+
335
+ examples ''
336
+ #--}}}
337
+ end
338
+
339
+ def inherited klass, &b
340
+ #--{{{
341
+ ret = super(klass, &b)
342
+ klass.class_initialize
343
+ ret
344
+ #--}}}
345
+ end
346
+
347
+ def run(*a, &b)
348
+ #--{{{
349
+ new(*a, &b).run
350
+ #--}}}
351
+ end
352
+
353
+ def defaults head = nil, *tail
354
+ #--{{{
355
+ @defaults ||= {}
356
+ return @defaults if head.nil?
357
+ return @defaults.update(head) if head.is_a?(Hash)
358
+ return @defaults.values_at(*head) if head.is_a?(Array)
359
+ return @defaults[head.to_s] if tail.empty?
360
+ keys = [head, tail].flatten.map{|k| k.to_s}
361
+ return @defaults.values_at(*keys)
362
+ #--}}}
363
+ end
364
+ alias_method "default", "defaults"
365
+ #--}}}
366
+ end
367
+
368
+ #class_attribute
369
+
370
+ class_initialize
371
+
372
+ attr :logger
373
+ attr :program
374
+ attr :argv
375
+ attr :env
376
+ attr :cmdline
377
+ attr :console
378
+ attr :options
379
+ attr :listoptions
380
+ attr :op
381
+ attr :logdev
382
+ attr :verbosity
383
+ alias console? console
384
+
385
+ def exit_status e = nil
386
+ (e ? (@exit_success = e) : (defined?(@exit_success) and @exit_success))
387
+ end
388
+ alias_method "exit_status?", "exit_status"
389
+
390
+ def initialize argv = ARGV, env = ENV
391
+ #--{{{
392
+ @argv = Util::mcp(argv.to_a)
393
+ @env = Util::mcp(env.to_hash)
394
+ @program = File::expand_path $0
395
+ @cmdline = ([@program] + @argv).join ' '
396
+ @console = STDIN.tty?
397
+ #--}}}
398
+ end
399
+ def klass; self.class; end
400
+ def required_arguments
401
+ #--{{{
402
+ klass::required_arguments
403
+ #--}}}
404
+ end
405
+ def optional_arguments
406
+ #--{{{
407
+ klass::optional_arguments
408
+ #--}}}
409
+ end
410
+ def run
411
+ #--{{{
412
+ logcatch do
413
+ begin
414
+ pre_run
415
+ pre_parse_options
416
+ parse_options
417
+ post_parse_options
418
+ pre_parse_argv
419
+ parse_argv
420
+ post_parse_argv
421
+ pre_init_logging
422
+ init_logging
423
+ post_init_logging
424
+ pre_main
425
+ status = main
426
+ post_main
427
+ post_run
428
+ exit(exit_status ? exit_status : (status ? EXIT_SUCCESS : EXIT_FAILURE))
429
+ rescue Errno::EPIPE
430
+ STDOUT.tty? ? raise : exit(EXIT_FAILURE)
431
+ end
432
+ end
433
+ #--}}}
434
+ end
435
+ def pre_parse_options; end
436
+ def post_parse_options; end
437
+ def pre_init_logging; end
438
+ def post_init_logging; end
439
+ def pre_parse_argv
440
+ #--{{{
441
+ if(@options.has_key?('help') or (@argv.size == 1 and @argv.first =~ %r/help/i))
442
+ usage STDOUT
443
+ exit EXIT_SUCCESS
444
+ end
445
+ if(@options.has_key?('version') or @argv.first =~ %r/version/i)
446
+ STDOUT.puts self.class.version
447
+ exit EXIT_SUCCESS
448
+ end
449
+ #--}}}
450
+ end
451
+ def post_parse_argv; end
452
+ def pre_run; end
453
+ def post_run; end
454
+ def pre_main; end
455
+ def post_main; end
456
+ def logcatch
457
+ #--{{{
458
+ ret = nil
459
+ # @logger ||= Logger::new STDERR
460
+ logger!
461
+ begin
462
+ ret = yield
463
+ rescue Exception => e
464
+ unless SystemExit === e
465
+ fatal{ e }
466
+ exit EXIT_FAILURE
467
+ if false
468
+ if logger.debug?
469
+ fatal{ e }
470
+ exit EXIT_FAILURE
471
+ else
472
+ fatal{ emsg(e) }
473
+ exit EXIT_FAILURE
474
+ end
475
+ end
476
+ else
477
+ exit e.status
478
+ end
479
+ end
480
+ ret
481
+ #--}}}
482
+ end
483
+ def usage port = STDERR
484
+ #--{{{
485
+ port << klass::usage << "\n" if(klass::usage and not klass::usage.empty?)
486
+ port << klass::examples << "\n" if(klass::examples and not klass::examples.empty?)
487
+
488
+ if klass::optspec
489
+ port << 'OPTIONS' << "\n"
490
+
491
+ klass::optspec.each do |os|
492
+ a, b, c = os
493
+ long, short, desc = nil
494
+ [a,b,c].each do |word|
495
+ next unless word
496
+ word.strip!
497
+ case word
498
+ when %r/^--[^-]/o
499
+ long = word
500
+ when %r/^-[^-]/o
501
+ short = word
502
+ else
503
+ desc = word
504
+ end
505
+ end
506
+
507
+ spec = ((long and short) ? [long, short] : [long])
508
+
509
+ if spec
510
+ port << Util::columnize(spec.join(', '), :width => 80, :indent => 2)
511
+ port << "\n"
512
+ end
513
+
514
+ if desc
515
+ port << Util::columnize(desc, :width => 80, :indent => 8)
516
+ port << "\n"
517
+ end
518
+ end
519
+
520
+ port << "\n"
521
+ end
522
+
523
+ port
524
+ #--}}}
525
+ end
526
+ def parse_options
527
+ #--{{{
528
+ @op = OptionParser::new
529
+ @options = {}
530
+ @listoptions = Hash::new{|h,k| h[k] = []}
531
+ klass::optspec.each do |spec|
532
+ k = spec.first.gsub(%r/(?:--)|(?:=.*$)|(?:\s+)/o,'')
533
+ @op.def_option(*spec) do |v|
534
+ @options[k] = v
535
+ @listoptions[k] << v
536
+ end
537
+ end
538
+ begin
539
+ op.parse! @argv
540
+ rescue OptionParser::InvalidOption => e
541
+ # preverve unknown options
542
+ #e.recover(argv)
543
+ invalid_option e
544
+ end
545
+ @options
546
+ #--}}}
547
+ end
548
+ def invalid_option e
549
+ #--{{{
550
+ # e.recover argv
551
+ fatal{ e.to_s }
552
+ exit EXIT_FAILURE
553
+ #--}}}
554
+ end
555
+ def init_logging opts = @options
556
+ #--{{{
557
+ log = Util::getopt 'log', opts
558
+ log_age = Util::getopt 'log_age', opts
559
+ log_size = Util::getopt 'log_size', opts
560
+ verbosity = Util::getopt 'verbosity', opts
561
+ log_age = Integer log_age rescue nil
562
+ log_size = Integer log_size rescue nil
563
+ $logger = self.logger = Logger::new(log || STDERR, log_age, log_size)
564
+ #
565
+ # hack to fix Logger sync bug
566
+ #
567
+ begin
568
+ class << @logger; attr :logdev unless @logger.respond_to?(:logdev); end
569
+ @logdev = @logger.logdev.dev
570
+ @logdev.sync = true
571
+ rescue
572
+ nil
573
+ end
574
+ level = nil
575
+ verbosity ||= 'info'
576
+ verbosity =
577
+ case verbosity
578
+ when /^\s*(?:4|d|debug)\s*$/io
579
+ level = 'Logging::DEBUG'
580
+ 4
581
+ when /^\s*(?:3|i|info)\s*$/io
582
+ level = 'Logging::INFO'
583
+ 3
584
+ when /^\s*(?:2|w|warn)\s*$/io
585
+ level = 'Logging::WARN'
586
+ 2
587
+ when /^\s*(?:1|e|error)\s*$/io
588
+ level = 'Logging::ERROR'
589
+ 1
590
+ when /^\s*(?:0|f|fatal)\s*$/io
591
+ level = 'Logging::FATAL'
592
+ 0
593
+ else
594
+ abort "illegal verbosity setting <#{ verbosity }>"
595
+ end
596
+ logger.level = 2 - ((verbosity % 5) - 2)
597
+ logger
598
+ #--}}}
599
+ end
600
+ def parse_argv
601
+ #--{{{
602
+ a, b = [], []
603
+ klass::required_arguments.each do |arg|
604
+ value = @argv.shift
605
+ if value
606
+ send "#{ arg }=", value
607
+ else
608
+ die 'msg' => "required_argument <#{ arg }> not given"
609
+ end
610
+ a << send("#{ arg }")
611
+ end
612
+ klass::optional_arguments.each do |arg|
613
+ value = @argv.shift
614
+ if value
615
+ send "#{ arg }=", value
616
+ end
617
+ b << send("#{ arg }")
618
+ end
619
+ [a, b, @argv]
620
+ #--}}}
621
+ end
622
+ def die opts = {}
623
+ #--{{{
624
+ msg = Util::getopt 'msg', opts, klass.usage
625
+ errno = Util::getopt 'errno', opts, EXIT_FAILURE
626
+ STDERR.puts("#{ msg }")
627
+ exit(Integer(errno))
628
+ #--}}}
629
+ end
630
+ def main
631
+ #--{{{
632
+ raise NotImplementedError, 'main'
633
+ #--}}}
634
+ end
635
+ def options? *keys
636
+ #--{{{
637
+ list = keys.map{|key| @options.has_key? key}
638
+ list.size > 0 and list.all?
639
+ #--}}}
640
+ end
641
+ alias_method "option?", "options?"
642
+ def options *keys
643
+ #--{{{
644
+ if keys.empty?
645
+ @options
646
+ else
647
+ keys.map{|key| @options[key]}
648
+ end
649
+ #--}}}
650
+ end
651
+ def option key
652
+ #--{{{
653
+ options(key).first
654
+ #--}}}
655
+ end
656
+ def defaults *a, &b
657
+ #--{{{
658
+ self.class.defaults *a, &b
659
+ #--}}}
660
+ end
661
+ alias_method "default", "defaults"
662
+
663
+ def argv_plus_stdin
664
+ #--{{{
665
+ stdin = argv.delete '-'
666
+ STDIN.each{|line| argv << line} if stdin
667
+ argv.map!{|arg| arg.strip}
668
+ argv
669
+ #--}}}
670
+ end
671
+
672
+ #
673
+ # casting methods
674
+ #
675
+ module Casting
676
+ #--{{{
677
+ def int_list *list
678
+ #--{{{
679
+ list.flatten.compact.map{|i| Util.atoi i}
680
+ #--}}}
681
+ end
682
+ def float_list *list
683
+ #--{{{
684
+ list.flatten.compact.map{|f| Float f}
685
+ #--}}}
686
+ end
687
+ def string_list *list
688
+ #--{{{
689
+ list.flatten.compact.map{|s| String s}
690
+ #--}}}
691
+ end
692
+ def string_cast arg
693
+ arg.to_s
694
+ end
695
+ def int_cast arg
696
+ Integer arg.to_s
697
+ end
698
+ def float_cast arg
699
+ Float arg
700
+ end
701
+ def bool_cast arg
702
+ arg ? true : false
703
+ end
704
+ def bool_cast arg
705
+ case arg
706
+ when Numeric
707
+ not arg.zero?
708
+ when String
709
+ case arg
710
+ when %r/^(1|t|true)$/i
711
+ true
712
+ when %r/^(0|f|false)$/i
713
+ false
714
+ else
715
+ raise ArgumentError, arg.inspect
716
+ end
717
+ else
718
+ arg ? true : false
719
+ end
720
+ end
721
+ def re_cast arg
722
+ Regexp === arg ? arg : %r/#{ arg }/
723
+ end
724
+ def pathname_cast arg, opts = {}
725
+ expand = opts['expand'] || opts[:expand]
726
+ pn = (Pathname === arg ? arg : Pathname.new(arg.to_s))
727
+ expand ? pn.expand_path : pn
728
+ end
729
+ #--}}}
730
+ end
731
+ include Casting
732
+
733
+ #
734
+ # param dsl and class
735
+ #
736
+ class Param < ::String
737
+ #--{{{
738
+ include Casting
739
+
740
+ def self.pattr name
741
+ #--{{{
742
+ module_eval <<-code
743
+ def #{ name } *_a, &b
744
+ _a.push lambda{ instance_eval &b } if b
745
+ _a.size == 0 ? (defined? @#{ name } and @#{ name }) : (@#{ name }=_a.shift)
746
+ end
747
+ def #{ name }?
748
+ defined?(@#{ name }) and @#{ name }
749
+ end
750
+ def #{ name }= val
751
+ #{ name }( val )
752
+ end
753
+ code
754
+ #--}}}
755
+ end
756
+
757
+ %w(
758
+ this
759
+ rhs
760
+ required_argument
761
+ optional_argument
762
+ long
763
+ short
764
+ description
765
+ default
766
+ munge
767
+ cast
768
+ ).each{|a| pattr a}
769
+
770
+ alias_method 'desc', 'description'
771
+ alias_method 'synopsis', 'description'
772
+
773
+ {
774
+ 'requires_argument' => 'required_argument',
775
+ 'argument_required' => 'required_argument',
776
+ 'arg_required' => 'required_argument',
777
+ 'req_arg' => 'required_argument',
778
+ 'opt_arg' => 'optional_argument',
779
+ 'argument_optional' => 'optional_argument',
780
+ 'arg_optional' => 'optional_argument',
781
+ }.each{|dst,src| define_method(dst){ send src, true }}
782
+
783
+ def initialize name, this = nil, &block
784
+ #--{{{
785
+ if name.is_a?(Array)
786
+ a, b, ignored = name
787
+ a, b = a.to_s, b.to_s
788
+ name = a.size > b.size ? a : b
789
+ st = a.size > b.size ? b : a
790
+ short st
791
+ end
792
+
793
+ name = name.to_s
794
+ @this = this
795
+
796
+ re = %r/\s*=\s*([^=]+)\s*$/
797
+ m, @rhs = re.match(name).to_a
798
+ name.gsub! re, '' if @rhs
799
+ name.gsub! %r/^\s*--/, ''
800
+
801
+ super(name.to_s.gsub(%r/^\s*--/,''))
802
+ instance_eval &block if block
803
+ #--}}}
804
+ end
805
+ def long
806
+ #--{{{
807
+ @long ||= "--#{ self }"
808
+ #--}}}
809
+ end
810
+ def to_a
811
+ #--{{{
812
+ [ long, short, description ].compact
813
+ #--}}}
814
+ end
815
+ def to_option
816
+ #--{{{
817
+ optspec = []
818
+
819
+ if long
820
+ s = "#{ long }"
821
+ if required_argument
822
+ s << "=#{ self }"
823
+ elsif optional_argument
824
+ s << "=[#{ self }]"
825
+ elsif rhs
826
+ s << "=#{ rhs }" unless s[%r/=/]
827
+ end
828
+ optspec << s
829
+ end
830
+
831
+ if short
832
+ s = "#{ short }".strip.gsub(%r/^-*/,'-')
833
+ optspec << s
834
+ end
835
+
836
+ if description
837
+ s = "#{ description }"
838
+ optspec << s
839
+ end
840
+
841
+ optspec
842
+ #--}}}
843
+ end
844
+ def argument what = :required
845
+ #--{{{
846
+ case what.to_s
847
+ when /opt/i
848
+ send "optional_argument", true
849
+ when /req/i
850
+ send "required_argument", true
851
+ else
852
+ raise "what <#{ what }>"
853
+ end
854
+ #--}}}
855
+ end
856
+ alias_method 'arg', 'argument'
857
+ def cast &b
858
+ #--{{{
859
+ b and @cast = b
860
+ cast? ? @cast : nil
861
+ #--}}}
862
+ end
863
+ def cast?
864
+ #--{{{
865
+ defined? @cast
866
+ #--}}}
867
+ end
868
+ def param name # call up!
869
+ #--{{{
870
+ @this.param name
871
+ #--}}}
872
+ end
873
+ def value this = self
874
+ #--{{{
875
+ if defined? @default
876
+ @default.respond_to?('call') ? @default.call : @default
877
+ else
878
+ nil
879
+ end
880
+ #--}}}
881
+ end
882
+ #--}}}
883
+ end
884
+ #
885
+ # meta-hooks for param dsl
886
+ #
887
+ class << self
888
+ #--{{{
889
+ def params
890
+ #--{{{
891
+ @params ||= {}
892
+ #--}}}
893
+ end
894
+ alias_method "parms", "params"
895
+ def param name, this = self, &block
896
+ #--{{{
897
+ parm =
898
+ if block or name.is_a?(Array)
899
+ param = Param.new name, this, &block
900
+ option *param.to_option
901
+ default param => param.default
902
+ params[ param.to_s ] = param
903
+ else
904
+ name = name.to_s
905
+ defined = params[ name ]
906
+ if defined
907
+ defined
908
+ else
909
+ param = Param.new name, this, &block
910
+ option *param.to_option
911
+ default param => param.default
912
+ params[ param.to_s ] = param
913
+ end
914
+ end
915
+ parm.value
916
+ #--}}}
917
+ end
918
+ alias_method "parm", "param"
919
+ def get_param name
920
+ params[ name.to_s ]
921
+ end
922
+ #--}}}
923
+ end
924
+ def params
925
+ #--{{{
926
+ self.class.params
927
+ #--}}}
928
+ end
929
+ alias_method "parms", "params"
930
+ def param? name
931
+ #--{{{
932
+ key = name.to_s
933
+ options.has_key? key
934
+ #--}}}
935
+ end
936
+ def param name, *a
937
+ #--{{{
938
+ key = name.to_s
939
+ pm = params[key] or raise "no such param <#{ key }>!"
940
+
941
+ value =
942
+ if options.has_key? key
943
+ options[key] || default(key)
944
+ else
945
+ a.empty? ? default(key) : a.shift
946
+ end
947
+
948
+ #value = value.call if value.respond_to?('call')
949
+ value = instance_eval &value if Proc === value
950
+
951
+ #value = pm.cast[value] if(option?(key) and pm.cast?)
952
+ value = pm.cast[value] if(value and pm.cast?)
953
+
954
+ value
955
+ #--}}}
956
+ end
957
+ alias_method "parm", "param"
958
+
959
+
960
+ def self.main &block
961
+ define_method :main, &block
962
+ end
963
+ #--}}}
964
+ end # class AbstractMain
965
+
966
+ class SimpleMain < AbstractMain
967
+ end
968
+
969
+ class ConfigurableMain < AbstractMain
970
+ #--{{{
971
+ def self.class_initialize
972
+ #--{{{
973
+ super
974
+ class_attribute :config_default_path
975
+ class_attribute :config_search_path
976
+ class_attribute :configfile
977
+
978
+ config_default_path "#{ prognam }.conf"
979
+
980
+ config_search_path %w(. ~ /usr/local/etc /usr/etc /etc)
981
+
982
+ configfile Class::new(ALib::ConfigFile) #{ default = DATA; DATA.rewind; }
983
+
984
+ optspec << [ '--config=path', 'valid path - specify config file (default nil)' ]
985
+ optspec << [ '--template=[path]', 'valid path - generate a template config file in path (default stdout)' ]
986
+ #--}}}
987
+ end
988
+
989
+ attribute :config
990
+
991
+ def pre_parse_argv
992
+ #--{{{
993
+ super
994
+ if(@options.has_key?('template') or @argv.first =~ %r/template/i)
995
+ @argv.shift if @argv.first =~ %r/template/i
996
+ arg = @argv.first
997
+ gen_template @options['template'] || @options['config'] || arg
998
+ exit EXIT_SUCCESS
999
+ end
1000
+ #--}}}
1001
+ end
1002
+ def gen_template template
1003
+ #--{{{
1004
+ klass::configfile::gen_template(template)
1005
+ self
1006
+ #--}}}
1007
+ end
1008
+ def post_parse_argv
1009
+ #--{{{
1010
+ init_config
1011
+ #--}}}
1012
+ end
1013
+ def init_config opts = @options
1014
+ #--{{{
1015
+ conf = Util::getopt 'config', opts
1016
+ @config =
1017
+ if conf
1018
+ klass::configfile::new(conf)
1019
+ else
1020
+ klass::configfile::any klass::config_default_path, klass::config_search_path
1021
+ end
1022
+ @config
1023
+ #--}}}
1024
+ end
1025
+ #--}}}
1026
+ end
1027
+
1028
+ end
1029
+