There are a number of ways you could do it, but the simplest is probably to create a new class to handle the conversion. For example, you could create a StringTo class and use that to handle various conversions. For your current problem you'd use a class method of 'integer', so it would look something like this:
Code:
number = StingTo.integer("6.09B")
So to do this, you'd first write a unit test class. This is what I ended up with:
Code:
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
require 'test/unit'
require 'string_to'
class TestStringTo < Test::Unit::TestCase
def setup
@decimal_text = "1.1"
@integer_text = "100"
@fraction_text = ".45"
@invalid_text = "X"
@invalid_postscript = "1.1X"
@valid_test_strings = [@decimal_text, @integer_text, @fraction_text]
end
def test_millions
for test_string in @valid_test_strings
@string = "#{test_string}M"
@target = (test_string.to_f * (10**6)).to_i
assert_output_matches_target
end
end
def test_billions
for test_string in @valid_test_strings
@string = "#{test_string}B"
@target = (test_string.to_f * (10**9)).to_i
assert_output_matches_target
end
end
def test_decimal
for test_string in @valid_test_strings
@string = "#{test_string}"
@target = test_string.to_i
assert_output_matches_target
end
end
def test_invalid
assert_raise RuntimeError do
StringTo.integer(@invalid_text)
end
assert_raise NoMethodError do
StringTo.integer(@invalid_postscript)
end
end
private
def assert_output_matches_target
assert_equal @target, StringTo.integer(@string)
end
end
As you write the test you can start writing the class. This is what I ended up with:
Code:
class StringTo
CONVERSIONS = {'M' => 6, 'B' => 9}
def self.integer(string)
number_followed_by_single_letter_pattern = /^(\d+|\d+\.\d*|\d*\.\d+)(\D)$/
number_only_pattern = /^(\d+|\d+\.\d*|\d*\.\d+)$/
if string =~ number_followed_by_single_letter_pattern
return ($1.to_f * (10 ** CONVERSIONS[$2])).to_i
elsif string =~ number_only_pattern
return string.to_i
else
raise
end
end
end
Bookmarks