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 CHANGED
@@ -2,3 +2,5 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ examples/**/log
6
+ examples/**/tmp
data/README.md CHANGED
@@ -1,16 +1,13 @@
1
1
  # Foreverb
2
2
 
3
- Small daemon framework **for ruby**, with logging, error handler watcher and much more.
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 a lot of alternatives, one of the best is [resque](https://github.com/defunkt/resque), so why another daemons framework?
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
- def stop(daemon)
17
- found = daemons.find_all { |d| d=~/#{daemon}/i }
18
- say "Daemon(s) matching '#{daemon}' not found", :red if found.empty?
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? "Do you want really stop #{daemon[-1]} with pid #{daemon[0]}?"
22
- say "Killing process #{daemon[-1]} with pid #{daemon[0]}...", :green
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 = "forever"
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 = ["lib"]
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" || on_ready.nil?
21
+ return if ARGV[0] == "stop"
17
22
 
18
23
  fork do
19
24
  $0 = "Forever: #{$0}"
20
- puts "=> Process demonized with pid #{Process.pid} with Forever v.#{Forever::VERSION}"
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
- on_ready.call if on_ready
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 = 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 = 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!(kill=true)
105
+ def stop!
82
106
  if exists?(pid)
83
107
  _pid = File.read(pid).to_i
84
- puts "=> Found pid #{_pid}..."
108
+ print "=> Found pid #{_pid}...\n"
85
109
  FileUtils.rm_f(pid)
86
110
  begin
87
- puts "=> Killing process #{_pid}..."
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
- puts "=> Pid not found, process seems don't exist!"
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
@@ -0,0 +1,13 @@
1
+ class Numeric
2
+ def seconds; self; end
3
+ alias :second :seconds
4
+
5
+ def minutes; self * 60; end
6
+ alias :minute :minutes
7
+
8
+ def hours; self * 3600; end
9
+ alias :hour :hours
10
+
11
+ def days; self * 86400; end
12
+ alias :day :days
13
+ end
@@ -1,3 +1,3 @@
1
1
  module Forever
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
3
3
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 7
9
- version: 0.1.7
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-08 00:00:00 +02:00
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: forever
81
+ rubyforge_project: foreverb
79
82
  rubygems_version: 1.3.6
80
83
  signing_key:
81
84
  specification_version: 3