1+ # encoding: UTF-8
2+
13require 'ruby-measurement/unit'
24require 'ruby-measurement/version'
35
@@ -7,28 +9,46 @@ class Measurement
79 SCIENTIFIC_REGEX = /\A #{ SCIENTIFIC_NUMBER } \s *#{ UNIT_REGEX } ?\z / . freeze
810 RATIONAL_REGEX = /\A ([+-]?\d +\s +)?((\d +)\/ (\d +))?\s *#{ UNIT_REGEX } ?\z / . freeze
911 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+
1131 attr_reader :quantity , :unit
12-
32+
1333 def initialize ( quantity , unit_name = :count )
1434 unit = unit_name
1535 unit = Unit [ unit_name . to_s ] if unit_name . kind_of? ( Symbol ) || unit_name . kind_of? ( String )
16-
36+
1737 raise ArgumentError , "Invalid quantity: #{ quantity } " unless quantity . kind_of? ( Numeric )
1838 raise ArgumentError , "Invalid unit: #{ unit_name } " unless unit . kind_of? ( Unit )
19-
39+
2040 @quantity = quantity
2141 @unit = unit
2242 end
23-
43+
2444 def inspect
2545 to_s
2646 end
27-
47+
2848 def to_s
2949 "#{ quantity } #{ unit } "
3050 end
31-
51+
3252 %w( + - * / ) . each do |operator |
3353 class_eval <<-END , __FILE__ , __LINE__ + 1
3454 def #{ operator } (obj)
@@ -47,7 +67,7 @@ def #{operator}(obj)
4767 end
4868 END
4969 end
50-
70+
5171 def **( obj )
5272 case obj
5373 when Numeric
@@ -56,82 +76,96 @@ def **(obj)
5676 raise ArgumentError , "Invalid arithmetic: #{ self } ** #{ obj } "
5777 end
5878 end
59-
79+
6080 def ==( obj )
6181 obj . kind_of? ( self . class ) && quantity == obj . quantity && unit == obj . unit
6282 end
63-
83+
6484 def convert_to ( unit_name )
6585 unit = Unit [ unit_name ]
6686 raise ArgumentError , "Invalid unit: '#{ unit_name } '" unless unit
67-
87+
6888 return dup if unit == @unit
69-
89+
7090 conversion = @unit . conversion ( unit . name )
7191 raise ArgumentError , "Invalid conversion: '#@unit ' to '#{ unit . name } '" unless conversion
72-
92+
7393 self . class . new ( conversion . call ( @quantity ) , unit . name )
7494 end
75-
95+
7696 def convert_to! ( unit_name )
7797 measurement = convert_to ( unit_name )
7898 @unit , @quantity = measurement . unit , measurement . quantity
7999 self
80100 end
81-
82- def self . parse ( str = '0' )
83- str = str . strip
84-
85- case str
86- when COMPLEX_REGEX then unit_name , quantity = parse_complex ( str )
87- when SCIENTIFIC_REGEX then unit_name , quantity = parse_scientific ( str )
88- when RATIONAL_REGEX then unit_name , quantity = parse_rational ( str )
89- else raise ArgumentError , "Unable to parse: '#{ str } '"
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 )
90118 end
91-
92- unit_name ||= 'count'
93- unit = Unit [ unit_name . strip . downcase ]
94- raise ArgumentError , "Invalid unit: '#{ unit_name } '" unless unit
95-
96- new ( quantity , unit )
97- end
98-
99- def self . define ( unit_name , &block )
100- Unit . define ( unit_name , &block )
101- end
102-
103- private
104-
105- def self . parse_complex ( str )
106- real , imaginary , unit_name = str . scan ( COMPLEX_REGEX ) . first
107- quantity = Complex ( real . to_f , imaginary . to_f ) . to_f
108- return unit_name , quantity
109- end
110-
111- def self . parse_scientific ( str )
112- whole , unit_name = str . scan ( SCIENTIFIC_REGEX ) . first
113- quantity = whole . to_f
114- return unit_name , quantity
115- end
116-
117- def self . parse_rational ( str )
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
125146 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
126162 end
127-
128- return unit_name , quantity
129163 end
130-
164+
131165 define ( :count ) do |unit |
132166 unit . convert_to ( :dozen ) { |value | value / 12.0 }
133167 end
134-
168+
135169 define ( :doz ) do |unit |
136170 unit . alias :dozen
137171 unit . convert_to ( :count ) { |value | value * 12.0 }
0 commit comments