ThiagoLelis-backgroundjob 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/main.rb ADDED
@@ -0,0 +1,60 @@
1
+ module Main
2
+ #
3
+ # top level constants
4
+ #
5
+ Main::VERSION = '2.8.2' unless
6
+ defined? Main::VERSION
7
+ def self.version() Main::VERSION end
8
+
9
+ Main::LIBDIR = File.join(File.dirname(File.expand_path(__FILE__)), self.name.downcase, '') unless
10
+ defined? Main::LIBDIR
11
+ def self.libdir() Main::LIBDIR end
12
+
13
+ Main::EXIT_SUCCESS = 0 unless defined? Main::EXIT_SUCCESS
14
+ Main::EXIT_FAILURE = 1 unless defined? Main::EXIT_FAILURE
15
+ Main::EXIT_WARN = 42 unless defined? Main::EXIT_WARN
16
+ #
17
+ # built-in
18
+ #
19
+ require 'logger'
20
+ require 'enumerator'
21
+ require 'set'
22
+ #
23
+ # use gems to pick up dependancies
24
+ #
25
+ begin
26
+ require 'rubygems'
27
+ rescue LoadError
28
+ 42
29
+ end
30
+
31
+ require 'fattr'
32
+ begin
33
+ version = Fattr.version
34
+ raise unless version[%r/^1\./]
35
+ rescue
36
+ abort "main requires fattrs >= 1.0.3 - gem install fattr"
37
+ end
38
+
39
+ require 'arrayfields'
40
+ begin
41
+ version = Arrayfields.version
42
+ raise unless version[%r/^4\./]
43
+ rescue
44
+ abort "main requires arrayfields >= 4.5.0 - gem install arrayfields"
45
+ end
46
+ #
47
+ # main's own libs
48
+ #
49
+ require libdir + 'stdext'
50
+ require libdir + 'softspoken'
51
+ require libdir + 'util'
52
+ require libdir + 'logger'
53
+ require libdir + 'usage'
54
+ require libdir + 'cast'
55
+ require libdir + 'parameter'
56
+ require libdir + 'getoptlong'
57
+ require libdir + 'mode'
58
+ require libdir + 'base'
59
+ require libdir + 'factories'
60
+ end
data/lib/main/base.rb ADDED
@@ -0,0 +1,515 @@
1
+ module Main
2
+ class Base
3
+ class << self
4
+ def wrap_run!
5
+ const_set :RUN, instance_method(:run)
6
+
7
+ class_eval do
8
+ def run *a, &b
9
+ argv.push "--#{ argv.shift }" if argv.first == 'help'
10
+ return mode_run! if mode_given?
11
+
12
+ status =
13
+ catch :exit do
14
+ begin
15
+
16
+ parse_parameters
17
+
18
+ if params['help'] and params['help'].given?
19
+ print usage.to_s
20
+ exit
21
+ end
22
+
23
+ pre_run
24
+ before_run
25
+ self.class.const_get(:RUN).bind(self).call(*a, &b)
26
+ after_run
27
+ post_run
28
+
29
+ finalize
30
+ rescue Exception => e
31
+ handle_exception e
32
+ end
33
+ nil
34
+ end
35
+
36
+ handle_throw status
37
+ end
38
+ end
39
+ end
40
+
41
+ def method_added m
42
+ return if @in_method_added
43
+ super if defined? super
44
+ @in_method_added = true
45
+ begin
46
+ wrap_run! if m.to_s == 'run'
47
+ ensure
48
+ @in_method_added = false
49
+ end
50
+ end
51
+
52
+ def self.inheritable_fattr name, &block
53
+ block ||= lambda{}
54
+ fattr( name ){
55
+ catch :value do
56
+ if parent?
57
+ value = parent.send name
58
+ value =
59
+ begin
60
+ Util.mcp value
61
+ rescue
62
+ value.clone rescue value.dup
63
+ end
64
+ throw :value, value
65
+ end
66
+ instance_eval &block
67
+ end
68
+ }
69
+ end
70
+
71
+ # fattrs
72
+ fattr( 'name' ){ File.basename $0 }
73
+ fattr( 'synopsis' ){ Usage.default_synopsis(self) }
74
+ fattr( 'description' )
75
+ fattr( 'usage' ){ Usage.default_usage self }
76
+ fattr( 'modes' ){ Mode.list }
77
+ fattr( 'mode_definitions' ){ Array.new }
78
+ fattr( 'mode_name' ){ 'main' }
79
+ fattr( 'parent' ){ nil }
80
+ fattr( 'children' ){ Set.new }
81
+
82
+ fattr( 'program' ){ File.basename $0 }
83
+ fattr( 'author' )
84
+ fattr( 'version' )
85
+ fattr( 'stdin' ){ $stdin }
86
+ fattr( 'stdout' ){ $stdout }
87
+ fattr( 'stderr' ){ $stderr }
88
+ fattr( 'logger' ){ stderr }
89
+ fattr( 'logger_level' ){ Logger::INFO }
90
+ fattr( 'exit_status' ){ EXIT_SUCCESS }
91
+ fattr( 'exit_success' ){ EXIT_SUCCESS }
92
+ fattr( 'exit_failure' ){ EXIT_FAILURE }
93
+ fattr( 'exit_warn' ){ EXIT_WARN }
94
+ inheritable_fattr( 'parameters' ){ Parameter::List[] }
95
+ inheritable_fattr( 'can_has_hash' ){ Hash.new }
96
+ inheritable_fattr( 'mixin_table' ){ Hash.new }
97
+
98
+ # override a few fattrs
99
+ def mode_name=(value)
100
+ @mode_name = Mode.new value
101
+ end
102
+
103
+ def usage *argv, &block
104
+ usage! unless defined? @usage
105
+ return @usage if argv.empty? and block.nil?
106
+ key, value, *ignored = argv
107
+ value = block.call if block
108
+ @usage[key.to_s] = value.to_s
109
+ end
110
+
111
+ def create parent = Base, *a, &b
112
+ Class.new parent do |child|
113
+ child.parent = parent unless parent == Base
114
+ parent.children.add child
115
+ child.context do
116
+ child.class_eval &b if b
117
+ child.default_options!
118
+ #child.wrap_run! unless child.const_defined?(:RUN)
119
+ mode_definitions.each do |name, block|
120
+ klass =
121
+ create context do
122
+ mode_name name.to_s
123
+ module_eval &block if block
124
+ end
125
+ modes.add klass
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ def context &block
132
+ @@context ||= []
133
+ unless block
134
+ @@context.last
135
+ else
136
+ begin
137
+ @@context.push self
138
+ block.call @@context.last
139
+ ensure
140
+ @@context.pop
141
+ end
142
+ end
143
+ end
144
+
145
+ module ::Main
146
+ singleton_class{
147
+ def current
148
+ ::Main::Base.context
149
+ end
150
+ }
151
+ end
152
+
153
+ def fully_qualified_mode
154
+ list = []
155
+ ancestors.each do |ancestor|
156
+ break unless ancestor < Base
157
+ list << ancestor.mode_name
158
+ end
159
+ list.reverse[1..-1]
160
+ end
161
+
162
+ def run(&b) define_method(:run, &b) end
163
+
164
+ def new(*a, &b)
165
+ allocate.instance_eval do
166
+ pre_initialize
167
+ before_initialize
168
+ main_initialize *a, &b
169
+ initialize
170
+ after_initialize
171
+ post_initialize
172
+ self
173
+ end
174
+ end
175
+ end
176
+
177
+ module DSL
178
+ def parameter *a, &b
179
+ (parameters << Parameter.create(:parameter, *a, &b)).last
180
+ end
181
+
182
+ def option *a, &b
183
+ (parameters << Parameter.create(:option, *a, &b)).last
184
+ end
185
+ alias_method 'opt', 'option'
186
+ alias_method 'switch', 'option'
187
+
188
+ def default_options!
189
+ option 'help', 'h' unless parameters.has_option?('help', 'h')
190
+ end
191
+
192
+ def argument *a, &b
193
+ (parameters << Parameter.create(:argument, *a, &b)).last
194
+ end
195
+ alias_method 'arg', 'argument'
196
+
197
+ def keyword *a, &b
198
+ (parameters << Parameter.create(:keyword, *a, &b)).last
199
+ end
200
+ alias_method 'kw', 'keyword'
201
+
202
+ def environment *a, &b
203
+ (parameters << Parameter.create(:environment, *a, &b)).last
204
+ end
205
+ alias_method 'env', 'environment'
206
+
207
+ =begin
208
+ def mode name, &b
209
+ klass =
210
+ create context do
211
+ mode_name name.to_s
212
+ module_eval &b if b
213
+ end
214
+ modes.add klass
215
+ end
216
+ =end
217
+
218
+ def mode name, &b
219
+ mode_definitions << [name, b]
220
+ end
221
+
222
+ def can_has ptype, *a, &b
223
+ key = a.map{|s| s.to_s}.sort_by{|s| -s.size }.first
224
+ can_has_hash.update key => [ptype, a, b]
225
+ key
226
+ end
227
+
228
+ def has key, *keys
229
+ keys = [key, *keys].flatten.compact.map{|k| k.to_s}
230
+ keys.map do |key|
231
+ ptype, a, b = can_has_hash[key]
232
+ abort "yo - can *not* has #{ key.inspect }!?" unless(ptype and a and b)
233
+ send ptype, *a, &b
234
+ key
235
+ end
236
+ end
237
+
238
+ def mixin name, *names, &block
239
+ names = [name, *names].flatten.compact.map{|name| name.to_s}
240
+ if block
241
+ names.each do |name|
242
+ mixin_table[name] = block
243
+ end
244
+ else
245
+ names.each do |name|
246
+ module_eval &mixin_table[name]
247
+ end
248
+ end
249
+ end
250
+
251
+ ## TODO - for some reason these hork the usage!
252
+
253
+ %w[ examples samples api ].each do |chunkname|
254
+ module_eval <<-code
255
+ def #{ chunkname } *a, &b
256
+ txt = b ? b.call : a.join("\\n")
257
+ usage['#{ chunkname }'] = txt
258
+ end
259
+ code
260
+ end
261
+ alias_method 'example', 'examples'
262
+ alias_method 'sample', 'samples'
263
+ end
264
+ extend DSL
265
+
266
+ fattr 'argv'
267
+ fattr 'env'
268
+ fattr 'params'
269
+ fattr 'logger'
270
+ fattr 'stdin'
271
+ fattr 'stdout'
272
+ fattr 'stderr'
273
+
274
+ %w(
275
+ program name synopsis description author version
276
+ exit_status exit_success exit_failure exit_warn
277
+ logger_level
278
+ usage
279
+ ).each{|a| fattr(a){ self.class.send a}}
280
+
281
+ %w( parameters param ).each do |dst|
282
+ alias_method "#{ dst }", "params"
283
+ alias_method "#{ dst }=", "params="
284
+ alias_method "#{ dst }?", "params?"
285
+ end
286
+
287
+ %w( debug info warn fatal error ).each do |m|
288
+ module_eval <<-code
289
+ def #{ m } *a, &b
290
+ logger.#{ m } *a, &b
291
+ end
292
+ code
293
+ end
294
+
295
+ =begin
296
+ =end
297
+ def pre_initialize() :hook end
298
+ def before_initialize() :hook end
299
+ def main_initialize argv = ARGV, env = ENV, opts = {}
300
+ @argv, @env, @opts = argv, env, opts
301
+ setup_finalizers
302
+ setup_io_restoration
303
+ setup_io_redirection
304
+ setup_logging
305
+ end
306
+ def initialize() :hook end
307
+ def after_initialize() :hook end
308
+ def post_initialize() :hook end
309
+
310
+ def setup_finalizers
311
+ @finalizers = finalizers = []
312
+ ObjectSpace.define_finalizer(self) do
313
+ while((f = finalizers.pop)); f.call; end
314
+ end
315
+ end
316
+
317
+ def finalize
318
+ while((f = @finalizers.pop)); f.call; end
319
+ end
320
+
321
+ def setup_io_redirection
322
+ self.stdin = @opts['stdin'] || @opts[:stdin] || self.class.stdin
323
+ self.stdout = @opts['stdout'] || @opts[:stdout] || self.class.stdout
324
+ self.stderr = @opts['stderr'] || @opts[:stderr] || self.class.stderr
325
+ end
326
+
327
+ def setup_logging
328
+ log = self.class.logger || stderr
329
+ self.logger = log
330
+ end
331
+ def logger= log
332
+ unless(defined?(@logger) and @logger == log)
333
+ case log
334
+ when ::Logger, Logger
335
+ @logger = log
336
+ when IO, StringIO
337
+ @logger = Logger.new log
338
+ @logger.level = logger_level
339
+ else
340
+ @logger = Logger.new *log
341
+ @logger.level = logger_level
342
+ end
343
+ end
344
+ @logger
345
+ end
346
+
347
+ def setup_io_restoration
348
+ [STDIN, STDOUT, STDERR].each do |io|
349
+ dup = io.dup and @finalizers.push lambda{ io.reopen dup }
350
+ end
351
+ end
352
+
353
+ def stdin= io
354
+ unless(defined?(@stdin) and (@stdin == io))
355
+ @stdin =
356
+ if io.respond_to? 'read'
357
+ io
358
+ else
359
+ fd = open io.to_s, 'r+'
360
+ @finalizers.push lambda{ fd.close }
361
+ fd
362
+ end
363
+ begin
364
+ STDIN.reopen @stdin
365
+ rescue
366
+ $stdin = @stdin
367
+ ::Object.const_set 'STDIN', @stdin
368
+ end
369
+ end
370
+ end
371
+
372
+ def stdout= io
373
+ unless(defined?(@stdout) and (@stdout == io))
374
+ @stdout =
375
+ if io.respond_to? 'write'
376
+ io
377
+ else
378
+ fd = open io.to_s, 'w+'
379
+ @finalizers.push lambda{ fd.close }
380
+ fd
381
+ end
382
+ STDOUT.reopen @stdout rescue($stdout = @stdout)
383
+ end
384
+ end
385
+
386
+ def stderr= io
387
+ unless(defined?(@stderr) and (@stderr == io))
388
+ @stderr =
389
+ if io.respond_to? 'write'
390
+ io
391
+ else
392
+ fd = open io.to_s, 'w+'
393
+ @finalizers.push lambda{ fd.close }
394
+ fd
395
+ end
396
+ STDERR.reopen @stderr rescue($stderr = @stderr)
397
+ end
398
+ end
399
+
400
+ def pre_parse_parameters() :hook end
401
+ def before_parse_parameters() :hook end
402
+ def parse_parameters
403
+ pre_parse_parameters
404
+
405
+ self.class.parameters.parse @argv, @env
406
+ @params = Parameter::Table.new
407
+ self.class.parameters.each{|p| @params[p.name.to_s] = p}
408
+
409
+ post_parse_parameters
410
+ end
411
+ def after_parse_parameters() :hook end
412
+ def post_parse_parameters() :hook end
413
+
414
+ def pre_run() :hook end
415
+ def before_run() :hook end
416
+ def run
417
+ raise NotImplementedError, 'run not defined'
418
+ end
419
+ def after_run() :hook end
420
+ def post_run() :hook end
421
+
422
+ def mode_given?
423
+ begin
424
+ modes.size > 0 and
425
+ argv.size > 0 and
426
+ modes.find_by_mode(argv.first)
427
+ rescue Mode::Ambiguous
428
+ true
429
+ end
430
+ end
431
+ def mode_run!
432
+ mode = modes.find_by_mode argv.shift
433
+ klass = modes[mode] or abort "bad mode <#{ mode }>"
434
+ main = klass.new @argv, @env, @opts
435
+ main.mode = mode
436
+ main.run
437
+ end
438
+ def modes
439
+ self.class.modes
440
+ end
441
+ fattr 'mode'
442
+
443
+ def help! status = 0
444
+ print usage.to_s
445
+ exit(status)
446
+ end
447
+
448
+ def abort message = 'exit'
449
+ raise SystemExit.new(message)
450
+ end
451
+
452
+ def handle_exception e
453
+ if e.respond_to?(:error_handler_before)
454
+ fcall(e, :error_handler_before, self)
455
+ end
456
+
457
+ if e.respond_to?(:error_handler_instead)
458
+ fcall(e, :error_handler_instead, self)
459
+ else
460
+ if e.respond_to? :status
461
+ exit_status(( e.status ))
462
+ end
463
+
464
+ if Softspoken === e or SystemExit === e
465
+ quiet = ((SystemExit === e and e.message.respond_to?('abort')) or # see main/stdext.rb
466
+ (SystemExit === e and e.message == 'exit'))
467
+ stderr.puts e.message unless quiet
468
+ else
469
+ fatal{ e }
470
+ end
471
+ end
472
+
473
+ if e.respond_to?(:error_handler_after)
474
+ fcall(e, :error_handler_after, self)
475
+ end
476
+
477
+ exit_status(( exit_failure )) if exit_status == exit_success
478
+ exit_status(( Integer(exit_status) rescue(exit_status ? 0 : 1) ))
479
+ exit exit_status
480
+ end
481
+
482
+ def fcall obj, m, *argv, &block
483
+ m = obj.method m
484
+ arity = m.arity
485
+ if arity >= 0
486
+ argv = argv[0, arity]
487
+ else
488
+ arity = arity.abs - 1
489
+ argv = argv[0, arity] + argv[arity .. -1]
490
+ end
491
+ m.call *argv, &block
492
+ end
493
+
494
+ def handle_throw status
495
+ exit(( Integer(status) rescue 0 ))
496
+ end
497
+
498
+ %w[ before instead after ].each do |which|
499
+ module_eval <<-code
500
+ def error_handler_#{ which } *argv, &block
501
+ block.call *argv
502
+ end
503
+ code
504
+ end
505
+
506
+ def instance_eval_block *argv, &block
507
+ sc =
508
+ class << self
509
+ self
510
+ end
511
+ sc.module_eval{ define_method '__instance_eval_block', &block }
512
+ fcall self, '__instance_eval_block', *argv, &block
513
+ end
514
+ end
515
+ end