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