diff --git a/lib/crono/period.rb b/lib/crono/period.rb index 13c39a3..65d27e5 100644 --- a/lib/crono/period.rb +++ b/lib/crono/period.rb @@ -1,19 +1,20 @@ module Crono # Period describe frequency of performing a task class Period - def initialize(period, at: nil) + DAYS = [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, + :sunday] + + def initialize(period, at: nil, on: nil) @period = period @at_hour, @at_min = parse_at(at) if at + @on = parse_on(on) if on end def next(since: nil) - if since.nil? - @next = Time.now.change(time_atts) - return @next if @next.future? - since = Time.now - end - - @period.since(since).change(time_atts) + return initial_next unless since + @next = @period.since(since) + @next = @next.beginning_of_week.advance(days: @on) if @on + @next.change(time_atts) end def description @@ -22,6 +23,28 @@ module Crono desc end + private + + def initial_next + next_time = initial_day.change(time_atts) + return next_time if next_time.future? + @period.from_now.change(time_atts) + end + + def initial_day + return Time.now unless @on + day = Time.now.beginning_of_week.advance(days: @on) + return day if day.future? + @period.from_now.beginning_of_week.advance(days: @on) + end + + def parse_on(on) + day_number = DAYS.index(on) + fail "Wrong 'on' day" unless day_number + fail "period should be at least 1 week to use 'on'" if @period < 1.week + day_number + end + def parse_at(at) case at when String @@ -34,8 +57,6 @@ module Crono end end - private - def time_atts { hour: @at_hour, min: @at_min }.compact end diff --git a/spec/period_spec.rb b/spec/period_spec.rb index f7ac2a2..b8a8543 100644 --- a/spec/period_spec.rb +++ b/spec/period_spec.rb @@ -15,6 +15,43 @@ describe Crono::Period do end describe '#next' do + context 'in weakly basis' do + it "should raise error if 'on' is wrong" do + expect { @period = Crono::Period.new(7.days, on: :bad_day) } + .to raise_error("Wrong 'on' day") + end + + it 'should raise error when period is less than 1 week' do + expect { @period = Crono::Period.new(6.days, on: :monday) } + .to raise_error("period should be at least 1 week to use 'on'") + end + + it "should return a 'on' day" do + @period = Crono::Period.new(1.week, on: :thursday, at: '15:30') + current_week = Time.now.beginning_of_week + last_run_time = current_week.advance(days: 1) # last run on the tuesday + next_run_at = Time.now.next_week.advance(days: 3) + .change(hour: 15, min: 30) + expect(@period.next(since: last_run_time)).to be_eql(next_run_at) + end + + it "should return a next week day 'on'" do + @period = Crono::Period.new(1.week, on: :thursday) + Timecop.freeze(Time.now.beginning_of_week.advance(days: 4)) do + expect(@period.next).to be_eql(Time.now.next_week.advance(days: 3)) + end + end + + it 'should return a current week day on the first run if not too late' do + @period = Crono::Period.new(7.days, on: :tuesday) + beginning_of_the_week = Time.now.beginning_of_week + tuesday = beginning_of_the_week.advance(days: 1) + Timecop.freeze(beginning_of_the_week) do + expect(@period.next).to be_eql(tuesday) + end + end + end + context 'in daily basis' do it 'should return the time 2 days from now' do @period = Crono::Period.new(2.day)