forever 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in forever.gemspec
4
+ gemspec
@@ -0,0 +1,114 @@
1
+ # Forever
2
+
3
+ Small daemon framework for ruby, with logging, error handler watcher and much more.
4
+
5
+ ## Why?
6
+
7
+ There are a lot of alternatives, one of the best is [resque](https://github.com/defunkt/resque), so why another daemons framework?
8
+ In my servers I've several daemons and what I need is:
9
+
10
+ * easily watch the process (memory, cpu)
11
+ * easily manage exceptions
12
+ * easily see logs
13
+ * easily start/stop/restart daemon
14
+
15
+ As like [sinatra](https://github.com/sinatra/sinatra) and [padrino](https://github.com/padrino/padrino-framework) I need a
16
+ **thin** framework to do these jobs in few seconds. This mean that:
17
+
18
+ 1) I can create a new job quickly
19
+ 2) I can watch, start, stop it quickly
20
+
21
+ So, if you have my needs, **Forever** can be the right choice for you.
22
+
23
+ ## Install:
24
+
25
+ ``` sh
26
+ $ gem install forever
27
+ ```
28
+
29
+ ## Deamon Example:
30
+
31
+ Place your script under your standard directory, generally on my env is _bin_ or _scripts_.
32
+
33
+ In that case is: ```bin/foo```
34
+
35
+ ``` rb
36
+ #!/usr/bin/ruby
37
+ require 'rubygems' unless defined?(Gem)
38
+ require 'forever'
39
+ require 'mail'
40
+
41
+ Forever.run do
42
+ ##
43
+ # You can set these values:
44
+ #
45
+ # dir "foo" # Default: File.expand_path('../../', __FILE__)
46
+ # file "bar" # Default: __FILE__
47
+ # log "bar.log" # Default: File.expand_path(dir, '/log/[file_name].log')
48
+ # pid "bar.pid" # Default: File.expand_path(dir, '/tmp/[file_name].pid')
49
+ #
50
+
51
+ on_error do |e|
52
+ Mail.deliver do
53
+ delivery_method :sendmail, :location => `which sendmail`.chomp
54
+ to "d.dagostino@lipsiasoft.com"
55
+ from "exceptions@lipsiasoft.com"
56
+ subject "[Foo Watcher] #{e.message}"
57
+ body "%s\n %s" % [e.message, e.backtrace.join("\n ")]
58
+ end
59
+ end
60
+
61
+ on_ready do
62
+ require 'bundler/setup'
63
+ require 'foo'
64
+ Foo.start_loop
65
+ end
66
+ end
67
+ ```
68
+
69
+ Assign right permission:
70
+
71
+ ``` sh
72
+ $ chmod +x bin/foo
73
+ ```
74
+
75
+ start the daemon:
76
+
77
+ ``` sh
78
+ $ bin/foo
79
+ ```
80
+
81
+ you should see an output like:
82
+
83
+ ```
84
+ $ bin/foo
85
+ => Process demonized with pid 19538
86
+ ```
87
+
88
+ you can stop it:
89
+
90
+ ```
91
+ $ bin/foo stop
92
+ => Found pid 19538...
93
+ => Killing process 19538...
94
+ ```
95
+
96
+ ## Monitor your daemon(s):
97
+
98
+ List daemons:
99
+
100
+ ```
101
+ $ forever list
102
+ PID RSS CPU CMD
103
+ 19838 32512 1.6 Forever: bin/githubwatcher
104
+ ```
105
+
106
+ Stop daemon(s):
107
+
108
+ ```
109
+ $ forever stop foo
110
+ Do you want really stop Forever: bin/foo with pid 19538? y
111
+ Killing process Forever: bin/foo with pid 19538...
112
+ ```
113
+
114
+ That's all!
@@ -0,0 +1,6 @@
1
+ require 'rubygems' unless defined?(Gem)
2
+ require 'bundler/gem_tasks'
3
+
4
+ task :clean do
5
+ `rm -rf pkg`
6
+ end
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/ruby
2
+ require 'rubygems' unless defined?(Gem)
3
+ require File.expand_path('../../lib/forever/version.rb', __FILE__)
4
+ require 'thor'
5
+
6
+ class CLI < Thor
7
+ desc "list", "List Forever daemons"
8
+ def list
9
+ say "PID\tRSS\tCPU\tCMD", :green
10
+ puts daemons.join("\n")
11
+ end
12
+
13
+ desc "stop [DAEMON]", "Stop a specified daemon"
14
+ def stop(daemon)
15
+ found = daemons.find_all { |d| d=~/#{daemon}/i }
16
+ say "Daemon(s) matching '#{daemon}' not found", :red if found.empty?
17
+ found.each do |daemon|
18
+ daemon = daemon.split("\t")
19
+ if yes? "Do you want really stop #{daemon[-1]} with pid #{daemon[0]}?"
20
+ say "Killing process #{daemon[-1]} with pid #{daemon[0]}...", :green
21
+ result = `kill #{daemon[0]}`.strip
22
+ say result, :red if result != ""
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+ def daemons
29
+ `ps axo pid,rss,pcpu,command | grep -vE "^USER|grep" | grep Forever: | awk '{print $1"\t"$2"\t"$3"\t"$4" "$5" "$6}'`.chomp.split("\n")
30
+ end
31
+ end
32
+
33
+ ARGV << "-h" if ARGV.empty?
34
+ CLI.start(ARGV)
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "forever/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "forever"
7
+ s.version = Forever::VERSION
8
+ s.authors = ["DAddYE"]
9
+ s.email = ["d.dagostino@lipsiasoft.com"]
10
+ s.homepage = "https://github.com/daddye/forever"
11
+ s.summary = %q{Small daemon framework for ruby}
12
+ s.description = %q{Small daemon framework for ruby, with logging, error handler and more...}
13
+
14
+ s.rubyforge_project = "forever"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ s.add_dependency 'thor', '~>0.14.6'
21
+ end
@@ -0,0 +1,12 @@
1
+ require "forever/base"
2
+
3
+ module Forever
4
+ extend self
5
+
6
+ def run(options={}, &block)
7
+ caller_file = caller(1).map { |line| line.split(/:(?=\d|in )/)[0,1] }.flatten.first
8
+ options[:dir] ||= File.expand_path('../../', caller_file) # => we presume we are calling it from a bin|script dir
9
+ options[:file] ||= File.expand_path(caller_file)
10
+ Base.new(options, &block)
11
+ end # run
12
+ end # Forever
@@ -0,0 +1,126 @@
1
+ require 'fileutils'
2
+
3
+ module Forever
4
+ class Base
5
+ def initialize(options={}, &block)
6
+ options.each { |k,v| send(k, v) }
7
+
8
+ Dir.chdir(dir) if exists?(dir)
9
+ FileUtils.mkdir(File.dirname(log), :noop => true) if exists?(log)
10
+ FileUtils.mkdir(File.dirname(pid), :noop => true) if exists?(pid)
11
+
12
+ instance_eval(&block)
13
+
14
+ stop!
15
+
16
+ return if ARGV[0] == "stop" || on_ready.nil?
17
+
18
+ fork do
19
+ $0 = "Forever: #{$0}"
20
+ puts "=> Process demonized with pid #{Process.pid}"
21
+
22
+ %w(INT TERM KILL).each { |signal| trap(signal) { stop! } }
23
+
24
+ File.open(pid, "w") { |f| f.write(Process.pid.to_s) }
25
+
26
+ stream = exists?(log) ? File.new(log, "w") : '/dev/null'
27
+ stream.sync = true
28
+
29
+ STDOUT.reopen(stream)
30
+ STDERR.reopen(STDOUT)
31
+
32
+ begin
33
+ on_ready.call
34
+ rescue Exception => e
35
+ Thread.list.reject { |t| t==Thread.current }.map(&:kill)
36
+ on_error[e] if on_error
37
+ stream.print "\n\n%s\n %s\n\n" % [e.message, e.backtrace.join("\n ")]
38
+ sleep 30
39
+ retry
40
+ end
41
+ end
42
+ end
43
+
44
+ ##
45
+ # Caller file
46
+ #
47
+ def file(value=nil)
48
+ value ? @_file = value : @_file
49
+ end
50
+
51
+ ##
52
+ # Base working Directory
53
+ #
54
+ def dir(value=nil)
55
+ value ? @_dir = value : @_dir
56
+ end
57
+
58
+ ##
59
+ # File were we redirect STOUT and STDERR, can be false.
60
+ #
61
+ # Default: dir + 'log/[process_name].log'
62
+ #
63
+ def log(value=nil)
64
+ @_log ||= File.join(dir, "log/#{File.basename(file)}.log") if exists?(dir, file)
65
+ value ? @_log = value : @_log
66
+ end
67
+
68
+ ##
69
+ # File were we store pid
70
+ #
71
+ # Default: dir + 'tmp/[process_name].pid'
72
+ #
73
+ def pid(value=nil)
74
+ @_pid ||= File.join(dir, "tmp/#{File.basename(file)}.pid") if exists?(dir, file)
75
+ value ? @_pid = value : @_pid
76
+ end
77
+
78
+ ##
79
+ # Search if there is a running process and stop it
80
+ #
81
+ def stop!(kill=true)
82
+ if exists?(pid)
83
+ _pid = File.read(pid).to_i
84
+ puts "=> Found pid #{_pid}..."
85
+ FileUtils.rm_f(pid)
86
+ begin
87
+ puts "=> Killing process #{_pid}..."
88
+ Process.kill(:KILL, _pid)
89
+ rescue Errno::ESRCH => e
90
+ puts "=> #{e.message}"
91
+ end
92
+ else
93
+ puts "=> Pid not found, process seems don't exist!"
94
+ end
95
+ end
96
+
97
+ ##
98
+ # Callback raised when an error occour
99
+ #
100
+ def on_error(&block)
101
+ block_given? ? @_on_error = block : @_on_error
102
+ end
103
+
104
+ ##
105
+ # Callback to fire when the daemon start
106
+ #
107
+ def on_ready(&block)
108
+ block_given? ? @_on_error = block : @_on_error
109
+ end
110
+
111
+ def to_s
112
+ "#<Forever dir:#{dir}, file:#{file}, log:#{log}, pid:#{pid}>"
113
+ end
114
+ alias :inspect :to_s
115
+
116
+ def config
117
+ { :dir => dir, :file => file, :log => log, :pid => pid }.to_yaml
118
+ end
119
+ alias :to_yaml :config
120
+
121
+ private
122
+ def exists?(*values)
123
+ values.all? { |value| value && File.exist?(value) }
124
+ end
125
+ end # Base
126
+ end # Forever
@@ -0,0 +1,3 @@
1
+ module Forever
2
+ VERSION = "0.0.8"
3
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: forever
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 8
9
+ version: 0.0.8
10
+ platform: ruby
11
+ authors:
12
+ - DAddYE
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-07-06 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: thor
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 14
30
+ - 6
31
+ version: 0.14.6
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: Small daemon framework for ruby, with logging, error handler and more...
35
+ email:
36
+ - d.dagostino@lipsiasoft.com
37
+ executables:
38
+ - forever
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - README.md
47
+ - Rakefile
48
+ - bin/forever
49
+ - forever.gemspec
50
+ - lib/forever.rb
51
+ - lib/forever/base.rb
52
+ - lib/forever/version.rb
53
+ has_rdoc: true
54
+ homepage: https://github.com/daddye/forever
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options: []
59
+
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ requirements: []
77
+
78
+ rubyforge_project: forever
79
+ rubygems_version: 1.3.6
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: Small daemon framework for ruby
83
+ test_files: []
84
+