clockworkd 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +21 -0
- data/README +89 -0
- data/Rakefile +22 -0
- data/VERSION +1 -0
- data/changelog +23 -0
- data/clockworkd.gemspec +36 -0
- data/contrib/clockworkd.monitrc +14 -0
- data/generators/clockworkd/clockworkd_generator.rb +12 -0
- data/generators/clockworkd/templates/clockworkd.yml +14 -0
- data/generators/clockworkd/templates/script +5 -0
- data/knownbugs +18 -0
- data/lib/clockworkd.rb +21 -0
- data/lib/clockworkd/command.rb +90 -0
- data/lib/clockworkd/cronline.rb +310 -0
- data/lib/clockworkd/event.rb +33 -0
- data/lib/clockworkd/recipes.rb +50 -0
- data/lib/clockworkd/worker.rb +102 -0
- data/lib/generators/clockworkd/clockworkd_generator.rb +21 -0
- data/lib/generators/clockworkd/templates/clockworkd.yml +14 -0
- data/lib/generators/clockworkd/templates/script +5 -0
- data/recipes/clockworkd.rb +1 -0
- data/roadmap +17 -0
- data/spec/lib/clockworkd/command_spec.rb +7 -0
- data/spec/lib/clockworkd/cronline_spec.rb +7 -0
- data/spec/lib/clockworkd/event_spec.rb +7 -0
- data/spec/lib/clockworkd/worker_spec.rb +7 -0
- data/spec/lib/clockworkd_spec.rb +8 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +26 -0
- metadata +93 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2011 Artem Rufanov
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
Clockworkd - a clock process to replace cron
|
2
|
+
============================================
|
3
|
+
|
4
|
+
Cron is non-ideal for running scheduled application tasks, especially in an app
|
5
|
+
deployed to multiple machines. [More details.](http://adam.heroku.com/past/2010/4/13/rethinking_cron/)
|
6
|
+
|
7
|
+
Clockworkd is a cron replacement. It runs as a lightweight, long-running Ruby
|
8
|
+
process which sits alongside your web processes (Mongrel/Thin) and your worker
|
9
|
+
processes (DJ/Resque/Minion/Stalker) to schedule recurring work at particular
|
10
|
+
times or dates. For example, refreshing feeds on an hourly basis, or send
|
11
|
+
reminder emails on a nightly basis, or generating invoices once a month on the
|
12
|
+
1st.
|
13
|
+
|
14
|
+
Quickstart
|
15
|
+
=======
|
16
|
+
|
17
|
+
Configure application:
|
18
|
+
|
19
|
+
In your Gemfile gem "clockworkd", ">= 0.2.5"
|
20
|
+
Run generator to generate config yml and script file
|
21
|
+
Use config/initiaizers to configure options for this gem
|
22
|
+
|
23
|
+
|
24
|
+
Modify clockworkd.yml:
|
25
|
+
|
26
|
+
# Run job every two minute: */2 * * * *
|
27
|
+
clear_sesssion_job:
|
28
|
+
cron: "59 1 * * *"
|
29
|
+
block: Delayed::Job.enqueue ClearSessionsJob.new
|
30
|
+
description: "This job clear sessions table"
|
31
|
+
|
32
|
+
Run it as console or daemon application (rails 3.x):
|
33
|
+
|
34
|
+
$ ruby script/clockworkd run
|
35
|
+
$ ruby script/clockworkd --identifier=0 start
|
36
|
+
|
37
|
+
Use clockworkd with capistrano:
|
38
|
+
|
39
|
+
after "deploy:stop", "clockworkd:stop"
|
40
|
+
after "deploy:start", "clockworkd:start"
|
41
|
+
after "deploy:restart", "clockworkd:restart"
|
42
|
+
|
43
|
+
Use monit script to monitor it:
|
44
|
+
|
45
|
+
check process clockworkd
|
46
|
+
with pidfile /var/www/apps/{app_name}/shared/pids/clockworkd.pid
|
47
|
+
start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/clockworkd start"
|
48
|
+
stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/clockworkd stop"
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
In production
|
53
|
+
=======
|
54
|
+
|
55
|
+
Only one clock process should ever be running across your whole application
|
56
|
+
deployment. For example, if your app is running on three VPS machines (two app
|
57
|
+
servers and one database), your app machines might have the following process
|
58
|
+
topography:
|
59
|
+
|
60
|
+
* App server 1: 3 web (thin start), 3 workers (rake jobs:work), 1 clock (clockwork clock.rb)
|
61
|
+
* App server 2: 3 web (thin start), 3 workers (rake jobs:work)
|
62
|
+
|
63
|
+
You should use Monit, God, Upstart, or Inittab to keep your clock process
|
64
|
+
running the same way you keep your web and workers running.
|
65
|
+
|
66
|
+
Meta
|
67
|
+
=======
|
68
|
+
|
69
|
+
Inspired by
|
70
|
+
|
71
|
+
* [clockwork] (http://github.com/adamwiggins/clockwork)
|
72
|
+
* [delayed_job] (https://github.com/collectiveidea/delayed_job)
|
73
|
+
* [rufus-scheduler](http://rufus.rubyforge.org/rufus-scheduler/)
|
74
|
+
* [resque-scehduler] (http://github.com/bvandenbos/resque-scheduler)
|
75
|
+
|
76
|
+
|
77
|
+
Installation
|
78
|
+
=======
|
79
|
+
|
80
|
+
* Type 'gem install --local clockworkd' with root account if you have installed RubyGems.
|
81
|
+
|
82
|
+
|
83
|
+
Example
|
84
|
+
=======
|
85
|
+
|
86
|
+
Example goes here.
|
87
|
+
|
88
|
+
Copyright (c) 2011 arufanov, released under the MIT license.
|
89
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'jeweler'
|
2
|
+
|
3
|
+
Jeweler::Tasks.new do |s|
|
4
|
+
s.name = "clockworkd"
|
5
|
+
s.summary = "A scheduler process to replace cron."
|
6
|
+
s.description = "A scheduler process to replace cron, using a more flexible Ruby syntax running as a single long-running process. Inspired by rufus-scheduler and resque-scheduler."
|
7
|
+
s.author = "Adam Wiggins"
|
8
|
+
s.email = "adam@heroku.com"
|
9
|
+
s.homepage = "http://github.com/arufanov/clockworkd"
|
10
|
+
s.executables = [ "clockworkd" ]
|
11
|
+
s.rubyforge_project = "clockworkd"
|
12
|
+
|
13
|
+
s.files = FileList["[A-Z]*", "{bin,lib}/**/*"]
|
14
|
+
end
|
15
|
+
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
|
18
|
+
task 'test' do
|
19
|
+
sh "ruby spec/lib/clockworkd_spec.rb"
|
20
|
+
end
|
21
|
+
|
22
|
+
task :build => :test
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.5
|
data/changelog
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Introduction:
|
2
|
+
To see the latest list of the change log please visit the Change Log page at www.majoron.com.
|
3
|
+
|
4
|
+
Legend:
|
5
|
+
Follow notation is used at change log, roadmap and known bugs. Each bug begins with a version,
|
6
|
+
then follow category of the bug inside {}. It can be bug report, feature request and etc.
|
7
|
+
Then follow component inside []. After follow bug number at bug tracking system between // signs.
|
8
|
+
And then follow a short description of the bug.
|
9
|
+
|
10
|
+
Example:
|
11
|
+
For example bug: "1.0 { Feature Request } [ AntHill ] / 380 / STLport support required" means
|
12
|
+
that bug was created for 1.0 version of the AntHill component, bug is feature request with
|
13
|
+
380 number at bug tracking system. And bug requires STLPort support implementation.
|
14
|
+
|
15
|
+
Version 0.2
|
16
|
+
-----------
|
17
|
+
0.2 { Bug Report } [ Clockworkd ] / X / Add before_fork & after fork hook to restore ActiveRecord connection after fork
|
18
|
+
0.2 { Bug Report } [ Clockworkd ] / X / Support rails 2.5.x
|
19
|
+
0.2 { Bug Report } [ Clockworkd ] / X / Generator that install clockwork to script was added
|
20
|
+
0.2 { Bug Report } [ Clockworkd ] / X / Capistrano receips was added
|
21
|
+
0.2 { Bug Report } [ Clockworkd ] / X / Monit script was added
|
22
|
+
0.2 { Bug Report } [ Clockworkd ] / X / Ability to run as daemons was added
|
23
|
+
|
data/clockworkd.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{clockworkd}
|
8
|
+
s.version = "0.2.5"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Adam Wiggins"]
|
12
|
+
s.date = %q{2011-07-31}
|
13
|
+
s.description = %q{A scheduler process to replace cron, using a more flexible Ruby syntax running as a single long-running process. Inspired by rufus-scheduler and resque-scheduler.}
|
14
|
+
s.email = %q{adam@heroku.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README"
|
17
|
+
]
|
18
|
+
s.files = Dir.glob('**/*') - Dir.glob('distrib/**/*') - Dir.glob('lib/api/**/*') - Dir.glob('doc/*.xpr')
|
19
|
+
s.homepage = %q{http://www.majoron.com/project/rbundle/clockworkd}
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.rubyforge_project = %q{clockworkd}
|
22
|
+
s.rubygems_version = %q{1.5.0}
|
23
|
+
s.summary = %q{A scheduler process to replace cron.}
|
24
|
+
s.test_files = Dir.glob('spec/**/*')
|
25
|
+
s.add_runtime_dependency 'tzinfo', '>= 0.3.23'
|
26
|
+
|
27
|
+
if s.respond_to? :specification_version then
|
28
|
+
s.specification_version = 3
|
29
|
+
|
30
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
31
|
+
else
|
32
|
+
end
|
33
|
+
else
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# an example Monit configuration file for clockworkd
|
2
|
+
# See: http://stackoverflow.com/questions/1226302/how-to-monitor-delayedjob-with-monit/1285611
|
3
|
+
#
|
4
|
+
# To use:
|
5
|
+
# 1. copy to /var/www/apps/{app_name}/shared/clockworkd.monitrc
|
6
|
+
# 2. replace {app_name} as appropriate
|
7
|
+
# 3. add this to your /etc/monit/monitrc
|
8
|
+
#
|
9
|
+
# include /var/www/apps/{app_name}/shared/clockworkd.monitrc
|
10
|
+
|
11
|
+
check process clockworkd
|
12
|
+
with pidfile /var/www/apps/{app_name}/shared/pids/clockworkd.pid
|
13
|
+
start program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/clockworkd start"
|
14
|
+
stop program = "/usr/bin/env RAILS_ENV=production /var/www/apps/{app_name}/current/script/clockworkd stop"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class ClockworkdGenerator < Rails::Generator::Base
|
2
|
+
default_options :skip_migration => false
|
3
|
+
|
4
|
+
def manifest
|
5
|
+
record do |m|
|
6
|
+
m.template 'script', 'script/clockworkd', :chmod => 0755
|
7
|
+
m.template 'clockworkd.yml', 'config/clockworkd.yml'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
# Run job every day at 1:59: 59 1 * * *
|
3
|
+
# Run job every two minute: */2 * * * *
|
4
|
+
clear_sesssion_job:
|
5
|
+
cron: "59 1 * * *"
|
6
|
+
block: Delayed::Job.enqueue ClearSessionsJob.new
|
7
|
+
description: "This job clear sessions table"
|
8
|
+
|
9
|
+
# Run job every day at 4:59: 59 4 * * *
|
10
|
+
# Run job every two minute: */2 * * * *
|
11
|
+
clear_job_reports_job:
|
12
|
+
cron: "59 4 * * *"
|
13
|
+
block: Delayed::Job.enqueue ClearJobReportsJob.new
|
14
|
+
description: "This job clear job report table"
|
data/knownbugs
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Introduction:
|
2
|
+
To see the latest list of the known bugs please visit the Known bugs page at www.majoron.com.
|
3
|
+
|
4
|
+
Legend:
|
5
|
+
Follow notation is used at change log, roadmap and known bugs. Each bug begins with a version,
|
6
|
+
then follow category of the bug inside {}. It can be bug report, feature request and etc.
|
7
|
+
Then follow component inside []. After follow bug number at bug tracking system between // signs.
|
8
|
+
And then follow a short description of the bug.
|
9
|
+
|
10
|
+
Example:
|
11
|
+
For example bug: "1.0 { Feature Request } [ AntHill ] / 380 / STLport support required" means
|
12
|
+
that bug was created for 1.0 version of the AntHill component, bug is feature request with
|
13
|
+
380 number at bug tracking system. And bug requires STLPort support implementation.
|
14
|
+
|
15
|
+
Version 0.2
|
16
|
+
-----------
|
17
|
+
0.2 { Bug Report } [ Clockworkd ] / X / There isn't known bugs
|
18
|
+
|
data/lib/clockworkd.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Include files
|
2
|
+
require File.dirname(__FILE__) + '/clockworkd/cronline'
|
3
|
+
require File.dirname(__FILE__) + '/clockworkd/event'
|
4
|
+
require File.dirname(__FILE__) + '/clockworkd/worker'
|
5
|
+
require File.dirname(__FILE__) + '/clockworkd/command'
|
6
|
+
|
7
|
+
unless 1.respond_to?(:seconds)
|
8
|
+
class Numeric
|
9
|
+
def seconds; self; end
|
10
|
+
alias :second :seconds
|
11
|
+
|
12
|
+
def minutes; self * 60; end
|
13
|
+
alias :minute :minutes
|
14
|
+
|
15
|
+
def hours; self * 3600; end
|
16
|
+
alias :hour :hours
|
17
|
+
|
18
|
+
def days; self * 86400; end
|
19
|
+
alias :day :days
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'daemons'
|
3
|
+
require 'optparse'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module Clockworkd
|
7
|
+
class Command
|
8
|
+
|
9
|
+
def initialize(args)
|
10
|
+
@files_to_reopen = []
|
11
|
+
@options = {
|
12
|
+
:quiet => true,
|
13
|
+
:identifier => 0,
|
14
|
+
:pid_dir => "#{Rails.root}/tmp/pids",
|
15
|
+
:clock_file => "#{Rails.root}/config/clockworkd.yml"
|
16
|
+
}
|
17
|
+
|
18
|
+
@monitor = false
|
19
|
+
|
20
|
+
opts = OptionParser.new do |opts|
|
21
|
+
opts.banner = "Usage: #{File.basename($0)} [options] start|stop|restart|run"
|
22
|
+
|
23
|
+
opts.on('-h', '--help', 'Show this message') do
|
24
|
+
puts opts
|
25
|
+
exit 1
|
26
|
+
end
|
27
|
+
opts.on('--clock-file=FILE', 'Specifies an alternate clockwork file with schuduling.') do |file|
|
28
|
+
@options[:clock_file] = file
|
29
|
+
end
|
30
|
+
opts.on('--pid-dir=DIR', 'Specifies an alternate directory in which to store the process ids.') do |dir|
|
31
|
+
@options[:pid_dir] = dir
|
32
|
+
end
|
33
|
+
opts.on('-i', '--identifier=n', 'A numeric identifier for the clockwork process.') do |n|
|
34
|
+
@options[:identifier] = n
|
35
|
+
end
|
36
|
+
opts.on('-m', '--monitor', 'Start monitor process.') do
|
37
|
+
@monitor = true
|
38
|
+
end
|
39
|
+
opts.on('--sleep-delay N', "Amount of time to sleep when no events are found") do |n|
|
40
|
+
@options[:sleep_delay] = n
|
41
|
+
end
|
42
|
+
end
|
43
|
+
@args = opts.parse!(args)
|
44
|
+
end
|
45
|
+
|
46
|
+
def daemonize
|
47
|
+
Clockworkd::Worker.before_fork
|
48
|
+
|
49
|
+
ObjectSpace.each_object(File) do |file|
|
50
|
+
@files_to_reopen << file unless file.closed?
|
51
|
+
end
|
52
|
+
|
53
|
+
dir = @options[:pid_dir]
|
54
|
+
Dir.mkdir(dir) unless File.exists?(dir)
|
55
|
+
|
56
|
+
process_name = "clockworkd.#{@options[:identifier]}"
|
57
|
+
run_process(process_name, dir)
|
58
|
+
end
|
59
|
+
|
60
|
+
def run_process(process_name, dir)
|
61
|
+
Daemons.run_proc(process_name, :multiple => false, :dir => dir, :dir_mode => :normal, :monitor => @monitor, :ARGV => @args) do |*args|
|
62
|
+
run process_name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def run(worker_name = nil)
|
67
|
+
Dir.chdir(Rails.root)
|
68
|
+
|
69
|
+
# Re-open file handles
|
70
|
+
@files_to_reopen.each do |file|
|
71
|
+
begin
|
72
|
+
file.reopen file.path, "a+"
|
73
|
+
file.sync = true
|
74
|
+
rescue ::Exception => e
|
75
|
+
raise e
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
Clockworkd::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'clockworkd.log'))
|
80
|
+
Clockworkd::Worker.after_fork
|
81
|
+
|
82
|
+
Clockworkd::Worker.new(@options).run
|
83
|
+
rescue => e
|
84
|
+
Rails.logger.fatal e
|
85
|
+
STDERR.puts e.message
|
86
|
+
exit 1
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,310 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006-2011, John Mettraux, jmettraux@gmail.com
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Made in Japan.
|
23
|
+
#++
|
24
|
+
|
25
|
+
require 'tzinfo'
|
26
|
+
|
27
|
+
|
28
|
+
module Clockworkd
|
29
|
+
|
30
|
+
#
|
31
|
+
# A 'cron line' is a line in the sense of a crontab
|
32
|
+
# (man 5 crontab) file line.
|
33
|
+
#
|
34
|
+
class CronLine
|
35
|
+
|
36
|
+
# The string used for creating this cronline instance.
|
37
|
+
#
|
38
|
+
attr_reader :original
|
39
|
+
|
40
|
+
attr_reader :seconds
|
41
|
+
attr_reader :minutes
|
42
|
+
attr_reader :hours
|
43
|
+
attr_reader :days
|
44
|
+
attr_reader :months
|
45
|
+
attr_reader :weekdays
|
46
|
+
attr_reader :monthdays
|
47
|
+
attr_reader :timezone
|
48
|
+
|
49
|
+
def initialize(line)
|
50
|
+
|
51
|
+
super()
|
52
|
+
|
53
|
+
@original = line
|
54
|
+
|
55
|
+
items = line.split
|
56
|
+
|
57
|
+
@timezone = (TZInfo::Timezone.get(items.last) rescue nil)
|
58
|
+
items.pop if @timezone
|
59
|
+
|
60
|
+
raise ArgumentError.new(
|
61
|
+
"not a valid cronline : '#{line}'"
|
62
|
+
) unless items.length == 5 or items.length == 6
|
63
|
+
|
64
|
+
offset = items.length - 5
|
65
|
+
|
66
|
+
@seconds = offset == 1 ? parse_item(items[0], 0, 59) : [ 0 ]
|
67
|
+
@minutes = parse_item(items[0 + offset], 0, 59)
|
68
|
+
@hours = parse_item(items[1 + offset], 0, 24)
|
69
|
+
@days = parse_item(items[2 + offset], 1, 31)
|
70
|
+
@months = parse_item(items[3 + offset], 1, 12)
|
71
|
+
@weekdays, @monthdays = parse_weekdays(items[4 + offset])
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns true if the given time matches this cron line.
|
75
|
+
#
|
76
|
+
def matches?(time)
|
77
|
+
|
78
|
+
time = Time.at(time) unless time.kind_of?(Time)
|
79
|
+
|
80
|
+
time = @timezone.utc_to_local(time.getutc) if @timezone
|
81
|
+
|
82
|
+
return false unless sub_match?(time.sec, @seconds)
|
83
|
+
return false unless sub_match?(time.min, @minutes)
|
84
|
+
return false unless sub_match?(time.hour, @hours)
|
85
|
+
return false unless date_match?(time)
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns the next time that this cron line is supposed to 'fire'
|
90
|
+
#
|
91
|
+
# This is raw, 3 secs to iterate over 1 year on my macbook :( brutal.
|
92
|
+
# (Well, I was wrong, takes 0.001 sec on 1.8.7 and 1.9.1)
|
93
|
+
#
|
94
|
+
# This method accepts an optional Time parameter. It's the starting point
|
95
|
+
# for the 'search'. By default, it's Time.now
|
96
|
+
#
|
97
|
+
# Note that the time instance returned will be in the same time zone that
|
98
|
+
# the given start point Time (thus a result in the local time zone will
|
99
|
+
# be passed if no start time is specified (search start time set to
|
100
|
+
# Time.now))
|
101
|
+
#
|
102
|
+
# Rufus::CronLine.new('30 7 * * *').next_time(
|
103
|
+
# Time.mktime(2008, 10, 24, 7, 29))
|
104
|
+
# #=> Fri Oct 24 07:30:00 -0500 2008
|
105
|
+
#
|
106
|
+
# Rufus::CronLine.new('30 7 * * *').next_time(
|
107
|
+
# Time.utc(2008, 10, 24, 7, 29))
|
108
|
+
# #=> Fri Oct 24 07:30:00 UTC 2008
|
109
|
+
#
|
110
|
+
# Rufus::CronLine.new('30 7 * * *').next_time(
|
111
|
+
# Time.utc(2008, 10, 24, 7, 29)).localtime
|
112
|
+
# #=> Fri Oct 24 02:30:00 -0500 2008
|
113
|
+
#
|
114
|
+
# (Thanks to K Liu for the note and the examples)
|
115
|
+
#
|
116
|
+
def next_time(now=Time.now)
|
117
|
+
|
118
|
+
time = @timezone ? @timezone.utc_to_local(now.getutc) : now
|
119
|
+
|
120
|
+
time = time - time.usec * 1e-6 + 1
|
121
|
+
# little adjustment before starting
|
122
|
+
|
123
|
+
loop do
|
124
|
+
|
125
|
+
unless date_match?(time)
|
126
|
+
time += (24 - time.hour) * 3600 - time.min * 60 - time.sec
|
127
|
+
next
|
128
|
+
end
|
129
|
+
unless sub_match?(time.hour, @hours)
|
130
|
+
time += (60 - time.min) * 60 - time.sec
|
131
|
+
next
|
132
|
+
end
|
133
|
+
unless sub_match?(time.min, @minutes)
|
134
|
+
time += 60 - time.sec
|
135
|
+
next
|
136
|
+
end
|
137
|
+
unless sub_match?(time.sec, @seconds)
|
138
|
+
time += 1
|
139
|
+
next
|
140
|
+
end
|
141
|
+
|
142
|
+
break
|
143
|
+
end
|
144
|
+
|
145
|
+
if @timezone
|
146
|
+
time = @timezone.local_to_utc(time)
|
147
|
+
time = time.getlocal unless now.utc?
|
148
|
+
end
|
149
|
+
|
150
|
+
time
|
151
|
+
end
|
152
|
+
|
153
|
+
# Returns an array of 6 arrays (seconds, minutes, hours, days,
|
154
|
+
# months, weekdays).
|
155
|
+
# This method is used by the cronline unit tests.
|
156
|
+
#
|
157
|
+
def to_array
|
158
|
+
|
159
|
+
[
|
160
|
+
@seconds,
|
161
|
+
@minutes,
|
162
|
+
@hours,
|
163
|
+
@days,
|
164
|
+
@months,
|
165
|
+
@weekdays,
|
166
|
+
@monthdays,
|
167
|
+
@timezone ? @timezone.name : nil
|
168
|
+
]
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
WEEKDAYS = %w[ sun mon tue wed thu fri sat ]
|
174
|
+
|
175
|
+
def parse_weekdays(item)
|
176
|
+
|
177
|
+
return nil if item == '*'
|
178
|
+
|
179
|
+
items = item.downcase.split(',')
|
180
|
+
|
181
|
+
weekdays = nil
|
182
|
+
monthdays = nil
|
183
|
+
|
184
|
+
items.each do |it|
|
185
|
+
|
186
|
+
if it.match(/#[12345]$/)
|
187
|
+
|
188
|
+
raise ArgumentError.new(
|
189
|
+
"ranges are not supported for monthdays (#{it})"
|
190
|
+
) if it.index('-')
|
191
|
+
|
192
|
+
(monthdays ||= []) << it
|
193
|
+
else
|
194
|
+
|
195
|
+
WEEKDAYS.each_with_index { |a, i| it.gsub!(/#{a}/, i.to_s) }
|
196
|
+
|
197
|
+
its = it.index('-') ? parse_range(it, 0, 7) : [ Integer(it) ]
|
198
|
+
its = its.collect { |i| i == 7 ? 0 : i }
|
199
|
+
|
200
|
+
(weekdays ||= []).concat(its)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
weekdays = weekdays.uniq if weekdays
|
205
|
+
|
206
|
+
[ weekdays, monthdays ]
|
207
|
+
end
|
208
|
+
|
209
|
+
def parse_item(item, min, max)
|
210
|
+
|
211
|
+
return nil if item == '*'
|
212
|
+
return parse_list(item, min, max) if item.index(',')
|
213
|
+
return parse_range(item, min, max) if item.index('*') or item.index('-')
|
214
|
+
|
215
|
+
i = Integer(item)
|
216
|
+
|
217
|
+
i = min if i < min
|
218
|
+
i = max if i > max
|
219
|
+
|
220
|
+
[ i ]
|
221
|
+
end
|
222
|
+
|
223
|
+
def parse_list(item, min, max)
|
224
|
+
|
225
|
+
item.split(',').inject([]) { |r, i|
|
226
|
+
r.push(parse_range(i, min, max))
|
227
|
+
}.flatten
|
228
|
+
end
|
229
|
+
|
230
|
+
def parse_range(item, min, max)
|
231
|
+
|
232
|
+
i = item.index('-')
|
233
|
+
j = item.index('/')
|
234
|
+
|
235
|
+
return item.to_i if (not i and not j)
|
236
|
+
|
237
|
+
inc = j ? Integer(item[j+1..-1]) : 1
|
238
|
+
|
239
|
+
istart = -1
|
240
|
+
iend = -1
|
241
|
+
|
242
|
+
if i
|
243
|
+
|
244
|
+
istart = Integer(item[0..i - 1])
|
245
|
+
|
246
|
+
if j
|
247
|
+
iend = Integer(item[i + 1..j - 1])
|
248
|
+
else
|
249
|
+
iend = Integer(item[i + 1..-1])
|
250
|
+
end
|
251
|
+
|
252
|
+
else # case */x
|
253
|
+
|
254
|
+
istart = min
|
255
|
+
iend = max
|
256
|
+
end
|
257
|
+
|
258
|
+
istart = min if istart < min
|
259
|
+
iend = max if iend > max
|
260
|
+
|
261
|
+
result = []
|
262
|
+
|
263
|
+
value = istart
|
264
|
+
loop do
|
265
|
+
result << value
|
266
|
+
value = value + inc
|
267
|
+
break if value > iend
|
268
|
+
end
|
269
|
+
|
270
|
+
result
|
271
|
+
end
|
272
|
+
|
273
|
+
def sub_match?(value, values)
|
274
|
+
|
275
|
+
values.nil? || values.include?(value)
|
276
|
+
end
|
277
|
+
|
278
|
+
def monthday_match(monthday, monthdays)
|
279
|
+
|
280
|
+
return true if monthdays == nil
|
281
|
+
return true if monthdays.include?(monthday)
|
282
|
+
end
|
283
|
+
|
284
|
+
def date_match?(date)
|
285
|
+
|
286
|
+
return false unless sub_match?(date.day, @days)
|
287
|
+
return false unless sub_match?(date.month, @months)
|
288
|
+
return false unless sub_match?(date.wday, @weekdays)
|
289
|
+
return false unless sub_match?(CronLine.monthday(date), @monthdays)
|
290
|
+
true
|
291
|
+
end
|
292
|
+
|
293
|
+
DAY_IN_SECONDS = 7 * 24 * 3600
|
294
|
+
|
295
|
+
def self.monthday(date)
|
296
|
+
|
297
|
+
count = 1
|
298
|
+
date2 = date.dup
|
299
|
+
|
300
|
+
loop do
|
301
|
+
date2 = date2 - DAY_IN_SECONDS
|
302
|
+
break if date2.month != date.month
|
303
|
+
count = count + 1
|
304
|
+
end
|
305
|
+
|
306
|
+
"#{WEEKDAYS[date.wday]}##{count}"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Clockworkd
|
2
|
+
class Event
|
3
|
+
attr_accessor :job, :block, :cronline, :cron_time, :next_time
|
4
|
+
|
5
|
+
def initialize(job, block, cronline)
|
6
|
+
@job = job
|
7
|
+
@block = block
|
8
|
+
@cronline = cronline
|
9
|
+
@cron_time = CronLine.new(@cronline)
|
10
|
+
@next_time = @cron_time.next_time
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
@job = job
|
15
|
+
end
|
16
|
+
|
17
|
+
def time?(t)
|
18
|
+
if (t.to_i - @next_time.to_i) > 0
|
19
|
+
@next_time = @cron_time.next_time
|
20
|
+
return true
|
21
|
+
end
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def run(t)
|
26
|
+
eval(@block)
|
27
|
+
rescue Exception => e
|
28
|
+
raise e
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Capistrano Recipes for managing clockworkd
|
2
|
+
#
|
3
|
+
# Add these callbacks to have the clockworkd process restart when the server
|
4
|
+
# is restarted:
|
5
|
+
#
|
6
|
+
# after "deploy:stop", "clockworkd:stop"
|
7
|
+
# after "deploy:start", "clockworkd:start"
|
8
|
+
# after "deploy:restart", "clockworkd:restart"
|
9
|
+
#
|
10
|
+
# If you want to use command line options, for example to start multiple workers,
|
11
|
+
# define a Capistrano variable clockworkd_args:
|
12
|
+
#
|
13
|
+
# set :clockworkd_args, "-n 2"
|
14
|
+
#
|
15
|
+
# If you've got clockworkd workers running on a servers, you can also specify
|
16
|
+
# which servers have clockworkd running and should be restarted after deploy.
|
17
|
+
#
|
18
|
+
# set :clockworkd_server_role, :worker
|
19
|
+
#
|
20
|
+
|
21
|
+
Capistrano::Configuration.instance.load do
|
22
|
+
namespace :clockworkd do
|
23
|
+
def rails_env
|
24
|
+
fetch(:rails_env, false) ? "RAILS_ENV=#{fetch(:rails_env)}" : ''
|
25
|
+
end
|
26
|
+
|
27
|
+
def args
|
28
|
+
fetch(:clockworkd_args, "")
|
29
|
+
end
|
30
|
+
|
31
|
+
def roles
|
32
|
+
fetch(:clockworkd_server_role, :app)
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Stop the clockworkd process"
|
36
|
+
task :stop, :roles => lambda { roles } do
|
37
|
+
run "cd #{current_path};#{rails_env} script/clockworkd stop"
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Start the clockworkd process"
|
41
|
+
task :start, :roles => lambda { roles } do
|
42
|
+
run "cd #{current_path};#{rails_env} script/clockworkd start #{args}"
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Restart the clockworkd process"
|
46
|
+
task :restart, :roles => lambda { roles } do
|
47
|
+
run "cd #{current_path};#{rails_env} script/clockworkd restart #{args}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Clockworkd
|
4
|
+
class Worker
|
5
|
+
cattr_accessor :clock_file, :sleep_delay, :logger
|
6
|
+
self.clock_file = "#{::Rails.root}/config/clockworkd.yml"
|
7
|
+
self.sleep_delay = 5
|
8
|
+
|
9
|
+
self.logger = if defined?(::Rails)
|
10
|
+
::Rails.logger
|
11
|
+
elsif defined?(RAILS_DEFAULT_LOGGER)
|
12
|
+
RAILS_DEFAULT_LOGGER
|
13
|
+
end
|
14
|
+
|
15
|
+
# name_prefix is ignored if name is set directly
|
16
|
+
attr_accessor :events
|
17
|
+
|
18
|
+
def initialize(options={})
|
19
|
+
self.events = []
|
20
|
+
|
21
|
+
@quiet = options.has_key?(:quiet) ? options[:quiet] : true
|
22
|
+
self.class.clock_file = options[:clock_file] if options.has_key?(:clock_file)
|
23
|
+
self.class.sleep_delay = options[:sleep_delay] if options.has_key?(:sleep_delay)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Hook method that is called before a new worker is forked
|
27
|
+
def self.before_fork
|
28
|
+
::ActiveRecord::Base.clear_all_connections!
|
29
|
+
end
|
30
|
+
|
31
|
+
# Hook method that is called after a new worker is forked
|
32
|
+
def self.after_fork
|
33
|
+
::ActiveRecord::Base.establish_connection
|
34
|
+
end
|
35
|
+
|
36
|
+
# Every worker has a unique name which by default is the pid of the process. There are some
|
37
|
+
# advantages to overriding this with something which survives worker retarts: Workers can#
|
38
|
+
# safely resume working on tasks which are locked by themselves. The worker will assume that
|
39
|
+
# it crashed before.
|
40
|
+
def name
|
41
|
+
return @name unless @name.nil?
|
42
|
+
"host:#{Socket.gethostname} pid:#{Process.pid}" rescue "pid:#{Process.pid}"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Sets the name of the worker.
|
46
|
+
# Setting the name to nil will reset the default worker name
|
47
|
+
def name=(val)
|
48
|
+
@name = val
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def run
|
53
|
+
trap('TERM') { log 'Exiting...'; $exit = true }
|
54
|
+
trap('INT') { log 'Exiting...'; $exit = true }
|
55
|
+
|
56
|
+
# Load scheduling
|
57
|
+
log "Load file with scheduling #{self.class.clock_file.to_s}"
|
58
|
+
yml_file = YAML::load(File.open(self.class.clock_file))
|
59
|
+
yml_file.each do |key, value|
|
60
|
+
job = key
|
61
|
+
block = value["block"]
|
62
|
+
cronline = value["cron"]
|
63
|
+
self.events << Event.new(job, block, cronline)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Start a processing
|
67
|
+
log "Starting clock for #{self.events.size} events: [ " + self.events.map { |e| e.to_s }.join(' ') + " ]"
|
68
|
+
loop do
|
69
|
+
tick
|
70
|
+
break if $exit
|
71
|
+
sleep(self.class.sleep_delay)
|
72
|
+
break if $exit
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def tick(t=Time.now)
|
77
|
+
to_run = self.events.select do |event|
|
78
|
+
event.time?(t)
|
79
|
+
end
|
80
|
+
|
81
|
+
to_run.each do |event|
|
82
|
+
log "Triggering #{event} with cronline #{event.cronline} and next time: #{event.next_time}"
|
83
|
+
begin
|
84
|
+
event.run(t)
|
85
|
+
rescue Exception => e
|
86
|
+
log "Unable to execute #{event} with error: #{e.to_s}", Logger::ERROR
|
87
|
+
raise e
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
to_run
|
92
|
+
end
|
93
|
+
|
94
|
+
def log(text, level = Logger::INFO)
|
95
|
+
text = "[Worker(#{name})] #{text}"
|
96
|
+
puts text unless @quiet
|
97
|
+
logger.add level, "#{Time.now.strftime('%FT%T%z')}: #{text}" if logger
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
class ClockworkdGenerator < Rails::Generators::Base
|
5
|
+
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
def self.source_root
|
9
|
+
@source_root ||= File.join(File.dirname(__FILE__), 'templates')
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_script_file
|
13
|
+
template 'script', 'script/clockworkd'
|
14
|
+
chmod 'script/clockworkd', 0755
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_clockworkd_file
|
18
|
+
template 'clockworkd.yml', 'config/clockworkd.yml'
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
# Run job every day at 1:59: 59 1 * * *
|
3
|
+
# Run job every two minute: */2 * * * *
|
4
|
+
clear_sesssion_job:
|
5
|
+
cron: "59 1 * * *"
|
6
|
+
block: Delayed::Job.enqueue ClearSessionsJob.new
|
7
|
+
description: "This job clear sessions table"
|
8
|
+
|
9
|
+
# Run job every day at 4:59: 59 4 * * *
|
10
|
+
# Run job every two minute: */2 * * * *
|
11
|
+
clear_job_reports_job:
|
12
|
+
cron: "59 4 * * *"
|
13
|
+
block: Delayed::Job.enqueue ClearJobReportsJob.new
|
14
|
+
description: "This job clear job report table"
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'clockworkd', 'recipes'))
|
data/roadmap
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Introduction:
|
2
|
+
To see the latest list of the roadmap please visit the Roadmap page at www.majoron.com.
|
3
|
+
|
4
|
+
Legend:
|
5
|
+
Follow notation is used at change log, roadmap and known bugs. Each bug begins with a version,
|
6
|
+
then follow category of the bug inside {}. It can be bug report, feature request and etc.
|
7
|
+
Then follow component inside []. After follow bug number at bug tracking system between // signs.
|
8
|
+
And then follow a short description of the bug.
|
9
|
+
|
10
|
+
Example:
|
11
|
+
For example bug: "1.0 { Feature Request } [ AntHill ] / 380 / STLport support required" means
|
12
|
+
that bug was created for 1.0 version of the AntHill component, bug is feature request with
|
13
|
+
380 number at bug tracking system. And bug requires STLPort support implementation.
|
14
|
+
|
15
|
+
Version 0.2
|
16
|
+
-----------
|
17
|
+
0.2 { Feature Request } [ Clockworkd ] / X / Add tests
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__), '../lib')
|
3
|
+
|
4
|
+
ENV["RAILS_ENV"] = "test"
|
5
|
+
require 'rubygems'
|
6
|
+
require 'bundler/setup'
|
7
|
+
require 'rspec'
|
8
|
+
require 'logger'
|
9
|
+
require 'rails'
|
10
|
+
require 'action_controller'
|
11
|
+
require 'clockworkd'
|
12
|
+
|
13
|
+
module Rails
|
14
|
+
module VERSION
|
15
|
+
MAJOR = 3
|
16
|
+
end
|
17
|
+
end unless defined? Rails
|
18
|
+
|
19
|
+
# Clockworkd.root = './'
|
20
|
+
RAILS_ROOT = './' unless defined?(RAILS_ROOT)
|
21
|
+
RAILS_ENV = 'test' unless defined?(RAILS_ENV)
|
22
|
+
|
23
|
+
RSpec.configure do |config|
|
24
|
+
config.mock_with :rspec
|
25
|
+
end
|
26
|
+
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: clockworkd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.5
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Adam Wiggins
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-07-31 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: tzinfo
|
16
|
+
requirement: &30906132 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.3.23
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *30906132
|
25
|
+
description: A scheduler process to replace cron, using a more flexible Ruby syntax
|
26
|
+
running as a single long-running process. Inspired by rufus-scheduler and resque-scheduler.
|
27
|
+
email: adam@heroku.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files:
|
31
|
+
- README
|
32
|
+
files:
|
33
|
+
- changelog
|
34
|
+
- clockworkd.gemspec
|
35
|
+
- contrib/clockworkd.monitrc
|
36
|
+
- generators/clockworkd/clockworkd_generator.rb
|
37
|
+
- generators/clockworkd/templates/clockworkd.yml
|
38
|
+
- generators/clockworkd/templates/script
|
39
|
+
- knownbugs
|
40
|
+
- lib/clockworkd/command.rb
|
41
|
+
- lib/clockworkd/cronline.rb
|
42
|
+
- lib/clockworkd/event.rb
|
43
|
+
- lib/clockworkd/recipes.rb
|
44
|
+
- lib/clockworkd/worker.rb
|
45
|
+
- lib/clockworkd.rb
|
46
|
+
- lib/generators/clockworkd/clockworkd_generator.rb
|
47
|
+
- lib/generators/clockworkd/templates/clockworkd.yml
|
48
|
+
- lib/generators/clockworkd/templates/script
|
49
|
+
- MIT-LICENSE
|
50
|
+
- Rakefile
|
51
|
+
- README
|
52
|
+
- recipes/clockworkd.rb
|
53
|
+
- roadmap
|
54
|
+
- spec/lib/clockworkd/command_spec.rb
|
55
|
+
- spec/lib/clockworkd/cronline_spec.rb
|
56
|
+
- spec/lib/clockworkd/event_spec.rb
|
57
|
+
- spec/lib/clockworkd/worker_spec.rb
|
58
|
+
- spec/lib/clockworkd_spec.rb
|
59
|
+
- spec/spec.opts
|
60
|
+
- spec/spec_helper.rb
|
61
|
+
- VERSION
|
62
|
+
homepage: http://www.majoron.com/project/rbundle/clockworkd
|
63
|
+
licenses: []
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project: clockworkd
|
82
|
+
rubygems_version: 1.8.10
|
83
|
+
signing_key:
|
84
|
+
specification_version: 3
|
85
|
+
summary: A scheduler process to replace cron.
|
86
|
+
test_files:
|
87
|
+
- spec/lib/clockworkd/command_spec.rb
|
88
|
+
- spec/lib/clockworkd/cronline_spec.rb
|
89
|
+
- spec/lib/clockworkd/event_spec.rb
|
90
|
+
- spec/lib/clockworkd/worker_spec.rb
|
91
|
+
- spec/lib/clockworkd_spec.rb
|
92
|
+
- spec/spec.opts
|
93
|
+
- spec/spec_helper.rb
|