invoca-utils 0.1.1 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|