rordash 0.1.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 +7 -0
- data/.github/workflows/main.yml +27 -0
- data/.gitignore +20 -0
- data/.rspec +4 -0
- data/.rubocop.yml +247 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/setup +8 -0
- data/dev/setup.rb +26 -0
- data/lib/rordash/debug_util.rb +37 -0
- data/lib/rordash/file_util.rb +95 -0
- data/lib/rordash/hash_util.rb +181 -0
- data/lib/rordash/numeric_util.rb +28 -0
- data/lib/rordash/object_util.rb +18 -0
- data/lib/rordash/path_util.rb +11 -0
- data/lib/rordash/regex_util.rb +36 -0
- data/lib/rordash/url_util.rb +15 -0
- data/lib/rordash/version.rb +5 -0
- data/lib/rordash.rb +18 -0
- data/rordash.gemspec +42 -0
- data/sig/rordash.rbs +4 -0
- data/spec/coverage_helper.rb +14 -0
- data/spec/fixtures/files/sample.csv +3 -0
- data/spec/fixtures/files/sample.json +6 -0
- data/spec/rordash/debug_util_spec.rb +50 -0
- data/spec/rordash/file_util_spec.rb +123 -0
- data/spec/rordash/hash_util_spec.rb +182 -0
- data/spec/rordash/numeric_util_spec.rb +63 -0
- data/spec/rordash/object_util_spec.rb +14 -0
- data/spec/rordash/path_util_spec.rb +24 -0
- data/spec/rordash/regex_util_spec.rb +86 -0
- data/spec/rordash/url_util_spec.rb +25 -0
- data/spec/rordash_spec.rb +7 -0
- data/spec/spec_helper.rb +26 -0
- metadata +323 -0
@@ -0,0 +1,182 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/ModuleLength
|
4
|
+
module Rordash
|
5
|
+
RSpec.describe HashUtil do
|
6
|
+
let(:hash) { { data: { nestedKey: { id: "some-identifier", items: [4, 9, 7] } } } }
|
7
|
+
|
8
|
+
describe 'dot notation' do
|
9
|
+
let(:dotted_hash) do
|
10
|
+
{ "data.nestedKey.id" => "some-identifier",
|
11
|
+
"data.nestedKey.items[0]" => 4,
|
12
|
+
"data.nestedKey.items[1]" => 9,
|
13
|
+
"data.nestedKey.items[2]" => 7 }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".dot" do
|
17
|
+
it "flattens deeply nested objects using dot notation" do
|
18
|
+
expect(described_class.dot(hash, keep_arrays: false)).to eql(dotted_hash)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe ".dotted_keys" do
|
23
|
+
it "flattens deeply nested objects to keys in dot notation" do
|
24
|
+
expect(described_class.dotted_keys(hash, keep_arrays: false)).to eql(dotted_hash.keys)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ".undot" do
|
29
|
+
it "hydrates dotted hash to deeply nested object" do
|
30
|
+
expect(described_class.undot(dotted_hash)).to eql(hash)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '.pick' do
|
35
|
+
it 'picks keys from hash' do
|
36
|
+
paths = %w[data.nestedKey.id data.nestedKey.items[2] data.nestedKey.items[1]]
|
37
|
+
expect(described_class.pick(hash, paths,
|
38
|
+
keep_arrays: false)).to eql({ data: { nestedKey: { id: "some-identifier",
|
39
|
+
items: [9, 7] } } })
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '.get' do
|
44
|
+
it 'picks keys from hash' do
|
45
|
+
expect(described_class.get(hash, 'data.nestedKey.items[1]')).to be(9)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '.set' do
|
50
|
+
it 'sets key value pair at specified dotted path' do
|
51
|
+
expect(described_class.set({}, 'data.nestedKey.items[1]',
|
52
|
+
10)).to eql({ data: { nestedKey: { items: [nil, 10] } } })
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'reject_blank_values' do
|
57
|
+
let(:input) { { a: '', b: ' ', c: nil, d: '123', e: [], f: {} } }
|
58
|
+
|
59
|
+
it 'removes blank values' do
|
60
|
+
expect(described_class.reject_blank_values(input)).to eql({ d: "123", e: [], f: {} })
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'does not deep remove blank values' do
|
64
|
+
expect(described_class.reject_blank_values(input.merge(f: { g: '' }))).to eql({ d: "123", e: [],
|
65
|
+
f: { g: '' } })
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'deep_reject_blank_values' do
|
70
|
+
let(:input) { { a: '', b: ' ', c: nil, d: '123', e: [], f: { g: '' }, h: [1, nil, '', ' ', 3] } }
|
71
|
+
|
72
|
+
it 'removes deep blank values' do
|
73
|
+
expect(described_class.deep_reject_blank_values(input)).to eql({ d: "123", h: [1, 3] })
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe ".from_string" do
|
79
|
+
let(:input) { JSON.dump(a: 1) }
|
80
|
+
|
81
|
+
it "convert json string to hash" do
|
82
|
+
expect(described_class.from_string(input)).to eql({ a: 1 })
|
83
|
+
end
|
84
|
+
|
85
|
+
it "handles hashes" do
|
86
|
+
expect(described_class.from_string(a: 1)).to eql(a: 1)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe ".group_by" do
|
91
|
+
let(:input) { [6.5, 4.12, 6.8, 5.4] }
|
92
|
+
|
93
|
+
context 'with hash input' do
|
94
|
+
let(:input) { [{ shared_key: 1, a: 1 }, { shared_key: 2, b: 2 }, { shared_key: 1, c: 3 }] }
|
95
|
+
|
96
|
+
it 'handles key only' do
|
97
|
+
expect(described_class.group_by(input,
|
98
|
+
:shared_key)).to eql({
|
99
|
+
1 => [{ shared_key: 1, a: 1 },
|
100
|
+
{ shared_key: 1,
|
101
|
+
c: 3 }], 2 => [{ shared_key: 2, b: 2 }]
|
102
|
+
})
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
it "groups elements" do
|
107
|
+
expect(described_class.group_by(input, lambda { |val|
|
108
|
+
val.floor
|
109
|
+
})).to eql({ 4 => [4.12], 5 => [5.4], 6 => [6.5, 6.8] })
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '.get_first_present' do
|
114
|
+
let(:google_address) { '2253-20800 Westminster Hwy, Richmond, BC V6V 2W3, Canada' }
|
115
|
+
let(:input) do
|
116
|
+
{ 'google_address' => '',
|
117
|
+
'main_property' => { 'google_address' => '2253-20800 Westminster Hwy, Richmond, BC V6V 2W3, Canada' } }
|
118
|
+
end
|
119
|
+
let(:paths) { %w[google_address main_property.google_address] }
|
120
|
+
|
121
|
+
subject(:present_val) { described_class.get_first_present(input, paths) }
|
122
|
+
|
123
|
+
it 'returns first filled match' do
|
124
|
+
expect(present_val).to eql(google_address)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe '.digest' do
|
129
|
+
let(:input) { { key: 'some-value' } }
|
130
|
+
|
131
|
+
subject(:digested_str) { described_class.digest(input) }
|
132
|
+
it 'returns encoded digest string of input' do
|
133
|
+
expect(digested_str).to eql('POWr8Qgq9OA+L8ejA4gkAG2kgNs=')
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe ".to_str" do
|
138
|
+
it "convert hash to json string" do
|
139
|
+
# rubocop:disable Layout/LineLength
|
140
|
+
expect(described_class.to_str(hash)).to eql("{\"data\":{\"nestedKey\":{\"id\":\"some-identifier\",\"items\":[4,9,7]}}}")
|
141
|
+
# rubocop:enable Layout/LineLength
|
142
|
+
end
|
143
|
+
|
144
|
+
it "handles strings" do
|
145
|
+
expect(described_class.to_str("{ a: 1 }")).to eql("{ a: 1 }")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe ".pretty" do
|
150
|
+
it "returns correctly formatted string" do
|
151
|
+
expect(described_class.pretty(hash)).to eql(%({
|
152
|
+
"data": {
|
153
|
+
"nestedKey": {
|
154
|
+
"id": "some-identifier",
|
155
|
+
"items": [
|
156
|
+
4,
|
157
|
+
9,
|
158
|
+
7
|
159
|
+
]
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}))
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'with array' do
|
166
|
+
let(:input) { [{ a: 1 }, b: 2] }
|
167
|
+
|
168
|
+
it "returns correctly formatted string" do
|
169
|
+
expect(described_class.pretty(input)).to eql(%([
|
170
|
+
{
|
171
|
+
"a": 1
|
172
|
+
},
|
173
|
+
{
|
174
|
+
"b": 2
|
175
|
+
}
|
176
|
+
]))
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
# rubocop:enable Metrics/ModuleLength
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
RSpec.describe NumericUtil do
|
5
|
+
describe '.numeric?' do
|
6
|
+
let(:value) { '12345' }
|
7
|
+
|
8
|
+
subject(:is_numeric) { described_class.numeric?(value) }
|
9
|
+
|
10
|
+
it 'returns true' do
|
11
|
+
expect(is_numeric).to be_truthy
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'with value with non numeric values' do
|
15
|
+
let(:value) { '12A345' }
|
16
|
+
|
17
|
+
it 'returns false' do
|
18
|
+
expect(is_numeric).to be_falsey
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with blank value' do
|
23
|
+
let(:value) { '' }
|
24
|
+
|
25
|
+
it 'returns false' do
|
26
|
+
expect(is_numeric).to be_falsey
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with whitespace' do
|
31
|
+
let(:value) { ' 1' }
|
32
|
+
|
33
|
+
it 'returns false' do
|
34
|
+
expect(is_numeric).to be_truthy
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '.convert_unit' do
|
40
|
+
let(:from_unit) { :m }
|
41
|
+
let(:to_unit) { :ft }
|
42
|
+
let(:value) { 10 }
|
43
|
+
|
44
|
+
subject(:converted_unit) { described_class.convert_unit(value, from_unit: from_unit, to_unit: to_unit) }
|
45
|
+
|
46
|
+
it 'calculates amount correctly' do
|
47
|
+
expect(converted_unit.round(4)).to be(32.8084)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '.convert_unit_sq' do
|
52
|
+
let(:from_unit) { :m }
|
53
|
+
let(:to_unit) { :ft }
|
54
|
+
let(:value) { 25 }
|
55
|
+
|
56
|
+
subject(:sq_unit) { described_class.convert_unit_sq(value, from_unit: from_unit, to_unit: to_unit) }
|
57
|
+
|
58
|
+
it 'calculates amount correctly' do
|
59
|
+
expect(sq_unit.round(3)).to be(269.098)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
RSpec.describe ObjectUtil do
|
5
|
+
describe '.to_classname' do
|
6
|
+
context 'when given a class object that is not defined' do
|
7
|
+
it 'returns nil' do
|
8
|
+
expect(described_class.to_classname("Foo")).to be_nil
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
RSpec.describe PathUtil do
|
5
|
+
describe '#fixtures_path' do
|
6
|
+
context 'without arg' do
|
7
|
+
it 'returns full fixture path' do
|
8
|
+
fixtures_path = Rails.root.join('spec', 'fixtures')
|
9
|
+
|
10
|
+
expect(described_class.fixtures_path).to eql(Pathname.new(fixtures_path))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'with filename' do
|
15
|
+
let(:filename) { 'some-file-name.png ' }
|
16
|
+
|
17
|
+
it 'returns full fixture path' do
|
18
|
+
fixtures_path = Rails.root.join('spec', 'fixtures')
|
19
|
+
expect(described_class.fixtures_path(filename)).to eql(Pathname.new("#{fixtures_path}/#{filename}"))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
RSpec.describe RegexUtil do
|
5
|
+
describe '.match?' do
|
6
|
+
subject(:matched) { described_class.match?(type, value) }
|
7
|
+
|
8
|
+
let(:value) { nil }
|
9
|
+
let(:type) { nil }
|
10
|
+
|
11
|
+
describe ':email' do
|
12
|
+
let(:type) { :email }
|
13
|
+
|
14
|
+
context 'with valid email' do
|
15
|
+
let(:value) { 'email@domain.com' }
|
16
|
+
|
17
|
+
it 'returns true' do
|
18
|
+
expect(matched).to be_truthy
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with invalid email' do
|
23
|
+
let(:value) { 'emaild.g' }
|
24
|
+
|
25
|
+
it 'returns false' do
|
26
|
+
expect(matched).to be_falsey
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with a URI' do
|
32
|
+
it 'returns true for a valid URI' do
|
33
|
+
expect(described_class.match?(:uri, 'https://www.example.com')).to be_truthy
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns false for an invalid URI' do
|
37
|
+
expect(described_class.match?(:uri, 'not a uri')).to be_falsey
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with a postal code' do
|
42
|
+
it 'returns true for a valid postal code' do
|
43
|
+
expect(described_class.match?(:postal_code, 'V5K 0A1')).to be_truthy
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns false for an invalid postal code' do
|
47
|
+
expect(described_class.match?(:postal_code, 'not a postal code')).to be_falsey
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'with a dotted index' do
|
52
|
+
it 'returns true for a valid dotted index' do
|
53
|
+
expect(described_class.match?(:dotted_index, 'person.address[0]')).to be_truthy
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'returns false for an invalid dotted index' do
|
57
|
+
expect(described_class.match?(:dotted_index, 'not a dotted index')).to be_falsey
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#match' do
|
63
|
+
it 'returns the matched value for a successful match' do
|
64
|
+
expect(described_class.match(:uri, 'https://www.example.com').to_s).to eq('https://www.example.com')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'returns nil for an unsuccessful match' do
|
68
|
+
expect(described_class.match(:uri, 'not a uri')).to be_nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#replace' do
|
73
|
+
it 'replaces the matched value with the replacement string' do
|
74
|
+
expect(described_class.replace(:postal_code, 'V5K 0A1', 'REDACTED')).to eq('REDACTED')
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'returns the original value if the regular expression does not match' do
|
78
|
+
expect(described_class.replace(:postal_code, 'not a postal code', 'REDACTED')).to eq('not a postal code')
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns the original value if the value is not a string' do
|
82
|
+
expect(described_class.replace(:postal_code, 123, 'REDACTED')).to eq(123)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rordash
|
4
|
+
RSpec.describe UrlUtil do
|
5
|
+
let(:url) { "http://html.joe.com/Jobs/123/249 Subert Place Feb29'27.pdf" }
|
6
|
+
|
7
|
+
describe '.safe_escape' do
|
8
|
+
it 'encodes url' do
|
9
|
+
expect(described_class.safe_escape(url)).to eql("http://html.joe.com/Jobs/123/249%20Subert%20Place%20Feb29'27.pdf")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '.error_from_http_status' do
|
14
|
+
it 'returns appropriate http status message' do
|
15
|
+
expect(described_class.error_from_http_status(400)).to eql("Bad Request")
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'with invalid http status code' do
|
19
|
+
it 'unknown http code message' do
|
20
|
+
expect(described_class.error_from_http_status(-1)).to eql("Invalid HTTP Status Code")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.join(__dir__, "..", 'dev', 'setup')
|
4
|
+
require Pathname.new(__dir__).realpath.join('coverage_helper').to_s
|
5
|
+
|
6
|
+
unless defined? Rails
|
7
|
+
module Rails
|
8
|
+
class << self
|
9
|
+
def root
|
10
|
+
Pathname.new(__dir__).join('..')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
# Enable flags like --only-failures and --next-failure
|
18
|
+
config.example_status_persistence_file_path = ".rspec_status"
|
19
|
+
|
20
|
+
# Disable RSpec exposing methods globally on `Module` and `main`
|
21
|
+
config.disable_monkey_patching!
|
22
|
+
|
23
|
+
config.expect_with :rspec do |c|
|
24
|
+
c.syntax = :expect
|
25
|
+
end
|
26
|
+
end
|