tins 1.59.0 → 1.60.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7843ec2310ee0b79ceccdfab9e4f1ad6269ffab2735ba0fbac09cad7d931df43
4
- data.tar.gz: 577d7f162cf3838f33afe119d847fdfd23b748f6807bf0c54aad592e6121b3ec
3
+ metadata.gz: 63bb3c1fda93e6a0e0899cf2fb2e0bd9607b8c15ef7a9490046cd3d6c7ee5041
4
+ data.tar.gz: 3005b68848301fcb1da0c2836206252adcd927ce979c82c5fe85676b1af07707
5
5
  SHA512:
6
- metadata.gz: 44ccfe0501987243c4fb871055ef1af9083079e4d7a676e56eb389100f3b6a8fcffbd0cfa05b7be495a829d930d840c32c69faa50b3c0dc81af4ea3de9f5f418
7
- data.tar.gz: 942ea6999ec5d3ed20dada3e6d5395fd68d0ab38f6c11723c05071522f92c756caeadc44b91a018a990e29184896f71b4d4dcf85795c5057c49e71d37eb0a564
6
+ metadata.gz: 5ecdfdd4a3843a7fde44098b1cb32afc62278ae7eeef163aebbb6785299684f03426aa13cc43f406471084c62e8a3cec84366adb79f8ca8a0c64ba5ad525cadc
7
+ data.tar.gz: 88a5c5ed662a83a2e4590dc525569ace5dadf6cddfd0e6bb3bfee4de8ee774508368035beeeb2362655a5131da990d920e0b06e520a26f1e38e891f15e2d9a0b
data/CHANGES.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-10-08 v1.60.1
4
+
5
+ * Fix `Limited` shutdown race and busy-spin
6
+ * Move `@tg.add` to the executor thread to close a `@count`/`@tg` race
7
+ * Replace `wait until done?` + `@executor.kill` with sentinel, `join`, `wait`
8
+ * Remove unused `done?`
9
+
10
+ ## 2026-10-02 v1.60.0
11
+
12
+ * Add unpadded `%H`, `%M`, and `%u` format directives to `Duration#format` for
13
+ unpadded hours, minutes, and seconds, complementing the existing zero-padded
14
+ `%h`, `%m`, and `%s`
15
+ * Update the directive regex in `format` from `/%[DdhmSs%]/` to
16
+ `/%[DdHhMmuSs%]/` to match the new directives
17
+ * Extend YARD documentation with an `@example` block demonstrating unpadded
18
+ components in a natural-language template
19
+ * Add `test_format_unpadded_directives` covering all three new directives in
20
+ both template and standalone usage
21
+ * Fix `assert_equal` argument order in `test_parse_percentage`
22
+
3
23
  ## 2026-09-25 v1.59.0
4
24
 
5
25
  * Add `*sels` and `&block` parameters to `squeeze` and `squeeze!`, allowing
data/lib/tins/duration.rb CHANGED
@@ -161,8 +161,11 @@ module Tins
161
161
  # The template string supports the following directives:
162
162
  # - `%S` - Sign indicator (negative sign if duration is negative)
163
163
  # - `%d` - Days component
164
+ # - `%H` - Hours component (unpadded)
164
165
  # - `%h` - Hours component (zero-padded to 2 digits)
166
+ # - `%M` - Minutes component (unpadded)
165
167
  # - `%m` - Minutes component (zero-padded to 2 digits)
168
+ # - `%u` - Seconds component (unpadded)
166
169
  # - `%s` - Seconds component (zero-padded to 2 digits)
167
170
  # - `%f` - Fractional seconds component (without the leading decimal point)
168
171
  # - `%D` - Smart format (automatically includes days, fractional seconds, and sign)
@@ -186,13 +189,21 @@ module Tins
186
189
  #
187
190
  # @example Custom precision
188
191
  # duration.format('%s.%f', precision: 2) # => "04.12"
192
+ #
193
+ # @example Unpadded components
194
+ # duration = Tins::Duration.new(93_784)
195
+ # duration.format('%d day(s), %H hours, %M minutes, and %u seconds')
196
+ # # => "1 day(s), 2 hours, 3 minutes, and 4 seconds"
189
197
  def format(template = '%S%d+%h:%m:%s.%f', precision: nil)
