Skip to content

Commit

Permalink
new version supports usps rate calc
Browse files Browse the repository at this point in the history
  • Loading branch information
rizen committed Nov 6, 2012
1 parent 3eb93a5 commit 8a0a880
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 54 deletions.
33 changes: 7 additions & 26 deletions Changelog
Original file line number Diff line number Diff line change
@@ -1,27 +1,8 @@
version 0.0400 - October 25, 2012
* Now an interface to the Box Calc web service.

version 0.0301 - October 18, 2012
* Optimizations adding up to over 500% faster performance.

version 0.0300 - October 18, 2012
* Removed extraneous file.
* Renamed packing_list to packing_instructions
* Added new packing_list function that produces a more useful, smaller, and faster dataset.
* Added category filtering of box types.

version 0.0201 - October 14, 2012
* Decided not to keep it as a public module for now.
* Add max item size to "no box fits" exception.
* Sorting for max item sizes was backwards.
version 0.0500 - October 27, 2012
* NOT backwards compatible.
* Now works asynchronously.
* Add SSL to web service.
* Add shipping_options method which returns USPS postage rate calculations.

version 0.0200 - October 11, 2012
* Add the weight of each box to the packing_list output.

version 0.0101 - October 10, 2012 (10 minutes later)
* Remove test diags and replace them with notes.
* Added a description of how the algorithm works.

version 0.0100 - October 10, 2012

* Initial release.
version 0.0400 - October 25, 2012
* First public release to web service.
23 changes: 22 additions & 1 deletion author.t/01_packing_list.t
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use 5.010;

use_ok 'Box::Calc';

note "API Key: $ENV{BOX_CALC_API_KEY}";
my $calc = Box::Calc->new(api_key => $ENV{BOX_CALC_API_KEY});

isa_ok $calc, 'Box::Calc';
Expand Down Expand Up @@ -33,7 +34,27 @@ $calc->add_item(
z => 4.5,
);

my $packing_list = $calc->packing_list;
my $packing_list = $calc->packing_list->recv;
is ref $packing_list, 'ARRAY', 'got a list back';
is $packing_list->[0]{name}, 'A', 'box A as it should be';

$calc->add_item(
quantity => 1,
name => 'T-Square',
weight => 16,
x => 12,
y => 24,
z => 0.25,
);

my $cv = $calc->packing_list;

isa_ok $cv, 'AnyEvent::CondVar';

eval { $cv->recv };

isa_ok $@, 'Ouch';


done_testing;

43 changes: 43 additions & 0 deletions author.t/02_shipping_options.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use strict;
use Test::More;
use lib '../lib';
use_ok 'Box::Calc';

note "API Key: $ENV{BOX_CALC_API_KEY}";
my $calc = Box::Calc->new(api_key => $ENV{BOX_CALC_API_KEY});

isa_ok $calc, 'Box::Calc';
$calc->add_box_type(
name => 'A',
weight => 20,
x => 5,
y => 10,
z => 8,
compatible_services => ['USPS First-Class', 'USPS Parcel Post', 'USPS Priority Medium Flat Rate Box'],
);
$calc->add_box_type(
name => 'B',
weight => 7,
x => 4,
y => 6,
z => 2,
compatible_services => ['USPS Priority Medium Flat Rate Box', 'USPS Priority'],
);
$calc->add_item(
quantity => 5,
name => 'Banana',
weight => 5,
x => 3,
y => 1,
z => 4.5,
);

my $options = $calc->shipping_options(from => 53716, to => 90210)->recv;

is ref $options, 'HASH', 'got a list back';
is $options->{'USPS Parcel Post'}{parcels}[0]{name}, 'A', 'box A as it should be';
ok ! exists $options->{'USPS First-Class'} , 'too big for first class';


done_testing();

5 changes: 4 additions & 1 deletion dist.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ license = Perl_5
copyright_holder = Plain Black Corporation
copyright_year = 2012

version = 0.0400
version = 0.0500

[@Classic]

Expand All @@ -20,6 +20,9 @@ Test::More = 0
Test::Deep = 0
Log::Any = 0
Ouch = 0.0400
JSON = 2.0;
AnyEvent::HTTP::LWP::UserAgent = 0;
AnyEvent = 0;

[PruneFiles]
filenames = BoxCalc.kpf
133 changes: 113 additions & 20 deletions lib/Box/Calc.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use Moose;
use Box::Calc::BoxType;
use Box::Calc::Item;
use Ouch;
use LWP::UserAgent;
use JSON qw(to_json from_json);
use AnyEvent::HTTP::LWP::UserAgent;
use AnyEvent;

