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
12 changes: 12 additions & 0 deletions .github/workflows/tests-schedule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: tests schedule
on:
schedule:
# Run at 06:20 UTC every Monday (weekly)
- cron: "20 6 * * 1"
workflow_dispatch:

jobs:
run-tests:
if: ${{ github.repository == 'simplymadeapps/simple-scheduler' }}
uses: ./.github/workflows/tests.yml
secrets: inherit
19 changes: 7 additions & 12 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,26 @@ on:
push:
branches:
- master
workflow_call:

jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby: [2.7.6, 3.0.4, 3.1.2]
ruby: [3.2.9, 3.3.10, 3.4.7]
env:
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
RAILS_ENV: test
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v5

- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: supercharge/[email protected]
- name: Install Code Climate
run: |
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
chmod +x ./cc-test-reporter
./cc-test-reporter before-build

- uses: supercharge/[email protected]

- name: Appraisal Install
run: bundle exec appraisal install
Expand All @@ -35,6 +33,3 @@ jobs:

- name: Rspec
run: bundle exec appraisal rspec

- name: Code Climate
run: ./cc-test-reporter after-build --exit-code $?
5 changes: 4 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ AllCops:
- "vendor/bundle/**/*"
- "gemfiles/**/*"
NewCops: enable
TargetRubyVersion: 2.7
TargetRubyVersion: 3.2

Gemspec/DevelopmentDependencies:
EnforcedStyle: gemspec

Layout/FirstArrayElementIndentation:
Enabled: false
Expand Down
17 changes: 11 additions & 6 deletions Appraisals
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
# frozen_string_literal: true

appraise "rails-6" do
gem "activejob", "~> 6.0"
gem "sidekiq", "~> 6.0"
appraise "rails-7" do
gem "activejob", "~> 7.2"
gem "sidekiq", "~> 7.0"
end

appraise "rails-7" do
gem "activejob", "~> 7.0"
gem "sidekiq", "~> 6.0"
appraise "rails-8" do
gem "activejob", "~> 8.0.0"
gem "sidekiq", "~> 8.0"
end

appraise "rails-8-1" do
gem "activejob", "~> 8.1.0"
gem "sidekiq", "~> 8.0"
end

appraise "latest" do
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# Simple Scheduler

