resque-status 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  <%= status_view :status_styles, :layout => false %>
2
2
 
3
3
  <h1 class='wi'>Statuses: <%= @status.uuid %>/<%= @status.name %></h1>
4
- <p class='intro'>Viewing a specific job created with JobWithStatus. <a href="<%= u(:statuses) %>">Return to the list of statuses</a></p>
4
+ <p class='intro'>Viewing a specific job created with Resque::Plugins::Status. <a href="<%= u(:statuses) %>">Return to the list of statuses</a></p>
5
5
 
6
6
  <div class="status-holder" rel="<%= @status.status %>" id="status_<%= @status.uuid %>">
7
7
  <div class="status-progress">
@@ -29,7 +29,7 @@
29
29
  var pct = json.pct_complete + "%";
30
30
  }
31
31
  $status.find('.status-progress-bar').animate({width: pct});
32
- $status.find('.status-progress p').text(pct)
32
+ $status.find('.status-progress p').text(pct)
33
33
  if (json.message) {
34
34
  $status.find('.status-message').html(json.message)
35
35
  }
@@ -2,11 +2,14 @@
2
2
 
3
3
  <h1 class='wi'>Statuses</h1>
4
4
  <%unless @statuses.empty?%>
5
- <form method="POST" action="<%= u(:statuses) %>/clear" class='clear-failed'>
6
- <input type='submit' name='' value='Clear Statuses' />
5
+ <form method="POST" action="<%= u(:statuses) %>/clear" class='clear-failed'>
6
+ <input type='submit' name='' value='Clear Statuses' onclick='return confirm("Are you absolutely sure? This cannot be undone.");' />
7
+ </form>
8
+ <form method="POST" action="<%= u(:statuses) %>/clear/completed" class='clear-failed'>
9
+ <input type='submit' name='' value='Clear Completed Statuses' onclick='return confirm("Are you absolutely sure? This cannot be undone.");' />
7
10
  </form>
8
11
  <%end%>
9
- <p class='intro'>These are recent jobs created with the JobWithStatus class</p>
12
+ <p class='intro'>These are recent jobs created with the Resque::Plugins::Status class</p>
10
13
  <table>
11
14
  <tr>
12
15
  <th>ID</th>
@@ -32,7 +35,7 @@
32
35
  <td><% if status.killable? %><a href="<%= u(:statuses) %>/<%= status.uuid %>/kill" class="kill">Kill</a><% end %></td>
33
36
  </tr>
34
37
  <% end %>
35
- <% else %>
38
+ <% else %>
36
39
  <tr>
37
40
  <td colspan="7" class='no-data'>No Statuses right now...</td>
38
41
  </tr>
@@ -40,14 +43,14 @@
40
43
  </table>
41
44
 
42
45
  <% unless @statuses.empty? %>
43
- <%= partial :next_more, :start => @start, :size => @size %>
46
+ <%= partial :next_more, :start => @start, :size => @size %>
44
47
  <% end %>
45
48
 
46
49
  <%= status_poll(@start) %>
47
50
 
48
51
  <script type="text/javascript" charset="utf-8">
49
52
  jQuery(function($) {
50
-
53
+
51
54
  $('a.kill').click(function(e) {
52
55
  e.preventDefault();
53
56
  var $link = $(this),
@@ -66,6 +69,6 @@
66
69
  return false
67
70
  }
68
71
  });
69
-
72
+
70
73
  });
71
74
  </script>
data/lib/resque/status.rb CHANGED
@@ -2,240 +2,5 @@ require 'resque'
2
2
  require 'redisk'
3
3
  require 'uuid'
4
4
 
