sidekiq_schedulable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 558a8b58ecf33d2edc81636f2f5ba511100a6378
4
+ data.tar.gz: 8a4596c211c0f8c14a4dfbe2afe14f1a26c312c9
5
+ SHA512:
6
+ metadata.gz: 8b0da8f414f5fc013f18dd7f4d1c321662f883c3e5e94ccd780f7b280430b86ae76c9f34793508b3e951a7bc3dc2b943e4abeeabe141a7066edda7c31f72c18d
7
+ data.tar.gz: 2a7ffab36ba92166caffe4e197b86784e4d327a2b484e3d30ee00325ff4bb24e873f7ce868490e36911e1b9d36378bd88995e154bb7a62823ee657a425657b5e
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # Sidekiq Schedulable
2
+
3
+ Schedule Cron style Sidekiq jobs
4
+
5
+ ## Usage
6
+
7
+ ```ruby
8
+ require 'sidekiq_schedulable'
9
+ ```
10
+
11
+ ```ruby
12
+ class MyJob
13
+ include Sidekiq::Worker
14
+ include Sidekiq::Schedulable
15
+
16
+ sidekiq_options retry: false, queue: 'my_scheduled_jobs_queue'
17
+ sidekiq_schedule '*/5 * * * * *'
18
+
19
+ def perform
20
+ RunReport.call
21
+ end
22
+ end
23
+ ```
@@ -0,0 +1,16 @@
1
+ module Sidekiq
2
+ module Schedulable
3
+ def self.included(klass)
4
+ klass.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def sidekiq_schedule(schedule)
9
+ SidekiqSchedulable.schedules[self.to_s] = {
10
+ worker: self,
11
+ at: schedule
12
+ }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ require 'sidekiq'
2
+ require 'sidekiq/schedulable'
3
+ require 'sidekiq_schedulable/startup'
4
+ require 'sidekiq_schedulable/middleware/server'
5
+ require 'sidekiq_schedulable/middleware/client'
6
+
7
+ module SidekiqSchedulable
8
+ def self.schedules
9
+ @schedules ||= {}
10
+ end
11
+
12
+ def self.boot!
13
+ Sidekiq.configure_server do |config|
14
+ config.server_middleware do |chain|
15
+ chain.add Middleware::Server
16
+ end
17
+
18
+ config.client_middleware do |chain|
19
+ chain.add Middleware::Client, schedules
20
+ end
21
+
22
+ config.on(:startup) do
23
+ Startup.new(schedules, Sidekiq::ScheduledSet.new).schedule!
24
+ end
25
+ end
26
+
27
+ Sidekiq.configure_client do |config|
28
+ config.client_middleware do |chain|
29
+ chain.add Middleware::Client, schedules
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ SidekiqSchedulable.boot!
@@ -0,0 +1,16 @@
1
+ module SidekiqSchedulable
2
+ module Middleware
3
+ class Client
4
+ def initialize(schedules = {})
5
+ @schedules = schedules
6
+ end
7
+
8
+ def call(worker_class, item, queue, redis_pool)
9
+ if schedule = @schedules[worker_class]
10
+ item['schedule'] = schedule[:at]
11
+ end
12
+ yield
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require 'sidekiq_schedulable/schedule'
2
+
3
+ module SidekiqSchedulable
4
+ module Middleware
5
+ class Server
6
+ def call(worker, item, queue)
7
+ yield
8
+ ensure
9
+ schedule_next_job(worker, item) if item['schedule']
10
+ end
11
+
12
+ private
13
+
14
+ def schedule_next_job(worker, item)
15
+ schedule = item['schedule']
16
+ time = Schedule.next_time(schedule)
17
+ worker.class.perform_at(time)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ require 'parse-cron'
2
+
3
+ module SidekiqSchedulable
4
+ module Schedule
5
+ def self.next_time(schedule)
6
+ CronParser.new(schedule).next(Time.now)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ require 'sidekiq_schedulable/schedule'
2
+
3
+ module SidekiqSchedulable
4
+ class Startup
5
+ def self.schedule!(schedules, current_jobs)
6
+ new(schedules, current_jobs).schedule!
7
+ end
8
+
9
+ def initialize(schedules, current_jobs)
10
+ @schedules = schedules
11
+ @current_jobs = current_jobs
12
+ end
13
+
14
+ def schedule!
15
+ schedules.each do |worker_class, schedule|
16
+ unless already_scheduled?(worker_class)
17
+ time = Schedule.next_time(schedule[:at])
18
+ worker = schedule[:worker]
19
+ worker.perform_at(time)
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :schedules, :current_jobs
27
+
28
+ def already_scheduled?(worker_class)
29
+ scheduled_jobs.any? do |job|
30
+ job.item['class'] == worker_class
31
+ end
32
+ end
33
+
34
+ def scheduled_jobs
35
+ @scheduled_jobs ||= current_jobs.select do |job|
36
+ job.item['schedule']
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module SidekiqSchedulable
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,22 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'sidekiq_schedulable/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "sidekiq_schedulable"
7
+ spec.version = SidekiqSchedulable::VERSION
8
+ spec.authors = ["Kevin Buchanan"]
9
+ spec.summary = "Scheduled Sidekiq jobs"
10
+ spec.description = "Schedule Cron style Sidekiq jobs"
11
+ spec.homepage = "https://github.com/kevinbuch/sidekiq_schedulable"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = `git ls-files -z`.split("\x0")
15
+ spec.require_paths = ["lib"]
16
+
17
+ spec.add_dependency "sidekiq", "~> 3.0"
18
+ spec.add_dependency "parse-cron", "~> 0.1.4"
19
+
20
+ spec.add_development_dependency "rspec", "~> 3.0"
21
+ spec.add_development_dependency "timecop", "~> 0.8.0"
22
+ end
@@ -0,0 +1,110 @@
1
+ require 'sidekiq_schedulable'
2
+ require 'sidekiq'
3
+ require 'sidekiq/testing'
4
+ require 'timecop'
5
+
6
+ describe SidekiqSchedulable do
7
+
8
+ Sidekiq::Testing.fake!
9
+
10
+ class TestWorker
11
+ include Sidekiq::Worker
12
+ include Sidekiq::Schedulable
13
+
14
+ sidekiq_schedule '*/10 * * * * *'
15
+ end
16
+
17
+ let(:midnight) { Time.new(2015, 10, 1, 0, 0, 0) }
18
+ let(:next_ten_minutes) { midnight + 10 * 60 }
19
+
20
+ before do
21
+ Timecop.freeze(midnight)
22
+ end
23
+
24
+ after do
25
+ TestWorker.jobs.clear
26
+ end
27
+
28
+ it "adds the schedule to the schedules" do
29
+ schedule = SidekiqSchedulable.schedules["TestWorker"]
30
+
31
+ expect(schedule[:at]).to eq('*/10 * * * * *')
32
+ expect(schedule[:worker]).to eq(TestWorker)
33
+ end
34
+
35
+ describe SidekiqSchedulable::Middleware::Server do
36
+ let(:worker) { TestWorker.new }
37
+ let(:middleware) { SidekiqSchedulable::Middleware::Server.new }
38
+
39
+ it "ensures the job is re-enqueued for next time" do
40
+ expect {
41
+ middleware.call(worker, { 'schedule' => '*/10 * * * * *' }, 'a queue') do
42
+ raise "Error"
43
+ end
44
+ }.to raise_error RuntimeError, "Error"
45
+
46
+ jobs = TestWorker.jobs
47
+
48
+ expect(jobs.size).to eq(1)
49
+ expect(jobs.first['at']).to eq(next_ten_minutes.to_f)
50
+ end
51
+
52
+ it "does not re-schedule if the job has no schedule" do
53
+ middleware.call(worker, {}, 'a queue') do
54
+ true
55
+ end
56
+
57
+ expect(TestWorker.jobs.size).to eq(0)
58
+ end
59
+ end
60
+
61
+ let(:schedules) {
62
+ { "TestWorker" => { worker: TestWorker, at: '*/10 * * * * *' } }
63
+ }
64
+
65
+ describe SidekiqSchedulable::Middleware::Client do
66
+ let(:middleware) { SidekiqSchedulable::Middleware::Client.new(schedules) }
67
+
68
+ it "adds the schedule to the job item" do
69
+ item = {}
70
+
71
+ middleware.call("TestWorker", item, "a queue", nil) do
72
+ true
73
+ end
74
+
75
+ expect(item['schedule']).to eq('*/10 * * * * *')
76
+ end
77
+
78
+ it "does not add the schedule if the worker has no schedule" do
79
+ item = {}
80
+
81
+ middleware.call("Array", item, "a queue", nil) do
82
+ true
83
+ end
84
+
85
+ expect(item['schedule']).to be_nil
86
+ end
87
+ end
88
+
89
+ describe SidekiqSchedulable::Startup do
90
+ Job = Struct.new(:item)
91
+
92
+ def current_jobs
93
+ TestWorker.jobs.map { |item| Job.new(item) }
94
+ end
95
+
96
+ it "enqueues a job for the given worker on an empty queue" do
97
+ SidekiqSchedulable::Startup.schedule!(schedules, current_jobs)
98
+
99
+ expect(TestWorker.jobs.size).to eq(1)
100
+ expect(TestWorker.jobs.first['at']).to eq(next_ten_minutes.to_f)
101
+ end
102
+
103
+ it "does not enqueue a duplicate job for the given worker" do
104
+ SidekiqSchedulable::Startup.schedule!(schedules, current_jobs)
105
+ SidekiqSchedulable::Startup.schedule!(schedules, current_jobs)
106
+
107
+ expect(TestWorker.jobs.size).to eq(1)
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,5 @@
1
+ require 'rspec'
2
+
3
+ RSpec.configure do |config|
4
+ config.order = :random
5
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sidekiq_schedulable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Buchanan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sidekiq
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: parse-cron
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.4
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: timecop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.8.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.8.0
69
+ description: Schedule Cron style Sidekiq jobs
70
+ email:
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - ".gitignore"
76
+ - Gemfile
77
+ - README.md
78
+ - lib/sidekiq/schedulable.rb
79
+ - lib/sidekiq_schedulable.rb
80
+ - lib/sidekiq_schedulable/middleware/client.rb
81
+ - lib/sidekiq_schedulable/middleware/server.rb
82
+ - lib/sidekiq_schedulable/schedule.rb
83
+ - lib/sidekiq_schedulable/startup.rb
84
+ - lib/sidekiq_schedulable/version.rb
85
+ - sidekiq_schedulable.gemspec
86
+ - spec/sidekiq_schedulable_spec.rb
87
+ - spec/spec_helper.rb
88
+ homepage: https://github.com/kevinbuch/sidekiq_schedulable
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.4.5
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Scheduled Sidekiq jobs
112
+ test_files: []