recurrent 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -35,7 +35,7 @@ Use the every method to create tasks.
35
35
  end
36
36
 
37
37
  ####Frequency
38
- The first argument is the frequency, it can be an integer in seconds, including ActiveSupport style 2.hours, 3.weeks etc, or an [IceCube::Rule](http://seejohncode.com/ice_cube/)
38
+ The first argument is the frequency, it can be an integer in seconds, including ActiveSupport style 2.hours, 3.weeks etc, or an [IceCube::Schedule](http://seejohncode.com/ice_cube/)
39
39
  ####Name
40
40
  The second argument is the name of the task
41
41
  ####Options
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- $: << File.expand_path("#{File.dirname(__FILE__)}/../lib")
3
2
  require 'rubygems'
4
3
  require 'trollop'
5
4
 
@@ -16,5 +15,5 @@ begin
16
15
  rescue LoadError
17
16
  require 'recurrent'
18
17
  end
19
-
18
+ $0 = "recurrent:worker:started-#{Time.now.to_s(:logging)}"
20
19
  Recurrent::Worker.new(opts).start
@@ -19,7 +19,7 @@ module Recurrent
19
19
  ")
20
20
  end
21
21
  end
22
- block_accessor :logger, :save_task_schedule, :load_task_schedule, :save_task_return_value, :process_locking, :handle_slow_task
22
+ block_accessor :logger, :save_task_schedule, :load_task_schedule, :save_task_return_value, :load_task_return_value, :process_locking, :handle_slow_task, :setup
23
23
 
24
24
  end
25
25
  end
@@ -1,5 +1,12 @@
1
1
  module IceCube
2
2
  class Rule
3
+
4
+ def ==(another_rule)
5
+ ['@interval', '@validation_types', '@validations'].all? do |variable|
6
+ self.instance_variable_get(variable) == another_rule.instance_variable_get(variable)
7
+ end && self.class.name == another_rule.class.name
8
+ end
9
+
3
10
  def frequency_in_seconds
4
11
  rule_type = self.class
5
12
  if rule_type == IceCube::YearlyRule
@@ -19,4 +26,12 @@ module IceCube
19
26
  end
20
27
  end
21
28
  end
22
- end
29
+
30
+ class Schedule
31
+ attr_writer :start_date
32
+
33
+ def has_same_rules?(other_schedule)
34
+ self.rrules == other_schedule.rrules
35
+ end
36
+ end
37
+ end
@@ -41,50 +41,26 @@ module Recurrent
41
41
  end
42
42
 
43
43
  def create_schedule(name, frequency, start_time=nil)
44
- logger.info "| Creating schedule"
45
- if frequency.is_a? IceCube::Rule
46
- logger.info "| Frequency is an IceCube Rule: #{frequency.to_s}"
47
- rule = frequency
48
- frequency_in_seconds = rule.frequency_in_seconds
44
+ saved_schedule = Configuration.load_task_schedule.call(name) if Configuration.load_task_schedule
45
+ new_schedule = frequency.is_a?(IceCube::Schedule) ? frequency : create_schedule_from_frequency(frequency, start_time)
46
+ if saved_schedule
47
+ use_saved_schedule_if_rules_match(saved_schedule, new_schedule)
49
48
  else
50
- logger.info "| Frequency is an integer: #{frequency}"
51
- rule = create_rule_from_frequency(frequency)
52
- logger.info "| IceCube Rule created: #{rule.to_s}"
53
- frequency_in_seconds = frequency
49
+ new_schedule
54
50
  end
55
- start_time ||= derive_start_time(name, frequency_in_seconds)
51
+ end
52
+
53
+ def create_schedule_from_frequency(frequency, start_time=nil)
54
+ logger.info "| Frequency is an integer: #{frequency}"
55
+ rule = create_rule_from_frequency(frequency)
56
+ logger.info "| IceCube Rule created: #{rule.to_s}"
57
+ frequency_in_seconds = frequency
58
+ start_time ||= derive_start_time_from_frequency(frequency_in_seconds)
56
59
  schedule = IceCube::Schedule.new(start_time)
57
60
  schedule.add_recurrence_rule rule
58
- logger.info "| schedule created"
59
61
  schedule
60
62
  end
61
63
 
62
- def derive_start_time(name, frequency)
63
- logger.info "| No start time provided, deriving one."
64
- if Configuration.load_task_schedule
65
- logger.info "| Attempting to derive from saved schedule"
66
- derive_start_time_from_saved_schedule(name, frequency)
67
- else
68
- derive_start_time_from_frequency(frequency)
69
- end
70
- end
71
-
72
- def derive_start_time_from_saved_schedule(name, frequency)
73
- saved_schedule = Configuration.load_task_schedule.call(name)
74
- if saved_schedule
75
- logger.info "| Saved schedule found"
76
- if saved_schedule.rrules.first.frequency_in_seconds == frequency
77
- logger.info "| Saved schedule frequency matches, setting start time to saved schedules next occurrence: #{saved_schedule.next_occurrence.to_s(:seconds)}"
78
- saved_schedule.next_occurrence
79
- else
80
- logger.info "| Schedule frequency does not match saved schedule frequency"
81
- derive_start_time_from_frequency(frequency)
82
- end
83
- else
84
- derive_start_time_from_frequency(frequency)
85
- end
86
- end
87
-
88
64
  def derive_start_time_from_frequency(frequency)
89
65
  logger.info "| Deriving start time from frequency"
90
66
  current_time = Time.now
@@ -135,6 +111,16 @@ module Recurrent
135
111
  end
136
112
  end
137
113
 
114
+ def use_saved_schedule_if_rules_match(saved_schedule, new_schedule)
115
+ if new_schedule.has_same_rules? saved_schedule
116
+ logger.info "| Schedule matches a saved schedule, using saved schedule."
117
+ saved_schedule.start_date = saved_schedule.next_occurrence
118
+ saved_schedule
119
+ else
120
+ new_schedule
121
+ end
122
+ end
123
+
138
124
  def self.define_frequencies(*frequencies)
139
125
  frequencies.each do |frequency|
140
126
  method_name = frequency == :day ? :daily? : :"#{frequency}ly?"
@@ -15,8 +15,18 @@ module Recurrent
15
15
  return handle_still_running(execution_time) if running?
16
16
  @thread = Thread.new do
17
17
  Thread.current["execution_time"] = execution_time
18
- return_value = action.call
19
- save_results(return_value) if save?
18
+ begin
19
+ if Configuration.load_task_return_value && action.arity == 1
20
+ previous_value = Configuration.load_task_return_value.call(name)
21
+ return_value = action.call(previous_value)
22
+ else
23
+ return_value = action.call
24
+ end
25
+ save_results(return_value) if save?
26
+ rescue => e
27
+ logger.warn("#{name} - #{e.message}")
28
+ logger.warn(e.backtrace)
29
+ end
20
30
  end
21
31
  end
22
32
 
@@ -28,8 +38,8 @@ module Recurrent
28
38
  end
29
39
 
30
40
  def next_occurrence
31
- return @next_occurrence if @next_occurrence && @next_occurrence.future?
32
- @next_occurrence = schedule.next_occurrence
41
+ occurrence = schedule.next_occurrence
42
+ schedule.start_date = occurrence
33
43
  end
34
44
 
35
45
  def save?
@@ -1,3 +1,3 @@
1
1
  module Recurrent
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -4,6 +4,7 @@ module Recurrent
4
4
  attr_accessor :scheduler, :logger
5
5
 
6
6
  def initialize(options={})
7
+ Configuration.setup.call if Configuration.setup
7
8
  file = options[:file]
8
9
  @scheduler = Scheduler.new(file)
9
10
  if options[:every]
@@ -61,7 +62,7 @@ module Recurrent
61
62
  lock_established = nil
62
63
  until lock_established
63
64
  break if $exit
64
- lock_established = Configuration.process_locking.call do
65
+ lock_established = Configuration.process_locking.call(*scheduler.tasks.map(&:name)) do
65
66
  execute
66
67
  end
67
68
  break if $exit
@@ -55,17 +55,16 @@ module Recurrent
55
55
  end
56
56
  end
57
57
 
58
- describe "create_schedule" do
59
- context "when frequency is an IceCube Rule" do
60
- subject do
58
+ describe "#create_schedule" do
59
+ context "when frequency is an IceCube::Schedule" do
60
+ before :each do
61
61
  rule = IceCube::Rule.daily(1)
62
- @scheduler.create_schedule(:test, rule)
63
- end
64
- it "should be a schedule" do
65
- subject.class.should == IceCube::Schedule
62
+ @schedule = IceCube::Schedule.new(Time.now)
63
+ @schedule.add_recurrence_rule rule
66
64
  end
67
- it "should have the correct rule" do
68
- subject.rrules.first.is_a? IceCube::DailyRule
65
+
66
+ it "returns the schedule" do
67
+ @scheduler.create_schedule(:test, @schedule).should == @schedule
69
68
  end
70
69
  end
71
70
 
@@ -83,7 +82,7 @@ module Recurrent
83
82
 
84
83
  context "when start time is not provided" do
85
84
  it "should derive its own start time" do
86
- @scheduler.should_receive(:derive_start_time)
85
+ @scheduler.should_receive(:derive_start_time_from_frequency).with(1.day)
87
86
  @scheduler.create_schedule(:test, 1.day)
88
87
  end
89
88
  end
@@ -96,7 +95,7 @@ module Recurrent
96
95
  end
97
96
  end
98
97
 
99
- describe "derive_start_time_from_frequency" do
98
+ describe "#derive_start_time_from_frequency" do
100
99
  context "when the current time is 11:35:12 am on July 26th, 2011" do
101
100
  before(:all) do
102
101
  Timecop.freeze(Time.local(2011, 7, 26, 11, 35, 12))
@@ -150,30 +149,26 @@ module Recurrent
150
149
  end
151
150
  end
152
151
 
153
- describe "derive_start_time_from_saved_schedule" do
152
+ describe "A schedule has a saved schedule" do
154
153
  before(:all) do
155
154
  @scheduler = Scheduler.new
156
155
  Configuration.load_task_schedule do |name|
157
- current_time = Time.new
158
- current_time.change(:sec => 0, :usec => 0)
159
- @scheduler.create_schedule(:test, 10.seconds, current_time) if name == :test
156
+ if name == :test
157
+ current_time = Time.new
158
+ current_time.change(:sec => 0, :usec => 0)
159
+ schedule = IceCube::Schedule.new(current_time)
160
+ schedule.add_recurrence_rule IceCube::SecondlyRule.new(10)
161
+ schedule
162
+ end
160
163
  end
161
164
  end
162
165
 
163
166
  describe "a schedule being created with a saved schedule with the same name and frequency" do
164
- it "derives its start time from the saved schedule" do
165
- @scheduler.should_not_receive(:derive_start_time_from_frequency)
166
- @scheduler.create_schedule(:test, 10.seconds)
167
- end
168
-
169
- describe "the created schedule's start time" do
170
- it "should be the next occurrence of the saved schedule" do
171
- saved_schedule = Configuration.load_task_schedule.call(:test)
172
- created_schedule = @scheduler.create_schedule(:test, 10.seconds)
173
- created_schedule.start_date.to_s(:seconds).should == saved_schedule.next_occurrence.to_s(:seconds)
174
- end
167
+ it "should return the saved schedule with its start time updated to be its next_occurrence" do
168
+ saved_schedule = Configuration.load_task_schedule.call(:test)
169
+ created_schedule = @scheduler.create_schedule(:test, 10.seconds)
170
+ created_schedule.start_date.to_s(:seconds).should == saved_schedule.next_occurrence.to_s(:seconds)
175
171
  end
176
-
177
172
  end
178
173
 
179
174
  describe "a schedule being created with a saved schedule with the same name and different frequency" do
@@ -10,7 +10,7 @@ module Recurrent
10
10
  before :each do
11
11
  @executing_task_time = 5.minutes.ago
12
12
  @current_time = Time.now
13
- @task = Task.new :name => 'execute test', :logger => Logger.new('some identifier')
13
+ @task = Task.new :name => 'execute test', :logger => Logger.new('some identifier'), :action => proc { 'blah' }
14
14
  end
15
15
 
16
16
  context "The task is still running a previous execution" do
@@ -40,30 +40,64 @@ module Recurrent
40
40
  @task.thread['execution_time'].should == @current_time
41
41
  end
42
42
 
43
- it "calls the action" do
44
- @task.action.should_receive(:call)
45
- @task.execute(@current_time)
43
+ context "load_task_return_value is configured" do
44
+ before :each do
45
+ Configuration.load_task_return_value do
46
+ "testing"
47
+ end
48
+ end
49
+
50
+ context "the action doesn't take an argument" do
51
+ it "calls the action with a nil argument" do
52
+ @task.action.should_receive(:call)
53
+ @task.execute(@current_time)
54
+ end
55
+ end
56
+
57
+ context "the action takes an argument" do
58
+ before :each do
59
+ @task.action = proc { |previous_value| "derp" }
60
+ end
61
+
62
+ it "loads the task return value and calls the action with it as an argument" do
63
+ @task.action.should_receive(:call).with("testing")
64
+ @task.execute(@current_time)
65
+ end
66
+
67
+ after :each do
68
+ @task.action = proc { "derp" }
69
+ end
70
+ end
71
+ after :each do
72
+ Configuration.load_task_return_value = nil
73
+ end
74
+ end
75
+
76
+ context "load_task_return_value is not configured" do
77
+
46
78
  end
79
+
80
+
81
+
47
82
  end
48
83
 
49
84
  describe "#next_occurrence" do
50
85
  context "a task that occurs ever 10 seconds and has just occurred" do
51
- subject do
52
- current_time = Time.new
53
- current_time.change(:sec => 0, :usec => 0)
54
- Timecop.freeze(current_time)
55
- Task.new(:name => :test, :schedule => Scheduler.new.create_schedule(:test, 10.seconds, current_time))
86
+ before :each do
87
+ @current_time = Time.new
88
+ @current_time.change(:sec => 0, :usec => 0)
89
+ Timecop.freeze(@current_time)
90
+ @task = Task.new(:name => :test, :schedule => Scheduler.new.create_schedule(:test, 10.seconds, @current_time))
56
91
  end
57
92
 
58
93
  it "should occur 10 seconds from now" do
59
- subject.next_occurrence.should == 10.seconds.from_now
94
+ @task.next_occurrence.should == 10.seconds.from_now
60
95
  end
61
96
 
62
- it "should cache its next occurrence while it's still valid" do
63
- subject.schedule.should_receive(:next_occurrence).and_return(10.seconds.from_now)
64
- subject.next_occurrence
65
- subject.schedule.should_not_receive(:next_occurrence)
66
- subject.next_occurrence
97
+ it "update the start time of the task to the time of this occurrence because IceCube::Schedule#next_occurrence gets progressively slower the farther back the start time is" do
98
+ @task.schedule.start_date.should == @current_time
99
+ @task.next_occurrence.should == 10.seconds.from_now
100
+ @task.schedule.start_date.should == 10.seconds.from_now
67
101
  end
68
102
 
69
103
  after(:each) do
@@ -41,5 +41,23 @@ module Recurrent
41
41
  Timecop.return
42
42
  end
43
43
  end
44
+
45
+ describe "setup" do
46
+ before :each do
47
+ Configuration.setup do
48
+ @setup = true
49
+ end
50
+ end
51
+
52
+ it "runs any configured setup when a worker is created" do
53
+ @setup.should == nil
54
+ Worker.new
55
+ @setup.should == true
56
+ end
57
+
58
+ after :each do
59
+ Configuration.setup = nil
60
+ end
61
+ end
44
62
  end
45
63
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recurrent
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Adam Kittelson
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-09 00:00:00 Z
18
+ date: 2011-08-13 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: ice_cube