foreverb 0.2.6 → 0.3.0.a
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/CHANGES.md +6 -0
- data/README.md +63 -3
- data/Rakefile +18 -8
- data/examples/{sample → complex} +3 -11
- data/examples/simple +32 -0
- data/examples/stress +12 -0
- data/foreverb.gemspec +1 -1
- data/lib/forever.rb +8 -8
- data/lib/forever/base.rb +83 -37
- data/lib/forever/every.rb +23 -10
- data/lib/forever/version.rb +1 -1
- data/spec/cli_spec.rb +19 -19
- data/spec/foreverb_spec.rb +33 -33
- data/spec/spec_helper.rb +13 -25
- metadata +21 -17
data/CHANGES.md
CHANGED
data/README.md
CHANGED
@@ -14,6 +14,8 @@ In my servers I've several daemons and what I need is:
|
|
14
14
|
* easily manage exceptions
|
15
15
|
* easily see logs
|
16
16
|
* easily start/stop/restart daemon
|
17
|
+
* no blocking jobs
|
18
|
+
* no blocking queue
|
17
19
|
|
18
20
|
As like [sinatra](https://github.com/sinatra/sinatra) and [padrino](https://github.com/padrino/padrino-framework) I need a
|
19
21
|
**thin** framework to do these jobs in few seconds. This mean that:
|
@@ -61,7 +63,7 @@ Forever.run do
|
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
64
|
-
|
66
|
+
before :each do # or if you prefer before :all
|
65
67
|
require 'bundler/setup'
|
66
68
|
require 'foo'
|
67
69
|
Foo.start_loop
|
@@ -124,7 +126,7 @@ So looking our [example](https://github.com/DAddYE/foreverb/blob/master/examples
|
|
124
126
|
Forever.run do
|
125
127
|
dir File.expand_path('../', __FILE__) # Default is ../../__FILE__
|
126
128
|
|
127
|
-
|
129
|
+
before :all do
|
128
130
|
puts "All jobs will will wait me for 1 second"; sleep 1
|
129
131
|
end
|
130
132
|
|
@@ -146,6 +148,7 @@ Forever.run do
|
|
146
148
|
|
147
149
|
every 15.seconds do
|
148
150
|
puts "Every 15 seconds, but my task require 10 seconds"; sleep 10
|
151
|
+
# This doesn't block other jobs and your queue !!!!!!!
|
149
152
|
end
|
150
153
|
|
151
154
|
every 10.seconds, :at => [":#{Time.now.min+1}", ":#{Time.now.min+2}"] do
|
@@ -207,6 +210,30 @@ you should see:
|
|
207
210
|
[14/07 15:48:40] Bye bye
|
208
211
|
```
|
209
212
|
|
213
|
+
## Filters
|
214
|
+
|
215
|
+
In foreverb we have a couple of filters, `before` and `after`, like rspec you should be able to filter `before :all` or `before :each`.
|
216
|
+
|
217
|
+
``` rb
|
218
|
+
before :all do
|
219
|
+
puts "This will be ran only at start"
|
220
|
+
end
|
221
|
+
|
222
|
+
before :each do
|
223
|
+
puts "Do that before each job"
|
224
|
+
end
|
225
|
+
|
226
|
+
# ... here jobs ...
|
227
|
+
|
228
|
+
after :all do
|
229
|
+
puts "This will be ran only at shutdown"
|
230
|
+
end
|
231
|
+
|
232
|
+
after :each do
|
233
|
+
puts "Do that after each job"
|
234
|
+
end
|
235
|
+
```
|
236
|
+
|
210
237
|
## CLI
|
211
238
|
|
212
239
|
### Help:
|
@@ -307,6 +334,8 @@ as for stop we allow `--all` and `-y`
|
|
307
334
|
|
308
335
|
## HACKS
|
309
336
|
|
337
|
+
### Bundler
|
338
|
+
|
310
339
|
Bundler has the bad behavior to load `Gemfile` from your current path, so if your `daemons` (ex: [githubwatcher](https://github.com/daddye/githubwatcher))
|
311
340
|
is shipped with their own `Gemfile` to prevent errors you must insert that line:
|
312
341
|
|
@@ -314,6 +343,37 @@ is shipped with their own `Gemfile` to prevent errors you must insert that line:
|
|
314
343
|
ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', __FILE__) # edit matching your Gemfile path
|
315
344
|
```
|
316
345
|
|
346
|
+
### Rails/Padrino prevent memory leaks
|
347
|
+
|
348
|
+
I highly suggest to use `fork` and `before` filters when you are using `forever` with frameworks, this since running same job on our ruby will eat a lot of
|
349
|
+
ram, so the better way that I found is that:
|
350
|
+
|
351
|
+
```rb
|
352
|
+
Forever.run :fork => true do
|
353
|
+
before :each do
|
354
|
+
require '/config/boot' # here the rails/padrino environment
|
355
|
+
end
|
356
|
+
|
357
|
+
every 10.seconds, :at => ['12:00', '00:00'] do
|
358
|
+
Project.all(&:perform_long_task)
|
359
|
+
end
|
360
|
+
|
361
|
+
every 1.minute do
|
362
|
+
Account.all.map(&:send_emails)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
```
|
366
|
+
|
367
|
+
This is similar to create a new process i.e.:
|
368
|
+
|
369
|
+
```rb
|
370
|
+
Process.fork do
|
371
|
+
require '/config/boot'
|
372
|
+
my_long_jobs
|
373
|
+
end
|
374
|
+
Process.waitall
|
375
|
+
```
|
376
|
+
|
317
377
|
## Extras
|
318
378
|
|
319
379
|
To see a most comprensive app running _foreverb_ + _growl_ see [githubwatcher gem](https://github.com/daddye/githubwatcher)
|
@@ -336,4 +396,4 @@ The above copyright notice and this permission notice shall be included in all c
|
|
336
396
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
337
397
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM,
|
338
398
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
339
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
399
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rubygems' unless defined?(Gem)
|
2
2
|
require 'bundler/gem_tasks'
|
3
3
|
require 'rspec/core/rake_task'
|
4
|
+
require 'rake/testtask'
|
4
5
|
|
5
6
|
%w(install release).each do |task|
|
6
7
|
Rake::Task[task].enhance do
|
@@ -8,9 +9,9 @@ require 'rspec/core/rake_task'
|
|
8
9
|
end
|
9
10
|
end
|
10
11
|
|
11
|
-
desc
|
12
|
+
desc 'Bump version on github'
|
12
13
|
task :bump do
|
13
|
-
if `git status -s`.strip ==
|
14
|
+
if `git status -s`.strip == ''
|
14
15
|
puts "\e[31mNothing to commit (working directory clean)\e[0m"
|
15
16
|
else
|
16
17
|
version = Bundler.load_gemspec(Dir[File.expand_path('../*.gemspec', __FILE__)].first).version
|
@@ -18,13 +19,22 @@ task :bump do
|
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
21
|
-
|
22
|
+
Rake::TestTask.new(:spec) do |t|
|
23
|
+
t.test_files = Dir['spec/**/*_spec.rb']
|
24
|
+
t.verbose = true
|
25
|
+
end
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
namespace :example do
|
28
|
+
Dir['./examples/*'].each do |path|
|
29
|
+
next if File.directory?(path)
|
30
|
+
name = File.basename(path)
|
31
|
+
desc "Run example #{name}"
|
32
|
+
task name, :fork do |t, args|
|
33
|
+
ENV['FORK'] = args[:fork]
|
34
|
+
exec "#{Gem.ruby} #{path} && sleep 3 && tail -f -n 150 #{path}/../log/#{name}.log; #{path} stop"
|
35
|
+
end
|
36
|
+
end
|
28
37
|
end
|
29
38
|
|
39
|
+
task :release => :bump
|
30
40
|
task :default => :spec
|
data/examples/{sample → complex}
RENAMED
@@ -3,7 +3,7 @@ require 'rubygems' unless defined?(Gem)
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'forever'
|
5
5
|
|
6
|
-
Forever.run do
|
6
|
+
Forever.run :fork => ENV['FORK'] do
|
7
7
|
dir File.expand_path('../', __FILE__) # Default is ../../__FILE__
|
8
8
|
|
9
9
|
every 5.seconds do
|
@@ -14,19 +14,11 @@ Forever.run do
|
|
14
14
|
puts "All jobs will will wait me for 1 second"; sleep 1
|
15
15
|
end
|
16
16
|
|
17
|
-
every
|
18
|
-
puts "Every 5 seconds from start"
|
19
|
-
end
|
20
|
-
|
21
|
-
every 30.seconds, :last => Time.now do
|
17
|
+
every 30.seconds do
|
22
18
|
puts "Every 30 seconds from start with boom"
|
23
19
|
raise "woooooa"
|
24
20
|
end
|
25
21
|
|
26
|
-
every 10.seconds, :at => "#{Time.now.hour}:00" do
|
27
|
-
puts "Every 10 seconds but first call at #{Time.now.hour}:00"
|
28
|
-
end
|
29
|
-
|
30
22
|
every 1.seconds, :at => "#{Time.now.hour}:#{Time.now.min+1}" do
|
31
23
|
puts "Every one second but first call at #{Time.now.hour}:#{Time.now.min}"
|
32
24
|
end
|
@@ -40,7 +32,7 @@ Forever.run do
|
|
40
32
|
end
|
41
33
|
|
42
34
|
every 15.seconds do
|
43
|
-
puts "Every 15 seconds, but my task
|
35
|
+
puts "Every 15 seconds, but my task requires 10 seconds"; sleep 10
|
44
36
|
end
|
45
37
|
|
46
38
|
every 10.seconds, :at => [":#{Time.now.min+1}", ":#{Time.now.min+2}"] do
|
data/examples/simple
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require 'rubygems' unless defined?(Gem)
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'forever'
|
5
|
+
|
6
|
+
Forever.run :fork => ENV['FORK'] do
|
7
|
+
dir File.expand_path('../', __FILE__) # Default is ../../__FILE__
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
puts 'before all'
|
11
|
+
end
|
12
|
+
|
13
|
+
after :each do
|
14
|
+
puts 'after all'
|
15
|
+
end
|
16
|
+
|
17
|
+
every 1.seconds do
|
18
|
+
puts 'wait me 10 seconds'; sleep 10
|
19
|
+
end
|
20
|
+
|
21
|
+
every 2.seconds do
|
22
|
+
puts 'every 2 seconds'
|
23
|
+
end
|
24
|
+
|
25
|
+
on_ready do
|
26
|
+
puts "All jobs will will wait me for 1 second"; sleep 1
|
27
|
+
end
|
28
|
+
|
29
|
+
on_exit do
|
30
|
+
puts "Bye bye"
|
31
|
+
end
|
32
|
+
end
|
data/examples/stress
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require 'rubygems' unless defined?(Gem)
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'forever'
|
5
|
+
|
6
|
+
Forever.run :fork => ENV['FORK'] do
|
7
|
+
dir File.expand_path('../', __FILE__) # Default is ../../__FILE__
|
8
|
+
|
9
|
+
(1..40).each do |i|
|
10
|
+
every(i.seconds) { puts 'Every %d seconds' % i; sleep i }
|
11
|
+
end
|
12
|
+
end
|
data/foreverb.gemspec
CHANGED
@@ -18,5 +18,5 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = %w(lib)
|
20
20
|
s.add_dependency 'thor', '~>0.14.6'
|
21
|
-
s.add_development_dependency '
|
21
|
+
s.add_development_dependency 'minitest'
|
22
22
|
end
|
data/lib/forever.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'yaml' unless defined?(YAML)
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
2
|
+
require 'forever/extensions'
|
3
|
+
require 'forever/every'
|
4
|
+
require 'forever/base'
|
5
|
+
require 'forever/version'
|
6
6
|
|
7
|
-
YAML::ENGINE.yamler =
|
7
|
+
YAML::ENGINE.yamler = 'syck' if defined?(YAML::ENGINE)
|
8
8
|
|
9
9
|
FOREVER_PATH = ENV['FOREVER_PATH'] ||= File.expand_path("~/.foreverb") unless defined?(FOREVER_PATH)
|
10
10
|
path = File.dirname(FOREVER_PATH)
|
@@ -15,8 +15,8 @@ module Forever
|
|
15
15
|
|
16
16
|
def run(options={}, &block)
|
17
17
|
caller_file = caller(1).map { |line| line.split(/:(?=\d|in )/)[0,1] }.flatten.first
|
18
|
-
options[:
|
19
|
-
options[:
|
18
|
+
options[:file] ||= File.expand_path(caller_file)
|
19
|
+
options[:dir] ||= File.expand_path('../../', options[:file]) # => we presume we are calling it from a bin|script dir
|
20
20
|
Base.new(options, &block)
|
21
21
|
end # run
|
22
|
-
end # Forever
|
22
|
+
end # Forever
|
data/lib/forever/base.rb
CHANGED
@@ -4,12 +4,18 @@ module Forever
|
|
4
4
|
|
5
5
|
class Base
|
6
6
|
include Every
|
7
|
+
attr_reader :started_at
|
7
8
|
|
8
9
|
def initialize(options={}, &block)
|
10
|
+
forking = options.delete(:fork)
|
11
|
+
|
12
|
+
# Run others methods
|
9
13
|
options.each { |k,v| send(k, v) }
|
10
14
|
|
11
15
|
instance_eval(&block)
|
12
16
|
|
17
|
+
raise 'No jobs defined!' if jobs.empty?
|
18
|
+
|
13
19
|
Dir.chdir(dir) if exists?(dir)
|
14
20
|
Dir.mkdir(File.dirname(log)) if log && !File.exist?(File.dirname(log))
|
15
21
|
Dir.mkdir(File.dirname(pid)) if pid && !File.exist?(File.dirname(pid))
|
@@ -48,9 +54,13 @@ module Forever
|
|
48
54
|
exit
|
49
55
|
end
|
50
56
|
|
57
|
+
# Enable REE - http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
|
58
|
+
GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
|
59
|
+
|
51
60
|
fork do
|
52
61
|
$0 = "Forever: #{$0}"
|
53
|
-
print "[\e[90m%s\e[0m] Process demonized with pid \e[1m%d\e[0m with Forever v.%s\n" %
|
62
|
+
print "[\e[90m%s\e[0m] Process demonized with pid \e[1m%d\e[0m with \e[1m%s\e[0m and Forever v.%s\n" %
|
63
|
+
[name, Process.pid, forking ? :fork : :thread, Forever::VERSION]
|
54
64
|
|
55
65
|
%w(INT TERM KILL).each { |signal| trap(signal) { stop! } }
|
56
66
|
trap(:HUP) do
|
@@ -65,27 +75,34 @@ module Forever
|
|
65
75
|
STDOUT.reopen(stream)
|
66
76
|
STDERR.reopen(STDOUT)
|
67
77
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
78
|
+
@started_at = Time.now
|
79
|
+
|
80
|
+
# Invoke our before :all filters
|
81
|
+
before_filters[:all].each { |block| safe_call(block) }
|
82
|
+
|
83
|
+
# Start deamons
|
84
|
+
until stopping?
|
85
|
+
if forking
|
86
|
+
begin
|
87
|
+
jobs.select { |job| job.time?(Time.now) }.each do |job|
|
88
|
+
Process.fork { job_call(job) }
|
89
|
+
end
|
90
|
+
rescue Errno::EAGAIN
|
91
|
+
puts "\n\nWait all processes since os cannot create a new one\n\n"
|
92
|
+
Process.waitall
|
77
93
|
end
|
94
|
+
else
|
95
|
+
jobs.each { |job| Thread.new { job_call(job) } if job.time?(Time.now) }
|
78
96
|
end
|
97
|
+
sleep 0.5
|
79
98
|
end
|
80
99
|
|
81
|
-
# Launch our workers
|
82
|
-
threads.map(&:join)
|
83
|
-
|
84
100
|
# If we are here it means we are exiting so we can remove the pid and pending stop.txt
|
85
101
|
FileUtils.rm_f(pid)
|
86
102
|
FileUtils.rm_f(stop_txt)
|
87
103
|
|
88
|
-
|
104
|
+
# Invoke our after :all filters
|
105
|
+
after_filters[:all].each { |block| safe_call(block) }
|
89
106
|
end
|
90
107
|
|
91
108
|
self
|
@@ -141,7 +158,7 @@ module Forever
|
|
141
158
|
pid_was = File.read(pid).to_i
|
142
159
|
FileUtils.rm_f(pid)
|
143
160
|
print "[\e[90m%s\e[0m] Killing process \e[1m%d\e[0m...\n" % [name, pid_was]
|
144
|
-
|
161
|
+
after_filters[:all].each { |block| safe_call(block) }
|
145
162
|
Process.kill(:KILL, pid_was)
|
146
163
|
else
|
147
164
|
print "[\e[90m%s\e[0m] Process with \e[1mnot found\e[0m" % name
|
@@ -174,14 +191,14 @@ module Forever
|
|
174
191
|
# Callback raised when at exit
|
175
192
|
#
|
176
193
|
def on_exit(&block)
|
177
|
-
|
194
|
+
after(:all, &block)
|
178
195
|
end
|
179
196
|
|
180
197
|
##
|
181
198
|
# Callback to fire when the daemon start (blocking, not in thread)
|
182
199
|
#
|
183
200
|
def on_ready(&block)
|
184
|
-
|
201
|
+
before(:all, &block)
|
185
202
|
end
|
186
203
|
|
187
204
|
##
|
@@ -214,29 +231,58 @@ module Forever
|
|
214
231
|
{ :dir => dir, :file => file, :log => log, :pid => pid }
|
215
232
|
end
|
216
233
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
config_was << config
|
222
|
-
File.open(FOREVER_PATH, "w") { |f| f.write config_was.to_yaml }
|
223
|
-
end
|
234
|
+
def before(filter, &block)
|
235
|
+
raise "Filter #{filter.inspect} not supported, available options are: :each, :all" unless [:each, :all].include?(filter)
|
236
|
+
before_filters[filter] << block
|
237
|
+
end
|
224
238
|
|
225
|
-
|
226
|
-
|
227
|
-
|
239
|
+
def after(filter, &block)
|
240
|
+
raise "Filter #{filter.inspect} not supported, available options are: :each, :all" unless [:each, :all].include?(filter)
|
241
|
+
after_filters[filter] << block
|
242
|
+
end
|
228
243
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
puts "\n\n%s\n %s\n\n" % [e.message, e.backtrace.join("\n ")]
|
234
|
-
on_error[e] if on_error
|
235
|
-
end
|
236
|
-
end
|
244
|
+
private
|
245
|
+
def before_filters
|
246
|
+
@_before_filters ||= Hash.new { |hash, k| hash[k] = [] }
|
247
|
+
end
|
237
248
|
|
238
|
-
|
239
|
-
|
249
|
+
def after_filters
|
250
|
+
@_after_filters ||= Hash.new { |hash, k| hash[k] = [] }
|
251
|
+
end
|
252
|
+
|
253
|
+
def stopping?
|
254
|
+
File.exist?(stop_txt) && File.mtime(stop_txt) > started_at
|
255
|
+
end
|
256
|
+
|
257
|
+
def write_config!
|
258
|
+
config_was = File.exist?(FOREVER_PATH) ? YAML.load_file(FOREVER_PATH) : []
|
259
|
+
config_was.delete_if { |conf| conf[:file] == file }
|
260
|
+
config_was << config
|
261
|
+
File.open(FOREVER_PATH, "w") { |f| f.write config_was.to_yaml }
|
262
|
+
end
|
263
|
+
|
264
|
+
def exists?(*values)
|
265
|
+
values.all? { |value| value && File.exist?(value) }
|
266
|
+
end
|
267
|
+
|
268
|
+
def job_call(job)
|
269
|
+
return unless job.time?(Time.now)
|
270
|
+
before_filters[:each].each { |block| safe_call(block) }
|
271
|
+
safe_call(job)
|
272
|
+
after_filters[:each].each { |block| safe_call(block) }
|
273
|
+
end
|
274
|
+
|
275
|
+
def safe_call(block)
|
276
|
+
begin
|
277
|
+
block.call
|
278
|
+
rescue Exception => e
|
279
|
+
puts "\n\n%s\n %s\n\n" % [e.message, e.backtrace.join("\n ")]
|
280
|
+
on_error[e] if on_error
|
240
281
|
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def stop_txt
|
285
|
+
@_stop_txt ||= File.join(dir, 'stop.txt')
|
286
|
+
end
|
241
287
|
end # Base
|
242
288
|
end # Forever
|
data/lib/forever/every.rb
CHANGED
@@ -1,26 +1,39 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
|
1
3
|
module Forever
|
2
4
|
module Every
|
3
5
|
class Job
|
4
|
-
attr_accessor :period, :option, :last, :running
|
5
6
|
|
6
7
|
def initialize(period, options, block)
|
7
|
-
@period
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@
|
8
|
+
@period = period
|
9
|
+
@at_start = options[:at] == :start && options.delete(:at) ? true : false
|
10
|
+
@at = options[:at] ? parse_at(*options[:at]) : []
|
11
|
+
@tmp = File.join(Dir.tmpdir, '%x' % rand(255**10))
|
12
|
+
@block = block
|
11
13
|
end
|
12
14
|
|
13
15
|
def call
|
14
|
-
@
|
16
|
+
File.open(@tmp, 'w') { |f| f.write('running') }
|
17
|
+
FileUtils.touch(@tmp)
|
15
18
|
@block.call
|
16
19
|
ensure
|
17
|
-
@
|
20
|
+
File.open(@tmp, 'w') { |f| f.write('idle') }
|
21
|
+
end
|
22
|
+
|
23
|
+
def running?
|
24
|
+
File.exist?(@tmp) && File.read(@tmp) == 'running'
|
25
|
+
end
|
26
|
+
|
27
|
+
def last
|
28
|
+
File.mtime(@tmp)
|
29
|
+
rescue Errno::ENOENT
|
30
|
+
0
|
18
31
|
end
|
19
32
|
|
20
33
|
def time?(t)
|
21
|
-
|
34
|
+
elapsed_ready = (t - last).to_i >= @period
|
22
35
|
time_ready = @at.empty? || @at.any? { |at| (at[0].empty? || t.hour == at[0].to_i) && (at[1].empty? || t.min == at[1].to_i) }
|
23
|
-
!running &&
|
36
|
+
!running? && elapsed_ready && time_ready
|
24
37
|
end
|
25
38
|
|
26
39
|
private
|
@@ -43,4 +56,4 @@ module Forever
|
|
43
56
|
@_jobs ||= []
|
44
57
|
end
|
45
58
|
end # Every
|
46
|
-
end # Forever
|
59
|
+
end # Forever
|
data/lib/forever/version.rb
CHANGED
data/spec/cli_spec.rb
CHANGED
@@ -1,34 +1,34 @@
|
|
1
|
-
require 'spec_helper'
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
2
|
|
3
|
-
describe
|
4
|
-
|
5
|
-
|
6
|
-
cli('list').should match(/Your config is empty/)
|
7
|
-
cli('list').should match(FOREVER_PATH)
|
8
|
-
cli('list -m').should match(/PID RSS CPU CMD/)
|
3
|
+
describe 'CLI' do
|
4
|
+
def cli(task)
|
5
|
+
`#{Gem.ruby} #{File.expand_path('../../bin/foreverb', __FILE__)} #{task}`
|
9
6
|
end
|
10
7
|
|
11
|
-
it
|
8
|
+
it 'should list daemons' do
|
9
|
+
cli('list').must_match(/Your config is empty/)
|
10
|
+
cli('list').must_match(FOREVER_PATH)
|
11
|
+
cli('list -m').must_match(/PID RSS CPU CMD/)
|
12
12
|
run_example
|
13
|
-
cli('list').
|
14
|
-
cli('list -m').
|
13
|
+
cli('list').must_match(/RUNNING/)
|
14
|
+
cli('list -m').must_match(/Forever:\s/)
|
15
15
|
end
|
16
16
|
|
17
17
|
it "should stop daemons" do
|
18
18
|
run_example
|
19
|
-
cli('list').
|
19
|
+
cli('list').must_match(/RUNNING/)
|
20
20
|
result = cli('stop -a -y')
|
21
|
-
result.
|
22
|
-
result.
|
23
|
-
cli('list').
|
21
|
+
result.must_match(/STOPPING/)
|
22
|
+
result.wont_match(/ERROR/)
|
23
|
+
cli('list').must_match(/NOT RUNNING/)
|
24
24
|
end
|
25
25
|
|
26
|
-
it
|
26
|
+
it 'should kill daemons' do
|
27
27
|
run_example
|
28
|
-
cli('list').
|
28
|
+
cli('list').must_match(/RUNNING/)
|
29
29
|
result = cli('kill -a -y')
|
30
|
-
result.
|
31
|
-
result.
|
32
|
-
cli('list').
|
30
|
+
result.must_match(/KILLING/)
|
31
|
+
result.wont_match(/ERROR/)
|
32
|
+
cli('list').must_match(/NOT RUNNING/)
|
33
33
|
end
|
34
34
|
end
|
data/spec/foreverb_spec.rb
CHANGED
@@ -1,48 +1,48 @@
|
|
1
|
-
require 'spec_helper'
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
2
|
|
3
3
|
describe Forever do
|
4
4
|
|
5
|
-
before :each do
|
6
|
-
$stdout = StringIO.new
|
7
|
-
ARGV << 'up'
|
8
|
-
end
|
9
|
-
|
10
5
|
it 'should set a basic config' do
|
11
|
-
|
12
|
-
@forever.dir.
|
13
|
-
@forever.log.
|
14
|
-
@forever.pid.
|
15
|
-
@forever.file.
|
6
|
+
run_example
|
7
|
+
@forever.dir.must_equal File.expand_path("../../", __FILE__)
|
8
|
+
@forever.log.must_equal File.join(@forever.dir, 'log', File.basename(example_filename, '.*') + '.log')
|
9
|
+
@forever.pid.must_equal File.join(@forever.dir, 'tmp', File.basename(example_filename, '.*') + '.pid')
|
10
|
+
@forever.file.must_equal example_filename
|
16
11
|
config = YAML.load_file(FOREVER_PATH)
|
17
|
-
config[0][:file].
|
18
|
-
config[0][:log].
|
19
|
-
config[0][:pid].
|
12
|
+
config[0][:file].must_equal example_filename
|
13
|
+
config[0][:log].must_equal @forever.log
|
14
|
+
config[0][:pid].must_equal @forever.pid
|
20
15
|
end
|
21
16
|
|
22
17
|
it 'should set a custom config' do
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@forever.
|
27
|
-
@forever.
|
28
|
-
@forever.pid.should == File.join(@forever.dir, 'tmp', File.basename(__FILE__) + '.pid')
|
29
|
-
@forever.file.should == __FILE__
|
18
|
+
run_example(:dir => Dir.tmpdir)
|
19
|
+
@forever.dir.must_equal Dir.tmpdir
|
20
|
+
@forever.log.must_equal File.join(@forever.dir, 'log', File.basename(example_filename, '.*') + '.log')
|
21
|
+
@forever.pid.must_equal File.join(@forever.dir, 'tmp', File.basename(example_filename, '.*') + '.pid')
|
22
|
+
@forever.file.must_equal example_filename
|
30
23
|
config = YAML.load_file(FOREVER_PATH)
|
31
|
-
config[0][:file].
|
32
|
-
config[0][:log].
|
33
|
-
config[0][:pid].
|
24
|
+
config[0][:file].must_equal example_filename
|
25
|
+
config[0][:log].must_equal @forever.log
|
26
|
+
config[0][:pid].must_equal @forever.pid
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should launch a daemon with threads with soft stop' do
|
30
|
+
run_example
|
31
|
+
sleep 0.1 while !File.exist?(@forever.pid)
|
32
|
+
pid = File.read(@forever.pid).to_i
|
33
|
+
sleep 1
|
34
|
+
out, err = capture_io { @forever.stop }
|
35
|
+
out.must_match(/waiting the daemon's death/i)
|
36
|
+
out.must_match(/#{pid}/)
|
34
37
|
end
|
35
38
|
|
36
|
-
it 'should launch a daemon' do
|
37
|
-
|
38
|
-
stdout_was, $stdout = $stdout, StringIO.new
|
39
|
-
@forever = Forever.run do
|
40
|
-
on_ready { sleep 2 }
|
41
|
-
end
|
39
|
+
it 'should launch a daemon with threads with soft stop' do
|
40
|
+
run_example(:fork => true)
|
42
41
|
sleep 0.1 while !File.exist?(@forever.pid)
|
43
42
|
pid = File.read(@forever.pid).to_i
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
sleep 1
|
44
|
+
out, err = capture_io { @forever.stop }
|
45
|
+
out.must_match(/waiting the daemon's death/i)
|
46
|
+
out.must_match(/#{pid}/)
|
47
47
|
end
|
48
48
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,48 +1,36 @@
|
|
1
1
|
FOREVER_PATH = ENV['FOREVER_PATH'] ||= File.expand_path("../tmp/db.yaml", __FILE__)
|
2
2
|
require 'rubygems' unless defined?(Gem)
|
3
3
|
require 'bundler/setup'
|
4
|
-
require '
|
4
|
+
require 'minitest/autorun'
|
5
5
|
require 'forever'
|
6
6
|
require 'fileutils'
|
7
|
+
require 'tmpdir'
|
7
8
|
|
8
|
-
|
9
|
-
def capture_stdout(&block)
|
10
|
-
stdout_was, $stdout = $stdout, StringIO.new
|
11
|
-
block.call
|
12
|
-
return $stdout
|
13
|
-
ensure
|
14
|
-
$stdout = stdout_was
|
15
|
-
end
|
9
|
+
$dir = File.expand_path('.')
|
16
10
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
end
|
11
|
+
class MiniTest::Spec
|
12
|
+
def run_example(options={}, &block)
|
13
|
+
block = proc { every(1.second) { puts 'foo' } } unless block_given?
|
14
|
+
capture_io { @forever = Forever.run(options, &block) }
|
23
15
|
end
|
24
16
|
|
25
|
-
|
26
|
-
output = `#{Gem.ruby} #{File.expand_path('../../bin/foreverb', __FILE__)} #{task}`
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
RSpec.configure do |config|
|
31
|
-
config.include(Helper)
|
17
|
+
let(:example_filename) { File.expand_path(__FILE__) }
|
32
18
|
|
33
|
-
|
19
|
+
before do
|
20
|
+
Dir.chdir($dir)
|
34
21
|
FileUtils.rm_rf File.dirname(FOREVER_PATH)
|
35
22
|
Dir.mkdir File.dirname(FOREVER_PATH)
|
36
23
|
ARGV.clear
|
37
24
|
end
|
38
25
|
|
39
|
-
|
26
|
+
after do
|
40
27
|
FileUtils.rm_rf(File.dirname(FOREVER_PATH))
|
41
28
|
if @forever
|
42
|
-
|
29
|
+
capture_io { @forever.stop! }
|
43
30
|
FileUtils.rm_rf(File.dirname(@forever.log)) if @forever.log
|
44
31
|
FileUtils.rm_rf(File.dirname(@forever.pid)) if @forever.pid
|
45
32
|
end
|
33
|
+
Dir.chdir($dir)
|
46
34
|
ARGV.clear
|
47
35
|
end
|
48
36
|
end
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreverb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 50
|
5
|
+
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
- a
|
11
|
+
version: 0.3.0.a
|
11
12
|
platform: ruby
|
12
13
|
authors:
|
13
14
|
- DAddYE
|
@@ -15,7 +16,7 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date: 2011-
|
19
|
+
date: 2011-10-04 00:00:00 +02:00
|
19
20
|
default_executable:
|
20
21
|
dependencies:
|
21
22
|
- !ruby/object:Gem::Dependency
|
@@ -35,19 +36,17 @@ dependencies:
|
|
35
36
|
type: :runtime
|
36
37
|
version_requirements: *id001
|
37
38
|
- !ruby/object:Gem::Dependency
|
38
|
-
name:
|
39
|
+
name: minitest
|
39
40
|
prerelease: false
|
40
41
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
42
|
none: false
|
42
43
|
requirements:
|
43
|
-
- -
|
44
|
+
- - ">="
|
44
45
|
- !ruby/object:Gem::Version
|
45
|
-
hash:
|
46
|
+
hash: 3
|
46
47
|
segments:
|
47
|
-
- 2
|
48
|
-
- 6
|
49
48
|
- 0
|
50
|
-
version:
|
49
|
+
version: "0"
|
51
50
|
type: :development
|
52
51
|
version_requirements: *id002
|
53
52
|
description: Small daemon framework for ruby, with logging, error handler, scheduling and much more.
|
@@ -66,7 +65,10 @@ files:
|
|
66
65
|
- README.md
|
67
66
|
- Rakefile
|
68
67
|
- bin/foreverb
|
69
|
-
- examples/
|
68
|
+
- examples/complex
|
69
|
+
- examples/simple
|
70
|
+
- examples/stress
|
71
|
+
- examples/tmp/complex.pid
|
70
72
|
- foreverb.gemspec
|
71
73
|
- lib/forever.rb
|
72
74
|
- lib/forever/base.rb
|
@@ -97,12 +99,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
99
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
100
|
none: false
|
99
101
|
requirements:
|
100
|
-
- - "
|
102
|
+
- - ">"
|
101
103
|
- !ruby/object:Gem::Version
|
102
|
-
hash:
|
104
|
+
hash: 25
|
103
105
|
segments:
|
104
|
-
-
|
105
|
-
|
106
|
+
- 1
|
107
|
+
- 3
|
108
|
+
- 1
|
109
|
+
version: 1.3.1
|
106
110
|
requirements: []
|
107
111
|
|
108
112
|
rubyforge_project: foreverb
|