=head1 NAME
Expand All @@ -27,8 +28,14 @@ Box::Calc - Packing Algorithm
$box_calc->add_item( 1, { x => 3.3, y => 3, z => 4, weight => 4.5, name => 'apple' });
$box_calc->add_item( 2, { x => 8, y => 2.5, z => 2.5, weight => 14, name => 'water bottle' });
# get a packing list
my $packing_list = $box_calc->packing_list;
# get a packing list synchronously
my $packing_list = $box_calc->packing_list->recv;
# get a packing list asynchronously
my $cv = $box_calc->packing_list;
# ... do stuff ...
my $packing_list = $cv->recv;
=head1 DESCRIPTION
Expand Down Expand Up @@ -136,6 +143,7 @@ Returns a data structure with all the item names and quantities packed into boxe
[
{ # box one
id => "xxx",
name => "big box",
weight => 30.1,
packing_list => {
Expand All @@ -150,15 +158,91 @@ Returns a data structure with all the item names and quantities packed into boxe

sub packing_list {
my $self = shift;
my $payload = {};
my $payload = {api_key => $self->api_key};
foreach my $type (@{$self->box_types}) {
push @{$payload->{box_types}}, {
weight => $type->weight,
x => $type->x,
y => $type->y,
z => $type->z,
name => $type->name,
};
}
foreach my $item (@{$self->items}) {
push @{$payload->{items}}, {
quantity => $item->quantity,
item => {
weight => $item->weight,
x => $item->x,
y => $item->y,
z => $item->z,
name => $item->name,
},
};
}
return $self->_call('packing_list', [$payload]);
}

=head2 shipping_options( params )
Returns a data structure with all the item names and quantities packed into boxes, and all the shipping methods and prices. This can be used to provide shipping pricing options.
{
'USPS Parcel Post' => {
postage => 11.12,
number_of_parcels => 1,
weight => 30.1,
parcels => [
{ # box one
id => "xxx",
name => "big box",
weight => 30.1,
packing_list => {
"soda" => 3,
"apple" => 1,
"water bottle" => 2,
},
shipping => {
postage => 11.12,
}
}
]
}
}
=over
=item params
A hash of parameters that affect the results returned.
=over
=item from
A 5 digit zip code where the packages will originate from.
=item to
A 5 digit zip code (if shipping inside the United States) or the name of a country (if shipping outside the United States).
=back
=back
=cut

sub shipping_options {
my ($self, %params) = @_;
my $payload = {api_key => $self->api_key, to => $params{to}, from => $params{from}};
foreach my $type (@{$self->box_types}) {
push @{$payload->{box_types}}, {
weight => $type->weight,
x => $type->x,
y => $type->y,
z => $type->z,
name => $type->name,
categories => $type->categories,
compatible_services => $type->compatible_services,
};
}
foreach my $item (@{$self->items}) {
Expand All @@ -173,9 +257,14 @@ sub packing_list {
},
};
}
return $self->_call('packing_list', [$self->api_key, $payload]);
return $self->_call('shipping_options', [$payload]);
}

has _uri => (
is => 'rw',
default => 'https://api.boxcalc.net/rpc',
);

sub _call {
my ($self, $method, $params) = @_;
my $payload = {
Expand All @@ -184,29 +273,33 @@ sub _call {
method => $method,
params => $params,
};
my $ua = LWP::UserAgent->new;
my $ua = AnyEvent::HTTP::LWP::UserAgent->new;
$ua->timeout(30);
my $response = $ua->post('http://api.boxcalc.net/rpc',
my $cv = AnyEvent->condvar;
$ua->post_async($self->_uri,
Content_Type => 'application/json',
Content => to_json($payload),
Accept => 'application/json',
);
my $content = $response->decoded_content;
my $hash = eval{from_json($content)};
if ($@) {
ouch 500, 'Unable to parse response.', $content;
}
if (exists $hash->{error}) {
ouch $hash->{error}{code}, $hash->{error}{message}, $hash->{error}{data};
}
return $hash->{result};
Accept => 'application/json')->cb(sub {
my $response = shift->recv;
my $content = $response->decoded_content;
my $hash = eval{from_json($content)};
if ($@) {
ouch 500, 'Unable to parse response.', $content;
}
if (exists $hash->{error}) {
ouch $hash->{error}{code}, $hash->{error}{message}, $hash->{error}{data};
}
$cv->send($hash->{result});
});
return $cv;
}

=head1 PREREQS
L<Moose>
L<Ouch>
L<LWP::UserAgent>
L<AnyEvent>
L<AnyEvent::HTTP::LWP::UserAgent>
L<JSON>
=head1 SUPPORT
Expand Down
10 changes: 6 additions & 4 deletions lib/Box/Calc/BoxType.pm
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ The weight of the empty box.
The name of your box.
=back
=item compatible_services
An array reference of shipping services this box is compatible with. See the complete list of services at L<http://api.boxcalc.net>. This is only necessary if you'r e using the C<shipping_options> method.
=back
=head2 name
=back
Returns the name of this box.
=cut

Expand Down Expand Up @@ -86,11 +87,12 @@ has name => (
required => 1,
);

has categories => (
has compatible_services => (
is => 'ro',
isa => 'ArrayRef',
default => sub {[]},
);


no Moose;
__PACKAGE__->meta->make_immutable;
4 changes: 2 additions & 2 deletions t/01_boxtype.t
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ use 5.010;

use_ok 'Box::Calc::BoxType';

my $container = Box::Calc::BoxType->new(x => 3, y => 7, z => 2, weight => 20, name => 'big, big box', categories => ['USPS Priority']);
my $container = Box::Calc::BoxType->new(x => 3, y => 7, z => 2, weight => 20, name => 'big, big box', compatible_services => ['USPS Priority']);

isa_ok $container, 'Box::Calc::BoxType';

is $container->x, 3, 'took x';
is $container->y, 7, 'took y';
is $container->z, 2, 'took z';
is $container->name, 'big, big box', 'took name';
cmp_deeply $container->categories, ['USPS Priority'], 'took categories';
cmp_deeply $container->compatible_services, ['USPS Priority'], 'took categories';

done_testing;

0 comments on commit 8a0a880

Please sign in to comment.