forever 0.0.8

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.
@@ -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
+