semi_daemon 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
File without changes
@@ -0,0 +1,12 @@
1
+ class SemiDaemon
2
+
3
+ module Version
4
+ MAJOR = 0
5
+ MINOR = 9
6
+ TINY = 1
7
+
8
+ # 0.9.1
9
+ FULL = [MAJOR, MINOR, TINY].join(".")
10
+ end
11
+
12
+ end
@@ -0,0 +1,147 @@
1
+ require File.dirname(__FILE__) + "/../vendor/god/system/process"
2
+
3
+ class SemiDaemon
4
+
5
+ class SimpleLogFormatter
6
+ def call(severity, time, progname, msg)
7
+ "#{time.strftime("%Y-%m-%d %H:%M:%S")}: #{msg}\n"
8
+ end
9
+ end
10
+
11
+ class << self
12
+
13
+ def daemon_name
14
+ self.to_s.underscore
15
+ end
16
+
17
+ def log_file(log=nil)
18
+ log ? @log_file=log : @log_file
19
+ end
20
+
21
+ def pid_file(pid_file=nil)
22
+ pid_file ? @pid_file=pid_file : @pid_file
23
+ end
24
+
25
+ def interval(int=nil)
26
+ int ? @interval=int : @interval
27
+ end
28
+
29
+ def inherited(child)
30
+ child.log_file "#{child.daemon_name}.log"
31
+ child.pid_file "#{child.daemon_name}.pid"
32
+ end
33
+
34
+ def stop_file
35
+ "#{daemon_name}.stop"
36
+ end
37
+
38
+ end
39
+
40
+ # create instance methods for our class methods
41
+ def method_missing(m,*args)
42
+ if [:log_file, :pid_file, :interval, :daemon_name,:stop_file].include?(m)
43
+ self.class.send(m)
44
+ else
45
+ super(m,*args)
46
+ end
47
+ end
48
+
49
+ # logging
50
+
51
+ def logger
52
+ @logger ||= begin
53
+ l=Logger.new root("log/#{log_file}")
54
+ l.formatter = SimpleLogFormatter.new
55
+ l
56
+ end
57
+ end
58
+
59
+ # process management
60
+
61
+ def root(path)
62
+ if defined?(Rails)
63
+ (Rails.root + path).to_s
64
+ else
65
+ dir = File.dirname(__FILE__)
66
+ path[0..0]=="/" ? path : File.join(dir, path)
67
+ end
68
+ end
69
+
70
+ def stop_file_path
71
+ root stop_file
72
+ end
73
+
74
+ def pid_file_path
75
+ root pid_file
76
+ end
77
+
78
+ def create_pid_file
79
+ File.open(pid_file_path,"w") { |f| f.write(Process.pid) }
80
+ end
81
+
82
+ def running?
83
+ if File.exists?(pid_file_path)
84
+ pid=File.read(pid_file_path)
85
+ God::System::Process.new(pid).exists?
86
+ end
87
+ end
88
+
89
+ def work
90
+ raise "You need to define the work method in your subclass of SemiDaemon."
91
+ end
92
+
93
+ def terminate
94
+ logger.info "#{daemon_name} stopped."
95
+ Rails.logger.info "#{daemon_name} stopped." if defined?(Rails)
96
+ File.delete(pid_file_path) rescue nil
97
+ File.delete(stop_file_path) rescue nil
98
+ exit
99
+ end
100
+
101
+ def transaction(&block)
102
+ terminate if @quit
103
+ @safe_to_quit=false
104
+ r=block.call
105
+ @safe_to_quit=true
106
+ terminate if @quit
107
+ r
108
+ end
109
+
110
+ def initialize
111
+ @safe_to_quit=true
112
+ end
113
+
114
+ def wants_quit?
115
+ File.exists?(stop_file_path) or @quit
116
+ end
117
+
118
+ def run
119
+ raise "You must specify `interval 1.minute` or similar in your class to set the interval." if interval.nil?
120
+ return if running?
121
+ Signal.trap("TERM") do
122
+ @quit=true
123
+ # if it's not safe to quit then we have to trust that our
124
+ # child class will quit when it can or else we'll quit at
125
+ # the beginning of the next event loop
126
+ terminate if @safe_to_quit
127
+ end
128
+
129
+ create_pid_file
130
+ logger.info "#{daemon_name} started (PID: #{Process.pid})."
131
+ while(true)
132
+ if wants_quit?
133
+ logger.info "Being asked to stop (#{@quit ? "TERM" : "stop file"}), stopping..."
134
+ terminate
135
+ end
136
+ logger.info "Working..."
137
+ work
138
+ if defined?(ActiveRecord::Base)
139
+ ActiveRecord::Base.clear_active_connections!
140
+ end
141
+ logger.info "Sleeping for #{interval/60} minutes..."
142
+ # we do this so we can stop more quickly while sleeping
143
+ interval.to_i.times { sleep 1 unless @quit }
144
+ end
145
+ end
146
+
147
+ end
data/readme.markdown ADDED
@@ -0,0 +1,49 @@
1
+ SemiDaemon
2
+ ==========
3
+
4
+ Why
5
+ ---
6
+
7
+ Before this I've tried `backgroundDRB`, `daemon_generator` (I think that's the name), and they never really worked to my satisfaction. I did find `background_job` for scheduled background stuff and liked how simple it was to setup and get started. SemiDaemon is kind of patterned after the simplicity of setting up it's daemon process.
8
+
9
+
10
+ An example class
11
+ ----------------
12
+
13
+ class CampaignSender < SemiDaemon
14
+
15
+ # defaulted based on the name of the class if you don't provide them
16
+ # log and pid will both be stored in your RAILS_ROOT/log folder
17
+ log_file "campaign_sender.log"
18
+ pid_file "campaign_sender.pid"
19
+
20
+ # you must provide an interval that your work function is called
21
+ interval 2.hours
22
+
23
+ # your work function is called every 2 hours
24
+ def work
25
+ logger.info "Running the queue..."
26
+ Campaign.run_queue!
27
+ rescue StandardError => e
28
+ logger.error "-----"
29
+ logger.error e.message
30
+ e.backtrace.each { |x| logger.error x }
31
+ logger.error "-----"
32
+ end
33
+
34
+ end
35
+
36
+ Setting it up with crontab
37
+ --------------------------
38
+
39
+ The following cron entry will start the daemon every hour. If the process is already running them the new process will simply quit.
40
+
41
+ 0 * * * * /usr/bin/ruby /u/apps/your_app/current/script/runner -e production "CampaignSender.new.run"
42
+
43
+ If the process ever dies for any reason cron will send you the stack trace via e-mail (if you have that setup right). And one hour (or less) later the process will fire itself right back up again.
44
+
45
+
46
+ Enjoy,
47
+
48
+ Josh Goebel
49
+ Dreamer3 on #rubyonrails/freenode
@@ -0,0 +1,17 @@
1
+ module God
2
+ module System
3
+
4
+ class Process
5
+ def initialize(pid)
6
+ @pid = pid.to_i
7
+ end
8
+
9
+ # Return true if this process is running, false otherwise
10
+ def exists?
11
+ !!::Process.kill(0, @pid) rescue false
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: semi_daemon
3
+ version: !ruby/object:Gem::Version
4
+ hash: 57
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 1
10
+ version: 0.9.1
11
+ platform: ruby
12
+ authors:
13
+ - Josh Goebel
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-01-06 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: simple class for creating long running daemon processes easily
23
+ email: josh@autoraptor.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - readme.markdown
30
+ files:
31
+ - Rakefile
32
+ - lib/semi_daemon/version.rb
33
+ - lib/semi_daemon.rb
34
+ - vendor/god/system/process.rb
35
+ - readme.markdown
36
+ has_rdoc: true
37
+ homepage:
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ hash: 3
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.6.2
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: simple class for creating long running daemon processes
70
+ test_files: []
71
+