foreverb 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -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
data/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # Foreverb
2
+
3
+ Small daemon framework **for ruby**, with logging, error handler watcher and much more.
4
+
5
+ The idea of the name and some concepts was taken by [forever for node.js](https://raw.github.com/indexzero/forever) written by Charlie Robbins.
6
+
7
+ Please don't consider this gem as a port because is not.
8
+
9
+ The executable name it's called ```foreverb``` to prevent collisions.
10
+
11
+ ## Why?
12
+
13
+ There are a lot of alternatives, one of the best is [resque](https://github.com/defunkt/resque), so why another daemons framework?
14
+ In my servers I've several daemons and what I need is:
15
+
16
+ * easily watch the process (memory, cpu)
17
+ * easily manage exceptions
18
+ * easily see logs
19
+ * easily start/stop/restart daemon
20
+
21
+ As like [sinatra](https://github.com/sinatra/sinatra) and [padrino](https://github.com/padrino/padrino-framework) I need a
22
+ **thin** framework to do these jobs in few seconds. This mean that:
23
+
24
+ * I can create a new job quickly
25
+ * I can watch, start, stop it quickly
26
+
27
+ So, if you have my needs, **Forever** can be the right choice for you.
28
+
29
+ ## Install:
30
+
31
+ ``` sh
32
+ $ gem install forever
33
+ ```
34
+
35
+ ## Deamon Example:
36
+
37
+ Place your script under your standard directory, generally on my env is _bin_ or _scripts_.
38
+
39
+ In that case is: ```bin/foo```
40
+
41
+ ``` rb
42
+ #!/usr/bin/ruby
43
+ require 'rubygems' unless defined?(Gem)
44
+ require 'forever'
45
+ require 'mail'
46
+
47
+ Forever.run do
48
+ ##
49
+ # You can set these values:
50
+ #
51
+ # dir "foo" # Default: File.expand_path('../../', __FILE__)
52
+ # file "bar" # Default: __FILE__
53
+ # log "bar.log" # Default: File.expand_path(dir, '/log/[file_name].log')
54
+ # pid "bar.pid" # Default: File.expand_path(dir, '/tmp/[file_name].pid')
55
+ #
56
+
57
+ on_error do |e|
58
+ Mail.deliver do
59
+ delivery_method :sendmail, :location => `which sendmail`.chomp
60
+ to "d.dagostino@lipsiasoft.com"
61
+ from "exceptions@lipsiasoft.com"
62
+ subject "[Foo Watcher] #{e.message}"
63
+ body "%s\n %s" % [e.message, e.backtrace.join("\n ")]
64
+ end
65
+ end
66
+
67
+ on_ready do
68
+ require 'bundler/setup'
69
+ require 'foo'
70
+ Foo.start_loop
71
+ end
72
+ end
73
+ ```
74
+
75
+ Assign right permission:
76
+
77
+ ``` sh
78
+ $ chmod +x bin/foo
79
+ ```
80
+
81
+ start the daemon:
82
+
83
+ ``` sh
84
+ $ bin/foo
85
+ ```
86
+
87
+ you should see an output like:
88
+
89
+ ``` sh
90
+ $ bin/foo
91
+ => Process demonized with pid 19538
92
+ ```
93
+
94
+ you can stop it:
95
+
96
+ ``` sh
97
+ $ bin/foo stop
98
+ => Found pid 19538...
99
+ => Killing process 19538...
100
+ ```
101
+
102
+ ## Monitor your daemon(s):
103
+
104
+ List daemons:
105
+
106
+ ``` sh
107
+ $ foreverb list
108
+ PID RSS CPU CMD
109
+ 19838 32512 1.6 Forever: bin/githubwatcher
110
+ ```
111
+
112
+ Stop daemon(s):
113
+
114
+ ``` sh
115
+ $ foreverb stop foo
116
+ Do you want really stop Forever: bin/foo with pid 19538? y
117
+ Killing process Forever: bin/foo with pid 19538...
118
+ ```
119
+
120
+ That's all!
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'rubygems' unless defined?(Gem)
2
+ require 'bundler/gem_tasks'
data/bin/foreverb ADDED
@@ -0,0 +1,42 @@
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
+
8
+
9
+ desc "list", "List Forever daemons"
10
+ def list
11
+ say "PID\tRSS\tCPU\tCMD", :green
12
+ puts daemons.join("\n")
13
+ end
14
+
15
+ desc "stop [DAEMON]", "Stop a specified daemon"
16
+ def stop(daemon)
17
+ found = daemons.find_all { |d| d=~/#{daemon}/i }
18
+ say "Daemon(s) matching '#{daemon}' not found", :red if found.empty?
19
+ found.each do |daemon|
20
+ daemon = daemon.split("\t")
21
+ if yes? "Do you want really stop #{daemon[-1]} with pid #{daemon[0]}?"
22
+ say "Killing process #{daemon[-1]} with pid #{daemon[0]}...", :green
23
+ result = `kill #{daemon[0]}`.strip
24
+ say result, :red if result != ""
25
+ end
26
+ end
27
+ end
28
+
29
+ map "--version" => :version
30
+ desc "version", "show the version number"
31
+ def version
32
+ say "Foreverb v.#{Forever::VERSION}", :green
33
+ end
34
+
35
+ private
36
+ def daemons
37
+ `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")
38
+ end
39
+ end
40
+
41
+ ARGV << "-h" if ARGV.empty?
42
+ CLI.start(ARGV)
data/foreverb.gemspec ADDED
@@ -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 = "foreverb"
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
data/lib/forever.rb ADDED
@@ -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,251 @@
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
+ instance_eval(&block)
9
+
10
+ Dir.chdir(dir) if exists?(dir)
11
+ Dir.mkdir(File.dirname(log)) if log && !File.exist?(File.dirname(log))
12
+ Dir.mkdir(File.dirname(pid)) if pid && !File.exist?(File.dirname(pid))
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) } if pid
25
+
26
+ stream = log ? File.new(log, "w") : File.open('/dev/null', 'w')
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 # Foreverrequire 'fileutils'
127
+
128
+ module Forever
129
+ class Base
130
+ def initialize(options={}, &block)
131
+ options.each { |k,v| send(k, v) }
132
+
133
+ instance_eval(&block)
134
+
135
+ Dir.chdir(dir) if exists?(dir)
136
+ Dir.mkdir(File.dirname(log)) if log && !File.exist?(File.dirname(log))
137
+ Dir.mkdir(File.dirname(pid)) if pid && !File.exist?(File.dirname(pid))
138
+
139
+ stop!
140
+
141
+ return if ARGV[0] == "stop" || on_ready.nil?
142
+
143
+ fork do
144
+ $0 = "Forever: #{$0}"
145
+ puts "=> Process demonized with pid #{Process.pid}"
146
+
147
+ %w(INT TERM KILL).each { |signal| trap(signal) { stop! } }
148
+
149
+ File.open(pid, "w") { |f| f.write(Process.pid.to_s) } if pid
150
+
151
+ stream = log ? File.new(log, "w") : File.open('/dev/null', 'w')
152
+ stream.sync = true
153
+
154
+ STDOUT.reopen(stream)
155
+ STDERR.reopen(STDOUT)
156
+
157
+ begin
158
+ on_ready.call
159
+ rescue Exception => e
160
+ Thread.list.reject { |t| t==Thread.current }.map(&:kill)
161
+ on_error[e] if on_error
162
+ stream.print "\n\n%s\n %s\n\n" % [e.message, e.backtrace.join("\n ")]
163
+ sleep 30
164
+ retry
165
+ end
166
+ end
167
+ end
168
+
169
+ ##
170
+ # Caller file
171
+ #
172
+ def file(value=nil)
173
+ value ? @_file = value : @_file
174
+ end
175
+
176
+ ##
177
+ # Base working Directory
178
+ #
179
+ def dir(value=nil)
180
+ value ? @_dir = value : @_dir
181
+ end
182
+
183
+ ##
184
+ # File were we redirect STOUT and STDERR, can be false.
185
+ #
186
+ # Default: dir + 'log/[process_name].log'
187
+ #
188
+ def log(value=nil)
189
+ @_log ||= File.join(dir, "log/#{File.basename(file)}.log") if exists?(dir, file)
190
+ value ? @_log = value : @_log
191
+ end
192
+
193
+ ##
194
+ # File were we store pid
195
+ #
196
+ # Default: dir + 'tmp/[process_name].pid'
197
+ #
198
+ def pid(value=nil)
199
+ @_pid ||= File.join(dir, "tmp/#{File.basename(file)}.pid") if exists?(dir, file)
200
+ value ? @_pid = value : @_pid
201
+ end
202
+
203
+ ##
204
+ # Search if there is a running process and stop it
205
+ #
206
+ def stop!(kill=true)
207
+ if exists?(pid)
208
+ _pid = File.read(pid).to_i
209
+ puts "=> Found pid #{_pid}..."
210
+ FileUtils.rm_f(pid)
211
+ begin
212
+ puts "=> Killing process #{_pid}..."
213
+ Process.kill(:KILL, _pid)
214
+ rescue Errno::ESRCH => e
215
+ puts "=> #{e.message}"
216
+ end
217
+ else
218
+ puts "=> Pid not found, process seems don't exist!"
219
+ end
220
+ end
221
+
222
+ ##
223
+ # Callback raised when an error occour
224
+ #
225
+ def on_error(&block)
226
+ block_given? ? @_on_error = block : @_on_error
227
+ end
228
+
229
+ ##
230
+ # Callback to fire when the daemon start
231
+ #
232
+ def on_ready(&block)
233
+ block_given? ? @_on_error = block : @_on_error
234
+ end
235
+
236
+ def to_s
237
+ "#<Forever dir:#{dir}, file:#{file}, log:#{log}, pid:#{pid}>"
238
+ end
239
+ alias :inspect :to_s
240
+
241
+ def config
242
+ { :dir => dir, :file => file, :log => log, :pid => pid }.to_yaml
243
+ end
244
+ alias :to_yaml :config
245
+
246
+ private
247
+ def exists?(*values)
248
+ values.all? { |value| value && File.exist?(value) }
249
+ end
250
+ end # Base
251
+ end # Forever
@@ -0,0 +1,3 @@
1
+ module Forever
2
+ VERSION = "0.1.5"
3
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: foreverb
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 5
9
+ version: 0.1.5
10
+ platform: ruby
11
+ authors:
12
+ - DAddYE
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-07-08 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
+ - foreverb
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - README.md
47
+ - Rakefile
48
+ - bin/foreverb
49
+ - foreverb.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
+