Skip to content

Commit 42a4007

Browse files
rubionicrkoster
andauthored
Inline palm_civet dependency (#4827)
* feat: inline palm_civet dependency The palm_civet dependency has not seen any releases for a long time. Given its small complexity, this commit inlines it into the cloud_controller codebase to reduce external dependencies. Changes: - Added lib/cloud_controller/palm_civet.rb with the inlined implementation - Added spec/unit/lib/cloud_controller/palm_civet_spec.rb with all tests - Updated ByteConverter to use the inlined VCAP::CloudController::PalmCivet - Removed palm_civet gem from Gemfile Fixes rubionic#1 * refactor: rename PalmCivet to ByteQuantity and add MIT license headers - Rename module from PalmCivet to ByteQuantity for clarity - Rename files from palm_civet.rb to byte_quantity.rb - Add MIT license attribution headers per original gem license - Update Gemfile.lock to remove palm_civet gem reference - Update all references in byte_converter.rb * style: fix rubocop offenses in byte_quantity files - Use single-quoted strings - Use do/end for multi-line blocks - Use sprintf instead of String#% - Use self-assignment shorthand (*=) - Use unless instead of if with negation - Use nil? predicate instead of == nil - Remove redundant return statements - Remove redundant self - Add underscores to large numeric literals - Provide exception object to raise --------- Co-authored-by: rkoster <hi@rkoster.dev>
1 parent 971d251 commit 42a4007

File tree

5 files changed

+263
-8
lines changed

5 files changed

+263
-8
lines changed

Gemfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ gem 'newrelic_rpm'
2222
gem 'nokogiri', '>=1.10.5'
2323
gem 'oj'
2424
gem 'openssl', '>= 3.2'
25-
gem 'palm_civet'
2625
gem 'prometheus-client'
2726
gem 'public_suffix'
2827
gem 'puma'

Gemfile.lock

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,6 @@ GEM
353353
openssl (4.0.1)
354354
os (1.1.4)
355355
ostruct (0.6.3)
356-
palm_civet (1.1.0)
357356
parallel (1.27.0)
358357
parallel_tests (5.6.0)
359358
parallel
@@ -664,7 +663,6 @@ DEPENDENCIES
664663
nokogiri (>= 1.10.5)
665664
oj
666665
openssl (>= 3.2)
667-
palm_civet
668666
parallel_tests
669667
pg
670668
prometheus-client

lib/cloud_controller/app_manifest/byte_converter.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
require 'palm_civet'
1+
require 'cloud_controller/byte_quantity'
22

33
module VCAP::CloudController
44
class ByteConverter
@@ -10,17 +10,17 @@ def convert_to_mb(human_readable_byte_value)
1010
return nil if human_readable_byte_value.blank?
1111
raise NonNumericError unless human_readable_byte_value.to_s.match?(/\A-?\d+(?:\.\d+)?/)
1212

13-
PalmCivet.to_megabytes(human_readable_byte_value.to_s)
14-
rescue PalmCivet::InvalidByteQuantityError
13+
ByteQuantity.to_megabytes(human_readable_byte_value.to_s)
14+
rescue ByteQuantity::InvalidByteQuantityError
1515
raise InvalidUnitsError
1616
end
1717

1818
def convert_to_b(human_readable_byte_value)
1919
return nil if human_readable_byte_value.blank?
2020
raise NonNumericError unless human_readable_byte_value.to_s.match?(/\A-?\d+(?:\.\d+)?/)
2121

22-
PalmCivet.to_bytes(human_readable_byte_value.to_s)
23-
rescue PalmCivet::InvalidByteQuantityError
22+
ByteQuantity.to_bytes(human_readable_byte_value.to_s)
23+
rescue ByteQuantity::InvalidByteQuantityError
2424
raise InvalidUnitsError
2525
end
2626

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Derived from the palm_civet library
2+
# Copyright (c) 2013 Anand Gaitonde
3+
# Licensed under the MIT License
4+
# https://github.com/goodmustache/palm_civet
5+
6+
module VCAP
7+
module CloudController
8+
module ByteQuantity
9+
BYTE = 1.0
10+
KILOBYTE = 1024 * BYTE
11+
MEGABYTE = 1024 * KILOBYTE
12+
GIGABYTE = 1024 * MEGABYTE
13+
TERABYTE = 1024 * GIGABYTE
14+
BYTESPATTERN = /^(-?\d+(?:\.\d+)?)([KMGT]i?B?|B)$/i
15+
16+
class InvalidByteQuantityError < RuntimeError
17+
def initialize(msg='byte quantity must be a positive integer with a unit of measurement like M, MB, MiB, G, GiB, or GB')
18+
super
19+
end
20+
end
21+
22+
# Returns a human-readable byte string of the form 10M, 12.5K, and so forth.
23+
# The following units are available:
24+
# * T: Terabyte
25+
# * G: Gigabyte
26+
# * M: Megabyte
27+
# * K: Kilobyte
28+
# * B: Byte
29+
# The unit that results in the smallest number greater than or equal to 1 is
30+
# always chosen.
31+
def self.byte_size(bytes)
32+
raise TypeError.new('must be an integer or float') unless bytes.is_a? Numeric
33+
34+
case
35+
when bytes >= TERABYTE
36+
unit = 'T'
37+
value = bytes / TERABYTE
38+
when bytes >= GIGABYTE
39+
unit = 'G'
40+
value = bytes / GIGABYTE
41+
when bytes >= MEGABYTE
42+
unit = 'M'
43+
value = bytes / MEGABYTE
44+
when bytes >= KILOBYTE
45+
unit = 'K'
46+
value = bytes / KILOBYTE
47+
when bytes >= BYTE
48+
unit = 'B'
49+
value = bytes
50+
else
51+
return '0'
52+
end
53+
54+
value = sprintf('%g', sprintf('%.1f', value))
55+
value << unit
56+
end
57+
58+
# Parses a string formatted by bytes_size as bytes. Note binary-prefixed and
59+
# SI prefixed units both mean a base-2 units:
60+
# * KB = K = KiB = 1024
61+
# * MB = M = MiB = 1024 * K
62+
# * GB = G = GiB = 1024 * M
63+
# * TB = T = TiB = 1024 * G
64+
def self.to_bytes(bytes)
65+
matches = BYTESPATTERN.match(bytes.strip)
66+
raise InvalidByteQuantityError if matches.nil?
67+
68+
value = Float(matches[1])
69+
70+
case matches[2][0].capitalize
71+
when 'T'
72+
value *= TERABYTE
73+
when 'G'
74+
value *= GIGABYTE
75+
when 'M'
76+
value *= MEGABYTE
77+
when 'K'
78+
value *= KILOBYTE
79+
end
80+
81+
value.to_i
82+
rescue TypeError
83+
raise InvalidByteQuantityError
84+
end
85+
86+
# Parses a string formatted by byte_size as megabytes.
87+
def self.to_megabytes(bytes)
88+
(to_bytes(bytes) / MEGABYTE).to_i
89+
end
90+
end
91+
end
92+
end
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Derived from the palm_civet library
2+
# Copyright (c) 2013 Anand Gaitonde
3+
# Licensed under the MIT License
4+
# https://github.com/goodmustache/palm_civet
5+
6+
require 'spec_helper'
7+
require 'cloud_controller/byte_quantity'
8+
9+
module VCAP::CloudController
10+
RSpec.describe ByteQuantity do
11+
describe '#byte_size' do
12+
it 'prints in the largest possible unit' do
13+
expect(ByteQuantity.byte_size(10 * ByteQuantity::TERABYTE)).to eq('10T')
14+
expect(ByteQuantity.byte_size(10.5 * ByteQuantity::TERABYTE)).to eq('10.5T')
15+
16+
expect(ByteQuantity.byte_size(10 * ByteQuantity::GIGABYTE)).to eq('10G')
17+
expect(ByteQuantity.byte_size(10.5 * ByteQuantity::GIGABYTE)).to eq('10.5G')
18+
19+
expect(ByteQuantity.byte_size(100 * ByteQuantity::MEGABYTE)).to eq('100M')
20+
expect(ByteQuantity.byte_size(100.5 * ByteQuantity::MEGABYTE)).to eq('100.5M')
21+
22+
expect(ByteQuantity.byte_size(100 * ByteQuantity::KILOBYTE)).to eq('100K')
23+
expect(ByteQuantity.byte_size(100.5 * ByteQuantity::KILOBYTE)).to eq('100.5K')
24+
25+
expect(ByteQuantity.byte_size(1)).to eq('1B')
26+
end
27+
28+
it "prints '0' for zero bytes" do
29+
expect(ByteQuantity.byte_size(0)).to eq('0')
30+
end
31+
32+
it 'raises a type error on non-number values' do
33+
expect do
34+
ByteQuantity.byte_size('something else')
35+
end.to raise_error(TypeError, 'must be an integer or float')
36+
end
37+
end
38+
39+
describe '#to_bytes' do
40+
it 'parses byte amounts with short units (e.g. M, G)' do
41+
expect(ByteQuantity.to_bytes('5B')).to eq(5)
42+
expect(ByteQuantity.to_bytes('5K')).to eq(5 * ByteQuantity::KILOBYTE)
43+
expect(ByteQuantity.to_bytes('5M')).to eq(5 * ByteQuantity::MEGABYTE)
44+
expect(ByteQuantity.to_bytes('5G')).to eq(5 * ByteQuantity::GIGABYTE)
45+
expect(ByteQuantity.to_bytes('5T')).to eq(5 * ByteQuantity::TERABYTE)
46+
end
47+
48+
it 'parses byte amounts that are float (e.g. 5.3KB)' do
49+
expect(ByteQuantity.to_bytes('13.5KB')).to eq(13_824)
50+
expect(ByteQuantity.to_bytes('4.5KB')).to eq(4608)
51+
expect(ByteQuantity.to_bytes('2.55KB')).to eq(2611)
52+
end
53+
54+
it 'parses byte amounts with long units (e.g MB, GB)' do
55+
expect(ByteQuantity.to_bytes('5MB')).to eq(5 * ByteQuantity::MEGABYTE)
56+
expect(ByteQuantity.to_bytes('5mb')).to eq(5 * ByteQuantity::MEGABYTE)
57+
expect(ByteQuantity.to_bytes('2GB')).to eq(2 * ByteQuantity::GIGABYTE)
58+
expect(ByteQuantity.to_bytes('3TB')).to eq(3 * ByteQuantity::TERABYTE)
59+
end
60+
61+
it 'parses byte amounts with long binary units (e.g MiB, GiB)' do
62+
expect(ByteQuantity.to_bytes('5MiB')).to eq(5 * ByteQuantity::MEGABYTE)
63+
expect(ByteQuantity.to_bytes('5mib')).to eq(5 * ByteQuantity::MEGABYTE)
64+
expect(ByteQuantity.to_bytes('2GiB')).to eq(2 * ByteQuantity::GIGABYTE)
65+
expect(ByteQuantity.to_bytes('3TiB')).to eq(3 * ByteQuantity::TERABYTE)
66+
end
67+
68+
it 'allows whitespace before and after the value' do
69+
expect(ByteQuantity.to_bytes("\t\n\r 5MB ")).to eq(5 * ByteQuantity::MEGABYTE)
70+
end
71+
72+
context 'when the byte amount is 0' do
73+
it 'returns 0 bytes' do
74+
expect(ByteQuantity.to_bytes('0TB')).to eq(0)
75+
end
76+
end
77+
78+
context 'when the byte amount is negative' do
79+
it 'returns a negative amount of bytes' do
80+
expect(ByteQuantity.to_bytes('-200B')).to eq(-200)
81+
end
82+
end
83+
84+
context 'when it raises an error' do
85+
it 'raises when the unit is missing' do
86+
expect do
87+
ByteQuantity.to_bytes('5')
88+
end.to raise_error(ByteQuantity::InvalidByteQuantityError)
89+
end
90+
91+
it 'raises when the unit is unrecognized' do
92+
expect do
93+
ByteQuantity.to_bytes('5MBB')
94+
end.to raise_error(ByteQuantity::InvalidByteQuantityError)
95+
96+
expect do
97+
ByteQuantity.to_bytes('5BB')
98+
end.to raise_error(ByteQuantity::InvalidByteQuantityError)
99+
end
100+
end
101+
end
102+
103+
describe '#to_megabytes' do
104+
it 'parses byte amounts with short units (e.g. M, G)' do
105+
expect(ByteQuantity.to_megabytes('5B')).to eq(0)
106+
expect(ByteQuantity.to_megabytes('5K')).to eq(0)
107+
expect(ByteQuantity.to_megabytes('5M')).to eq(5)
108+
expect(ByteQuantity.to_megabytes('5m')).to eq(5)
109+
expect(ByteQuantity.to_megabytes('5G')).to eq(5120)
110+
expect(ByteQuantity.to_megabytes('5T')).to eq(5_242_880)
111+
end
112+
113+
it 'parses byte amounts with long units (e.g MB, GB)' do
114+
expect(ByteQuantity.to_megabytes('5B')).to eq(0)
115+
expect(ByteQuantity.to_megabytes('5KB')).to eq(0)
116+
expect(ByteQuantity.to_megabytes('5MB')).to eq(5)
117+
expect(ByteQuantity.to_megabytes('5mb')).to eq(5)
118+
expect(ByteQuantity.to_megabytes('5GB')).to eq(5120)
119+
expect(ByteQuantity.to_megabytes('5TB')).to eq(5_242_880)
120+
end
121+
122+
it 'parses byte amounts with long binary units (e.g MiB, GiB)' do
123+
expect(ByteQuantity.to_megabytes('5B')).to eq(0)
124+
expect(ByteQuantity.to_megabytes('5KiB')).to eq(0)
125+
expect(ByteQuantity.to_megabytes('5MiB')).to eq(5)
126+
expect(ByteQuantity.to_megabytes('5mib')).to eq(5)
127+
expect(ByteQuantity.to_megabytes('5GiB')).to eq(5120)
128+
expect(ByteQuantity.to_megabytes('5TiB')).to eq(5_242_880)
129+
end
130+
131+
it 'allows whitespace before and after the value' do
132+
expect(ByteQuantity.to_megabytes("\t\n\r 5MB ")).to eq(5)
133+
end
134+
135+
context 'when the byte amount is 0' do
136+
it 'returns 0 megabytes' do
137+
expect(ByteQuantity.to_megabytes('0TB')).to eq(0)
138+
end
139+
end
140+
141+
context 'when the byte amount is negative' do
142+
it 'returns a negative amount of megabytes' do
143+
expect(ByteQuantity.to_megabytes('-200MB')).to eq(-200)
144+
end
145+
end
146+
147+
context 'when it raises an error' do
148+
it 'raises when the unit is missing' do
149+
expect do
150+
ByteQuantity.to_megabytes('5')
151+
end.to raise_error(ByteQuantity::InvalidByteQuantityError)
152+
end
153+
154+
it 'raises when the unit is unrecognized' do
155+
expect do
156+
ByteQuantity.to_megabytes('5MBB')
157+
end.to raise_error(ByteQuantity::InvalidByteQuantityError)
158+
159+
expect do
160+
ByteQuantity.to_megabytes('5BB')
161+
end.to raise_error(ByteQuantity::InvalidByteQuantityError)
162+
end
163+
end
164+
end
165+
end
166+
end

0 commit comments

Comments
 (0)