semi_daemon 0.9.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.
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
+