djinn 0.0.1 → 0.0.2
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/.gitignore +3 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/djinn.gemspec +9 -2
- data/example/basic.rb +28 -0
- data/example/em.rb +58 -0
- data/lib/djinn.rb +104 -0
- data/lib/djinn/logging_helpers.rb +21 -0
- data/lib/djinn/pid_file.rb +30 -0
- data/lib/djinn/rails/boot_daemon.rb +29 -0
- data/lib/djinn/rails/daemon_base.rb +99 -0
- data/lib/djinn/tonic.rb +55 -0
- metadata +11 -4
data/.gitignore
CHANGED
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ begin
|
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "djinn"
|
8
|
-
gem.summary = %Q{
|
8
|
+
gem.summary = %Q{Simple helper for creating daemons}
|
9
9
|
gem.description = %Q{Helper for creating custom daemon, mostly for rails}
|
10
10
|
gem.email = "darksavant@gmail.com"
|
11
11
|
gem.homepage = "http://github.com/craigp/djinn"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/djinn.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{djinn}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Craig Paterson"]
|
@@ -24,7 +24,14 @@ Gem::Specification.new do |s|
|
|
24
24
|
"Rakefile",
|
25
25
|
"VERSION",
|
26
26
|
"djinn.gemspec",
|
27
|
+
"example/basic.rb",
|
28
|
+
"example/em.rb",
|
27
29
|
"lib/djinn.rb",
|
30
|
+
"lib/djinn/logging_helpers.rb",
|
31
|
+
"lib/djinn/pid_file.rb",
|
32
|
+
"lib/djinn/rails/boot_daemon.rb",
|
33
|
+
"lib/djinn/rails/daemon_base.rb",
|
34
|
+
"lib/djinn/tonic.rb",
|
28
35
|
"test/helper.rb",
|
29
36
|
"test/test_djinn.rb"
|
30
37
|
]
|
@@ -32,7 +39,7 @@ Gem::Specification.new do |s|
|
|
32
39
|
s.rdoc_options = ["--charset=UTF-8"]
|
33
40
|
s.require_paths = ["lib"]
|
34
41
|
s.rubygems_version = %q{1.3.7}
|
35
|
-
s.summary = %q{
|
42
|
+
s.summary = %q{Simple helper for creating daemons}
|
36
43
|
s.test_files = [
|
37
44
|
"test/helper.rb",
|
38
45
|
"test/test_djinn.rb"
|
data/example/basic.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift(File.join(File.dirname(__FILE__), "../lib/"))
|
4
|
+
|
5
|
+
require 'djinn'
|
6
|
+
require 'rubygems'
|
7
|
+
|
8
|
+
class Basic
|
9
|
+
|
10
|
+
include Djinn
|
11
|
+
|
12
|
+
# not providing a "perform" method falls back to the
|
13
|
+
# base method in Djinn..
|
14
|
+
|
15
|
+
def handle_exit
|
16
|
+
puts "Handling a nice graceful exit myself.."
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
puts "Running for 10 secs and then stopping.."
|
23
|
+
|
24
|
+
djinn = Basic.new
|
25
|
+
# djinn.run
|
26
|
+
djinn.start
|
27
|
+
sleep(10)
|
28
|
+
djinn.stop
|
data/example/em.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), "../lib/"))
|
2
|
+
|
3
|
+
require 'djinn'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'eventmachine'
|
6
|
+
|
7
|
+
WORKER_INTERVAL = 5
|
8
|
+
|
9
|
+
class Worker
|
10
|
+
|
11
|
+
include EM::Deferrable
|
12
|
+
|
13
|
+
def do_stuff
|
14
|
+
puts "Worker doing stuff.."
|
15
|
+
succeed(Time.now)
|
16
|
+
rescue => e
|
17
|
+
fail(e)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class EventMachineDjinn
|
23
|
+
|
24
|
+
include Djinn
|
25
|
+
|
26
|
+
def perform
|
27
|
+
|
28
|
+
worker = EM.spawn do
|
29
|
+
worker = Worker.new
|
30
|
+
worker.callback { |time| puts "Worker completed at: #{time}" }
|
31
|
+
worker.errback { |ex| puts "Twitter worker failed: #{ex}" }
|
32
|
+
worker.do_stuff
|
33
|
+
end
|
34
|
+
|
35
|
+
EM.run do
|
36
|
+
log "Workers will run every #{WORKER_INTERVAL} secs"
|
37
|
+
EM::PeriodicTimer.new(WORKER_INTERVAL) do
|
38
|
+
worker.notify
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def handle_exit
|
45
|
+
EM.stop
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
puts "Running for 30 secs and then stopping.."
|
52
|
+
|
53
|
+
djinn = EventMachineDjinn.new
|
54
|
+
djinn.start
|
55
|
+
sleep(30)
|
56
|
+
djinn.stop
|
57
|
+
|
58
|
+
|
data/lib/djinn.rb
CHANGED
@@ -0,0 +1,104 @@
|
|
1
|
+
module Djinn
|
2
|
+
|
3
|
+
require 'djinn/tonic'
|
4
|
+
require 'djinn/pid_file'
|
5
|
+
require 'djinn/logging_helpers'
|
6
|
+
|
7
|
+
include Djinn::Tonic
|
8
|
+
include Djinn::LoggingHelpers
|
9
|
+
|
10
|
+
attr_reader :config
|
11
|
+
|
12
|
+
def log msg
|
13
|
+
puts "#{Time.now.strftime("%m/%d/%Y %H:%M:%S")}: #{msg}"
|
14
|
+
STDOUT.flush
|
15
|
+
end
|
16
|
+
|
17
|
+
def perform
|
18
|
+
# base implementation does nothing worthwhile
|
19
|
+
trap('TERM') { handle_exit }
|
20
|
+
trap('INT') { handle_exit }
|
21
|
+
while true
|
22
|
+
log("[#{name}] Djinn is running..")
|
23
|
+
sleep(5)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def handle_exit
|
28
|
+
# override this with useful exit code if you need it
|
29
|
+
exit(0)
|
30
|
+
end
|
31
|
+
|
32
|
+
def start config={}
|
33
|
+
@config = (config.empty?) ? load_config : config.empty?
|
34
|
+
log "Starting #{underscore(name)} in the background.."
|
35
|
+
logfile = get_logfile(config)
|
36
|
+
daemonize(logfile, get_pidfile(config)) do
|
37
|
+
trap('TERM') { handle_exit }
|
38
|
+
trap('INT') { handle_exit }
|
39
|
+
perform
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def run config={}
|
44
|
+
@config = (config.empty?) ? load_config : config.empty?
|
45
|
+
log "Starting #{underscore(name)} in the foreground.."
|
46
|
+
trap('TERM') { handle_exit }
|
47
|
+
trap('INT') { handle_exit }
|
48
|
+
perform
|
49
|
+
end
|
50
|
+
|
51
|
+
def restart
|
52
|
+
stop
|
53
|
+
start
|
54
|
+
end
|
55
|
+
|
56
|
+
def stop
|
57
|
+
pidfile = get_pidfile(load_config)
|
58
|
+
log 'No such process' and exit unless pidfile.pid
|
59
|
+
begin
|
60
|
+
log "Sending TERM signal to process #{pidfile.pid}"
|
61
|
+
Process.kill("TERM", pidfile.pid)
|
62
|
+
rescue
|
63
|
+
log 'Could not find process'
|
64
|
+
ensure
|
65
|
+
pidfile.remove
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def name
|
72
|
+
self.class
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_pidfile config
|
76
|
+
pid_file_path = config[:pid_file_path] ||
|
77
|
+
File.join(Dir.pwd, "#{underscore(name)}.pid")
|
78
|
+
PidFile.new(pid_file_path)
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_logfile config
|
82
|
+
log_file_path = config[:log_file_path] ||
|
83
|
+
File.join(Dir.pwd, "#{underscore(name)}.log")
|
84
|
+
log_file_path
|
85
|
+
end
|
86
|
+
|
87
|
+
def load_config
|
88
|
+
config_path = File.join(Dir.pwd, "#{underscore(name)}.yml")
|
89
|
+
if File.exists?(config_path)
|
90
|
+
YAML.load_file(config_path)
|
91
|
+
else
|
92
|
+
{}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def underscore(camel_cased_word)
|
97
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
98
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
99
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
100
|
+
tr("-", "_").
|
101
|
+
downcase
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Djinn
|
2
|
+
module LoggingHelpers
|
3
|
+
|
4
|
+
def green text
|
5
|
+
colorize 32, text
|
6
|
+
end
|
7
|
+
|
8
|
+
def red text
|
9
|
+
colorize 31, text
|
10
|
+
end
|
11
|
+
|
12
|
+
def cyan text
|
13
|
+
colorize 36, text
|
14
|
+
end
|
15
|
+
|
16
|
+
def colorize color, text
|
17
|
+
"\033[#{color}m#{text}\033[0m"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Djinn
|
2
|
+
class PidFile
|
3
|
+
|
4
|
+
attr_reader :file
|
5
|
+
|
6
|
+
def initialize(file)
|
7
|
+
@file = file
|
8
|
+
end
|
9
|
+
|
10
|
+
def pid
|
11
|
+
File.exists?(@file) and IO.read(@file).to_i
|
12
|
+
end
|
13
|
+
|
14
|
+
def remove
|
15
|
+
File.unlink(@file) if pid
|
16
|
+
end
|
17
|
+
|
18
|
+
def create
|
19
|
+
File.open(@file, "w") { |f| f.write($$) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def ensure_empty!(msg = nil)
|
23
|
+
if self.pid
|
24
|
+
$stdout.puts msg if msg
|
25
|
+
exit 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift(File.dirname(__FILE__))
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
environment = ENV["RAILS_ENV"] || "development"
|
9
|
+
@action = "run"
|
10
|
+
|
11
|
+
OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: social_daemon [options] {start|stop|restart|run}"
|
13
|
+
|
14
|
+
opts.on("-e", "--environment ENV", "Run in specific Rails environment") do |e|
|
15
|
+
environment = e
|
16
|
+
end
|
17
|
+
|
18
|
+
@action = opts.permute!(ARGV)
|
19
|
+
# (puts opts; exit(1)) unless action.size == 1
|
20
|
+
|
21
|
+
@action = @action.first || "run"
|
22
|
+
(puts opts; exit(1)) unless %w(start stop restart run).include?(@action)
|
23
|
+
|
24
|
+
end.parse!
|
25
|
+
|
26
|
+
ENV["RAILS_ENV"] = environment
|
27
|
+
|
28
|
+
require 'daemon_base'
|
29
|
+
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Djinn
|
2
|
+
module Rails
|
3
|
+
class Base
|
4
|
+
|
5
|
+
require 'yaml'
|
6
|
+
require 'daemon'
|
7
|
+
require 'pid_file'
|
8
|
+
require 'daemon_logging'
|
9
|
+
|
10
|
+
extend Djinn::Daemon
|
11
|
+
include Djinn::LoggingHelpers
|
12
|
+
|
13
|
+
def log m
|
14
|
+
DaemonBase.log m
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
def log m
|
20
|
+
puts "#{Time.now.strftime("%m/%d/%Y %H:%M:%S")}: #{m}"
|
21
|
+
STDOUT.flush
|
22
|
+
end
|
23
|
+
|
24
|
+
def perform config
|
25
|
+
raise "Not implemented"
|
26
|
+
end
|
27
|
+
|
28
|
+
def start
|
29
|
+
log "Starting #{underscore(self.name)} in the background.."
|
30
|
+
config = load_config
|
31
|
+
logfile = get_logfile(config)
|
32
|
+
daemonize(config, logfile, get_pidfile(config)) do
|
33
|
+
load_rails
|
34
|
+
self.new(config).do_stuff
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def run
|
39
|
+
log "Starting #{underscore(self.name)} in the foreground.."
|
40
|
+
load_rails
|
41
|
+
self.new(load_config).do_stuff
|
42
|
+
end
|
43
|
+
|
44
|
+
def restart
|
45
|
+
stop
|
46
|
+
start
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop
|
50
|
+
pidfile = get_pidfile(load_config)
|
51
|
+
log 'No such process' and exit unless pidfile.pid
|
52
|
+
begin
|
53
|
+
log "Sending TERM signal to process #{pidfile.pid}"
|
54
|
+
Process.kill("TERM", pidfile.pid)
|
55
|
+
rescue
|
56
|
+
log 'Could not find process'
|
57
|
+
ensure
|
58
|
+
pidfile.remove
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def get_pidfile(config)
|
65
|
+
PidFile.new(File.join(File.dirname(__FILE__), '..', '..', 'log', config['pid_file']))
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_logfile(config)
|
69
|
+
File.join(File.dirname(__FILE__), '..', '..', 'log', config['log_file'])
|
70
|
+
end
|
71
|
+
|
72
|
+
def load_rails
|
73
|
+
log "Loading Rails in #{ENV['RAILS_ENV']} environment"
|
74
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'config', 'environment')
|
75
|
+
end
|
76
|
+
|
77
|
+
def load_config
|
78
|
+
path = File.join(File.dirname(__FILE__), '..', '..', 'config', "#{underscore(self.name)}.yml")
|
79
|
+
unless File.exists?(path)
|
80
|
+
log "No config file for daemon: #{path}"
|
81
|
+
exit(1)
|
82
|
+
else
|
83
|
+
YAML.load_file(path)[ENV['RAILS_ENV']]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def underscore(camel_cased_word)
|
88
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
89
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
90
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
91
|
+
tr("-", "_").
|
92
|
+
downcase
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/djinn/tonic.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Djinn
|
2
|
+
module Tonic
|
3
|
+
|
4
|
+
def daemonize(logfile, pidfile, &block)
|
5
|
+
|
6
|
+
pidfile.ensure_empty! "ERROR: It looks like I'm already running. Not starting."
|
7
|
+
|
8
|
+
puts "Djinn is leaving process #{$$}"
|
9
|
+
|
10
|
+
srand # Split rand streams between spawning and daemonized process
|
11
|
+
#fork and exit # Fork and exit from the parent
|
12
|
+
fork do
|
13
|
+
|
14
|
+
puts "Daemonizing on process #{$$}"
|
15
|
+
puts system("ps aux | grep #{$$}")
|
16
|
+
|
17
|
+
# trap('TERM') { do_exit }
|
18
|
+
# trap('INT') { do_exit }
|
19
|
+
|
20
|
+
#Dir.chdir "/" # Release old working directory
|
21
|
+
File.umask 0000 # Ensure sensible umask
|
22
|
+
|
23
|
+
puts 'Making sure all file descriptors are closed'
|
24
|
+
ObjectSpace.each_object(IO) do |io|
|
25
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
26
|
+
begin
|
27
|
+
io.close unless io.closed?
|
28
|
+
rescue ::Exception
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
puts "Writing PID file: #{pidfile.file}"
|
34
|
+
pidfile.create
|
35
|
+
|
36
|
+
puts 'Detaching from the controlling terminal'
|
37
|
+
unless sess_id = Process.setsid
|
38
|
+
raise 'cannot detach from controlling terminal'
|
39
|
+
end
|
40
|
+
|
41
|
+
# Redirect IO
|
42
|
+
puts "Logging to: #{logfile}"
|
43
|
+
STDIN.reopen('/dev/null')
|
44
|
+
STDOUT.reopen(logfile, 'a')
|
45
|
+
STDERR.reopen(STDOUT)
|
46
|
+
|
47
|
+
yield
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: djinn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Craig Paterson
|
@@ -51,7 +51,14 @@ files:
|
|
51
51
|
- Rakefile
|
52
52
|
- VERSION
|
53
53
|
- djinn.gemspec
|
54
|
+
- example/basic.rb
|
55
|
+
- example/em.rb
|
54
56
|
- lib/djinn.rb
|
57
|
+
- lib/djinn/logging_helpers.rb
|
58
|
+
- lib/djinn/pid_file.rb
|
59
|
+
- lib/djinn/rails/boot_daemon.rb
|
60
|
+
- lib/djinn/rails/daemon_base.rb
|
61
|
+
- lib/djinn/tonic.rb
|
55
62
|
- test/helper.rb
|
56
63
|
- test/test_djinn.rb
|
57
64
|
has_rdoc: true
|
@@ -87,7 +94,7 @@ rubyforge_project:
|
|
87
94
|
rubygems_version: 1.3.7
|
88
95
|
signing_key:
|
89
96
|
specification_version: 3
|
90
|
-
summary:
|
97
|
+
summary: Simple helper for creating daemons
|
91
98
|
test_files:
|
92
99
|
- test/helper.rb
|
93
100
|
- test/test_djinn.rb
|