pidly 0.1.0
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/.document +3 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.rdoc +4 -0
- data/LICENSE.txt +20 -0
- data/README.md +59 -0
- data/Rakefile +38 -0
- data/gemspec.yml +14 -0
- data/lib/pidly.rb +2 -0
- data/lib/pidly/callbacks.rb +120 -0
- data/lib/pidly/control.rb +331 -0
- data/lib/pidly/logger.rb +49 -0
- data/lib/pidly/version.rb +4 -0
- data/pidly.gemspec +15 -0
- data/spec/control_spec.rb +65 -0
- data/spec/pidly_spec.rb +8 -0
- data/spec/spec_helper.rb +33 -0
- metadata +108 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour --format documentation
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup rdoc --title "pidly Documentation" --protected
|
data/ChangeLog.rdoc
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Dustin Willis Webber
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# pidly
|
2
|
+
|
3
|
+
* [Homepage][https://github.com/mephux/pidly]
|
4
|
+
* [Documentation][http://rubydoc.info/gems/pidly/frames]
|
5
|
+
|
6
|
+
## Description
|
7
|
+
|
8
|
+
Pidly is a very minimalistic daemon library that doesn't make assumptions. Pidly allows you to control the
|
9
|
+
daemon without getting in the way with forced verbose output and usage messages.
|
10
|
+
|
11
|
+
## Examples
|
12
|
+
|
13
|
+
require 'pidly'
|
14
|
+
|
15
|
+
class Test < Pidly::Control
|
16
|
+
|
17
|
+
before_start do
|
18
|
+
"BEFORE START #{@pid}"
|
19
|
+
end
|
20
|
+
|
21
|
+
start :when_daemon_starts
|
22
|
+
|
23
|
+
stop do
|
24
|
+
"Attempting to kill process: #{@pid}"
|
25
|
+
end
|
26
|
+
|
27
|
+
after_stop :test_after_daemon_stops
|
28
|
+
|
29
|
+
error do
|
30
|
+
"SENDING EMAIL | Error Count: #{@error_count}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def when_daemon_starts
|
34
|
+
loop do
|
35
|
+
print "TEST FROM #{@pid}"
|
36
|
+
sleep 2
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
@daemon = Test.spawn(
|
43
|
+
:name => 'Test Daemon',
|
44
|
+
:path => '/tmp',
|
45
|
+
:verbose => true
|
46
|
+
)
|
47
|
+
|
48
|
+
# @daemon.send ARGV.first
|
49
|
+
@daemon.start # stop, status, restart, and kill.
|
50
|
+
|
51
|
+
## Install
|
52
|
+
|
53
|
+
$ gem install pidly
|
54
|
+
|
55
|
+
## Copyright
|
56
|
+
|
57
|
+
Copyright (c) 2011 Dustin Willis Webber
|
58
|
+
|
59
|
+
See LICENSE.txt for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
gem 'ore-tasks', '~> 0.4'
|
6
|
+
require 'ore/tasks'
|
7
|
+
|
8
|
+
Ore::Tasks.new
|
9
|
+
rescue LoadError => e
|
10
|
+
STDERR.puts e.message
|
11
|
+
STDERR.puts "Run `gem install ore-tasks` to install 'ore/tasks'."
|
12
|
+
end
|
13
|
+
|
14
|
+
begin
|
15
|
+
gem 'rspec', '~> 2.4'
|
16
|
+
require 'rspec/core/rake_task'
|
17
|
+
|
18
|
+
RSpec::Core::RakeTask.new
|
19
|
+
rescue LoadError => e
|
20
|
+
task :spec do
|
21
|
+
abort "Please run `gem install rspec` to install RSpec."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
task :test => :spec
|
26
|
+
task :default => :spec
|
27
|
+
|
28
|
+
begin
|
29
|
+
gem 'yard', '~> 0.6.0'
|
30
|
+
require 'yard'
|
31
|
+
|
32
|
+
YARD::Rake::YardocTask.new
|
33
|
+
rescue LoadError => e
|
34
|
+
task :yard do
|
35
|
+
abort "Please run `gem install yard` to install YARD."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
task :doc => :yard
|
data/gemspec.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
name: pidly
|
2
|
+
summary: "A super simple daemon library."
|
3
|
+
description:
|
4
|
+
"Pidly is a very minimalistic daemon library that doesnt make assumptions. Pidly allows you to control the
|
5
|
+
daemon without getting in the way with forced verbose output and usage messages."
|
6
|
+
license: MIT
|
7
|
+
authors: Dustin Willis Webber
|
8
|
+
homepage: https://github.com/mephux/pidly
|
9
|
+
has_yard: true
|
10
|
+
|
11
|
+
development_dependencies:
|
12
|
+
ore-tasks: ~> 0.4
|
13
|
+
rspec: ~> 2.4
|
14
|
+
yard: ~> 0.6.0
|
data/lib/pidly.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
module Pidly
|
2
|
+
#
|
3
|
+
# Pidly before/after callbacks
|
4
|
+
#
|
5
|
+
module Callbacks
|
6
|
+
|
7
|
+
#
|
8
|
+
# Before start
|
9
|
+
#
|
10
|
+
# Right before the daemon is instructed to start the
|
11
|
+
# following callback will be invoked and executed.
|
12
|
+
#
|
13
|
+
# @param [Symbol] callback Method name
|
14
|
+
# @yield [] Code to be executed upon callback invocation
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# before_start :method_name
|
18
|
+
# # OR
|
19
|
+
# before_start { puts "#{@pid} is about to start!" }
|
20
|
+
#
|
21
|
+
def before_start(callback=nil, &block)
|
22
|
+
add_callback(:before_start, (callback || block))
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Start
|
27
|
+
#
|
28
|
+
# When the daemon is instructed to start the
|
29
|
+
# following callback will be invoked and executed.
|
30
|
+
#
|
31
|
+
# @param [Symbol] callback Method name
|
32
|
+
# @yield [] Code to be executed upon callback invocation
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# start :method_name
|
36
|
+
# # OR
|
37
|
+
# start { puts "Daemon Started!" }
|
38
|
+
#
|
39
|
+
def start(callback=nil, &block)
|
40
|
+
add_callback(:start, (callback || block))
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Stop
|
45
|
+
#
|
46
|
+
# When the daemon is instructed to stop the
|
47
|
+
# following callback will be invoked and executed.
|
48
|
+
#
|
49
|
+
# @param [Symbol] callback Method name
|
50
|
+
# @yield [] Code to be executed upon callback invocation
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# stop :method_name
|
54
|
+
# # OR
|
55
|
+
# stop { puts "Attempting to stop #{@name} with pid #{@pid}!" }
|
56
|
+
#
|
57
|
+
def stop(callback=nil, &block)
|
58
|
+
add_callback(:stop, (callback || block))
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# After stop
|
63
|
+
#
|
64
|
+
# Right after the daemon is instructed to stop the
|
65
|
+
# following callback will be invoked and executed.
|
66
|
+
#
|
67
|
+
# @param [Symbol] callback Method name
|
68
|
+
# @yield [] Code to be executed upon callback invocation
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# after_start :method_name
|
72
|
+
# # OR
|
73
|
+
# after_start { puts "#{@pid} was just killed!" }
|
74
|
+
#
|
75
|
+
def after_stop(callback=nil, &block)
|
76
|
+
add_callback(:after_stop, (callback || block))
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Error
|
81
|
+
#
|
82
|
+
# If the daemon encounters an error or an exception is raised
|
83
|
+
# the following callback will be invoked and executed.
|
84
|
+
#
|
85
|
+
# @param [Symbol] callback Method name
|
86
|
+
# @yield [] Code to be executed upon callback invocation
|
87
|
+
#
|
88
|
+
# @example
|
89
|
+
# error :send_error_email
|
90
|
+
# # OR
|
91
|
+
# error { puts "ZOMG! #{@name} failed!" }
|
92
|
+
#
|
93
|
+
def error(callback=nil, &block)
|
94
|
+
add_callback(:error, (callback || block))
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# Add callback
|
99
|
+
#
|
100
|
+
# @param [Symbol] callback Callback method name
|
101
|
+
# @param [Symbol, nil] invoke Method to call
|
102
|
+
# @yield [] Code to be executed upon callback invocation
|
103
|
+
#
|
104
|
+
def add_callback(callback, invoke)
|
105
|
+
Control.class_variable_set(:"@@#{callback}", invoke)
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Extend and include callback methods
|
110
|
+
#
|
111
|
+
# @param [Class] receiver The calling class
|
112
|
+
#
|
113
|
+
def self.included(receiver)
|
114
|
+
puts receiver.class
|
115
|
+
receiver.extend self
|
116
|
+
end
|
117
|
+
|
118
|
+
end # modle Callbacks
|
119
|
+
|
120
|
+
end # module Pidly
|
@@ -0,0 +1,331 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
require 'pidly/callbacks'
|
5
|
+
require 'pidly/logger'
|
6
|
+
|
7
|
+
#
|
8
|
+
# Pidly namespace
|
9
|
+
#
|
10
|
+
module Pidly
|
11
|
+
|
12
|
+
#
|
13
|
+
# Pidly daemon control
|
14
|
+
#
|
15
|
+
class Control
|
16
|
+
|
17
|
+
# Include callbacks
|
18
|
+
include Pidly::Callbacks
|
19
|
+
|
20
|
+
# Include logging helpers
|
21
|
+
include Pidly::Logger
|
22
|
+
|
23
|
+
attr_accessor :daemon, :name, :pid_file,
|
24
|
+
:log_file, :path, :sync_log, :allow_multiple,
|
25
|
+
:verbose, :pid, :timeout, :error_count, :messages
|
26
|
+
|
27
|
+
#
|
28
|
+
# Initialize control object
|
29
|
+
#
|
30
|
+
# @param [Hash] options The options to create a controller with.
|
31
|
+
#
|
32
|
+
# @raise [RuntimeError]
|
33
|
+
# Raise exception if path does not exist
|
34
|
+
#
|
35
|
+
# @raise [RuntimeError]
|
36
|
+
# Raise exception if path is not readable or writable.
|
37
|
+
#
|
38
|
+
def initialize(options={})
|
39
|
+
|
40
|
+
@messages = []
|
41
|
+
|
42
|
+
@error_count = 0
|
43
|
+
|
44
|
+
@name = options.fetch(:name)
|
45
|
+
|
46
|
+
if options.has_key?(:path)
|
47
|
+
@path = Pathname.new(options.fetch(:path))
|
48
|
+
else
|
49
|
+
@path = Pathname.new('/tmp')
|
50
|
+
end
|
51
|
+
|
52
|
+
unless @path.directory?
|
53
|
+
raise('Path does not exist or is not a directory.')
|
54
|
+
end
|
55
|
+
|
56
|
+
unless @path.readable? && @path.writable?
|
57
|
+
raise('Path must be readable and writable.')
|
58
|
+
end
|
59
|
+
|
60
|
+
if options.has_key?(:pid_file)
|
61
|
+
@pid_file = options.fetch(:pid_path)
|
62
|
+
else
|
63
|
+
@pid_file = File.join(@path.to_s, 'pids', @name + '.pid')
|
64
|
+
end
|
65
|
+
|
66
|
+
if options.has_key?(:log_file)
|
67
|
+
@log_file = options.fetch(:log_path)
|
68
|
+
else
|
69
|
+
@log_file = File.join(@path.to_s, 'logs', @name + '.log')
|
70
|
+
end
|
71
|
+
|
72
|
+
@pid = fetch_pid if File.file?(@pid_file)
|
73
|
+
|
74
|
+
@sync_log = options.fetch(:sync_log, true)
|
75
|
+
|
76
|
+
@allow_multiple = options.fetch(:allow_multiple, false)
|
77
|
+
|
78
|
+
@signal = options.fetch(:signal, "TERM")
|
79
|
+
|
80
|
+
@timeout = options.fetch(:timeout, 10)
|
81
|
+
|
82
|
+
@verbosity = options.fetch(:verbose, false)
|
83
|
+
|
84
|
+
@logger = options.fetch(:logger, true)
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Spawn
|
89
|
+
#
|
90
|
+
# @param [Hash] options The options to create a controller with.
|
91
|
+
#
|
92
|
+
# @option options [String] :name Daemon name
|
93
|
+
#
|
94
|
+
# @option options [String] :path Path to create the log/pids directory
|
95
|
+
#
|
96
|
+
# @option options [String] :pid_file Pid file path
|
97
|
+
#
|
98
|
+
# @option options [String] :log_file Log file path
|
99
|
+
#
|
100
|
+
# @option options [true, false] :sync_log Synchronize log files
|
101
|
+
#
|
102
|
+
# @option options [true, false] :allow_multiple
|
103
|
+
# Allow multiple daemons of the same type
|
104
|
+
#
|
105
|
+
# @option options [true, false] :sync_log Synchronize log files
|
106
|
+
#
|
107
|
+
# @option options [String] :signal Trap signal
|
108
|
+
#
|
109
|
+
# @option options [Integer] :timeout Timeout for Process#wait
|
110
|
+
#
|
111
|
+
# @option options [true, false] :verbose Display daemon messages
|
112
|
+
#
|
113
|
+
# @option options [true, false] :logger Enable daemon logging
|
114
|
+
#
|
115
|
+
# @return [Control] Control object
|
116
|
+
#
|
117
|
+
def self.spawn(options={})
|
118
|
+
@daemon = new(options)
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# Start
|
123
|
+
#
|
124
|
+
# Validate callbacks and start daemon
|
125
|
+
#
|
126
|
+
def start
|
127
|
+
validate_files_and_paths!
|
128
|
+
validate_callbacks!
|
129
|
+
|
130
|
+
unless @allow_multiple
|
131
|
+
if running?
|
132
|
+
log(:error, "#{@name} is already running (PID #{@pid})")
|
133
|
+
return
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
@pid = fork do
|
138
|
+
begin
|
139
|
+
Process.setsid
|
140
|
+
|
141
|
+
open(@pid_file, 'w') do |f|
|
142
|
+
f << Process.pid
|
143
|
+
@pid = Process.pid
|
144
|
+
end
|
145
|
+
|
146
|
+
execute_callback(:before_start)
|
147
|
+
|
148
|
+
Dir.chdir @path.to_s
|
149
|
+
File.umask 0000
|
150
|
+
|
151
|
+
if @logger
|
152
|
+
log = File.new(@log_file, "a")
|
153
|
+
log.sync = @sync_log
|
154
|
+
|
155
|
+
STDIN.reopen "/dev/null"
|
156
|
+
STDOUT.reopen log
|
157
|
+
STDERR.reopen STDOUT
|
158
|
+
end
|
159
|
+
|
160
|
+
trap("TERM") do
|
161
|
+
stop
|
162
|
+
end
|
163
|
+
|
164
|
+
execute_callback(:start)
|
165
|
+
|
166
|
+
rescue RuntimeError => message
|
167
|
+
STDERR.puts message
|
168
|
+
STDERR.puts message.backtrace
|
169
|
+
|
170
|
+
execute_callback(:error)
|
171
|
+
rescue => message
|
172
|
+
STDERR.puts message
|
173
|
+
STDERR.puts message.backtrace
|
174
|
+
|
175
|
+
execute_callback(:error)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
rescue => message
|
180
|
+
STDERR.puts message
|
181
|
+
STDERR.puts message.backtrace
|
182
|
+
execute_callback(:error)
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# Stop
|
187
|
+
#
|
188
|
+
# Stop daemon and remove pid file
|
189
|
+
#
|
190
|
+
def stop
|
191
|
+
|
192
|
+
if running?
|
193
|
+
|
194
|
+
Process.kill(@signal, @pid)
|
195
|
+
FileUtils.rm(@pid_file)
|
196
|
+
|
197
|
+
execute_callback(:stop)
|
198
|
+
|
199
|
+
begin
|
200
|
+
Process.wait(@pid)
|
201
|
+
rescue Errno::ECHILD
|
202
|
+
end
|
203
|
+
|
204
|
+
@timeout.downto(0) do
|
205
|
+
sleep 1
|
206
|
+
exit unless running?
|
207
|
+
end
|
208
|
+
|
209
|
+
Process.kill 9, @pid if running?
|
210
|
+
execute_callback(:after_stop)
|
211
|
+
|
212
|
+
else
|
213
|
+
FileUtils.rm(@pid_file) if File.exists?(@pid_file)
|
214
|
+
log(:info, "PID file not found. Is the daemon started?")
|
215
|
+
end
|
216
|
+
|
217
|
+
rescue Errno::ENOENT
|
218
|
+
end
|
219
|
+
|
220
|
+
#
|
221
|
+
# Status
|
222
|
+
#
|
223
|
+
# Return current daemon status and pid
|
224
|
+
#
|
225
|
+
# @return [String] Status
|
226
|
+
#
|
227
|
+
def status
|
228
|
+
if running?
|
229
|
+
log(:info, "#{@name} is running (PID #{@pid})")
|
230
|
+
else
|
231
|
+
log(:info, "#{@name} is NOT running")
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
#
|
236
|
+
# Restart
|
237
|
+
#
|
238
|
+
# Restart the daemon
|
239
|
+
#
|
240
|
+
def restart
|
241
|
+
stop; sleep 1 while running?; start
|
242
|
+
end
|
243
|
+
|
244
|
+
#
|
245
|
+
# Kill
|
246
|
+
#
|
247
|
+
# @param [String] remove_pid_file Remove the daemon pid file
|
248
|
+
#
|
249
|
+
def kill(remove_pid_file=true)
|
250
|
+
if running?
|
251
|
+
log(:info, "Killing #{@name} (PID #{@pid})")
|
252
|
+
Process.kill 9, @pid
|
253
|
+
end
|
254
|
+
|
255
|
+
FileUtils.rm(@pid_file) if remove_pid_file
|
256
|
+
rescue Errno::ENOENT
|
257
|
+
end
|
258
|
+
|
259
|
+
#
|
260
|
+
# Running?
|
261
|
+
#
|
262
|
+
# @return [true, false] Return the running status of the daemon.
|
263
|
+
#
|
264
|
+
def running?
|
265
|
+
Process.kill 0, @pid
|
266
|
+
true
|
267
|
+
rescue Errno::ESRCH
|
268
|
+
false
|
269
|
+
rescue Errno::EPERM
|
270
|
+
true
|
271
|
+
rescue
|
272
|
+
false
|
273
|
+
end
|
274
|
+
|
275
|
+
def validate_files_and_paths!
|
276
|
+
log = Pathname.new(@log_file).dirname
|
277
|
+
pid = Pathname.new(@pid_file).dirname
|
278
|
+
|
279
|
+
unless File.directory?(log)
|
280
|
+
FileUtils.mkdir_p(log.to_s)
|
281
|
+
end
|
282
|
+
|
283
|
+
unless File.directory?(pid)
|
284
|
+
FileUtils.mkdir_p(pid.to_s)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def validate_callbacks!
|
289
|
+
unless Control.class_variable_defined?(:"@@start")
|
290
|
+
raise('You must define a "start" callback.')
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def execute_callback(callback_name)
|
295
|
+
@error_count += 1 if callback_name == :error
|
296
|
+
|
297
|
+
if Control.class_variable_defined?(:"@@#{callback_name}")
|
298
|
+
callback = Control.class_variable_get(:"@@#{callback_name}")
|
299
|
+
|
300
|
+
if callback.kind_of?(Symbol)
|
301
|
+
|
302
|
+
unless self.respond_to?(callback.to_sym)
|
303
|
+
raise("Undefined callback method: #{callback}")
|
304
|
+
end
|
305
|
+
|
306
|
+
self.send(callback.to_sym)
|
307
|
+
|
308
|
+
elsif callback.respond_to?(:call)
|
309
|
+
|
310
|
+
self.instance_eval(&callback)
|
311
|
+
|
312
|
+
else
|
313
|
+
nil
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
def fetch_pid
|
321
|
+
IO.read(@pid_file).to_i
|
322
|
+
rescue
|
323
|
+
nil
|
324
|
+
end
|
325
|
+
|
326
|
+
private :validate_callbacks!, :fetch_pid,
|
327
|
+
:validate_files_and_paths!, :execute_callback
|
328
|
+
|
329
|
+
end # class Control
|
330
|
+
|
331
|
+
end # module Pidly
|
data/lib/pidly/logger.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Pidly
|
2
|
+
#
|
3
|
+
# Logger namespace
|
4
|
+
#
|
5
|
+
module Logger
|
6
|
+
|
7
|
+
attr_accessor :verbosity
|
8
|
+
|
9
|
+
#
|
10
|
+
# Verbose
|
11
|
+
#
|
12
|
+
# @return [true, false] Is the logging level verbose?
|
13
|
+
#
|
14
|
+
def verbose?
|
15
|
+
@verbosity
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Log
|
20
|
+
#
|
21
|
+
# @param [String, Symbol] type Log type (info or error)
|
22
|
+
# @param [String] message Log message
|
23
|
+
#
|
24
|
+
# @return [Strign] Log message
|
25
|
+
#
|
26
|
+
def log(type, message)
|
27
|
+
case type.to_sym
|
28
|
+
when :info
|
29
|
+
msg = message
|
30
|
+
when :error
|
31
|
+
msg = message
|
32
|
+
end
|
33
|
+
|
34
|
+
@messages << msg
|
35
|
+
puts msg if verbose?
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Extend and include callback methods
|
40
|
+
#
|
41
|
+
# @param [Class] receiver The calling class
|
42
|
+
#
|
43
|
+
def self.included(receiver)
|
44
|
+
receiver.extend self
|
45
|
+
end
|
46
|
+
|
47
|
+
end # class Logger
|
48
|
+
|
49
|
+
end # module Pidly
|
data/pidly.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
begin
|
4
|
+
Ore::Specification.new do |gemspec|
|
5
|
+
# custom logic here
|
6
|
+
end
|
7
|
+
rescue NameError
|
8
|
+
begin
|
9
|
+
require 'ore/specification'
|
10
|
+
retry
|
11
|
+
rescue LoadError
|
12
|
+
STDERR.puts "The '#{__FILE__}' file requires Ore."
|
13
|
+
STDERR.puts "Run `gem install ore-core` to install Ore."
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pidly'
|
3
|
+
|
4
|
+
describe Control do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
@daemon = Test.spawn(
|
8
|
+
:name => 'test',
|
9
|
+
:path => '/tmp',
|
10
|
+
:verbose => false
|
11
|
+
)
|
12
|
+
@daemon.kill if @daemon.running?
|
13
|
+
@daemon.start
|
14
|
+
sleep 1
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should be running" do
|
18
|
+
@daemon.running?.should == true
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should have an active status, name and pid" do
|
22
|
+
@daemon.status
|
23
|
+
reply = "#{@daemon.name} is running (PID #{@daemon.pid})"
|
24
|
+
@daemon.messages.last.should == reply
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should have the correct pid path" do
|
28
|
+
@daemon.pid_file.should == "/tmp/pids/#{@daemon.name}.pid"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should have a pid file that exists" do
|
32
|
+
File.exists?("/tmp/pids/#{@daemon.name}.pid").should == true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should have the correct log file" do
|
36
|
+
@daemon.log_file.should == "/tmp/logs/#{@daemon.name}.log"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should have a log file that exists" do
|
40
|
+
File.exists?("/tmp/logs/#{@daemon.name}.log").should == true
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should fail when trying to start another daemon" do
|
44
|
+
@daemon.start
|
45
|
+
reply = "#{@daemon.name} is already running (PID #{@daemon.pid})"
|
46
|
+
|
47
|
+
@daemon.messages.last.should == reply
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should write to the log file" do
|
51
|
+
file = File.open(@daemon.log_file, 'r')
|
52
|
+
file.read.should =~ /TEST FROM #{@daemon.pid}/
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should write to the pid file" do
|
56
|
+
file = File.open(@daemon.pid_file, 'r')
|
57
|
+
file.read.should =~ /#{@daemon.pid}/
|
58
|
+
end
|
59
|
+
|
60
|
+
after(:all) do
|
61
|
+
@daemon.kill if @daemon
|
62
|
+
FileUtils.rm @daemon.log_file.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/spec/pidly_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
gem 'rspec', '~> 2.4'
|
2
|
+
require 'rspec'
|
3
|
+
|
4
|
+
require 'pp'
|
5
|
+
require 'pidly'
|
6
|
+
include Pidly
|
7
|
+
|
8
|
+
class Test < Pidly::Control
|
9
|
+
|
10
|
+
before_start do
|
11
|
+
"BEFORE START #{@pid}"
|
12
|
+
end
|
13
|
+
|
14
|
+
start :when_daemon_starts
|
15
|
+
|
16
|
+
stop do
|
17
|
+
"Attempting to kill process: #{@pid}"
|
18
|
+
end
|
19
|
+
|
20
|
+
after_stop :test_after_daemon_stops
|
21
|
+
|
22
|
+
error do
|
23
|
+
"SENDING EMAIL | Error Count: #{@error_count}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def when_daemon_starts
|
27
|
+
loop do
|
28
|
+
print "TEST FROM #{@pid}"
|
29
|
+
sleep 2
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pidly
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dustin Willis Webber
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-04-06 00:00:00 -04:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: ore-tasks
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0.4"
|
25
|
+
type: :development
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ~>
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "2.4"
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: yard
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.6.0
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
description: Pidly is a very minimalistic daemon library that doesnt make assumptions. Pidly allows you to control the daemon without getting in the way with forced verbose output and usage messages.
|
50
|
+
email: []
|
51
|
+
|
52
|
+
executables: []
|
53
|
+
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files:
|
57
|
+
- README.md
|
58
|
+
- ChangeLog.rdoc
|
59
|
+
- LICENSE.txt
|
60
|
+
files:
|
61
|
+
- .document
|
62
|
+
- .rspec
|
63
|
+
- .yardopts
|
64
|
+
- ChangeLog.rdoc
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- gemspec.yml
|
69
|
+
- lib/pidly.rb
|
70
|
+
- lib/pidly/callbacks.rb
|
71
|
+
- lib/pidly/control.rb
|
72
|
+
- lib/pidly/logger.rb
|
73
|
+
- lib/pidly/version.rb
|
74
|
+
- pidly.gemspec
|
75
|
+
- spec/control_spec.rb
|
76
|
+
- spec/pidly_spec.rb
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
has_rdoc: yard
|
79
|
+
homepage: https://github.com/mephux/pidly
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: "0"
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: "0"
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project: pidly
|
102
|
+
rubygems_version: 1.6.1
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: A super simple daemon library.
|
106
|
+
test_files:
|
107
|
+
- spec/control_spec.rb
|
108
|
+
- spec/pidly_spec.rb
|