invoca-utils 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/pipeline.yml +20 -0
  3. data/.gitignore +1 -3
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -1
  6. data/.tool-versions +1 -0
  7. data/Appraisals +13 -0
  8. data/CHANGELOG.md +6 -0
  9. data/Gemfile +9 -16
  10. data/Gemfile.lock +39 -48
  11. data/Rakefile +9 -6
  12. data/gemfiles/activesupport_5.gemfile +12 -0
  13. data/gemfiles/activesupport_5.gemfile.lock +58 -0
  14. data/gemfiles/activesupport_6.gemfile +12 -0
  15. data/gemfiles/activesupport_6.gemfile.lock +59 -0
  16. data/gemfiles/activesupport_7.gemfile +12 -0
  17. data/gemfiles/activesupport_7.gemfile.lock +57 -0
  18. data/invoca-utils.gemspec +18 -6
  19. data/lib/invoca/utils/version.rb +1 -1
  20. data/{test → spec}/helpers/constant_overrides.rb +0 -0
  21. data/spec/spec_helper.rb +16 -0
  22. data/spec/unit/array_spec.rb +20 -0
  23. data/spec/unit/enumerable_spec.rb +80 -0
  24. data/{test/unit/exceptions_test.rb → spec/unit/exceptions_spec.rb} +17 -17
  25. data/spec/unit/guaranteed_utf8_string_spec.rb +260 -0
  26. data/spec/unit/hash_spec.rb +81 -0
  27. data/spec/unit/hash_with_indifferent_access_spec.rb +100 -0
  28. data/spec/unit/map_compact_spec.rb +25 -0
  29. data/{test/unit/module_test.rb → spec/unit/module_spec.rb} +4 -4
  30. data/spec/unit/multi_sender_spec.rb +54 -0
  31. data/{test/unit/stable_sort_test.rb → spec/unit/stable_sort_spec.rb} +14 -14
  32. data/spec/unit/time_calculations_spec.rb +39 -0
  33. data/{test/unit/utils_test.rb → spec/unit/utils_spec.rb} +14 -14
  34. metadata +59 -37
  35. data/.jenkins/Jenkinsfile +0 -50
  36. data/.jenkins/ruby_build_pod.yml +0 -19
  37. data/test/test_helper.rb +0 -14
  38. data/test/unit/array_test.rb +0 -20
  39. data/test/unit/enumerable_test.rb +0 -80
  40. data/test/unit/guaranteed_utf8_string_test.rb +0 -263
  41. data/test/unit/hash_test.rb +0 -81
  42. data/test/unit/hash_with_indifferent_access_test.rb +0 -100
  43. data/test/unit/map_compact_test.rb +0 -25
  44. data/test/unit/multi_sender_test.rb +0 -56
  45. data/test/unit/time_calculations_test.rb +0 -39
@@ -1,23 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../test_helper'
3
+ require_relative '../spec_helper'
4
4
 
5
- class ExceptionsTest < Minitest::Test
5
+ describe Invoca::Utils do
6
6
  context "exceptions" do
7
7
  context ".retry_on_exception" do
8
- should "default retries: to 1" do
8
+ it "default retries: to 1" do
9
9
  times = 0
10
10
  tries = []
11
11
  result = Invoca::Utils.retry_on_exception(ArgumentError) do |try|
12
12
  tries << try
13
13
  times += 1
14
14
  end
15
- assert_equal 1, result
16
- assert_equal [0], tries
15
+ expect(result).to eq(1)
16
+ expect(tries).to eq([0])
17
17
  end
18
18
 
19
19
  context "when never raising an exception" do
20
- should "return result" do
20
+ it "return result" do
21
21
  times = 0
22
22
  tries = []
23
23
  result = Invoca::Utils.retry_on_exception(ArgumentError, retries: 2) do |try|
@@ -26,26 +26,26 @@ class ExceptionsTest < Minitest::Test
26
26
  try == 0 and raise ArgumentError, '!!!'
27
27
  times
28
28
  end
