sidekiq-scheduler 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -5,22 +5,38 @@
5
5
  sidekiq-scheduler is an extension to [Sidekiq](http://github.com/mperham/sidekiq)
6
6
  that adds support for queueing jobs in the future.
7
7
 
8
- At the moment job scheduling is only supported in a delayed fashion. Replacing cron
9
- is not the intention of this gem. Delayed jobs are Sidekiq jobs that you want to run
10
- at some point in the future.
8
+ This table explains the version requirements for redis
11
9
 
10
+ | sidekiq-scheduler version | required redis version|
11
+ |:--------------------------|----------------------:|
12
+ | ~> 1.0.0 | >= 2.2.0 |
13
+
14
+ Job scheduling is supported in two different way: Recurring (scheduled) and
15
+ Delayed.
16
+
17
+ Scheduled jobs are like cron jobs, recurring on a regular basis. Delayed
18
+ jobs are resque jobs that you want to run at some point in the future.
12
19
  The syntax is pretty explanatory:
13
20
 
14
21
  MyWorker.perform_in(5.days, 'arg1', 'arg2') # run a job in 5 days
15
22
  # or
16
23
  MyWorker.perform_at(5.days.from_now, 'arg1', 'arg2') # run job at a specific time
17
24
 
25
+ ### Documentation
26
+
27
+ This README covers what most people need to know. If you're looking for
28
+ details on individual methods, you might want to try the [rdoc](http://rdoc.info/github/yabawock/sidekiq-scheduler/master/frames).
29
+
30
+
18
31
  ## Installation
19
32
 
20
- # Rails 3.x: add it to your Gemfile
33
+ #To install:
34
+ gem install resque-scheduler
35
+
36
+ #If you use a Gemfile:
21
37
  gem 'sidekiq-scheduler'
22
38
 
23
- # Starting the scheduler
39
+ #Starting the scheduler
24
40
  bundle exec sidekiq-scheduler
25
41
 
26
42
  The scheduler will perform identically to a normal sidekiq worker with
@@ -30,9 +46,12 @@ node but all normal configuration options apply.
30
46
 
31
47
  NOTE: Since it's currently not possible to hook into the default option
32
48
  parsing provided by sidekiq you will need to use a configuration file to
33
- override the scheduler options. Currently the only option available is
49
+ override the scheduler options.
50
+ Available options are:
34
51
 
35
- resolution: <seconds between schedule runs>
52
+ :resolution: <seconds between schedule runs>
53
+ :schedule: <the schedule to be run>
54
+ :dynamic: <if true the schedule can we modified in runtime>
36
55
 
37
56
  The scheduling thread will sleep this many seconds between looking for
38
57
  jobs that need moving to the worker queue. The default is 5 seconds
@@ -58,6 +77,9 @@ the scheduler will pull it from the delayed queue and put it in the appropriate
58
77
  work queue for the given job. It will then be processed as soon as a worker is
59
78
  available (just like any other Sidekiq job).
60
79
 
80
+ The `5.days` syntax will only work if you are using ActiveSupport (Rails). If you
81
+ are not using Rails, just provide `perform_in` with the number of seconds.
82
+
61
83
  NOTE: The job does not fire **exactly** at the time supplied. Rather, once that
62
84
  time is in the past, the job moves from the delayed queue to the actual work
63
85
  queue and will be completed as workers are free to process it.
@@ -85,6 +107,81 @@ If you have the need to cancel a delayed job, you can do it like this:
85
107
  # remove the job with exactly the same parameters:
86
108
  MyWorker.remove_delayed(<timestamp>, 'arg1', 'arg2')
87
109
 
110
+ ## Scheduled Jobs (Recurring Jobs)
111
+
112
+ Scheduled (or recurring) jobs are logically no different than a standard cron
113
+ job. They are jobs that run based on a fixed schedule which is set at
114
+ startup.
115
+
116
+ The schedule is a list of Resque worker classes with arguments and a
117
+ schedule frequency (in crontab syntax). The schedule is just a hash, but
118
+ is most likely stored in a YAML like so:
119
+
120
+ CancelAbandonedOrders:
121
+ cron: "*/5 * * * *"
122
+
123
+ queue_documents_for_indexing:
124
+ cron: "0 0 * * *"
125
+ # you can use rufus-scheduler "every" syntax in place of cron if you prefer
126
+ # every: 1hr
127
+ # By default the job name (hash key) will be taken as worker class name.
128
+ # If you want to have a different job name and class name, provide the 'class' option
129
+ class: QueueDocuments
130
+ queue: high
131
+ args:
132
+ description: "This job queues all content for indexing in solr"
133
+
134
+ clear_leaderboards_contributors:
135
+ cron: "30 6 * * 1"
136
+ class: ClearLeaderboards
137
+ queue: low
138
+ args: contributors
139
+ description: "This job resets the weekly leaderboard for contributions"
140
+
141
+ You can provide options to "every" or "cron" via Array:
142
+
143
+ clear_leaderboards_moderator:
144
+ every: ["30s", :first_in => '120s']
145
+ class: CheckDaemon
146
+ queue: daemons
147
+ description: "This job will check Daemon every 30 seconds after 120 seconds after start"
148
+
149
+
150
+ NOTE: Six parameter cron's are also supported (as they supported by
151
+ rufus-scheduler which powers the sidekiq-scheduler process). This allows you
152
+ to schedule jobs per second (ie: "30 * * * * *" would fire a job every 30
153
+ seconds past the minute).
154
+
155
+ A big shout out to [rufus-scheduler](http://github.com/jmettraux/rufus-scheduler)
156
+ for handling the heavy lifting of the actual scheduling engine.
157
+
158
+ ### Time zones
159
+
160
+ Note that if you use the cron syntax, this will be interpreted as in the server time zone
161
+ rather than the `config.time_zone` specified in Rails.
162
+
163
+ You can explicitly specify the time zone that rufus-scheduler will use:
164
+
165
+ cron: "30 6 * * 1 Europe/Stockholm"
166
+
167
+ Also note that `config.time_zone` in Rails allows for a shorthand (e.g. "Stockholm")
168
+ that rufus-scheduler does not accept. If you write code to set the scheduler time zone
169
+ from the `config.time_zone` value, make sure it's the right format, e.g. with:
170
+
171
+ ActiveSupport::TimeZone.find_tzinfo(Rails.configuration.time_zone).name
172
+
173
+ A future version of sidekiq-scheduler may do this for you.
174
+
175
+ ## Using with Testing
176
+
177
+ Sidekiq uses a jobs array on workers for testing, which is supported by sidekiq-scheduler when you require the test code:
178
+
179
+ require 'sidekiq/testing'
180
+ require 'sidekiq-scheduler/testing'
181
+
182
+ MyWorker.perform_in 5, 'arg1'
183
+ puts MyWorker.jobs.inspect
184
+
88
185
  ## Note on Patches / Pull Requests
89
186
 
90
187
  * Fork the project.
@@ -0,0 +1,35 @@
1
+ Capistrano::Configuration.instance.load do
2
+ before "deploy", "sidekiq_scheduler:quiet"
3
+ after "deploy:stop", "sidekiq_scheduler:stop"
4
+ after "deploy:start", "sidekiq_scheduler:start"
5
+ after "deploy:restart", "sidekiq_scheduler:restart"
6
+
7
+ _cset(:sidekiq_timeout) { 10 }
8
+ _cset(:sidekiq_role) { :app }
9
+
10
+ namespace :sidekiq_scheduler do
11
+
12
+ desc "Quiet sidekiq-scheduler with sidekiq (stop accepting new work)"
13
+ task :quiet, :roles => lambda { fetch(:sidekiq_role) } do
14
+ run "cd #{current_path} && if [ -f #{current_path}/tmp/pids/sidekiq.pid ]; then #{fetch(:bundle_cmd, "bundle")} exec sidekiqctl quiet #{current_path}/tmp/pids/sidekiq.pid ; fi"
15
+ end
16
+
17
+ desc "Stop sidekiq-scheduler with sidekiq"
18
+ task :stop, :roles => lambda { fetch(:sidekiq_role) } do
19
+ run "cd #{current_path} && if [ -f #{current_path}/tmp/pids/sidekiq.pid ]; then #{fetch(:bundle_cmd, "bundle")} exec sidekiqctl stop #{current_path}/tmp/pids/sidekiq.pid #{fetch :sidekiq_timeout} ; fi"
20
+ end
21
+
22
+ desc "Start sidekiq-scheduler with sidekiq"
23
+ task :start, :roles => lambda { fetch(:sidekiq_role) } do
24
+ rails_env = fetch(:rails_env, "production")
25
+ run "cd #{current_path} ; nohup #{fetch(:bundle_cmd, "bundle")} exec sidekiq-scheduler -e #{rails_env} -C #{current_path}/config/sidekiq.yml -P #{current_path}/tmp/pids/sidekiq.pid >> #{current_path}/log/sidekiq.log 2>&1 &", :pty => false
26
+ end
27
+
28
+ desc "Restart sidekiq-scheduler with sidekiq"
29
+ task :restart, :roles => lambda { fetch(:sidekiq_role) } do
30
+ stop
31
+ start
32
+ end
33
+
34
+ end
35
+ end
@@ -11,8 +11,15 @@ module SidekiqScheduler
11
11
  end
12
12
 
13
13
  def run_scheduler
14
- scheduler_options = { :scheduler => true, :resolution => 5 }
14
+ scheduler_options = { :scheduler => true, :resolution => 5, :schedule => nil }
15
15
  scheduler_options.merge!(options)
16
+
17
+ if options[:config_file]
18
+ file_options = YAML.load_file(options[:config_file])
19
+ options.merge!(file_options)
20
+ options.delete(:config_file)
21
+ end
22
+
16
23
  scheduler = SidekiqScheduler::Manager.new(scheduler_options)
17
24
  scheduler.start!
18
25
  run_manager
@@ -4,6 +4,9 @@ require 'multi_json'
4
4
 
5
5
  require 'sidekiq/util'
6
6
 
7
+ require 'sidekiq/scheduler'
8
+ require 'sidekiq-scheduler/schedule'
9
+
7
10
  module SidekiqScheduler
8
11
 
9
12
  ##
@@ -16,10 +19,11 @@ module SidekiqScheduler
16
19
  include Celluloid
17
20
 
18
21
  def initialize(options={})
19
- logger.info "Booting sidekiq scheduler #{SidekiqScheduler::VERSION} with Redis at #{redis { |r| r.client.location} }"
20
- logger.debug { options.inspect }
21
22
  @enabled = options[:scheduler]
22
23
  @resolution = options[:resolution] || 5
24
+
25
+ Sidekiq::Scheduler.dynamic = options[:dynamic] || false
26
+ Sidekiq.schedule = options[:schedule] if options[:schedule]
23
27
  end
24
28
 
25
29
  def stop
@@ -27,6 +31,14 @@ module SidekiqScheduler
27
31
  end
28
32
 
29
33
  def start
34
+ #Load the schedule into rufus
35
+ #If dynamic is set, load that schedule otherwise use normal load
36
+ if @enabled && Sidekiq::Scheduler.dynamic
37
+ Sidekiq::Scheduler.reload_schedule!
38
+ elsif @enabled
39
+ Sidekiq::Scheduler.load_schedule!
40
+ end
41
+
30
42
  schedule(true)
31
43
  end
32
44
 
@@ -68,7 +80,8 @@ module SidekiqScheduler
68
80
 
69
81
  # Dispatch loop
70
82
  loop do
71
- break logger.debug('no scheduler queues to process') unless timestamp = find_next_timestamp
83
+ Sidekiq::Scheduler.update_schedule if Sidekiq::Scheduler.dynamic
84
+ break unless timestamp = find_next_timestamp
72
85
  find_scheduled_work(timestamp)
73
86
  end
74
87
 
@@ -0,0 +1,125 @@
1
+ module SidekiqScheduler
2
+ module Schedule
3
+
4
+ # Accepts a new schedule configuration of the form:
5
+ #
6
+ # {
7
+ # "MakeTea" => {
8
+ # "every" => "1m" },
9
+ # "some_name" => {
10
+ # "cron" => "5/* * * *",
11
+ # "class" => "DoSomeWork",
12
+ # "args" => "work on this string",
13
+ # "description" => "this thing works it"s butter off" },
14
+ # ...
15
+ # }
16
+ #
17
+ # Hash keys can be anything and are used to describe and reference
18
+ # the scheduled job. If the "class" argument is missing, the key
19
+ # is used implicitly as "class" argument - in the "MakeTea" example,
20
+ # "MakeTea" is used both as job name and sidekiq worker class.
21
+ #
22
+ # :cron can be any cron scheduling string
23
+ #
24
+ # :every can be used in lieu of :cron. see rufus-scheduler's 'every' usage
25
+ # for valid syntax. If :cron is present it will take precedence over :every.
26
+ #
27
+ # :class must be a sidekiq worker class. If it is missing, the job name (hash key)
28
+ # will be used as :class.
29
+ #
30
+ # :args can be any yaml which will be converted to a ruby literal and
31
+ # passed in a params. (optional)
32
+ #
33
+ # :rails_envs is the list of envs where the job gets loaded. Envs are
34
+ # comma separated (optional)
35
+ #
36
+ # :description is just that, a description of the job (optional). If
37
+ # params is an array, each element in the array is passed as a separate
38
+ # param, otherwise params is passed in as the only parameter to perform.
39
+ def schedule=(schedule_hash)
40
+ schedule_hash = prepare_schedule(schedule_hash)
41
+
42
+ if Sidekiq::Scheduler.dynamic
43
+ schedule_hash.each do |name, job_spec|
44
+ set_schedule(name, job_spec)
45
+ end
46
+ end
47
+ @schedule = schedule_hash
48
+ end
49
+
50
+ def schedule
51
+ @schedule ||= {}
52
+ end
53
+
54
+ # reloads the schedule from redis
55
+ def reload_schedule!
56
+ @schedule = get_schedule
57
+ end
58
+
59
+ # Retrive the schedule configuration for the given name
60
+ # if the name is nil it returns a hash with all the
61
+ # names end their schedules.
62
+ def get_schedule(name = nil)
63
+ if name.nil?
64
+ get_all_schedules
65
+ else
66
+ encoded_schedule = Sidekiq.redis { |r| r.hget(:schedules, name) }
67
+ encoded_schedule.nil? ? nil : MultiJson.decode(encoded_schedule)
68
+ end
69
+ end
70
+
71
+ # gets the schedule as it exists in redis
72
+ def get_all_schedules
73
+ schedules = nil
74
+ if Sidekiq.redis { |r| r.exists(:schedules) }
75
+ schedules = {}
76
+
77
+ Sidekiq.redis { |r| r.hgetall(:schedules) }.tap do |h|
78
+ h.each do |name, config|
79
+ schedules[name] = MultiJson.decode(config)
80
+ end
81
+ end
82
+ end
83
+
84
+ schedules
85
+ end
86
+
87
+ # Create or update a schedule with the provided name and configuration.
88
+ #
89
+ # Note: values for class and custom_job_class need to be strings,
90
+ # not constants.
91
+ #
92
+ # Resque.set_schedule('some_job', {:class => 'SomeJob',
93
+ # :every => '15mins',
94
+ # :queue => 'high',
95
+ # :args => '/tmp/poop'})
96
+ def set_schedule(name, config)
97
+ existing_config = get_schedule(name)
98
+ unless existing_config && existing_config == config
99
+ Sidekiq.redis { |r| r.hset(:schedules, name, MultiJson.encode(config)) }
100
+ Sidekiq.redis { |r| r.sadd(:schedules_changed, name) }
101
+ end
102
+ config
103
+ end
104
+
105
+ # remove a given schedule by name
106
+ def remove_schedule(name)
107
+ Sidekiq.redis { |r| r.hdel(:schedules, name) }
108
+ Sidekiq.redis { |r| r.sadd(:schedules_changed, name) }
109
+ end
110
+
111
+ private
112
+
113
+ def prepare_schedule(schedule_hash)
114
+ prepared_hash = {}
115
+ schedule_hash.each do |name, job_spec|
116
+ job_spec = job_spec.dup
117
+ job_spec['class'] = name unless job_spec.key?('class') || job_spec.key?(:class)
118
+ prepared_hash[name] = job_spec
119
+ end
120
+ prepared_hash
121
+ end
122
+ end
123
+ end
124
+
125
+ Sidekiq.extend SidekiqScheduler::Schedule
@@ -1,3 +1,3 @@
1
1
  module SidekiqScheduler
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -0,0 +1,163 @@
1
+ require 'rufus/scheduler'
2
+ require 'thwait'
3
+ require 'sidekiq/util'
4
+ require 'sidekiq-scheduler/manager'
5
+
6
+ module Sidekiq
7
+ class Scheduler
8
+ extend Sidekiq::Util
9
+
10
+ class << self
11
+ # If set, will try to update the schulde in the loop
12
+ attr_accessor :dynamic
13
+ end
14
+
15
+ # the Rufus::Scheduler jobs that are scheduled
16
+ def self.scheduled_jobs
17
+ @@scheduled_jobs
18
+ end
19
+
20
+ def self.print_schedule
21
+ if self.rufus_scheduler
22
+ logger.info "Scheduling Info\tLast Run"
23
+ scheduler_jobs = self.rufus_scheduler.all_jobs
24
+ scheduler_jobs.each do |k, v|
25
+ logger.info "#{v.t}\t#{v.last}\t"
26
+ end
27
+ end
28
+ end
29
+
30
+ # Pulls the schedule from Sidekiq.schedule and loads it into the
31
+ # rufus scheduler instance
32
+ def self.load_schedule!
33
+ logger.info 'Loading Schedule'
34
+
35
+ # Need to load the schedule from redis for the first time if dynamic
36
+ Sidekiq.reload_schedule! if dynamic
37
+
38
+ logger.info 'Schedule empty! Set Sidekiq.schedule' if Sidekiq.schedule.empty?
39
+
40
+ @@scheduled_jobs = {}
41
+
42
+ Sidekiq.schedule.each do |name, config|
43
+ self.load_schedule_job(name, config)
44
+ end
45
+
46
+ Sidekiq.redis { |r| r.del(:schedules_changed) }
47
+
48
+ logger.info 'Schedules Loaded'
49
+ end
50
+
51
+ # modify interval type value to value with options if options available
52
+ def self.optionizate_interval_value(value)
53
+ args = value
54
+ if args.is_a?(::Array)
55
+ return args.first if args.size > 2 || !args.last.is_a?(::Hash)
56
+ # symbolize keys of hash for options
57
+ args[1] = args[1].inject({}) do |m, i|
58
+ key, value = i
59
+ m[(key.to_sym rescue key) || key] = value
60
+ m
61
+ end
62
+ end
63
+ args
64
+ end
65
+
66
+ # Loads a job schedule into the Rufus::Scheduler and stores it in @@scheduled_jobs
67
+ def self.load_schedule_job(name, config)
68
+ # If rails_env is set in the config, enforce ENV['RAILS_ENV'] as
69
+ # required for the jobs to be scheduled. If rails_env is missing, the
70
+ # job should be scheduled regardless of what ENV['RAILS_ENV'] is set
71
+ # to.
72
+ if config['rails_env'].nil? || self.rails_env_matches?(config)
73
+ logger.info "Scheduling #{name} "
74
+ interval_defined = false
75
+ interval_types = %w{cron every}
76
+ interval_types.each do |interval_type|
77
+ if !config[interval_type].nil? && config[interval_type].length > 0
78
+ args = self.optionizate_interval_value(config[interval_type])
79
+
80
+ @@scheduled_jobs[name] = self.rufus_scheduler.send(interval_type, *args) do
81
+ logger.info "queueing #{config['class']} (#{name})"
82
+ config.delete(interval_type)
83
+ self.handle_errors { self.enqueue_from_config(config) }
84
+ end
85
+
86
+ interval_defined = true
87
+
88
+ break
89
+ end
90
+ end
91
+
92
+ unless interval_defined
93
+ logger.info "no #{interval_types.join(' / ')} found for #{config['class']} (#{name}) - skipping"
94
+ end
95
+ end
96
+ end
97
+
98
+ # Returns true if the given schedule config hash matches the current
99
+ # ENV['RAILS_ENV']
100
+ def self.rails_env_matches?(config)
101
+ config['rails_env'] && ENV['RAILS_ENV'] && config['rails_env'].gsub(/\s/,'').split(',').include?(ENV['RAILS_ENV'])
102
+ end
103
+
104
+ def self.handle_errors
105
+ begin
106
+ yield
107
+ rescue Exception => e
108
+ logger.info "#{e.class.name}: #{e.message}"
109
+ end
110
+ end
111
+
112
+ # Enqueue a job based on a config hash
113
+ def self.enqueue_from_config(job_config)
114
+ job_config['class'] = constantize(job_config['class']) # Sidekiq expects the class to be constantized.
115
+ job_config['args'] = '' if job_config['args'].nil?
116
+
117
+ Sidekiq::Client.push(job_config)
118
+ end
119
+
120
+ def self.rufus_scheduler
121
+ @rufus_scheduler ||= Rufus::Scheduler.start_new
122
+ end
123
+
124
+ # Stops old rufus scheduler and creates a new one. Returns the new
125
+ # rufus scheduler
126
+ def self.clear_schedule!
127
+ self.rufus_scheduler.stop
128
+ @rufus_scheduler = nil
129
+ @@scheduled_jobs = {}
130
+ self.rufus_scheduler
131
+ end
132
+
133
+ def self.reload_schedule!
134
+ logger.info 'Reloading Schedule'
135
+ self.clear_schedule!
136
+ self.load_schedule!
137
+ end
138
+
139
+ def self.update_schedule
140
+ if Sidekiq.redis { |r| r.scard(:schedules_changed) } > 0
141
+ logger.info 'Updating schedule'
142
+ Sidekiq.reload_schedule!
143
+ while schedule_name = Sidekiq.redis { |r| r.spop(:schedules_changed) }
144
+ if Sidekiq.schedule.keys.include?(schedule_name)
145
+ self.unschedule_job(schedule_name)
146
+ self.load_schedule_job(schedule_name, Sidekiq.schedule[schedule_name])
147
+ else
148
+ self.unschedule_job(schedule_name)
149
+ end
150
+ end
151
+ logger.info 'Schedules Loaded'
152
+ end
153
+ end
154
+
155
+ def self.unschedule_job(name)
156
+ if self.scheduled_jobs[name]
157
+ logger.debug "Removing schedule #{name}"
158
+ self.scheduled_jobs[name].unschedule
159
+ self.scheduled_jobs.delete(name)
160
+ end
161
+ end
162
+ end
163
+ end
data/test/client_test.rb CHANGED
@@ -4,7 +4,7 @@ require 'timecop'
4
4
  class ClientTest < MiniTest::Unit::TestCase
5
5
  describe 'with real redis' do
6
6
  before do
7
- Sidekiq.redis = REDIS
7
+ Sidekiq.redis = Sidekiq::RedisConnection.create(:url => 'redis://localhost/15', :namespace => 'testy')
8
8
  Sidekiq.redis {|c| c.flushdb }
9
9
  end
10
10
 
@@ -82,7 +82,7 @@ class ClientTest < MiniTest::Unit::TestCase
82
82
  end
83
83
  end
84
84
 
85
- describe 'with mock redis' do
85
+ describe 'redis calls' do
86
86
  before do
87
87
  @redis = MiniTest::Mock.new
88
88
  def @redis.multi; yield; end
@@ -100,9 +100,9 @@ class ClientTest < MiniTest::Unit::TestCase
100
100
  end
101
101
 
102
102
  it 'pushes delayed messages to redis' do
103
- @redis.expect :rpush, 1, ['delayed:1331284491', String]
104
- @redis.expect :zadd, 1, ['delayed_queue_schedule', 1331284491, 1331284491]
105
- Sidekiq::Client.delayed_push('foo', 1331284491, 'class' => 'Foo', 'args' => [1, 2])
103
+ @redis.expect :rpush, 1, ['delayed:1661284491', String]
104
+ @redis.expect :zadd, 1, ['delayed_queue_schedule', 1661284491, 1661284491]
105
+ Sidekiq::Client.delayed_push('foo', 1661284491, 'class' => 'Foo', 'args' => [1, 2])
106
106
  @redis.verify
107
107
  end
108
108
 
@@ -114,17 +114,20 @@ class ClientTest < MiniTest::Unit::TestCase
114
114
  end
115
115
 
116
116
  it 'handles perform_at' do
117
- @redis.expect :rpush, 1, ['delayed:1331284491', String]
118
- @redis.expect :zadd, 1, ['delayed_queue_schedule', 1331284491, 1331284491]
117
+
118
+ #@redis.expect :rpush, 1, ['delayed:1331284491', String]
119
+ @redis.expect :zadd, 1, ['schedule', "1331284491.0", String]
119
120
  MyWorker.perform_at(1331284491, 1, 2)
121
+
120
122
  @redis.verify
121
123
  end
122
124
 
123
125
  it 'handles perform_in' do
126
+
124
127
  Timecop.freeze(Time.now) do
125
128
  timestamp = Time.now + 30
126
- @redis.expect :rpush, 1, ["delayed:#{timestamp.to_i}", String]
127
- @redis.expect :zadd, 1, ['delayed_queue_schedule', timestamp.to_i, timestamp.to_i]
129
+ #@redis.expect :rpush, 1, ["delayed:#{timestamp.to_i}", String]
130
+ @redis.expect :zadd, 1, ['schedule', "#{timestamp.to_f}", String]
128
131
  MyWorker.perform_in(30, 1, 2)
129
132
  @redis.verify
130
133
  end
data/test/config.yml CHANGED
@@ -8,3 +8,4 @@
8
8
  :queues:
9
9
  - [often, 2]
10
10
  - [seldom, 1]
11
+ ---
@@ -0,0 +1,286 @@
1
+ require 'test_helper'
2
+
3
+ class ManagerTest < MiniTest::Unit::TestCase
4
+ describe 'Sidekiq::Scheduler' do
5
+
6
+ before do
7
+ Sidekiq::Scheduler.dynamic = false
8
+ Sidekiq.redis { |r| r.del(:schedules) }
9
+ Sidekiq.redis { |r| r.del(:schedules_changed) }
10
+ Sidekiq::Scheduler.clear_schedule!
11
+ Sidekiq::Scheduler.send(:class_variable_set, :@@scheduled_jobs, {})
12
+ end
13
+
14
+ it 'enqueue constantizes' do
15
+ # The job should be loaded, since a missing rails_env means ALL envs.
16
+ ENV['RAILS_ENV'] = 'production'
17
+ config = {
18
+ 'cron' => '* * * * *',
19
+ 'class' => 'SomeRealClass',
20
+ 'args' => '/tmp'
21
+ }
22
+
23
+ Sidekiq::Client.expects(:push).with(
24
+ {
25
+ 'cron' => '* * * * *',
26
+ 'class' => SomeRealClass,
27
+ 'args' => '/tmp'
28
+ }
29
+ )
30
+ Sidekiq::Scheduler.enqueue_from_config(config)
31
+ end
32
+
33
+ it 'enqueue_from_config respects queue params' do
34
+ config = {
35
+ 'cron' => '* * * * *',
36
+ 'class' => 'SomeIvarJob',
37
+ 'queue' => 'high'
38
+ }
39
+
40
+ Sidekiq::Client.expects(:push).with(
41
+ {
42
+ 'cron' => '* * * * *',
43
+ 'class' => SomeIvarJob,
44
+ 'args' => '',
45
+ 'queue' => 'high'
46
+ }
47
+ )
48
+
49
+ Sidekiq::Scheduler.enqueue_from_config(config)
50
+ end
51
+
52
+ it 'config makes it into the rufus_scheduler' do
53
+ assert_equal(0, Sidekiq::Scheduler.rufus_scheduler.all_jobs.size)
54
+ Sidekiq.schedule = {
55
+ :some_ivar_job => {
56
+ 'cron' => '* * * * *',
57
+ 'class' => 'SomeIvarJob',
58
+ 'args' => '/tmp'
59
+ }
60
+ }
61
+
62
+
63
+ Sidekiq::Scheduler.load_schedule!
64
+
65
+ assert_equal(1, Sidekiq::Scheduler.rufus_scheduler.all_jobs.size)
66
+ assert Sidekiq::Scheduler.scheduled_jobs.include?(:some_ivar_job)
67
+ end
68
+
69
+ it 'can reload schedule' do
70
+ Sidekiq::Scheduler.dynamic = true
71
+ Sidekiq.schedule = {
72
+ :some_ivar_job => {
73
+ 'cron' => '* * * * *',
74
+ 'class' => 'SomeIvarJob',
75
+ 'args' => '/tmp'
76
+ }
77
+ }
78
+
79
+ Sidekiq::Scheduler.load_schedule!
80
+
81
+ assert_equal(1, Sidekiq::Scheduler.rufus_scheduler.all_jobs.size)
82
+ assert Sidekiq::Scheduler.scheduled_jobs.include?(:some_ivar_job)
83
+
84
+ Sidekiq.redis { |r| r.del(:schedules) }
85
+ Sidekiq.redis do |r|
86
+ r.hset(
87
+ :schedules,
88
+ 'some_ivar_job2',
89
+ MultiJson.encode(
90
+ {
91
+ 'cron' => '* * * * *',
92
+ 'class' => 'SomeIvarJob',
93
+ 'args' => '/tmp/2'
94
+ }
95
+ )
96
+ )
97
+ end
98
+
99
+ Sidekiq::Scheduler.reload_schedule!
100
+
101
+ assert_equal(1, Sidekiq::Scheduler.rufus_scheduler.all_jobs.size)
102
+
103
+ assert_equal '/tmp/2', Sidekiq.schedule['some_ivar_job2']['args']
104
+ assert Sidekiq::Scheduler.scheduled_jobs.include?('some_ivar_job2')
105
+ end
106
+
107
+ it 'load_schedule_job loads a schedule' do
108
+ Sidekiq::Scheduler.load_schedule_job(
109
+ 'some_ivar_job',
110
+ {
111
+ 'cron' => '* * * * *',
112
+ 'class' => 'SomeIvarJob',
113
+ 'args' => '/tmp'
114
+ }
115
+ )
116
+
117
+ assert_equal(1, Sidekiq::Scheduler.rufus_scheduler.all_jobs.size)
118
+ assert_equal(1, Sidekiq::Scheduler.scheduled_jobs.size)
119
+ assert Sidekiq::Scheduler.scheduled_jobs.keys.include?('some_ivar_job')
120
+ end
121
+
122
+ it 'load_schedule_job with every with options' do
123
+ Sidekiq::Scheduler.load_schedule_job(
124
+ 'some_ivar_job',
125
+ {
126
+ 'every' => ['30s', {'first_in' => '60s'}],
127
+ 'class' => 'SomeIvarJob',
128
+ 'args' => '/tmp'
129
+ }
130
+ )
131
+
132
+ assert_equal(1, Sidekiq::Scheduler.rufus_scheduler.all_jobs.size)
133
+ assert_equal(1, Sidekiq::Scheduler.scheduled_jobs.size)
134
+ assert Sidekiq::Scheduler.scheduled_jobs.keys.include?('some_ivar_job')
135
+ assert Sidekiq::Scheduler.scheduled_jobs['some_ivar_job'].params.keys.include?(:first_in)
136
+ end
137
+
138
+ it 'load_schedule_job with cron with options' do
139
+ Sidekiq::Scheduler.load_schedule_job(
140
+ 'some_ivar_job',
141
+ {
142
+ 'cron' => ['* * * * *', {'allow_overlapping' => 'true'}],
143
+ 'class' => 'SomeIvarJob',
144
+ 'args' => '/tmp'
145
+ }
146
+ )
147
+
148
+ assert_equal(1, Sidekiq::Scheduler.rufus_scheduler.all_jobs.size)
149
+ assert_equal(1, Sidekiq::Scheduler.scheduled_jobs.size)
150
+ assert Sidekiq::Scheduler.scheduled_jobs.keys.include?('some_ivar_job')
151
+ assert Sidekiq::Scheduler.scheduled_jobs['some_ivar_job'].params.keys.include?(:allow_overlapping)
152
+ end
153
+
154
+ it 'does not load the schedule without cron' do
155
+ Sidekiq::Scheduler.load_schedule_job(
156
+ 'some_ivar_job',
157
+ {
158
+ 'class' => 'SomeIvarJob',
159
+ 'args' => '/tmp'
160
+ }
161
+ )
162
+
163
+ assert_equal(0, Sidekiq::Scheduler.rufus_scheduler.all_jobs.size)
164
+ assert_equal(0, Sidekiq::Scheduler.scheduled_jobs.size)
165
+ assert !Sidekiq::Scheduler.scheduled_jobs.keys.include?('some_ivar_job')
166
+ end
167
+
168
+ it 'does not load the schedule with an empty cron' do
169
+ Sidekiq::Scheduler.load_schedule_job(
170
+ 'some_ivar_job',
171
+ {
172
+ 'cron' => '',
173
+ 'class' => 'SomeIvarJob',
174
+ 'args' => '/tmp'
175
+ }
176
+ )
177
+
178
+ assert_equal(0, Sidekiq::Scheduler.rufus_scheduler.all_jobs.size)
179
+ assert_equal(0, Sidekiq::Scheduler.scheduled_jobs.size)
180
+ assert !Sidekiq::Scheduler.scheduled_jobs.keys.include?('some_ivar_job')
181
+ end
182
+
183
+ it 'update_schedule' do
184
+ Sidekiq::Scheduler.dynamic = true
185
+ Sidekiq.schedule =
186
+ {
187
+ 'some_ivar_job' => {'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp'},
188
+ 'another_ivar_job' => {'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/5'},
189
+ 'stay_put_job' => {'cron' => '* * * * *', 'class' => 'SomeJob', 'args' => '/tmp'}
190
+ }
191
+
192
+ Sidekiq::Scheduler.load_schedule!
193
+
194
+ Sidekiq.set_schedule(
195
+ 'some_ivar_job',
196
+ {
197
+ 'cron' => '* * * * *',
198
+ 'class' => 'SomeIvarJob',
199
+ 'args' => '/tmp/2'
200
+ }
201
+ )
202
+ Sidekiq.set_schedule(
203
+ 'new_ivar_job',
204
+ {
205
+ 'cron' => '* * * * *',
206
+ 'class' => 'SomeJob',
207
+ 'args' => '/tmp/3'
208
+ }
209
+ )
210
+ Sidekiq.set_schedule(
211
+ 'stay_put_job',
212
+ {
213
+ 'cron' => '* * * * *',
214
+ 'class' => 'SomeJob',
215
+ 'args' => '/tmp'
216
+ }
217
+ )
218
+ Sidekiq.remove_schedule('another_ivar_job')
219
+
220
+ Sidekiq::Scheduler.update_schedule
221
+
222
+ assert_equal(3, Sidekiq::Scheduler.rufus_scheduler.all_jobs.size)
223
+ assert_equal(3, Sidekiq::Scheduler.scheduled_jobs.size)
224
+
225
+
226
+ %w(some_ivar_job new_ivar_job stay_put_job).each do |job_name|
227
+ assert Sidekiq::Scheduler.scheduled_jobs.keys.include?(job_name)
228
+ assert Sidekiq.schedule.keys.include?(job_name)
229
+ end
230
+ assert !Sidekiq::Scheduler.scheduled_jobs.keys.include?('another_ivar_job')
231
+ assert !Sidekiq.schedule.keys.include?('another_ivar_job')
232
+ assert_equal 0, Sidekiq.redis { |r| r.scard(:schedules_changed) }
233
+ end
234
+
235
+ it 'update_schedule with mocks' do
236
+ Sidekiq::Scheduler.dynamic = true
237
+ Sidekiq.schedule = {
238
+ 'some_ivar_job' => {'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp'},
239
+ 'another_ivar_job' => {'cron' => '* * * * *', 'class' => 'SomeIvarJob', 'args' => '/tmp/5'},
240
+ 'stay_put_job' => {'cron' => '* * * * *', 'class' => 'SomeJob', 'args' => '/tmp'}
241
+ }
242
+
243
+ Sidekiq::Scheduler.load_schedule!
244
+
245
+ Sidekiq::Scheduler.rufus_scheduler.expects(:unschedule).with(Sidekiq::Scheduler.scheduled_jobs['some_ivar_job'].job_id)
246
+ Sidekiq::Scheduler.rufus_scheduler.expects(:unschedule).with(Sidekiq::Scheduler.scheduled_jobs['another_ivar_job'].job_id)
247
+
248
+ Sidekiq.set_schedule(
249
+ 'some_ivar_job',
250
+ {
251
+ 'cron' => '* * * * *',
252
+ 'class' => 'SomeIvarJob',
253
+ 'args' => '/tmp/2'
254
+ }
255
+ )
256
+ Sidekiq.set_schedule(
257
+ 'new_ivar_job',
258
+ {
259
+ 'cron' => '* * * * *',
260
+ 'class' => 'SomeJob',
261
+ 'args' => '/tmp/3'
262
+ }
263
+ )
264
+ Sidekiq.set_schedule(
265
+ 'stay_put_job',
266
+ {
267
+ 'cron' => '* * * * *',
268
+ 'class' => 'SomeJob',
269
+ 'args' => '/tmp'
270
+ }
271
+ )
272
+ Sidekiq.remove_schedule('another_ivar_job')
273
+
274
+ Sidekiq::Scheduler.update_schedule
275
+
276
+ assert_equal(3, Sidekiq::Scheduler.scheduled_jobs.size)
277
+ %w(some_ivar_job new_ivar_job stay_put_job).each do |job_name|
278
+ assert Sidekiq::Scheduler.scheduled_jobs.keys.include?(job_name)
279
+ assert Sidekiq.schedule.keys.include?(job_name)
280
+ end
281
+ assert !Sidekiq::Scheduler.scheduled_jobs.keys.include?('another_ivar_job')
282
+ assert !Sidekiq.schedule.keys.include?('another_ivar_job')
283
+ assert_equal 0, Sidekiq.redis { |r| r.scard(:schedules_changed) }
284
+ end
285
+ end
286
+ end
data/test/manager_test.rb CHANGED
@@ -5,7 +5,7 @@ require 'sidekiq/manager'
5
5
  class ManagerTest < MiniTest::Unit::TestCase
6
6
  describe 'with redis' do
7
7
  before do
8
- Sidekiq.redis = REDIS
8
+ Sidekiq.redis = Sidekiq::RedisConnection.create(:url => 'redis://localhost/15', :namespace => 'testy')
9
9
  Sidekiq.redis {|c| c.flushdb }
10
10
  @scheduler = SidekiqScheduler::Manager.new
11
11
  $processed = 0
@@ -0,0 +1,124 @@
1
+ require 'test_helper'
2
+
3
+ class ScheduleTest < MiniTest::Unit::TestCase
4
+ #class ScheduleManager
5
+ # extend SidekiqScheduler::ScheduleManager
6
+ #end
7
+
8
+
9
+ describe 'SidekiqScheduler::Schedule' do
10
+ it 'schedule= sets the schedule' do
11
+ Sidekiq::Scheduler.dynamic = true
12
+ Sidekiq.schedule = {
13
+ 'my_ivar_job' => {
14
+ 'cron' => '* * * * *',
15
+ 'class' => 'SomeIvarJob',
16
+ 'args' => '/tmp/75'
17
+ }
18
+ }
19
+
20
+ assert_equal(
21
+ {
22
+ 'cron' => '* * * * *',
23
+ 'class' => 'SomeIvarJob',
24
+ 'args' => '/tmp/75'
25
+ },
26
+ MultiJson.decode(Sidekiq.redis { |r|
27
+ r.hget(:schedules, 'my_ivar_job')
28
+ })
29
+ )
30
+ end
31
+
32
+ it "schedule= uses job name as 'class' argument if it's missing" do
33
+ Sidekiq::Scheduler.dynamic = true
34
+ Sidekiq.schedule = {
35
+ 'SomeIvarJob' => {
36
+ 'cron' => '* * * * *',
37
+ 'args' => '/tmp/75'
38
+ }
39
+ }
40
+
41
+ assert_equal(
42
+ {
43
+ 'cron' => '* * * * *',
44
+ 'class' => 'SomeIvarJob',
45
+ 'args' => '/tmp/75'
46
+ },
47
+ MultiJson.decode(Sidekiq.redis { |r| r.hget(:schedules, 'SomeIvarJob') })
48
+ )
49
+ assert_equal('SomeIvarJob', Sidekiq.schedule['SomeIvarJob']['class'])
50
+ end
51
+
52
+ it 'schedule= does not mutate argument' do
53
+ schedule = {
54
+ 'SomeIvarJob' => {
55
+ 'cron' => '* * * * *',
56
+ 'args' => '/tmp/75'
57
+ }
58
+ }
59
+ Sidekiq.schedule = schedule
60
+ assert !schedule['SomeIvarJob'].key?('class')
61
+ end
62
+
63
+ it 'set_schedule can set an individual schedule' do
64
+ Sidekiq.set_schedule(
65
+ 'some_ivar_job',
66
+ {
67
+ 'cron' => '* * * * *',
68
+ 'class' => 'SomeIvarJob',
69
+ 'args' => '/tmp/22'
70
+ }
71
+ )
72
+ assert_equal(
73
+ {
74
+ 'cron' => '* * * * *',
75
+ 'class' => 'SomeIvarJob',
76
+ 'args' => '/tmp/22'
77
+ },
78
+ MultiJson.decode(Sidekiq.redis { |r| r.hget(:schedules, 'some_ivar_job') })
79
+ )
80
+ assert Sidekiq.redis { |r| r.sismember(:schedules_changed, 'some_ivar_job') }
81
+ end
82
+
83
+ it 'get_schedule returns a schedule' do
84
+ Sidekiq.redis { |r| r.hset(
85
+ :schedules,
86
+ 'some_ivar_job2',
87
+ MultiJson.encode(
88
+ {
89
+ 'cron' => '* * * * *',
90
+ 'class' => 'SomeIvarJob',
91
+ 'args' => '/tmp/33'
92
+ }
93
+ )
94
+ ) }
95
+ assert_equal(
96
+ {
97
+ 'cron' => '* * * * *',
98
+ 'class' => 'SomeIvarJob',
99
+ 'args' => '/tmp/33'
100
+ },
101
+ Sidekiq.get_schedule('some_ivar_job2')
102
+ )
103
+ end
104
+
105
+ it 'remove_schedule removes a schedule' do
106
+ Sidekiq.redis do |r|
107
+ r.hset(
108
+ :schedules,
109
+ 'some_ivar_job3',
110
+ MultiJson.encode(
111
+ {
112
+ 'cron' => '* * * * *',
113
+ 'class' => 'SomeIvarJob',
114
+ 'args' => '/tmp/44'
115
+ }
116
+ )
117
+ )
118
+ end
119
+ Sidekiq.remove_schedule('some_ivar_job3')
120
+ assert_equal nil, Sidekiq.redis{ |r| r.hget(:schedules, 'some_ivar_job3') }
121
+ assert Sidekiq.redis{ |r| r.sismember(:schedules_changed, 'some_ivar_job3') }
122
+ end
123
+ end
124
+ end
data/test/test_helper.rb CHANGED
@@ -2,13 +2,45 @@ require 'minitest/unit'
2
2
  require 'minitest/pride'
3
3
  require 'minitest/autorun'
4
4
  require 'sidekiq-scheduler'
5
+ require 'mocha'
6
+ require 'multi_json'
7
+ require 'mock_redis'
5
8
 
6
9
  require 'sidekiq'
7
10
  require 'sidekiq/util'
8
- Sidekiq::Util.logger.level = Logger::ERROR
11
+ if Sidekiq.respond_to?(:logger)
12
+ Sidekiq.logger.level = Logger::ERROR
13
+ else
14
+ Sidekiq::Util.logger.level = Logger::ERROR
15
+ end
9
16
 
10
17
  # Load support files
11
18
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
12
19
 
13
20
  require 'sidekiq/redis_connection'
14
- REDIS = Sidekiq::RedisConnection.create(:url => "redis://localhost/15", :namespace => 'testy')
21
+
22
+ #Setup redis mock to avoid having a dependency
23
+ # with redis server during tests
24
+ $redis = ConnectionPool.new(:timeout => 1, :size => 1) { MockRedis.new }
25
+ Sidekiq.redis = $redis
26
+
27
+ class MiniTest::Spec
28
+ before :each do
29
+ $redis.with_connection { |conn| conn.flushdb }
30
+ end
31
+ end
32
+
33
+ class SomeJob
34
+ include Sidekiq::Worker
35
+ def self.perform(repo_id, path)
36
+ end
37
+ end
38
+
39
+ class SomeIvarJob < SomeJob
40
+ sidekiq_options :queue => :ivar
41
+ end
42
+
43
+ class SomeRealClass
44
+ include Sidekiq::Worker
45
+ sidekiq_options :queue => :some_real_queue
46
+ end
data/test/testing_test.rb CHANGED
@@ -11,7 +11,7 @@ class TestingTest < MiniTest::Unit::TestCase
11
11
  assert_equal 0, DirectWorker.jobs.size
12
12
  assert DirectWorker.perform_at(1331759054, 1, 2)
13
13
  assert_equal 1, DirectWorker.jobs.size
14
- assert_equal 1331759054, DirectWorker.jobs[0]['timestamp']
14
+ assert_equal 1331759054, DirectWorker.jobs[0]['at']
15
15
  DirectWorker.jobs.clear
16
16
 
17
17
  # perform_in
@@ -20,7 +20,7 @@ class TestingTest < MiniTest::Unit::TestCase
20
20
  assert_equal 0, DirectWorker.jobs.size
21
21
  assert DirectWorker.perform_in(30, 1, 2)
22
22
  assert_equal 1, DirectWorker.jobs.size
23
- assert_equal timestamp.to_i, DirectWorker.jobs[0]['timestamp']
23
+ assert_equal timestamp.to_f, DirectWorker.jobs[0]['at']
24
24
  end
25
25
  ensure
26
26
  # Undo override
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-scheduler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,59 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-24 00:00:00.000000000 Z
12
+ date: 2012-08-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sidekiq
16
- requirement: &70233824413420 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '1.0'
21
+ version: '2.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70233824413420
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: redis
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 2.0.1
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 2.0.1
46
+ - !ruby/object:Gem::Dependency
47
+ name: rufus-scheduler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
25
62
  - !ruby/object:Gem::Dependency
26
63
  name: rake
27
- requirement: &70233824411780 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
28
65
  none: false
29
66
  requirements:
30
67
  - - ! '>='
@@ -32,10 +69,15 @@ dependencies:
32
69
  version: '0'
33
70
  type: :development
34
71
  prerelease: false
35
- version_requirements: *70233824411780
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
36
78
  - !ruby/object:Gem::Dependency
37
79
  name: timecop
38
- requirement: &70233834978940 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
39
81
  none: false
40
82
  requirements:
41
83
  - - ! '>='
@@ -43,7 +85,60 @@ dependencies:
43
85
  version: '0'
44
86
  type: :development
45
87
  prerelease: false
46
- version_requirements: *70233834978940
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: mocha
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: minitest
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: mock_redis
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
47
142
  description: Light weight job scheduling extension for Sidekiq that adds support for
48
143
  queueing items in the future.
49
144
  email:
@@ -54,9 +149,12 @@ extensions: []
54
149
  extra_rdoc_files: []
55
150
  files:
56
151
  - bin/sidekiq-scheduler
152
+ - lib/sidekiq/scheduler.rb
153
+ - lib/sidekiq-scheduler/capistrano.rb
57
154
  - lib/sidekiq-scheduler/cli.rb
58
155
  - lib/sidekiq-scheduler/client.rb
59
156
  - lib/sidekiq-scheduler/manager.rb
157
+ - lib/sidekiq-scheduler/schedule.rb
60
158
  - lib/sidekiq-scheduler/testing.rb
61
159
  - lib/sidekiq-scheduler/version.rb
62
160
  - lib/sidekiq-scheduler/worker.rb
@@ -69,7 +167,9 @@ files:
69
167
  - test/client_test.rb
70
168
  - test/config.yml
71
169
  - test/fake_env.rb
170
+ - test/lib/sidekiq/scheduler_test.rb
72
171
  - test/manager_test.rb
172
+ - test/schedule_test.rb
73
173
  - test/support/direct_worker.rb
74
174
  - test/support/my_worker.rb
75
175
  - test/test_helper.rb
@@ -88,7 +188,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
88
188
  version: '0'
89
189
  segments:
90
190
  - 0
91
- hash: 4377536769909250181
191
+ hash: -1906038753253799220
92
192
  required_rubygems_version: !ruby/object:Gem::Requirement
93
193
  none: false
94
194
  requirements:
@@ -97,10 +197,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
197
  version: '0'
98
198
  segments:
99
199
  - 0
100
- hash: 4377536769909250181
200
+ hash: -1906038753253799220
101
201
  requirements: []
102
202
  rubyforge_project:
103
- rubygems_version: 1.8.17
203
+ rubygems_version: 1.8.23
104
204
  signing_key:
105
205
  specification_version: 3
106
206
  summary: Light weight job scheduling extension for Sidekiq
@@ -109,7 +209,9 @@ test_files:
109
209
  - test/client_test.rb
110
210
  - test/config.yml
111
211
  - test/fake_env.rb
212
+ - test/lib/sidekiq/scheduler_test.rb
112
213
  - test/manager_test.rb
214
+ - test/schedule_test.rb
113
215
  - test/support/direct_worker.rb
114
216
  - test/support/my_worker.rb
115
217
  - test/test_helper.rb