resque-scheduler 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of resque-scheduler might be problematic. Click here for more details.

data/.gitignore CHANGED
@@ -4,3 +4,5 @@ pkg
4
4
  nbproject
5
5
  Gemfile.lock
6
6
  .rvmrc
7
+ *.swp
8
+
data/AUTHORS.md CHANGED
@@ -21,6 +21,7 @@ Resque Scheduler authors
21
21
  - Denis Yagofarov
22
22
  - Evan Tahler
23
23
  - Giovanni Cappellotto
24
+ - Harry Lascelles
24
25
  - Henrik Nyh
25
26
  - James Le Cuirot
26
27
  - John Crepezzi
@@ -47,6 +48,7 @@ Resque Scheduler authors
47
48
  - Ryan Biesemeyer
48
49
  - Ryan Carver
49
50
  - Sebastian Kippe
51
+ - Spring MC
50
52
  - Tim Liner
51
53
  - Tony Lewis
52
54
  - V Sreekanth
data/README.md CHANGED
@@ -319,6 +319,13 @@ scheduled jobs, however, will not fire upon recovery of the scheduler process.
319
319
  Think of scheduled (recurring) jobs as cron jobs - if you stop cron, it doesn't fire
320
320
  missed jobs once it starts back up.
321
321
 
322
+ You might want to share a redis instance amongst multiple Rails applications with different
323
+ scheduler with different config yaml files. If this is the case, normally, only one will ever
324
+ run, leading to undesired behaviour. To allow different scheduler configs run at the same time
325
+ on one redis, you can either namespace your redis connections, or supply an environment variable
326
+ to split the shared lock key resque-scheduler uses thus:
327
+
328
+ RESQUE_SCHEDULER_MASTER_LOCK_PREFIX=MyApp: rake resque:scheduler
322
329
 
323
330
  ### resque-web Additions
324
331
 
@@ -328,7 +335,7 @@ the delayed queue.
328
335
 
329
336
  The Schedule tab:
330
337
 