5
- module Resque
6
- # Resque::Status is a Hash object that has helper methods for dealing with
7
- # the common status attributes. It also has a number of class methods for
8
- # creating/updating/retrieving status objects from Redis
9
- class Status < Hash
10
- VERSION = '0.2.4'
11
-
12
- extend Resque::Helpers
13
-
14
- # Create a status, generating a new UUID, passing the message to the status
15
- # Returns the UUID of the new status.
16
- def self.create(*messages)
17
- uuid = generate_uuid
18
- set(uuid, *messages)
19
- redis.zadd(set_key, Time.now.to_i, uuid)
20
- redis.zremrangebyscore(set_key, 0, Time.now.to_i - @expire_in) if @expire_in
21
- uuid
22
- end
23
-
24
- # Get a status by UUID. Returns a Resque::Status
25
- def self.get(uuid)
26
- val = redis.get(status_key(uuid))
27
- val ? Resque::Status.new(uuid, decode(val)) : nil
28
- end
29
-
30
- # set a status by UUID. <tt>messages</tt> can be any number of stirngs or hashes
31
- # that are merged in order to create a single status.
32
- def self.set(uuid, *messages)
33
- val = Resque::Status.new(uuid, *messages)
34
- redis.set(status_key(uuid), encode(val))
35
- if expire_in
36
- redis.expire(status_key(uuid), expire_in)
37
- end
38
- val
39
- end
40
-
41
- # clear statuses from redis passing an optional range. See `statuses` for info
42
- # about ranges
43
- def self.clear(range_start = nil, range_end = nil)
44
- status_ids(range_start, range_end).each do |id|
45
- redis.del(status_key(id))
46
- redis.zrem(set_key, id)
47
- end
48
- end
49
-
50
- # returns a Redisk::Logger scoped to the UUID. Any options passed are passed
51
- # to the logger initialization.
52
- #
53
- # Ensures that Redisk is logging to the same Redis connection as Resque.
54
- def self.logger(uuid, options = {})
55
- Redisk.redis = redis
56
- Redisk::Logger.new(logger_key(uuid), options)
57
- end
58
-
59
- def self.count
60
- redis.zcard(set_key)
61
- end
62
-
63
- # Return <tt>num</tt> Resque::Status objects in reverse chronological order.
64
- # By default returns the entire set.
65
- # @param [Numeric] range_start The optional starting range
66
- # @param [Numeric] range_end The optional ending range
67
- # @example retuning the last 20 statuses
68
- # Resque::Status.statuses(0, 20)
69
- def self.statuses(range_start = nil, range_end = nil)
70
- status_ids(range_start, range_end).collect do |id|
71
- get(id)
72
- end.compact
73
- end
74
-
75
- # Return the <tt>num</tt> most recent status/job UUIDs in reverse chronological order.
76
- def self.status_ids(range_start = nil, range_end = nil)
77
- unless range_end && range_start
78
- # Because we want a reverse chronological order, we need to get a range starting
79
- # by the higest negative number.
80
- redis.zrevrange(set_key, 0, -1) || []
81
- else
82
- # Because we want a reverse chronological order, we need to get a range starting
83
- # by the higest negative number. The ordering is transparent from the API user's
84
- # perspective so we need to convert the passed params
85
- (redis.zrevrange(set_key, (range_start.abs), ((range_end || 1).abs)) || [])
86
- end
87
- end
88
-
89
- # Kill the job at UUID on its next iteration this works by adding the UUID to a
90
- # kill list (a.k.a. a list of jobs to be killed. Each iteration the job checks
91
- # if it _should_ be killed by calling <tt>tick</tt> or <tt>at</tt>. If so, it raises
92
- # a <tt>Resque::JobWithStatus::Killed</tt> error and sets the status to 'killed'.
93
- def self.kill(uuid)
94
- redis.sadd(kill_key, uuid)
95
- end
96
-
97
- # Remove the job at UUID from the kill list
98
- def self.killed(uuid)
99
- redis.srem(kill_key, uuid)
100
- end
101
-
102
- # Return the UUIDs of the jobs on the kill list
103
- def self.kill_ids
104
- redis.smembers(kill_key)
105
- end
106
-
107
- # Check whether a job with UUID is on the kill list
108
- def self.should_kill?(uuid)
109
- redis.sismember(kill_key, uuid)
110
- end
111
-
112
- # The time in seconds that jobs and statuses should expire from Redis (after
113
- # the last time they are touched/updated)
114
- def self.expire_in
115
- @expire_in
116
- end
117
-
118
- # Set the <tt>expire_in</tt> time in seconds
119
- def self.expire_in=(seconds)
120
- @expire_in = seconds.nil? ? nil : seconds.to_i
121
- end
122
-
123
- def self.status_key(uuid)
124
- "status:#{uuid}"
125
- end
126
-
127
- def self.set_key
128
- "_statuses"
129
- end
130
-
131
- def self.kill_key
132
- "_kill"
133
- end
134
-
135
- def self.logger_key(uuid)
136
- "_log:#{uuid}"
137
- end
138
-
139
- def self.generate_uuid
140
- UUID.generate(:compact)
141
- end
142
-
143
- def self.hash_accessor(name, options = {})
144
- options[:default] ||= nil
145
- coerce = options[:coerce] ? ".#{options[:coerce]}" : ""
146
- module_eval <<-EOT
147
- def #{name}
148
- value = (self['#{name}'] ? self['#{name}']#{coerce} : #{options[:default].inspect})
149
- yield value if block_given?
150
- value
151
- end
152
-
153
- def #{name}=(value)
154
- self['#{name}'] = value
155
- end
156
-
157
- def #{name}?
158
- !!self['#{name}']
159
- end
160
- EOT
161
- end
162
-
163
- STATUSES = %w{queued working completed failed killed}.freeze
164
-
165
- hash_accessor :uuid
166
- hash_accessor :name
167
- hash_accessor :status
168
- hash_accessor :message
169
- hash_accessor :time
170
- hash_accessor :options
171
-
172
- hash_accessor :num
173
- hash_accessor :total
174
-
175
- # Create a new Resque::Status object. If multiple arguments are passed
176
- # it is assumed the first argument is the UUID and the rest are status objects.
177
- # All arguments are subsequentily merged in order. Strings are assumed to
178
- # be messages.
179
- def initialize(*args)
180
- super nil
181
- base_status = {
182
- 'time' => Time.now.to_i,
183
- 'status' => 'queued'
184
- }
185
- base_status['uuid'] = args.shift if args.length > 1
186
- status_hash = args.inject(base_status) do |final, m|
187
- m = {'message' => m} if m.is_a?(String)
188
- final.merge(m || {})
189
- end
190
- self.replace(status_hash)
191
- end
192
-
193
- # calculate the % completion of the job based on <tt>status</tt>, <tt>num</tt>
194
- # and <tt>total</tt>
195
- def pct_complete
196
- case status
197
- when 'completed' then 100
198
- when 'queued' then 0
199
- else
200
- t = (total == 0 || total.nil?) ? 1 : total
201
- (((num || 0).to_f / t.to_f) * 100).to_i
202
- end
203
- end
204
-
205
- # Return the time of the status initialization. If set returns a <tt>Time</tt>
206
- # object, otherwise returns nil
207
- def time
208
- time? ? Time.at(self['time']) : nil
209
- end
210
-
211
- STATUSES.each do |status|
212
- define_method("#{status}?") do
213
- self['status'] === status
214
- end
215
- end
216
-
217
- # Can the job be killed? 'failed', 'completed', and 'killed' jobs cant be killed
218
- # (for pretty obvious reasons)
219
- def killable?
220
- !['failed', 'completed', 'killed'].include?(self.status)
221
- end
222
-
223
- unless method_defined?(:to_json)
224
- def to_json(*args)
225
- json
226
- end
227
- end
228
-
229
- # Return a JSON representation of the current object.
230
- def json
231
- h = self.dup
232
- h['pct_complete'] = pct_complete
233
- self.class.encode(h)
234
- end
235
-
236
- def inspect
237
- "#<Resque::Status #{super}>"
238
- end
239
-
240
- end
241
- end
5
+ require 'resque/plugins/status'
6
+ require 'resque/job_with_status'
@@ -1,53 +1,58 @@
1
- require 'resque/status'
1
+ require 'resque-status'
2
2
 
3
3
  module Resque
4
4
  module StatusServer
5
-
5
+
6
6
  VIEW_PATH = File.join(File.dirname(__FILE__), 'server', 'views')
7
-
7
+
8
8
  def self.registered(app)
9
-
9
+
10
10
  app.get '/statuses' do
11
11
  @start = params[:start].to_i
12
12
  @end = @start + (params[:per_page] || 50)
13
- @statuses = Resque::Status.statuses(@start, @end)
13
+ @statuses = Resque::Plugins::Status::Hash.statuses(@start, @end)
14
14
  @size = @statuses.size
15
15
  status_view(:statuses)
16
16
  end
17
-
17
+
18
18
  app.get '/statuses/:id.js' do
19
- @status = Resque::Status.get(params[:id])
19
+ @status = Resque::Plugins::Status::Hash.get(params[:id])
20
20
  content_type :js
21
21
  @status.json
22
22
  end
23
-
23
+
24
24
  app.get '/statuses/:id' do
25
- @status = Resque::Status.get(params[:id])
25
+ @status = Resque::Plugins::Status::Hash.get(params[:id])
26
26
  status_view(:status)
27
27
  end
28
-
28
+
29
29
  app.post '/statuses/:id/kill' do
30
- Resque::Status.kill(params[:id])
30
+ Resque::Plugins::Status::Hash.kill(params[:id])
31
31
  redirect u(:statuses)
32
32
  end
33
-
33
+
34
34
  app.post '/statuses/clear' do
35
- Resque::Status.clear
35
+ Resque::Plugins::Status::Hash.clear
36
36
  redirect u(:statuses)
37
37
  end
38
-
38
+
39
+ app.post '/statuses/clear/completed' do
40
+ Resque::Status.clear_completed
41
+ redirect u(:statuses)
42
+ end
43
+
39
44
  app.get "/statuses.poll" do
40
45
  content_type "text/plain"
41
46
  @polling = true
42
47
 
43
48
  @start = params[:start].to_i
44
49
  @end = @start + (params[:per_page] || 50)
45
- @statuses = Resque::Status.statuses(@start, @end)
50
+ @statuses = Resque::Plugins::Status::Hash.statuses(@start, @end)
46
51
  @size = @statuses.size
47
52
 
48
53
  status_view(:statuses, {:layout => false})
49
54
  end
50
-
55
+
51
56
  app.helpers do
52
57
  def status_view(filename, options = {}, locals = {})
53
58
  erb(File.read(File.join(::Resque::StatusServer::VIEW_PATH, "#{filename}.erb")), options, locals)
@@ -62,9 +67,9 @@ module Resque
62
67
  "<p class='poll'>#{text}</p>"
63
68
  end
64
69
  end
65
-
70
+
66
71
  app.tabs << "Statuses"
67
-
72
+
68
73
  end
69
74
 
70
75
  end
@@ -4,26 +4,31 @@
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{resque-status}
8
- s.version = "0.2.4"
7
+ s.name = "resque-status"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Aaron Quint"]
12
- s.date = %q{2011-08-10}
13
- s.description = %q{resque-status is an extension to the resque queue system that provides simple trackable jobs. It provides a Resque::Status class which can set/get the statuses of jobs and a Resque::JobWithStatus class that when subclassed provides easily trackable/killable jobs.}
14
- s.email = %q{aaron@quirkey.com}
12
+ s.date = "2012-01-22"
13
+ s.description = "resque-status is an extension to the resque queue system that provides simple trackable jobs. It provides a Resque::Plugins::Status::Hash class which can set/get the statuses of jobs and a Resque::Plugins::Status class that when included provides easily trackable/killable jobs."
14
+ s.email = "aaron@quirkey.com"
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE",
17
17
  "README.rdoc"
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
21
23
  "LICENSE",
22
24
  "README.rdoc",
23
25
  "Rakefile",
24
26
  "examples/sleep_job.rb",
25
27
  "init.rb",
28
+ "lib/resque-status.rb",
26
29
  "lib/resque/job_with_status.rb",
30
+ "lib/resque/plugins/status.rb",
31
+ "lib/resque/plugins/status/hash.rb",
27
32
  "lib/resque/server/views/status.erb",
28
33
  "lib/resque/server/views/status_styles.erb",
29
34
  "lib/resque/server/views/statuses.erb",
@@ -32,25 +37,37 @@ Gem::Specification.new do |s|
32
37
  "resque-status.gemspec",
33
38
  "test/redis-test.conf",
34
39
  "test/test_helper.rb",
35
- "test/test_resque-job_with_status.rb",
36
- "test/test_resque-status.rb"
40
+ "test/test_resque_plugins_status.rb",
41
+ "test/test_resque_plugins_status_hash.rb"
37
42
  ]
