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 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