190
- result = template.gsub(/%[DdhmSs%]/) { |directive|
198
+ result = template.gsub(/%[DdHhMmuSs%]/) { |directive|
191
199
  case directive
192
200
  when '%S' then ?- if negative?
193
201
  when '%d' then @days
202
+ when '%H' then '%u' % @hours
194
203
  when '%h' then '%02u' % @hours
204
+ when '%M' then '%u' % @minutes
195
205
  when '%m' then '%02u' % @minutes
206
+ when '%u' then '%u' % @seconds
196
207
  when '%s' then '%02u' % @seconds
197
208
  when '%D' then format_smart
198
209
  when '%%' then '%'
data/lib/tins/limited.rb CHANGED
@@ -70,8 +70,9 @@ module Tins
70
70
  yield self
71
71
  end
72
72
  ensure
73
- wait until done?
74
- @executor.kill
73
+ @tasks << nil
74
+ @executor.join
75
+ wait
75
76
  end
76
77
  end
77
78
 
@@ -84,13 +85,6 @@ module Tins
84
85
 
85
86
  private
86
87
 
87
- # Check if all tasks and threads have completed.
88
- #
89
- # @return [Boolean] true if no tasks remain and no threads are running
90
- def done?
91
- @tasks.empty? && @tg.list.empty?
92
- end
93
-
94
88
  # Wait for all threads in the thread group to complete.
95
89
  #
96
90
  # @return [void]
@@ -106,15 +100,15 @@ module Tins
106
100
  @mutex.synchronize do
107
101
  loop do
108
102
  if @count < @maximum
109
- task = @tasks.pop
103
+ task = @tasks.pop or break
110
104
  @count += 1
111
- Thread.new do
112
- @tg.add Thread.current
105
+ thread = Thread.new do
113
106
  task.(Thread.current)
114
107
  ensure
115
108
  @count -= 1
116
109
  @continue.signal
117
110
  end
111
+ @tg.add thread
118
112
  else
119
113
  @continue.wait(@mutex)
120
114
  end
data/lib/tins/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Tins
2
2
  # Tins version
3
- VERSION = '1.59.0'
3
+ VERSION = '1.60.1'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
@@ -96,7 +96,18 @@ module Tins
96
96
  def test_parse_percentage
97
97
  duration_string = '1%2'
98
98
  seconds = Tins::Duration.parse(duration_string, template: '%h%%%m')
99
- assert_equal seconds, 3720
99
+ assert_equal 3720, seconds
100
+ end
101
+
102
+ def test_format_unpadded_directives
103
+ d = Tins::Duration.new(93_784) # 1d 2h 3m 4s
104
+ assert_equal(
105
+ '1 day(s), 2 hours, 3 minutes, and 4 seconds',
106
+ d.format('%d day(s), %H hours, %M minutes, and %u seconds')
107
+ )
108
+ assert_equal '2', d.format('%H')
109
+ assert_equal '3', d.format('%M')
110
+ assert_equal '4', d.format('%u')
100
111
  end
101
112
  end
102
113
  end
@@ -18,5 +18,19 @@ module Tins
18
18
  end
19
19
  assert_equal 10, count.keys.uniq.size
20
20
  end
21
+
22
+ def test_process_with_fast_stop
23
+ count = {}
24
+ Tins::Limited.new(5, name: 'sleeper').process do |limited|
25
+ 10.times do
26
+ limited.execute do
27
+ count[Thread.current] = true
28
+ sleep 1
29
+ end
30
+ end
31
+ limited.stop
32
+ end
33
+ assert_equal 10, count.keys.uniq.size
34
+ end
21
35
  end
22
36
  end
data/tins.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: tins 1.59.0 ruby lib
2
+ # stub: tins 1.60.1 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "tins".freeze
6
- s.version = "1.59.0".freeze
6
+ s.version = "1.60.1".freeze
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
17
17
  s.licenses = ["MIT".freeze]
18
18
  s.rdoc_options = ["--title".freeze, "Tins - Useful stuff.".freeze, "--main".freeze, "README.md".freeze]
19
19
  s.required_ruby_version = Gem::Requirement.new(">= 3.1".freeze)
20
- s.rubygems_version = "4.0.20".freeze
20
+ s.rubygems_version = "4.0.22".freeze
21
21
  s.summary = "Useful stuff.".freeze
22
22
  s.test_files = ["tests/annotate_test.rb".freeze, "tests/ask_and_send_test.rb".freeze, "tests/attempt_test.rb".freeze, "tests/bijection_test.rb".freeze, "tests/blank_full_test.rb".freeze, "tests/case_predicate_test.rb".freeze, "tests/concern_test.rb".freeze, "tests/date_dummy_test.rb".freeze, "tests/date_time_dummy_test.rb".freeze, "tests/deep_dup_test.rb".freeze, "tests/deep_transform_test.rb".freeze, "tests/delegate_test.rb".freeze, "tests/deprecate_test.rb".freeze, "tests/dslkit_test.rb".freeze, "tests/duration_test.rb".freeze, "tests/dynamic_scope_test.rb".freeze, "tests/enum_squeeze_test.rb".freeze, "tests/expose_test.rb".freeze, "tests/extract_last_argument_options_test.rb".freeze, "tests/file_binary_test.rb".freeze, "tests/find_test.rb".freeze, "tests/from_module_test.rb".freeze, "tests/generator_test.rb".freeze, "tests/go_test.rb".freeze, "tests/hash_bfs_test.rb".freeze, "tests/hash_dfs_test.rb".freeze, "tests/hash_symbolize_keys_recursive_test.rb".freeze, "tests/hash_union_test.rb".freeze, "tests/if_predicate_test.rb".freeze, "tests/implement_test.rb".freeze, "tests/limited_test.rb".freeze, "tests/lines_file_test.rb".freeze, "tests/lru_cache_test.rb".freeze, "tests/memoize_test.rb".freeze, "tests/method_description_test.rb".freeze, "tests/minimize_test.rb".freeze, "tests/module_group_test.rb".freeze, "tests/named_set_test.rb".freeze, "tests/named_test.rb".freeze, "tests/null_test.rb".freeze, "tests/p_test.rb".freeze, "tests/partial_application_test.rb".freeze, "tests/proc_compose_test.rb".freeze, "tests/proc_prelude_test.rb".freeze, "tests/range_plus_test.rb".freeze, "tests/require_maybe_test.rb".freeze, "tests/responding_test.rb".freeze, "tests/rotate_test.rb".freeze, "tests/scope_test.rb".freeze, "tests/secure_write_test.rb".freeze, "tests/sexy_singleton_test.rb".freeze, "tests/string_byte_order_mark_test.rb".freeze, "tests/string_camelize_test.rb".freeze, "tests/string_named_placeholders.rb".freeze, "tests/string_underscore_test.rb".freeze, "tests/string_version_test.rb".freeze, "tests/subhash_test.rb".freeze, "tests/temp_io_test.rb".freeze, "tests/test_helper.rb".freeze, "tests/time_dummy_test.rb".freeze, "tests/time_freezer_test.rb".freeze, "tests/to_test.rb".freeze, "tests/token_test.rb".freeze, "tests/unit_test.rb".freeze, "tests/annotate_test.rb".freeze, "tests/ask_and_send_test.rb".freeze, "tests/attempt_test.rb".freeze, "tests/bijection_test.rb".freeze, "tests/blank_full_test.rb".freeze, "tests/case_predicate_test.rb".freeze, "tests/concern_test.rb".freeze, "tests/date_dummy_test.rb".freeze, "tests/date_time_dummy_test.rb".freeze, "tests/deep_dup_test.rb".freeze, "tests/deep_transform_test.rb".freeze, "tests/delegate_test.rb".freeze, "tests/deprecate_test.rb".freeze, "tests/dslkit_test.rb".freeze, "tests/duration_test.rb".freeze, "tests/dynamic_scope_test.rb".freeze, "tests/enum_squeeze_test.rb".freeze, "tests/expose_test.rb".freeze, "tests/extract_last_argument_options_test.rb".freeze, "tests/file_binary_test.rb".freeze, "tests/find_test.rb".freeze, "tests/from_module_test.rb".freeze, "tests/generator_test.rb".freeze, "tests/go_test.rb".freeze, "tests/hash_bfs_test.rb".freeze, "tests/hash_dfs_test.rb".freeze, "tests/hash_symbolize_keys_recursive_test.rb".freeze, "tests/hash_union_test.rb".freeze, "tests/if_predicate_test.rb".freeze, "tests/implement_test.rb".freeze, "tests/limited_test.rb".freeze, "tests/lines_file_test.rb".freeze, "tests/lru_cache_test.rb".freeze, "tests/memoize_test.rb".freeze, "tests/method_description_test.rb".freeze, "tests/minimize_test.rb".freeze, "tests/module_group_test.rb".freeze, "tests/named_set_test.rb".freeze, "tests/named_test.rb".freeze, "tests/null_test.rb".freeze, "tests/p_test.rb".freeze, "tests/partial_application_test.rb".freeze, "tests/proc_compose_test.rb".freeze, "tests/proc_prelude_test.rb".freeze, "tests/range_plus_test.rb".freeze, "tests/require_maybe_test.rb".freeze, "tests/responding_test.rb".freeze, "tests/rotate_test.rb".freeze, "tests/scope_test.rb".freeze, "tests/secure_write_test.rb".freeze, "tests/sexy_singleton_test.rb".freeze, "tests/string_byte_order_mark_test.rb".freeze, "tests/string_camelize_test.rb".freeze, "tests/string_underscore_test.rb".freeze, "tests/string_version_test.rb".freeze, "tests/subhash_test.rb".freeze, "tests/temp_io_test.rb".freeze, "tests/time_dummy_test.rb".freeze, "tests/time_freezer_test.rb".freeze, "tests/to_test.rb".freeze, "tests/token_test.rb".freeze, "tests/unit_test.rb".freeze]
23
23
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tins
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.59.0
4
+ version: 1.60.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -528,7 +528,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
528
528
  - !ruby/object:Gem::Version
529
529
  version: '0'
530
530
  requirements: []
531
- rubygems_version: 4.0.20
531
+ rubygems_version: 4.0.22
532
532
  specification_version: 4
533
533
  summary: Useful stuff.
534
534
  test_files: