stalk_climber 0.0.6 → 0.1.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.
@@ -2,6 +2,7 @@ module StalkClimber
2
2
  module LazyEnumerable
3
3
  include Enumerable
4
4
 
5
+ # Convert Enumerable public methods to their lazy counterparts
5
6
  def self.make_lazy(*methods)
6
7
  methods.each do |method|
7
8
  define_method method do |*args, &block|
@@ -0,0 +1,182 @@
1
+ module StalkClimber
2
+ class Tube < Beaneater::Tube
3
+
4
+ # List of available attributes available via the stats object.
5
+ STATS_ATTRIBUTES = %w[
6
+ cmd-delete cmd-pause-tube current-jobs-buried current-jobs-delayed
7
+ current-jobs-ready current-jobs-reserved current-jobs-urgent current-using
8
+ current-waiting current-watching name pause pause-time-left total-jobs
9
+ ]
10
+
11
+ # Lookup table of which method to call to retrieve each stat
12
+ STATS_METHODS = Hash[
13
+ STATS_ATTRIBUTES.zip(STATS_ATTRIBUTES.map {|stat| stat.gsub(/-/, '_')})
14
+ ]
15
+
16
+ # Pause and name are available as stats but are not included in this list
17
+ # because Beaneater::Tube already defines methods with those names. Instead,
18
+ # the pause stat can be obtained via Tube#pause_time and name can be obtained
19
+ # via the superclass method.
20
+ STATS_METHODS.values.reject {|k,v| %w[name pause].include?(k)}.each do |method_name|
21
+ define_method method_name do |force_refresh = true|
22
+ return stats(force_refresh).send(method_name)
23
+ end
24
+ end
25
+
26
+
27
+ # :call-seq:
28
+ # exists?() => Boolean
29
+ #
30
+ # Determines if a tube exists by retrieving stats for the tube. If Beaneater
31
+ # can't find the tube then it oes not exist and false is returned. If stats
32
+ # are retrieved successfully then true is returned. The stats command is used
33
+ # because it will return a response of near constant size, whereas, depending
34
+ # on the environment, list-tubes could return a much larger response.
35
+ # Additionally, updated stats information is usually more valuable than a list
36
+ # of tubes that is immediately discarded.
37
+ def exists?
38
+ return !stats.nil?
39
+ rescue Beaneater::NotFoundError
40
+ return false
41
+ end
42
+
43
+
44
+ # :call-seq:
45
+ # pause_time(force_refresh = false) => Integer
46
+ #
47
+ # Returns pause stat which indicates the duration the tube is currently
48
+ # paused for. The name pause_time is used because Beaneater::Tube already
49
+ # defines a pause method.
50
+ def pause_time(force_refresh = false)
51
+ return stats(force_refresh).pause
52
+ end
53
+
54
+
55
+ # :call-seq:
56
+ # stats(force_refresh = true) => Beaneater::StatStruct
57
+ #
58
+ # Returns related stats for this tube.
59
+ #
60
+ # @tube.stats.current_jobs_delayed
61
+ # #=> 3
62
+ def stats(force_refresh = true)
63
+ if force_refresh || @stats.nil?
64
+ stats = transmit_to_all("stats-tube #{name}", :merge => true)[:body]
65
+ @stats = Beaneater::StatStruct.from_hash(stats)
66
+ end
67
+ return @stats
68
+ end
69
+
70
+
71
+ # :call-seq:
72
+ # to_h() => Hash
73
+ #
74
+ # Returns a hash of all tube attributes derived from updated stats
75
+ #
76
+ # tube = StalkClimber::Tube.new(connection_pool, 'stalk_climber')
77
+ # tube.to_h
78
+ # #=> {"cmd-delete"=>100, "cmd-pause-tube"=>101, "current-jobs-buried"=>102, "current-jobs-delayed"=>103, "current-jobs-ready"=>104, "current-jobs-reserved"=>105, "current-jobs-urgent"=>106, "current-using"=>107, "current-waiting"=>108, "current-watching"=>109, "name"=>"stalk_climber", "pause"=>111, "pause-time-left"=>110, "total-jobs"=>112}
79
+ def to_h
80
+ stats
81
+ return Hash[
82
+ STATS_METHODS.map do |stat, method_name|
83
+ [stat, stats(false).send(method_name)]
84
+ end
85
+ ]
86
+ end
87
+
88
+
89
+ # :call-seq:
90
+ # to_s() => String
91
+ #
92
+ # Returns string representation of the tube
93
+ #
94
+ # tube = StalkClimber::Tube.new(connection_pool, 'stalk_climber')
95
+ # tube.to_s
96
+ # #=> #<StalkClimber::Tube name="stalk_climber">
97
+ def to_s
98
+ "#<StalkClimber::Tube name=#{name}>"
99
+ end
100
+ alias :inspect :to_s
101
+
102
+
103
+ # :method: cmd_delete
104
+ # :call-seq:
105
+ # cmd_delete() => Integer
106
+ #
107
+ # Returns the number of times the delete command has been called for
108
+ # jobs in the tube.
109
+
110
+ # :method: cmd_pause_tube
111
+ # :call-seq:
112
+ # cmd_pause_tube() => Integer
113
+ #
114
+ # Returns the number of times the pause-tube command has been called on
115
+ # the tube.
116
+
117
+ # :method: current_jobs_buried
118
+ # :call-seq:
119
+ # current_jobs_buried() => Integer
120
+ #
121
+ # Returns the number of jobs in the tube that are currently buried.
122
+
123
+ # :method: current_jobs_delayed
124
+ # :call-seq:
125
+ # current_jobs_buried() => Integer
126
+ #
127
+ # Returns the number of jobs in the tube that are currently buried.
128
+
129
+ # :method: current_jobs_ready
130
+ # :call-seq:
131
+ # current_jobs_ready() => Integer
132
+ #
133
+ # Returns the number of jobs in the tube that are currently ready.
134
+
135
+ # :method: current_jobs_reserved
136
+ # :call-seq:
137
+ # current_jobs_reserved() => Integer
138
+ #
139
+ # Returns the number of jobs in the tube that are currently reserved.
140
+
141
+ # :method: current_jobs_urgent
142
+ # :call-seq:
143
+ # current_jobs_urgent() => Integer
144
+ #
145
+ # Returns the number of jobs currently in the tube with a priority
146
+ # less than 1024, and thus deemed urgent.
147
+
148
+ # :method: current_using
149
+ # :call-seq:
150
+ # current_using() => Integer
151
+ #
152
+ # Returns the number of connections that are currently using the tube.
153
+
154
+ # :method: current_waiting
155
+ # :call-seq:
156
+ # current_waiting() => Integer
157
+ #
158
+ # Returns the number of connections that have issued reserve requests
159
+ # for the tube and are currently awaiting a reply.
160
+
161
+ # :method: current_watching
162
+ # :call-seq:
163
+ # current_watching() => Integer
164
+ #
165
+ # Returns the number of connections that are watching the tube.
166
+
167
+ # :method: pause_time_left
168
+ # :call-seq:
169
+ # pause_time_left() => Integer
170
+ #
171
+ # Returns the number of seconds remaining before the tube is no longer
172
+ # paused.
173
+
174
+ # :method: total_jobs
175
+ # :call-seq:
176
+ # total_jobs() => Integer
177
+ #
178
+ # Returns the total number of jobs that have been registered with the tube
179
+ # since the tube was most recently created.
180
+
181
+ end
182
+ end
@@ -0,0 +1,37 @@
1
+ module StalkClimber
2
+ class Tubes < Beaneater::Tubes
3
+
4
+ extend Forwardable
5
+ include RUBY_VERSION >= '2.0.0' ? LazyEnumerable : Enumerable
6
+
7
+ def_delegator :all, :each
8
+
9
+
10
+ # :call-seq:
11
+ # all() => Array[StalkClimber::Tube]
12
+ #
13
+ # List of all known beanstalk tubes in the connection pool.
14
+ # Adapted with minor modification from Beaneater::Tubes#all
15
+ def all
16
+ return transmit_to_all('list-tubes', :merge => true)[:body].map do |tube_name|
17
+ StalkClimber::Tube.new(self.pool, tube_name)
18
+ end
19
+ end
20
+
21
+ # :call-seq:
22
+ # each() => Enumerator
23
+ # each {|tube| block }
24
+ #
25
+ # Interface for tube enumerator/enumeration. Tubes are mostly in creation order,
26
+ # though order can vary depending on the order in which responses to list-tubes are
27
+ # received from each of the Beanstalkd servers. Returns an instance of
28
+ # StalkClimber::Tube for each tube. Unlike Jobs, tubes are not cached because
29
+ # they are more easily accessible
30
+ #
31
+ # tubes = StalkClimber::Tubes.new(connection_pool)
32
+ # tubes.each do |tube|
33
+ # tube.clear
34
+ # end
35
+
36
+ end
37
+ end
@@ -1,3 +1,4 @@
1
1
  module StalkClimber
