progressrus 0.1.6 → 1.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a654e74220bfd54eb96f2c18390e8fcc794ae937
4
- data.tar.gz: db38bb7f163a7ef1d2cca42fde38a644464e0316
2
+ SHA256:
3
+ metadata.gz: 564bda7bfb2cfd735379ef412a908303655e1fe2853cd666d6d6fcbcefca5a6b
4
+ data.tar.gz: 0acaae1e3e6cdd2d69be60b27dfbe5d5185af7f7d6677b5ad0e190918cbab43b
5
5
  SHA512:
6
- metadata.gz: 80adef7173fa11523e3ea323333d8b4dd50cfdc9b88c41dc85ba718b9c2b5f0e25a97086f9e0e552e9b1878ac4c0e07aefd20e8d9370ca7ffe0f776d7d78a5b1
7
- data.tar.gz: 2a4bdbef69f9f31d8675f922ff73d02325c1732d8fb24823b12b6765ca1451cb6f0176af7c45e08b817663d1fa65e613951300480a88941f2818fb045863cc08
6
+ metadata.gz: 417d841b14a93351ed90ff754706467c42e29663bd076d55b9d7e23bca8ceef7989095f8eab1ce14cfc97caf51ed9497079f79498c775a6f06a4a20358c7417a
7
+ data.tar.gz: 514c8bcf9fb63500629630255861d590c35cbbdb5ab05c4798a37d30a32ffe51b4a8c59d11cb3f53ccc5d86aafdb52bab48c0c23972596e2a6f96208b9eb557d
@@ -2,5 +2,7 @@ language: ruby
2
2
  services:
3
3
  - redis-server
4
4
  rvm:
5
- - 2.0.0
6
- - 2.1.1
5
+ - 2.4
6
+ - 2.5
7
+ - 2.6
8
+ - 2.7
@@ -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
- @@stores ||= Store.new(Store::Redis.new(::Redis.new(host: ENV["PROGRESSRUS_REDIS_HOST"] || "localhost")))
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 scope(scope, store: :first)
18
- stores.find_by_name(store).scope(scope)
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: :first)
23
- stores.find_by_name(store).find(scope, id)
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
- def flush(scope, id = nil, store: :first)
27
- stores.find_by_name(store).flush(scope, id)
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: Progressrus.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.each { |store| store.flush(scope, id) }
133
+ stores.each_value { |store| store.flush(scope, id) }
88
134
  end
89
135
 
90
136
  def status
@@ -160,11 +206,23 @@ 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.each do |store|
221
+ stores.each_value do |store|
165
222
  begin
166
223
  store.persist(self, force: force, expires_at: expires_at)
167
- rescue Progressrus::Store::BackendError => e
224
+ rescue Progressrus::Store::BackendError
225
+ # noop
168
226
  end
169
227
  end
170
228
  @persisted = true
@@ -1,5 +1,5 @@
1
1
  module Enumerable
2
- def with_progress(**args, &block)
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({total: total}.merge(args))
28
+ @progress = Progressrus.new(total: total, **args)
29
29
  end
30
30
  end
31
31
  end
@@ -3,7 +3,7 @@ class Progressrus
3
3
  class Redis < Base
4
4
  BACKEND_EXCEPTIONS = [ ::Redis::BaseError ]
5
5
 
6
- attr_reader :redis, :interval, :persisted_at, :prefix, :name
6
+ attr_reader :redis, :interval, :prefix, :name
7
7
 
8
8
  def initialize(instance, prefix: "progressrus", interval: 1, now: Time.now)
9
9
  @name = :redis
@@ -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
@@ -59,7 +59,11 @@ class Progressrus
59
59
  private
60
60
 
61
61
  def key(scope)
62
- "#{prefix}:#{scope.join(":")}"
62
+ if prefix.respond_to?(:call)
63
+ prefix.call(scope)
64
+ else
65
+ "#{prefix}:#{scope.join(":")}"
66
+ end
63
67
  end
64
68
 
65
69
  def deserialize(value)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Progressrus
2
- VERSION = "0.1.6"
4
+ VERSION = '1.0.2'
3
5
  end
@@ -18,10 +18,9 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "redis", "~> 3.0"
21
+ spec.add_dependency "redis", ">= 3.0"
22
22
  spec.add_dependency "ruby-progressbar", "~> 1.0"
23
23
 
24
- spec.add_development_dependency "bundler", "~> 1.3"
25
24
  spec.add_development_dependency "rake"
26
25
  spec.add_development_dependency "mocha", "~> 0.14"
27
26
  spec.add_development_dependency "pry"
@@ -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.first.flush(scope) if Progressrus.stores.first
10
+ Progressrus.stores[:redis].flush(scope) if Progressrus.stores[:redis]
11
11
  end
12
12
  end
13
13
  end
