tins 1.58.0 → 1.59.0

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: 564a0581732d7b53d9882960e13662d1d279f7edf7d1bc4c6d717a5dc2f27188
4
- data.tar.gz: 36ff7350160cdcc8c606ba6c91aa74d9fa7c0617caebbe7af80473f1ce499829
3
+ metadata.gz: 7843ec2310ee0b79ceccdfab9e4f1ad6269ffab2735ba0fbac09cad7d931df43
4
+ data.tar.gz: 577d7f162cf3838f33afe119d847fdfd23b748f6807bf0c54aad592e6121b3ec
5
5
  SHA512:
6
- metadata.gz: 7bf1f364ec0c9a8b9e8db2fbcde42b9b93574f09fc2ebcadc089c62a4510cb766534e85ea40f2073311d36a9e64644194877e6673f3888f1a1327504004b6f6f
7
- data.tar.gz: cdd850325aff0d57ccd781d8859e940161952bff48d77d72e260785ea4029fe57bfb18123471d83c1548a3e56105b2190fcb3e273aa0eb1c4e26710a0103c3df
6
+ metadata.gz: 44ccfe0501987243c4fb871055ef1af9083079e4d7a676e56eb389100f3b6a8fcffbd0cfa05b7be495a829d930d840c32c69faa50b3c0dc81af4ea3de9f5f418
7
+ data.tar.gz: 942ea6999ec5d3ed20dada3e6d5395fd68d0ab38f6c11723c05071522f92c756caeadc44b91a018a990e29184896f71b4d4dcf85795c5057c49e71d37eb0a564
data/CHANGES.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-09-25 v1.59.0
4
+
5
+ * Add `*sels` and `&block` parameters to `squeeze` and `squeeze!`, allowing
6
+ squeezing to be restricted to elements matching case-equality (`===`)
7
+ selectors or a predicate block
8
+ * Raise `ArgumentError` when both `*sels` and `&block` are provided
9
+ * Change hot-loop comparison to `item != prev || !block.(item)` so non-matching
10
+ elements are never collapsed
11
+ * Forward `(*sels, &block)` from `squeeze!` to `squeeze`
12
+ * Extend YARD documentation with `@param` tags and `@example` blocks covering
13
+ range, type, predicate, and `String`/`Enumerable` invariance
14
+ * Add 12 new test cases in `tests/enum_squeeze_test.rb` covering single/multi
15
+ value selectors, type selectors, range selectors, predicate blocks, the
16
+ mutual-exclusion guard, the `String#squeeze`/`Enumerable#squeeze` invariance
17
+ property, and `squeeze!` forwarding with selectors and blocks
18
+
3
19
  ## 2026-09-24 v1.58.0
4
20
 
5
21
  * Freeze the `UNSET` sentinel constant in `Tins::Delegate` to prevent
@@ -10,6 +10,19 @@ module Tins
10
10
  # Two elements are considered duplicates when
11
11
  # +item == previous+ is true.
12
12
  #
13
+ # Optionally restrict squeezing to elements matching one or
14
+ # more selectors via case-equality (+===+), mirroring how
15
+ # +String#squeeze+ uses character-class expressions.
16
+ # Elements not matching any selector are never collapsed.
17
+ #
18
+ # @param [Array] sels
19
+ # Case-equality matchers (types, ranges, regexes, or any
20
+ # object responding to +===+) defining which elements are
21
+ # eligible for squeezing.
22
+ # @param [Proc, nil] block
23
+ # A predicate; when it returns false for an element, that
24
+ # element is never collapsed. Mutually exclusive with +sels+.
25
+ #
13
26
  # @return [Array] a new array with consecutive duplicates removed
14
27
  #
15
28
  # @example Basic usage:
@@ -20,12 +33,40 @@ module Tins
20
33
  #
21
34
  # @example Mirrors String#squeeze semantics:
22
35
  # "aabbcc".squeeze # => "abc"
23
- def squeeze
36
+ #
37
+ # @example Squeeze only elements in a range:
38
+ # [1, 3, 2, 2, 4, 6, 3, 3, 7].squeeze(2..3)
39
+ # # => [1, 3, 2, 4, 6, 3, 7]
40
+ #
41
+ # @example Squeeze only elements of a given type:
42
+ # [[1], '1', [1], [1], '1', '1', [1]].squeeze(String)
43
+ # # => [[1], "1", [1], [1], "1", [1]]
44
+ #
45
+ # @example Squeeze using a predicate block:
46
+ # [1, 3, 2, 2, 4, 6, 3, 3, 7].squeeze(&:even?)
47
+ # # => [1, 3, 2, 4, 6, 3, 3, 7]
48
+ #
49
+ # @example Enumerable/String invariance:
50
+ # str = 'fooaabaaz'
51
+ # str.squeeze(?a) == str.split('').squeeze(?a).join
52
+ # # => true
53
+ def squeeze(*sels, &block)
54
+ !sels.empty? && block and raise ArgumentError,
55
+ 'you cannot pass both *sels and &block'
56
+
57
+ unless block
58
+ if sels.empty?
59
+ block = -> x { true }
60
+ else
61
+ block = -> x { sels.any? { _1 === x } }
62
+ end
63
+ end
64
+
24
65
  result = []
25
66
  prev = A_THING_THAT_IS_NOT_A_THING
26
67
 
27
68
  each do |item|
28
- unless item == prev
69
+ if item != prev || !block.(item)
29
70
  result << item
30
71
  prev = item
31
72
  end
@@ -39,6 +80,11 @@ module Tins
39
80
  # Destructive counterpart to +squeeze+: the receiver is modified
40
81
  # via +replace+; no new object is allocated.
