feedupdater 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,2 @@
1
+ == FeedUpdater 0.1.0
2
+ * initial version
data/README ADDED
@@ -0,0 +1,34 @@
1
+ The FeedUpdater is a simple daemon for handling the automatic updating of
2
+ FeedTools feed objects.
3
+
4
+ USAGE: feed_updater <subcommand>
5
+
6
+ Subcommands:
7
+ start
8
+ stop
9
+ restart
10
+ install
11
+
12
+ The FeedUpdater daemon is very easy to use, especially if you're using it
13
+ with a Rails application. If you're going to be using it with a Rails app,
14
+ simply install the feedupdater gem, navigate to your Rails app, and run
15
+ the 'feed_updater install' command. This will install FeedUpdater. Then
16
+ edit the feed_updater.yml config file as necessary. You will probably need
17
+ to define a custom updater class, which should probably go in your lib
18
+ directory. It's recommended that you use the example/custom_updater.rb file
19
+ as a starting point. You should using the updater events to copy any
20
+ required fields to feed subscription tables or item tables as necessary so
21
+ that a full feed parse isn't required every time the feed is displayed.
22
+
23
+ The config file also has a start delay option. This should almost always be
24
+ set to true. You should only turn it off when you are debugging a custom
25
+ updater class, and it should probably never be turned off in production.
26
+ Keeping it on prevents FeedUpdater from hitting the CPU too hard immediately
27
+ after a server reboot, and also prevents hundreds of update processes from
28
+ kicking off simultaneously on a shared server.
29
+
30
+ If you chose to use FeedUpdater outside a Rails application, you should try
31
+ to use a similar directory structure to what would be found in a Rails
32
+ setup. FeedUpdater will usually look in the current directory if it cannot
33
+ find a file its looking for though, so if FeedUpdater can't find a file it
34
+ needs, changing directories will probably fix the problem.
data/bin/feed_updater ADDED
@@ -0,0 +1,309 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ #--
4
+ # Copyright (c) 2005 Robert Aman
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+
26
+ if RUBY_PLATFORM =~ /mswin/
27
+ puts "FeedUpdater is not supported on Windows due to a lack of fork()."
28
+ exit
29
+ end
30
+
31
+ # :stopdoc:
32
+ # ROFLCOPTERS?
33
+ # This mainly exists because of the approximately 1 billion different
34
+ # ways to deploy this script.
35
+ class FileStalker
36
+ def self.hunt(paths)
37
+ for path in paths
38
+ if File.exists?(File.expand_path(path))
39
+ return File.expand_path(path)
40
+ elsif File.exists?(File.expand_path(
41
+ File.dirname(__FILE__) + path))
42
+ return File.expand_path(File.dirname(__FILE__) + path)
43
+ elsif File.exists?(File.expand_path(
44
+ File.dirname(__FILE__) + "/" + path))
45
+ return File.expand_path(File.dirname(__FILE__) + "/" + path)
46
+ end
47
+ end
48
+ return nil
49
+ end
50
+ end
51
+ # :startdoc:
52
+
53
+ $:.unshift(FileStalker.hunt([
54
+ "/../vendor/feedupdater/lib",
55
+ "vendor/feedupdater/lib",
56
+ "../lib"]))
57
+ $:.uniq!
58
+
59
+ require 'yaml'
60
+ require 'feed_updater'
61
+
62
+ if ARGV[0].nil?
63
+ # Exit quickly if no command line arguments given.
64
+
65
+ feed_updater_version = FeedTools::FEED_UPDATER_VERSION::STRING
66
+ feed_tools_version = "<error: not loaded>"
67
+ if defined?(FeedTools::FEED_TOOLS_VERSION::STRING)
68
+ feed_tools_version = FeedTools::FEED_TOOLS_VERSION::STRING
69
+ elsif defined?(FEED_TOOLS_VERSION)
70
+ feed_tools_version = FEED_TOOLS_VERSION
71
+ end
72
+ puts ""
73
+ puts "FeedUpdater #{feed_updater_version} / FeedTools #{feed_tools_version}"
74
+ puts "USAGE: feed_updater <subcommand>"
75
+ puts ""
76
+ puts "Subcommands:"
77
+ puts " start"
78
+ puts " This will start the daemon if it hasn't already been started."
79
+ puts ""
80
+ puts " stop"
81
+ puts " This will stop the daemon if it's already running."
82
+ puts ""
83
+ puts " restart"
84
+ puts " This will restart the daemon."
85
+ puts ""
86
+ puts " install"
87
+ puts " If the current directory is a Rails installation, this will"
88
+ puts " install FeedUpdater into the vendor folder and add a new script"
89
+ puts " to the scripts/ directory for running FeedUpdater. Rerunning"
90
+ puts " the install subcommand will update the files against the latest"
91
+ puts " version of the FeedUpdater gem file you have installed. Rails"
92
+ puts " is NOT required to use FeedUpdater."
93
+ puts ""
94
+ exit
95
+ elsif ARGV[0] == "install"
96
+ rails_root = nil
97
+ install_vendor_directory = nil
98
+ install_config_directory = nil
99
+ install_script_directory = nil
100
+
101
+ current_directory = File.expand_path(".")
102
+ loop do
103
+ entries = Dir.entries(current_directory)
104
+ if entries.include?("vendor") &&
105
+ entries.include?("config") &&
106
+ entries.include?("script")
107
+ rails_root = current_directory
108
+ break
109
+ else
110
+ break if current_directory == "/"
111
+ current_directory = File.expand_path(current_directory + "/..")
112
+ end
113
+ end
114
+ if rails_root.nil?
115
+ puts "You must run the install command when the current directory is"
116
+ puts "a Rails application."
117
+ else
118
+ install_vendor_directory = File.expand_path(rails_root + "/vendor")
119
+ install_config_directory = File.expand_path(rails_root + "/config")
120
+ install_script_directory = File.expand_path(rails_root + "/script")
121
+
122
+ # Install FeedUpdater gem to application vendor directory
123
+ system("rm -rf #{install_vendor_directory}/feedupdater")
124
+ unpack_string = `gem unpack feedupdater`
125
+ feed_updater_version =
126
+ unpack_string.scan(/\'feedupdater\-(.*)\'/).flatten[0]
127
+ if feed_updater_version.nil? || feed_updater_version == ""
128
+ puts "You need to have the feedupdater gem installed for the install"
129
+ puts "subcommand to run correctly."
130
+ exit
131
+ end
132
+ puts "Installing FeedUpdater #{feed_updater_version} " +
133
+ "to vendor directory..."
134
+
135
+ system("cp -rp feedupdater-#{feed_updater_version} " +
136
+ "#{install_vendor_directory}")
137
+ system("rm -rf feedupdater-#{feed_updater_version}")
138
+ system("mv #{install_vendor_directory}/" +
139
+ "feedupdater-#{feed_updater_version} " +
140
+ "#{install_vendor_directory}/feedupdater")
141
+
142
+ # Install FeedTools gem to application vendor directory
143
+ system("rm -rf #{install_vendor_directory}/feedtools")
144
+ unpack_string = `gem unpack feedtools`
145
+ feed_tools_version =
146
+ unpack_string.scan(/\'feedtools\-(.*)\'/).flatten[0]
147
+ if feed_tools_version.nil? || feed_tools_version == ""
148
+ puts "You need to have the feedtools gem installed for the install"
149
+ puts "subcommand to run correctly."
150
+ exit
151
+ end
152
+ puts "Installing FeedTools #{feed_tools_version} to vendor directory..."
153
+
154
+ system("cp -rp feedtools-#{feed_tools_version} " +
155
+ "#{install_vendor_directory}")
156
+ system("rm -rf feedtools-#{feed_tools_version}")
157
+ system("mv #{install_vendor_directory}/" +
158
+ "feedtools-#{feed_tools_version} " +
159
+ "#{install_vendor_directory}/feedtools")
160
+
161
+ # Copying the default config file to the config directory
162
+ if !File.exists?(File.expand_path(install_config_directory +
163
+ "/feed_updater.yml"))
164
+ puts "Installing default config file..."
165
+ config_file =
166
+ File.expand_path(File.dirname(__FILE__) + '/../config/feed_updater.yml')
167
+ system("cp -p #{config_file} #{install_config_directory}")
168
+ else
169
+ puts "Config file already exists, skipping..."
170
+ end
171
+
172
+ # Copying this executable to the script directory
173
+ puts "Copying executable to script directory..."
174
+ system("rm -rf #{install_script_directory}/feed_updater")
175
+ system("cp -p #{__FILE__} #{install_script_directory}")
176
+ system("chmod +x #{install_script_directory}/feed_updater")
177
+
178
+ puts "Use script/feed_updater to run FeedUpdater."
179
+ end
180
+ exit
181
+ end
182
+
183
+ config_file = FileStalker.hunt([
184
+ "./feed_updater.yml",
185
+ "../config/feed_updater.yml"
186
+ ])
187
+ if config_file.nil?
188
+ puts "Could not locate feed_updater.yml config file."
189
+ exit
190
+ end
191
+
192
+ config_hash = YAML.load(File.open(config_file, "r").read)
193
+ load_script_path = config_hash["load_script"]
194
+ if !load_script_path.nil?
195
+ result = FileStalker.hunt([
196
+ load_script_path,
197
+ File.dirname(config_file) + "/" + load_script_path,
198
+ File.dirname(config_file) + load_script_path,
199
+ File.dirname(__FILE__) + "/../" + load_script_path,
200
+ File.dirname(__FILE__) + "/.." + load_script_path
201
+ ])
202
+ if result.nil?
203
+ puts "Could not locate #{load_script_path.inspect} file."
204
+ exit
205
+ else
206
+ load_script_path = result
207
+ end
208
+ end
209
+
210
+ pid_file_path = config_hash["pid_file_path"]
211
+ if !pid_file_path.nil?
212
+ result = FileStalker.hunt([
213
+ pid_file_path,
214
+ File.dirname(config_file) + "/" + pid_file_path,
215
+ File.dirname(config_file) + pid_file_path,
216
+ File.dirname(__FILE__) + "/../" + pid_file_path,
217
+ File.dirname(__FILE__) + "/.." + pid_file_path,
218
+ "../" + pid_file_path
219
+ ])
220
+ pid_file_path = result if !result.nil?
221
+ else
222
+ result = FileStalker.hunt([
223
+ File.dirname(__FILE__) + "/../config",
224
+ "./config",
225
+ File.dirname(__FILE__) + "/config"
226
+ ])
227
+ pid_file_path = result if !result.nil?
228
+ end
229
+
230
+ log_file_path = config_hash["log_file_path"]
231
+ if !log_file_path.nil?
232
+ result = FileStalker.hunt([
233
+ log_file_path,
234
+ File.dirname(config_file) + "/" + log_file_path,
235
+ File.dirname(config_file) + log_file_path,
236
+ File.dirname(__FILE__) + "/../" + log_file_path,
237
+ File.dirname(__FILE__) + "/.." + log_file_path,
238
+ "../" + log_file_path
239
+ ])
240
+ log_file_path = result if !result.nil?
241
+ else
242
+ result = FileStalker.hunt([
243
+ File.dirname(__FILE__) + "/../log",
244
+ "./log",
245
+ File.dirname(__FILE__) + "/log",
246
+ File.dirname(__FILE__) + "/../logs",
247
+ "./logs",
248
+ File.dirname(__FILE__) + "/logs"
249
+ ])
250
+ log_file_path = result if !result.nil?
251
+ end
252
+
253
+ script_class = nil
254
+ existing_subclasses = []
255
+ ObjectSpace.each_object do |instance|
256
+ if instance.class.name == "Class" &&
257
+ instance.ancestors.include?(FeedTools::FeedUpdater)
258
+ existing_subclasses << instance
259
+ end
260
+ end
261
+ load(load_script_path)
262
+ loaded_subclasses = []
263
+ ObjectSpace.each_object do |instance|
264
+ if instance.class.name == "Class" &&
265
+ instance.ancestors.include?(FeedTools::FeedUpdater)
266
+ loaded_subclasses << instance
267
+ end
268
+ end
269
+ script_classes = (loaded_subclasses - existing_subclasses)
270
+ if script_classes.size == 0
271
+ puts "There do not appear to be any subclasses of FeedTools::FeedUpdater."
272
+ exit
273
+ elsif script_classes.size > 1
274
+ puts "There appear to be multiple subclasses of FeedTools::FeedUpdater."
275
+ exit
276
+ else
277
+ script_class = script_classes[0]
278
+ end
279
+
280
+ updater = nil
281
+ if load_script_path.nil?
282
+ updater = FeedTools::FeedUpdater.new
283
+ FeedTools::FeedUpdater.on_update do |feed, seconds|
284
+ updater.logger.info("Loaded '#{feed.href}'.")
285
+ updater.logger.info("=> Updated (#{feed.title}) in #{seconds} seconds.")
286
+ end
287
+ FeedTools::FeedUpdater.on_error do |href, error|
288
+ updater.logger.info("Error updating '#{href}':")
289
+ updater.logger.info(error)
290
+ end
291
+ else
292
+ updater = script_class.new
293
+ end
294
+
295
+ for key in config_hash.keys
296
+ key_sym = key.to_s.to_sym
297
+ updater.updater_options[key_sym] = config_hash[key]
298
+ end
299
+ updater.pid_file_dir = pid_file_path
300
+ updater.log_file_dir = log_file_path
301
+
302
+ case ARGV[0]
303
+ when "start"
304
+ updater.start()
305
+ when "stop"
306
+ updater.stop()
307
+ when "restart"
308
+ updater.restart()
309
+ end
@@ -0,0 +1 @@
1
+ 617
@@ -0,0 +1,12 @@
1
+ # This ensures that you don't break shared servers.
2
+ start_delay: false
3
+
4
+ # This is the path to the custom handler for updated feeds.
5
+ # See the documentation for details.
6
+ load_script: example/custom_updater.rb
7
+
8
+ # This is the path to store the pid files.
9
+ pid_file_path: config
10
+
11
+ # This is the path to store the log files.
12
+ log_file_path: log
@@ -0,0 +1,14 @@
1
+ class CustomUpdater < FeedTools::FeedUpdater
2
+ on_update do |feed, seconds|
3
+ logger.info("Loaded '#{feed.href}'.")
4
+ logger.info("=> Updated (#{feed.title}) in #{seconds} seconds.")
5
+ end
6
+
7
+ on_error do |href, error|
8
+ logger.info("Error updating '#{href}':")
9
+ logger.info(error)
10
+ end
11
+
12
+ on_complete do |updated_feed_hrefs|
13
+ end
14
+ end
@@ -0,0 +1,298 @@
1
+ require 'daemons/pidfile'
2
+ require 'daemons/pidmem'
3
+
4
+
5
+ module Daemons
6
+
7
+ class Application
8
+
9
+ attr_accessor :app_argv
10
+ attr_accessor :controller_argv
11
+
12
+ # the Pid instance belonging to this application
13
+ attr_reader :pid
14
+
15
+ # the ApplicationGroup the application belongs to
16
+ attr_reader :group
17
+
18
+ # my private options
19
+ attr_reader :options
20
+
21
+
22
+ def initialize(group, add_options = {}, pid = nil)
23
+ @group = group
24
+ @options = group.options.dup
25
+ @options.update(add_options)
26
+
27
+ unless @pid = pid
28
+ if dir = pidfile_dir
29
+ @pid = PidFile.new(pidfile_dir(), @group.app_name, @group.multiple)
30
+ else
31
+ @pid = PidMem.new
32
+ end
33
+ end
34
+ end
35
+
36
+ def script
37
+ @script || @group.script
38
+ end
39
+
40
+ def pidfile_dir
41
+ Pid.dir(@dir_mode || @group.dir_mode, @dir || @group.dir, @script || @group.script)
42
+ end
43
+
44
+ def logfile
45
+ (options[:log_output] && pidfile_dir()) ? File.join(pidfile_dir(), @group.app_name + '.output') : nil
46
+ end
47
+
48
+ def start_none
49
+ unless options[:ontop]
50
+ Daemonize.daemonize #(logfile)
51
+ else
52
+ Daemonize.simulate
53
+ end
54
+
55
+ @pid.pid = Process.pid
56
+
57
+
58
+ # We need this to remove the pid-file if the applications exits by itself.
59
+ # Note that <tt>at_text</tt> will only be run if the applications exits by calling
60
+ # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
61
+ # in your application!
62
+ #
63
+ at_exit {
64
+ @pid.cleanup rescue nil
65
+
66
+ # If the option <tt>:backtrace</tt> is used and the application did exit by itself
67
+ # create a exception log.
68
+ if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
69
+ exception_log() rescue nil
70
+ end
71
+
72
+ }
73
+
74
+ # This part is needed to remove the pid-file if the application is killed by
75
+ # daemons or manually by the user.
76
+ # Note that the applications is not supposed to overwrite the signal handler for
77
+ # 'TERM'.
78
+ #
79
+ trap('TERM') {
80
+ @pid.cleanup rescue nil
81
+ $daemons_sigterm = true
82
+
83
+ exit
84
+ }
85
+ end
86
+
87
+ def start_exec
88
+ unless options[:ontop]
89
+ Daemonize.daemonize(logfile)
90
+ else
91
+ Daemonize.simulate(logfile)
92
+ end
93
+
94
+ @pid.pid = Process.pid
95
+
96
+ ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
97
+ # haven't tested yet if this is really passed to the exec'd process...
98
+
99
+ Kernel.exec(script(), *ARGV)
100
+ end
101
+
102
+ def start_load
103
+ unless options[:ontop]
104
+ Daemonize.daemonize(logfile)
105
+ else
106
+ Daemonize.simulate(logfile)
107
+ end
108
+
109
+ @pid.pid = Process.pid
110
+
111
+
112
+ # We need this to remove the pid-file if the applications exits by itself.
113
+ # Note that <tt>at_text</tt> will only be run if the applications exits by calling
114
+ # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
115
+ # in your application!
116
+ #
117
+ at_exit {
118
+ @pid.cleanup rescue nil
119
+
120
+ # If the option <tt>:backtrace</tt> is used and the application did exit by itself
121
+ # create a exception log.
122
+ if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
123
+ exception_log() rescue nil
124
+ end
125
+
126
+ }
127
+
128
+ # This part is needed to remove the pid-file if the application is killed by
129
+ # daemons or manually by the user.
130
+ # Note that the applications is not supposed to overwrite the signal handler for
131
+ # 'TERM'.
132
+ #
133
+ trap('TERM') {
134
+ @pid.cleanup rescue nil
135
+ $daemons_sigterm = true
136
+
137
+ exit
138
+ }
139
+
140
+ # Know we really start the script...
141
+ $DAEMONS_ARGV = @controller_argv
142
+ ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
143
+
144
+ ARGV.clear
145
+ ARGV.concat @app_argv if @app_argv
146
+
147
+ # TODO: begin - rescue - end around this and exception logging
148
+ load script()
149
+ end
150
+
151
+ def start_proc
152
+ return unless options[:proc]
153
+
154
+ unless options[:ontop]
155
+ @pid.pid = Daemonize.call_as_daemon(options[:proc], logfile)
156
+ else
157
+ # Daemonize.simulate(logfile)
158
+ #
159
+ # @pid.pid = Process.pid
160
+ #
161
+ # Thread.new(&options[:proc])
162
+ unless @pid.pid = Process.fork
163
+ Daemonize.simulate(logfile)
164
+ options[:proc].call
165
+ exit
166
+ else
167
+ Process.detach(@pid.pid)
168
+ end
169
+ end
170
+ end
171
+
172
+
173
+ def start
174
+ @group.create_monitor(@group.applications[0] || self)
175
+
176
+ case options[:mode]
177
+ when :none
178
+ start_none
179
+ when :exec
180
+ start_exec
181
+ when :load
182
+ start_load
183
+ when :proc
184
+ start_proc
185
+ else
186
+ start_load
187
+ end
188
+ end
189
+
190
+ # def run
191
+ # if @group.controller.options[:exec]
192
+ # run_via_exec()
193
+ # else
194
+ # run_via_load()
195
+ # end
196
+ # end
197
+ #
198
+ # def run_via_exec
199
+ #
200
+ # end
201
+ #
202
+ # def run_via_load
203
+ #
204
+ # end
205
+
206
+
207
+ # This is a nice little function for debugging purposes:
208
+ # In case a multi-threaded ruby script exits due to an uncaught exception
209
+ # it may be difficult to find out where the exception came from because
210
+ # one cannot catch exceptions that are thrown in threads other than the main
211
+ # thread.
212
+ #
213
+ # This function searches for all exceptions in memory and outputs them to STDERR
214
+ # (if it is connected) and to a log file in the pid-file directory.
215
+ #
216
+ def exception_log
217
+ require 'logger'
218
+
219
+ l_file = Logger.new(File.join(pidfile_dir(), @group.app_name + '.log'))
220
+
221
+
222
+ # the code below only logs the last exception
223
+ # e = nil
224
+ #
225
+ # ObjectSpace.each_object {|o|
226
+ # if ::Exception === o
227
+ # e = o
228
+ # end
229
+ # }
230
+ #
231
+ # l_file.error e
232
+ # l_file.close
233
+
234
+ # this code logs every exception found in memory
235
+ ObjectSpace.each_object {|o|
236
+ if ::Exception === o
237
+ l_file.error o
238
+ end
239
+ }
240
+
241
+ l_file.close
242
+ end
243
+
244
+
245
+ def stop
246
+ if options[:force] and not running?
247
+ self.zap
248
+ return
249
+ end
250
+
251
+ # Catch errors when trying to kill a process that doesn't
252
+ # exist. This happens when the process quits and hasn't been
253
+ # restarted by the monitor yet. By catching the error, we allow the
254
+ # pid file clean-up to occur.
255
+ begin
256
+ Process.kill('TERM', @pid.pid)
257
+ rescue Errno::ESRCH => e
258
+ puts "#{e} #{@pid.pid}"
259
+ puts "deleting pid-file."
260
+ end
261
+
262
+ # We try to remove the pid-files by ourselves, in case the application
263
+ # didn't clean it up.
264
+ @pid.cleanup rescue nil
265
+
266
+ end
267
+
268
+ def zap
269
+ @pid.cleanup
270
+ end
271
+
272
+ def zap!
273
+ @pid.cleanup rescue nil
274
+ end
275
+
276
+ def show_status
277
+ running = self.running?
278
+
279
+ puts "#{self.group.app_name}: #{running ? '' : 'not '}running#{(running and @pid.exists?) ? ' [pid ' + @pid.pid.to_s + ']' : ''}#{(@pid.exists? and not running) ? ' (but pid-file exists: ' + @pid.pid.to_s + ')' : ''}"
280
+ end
281
+
282
+ # This function implements a (probably too simle) method to detect
283
+ # whether the program with the pid found in the pid-file is still running.
284
+ # It just searches for the pid in the output of <tt>ps ax</tt>, which
285
+ # is probably not a good idea in some cases.
286
+ # Alternatives would be to use a direct access method the unix process control
287
+ # system.
288
+ #
289
+ def running?
290
+ if @pid.exists?
291
+ return Pid.running?(@pid.pid)
292
+ end
293
+
294
+ return false
295
+ end
296
+ end
297
+
298
+ end