rordash 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|