wristwatch 0.0.1
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 +4 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/lib/core_ext/numeric.rb +5 -0
- data/lib/wristwatch.rb +19 -0
- data/lib/wristwatch/cron.rake +4 -0
- data/lib/wristwatch/dispatch.rb +17 -0
- data/lib/wristwatch/intervals.rb +51 -0
- data/lib/wristwatch/manifest.rb +32 -0
- data/lib/wristwatch/railtie.rb +7 -0
- data/lib/wristwatch/schedule.rb +38 -0
- data/lib/wristwatch/slices.rb +51 -0
- data/lib/wristwatch/task.rb +15 -0
- data/lib/wristwatch/version.rb +3 -0
- data/spec/dispatch_spec.rb +31 -0
- data/spec/manifest_spec.rb +34 -0
- data/spec/schedule_spec.rb +87 -0
- data/spec/spec_helper.rb +6 -0
- data/wristwatch.gemspec +23 -0
- metadata +86 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/wristwatch.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "core_ext/numeric"
|
2
|
+
require "wristwatch/version"
|
3
|
+
require "wristwatch/intervals"
|
4
|
+
require "wristwatch/schedule"
|
5
|
+
require "wristwatch/manifest"
|
6
|
+
require "wristwatch/task"
|
7
|
+
require "wristwatch/dispatch"
|
8
|
+
|
9
|
+
if defined?(Rails)
|
10
|
+
require 'wristwatch/railtie'
|
11
|
+
end
|
12
|
+
|
13
|
+
module Wristwatch
|
14
|
+
extend self
|
15
|
+
|
16
|
+
def run
|
17
|
+
Dispatch.new(Manifest.new.build, Schedule.new).execute
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Wristwatch
|
2
|
+
class Dispatch
|
3
|
+
|
4
|
+
def initialize(tasks, schedule)
|
5
|
+
@tasks = tasks
|
6
|
+
@schedule = schedule
|
7
|
+
end
|
8
|
+
|
9
|
+
def list
|
10
|
+
@schedule.inject([]) {|list, interval| list += @tasks[interval] }
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute
|
14
|
+
list.each(&:call)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Wristwatch
|
2
|
+
module Intervals
|
3
|
+
def hourly
|
4
|
+
true
|
5
|
+
end
|
6
|
+
|
7
|
+
def bi_hourly
|
8
|
+
now.hour.divisible_by?(2)
|
9
|
+
end
|
10
|
+
|
11
|
+
def quarter_hourly
|
12
|
+
now.hour.divisible_by?(6)
|
13
|
+
end
|
14
|
+
|
15
|
+
def bi_daily
|
16
|
+
now.hour.divisible_by?(12)
|
17
|
+
end
|
18
|
+
|
19
|
+
def daily
|
20
|
+
now.hour.eql?(0)
|
21
|
+
end
|
22
|
+
|
23
|
+
def weekly
|
24
|
+
daily and now.wday.eql?(0)
|
25
|
+
end
|
26
|
+
|
27
|
+
def bi_weekly
|
28
|
+
daily and ( now.day.eql?(1) or now.day.eql?(15) )
|
29
|
+
end
|
30
|
+
|
31
|
+
def monthly
|
32
|
+
daily and now.day.eql?(1)
|
33
|
+
end
|
34
|
+
|
35
|
+
def bi_monthly
|
36
|
+
daily and monthly and now.month.divisible_by?(2, 1)
|
37
|
+
end
|
38
|
+
|
39
|
+
def quarterly
|
40
|
+
daily and monthly and now.month.divisible_by?(3, 1)
|
41
|
+
end
|
42
|
+
|
43
|
+
def semiannually
|
44
|
+
daily and monthly and now.month.divisible_by?(6, 1)
|
45
|
+
end
|
46
|
+
|
47
|
+
def annually
|
48
|
+
daily and monthly and now.month.eql?(1)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Wristwatch
|
2
|
+
class Manifest < Hash
|
3
|
+
|
4
|
+
def build
|
5
|
+
instance_eval instructions
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_task(name, *args, &blk)
|
9
|
+
raise ArgumentError, "Wristwatch manifests require that you pass a block to be executed" unless block_given?
|
10
|
+
self[name] = [] unless has_key?(name)
|
11
|
+
self[name] << build_task(*args, &blk)
|
12
|
+
end
|
13
|
+
|
14
|
+
def instructions
|
15
|
+
File.read('config/wristwatch_jobs.rb')
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def build_task(*args, &blk)
|
21
|
+
Wristwatch::Task.new(*args, &blk)
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(meth, *args, &blk)
|
25
|
+
if blk
|
26
|
+
add_task(meth, *args, &blk)
|
27
|
+
else
|
28
|
+
super(meth, *args, &blk)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Wristwatch
|
2
|
+
class Schedule
|
3
|
+
include Intervals
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :now
|
7
|
+
|
8
|
+
def initialize(time = Time.now)
|
9
|
+
@now = time
|
10
|
+
end
|
11
|
+
|
12
|
+
def intervals
|
13
|
+
available_intervals.select { |interval| send(interval) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def available_intervals
|
17
|
+
Intervals.instance_methods
|
18
|
+
end
|
19
|
+
|
20
|
+
def each
|
21
|
+
intervals.each do |interval|
|
22
|
+
yield interval
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(meth, *args, &blk)
|
27
|
+
if meth.to_s[meth.to_s.length - 1, 1] === "?"
|
28
|
+
begin
|
29
|
+
send(meth[0, meth.to_s.length - 1], *args, &blk)
|
30
|
+
rescue NoMethodError
|
31
|
+
super(meth, *args, &blk)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
super(meth, *args, &blk)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Wristwatch
|
2
|
+
module Intervals
|
3
|
+
def hourly
|
4
|
+
true
|
5
|
+
end
|
6
|
+
|
7
|
+
def bi_hourly
|
8
|
+
now.hour.divisible_by?(2)
|
9
|
+
end
|
10
|
+
|
11
|
+
def quarter_hourly
|
12
|
+
now.hour.divisible_by?(6)
|
13
|
+
end
|
14
|
+
|
15
|
+
def bi_daily
|
16
|
+
now.hour.divisible_by?(12)
|
17
|
+
end
|
18
|
+
|
19
|
+
def daily
|
20
|
+
now.hour.eql?(0)
|
21
|
+
end
|
22
|
+
|
23
|
+
def weekly
|
24
|
+
daily and now.wday.eql?(0)
|
25
|
+
end
|
26
|
+
|
27
|
+
def bi_weekly
|
28
|
+
daily and ( now.day.eql?(1) or now.day.eql?(15) )
|
29
|
+
end
|
30
|
+
|
31
|
+
def monthly
|
32
|
+
daily and now.day.eql?(1)
|
33
|
+
end
|
34
|
+
|
35
|
+
def bi_monthly
|
36
|
+
daily and monthly and now.month.divisible_by?(2, 1)
|
37
|
+
end
|
38
|
+
|
39
|
+
def quarterly
|
40
|
+
daily and monthly and now.month.divisible_by?(3, 1)
|
41
|
+
end
|
42
|
+
|
43
|
+
def semiannually
|
44
|
+
daily and monthly and now.month.divisible_by?(6, 1)
|
45
|
+
end
|
46
|
+
|
47
|
+
def annually
|
48
|
+
daily and monthly and now.month.eql?(1)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Wristwatch::Dispatch do
|
4
|
+
let(:schedule) { Wristwatch::Schedule.new }
|
5
|
+
let(:tasks) { {
|
6
|
+
:daily => ["daily_task_1", "daily_task_2", "daily_task_3"],
|
7
|
+
:monthly => ["monthly_task"],
|
8
|
+
:weekly => ["weekly_task_1", "weekly_task_2"]
|
9
|
+
} }
|
10
|
+
before(:each) do
|
11
|
+
schedule.stub(:intervals).and_return([:daily, :weekly])
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { Wristwatch::Dispatch.new(tasks, schedule) }
|
15
|
+
|
16
|
+
its(:list) { should eql ["daily_task_1", "daily_task_2", "daily_task_3", "weekly_task_1", "weekly_task_2"] }
|
17
|
+
|
18
|
+
describe "#execute" do
|
19
|
+
let(:proc_1) { "a proc" }
|
20
|
+
let(:proc_2) { "another proc" }
|
21
|
+
before(:each) do
|
22
|
+
subject.stub(:list).and_return([proc_1, proc_2])
|
23
|
+
end
|
24
|
+
|
25
|
+
it "executes each proc in the dispatch in order" do
|
26
|
+
proc_1.should_receive(:call)
|
27
|
+
proc_2.should_receive(:call)
|
28
|
+
subject.execute
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Wristwatch::Manifest do
|
4
|
+
describe "building tasks from a file" do
|
5
|
+
before(:each) do
|
6
|
+
subject.stub(:instructions).and_return( <<-RUBY
|
7
|
+
|
8
|
+
daily "A daily job" do
|
9
|
+
"contents of a daily job"
|
10
|
+
end
|
11
|
+
|
12
|
+
hourly "An hourly job" do
|
13
|
+
"contents of an hourly job"
|
14
|
+
end
|
15
|
+
|
16
|
+
hourly "Another hourly job" do
|
17
|
+
"contents of another hourly job"
|
18
|
+
end
|
19
|
+
|
20
|
+
RUBY
|
21
|
+
)
|
22
|
+
subject.should_receive(:build_task).exactly(3).times
|
23
|
+
subject.build
|
24
|
+
end
|
25
|
+
|
26
|
+
it { should have_key(:daily) }
|
27
|
+
it { should have_key(:hourly) }
|
28
|
+
|
29
|
+
it "should have two tasks in the hourly array" do
|
30
|
+
subject[:hourly].should have(2).tasks
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Wristwatch::Schedule do
|
4
|
+
before(:each) { Time.stub!(:now).and_return(time) }
|
5
|
+
|
6
|
+
context "whenever" do
|
7
|
+
let(:time) { Time.new }
|
8
|
+
it {should be_hourly}
|
9
|
+
its(:intervals) {should include(:hourly)}
|
10
|
+
end
|
11
|
+
|
12
|
+
context "on even hours" do
|
13
|
+
let(:time) { Time.new(2011, 3, 3, 2) }
|
14
|
+
it {should be_bi_hourly}
|
15
|
+
its(:intervals) {should include(:bi_hourly)}
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "quarter-daily" do
|
19
|
+
[6, 18].each do |hour|
|
20
|
+
context "at #{hour} o'clock daily" do
|
21
|
+
let(:time) {Time.new(2011, 3, 3, hour) }
|
22
|
+
its(:intervals) { should eql [:hourly, :bi_hourly, :quarter_hourly]}
|
23
|
+
it {should be_quarter_hourly }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "bi-daily" do
|
29
|
+
context "at 12 o'clock daily" do
|
30
|
+
let(:time) {Time.new(2011, 3, 3, 12) }
|
31
|
+
its(:intervals) { should eql [:hourly, :bi_hourly, :quarter_hourly, :bi_daily]}
|
32
|
+
it {should be_bi_daily }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "at midnight" do
|
37
|
+
let(:time) { Time.new(2011, 3, 3) }
|
38
|
+
its(:intervals) { should eql [:hourly, :bi_hourly, :quarter_hourly, :bi_daily, :daily]}
|
39
|
+
it {should be_daily}
|
40
|
+
end
|
41
|
+
|
42
|
+
context "on Sunday" do
|
43
|
+
let(:time) { Time.new(2011, 3, 6) }
|
44
|
+
its(:intervals) { should eql [:hourly, :bi_hourly, :quarter_hourly, :bi_daily, :daily, :weekly] }
|
45
|
+
it { should be_weekly }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "on the 15th of the month" do
|
49
|
+
let(:time) { Time.new(2011, 3, 15) }
|
50
|
+
its(:intervals) { should eql [:hourly, :bi_hourly, :quarter_hourly, :bi_daily, :daily, :bi_weekly] }
|
51
|
+
it { should be_bi_weekly }
|
52
|
+
end
|
53
|
+
|
54
|
+
context "monthly" do
|
55
|
+
let(:time) { Time.new(2011, 2, 1) }
|
56
|
+
its(:intervals) { should eql [:hourly, :bi_hourly, :quarter_hourly, :bi_daily, :daily, :bi_weekly, :monthly ] }
|
57
|
+
it { should be_monthly }
|
58
|
+
end
|
59
|
+
|
60
|
+
context "bi-monthly" do
|
61
|
+
let(:time) { Time.new(2011, 3, 1) }
|
62
|
+
its(:intervals) { should eql [:hourly, :bi_hourly, :quarter_hourly, :bi_daily, :daily, :bi_weekly, :monthly, :bi_monthly ] }
|
63
|
+
it { should be_bi_monthly }
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "quarterly" do
|
67
|
+
[4, 10].each do |month|
|
68
|
+
context "on month #{month}" do
|
69
|
+
let(:time) { Time.new(2011, month, 1) }
|
70
|
+
its(:intervals) { should eql [:hourly, :bi_hourly, :quarter_hourly, :bi_daily, :daily, :bi_weekly, :monthly, :quarterly] }
|
71
|
+
it { should be_quarterly }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "semiannually" do
|
77
|
+
let(:time) { Time.new(2011, 7, 1) }
|
78
|
+
its(:intervals) { should eql [:hourly, :bi_hourly, :quarter_hourly, :bi_daily, :daily, :bi_weekly, :monthly, :bi_monthly, :quarterly, :semiannually] }
|
79
|
+
it { should be_semiannually }
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "annually" do
|
83
|
+
let(:time) { Time.new(2011, 1, 1) }
|
84
|
+
its(:intervals) { should eql [:hourly, :bi_hourly, :quarter_hourly, :bi_daily, :daily, :bi_weekly, :monthly, :bi_monthly, :quarterly, :semiannually, :annually] }
|
85
|
+
it { should be_annually }
|
86
|
+
end
|
87
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/wristwatch.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "wristwatch/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "wristwatch"
|
7
|
+
s.version = Wristwatch::VERSION
|
8
|
+
s.authors = ["Scott Burton"]
|
9
|
+
s.email = ["scott@chaione.com"]
|
10
|
+
s.homepage = "http://github.com/chaione/wristwatch"
|
11
|
+
s.summary = %q{Easy cron dispatching}
|
12
|
+
s.description = %q{Easy cron dispatching}
|
13
|
+
|
14
|
+
s.rubyforge_project = "wristwatch"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
#s.add_runtime_dependency "rails", ">= 3.0"
|
22
|
+
s.add_development_dependency "rspec", ">= 2.0"
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wristwatch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Scott Burton
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-10-31 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "2.0"
|
24
|
+
type: :development
|
25
|
+
version_requirements: *id001
|
26
|
+
description: Easy cron dispatching
|
27
|
+
email:
|
28
|
+
- scott@chaione.com
|
29
|
+
executables: []
|
30
|
+
|
31
|
+
extensions: []
|
32
|
+
|
33
|
+
extra_rdoc_files: []
|
34
|
+
|
35
|
+
files:
|
36
|
+
- .gitignore
|
37
|
+
- Gemfile
|
38
|
+
- Rakefile
|
39
|
+
- lib/core_ext/numeric.rb
|
40
|
+
- lib/wristwatch.rb
|
41
|
+
- lib/wristwatch/cron.rake
|
42
|
+
- lib/wristwatch/dispatch.rb
|
43
|
+
- lib/wristwatch/intervals.rb
|
44
|
+
- lib/wristwatch/manifest.rb
|
45
|
+
- lib/wristwatch/railtie.rb
|
46
|
+
- lib/wristwatch/schedule.rb
|
47
|
+
- lib/wristwatch/slices.rb
|
48
|
+
- lib/wristwatch/task.rb
|
49
|
+
- lib/wristwatch/version.rb
|
50
|
+
- spec/dispatch_spec.rb
|
51
|
+
- spec/manifest_spec.rb
|
52
|
+
- spec/schedule_spec.rb
|
53
|
+
- spec/spec_helper.rb
|
54
|
+
- wristwatch.gemspec
|
55
|
+
homepage: http://github.com/chaione/wristwatch
|
56
|
+
licenses: []
|
57
|
+
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project: wristwatch
|
78
|
+
rubygems_version: 1.8.10
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: Easy cron dispatching
|
82
|
+
test_files:
|
83
|
+
- spec/dispatch_spec.rb
|
84
|
+
- spec/manifest_spec.rb
|
85
|
+
- spec/schedule_spec.rb
|
86
|
+
- spec/spec_helper.rb
|