Permalink
Browse files

new version supports usps rate calc

  • Loading branch information...
1 parent 3eb93a5 commit 8a0a8806f48c3f8d079f8c73f720ef8ba648253b @rizen committed Nov 6, 2012
Showing with 197 additions and 54 deletions.
  1. +7 −26 Changelog
  2. +22 −1 author.t/01_packing_list.t
  3. +43 −0 author.t/02_shipping_options.t
  4. +4 −1 dist.ini
  5. +113 −20 lib/Box/Calc.pm
  6. +6 −4 lib/Box/Calc/BoxType.pm
  7. +2 −2 t/01_boxtype.t
View
@@ -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.
@@ -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';
@@ -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;
@@ -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();
+
View
@@ -4,7 +4,7 @@ license = Perl_5
copyright_holder = Plain Black Corporation
copyright_year = 2012
-version = 0.0400
+version = 0.0500
[@Classic]
@@ -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
View
@@ -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
@@ -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
@@ -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 => {
@@ -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}) {
@@ -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 = {
@@ -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
@@ -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
@@ -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;
View
@@ -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.