@@ -6,7 +6,7 @@ class IntegrationTest < Minitest::Test
6
6
  end
7
7
 
8
8
  def teardown
9
- Progressrus.stores.first.flush(@progress.scope)
9
+ Progressrus.stores[:redis].flush(@progress.scope)
10
10
  end
11
11
 
12
12
  def test_create_tick_and_see_status_in_redis
@@ -6,16 +6,23 @@ class ProgressrusTest < Minitest::Test
6
6
  end
7
7
 
8
8
  def teardown
9
- Progressrus.stores.default!
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.first
14
+ assert_instance_of Progressrus::Store::Redis, Progressrus.stores[:redis]
14
15
  end
15
16
 
16
17
  def test_add_to_store
17
- Progressrus.stores << Progressrus::Store::Base.new
18
- assert_instance_of Progressrus::Store::Base, Progressrus.stores[1]
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 = mock()
237
+ mysql = mock_store
200
238
  mysql.expects(:persist).once
201
239
 
202
- redis = Progressrus.stores.first
240
+ redis = Progressrus.stores[:redis]
203
241
  redis.expects(:persist).once
204
242
 
205
- Progressrus.stores << mysql
243
+ Progressrus.add_store(:mysql, mysql)
206
244
 
207
- @progress.tick
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.first
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 test_default_scope_on_first
238
- Progressrus.stores.clear
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
- def test_support_scope_last
253
- Progressrus.stores.clear
279
+ mysql = mock_store
280
+ redis = mock_store
254
281
 
255
- mysql = mock()
256
- redis = mock()
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"], store: :last)
288
+ Progressrus.scope(["oemg"])
265
289
  end
266
290
 
267
291
  def test_support_scope_by_name
268
- Progressrus.stores.clear
292
+ Progressrus.clear_stores
269
293
 
270
- mysql = mock()
271
- redis = mock()
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.stores << mysql
277
- Progressrus.stores << redis
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
@@ -1,6 +1,11 @@
1
1
  require_relative "../test_helper"
2
2
 
3
3
  class RedisStoreTest < Minitest::Test
4
+ class PrefixClass
5
+ def self.call(scope)
6
+ "custom_prefix:#{scope.join(':')}"
7
+ end
8
+ end
4
9
  def setup
5
10
  @scope = ["walrus", "1234"]
6
11
  @progress = Progressrus.new(
@@ -17,11 +22,19 @@ class RedisStoreTest < Minitest::Test
17
22
  name: "oemg-name-two"
18
23
  )
19
24
 
20
- @store = Progressrus::Store::Redis.new(::Redis.new(host: ENV["PROGRESSRUS_REDIS_HOST"] || "localhost"))
25
+ @redis = ::Redis.new(host: ENV["PROGRESSRUS_REDIS_HOST"] || "localhost")
26
+
27
+ @store = Progressrus::Store::Redis.new(@redis)
21
28
  end
22
29
 
23
30
  def teardown
24
- @store.flush(@scope)
31
+ @store.flush(@scope)
32
+ end
33
+
34
+ def test_prefix_can_be_a_proc
35
+ store = Progressrus::Store::Redis.new(@redis, prefix: PrefixClass)
36
+ store.persist(@progress)
37
+ refute_empty(@redis.hgetall('custom_prefix:walrus:1234'))
25
38
  end
26
39
 
27
40
  def test_persist_should_set_key_value_if_outdated
@@ -99,5 +112,4 @@ class RedisStoreTest < Minitest::Test
99
112
  @store.persist(@progress)
100
113
  @store.persist(@progress, force: true)
101
114
  end
102
-
103
115
  end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: progressrus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Eskildsen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-30 00:00:00.000000000 Z
11
+ date: 2020-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '3.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.0'
27
27
  - !ruby/object:Gem::Dependency
@@ -38,20 +38,6 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.0'
41
- - !ruby/object:Gem::Dependency
42
- name: bundler
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '1.3'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '1.3'
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: rake
57
43
  requirement: !ruby/object:Gem::Requirement
@@ -153,7 +139,6 @@ files:
153
139
  - lib/progressrus/core_ext/enumerable.rb
154
140
  - lib/progressrus/railtie.rb
155
141
  - lib/progressrus/server.rb
156
- - lib/progressrus/store.rb
157
142
  - lib/progressrus/store/base.rb
158
143
  - lib/progressrus/store/progressbar.rb
159
144
  - lib/progressrus/store/redis.rb
@@ -185,8 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
170
  - !ruby/object:Gem::Version
186
171
  version: '0'
187
172
  requirements: []
188
- rubyforge_project:
189
- rubygems_version: 2.6.14
173
+ rubygems_version: 3.0.3
190
174
  signing_key:
191
175
  specification_version: 4
192
176
  summary: Monitor the progress of remote, long-running jobs.
@@ -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