Datasheet
Chapter 1: Building Resources
27
attr_accessor :result, :tokens, :state, :ingredient_words,
:instruction_words
def initialize(str, ingredient)
@result = ingredient
@tokens = str.split()
@state = :amount
@ingredient_words = []
@instruction_words = []
end
def parse
tokens.each do |token|
consumed = self.send(state, token)
redo unless consumed
end
result.ingredient = ingredient_words.join(“ “)
result.instruction = instruction_words.join(“ “)
result
end
end
The parse method is of the most interest. After splitting the input string into individual words, the class
loops through each word, calling a method named by the current state. The states are intended to mimic
the piece of data being read, so they start with
:amount , because the expectation is that the numerical
amount of the ingredient will start the line. Each
state method returns true or false . If false is
returned, then the loop is rerun with the same token (presumably a method that returns
false will have
changed the state of the system so that a different method can attempt to consume the token). After the
parser runs out of tokens, it builds up the ingredient and instruction strings out of the lists that the
parser has gathered.
The parser contains one method for each piece of data, starting with the amount of ingredient to be used,
as follows:
def amount(token)
if token.index(“/”)
numerator, denominator = token.split(“/”)
fraction = Rational(numerator.to_i, denominator.to_i)
amount = fraction.to_f
elsif token.to_f > 0
amount = token.to_f
end
result.amount += amount
self.state = :unit
true
end
If the input token contains a slash, then the assumption is that the user has entered a fraction, and the
string is split into two pieces and a Ruby rational object is created and then converted to a float (because
the database stores the data as a float). Otherwise, if it ’ s an integer or rational value, the number is taken
as is. The number is added to the amount already in the result (because an improper fraction would
come through this method in two separate pieces). The state is changed to
:unit , and the method
returns
true to signify that the token has been consumed.
c01.indd 27c01.indd 27 1/30/08 4:02:29 PM1/30/08 4:02:29 PM