timberline 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+
3
+ describe Timberline::Queue do
4
+ describe "#initialize" do
5
+ it "raises an ArgumentError if no queue_name is provided" do
6
+ expect { Timberline::Queue.new(nil) }.to raise_error(ArgumentError)
7
+ end
8
+
9
+ it "loads a default read_timeout of 0" do
10
+ expect(Timberline::Queue.new("fritters").read_timeout).to eq(0)
11
+ end
12
+
13
+ it "allows you to override the default read_timeout" do
14
+ expect(Timberline::Queue.new("fritters", 50).read_timeout).to eq(50)
15
+ end
16
+
17
+ it "adds the queue's name to the Timberline queue listing" do
18
+ Timberline::Queue.new("fritters")
19
+ expect(Timberline.all_queues.map { |q| q.queue_name }).to include("fritters")
20
+ end
21
+ end
22
+
23
+ describe "#delete" do
24
+ subject { Timberline::Queue.new("fritters") }
25
+
26
+ before do
27
+ subject.delete
28
+ end
29
+
30
+ it "removes the queue from redis" do
31
+ expect(Timberline.redis["fritters"]).to be_nil
32
+ end
33
+
34
+ it "removes all of the queue's attributes from redis" do
35
+ expect(Timberline.redis["fritters:*"]).to be_nil
36
+ end
37
+
38
+ it "removes the queue from the Timberline queue listing" do
39
+ expect(Timberline.all_queues.map { |q| q.queue_name }).not_to include("fritters")
40
+ end
41
+ end
42
+
43
+ describe "#pop" do
44
+ subject { Timberline::Queue.new("fritters") }
45
+ before { subject.push("apple") }
46
+
47
+ it "will block if the queue is paused" do
48
+ expect(subject).to receive(:block_while_paused)
49
+ subject.pop
50
+ end
51
+
52
+ it "will return a Timberline::Envelope object" do
53
+ expect(subject.pop).to be_a Timberline::Envelope
54
+ end
55
+ end
56
+
57
+ describe "#push" do
58
+ subject { Timberline::Queue.new("fritters") }
59
+
60
+ it "returns the current size of the queue when you push" do
61
+ expect(subject.push("Test")).to be_a Numeric
62
+ end
63
+
64
+ it "puts an item on the queue when you push" do
65
+ subject.push("Test")
66
+ expect(subject.length).to eq(1)
67
+ end
68
+
69
+ it "properly formats data so it can be popped successfully" do
70
+ subject.push("Test")
71
+ expect(subject.pop).to be_a Timberline::Envelope
72
+ end
73
+ end
74
+
75
+ describe "#pause" do
76
+ subject { Timberline::Queue.new("fritters") }
77
+
78
+ before do
79
+ subject.pause
80
+ end
81
+
82
+ it "puts the queue in paused mode" do
83
+ expect(subject).to be_paused
84
+ end
85
+ end
86
+
87
+ describe "#unpause" do
88
+ subject { Timberline::Queue.new("fritters") }
89
+
90
+ before do
91
+ subject.pause
92
+ subject.unpause
93
+ end
94
+
95
+ it "unpauses the queue" do
96
+ expect(subject).not_to be_paused
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,375 @@
1
+ require 'spec_helper'
2
+
3
+ describe Timberline do
4
+ describe ".redis=" do
5
+ context "if Timberline hasn't been configured yet" do
6
+ before { Timberline.redis = nil }
7
+
8
+ it "initializes a new configuration object" do
9
+ expect(Timberline.config).not_to be_nil
10
+ end
11
+ end
12
+
13
+ context "if the argument is a Redis instance" do
14
+ before { Timberline.redis = Redis.new }
15
+
16
+ it "wraps the server in a namespace" do
17
+ expect(Timberline.redis).to be_a Redis::Namespace
18
+ end
19
+ end
20
+
21
+ context "if the argument is a Redis::Namespace instance" do
22
+ let(:namespace) { Redis::Namespace.new("fritters", redis: Redis.new) }
23
+ before { Timberline.redis = namespace }
24
+
25
+ it "accepts the namespace as-is" do
26
+ expect(Timberline.redis).to eq(namespace)
27
+ end
28
+ end
29
+
30
+ context "if the argument is nil" do
31
+ before do
32
+ Timberline.redis = Redis.new
33
+ Timberline.redis = nil
34
+ end
35
+
36
+ it "clears out the existing redis server" do
37
+ expect(Timberline.instance_variable_get("@redis")).to be_nil
38
+ end
39
+ end
40
+
41
+ context "if the argument is not an instance of nil, Redis, or Redis::Namespace" do
42
+ it "raises an exception" do
43
+ expect { Timberline.redis = "this isn't redis" }.to raise_error(StandardError)
44
+ end
45
+ end
46
+ end
47
+
48
+ describe ".redis" do
49
+ context "if Timberline hasn't been configured yet" do
50
+ before { Timberline.redis }
51
+
52
+ it "initializes a new configuration object" do
53
+ expect(Timberline.config).not_to be_nil
54
+ end
55
+
56
+ it "initializes a default redis server" do
57
+ expect(Timberline.redis).not_to be_nil
58
+ end
59
+ end
60
+
61
+ context "if Timberline has already had a redis server provided" do
62
+ let(:namespace) { Redis::Namespace.new("fritters", redis: Redis.new) }
63
+ before { Timberline.redis = namespace }
64
+
65
+ it "returns that server" do
66
+ expect(Timberline.redis).to eq(namespace)
67
+ end
68
+ end
69
+ end
70
+
71
+ describe ".all_queues" do
72
+ context "when there are no queues" do
73
+ before { allow(Timberline.redis).to receive(:smembers) { [] } }
74
+
75
+ it "returns an empty list" do
76
+ expect(Timberline.all_queues).to eq([])
77
+ end
78
+ end
79
+
80
+ context "when there are some queues" do
81
+ before { allow(Timberline.redis).to receive(:smembers) { %w(my_queue your_queue) } }
82
+
83
+ it "returns a list of appropriate size" do
84
+ expect(Timberline.all_queues.size).to eq(2)
85
+ end
86
+
87
+ it "returns a list of Queue objects" do
88
+ expect(Timberline.all_queues.first).to be_a Timberline::Queue
89
+ expect(Timberline.all_queues.last).to be_a Timberline::Queue
90
+ end
91
+ end
92
+ end
93
+
94
+ describe ".error_queue" do
95
+ it "returns a Timberline::Queue" do
96
+ expect(Timberline.error_queue).to be_a Timberline::Queue
97
+ end
98
+
99
+ it "returns the timberline_errors queue" do
100
+ expect(Timberline.error_queue.queue_name).to eq("timberline_errors")
101
+ end
102
+ end
103
+
104
+ describe ".queue" do
105
+ it "returns a Timberline::Queue" do
106
+ expect(Timberline.queue("fritters")).to be_a Timberline::Queue
107
+ end
108
+
109
+ it "returns a queue with the appropriate name" do
110
+ expect(Timberline.queue("fritters").queue_name).to eq("fritters")
111
+ end
112
+ end
113
+
114
+ describe ".push" do
115
+ let(:data) { "some data" }
116
+ let(:metadata) { { meta: :data } }
117
+
118
+ it "uses the #push method on the specified queue" do
119
+ expect_any_instance_of(Timberline::Queue).to receive(:push).with(data, metadata)
120
+ Timberline.push("fritters", data, metadata)
121
+ end
122
+ end
123
+
124
+ describe ".retry_item" do
125
+ let(:queue) { Timberline.queue("test_queue") }
126
+ let(:item) { Timberline.push(queue.queue_name, "Howdy kids."); queue.pop }
127
+
128
+ context "when the item has not yet been retried" do
129
+ before { Timberline.retry_item(item) }
130
+
131
+ it "puts the item back on the queue" do
132
+ expect(queue.length).to eq(1)
133
+ end
134
+
135
+ it "updates the retry count on the item" do
136
+ expect(item.retries).to eq(1)
137
+ end
138
+ end
139
+
140
+ context "when the item has been retried less than the maximum number of times" do
141
+ before do
142
+ Timberline.configure do |c|
143
+ c.max_retries = 5
144
+ end
145
+
146
+ item.retries = 3
147
+
148
+ Timberline.retry_item(item)
149
+ end
150
+
151
+ it "puts the item back on the queue" do
152
+ expect(queue.length).to eq(1)
153
+ end
154
+
155
+ it "updates the retry count on the item" do
156
+ expect(item.retries).to eq(4)
157
+ end
158
+ end
159
+
160
+ context "when the item has been retried the maximum number of times" do
161
+ before do
162
+ Timberline.configure do |c|
163
+ c.max_retries = 5
164
+ end
165
+
166
+ item.retries = 5
167
+ end
168
+
169
+ it "passes the item on to the error handling" do
170
+ expect(Timberline).to receive(:error_item).with(item)
171
+ Timberline.retry_item(item)
172
+ end
173
+ end
174
+ end
175
+
176
+ describe ".error_item" do
177
+ let(:queue) { Timberline.queue("test_queue") }
178
+ let(:error_queue) { Timberline.error_queue }
179
+ let(:item) { Timberline.push(queue.queue_name, "Howdy kids."); queue.pop }
180
+
181
+ before { Timberline.error_item(item) }
182
+
183
+ it "puts the item on the error_queue" do
184
+ expect(error_queue.length).to eq(1)
185
+ end
186
+
187
+ it "updates the fatal_error_at property on the item" do
188
+ expect(item.fatal_error_at).not_to be_nil
189
+ end
190
+ end
191
+
192
+ describe ".pause" do
193
+ context "when a Queue is not paused" do
194
+ subject { Timberline.queue("pause_test_queue") }
195
+
196
+ before do
197
+ Timberline.pause(subject.queue_name)
198
+ end
199
+
200
+ it "pauses the queue" do
201
+ expect(subject.paused?).to be true
202
+ end
203
+ end
204
+
205
+ context "when a Queue is already paused" do
206
+ subject { Timberline.queue("pause_test_queue") }
207
+
208
+ before do
209
+ subject.pause
210
+ Timberline.pause(subject.queue_name)
211
+ end
212
+
213
+ it "keeps the queue paused" do
214
+ expect(subject.paused?).to be true
215
+ end
216
+ end
217
+ end
218
+
219
+ describe ".unpause" do
220
+ context "when a Queue is paused" do
221
+ subject { Timberline.queue("pause_test_queue") }
222
+
223
+ before do
224
+ subject.pause
225
+ Timberline.unpause(subject.queue_name)
226
+ end
227
+
228
+ it "unpauses the queue" do
229
+ expect(subject.paused?).to be false
230
+ end
231
+ end
232
+
233
+ context "when a Queue is already unpaused" do
234
+ subject { Timberline.queue("pause_test_queue") }
235
+
236
+ before do
237
+ Timberline.unpause(subject.queue_name)
238
+ end
239
+
240
+ it "keeps the queue unpaused" do
241
+ expect(subject.paused?).to be false
242
+ end
243
+ end
244
+ end
245
+
246
+ describe ".configure" do
247
+ context "if Timberline hasn't been configured yet" do
248
+ it "initializes a new configuration object" do
249
+ Timberline.configure {}
250
+ expect(Timberline.config).not_to be_nil
251
+ end
252
+
253
+ it "yields the new config object to the block" do
254
+ expect { |b| Timberline.configure &b }.to yield_with_args(Timberline.config)
255
+ end
256
+ end
257
+
258
+ context "if Timberline has been configured" do
259
+ before do
260
+ Timberline.configure {}
261
+ end
262
+
263
+ it "yields the Timberlin config object to the block" do
264
+ expect { |b| Timberline.configure &b }.to yield_with_args(Timberline.config)
265
+ end
266
+ end
267
+ end
268
+
269
+ describe ".max_retries" do
270
+ context "if Timberline hasn't been configured yet" do
271
+ before { Timberline.max_retries }
272
+
273
+ it "initializes a new configuration object" do
274
+ expect(Timberline.config).not_to be_nil
275
+ end
276
+ end
277
+
278
+ context "if Timberline has been configured" do
279
+ before do
280
+ Timberline.configure do |c|
281
+ c.max_retries = 10
282
+ end
283
+ end
284
+
285
+ it "returns the configured number of maximum retries" do
286
+ expect(Timberline.max_retries).to eq(10)
287
+ end
288
+ end
289
+ end
290
+
291
+ describe ".stat_timeout" do
292
+ context "if Timberline hasn't been configured yet" do
293
+ before { Timberline.stat_timeout }
294
+
295
+ it "initializes a new configuration object" do
296
+ expect(Timberline.config).not_to be_nil
297
+ end
298
+ end
299
+
300
+ context "if Timberline has been configured" do
301
+ before do
302
+ Timberline.configure do |c|
303
+ c.stat_timeout = 10
304
+ end
305
+ end
306
+
307
+ it "returns the configured number of minutes for stat timeout" do
308
+ expect(Timberline.stat_timeout).to eq(10)
309
+ end
310
+ end
311
+ end
312
+
313
+ describe ".stat_timeout_seconds" do
314
+ context "if Timberline hasn't been configured yet" do
315
+ before { Timberline.stat_timeout_seconds }
316
+
317
+ it "initializes a new configuration object" do
318
+ expect(Timberline.config).not_to be_nil
319
+ end
320
+ end
321
+
322
+ context "if Timberline has been configured" do
323
+ before do
324
+ Timberline.configure do |c|
325
+ c.stat_timeout = 10
326
+ end
327
+ end
328
+
329
+ it "returns the configured number of seconds for stat timeout" do
330
+ expect(Timberline.stat_timeout_seconds).to eq(600)
331
+ end
332
+ end
333
+ end
334
+
335
+ describe ".watch" do
336
+ let(:queue) { Timberline.queue("test_queue") }
337
+ let(:queue_name) { queue.queue_name }
338
+ let(:success_item) { Timberline::Envelope.from_json(Timberline.redis.xmembers(queue.attr("success_stats")).first) }
339
+ let(:retried_item) { Timberline::Envelope.from_json(Timberline.redis.xmembers(queue.attr("retry_stats")).first) }
340
+ let(:errored_item) { Timberline::Envelope.from_json(Timberline.redis.xmembers(queue.attr("error_stats")).first) }
341
+
342
+ before do
343
+ # Make sure that the watch doesn't run forever.
344
+ Timberline.watch_proc = lambda { queue.length > 0 }
345
+ Timberline.push(queue_name, "Hey There!")
346
+ end
347
+
348
+ context "If the item can be processed successfully" do
349
+ it "logs the success of the item" do
350
+ expect_any_instance_of(Timberline::Queue).to receive(:add_success_stat)
351
+ Timberline.watch(queue_name) do |item|
352
+ # don't do anything
353
+ end
354
+ end
355
+ end
356
+
357
+ context "If the item is retried" do
358
+ it "logs that the item was retried" do
359
+ expect_any_instance_of(Timberline::Queue).to receive(:add_retry_stat)
360
+ Timberline.watch(queue_name) do |item|
361
+ raise Timberline::ItemRetried
362
+ end
363
+ end
364
+ end
365
+
366
+ context "If the item can be processed successfully" do
367
+ it "logs that the item was retried" do
368
+ expect_any_instance_of(Timberline::Queue).to receive(:add_error_stat)
369
+ Timberline.watch(queue_name) do |item|
370
+ raise Timberline::ItemErrored
371
+ end
372
+ end
373
+ end
374
+ end
375
+ end
@@ -0,0 +1,19 @@
1
+ require 'rspec'
2
+ require 'pry'
3
+
4
+ require File.expand_path("../../lib/timberline", __FILE__)
5
+
6
+ Dir[File.expand_path("../support/**/*.rb", __FILE__)].each { |f| require f }
7
+
8
+ RSpec.configure do |config|
9
+ config.mock_with :rspec
10
+ config.expect_with :rspec do |c|
11
+ c.syntax = [:expect]
12
+ end
13
+
14
+ config.order = "random"
15
+
16
+ config.after(:each) do
17
+ SpecSupport::TimberlineReset.clear_config
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module SpecSupport
2
+ module FakeRails
3
+ def self.create_fake_env
4
+ fake_rails = OpenStruct.new(:root => File.join(File.dirname(File.path(__FILE__)), "..", "fake_rails"), :env => "development")
5
+ Object.send(:const_set, :Rails, fake_rails)
6
+ end
7
+
8
+ def self.create_fake_env_without_config
9
+ fake_rails = OpenStruct.new(:root => File.join(File.dirname(File.path(__FILE__)), "..", "gibberish"), :env => "development")
10
+ Object.send(:const_set, :Rails, fake_rails)
11
+ end
12
+
13
+ def self.destroy_fake_env
14
+ Object.send(:remove_const, :Rails)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ module SpecSupport
2
+ module TimberlineReset
3
+ def self.clear_config
4
+ Timberline.redis = nil
5
+ Timberline.instance_variable_set("@config", nil)
6
+ clear_test_db
7
+ Timberline.redis = nil
8
+ end
9
+
10
+ # Use database 15 for testing, so we don't risk overwriting any data that's
11
+ # actually useful
12
+ def self.clear_test_db
13
+ Timberline.config do |c|
14
+ c.database = 15
15
+ end
16
+ Timberline.redis.flushdb
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ module SpecSupport
2
+ module TimberlineYaml
3
+ def self.load_constant
4
+ config_file = File.join(File.dirname(File.path(__FILE__)), "..", "config", "test_config.yaml")
5
+ Object.send(:const_set, :TIMBERLINE_YAML, config_file)
6
+ end
7
+
8
+ def self.load_constant_for_missing_file
9
+ config_file = File.join(File.dirname(File.path(__FILE__)), "..", "config", "party_config.yaml")
10
+ Object.send(:const_set, :TIMBERLINE_YAML, config_file)
11
+ end
12
+
13
+ def self.destroy_constant
14
+ Object.send(:remove_const, :TIMBERLINE_YAML)
15
+ end
16
+ end
17
+ end
data/timberline.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.version = Timberline::VERSION
8
8
  s.authors = ["Tommy Morgan"]
