invoca-utils 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 926143286cf40283408cd86732347339453d12a671c2e1539f01e391cef84ed4
4
- data.tar.gz: b4114b43131585610f6e875680895e1be15fe68e164a0a8904ddec46b59e58cf
3
+ metadata.gz: 54dcad50905bf9c0d48905b88e563c6418ea4bc852e4f85d4cd892e2b2031096
4
+ data.tar.gz: 592dde85e812312a5fb5ff67d34da1d5a0127f0c2c2f5f60538baab3c63d32ff
5
5
  SHA512:
6
- metadata.gz: 50a6cf86a1547385b1b7dabfce31f4a89d0156f872f7dde7b6e179afd6373f8b56cc6e4bfea3b383af02aaabb6567a096d4dfe9250a8e639a0f02c3d0535455f
7
- data.tar.gz: 2bcfc2fa9de854a2f78ba03a5bd9814fc921c9ab668ca0660491b14679e81536a9e4c61cb6e4b8f00f88ddb609a891956f02094de64e48efbe0411fdc4afbeff
6
+ metadata.gz: 6fac4beb8eef9b3a4ebc3615cb7711c9134e401e34ba26a2d0012b89c7962ca19440b2ceb2e0fafd5aaafcd4576717d10fddd276c4583858ffc725904194464a
7
+ data.tar.gz: c312101bd66d62f9c145610be64a8e75ec931387d4dc33f581885dc5707ff7565e474fd618ddbf569ed9091a591d0f2e0a164b7899ffe311b889c62fbc5a7d37
@@ -4,6 +4,12 @@ Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4
4
 
5
5
  Note: This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.4.0] - 2020-06-09
8
+ ### Added
9
+ - Added `Invoca::Utils.retry_on_exception`.
10
+ - Added `Invoca::Utils::GuaranteedUTF8String.normalize_all_strings` to normalize
11
+ all strings found in a JSON doc of hashes, arrays, and values.
12
+
7
13
  ## [0.3.0] - 2020-04-28
8
14
  ### Added
9
15
  - Array::* operator changed to use alias_method instead of prepend to prevent infinite recursion when HoboSupport gem is present
@@ -17,5 +23,6 @@ Note: This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0
17
23
  - Enumerable::build_hash method ported from HoboSupport
18
24
  - Enumerable::* operator ported from HoboSupport
19
25
 
26
+ [0.4.0]: https://github.com/Invoca/invoca-utils/compare/v0.3.0...v0.4.0
20
27
  [0.3.0]: https://github.com/Invoca/invoca-utils/compare/v0.2.0...v0.3.0
21
28
  [0.2.0]: https://github.com/Invoca/invoca-utils/compare/v0.1.1...v0.2.0
data/Gemfile CHANGED
@@ -4,7 +4,7 @@ source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
6
 
7
- group :development do
7
+ source 'https://rubygems.org' do
8
8
  gem 'activesupport', '~> 4.2'
9
9
  gem 'minitest'
10
10
  gem 'minitest-reporters'
@@ -16,3 +16,7 @@ group :development do
16
16
  gem 'test-unit', '= 1.2.3'
17
17
  gem 'tzinfo'
18
18
  end
