kthxbye 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.document +5 -0
  2. data/.gitignore +33 -0
  3. data/DESIGN.textile +81 -0
  4. data/Gemfile +21 -0
  5. data/Gemfile.lock +42 -0
  6. data/LICENSE +20 -0
  7. data/README.textile +91 -0
  8. data/Rakefile +53 -0
  9. data/VERSION +1 -0
  10. data/config.ru +7 -0
  11. data/lib/kthxbye.rb +151 -0
  12. data/lib/kthxbye/config.rb +35 -0
  13. data/lib/kthxbye/exceptions.rb +4 -0
  14. data/lib/kthxbye/failure.rb +62 -0
  15. data/lib/kthxbye/helper.rb +42 -0
  16. data/lib/kthxbye/job.rb +127 -0
  17. data/lib/kthxbye/version.rb +5 -0
  18. data/lib/kthxbye/web_interface.rb +117 -0
  19. data/lib/kthxbye/web_interface/public/application.js +16 -0
  20. data/lib/kthxbye/web_interface/public/awesome-buttons.css +108 -0
  21. data/lib/kthxbye/web_interface/public/jquery.js +154 -0
  22. data/lib/kthxbye/web_interface/public/style.css +128 -0
  23. data/lib/kthxbye/web_interface/views/error.haml +5 -0
  24. data/lib/kthxbye/web_interface/views/failed.haml +26 -0
  25. data/lib/kthxbye/web_interface/views/hash.haml +6 -0
  26. data/lib/kthxbye/web_interface/views/layout.haml +33 -0
  27. data/lib/kthxbye/web_interface/views/overview.haml +2 -0
  28. data/lib/kthxbye/web_interface/views/queues.haml +31 -0
  29. data/lib/kthxbye/web_interface/views/set.haml +4 -0
  30. data/lib/kthxbye/web_interface/views/stats.haml +32 -0
  31. data/lib/kthxbye/web_interface/views/view_backtrace.haml +8 -0
  32. data/lib/kthxbye/web_interface/views/workers.haml +24 -0
  33. data/lib/kthxbye/web_interface/views/working.haml +19 -0
  34. data/lib/kthxbye/worker.rb +221 -0
  35. data/test/helper.rb +18 -0
  36. data/test/redis-test.conf +115 -0
  37. data/test/test_failure.rb +51 -0
  38. data/test/test_helper.rb +86 -0
  39. data/test/test_kthxbye.rb +213 -0
  40. data/test/test_worker.rb +148 -0
  41. metadata +364 -0