29
- assert_equal 2, result
30
- assert_equal [0, 1], tries
29
+ expect(result).to eq(2)
30
+ expect(tries).to eq([0,1])
31
31
  end
32
32
  end
33
33
 
34
34
  context "when always raising an exception" do
35
- should "retry and finally raise" do
35
+ it "retry and finally raise" do
36
36
  tries = []
37
- assert_raises(ArgumentError, /!!! 2/) do
37
+ expect do
38
38
  Invoca::Utils.retry_on_exception(ArgumentError, retries: 1) do |try|
39
39
  tries << try
40
40
  raise ArgumentError, "!!! #{try + 1}"
41
41
  end
42
- end
43
- assert_equal [0, 1], tries
42
+ end.to raise_exception(ArgumentError, /!!! 2/)
43
+ expect(tries).to eq([0,1])
44
44
  end
45
45
  end
46
46
 
47
47
  context "when raising but then succeeding" do
48
- should "retry and finally return result" do
48
+ it "retry and finally return result" do
49
49
  times = 0
50
50
  result = Invoca::Utils.retry_on_exception(ArgumentError, retries: 1) do
51
51
  times += 1
@@ -55,12 +55,12 @@ class ExceptionsTest < Minitest::Test
55
55
  times
56
56
  end
57
57
  end
58
- assert_equal 2, result
58
+ expect(result).to eq(2)
59
59
  end
60
60
  end
61
61
 
62
62
  context "when raising different exceptions (array notation) but then succeeding" do
63
- should "retry and finally return result" do
63
+ it "retry and finally return result" do
64
64
  times = 0
65
65
  tries = []
66
66
  result = Invoca::Utils.retry_on_exception([ArgumentError, RuntimeError], retries: 2) do |try|
@@ -75,8 +75,8 @@ class ExceptionsTest < Minitest::Test
75
75
  times
76
76
  end
77
77
  end
78
- assert_equal 3, result
79
- assert_equal [0, 1, 2], tries
78
+ expect(result).to eq(3)
79
+ expect(tries).to eq([0, 1, 2])
80
80
  end
81
81
  end
82
82
  end
