ruby-measurement 1.2.2 → 1.2.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/.travis.yml +1 -0
- data/CHANGELOG.md +15 -12
- data/lib/ruby-measurement/core_ext/string.rb +1 -1
- data/lib/ruby-measurement/measurement.rb +95 -61
- data/lib/ruby-measurement/unit.rb +31 -31
- data/lib/ruby-measurement/version.rb +1 -1
- data/spec/ruby-measurement/core_ext/string_spec.rb +5 -5
- data/spec/ruby-measurement/definitions/metric/area_spec.rb +15 -15
- data/spec/ruby-measurement/definitions/metric/capacity_spec.rb +8 -8
- data/spec/ruby-measurement/definitions/metric/length_spec.rb +63 -63
- data/spec/ruby-measurement/definitions/metric/volume_spec.rb +63 -63
- data/spec/ruby-measurement/definitions/metric/weight_spec.rb +79 -79
- data/spec/ruby-measurement/definitions/us_customary/area_spec.rb +35 -35
- data/spec/ruby-measurement/definitions/us_customary/capacity_spec.rb +15 -15
- data/spec/ruby-measurement/definitions/us_customary/length_spec.rb +63 -63
- data/spec/ruby-measurement/definitions/us_customary/volume_spec.rb +48 -48
- data/spec/ruby-measurement/definitions/us_customary/weight_spec.rb +35 -35
- data/spec/ruby-measurement/measurement_spec.rb +104 -70
- data/spec/ruby-measurement/unit_builder_spec.rb +15 -15
- data/spec/ruby-measurement/unit_spec.rb +27 -27
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31687bfdd62b83ca04ffba2a8d1c106d767f8ac4
|
4
|
+
data.tar.gz: 5eb61379bb4dc5b1eaf051c96cf1c55353bb4b0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25e76a22f35f3e5af25a1f79bf0f396eeba2273da6d6d8011dc11f34e02e868b374227aa1f95ee6a541ddaf1b98d6408ddb30f6898a49c43d12852128e8ba79f
|
7
|
+
data.tar.gz: e7f70d5166d49109632bbc8c8a0b8cf3c706fea85c5dc4b3abaddebc0af46581468394ffdae920fdea235bad66dcf1cc0d9b6aa9afe6ab52988ac593454d8d4c
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,32 +1,35 @@
|
|
1
|
+
## ruby-measurement 1.2.3 (Mar 27, 2016)
|
2
|
+
|
3
|
+
* Updated parsing to accept UTF-8 fractions (½, ⅓, etc.). *Matt Huggins*
|
4
|
+
|
1
5
|
## ruby-measurement 1.2.2 (Jul 13, 2014)
|
2
6
|
|
3
|
-
*
|
7
|
+
* Fixed `Measurement::Unit#==` to properly compare units. *Matt Huggins*
|
4
8
|
|
5
9
|
## ruby-measurement 1.2.1 (Jul 27, 2013)
|
6
10
|
|
7
|
-
*
|
11
|
+
* Added `Measurement.names` to list all available units. *Daniele Palombo*
|
8
12
|
|
9
13
|
## ruby-measurement 1.2.0 (Mar 16, 2013)
|
10
14
|
|
11
|
-
*
|
15
|
+
* Added `String#to_measurement`, `String#to_unit`,
|
12
16
|
`Symbol#to_unit`, and `Numeric#to_measurement`. *Matt Huggins*
|
13
17
|
|
14
18
|
## ruby-measurement 1.1.0 (Feb 6, 2013)
|
15
19
|
|
16
|
-
*
|
17
|
-
*
|
18
|
-
*
|
19
|
-
*
|
20
|
-
|
21
|
-
* Fix several conversions that were found to be miscalculating.
|
20
|
+
* Moved unit definition logic into `Unit` class. *Matt Huggins*
|
21
|
+
* Added test suite. *Matt Huggins*
|
22
|
+
* Fixed parsing of `'` and `"` for feet and inches. *Matt Huggins*
|
23
|
+
* Added missing metric weights and U.S. customary capacities. *Matt Huggins*
|
24
|
+
* Fixed several conversions that were found to be miscalculating.
|
22
25
|
*Matt Huggins*
|
23
26
|
|
24
27
|
## ruby-measurement 1.0.0 (Jan 22, 2013)
|
25
28
|
|
26
|
-
*
|
27
|
-
*
|
29
|
+
* Fixed parsing of mixed fractions. *Matt Huggins*
|
30
|
+
* Updated parsing of quantity & unit to work when not separated by a space.
|
28
31
|
*Matt Huggins*
|
29
|
-
*
|
32
|
+
* Updated `Measurement` initializer to accept quantity & unit instead of a
|
30
33
|
single string for parsing. *Matt Huggins*
|
31
34
|
|
32
35
|
## ruby-measurement 0.0.1 (Jan 21, 2013)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
1
3
|
require 'ruby-measurement/unit'
|
2
4
|
require 'ruby-measurement/version'
|
3
5
|
|
@@ -7,28 +9,46 @@ class Measurement
|
|
7
9
|
SCIENTIFIC_REGEX = /\A#{SCIENTIFIC_NUMBER}\s*#{UNIT_REGEX}?\z/.freeze
|
8
10
|
RATIONAL_REGEX = /\A([+-]?\d+\s+)?((\d+)\/(\d+))?\s*#{UNIT_REGEX}?\z/.freeze
|
9
11
|
COMPLEX_REGEX = /\A#{SCIENTIFIC_NUMBER}?#{SCIENTIFIC_NUMBER}i\s*#{UNIT_REGEX}?\z/.freeze
|
10
|
-
|
12
|
+
|
13
|
+
RATIOS = {
|
14
|
+
'¼' => '1/4',
|
15
|
+
'½' => '1/2',
|
16
|
+
'¾' => '3/4',
|
17
|
+
'⅓' => '1/3',
|
18
|
+
'⅔' => '2/3',
|
19
|
+
'⅕' => '1/5',
|
20
|
+
'⅖' => '2/5',
|
21
|
+
'⅗' => '3/5',
|
22
|
+
'⅘' => '4/5',
|
23
|
+
'⅙' => '1/6',
|
24
|
+
'⅚' => '5/6',
|
25
|
+
'⅛' => '1/8',
|
26
|
+
'⅜' => '3/8',
|
27
|
+
'⅝' => '5/8',
|
28
|
+
'⅞' => '7/8',
|
29
|
+
}.freeze
|
30
|
+
|
11
31
|
attr_reader :quantity, :unit
|
12
|
-
|
32
|
+
|
13
33
|
def initialize(quantity, unit_name = :count)
|
14
34
|
unit = unit_name
|
15
35
|
unit = Unit[unit_name.to_s] if unit_name.kind_of?(Symbol) || unit_name.kind_of?(String)
|
16
|
-
|
36
|
+
|
17
37
|
raise ArgumentError, "Invalid quantity: #{quantity}" unless quantity.kind_of?(Numeric)
|
18
38
|
raise ArgumentError, "Invalid unit: #{unit_name}" unless unit.kind_of?(Unit)
|
19
|
-
|
39
|
+
|
20
40
|
@quantity = quantity
|
21
41
|
@unit = unit
|
22
42
|
end
|
23
|
-
|
43
|
+
|
24
44
|
def inspect
|
25
45
|
to_s
|
26
46
|
end
|
27
|
-
|
47
|
+
|
28
48
|
def to_s
|
29
49
|
"#{quantity} #{unit}"
|
30
50
|
end
|
31
|
-
|
51
|
+
|
32
52
|
%w(+ - * /).each do |operator|
|
33
53
|
class_eval <<-END, __FILE__, __LINE__ + 1
|
34
54
|
def #{operator}(obj)
|
@@ -47,7 +67,7 @@ class Measurement
|
|
47
67
|
end
|
48
68
|
END
|
49
69
|
end
|
50
|
-
|
70
|
+
|
51
71
|
def **(obj)
|
52
72
|
case obj
|
53
73
|
when Numeric
|
@@ -56,82 +76,96 @@ class Measurement
|
|
56
76
|
raise ArgumentError, "Invalid arithmetic: #{self} ** #{obj}"
|
57
77
|
end
|
58
78
|
end
|
59
|
-
|
79
|
+
|
60
80
|
def ==(obj)
|
61
81
|
obj.kind_of?(self.class) && quantity == obj.quantity && unit == obj.unit
|
62
82
|
end
|
63
|
-
|
83
|
+
|
64
84
|
def convert_to(unit_name)
|
65
85
|
unit = Unit[unit_name]
|
66
86
|
raise ArgumentError, "Invalid unit: '#{unit_name}'" unless unit
|
67
|
-
|
87
|
+
|
68
88
|
return dup if unit == @unit
|
69
|
-
|
89
|
+
|
70
90
|
conversion = @unit.conversion(unit.name)
|
71
91
|
raise ArgumentError, "Invalid conversion: '#@unit' to '#{unit.name}'" unless conversion
|
72
|
-
|
92
|
+
|
73
93
|
self.class.new(conversion.call(@quantity), unit.name)
|
74
94
|
end
|
75
|
-
|
95
|
+
|
76
96
|
def convert_to!(unit_name)
|
77
97
|
measurement = convert_to(unit_name)
|
78
98
|
@unit, @quantity = measurement.unit, measurement.quantity
|
79
99
|
self
|
80
100
|
end
|
81
|
-
|
82
|
-
|
83
|
-
str =
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
101
|
+
|
102
|
+
class << self
|
103
|
+
def parse(str = '0')
|
104
|
+
str = normalize(str)
|
105
|
+
|
106
|
+
case str
|
107
|
+
when COMPLEX_REGEX then unit_name, quantity = parse_complex(str)
|
108
|
+
when SCIENTIFIC_REGEX then unit_name, quantity = parse_scientific(str)
|
109
|
+
when RATIONAL_REGEX then unit_name, quantity = parse_rational(str)
|
110
|
+
else raise ArgumentError, "Unable to parse: '#{str}'"
|
111
|
+
end
|
112
|
+
|
113
|
+
unit_name ||= 'count'
|
114
|
+
unit = Unit[unit_name.strip.downcase]
|
115
|
+
raise ArgumentError, "Invalid unit: '#{unit_name}'" unless unit
|
116
|
+
|
117
|
+
new(quantity, unit)
|
90
118
|
end
|
91
|
-
|
92
|
-
unit_name
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
whole, _, numerator, denominator, unit_name = str.scan(RATIONAL_REGEX).first
|
119
|
-
|
120
|
-
if numerator && denominator
|
121
|
-
numerator = numerator.to_f + (denominator.to_f * whole.to_f)
|
122
|
-
denominator = denominator.to_f
|
123
|
-
quantity = Rational(numerator, denominator).to_f
|
124
|
-
else
|
119
|
+
|
120
|
+
def define(unit_name, &block)
|
121
|
+
Unit.define(unit_name, &block)
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def normalize(str)
|
127
|
+
str.dup.tap do |str|
|
128
|
+
if str =~ Regexp.new(/(#{RATIOS.keys.join('|')})/)
|
129
|
+
RATIOS.each do |search, replace|
|
130
|
+
str.gsub!(search) { " #{replace}" }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
str.strip!
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def parse_complex(str)
|
139
|
+
real, imaginary, unit_name = str.scan(COMPLEX_REGEX).first
|
140
|
+
quantity = Complex(real.to_f, imaginary.to_f).to_f
|
141
|
+
return unit_name, quantity
|
142
|
+
end
|
143
|
+
|
144
|
+
def parse_scientific(str)
|
145
|
+
whole, unit_name = str.scan(SCIENTIFIC_REGEX).first
|
125
146
|
quantity = whole.to_f
|
147
|
+
return unit_name, quantity
|
148
|
+
end
|
149
|
+
|
150
|
+
def parse_rational(str)
|
151
|
+
whole, _, numerator, denominator, unit_name = str.scan(RATIONAL_REGEX).first
|
152
|
+
|
153
|
+
if numerator && denominator
|
154
|
+
numerator = numerator.to_f + (denominator.to_f * whole.to_f)
|
155
|
+
denominator = denominator.to_f
|
156
|
+
quantity = Rational(numerator, denominator).to_f
|
157
|
+
else
|
158
|
+
quantity = whole.to_f
|
159
|
+
end
|
160
|
+
|
161
|
+
return unit_name, quantity
|
126
162
|
end
|
127
|
-
|
128
|
-
return unit_name, quantity
|
129
163
|
end
|
130
|
-
|
164
|
+
|
131
165
|
define(:count) do |unit|
|
132
166
|
unit.convert_to(:dozen) { |value| value / 12.0 }
|
133
167
|
end
|
134
|
-
|
168
|
+
|
135
169
|
define(:doz) do |unit|
|
136
170
|
unit.alias :dozen
|
137
171
|
unit.convert_to(:count) { |value| value * 12.0 }
|
@@ -3,85 +3,85 @@ require 'set'
|
|
3
3
|
class Measurement
|
4
4
|
class Unit
|
5
5
|
attr_reader :name, :aliases, :conversions
|
6
|
-
|
6
|
+
|
7
7
|
@definitions = {}
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(name)
|
10
10
|
@name = name.to_s
|
11
11
|
@aliases = Set.new
|
12
12
|
@conversions = {}
|
13
13
|
add_alias(name)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def add_alias(*args)
|
17
17
|
args.each do |unit_alias|
|
18
18
|
@aliases << unit_alias.to_s
|
19
19
|
self.class[unit_alias] = self
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def add_conversion(unit_name, &block)
|
24
24
|
@conversions[unit_name.to_s] = block
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def conversion(unit_name)
|
28
28
|
unit = self.class[unit_name]
|
29
29
|
return nil unless unit
|
30
|
-
|
30
|
+
|
31
31
|
unit.aliases.each do |unit_alias|
|
32
32
|
conversion = @conversions[unit_alias.to_s]
|
33
33
|
return conversion if conversion
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
nil
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def inspect
|
40
40
|
to_s
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
def to_s
|
44
44
|
name
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
def ==(obj)
|
48
48
|
obj.kind_of?(self.class) && name == obj.name && aliases == obj.aliases && conversions.all? do |key, proc|
|
49
49
|
[-2.5, -1, 0, 1, 2.5].all? { |n| proc.call(n) == obj.conversions[key].call(n) }
|
50
50
|
end
|
51
51
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
52
|
+
|
53
|
+
class << self
|
54
|
+
def define(unit_name, &block)
|
55
|
+
Builder.new(unit_name, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def [](unit_name)
|
59
|
+
@definitions[unit_name.to_s.downcase]
|
60
|
+
end
|
61
|
+
|
62
|
+
def []=(unit_name, unit)
|
63
|
+
@definitions[unit_name.to_s.downcase] = unit
|
64
|
+
end
|
65
|
+
|
66
|
+
def names
|
67
|
+
@definitions.keys
|
68
|
+
end
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
class Builder
|
72
72
|
def initialize(unit_name, &block)
|
73
73
|
@unit = Unit.new(unit_name)
|
74
74
|
block.call(self) if block_given?
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
def alias(*args)
|
78
78
|
@unit.add_alias(*args)
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
81
|
def convert_to(unit_name, &block)
|
82
82
|
@unit.add_conversion(unit_name, &block)
|
83
83
|
end
|
84
|
-
|
84
|
+
|
85
85
|
def to_unit
|
86
86
|
@unit
|
87
87
|
end
|
@@ -6,29 +6,29 @@ RSpec.describe String do
|
|
6
6
|
subject { '3' }
|
7
7
|
specify { expect(subject.to_measurement).to eq Measurement.new(3) }
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
describe 'with valid quantity and unit' do
|
11
11
|
subject { '3 dozen' }
|
12
12
|
specify { expect(subject.to_measurement).to eq Measurement.new(3, :dozen) }
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
describe 'with valid quantity and invalid unit' do
|
16
16
|
subject { '3 people' }
|
17
17
|
specify { expect { subject.to_measurement }.to raise_error }
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
describe 'with invalid input' do
|
21
21
|
subject { 'foobar' }
|
22
22
|
specify { expect { subject.to_measurement }.to raise_error }
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
describe '#to_unit' do
|
27
27
|
describe 'with valid unit' do
|
28
28
|
subject { 'dozen' }
|
29
29
|
specify { expect(subject.to_unit).to eq Measurement::Unit[:dozen] }
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
describe 'with invalid unit' do
|
33
33
|
subject { 'person' }
|
34
34
|
specify { expect { subject.to_unit }.to raise_error }
|