always 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -5
- data/Gemfile.lock +4 -53
- data/always.gemspec +2 -1
- data/lib/always.rb +58 -7
- data/test/test_always.rb +31 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed19cd7a3109c141b0537f2229876cc00f5d0a70740095b810b281c47fe44834
|
4
|
+
data.tar.gz: 793763757135f8492edb8bb6b6abb6b9ee19459c7e35d3acde3ac13f0c97a230
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c09324fe351d257d252e466e9be7d263ffb0e9781d1134b777ae207ee0321779273b82771d595fad47f4ca67ed2bebbc4a8864758c0f522f0233f66b5745b79
|
7
|
+
data.tar.gz: 44c0a7f8d9117e69845c58577b6f40ccd29f7292e07c9df0cc18a05870a3fb4b5795c85cc0bfcd5434c543ed5575a081b35de6cc7a402ef59d70459ac87659de
|
data/Gemfile
CHANGED
@@ -23,15 +23,11 @@
|
|
23
23
|
source 'https://rubygems.org'
|
24
24
|
gemspec
|
25
25
|
|
26
|
-
gem 'cucumber', '9.2.0', require: false
|
27
26
|
gem 'minitest', '5.23.1', require: false
|
28
|
-
gem 'net-ping', '2.0.8', require: false
|
29
27
|
gem 'rake', '13.2.1', require: false
|
30
28
|
gem 'rspec-rails', '6.1.2', require: false
|
31
|
-
gem 'rubocop', '1.64.
|
32
|
-
gem 'rubocop-performance', '1.21.0', require: false
|
29
|
+
gem 'rubocop', '1.64.1', require: false
|
33
30
|
gem 'rubocop-rspec', '2.29.2', require: false
|
34
31
|
gem 'simplecov', '0.22.0', require: false
|
35
32
|
gem 'simplecov-cobertura', '2.1.0', require: false
|
36
|
-
gem 'webmock', '3.23.1', require: false
|
37
33
|
gem 'yard', '0.9.36', require: false
|
data/Gemfile.lock
CHANGED
@@ -2,6 +2,7 @@ PATH
|
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
4
|
always (0.0.0)
|
5
|
+
concurrent-ruby (~> 1.1)
|
5
6
|
|
6
7
|
GEM
|
7
8
|
remote: https://rubygems.org/
|
@@ -32,50 +33,17 @@ GEM
|
|
32
33
|
minitest (>= 5.1)
|
33
34
|
mutex_m
|
34
35
|
tzinfo (~> 2.0)
|
35
|
-
addressable (2.8.6)
|
36
|
-
public_suffix (>= 2.0.2, < 6.0)
|
37
36
|
ast (2.4.2)
|
38
37
|
base64 (0.2.0)
|
39
38
|
bigdecimal (3.1.8)
|
40
39
|
builder (3.2.4)
|
41
|
-
concurrent-ruby (1.
|
40
|
+
concurrent-ruby (1.3.1)
|
42
41
|
connection_pool (2.4.1)
|
43
|
-
crack (1.0.0)
|
44
|
-
bigdecimal
|
45
|
-
rexml
|
46
42
|
crass (1.0.6)
|
47
|
-
cucumber (9.2.0)
|
48
|
-
builder (~> 3.2)
|
49
|
-
cucumber-ci-environment (> 9, < 11)
|
50
|
-
cucumber-core (> 13, < 14)
|
51
|
-
cucumber-cucumber-expressions (~> 17.0)
|
52
|
-
cucumber-gherkin (> 24, < 28)
|
53
|
-
cucumber-html-formatter (> 20.3, < 22)
|
54
|
-
cucumber-messages (> 19, < 25)
|
55
|
-
diff-lcs (~> 1.5)
|
56
|
-
mini_mime (~> 1.1)
|
57
|
-
multi_test (~> 1.1)
|
58
|
-
sys-uname (~> 1.2)
|
59
|
-
cucumber-ci-environment (10.0.1)
|
60
|
-
cucumber-core (13.0.2)
|
61
|
-
cucumber-gherkin (>= 27, < 28)
|
62
|
-
cucumber-messages (>= 20, < 23)
|
63
|
-
cucumber-tag-expressions (> 5, < 7)
|
64
|
-
cucumber-cucumber-expressions (17.1.0)
|
65
|
-
bigdecimal
|
66
|
-
cucumber-gherkin (27.0.0)
|
67
|
-
cucumber-messages (>= 19.1.4, < 23)
|
68
|
-
cucumber-html-formatter (21.3.1)
|
69
|
-
cucumber-messages (> 19, < 25)
|
70
|
-
cucumber-messages (22.0.0)
|
71
|
-
cucumber-tag-expressions (6.1.0)
|
72
43
|
diff-lcs (1.5.1)
|
73
44
|
docile (1.4.0)
|
74
45
|
drb (2.2.1)
|
75
46
|
erubi (1.12.0)
|
76
|
-
ffi (1.16.3)
|
77
|
-
ffi (1.16.3-x64-mingw-ucrt)
|
78
|
-
hashdiff (1.1.0)
|
79
47
|
i18n (1.14.5)
|
80
48
|
concurrent-ruby (~> 1.0)
|
81
49
|
io-console (0.7.2)
|
@@ -87,11 +55,8 @@ GEM
|
|
87
55
|
loofah (2.22.0)
|
88
56
|
crass (~> 1.0.2)
|
89
57
|
nokogiri (>= 1.12.0)
|
90
|
-
mini_mime (1.1.5)
|
91
58
|
minitest (5.23.1)
|
92
|
-
multi_test (1.1.0)
|
93
59
|
mutex_m (0.2.0)
|
94
|
-
net-ping (2.0.8)
|
95
60
|
nokogiri (1.16.5-aarch64-linux)
|
96
61
|
racc (~> 1.4)
|
97
62
|
nokogiri (1.16.5-arm-linux)
|
@@ -112,7 +77,6 @@ GEM
|
|
112
77
|
racc
|
113
78
|
psych (5.1.2)
|
114
79
|
stringio
|
115
|
-
public_suffix (5.0.5)
|
116
80
|
racc (1.8.0)
|
117
81
|
rack (3.0.11)
|
118
82
|
rack-session (2.0.0)
|
@@ -163,7 +127,7 @@ GEM
|
|
163
127
|
rspec-mocks (~> 3.13)
|
164
128
|
rspec-support (~> 3.13)
|
165
129
|
rspec-support (3.13.1)
|
166
|
-
rubocop (1.64.
|
130
|
+
rubocop (1.64.1)
|
167
131
|
json (~> 2.3)
|
168
132
|
language_server-protocol (>= 3.17.0)
|
169
133
|
parallel (~> 1.10)
|
@@ -180,9 +144,6 @@ GEM
|
|
180
144
|
rubocop (~> 1.41)
|
181
145
|
rubocop-factory_bot (2.25.1)
|
182
146
|
rubocop (~> 1.41)
|
183
|
-
rubocop-performance (1.21.0)
|
184
|
-
rubocop (>= 1.48.1, < 2.0)
|
185
|
-
rubocop-ast (>= 1.31.1, < 2.0)
|
186
147
|
rubocop-rspec (2.29.2)
|
187
148
|
rubocop (~> 1.40)
|
188
149
|
rubocop-capybara (~> 2.17)
|
@@ -202,16 +163,10 @@ GEM
|
|
202
163
|
simplecov_json_formatter (0.1.4)
|
203
164
|
stringio (3.1.0)
|
204
165
|
strscan (3.1.0)
|
205
|
-
sys-uname (1.2.3)
|
206
|
-
ffi (~> 1.1)
|
207
166
|
thor (1.3.1)
|
208
167
|
tzinfo (2.0.6)
|
209
168
|
concurrent-ruby (~> 1.0)
|
210
169
|
unicode-display_width (2.5.0)
|
211
|
-
webmock (3.23.1)
|
212
|
-
addressable (>= 2.8.0)
|
213
|
-
crack (>= 0.3.2)
|
214
|
-
hashdiff (>= 0.4.0, < 2.0.0)
|
215
170
|
webrick (1.8.1)
|
216
171
|
yard (0.9.36)
|
217
172
|
zeitwerk (2.6.15)
|
@@ -227,17 +182,13 @@ PLATFORMS
|
|
227
182
|
|
228
183
|
DEPENDENCIES
|
229
184
|
always!
|
230
|
-
cucumber (= 9.2.0)
|
231
185
|
minitest (= 5.23.1)
|
232
|
-
net-ping (= 2.0.8)
|
233
186
|
rake (= 13.2.1)
|
234
187
|
rspec-rails (= 6.1.2)
|
235
|
-
rubocop (= 1.64.
|
236
|
-
rubocop-performance (= 1.21.0)
|
188
|
+
rubocop (= 1.64.1)
|
237
189
|
rubocop-rspec (= 2.29.2)
|
238
190
|
simplecov (= 0.22.0)
|
239
191
|
simplecov-cobertura (= 2.1.0)
|
240
|
-
webmock (= 3.23.1)
|
241
192
|
yard (= 0.9.36)
|
242
193
|
|
243
194
|
BUNDLED WITH
|
data/always.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
|
|
26
26
|
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
|
27
27
|
s.required_ruby_version = '>=3.2'
|
28
28
|
s.name = 'always'
|
29
|
-
s.version = '0.0.
|
29
|
+
s.version = '0.0.3'
|
30
30
|
s.license = 'MIT'
|
31
31
|
s.summary = 'A simple Ruby framework that spins a loop forever, in a background thread'
|
32
32
|
s.description =
|
@@ -40,5 +40,6 @@ Gem::Specification.new do |s|
|
|
40
40
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
41
41
|
s.rdoc_options = ['--charset=UTF-8']
|
42
42
|
s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
|
43
|
+
s.add_runtime_dependency 'concurrent-ruby', '~>1.1'
|
43
44
|
s.metadata['rubygems_mfa_required'] = 'true'
|
44
45
|
end
|
data/lib/always.rb
CHANGED
@@ -20,50 +20,101 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
21
|
# SOFTWARE.
|
22
22
|
|
23
|
+
require 'concurrent/atom'
|
24
|
+
|
23
25
|
# Always.
|
26
|
+
#
|
27
|
+
# In order to start five threads performing the same piece of code
|
28
|
+
# over and over again, with a 60-seconds pause between cycles, do this:
|
29
|
+
#
|
30
|
+
# require 'always'
|
31
|
+
# a = Always.new(5)
|
32
|
+
# a.start(60) do
|
33
|
+
# puts 'Hello, world!
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# Then, in order stop them all together:
|
37
|
+
#
|
38
|
+
# a.stop
|
39
|
+
#
|
40
|
+
# It's possible to get a quick summary of the thread pool, by calling +to_s+.
|
41
|
+
#
|
24
42
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
25
43
|
# Copyright:: Copyright (c) 2024 Yegor Bugayenko
|
26
44
|
# License:: MIT
|
27
45
|
class Always
|
28
46
|
# The version of the framework.
|
29
|
-
VERSION = '0.0.
|
47
|
+
VERSION = '0.0.3'
|
30
48
|
|
31
49
|
# Constructor.
|
32
|
-
|
50
|
+
# @param [Integer] total The number of threads to run
|
51
|
+
def initialize(total)
|
52
|
+
raise "The number of threads (#{total}) must be positive" unless total.positive?
|
53
|
+
|
33
54
|
@total = total
|
34
|
-
@pause = pause
|
35
55
|
@on_error = nil
|
36
56
|
@threads = []
|
57
|
+
@cycles = Concurrent::Atom.new(0)
|
58
|
+
@errors = Concurrent::Atom.new(0)
|
37
59
|
end
|
38
60
|
|
39
61
|
# What to do when an exception occurs?
|
62
|
+
#
|
63
|
+
# Call it like this (the +e+ provided is the exception and +i+ is the
|
64
|
+
# number of the thread where it occured):
|
65
|
+
#
|
66
|
+
# a = Always.new(5)
|
67
|
+
# a.on_error do |e, i|
|
68
|
+
# puts e.message
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# If the block that you provided will also throw an error, it will
|
72
|
+
# simply be ignored (not logged anywhere, just ignored!)
|
73
|
+
# @return [Always] Returns itself
|
40
74
|
def on_error(&block)
|
41
75
|
@on_error = block
|
76
|
+
self
|
42
77
|
end
|
43
78
|
|
44
|
-
|
79
|
+
# Start them all.
|
80
|
+
# @param [Integer] pause The delay between cycles, in seconds
|
81
|
+
def start(pause = 0, &)
|
82
|
+
raise 'It is running now, call .stop() first' unless @threads.empty?
|
83
|
+
|
45
84
|
(0..@total - 1).each do |i|
|
46
85
|
@threads[i] = Thread.new do
|
47
|
-
body(i)
|
86
|
+
body(i, pause, &)
|
87
|
+
@cycles.swap { |c| c + 1 }
|
48
88
|
end
|
49
89
|
end
|
50
90
|
end
|
51
91
|
|
92
|
+
# Stop them all.
|
52
93
|
def stop
|
53
94
|
@threads.each(&:terminate)
|
95
|
+
@threads = []
|
96
|
+
end
|
97
|
+
|
98
|
+
# Represent its internal state as a string.
|
99
|
+
# @return [String] Something like "4/230/23", where 4 is the number of running
|
100
|
+
# threads, 230 is the number of successfull loops, and 23 is the number
|
101
|
+
# of failures occured so far.
|
102
|
+
def to_s
|
103
|
+
"#{@threads.size}/#{@cycles.value}/#{@errors.value}"
|
54
104
|
end
|
55
105
|
|
56
106
|
private
|
57
107
|
|
58
108
|
# rubocop:disable Lint/RescueException
|
59
|
-
def body(idx)
|
109
|
+
def body(idx, pause)
|
60
110
|
loop do
|
61
111
|
begin
|
62
112
|
yield
|
63
113
|
rescue Exception => e
|
114
|
+
@errors.swap { |c| c + 1 }
|
64
115
|
@on_error&.call(e, idx)
|
65
116
|
end
|
66
|
-
sleep(
|
117
|
+
sleep(pause)
|
67
118
|
rescue Exception
|
68
119
|
# If we reach this point, we must not even try to
|
69
120
|
# do anything. Here we must quietly ignore everything
|
data/test/test_always.rb
CHANGED
@@ -35,4 +35,35 @@ class TestAlways < Minitest::Test
|
|
35
35
|
end
|
36
36
|
a.stop
|
37
37
|
end
|
38
|
+
|
39
|
+
def test_with_error
|
40
|
+
a = Always.new(5)
|
41
|
+
failures = 0
|
42
|
+
a.on_error { |_e, i| failures += i }.start do
|
43
|
+
raise 'intentionally'
|
44
|
+
end
|
45
|
+
sleep(0.1)
|
46
|
+
a.stop
|
47
|
+
assert(failures.positive?)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_converts_to_string
|
51
|
+
a = Always.new(5)
|
52
|
+
a.start do
|
53
|
+
# nothing
|
54
|
+
end
|
55
|
+
assert(a.to_s.start_with?('5/'))
|
56
|
+
a.stop
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_with_counter
|
60
|
+
a = Always.new(1)
|
61
|
+
done = 0
|
62
|
+
a.start do
|
63
|
+
done += 1
|
64
|
+
end
|
65
|
+
sleep(0.1)
|
66
|
+
a.stop
|
67
|
+
assert(done.positive?)
|
68
|
+
end
|
38
69
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: always
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
@@ -9,7 +9,21 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2024-05-31 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: concurrent-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
13
27
|
description: You may need this framework if you have a routine task that must be performed
|
14
28
|
every once in a while, but may raise exceptions which you don't want to cause a
|
15
29
|
termination of the entire routine cycle.
|