@@ -0,0 +1,260 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/invoca/utils/guaranteed_utf8_string'
4
+ require_relative '../spec_helper'
5
+
6
+ describe Invoca::Utils::GuaranteedUTF8String do
7
+ class HasNoTo_sMethod
8
+ undef :to_s
9
+ end
10
+
11
+ class BasicObjectWithKernelMethods
12
+ end
13
+
14
+ class ConvertibleToString
15
+ attr_reader :to_s
16
+
17
+ def initialize(string)
18
+ @to_s = string
19
+ end
20
+ end
21
+
22
+ context '.normalize_string' do
23
+ it 'raise an error if called with an object with no to_s method' do
24
+ expect do
25
+ Invoca::Utils::GuaranteedUTF8String.normalize_string(HasNoTo_sMethod.new)
26
+ end.to raise_exception(ArgumentError, /must be passed a string or an object with a non-Kernel \.to_s method but instead was HasNoTo_sMethod/)
27
+ end
28
+
29
+ it 'raise an error if called with a basic Ruby object' do
30
+ expect do
31
+ Invoca::Utils::GuaranteedUTF8String.normalize_string(BasicObjectWithKernelMethods.new)
32
+ end.to raise_exception(ArgumentError,
33
+ /must be passed a string or an object with a non-Kernel \.to_s method but instead was BasicObjectWithKernelMethods/)
34
+ end
35
+
36
+ it 'not mutate the original string' do
37
+ ascii_string = 'new string'.encode('ASCII')
38
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(ascii_string)
39
+
40
+ expect(encoded_string).to eq(ascii_string)
41
+ expect(ascii_string.encoding).to eq(Encoding::ASCII)
42
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
43
+ end
44
+
45
+ it 'return UTF-8 encoded string' do
46
+ original_string = "this,is,a,valid,utf-8,csv,string\none,two,three,four,five,six,seven\n"
47
+
48
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(original_string)
49
+
50
+ expect(encoded_string).to eq(original_string)
51
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
52
+ end
53
+
54
+ context "normalize_utf16" do
55
+ UTF16_LE_BOM = "\xFF\xFE"
56
+ UTF16_BE_BOM = "\xFE\xFF"
57
+ UTF16_LE_TEST_STRING = (UTF16_LE_BOM + "v\x00a\x00l\x00i\x00d\x00,\x00u\x00t\x00f\x00-\x001\x006\x00\n\x00s\x00e\x00c\x00o\x00n\x00d\x00").force_encoding('BINARY').freeze
58
+ UTF16_BE_TEST_STRING = (UTF16_BE_BOM + "\x00v\x00a\x00l\x00i\x00d\x00,\x00u\x00t\x00f\x00-\x001\x006\x00\n\x00s\x00e\x00c\x00o\x00n\x00d").force_encoding('BINARY').freeze
59
+
60
+ it 'accept UTF-16LE in BINARY and return UTF-8 encoded string when true' do
61
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(UTF16_LE_TEST_STRING, normalize_utf16: true)
62
+
63
+ expect(encoded_string).to eq("valid,utf-16\nsecond")
64
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
65
+ end
66
+
67
+ it 'not check for UTF-16LE in BINARY and return UTF-8 encoded string when false' do
68
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(UTF16_LE_TEST_STRING, normalize_utf16: false)
69
+ expected = "ÿþv\u0000a\u0000l\u0000i\u0000d\u0000,\u0000u\u0000t\u0000f\u0000-\u00001\u00006\u0000\n\u0000s\u0000e\u0000c\u0000o\u0000n\u0000d\u0000"
70
+
71
+ expect(encoded_string).to eq(expected)
72
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
73
+ end
74
+
75
+ it 'accept UTF-16BE in BINARY and return UTF-8 encoded string when true' do
76
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(UTF16_BE_TEST_STRING, normalize_utf16: true)
77
+
78
+ expect(encoded_string).to eq("valid,utf-16\nsecond")
79
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
80
+ end
81
+
82
+ it 'not check for UTF-16BE in BINARY and return UTF-8 encoded string when false' do
83
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(UTF16_BE_TEST_STRING, normalize_utf16: false)
84
+ expected = "þÿ\u0000v\u0000a\u0000l\u0000i\u0000d\u0000,\u0000u\u0000t\u0000f\u0000-\u00001\u00006\u0000\n\u0000s\u0000e\u0000c\u0000o\u0000n\u0000d"
85
+
86
+ expect(encoded_string).to eq(expected)
87
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
88
+ end
89
+
90
+ context "containing embedded CP1252" do
91
+ it 'accept UTF-16LE in BINARY and return UTF-8 encoded string with "private" CP1252 when normalize_utf16: true, normalize_cp1252: false' do
92
+ original_string = (UTF16_LE_BOM + "\x91\x00s\x00m\x00a\x00r\x00t\x00 \x00q\x00u\x00o\x00t\x00e\x00s\x00\x92\x00\n\x00s\x00e\x00c\x00o\x00n\x00d\x00").force_encoding('BINARY')
93
+
94
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(original_string, normalize_utf16: true, normalize_cp1252: false)
95
+
96
+ expect(encoded_string).to eq("\u0091smart quotes\u0092\nsecond")
97
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
98
+ end
99
+
100
+ it 'accept UTF-16LE in BINARY and return UTF-8 encoded string with normalized CP1252 when normalize_utf16: true, normalize_cp1252: true' do
101
+ original_string = (UTF16_LE_BOM + "\x91\x00s\x00m\x00a\x00r\x00t\x00 \x00q\x00u\x00o\x00t\x00e\x00s\x00\x92\x00\n\x00s\x00e\x00c\x00o\x00n\x00d\x00").force_encoding('BINARY')
102
+
103
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(original_string, normalize_utf16: true)
104
+
105
+ expect(encoded_string).to eq("‘smart quotes’\nsecond")
106
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
107
+ end
108
+
109
+ it 'accept UTF-16BE in BINARY and return UTF-8 encoded string when normalize_utf16: true, normalize_cp1252: false' do
110
+ original_string = (UTF16_BE_BOM + "\x00\x91\x00s\x00m\x00a\x00r\x00t\x00 \x00q\x00u\x00o\x00t\x00e\x00s\x00\x92\x00\n\x00s\x00e\x00c\x00o\x00n\x00d").force_encoding('BINARY')
111
+
112
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(original_string, normalize_utf16: true, normalize_cp1252: false)
113
+
114
+ expect(encoded_string).to eq("\u0091smart quotes\u0092\nsecond")
115
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
116
+ end
117
+
118
+ it 'accept UTF-16BE in BINARY and return UTF-8 encoded string when normalize_utf16: true, normalize_cp1252: true' do
119
+ original_string = (UTF16_BE_BOM + "\x00\x91\x00s\x00m\x00a\x00r\x00t\x00 \x00q\x00u\x00o\x00t\x00e\x00s\x00\x92\x00\n\x00s\x00e\x00c\x00o\x00n\x00d").force_encoding('BINARY')
120
+
121
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(original_string, normalize_utf16: true, normalize_cp1252: true)
122
+
123
+ expect(encoded_string).to eq("‘smart quotes’\nsecond")
124
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
125
+ end
126
+ end
127
+ end
128
+
129
+ context 'normalize_cp1252' do
130
+ before do
131
+ @string = "This,is,NOT,a,valid,utf-8,csv,string\r\none,two,three,four,\x81five,\x91smart quotes\x92,\x93suck!\x94\n"
132
+ end
133
+
134
+ it 'raise ArgumentError when false' do
135
+ expect do
136
+ Invoca::Utils::GuaranteedUTF8String.normalize_string(@string, normalize_cp1252: false)
137
+ end.to raise_exception(ArgumentError)
138
+ end
139
+
140
+ it 'return UTF-8 encoded string after falling back to CP1252 encoding when true' do
141
+ expected_string = "This,is,NOT,a,valid,utf-8,csv,string\none,two,three,four,~five,‘smart quotes’,“suck!”\n"
142
+
143
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(@string)
144
+
145
+ expect(encoded_string).to eq(expected_string)
146
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
147
+ end
148
+
149
+ it "encode all 255 UTF-8 characters, returning ~ when the character isn't mapped in CP1252" do
150
+ all_8_bit_characters = (1..255).map(&:chr).join
151
+
152
+ final_utf_8_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(all_8_bit_characters)
153
+ expected_string = "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000A\u000B\u000C\u000A\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007F€~‚ƒ„…†‡ˆ‰Š‹Œ~Ž~~‘’“”•–—˜™š›œ~žŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
154
+
155
+ expect(final_utf_8_string).to eq(expected_string)
156
+ end
157
+ end
158
+
159
+ context 'normalize_newlines' do
160
+ before do
161
+ @string = "This string\n\n\n has line feeds\ncarriage\r\r returns\rand Windows\r\n\r\n new line chars\r\nend of \n\r\r\r\nstring"
162
+ end
163
+
164
+ it 'return UTF-8 encoded string without normalized return chars when false' do
165
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(@string, normalize_newlines: false)
166
+
167
+ expect(encoded_string).to eq(@string)
168
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
169
+ end
170
+
171
+ it 'return UTF-8 encoded string with normalized return chars when true' do
172
+ expected_string = "This string\n\n\n has line feeds\ncarriage\n\n returns\nand Windows\n\n new line chars\nend of \n\n\n\nstring"
173
+
174
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(@string, normalize_newlines: true)
175
+
176
+ expect(encoded_string).to eq(expected_string)
177
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
178
+ end
179
+ end
180
+
181
+ context 'remove_utf8_bom' do
182
+ before do
183
+ @original_string = "\xEF\xBB\xBFthis,is,a,valid,utf-8,csv,string\none,two,three,four,five,six,seven\n"
184
+ end
185
+
186
+ it 'return UTF-8 encoded string with BOM intact when false' do
187
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(@original_string, remove_utf8_bom: false)
188
+
189
+ expect(encoded_string).to eq("\xEF\xBB\xBFthis,is,a,valid,utf-8,csv,string\none,two,three,four,five,six,seven\n")
190
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
191
+ end
192
+
193
+ it 'return UTF-8 encoded string without BOM when true' do
194
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(@original_string, remove_utf8_bom: true)
195
+
196
+ expect(encoded_string).to eq("this,is,a,valid,utf-8,csv,string\none,two,three,four,five,six,seven\n")
197
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
198
+ end
199
+ end
200
+
201
+ context 'replace_unicode_beyond_ffff' do
202
+ before do
203
+ @string = "This string has some ✓ valid UTF-8 but also some 😹 emoji \xf0\x9f\x98\xb9 that are > U+FFFF"
204
+ end
205
+
206
+ it "consider UTF-8 code points that take > 3 bytes (above U+FFFF) to be invalid (since MySQL can't store them unless column is declared mb4) and encode them as ~ when false" do
207
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(@string, replace_unicode_beyond_ffff: false)
208
+
209
+ expect(encoded_string).to eq(@string)
210
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
211
+ end
212
+
213
+ it "consider UTF-8 code points that take > 3 bytes (above U+FFFF) to be invalid (since MySQL can't store them unless column is declared mb4) and encode them as ~ when true" do
214
+ expected_string = 'This string has some ✓ valid UTF-8 but also some ~ emoji ~ that are > U+FFFF'
215
+
216
+ encoded_string = Invoca::Utils::GuaranteedUTF8String.normalize_string(@string, replace_unicode_beyond_ffff: true)
217
+
218
+ expect(encoded_string).to eq(expected_string)
219
+ expect(encoded_string.encoding).to eq(Encoding::UTF_8)
220
+ end
221
+ end
222
+
223
+ context ".normalize_strings" do
224
+ it "walk json doc, replacing strings in: values, inside array elements, and hash keys and values" do
225
+ json_doc = {
226
+ '😹' => "\xE2\x9C\x93 laughing cat",
227
+ ['😹'] => ["\xE2", "\xf0\x9f\x98\xb9", { "newline" => "\r\n" }],
228
+ 'cp1252' => "\x91smart quotes\x92"
229
+ }
230
+
231
+ normalized_json = Invoca::Utils::GuaranteedUTF8String.normalize_all_strings(json_doc,
232
+ normalize_utf16: true,
233
+ normalize_cp1252: true,
234
+ normalize_newlines: true,
235
+ remove_utf8_bom: true,
236
+ replace_unicode_beyond_ffff: true)
237
+
238
+ expect({
239
+ '~' => "✓ laughing cat",
240
+ ['~'] => ["â", "~", { "newline" => "\n" }],
241
+ 'cp1252' => "‘smart quotes’"
242
+ }).to eq(normalized_json)
243
+ end
244
+ end
245
+ end
246
+
247
+ context 'constructor' do
248
+ it 'call normalize_string with the default conversions' do
249
+ expect(Invoca::Utils::GuaranteedUTF8String).to receive(:normalize_string).with('')
250
+
251
+ Invoca::Utils::GuaranteedUTF8String.new('').to_string
252
+ end
253
+
254
+ it 'do the same when using to_s alias' do
255
+ expect(Invoca::Utils::GuaranteedUTF8String).to receive(:normalize_string).with('')
256
+
257
+ Invoca::Utils::GuaranteedUTF8String.new('').to_s
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/invoca/utils/hash.rb'
4
+ require_relative '../spec_helper'
5
+
6
+ describe Hash do
7
+
8
+ context 'select_hash' do
9
+ it 'return a hash containing key/values identified by the block' do
10
+ expect({ 1 => 2, 3 => 4, 6 => 5 }.select_hash { |key, value| key < value }).to eq({ 1 => 2, 3 => 4 })
11
+ end
12
+
13
+ it 'handle blocks that only check values' do
14
+ expect({ 1 => 2, 3 => 4, 6 => 5 }.select_hash { |value| value != 2 }).to eq({ 3 => 4, 6 => 5 })
15
+ end
16
+ end
17
+
18
+ context 'map_hash' do
19
+ it 'return a hash containing values updated by the block' do
20
+ expect({ 1 => 2, 3 => 4, 6 => 5 }.map_hash { |key, value| key < value }).to eq({ 1 => true, 3 => true, 6 => false })
21
+ end
22
+
23
+ it 'handle blocks that only receive values' do
24
+ expect({ 1 => 2, 3 => 4, 6 => 5 }.map_hash { |value| value * 2 }).to eq({ 1 => 4, 3 => 8, 6 => 10 })
25
+ end
26
+ end
27
+
28
+ context 'partition_hash' do
29
+ it 'return two hashes, the first contains the pairs with matching keys, the second contains the rest' do
30
+ expect( { 1 => 2, 3 => 4, 6 => 5 }.partition_hash([1, 3])).to eq([{ 1 => 2, 3 => 4 }, { 6 => 5 }])
31
+ end
32
+
33
+ it 'return two hashes, the first contains the pairs with identified by the block, the second contains the rest' do
34
+ expect( { 1 => 2, 3 => 4, 6 => 5 }.partition_hash { |key, value| key < value }).to eq([{ 1 => 2, 3 => 4 }, { 6 => 5 }])
35
+ end
36
+
37
+ it 'handle no matches' do
38
+ expect( { 1 => 2, 3 => 4, 6 => 5 }.partition_hash([100])).to eq([{}, { 1 => 2, 3 => 4, 6 => 5 }])
39
+ end
40
+
41
+ it 'handle all matches' do
42
+ expect( { 1 => 2, 3 => 4, 6 => 5 }.partition_hash { |_key, _value| true }).to eq([{ 1 => 2, 3 => 4, 6 => 5 }, {}])
43
+ end
44
+ end
45
+
46
+ context '- operator' do
47
+ it 'return a hash with pairs removed that match the keys in rhs array' do
48
+ expect({ 1 => 2, 3 => 4, 6 => 5 } - [1, 6]).to eq({ 3 => 4 })
49
+ end
50
+
51
+ it 'handle empty rhs array' do
52
+ expect({ 1 => 2, 3 => 4, 6 => 5 } - []).to eq({ 1 => 2, 3 => 4, 6 => 5 })
53
+ end
54
+
55
+ it 'handle no matches in rhs array' do
56
+ expect({ 1 => 2, 3 => 4, 6 => 5 } - [100, 600]).to eq({ 1 => 2, 3 => 4, 6 => 5 })
57
+ end
58
+
59
+ it 'handle all matches in rhs array' do
60
+ expect({ 1 => 2, 3 => 4, 6 => 5 } - [1, 3, 6]).to eq({})
61
+ end
62
+ end
63
+
64
+ context '& operator' do
65
+ it 'return a hash with pairs removed that do NOT match the keys in rhs array' do
66
+ expect({ 1 => 2, 3 => 4, 6 => 5 } & [1, 6]).to eq({ 1 => 2, 6 => 5 })
67
+ end
68
+
69
+ it 'handle empty rhs array' do
70
+ expect({ 1 => 2, 3 => 4, 6 => 5 } & []).to eq({})
71
+ end
72
+
73
+ it 'handle no matches in rhs array' do
74
+ expect({ 1 => 2, 3 => 4, 6 => 5 } & [100, 600]).to eq({})
75
+ end
76
+
77
+ it 'handle all matches in rhs array' do
78
+ expect({ 1 => 2, 3 => 4, 6 => 5 } & [1, 3, 6]).to eq({ 1 => 2, 3 => 4, 6 => 5 })
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/hash_with_indifferent_access'
4
+ require_relative '../../lib/invoca/utils/hash_with_indifferent_access.rb'
5
+ require_relative '../spec_helper'
6
+
7
+ describe HashWithIndifferentAccess do
8
+
9
+ context 'partition_hash' do
10
+ before do
11
+ @hash_to_test = HashWithIndifferentAccess.new('one' => 2, :three => 4, 'six' => 5)
12
+ end
13
+
14
+ it 'return two hashes, the first contains the pairs with matching keys, the second contains the rest' do
15
+ expect(@hash_to_test.partition_hash(['one', 'three'])).to eq([{ 'one' => 2, 'three' => 4 }, { 'six' => 5 }])
16
+ end
17
+
18
+ it 'return two hashes, the first contains the pairs with identified by the block, the second contains the rest' do
19
+ expect(@hash_to_test.partition_hash { |key, _value| ['one', 'three'].include?(key) }).to eq([{ 'one' => 2, 'three' => 4 }, { 'six' => 5 }])
20
+ end
21
+
22
+ it 'handle no matches' do
23
+ expect(@hash_to_test.partition_hash([:not_found])).to eq([{}, @hash_to_test])
24
+ end
25
+
26
+ it 'handle all matches' do
27
+ expect(@hash_to_test.partition_hash { |_key, _value| true }).to eq([@hash_to_test, {}])
28
+ end
29
+
30
+ it 'handle symbols for key matching' do
31
+ expect(@hash_to_test.partition_hash([:one, :three])).to eq([{ 'one' => 2, 'three' => 4 }, { 'six' => 5 }])
32
+ end
33
+
34
+ it 'return HashWithIndifferentAccess objects' do
35
+ matched, unmatched = @hash_to_test.partition_hash([:one, :three])
36
+ expect(matched.is_a?(HashWithIndifferentAccess)).to be_truthy
37
+ expect(unmatched.is_a?(HashWithIndifferentAccess)).to be_truthy
38
+ end
39
+ end
40
+
41
+ context '- operator' do
42
+ before do
43
+ @hash_to_test = HashWithIndifferentAccess.new('one' => 2, :three => 4, 'six' => 5)
44
+ end
45
+
46
+ it 'return a hash with pairs removed that match the keys in rhs array' do
47
+ expect(@hash_to_test - ['one', 'six']).to eq({ 'three' => 4 })
48
+ end
49
+
50
+ it 'handle empty rhs array' do
51
+ expect(@hash_to_test - []).to eq(@hash_to_test)
52
+ end
53
+
54
+ it 'handle no matches in rhs array' do
55
+ expect(@hash_to_test - ['100', '600']).to eq(@hash_to_test)
56
+ end
57
+
58
+ it 'handle all matches in rhs array' do
59
+ expect(@hash_to_test - ['one', 'three', 'six']).to eq({})
60
+ end
61
+
62
+ it 'handle symbols for key matching' do
63
+ expect(@hash_to_test - [:one, :three]).to eq({ 'six' => 5 })
64
+ end
65
+
66
+ it 'return HashWithIndifferentAccess object' do
67
+ expect((@hash_to_test - [:one, :three]).is_a?(HashWithIndifferentAccess)).to be_truthy
68
+ end
69
+ end
70
+
71
+ context '& operator' do
72
+ before do
73
+ @hash_to_test = HashWithIndifferentAccess.new('one' => 2, :three => 4, 'six' => 5)
74
+ end
75
+
76
+ it 'return a hash with pairs removed that do NOT match the keys in rhs array' do
77
+ expect(@hash_to_test & ['one', 'six']).to eq({ 'one' => 2, 'six' => 5 })
78
+ end
79
+
80
+ it 'handle empty rhs array' do
81
+ expect(@hash_to_test & []).to eq({})
82
+ end
83
+
84
+ it 'handle no matches in rhs array' do
85
+ expect(@hash_to_test & ['100', '600']).to eq({})
86
+ end
87
+
88
+ it 'handle all matches in rhs array' do
89
+ expect(@hash_to_test & ['one', 'three', 'six']).to eq(@hash_to_test)
90
+ end
91
+
92
+ it 'handle symbols for key matching' do
93
+ expect(@hash_to_test & [:one, :three]).to eq({ 'one' => 2, 'three' => 4 })
94
+ end
95
+
96
+ it 'return HashWithIndifferentAccess object' do
97
+ expect((@hash_to_test & [:one, :three]).is_a?(HashWithIndifferentAccess)).to be_truthy
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper'
4
+
5
+ describe Enumerable do
6
+ it "map_compact" do
7
+ expect([1, 2, nil, 3, 4].map_compact { |item| item**2 if (nil == item ? nil : item.odd?) }).to eq([1, 9])
8
+ end
9
+
10
+ it "map_compact to empty if nothing matches" do
11
+ expect({:a => 'aaa', :b => 'bbb'}.map_compact { |key, value| value if key == :c }).to eq([])
12
+ end
13
+
14
+ it "map_compact a hash" do
15
+ expect({:a => 'aaa', :b => 'bbb'}.map_compact { |key, value| value if key == :b }).to eq(['bbb'])
16
+ end
17
+
18
+ it "map_compact empty collection" do
19
+ expect([].map_compact { |item| true }).to eq([])
20
+ end
21
+
22
+ it "not map_compact false" do
23
+ expect([nil, false].map_compact { |a| a }).to eq([false])
24
+ end
25
+ end
@@ -3,9 +3,9 @@
3
3
  # must require active_support's alias_method_chain first, to ensure that our module monkey patches it
