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 +4 -4
- data/CHANGES.md +16 -0
- data/lib/tins/enum_squeeze.rb +55 -4
- data/lib/tins/version.rb +1 -1
- data/tests/enum_squeeze_test.rb +87 -0
- data/tins.gemspec +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7843ec2310ee0b79ceccdfab9e4f1ad6269ffab2735ba0fbac09cad7d931df43
|
|
4
|
+
data.tar.gz: 577d7f162cf3838f33afe119d847fdfd23b748f6807bf0c54aad592e6121b3ec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
data/lib/tins/enum_squeeze.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
data/tests/enum_squeeze_test.rb
CHANGED
|
@@ -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.
|
|
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.
|
|
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]
|