[![tests](https://github.com/simplymadeapps/simple_scheduler/actions/workflows/tests.yml/badge.svg)](https://github.com/simplymadeapps/simple_scheduler/actions/workflows/tests.yml)
[![Code Climate](https://codeclimate.com/github/simplymadeapps/simple_scheduler/badges/gpa.svg)](https://codeclimate.com/github/simplymadeapps/simple_scheduler)
[![Test Coverage](https://codeclimate.com/github/simplymadeapps/simple_scheduler/badges/coverage.svg)](https://codeclimate.com/github/simplymadeapps/simple_scheduler/coverage)
[![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/simplymadeapps/simple_scheduler/)

Simple Scheduler is a scheduling add-on that is designed to be used with
Expand All @@ -27,7 +25,7 @@ Every option we evaluated seems to have the same flaw: **If your server is down,

You must be using:

- Rails 5.0+
- Rails 7.2+
- ActiveJob
- [Sidekiq](http://sidekiq.org)
- [Heroku Scheduler](https://elements.heroku.com/addons/scheduler)*
Expand Down
4 changes: 2 additions & 2 deletions gemfiles/rails_7.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

source "https://rubygems.org"

gem "activejob", "~> 7.0"
gem "sidekiq", "~> 6.0"
gem "activejob", "~> 7.2"
gem "sidekiq", "~> 7.0"

gemspec path: "../"
4 changes: 2 additions & 2 deletions gemfiles/rails_6.gemfile → gemfiles/rails_8.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

source "https://rubygems.org"

gem "activejob", "~> 6.0"
gem "sidekiq", "~> 6.0"
gem "activejob", "~> 8.0.0"
gem "sidekiq", "~> 8.0"

gemspec path: "../"
8 changes: 8 additions & 0 deletions gemfiles/rails_8_1.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This file was generated by Appraisal

source "https://rubygems.org"

gem "activejob", "~> 8.1.0"
gem "sidekiq", "~> 8.0"

gemspec path: "../"
12 changes: 6 additions & 6 deletions lib/simple_scheduler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

require "active_job"
require "sidekiq/api"
require_relative "./simple_scheduler/at"
require_relative "./simple_scheduler/future_job"
require_relative "./simple_scheduler/railtie"
require_relative "./simple_scheduler/scheduler_job"
require_relative "./simple_scheduler/task"
require_relative "./simple_scheduler/version"
require_relative "simple_scheduler/at"
require_relative "simple_scheduler/future_job"
require_relative "simple_scheduler/railtie"
require_relative "simple_scheduler/scheduler_job"
require_relative "simple_scheduler/task"
require_relative "simple_scheduler/version"

# Module for scheduling jobs at specific times using Sidekiq.
module SimpleScheduler
Expand Down
52 changes: 27 additions & 25 deletions lib/simple_scheduler/at.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# frozen_string_literal: true

require "delegate"

module SimpleScheduler
# A Time class for parsing the :at option on a task into the first time it should run.
# A delegator that parses the :at option on a task into the first time it should run.
# Instead of inheriting from Time (which conflicts with ActiveSupport time helpers),
# this wraps a Time instance and selectively overrides hour/hour? semantics.
# Time.now
# # => 2016-12-09 08:24:11 -0600
# SimpleScheduler::At.new("*:30")
Expand All @@ -10,8 +14,8 @@ module SimpleScheduler
# # => 2016-12-10 01:00:00 -0600
# SimpleScheduler::At.new("Sun 0:00")
# # => 2016-12-11 00:00:00 -0600
class At < Time
AT_PATTERN = /\A(Sun|Mon|Tue|Wed|Thu|Fri|Sat)?\s?(?:\*{1,2}|((?:\b[0-1]?[0-9]|2[0-3]))):([0-5]\d)\z/.freeze
class At < SimpleDelegator
AT_PATTERN = /\A(Sun|Mon|Tue|Wed|Thu|Fri|Sat)?\s?(?:\*{1,2}|((?:\b[0-1]?[0-9]|2[0-3]))):([0-5]\d)\z/
DAYS = %w[Sun Mon Tue Wed Thu Fri Sat].freeze

# Error class raised when an invalid string is given for the time.
Expand All @@ -30,15 +34,13 @@ class InvalidTime < StandardError; end
def initialize(at, time_zone = nil)
@at = at
@time_zone = time_zone || Time.zone
super(parsed_time.year, parsed_time.month, parsed_time.day,
parsed_time.hour, parsed_time.min, parsed_time.sec, parsed_time.utc_offset)
super(parsed_time) # Delegate all Time methods to parsed Time instance
end

# Always returns the specified hour if the hour was given, otherwise
# it returns the hour calculated based on other specified options.
# Returns the specified hour if present in the at string, else the delegated Time's hour.
# @return [Integer]
def hour
hour? ? at_hour : super
hour? ? at_hour : __getobj__.hour
end

# Returns whether or not the hour was specified in the :at string.
Expand All @@ -51,8 +53,9 @@ def hour?

def at_match
@at_match ||= begin
match = AT_PATTERN.match(@at)
raise InvalidTime, "The `at` option is required." if @at.nil?

match = AT_PATTERN.match(@at)
raise InvalidTime, "The `at` option '#{@at}' is invalid." if match.nil?

match
Expand All @@ -77,11 +80,11 @@ def at_wday?

def next_hour
@next_hour ||= begin
next_hour = at_hour
h = at_hour
# Add an additional hour if a specific hour wasn't given, if the minutes
# given are less than the current time's minutes.
next_hour += 1 if next_hour?
next_hour
h += 1 if next_hour?
h
end
end

Expand All @@ -94,37 +97,36 @@ def now
end

def parsed_day
parsed_day = now.beginning_of_day
day = now.beginning_of_day

# If no day of the week is given, return today
return parsed_day unless at_wday?
return day unless at_wday?

# Shift to the correct day of the week if given
add_days = at_wday - parsed_day.wday
add_days += 7 if parsed_day.wday > at_wday
parsed_day + add_days.days
add_days = at_wday - day.wday
add_days += 7 if day.wday > at_wday
day + add_days.days
end

# Returns the very first time a job should be run for the scheduled task.
# @return [Time]
def parsed_time
return @parsed_time if @parsed_time
return @parsed_time if defined?(@parsed_time) && @parsed_time

@parsed_time = parsed_day
time_object = parsed_day
change_hour = next_hour

# There is no hour 24, so we need to move to the next day
if change_hour == 24
@parsed_time = 1.day.from_now(@parsed_time)
time_object = 1.day.from_now(time_object)
change_hour = 0
end

@parsed_time = @parsed_time.change(hour: change_hour, min: at_min)
time_object = time_object.change(hour: change_hour, min: at_min)

# If the parsed time is still before the current time, add an additional day if
# the week day wasn't specified or add an additional week to get the correct time.
@parsed_time += at_wday? ? 1.week : 1.day if now > @parsed_time
@parsed_time
time_object += at_wday? ? 1.week : 1.day if now > time_object

@parsed_time = time_object
end
end
end
2 changes: 1 addition & 1 deletion lib/simple_scheduler/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module SimpleScheduler
VERSION = "1.2.0"
VERSION = "2.0.0"
end
11 changes: 6 additions & 5 deletions simple_scheduler.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@ Gem::Specification.new do |s|
s.license = "MIT"

s.metadata["rubygems_mfa_required"] = "true"
s.required_ruby_version = ">= 2.7.0"
s.required_ruby_version = ">= 3.2.0"

s.files = Dir["{lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]

s.add_dependency "activejob", ">= 6.0"
s.add_dependency "sidekiq", ">= 6.0"
s.add_dependency "activejob", ">= 7.2"
s.add_dependency "sidekiq", ">= 7.0"

s.add_development_dependency "appraisal"
s.add_development_dependency "rainbow"
s.add_development_dependency "rspec-rails"
s.add_development_dependency "rspec-rails", "~> 8"
s.add_development_dependency "rubocop", "~> 1.28"
s.add_development_dependency "simplecov", "< 0.18" # https://github.com/codeclimate/test-reporter/issues/413
s.add_development_dependency "simplecov"
s.add_development_dependency "simplecov-rcov"
end
6 changes: 3 additions & 3 deletions spec/simple_scheduler/future_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@
let(:application_job) do
Sidekiq::SortedEntry.new(
nil,
nil,
1,
"wrapped" => "SomeApplicationJob",
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
)
Expand All @@ -179,7 +179,7 @@
let(:simple_scheduler_job1) do
Sidekiq::SortedEntry.new(
nil,
nil,
1,
"wrapped" => "SimpleScheduler::FutureJob",
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
"args" => [{ "arguments" => [{ "class" => "TestJob", "name" => "test_task" }] }]
Expand All @@ -189,7 +189,7 @@
let(:simple_scheduler_job2) do
Sidekiq::SortedEntry.new(
nil,
nil,
1,
"wrapped" => "SimpleScheduler::FutureJob",
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
"args" => [{ "arguments" => [{ "class" => "AnotherJob", "name" => "another_task" }] }]
Expand Down
6 changes: 3 additions & 3 deletions spec/simple_scheduler/task_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
let(:sidekiq_entry_matching_class_and_name) do
Sidekiq::SortedEntry.new(
nil,
nil,
1,
"wrapped" => "SimpleScheduler::FutureJob",
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
"args" => [{ "arguments" => [{ "class" => "TestJob", "name" => "test_task" }] }]
Expand All @@ -39,7 +39,7 @@
let(:sidekiq_entry_matching_class_wrong_task_name) do
Sidekiq::SortedEntry.new(
nil,
nil,
1,
"wrapped" => "SimpleScheduler::FutureJob",
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
"args" => [{ "arguments" => [{ "class" => "TestJob", "name" => "wrong_task" }] }]
Expand All @@ -49,7 +49,7 @@
let(:sidekiq_entry_wrong_class) do
Sidekiq::SortedEntry.new(
nil,
nil,
1,
"wrapped" => "SimpleScheduler::FutureJob",
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
"args" => [{ "arguments" => [{ "class" => "SomeOtherJob", "name" => "test_task" }] }]
Expand Down