tuber 0.0.1 → 0.5.1

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.
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ describe "beanstalk-client" do
6
+ before do
7
+ @beanstalk = Tuber.new(tuber_address)
8
+ @tubes = ['one', 'two', 'three']
9
+
10
+ # Put something on each tube so they exist
11
+ tube_one = @beanstalk.tubes.find('one')
12
+ tube_one.put('one')
13
+
14
+ tube_two = @beanstalk.tubes.find('two')
15
+ tube_two.put('two')
16
+ end
17
+
18
+ describe "test thread safe one" do
19
+ before do
20
+ # Create threads that will execute
21
+ # A: use one
22
+ # B: use one
23
+ # B: put two
24
+ # A: put one
25
+ a = Thread.new do
26
+ tube_one = @beanstalk.tubes.find('one')
27
+ sleep 0.5
28
+ tube_one.put('one')
29
+ end
30
+
31
+ b = Thread.new do
32
+ sleep 0.125
33
+ tube_two = @beanstalk.tubes.find('two')
34
+ tube_two.put('two')
35
+ end
36
+
37
+ a.join
38
+ b.join
39
+ end
40
+
41
+ it "should return correct current-jobs-ready for tube one" do
42
+ one = @beanstalk.tubes.find('one').stats
43
+ assert_equal 2, one.current_jobs_ready
44
+ end
45
+
46
+ it "should return correct current-jobs-ready for tube two" do
47
+ two = @beanstalk.tubes.find('two').stats
48
+ assert_equal 2, two.current_jobs_ready
49
+ end
50
+ end
51
+
52
+ describe "test thread safe two" do
53
+ before do
54
+ a = Thread.new do
55
+ tube_one = @beanstalk.tubes.find('one')
56
+ sleep 0.5
57
+ tube_one.put('one')
58
+ end
59
+
60
+ b = Thread.new do
61
+ tube_two = @beanstalk.tubes.find('two')
62
+ sleep 0.125
63
+ tube_two.put('two')
64
+ end
65
+
66
+ a.join
67
+ b.join
68
+ end
69
+
70
+ it "should return correct current-jobs-ready for tube one" do
71
+ one = @beanstalk.tubes.find('one').stats
72
+ assert_equal 2, one.current_jobs_ready
73
+ end
74
+
75
+ it "should return correct current-jobs-ready for tube two" do
76
+ two = @beanstalk.tubes.find('two').stats
77
+ assert_equal 2, two.current_jobs_ready
78
+ end
79
+ end
80
+
81
+ describe "test delete job in reserved state" do
82
+ before do
83
+ @tube_three = @beanstalk.tubes.find('three')
84
+ @tube_three.put('one')
85
+ @job = @tube_three.reserve
86
+ end
87
+
88
+ it "should be deleted properly" do
89
+ assert_equal 'one', @job.body
90
+ assert_equal 'one', @beanstalk.jobs.find(@job.id).body
91
+ @job.delete
92
+ assert_nil @beanstalk.jobs.find(@job.id)
93
+ end
94
+ end
95
+
96
+ describe "test delete job in buried state" do
97
+ before do
98
+ @tube_three = @beanstalk.tubes.find('three')
99
+ @tube_three.put('two')
100
+ @job = @tube_three.reserve
101
+ end
102
+
103
+ it "should delete job as expected in buried state" do
104
+ assert_equal 'two', @job.body
105
+ @job.bury
106
+ assert_equal 'two', @tube_three.peek(:buried).body
107
+
108
+ @job.delete
109
+ assert_nil @beanstalk.jobs.find(@job.id)
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,329 @@
1
+ # frozen_string_literal: true
2
+
3
+ # test/tubes_test.rb
4
+
5
+ require File.expand_path('../test_helper', __FILE__)
6
+
7
+ describe Tuber::Tubes do
8
+ describe "for #find" do
9
+ before do
10
+ @beanstalk = stub
11
+ @tubes = Tuber::Tubes.new(@beanstalk)
12
+ end
13
+
14
+ it("should return Tube obj") { assert_kind_of Tuber::Tube, @tubes.find(:foo) }
15
+ it("should return Tube name") { assert_equal "foo", @tubes.find(:foo).name }
16
+ it("should support hash syntax") { assert_equal "bar", @tubes["bar"].name }
17
+ end # find
18
+
19
+ describe "for #use" do
20
+ before do
21
+ @beanstalk = Tuber.new(tuber_address)
22
+ end
23
+
24
+ it "should switch to used tube for valid name" do
25
+ Tuber::Tube.new(@beanstalk, 'some_name')
26
+ @beanstalk.tubes.use('some_name')
27
+ assert_equal 'some_name', @beanstalk.tubes.used.name
28
+ end
29
+
30
+ it "should raise for invalid tube name" do
31
+ assert_raises(Tuber::InvalidTubeName) { @beanstalk.tubes.use('; ') }
32
+ end
33
+ end # use
34
+
35
+ describe "for #watch & #watched" do
36
+ before do
37
+ @beanstalk = Tuber.new(tuber_address)
38
+ end
39
+
40
+ it 'should watch specified tubes' do
41
+ @beanstalk.tubes.watch('foo')
42
+ @beanstalk.tubes.watch('bar')
43
+ assert_equal ['default', 'foo', 'bar'].sort, @beanstalk.tubes.watched.map(&:name).sort
44
+ end
45
+
46
+ it 'should raise invalid name for bad tube' do
47
+ assert_raises(Tuber::InvalidTubeName) { @beanstalk.tubes.watch('; ') }
48
+ end
49
+ end # watch! & watched
50
+
51
+ describe "for #all" do
52
+ before do
53
+ @beanstalk = Tuber.new(tuber_address)
54
+ @beanstalk.tubes.find('foo').put 'bar'
55
+ @beanstalk.tubes.find('bar').put 'foo'
56
+ end
57
+
58
+ it 'should retrieve all tubes' do
59
+ ['default', 'foo', 'bar'].each do |t|
60
+ assert @beanstalk.tubes.all.map(&:name).include?(t)
61
+ end
62
+ end
63
+ end # all
64
+
65
+ describe "for Enumerable" do
66
+ before do
67
+ @beanstalk = Tuber.new(tuber_address)
68
+ @beanstalk.tubes.find('foo').put 'bar'
69
+ @beanstalk.tubes.find('bar').put 'foo'
70
+ end
71
+
72
+ it 'should map tubes' do
73
+ ['default', 'foo', 'bar'].each do |t|
74
+ assert @beanstalk.tubes.map(&:name).include?(t)
75
+ end
76
+ end
77
+ end
78
+
79
+ describe "for #used" do
80
+ before do
81
+ @beanstalk = Tuber.new(tuber_address)
82
+ @beanstalk.tubes.find('foo').put 'bar'
83
+ @beanstalk.tubes.find('bar').put 'foo'
84
+ end
85
+
86
+ it 'should retrieve used tube' do
87
+ assert_equal 'bar', @beanstalk.tubes.used.name
88
+ end
89
+
90
+ it 'should support dashed tubes' do
91
+ @beanstalk.tubes.find('der-bam').put 'foo'
92
+ assert_equal 'der-bam', @beanstalk.tubes.used.name
93
+ end
94
+ end # used
95
+
96
+ describe "for #watch!" do
97
+ before do
98
+ @beanstalk = Tuber.new(tuber_address)
99
+ end
100
+
101
+ it 'should watch specified tubes' do
102
+ @beanstalk.tubes.watch!(:foo)
103
+ @beanstalk.tubes.watch!('bar')
104
+ assert_equal ['bar'].sort, @beanstalk.tubes.watched.map(&:name).sort
105
+ end
106
+ end # watch!
107
+
108
+ describe "for #ignore" do
109
+ before do
110
+ @beanstalk = Tuber.new(tuber_address)
111
+ end
112
+
113
+ it 'should ignore specified tubes' do
114
+ @beanstalk.tubes.watch('foo')
115
+ @beanstalk.tubes.watch('bar')
116
+ @beanstalk.tubes.ignore('foo')
117
+ assert_equal ['default', 'bar'].sort, @beanstalk.tubes.watched.map(&:name).sort
118
+ end
119
+ end # ignore
120
+
121
+ describe "for #watch with weight" do
122
+ before do
123
+ @beanstalk = stub
124
+ @connection = stub(tubes_watched: ['default'])
125
+ @beanstalk.stubs(:connection).returns(@connection)
126
+ @tubes = Tuber::Tubes.new(@beanstalk)
127
+ end
128
+
129
+ it 'should send watch command with weight' do
130
+ @connection.expects(:transmit).with("watch foo 4").returns({status: "WATCHING", id: "2"})
131
+ @connection.expects(:add_to_watched).with("foo")
132
+ @tubes.watch('foo', weight: 4)
133
+ end
134
+
135
+ it 'should send watch command without weight by default' do
136
+ @connection.expects(:transmit).with("watch foo").returns({status: "WATCHING", id: "2"})
137
+ @connection.expects(:add_to_watched).with("foo")
138
+ @tubes.watch('foo')
139
+ end
140
+ end # watch with weight
141
+
142
+ describe "for #reserve_mode" do
143
+ before do
144
+ @beanstalk = stub
145
+ @connection = stub
146
+ @beanstalk.stubs(:connection).returns(@connection)
147
+ @tubes = Tuber::Tubes.new(@beanstalk)
148
+ end
149
+
150
+ it 'should send reserve-mode weighted command' do
151
+ @connection.expects(:transmit).with("reserve-mode weighted").returns({status: "USING", id: "weighted"})
152
+ @tubes.reserve_mode(:weighted)
153
+ end
154
+
155
+ it 'should send reserve-mode fifo command' do
156
+ @connection.expects(:transmit).with("reserve-mode fifo").returns({status: "USING", id: "fifo"})
157
+ @tubes.reserve_mode(:fifo)
158
+ end
159
+ end # reserve_mode
160
+
161
+ describe "for #reserve" do
162
+ before do
163
+ @beanstalk = Tuber.new(tuber_address)
164
+ @tube = @beanstalk.tubes.find 'tube'
165
+ @time = Time.now.to_i
166
+ @tube.put "foo reserve #{@time}"
167
+ end
168
+
169
+ it("should reserve job") do
170
+ @beanstalk.tubes.watch 'tube'
171
+ job = @beanstalk.tubes.reserve
172
+ assert_equal "foo reserve #{@time}", job.body
173
+ job.delete
174
+ end
175
+
176
+ it("should reserve job with block") do
177
+ @beanstalk.tubes.watch 'tube'
178
+ job = nil
179
+ @beanstalk.tubes.reserve { |j| job = j; job.delete }
180
+ assert_equal "foo reserve #{@time}", job.body
181
+ end
182
+
183
+ it("should reserve job with block and timeout") do
184
+ @beanstalk.tubes.watch 'tube'
185
+ job = nil
186
+ @beanstalk.tubes.reserve(0) { |j| job = j; job.delete }
187
+ assert_equal "foo reserve #{@time}", job.body
188
+ end
189
+
190
+ it "should raise TimedOutError with timeout" do
191
+ @beanstalk.tubes.watch 'tube'
192
+ @beanstalk.tubes.reserve(0) { |j| job = j; job.delete }
193
+ assert_raises(Tuber::TimedOutError) { @beanstalk.tubes.reserve(0) }
194
+ end
195
+
196
+ it "should raise DeadlineSoonError with ttr 1" do
197
+ @tube.reserve.delete
198
+ @tube.put "foo reserve #{@time}", :ttr => 1
199
+ @beanstalk.tubes.watch 'tube'
200
+ @beanstalk.tubes.reserve
201
+ assert_raises(Tuber::DeadlineSoonError) { @beanstalk.tubes.reserve(0) }
202
+ end
203
+
204
+ end # reserve
205
+
206
+ describe "for #reserve_batch" do
207
+ before do
208
+ require_tuber!
209
+ @beanstalk = Tuber.new(tuber_address)
210
+ @tube = @beanstalk.tubes.find 'batch_tube'
211
+ end
212
+
213
+ it "should return empty array on empty queue" do
214
+ @beanstalk.tubes.watch! 'batch_tube'
215
+ jobs = @beanstalk.tubes.reserve_batch(5)
216
+ assert_equal 0, jobs.size
217
+ end
218
+
219
+ it "should reserve 1 job" do
220
+ @tube.put "batch job 1"
221
+ @beanstalk.tubes.watch! 'batch_tube'
222
+ jobs = @beanstalk.tubes.reserve_batch(1)
223
+ assert_equal 1, jobs.size
224
+ assert_kind_of Tuber::Job, jobs.first
225
+ assert_equal "batch job 1", jobs.first.body
226
+ jobs.each(&:delete)
227
+ end
228
+
229
+ it "should reserve N jobs" do
230
+ 3.times { |i| @tube.put "batch job #{i}" }
231
+ @beanstalk.tubes.watch! 'batch_tube'
232
+ jobs = @beanstalk.tubes.reserve_batch(3)
233
+ assert_equal 3, jobs.size
234
+ jobs.each do |job|
235
+ assert_kind_of Tuber::Job, job
236
+ job.delete
237
+ end
238
+ end
239
+
240
+ it "should return partial results when fewer available than requested" do
241
+ 2.times { |i| @tube.put "partial job #{i}" }
242
+ @beanstalk.tubes.watch! 'batch_tube'
243
+ jobs = @beanstalk.tubes.reserve_batch(10)
244
+ assert_equal 2, jobs.size
245
+ jobs.each(&:delete)
246
+ end
247
+
248
+ it "should return deletable jobs" do
249
+ @tube.put "deletable job"
250
+ @beanstalk.tubes.watch! 'batch_tube'
251
+ jobs = @beanstalk.tubes.reserve_batch(1)
252
+ assert_equal 1, jobs.size
253
+ jobs.first.delete
254
+ assert_raises(Tuber::NotFoundError) { jobs.first.stats }
255
+ end
256
+
257
+ it "should return empty array when long-poll times out" do
258
+ @beanstalk.tubes.watch! 'batch_tube'
259
+ jobs = @beanstalk.tubes.reserve_batch(5, 1)
260
+ assert_equal 0, jobs.size
261
+ end
262
+
263
+ it "should drain ready jobs with a positive timeout" do
264
+ 2.times { |i| @tube.put "timeout job #{i}" }
265
+ @beanstalk.tubes.watch! 'batch_tube'
266
+ jobs = @beanstalk.tubes.reserve_batch(10, 1)
267
+ assert_equal 2, jobs.size
268
+ jobs.each(&:delete)
269
+ end
270
+
271
+ it "should block until a job arrives within the timeout" do
272
+ @beanstalk.tubes.watch! 'batch_tube'
273
+ Thread.new do
274
+ sleep 0.2
275
+ producer = Tuber.new(tuber_address)
276
+ producer.tubes.find('batch_tube').put "delayed job"
277
+ producer.close
278
+ end
279
+ jobs = @beanstalk.tubes.reserve_batch(5, 3)
280
+ assert_equal 1, jobs.size
281
+ assert_equal "delayed job", jobs.first.body
282
+ jobs.each(&:delete)
283
+ end
284
+ end # reserve_batch
285
+ describe "for #reserve_job" do
286
+ before do
287
+ @beanstalk = Tuber.new(tuber_address)
288
+ @tube = @beanstalk.tubes.find 'reserve_job_tube'
289
+ @time = Time.now.to_i
290
+ @tube.put "reserve_job test #{@time}"
291
+ end
292
+
293
+ it "should reserve a specific job by id" do
294
+ job_id = @tube.peek(:ready).id
295
+ job = @beanstalk.tubes.reserve_job(job_id)
296
+ assert_kind_of Tuber::Job, job
297
+ assert_equal job_id, job.id
298
+ assert_equal "reserve_job test #{@time}", job.body
299
+ job.delete
300
+ end
301
+
302
+ it "should return nil for non-existent job" do
303
+ result = @beanstalk.tubes.reserve_job(999999999)
304
+ assert_nil result
305
+ end
306
+ end # reserve_job
307
+
308
+ describe "for #stats_group" do
309
+ before do
310
+ require_tuber!
311
+ @beanstalk = Tuber.new(tuber_address)
312
+ @tube = @beanstalk.tubes.find 'stats_group_tube'
313
+ @group_name = "test_group_#{Time.now.to_i}"
314
+ 2.times { @tube.put "group job", grp: @group_name }
315
+ end
316
+
317
+ it "should return stats for a group" do
318
+ stats = @beanstalk.tubes.stats_group(@group_name)
319
+ assert_equal @group_name, stats.name
320
+ assert_equal 2, stats.ready
321
+ assert_equal 0, stats.reserved
322
+ assert_equal 0, stats.buried
323
+ end
324
+
325
+ it "should raise NotFoundError for non-existent group" do
326
+ assert_raises(Tuber::NotFoundError) { @beanstalk.tubes.stats_group("nonexistent_group_xyz") }
327
+ end
328
+ end # stats_group
329
+ end # Tuber::Tubes
data/tuber.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ # -*- encoding: utf-8 -*-
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'tuber/version'
6
+
7
+ Gem::Specification.new do |gem|
8
+ gem.name = "tuber"
9
+ gem.version = Tuber::VERSION
10
+ gem.authors = ["Dan Milne", "Nico Taing"]
11
+ gem.email = ["d@nmilne.com"]
12
+ gem.description = %q{The Ruby client for the Tuber job queue server: unique jobs, concurrency keys, job group pipelines, batch operations and weighted tubes. Also fully compatible with beanstalkd.}
13
+ gem.summary = %q{Ruby client for the Tuber job queue server.}
14
+ gem.homepage = "https://github.com/tuberq/tuber-gem"
15
+ gem.license = 'MIT'
16
+
17
+ gem.required_ruby_version = '>= 2.7.0'
18
+
19
+ gem.metadata = {
20
+ 'source_code_uri' => gem.homepage,
21
+ 'changelog_uri' => "#{gem.homepage}/blob/main/CHANGELOG.md",
22
+ 'bug_tracker_uri' => "#{gem.homepage}/issues",
23
+ }
24
+
25
+ gem.files = `git ls-files`.split($/)
26
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
27
+ gem.require_paths = ["lib"]
28
+ gem.add_development_dependency 'minitest', "~> 4.1.0"
29
+ gem.add_development_dependency 'rake'
30
+ gem.add_development_dependency 'mocha'
31
+ gem.add_development_dependency 'term-ansicolor'
32
+ gem.add_development_dependency 'json'
33
+ end
metadata CHANGED
@@ -1,30 +1,136 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tuber
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Milne
8
+ - Nico Taing
8
9
  bindir: bin
