djinn 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -19,3 +19,6 @@ rdoc
19
19
  pkg
20
20
 
21
21
  ## PROJECT::SPECIFIC
22
+ *.log
23
+ log
24
+ **/*.log
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{Poorly-named Daemon Helper}
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
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.1"
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{Poorly-named Daemon Helper}
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
@@ -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: 29
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
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: Poorly-named Daemon Helper
97
+ summary: Simple helper for creating daemons
91
98
  test_files:
92
99
  - test/helper.rb
93
100
  - test/test_djinn.rb