dyoder-hive 0.1

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.
Files changed (3) hide show
  1. data/lib/scheduler.rb +70 -0
  2. data/lib/worker.rb +103 -0
  3. metadata +63 -0
data/lib/scheduler.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'rubygems'
2
+ require 'facets/duration' unless 1.respond_to? :minute
3
+
4
+ module Hive
5
+
6
+ class Scheduler < Hive::Worker
7
+
8
+ def stop_tasks
9
+ threads = Thread.list.select { |t| t.alive? && t != Thread.current }
10
+ until threads.empty?
11
+ logger.info "#{threads.count} threads still running ... "
12
+ threads.each { |t| t.kill && t != Thread.current }
13
+ threads = threads.select { |t| t.alive? }
14
+ end
15
+ logger.info "All threads are finished."
16
+ end
17
+
18
+ private
19
+
20
+ def schedule( method, options )
21
+ raise ArgumentError.new( "Invalid schedule for task #{method}.") unless valid?(options)
22
+ logger.info "Scheduling task #{method} ..."
23
+ Thread.new do
24
+ t = Time.now
25
+ loop do
26
+ t += 60
27
+ sleep( ( options[:interval] || ( t - Time.now ) ).to_i )
28
+ if match( t, options )
29
+ if self.respond_to?( method )
30
+ Thread.new do
31
+ logger.info "Running task #{method} ..."
32
+ self.send( method )
33
+ logger.info "Task #{method} complete."
34
+ end
35
+ else
36
+ logger.warn "Task #{method} scheduled but not defined."
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def valid?( options )
44
+ ! ( options.keys & [ :minute, :hour, :day, :weekday, :month, :interval] ).empty?
45
+ end
46
+
47
+ def match( t, options )
48
+ options[:interval] ||
49
+ (( match_item( options[:minute], t.min )) &&
50
+ ( match_item( options[:hour], t.hour )) &&
51
+ ( match_item( options[:day], t.day )) &&
52
+ ( match_item( options[:weekday], t.wday )) &&
53
+ ( match_item( options[:month], t.month )))
54
+ end
55
+
56
+ def match_item( x, y )
57
+ case x
58
+ when nil then true
59
+ when Array then x.include? y
60
+ when Range then x.include? y
61
+ when Integer then x == y
62
+ else
63
+ logger.warn "Value #{x.inspect} will never be matched"
64
+ false
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ end
data/lib/worker.rb ADDED
@@ -0,0 +1,103 @@
1
+ module Kernel
2
+ def safe_trap(*signals)
3
+ signals.each { |s| trap(s) { yield } }
4
+ Thread.new { loop { sleep 1 } } if RUBY_PLATFORM =~ /mswin32/
5
+ end
6
+ end
7
+
8
+ module Hive
9
+
10
+ # "Workers" are just dedicated processes. Managers, Servers, and Monitors are all
11
+ # examples of Workers. This class just encapsulates the common features across all
12
+ # Workers: daemonization, signal traps, console support, logging, only-ness, etc.
13
+
14
+ class Worker
15
+
16
+ def self.run( options = {} )
17
+ @instance ||= new( options )
18
+ @instance.start
19
+ end
20
+
21
+ # make this the one-and-only
22
+ def self.instance ; @instance ; end
23
+ class << self ; private :new, :allocate ; end
24
+ private :dup, :clone
25
+
26
+ attr_accessor :logger, :options
27
+
28
+ def initialize( options )
29
+ @options = options
30
+ end
31
+
32
+ # returns the PID of the new process
33
+ def start
34
+ pid = daemonize if options[ :daemon ]
35
+ puts "#{self.class.name} process #{pid} started ..." if pid
36
+ return pid if pid
37
+ begin
38
+ # from here on in, we're in the daemon
39
+ start_logger ; logger.info "#{self.class} starting ..."
40
+ start_debugger if options[:debug] # unless Kernel.engine == 'jruby'
41
+ # various ways to talk to a worker
42
+ set_traps ; start_console ; start_drb
43
+ start_tasks.join
44
+ rescue Exception => e
45
+ logger.error e.to_s
46
+ end
47
+ end
48
+
49
+ def stop
50
+ logger.info "#{self.class} shutting down ..."
51
+ @console.stop if @console
52
+ stop_tasks
53
+ end
54
+
55
+ def restart ; stop ; start ; end
56
+
57
+ def daemonize
58
+ pwd = Dir.pwd ; pid = fork ; return pid if pid ; Dir.chdir( pwd )
59
+ File.umask 0000 ; STDIN.reopen( '/dev/null') ;
60
+ STDOUT.reopen( '/dev/null', 'a' ) ; STDERR.reopen( STDOUT )
61
+ nil # return nil for child process, just like fork does
62
+ end
63
+
64
+ def set_traps
65
+ safe_trap( 'HUP' ) { restart }
66
+ safe_trap( 'TERM','INT' ) { stop }
67
+ end
68
+
69
+ def start_console
70
+ # TODO: add live console support
71
+ end
72
+
73
+ def start_drb
74
+ # TODO: add DRb support
75
+ end
76
+
77
+ def start_debugger
78
+ require 'ruby-debug' ; Debugger.start
79
+ Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
80
+ logger.info "ruby-debug enabled"
81
+ end
82
+
83
+ def start_logger
84
+ if options[ :logger ]
85
+ @logger = options[ :logger ]
86
+ else
87
+ require 'logger'
88
+ @logger = Logger.new( 'log.out' )
89
+ end
90
+ end
91
+
92
+ protected
93
+
94
+ # workers should override these methods
95
+ def start_tasks
96
+ end
97
+
98
+ def stop_tasks
99
+ end
100
+
101
+ end
102
+
103
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dyoder-hive
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Dan Yoder
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-01 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: faces/duration
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: "1.0"
24
+ version:
25
+ description:
26
+ email: dan@zeraweb.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/scheduler.rb
35
+ - lib/worker.rb
36
+ has_rdoc: true
37
+ homepage: http://github.com/dyoder/hive
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.8.7
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.2.0
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: Open-source Ruby framework for background processing.
62
+ test_files: []
63
+