9
10
  cert_chain: []
10
11
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
12
- description: Ruby client for Tuber, a fast beanstalkd-compatible job queue server.
13
- This release reserves the gem name; use the russet gem for a full-featured client
14
- in the meantime.
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.1.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mocha
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: term-ansicolor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: json
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: 'The Ruby client for the Tuber job queue server: unique jobs, concurrency
84
+ keys, job group pipelines, batch operations and weighted tubes. Also fully compatible
85
+ with beanstalkd.'
15
86
  email:
16
87
  - d@nmilne.com
17
88
  executables: []
18
89
  extensions: []
19
90
  extra_rdoc_files: []
20
91
  files:
92
+ - ".github/workflows/ruby.yml"
93
+ - ".gitignore"
94
+ - ".yardopts"
95
+ - CHANGELOG.md
96
+ - Gemfile
97
+ - LICENSE.txt
21
98
  - README.md
99
+ - Rakefile
100
+ - TODO
101
+ - examples/demo.rb
22
102
  - lib/tuber.rb
103
+ - lib/tuber/configuration.rb
104
+ - lib/tuber/connection.rb
105
+ - lib/tuber/errors.rb
106
+ - lib/tuber/job.rb
107
+ - lib/tuber/job/collection.rb
108
+ - lib/tuber/job/record.rb
109
+ - lib/tuber/stats.rb
110
+ - lib/tuber/stats/fast_struct.rb
111
+ - lib/tuber/stats/stat_struct.rb
112
+ - lib/tuber/tube.rb
113
+ - lib/tuber/tube/collection.rb
114
+ - lib/tuber/tube/record.rb
115
+ - lib/tuber/version.rb
116
+ - test/connection_test.rb
117
+ - test/errors_test.rb
118
+ - test/job_test.rb
119
+ - test/jobs_test.rb
120
+ - test/prompt_regexp_test.rb
121
+ - test/stat_struct_test.rb
122
+ - test/stats_test.rb
123
+ - test/test_helper.rb
124
+ - test/tube_test.rb
125
+ - test/tuber_test.rb
126
+ - test/tubes_test.rb
127
+ - tuber.gemspec
23
128
  homepage: https://github.com/tuberq/tuber-gem
24
129
  licenses:
25
130
  - MIT
26
131
  metadata:
27
132
  source_code_uri: https://github.com/tuberq/tuber-gem
133
+ changelog_uri: https://github.com/tuberq/tuber-gem/blob/main/CHANGELOG.md
28
134
  bug_tracker_uri: https://github.com/tuberq/tuber-gem/issues
29
135
  rdoc_options: []
30
136
  require_paths:
@@ -42,5 +148,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
42
148
  requirements: []
43
149
  rubygems_version: 4.0.16
44
150
  specification_version: 4
45
- summary: Ruby client for the Tuber job queue server
151
+ summary: Ruby client for the Tuber job queue server.
46
152
  test_files: []