wristwatch 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in wristwatch.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ class Numeric
2
+ def divisible_by?(int, offset = 0)
3
+ modulo(int) == offset
4
+ end
5
+ end
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,4 @@
1
+ desc "Hourly cron master task"
2
+ task :cron => :environment do
3
+ Wristwatch.run
4
+ 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,7 @@
1
+ module Wristwatch
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ load "wristwatch/cron.rake"
5
+ end
6
+ end
7
+ 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,15 @@
1
+ module Wristwatch
2
+ class Task
3
+ attr_reader :name
4
+ def initialize(*args, &blk)
5
+ @options = args.last.is_a?(Hash) ? args.last : {}
6
+ @name = args[0]
7
+ @proc = blk
8
+ end
9
+
10
+ def call
11
+ puts "running '#{name}'"
12
+ @proc.call
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Wristwatch
2
+ VERSION = "0.0.1"
3
+ 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
@@ -0,0 +1,6 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+ require 'rspec'
4
+ $:.unshift(File.join(File.expand_path(__FILE__), "..", "lib"))
5
+ require 'wristwatch.rb'
6
+
@@ -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