foreverb 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.md +79 -6
- data/Rakefile +15 -1
- data/bin/foreverb +12 -7
- data/examples/sample +44 -0
- data/foreverb.gemspec +3 -3
- data/lib/forever/base.rb +43 -11
- data/lib/forever/every.rb +46 -0
- data/lib/forever/numeric.rb +13 -0
- data/lib/forever/version.rb +1 -1
- metadata +8 -5
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,16 +1,13 @@
|
|
1
1
|
# Foreverb
|
2
2
|
|
3
|
-
Small daemon framework **for ruby**, with logging, error handler
|
3
|
+
Small daemon framework **for ruby**, with logging, error handler, scheduling and much more.
|
4
4
|
|
5
5
|
My inspiration was [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.
|
6
|
+
My scheduling inspiration was taken from [clockwork](https://github.com/adamwiggins/clockwork) written by Adam Wiggins.
|
10
7
|
|
11
8
|
## Why?
|
12
9
|
|
13
|
-
There are
|
10
|
+
There are some alternatives, one of the best is [resque](https://github.com/defunkt/resque), so why another daemons framework?
|
14
11
|
In my servers I've several daemons and what I need is:
|
15
12
|
|
16
13
|
* easily watch the process (memory, cpu)
|
@@ -99,6 +96,74 @@ $ bin/foo stop
|
|
99
96
|
=> Killing process 19538...
|
100
97
|
```
|
101
98
|
|
99
|
+
## Scheduling
|
100
|
+
|
101
|
+
You can use ```every``` method to schedule repetitive tasks.
|
102
|
+
|
103
|
+
``` rb
|
104
|
+
# Taken from, examples/sample
|
105
|
+
Forever.run do
|
106
|
+
dir File.expand_path('../', __FILE__) # Default is ../../__FILE__
|
107
|
+
|
108
|
+
on_ready do
|
109
|
+
puts inspect
|
110
|
+
end
|
111
|
+
|
112
|
+
every 1.seconds do
|
113
|
+
puts "Every one seconds"
|
114
|
+
end
|
115
|
+
|
116
|
+
every 2.seconds do
|
117
|
+
puts "Every two seconds"
|
118
|
+
end
|
119
|
+
|
120
|
+
every 3.seconds do
|
121
|
+
puts "Every three seconds, long task"
|
122
|
+
sleep 10
|
123
|
+
end
|
124
|
+
|
125
|
+
every 1.day, :at => "18:28" do
|
126
|
+
puts "Every day at 18:28"
|
127
|
+
end
|
128
|
+
|
129
|
+
on_error do |e|
|
130
|
+
puts "Boom raised: #{e.message}"
|
131
|
+
end
|
132
|
+
|
133
|
+
on_exit do
|
134
|
+
puts "Bye bye"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
You should see in logs this:
|
140
|
+
|
141
|
+
```
|
142
|
+
$ examples/sample
|
143
|
+
=> Pid not found, process seems don't exist!
|
144
|
+
=> Process demonized with pid 1252 with Forever v.0.1.7
|
145
|
+
[11/07 18:27:08] #<Forever dir:/Developer/src/extras/foreverb/examples, file:/Developer/src/extras/foreverb/examples/sample, log:/Developer/src/extras/foreverb/examples/log/sample.log, pid:/Developer/src/extras/foreverb/examples/tmp/sample.pid jobs:4>
|
146
|
+
[11/07 18:27:08] Every one seconds
|
147
|
+
[11/07 18:27:08] Every two seconds
|
148
|
+
[11/07 18:27:08] Every three seconds, long task
|
149
|
+
[11/07 18:27:09] Every one seconds
|
150
|
+
[11/07 18:27:10] Every two seconds
|
151
|
+
...
|
152
|
+
[11/07 18:27:17] Every one seconds
|
153
|
+
[11/07 18:27:18] Every one seconds
|
154
|
+
[11/07 18:27:18] Every two seconds
|
155
|
+
[11/07 18:27:19] Every three seconds, long task
|
156
|
+
...
|
157
|
+
[11/07 18:27:58] Every one seconds
|
158
|
+
[11/07 18:27:59] Every one seconds
|
159
|
+
[11/07 18:28:00] Every two seconds
|
160
|
+
[11/07 18:28:00] Every one seconds
|
161
|
+
[11/07 18:28:00] Every day at 18:28
|
162
|
+
=> Found pid 1252...
|
163
|
+
=> Killing process 1252...
|
164
|
+
[11/07 18:28:18] Bye bye
|
165
|
+
```
|
166
|
+
|
102
167
|
## Monitor your daemon(s):
|
103
168
|
|
104
169
|
List daemons:
|
@@ -115,10 +180,18 @@ Stop daemon(s):
|
|
115
180
|
$ foreverb stop foo
|
116
181
|
Do you want really stop Forever: bin/foo with pid 19538? y
|
117
182
|
Killing process Forever: bin/foo with pid 19538...
|
183
|
+
|
184
|
+
$ foreverb stop --all -y
|
185
|
+
Killing process Forever: /usr/bin/githubwatcher with pid 2824
|
186
|
+
Killing process Forever: examples/sample with pid 2836
|
118
187
|
```
|
119
188
|
|
120
189
|
That's all!
|
121
190
|
|
191
|
+
## Extras
|
192
|
+
|
193
|
+
To see a most comprensive app running _foreverb_ + _growl_ see [githubwatcher gem](https://github.com/daddye/githubwatcher)
|
194
|
+
|
122
195
|
## Author
|
123
196
|
|
124
197
|
DAddYE, you can follow me on twitter [@daddye](http://twitter.com/daddye)
|
data/Rakefile
CHANGED
@@ -1,2 +1,16 @@
|
|
1
1
|
require 'rubygems' unless defined?(Gem)
|
2
|
-
require 'bundler/gem_tasks'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
|
4
|
+
%w(install release).each do |task|
|
5
|
+
Rake::Task[task].enhance do
|
6
|
+
sh "rm -rf pkg"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Bump version on github"
|
11
|
+
task :bump do
|
12
|
+
version = Bundler.load_gemspec(Dir[File.expand_path('../*.gemspec', __FILE__)].first).version
|
13
|
+
sh "git add .; git commit -m \"Bump to version #{version}\""
|
14
|
+
end
|
15
|
+
|
16
|
+
task :release => :bump
|
data/bin/foreverb
CHANGED
@@ -12,14 +12,19 @@ class CLI < Thor
|
|
12
12
|
puts daemons.join("\n")
|
13
13
|
end
|
14
14
|
|
15
|
-
desc "stop [DAEMON]", "Stop a specified daemon"
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
desc "stop [DAEMON] [--all] [--yes]", "Stop a specified daemon"
|
16
|
+
method_option :all, :type => :boolean, :aliases => "-a", :desc => "Stop all daemons"
|
17
|
+
method_option :yes, :type => :boolean, :aliases => "-y", :desc => "Don't ask permission to kill process"
|
18
|
+
def stop(daemon=nil)
|
19
|
+
say "You must provide a daemon name or provide --all option", :red and return if daemon.nil? && !options.all
|
20
|
+
found = options.all ? daemons : daemons.find_all { |d| d=~/#{daemon}/i }
|
21
|
+
say "Daemon(s) matching '#{daemon}' not found", :red if found.empty? && !daemon.nil?
|
22
|
+
say "Daemons not found", :red if found.empty? && options.all && daemon.nil?
|
23
|
+
|
19
24
|
found.each do |daemon|
|
20
|
-
daemon = daemon.split("\t")
|
21
|
-
if yes?
|
22
|
-
say "Killing process #{daemon[-1]} with pid #{daemon[0]}
|
25
|
+
daemon = daemon.split("\t").map(&:strip)
|
26
|
+
if options.yes || yes?("Do you want really stop \e[1m#{daemon[-1]}\e[0m with pid \e[1m#{daemon[0]}\e[0m?")
|
27
|
+
say "Killing process \e[1m#{daemon[-1]}\e[0m\e[32m with pid \e[1m#{daemon[0]}", :green
|
23
28
|
result = `kill #{daemon[0]}`.strip
|
24
29
|
say result, :red if result != ""
|
25
30
|
end
|
data/examples/sample
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require 'rubygems' unless defined?(Gem)
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'forever'
|
5
|
+
|
6
|
+
$stdout_was = STDOUT.dup
|
7
|
+
|
8
|
+
Forever.run do
|
9
|
+
def puts(text=nil)
|
10
|
+
text = super(text)
|
11
|
+
$stdout_was.print text
|
12
|
+
end
|
13
|
+
|
14
|
+
dir File.expand_path('../', __FILE__) # Default is ../../__FILE__
|
15
|
+
|
16
|
+
on_ready do
|
17
|
+
puts inspect
|
18
|
+
end
|
19
|
+
|
20
|
+
every 1.seconds do
|
21
|
+
puts "Every one seconds"
|
22
|
+
end
|
23
|
+
|
24
|
+
every 2.seconds do
|
25
|
+
puts "Every two seconds"
|
26
|
+
end
|
27
|
+
|
28
|
+
every 3.seconds do
|
29
|
+
puts "Every three seconds, long task"
|
30
|
+
sleep 10
|
31
|
+
end
|
32
|
+
|
33
|
+
every 1.minutes, :at => "%d:%d" % [Time.now.hour, Time.now.min+1] do
|
34
|
+
puts "Every minutes at #{"%d:%d" % [Time.now.hour, Time.now.min]}"
|
35
|
+
end
|
36
|
+
|
37
|
+
on_error do |e|
|
38
|
+
puts "Boom raised: #{e.message}"
|
39
|
+
end
|
40
|
+
|
41
|
+
on_exit do
|
42
|
+
puts "Bye bye"
|
43
|
+
end
|
44
|
+
end
|
data/foreverb.gemspec
CHANGED
@@ -9,13 +9,13 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.email = ["d.dagostino@lipsiasoft.com"]
|
10
10
|
s.homepage = "https://github.com/daddye/forever"
|
11
11
|
s.summary = %q{Small daemon framework for ruby}
|
12
|
-
s.description = %q{Small daemon framework for ruby, with logging, error handler and more
|
12
|
+
s.description = %q{Small daemon framework for ruby, with logging, error handler, scheduling and much more.}
|
13
13
|
|
14
|
-
s.rubyforge_project = "
|
14
|
+
s.rubyforge_project = "foreverb"
|
15
15
|
|
16
16
|
s.files = `git ls-files`.split("\n")
|
17
17
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
-
s.require_paths =
|
19
|
+
s.require_paths = %w(lib)
|
20
20
|
s.add_dependency 'thor', '~>0.14.6'
|
21
21
|
end
|
data/lib/forever/base.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'fileutils'
|
2
|
+
require 'forever/every'
|
2
3
|
|
3
4
|
module Forever
|
5
|
+
DATE_FORMAT = "%d/%m %H:%M:%S"
|
6
|
+
|
4
7
|
class Base
|
8
|
+
include Every
|
9
|
+
|
5
10
|
def initialize(options={}, &block)
|
6
11
|
options.each { |k,v| send(k, v) }
|
7
12
|
|
@@ -13,11 +18,11 @@ module Forever
|
|
13
18
|
|
14
19
|
stop!
|
15
20
|
|
16
|
-
return if ARGV[0] == "stop"
|
21
|
+
return if ARGV[0] == "stop"
|
17
22
|
|
18
23
|
fork do
|
19
24
|
$0 = "Forever: #{$0}"
|
20
|
-
|
25
|
+
print "=> Process demonized with pid #{Process.pid} with Forever v.#{Forever::VERSION}\n"
|
21
26
|
|
22
27
|
%w(INT TERM KILL).each { |signal| trap(signal) { stop! } }
|
23
28
|
|
@@ -29,10 +34,18 @@ module Forever
|
|
29
34
|
STDOUT.reopen(stream)
|
30
35
|
STDERR.reopen(STDOUT)
|
31
36
|
|
37
|
+
Thread.abort_on_exception = true
|
38
|
+
|
32
39
|
begin
|
33
|
-
|
40
|
+
threads = []
|
41
|
+
threads << Thread.new { on_ready.call } if on_ready
|
42
|
+
jobs.each do |job|
|
43
|
+
threads << Thread.new do
|
44
|
+
loop { job.call if job.time?(Time.now); sleep 1 }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
threads.map(&:join)
|
34
48
|
rescue Exception => e
|
35
|
-
Thread.list.reject { |t| t==Thread.current }.map(&:kill)
|
36
49
|
on_error[e] if on_error
|
37
50
|
stream.print "\n\n%s\n %s\n\n" % [e.message, e.backtrace.join("\n ")]
|
38
51
|
sleep 30
|
@@ -41,6 +54,17 @@ module Forever
|
|
41
54
|
end
|
42
55
|
end
|
43
56
|
|
57
|
+
##
|
58
|
+
# A replacement of puts that has the aim to works correclty with multiple threads
|
59
|
+
# and log date. If you want to personalize date, edit DATE_FORMAT.
|
60
|
+
#
|
61
|
+
def puts(text="")
|
62
|
+
text = "[%s] %s" % [Time.now.strftime(DATE_FORMAT), text.to_s]
|
63
|
+
text += "\n" unless text[-1] == ?\n
|
64
|
+
print text; $stdout.flush
|
65
|
+
text
|
66
|
+
end
|
67
|
+
|
44
68
|
##
|
45
69
|
# Caller file
|
46
70
|
#
|
@@ -62,7 +86,7 @@ module Forever
|
|
62
86
|
#
|
63
87
|
def log(value=nil)
|
64
88
|
@_log ||= File.join(dir, "log/#{File.basename(file)}.log") if exists?(dir, file)
|
65
|
-
value ? @_log
|
89
|
+
value.nil? ? @_log : @_log = value
|
66
90
|
end
|
67
91
|
|
68
92
|
##
|
@@ -72,25 +96,26 @@ module Forever
|
|
72
96
|
#
|
73
97
|
def pid(value=nil)
|
74
98
|
@_pid ||= File.join(dir, "tmp/#{File.basename(file)}.pid") if exists?(dir, file)
|
75
|
-
value ? @_pid
|
99
|
+
value.nil? ? @_pid : @_pid = value
|
76
100
|
end
|
77
101
|
|
78
102
|
##
|
79
103
|
# Search if there is a running process and stop it
|
80
104
|
#
|
81
|
-
def stop!
|
105
|
+
def stop!
|
82
106
|
if exists?(pid)
|
83
107
|
_pid = File.read(pid).to_i
|
84
|
-
|
108
|
+
print "=> Found pid #{_pid}...\n"
|
85
109
|
FileUtils.rm_f(pid)
|
86
110
|
begin
|
87
|
-
|
111
|
+
print "=> Killing process #{_pid}...\n"
|
112
|
+
on_exit.call if on_exit
|
88
113
|
Process.kill(:KILL, _pid)
|
89
114
|
rescue Errno::ESRCH => e
|
90
115
|
puts "=> #{e.message}"
|
91
116
|
end
|
92
117
|
else
|
93
|
-
|
118
|
+
print "=> Pid not found, process seems don't exist!\n"
|
94
119
|
end
|
95
120
|
end
|
96
121
|
|
@@ -101,6 +126,13 @@ module Forever
|
|
101
126
|
block_given? ? @_on_error = block : @_on_error
|
102
127
|
end
|
103
128
|
|
129
|
+
##
|
130
|
+
# Callback raised when at exit
|
131
|
+
#
|
132
|
+
def on_exit(&block)
|
133
|
+
block_given? ? @_on_exit = block : @_on_exit
|
134
|
+
end
|
135
|
+
|
104
136
|
##
|
105
137
|
# Callback to fire when the daemon start
|
106
138
|
#
|
@@ -109,7 +141,7 @@ module Forever
|
|
109
141
|
end
|
110
142
|
|
111
143
|
def to_s
|
112
|
-
"#<Forever dir:#{dir}, file:#{file}, log:#{log}, pid:#{pid}>"
|
144
|
+
"#<Forever dir:#{dir}, file:#{file}, log:#{log}, pid:#{pid} jobs:#{jobs.size}>"
|
113
145
|
end
|
114
146
|
alias :inspect :to_s
|
115
147
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'forever/numeric'
|
2
|
+
|
3
|
+
module Forever
|
4
|
+
module Every
|
5
|
+
class Job
|
6
|
+
attr_accessor :period, :option, :last, :running
|
7
|
+
|
8
|
+
def initialize(period, options, block)
|
9
|
+
@period, @options, @last, @running = period, options, 0, false
|
10
|
+
@at = parse_at(@options[:at])
|
11
|
+
@block = block
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
@last, @running = Time.now, true
|
16
|
+
@block.call
|
17
|
+
ensure
|
18
|
+
@running = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def time?(t)
|
22
|
+
ellapsed_ready = (t - @last).to_i >= @period
|
23
|
+
time_ready = @at.nil? || (t.hour == @at[0] && t.min == @at[1])
|
24
|
+
!running && ellapsed_ready && time_ready
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def parse_at(at)
|
29
|
+
return unless at.is_a?(String)
|
30
|
+
m = at.match(/^(\d{1,2}):(\d{1,2})$/)
|
31
|
+
raise "Failed to parse #{at}" unless m
|
32
|
+
hour, min = m[1].to_i, m[2].to_i
|
33
|
+
raise "Failed to parse #{at}" if hour >= 24 || min >= 60
|
34
|
+
[hour, min]
|
35
|
+
end
|
36
|
+
end # Job
|
37
|
+
|
38
|
+
def every(period, options={}, &block)
|
39
|
+
jobs << Job.new(period, options, block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def jobs
|
43
|
+
@_jobs ||= []
|
44
|
+
end
|
45
|
+
end # Every
|
46
|
+
end # Forever
|
data/lib/forever/version.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 8
|
9
|
+
version: 0.1.8
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- DAddYE
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-07-
|
17
|
+
date: 2011-07-11 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -31,7 +31,7 @@ dependencies:
|
|
31
31
|
version: 0.14.6
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
|
-
description: Small daemon framework for ruby, with logging, error handler and more
|
34
|
+
description: Small daemon framework for ruby, with logging, error handler, scheduling and much more.
|
35
35
|
email:
|
36
36
|
- d.dagostino@lipsiasoft.com
|
37
37
|
executables:
|
@@ -46,9 +46,12 @@ files:
|
|
46
46
|
- README.md
|
47
47
|
- Rakefile
|
48
48
|
- bin/foreverb
|
49
|
+
- examples/sample
|
49
50
|
- foreverb.gemspec
|
50
51
|
- lib/forever.rb
|
51
52
|
- lib/forever/base.rb
|
53
|
+
- lib/forever/every.rb
|
54
|
+
- lib/forever/numeric.rb
|
52
55
|
- lib/forever/version.rb
|
53
56
|
has_rdoc: true
|
54
57
|
homepage: https://github.com/daddye/forever
|
@@ -75,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
78
|
version: "0"
|
76
79
|
requirements: []
|
77
80
|
|
78
|
-
rubyforge_project:
|
81
|
+
rubyforge_project: foreverb
|
79
82
|
rubygems_version: 1.3.6
|
80
83
|
signing_key:
|
81
84
|
specification_version: 3
|