38
- s.homepage = %q{http://github.com/quirkey/resque-status}
43
+ s.homepage = "http://github.com/quirkey/resque-status"
39
44
  s.require_paths = ["lib"]
40
- s.rubyforge_project = %q{quirkey}
41
- s.rubygems_version = %q{1.6.2}
42
- s.summary = %q{resque-status is an extension to the resque queue system that provides simple trackable jobs.}
45
+ s.rubyforge_project = "quirkey"
46
+ s.rubygems_version = "1.8.10"
47
+ s.summary = "resque-status is an extension to the resque queue system that provides simple trackable jobs."
43
48
 
44
49
  if s.respond_to? :specification_version then
45
50
  s.specification_version = 3
46
51
 
47
52
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
53
+ s.add_runtime_dependency(%q<redisk>, [">= 0.2.1"])
54
+ s.add_runtime_dependency(%q<resque>, [">= 1.3.1"])
55
+ s.add_runtime_dependency(%q<uuid>, [">= 2.0.2"])
56
+ s.add_runtime_dependency(%q<mocha>, [">= 0.9.8"])
57
+ s.add_runtime_dependency(%q<shoulda>, [">= 2.10.2"])
58
+ s.add_runtime_dependency(%q<jeweler>, [">= 0"])
48
59
  s.add_runtime_dependency(%q<uuid>, [">= 2.0.2"])
49
60
  s.add_runtime_dependency(%q<resque>, [">= 1.3.1"])
50
61
  s.add_runtime_dependency(%q<redisk>, [">= 0.2.1"])
51
62
  s.add_development_dependency(%q<shoulda>, [">= 2.10.2"])
52
63
  s.add_development_dependency(%q<mocha>, [">= 0.9.8"])
53
64
  else
65
+ s.add_dependency(%q<redisk>, [">= 0.2.1"])
66
+ s.add_dependency(%q<resque>, [">= 1.3.1"])
67
+ s.add_dependency(%q<uuid>, [">= 2.0.2"])
68
+ s.add_dependency(%q<mocha>, [">= 0.9.8"])
69
+ s.add_dependency(%q<shoulda>, [">= 2.10.2"])
70
+ s.add_dependency(%q<jeweler>, [">= 0"])
54
71
  s.add_dependency(%q<uuid>, [">= 2.0.2"])
55
72
  s.add_dependency(%q<resque>, [">= 1.3.1"])
56
73
  s.add_dependency(%q<redisk>, [">= 0.2.1"])
@@ -58,6 +75,12 @@ Gem::Specification.new do |s|
58
75
  s.add_dependency(%q<mocha>, [">= 0.9.8"])
59
76
  end
60
77
  else
78
+ s.add_dependency(%q<redisk>, [">= 0.2.1"])
79
+ s.add_dependency(%q<resque>, [">= 1.3.1"])
80
+ s.add_dependency(%q<uuid>, [">= 2.0.2"])
81
+ s.add_dependency(%q<mocha>, [">= 0.9.8"])
82
+ s.add_dependency(%q<shoulda>, [">= 2.10.2"])
83
+ s.add_dependency(%q<jeweler>, [">= 0"])
61
84
  s.add_dependency(%q<uuid>, [">= 2.0.2"])
62
85
  s.add_dependency(%q<resque>, [">= 1.3.1"])
63
86
  s.add_dependency(%q<redisk>, [">= 0.2.1"])