2
- VERSION = '0.0.6'
2
+ # StalkClimber version number
3
+ VERSION = '0.1.0'
3
4
  end
@@ -6,13 +6,15 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
6
 
7
7
  require 'test/unit'
8
8
  require 'mocha/setup'
9
-
9
+ require 'minitest/autorun'
10
+ require 'minitest/should'
10
11
  require 'stalk_climber'
12
+ require 'securerandom'
11
13
 
12
14
  BEANSTALK_ADDRESS = ENV['BEANSTALK_ADDRESS'] || 'beanstalk://localhost'
13
15
  BEANSTALK_ADDRESSES = ENV['BEANSTALK_ADDRESSES'] || BEANSTALK_ADDRESS
14
16
 
15
- class Test::Unit::TestCase
17
+ class StalkClimber::TestCase < MiniTest::Should::TestCase
16
18
 
17
19
  def seed_jobs(count = 5)
18
20
  count.times.map do
@@ -0,0 +1,260 @@
1
+ require 'test_helper'
2
+
3
+ class BeaneaterJobTest < StalkClimber::TestCase
4
+
5
+ setup do
6
+ @pool = Beaneater::Pool.new(['localhost'])
7
+ @tube = @pool.tubes.find 'tube'
8
+ end
9
+
10
+ context "for #bury" do
11
+ setup do
12
+ @time = Time.now.to_i
13
+ @tube.put "foo bury #{@time}", :pri => 5
14
+ end
15
+
16
+ should "be buried with same pri" do
17
+ job = @tube.reserve
18
+ assert_equal "foo bury #{@time}", job.body
19
+ assert_equal 'reserved', job.stats.state
20
+ job.bury
21
+ assert_equal 'buried', job.stats.state
22
+ assert_equal 5, job.stats.pri
23
+ assert_equal "foo bury #{@time}", @tube.peek(:buried).body
24
+ end
25
+
26
+ should "be released with new pri" do
27
+ job = @tube.reserve
28
+ assert_equal "foo bury #{@time}", job.body
29
+ assert_equal 'reserved', job.stats.state
30
+ job.bury(:pri => 10)
31
+ assert_equal 'buried', job.stats.state
32
+ assert_equal 10, job.stats.pri
33
+ assert_equal "foo bury #{@time}", @tube.peek(:buried).body
34
+ end
35
+
36
+ should "not bury if not reserved" do
37
+ job = @tube.peek(:ready)
38
+ assert_raises(Beaneater::JobNotReserved) { job.bury }
39
+ end
40
+
41
+ should "not bury if reserved and deleted" do
42
+ job = @tube.reserve
43
+ job.delete
44
+ assert_equal false, job.reserved
45
+ assert_raises(Beaneater::NotFoundError) { job.bury }
46
+ end
47
+ end # bury
48
+
49
+ context "for #release" do
50
+ setup do
51
+ @time = Time.now.to_i
52
+ @tube.put "foo release #{@time}", :pri => 5
53
+ end
54
+
55
+ should "be released with same pri" do
56
+ job = @tube.reserve
57
+ assert_equal "foo release #{@time}", job.body
58
+ assert_equal 'reserved', job.stats.state
59
+ job.release
60
+ assert_equal 'ready', job.stats.state
61
+ assert_equal 5, job.stats.pri
62
+ assert_equal 0, job.stats.delay
63
+ end
64
+
65
+ should "be released with new pri" do
66
+ job = @tube.reserve
67
+ assert_equal "foo release #{@time}", job.body
68
+ assert_equal 'reserved', job.stats.state
69
+ job.release :pri => 10, :delay => 2
70
+ assert_equal 'delayed', job.stats.state
71
+ assert_equal 10, job.stats.pri
72
+ assert_equal 2, job.stats.delay
73
+ end
74
+
75
+ should "not released if not reserved" do
76
+ job = @tube.peek(:ready)
77
+ assert_raises(Beaneater::JobNotReserved) { job.release }
78
+ end
79
+
80
+ should "not release if not reserved and buried" do
81
+ job = @tube.reserve
82
+ job.bury
83
+ assert_raises(Beaneater::JobNotReserved) { job.release }
84
+ end
85
+ end # release
86
+
87
+ describe "for #delete" do
88
+ setup do
89
+ @tube.put 'foo'
90
+ end
91
+
92
+ should "deletable" do
93
+ job = @tube.peek(:ready)
94
+ assert_equal 'foo', job.body
95
+ job.delete
96
+ assert_nil @tube.peek(:ready)
97
+ end
98
+ end # delete
99
+
100
+ describe "for #touch" do
101
+ setup do
102
+ @tube.put 'foo touch', :ttr => 1
103
+ end
104
+
105
+ should "be toucheable" do
106
+ job = @tube.reserve
107
+ assert_equal 'foo touch', job.body
108
+ job.touch
109
+ assert_equal 1, job.stats.reserves
110
+ job.delete
111
+ end
112
+
113
+ should "not touch if not reserved" do
114
+ job = @tube.peek(:ready)
115
+ assert_raises(Beaneater::JobNotReserved) { job.touch }
116
+ end
117
+
118
+ should "not touch if not reserved and released" do
119
+ job = @tube.reserve
120
+ job.release
121
+ assert_raises(Beaneater::JobNotReserved) { job.touch }
122
+ end
123
+
124
+ should "not touch if reserved and deleted" do
125
+ job = @tube.reserve
126
+ job.delete
127
+ assert_raises(Beaneater::NotFoundError) { job.touch }
128
+ end
129
+ end # touch
130
+
131
+ describe "for #kick" do
132
+ setup do
133
+ @tube.put 'foo touch', :ttr => 1
134
+ end
135
+
136
+ should "be toucheable" do
137
+ job = @tube.reserve
138
+ assert_equal 'foo touch', job.body
139
+ job.bury
140
+ assert_equal 1, @tube.stats.current_jobs_buried
141
+ if @pool.stats.version.to_f > 1.7
142
+ job.kick
143
+ assert_equal 0, @tube.stats.current_jobs_buried
144
+ assert_equal 1, @tube.stats.current_jobs_ready
145
+ end
146
+ end
147
+ end # kick
148
+
149
+ describe "for #stats" do
150
+ setup do
151
+ @tube.put 'foo'
152
+ @job = @tube.peek(:ready)
153
+ end
154
+
155
+ should "have stats" do
156
+ assert_equal 'tube', @job.stats['tube']
157
+ assert_equal 'ready', @job.stats.state
158
+ end
159
+
160
+ should "return nil for deleted job with no stats" do
161
+ @job.delete
162
+ assert_raises(Beaneater::NotFoundError) { @job.stats }
163
+ end
164
+ end # stats
165
+
166
+ describe "for #reserved?" do
167
+ setup do
168
+ @tube.put 'foo'
169
+ @job = @tube.peek(:ready)
170
+ end
171
+
172
+ should "have stats" do
173
+ assert_equal false, @job.reserved?
174
+ job = @tube.reserve
175
+ assert_equal job.id, @job.id
176
+ assert_equal true, @job.reserved?
177
+ @job.delete
178
+ assert_raises(Beaneater::NotFoundError) { @job.reserved? }
179
+ end
180
+ end # reserved?
181
+
182
+ describe "for #exists?" do
183
+ setup do
184
+ @tube.put 'foo'
185
+ @job = @tube.peek(:ready)
186
+ end
187
+
188
+ should "exists?" do
189
+ assert @job.exists?
190
+ end
191
+
192
+ should "not exist" do
193
+ @job.delete
194
+ assert !@job.exists?
195
+ end
196
+ end # exists?
197
+
198
+ describe "for #tube" do
199
+ setup do
200
+ @tube.put 'bar'
201
+ @job = @tube.peek(:ready)
202
+ end
203
+
204
+ should "have stats" do
205
+ job = @tube.reserve
206
+ assert_equal @tube.name, job.tube
207
+ job.release
208
+ end
209
+ end # tube
210
+
211
+ describe "for #pri" do
212
+ setup do
213
+ @tube.put 'bar', :pri => 1
214
+ @job = @tube.peek(:ready)
215
+ end
216
+
217
+ should "return pri" do
218
+ job = @tube.reserve
219
+ assert_equal 1, job.pri
220
+ job.release
221
+ end
222
+ end # tube
223
+
224
+
225
+ describe "for #ttr" do
226
+ setup do
227
+ @tube.put 'bar', :ttr => 5
228
+ @job = @tube.peek(:ready)
229
+ end
230
+
231
+ should "return ttr" do
232
+ job = @tube.reserve
233
+ assert_equal 5, job.ttr
234
+ job.release
235
+ end
236
+ end # tube
237
+
238
+ describe "for #delay" do
239
+ setup do
240
+ @tube.put 'bar', :delay => 5
241
+ @job = @tube.peek(:delayed)
242
+ end
243
+
244
+ should "return delay" do
245
+ assert_equal 5, @job.delay
246
+ end
247
+ end # tube
248
+
249
+ after do
250
+ cleanup_tubes!(['tube'])
251
+ end
252
+
253
+ def cleanup_tubes!(tubes, bp=nil)
254
+ bp ||= @pool
255
+ tubes.each do |name|
256
+ bp.tubes.find(name).clear
257
+ end
258
+ end
259
+
260
+ end # Beaneater::Job