clockworkd 0.2.5
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/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
|