alib 0.3.1 → 0.4.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,152 @@
1
+ #
2
+ # the logging module extends classes (both at instance and class level) with
3
+ # many methods useful for logging. it relies on the builtin Logger class
4
+ #
5
+ module ALib::Logging
6
+ #--{{{
7
+ #
8
+ # a module that adds an accessor to Logging objects in ored to fix a bug where
9
+ # not all logging devices are put into sync mode, resulting in improper log
10
+ # rolling. this is a hack.
11
+ #
12
+ module LoggerExt
13
+ #--{{{
14
+ attr :logdev
15
+ def << *args
16
+ args.flatten.each{|a| super a}
17
+ self
18
+ end
19
+ #--}}}
20
+ end # module LoggerExt
21
+ #
22
+ # implementations of the methods shared by both classes and objects of classes
23
+ # which include Logging
24
+ #
25
+ module LogMethods
26
+ #--{{{
27
+ def __logger_mutex
28
+ #--{{{
29
+ unless defined?(@__logger_mutex) and @__logger_mutex
30
+ begin
31
+ Thread.critical = true
32
+ @__logger_mutex = Sync::new unless defined?(@__logger_mutex) and @__logger_mutex
33
+ ensure
34
+ Thread.critical = false
35
+ end
36
+ end
37
+ @__logger_mutex
38
+ #--}}}
39
+ end
40
+ def __logger_sync
41
+ #--{{{
42
+ __logger_mutex.synchronize{ yield }
43
+ #--}}}
44
+ end
45
+ def logger
46
+ #--{{{
47
+ return @logger if defined?(@logger)
48
+ __logger_sync do
49
+ unless defined?(@logger)
50
+ klass = Class === self ? self : self::class
51
+ self.logger = klass::default_logger
52
+ end
53
+ end
54
+ @logger
55
+ #--}}}
56
+ end
57
+ def logger= log
58
+ #--{{{
59
+ __logger_sync do
60
+ @logger = log
61
+ @logger.extend LoggerExt
62
+ @logger.logdev.dev.sync = true rescue nil
63
+ @logger
64
+ end
65
+ #--}}}
66
+ end
67
+ %w( debug info warn error fatal ).each do |meth|
68
+ module_eval <<-code
69
+ def #{ meth }(*a, &b)
70
+ __logger_sync{ logger.#{ meth }(*a, &b) }
71
+ end
72
+ code
73
+ end
74
+ def log_err e
75
+ #--{{{
76
+ if logger.debug?
77
+ error{ errmsg e }
78
+ else
79
+ error{ emsg e }
80
+ end
81
+ #--}}}
82
+ end
83
+ def emsg e
84
+ #--{{{
85
+ "#{ e.message } - (#{ e.class })"
86
+ #--}}}
87
+ end
88
+ def btrace e
89
+ #--{{{
90
+ e.backtrace.join("\n")
91
+ #--}}}
92
+ end
93
+ def errmsg e
94
+ #--{{{
95
+ emsg(e) << "\n" << btrace(e)
96
+ #--}}}
97
+ end
98
+ #--}}}
99
+ end # module LogMethods
100
+
101
+ module LogClassMethods
102
+ #--{{{
103
+ def default_logger
104
+ #--{{{
105
+ return @default_logger if defined?(@default_logger)
106
+ __logger_sync do
107
+ unless defined?(@default_logger)
108
+ self.default_logger = Logger.new STDERR
109
+ # @default_logger.warn{ "<#{ self }> using default logger"}
110
+ end
111
+ end
112
+ @default_logger
113
+ #--}}}
114
+ end
115
+ def default_logger= log
116
+ #--{{{
117
+ __logger_sync do
118
+ @default_logger = (Logger === log ? log : Logger::new(log))
119
+ @default_logger.extend LoggerExt
120
+ @default_logger.logdev.dev.sync = true rescue nil
121
+ @default_logger
122
+ end
123
+ #--}}}
124
+ end
125
+ #--}}}
126
+ end
127
+
128
+ EOL = "\n"
129
+ DIV0 = ("." * 79) << EOL
130
+ DIV1 = ("-" * 79) << EOL
131
+ DIV2 = ("=" * 79) << EOL
132
+ DIV3 = ("#" * 79) << EOL
133
+ SEC0 = ("." * 16) << EOL
134
+ SEC1 = ("-" * 16) << EOL
135
+ SEC2 = ("=" * 16) << EOL
136
+ SEC3 = ("#" * 16) << EOL
137
+
138
+ class << self
139
+ #--{{{
140
+ def append_features c
141
+ #--{{{
142
+ ret = super
143
+ c.extend LogMethods
144
+ c.extend LogClassMethods
145
+ ret
146
+ #--}}}
147
+ end
148
+ #--}}}
149
+ end
150
+ include LogMethods
151
+ #--}}}
152
+ end # module Logging
@@ -0,0 +1,594 @@
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
+ EXIT_SUCCESS = 0
14
+ EXIT_PSEUDO_SUCCESS = 42
15
+ EXIT_FAILURE = 1
16
+
17
+ class << self
18
+ #--{{{
19
+ def stringlist(*list)
20
+ #--{{{
21
+ list.flatten.compact.map{|item| "#{ item }".strip}
22
+ #--}}}
23
+ end
24
+ def instance_attributes(*names)
25
+ #--{{{
26
+ names = stringlist names
27
+ names.each do |name|
28
+ getter = "#{ name }"
29
+ setter = "#{ name }="
30
+ code = <<-code
31
+ def #{ name }(*a)
32
+ unless a.empty?
33
+ self.#{ name }= a.shift
34
+ else
35
+ @#{ name }
36
+ end
37
+ end
38
+ def #{ name }= value
39
+ @#{ name } = value
40
+ end
41
+ alias #{ name }? #{ name }
42
+ code
43
+ module_eval code
44
+ end
45
+ #--}}}
46
+ end
47
+ alias instance_attribute instance_attributes
48
+ alias i_attrs instance_attributes
49
+ alias i_attr instance_attributes
50
+ alias attribute instance_attributes
51
+ alias attributes instance_attributes
52
+
53
+ def class_attributes(*names)
54
+ #--{{{
55
+ names = stringlist names
56
+ names.each do |name|
57
+ getter = "#{ name }"
58
+ setter = "#{ name }="
59
+ code = <<-code
60
+ class << self
61
+ def #{ name }(*a)
62
+ unless a.empty?
63
+ self.#{ name }= a.shift
64
+ else
65
+ @#{ name }
66
+ end
67
+ end
68
+ def #{ name }= value
69
+ @#{ name } = value
70
+ end
71
+ alias #{ name }? #{ name }
72
+ end
73
+ code
74
+ module_eval code
75
+ end
76
+ #--}}}
77
+ end
78
+ alias class_attribute class_attributes
79
+ alias c_attrs class_attributes
80
+ alias c_attr class_attributes
81
+
82
+ %w( version author program optspec ).each do |meth|
83
+ #--{{{
84
+ module_eval <<-code
85
+ def #{ meth }(*a)
86
+ unless a.empty?
87
+ self.#{ meth }= a.shift
88
+ else
89
+ @#{ meth }
90
+ end
91
+ end
92
+ def #{ meth }= value
93
+ @#{ meth } = value
94
+ end
95
+ def #{ meth }?
96
+ defined? @#{ meth } and @#{ meth }
97
+ end
98
+ code
99
+ #--}}}
100
+ end
101
+ alias prognam program
102
+ alias prognam= program=
103
+ alias prognam? program?
104
+
105
+ %w( usage examples ).each do |meth|
106
+ #--{{{
107
+ module_eval <<-code
108
+ def #{ meth }(*a)
109
+ unless a.empty?
110
+ self.#{ meth }= a.shift
111
+ else
112
+ @#{ meth }
113
+ end
114
+ end
115
+ def #{ meth }= msg
116
+ @#{ meth } = unindent_block msg.to_s
117
+ end
118
+ def #{ meth }?
119
+ defined? @#{ meth } and @#{ meth }
120
+ end
121
+ code
122
+ #--}}}
123
+ end
124
+
125
+
126
+ def unindent_block buf
127
+ #--{{{
128
+ buf = "#{ buf }"
129
+ lines = buf.to_a
130
+ indent_pat = %r/^\s*[\s|]/
131
+ indent = lines.first[ indent_pat ] rescue nil
132
+ if indent
133
+ lines.map do |line|
134
+ line[ indent.size..-1 ] || "\n"
135
+ end.join
136
+ else
137
+ buf
138
+ end
139
+ #--}}}
140
+ end
141
+
142
+ def options(*list)
143
+ #--{{{
144
+ @optspec ||= []
145
+ return @optspec if list.empty?
146
+ list = [list] unless Array === list.first
147
+ list.each{|spec| (@optspec ||= []) << spec}
148
+ #--}}}
149
+ end
150
+ alias option options
151
+
152
+ def required_arguments(*list)
153
+ #--{{{
154
+ @required_arguments ||= []
155
+ list.flatten.each do |arg|
156
+ return(optional_argument(arg)) if Hash === arg
157
+ unless instance_methods.include? "#{ arg }"
158
+ module_eval <<-code
159
+ def #{ arg }(*__list)
160
+ if __list.empty?
161
+ @#{ arg }
162
+ else
163
+ send('#{ arg }=', *__list)
164
+ end
165
+ end
166
+ def #{ arg }=(__arg, *__list)
167
+ if __list.empty?
168
+ @#{ arg } = __arg
169
+ else
170
+ @#{ arg } = ([__arg] + __list)
171
+ end
172
+ end
173
+ def #{ arg }?
174
+ defined? @#{ arg } and @#{ arg }
175
+ end
176
+ code
177
+ end
178
+ @required_arguments << "#{ arg }"
179
+ end
180
+ @required_arguments
181
+ #--}}}
182
+ end
183
+ alias required_argument required_arguments
184
+ alias arguments required_arguments
185
+ alias argument required_arguments
186
+
187
+ def optional_arguments(*list)
188
+ #--{{{
189
+ @optional_arguments ||= []
190
+ list.flatten.each do |arg|
191
+ arg, default =
192
+ case arg
193
+ when Hash
194
+ arg.to_a.first
195
+ else
196
+ [arg, nil]
197
+ end
198
+ @do_not_gc ||= []
199
+ @do_not_gc << default unless @do_not_gc.include? default
200
+ unless instance_methods.include? "#{ arg }"
201
+ module_eval <<-code
202
+ def #{ arg }(*__list)
203
+ unless @#{ arg }
204
+ @#{ arg } = ObjectSpace::_id2ref #{ default.object_id }
205
+ end
206
+ if __list.empty?
207
+ @#{ arg }
208
+ else
209
+ send('#{ arg }=', *__list)
210
+ end
211
+ end
212
+ def #{ arg }=(__arg, *__list)
213
+ if __list.empty?
214
+ @#{ arg } = __arg
215
+ else
216
+ @#{ arg } = ([__arg] + __list)
217
+ end
218
+ end
219
+ def #{ arg }?
220
+ defined? @#{ arg } and @#{ arg }
221
+ end
222
+ code
223
+ end
224
+ @optional_arguments << "#{ arg }"
225
+ end
226
+ @optional_arguments
227
+ #--}}}
228
+ end
229
+ alias optional_argument optional_arguments
230
+
231
+ def class_initialize
232
+ #--{{{
233
+ version '0.0.0'
234
+
235
+ author 'ara.t.howard@noaa.gov'
236
+
237
+ program File.basename($0)
238
+
239
+ optspec [
240
+ [ '--help', '-h', 'this message' ],
241
+ [ '--log=path','-l', 'set log file - (default stderr)' ],
242
+ [ '--verbosity=verbostiy', '-v', '0|fatal < 1|error < 2|warn < 3|info < 4|debug - (default info)' ],
243
+ ]
244
+
245
+ usage <<-usage
246
+ NAME
247
+ #{ program } v#{ version }
248
+
249
+ SYNOPSIS
250
+ #{ program } [options]+ [file]+
251
+ usage
252
+
253
+ examples ''
254
+ #--}}}
255
+ end
256
+
257
+ def inherited klass
258
+ #--{{{
259
+ ret = super
260
+ klass.class_initialize
261
+ ret
262
+ #--}}}
263
+ end
264
+
265
+ def run(*a, &b)
266
+ #--{{{
267
+ new(*a, &b).run
268
+ #--}}}
269
+ end
270
+ #--}}}
271
+ end
272
+
273
+ class_initialize
274
+
275
+ attr :logger
276
+ attr :program
277
+ attr :argv
278
+ attr :env
279
+ attr :cmdline
280
+ attr :console
281
+ attr :options
282
+ attr :listoptions
283
+ attr :op
284
+ attr :logdev
285
+ attr :verbosity
286
+ alias console? console
287
+
288
+ def initialize argv = ARGV, env = ENV
289
+ #--{{{
290
+ @argv = Util::mcp(argv.to_a)
291
+ @env = Util::mcp(env.to_hash)
292
+ @program = File::expand_path $0
293
+ @cmdline = ([@program] + @argv).join ' '
294
+ @console = STDIN.tty?
295
+ #--}}}
296
+ end
297
+ def klass; self.class; end
298
+ def required_arguments
299
+ #--{{{
300
+ klass::required_arguments
301
+ #--}}}
302
+ end
303
+ def optional_arguments
304
+ #--{{{
305
+ klass::optional_arguments
306
+ #--}}}
307
+ end
308
+ def run
309
+ #--{{{
310
+ logcatch do
311
+ begin
312
+ pre_run
313
+ pre_parse_options
314
+ parse_options
315
+ post_parse_options
316
+ pre_parse_argv
317
+ parse_argv
318
+ post_parse_argv
319
+ init_logging
320
+ pre_main
321
+ status = main
322
+ post_main
323
+ post_run
324
+ exit(status ? EXIT_SUCCESS : EXIT_FAILURE)
325
+ rescue Errno::EPIPE
326
+ STDOUT.tty? ? raise : exit(EXIT_FAILURE)
327
+ end
328
+ end
329
+ #--}}}
330
+ end
331
+ def pre_parse_options; end
332
+ def post_parse_options; end
333
+ def pre_parse_argv
334
+ #--{{{
335
+ if(@options.has_key?('help') or (@argv.size == 1 and @argv.first =~ %r/help/i))
336
+ usage STDOUT
337
+ exit EXIT_SUCCESS
338
+ end
339
+ if(@options.has_key?('version') or @argv.first =~ %r/version/i)
340
+ STDOUT.puts self.class.version
341
+ exit EXIT_SUCCESS
342
+ end
343
+ #--}}}
344
+ end
345
+ def post_parse_argv; end
346
+ def pre_run; end
347
+ def post_run; end
348
+ def pre_main; end
349
+ def post_main; end
350
+ def logcatch
351
+ #--{{{
352
+ ret = nil
353
+ @logger ||= Logger::new STDERR
354
+ begin
355
+ ret = yield
356
+ rescue Exception => e
357
+ unless SystemExit === e
358
+ fatal{ e }
359
+ exit EXIT_FAILURE
360
+ if false
361
+ if logger.debug?
362
+ fatal{ e }
363
+ exit EXIT_FAILURE
364
+ else
365
+ fatal{ emsg(e) }
366
+ exit EXIT_FAILURE
367
+ end
368
+ end
369
+ else
370
+ exit e.status
371
+ end
372
+ end
373
+ ret
374
+ #--}}}
375
+ end
376
+ def usage port = STDERR
377
+ #--{{{
378
+ port << klass::usage << "\n" if(klass::usage and not klass::usage.empty?)
379
+ port << klass::examples << "\n" if(klass::examples and not klass::examples.empty?)
380
+
381
+ if klass::optspec
382
+ port << 'OPTIONS' << "\n"
383
+
384
+ klass::optspec.each do |os|
385
+ a, b, c = os
386
+ long, short, desc = nil
387
+ [a,b,c].each do |word|
388
+ next unless word
389
+ word.strip!
390
+ case word
391
+ when %r/^--[^-]/o
392
+ long = word
393
+ when %r/^-[^-]/o
394
+ short = word
395
+ else
396
+ desc = word
397
+ end
398
+ end
399
+
400
+ spec = ((long and short) ? [long, short] : [long])
401
+
402
+ if spec
403
+ port << Util::columnize(spec.join(', '), :width => 80, :indent => 2)
404
+ port << "\n"
405
+ end
406
+
407
+ if desc
408
+ port << Util::columnize(desc, :width => 80, :indent => 8)
409
+ port << "\n"
410
+ end
411
+ end
412
+
413
+ port << "\n"
414
+ end
415
+
416
+ port
417
+ #--}}}
418
+ end
419
+ def parse_options
420
+ #--{{{
421
+ @op = OptionParser::new
422
+ @options = {}
423
+ @listoptions = Hash::new{|h,k| h[k] = []}
424
+ klass::optspec.each do |spec|
425
+ k = spec.first.gsub(%r/(?:--)|(?:=.*$)|(?:\s+)/o,'')
426
+ @op.def_option(*spec) do |v|
427
+ @options[k] = v
428
+ @listoptions[k] << v
429
+ end
430
+ end
431
+ begin
432
+ op.parse! @argv
433
+ rescue OptionParser::InvalidOption => e
434
+ # preverve unknown options
435
+ #e.recover(argv)
436
+ invalid_option e
437
+ end
438
+ @options
439
+ #--}}}
440
+ end
441
+ def invalid_option e
442
+ #--{{{
443
+ # e.recover argv
444
+ fatal{ e.to_s }
445
+ exit EXIT_FAILURE
446
+ #--}}}
447
+ end
448
+ def init_logging opts = @options
449
+ #--{{{
450
+ log = Util::getopt 'log', opts
451
+ log_age = Util::getopt 'log_age', opts
452
+ log_size = Util::getopt 'log_size', opts
453
+ verbosity = Util::getopt 'verbosity', opts
454
+ log_age = Integer log_age rescue nil
455
+ log_size = Integer log_size rescue nil
456
+ $logger = @logger = Logger::new(log || STDERR, log_age, log_size)
457
+ #
458
+ # hack to fix Logger sync bug
459
+ #
460
+ begin
461
+ class << @logger; attr :logdev unless @logger.respond_to?(:logdev); end
462
+ @logdev = @logger.logdev.dev
463
+ @logdev.sync = true
464
+ rescue
465
+ nil
466
+ end
467
+ level = nil
468
+ verbosity ||= 'info'
469
+ verbosity =
470
+ case verbosity
471
+ when /^\s*(?:4|d|debug)\s*$/io
472
+ level = 'Logging::DEBUG'
473
+ 4
474
+ when /^\s*(?:3|i|info)\s*$/io
475
+ level = 'Logging::INFO'
476
+ 3
477
+ when /^\s*(?:2|w|warn)\s*$/io
478
+ level = 'Logging::WARN'
479
+ 2
480
+ when /^\s*(?:1|e|error)\s*$/io
481
+ level = 'Logging::ERROR'
482
+ 1
483
+ when /^\s*(?:0|f|fatal)\s*$/io
484
+ level = 'Logging::FATAL'
485
+ 0
486
+ else
487
+ abort "illegal verbosity setting <#{ verbosity }>"
488
+ end
489
+ @logger.level = 2 - ((verbosity % 5) - 2)
490
+ @logger
491
+ #--}}}
492
+ end
493
+ def parse_argv
494
+ #--{{{
495
+ a, b = [], []
496
+ klass::required_arguments.each do |arg|
497
+ value = @argv.shift
498
+ if value
499
+ send "#{ arg }=", value
500
+ else
501
+ die 'msg' => "required_argument <#{ arg }> not given"
502
+ end
503
+ a << send("#{ arg }")
504
+ end
505
+ klass::optional_arguments.each do |arg|
506
+ value = @argv.shift
507
+ if value
508
+ send "#{ arg }=", value
509
+ end
510
+ b << send("#{ arg }")
511
+ end
512
+ [a, b, @argv]
513
+ #--}}}
514
+ end
515
+ def die opts = {}
516
+ #--{{{
517
+ msg = Util::getopt 'msg', opts, klass.usage
518
+ errno = Util::getopt 'errno', opts, EXIT_FAILURE
519
+ STDERR.puts("#{ msg }")
520
+ exit(Integer(errno))
521
+ #--}}}
522
+ end
523
+ def main
524
+ #--{{{
525
+ raise NotImplementedError, 'main'
526
+ #--}}}
527
+ end
528
+ #--}}}
529
+ end # class AbstractMain
530
+
531
+ class SimpleMain < AbstractMain
532
+ end
533
+
534
+ class ConfigurableMain < AbstractMain
535
+ #--{{{
536
+ def self.class_initialize
537
+ #--{{{
538
+ super
539
+ class_attribute :config_default_path
540
+ class_attribute :config_search_path
541
+ class_attribute :configfile
542
+
543
+ config_default_path "#{ prognam }.conf"
544
+
545
+ config_search_path %w(. ~ /usr/local/etc /usr/etc /etc)
546
+
547
+ configfile Class::new(ALib::ConfigFile) #{ default = DATA; DATA.rewind; }
548
+
549
+ optspec << [ '--config=path', 'valid path - specify config file (default nil)' ]
550
+ optspec << [ '--template=[path]', 'valid path - generate a template config file in path (default stdout)' ]
551
+ #--}}}
552
+ end
553
+
554
+ attribute :config
555
+
556
+ def pre_parse_argv
557
+ #--{{{
558
+ super
559
+ if(@options.has_key?('template') or @argv.first =~ %r/template/i)
560
+ @argv.shift if @argv.first =~ %r/template/i
561
+ arg = @argv.first
562
+ gen_template @options['template'] || @options['config'] || arg
563
+ exit EXIT_SUCCESS
564
+ end
565
+ #--}}}
566
+ end
567
+ def gen_template template
568
+ #--{{{
569
+ klass::configfile::gen_template(template)
570
+ self
571
+ #--}}}
572
+ end
573
+ def post_parse_argv
574
+ #--{{{
575
+ init_config
576
+ #--}}}
577
+ end
578
+ def init_config opts = @options
579
+ #--{{{
580
+ conf = Util::getopt 'config', opts
581
+ @config =
582
+ if conf
583
+ klass::configfile::new(conf)
584
+ else
585
+ klass::configfile::any klass::config_default_path, klass::config_search_path
586
+ end
587
+ @config
588
+ #--}}}
589
+ end
590
+ #--}}}
591
+ end
592
+
593
+ end
594
+