alib 0.3.1 → 0.4.0

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