progressrus 0.1.8 → 1.0.3
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 +4 -4
- data/.github/workflows/ci.yml +38 -0
- data/lib/progressrus/core_ext/enumerable.rb +2 -2
- data/lib/progressrus/store/redis.rb +5 -5
- data/lib/progressrus/version.rb +1 -1
- data/lib/progressrus.rb +69 -12
- data/tasks/redis_store.rake +1 -1
- data/test/integration_test.rb +3 -3
- data/test/progressrus_test.rb +66 -36
- metadata +7 -8
- data/.travis.yml +0 -7
- data/lib/progressrus/store.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d353cca4bf4eca46acd2837f9e62f6dbe35834cd3efe00bf66ece1c02a2924d0
|
4
|
+
data.tar.gz: 2b8c0dc2e6ec18c4f22b3a911bedfbcd73232517c4576dcc4971c46fc7abaabb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1b1f0472d3ed65a214a020433315f7dd9cc37618fe992125e9a8836b320e0b92cc3adb5dca28f9109f15bb670abf6392a9493df3e7bd5921b9206f78e9d0b3c
|
7
|
+
data.tar.gz: 9823e1025dff167ffc79e33d12cc636fd5494a23cf5c97500fb655eb69a5aabbb0df45518ed8928f11ac41cf8b62f9f0f196d125cde2f3f98b0e5e3980206e21
|
@@ -0,0 +1,38 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
|
13
|
+
strategy:
|
14
|
+
fail-fast: false
|
15
|
+
matrix:
|
16
|
+
ruby: ["2.6", "2.7", "3.0", "3.1"]
|
17
|
+
services:
|
18
|
+
redis:
|
19
|
+
image: redis
|
20
|
+
options: >-
|
21
|
+
--health-cmd "redis-cli ping"
|
22
|
+
--health-interval 10s
|
23
|
+
--health-timeout 5s
|
24
|
+
--health-retries 5
|
25
|
+
ports:
|
26
|
+
- 6379:6379
|
27
|
+
|
28
|
+
steps:
|
29
|
+
- uses: actions/checkout@v2
|
30
|
+
- name: Set up Ruby
|
31
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
32
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
33
|
+
uses: ruby/setup-ruby@v1
|
34
|
+
with:
|
35
|
+
bundler-cache: true # 'bundle install' and cache gems
|
36
|
+
ruby-version: ${{ matrix.ruby }}
|
37
|
+
- name: Run tests
|
38
|
+
run: bundle exec rake
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Enumerable
|
2
|
-
def with_progress(
|
2
|
+
def with_progress(args = {}, &block)
|
3
3
|
if block_given?
|
4
4
|
progresser = progress(args)
|
5
5
|
begin
|
@@ -25,7 +25,7 @@ module Enumerable
|
|
25
25
|
# Lazily read the size, for some enumerable this may be quite expensive and
|
26
26
|
# using this method should come with a warning in the documentation.
|
27
27
|
total = self.size unless args[:total]
|
28
|
-
@progress = Progressrus.new(
|
28
|
+
@progress = Progressrus.new(total: total, **args)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -17,9 +17,9 @@ class Progressrus
|
|
17
17
|
if outdated?(progress) || force
|
18
18
|
key_for_scope = key(progress.scope)
|
19
19
|
|
20
|
-
redis.pipelined do
|
21
|
-
|
22
|
-
|
20
|
+
redis.pipelined do |pipeline|
|
21
|
+
pipeline.hset(key_for_scope, progress.id, progress.to_serializeable.to_json)
|
22
|
+
pipeline.expireat(key_for_scope, expires_at.to_i) if expires_at
|
23
23
|
end
|
24
24
|
|
25
25
|
@persisted_ats[progress.scope][progress.id] = now
|
@@ -31,7 +31,7 @@ class Progressrus
|
|
31
31
|
def scope(scope)
|
32
32
|
scope = redis.hgetall(key(scope))
|
33
33
|
scope.each_pair { |id, value|
|
34
|
-
scope[id] = Progressrus.new(deserialize(value))
|
34
|
+
scope[id] = Progressrus.new(**deserialize(value))
|
35
35
|
}
|
36
36
|
rescue *BACKEND_EXCEPTIONS => e
|
37
37
|
raise Progressrus::Store::BackendError.new(e)
|
@@ -41,7 +41,7 @@ class Progressrus
|
|
41
41
|
value = redis.hget(key(scope), id)
|
42
42
|
return unless value
|
43
43
|
|
44
|
-
Progressrus.new(deserialize(value))
|
44
|
+
Progressrus.new(**deserialize(value))
|
45
45
|
rescue *BACKEND_EXCEPTIONS => e
|
46
46
|
raise Progressrus::Store::BackendError.new(e)
|
47
47
|
end
|
data/lib/progressrus/version.rb
CHANGED
data/lib/progressrus.rb
CHANGED
@@ -2,29 +2,75 @@ require 'json'
|
|
2
2
|
require 'securerandom'
|
3
3
|
require 'redis'
|
4
4
|
require 'time'
|
5
|
-
require_relative "progressrus/store"
|
6
5
|
require_relative "progressrus/store/base"
|
7
6
|
require_relative "progressrus/store/redis"
|
8
7
|
require_relative "progressrus/store/progressbar"
|
9
8
|
require_relative "progressrus/core_ext/enumerable"
|
10
9
|
|
11
10
|
class Progressrus
|
11
|
+
@mutex = Mutex.new
|
12
|
+
|
13
|
+
class InvalidStoreError < StandardError
|
14
|
+
def initialize
|
15
|
+
message = <<~MSG
|
16
|
+
The store needs to implement `persist`, `scope`, `find` and `flush`
|
17
|
+
We have a base class that your store can inherit from:
|
18
|
+
Progressrus::Store::Base
|
19
|
+
MSG
|
20
|
+
super(message)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class StoreNotFoundError < StandardError
|
25
|
+
def initialize(store)
|
26
|
+
message = <<~MSG
|
27
|
+
The store `#{store}` does not exists.
|
28
|
+
Available stores: #{Progressrus.stores.keys.join(', ')}
|
29
|
+
MSG
|
30
|
+
super(message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
12
34
|
class << self
|
35
|
+
attr_reader :mutex
|
36
|
+
|
37
|
+
def clear_stores
|
38
|
+
@stores = {}
|
39
|
+
end
|
40
|
+
|
13
41
|
def stores
|
14
|
-
|
42
|
+
mutex.synchronize do
|
43
|
+
@stores ||= {
|
44
|
+
redis: Store::Redis.new(::Redis.new(host: ENV["PROGRESSRUS_REDIS_HOST"] || "localhost"))
|
45
|
+
}
|
46
|
+
end
|
15
47
|
end
|
16
48
|
|
17
|
-
def
|
18
|
-
|
49
|
+
def add_store(name, store)
|
50
|
+
validate_store!(store)
|
51
|
+
stores[name] = store
|
52
|
+
end
|
53
|
+
|
54
|
+
def scope(scope, store: :redis)
|
55
|
+
stores[store].scope(scope)
|
19
56
|
end
|
20
57
|
alias_method :all, :scope
|
21
58
|
|
22
|
-
def find(scope, id, store: :
|
23
|
-
stores
|
59
|
+
def find(scope, id, store: :redis)
|
60
|
+
stores[store].find(scope, id)
|
61
|
+
end
|
62
|
+
|
63
|
+
def flush(scope, id = nil, store: :redis)
|
64
|
+
stores[store].flush(scope, id)
|
24
65
|
end
|
25
66
|
|
26
|
-
|
27
|
-
|
67
|
+
private
|
68
|
+
|
69
|
+
def validate_store!(store)
|
70
|
+
valid = Store::Base.new.public_methods(false).all? do |method|
|
71
|
+
store.respond_to?(method)
|
72
|
+
end
|
73
|
+
raise InvalidStoreError unless valid
|
28
74
|
end
|
29
75
|
end
|
30
76
|
|
@@ -38,7 +84,7 @@ class Progressrus
|
|
38
84
|
attr_writer :params
|
39
85
|
|
40
86
|
def initialize(scope: "progressrus", total: nil, name: nil,
|
41
|
-
id: SecureRandom.uuid, params: {}, stores:
|
87
|
+
id: SecureRandom.uuid, params: {}, stores: nil,
|
42
88
|
completed_at: nil, started_at: nil, count: 0, failed_at: nil,
|
43
89
|
error_count: 0, persist: false, expires_at: nil, persisted: false)
|
44
90
|
|
@@ -49,7 +95,7 @@ class Progressrus
|
|
49
95
|
@total = total
|
50
96
|
@id = id
|
51
97
|
@params = params
|
52
|
-
@stores = stores
|
98
|
+
@stores = extract_stores(stores)
|
53
99
|
@count = count
|
54
100
|
@error_count = error_count
|
55
101
|
|
@@ -84,7 +130,7 @@ class Progressrus
|
|
84
130
|
end
|
85
131
|
|
86
132
|
def flush
|
87
|
-
stores.
|
133
|
+
stores.each_value { |store| store.flush(scope, id) }
|
88
134
|
end
|
89
135
|
|
90
136
|
def status
|
@@ -160,8 +206,19 @@ class Progressrus
|
|
160
206
|
|
161
207
|
private
|
162
208
|
|
209
|
+
def extract_stores(stores)
|
210
|
+
return Progressrus.stores unless stores
|
211
|
+
|
212
|
+
Array(stores).each_with_object({}) do |store, hash|
|
213
|
+
stored_found = Progressrus.stores[store]
|
214
|
+
raise StoreNotFoundError, store unless stored_found
|
215
|
+
|
216
|
+
hash[store] = stored_found
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
163
220
|
def persist(force: false)
|
164
|
-
stores.
|
221
|
+
stores.each_value do |store|
|
165
222
|
begin
|
166
223
|
store.persist(self, force: force, expires_at: expires_at)
|
167
224
|
rescue Progressrus::Store::BackendError
|
data/tasks/redis_store.rake
CHANGED
@@ -7,7 +7,7 @@ namespace :progressrus do
|
|
7
7
|
task :flush, :environment do |t, args|
|
8
8
|
scope = *args
|
9
9
|
raise ArgumentError.new("Must pass [scope] to progressrus:store:flush[scope(,parts)] task.") unless scope.length > 0
|
10
|
-
Progressrus.stores.
|
10
|
+
Progressrus.stores[:redis].flush(scope) if Progressrus.stores[:redis]
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
data/test/integration_test.rb
CHANGED
@@ -6,7 +6,7 @@ class IntegrationTest < Minitest::Test
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def teardown
|
9
|
-
Progressrus.stores.
|
9
|
+
Progressrus.stores[:redis].flush(@progress.scope)
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_create_tick_and_see_status_in_redis
|
@@ -44,7 +44,7 @@ class IntegrationTest < Minitest::Test
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def test_tick_on_enumerable
|
47
|
-
a = (0..10)
|
47
|
+
a = (0..10).to_a
|
48
48
|
b = a.with_progress(scope: "walrus").map(&:to_i)
|
49
49
|
|
50
50
|
ticks = Progressrus.scope(["walrus"]).values
|
@@ -57,7 +57,7 @@ class IntegrationTest < Minitest::Test
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def test_tick_on_enumerable_calls_fail_on_exception
|
60
|
-
a = (0..10)
|
60
|
+
a = (0..10).to_a
|
61
61
|
|
62
62
|
assert_raises ArgumentError do
|
63
63
|
a.with_progress(scope: "walrus").each do
|
data/test/progressrus_test.rb
CHANGED
@@ -6,16 +6,23 @@ class ProgressrusTest < Minitest::Test
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def teardown
|
9
|
-
Progressrus.
|
9
|
+
Progressrus.clear_stores
|
10
|
+
Progressrus.add_store(:redis, Progressrus::Store::Redis.new(::Redis.new(host: ENV["PROGRESSRUS_REDIS_HOST"] || "localhost")))
|
10
11
|
end
|
11
12
|
|
12
13
|
def test_defaults_to_redis_store
|
13
|
-
assert_instance_of Progressrus::Store::Redis, Progressrus.stores
|
14
|
+
assert_instance_of Progressrus::Store::Redis, Progressrus.stores[:redis]
|
14
15
|
end
|
15
16
|
|
16
17
|
def test_add_to_store
|
17
|
-
Progressrus.
|
18
|
-
assert_instance_of Progressrus::Store::Base, Progressrus.stores[
|
18
|
+
Progressrus.add_store(:test, Progressrus::Store::Base.new)
|
19
|
+
assert_instance_of Progressrus::Store::Base, Progressrus.stores[:test]
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_add_store_raise_error_when_invalid_store
|
23
|
+
assert_raises(Progressrus::InvalidStoreError) do
|
24
|
+
Progressrus.add_store(:test, mock)
|
25
|
+
end
|
19
26
|
end
|
20
27
|
|
21
28
|
def test_scope_should_initialize_with_symbol_or_string
|
@@ -33,6 +40,37 @@ class ProgressrusTest < Minitest::Test
|
|
33
40
|
assert_equal 'Wally', progressrus.name
|
34
41
|
end
|
35
42
|
|
43
|
+
def test_not_passing_stores_default_to_all_stores
|
44
|
+
progressrus = Progressrus.new(scope: ['walruses', 1])
|
45
|
+
assert_equal Progressrus.stores, progressrus.stores
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_passing_stores
|
49
|
+
mysql = mock_store
|
50
|
+
Progressrus.add_store(:mysql, mysql)
|
51
|
+
progressrus = Progressrus.new(scope: ['walruses', 1], stores: :mysql)
|
52
|
+
assert_equal({ mysql: mysql }, progressrus.stores)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_passing_multiple_stores
|
56
|
+
Progressrus.clear_stores
|
57
|
+
|
58
|
+
mysql = mock_store
|
59
|
+
redis = mock_store
|
60
|
+
|
61
|
+
Progressrus.add_store(:mysql, mysql)
|
62
|
+
Progressrus.add_store(:redis, redis)
|
63
|
+
|
64
|
+
progressrus = Progressrus.new(scope: ['walruses', 1], stores: %i(mysql redis))
|
65
|
+
assert_equal({ mysql: mysql, redis: redis }, progressrus.stores)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_passing_non_existing_store_raises_error
|
69
|
+
assert_raises(Progressrus::StoreNotFoundError) do
|
70
|
+
Progressrus.new(scope: ['walruses', 1], stores: :not_found)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
36
74
|
def test_initialize_without_name_should_use_id
|
37
75
|
progressrus = Progressrus.new(id: 'oemg')
|
38
76
|
assert_equal 'oemg', progressrus.name
|
@@ -196,19 +234,20 @@ class ProgressrusTest < Minitest::Test
|
|
196
234
|
end
|
197
235
|
|
198
236
|
def test_persist_yields_persist_to_each_store
|
199
|
-
mysql =
|
237
|
+
mysql = mock_store
|
200
238
|
mysql.expects(:persist).once
|
201
239
|
|
202
|
-
redis = Progressrus.stores
|
240
|
+
redis = Progressrus.stores[:redis]
|
203
241
|
redis.expects(:persist).once
|
204
242
|
|
205
|
-
Progressrus.
|
243
|
+
Progressrus.add_store(:mysql, mysql)
|
206
244
|
|
207
|
-
|
245
|
+
progress = Progressrus.new(scope: :progressrus, total: 100)
|
246
|
+
progress.tick
|
208
247
|
end
|
209
248
|
|
210
249
|
def test_tick_and_complete_dont_raise_if_store_is_unavailable
|
211
|
-
store = Progressrus.stores
|
250
|
+
store = Progressrus.stores[:redis]
|
212
251
|
store.redis.expects(:hset).at_least_once.raises(::Redis::BaseError)
|
213
252
|
@progress.tick
|
214
253
|
@progress.complete
|
@@ -234,47 +273,32 @@ class ProgressrusTest < Minitest::Test
|
|
234
273
|
end
|
235
274
|
|
236
275
|
|
237
|
-
def
|
238
|
-
Progressrus.
|
239
|
-
|
240
|
-
mysql = mock()
|
241
|
-
redis = mock()
|
242
|
-
|
243
|
-
Progressrus.stores << mysql
|
244
|
-
Progressrus.stores << redis
|
245
|
-
|
246
|
-
mysql.expects(:scope).once
|
247
|
-
redis.expects(:scope).never
|
248
|
-
|
249
|
-
Progressrus.scope(["oemg"])
|
250
|
-
end
|
276
|
+
def test_default_scope_redis_store
|
277
|
+
Progressrus.clear_stores
|
251
278
|
|
252
|
-
|
253
|
-
|
279
|
+
mysql = mock_store
|
280
|
+
redis = mock_store
|
254
281
|
|
255
|
-
mysql
|
256
|
-
redis
|
257
|
-
|
258
|
-
Progressrus.stores << mysql
|
259
|
-
Progressrus.stores << redis
|
282
|
+
Progressrus.add_store(:mysql, mysql)
|
283
|
+
Progressrus.add_store(:redis, redis)
|
260
284
|
|
261
285
|
mysql.expects(:scope).never
|
262
286
|
redis.expects(:scope).once
|
263
287
|
|
264
|
-
Progressrus.scope(["oemg"]
|
288
|
+
Progressrus.scope(["oemg"])
|
265
289
|
end
|
266
290
|
|
267
291
|
def test_support_scope_by_name
|
268
|
-
Progressrus.
|
292
|
+
Progressrus.clear_stores
|
269
293
|
|
270
|
-
mysql =
|
271
|
-
redis =
|
294
|
+
mysql = mock_store
|
295
|
+
redis = mock_store
|
272
296
|
|
273
297
|
mysql.stubs(:name).returns(:mysql)
|
274
298
|
redis.stubs(:name).returns(:redis)
|
275
299
|
|
276
|
-
Progressrus.
|
277
|
-
Progressrus.
|
300
|
+
Progressrus.add_store(:mysql, mysql)
|
301
|
+
Progressrus.add_store(:redis, redis)
|
278
302
|
|
279
303
|
mysql.expects(:scope).never
|
280
304
|
redis.expects(:scope).once
|
@@ -427,4 +451,10 @@ class ProgressrusTest < Minitest::Test
|
|
427
451
|
|
428
452
|
refute progress.expired?(now: time)
|
429
453
|
end
|
454
|
+
|
455
|
+
private
|
456
|
+
|
457
|
+
def mock_store
|
458
|
+
Progressrus::Store::Base.new
|
459
|
+
end
|
430
460
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: progressrus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Eskildsen
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -129,8 +129,8 @@ executables: []
|
|
129
129
|
extensions: []
|
130
130
|
extra_rdoc_files: []
|
131
131
|
files:
|
132
|
+
- ".github/workflows/ci.yml"
|
132
133
|
- ".gitignore"
|
133
|
-
- ".travis.yml"
|
134
134
|
- Gemfile
|
135
135
|
- LICENSE.txt
|
136
136
|
- README.md
|
@@ -139,7 +139,6 @@ files:
|
|
139
139
|
- lib/progressrus/core_ext/enumerable.rb
|
140
140
|
- lib/progressrus/railtie.rb
|
141
141
|
- lib/progressrus/server.rb
|
142
|
-
- lib/progressrus/store.rb
|
143
142
|
- lib/progressrus/store/base.rb
|
144
143
|
- lib/progressrus/store/progressbar.rb
|
145
144
|
- lib/progressrus/store/redis.rb
|
@@ -156,7 +155,7 @@ homepage: https://github.com/Sirupsen/progressrus
|
|
156
155
|
licenses:
|
157
156
|
- MIT
|
158
157
|
metadata: {}
|
159
|
-
post_install_message:
|
158
|
+
post_install_message:
|
160
159
|
rdoc_options: []
|
161
160
|
require_paths:
|
162
161
|
- lib
|
@@ -171,8 +170,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
171
170
|
- !ruby/object:Gem::Version
|
172
171
|
version: '0'
|
173
172
|
requirements: []
|
174
|
-
rubygems_version: 3.
|
175
|
-
signing_key:
|
173
|
+
rubygems_version: 3.3.3
|
174
|
+
signing_key:
|
176
175
|
specification_version: 4
|
177
176
|
summary: Monitor the progress of remote, long-running jobs.
|
178
177
|
test_files:
|
data/.travis.yml
DELETED
data/lib/progressrus/store.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
class Progressrus
|
2
|
-
class Store < Array
|
3
|
-
def initialize(default)
|
4
|
-
@default = default
|
5
|
-
self << default
|
6
|
-
end
|
7
|
-
|
8
|
-
def default
|
9
|
-
@default
|
10
|
-
end
|
11
|
-
|
12
|
-
def default!
|
13
|
-
clear
|
14
|
-
self << default
|
15
|
-
end
|
16
|
-
|
17
|
-
def find_by_name(name)
|
18
|
-
return first if name == :first
|
19
|
-
return last if name == :last
|
20
|
-
|
21
|
-
find { |store| store.name == name }
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|