So I am parsing XML into objects using LibXML::XML::Reader. I had this code:
1 def parse_events()
2 events = Events.new
3 @reader.read #go to next element
4 #read while have reached end of events
5 while not(is_end_element?('events')) do
6 if is_element?('type')
7 @reader.read
8 events.type = @reader.value
9 @reader.read
10 has_reached_end_tag?('type')
11 end
12 if is_element?('duplications')
13 @reader.read
14 events.duplications = @reader.value.to_i
15 @reader.read
16 has_reached_end_tag?('duplications')
17 end
18 if is_element?('speciations')
19 @reader.read
20 events.speciations = @reader.value.to_i
21 @reader.read
22 has_reached_end_tag?('speciations')
23 end
24 if is_element?('losses')
25 @reader.read
26 events.losses = @reader.value.to_i
27 @reader.read
28 has_reached_end_tag?('losses')
29 end
30 if is_element?('confidence')
31 events.confidence = parse_confidence
32 end
33 @reader.read
34 end
35 return events
36 end #parse_events
This was too much of code duplication, but the problem was that each time different method of the events object is called.
After some searching on interwebs, luckily, I found out that ruby has a method send, which invokes the method identified by symbol and passes any arguments.
So I created such method:
def parse_simple_element(object, name)
if is_element?(name)
@reader.read
object.send("#{name}=", @reader.value)
@reader.read
has_reached_end_tag?(name)
end
end
So now the method parse_events() can be rewritten like this:
def parse_events()
events = Events.new
@reader.read #go to next element
while not(is_end_element?('events')) do
['type', 'duplications', 'speciations', 'losses'].each { |elmt|
parse_simple_element(events, elmt)
}
if is_element?('confidence')
events.confidence = parse_confidence
end
@reader.read
end
return events
end #parse_events
Much shorter!! Now I run the unit tests and it works! No worries that I broke something while refactoring code. In cases like this I really appreciate the power of unit testing.