invoca-utils 0.1.1 → 0.4.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 +5 -5
- data/.gitignore +3 -2
- data/.jenkins/Jenkinsfile +50 -0
- data/.jenkins/ruby_build_pod.yml +19 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +32 -0
- data/Gemfile +11 -5
- data/Gemfile.lock +37 -24
- data/Rakefile +4 -9
- data/invoca-utils.gemspec +2 -3
- data/lib/invoca/utils.rb +11 -5
- data/lib/invoca/utils/array.rb +18 -0
- data/lib/invoca/utils/enumerable.rb +41 -0
- data/lib/invoca/utils/exceptions.rb +27 -0
- data/lib/invoca/utils/guaranteed_utf8_string.rb +22 -7
- data/lib/invoca/utils/hash.rb +43 -0
- data/lib/invoca/utils/hash_with_indifferent_access.rb +42 -0
- data/lib/invoca/utils/module.rb +29 -0
- data/lib/invoca/utils/multi_sender.rb +22 -0
- data/lib/invoca/utils/version.rb +1 -1
- data/test/test_helper.rb +5 -0
- data/test/unit/array_test.rb +20 -0
- data/test/unit/enumerable_test.rb +80 -0
- data/test/unit/exceptions_test.rb +84 -0
- data/test/unit/guaranteed_utf8_string_test.rb +23 -0
- data/test/unit/hash_test.rb +81 -0
- data/test/unit/hash_with_indifferent_access_test.rb +100 -0
- data/test/unit/module_test.rb +39 -0
- data/test/unit/multi_sender_test.rb +56 -0
- metadata +28 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bc2c9db5fc433f7e4f0abfc7ba11ca6a41065e5a4e9a233bb6f38fa8d310e0d0
|
4
|
+
data.tar.gz: 426e3b56dcb95d79b964d613198b2c483050da092999a4ccd4e29348231b0588
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0e762c4b3a2e044abc0a340dc741a9258b2a3afbca3caf575f5f160067fef863ce10d40b03bc374275d0953c9984fd0bf2253df8d5cdcc0e43774bbf42948fe
|
7
|
+
data.tar.gz: fc973c0be3d37b5f8e38f2e12d9ea45a87539c78ff8afe7b7f8adf8351d623f847af68a1d9df96bbc39938894dfb6e2bf2fe4f36ef182278f80b3511381cdedf
|
data/.gitignore
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/groovy
|
2
|
+
@Library('jenkins-pipeline@v0.4.5')
|
3
|
+
import com.invoca.docker.*;
|
4
|
+
pipeline {
|
5
|
+
agent {
|
6
|
+
kubernetes {
|
7
|
+
defaultContainer "ruby"
|
8
|
+
yamlFile ".jenkins/ruby_build_pod.yml"
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
environment {
|
13
|
+
GITHUB_TOKEN = credentials('github_token')
|
14
|
+
BUNDLE_GEM__FURY__IO = credentials('gemfury_deploy_token')
|
15
|
+
}
|
16
|
+
|
17
|
+
stages {
|
18
|
+
stage('Setup') {
|
19
|
+
steps {
|
20
|
+
script {
|
21
|
+
sh 'bundle install'
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
stage('Unit Test') {
|
26
|
+
steps {
|
27
|
+
script {
|
28
|
+
sh 'bundle exec rake'
|
29
|
+
}
|
30
|
+
}
|
31
|
+
post {
|
32
|
+
always { junit '*/reports/*.xml' }
|
33
|
+
success { updateGitHubStatus('clean-build', 'success', 'Unit tests.') }
|
34
|
+
failure { updateGitHubStatus('clean-build', 'failure', 'Unit tests.') }
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
void updateGitHubStatus(String context, String status, String description) {
|
41
|
+
gitHubStatus([
|
42
|
+
repoSlug: 'Invoca/invoca-utils',
|
43
|
+
sha: env.GIT_COMMIT,
|
44
|
+
description: description,
|
45
|
+
context: context,
|
46
|
+
targetURL: env.BUILD_URL,
|
47
|
+
token: env.GITHUB_TOKEN,
|
48
|
+
status: status
|
49
|
+
])
|
50
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
---
|
2
|
+
apiVersion: v1
|
3
|
+
kind: Pod
|
4
|
+
metadata:
|
5
|
+
labels:
|
6
|
+
jenkins/invoca-utils: 'true'
|
7
|
+
namespace: jenkins
|
8
|
+
name: invoca-utils
|
9
|
+
spec:
|
10
|
+
containers:
|
11
|
+
- name: ruby
|
12
|
+
image: ruby:2.6.5
|
13
|
+
tty: true
|
14
|
+
resources:
|
15
|
+
requests:
|
16
|
+
memory: "100Mi"
|
17
|
+
command:
|
18
|
+
- cat
|
19
|
+
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.6.5
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# CHANGELOG for `invoca-utils`
|
2
|
+
|
3
|
+
Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
4
|
+
|
5
|
+
Note: This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [0.4.1] - 2020-06-17
|
8
|
+
### Fixed
|
9
|
+
- Support Ruby < 2.5 by adding `begin`/`end` around `rescue` in `retry_on_exception`.
|
10
|
+
|
11
|
+
## [0.4.0] - 2020-06-09
|
12
|
+
### Added
|
13
|
+
- Added `Invoca::Utils.retry_on_exception`.
|
14
|
+
- Added `Invoca::Utils::GuaranteedUTF8String.normalize_all_strings` to normalize
|
15
|
+
all strings found in a JSON doc of hashes, arrays, and values.
|
16
|
+
|
17
|
+
## [0.3.0] - 2020-04-28
|
18
|
+
### Added
|
19
|
+
- Array::* operator changed to use alias_method instead of prepend to prevent infinite recursion when HoboSupport gem is present
|
20
|
+
- Enumerable::map_and_find, map_with_index, and map_hash methods ported from HoboSupport
|
21
|
+
- Hash::select_hash, map_hash, partition_hash, & and - methods ported from HoboSupport
|
22
|
+
- HashWithIndifferentAccess::partition_hash, & and - methods ported from HoboSupport
|
23
|
+
- Module::alias_method_chain ported from HoboSupport
|
24
|
+
|
25
|
+
## [0.2.0] - 2020-04-27
|
26
|
+
### Added
|
27
|
+
- Enumerable::build_hash method ported from HoboSupport
|
28
|
+
- Enumerable::* operator ported from HoboSupport
|
29
|
+
|
30
|
+
[0.4.0]: https://github.com/Invoca/invoca-utils/compare/v0.3.0...v0.4.0
|
31
|
+
[0.3.0]: https://github.com/Invoca/invoca-utils/compare/v0.2.0...v0.3.0
|
32
|
+
[0.2.0]: https://github.com/Invoca/invoca-utils/compare/v0.1.1...v0.2.0
|
data/Gemfile
CHANGED
@@ -4,13 +4,19 @@ source 'https://rubygems.org'
|
|
4
4
|
|
5
5
|
gemspec
|
6
6
|
|
7
|
-
|
7
|
+
source 'https://rubygems.org' do
|
8
|
+
gem 'activesupport', '~> 4.2'
|
9
|
+
gem 'minitest'
|
10
|
+
gem 'minitest-reporters'
|
11
|
+
gem 'pry'
|
8
12
|
gem 'rake'
|
9
|
-
gem 'test-unit', '= 1.2.3'
|
10
13
|
gem 'rr', '=1.1.2'
|
11
|
-
gem 'shoulda', '= 3.5.0'
|
12
|
-
gem 'pry'
|
13
14
|
gem 'ruby-prof'
|
14
|
-
gem '
|
15
|
+
gem 'shoulda', '= 3.5.0'
|
16
|
+
gem 'test-unit', '= 1.2.3'
|
15
17
|
gem 'tzinfo'
|
16
18
|
end
|
19
|
+
|
20
|
+
source 'https://gem.fury.io/invoca' do
|
21
|
+
gem 'test_overrides', '~> 0.13'
|
22
|
+
end
|
data/Gemfile.lock
CHANGED
@@ -1,30 +1,39 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
invoca-utils (0.
|
4
|
+
invoca-utils (0.4.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
|
+
remote: https://gem.fury.io/invoca/
|
8
9
|
specs:
|
9
|
-
activesupport (
|
10
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
10
|
+
activesupport (4.2.11.3)
|
11
11
|
i18n (~> 0.7)
|
12
12
|
minitest (~> 5.1)
|
13
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
13
14
|
tzinfo (~> 1.1)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
ansi (1.5.0)
|
16
|
+
builder (3.2.4)
|
17
|
+
coderay (1.1.3)
|
18
|
+
concurrent-ruby (1.1.6)
|
19
|
+
hoe (3.22.1)
|
20
|
+
rake (>= 0.8, < 15.0)
|
21
|
+
i18n (0.9.5)
|
19
22
|
concurrent-ruby (~> 1.0)
|
20
|
-
method_source (0.
|
21
|
-
minitest (5.
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
method_source (1.0.0)
|
24
|
+
minitest (5.14.1)
|
25
|
+
minitest-reporters (1.4.2)
|
26
|
+
ansi
|
27
|
+
builder
|
28
|
+
minitest (>= 5.0)
|
29
|
+
ruby-progressbar
|
30
|
+
pry (0.13.1)
|
31
|
+
coderay (~> 1.1)
|
32
|
+
method_source (~> 1.0)
|
33
|
+
rake (13.0.1)
|
26
34
|
rr (1.1.2)
|
27
|
-
ruby-prof (
|
35
|
+
ruby-prof (1.4.1)
|
36
|
+
ruby-progressbar (1.10.1)
|
28
37
|
shoulda (3.5.0)
|
29
38
|
shoulda-context (~> 1.0, >= 1.0.1)
|
30
39
|
shoulda-matchers (>= 1.4.1, < 3.0)
|
@@ -33,23 +42,27 @@ GEM
|
|
33
42
|
activesupport (>= 3.0.0)
|
34
43
|
test-unit (1.2.3)
|
35
44
|
hoe (>= 1.5.1)
|
45
|
+
test_overrides (0.13.0)
|
36
46
|
thread_safe (0.3.6)
|
37
|
-
tzinfo (1.2.
|
47
|
+
tzinfo (1.2.7)
|
38
48
|
thread_safe (~> 0.1)
|
39
49
|
|
40
50
|
PLATFORMS
|
41
51
|
ruby
|
42
52
|
|
43
53
|
DEPENDENCIES
|
54
|
+
activesupport (~> 4.2)!
|
44
55
|
invoca-utils!
|
45
|
-
minitest
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
56
|
+
minitest!
|
57
|
+
minitest-reporters!
|
58
|
+
pry!
|
59
|
+
rake!
|
60
|
+
rr (= 1.1.2)!
|
61
|
+
ruby-prof!
|
62
|
+
shoulda (= 3.5.0)!
|
63
|
+
test-unit (= 1.2.3)!
|
64
|
+
test_overrides (~> 0.13)!
|
65
|
+
tzinfo!
|
53
66
|
|
54
67
|
BUNDLED WITH
|
55
68
|
1.17.3
|
data/Rakefile
CHANGED
@@ -3,15 +3,10 @@
|
|
3
3
|
|
4
4
|
require "bundler/gem_tasks"
|
5
5
|
require 'rake/testtask'
|
6
|
-
|
7
|
-
Rake::TestTask.new(:test) do |t|
|
8
|
-
t.libs << 'lib'
|
9
|
-
t.libs << 'test'
|
10
|
-
t.pattern = 'test/**/*_test.rb'
|
11
|
-
t.verbose = false
|
12
|
-
end
|
6
|
+
require 'rake_test_warning_false'
|
13
7
|
|
14
8
|
task default: :test
|
15
9
|
|
16
|
-
|
17
|
-
|
10
|
+
Rake::TestTask.new do |t|
|
11
|
+
t.pattern = "test/**/*_test.rb"
|
12
|
+
end
|
data/invoca-utils.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
lib = File.expand_path('
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
3
|
require 'invoca/utils/version'
|
4
4
|
|
@@ -7,8 +7,7 @@ Gem::Specification.new do |spec|
|
|
7
7
|
spec.version = Invoca::Utils::VERSION
|
8
8
|
spec.authors = ["Invoca development"]
|
9
9
|
spec.email = ["development@invoca.com"]
|
10
|
-
spec.summary =
|
11
|
-
spec.description = %q{A public collection of helpers used in multiple projects}
|
10
|
+
spec.summary = "A public collection of helpers used in multiple projects"
|
12
11
|
spec.homepage = ""
|
13
12
|
spec.license = "MIT"
|
14
13
|
|
data/lib/invoca/utils.rb
CHANGED
@@ -1,19 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module Invoca
|
4
|
+
module Utils
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require "invoca/utils/module"
|
9
|
+
require "invoca/utils/array"
|
10
|
+
require "invoca/utils/enumerable"
|
3
11
|
require "invoca/utils/diff"
|
12
|
+
require "invoca/utils/hash"
|
13
|
+
require "invoca/utils/hash_with_indifferent_access"
|
4
14
|
require "invoca/utils/http"
|
5
15
|
require "invoca/utils/map_compact"
|
6
16
|
require "invoca/utils/min_max"
|
7
17
|
require "invoca/utils/stable_sort"
|
8
18
|
require "invoca/utils/time"
|
19
|
+
require "invoca/utils/exceptions"
|
9
20
|
require "invoca/utils/guaranteed_utf8_string"
|
10
21
|
require "invoca/utils/version"
|
11
22
|
|
12
|
-
module Invoca
|
13
|
-
module Utils
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
23
|
unless defined?(Diff)
|
18
24
|
Diff = Invoca::Utils::Diff
|
19
25
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './multi_sender'
|
4
|
+
|
5
|
+
# Invoca ::Array extensions
|
6
|
+
# TODO: Once the hobo_support gem is no longer used by any of our code, use prepend instead of alias
|
7
|
+
class Array
|
8
|
+
|
9
|
+
alias_method :original_multiply_operator, :* # rubocop:disable Style/Alias
|
10
|
+
|
11
|
+
def *(rhs = nil)
|
12
|
+
if rhs
|
13
|
+
original_multiply_operator(rhs)
|
14
|
+
else
|
15
|
+
Invoca::Utils::MultiSender.new(self, :map)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './multi_sender'
|
4
|
+
|
5
|
+
# Invoca ::Enumerable extensions
|
6
|
+
module Enumerable
|
7
|
+
def map_and_find(not_found = nil)
|
8
|
+
each do |x|
|
9
|
+
val = yield(x)
|
10
|
+
return val if val
|
11
|
+
end
|
12
|
+
not_found
|
13
|
+
end
|
14
|
+
|
15
|
+
def map_with_index(res = [])
|
16
|
+
each_with_index do |x, i|
|
17
|
+
res << yield(x, i)
|
18
|
+
end
|
19
|
+
res
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_hash(res = {})
|
23
|
+
each do |x|
|
24
|
+
pair = block_given? ? yield(x) : x
|
25
|
+
res[pair.first] = pair.last if pair
|
26
|
+
end
|
27
|
+
res
|
28
|
+
end
|
29
|
+
|
30
|
+
def map_hash(res = {})
|
31
|
+
each do |x|
|
32
|
+
v = yield x
|
33
|
+
res[x] = v
|
34
|
+
end
|
35
|
+
res
|
36
|
+
end
|
37
|
+
|
38
|
+
def *
|
39
|
+
Invoca::Utils::MultiSender.new(self, :map)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Invoca
|
4
|
+
module Utils
|
5
|
+
class << self
|
6
|
+
# Yields and rescues any exceptions given in `exception_classes`, retrying the given number of times.
|
7
|
+
# The final retry does not rescue any exceptions.
|
8
|
+
# The try number (0..retries) is yielded as a block param.
|
9
|
+
#
|
10
|
+
# @param [Class or Array(Class)] exception_classes - exception()s) to rescue
|
11
|
+
# @param [Integer] retries: - 1+ count of retries (1 retry = up to 2 tries total)
|
12
|
+
# @param [Proc] before_retry - optional proc which is called before each retry, with the exception passed as a block param
|
13
|
+
# @return the value from yield
|
14
|
+
def retry_on_exception(exception_classes, retries: 1, before_retry: nil)
|
15
|
+
retries.times do |attempt_number|
|
16
|
+
begin
|
17
|
+
return yield(attempt_number)
|
18
|
+
rescue *Array(exception_classes) => ex
|
19
|
+
before_retry&.call(ex)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
yield(retries) # no rescue for this last try, so any exceptions will raise out
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -21,6 +21,7 @@ module Invoca
|
|
21
21
|
REPLACE_CHARACTER = '~'
|
22
22
|
|
23
23
|
class << self
|
24
|
+
# normalizes a string to UTF-8
|
24
25
|
def normalize_string(orig_string,
|
25
26
|
normalize_utf16: true,
|
26
27
|
normalize_cp1252: true,
|
@@ -43,6 +44,22 @@ module Invoca
|
|
43
44
|
replace_unicode_beyond_ffff: replace_unicode_beyond_ffff)
|
44
45
|
end
|
45
46
|
|
47
|
+
# Walks a JSON doc of hashes, arrays, and values and normalizes all strings found to UTF-8
|
48
|
+
def normalize_all_strings(value, **options)
|
49
|
+
case value
|
50
|
+
when Hash
|
51
|
+
value.each_with_object({}) do |(k, v), result|
|
52
|
+
result[normalize_all_strings(k, **options)] = normalize_all_strings(v, **options)
|
53
|
+
end
|
54
|
+
when Array
|
55
|
+
value.map { |v| normalize_all_strings(v, **options) }
|
56
|
+
when String
|
57
|
+
normalize_string(value, **options)
|
58
|
+
else
|
59
|
+
value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
46
63
|
private
|
47
64
|
|
48
65
|
def normalize_string_from_utf8(string,
|
@@ -54,13 +71,11 @@ module Invoca
|
|
54
71
|
found_utf_16 = normalize_utf_16(string, normalize_cp1252: normalize_cp1252) if normalize_utf16
|
55
72
|
if found_utf_16
|
56
73
|
string.encode!('UTF-8')
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
raise ArgumentError, 'Could not normalize to utf8 due to invalid characters (probably CP1252)'
|
63
|
-
end
|
74
|
+
elsif !string.valid_encoding?
|
75
|
+
if normalize_cp1252
|
76
|
+
cp1252_to_utf_8(string)
|
77
|
+
else
|
78
|
+
raise ArgumentError, 'Could not normalize to utf8 due to invalid characters (probably CP1252)'
|
64
79
|
end
|
65
80
|
end
|
66
81
|
normalize_newlines(string) if normalize_newlines
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Invoca ::Hash extensions
|
4
|
+
class Hash
|
5
|
+
def select_hash(&block)
|
6
|
+
res = {}
|
7
|
+
each { |k, v| res[k] = v if (block.arity == 1 ? yield(v) : yield(k, v)) } # rubocop:disable Style/ParenthesesAroundCondition
|
8
|
+
res
|
9
|
+
end
|
10
|
+
|
11
|
+
def map_hash(&block)
|
12
|
+
res = {}
|
13
|
+
each { |k, v| res[k] = block.arity == 1 ? yield(v) : yield(k, v) }
|
14
|
+
res
|
15
|
+
end
|
16
|
+
|
17
|
+
def partition_hash(keys = nil)
|
18
|
+
yes = {}
|
19
|
+
no = {}
|
20
|
+
each do |k, v|
|
21
|
+
if block_given? ? yield(k, v) : keys.include?(k)
|
22
|
+
yes[k] = v
|
23
|
+
else
|
24
|
+
no[k] = v
|
25
|
+
end
|
26
|
+
end
|
27
|
+
[yes, no]
|
28
|
+
end
|
29
|
+
|
30
|
+
# rubocop:disable Naming/BinaryOperatorParameterName
|
31
|
+
def -(keys)
|
32
|
+
res = {}
|
33
|
+
each_pair { |k, v| res[k] = v unless k.in?(keys) }
|
34
|
+
res
|
35
|
+
end
|
36
|
+
|
37
|
+
def &(keys)
|
38
|
+
res = {}
|
39
|
+
keys.each { |k| res[k] = self[k] if has_key?(k) }
|
40
|
+
res
|
41
|
+
end
|
42
|
+
# rubocop:enable Naming/BinaryOperatorParameterName
|
43
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Invoca ::HashWithIndifferentAccess extensions
|
4
|
+
if defined? HashWithIndifferentAccess
|
5
|
+
|
6
|
+
class HashWithIndifferentAccess
|
7
|
+
|
8
|
+
# rubocop:disable Naming/BinaryOperatorParameterName
|
9
|
+
def -(keys)
|
10
|
+
res = HashWithIndifferentAccess.new
|
11
|
+
keys = keys.map { |k| k.is_a?(Symbol) ? k.to_s : k }
|
12
|
+
each_pair { |k, v| res[k] = v unless k.in?(keys) }
|
13
|
+
res
|
14
|
+
end
|
15
|
+
|
16
|
+
def &(keys)
|
17
|
+
res = HashWithIndifferentAccess.new
|
18
|
+
keys.each do |k|
|
19
|
+
k = k.to_s if k.is_a?(Symbol)
|
20
|
+
res[k] = self[k] if has_key?(k)
|
21
|
+
end
|
22
|
+
res
|
23
|
+
end
|
24
|
+
# rubocop:enable Naming/BinaryOperatorParameterName
|
25
|
+
|
26
|
+
def partition_hash(keys = nil)
|
27
|
+
keys = keys&.map { |k| k.is_a?(Symbol) ? k.to_s : k }
|
28
|
+
yes = HashWithIndifferentAccess.new
|
29
|
+
no = HashWithIndifferentAccess.new
|
30
|
+
each do |k, v|
|
31
|
+
if block_given? ? yield(k, v) : keys.include?(k)
|
32
|
+
yes[k] = v
|
33
|
+
else
|
34
|
+
no[k] = v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
[yes, no]
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Module
|
4
|
+
|
5
|
+
# Custom alias_method_chain that won't cause infinite recursion if called twice.
|
6
|
+
# NOTE: Calling alias_method_chain on alias_method_chain was just way too confusing, so I copied it :-/
|
7
|
+
def alias_method_chain(target, feature)
|
8
|
+
# Strip out punctuation on predicates, bang or writer methods since
|
9
|
+
# e.g. target?_without_feature is not a valid method name.
|
10
|
+
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1 # rubocop:disable Style/PerlBackrefs
|
11
|
+
yield(aliased_target, punctuation) if block_given?
|
12
|
+
|
13
|
+
with_method = "#{aliased_target}_with_#{feature}#{punctuation}"
|
14
|
+
without_method = "#{aliased_target}_without_#{feature}#{punctuation}"
|
15
|
+
|
16
|
+
unless method_defined?(without_method)
|
17
|
+
alias_method without_method, target
|
18
|
+
alias_method target, with_method
|
19
|
+
|
20
|
+
if public_method_defined?(without_method)
|
21
|
+
public target
|
22
|
+
elsif protected_method_defined?(without_method)
|
23
|
+
protected target
|
24
|
+
elsif private_method_defined?(without_method)
|
25
|
+
private target
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Invoca
|
4
|
+
module Utils
|
5
|
+
class MultiSender
|
6
|
+
undef_method(*(instance_methods - [:__id__, :__send__, :object_id]))
|
7
|
+
|
8
|
+
def initialize(enumerable, method)
|
9
|
+
@enumerable = enumerable
|
10
|
+
@method = method
|
11
|
+
end
|
12
|
+
|
13
|
+
# rubocop:disable Style/MethodMissingSuper
|
14
|
+
# rubocop:disable Style/MissingRespondToMissing
|
15
|
+
def method_missing(name, *args, &block)
|
16
|
+
@enumerable.send(@method) { |x| x.send(name, *args, &block) }
|
17
|
+
end
|
18
|
+
# rubocop:enable Style/MethodMissingSuper
|
19
|
+
# rubocop:enable Style/MissingRespondToMissing
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/invoca/utils/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../lib/invoca/utils/array.rb'
|
4
|
+
require_relative '../test_helper'
|
5
|
+
|
6
|
+
class ArrayTest < Minitest::Test
|
7
|
+
context '* operator' do
|
8
|
+
should 'call the same method on each item in an array and return the results as an array' do
|
9
|
+
assert_equal([4, 5, 5], ['some', 'short', 'words'].*.length)
|
10
|
+
end
|
11
|
+
|
12
|
+
should 'handle methods with arguments' do
|
13
|
+
assert_equal(['om', 'ho', 'or'], ['some', 'short', 'words'].*.slice(1, 2))
|
14
|
+
end
|
15
|
+
|
16
|
+
should 'not alter normal behavior (multiplication) when there is a right hand side to the expression' do
|
17
|
+
assert_equal(['some', 'short', 'words', 'some', 'short', 'words'], ['some', 'short', 'words'] * 2)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require_relative '../../lib/invoca/utils/enumerable.rb'
|
5
|
+
require_relative '../test_helper'
|
6
|
+
|
7
|
+
class EnumerableTest < Minitest::Test
|
8
|
+
|
9
|
+
context 'map_and_find' do
|
10
|
+
should 'return the mapped value of the first match' do
|
11
|
+
assert_equal('FOUND 3', [1, 2, 3, 4].map_and_find { |v| 'FOUND 3' if v == 3 })
|
12
|
+
end
|
13
|
+
|
14
|
+
should 'return the mapped value of the first match, even if there are multiple matches' do
|
15
|
+
assert_equal('FOUND 3', [1, 2, 3, 4].map_and_find { |v| "FOUND #{v}" if v > 2 })
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'return the provided argument if the value is not found' do
|
19
|
+
assert_equal('NOT FOUND', [1, 2, 3, 4].map_and_find('NOT FOUND') { |v| "FOUND 6" if v == 6 })
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'return nil if the value is not found and no argument is provided' do
|
23
|
+
assert_nil([1, 2, 3, 4].map_and_find { |v| "FOUND 6" if v == 6 })
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'map_with_index' do
|
28
|
+
should 'call the block with the value and index' do
|
29
|
+
assert_equal([10, 21, 32, 43], [10, 20, 30, 40].map_with_index { |v, index| v + index })
|
30
|
+
end
|
31
|
+
|
32
|
+
should 'assumulate into the provided enumerable' do
|
33
|
+
assert_equal([1, 10, 21, 32, 43], [10, 20, 30, 40].map_with_index([1]) { |v, index| v + index })
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'map_hash' do
|
38
|
+
should 'convert enumerables into a hash using the value for key and the map result as the hash value' do
|
39
|
+
assert_equal({ 1 => 11, 2 => 12, 3 => 13 }, [1, 2, 3].map_hash { |v| v + 10 })
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'includes nils returned from map' do
|
43
|
+
assert_equal({ 1 => 11, 2 => nil, 3 => 13 }, [1, 2, 3].map_hash { |v| v + 10 unless v == 2 })
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'build_hash' do
|
48
|
+
should 'convert arrays of [key, value] to a hash of { key => value }' do
|
49
|
+
assert_equal({ 'some' => 4, 'short' => 5, 'words' => 5 }, ['some', 'short', 'words'].build_hash { |s| [s, s.length] })
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'ignore nils returned from map' do
|
53
|
+
assert_equal({ 'some' => 4, 'words' => 5 }, ['some', 'short', 'words'].build_hash { |s| s == 'short' ? nil : [s, s.length] })
|
54
|
+
end
|
55
|
+
|
56
|
+
# these seem like erroneous behavior, but, they have been left as-is for backward compatibility with hobosupport::Enumerable::build_hash
|
57
|
+
|
58
|
+
should 'convert arrays of [single_value] to a hash of { single_value => single_value }' do
|
59
|
+
assert_equal({ 'some' => 4, 'short' => 'short', 'words' => 5 }, ['some', 'short', 'words'].build_hash { |s| s == 'short' ? [s] : [s, s.length] })
|
60
|
+
end
|
61
|
+
|
62
|
+
should 'convert arrays of [first, ..., last] to a hash of { first => last }' do
|
63
|
+
assert_equal({ 'some' => 4, 'short' => 'three', 'words' => 5 }, ['some', 'short', 'words'].build_hash { |s| s == 'short' ? [s, 'two', 'three'] : [s, s.length] })
|
64
|
+
end
|
65
|
+
|
66
|
+
should 'convert empty arrays to a hash of { nil => nil }' do
|
67
|
+
assert_equal({ 'some' => 4, nil => nil, 'words' => 5 }, ['some', 'short', 'words'].build_hash { |s| s == 'short' ? [] : [s, s.length] })
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context '* operator' do
|
72
|
+
should 'call the same method on each item in an Set and return the results as an array' do
|
73
|
+
assert_equal([4, 5, 5], Set['some', 'short', 'words'].*.length)
|
74
|
+
end
|
75
|
+
|
76
|
+
should 'call the same method on each item in an Hash and return the results as an array' do
|
77
|
+
assert_equal(['key1:value1', 'key2:value2'], { key1: 'value1', key2: 'value2' }.*.join(':'))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../test_helper'
|
4
|
+
|
5
|
+
class ExceptionsTest < Minitest::Test
|
6
|
+
context "exceptions" do
|
7
|
+
context ".retry_on_exception" do
|
8
|
+
should "default retries: to 1" do
|
9
|
+
times = 0
|
10
|
+
tries = []
|
11
|
+
result = Invoca::Utils.retry_on_exception(ArgumentError) do |try|
|
12
|
+
tries << try
|
13
|
+
times += 1
|
14
|
+
end
|
15
|
+
assert_equal 1, result
|
16
|
+
assert_equal [0], tries
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when never raising an exception" do
|
20
|
+
should "return result" do
|
21
|
+
times = 0
|
22
|
+
tries = []
|
23
|
+
result = Invoca::Utils.retry_on_exception(ArgumentError, retries: 2) do |try|
|
24
|
+
tries << try
|
25
|
+
times += 1
|
26
|
+
try == 0 and raise ArgumentError, '!!!'
|
27
|
+
times
|
28
|
+
end
|
29
|
+
assert_equal 2, result
|
30
|
+
assert_equal [0, 1], tries
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when always raising an exception" do
|
35
|
+
should "retry and finally raise" do
|
36
|
+
tries = []
|
37
|
+
assert_raises(ArgumentError, /!!! 2/) do
|
38
|
+
Invoca::Utils.retry_on_exception(ArgumentError, retries: 1) do |try|
|
39
|
+
tries << try
|
40
|
+
raise ArgumentError, "!!! #{try + 1}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
assert_equal [0, 1], tries
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when raising but then succeeding" do
|
48
|
+
should "retry and finally return result" do
|
49
|
+
times = 0
|
50
|
+
result = Invoca::Utils.retry_on_exception(ArgumentError, retries: 1) do
|
51
|
+
times += 1
|
52
|
+
if times == 1
|
53
|
+
raise ArgumentError, "!!! #{times}"
|
54
|
+
else
|
55
|
+
times
|
56
|
+
end
|
57
|
+
end
|
58
|
+
assert_equal 2, result
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "when raising different exceptions (array notation) but then succeeding" do
|
63
|
+
should "retry and finally return result" do
|
64
|
+
times = 0
|
65
|
+
tries = []
|
66
|
+
result = Invoca::Utils.retry_on_exception([ArgumentError, RuntimeError], retries: 2) do |try|
|
67
|
+
tries << try
|
68
|
+
times += 1
|
69
|
+
case times
|
70
|
+
when 1
|
71
|
+
raise ArgumentError, "!!! #{times}"
|
72
|
+
when 2
|
73
|
+
raise RuntimeError, "!!! #{times}"
|
74
|
+
else
|
75
|
+
times
|
76
|
+
end
|
77
|
+
end
|
78
|
+
assert_equal 3, result
|
79
|
+
assert_equal [0, 1, 2], tries
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -221,6 +221,29 @@ class GuaranteedUTF8StringTest < Minitest::Test
|
|
221
221
|
assert_equal Encoding::UTF_8, encoded_string.encoding
|
222
222
|
end
|
223
223
|
end
|
224
|
+
|
225
|
+
context ".normalize_strings" do
|
226
|
+
should "walk json doc, replacing strings in: values, inside array elements, and hash keys and values" do
|
227
|
+
json_doc = {
|
228
|
+
'😹' => "\xE2\x9C\x93 laughing cat",
|
229
|
+
['😹'] => ["\xE2", "\xf0\x9f\x98\xb9", { "newline" => "\r\n" }],
|
230
|
+
'cp1252' => "\x91smart quotes\x92"
|
231
|
+
}
|
232
|
+
|
233
|
+
normalized_json = Invoca::Utils::GuaranteedUTF8String.normalize_all_strings(json_doc,
|
234
|
+
normalize_utf16: true,
|
235
|
+
normalize_cp1252: true,
|
236
|
+
normalize_newlines: true,
|
237
|
+
remove_utf8_bom: true,
|
238
|
+
replace_unicode_beyond_ffff: true)
|
239
|
+
|
240
|
+
assert_equal({
|
241
|
+
'~' => "✓ laughing cat",
|
242
|
+
['~'] => ["â", "~", { "newline" => "\n" }],
|
243
|
+
'cp1252' => "‘smart quotes’"
|
244
|
+
}, normalized_json)
|
245
|
+
end
|
246
|
+
end
|
224
247
|
end
|
225
248
|
|
226
249
|
context 'constructor' do
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../lib/invoca/utils/hash.rb'
|
4
|
+
require_relative '../test_helper'
|
5
|
+
|
6
|
+
class HashTest < Minitest::Test
|
7
|
+
|
8
|
+
context 'select_hash' do
|
9
|
+
should 'return a hash containing key/values identified by the block' do
|
10
|
+
assert_equal({ 1 => 2, 3 => 4 }, { 1 => 2, 3 => 4, 6 => 5 }.select_hash { |key, value| key < value })
|
11
|
+
end
|
12
|
+
|
13
|
+
should 'handle blocks that only check values' do
|
14
|
+
assert_equal({ 3 => 4, 6 => 5 }, { 1 => 2, 3 => 4, 6 => 5 }.select_hash { |value| value != 2 })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'map_hash' do
|
19
|
+
should 'return a hash containing values updated by the block' do
|
20
|
+
assert_equal({ 1 => true, 3 => true, 6 => false }, { 1 => 2, 3 => 4, 6 => 5 }.map_hash { |key, value| key < value })
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'handle blocks that only receive values' do
|
24
|
+
assert_equal({ 1 => 4, 3 => 8, 6 => 10 }, { 1 => 2, 3 => 4, 6 => 5 }.map_hash { |value| value * 2 })
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'partition_hash' do
|
29
|
+
should 'return two hashes, the first contains the pairs with matching keys, the second contains the rest' do
|
30
|
+
assert_equal([{ 1 => 2, 3 => 4 }, { 6 => 5 }], { 1 => 2, 3 => 4, 6 => 5 }.partition_hash([1, 3]))
|
31
|
+
end
|
32
|
+
|
33
|
+
should 'return two hashes, the first contains the pairs with identified by the block, the second contains the rest' do
|
34
|
+
assert_equal([{ 1 => 2, 3 => 4 }, { 6 => 5 }], { 1 => 2, 3 => 4, 6 => 5 }.partition_hash { |key, value| key < value })
|
35
|
+
end
|
36
|
+
|
37
|
+
should 'handle no matches' do
|
38
|
+
assert_equal([{}, { 1 => 2, 3 => 4, 6 => 5 }], { 1 => 2, 3 => 4, 6 => 5 }.partition_hash([100]))
|
39
|
+
end
|
40
|
+
|
41
|
+
should 'handle all matches' do
|
42
|
+
assert_equal([{ 1 => 2, 3 => 4, 6 => 5 }, {}], { 1 => 2, 3 => 4, 6 => 5 }.partition_hash { |_key, _value| true })
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context '- operator' do
|
47
|
+
should 'return a hash with pairs removed that match the keys in rhs array' do
|
48
|
+
assert_equal({ 3 => 4 }, { 1 => 2, 3 => 4, 6 => 5 } - [1, 6])
|
49
|
+
end
|
50
|
+
|
51
|
+
should 'handle empty rhs array' do
|
52
|
+
assert_equal({ 1 => 2, 3 => 4, 6 => 5 }, { 1 => 2, 3 => 4, 6 => 5 } - [])
|
53
|
+
end
|
54
|
+
|
55
|
+
should 'handle no matches in rhs array' do
|
56
|
+
assert_equal({ 1 => 2, 3 => 4, 6 => 5 }, { 1 => 2, 3 => 4, 6 => 5 } - [100, 600])
|
57
|
+
end
|
58
|
+
|
59
|
+
should 'handle all matches in rhs array' do
|
60
|
+
assert_equal({}, { 1 => 2, 3 => 4, 6 => 5 } - [1, 3, 6])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context '& operator' do
|
65
|
+
should 'return a hash with pairs removed that do NOT match the keys in rhs array' do
|
66
|
+
assert_equal({ 1 => 2, 6 => 5 }, { 1 => 2, 3 => 4, 6 => 5 } & [1, 6])
|
67
|
+
end
|
68
|
+
|
69
|
+
should 'handle empty rhs array' do
|
70
|
+
assert_equal({}, { 1 => 2, 3 => 4, 6 => 5 } & [])
|
71
|
+
end
|
72
|
+
|
73
|
+
should 'handle no matches in rhs array' do
|
74
|
+
assert_equal({}, { 1 => 2, 3 => 4, 6 => 5 } & [100, 600])
|
75
|
+
end
|
76
|
+
|
77
|
+
should 'handle all matches in rhs array' do
|
78
|
+
assert_equal({ 1 => 2, 3 => 4, 6 => 5 }, { 1 => 2, 3 => 4, 6 => 5 } & [1, 3, 6])
|
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 '../test_helper'
|
6
|
+
|
7
|
+
class HashWithIndifferentAccessTest < Minitest::Test
|
8
|
+
|
9
|
+
context 'partition_hash' do
|
10
|
+
setup do
|
11
|
+
@hash_to_test = HashWithIndifferentAccess.new('one' => 2, :three => 4, 'six' => 5)
|
12
|
+
end
|
13
|
+
|
14
|
+
should 'return two hashes, the first contains the pairs with matching keys, the second contains the rest' do
|
15
|
+
assert_equal([{ 'one' => 2, 'three' => 4 }, { 'six' => 5 }], @hash_to_test.partition_hash(['one', 'three']))
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'return two hashes, the first contains the pairs with identified by the block, the second contains the rest' do
|
19
|
+
assert_equal([{ 'one' => 2, 'three' => 4 }, { 'six' => 5 }], @hash_to_test.partition_hash { |key, _value| ['one', 'three'].include?(key) })
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'handle no matches' do
|
23
|
+
assert_equal([{}, @hash_to_test], @hash_to_test.partition_hash([:not_found]))
|
24
|
+
end
|
25
|
+
|
26
|
+
should 'handle all matches' do
|
27
|
+
assert_equal([@hash_to_test, {}], @hash_to_test.partition_hash { |_key, _value| true })
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'handle symbols for key matching' do
|
31
|
+
assert_equal([{ 'one' => 2, 'three' => 4 }, { 'six' => 5 }], @hash_to_test.partition_hash([:one, :three]))
|
32
|
+
end
|
33
|
+
|
34
|
+
should 'return HashWithIndifferentAccess objects' do
|
35
|
+
matched, unmatched = @hash_to_test.partition_hash([:one, :three])
|
36
|
+
assert(matched.is_a?(HashWithIndifferentAccess))
|
37
|
+
assert(unmatched.is_a?(HashWithIndifferentAccess))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context '- operator' do
|
42
|
+
setup do
|
43
|
+
@hash_to_test = HashWithIndifferentAccess.new('one' => 2, :three => 4, 'six' => 5)
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'return a hash with pairs removed that match the keys in rhs array' do
|
47
|
+
assert_equal({ 'three' => 4 }, @hash_to_test - ['one', 'six'])
|
48
|
+
end
|
49
|
+
|
50
|
+
should 'handle empty rhs array' do
|
51
|
+
assert_equal(@hash_to_test, @hash_to_test - [])
|
52
|
+
end
|
53
|
+
|
54
|
+
should 'handle no matches in rhs array' do
|
55
|
+
assert_equal(@hash_to_test, @hash_to_test - ['100', '600'])
|
56
|
+
end
|
57
|
+
|
58
|
+
should 'handle all matches in rhs array' do
|
59
|
+
assert_equal({}, @hash_to_test - ['one', 'three', 'six'])
|
60
|
+
end
|
61
|
+
|
62
|
+
should 'handle symbols for key matching' do
|
63
|
+
assert_equal({ 'six' => 5 }, @hash_to_test - [:one, :three])
|
64
|
+
end
|
65
|
+
|
66
|
+
should 'return HashWithIndifferentAccess object' do
|
67
|
+
assert((@hash_to_test - [:one, :three]).is_a?(HashWithIndifferentAccess))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context '& operator' do
|
72
|
+
setup do
|
73
|
+
@hash_to_test = HashWithIndifferentAccess.new('one' => 2, :three => 4, 'six' => 5)
|
74
|
+
end
|
75
|
+
|
76
|
+
should 'return a hash with pairs removed that do NOT match the keys in rhs array' do
|
77
|
+
assert_equal({ 'one' => 2, 'six' => 5 }, @hash_to_test & ['one', 'six'])
|
78
|
+
end
|
79
|
+
|
80
|
+
should 'handle empty rhs array' do
|
81
|
+
assert_equal({}, @hash_to_test & [])
|
82
|
+
end
|
83
|
+
|
84
|
+
should 'handle no matches in rhs array' do
|
85
|
+
assert_equal({}, @hash_to_test & ['100', '600'])
|
86
|
+
end
|
87
|
+
|
88
|
+
should 'handle all matches in rhs array' do
|
89
|
+
assert_equal(@hash_to_test, @hash_to_test & ['one', 'three', 'six'])
|
90
|
+
end
|
91
|
+
|
92
|
+
should 'handle symbols for key matching' do
|
93
|
+
assert_equal({ 'one' => 2, 'three' => 4 }, @hash_to_test & [:one, :three])
|
94
|
+
end
|
95
|
+
|
96
|
+
should 'return HashWithIndifferentAccess object' do
|
97
|
+
assert((@hash_to_test & [:one, :three]).is_a?(HashWithIndifferentAccess))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# must require active_support's alias_method_chain first, to ensure that our module monkey patches it
|
4
|
+
require 'active_support/core_ext/module/aliasing'
|
5
|
+
require_relative '../../lib/invoca/utils/module.rb'
|
6
|
+
require_relative '../test_helper'
|
7
|
+
|
8
|
+
class ModuleTest < Minitest::Test
|
9
|
+
class NumberFun
|
10
|
+
def self.around_filter(around_method, method_names)
|
11
|
+
method_names.each do |meth|
|
12
|
+
define_method("#{meth}_with_around_filter") do |*args|
|
13
|
+
send(around_method, *args) do |*ar_args|
|
14
|
+
send("#{meth}_without_around_filter", *ar_args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method_chain meth, :around_filter
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def increment_filter(num)
|
23
|
+
yield(num + 1)
|
24
|
+
end
|
25
|
+
|
26
|
+
def number_printer(num)
|
27
|
+
num
|
28
|
+
end
|
29
|
+
|
30
|
+
around_filter :increment_filter, [:number_printer]
|
31
|
+
around_filter :increment_filter, [:number_printer]
|
32
|
+
end
|
33
|
+
|
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))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../lib/invoca/utils/multi_sender.rb'
|
4
|
+
require_relative '../test_helper'
|
5
|
+
|
6
|
+
class MultiSenderTest < Minitest::Test
|
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 'MultiSender' do
|
34
|
+
context 'with custom Enumerable' do
|
35
|
+
setup do
|
36
|
+
linked_list = LinkedList.new('some') << 'short' << 'words'
|
37
|
+
@multi_sender = Invoca::Utils::MultiSender.new(linked_list, :map)
|
38
|
+
end
|
39
|
+
|
40
|
+
should 'call the same method on each item in an Enumerable and return the results as an array' do
|
41
|
+
assert_equal([5, 5, 4], @multi_sender.length)
|
42
|
+
end
|
43
|
+
|
44
|
+
should 'handle methods with arguments' do
|
45
|
+
assert_equal(['or', 'ho', 'om'], @multi_sender.slice(1, 2))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'with built-in Array' do
|
50
|
+
should 'call the same method on each item in an Array and return the results as an array' do
|
51
|
+
multi_sender = Invoca::Utils::MultiSender.new(['some', 'short', 'words'], :map)
|
52
|
+
assert_equal([4, 5, 5], multi_sender.length)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: invoca-utils
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Invoca development
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description:
|
14
14
|
email:
|
15
15
|
- development@invoca.com
|
16
16
|
executables: []
|
@@ -18,8 +18,11 @@ extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
20
|
- ".gitignore"
|
21
|
+
- ".jenkins/Jenkinsfile"
|
22
|
+
- ".jenkins/ruby_build_pod.yml"
|
21
23
|
- ".rubocop.yml"
|
22
24
|
- ".ruby-version"
|
25
|
+
- CHANGELOG.md
|
23
26
|
- Gemfile
|
24
27
|
- Gemfile.lock
|
25
28
|
- LICENSE.txt
|
@@ -27,18 +30,32 @@ files:
|
|
27
30
|
- Rakefile
|
28
31
|
- invoca-utils.gemspec
|
29
32
|
- lib/invoca/utils.rb
|
33
|
+
- lib/invoca/utils/array.rb
|
30
34
|
- lib/invoca/utils/diff.rb
|
35
|
+
- lib/invoca/utils/enumerable.rb
|
36
|
+
- lib/invoca/utils/exceptions.rb
|
31
37
|
- lib/invoca/utils/guaranteed_utf8_string.rb
|
38
|
+
- lib/invoca/utils/hash.rb
|
39
|
+
- lib/invoca/utils/hash_with_indifferent_access.rb
|
32
40
|
- lib/invoca/utils/http.rb
|
33
41
|
- lib/invoca/utils/map_compact.rb
|
34
42
|
- lib/invoca/utils/min_max.rb
|
43
|
+
- lib/invoca/utils/module.rb
|
44
|
+
- lib/invoca/utils/multi_sender.rb
|
35
45
|
- lib/invoca/utils/stable_sort.rb
|
36
46
|
- lib/invoca/utils/time.rb
|
37
47
|
- lib/invoca/utils/version.rb
|
38
48
|
- test/helpers/constant_overrides.rb
|
39
49
|
- test/test_helper.rb
|
50
|
+
- test/unit/array_test.rb
|
51
|
+
- test/unit/enumerable_test.rb
|
52
|
+
- test/unit/exceptions_test.rb
|
40
53
|
- test/unit/guaranteed_utf8_string_test.rb
|
54
|
+
- test/unit/hash_test.rb
|
55
|
+
- test/unit/hash_with_indifferent_access_test.rb
|
41
56
|
- test/unit/map_compact_test.rb
|
57
|
+
- test/unit/module_test.rb
|
58
|
+
- test/unit/multi_sender_test.rb
|
42
59
|
- test/unit/stable_sort_test.rb
|
43
60
|
- test/unit/time_calculations_test.rb
|
44
61
|
- test/unit/utils_test.rb
|
@@ -62,16 +79,22 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
79
|
- !ruby/object:Gem::Version
|
63
80
|
version: '0'
|
64
81
|
requirements: []
|
65
|
-
|
66
|
-
rubygems_version: 2.6.13
|
82
|
+
rubygems_version: 3.0.3
|
67
83
|
signing_key:
|
68
84
|
specification_version: 4
|
69
85
|
summary: A public collection of helpers used in multiple projects
|
70
86
|
test_files:
|
71
87
|
- test/helpers/constant_overrides.rb
|
72
88
|
- test/test_helper.rb
|
89
|
+
- test/unit/array_test.rb
|
90
|
+
- test/unit/enumerable_test.rb
|
91
|
+
- test/unit/exceptions_test.rb
|
73
92
|
- test/unit/guaranteed_utf8_string_test.rb
|
93
|
+
- test/unit/hash_test.rb
|
94
|
+
- test/unit/hash_with_indifferent_access_test.rb
|
74
95
|
- test/unit/map_compact_test.rb
|
96
|
+
- test/unit/module_test.rb
|
97
|
+
- test/unit/multi_sender_test.rb
|
75
98
|
- test/unit/stable_sort_test.rb
|
76
99
|
- test/unit/time_calculations_test.rb
|
77
100
|
- test/unit/utils_test.rb
|