Skip to content

Commit

Permalink
Adapted Weather::WWO to Weather::Underground::Forecast
Browse files Browse the repository at this point in the history
  • Loading branch information
mateu committed Dec 29, 2010
1 parent 35076e8 commit 1825dc2
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
.project
.includepath
.build
Weather-Underground-Forecast*
2 changes: 2 additions & 0 deletions Changes
@@ -0,0 +1,2 @@
{{$NEXT}}
- Initial Release
Empty file removed README
Empty file.
18 changes: 18 additions & 0 deletions dist.ini
@@ -0,0 +1,18 @@
name = Weather-Underground-Forecast
abstract = Weather Underground Forecast Data

[VersionFromModule]

[NextRelease]

[@Git]

[@Basic]

[PodSyntaxTests]
[PodCoverageTests]
[CriticTests]

[AutoPrereqs]


199 changes: 199 additions & 0 deletions lib/Weather/Underground/Forecast.pm
@@ -0,0 +1,199 @@
package Weather::Underground::Forecast;
use Moose;
use namespace::autoclean;
use LWP::Simple;
use XML::Simple;

use Data::Dumper::Concise;

our $VERSION = '0.01';

=head1 Name
Weather::Underground::Forecast - Simple API to Weather Underground Forecast Data
=head1 Synopsis
Get the weather forecast:
my $forecast = Weather::Underground::Forecast->new(
location => $location,
temperature_units => 'fahrenheit', # or 'celsius'
);
Where the $location can be:
* city,state (Bloomington,IN)
* zip code (47401)
* latitude,longitude (46,-113)
my ($highs, $lows) = $forecast->temperatures;
NOTE: I<location> is the only required parameter to C<new()>
=cut

has 'location' => (
is => 'rw',
isa => 'Str',
required => 1,
writer => 'set_location',
);
has 'temperature_units' => (
is => 'ro',
isa => 'Str',
'default' => 'fahrenheit',
);
has 'data' => (
is => 'rw',
isa => 'ArrayRef[HashRef]',
lazy_build => 1,
);
has 'source_URL' => (
is => 'ro',
isa => 'Any',
lazy_build => 1,
);

# When the location changes, we want to clear the data to insure a new data fetch will happen.
# We need this since data is lazily built, and we used a distinct name for the writer
# so we only clear data when we set the location anytime after initial object construction.
after 'set_location' => sub {
my $self = shift;
$self->clear_data;
};

=head1 Methods
=head2 temperatures
Get the high and low temperatures for the number of days specified.
Returns: Array of two ArrayRefs being the high and low temperatures
Example: my ($highs, $lows) = $wunder->forecast_temperaures;
=cut

sub temperatures {
my $self = shift;
return ( $self->highs, $self->lows );
}

=head2 highs
Get an ArrayRef[Int] of the forecasted high temperatures.
=cut

sub highs {
my $self = shift;

my $key1 = 'high';
my $key2 = $self->temperature_units;
return $self->get_forecast_data_by_two_keys( $key1, $key2 );
}

=head2 lows
Get an ArrayRef[Int] of the forecasted low temperatures.
=cut

sub lows {
my $self = shift;

my $key1 = 'low';
my $key2 = $self->temperature_units;
return $self->get_forecast_data_by_two_keys( $key1, $key2 );
}

=head2 precipitation
Get an ArrayRef[Int] of the forecasted chance of precipitation.
=cut

sub precipitation {
my $self = shift;

return $self->get_forecast_data_by_one_key('pop');
}

=head2 get_forecast_data_by_one_key
Get the values for a single forecast metric that is
only one key deep. An examples is: 'pop' (prob. of precip.)
NOTE: One can dump the data attribute to see
the exact data structure and keys available.
=cut

sub get_forecast_data_by_one_key {
my ( $self, $key ) = @_;

return [ map { $_->{$key} } @{ $self->data } ];
}

=head2 get_forecast_data_by_two_keys
Like the one_key method above but for values that are
two keys deep in the data structure.
=cut

sub get_forecast_data_by_two_keys {
my ( $self, $key1, $key2 ) = @_;

return [ map { $_->{$key1}->{$key2} } @{ $self->data } ];
}

sub _query_URL {
my $self = shift;
return $self->source_URL . $self->location;
}

# Builders

sub _build_data {
my $self = shift;

my $content = get( $self->_query_URL );
die "Couldn't get URL: ", $self->_query_URL unless defined $content;

my $xml = XML::Simple->new;
my $data_ref = $xml->XMLin($content);
my $forecasts = $data_ref->{simpleforecast}->{forecastday};

return $forecasts;
}

sub _build_source_URL {
my $self = shift;
return
'http://api.wunderground.com/auto/wui/geo/ForecastXML/index.xml?query=';
}

__PACKAGE__->meta->make_immutable;
1

__END__
=head1 Limitations
It is possible that location could have more than one forecast.
The behavior of that possibility has not been tested.
=head1 Authors
Mateu Hunter C<hunter@missoula.org>
=head1 Copyright
Copyright 2010, Mateu Hunter
=head1 License
You may distribute this code under the same terms as Perl itself.
=cut
31 changes: 31 additions & 0 deletions t/basic.t
@@ -0,0 +1,31 @@
use strict;
use warnings;
use Test::More;
use Weather::Underground::Forecast;
use LWP::Simple;
use Data::Dumper::Concise;

my $wunder_forecast = Weather::Underground::Forecast->new(
location => 'Missoula,MT',
temperature_units => 'fahrenheit', # or 'celsius'
);

isa_ok( $wunder_forecast, 'Weather::Underground::Forecast' );
can_ok( 'Weather::Underground::Forecast', ( 'temperatures', 'precipitation' ) );

SKIP:
{

# Test internet connection
my $source_URL = $wunder_forecast->_query_URL;
my $content = get($source_URL);
skip( 'Skipping live test using Internet', 3 ) if !$content;

my ( $highs, $lows ) = $wunder_forecast->temperatures;
my $chance_of_precip = $wunder_forecast->precipitation;
is( ref($highs), 'ARRAY', 'highs data structure' );
is( ref($lows), 'ARRAY', 'lows data structure' );
is( ref($chance_of_precip), 'ARRAY', 'precips data structure' );
}

done_testing();

0 comments on commit 1825dc2

Please sign in to comment.