Mo' Wiki
/hunter/perl ironman/when a script becomes a module
From Script to Modules
Recently I wrote about using Chart::Clicker to create plots of the high and low temperature forecast. The code I demonstrated was in the form a script.
The code did its job, but was not structured so well nor was it as easy to (re)use as it could be. Let’s examine the situation and see if we can’t make it better.
Separation of Concerns
Instead of one big script let’s break the work into parts. A natural break down is:
- Get Data to Plot
- Plot the Data
Get Data
The chart itself doesn’t care where the data comes from so let’s encapsulate that functionality into its own module. The original script used the subroutine get_high_low_data() to query a temperature forecast URL to obtain the data. We’ll derive a class that will handle this behavior.
Weather::Underground::Forecast
The class will have the characteristic that if you provide the object constructor a location (in the form of zip, ‘lat,long’ or ‘city,ST’) then it will be equipped to retrieve the highs and lows for the location. Something like this is the idea:
use Weather::Underground::Forecast; my $forecaster = Weather::Underground::Forecast->new(location => 99701); my ($highs, $lows) = $forecaster->temperatures;
So the consumer of the “Get Data” class just has to pass in a location, then ask for the temperatures. They don’t need to know how the data is obtained. Note in the original script we were scraping data via WWW::Mechanize and HTML::TreeBuilder. They are handy modules when you don’t have a API to get at data, but in this case we do. Thus, Weather::Underground::Forecast was born to get at the weather source API instead of scraping. The weather source API returns XML data, so we parse the highs and lows from that, but again as the consumer of the class you may not care at all about these encapsulated details. You just want to get the data and and use it make a nice chart of the temperature forecast.
Plot Data
The other major part of the process is to plot the data. This gave rise to Chart::Weather::Forecast::Temperature. In design of this module, I went for about the simplest API I could: a few documented attributes and one documented method - create_chart. The usage scenario is:
use Chart::Weather::Forecast::Temperature; my $charter = Chart::Weather::Forecast::Temperature->new( highs => $highs, lows => $lows, chart_temperature_file => '/tmp/temperature-forecast.png', ); $charter->create_chart;
So one passes the high and low data to the constructor. Then they just call create_chart to do the work and write out the plot file.
Put It Together
Putting the two modules together we get the complete functionality of the original script in a very concise fashion:
use Weather::Underground::Forecast; use Chart::Weather::Forecast::Temperature; my $forecaster = Weather::Underground::Forecast->new(location => 99701); my $charter = Chart::Weather::Forecast::Temperature->new( highs => $forecaster->highs, lows => $forecaster->lows ); $charter->create_chart; # writes to /tmp/temperature-forecast.png by default
With just a few lines of code one constructs a couple of objects and calls a few methods to obtain a plot of the temperature forecast for an arbitrary location. Now instead of a copying, pasting and editing somebody else’s script to one’s personal tastes, one can instead install a couple of CPAN modules and put them to work with a few strokes of the pen.. errr.. keyboard.