invoca-utils 0.4.0 → 0.5.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.
Files changed (46) 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 +10 -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/exceptions.rb +5 -3
  20. data/lib/invoca/utils/version.rb +1 -1
  21. data/{test → spec}/helpers/constant_overrides.rb +0 -0
  22. data/spec/spec_helper.rb +16 -0
  23. data/spec/unit/array_spec.rb +20 -0
  24. data/spec/unit/enumerable_spec.rb +80 -0
  25. data/{test/unit/exceptions_test.rb → spec/unit/exceptions_spec.rb} +17 -17
  26. data/spec/unit/guaranteed_utf8_string_spec.rb +260 -0
  27. data/spec/unit/hash_spec.rb +81 -0
  28. data/spec/unit/hash_with_indifferent_access_spec.rb +100 -0
  29. data/spec/unit/map_compact_spec.rb +25 -0
  30. data/{test/unit/module_test.rb → spec/unit/module_spec.rb} +4 -4
  31. data/spec/unit/multi_sender_spec.rb +54 -0
  32. data/{test/unit/stable_sort_test.rb → spec/unit/stable_sort_spec.rb} +14 -14
  33. data/spec/unit/time_calculations_spec.rb +39 -0
  34. data/{test/unit/utils_test.rb → spec/unit/utils_spec.rb} +14 -14
  35. metadata +59 -37
  36. data/.jenkins/Jenkinsfile +0 -50
  37. data/.jenkins/ruby_build_pod.yml +0 -19
  38. data/test/test_helper.rb +0 -14
  39. data/test/unit/array_test.rb +0 -20
  40. data/test/unit/enumerable_test.rb +0 -80
  41. data/test/unit/guaranteed_utf8_string_test.rb +0 -263
  42. data/test/unit/hash_test.rb +0 -81
  43. data/test/unit/hash_with_indifferent_access_test.rb +0 -100
  44. data/test/unit/map_compact_test.rb +0 -25
  45. data/test/unit/multi_sender_test.rb +0 -56
  46. 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