Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,19 @@ Clockwork.every(1.day, 'run.me.in.new.thread', :thread => true)

If a job is long-running or IO-intensive, this option helps keep the clock precise.

### :skip_first_run

Normally, a clockwork process that is defined to run in a specified period will run at startup.
This is sometimes undesired behaviour, if the action being run relies on other processes booting which may be slower than clock.
To avoid this problem, `:skip_first_run` can be used.

```ruby
Clockwork.every(5.minutes, 'myjob', :skip_first_run => true)
```

The above job will not run at initial boot, and instead run every 5 minutes after boot.


Configuration
-----------------------

Expand Down
18 changes: 15 additions & 3 deletions lib/clockwork/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ def initialize(manager, period, job, block, options={})
@period = period
@job = job
@at = At.parse(options[:at])
@last = nil
@block = block
@if = options[:if]
@thread = options.fetch(:thread, @manager.config[:thread])
@timezone = options.fetch(:tz, @manager.config[:tz])
@skip_first_run = options[:skip_first_run]
@last = @skip_first_run ? convert_timezone(Time.now) : nil
end

def convert_timezone(t)
Expand All @@ -21,7 +22,10 @@ def convert_timezone(t)

def run_now?(t)
t = convert_timezone(t)
elapsed_ready(t) and (@at.nil? or @at.ready?(t)) and (@if.nil? or @if.call(t))
return false unless elapsed_ready?(t)
return false unless run_at?(t)
return false unless run_if?(t)
true
end

def thread?
Expand Down Expand Up @@ -57,10 +61,18 @@ def execute
@manager.handle_error e
end

def elapsed_ready(t)
def elapsed_ready?(t)
@last.nil? || (t - @last.to_i).to_i >= @period
end

def run_at?(t)
@at.nil? || @at.ready?(t)
end

def run_if?(t)
@if.nil? || @if.call(t)
end

def validate_if_option(if_option)
if if_option && !if_option.respond_to?(:call)
raise ArgumentError.new(':if expects a callable object, but #{if_option} does not respond to call')
Expand Down
37 changes: 37 additions & 0 deletions test/event_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,41 @@
end
end
end

describe '#run_now?' do
before do
@manager = Class.new
@manager.stubs(:config).returns({})
end

describe 'event skip_first_run option set to true' do
it 'returns false on first attempt' do
event = Clockwork::Event.new(@manager, 1, nil, nil, :skip_first_run => true)
assert_equal false, event.run_now?(Time.now)
end

it 'returns true on subsequent attempts' do
event = Clockwork::Event.new(@manager, 1, nil, nil, :skip_first_run => true)
# first run
event.run_now?(Time.now)

# second run
assert_equal true, event.run_now?(Time.now + 1)
end
end

describe 'event skip_first_run option not set' do
it 'returns true on first attempt' do
event = Clockwork::Event.new(@manager, 1, nil, nil)
assert_equal true, event.run_now?(Time.now + 1)
end
end

describe 'event skip_first_run option set to false' do
it 'returns true on first attempt' do
event = Clockwork::Event.new(@manager, 1, nil, nil, :skip_first_run => false)
assert_equal true, event.run_now?(Time.now)
end
end
end
end