4
4
  require 'active_support/core_ext/module/aliasing'
5
5
  require_relative '../../lib/invoca/utils/module.rb'
6
- require_relative '../test_helper'
6
+ require_relative '../spec_helper'
7
7
 
8
- class ModuleTest < Minitest::Test
8
+ describe Module do
9
9
  class NumberFun
10
10
  def self.around_filter(around_method, method_names)
11
11
  method_names.each do |meth|
@@ -32,8 +32,8 @@ class ModuleTest < Minitest::Test
32
32
  end
33
33
 
34
34
  context 'alias_method_chain' do
35
- should 'not cause infinite recursion when double aliasing the same method' do
36
- assert_equal(4, NumberFun.new.number_printer(3))
35
+ it 'not cause infinite recursion when double aliasing the same method' do
36
+ expect(NumberFun.new.number_printer(3)).to eq(4)
37
37
  end
38
38
  end
39
39
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/invoca/utils/multi_sender.rb'
4
+ require_relative '../spec_helper'
5
+
6
+ describe Invoca::Utils::MultiSender do
7
+ # create enumerable class for testing
8
+ class LinkedList
9
+ include Enumerable
10
+
11
+ def initialize(head, tail = nil)
12
+ @head, @tail = head, tail
13
+ end
14
+
15
+ def <<(item)
16
+ LinkedList.new(item, self)
17
+ end
18
+
19
+ def inspect
20
+ [@head, @tail].inspect
21
+ end
22
+
23
+ def each(&block)
24
+ if block_given?
25
+ block.call(@head)
26
+ @tail&.each(&block)
27
+ else
28
+ to_enum(:each)
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'with custom Enumerable' do
34
+ before do
35
+ linked_list = LinkedList.new('some') << 'short' << 'words'
36
+ @multi_sender = Invoca::Utils::MultiSender.new(linked_list, :map)
37
+ end
38
+
39
+ it 'call the same method on each item in an Enumerable and return the results as an array' do
40
+ expect(@multi_sender.length).to eq([5, 5, 4])
41
+ end
42
+
43
+ it 'handle methods with arguments' do
44
+ expect(@multi_sender.slice(1, 2)).to eq(['or', 'ho', 'om'])
45
+ end
46
+ end
47
+
48
+ context 'with built-in Array' do
49
+ it 'call the same method on each item in an Array and return the results as an array' do
50
+ multi_sender = Invoca::Utils::MultiSender.new(['some', 'short', 'words'], :map)
51
+ expect(multi_sender.length).to eq([4, 5, 5])
52
+ end
53
+ end
54
+ end