always 0.0.1 → 0.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 +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 +20 -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: 1d5c02ea320168984342dcf8805da07bec45d9a685097400c9f3e406a75a96b9
|
|
4
|
+
data.tar.gz: 0db1ae1c2006db2de79982172f8fd7cc1d53e3fb8dfaffc8e474ea5972139224
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 628d7ddcbc48d5e1b87adcccb7446a60c0ec1b4f1e047ee85b9a3188f76e3dbd42de40d384fc7e4f84de21c750ba26312b136d63a1248b575a1d3c2d68430a0a
|
|
7
|
+
data.tar.gz: d9e259a904b1785543bb0282f026b0ee08e7ec1b641ee2243bd23d063a99df90b9dc69c69438f6d36f09bc7351a893b32581d0f475b7761d9997d7f112afa75a
|
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.2'
|
|
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.2'
|
|
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,24 @@ 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
|
|
38
58
|
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.2
|
|
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.
|