ruby-measurement 1.2.2 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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 }
|