invoca-utils 0.0.2 → 0.0.3
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 +4 -4
- data/.gitignore +1 -1
- data/.ruby-version +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +56 -0
- data/Rakefile +8 -11
- data/invoca-utils.gemspec +1 -1
- data/lib/invoca/utils/diff.rb +3 -3
- data/lib/invoca/utils/guaranteed_utf8_string.rb +68 -0
- data/lib/invoca/utils/http.rb +45 -0
- data/lib/invoca/utils/map_compact.rb +10 -0
- data/lib/invoca/utils/min_max.rb +9 -0
- data/lib/invoca/utils/stable_sort.rb +23 -0
- data/lib/invoca/utils/time.rb +34 -0
- data/lib/invoca/utils/version.rb +1 -1
- data/lib/invoca/utils.rb +7 -3
- data/test/unit/guaranteed_utf8_string_test.rb +116 -0
- data/test/unit/map_compact_test.rb +23 -0
- data/test/unit/stable_sort_test.rb +52 -0
- data/test/unit/time_calculations_test.rb +38 -0
- data/test/unit/utils_test.rb +2 -2
- metadata +33 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9af5982666a515cac52ec601dcc30bb8be8b473a
|
|
4
|
+
data.tar.gz: ab9b463b80311d295136c72b5df683b959e03579
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0b807a493e7bedc3eca0901d3276723d8cadc91cd3a67a2b31b009695f0be4ffd16562254802a9aab4526b29bd463da17b942400e197a122d08a41e0c37b8c8c
|
|
7
|
+
data.tar.gz: 4fcf0da07ac921a2410271dd84c94599ca5ee8893be9db532e41970141fdd35172459f843a32c8cabbc2887b13b4d039991059322a43359090565cbf34a603ea
|
data/.gitignore
CHANGED
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.4.2
|
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
invoca-utils (0.0.3)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
activesupport (5.1.4)
|
|
10
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
11
|
+
i18n (~> 0.7)
|
|
12
|
+
minitest (~> 5.1)
|
|
13
|
+
tzinfo (~> 1.1)
|
|
14
|
+
coderay (1.1.2)
|
|
15
|
+
concurrent-ruby (1.0.5)
|
|
16
|
+
hoe (3.16.2)
|
|
17
|
+
rake (>= 0.8, < 13.0)
|
|
18
|
+
i18n (0.9.1)
|
|
19
|
+
concurrent-ruby (~> 1.0)
|
|
20
|
+
method_source (0.9.0)
|
|
21
|
+
minitest (5.11.1)
|
|
22
|
+
pry (0.11.3)
|
|
23
|
+
coderay (~> 1.1.0)
|
|
24
|
+
method_source (~> 0.9.0)
|
|
25
|
+
rake (12.3.0)
|
|
26
|
+
rr (1.1.2)
|
|
27
|
+
ruby-prof (0.17.0)
|
|
28
|
+
shoulda (3.5.0)
|
|
29
|
+
shoulda-context (~> 1.0, >= 1.0.1)
|
|
30
|
+
shoulda-matchers (>= 1.4.1, < 3.0)
|
|
31
|
+
shoulda-context (1.2.2)
|
|
32
|
+
shoulda-matchers (2.8.0)
|
|
33
|
+
activesupport (>= 3.0.0)
|
|
34
|
+
test-unit (1.2.3)
|
|
35
|
+
hoe (>= 1.5.1)
|
|
36
|
+
thread_safe (0.3.6)
|
|
37
|
+
tzinfo (1.2.4)
|
|
38
|
+
thread_safe (~> 0.1)
|
|
39
|
+
|
|
40
|
+
PLATFORMS
|
|
41
|
+
ruby
|
|
42
|
+
|
|
43
|
+
DEPENDENCIES
|
|
44
|
+
bundler (~> 1.6)
|
|
45
|
+
invoca-utils!
|
|
46
|
+
minitest
|
|
47
|
+
pry
|
|
48
|
+
rake
|
|
49
|
+
rr (= 1.1.2)
|
|
50
|
+
ruby-prof
|
|
51
|
+
shoulda (= 3.5.0)
|
|
52
|
+
test-unit (= 1.2.3)
|
|
53
|
+
tzinfo
|
|
54
|
+
|
|
55
|
+
BUNDLED WITH
|
|
56
|
+
1.16.0
|
data/Rakefile
CHANGED
|
@@ -2,17 +2,14 @@
|
|
|
2
2
|
require "bundler/gem_tasks"
|
|
3
3
|
require 'rake/testtask'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
t.pattern = 'test/unit/**/*_test.rb'
|
|
11
|
-
t.verbose = true
|
|
12
|
-
end
|
|
13
|
-
Rake::Task['test:unit'].comment = "Run the unit tests"
|
|
14
|
-
|
|
5
|
+
Rake::TestTask.new(:test) do |t|
|
|
6
|
+
t.libs << 'lib'
|
|
7
|
+
t.libs << 'test'
|
|
8
|
+
t.pattern = 'test/**/*_test.rb'
|
|
9
|
+
t.verbose = false
|
|
15
10
|
end
|
|
16
11
|
|
|
17
|
-
task :
|
|
12
|
+
task default: :test
|
|
13
|
+
|
|
14
|
+
task :default => 'test'
|
|
18
15
|
|
data/invoca-utils.gemspec
CHANGED
|
@@ -20,11 +20,11 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.6"
|
|
22
22
|
spec.add_development_dependency "rake"
|
|
23
|
-
|
|
24
23
|
spec.add_development_dependency "test-unit", "= 1.2.3"
|
|
25
24
|
spec.add_development_dependency "rr", "=1.1.2"
|
|
26
25
|
spec.add_development_dependency "shoulda", "= 3.5.0"
|
|
27
26
|
spec.add_development_dependency "pry"
|
|
28
27
|
spec.add_development_dependency "ruby-prof"
|
|
29
28
|
spec.add_development_dependency "minitest"
|
|
29
|
+
spec.add_development_dependency "tzinfo"
|
|
30
30
|
end
|
data/lib/invoca/utils/diff.rb
CHANGED
|
@@ -87,7 +87,7 @@ class Diff
|
|
|
87
87
|
result << " #{format arg}\n" unless arg.nil? || options[:short_description]
|
|
88
88
|
end
|
|
89
89
|
if curr_diff && curr_diff[1].first == index
|
|
90
|
-
verb,
|
|
90
|
+
verb, _a_range, _b_range, del, add = curr_diff
|
|
91
91
|
result <<
|
|
92
92
|
case verb
|
|
93
93
|
when 'd'
|
|
@@ -96,8 +96,8 @@ class Diff
|
|
|
96
96
|
add.map { |t| "+ #{format t}\n"}.join +
|
|
97
97
|
(arg.nil? ? '' : " #{format arg}\n")
|
|
98
98
|
when 'c'
|
|
99
|
-
del.map_with_index { |t,
|
|
100
|
-
add.
|
|
99
|
+
del.map_with_index { |t, del_index| "- #{format t}\n#{nested_compare(del, add, del_index)}" }.join +
|
|
100
|
+
add.map { |t| "+ #{format t}\n" }.join
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This class expects to be initialized with a string and guarantees that the output of the to_string method is in UTF-8 format and fits in 3 bytes/char or less.
|
|
4
|
+
module Invoca
|
|
5
|
+
module Utils
|
|
6
|
+
class GuaranteedUTF8String
|
|
7
|
+
def initialize(string)
|
|
8
|
+
if string.is_a?(String) ||
|
|
9
|
+
(string.respond_to?(:to_s) &&
|
|
10
|
+
string.method(:to_s).owner != Kernel) # the lame .to_s from Kernel just calls .inspect :(
|
|
11
|
+
@string = string.to_s
|
|
12
|
+
else
|
|
13
|
+
raise ArgumentError, "#{self.class} must be initialized with a string or an object with a non-Kernel .to_s method but instead was #{string.class} #{string.inspect}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_string
|
|
18
|
+
@to_string ||= normalize_string(@string)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
alias_method :to_s, :to_string
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# chosen because this is a 1-byte ASCII character that is not used in any of the popular escaping systems: XML, HTML, HTTP URIs, HTTP Form Post, JSON
|
|
26
|
+
REPLACE_CHARACTER = '~' unless defined?(REPLACE_CHARACTER)
|
|
27
|
+
|
|
28
|
+
def normalize_string(str)
|
|
29
|
+
str = @string.dup
|
|
30
|
+
str.force_encoding('UTF-8')
|
|
31
|
+
if !str.valid_encoding?
|
|
32
|
+
cp1252_to_utf_8(str)
|
|
33
|
+
end
|
|
34
|
+
normalize_newlines(str)
|
|
35
|
+
remove_bom(str)
|
|
36
|
+
replace_unicode_beyond_ffff(str)
|
|
37
|
+
str
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def normalize_newlines(str)
|
|
41
|
+
str.gsub!(/ \r\n | \r | \n /x, "\n")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def cp1252_to_utf_8(str)
|
|
45
|
+
str.force_encoding('CP1252')
|
|
46
|
+
str.encode!(
|
|
47
|
+
'UTF-8',
|
|
48
|
+
replace: REPLACE_CHARACTER,
|
|
49
|
+
undef: :replace,
|
|
50
|
+
invalid: :replace
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def remove_bom(str)
|
|
55
|
+
str.sub!(/\A \xEF\xBB\xBF/x, '')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Note MySQL can only store Unicode up to code point U+FFFF in the standard mb3 storage type. There is an option to use mb4 which
|
|
59
|
+
# is needed to hold the code points above that (including emoji) but we haven't enabled that on any columns yet since
|
|
60
|
+
# it would take a data migration and didn't seem that important.
|
|
61
|
+
|
|
62
|
+
def replace_unicode_beyond_ffff(str)
|
|
63
|
+
str.gsub!(/[^\u{0}-\u{ffff}]/x, REPLACE_CHARACTER)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
|
|
3
|
+
if RUBY_VERSION == '1.8.7'
|
|
4
|
+
module ::Net #:nodoc:
|
|
5
|
+
class HTTP < Protocol
|
|
6
|
+
def connect
|
|
7
|
+
D "opening connection to #{conn_address()}..."
|
|
8
|
+
s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
|
|
9
|
+
D "opened"
|
|
10
|
+
if use_ssl?
|
|
11
|
+
unless @ssl_context.verify_mode
|
|
12
|
+
warn "warning: peer certificate won't be verified in this SSL session"
|
|
13
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
14
|
+
end
|
|
15
|
+
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
|
|
16
|
+
s.sync_close = true
|
|
17
|
+
end
|
|
18
|
+
@socket = BufferedIO.new(s)
|
|
19
|
+
@socket.read_timeout = @read_timeout
|
|
20
|
+
@socket.debug_output = @debug_output
|
|
21
|
+
if use_ssl?
|
|
22
|
+
if proxy?
|
|
23
|
+
@socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
|
|
24
|
+
@address, @port, HTTPVersion)
|
|
25
|
+
@socket.writeline "Host: #{@address}:#{@port}"
|
|
26
|
+
if proxy_user
|
|
27
|
+
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
|
|
28
|
+
credential.delete!("\r\n")
|
|
29
|
+
@socket.writeline "Proxy-Authorization: Basic #{credential}"
|
|
30
|
+
end
|
|
31
|
+
@socket.writeline ''
|
|
32
|
+
HTTPResponse.read_new(@socket).value
|
|
33
|
+
end
|
|
34
|
+
# NOTE: Change here. This was just s.connect, now we have added a timeout.
|
|
35
|
+
timeout(@open_timeout) { s.connect }
|
|
36
|
+
if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
|
37
|
+
s.post_connection_check(@address)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
on_connect
|
|
41
|
+
end
|
|
42
|
+
private :connect
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module ::Enumerable
|
|
2
|
+
|
|
3
|
+
# A stable sort will preserve the original order of two elements if their sort keys are identical.
|
|
4
|
+
|
|
5
|
+
def stable_sort
|
|
6
|
+
n = -1
|
|
7
|
+
if block_given?
|
|
8
|
+
collect {|x| n += 1; [x, n]
|
|
9
|
+
}.sort! {|a, b|
|
|
10
|
+
c = yield a[0], b[0]
|
|
11
|
+
if c.nonzero? then c else a[1] <=> b[1] end
|
|
12
|
+
}.collect! {|x| x[0]}
|
|
13
|
+
else
|
|
14
|
+
sort_by {|x| n += 1; [x, n]}
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def stable_sort_by
|
|
19
|
+
block_given? or return enum_for(:stable_sort_by)
|
|
20
|
+
n = -1
|
|
21
|
+
sort_by {|x| n += 1; [(yield x), n]}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Invoca ::Time extensions
|
|
2
|
+
class ::Time
|
|
3
|
+
def to_ms
|
|
4
|
+
@to_ms ||= (self.to_f * 1000).to_i
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
#rfc3339ms is like rfc3339 but with milliseconds
|
|
8
|
+
if RUBY_VERSION < "1.9.3"
|
|
9
|
+
def rfc3339ms
|
|
10
|
+
strftime("%Y-%m-%dT%H:%M:%S.#{ms_for_3339}%z")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ms_for_3339
|
|
14
|
+
@ms_for_3339 ||= ("%03.3d" % (self.to_ms % 1000))
|
|
15
|
+
end
|
|
16
|
+
private :ms_for_3339
|
|
17
|
+
else
|
|
18
|
+
def rfc3339ms
|
|
19
|
+
strftime("%Y-%m-%dT%H:%M:%S.%L%z")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def beginning_of_hour
|
|
24
|
+
change(:min => 0, :sec => 0, :usec => 0)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def end_of_day_whole_sec # usec can be bad because it isn't preserved by MySQL
|
|
28
|
+
change(:hour => 23, :min => 59, :sec => 59, :usec => 0)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def whole_sec
|
|
32
|
+
change(:usec => 0)
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/invoca/utils/version.rb
CHANGED
data/lib/invoca/utils.rb
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
require "invoca/utils/version"
|
|
2
|
-
|
|
3
1
|
require "invoca/utils/diff"
|
|
2
|
+
require "invoca/utils/http"
|
|
3
|
+
require "invoca/utils/map_compact"
|
|
4
|
+
require "invoca/utils/min_max"
|
|
5
|
+
require "invoca/utils/stable_sort"
|
|
6
|
+
require "invoca/utils/time"
|
|
7
|
+
require "invoca/utils/guaranteed_utf8_string"
|
|
8
|
+
require "invoca/utils/version"
|
|
4
9
|
|
|
5
10
|
module Invoca
|
|
6
11
|
module Utils
|
|
7
|
-
|
|
8
12
|
end
|
|
9
13
|
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../test_helper'
|
|
4
|
+
|
|
5
|
+
class GuaranteedUTF8StringTest < Minitest::Test
|
|
6
|
+
class HasNoTo_sMethod
|
|
7
|
+
undef :to_s
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class BasicObjectWithKernelMethods
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class ConvertibleToString
|
|
14
|
+
attr_reader :to_s
|
|
15
|
+
|
|
16
|
+
def initialize(string)
|
|
17
|
+
@to_s = string
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
should "raise an error if initialized with an object with no to_s method" do
|
|
22
|
+
assert_raises ArgumentError, /GuaranteedUTF8String must be initialized with a string or an object with a non-Kernel \.to_s method but instead was GuaranteedUTF8StringTest::HasNoTo_sMethod/ do
|
|
23
|
+
Invoca::Utils::GuaranteedUTF8String.new(HasNoTo_sMethod.new)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
should "raise an error if initialized with a basic Ruby object" do
|
|
28
|
+
assert_raises ArgumentError, /GuaranteedUTF8String must be initialized with a string or an object with a non-Kernel \.to_s method but instead was GuaranteedUTF8StringTest::BasicObjectWithKernelMethods/ do
|
|
29
|
+
Invoca::Utils::GuaranteedUTF8String.new(BasicObjectWithKernelMethods.new)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
should "convert to a string with to_s if possible" do
|
|
34
|
+
result = Invoca::Utils::GuaranteedUTF8String.new(ConvertibleToString.new("test string"))
|
|
35
|
+
assert_equal "test string", result.to_string
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context "#to_string" do
|
|
39
|
+
should "not mutate the original string" do
|
|
40
|
+
ascii_string = "new string".encode("ASCII")
|
|
41
|
+
utf8_string_instance = Invoca::Utils::GuaranteedUTF8String.new(ascii_string)
|
|
42
|
+
encoded_string = utf8_string_instance.to_string
|
|
43
|
+
|
|
44
|
+
assert_equal ascii_string, encoded_string
|
|
45
|
+
assert_equal Encoding::ASCII, ascii_string.encoding
|
|
46
|
+
assert_equal Encoding::UTF_8, encoded_string.encoding
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
should "return UTF-8 encoded string" do
|
|
50
|
+
original_string = "this,is,a,valid,utf-8,csv,string\none,two,three,four,five,six,seven\n"
|
|
51
|
+
|
|
52
|
+
encoded_string = Invoca::Utils::GuaranteedUTF8String.new(original_string).to_string
|
|
53
|
+
|
|
54
|
+
assert_equal original_string, encoded_string
|
|
55
|
+
assert_equal Encoding::UTF_8, encoded_string.encoding
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
should "return UTF-8 encoded string without BOM" do
|
|
59
|
+
original_string = "\xEF\xBB\xBFthis,is,a,valid,utf-8,csv,string\none,two,three,four,five,six,seven\n"
|
|
60
|
+
|
|
61
|
+
encoded_string = Invoca::Utils::GuaranteedUTF8String.new(original_string).to_string
|
|
62
|
+
|
|
63
|
+
assert_equal "this,is,a,valid,utf-8,csv,string\none,two,three,four,five,six,seven\n", encoded_string
|
|
64
|
+
assert_equal Encoding::UTF_8, encoded_string.encoding
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
should "return UTF-8 encoded string using to_s alias" do
|
|
68
|
+
original_string = "this,is,a,valid,utf-8,csv,string\none,two,three,four,five,six,seven\n"
|
|
69
|
+
|
|
70
|
+
encoded_string = Invoca::Utils::GuaranteedUTF8String.new(original_string).to_s
|
|
71
|
+
|
|
72
|
+
assert_equal original_string, encoded_string
|
|
73
|
+
assert_equal Encoding::UTF_8, encoded_string.encoding
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
should "return UTF-8 encoded string after falling back to CP1252 encoding" do
|
|
77
|
+
string = "This,is,NOT,a,valid,utf-8,csv,string\r\none,two,three,four,\x81five\xF6,six,seven,eight\n"
|
|
78
|
+
expected_string = "This,is,NOT,a,valid,utf-8,csv,string\none,two,three,four,~fiveö,six,seven,eight\n"
|
|
79
|
+
|
|
80
|
+
encoded_string = Invoca::Utils::GuaranteedUTF8String.new(string).to_string
|
|
81
|
+
|
|
82
|
+
assert_equal expected_string, encoded_string
|
|
83
|
+
assert_equal Encoding::UTF_8, encoded_string.encoding
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
should "return UTF-8 encoded string with normalized return chars" do
|
|
87
|
+
string = "This string\n\n\n has line feeds\ncarriage\r\r returns\rand Windows\r\n\r\n new line chars\r\nend of \n\r\r\r\nstring"
|
|
88
|
+
expected_string = "This string\n\n\n has line feeds\ncarriage\n\n returns\nand Windows\n\n new line chars\nend of \n\n\n\nstring"
|
|
89
|
+
|
|
90
|
+
encoded_string = Invoca::Utils::GuaranteedUTF8String.new(string).to_string
|
|
91
|
+
|
|
92
|
+
assert_equal expected_string, encoded_string
|
|
93
|
+
assert_equal Encoding::UTF_8, encoded_string.encoding
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
should "encode all 255 UTF-8 characters, returning ~ when the character isn't mapped in CP1252" do
|
|
97
|
+
all_8_bit_characters = (1..255).map { |char| char.chr }.join
|
|
98
|
+
|
|
99
|
+
final_utf_8_string = Invoca::Utils::GuaranteedUTF8String.new(all_8_bit_characters.dup).to_s
|
|
100
|
+
expected_string = "\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000A\u000B\u000C\u000A\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007F€~‚ƒ„…†‡ˆ‰Š‹Œ~Ž~~‘’“”•–—˜™š›œ~žŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
|
|
101
|
+
|
|
102
|
+
assert_equal expected_string, final_utf_8_string
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
should "consider UTF-8 code points that take > 3 bytes (above U+FFFF) to be invalid (since MySQL can't store them unless column is declared mb4) and encode them as ~" do
|
|
106
|
+
string = "This string has some ✓ valid UTF-8 but also some 😹 emoji \xf0\x9f\x98\xb9 that are > U+FFFF"
|
|
107
|
+
expected_string = "This string has some ✓ valid UTF-8 but also some ~ emoji ~ that are > U+FFFF"
|
|
108
|
+
|
|
109
|
+
encoded_string = Invoca::Utils::GuaranteedUTF8String.new(string).to_string
|
|
110
|
+
|
|
111
|
+
assert_equal expected_string, encoded_string
|
|
112
|
+
assert_equal Encoding::UTF_8, encoded_string.encoding
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require_relative '../test_helper'
|
|
2
|
+
|
|
3
|
+
class MapCompactTest < Minitest::Test
|
|
4
|
+
should "map_compact" do
|
|
5
|
+
assert_equal [1, 9], [1, 2, nil, 3, 4].map_compact { |item| item**2 if (nil == item ? nil : item.odd?) }
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
should "map_compact to empty if nothing matches" do
|
|
9
|
+
assert_equal [], {:a => 'aaa', :b => 'bbb'}.map_compact { |key, value| value if key == :c }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
should "map_compact a hash" do
|
|
13
|
+
assert_equal ['bbb'], {:a => 'aaa', :b => 'bbb'}.map_compact { |key, value| value if key == :b }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
should "map_compact empty collection" do
|
|
17
|
+
assert_equal [], [].map_compact { |item| true }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
should "not map_compact false" do
|
|
21
|
+
assert_equal [false], [nil, false].map_compact { |a| a }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require_relative '../test_helper'
|
|
2
|
+
|
|
3
|
+
class StableSortTest < Minitest::Test
|
|
4
|
+
context "#stable_sort_by" do
|
|
5
|
+
should "preserve the original order if all sort the same" do
|
|
6
|
+
list_to_sort = [:b, :d, :c, :a, :e, :f]
|
|
7
|
+
|
|
8
|
+
assert_equal list_to_sort, list_to_sort.stable_sort_by { |c| 0 }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
should "order by keys first and then position" do
|
|
12
|
+
list_to_sort = [:b, :d, :c, :a, :e, :f]
|
|
13
|
+
order = [:a, :b, :c]
|
|
14
|
+
|
|
15
|
+
result = list_to_sort.stable_sort_by { |c| order.index(c) || order.length }
|
|
16
|
+
assert_equal [:a, :b, :c, :d, :e, :f], result
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
should "order by keys only if needed" do
|
|
20
|
+
list_to_sort = [:b, :d, :c, :a, :e, :f]
|
|
21
|
+
result = list_to_sort.stable_sort_by { |c| c.to_s }
|
|
22
|
+
assert_equal [:a, :b, :c, :d, :e, :f], result
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
context "stable_sort" do
|
|
27
|
+
should "preserve the original order if all sort the same" do
|
|
28
|
+
list_to_sort = [:b, :d, :c, :a, :e, :f]
|
|
29
|
+
|
|
30
|
+
assert_equal list_to_sort, list_to_sort.stable_sort { |first, second| 0 }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
should "order by keys first and then position" do
|
|
34
|
+
list_to_sort = [:b, :d, :c, :a, :e, :f]
|
|
35
|
+
order = [:a, :b, :c]
|
|
36
|
+
|
|
37
|
+
result = list_to_sort.stable_sort do |first, second|
|
|
38
|
+
first_pos = order.index(first) || order.length
|
|
39
|
+
second_pos = order.index(second) || order.length
|
|
40
|
+
first_pos <=> second_pos
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
assert_equal [:a, :b, :c, :d, :e, :f], result
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
should "order by keys only if needed" do
|
|
47
|
+
list_to_sort = [:b, :d, :c, :a, :e, :f]
|
|
48
|
+
result = list_to_sort.stable_sort{ |first, second| first.to_s <=> second.to_s }
|
|
49
|
+
assert_equal [:a, :b, :c, :d, :e, :f], result
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require_relative '../test_helper'
|
|
2
|
+
require 'active_support/all'
|
|
3
|
+
|
|
4
|
+
class TimeCalculationsTest < Minitest::Test
|
|
5
|
+
context "beginning_of_hour" do
|
|
6
|
+
Time.zone = 'Pacific Time (US & Canada)'
|
|
7
|
+
[
|
|
8
|
+
Time.now,
|
|
9
|
+
Time.zone.now,
|
|
10
|
+
Time.local(2009),
|
|
11
|
+
Time.local(2009,3,4,5),
|
|
12
|
+
Time.local(2001,12,31,23,59),
|
|
13
|
+
Time.local(1970,1,1)
|
|
14
|
+
].each_with_index do |time, index|
|
|
15
|
+
should "give back a time with no minutes, seconds, or msec: #{time} (#{index})" do
|
|
16
|
+
t = time.beginning_of_hour
|
|
17
|
+
assert_equal t.year, time.year
|
|
18
|
+
assert_equal t.month, time.month
|
|
19
|
+
assert_equal t.day, time.day
|
|
20
|
+
assert_equal t.hour, time.hour
|
|
21
|
+
assert_equal 0, t.min
|
|
22
|
+
assert_equal 0, t.sec
|
|
23
|
+
assert_equal 0, t.usec
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context "end_of_day_whole_sec" do
|
|
29
|
+
should "return the end of day with whole_sec" do
|
|
30
|
+
t = Time.now
|
|
31
|
+
end_of_day = t.end_of_day
|
|
32
|
+
end_whole_sec = t.end_of_day_whole_sec
|
|
33
|
+
assert_equal 0.0, end_whole_sec.usec
|
|
34
|
+
assert_equal end_of_day.to_i, end_whole_sec.to_i
|
|
35
|
+
assert_equal end_of_day.sec, end_whole_sec.sec
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/test/unit/utils_test.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: invoca-utils
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cary Penniman
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2018-09-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -122,6 +122,20 @@ dependencies:
|
|
|
122
122
|
- - ">="
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
124
|
version: '0'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: tzinfo
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
125
139
|
description: A public collection of helpers used in multiple projects
|
|
126
140
|
email:
|
|
127
141
|
- cpenniman@invoca.com
|
|
@@ -130,15 +144,27 @@ extensions: []
|
|
|
130
144
|
extra_rdoc_files: []
|
|
131
145
|
files:
|
|
132
146
|
- ".gitignore"
|
|
147
|
+
- ".ruby-version"
|
|
133
148
|
- Gemfile
|
|
149
|
+
- Gemfile.lock
|
|
134
150
|
- LICENSE.txt
|
|
135
151
|
- README.md
|
|
136
152
|
- Rakefile
|
|
137
153
|
- invoca-utils.gemspec
|
|
138
154
|
- lib/invoca/utils.rb
|
|
139
155
|
- lib/invoca/utils/diff.rb
|
|
156
|
+
- lib/invoca/utils/guaranteed_utf8_string.rb
|
|
157
|
+
- lib/invoca/utils/http.rb
|
|
158
|
+
- lib/invoca/utils/map_compact.rb
|
|
159
|
+
- lib/invoca/utils/min_max.rb
|
|
160
|
+
- lib/invoca/utils/stable_sort.rb
|
|
161
|
+
- lib/invoca/utils/time.rb
|
|
140
162
|
- lib/invoca/utils/version.rb
|
|
141
163
|
- test/test_helper.rb
|
|
164
|
+
- test/unit/guaranteed_utf8_string_test.rb
|
|
165
|
+
- test/unit/map_compact_test.rb
|
|
166
|
+
- test/unit/stable_sort_test.rb
|
|
167
|
+
- test/unit/time_calculations_test.rb
|
|
142
168
|
- test/unit/utils_test.rb
|
|
143
169
|
homepage: ''
|
|
144
170
|
licenses:
|
|
@@ -160,10 +186,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
160
186
|
version: '0'
|
|
161
187
|
requirements: []
|
|
162
188
|
rubyforge_project:
|
|
163
|
-
rubygems_version: 2.
|
|
189
|
+
rubygems_version: 2.6.13
|
|
164
190
|
signing_key:
|
|
165
191
|
specification_version: 4
|
|
166
192
|
summary: A public collection of helpers used in multiple projects
|
|
167
193
|
test_files:
|
|
168
194
|
- test/test_helper.rb
|
|
195
|
+
- test/unit/guaranteed_utf8_string_test.rb
|
|
196
|
+
- test/unit/map_compact_test.rb
|
|
197
|
+
- test/unit/stable_sort_test.rb
|
|
198
|
+
- test/unit/time_calculations_test.rb
|
|
169
199
|
- test/unit/utils_test.rb
|