resque-state 1.0.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.
- checksums.yaml +7 -0
- data/.travis.yml +4 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +102 -0
- data/LICENSE +21 -0
- data/README.rdoc +208 -0
- data/Rakefile +48 -0
- data/examples/sleep_job.rb +36 -0
- data/init.rb +1 -0
- data/lib/resque/job_with_state.rb +5 -0
- data/lib/resque/plugins/state/hash.rb +326 -0
- data/lib/resque/plugins/state.rb +289 -0
- data/lib/resque/server/views/state.erb +91 -0
- data/lib/resque/server/views/state_styles.erb +104 -0
- data/lib/resque/server/views/statuses.erb +79 -0
- data/lib/resque/state.rb +8 -0
- data/lib/resque/state_server.rb +85 -0
- data/lib/resque-state.rb +1 -0
- data/resque-state.gemspec +65 -0
- data/test/test_helper.rb +100 -0
- data/test/test_resque_plugins_state.rb +426 -0
- data/test/test_resque_plugins_state_hash.rb +255 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 31009a841c78f3ee6288076f7aaf5e5bb7e1f1b0
|
4
|
+
data.tar.gz: 7afc133998ecc29bb2cc87e35b3af102a1d9cc14
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 55a8fe604e4f315a3c994f5381fe35c824a0d8995c99f9c573afa7a882d4ff719df3a157e8d923f5b9a1210162264b590f0d67537e6771d4fedcf5ee355f02a2
|
7
|
+
data.tar.gz: d7d756b11b3dbd8cde9f6c3694a6f094c72a772412c75c400fce68ef658859a97e507c01701047f84682d6c23e93d245241a0ecfaa8a0210027d28241e01dbe9
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'resque', '~>1.19'
|
4
|
+
|
5
|
+
group :test do
|
6
|
+
gem 'mocha', '~>0.9'
|
7
|
+
gem 'minitest', '~> 5.5'
|
8
|
+
gem 'simplecov'
|
9
|
+
gem 'fakeredis', '~> 0.5.0'
|
10
|
+
gem "codeclimate-test-reporter", require: nil
|
11
|
+
end
|
12
|
+
|
13
|
+
group :development do
|
14
|
+
gem 'jeweler', '~> 2.1'
|
15
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.4.0)
|
5
|
+
builder (3.2.2)
|
6
|
+
codeclimate-test-reporter (0.6.0)
|
7
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
8
|
+
descendants_tracker (0.0.4)
|
9
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
10
|
+
docile (1.1.5)
|
11
|
+
fakeredis (0.5.0)
|
12
|
+
redis (~> 3.0)
|
13
|
+
faraday (0.9.2)
|
14
|
+
multipart-post (>= 1.2, < 3)
|
15
|
+
git (1.3.0)
|
16
|
+
github_api (0.14.5)
|
17
|
+
addressable (~> 2.4.0)
|
18
|
+
descendants_tracker (~> 0.0.4)
|
19
|
+
faraday (~> 0.8, < 0.10)
|
20
|
+
hashie (>= 3.4)
|
21
|
+
oauth2 (~> 1.0)
|
22
|
+
hashie (3.4.4)
|
23
|
+
highline (1.7.8)
|
24
|
+
jeweler (2.1.1)
|
25
|
+
builder
|
26
|
+
bundler (>= 1.0)
|
27
|
+
git (>= 1.2.5)
|
28
|
+
github_api
|
29
|
+
highline (>= 1.6.15)
|
30
|
+
nokogiri (>= 1.5.10)
|
31
|
+
rake
|
32
|
+
rdoc
|
33
|
+
semver
|
34
|
+
json (1.8.3)
|
35
|
+
json (1.8.3-java)
|
36
|
+
jwt (1.5.4)
|
37
|
+
metaclass (0.0.4)
|
38
|
+
mini_portile2 (2.1.0)
|
39
|
+
minitest (5.9.0)
|
40
|
+
mocha (0.14.0)
|
41
|
+
metaclass (~> 0.0.1)
|
42
|
+
mono_logger (1.1.0)
|
43
|
+
multi_json (1.12.1)
|
44
|
+
multi_xml (0.5.5)
|
45
|
+
multipart-post (2.0.0)
|
46
|
+
nokogiri (1.6.8)
|
47
|
+
mini_portile2 (~> 2.1.0)
|
48
|
+
pkg-config (~> 1.1.7)
|
49
|
+
nokogiri (1.6.8-java)
|
50
|
+
oauth2 (1.2.0)
|
51
|
+
faraday (>= 0.8, < 0.10)
|
52
|
+
jwt (~> 1.0)
|
53
|
+
multi_json (~> 1.3)
|
54
|
+
multi_xml (~> 0.5)
|
55
|
+
rack (>= 1.2, < 3)
|
56
|
+
pkg-config (1.1.7)
|
57
|
+
rack (1.6.4)
|
58
|
+
rack-protection (1.5.3)
|
59
|
+
rack
|
60
|
+
rake (11.2.2)
|
61
|
+
rdoc (4.2.2)
|
62
|
+
json (~> 1.4)
|
63
|
+
redis (3.3.1)
|
64
|
+
redis-namespace (1.5.2)
|
65
|
+
redis (~> 3.0, >= 3.0.4)
|
66
|
+
resque (1.26.0)
|
67
|
+
mono_logger (~> 1.0)
|
68
|
+
multi_json (~> 1.0)
|
69
|
+
redis-namespace (~> 1.3)
|
70
|
+
sinatra (>= 0.9.2)
|
71
|
+
vegas (~> 0.1.2)
|
72
|
+
semver (1.0.1)
|
73
|
+
simplecov (0.12.0)
|
74
|
+
docile (~> 1.1.0)
|
75
|
+
json (>= 1.8, < 3)
|
76
|
+
simplecov-html (~> 0.10.0)
|
77
|
+
simplecov-html (0.10.0)
|
78
|
+
sinatra (1.4.7)
|
79
|
+
rack (~> 1.5)
|
80
|
+
rack-protection (~> 1.4)
|
81
|
+
tilt (>= 1.3, < 3)
|
82
|
+
thread_safe (0.3.5)
|
83
|
+
thread_safe (0.3.5-java)
|
84
|
+
tilt (2.0.5)
|
85
|
+
vegas (0.1.11)
|
86
|
+
rack (>= 1.0.0)
|
87
|
+
|
88
|
+
PLATFORMS
|
89
|
+
java
|
90
|
+
ruby
|
91
|
+
|
92
|
+
DEPENDENCIES
|
93
|
+
codeclimate-test-reporter
|
94
|
+
fakeredis (~> 0.5.0)
|
95
|
+
jeweler (~> 2.1)
|
96
|
+
minitest (~> 5.5)
|
97
|
+
mocha (~> 0.9)
|
98
|
+
resque (~> 1.19)
|
99
|
+
simplecov
|
100
|
+
|
101
|
+
BUNDLED WITH
|
102
|
+
1.12.5
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2016 Nathan V
|
2
|
+
Copyright (c) 2009 Aaron Quint
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
= resque-state
|
2
|
+
|
3
|
+
{<img src="https://img.shields.io/badge/license-MIT-blue.svg" />}[https://github.com/nathan-v/resque-state/blob/master/LICENSE]
|
4
|
+
|
5
|
+
{<img src="https://codeclimate.com/github/nathan-v/resque-state/badges/gpa.svg" />}[https://codeclimate.com/github/nathan-v/resque-state]
|
6
|
+
{<img src="https://codeclimate.com/github/nathan-v/resque-state/badges/issue_count.svg" />}[https://codeclimate.com/github/nathan-v/resque-state]
|
7
|
+
{<img src="https://codeclimate.com/github/nathan-v/resque-state/badges/coverage.svg" />}[https://codeclimate.com/github/nathan-v/resque-state/coverage]
|
8
|
+
{<img src="https://travis-ci.org/nathan-v/resque-state.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/nathan-v/resque-state]
|
9
|
+
{<img src="https://gemnasium.com/badges/github.com/nathan-v/resque-state.svg" />}[https://gemnasium.com/github.com/nathan-v/resque-state]
|
10
|
+
{<img src="https://img.shields.io/github/issues/nathan-v/resque-state.svg" />}[https://github.com/nathan-v/resque-state/issues]
|
11
|
+
|
12
|
+
|
13
|
+
resque-state is an extension to the resque queue system that provides simple trackable jobs.
|
14
|
+
|
15
|
+
== About
|
16
|
+
|
17
|
+
resque-state provides a set of simple classes that extend resque's default
|
18
|
+
functionality (with 0% monkey patching) to give apps a way to track specific
|
19
|
+
job instances and their state. It achieves this by giving job instances UUID's
|
20
|
+
and allowing the job instances to report their state from within their iterations.
|
21
|
+
|
22
|
+
== Installation
|
23
|
+
|
24
|
+
Ruby 1.9.3 and up are supported.
|
25
|
+
|
26
|
+
resque-state <b>requires Redis >= 1.1</b> (though I recommend getting the latest stable version).
|
27
|
+
You can download Redis here: http://redis.io/ or install it
|
28
|
+
using homebrew (brew install redis).
|
29
|
+
|
30
|
+
Install the resque-state gem (which will pull in the dependencies).
|
31
|
+
|
32
|
+
gem install resque-state
|
33
|
+
|
34
|
+
With newer Rails add this to your Gemfile:
|
35
|
+
|
36
|
+
# Gemfile
|
37
|
+
gem 'resque-state'
|
38
|
+
|
39
|
+
Then in an initializer:
|
40
|
+
|
41
|
+
# config/initializers/resque.rb
|
42
|
+
Resque.redis = "your/redis/socket" # default localhost:6379
|
43
|
+
Resque::Plugins::State::Hash.expire_in = (24 * 60 * 60) # 24hrs in seconds
|
44
|
+
|
45
|
+
== Usage
|
46
|
+
|
47
|
+
The most direct way to use resque-state is to create your jobs using the
|
48
|
+
Resque::Plugins::State module. An example job would look something like:
|
49
|
+
|
50
|
+
class SleepJob
|
51
|
+
include Resque::Plugins::State
|
52
|
+
|
53
|
+
def perform
|
54
|
+
total = (options['length'] || 1000).to_i
|
55
|
+
total.times do |i|
|
56
|
+
num = i+1
|
57
|
+
at(num, total, "At #{num} of #{total}")
|
58
|
+
sleep(1)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
One major difference is that instead of implementing <tt>perform</tt> as a
|
64
|
+
class method, we do our job implementation within instances of the job class.
|
65
|
+
|
66
|
+
In order to queue a SleepJob up, we also won't use <tt>Resque.enqueue</tt>, instead
|
67
|
+
we'll use the <tt>create</tt> class method which will wrap <tt>enqueue</tt> and
|
68
|
+
creating a unique id (UUID) for us to track the job with.
|
69
|
+
|
70
|
+
job_id = SleepJob.create(length: 100)
|
71
|
+
|
72
|
+
This will create a UUID enqueue the job and pass the :length option on the SleepJob
|
73
|
+
instance as options['length'] (as you can see above).
|
74
|
+
|
75
|
+
Now that we have a UUID its really easy to get the state:
|
76
|
+
|
77
|
+
state = Resque::Plugins::State::Hash.get(job_id)
|
78
|
+
|
79
|
+
This returns a Resque::Plugins::State::Hash object, which is a Hash (with benefits).
|
80
|
+
|
81
|
+
state.pct_complete #=> 0
|
82
|
+
state.status #=> 'queued'
|
83
|
+
state.queued? #=> true
|
84
|
+
state.working? #=> false
|
85
|
+
state.time #=> Time object
|
86
|
+
state.message #=> "Created at ..."
|
87
|
+
|
88
|
+
Once the worker reserves the job, the instance of SleepJob updates the state at
|
89
|
+
each iteration using <tt>at()</tt>
|
90
|
+
|
91
|
+
state = Resque::Plugins::State::Hash.get(job_id)
|
92
|
+
state.working? #=> true
|
93
|
+
state.num #=> 5
|
94
|
+
state.total #=> 100
|
95
|
+
state.pct_complete #=> 5
|
96
|
+
|
97
|
+
If an error occurs within the job instance, the state is set to 'failed' and then
|
98
|
+
the error is re-raised so that Resque can capture it.
|
99
|
+
|
100
|
+
Its also possible to get a list of current/recent job statuses:
|
101
|
+
|
102
|
+
Resque::Plugins::State::Hash.statuses #=> [#<Resque::Plugins::State::Hash>, ...]
|
103
|
+
|
104
|
+
=== Passing back data from the job
|
105
|
+
|
106
|
+
You may want to save data from inside the job to access it from outside the job.
|
107
|
+
|
108
|
+
A common use-case is web-triggered jobs that create files, later available for
|
109
|
+
download by the user.
|
110
|
+
|
111
|
+
A Status is actually just a hash, so inside a job you can do:
|
112
|
+
|
113
|
+
set_status(filename: "myfilename")
|
114
|
+
|
115
|
+
Also, all the status setting methods take any number of hash arguments. So you could do:
|
116
|
+
|
117
|
+
completed('filename' => '/myfilename')
|
118
|
+
|
119
|
+
=== Kill! Kill! Kill!
|
120
|
+
|
121
|
+
Because we're tracking UUIDs per instance, and we're checking in/updating the status
|
122
|
+
on each iteration (using <tt>at</tt> or <tt>tick</tt>) we can kill specific jobs
|
123
|
+
by UUID.
|
124
|
+
|
125
|
+
Resque::Plugins::State::Hash.kill(job_id)
|
126
|
+
|
127
|
+
The next time the job at job_id calls <tt>at</tt> or <tt>tick</tt>, it will raise a <tt>Killed</tt>
|
128
|
+
error and set the status to killed.
|
129
|
+
|
130
|
+
=== Hold up: Pausing
|
131
|
+
|
132
|
+
Since we perhaps might want to just have a job sit and wait a bit rather than have it die
|
133
|
+
completely there's pause. This tells the job to sleep for 10 seconds before checking in
|
134
|
+
again.
|
135
|
+
|
136
|
+
Resque::Plugins::State::Hash.pause(job_id)
|
137
|
+
|
138
|
+
The next time the job at job_id calls <tt>at</tt> or <tt>tick</tt>, it will start a while
|
139
|
+
loop with a 10 second sleep until Resque::Plugins::State::Hash.unpause is called.
|
140
|
+
|
141
|
+
=== Percent Complete and setting the message
|
142
|
+
|
143
|
+
Use <tt>at</tt> or <tt>tick</tt> to show progress in your job's <tt>perform</tt> function
|
144
|
+
(which is displayed on the resque-web state tab). This will also be where <tt>Killed</tt>
|
145
|
+
is raised if the job is killed.
|
146
|
+
|
147
|
+
at(steps_completed, total_steps, "${steps_completed} of #{total_steps} steps completed!")
|
148
|
+
|
149
|
+
=== Expiration
|
150
|
+
|
151
|
+
Since Redis is RAM based, we probably don't want to keep these statuses around forever
|
152
|
+
(at least until @antirez releases the VM feature). By setting expire_in, all statuses
|
153
|
+
and their related keys will expire in expire_in seconds from the last time theyre updated:
|
154
|
+
|
155
|
+
Resque::Plugins::State::Hash.expire_in = (60 * 60) # 1 hour
|
156
|
+
|
157
|
+
=== Testing
|
158
|
+
|
159
|
+
Recent versions of Resque introduced <tt>Resque.inline</tt> which changes the behavior to
|
160
|
+
instead of enqueueing and performing jobs to just executing them inline. In Resque
|
161
|
+
itself this removes the dependency on a Redis, however, <tt>Resque::State</tt> uses Redis
|
162
|
+
to store information about jobs, so though <tt>inline</tt> "works", you will still need
|
163
|
+
to use or mock a redis connection. You should be able to use a library like
|
164
|
+
https://github.com/causes/mock_redis alongside <tt>inline</tt> if you really want to
|
165
|
+
avoid Redis connections in your test.
|
166
|
+
|
167
|
+
=== resque-web
|
168
|
+
|
169
|
+
Though the main purpose of these trackable jobs is to allow you to surface the status
|
170
|
+
of user created jobs through your apps' own UI, I've added a simple example UI
|
171
|
+
as a plugin to resque-web. This adds a State tab to resque-web
|
172
|
+
|
173
|
+
To use, you need to setup a resque-web config file:
|
174
|
+
|
175
|
+
# ~/resque_conf.rb
|
176
|
+
require 'resque/state_server'
|
177
|
+
|
178
|
+
Then start resque-web with your config:
|
179
|
+
|
180
|
+
resque-web ~/resque_conf.rb
|
181
|
+
|
182
|
+
If you're using Rails you can just require 'resque/state_server' in your resque
|
183
|
+
initializer or application.rb.
|
184
|
+
|
185
|
+
|
186
|
+
== More
|
187
|
+
|
188
|
+
* Source: http://github.com/nathan-v/resque-state
|
189
|
+
* API Docs: http://rdoc.info/projects/nathan-v/resque-state
|
190
|
+
* Examples: http://github.com/nathan-v/resque-state/tree/master/examples
|
191
|
+
* Resque: https://github.com/resque/resque
|
192
|
+
|
193
|
+
== Thanks
|
194
|
+
|
195
|
+
Resque is awesome, @defunkt needs a shout-out.
|
196
|
+
|
197
|
+
== Note on Patches/Pull Requests
|
198
|
+
|
199
|
+
PRs are welcome. Please always include relevant tests and ensure your changes follow
|
200
|
+
Ruby style conventions as much as possible.
|
201
|
+
|
202
|
+
== Copyright
|
203
|
+
|
204
|
+
Copyright (c) 2016 Nathan V.
|
205
|
+
|
206
|
+
Copyright (c) 2010 Aaron Quint.
|
207
|
+
|
208
|
+
MIT licensed. See LICENSE file for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
$LOAD_PATH.unshift './lib'
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'resque-state'
|
5
|
+
require 'resque/tasks'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |gem|
|
10
|
+
gem.name = 'resque-state'
|
11
|
+
gem.version = Resque::Plugins::State::VERSION.dup
|
12
|
+
gem.summary = %(resque-state is an extension to the resque queue system
|
13
|
+
that provides simple trackable jobs.).gsub("\n", ' ').squeeze(' ')
|
14
|
+
gem.description = %(resque-state is an extension to the resque queue
|
15
|
+
system that provides simple trackable jobs. It provides a
|
16
|
+
Resque::Plugins::State::Hash class which can set/get the statuses of jobs
|
17
|
+
and a Resque::Plugins::State class that, when included, provides easily
|
18
|
+
trackable/killable/pausable jobs.).gsub("\n", ' ').squeeze(' ')
|
19
|
+
gem.email = 'nathan.v@gmail.com'
|
20
|
+
gem.homepage = 'http://github.com/nathan-v/resque-state'
|
21
|
+
gem.rubyforge_project = 'nathan-v'
|
22
|
+
gem.authors = ['Aaron Quint', 'Nathan V']
|
23
|
+
gem.licenses = 'MIT'
|
24
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20
|
25
|
+
# for additional settings
|
26
|
+
end
|
27
|
+
Jeweler::RubygemsDotOrgTasks.new
|
28
|
+
rescue LoadError
|
29
|
+
puts 'Jeweler (or a dependency) not available. Install it with: gem install'\
|
30
|
+
' jeweler'.gsub("\n", ' ').squeeze(' ')
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'rake/testtask'
|
34
|
+
Rake::TestTask.new(:test) do |test|
|
35
|
+
test.libs << 'lib' << 'test'
|
36
|
+
test.pattern = 'test/**/test_*.rb'
|
37
|
+
test.verbose = true
|
38
|
+
end
|
39
|
+
|
40
|
+
desc 'Generates a coverage report'
|
41
|
+
task :coverage do
|
42
|
+
ENV['COVERAGE'] = 'true'
|
43
|
+
Rake::Task['test'].execute
|
44
|
+
end
|
45
|
+
|
46
|
+
task :test
|
47
|
+
|
48
|
+
task default: :coverage
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'resque/job_with_state' # in rails you would probably do this in an initializer
|
2
|
+
|
3
|
+
# sleeps for _length_ seconds updating the status every second
|
4
|
+
|
5
|
+
class SleepJob
|
6
|
+
include Resque::Plugins::State
|
7
|
+
|
8
|
+
def perform
|
9
|
+
total = options.has_key?('length') ? options['length'].to_i : 1000
|
10
|
+
num = 0
|
11
|
+
while num < total
|
12
|
+
at(num, total, "At #{num} of #{total}")
|
13
|
+
sleep(1)
|
14
|
+
num += 1
|
15
|
+
end
|
16
|
+
completed
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
if __FILE__ == $0
|
23
|
+
# Make sure you have a worker running
|
24
|
+
# rake -rexamples/sleep_job.rb resque:work QUEUE=statused
|
25
|
+
|
26
|
+
# running the job
|
27
|
+
puts "Creating the SleepJob"
|
28
|
+
job_id = SleepJob.create :length => 100
|
29
|
+
puts "Got back #{job_id}"
|
30
|
+
|
31
|
+
# check the status until its complete
|
32
|
+
while status = Resque::Plugins::State::Hash.get(job_id) and !status.completed? && !status.failed?
|
33
|
+
sleep 1
|
34
|
+
puts status.inspect
|
35
|
+
end
|
36
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'resque-state'
|