@@ -2040,40 +2040,35 @@ def unit_signature
20402040 # @todo This should either be a separate class or at least a class method
20412041 def parse ( passed_unit_string = "0" )
20422042 unit_string = passed_unit_string . dup
2043- unit_string = "#{ Regexp . last_match ( 1 ) } USD" if unit_string =~ /\$ \s *(#{ NUMBER_REGEX } )/
2043+ unit_string = "#{ Regexp . last_match ( 'usd' ) } USD" if unit_string =~ /\$ \s *(?<usd> #{ NUMBER_REGEX } )/
20442044 unit_string . gsub! ( "\u00b0 " . encode ( "utf-8" ) , "deg" ) if unit_string . encoding == Encoding ::UTF_8
20452045
20462046 unit_string . gsub! ( /(\d )[_,](\d )/ , '\1\2' ) # remove underscores and commas in numbers
20472047
20482048 unit_string . gsub! ( /[%'"#]/ , "%" => "percent" , "'" => "feet" , '"' => "inch" , "#" => "pound" )
20492049 if unit_string . start_with? ( COMPLEX_NUMBER )
20502050 match = unit_string . match ( COMPLEX_REGEX )
2051- real_str = match [ :real ]
2052- imaginary_str = match [ :imaginary ]
2051+ real_str , imaginary_str , unit_s = match . values_at ( :real , :imaginary , :unit )
20532052 real = Float ( real_str ) if real_str
20542053 imaginary = Float ( imaginary_str )
2055- unit_s = match [ :unit ]
20562054 real_as_int = real . to_i if real
20572055 real = real_as_int if real_as_int == real
20582056 imaginary_as_int = imaginary . to_i
20592057 imaginary = imaginary_as_int if imaginary_as_int == imaginary
20602058 complex = Complex ( real || 0 , imaginary )
20612059 complex_real = complex . real
20622060 complex = complex . to_i if complex . imaginary . zero? && complex_real == complex_real . to_i
2063- result = unit_class . new ( unit_s || 1 ) * complex
2064- copy ( result )
2065- return
2061+ return copy ( unit_class . new ( unit_s || 1 ) * complex )
20662062 end
20672063
20682064 if unit_string . start_with? ( RATIONAL_NUMBER )
20692065 match = unit_string . match ( RATIONAL_REGEX )
2070- numerator = Integer ( match [ :numerator ] )
2071- denominator = Integer ( match [ :denominator ] )
2072- proper_string = match [ :proper ]
2066+ numerator_string , denominator_string , proper_string , unit_s = match . values_at ( :numerator , :denominator , :proper , :unit )
2067+ numerator = Integer ( numerator_string )
2068+ denominator = Integer ( denominator_string )
20732069 raise ArgumentError , "Improper fractions must have a whole number part" if proper_string && !proper_string . match? ( /^#{ INTEGER_REGEX } $/ )
20742070
20752071 proper = proper_string . to_i
2076- unit_s = match [ :unit ]
20772072 fraction = Rational ( numerator , denominator )
20782073 rational = if proper . negative?
20792074 ( proper - fraction )
@@ -2082,15 +2077,12 @@ def parse(passed_unit_string = "0")
20822077 end
20832078 rational_as_int = rational . to_int
20842079 rational = rational_as_int if rational_as_int == rational
2085- result = unit_class . new ( unit_s || 1 ) * rational
2086- copy ( result )
2087- return
2080+ return copy ( unit_class . new ( unit_s || 1 ) * rational )
20882081 end
20892082
20902083 match = unit_string . match ( NUMBER_REGEX )
2091- unit_str = match [ :unit ]
2084+ unit_str , scalar_str = match . values_at ( :unit , :scalar )
20922085 unit = unit_class . cached . get ( unit_str )
2093- scalar_str = match [ :scalar ]
20942086 mult = scalar_str == "" ? 1.0 : scalar_str . to_f
20952087 mult_as_int = mult . to_int
20962088 mult = mult_as_int if mult_as_int == mult
@@ -2113,63 +2105,50 @@ def parse(passed_unit_string = "0")
21132105 unit_string . gsub! ( /[<>]/ , "" )
21142106
21152107 if ( match = unit_string . match ( TIME_REGEX ) )
2116- hours = match [ :hour ]
2117- minutes = match [ :min ]
2118- seconds = match [ :sec ]
2119- milliseconds = match [ :msec ]
2108+ hours , minutes , seconds , milliseconds = match . values_at ( :hour , :min , :sec , :msec )
21202109 raise ArgumentError , "Invalid Duration" if [ hours , minutes , seconds , milliseconds ] . all? ( &:nil? )
21212110
2122- result = unit_class . new ( "#{ hours || 0 } hours" ) +
2123- unit_class . new ( "#{ minutes || 0 } minutes" ) +
2124- unit_class . new ( "#{ seconds || 0 } seconds" ) +
2125- unit_class . new ( "#{ milliseconds || 0 } milliseconds" )
2126- copy ( result )
2127- return
2111+ return copy ( unit_class . new ( "#{ hours || 0 } hours" ) +
2112+ unit_class . new ( "#{ minutes || 0 } minutes" ) +
2113+ unit_class . new ( "#{ seconds || 0 } seconds" ) +
2114+ unit_class . new ( "#{ milliseconds || 0 } milliseconds" ) )
21282115 end
21292116
2130- # Special processing for unusual unit strings
21312117 # feet -- 6'5"
21322118 if ( match = unit_string . match ( FEET_INCH_REGEX ) )
2133- feet = Integer ( match [ :feet ] )
2134- inches = match [ :inches ]
2135- result = if feet . negative?
2136- unit_class . new ( "#{ feet } ft" ) - unit_class . new ( "#{ inches } inches" )
2137- else
2138- unit_class . new ( "#{ feet } ft" ) + unit_class . new ( "#{ inches } inches" )
2139- end
2140- copy ( result )
2141- return
2119+ feet_str , inches = match . values_at ( :feet , :inches )
2120+ feet = Integer ( feet_str )
2121+ return copy ( if feet . negative?
2122+ unit_class . new ( "#{ feet } ft" ) - unit_class . new ( "#{ inches } inches" )
2123+ else
2124+ unit_class . new ( "#{ feet } ft" ) + unit_class . new ( "#{ inches } inches" )
2125+ end )
21422126 end
21432127
21442128 # weight -- 8 lbs 12 oz
21452129 if ( match = unit_string . match ( LBS_OZ_REGEX ) )
2146- pounds = Integer ( match [ :pounds ] )
2147- oz = match [ :oz ]
2148- result = if pounds . negative?
2149- unit_class . new ( "#{ pounds } lbs" ) - unit_class . new ( "#{ oz } oz" )
2150- else
2151- unit_class . new ( "#{ pounds } lbs" ) + unit_class . new ( "#{ oz } oz" )
2152- end
2153- copy ( result )
2154- return
2130+ pounds_str , oz = match . values_at ( :pounds , :oz )
2131+ pounds = Integer ( pounds_str )
2132+ return copy ( if pounds . negative?
2133+ unit_class . new ( "#{ pounds } lbs" ) - unit_class . new ( "#{ oz } oz" )
2134+ else
2135+ unit_class . new ( "#{ pounds } lbs" ) + unit_class . new ( "#{ oz } oz" )
2136+ end )
21552137 end
21562138
21572139 # stone -- 3 stone 5, 2 stone, 14 stone 3 pounds, etc.
21582140 if ( match = unit_string . match ( STONE_LB_REGEX ) )
2159- stone = Integer ( match [ :stone ] )
2160- pounds = match [ :pounds ]
2161- result = if stone . negative?
2162- unit_class . new ( "#{ stone } stone" ) - unit_class . new ( "#{ pounds } lbs" )
2163- else
2164- unit_class . new ( "#{ stone } stone" ) + unit_class . new ( "#{ pounds } lbs" )
2165- end
2166- copy ( result )
2167- return
2141+ stone_str , pounds = match . values_at ( :stone , :pounds )
2142+ stone = Integer ( stone_str )
2143+ return copy ( if stone . negative?
2144+ unit_class . new ( "#{ stone } stone" ) - unit_class . new ( "#{ pounds } lbs" )
2145+ else
2146+ unit_class . new ( "#{ stone } stone" ) + unit_class . new ( "#{ pounds } lbs" )
2147+ end )
21682148 end
21692149
21702150 # more than one per. I.e., "1 m/s/s"
2171- raise ( ArgumentError , "'#{ passed_unit_string } ' Unit not recognized" ) if unit_string . count ( "/" ) > 1
2172- raise ( ArgumentError , "'#{ passed_unit_string } ' Unit not recognized #{ unit_string } " ) if unit_string =~ /\s [02-9]/
2151+ validate_unit_string_format ( passed_unit_string , unit_string )
21732152
21742153 @scalar , top , bottom = unit_string . scan ( UNIT_STRING_REGEX ) [ 0 ] # parse the string into parts
21752154 top . scan ( TOP_REGEX ) . each do |item |
@@ -2212,13 +2191,11 @@ def parse(passed_unit_string = "0")
22122191 # eliminate all known terms from this string. This is a quick check to see if the passed unit
22132192 # contains terms that are not defined.
22142193 used = "#{ top } #{ bottom } " . gsub ( unit_match_regex , "" ) . gsub ( %r{[\d *, "'_^/$]} , "" )
2215- raise ( ArgumentError , "' #{ passed_unit_string } ' Unit not recognized" ) unless used . empty?
2194+ invalid_unit ( passed_unit_string ) unless used . empty?
22162195
22172196 prefix_map = unit_class . prefix_map
22182197 unit_map = unit_class . unit_map
2219- transform_units = lambda do |item |
2220- prefix = item [ 0 ]
2221- unit = item [ 1 ]
2198+ transform_units = lambda do |( prefix , unit ) |
22222199 prefix_value = prefix_map [ prefix ]
22232200 unit_value = unit_map [ unit ]
22242201 prefix_value ? [ prefix_value , unit_value ] : [ unit_value ]
@@ -2232,5 +2209,22 @@ def parse(passed_unit_string = "0")
22322209 @denominator = UNITY_ARRAY if @denominator . empty?
22332210 self
22342211 end
2212+
2213+ def validate_unit_string_format ( passed_unit_string , unit_string )
2214+ slash_count = unit_string . count ( "/" )
2215+ return if slash_count <= 1 && unit_string !~ /\s [02-9]/
2216+
2217+ if slash_count > 1
2218+ invalid_unit ( passed_unit_string )
2219+ else
2220+ invalid_unit ( passed_unit_string , unit_string )
2221+ end
2222+ end
2223+
2224+ def invalid_unit ( unit_string , additional_info = nil )
2225+ error_msg = "'#{ unit_string } ' Unit not recognized"
2226+ error_msg += " #{ additional_info } " if additional_info
2227+ raise ArgumentError , error_msg
2228+ end
22352229 end
22362230end
0 commit comments