@@ -0,0 +1,213 @@
1
+ require 'test_helper'
2
+
3
+ class TestKthxbye < Test::Unit::TestCase
4
+ context "See Kthxbye Configuration" do
5
+
6
+ should "configure an app with given params" do
7
+ k = Kthxbye::Config.setup(:redis_server => "localhost", :redis_port => 8080, :verbose => true)
8
+
9
+ assert_equal 'localhost', Kthxbye::Config.options[:redis_server]
10
+ assert_equal 8080, Kthxbye::Config.options[:redis_port]
11
+ assert_equal true, Kthxbye::Config.options[:verbose]
12
+ end
13
+
14
+ should "configure an app with default params" do
15
+ Kthxbye::Config.setup
16
+
17
+ assert_equal '127.0.0.1', Kthxbye::Config.options[:redis_server]
18
+ assert_equal 9876, Kthxbye::Config.options[:redis_port]
19
+ assert_equal false, Kthxbye::Config.options[:verbose]
20
+ end
21
+
22
+ end
23
+
24
+ context "See Kthxbye" do
25
+ setup do
26
+ Kthxbye::Config.setup
27
+ Kthxbye.redis.flushall
28
+ end
29
+
30
+ should "register unregister and delete queues" do
31
+ assert_equal 0, Kthxbye.queues.size
32
+
33
+ assert Kthxbye.register_queue "dogs"
34
+ assert Kthxbye.register_queue "cats"
35
+
36
+ assert_equal 2, Kthxbye.queues.size
37
+ assert_equal ["cats", "dogs"], Kthxbye.queues
38
+
39
+ assert Kthxbye.unregister_queue("dogs")
40
+ assert_equal ["cats"], Kthxbye.queues
41
+
42
+ assert Kthxbye.delete_queue("cats")
43
+ assert_equal [], Kthxbye.queues
44
+ end
45
+
46
+ should "store a couple of jobs and return good ids" do
47
+ assert id = Kthxbye.enqueue("test", SimpleJob, {:hello => "world"}, "test params")
48
+ assert id2 = Kthxbye.enqueue("test", GoodJob, "I am a sentence to print")
49
+ assert_equal 1, id
50
+ assert_equal 2, id2
51
+ end
52
+
53
+ should "enqueue a job and show a queue size of one" do
54
+ assert_equal 0, Kthxbye.size("test")
55
+ Kthxbye.enqueue("test", SimpleJob, {:hello => "world"}, "test params")
56
+ assert_equal 1, Kthxbye.size("test")
57
+ end
58
+
59
+ should "peek at the datastore for a specific job" do
60
+ id = Kthxbye.enqueue("test", SimpleJob, {:hello => "world"}, "test params")
61
+ assert_equal [{'hello' => "world"}, "test params"], Kthxbye.data_peek("test", id)['payload']
62
+ end
63
+
64
+ should "peek at all the data stores" do
65
+ Kthxbye.enqueue("test", SimpleJob, "first", "job")
66
+ Kthxbye.enqueue("test", SimpleJob, "second", "job")
67
+
68
+ assert_equal ({'1' => {'klass' => 'SimpleJob', 'payload' => ["first", "job"]},
69
+ '2' => {'klass' => 'SimpleJob', 'payload' => ["second", "job"]}}), Kthxbye.data_peek("test")
70
+ end
71
+
72
+ should "peek at the resultstore for a specific job" do
73
+ id = Kthxbye.enqueue("test", GoodJob, "Jim")
74
+ worker = Kthxbye::Worker.new("test", 0)
75
+ worker.run
76
+
77
+ assert_equal "Good job, Jim", Kthxbye.result_peek("test", id)
78
+ end
79
+
80
+ should "peek at all the result stores" do
81
+ Kthxbye.enqueue("test", GoodJob, "Jim")
82
+ Kthxbye.enqueue("test", GoodJob, "Dwight")
83
+ worker = Kthxbye::Worker.new("test", 0)
84
+ worker.run
85
+
86
+ assert_equal ({'1' => "Good job, Jim", '2' => "Good job, Dwight"}), Kthxbye.result_peek("test")
87
+ end
88
+
89
+ should "show its queues correctly" do
90
+ Kthxbye.enqueue("these-jobs", SimpleJob, {:hello => "world"}, "test params")
91
+ Kthxbye.enqueue("those-jobs", SimpleJob, {:hello => "world"}, "test params")
92
+
93
+ assert_equal ["these-jobs", "those-jobs"], Kthxbye.queues
94
+
95
+ Kthxbye.delete_queue("these-jobs")
96
+ assert_equal ["those-jobs"], Kthxbye.queues
97
+ end
98
+
99
+ should "grab a job off the queue" do
100
+ Kthxbye.enqueue("these-jobs", SimpleJob, {:hello => "world"}, "test params")
101
+
102
+ assert job = Kthxbye.salvage("these-jobs")
103
+ assert_equal Kthxbye::Job, job.class
104
+ assert_equal 1, job.id
105
+ assert_equal "these-jobs", job.queue
106
+ assert_equal "SimpleJob", job.data['klass']
107
+ end
108
+
109
+ should "return a job" do
110
+ id = Kthxbye.enqueue( "test", SimpleJob, 1, 2 )
111
+ assert job = Kthxbye::Job.find( id, "test" )
112
+ assert_equal Kthxbye::Job, job.class
113
+ assert_equal [1, 2], job.data['payload']
114
+ end
115
+
116
+ should "perform a job" do
117
+ id = Kthxbye.enqueue( "test", GoodJob, "Lukas" )
118
+ job = Kthxbye::Job.find(id, "test")
119
+
120
+ assert result = job.perform
121
+ assert "Good job, Lukas!", result
122
+ end
123
+
124
+ should "retry a job if attempts setting is > 1" do
125
+ Kthxbye::Config.options[:attempts] = 2
126
+ id = Kthxbye.enqueue( "test", BadJob )
127
+ job = Kthxbye::Job.find(id, "test")
128
+
129
+ job.perform
130
+ assert_equal 2, job.instance_variable_get(:@failed_attempts)
131
+
132
+ end
133
+
134
+ should "delete a job" do
135
+ id = Kthxbye.enqueue("test", SimpleJob, 1, 2 )
136
+ assert_equal :active, Kthxbye::Job.destroy(id, "test")
137
+ assert_equal nil, Kthxbye::Job.find(id, "test")
138
+ end
139
+
140
+ should "return all keys" do
141
+ Kthxbye.enqueue("test", SimpleJob, 1, 2 )
142
+ Kthxbye.register_queue("money")
143
+
144
+ assert_equal ["data-store:test", "queue:test", "queues", "uniq_id"], Kthxbye.keys.sort
145
+ end
146
+
147
+ should "register and unregister workers" do
148
+ assert_equal [], Kthxbye.workers
149
+
150
+ worker = Kthxbye::Worker.new("bill")
151
+ worker.register_worker
152
+
153
+ assert_equal [worker], Kthxbye.workers
154
+
155
+ worker2 = Kthxbye::Worker.new("holiday")
156
+ worker2.register_worker
157
+
158
+ assert_equal [worker, worker2], Kthxbye.workers.sort
159
+
160
+ worker2.unregister_worker
161
+ assert_equal [worker], Kthxbye.workers
162
+
163
+ worker.unregister_worker
164
+ assert_equal [], Kthxbye.workers
165
+ end
166
+
167
+ should "show all active workers" do
168
+ worker = Kthxbye::Worker.new( "test" )
169
+ id = Kthxbye.enqueue( "test", SimpleJob, 1, 2 )
170
+ worker.register_worker
171
+ worker.working( Kthxbye::Job.find( id, "test" ) )
172
+
173
+ assert_equal [[worker, {'job_id' => id, 'started' => Time.now.to_s }]], Kthxbye.working
174
+ end
175
+
176
+ should "return the correct status of a job" do
177
+ worker1 = Kthxbye::Worker.new( "good", 0 )
178
+ worker2 = Kthxbye::Worker.new( "bad", 0 )
179
+
180
+ good_job = Kthxbye.enqueue( "good", GoodJob, "Dave" )
181
+ bad_job = Kthxbye.enqueue( "bad", BadJob )
182
+ job = Kthxbye::Job.find(good_job, "good")
183
+
184
+ assert_equal :active, job.status
185
+ job.dequeue
186
+ assert_equal :inactive, job.status
187
+ job.rerun
188
+
189
+ worker1.run
190
+ assert_equal :succeeded, job.status
191
+
192
+ job = Kthxbye::Job.find(bad_job, "bad")
193
+ worker2.run
194
+
195
+ assert_equal :failed, job.status
196
+ end
197
+
198
+ should "display nice inspect" do
199
+ i = Kthxbye.inspect
200
+
201
+ assert_equal Kthxbye::Version, i[:version]
202
+ assert_equal 0, i[:keys]
203
+ assert_equal 0, i[:workers]
204
+ assert_equal 0, i[:queues]
205
+ assert_equal 0, i[:working]
206
+ assert_equal 0, i[:pending]
207
+ end
208
+
209
+
210
+
211
+ end
212
+ end
213
+
@@ -0,0 +1,148 @@
1
+
2
+ class TestWorker < Test::Unit::TestCase
3
+ context "See Kthxbye::Worker" do
4
+ setup do
5
+ Kthxbye::Config.setup(:verbose => false, :redis_port => 9876)
6
+ Kthxbye.redis.flushall
7
+
8
+ @id = Kthxbye.enqueue( "test", SimpleJob, 1, 2 )
9
+ @worker = Kthxbye::Worker.new("test", 0)
10
+ end
11
+
12
+ should "register and show a worker" do
13
+ @worker.run do
14
+ assert_equal [@worker], Kthxbye.workers
15
+ end
16
+ end
17
+
18
+ should "find a registered worker and its queues" do
19
+ @worker.run do
20
+ found = Kthxbye::Worker.find(@worker.id)
21
+ assert_equal @worker, found
22
+ end
23
+ end
24
+
25
+ should "startup a worker and register it as working" do
26
+ @worker.run do
27
+ assert_equal [[@worker.to_s, {'job_id' => @id, 'started' => Time.now.to_s}]], Kthxbye.working
28
+ end
29
+ end
30
+
31
+ should "pull jobs from given queue and run them" do
32
+ @worker.run
33
+
34
+ assert_equal 0, Kthxbye.size("test")
35
+ end
36
+
37
+ should "store a job's results and retrieve them" do
38
+ id = Kthxbye.enqueue("new", GoodJob, "Lukas")
39
+ worker = Kthxbye::Worker.new("new", 0)
40
+ worker.run
41
+
42
+ assert_equal 1, Kthxbye.job_results("new").size
43
+ assert_equal "Good job, Lukas", Kthxbye.job_results("new", id)
44
+ end
45
+
46
+ should "capture and report failed jobs" do
47
+ id = Kthxbye.enqueue( "bad", BadJob )
48
+ id2 = Kthxbye.enqueue( "bad", BadJobWithSyntaxError )
49
+
50
+ assert_equal 0, Kthxbye::Failure.count
51
+
52
+ worker = Kthxbye::Worker.new("bad", 0 )
53
+ worker.run
54
+
55
+ assert_equal 2, Kthxbye::Failure.count
56
+ assert_equal "Bad job!", Kthxbye::Failure.find(id)["error"]
57
+ assert_equal "Extra Bad job!", Kthxbye::Failure.find(id2)["error"]
58
+
59
+ assert_equal ["Bad job!", "Extra Bad job!"], Kthxbye::Failure.all.map{|x| x['error']}
60
+
61
+ assert_equal 1, Kthxbye::Failure.count_type(RuntimeError)
62
+ end
63
+
64
+ should "report queues available to workers when added via csv (alphabetical)" do
65
+ worker = Kthxbye::Worker.new("good,bad,ugly")
66
+ assert_equal ["good", "bad", "ugly"].sort, worker.queues
67
+ end
68
+
69
+ should "report queues available to workers when using * (alphabetical)" do
70
+ Kthxbye.register_queue("hello")
71
+ Kthxbye.register_queue("world")
72
+
73
+ worker = Kthxbye::Worker.new("*")
74
+ assert_equal ["hello", "test", "world"].sort, worker.queues
75
+ end
76
+
77
+ should "report queues available to worker when passed an array (alphabetical)" do
78
+ worker = Kthxbye::Worker.new(["one", "two", "three"])
79
+ assert_equal ["one", "two", "three"].sort, worker.queues
80
+ end
81
+
82
+ should "work jobs from multiple queues" do
83
+ Kthxbye.enqueue("other", GoodJob, "Boieee")
84
+ Kthxbye.enqueue("more", GoodJob, "Lukas")
85
+
86
+ worker = Kthxbye::Worker.new("*", 0)
87
+
88
+ worker.run
89
+
90
+ assert_equal 0, Kthxbye.size("test")
91
+ assert_equal 0, Kthxbye.size("other")
92
+ assert_equal 0, Kthxbye.size("more")
93
+ end
94
+
95
+ should "return job active once working it" do
96
+ job = Kthxbye::Job.find(@id, "test")
97
+
98
+ @worker.working(job)
99
+
100
+ assert @worker.working?
101
+ assert_equal @worker.current_job, job
102
+
103
+ assert job.active?
104
+ end
105
+
106
+ should "know when it is working" do
107
+ @worker.run do
108
+ assert @worker.working?
109
+ assert Kthxbye::Job.find(@id, @worker.current_queue ).active?
110
+ end
111
+ end
112
+
113
+ should "know which is the current queue" do
114
+ worker = Kthxbye::Worker.new("*", 0)
115
+ worker.run do
116
+ assert_equal "test", worker.current_queue
117
+ end
118
+ end
119
+
120
+ should "know which is the current job id" do
121
+ worker = Kthxbye::Worker.new("*", 0)
122
+ worker.run do
123
+ assert_equal Kthxbye::Job.find(@id, worker.current_queue), worker.current_job
124
+ end
125
+ end
126
+
127
+ should "find the current job" do
128
+ @worker.run do
129
+ assert_equal ({'job_id' => @id, 'started' => Time.now.to_s}), Kthxbye::Worker.working_on(@worker.id)
130
+ end
131
+ end
132
+
133
+ should "requeue a job and report error if worker is killed mid-process" do
134
+ @worker.working( Kthxbye::Job.find( @id, "test" ) )
135
+ @worker.unregister_worker
136
+
137
+ assert_equal 1, Kthxbye::Failure.all.size
138
+ assert_equal "Kthxbye::ActiveWorkerKilled", Kthxbye::Failure.find(@id)['type']
139
+ assert_not_nil job = Kthxbye::Job.find( @id, "test" )
140
+ assert_equal 1, job.failed_attempts
141
+ end
142
+
143
+ should "show a pretty inspect" do
144
+ assert_equal "#<Worker: #{@worker.id}>", @worker.inspect
145
+ end
146
+
147
+ end
148
+ end
metadata ADDED
@@ -0,0 +1,364 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kthxbye
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Luke van der Hoeven
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-23 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ prerelease: false
23
+ name: redis
24
+ version_requirements: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ requirement: *id001
34
+ type: :runtime
35
+ - !ruby/object:Gem::Dependency
36
+ prerelease: false
37
+ name: yajl-ruby
38
+ version_requirements: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ requirement: *id002
48
+ type: :runtime
49
+ - !ruby/object:Gem::Dependency
50
+ prerelease: false
51
+ name: json
52
+ version_requirements: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ requirement: *id003
62
+ type: :runtime
63
+ - !ruby/object:Gem::Dependency
64
+ prerelease: false
65
+ name: mail
66
+ version_requirements: &id004 !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirement: *id004
76
+ type: :runtime
77
+ - !ruby/object:Gem::Dependency
78
+ prerelease: false
79
+ name: i18n
80
+ version_requirements: &id005 !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ requirement: *id005
90
+ type: :runtime
91
+ - !ruby/object:Gem::Dependency
92
+ prerelease: false
93
+ name: sinatra
94
+ version_requirements: &id006 !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ requirement: *id006
104
+ type: :runtime
105
+ - !ruby/object:Gem::Dependency
106
+ prerelease: false
107
+ name: shoulda
108
+ version_requirements: &id007 !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ requirement: *id007
118
+ type: :development
119
+ - !ruby/object:Gem::Dependency
120
+ prerelease: false
121
+ name: bundler
122
+ version_requirements: &id008 !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ~>
126
+ - !ruby/object:Gem::Version
127
+ hash: 23
128
+ segments:
129
+ - 1
130
+ - 0
131
+ - 0
132
+ version: 1.0.0
133
+ requirement: *id008
134
+ type: :development
135
+ - !ruby/object:Gem::Dependency
136
+ prerelease: false
137
+ name: jeweler
138
+ version_requirements: &id009 !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ~>
142
+ - !ruby/object:Gem::Version
143
+ hash: 270495430
144
+ segments:
145
+ - 1
146
+ - 5
147
+ - 0
148
+ - pre3
149
+ version: 1.5.0.pre3
150
+ requirement: *id009
151
+ type: :development
152
+ - !ruby/object:Gem::Dependency
153
+ prerelease: false
154
+ name: rcov
155
+ version_requirements: &id010 !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ hash: 3
161
+ segments:
162
+ - 0
163
+ version: "0"
164
+ requirement: *id010
165
+ type: :development
166
+ - !ruby/object:Gem::Dependency
167
+ prerelease: false
168
+ name: shoulda
169
+ version_requirements: &id011 !ruby/object:Gem::Requirement
170
+ none: false
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ hash: 3
175
+ segments:
176
+ - 0
177
+ version: "0"
178
+ requirement: *id011
179
+ type: :development
180
+ - !ruby/object:Gem::Dependency
181
+ prerelease: false
182
+ name: bundler
183
+ version_requirements: &id012 !ruby/object:Gem::Requirement
184
+ none: false
185
+ requirements:
186
+ - - ~>
187
+ - !ruby/object:Gem::Version
188
+ hash: 23
189
+ segments:
190
+ - 1
191
+ - 0
192
+ - 0
193
+ version: 1.0.0
194
+ requirement: *id012
195
+ type: :development
196
+ - !ruby/object:Gem::Dependency
197
+ prerelease: false
198
+ name: jeweler
199
+ version_requirements: &id013 !ruby/object:Gem::Requirement
200
+ none: false
201
+ requirements:
202
+ - - ~>
203
+ - !ruby/object:Gem::Version
204
+ hash: 270495430
205
+ segments:
206
+ - 1
207
+ - 5
208
+ - 0
209
+ - pre3
210
+ version: 1.5.0.pre3
211
+ requirement: *id013
212
+ type: :development
213
+ - !ruby/object:Gem::Dependency
214
+ prerelease: false
215
+ name: rcov
216
+ version_requirements: &id014 !ruby/object:Gem::Requirement
217
+ none: false
218
+ requirements:
219
+ - - ">="
220
+ - !ruby/object:Gem::Version
221
+ hash: 3
222
+ segments:
223
+ - 0
224
+ version: "0"
225
+ requirement: *id014
226
+ type: :development
227
+ - !ruby/object:Gem::Dependency
228
+ prerelease: false
229
+ name: redis
230
+ version_requirements: &id015 !ruby/object:Gem::Requirement
231
+ none: false
232
+ requirements:
233
+ - - ~>
234
+ - !ruby/object:Gem::Version
235
+ hash: 5
236
+ segments:
237
+ - 2
238
+ - 0
239
+ - 5
240
+ version: 2.0.5
241
+ requirement: *id015
242
+ type: :runtime
243
+ - !ruby/object:Gem::Dependency
244
+ prerelease: false
245
+ name: yajl-ruby
246
+ version_requirements: &id016 !ruby/object:Gem::Requirement
247
+ none: false
248
+ requirements:
249
+ - - ">="
250
+ - !ruby/object:Gem::Version
251
+ hash: 13
252
+ segments:
253
+ - 0
254
+ - 7
255
+ - 7
256
+ version: 0.7.7
257
+ requirement: *id016
258
+ type: :runtime
259
+ - !ruby/object:Gem::Dependency
260
+ prerelease: false
261
+ name: json
262
+ version_requirements: &id017 !ruby/object:Gem::Requirement
263
+ none: false
264
+ requirements:
265
+ - - ~>
266
+ - !ruby/object:Gem::Version
267
+ hash: 11
268
+ segments:
269
+ - 1
270
+ - 4
271
+ - 6
272
+ version: 1.4.6
273
+ requirement: *id017
274
+ type: :runtime
275
+ description: "Kthxbye is the answer to a fairly unique-yet-common problem: Background job processing when we care about the result."
276
+ email: hungerandthirst@gmail.com
277
+ executables: []
278
+
279
+ extensions: []
280
+
281
+ extra_rdoc_files:
282
+ - LICENSE
283
+ - README.textile
284
+ files:
285
+ - .document
286
+ - .gitignore
287
+ - DESIGN.textile
288
+ - Gemfile
289
+ - Gemfile.lock
290
+ - LICENSE
291
+ - README.textile
292
+ - Rakefile
293
+ - VERSION
294
+ - config.ru
295
+ - lib/kthxbye.rb
296
+ - lib/kthxbye/config.rb
297
+ - lib/kthxbye/exceptions.rb
298
+ - lib/kthxbye/failure.rb
299
+ - lib/kthxbye/helper.rb
300
+ - lib/kthxbye/job.rb
301
+ - lib/kthxbye/version.rb
302
+ - lib/kthxbye/web_interface.rb
303
+ - lib/kthxbye/web_interface/public/application.js
304
+ - lib/kthxbye/web_interface/public/awesome-buttons.css
305
+ - lib/kthxbye/web_interface/public/jquery.js
306
+ - lib/kthxbye/web_interface/public/style.css
307
+ - lib/kthxbye/web_interface/views/error.haml
308
+ - lib/kthxbye/web_interface/views/failed.haml
309
+ - lib/kthxbye/web_interface/views/hash.haml
310
+ - lib/kthxbye/web_interface/views/layout.haml
311
+ - lib/kthxbye/web_interface/views/overview.haml
312
+ - lib/kthxbye/web_interface/views/queues.haml
313
+ - lib/kthxbye/web_interface/views/set.haml
314
+ - lib/kthxbye/web_interface/views/stats.haml
315
+ - lib/kthxbye/web_interface/views/view_backtrace.haml
316
+ - lib/kthxbye/web_interface/views/workers.haml
317
+ - lib/kthxbye/web_interface/views/working.haml
318
+ - lib/kthxbye/worker.rb
319
+ - test/helper.rb
320
+ - test/redis-test.conf
321
+ - test/test_failure.rb
322
+ - test/test_helper.rb
323
+ - test/test_kthxbye.rb
324
+ - test/test_worker.rb
325
+ has_rdoc: true
326
+ homepage: http://github.com/plukevdh/kthxbye
327
+ licenses: []
328
+
329
+ post_install_message:
330
+ rdoc_options: []
331
+
332
+ require_paths:
333
+ - lib
334
+ required_ruby_version: !ruby/object:Gem::Requirement
335
+ none: false
336
+ requirements:
337
+ - - ">="
338
+ - !ruby/object:Gem::Version
339
+ hash: 3
340
+ segments:
341
+ - 0
342
+ version: "0"
343
+ required_rubygems_version: !ruby/object:Gem::Requirement
344
+ none: false
345
+ requirements:
346
+ - - ">="
347
+ - !ruby/object:Gem::Version
348
+ hash: 3
349
+ segments:
350
+ - 0
351
+ version: "0"
352
+ requirements: []
353
+
354
+ rubyforge_project:
355
+ rubygems_version: 1.3.7
356
+ signing_key:
357
+ specification_version: 3
358
+ summary: Async processing + results notification
359
+ test_files:
360
+ - test/helper.rb
361
+ - test/test_failure.rb
362
+ - test/test_helper.rb
363
+ - test/test_kthxbye.rb
364
+ - test/test_worker.rb