19
+
20
+ source 'https://gem.fury.io/invoca' do
21
+ gem 'test_overrides', '~> 0.13'
22
+ end
@@ -1,37 +1,38 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- invoca-utils (0.3.0)
4
+ invoca-utils (0.4.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
+ remote: https://gem.fury.io/invoca/
8
9
  specs:
9
- activesupport (4.2.11.1)
10
+ activesupport (4.2.11.3)
10
11
  i18n (~> 0.7)
11
12
  minitest (~> 5.1)
12
13
  thread_safe (~> 0.3, >= 0.3.4)
13
14
  tzinfo (~> 1.1)
14
15
  ansi (1.5.0)
15
16
  builder (3.2.4)
16
- coderay (1.1.2)
17
+ coderay (1.1.3)
17
18
  concurrent-ruby (1.1.6)
18
19
  hoe (3.22.1)
19
20
  rake (>= 0.8, < 15.0)
20
21
  i18n (0.9.5)
21
22
  concurrent-ruby (~> 1.0)
22
- method_source (0.9.0)
23
- minitest (5.14.0)
24
- minitest-reporters (1.1.14)
23
+ method_source (1.0.0)
24
+ minitest (5.14.1)
25
+ minitest-reporters (1.4.2)
25
26
  ansi
26
27
  builder
27
28
  minitest (>= 5.0)
28
29
  ruby-progressbar
29
- pry (0.11.3)
30
- coderay (~> 1.1.0)
31
- method_source (~> 0.9.0)
30
+ pry (0.13.1)
31
+ coderay (~> 1.1)
32
+ method_source (~> 1.0)
32
33
  rake (13.0.1)
33
34
  rr (1.1.2)
34
- ruby-prof (0.17.0)
35
+ ruby-prof (1.4.1)
35
36
  ruby-progressbar (1.10.1)
36
37
  shoulda (3.5.0)
37
38
  shoulda-context (~> 1.0, >= 1.0.1)
@@ -41,6 +42,7 @@ GEM
41
42
  activesupport (>= 3.0.0)
42
43
  test-unit (1.2.3)
43
44
  hoe (>= 1.5.1)
45
+ test_overrides (0.13.0)
44
46
  thread_safe (0.3.6)
45
47
  tzinfo (1.2.7)
46
48
  thread_safe (~> 0.1)
@@ -49,17 +51,18 @@ PLATFORMS
49
51
  ruby
50
52
 
51
53
  DEPENDENCIES
52
- activesupport (~> 4.2)
54
+ activesupport (~> 4.2)!
53
55
  invoca-utils!
54
- minitest
55
- minitest-reporters
56
- pry
57
- rake
58
- rr (= 1.1.2)
59
- ruby-prof
60
- shoulda (= 3.5.0)
61
- test-unit (= 1.2.3)
62
- tzinfo
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!
63
66
 
64
67
  BUNDLED WITH
65
68
  1.17.3
data/Rakefile CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  require "bundler/gem_tasks"
5
5
  require 'rake/testtask'
6
+ require 'rake_test_warning_false'
6
7
 
7
8
  task default: :test
8
9
 
@@ -1,4 +1,4 @@
1
- lib = File.expand_path('../lib', __FILE__)
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 = %q{A public collection of helpers used in multiple projects}
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
 
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ module Invoca
4
+ module Utils
5
+ end
6
+ end
7
+
3
8
  require "invoca/utils/module"
4
9
  require "invoca/utils/array"
5
10
  require "invoca/utils/enumerable"
@@ -11,14 +16,10 @@ require "invoca/utils/map_compact"
11
16
  require "invoca/utils/min_max"
12
17
  require "invoca/utils/stable_sort"
13
18
  require "invoca/utils/time"
19
+ require "invoca/utils/exceptions"
14
20
  require "invoca/utils/guaranteed_utf8_string"
15
21
  require "invoca/utils/version"
16
22
 
17
- module Invoca
18
- module Utils
19
- end
20
- end
21
-
22
23
  unless defined?(Diff)
23
24
  Diff = Invoca::Utils::Diff
24
25
  end
@@ -0,0 +1,25 @@
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
+ return yield(attempt_number)
17
+ rescue *Array(exception_classes) => ex
18
+ before_retry&.call(ex)
19
+ end
20
+
21
+ yield(retries) # no rescue for this last try, so any exceptions will raise out
22
+ end
23
+ end
24
+ end
25
+ 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
- else
58
- unless string.valid_encoding?
59
- if normalize_cp1252
60
- cp1252_to_utf_8(string)
61
- else
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
@@ -2,7 +2,6 @@
2
2
 
3
3
  # Invoca ::Hash extensions
4
4
  class Hash
5
-
6
5
  def select_hash(&block)
7
6
  res = {}
8
7
  each { |k, v| res[k] = v if (block.arity == 1 ? yield(v) : yield(k, v)) } # rubocop:disable Style/ParenthesesAroundCondition
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Invoca
4
4
  module Utils
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  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
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.3.0
4
+ version: 0.4.0
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-04-29 00:00:00.000000000 Z
11
+ date: 2020-06-09 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: A public collection of helpers used in multiple projects
13
+ description:
14
14
  email:
15
15
  - development@invoca.com
16
16
  executables: []
@@ -33,6 +33,7 @@ files:
33
33
  - lib/invoca/utils/array.rb
34
34
  - lib/invoca/utils/diff.rb
35
35
  - lib/invoca/utils/enumerable.rb
36
+ - lib/invoca/utils/exceptions.rb
36
37
  - lib/invoca/utils/guaranteed_utf8_string.rb
37
38
  - lib/invoca/utils/hash.rb
38
39
  - lib/invoca/utils/hash_with_indifferent_access.rb
@@ -48,6 +49,7 @@ files:
48
49
  - test/test_helper.rb
49
50
  - test/unit/array_test.rb
50
51
  - test/unit/enumerable_test.rb
52
+ - test/unit/exceptions_test.rb
51
53
  - test/unit/guaranteed_utf8_string_test.rb
52
54
  - test/unit/hash_test.rb
53
55
  - test/unit/hash_with_indifferent_access_test.rb
@@ -86,6 +88,7 @@ test_files:
86
88
  - test/test_helper.rb
87
89
  - test/unit/array_test.rb
88
90
  - test/unit/enumerable_test.rb
91
+ - test/unit/exceptions_test.rb
89
92
  - test/unit/guaranteed_utf8_string_test.rb
90
93
  - test/unit/hash_test.rb
91
94
  - test/unit/hash_with_indifferent_access_test.rb