9
9
  s.email = ["tommy.morgan@gmail.com"]
10
- s.homepage = "http://github.com/duwanis/timberline"
10
+ s.homepage = "http://github.com/treehouse/timberline"
11
11
  s.summary = %q{Timberline is a simple and extensible queuing system built in Ruby and backed by Redis.}
12
12
  s.description = %q{Timberline is a simple and extensible queuing system built in Ruby and backed by Redis. It makes as few assumptions as possible about how you want to interact with your queues while also allowing for some functionality that should be universally useful, like allowing for automatic retries of jobs and queue statistics.}
13
13
 
@@ -18,14 +18,13 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- # specify any dependencies here; for example:
22
- # s.add_development_dependency "rspec"
23
- # s.add_runtime_dependency "rest-client"
24
21
  s.add_runtime_dependency "redis"
25
22
  s.add_runtime_dependency "redis-namespace"
26
23
  s.add_runtime_dependency "redis-expiring-set"
27
24
  s.add_runtime_dependency "trollop"
28
25
  s.add_runtime_dependency "daemons"
29
26
 
30
- s.add_development_dependency "nutrasuite"
27
+ s.add_development_dependency "rake"
28
+ s.add_development_dependency "rspec", '~> 3.0.0.rc1'
29
+ s.add_development_dependency "pry"
31
30
  end