Introduction
4 people came to geek night: Ahrum, Josh, Nick, and myself (Stacy).
We focussed one the last stage of Kata: Removing duplication from the weather & football file processors.
We experimented with and applied some advanced Ruby techniques for the first time, including meta programming with method_missing, send, class_eval, etc. After pwning in Java for so long it's great to get a good feel for these Ruby idioms.
Details
The first thing we tackled was to remove the duplication the the FootballManager class so that only one method actually parsed the file.
Next we converted the WeatherMan class to use a hash to store each days weather, it was using an array before (because the indexing field, day, is a integer).
Next we converted the WeatherMan & Weather classes to use regular expressions to extracting data.
This made the two solutions very similar so we extracted out the file processing behaviour into a FileParser which is given a class to instantiate for each row (Weather, Standing) and a regular expression to identify lines which contain valid data.
We next noticed that the 'data aggregators', WeatherMan & FootballManager had the same methods as the 'data entry classes'; Weather & Standing, viz: maximum_temperater, goals_for, etc. the only difference was the methods on the aggregators had one parameter, the key of the entry (day, team). So we removed the methods on the aggregators and implemented them using method_missing. Our method_missing method took an invocation like: football_manager.goals_for('Arsenal') and invoked self.standing_of_team('Arsenal').goals_for.
2 people left early (before 9pm) leaving 2 of us to go bonkers with DSL's.
We transformed the Weather and Standing classes into specifications:
class Weather < CSVRecord
columns :day.is_an(:integer), :max_temperature.is_an(:integer), :min_temperature.is_an(:integer)
record_format '^\s*(\d+)\s*(\d+)\s*(\d+)'
def spread
return max_temperature - min_temperature
end
endclass Standing < CSVRecord
columns :team, :goals_for.is_an(:integer), :goals_against.is_an(:integer)
record_format '(\w*)\s*\d+\s*\d+\s*\d+\s*\d+\s*(\d+)\s*-\s*(\d+)'
def spread
return (goals_for - goals_against).abs
end
end