From 5f955564fb312837faa17cda1594938ff201eeb2 Mon Sep 17 00:00:00 2001 From: Dan Elbert Date: Fri, 15 Jan 2016 16:09:34 -0600 Subject: [PATCH] Unit conversion --- app/models/unit_conversion.rb | 43 +++++++++++++++++++++++------ spec/models/unit_conversion_spec.rb | 13 +++++++++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/app/models/unit_conversion.rb b/app/models/unit_conversion.rb index 4768863..d5c2846 100644 --- a/app/models/unit_conversion.rb +++ b/app/models/unit_conversion.rb @@ -48,6 +48,20 @@ module UnitConversion unit.compatible_with? Unitwise(1, 'g/ml') end + def volume?(unit) + unit.compatible_with? Unitwise(1, 'ml') + end + + def mass?(unit) + unit.compatible_with? Unitwise(1, 'g') + end + + def get_density(str) + unit = parse(str) + raise UnknownUnitError, "#{str} expected to be a density" unless density?(unit) + unit + end + def normalize_unit_names(unit_description) unit_description.downcase.gsub(/[a-z]+/) do |match| UNIT_ALIAS_MAP[match] || match @@ -71,7 +85,7 @@ module UnitConversion end end - def convert(quantity, factor, input_unit, output_unit) + def convert(quantity, factor, input_unit, output_unit, density = nil) value = get_value(quantity) factor = get_value(factor) @@ -80,23 +94,36 @@ module UnitConversion factor = factor.to_d(10) end - input_unit = normalize_unit_names(input_unit) - output_unit = normalize_unit_names(output_unit) + converted = value * factor - original = Unitwise(value, input_unit) - scaled = original * factor + input_unit = normalize_unit_names(input_unit) unless input_unit.nil? + output_unit = normalize_unit_names(output_unit) unless output_unit.nil? - converted = scaled.convert_to output_unit + if input_unit && output_unit && input_unit != output_unit + in_unit = Unitwise(1, input_unit) + out_unit = Unitwise(1, output_unit) + unit = Unitwise(converted, input_unit) + + if volume?(in_unit) && mass?(out_unit) + unit_density = get_density(density) + unit = unit * unit_density + elsif mass?(in_unit) && volume?(out_unit) + unit_density = get_density(density) + unit = unit / unit_density + end + + converted = unit.convert_to(output_unit).value + end if value.is_a? Rational - rational_val = converted.value.to_r.rationalize(0.001) + rational_val = converted.to_r.rationalize(0.001) if rational_val.denominator == 1 rational_val.to_i.to_s else rational_val.to_s end else - "%g" % ("%.3f" % converted.value) + "%g" % ("%.3f" % converted) end end diff --git a/spec/models/unit_conversion_spec.rb b/spec/models/unit_conversion_spec.rb index 7aa9e33..2abe873 100644 --- a/spec/models/unit_conversion_spec.rb +++ b/spec/models/unit_conversion_spec.rb @@ -40,11 +40,24 @@ RSpec.describe UnitConversion do expect(UnitConversion.convert('2.0', '1', 'tablespoon', 'cup')).to eq '0.125' end + it 'scales odd units without conversion' do + expect(UnitConversion.convert('1/2', '2', 'slices', 'slices')).to eq '1' + expect(UnitConversion.convert('4', '1/8', nil, nil)).to eq '1/2' + end + it 'converts and scales' do expect(UnitConversion.convert('1/2', '2', 'cup', 'tbsp')).to eq '16' expect(UnitConversion.convert('2.0', '1 1/2', 'tablespoon', 'cup')).to eq '0.188' expect(UnitConversion.convert('2', '1 1/2', 'tablespoon', 'cup')).to eq '3/16' end + + it 'converts from volume to mass' do + expect(UnitConversion.convert('5', '1', 'cup', 'ounce', '5 oz/c')).to eq '25' + end + + it 'converts from mass to volume' do + expect(UnitConversion.convert('25', '1', 'ounce', 'cup', '5 oz/c')).to eq '5' + end end describe '.parse' do