331
- ![The Schedule Tab](http://img.skitch.com/20100111-km2f5gmtpbq23enpujbruj6mgk.png)
338
+ ![The Schedule Tab](https://f.cloud.github.com/assets/45143/1178456/c99e5568-21b0-11e3-8c57-e1305d0ee8ef.png)
332
339
 
333
340
  The Delayed tab:
334
341
 
@@ -44,6 +44,7 @@ module Resque
44
44
  # Schedule all jobs and continually look for delayed jobs (never returns)
45
45
  def run
46
46
  $0 = "resque-scheduler: Starting"
47
+
47
48
  # trap signals
48
49
  register_signal_handlers
49
50
 
@@ -307,10 +308,12 @@ module Resque
307
308
  true
308
309
  end
309
310
 
310
- # Sets the shutdown flag, exits if sleeping
311
+ # Sets the shutdown flag, clean schedules and exits if sleeping
311
312
  def shutdown
312
313
  @shutdown = true
314
+
313
315
  if @sleeping
316
+ Resque.clean_schedules
314
317
  Thread.new { release_master_lock! }
315
318
  exit
316
319
  end
@@ -80,7 +80,7 @@ module Resque
80
80
  end
81
81
 
82
82
  def master_lock_key
83
- :resque_scheduler_master_lock
83
+ "#{ENV['RESQUE_SCHEDULER_MASTER_LOCK_PREFIX'] || ''}resque_scheduler_master_lock".to_sym
84
84
  end
85
85
 
86
86
  def redis_master_version
@@ -25,7 +25,7 @@ module ResqueScheduler
25
25
  # is used implicitly as "class" argument - in the "MakeTea" example,
26
26
  # "MakeTea" is used both as job name and resque worker class.
27
27
  #
28
- # Any jobs that were in the old schedule, but are not
28
+ # Any jobs that were in the old schedule, but are not
29
29
  # present in the new schedule, will be removed.
30
30
  #
31
31
  # :cron can be any cron scheduling string
@@ -46,23 +46,27 @@ module ResqueScheduler
46
46
  # params is an array, each element in the array is passed as a separate
47
47
  # param, otherwise params is passed in as the only parameter to perform.
48
48
  def schedule=(schedule_hash)
49
+ # clean the schedules as it exists in redis
50
+ clean_schedules
51
+
49
52
  schedule_hash = prepare_schedule(schedule_hash)
50
53
 
51
- if Resque::Scheduler.dynamic
52
- reload_schedule!
53
- schedule_hash.each do |name, job_spec|
54
- set_schedule(name, job_spec)
55
- end
56
- (schedule.keys - schedule_hash.keys.map(&:to_s)).each do |name|
57
- remove_schedule(name)
58
- end
54
+ # store all schedules in redis, so we can retrieve them back everywhere.
55
+ schedule_hash.each do |name, job_spec|
56
+ set_schedule(name, job_spec)
59
57
  end
60
- @schedule = schedule_hash
58
+
59
+ # ensure only return the successfully saved data!
60
+ reload_schedule!
61
61
  end
62
62
 
63
63
  # Returns the schedule hash
64
64
  def schedule
65
- @schedule ||= {}
65
+ @schedule ||= get_schedules
66
+ if @schedule.nil?
67
+ return {}
68
+ end
69
+ @schedule
66
70
  end
67
71
 
68
72
  # reloads the schedule from redis
@@ -70,17 +74,28 @@ module ResqueScheduler
70
74
  @schedule = get_schedules
71
75
  end
72
76
 
73
- # gets the schedule as it exists in redis
77
+ # gets the schedules as it exists in redis
74
78
  def get_schedules
79
+ unless redis.exists(:schedules)
80
+ return nil
81
+ end
82
+
83
+ redis.hgetall(:schedules).tap do |h|
84
+ h.each do |name, config|
85
+ h[name] = decode(config)
86
+ end
87
+ end
88
+ end
89
+
90
+ # clean the schedules as it exists in redis, useful for first setup?
91
+ def clean_schedules
75
92
  if redis.exists(:schedules)
76
- redis.hgetall(:schedules).tap do |h|
77
- h.each do |name, config|
78
- h[name] = decode(config)
79
- end
93
+ redis.hkeys(:schedules).each do |key|
94
+ remove_schedule(key)
80
95
  end
81
- else
82
- nil
83
96
  end
97
+ @schedule = nil
98
+ true
84
99
  end
85
100
 
86
101
  # Create or update a schedule with the provided name and configuration.
@@ -314,7 +329,7 @@ module ResqueScheduler
314
329
  redis.unwatch
315
330
  end
316
331
  end
317
-
332
+
318
333
  def validate_job!(klass)
319
334
  if klass.to_s.empty?
320
335
  raise Resque::NoClassError.new("Jobs must be given a class.")
@@ -27,7 +27,30 @@ module ResqueScheduler
27
27
  end
28
28
 
29
29
  post "/schedule/requeue" do
30
- config = Resque.schedule[params['job_name']]
30
+ @job_name = params['job_name'] || params[:job_name]
31
+ config = Resque.schedule[@job_name]
32
+ @parameters = config['parameters'] || config[:parameters]
33
+ if @parameters
34
+ erb File.read(File.join(File.dirname(__FILE__), 'server/views/requeue-params.erb'))
35
+ else
36
+ Resque::Scheduler.enqueue_from_config(config)
37
+ redirect u("/overview")
38
+ end
39
+ end
40
+
41
+ post "/schedule/requeue_with_params" do
42
+ job_name = params['job_name'] || params[:job_name]
43
+ config = Resque.schedule[job_name]
44
+ # Build args hash from post data (removing the job name)
45
+ submitted_args = params.reject {|key, value| key == 'job_name' || key == :job_name}
46
+
47
+ # Merge constructed args hash with existing args hash for
48
+ # the job, if it exists
49
+ config_args = config['args'] || config[:args] || {}
50
+ config_args = config_args.merge(submitted_args)
51
+
52
+ # Insert the args hash into config and queue the resque job
53
+ config = config.merge({'args' => config_args})
31
54
  Resque::Scheduler.enqueue_from_config(config)
32
55
  redirect u("/overview")
33
56
  end
@@ -0,0 +1,23 @@
1
+ <h1><%= @job_name %></h1>
2
+
3
+ <p class='intro'>
4
+ This job requires parameters:
5
+ </p>
6
+
7
+ <form style="float:left" action="<%= u "/schedule/requeue_with_params" %>" method="post">
8
+ <table>
9
+ <% @parameters.each do |key, value| %>
10
+ <% value ||= {} %>
11
+ <tr>
12
+ <td>
13
+ <%= key %>
14
+ <% if value['description'] || value[:description] %>
15
+ <span style="border-bottom:1px dotted;" title="<%=value ['description'] || value[:description] %>">(?)</span>
16
+ <% end %>:
17
+ </td>
18
+ <td><input type="text" name="<%= key %>" value="<%= value['default'] || value[:default] %>"></td>
19
+ <% end %>
20
+ </table>
21
+ <input type="hidden" name="job_name" value="<%= @job_name %>">
22
+ <input type="submit" value="Queue now">
23
+ </form>
@@ -27,10 +27,10 @@
27
27
  </td>
28
28
  <td><%= h name %></td>
29
29
  <td><%= h config['description'] %></td>
30
- <td style="white-space:nowrap"><%= if !config['every'].nil?
30
+ <td style="white-space:nowrap"><%= if !config['every'].nil?
31
31
  h('every: ' + (config['every'].is_a?(Array) ? config['every'].join(", ") : config['every'].to_s ))
32
32
  elsif !config['cron'].nil?
33
- h('cron: ' + config['cron'].to_s)
33
+ h('cron: ' + config['cron'].to_s)
34
34
  else
35
35
  'Not currently scheduled'
36
36
  end %></td>
@@ -1,3 +1,3 @@
1
1
  module ResqueScheduler
2
- VERSION = '2.1.0'
2
+ VERSION = '2.1.1'
3
3
  end
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency 'json' if RUBY_VERSION < '1.9'
27
27
  spec.add_development_dependency 'rubocop' unless RUBY_VERSION < '1.9'
28
28
 
29
- spec.add_runtime_dependency 'redis', '>= 2.0.1'
29
+ spec.add_runtime_dependency 'redis', '>= 3.0.0'
30
30
  spec.add_runtime_dependency 'resque', ['>= 1.20.0', '< 1.25']
31
- spec.add_runtime_dependency 'rufus-scheduler', '>= 0'
31
+ spec.add_runtime_dependency 'rufus-scheduler', '~> 2.0'
32
32
  end
@@ -30,3 +30,87 @@ context "on GET to /delayed" do
30
30
 
31
31
  should_respond_with_success
32
32
  end
33
+
34
+ def resque_schedule
35
+ {
36
+ 'job_without_params' => {
37
+ 'cron' => "* * * * *",
38
+ 'class' => 'JobWithoutParams',
39
+ 'args' => {"host" => 'localhost'},
40
+ 'rails_env' => 'production'},
41
+ 'job_with_params' => {
42
+ 'cron' => "* * * * *",
43
+ 'class' => 'JobWithParams',
44
+ 'args' => {"host" => 'localhost'},
45
+ 'parameters' => {
46
+ 'log_level' => {
47
+ 'description' => 'The level of logging',
48
+ 'default' => 'warn'
49
+ }
50
+ }
51
+ }
52
+ }
53
+ end
54
+
55
+ context "POST /schedule/requeue" do
56
+ setup do
57
+ Resque.schedule = resque_schedule
58
+ Resque::Scheduler.load_schedule!
59
+ end
60
+
61
+ test 'job without params' do
62
+ # Regular jobs without params should redirect to /overview
63
+ job_name = 'job_without_params'
64
+ Resque::Scheduler.stubs(:enqueue_from_config).once.with(Resque.schedule[job_name])
65
+
66
+ post '/schedule/requeue', {'job_name' => job_name}
67
+ follow_redirect!
68
+ assert_equal "http://example.org/overview", last_request.url
69
+ assert last_response.ok?
70
+ end
71
+
72
+ test 'job with params' do
73
+ # If a job has params defined,
74
+ # it should render the template with a form for the job params
75
+ job_name = 'job_with_params'
76
+ post '/schedule/requeue', {'job_name' => job_name}
77
+
78
+ assert last_response.ok?, last_response.errors
79
+ assert last_response.body.include?("This job requires parameters")
80
+ assert last_response.body.include?("<input type=\"hidden\" name=\"job_name\" value=\"#{job_name}\">")
81
+
82
+ Resque.schedule[job_name]['parameters'].each do |param_name, param_config|
83
+ assert last_response.body.include?(
84
+ "<span style=\"border-bottom:1px dotted;\" title=\"#{param_config['description']}\">(?)</span>")
85
+ assert last_response.body.include?(
86
+ "<input type=\"text\" name=\"log_level\" value=\"#{param_config['default']}\">")
87
+ end
88
+ end
89
+ end
90
+
91
+ context "POST /schedule/requeue_with_params" do
92
+ setup do
93
+ Resque.schedule = resque_schedule
94
+ Resque::Scheduler.load_schedule!
95
+ end
96
+
97
+ test 'job with params' do
98
+ job_name = 'job_with_params'
99
+ log_level = 'error'
100
+
101
+ job_config = Resque.schedule[job_name]
102
+ args = job_config['args'].merge({'log_level' => log_level})
103
+ job_config = job_config.merge({'args' => args})
104
+
105
+ Resque::Scheduler.stubs(:enqueue_from_config).once.with(job_config)
106
+
107
+ post '/schedule/requeue_with_params', {
108
+ 'job_name' => job_name,
109
+ 'log_level' => log_level
110
+ }
111
+ follow_redirect!
112
+ assert_equal "http://example.org/overview", last_request.url
113
+
114
+ assert last_response.ok?, last_response.errors
115
+ end
116
+ end
@@ -46,7 +46,7 @@ context "Resque::Scheduler" do
46
46
  Resque::Scheduler.load_schedule!
47
47
 
48
48
  assert_equal(1, Resque::Scheduler.rufus_scheduler.all_jobs.size)
49
- assert Resque::Scheduler.scheduled_jobs.include?(:some_ivar_job)
49
+ assert Resque::Scheduler.scheduled_jobs.include?('some_ivar_job')
50
50
  end
51
51
 
52
52
  test "can reload schedule" do
@@ -191,7 +191,7 @@ context "Resque::Scheduler" do
191
191
  assert_equal({'cron' => "* * * * *", 'class' => 'SomeIvarJob', 'args' => "/tmp/75"},
192
192
  Resque.decode(Resque.redis.hget(:schedules, "my_ivar_job")))
193
193
  end
194
-
194
+
195
195
  test "schedule= removes schedules not present in the given schedule argument" do
196
196
  Resque::Scheduler.dynamic = true
197
197
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-scheduler
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-09-20 00:00:00.000000000 Z
12
+ date: 2013-10-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -98,7 +98,7 @@ dependencies:
98
98
  requirements:
99
99
  - - ! '>='
100
100
  - !ruby/object:Gem::Version
101
- version: 2.0.1
101
+ version: 3.0.0
102
102
  type: :runtime
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
@@ -106,7 +106,7 @@ dependencies:
106
106
  requirements:
107
107
  - - ! '>='
108
108
  - !ruby/object:Gem::Version
109
- version: 2.0.1
109
+ version: 3.0.0
110
110
  - !ruby/object:Gem::Dependency
111
111
  name: resque
112
112
  requirement: !ruby/object:Gem::Requirement
@@ -134,17 +134,17 @@ dependencies:
134
134
  requirement: !ruby/object:Gem::Requirement
135
135
  none: false
136
136
  requirements:
137
- - - ! '>='
137
+ - - ~>
138
138
  - !ruby/object:Gem::Version
139
- version: '0'
139
+ version: '2.0'
140
140
  type: :runtime
141
141
  prerelease: false
142
142
  version_requirements: !ruby/object:Gem::Requirement
143
143
  none: false
144
144
  requirements:
145
- - - ! '>='
145
+ - - ~>
146
146
  - !ruby/object:Gem::Version
147
- version: '0'
147
+ version: '2.0'
148
148
  description: ! "Light weight job scheduling on top of Resque.\n Adds methods enqueue_at/enqueue_in
149
149
  to schedule jobs in the future.\n Also supports queueing jobs on a fixed, cron-like
150
150
  schedule."
@@ -176,6 +176,7 @@ files:
176
176
  - lib/resque_scheduler/server.rb
177
177
  - lib/resque_scheduler/server/views/delayed.erb
178
178
  - lib/resque_scheduler/server/views/delayed_timestamp.erb
179
+ - lib/resque_scheduler/server/views/requeue-params.erb
179
180
  - lib/resque_scheduler/server/views/scheduler.erb
180
181
  - lib/resque_scheduler/tasks.rb
181
182
  - lib/resque_scheduler/version.rb
@@ -204,18 +205,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
204
205
  - - ! '>='
205
206
  - !ruby/object:Gem::Version
206
207
  version: '0'
207
- segments:
208
- - 0
209
- hash: 2224918234097590124
210
208
  required_rubygems_version: !ruby/object:Gem::Requirement
211
209
  none: false
212
210
  requirements:
213
211
  - - ! '>='
214
212
  - !ruby/object:Gem::Version
215
213
  version: '0'
216
- segments:
217
- - 0
218
- hash: 2224918234097590124
219
214
  requirements: []
220
215
  rubyforge_project:
221
216
  rubygems_version: 1.8.23