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.
- data/Gemfile +8 -0
- data/Gemfile.lock +51 -0
- data/README.rdoc +30 -18
- data/Rakefile +6 -4
- data/examples/sleep_job.rb +9 -8
- data/init.rb +1 -1
- data/lib/resque-status.rb +1 -0
- data/lib/resque/job_with_status.rb +1 -198
- data/lib/resque/plugins/status.rb +213 -0
- data/lib/resque/plugins/status/hash.rb +242 -0
- data/lib/resque/server/views/status.erb +2 -2
- data/lib/resque/server/views/statuses.erb +10 -7
- data/lib/resque/status.rb +2 -237
- data/lib/resque/status_server.rb +23 -18
- data/resque-status.gemspec +34 -11
- data/test/test_helper.rb +16 -7
- data/test/{test_resque-job_with_status.rb → test_resque_plugins_status.rb} +94 -10
- data/test/test_resque_plugins_status_hash.rb +153 -0
- metadata +89 -20
- data/test/test_resque-status.rb +0 -153
@@ -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
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
6
|
-
|
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'
|
data/lib/resque/status_server.rb
CHANGED
@@ -1,53 +1,58 @@
|
|
1
|
-
require 'resque
|
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
|
data/resque-status.gemspec
CHANGED
@@ -4,26 +4,31 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.
|
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 =
|
13
|
-
s.description =
|
14
|
-
s.email =
|
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/
|
36
|
-
"test/
|
40
|
+
"test/test_resque_plugins_status.rb",
|
41
|
+
"test/test_resque_plugins_status_hash.rb"
|
37
42
|
]
|
38
|
-
s.homepage =
|
43
|
+
s.homepage = "http://github.com/quirkey/resque-status"
|
39
44
|
s.require_paths = ["lib"]
|
40
|
-
s.rubyforge_project =
|
41
|
-
s.rubygems_version =
|
42
|
-
s.summary =
|
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"])
|