resque-status 0.2.4 → 0.3.0

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.
@@ -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"])