foreverb 0.1.7 → 0.1.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.
- 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
|