diff --git a/CHANGELOG.md b/CHANGELOG.md index 47106a9..9792b3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - Add `from:` parameter to `Mel::Job::Every#run_every` methods +- Add `from:` parameter to `Mel::Job::On#run_on` methods ## [0.17.2] - 2023-12-04 diff --git a/README.md b/README.md index 0098a6f..4612ed5 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,14 @@ This makes *Redis* the *source of truth* for schedules, allowing to easily scale DoSomeWork.run_on("0 */2 * * *", for: 6.hours, arg_1: 5, arg_2: "value") ``` + This will do the first run relative to now. For instance, if the time now is 03:00, the first run would be at 04:00, the next run at 06:00, and so on. If you would like to do the first run relative to some other time, specify that in a `from:` argument: + + ```crystal + # ->>> src/app/some_file.cr + + DoSomeWork.run_on("0 */2 * * *", from: 3.days.from_now, for: 6.hours, arg_1: 5, arg_2: "value") + ``` + Instead of `for:`, you may use `till:` and specify a `Time`. Leave those out to run forever. The `DoSomeWork.run_*` methods accept the following additional arguments: diff --git a/spec/mel/job/on_spec.cr b/spec/mel/job/on_spec.cr index fb7be12..2318083 100644 --- a/spec/mel/job/on_spec.cr +++ b/spec/mel/job/on_spec.cr @@ -5,7 +5,6 @@ describe Mel::Job::On do address = "user@domain.tld" id = "1001" schedule = "0 */2 * * *" - cron = CronParser.new(schedule) SendEmailOnJob.run_on(schedule, id: id, address: address) @@ -43,6 +42,32 @@ describe Mel::Job::On do Mel::CronTask.find(id).should be_a(Mel::CronTask) end + it "starts at specified time" do + address = "user@domain.tld" + id = "1001" + schedule = "0 */2 * * *" + cron = CronParser.new(schedule) + + SendEmailOnJob.run_on( + schedule, + id: id, + from: 10.days.from_now, + address: address + ) + + Time::Location.local = Time::Location.load("Europe/Berlin") + + Timecop.travel(cron.next(6.days.from_now)) do + task = Mel::CronTask.find(id) + task.try(&.due?).should be_false + end + + Timecop.travel(cron.next(10.days.from_now)) do + task = Mel::CronTask.find(id) + task.try(&.due?).should be_true + end + end + it "deletes task after given time" do address = "user@domain.tld" id = "1001" diff --git a/src/mel/job/on.cr b/src/mel/job/on.cr index f3859f8..6dd8f7b 100644 --- a/src/mel/job/on.cr +++ b/src/mel/job/on.cr @@ -5,6 +5,7 @@ module Mel::Job::On def self.run_on( schedule : String, for : Time::Span, + from : Time = Time.local, id = UUID.random.hexstring, retries = nil, redis = nil, @@ -12,20 +13,21 @@ module Mel::Job::On **job_args ) till = for.from_now - run_on(schedule, till, id, retries, redis, force, **job_args) + run_on(schedule, till, from, id, retries, redis, force, **job_args) end def self.run_on( schedule : String, till : Time? = nil, + from : Time = Time.local, id = UUID.random.hexstring, retries = nil, redis = nil, force = false, **job_args ) : String? + time = CronParser.new(schedule).next(from) job = new(**job_args) - time = CronParser.new(schedule).next task = Mel::CronTask.new(id.to_s, job, time, retries, till, schedule) task.id if task.enqueue(redis, force: force)