41
82
  #
83
+ # @param [Array] sels
84
+ # Case-equality matchers (see +squeeze+).
85
+ # @param [Proc, nil] block
86
+ # A predicate (see +squeeze+).
87
+ #
42
88
  # @return [self] if one or more consecutive duplicates were removed
43
89
  # @return [nil] if the receiver was already free of consecutive
44
90
  # duplicates
@@ -55,9 +101,14 @@ module Tins
55
101
  # b = [1, 2, 3]
56
102
  # b.squeeze! # => nil
57
103
  # b # => [1, 2, 3]
58
- def squeeze!
104
+ #
105
+ # @example Squeezing with a selector:
106
+ # a = [1, 3, 2, 2, 4, 6, 3, 3, 7]
107
+ # a.squeeze!(2..3)
108
+ # a # => [1, 3, 2, 4, 6, 3, 7]
109
+ def squeeze!(*sels, &block)
59
110
  respond_to?(:replace) or raise 'cannot be squeezed in place!'
60
- squeezed = squeeze
111
+ squeezed = squeeze(*sels, &block)
61
112
  return if squeezed.count == count
62
113
  replace squeezed
63
114
  end
data/lib/tins/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Tins
2
2
  # Tins version
3
- VERSION = '1.58.0'
3
+ VERSION = '1.59.0'
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:
@@ -50,6 +50,71 @@ module Tins
50
50
  assert_equal [1, 2, 3], result
51
51
  end
52
52
 
53
+ # --- squeeze with selectors ---
54
+
55
+ def test_squeeze_single_value_selector
56
+ assert_equal(
57
+ [1, 3, 2, 2, 4, 6, 3, 7],
58
+ [1, 3, 2, 2, 4, 6, 3, 3, 7].squeeze(3)
59
+ )
60
+ end
61
+
62
+ def test_squeeze_multiple_value_selectors
63
+ assert_equal(
64
+ [1, 3, 2, 4, 6, 3, 7],
65
+ [1, 3, 2, 2, 4, 6, 3, 3, 7].squeeze(3, 2)
66
+ )
67
+ end
68
+
69
+ def test_squeeze_type_selector
70
+ assert_equal(
71
+ [[1], "1", [1], [1], "1", [1]],
72
+ [[1], '1', [1], [1], '1', '1', [1]].squeeze(String)
73
+ )
74
+ end
75
+
76
+ def test_squeeze_multiple_type_selectors
77
+ assert_equal(
78
+ [[1], "1", [1], "1", [1]],
79
+ [[1], '1', [1], [1], '1', '1', [1]].squeeze(String, Array)
80
+ )
81
+ end
82
+
83
+ def test_squeeze_range_selector
84
+ # Only 2 and 3 are in range; 5,5 survives (out of scope)
85
+ assert_equal(
86
+ [1, 3, 2, 4, 6, 3, 7, 5, 5, 7],
87
+ [1, 3, 2, 2, 4, 6, 3, 3, 7, 5, 5, 7].squeeze(2..3)
88
+ )
89
+ end
90
+
91
+ def test_squeeze_even_block
92
+ assert_equal(
93
+ [1, 3, 2, 4, 6, 3, 3, 7],
94
+ [1, 3, 2, 2, 4, 6, 3, 3, 7].squeeze(&:even?)
95
+ )
96
+ end
97
+
98
+ def test_squeeze_odd_block
99
+ assert_equal(
100
+ [1, 3, 2, 2, 4, 6, 3, 7],
101
+ [1, 3, 2, 2, 4, 6, 3, 3, 7].squeeze(&:odd?)
102
+ )
103
+ end
104
+
105
+ def test_squeeze_raises_when_both_sels_and_block
106
+ assert_raise(ArgumentError) do
107
+ [1, 1, 2].squeeze(Integer) { |x| true }
108
+ end
109
+ end
110
+
111
+ def test_string_array_invariance
112
+ str = 'fooaabaaz'
113
+ sels = [?a]
114
+ assert_equal str.squeeze(*sels),
115
+ str.split('').squeeze(*sels).join
116
+ end
117
+
53
118
  # --- squeeze! (bang) ---
54
119
 
55
120
  def test_squeeze_bang_modifies_in_place
@@ -80,5 +145,27 @@ module Tins
80
145
  enum = [1, 1, 2].each # Enumerator has no #replace
81
146
  assert_raise(RuntimeError) { enum.squeeze! }
82
147
  end
148
+
149
+ # --- squeeze! with selectors ---
150
+
151
+ def test_squeeze_bang_with_selector
152
+ a = [1, 3, 2, 2, 4, 6, 3, 3, 7]
153
+ result = a.squeeze!(3)
154
+ assert_equal [1, 3, 2, 2, 4, 6, 3, 7], a
155
+ assert_same a, result
156
+ end
157
+
158
+ def test_squeeze_bang_with_selector_no_change
159
+ a = [1, 2, 4, 6, 7]
160
+ assert_nil a.squeeze!(3)
161
+ assert_equal [1, 2, 4, 6, 7], a
162
+ end
163
+
164
+ def test_squeeze_bang_with_block
165
+ a = [1, 3, 2, 2, 4, 6, 3, 3, 7]
166
+ result = a.squeeze!(&:even?)
167
+ assert_equal [1, 3, 2, 4, 6, 3, 3, 7], a
168
+ assert_same a, result
169
+ end
83
170
  end
84
171
  end
data/tins.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: tins 1.58.0 ruby lib
2
+ # stub: tins 1.59.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "tins".freeze
6
- s.version = "1.58.0".freeze
6
+ s.version = "1.59.0".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]
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.58.0
4
+ version: 1.59.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank