The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
Build.PL 25
Changes 011
LICENSE 33
MANIFEST 11
META.json 69
META.yml 45
README 125173
cpanfile 05
lib/WebService/Linode/Base.pm 1585
lib/WebService/Linode/DNS.pm 22
lib/WebService/Linode.pm 168266
t/release-kwalitee.t 27
12 files changed (This is a version diff) 328572
@@ -1,4 +1,7 @@
-# This Build.PL for WebService-Linode was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.006.
+# This Build.PL for WebService-Linode was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.007.
+use strict;
+use warnings;
+
 use 5.008005;
-use Module::Build::Tiny 0.036;
+use Module::Build::Tiny 0.038;
 Build_PL();
@@ -1,5 +1,16 @@
 Revision history for Perl module WebService::Linode
 
+0.24  2014-10-18 22:43:33 EDT
+    - add explicit section to POD for send_queued_requests
+    - Add Test::Pod::Coverage to cpanfile for develop
+
+0.23  2014-10-14 16:31:36 EDT
+    - Batched API request support by Kevan Benson <kbenson@cpan.org>
+
+0.22  2014-07-24 10:13:47 EDT
+    - support for the Linode Images service, currently in beta
+      see <https://forum.linode.com/viewtopic.php?f=26&t=11180>
+
 0.21  2014-07-01 13:02:10 EDT
     - new avail_nodebalancers and linode_ip_setrdns methods
     - linode_disk_create now optionaly takes fromdistributionid, isreadonly,   
@@ -1,4 +1,4 @@
-This software is copyright (c) 2008-2009 by =over.
+This software is copyright (c) 2008-2014 by =over.
 
 This is free software; you can redistribute it and/or modify it under
 the same terms as the Perl 5 programming language system itself.
@@ -12,7 +12,7 @@ b) the "Artistic License"
 
 --- The GNU General Public License, Version 1, February 1989 ---
 
-This software is Copyright (c) 2008-2009 by =over.
+This software is Copyright (c) 2008-2014 by =over.
 
 This is free software, licensed under:
 
@@ -272,7 +272,7 @@ That's all there is to it!
 
 --- The Artistic License 1.0 ---
 
-This software is Copyright (c) 2008-2009 by =over.
+This software is Copyright (c) 2008-2014 by =over.
 
 This is free software, licensed under:
 
@@ -1,4 +1,4 @@
-# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.019.
+# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.020.
 Build.PL
 Changes
 LICENSE
@@ -4,7 +4,7 @@
       "=over"
    ],
    "dynamic_config" : 0,
-   "generated_by" : "Dist::Milla version v1.0.5, Dist::Zilla version 5.019, CPAN::Meta::Converter version 2.141520",
+   "generated_by" : "Dist::Milla version v1.0.8, Dist::Zilla version 5.020, CPAN::Meta::Converter version 2.142690",
    "license" : [
       "perl_5"
    ],
@@ -26,13 +26,15 @@
    "prereqs" : {
       "configure" : {
          "requires" : {
-            "Module::Build::Tiny" : "0.036"
+            "Module::Build::Tiny" : "0.038"
          }
       },
       "develop" : {
          "requires" : {
-            "Test::Kwalitee" : "1.12",
-            "Test::Pod" : "1.41"
+            "Dist::Milla" : "v1.0.8",
+            "Test::Kwalitee" : "1.21",
+            "Test::Pod" : "1.41",
+            "Test::Pod::Coverage" : "0"
          }
       },
       "runtime" : {
@@ -61,11 +63,12 @@
          "web" : "https://github.com/mikegrb/WebService-Linode"
       }
    },
-   "version" : "0.21",
+   "version" : "0.24",
    "x_contributors" : [
+      "Kevan Benson <kentrak@gmail.com>",
+      "Mike Greb <mikegrb@cpan.org>",
       "Neil Bowers <neil@bowers.com>",
       "Stan Schwertly <stan@schwertly.com>",
-      "mikegrb <michael@thegrebs.com>",
       "trevorparker <trevor@trevorparker.com>"
    ]
 }
@@ -5,9 +5,9 @@ author:
 build_requires:
   Test::More: '0.88'
 configure_requires:
-  Module::Build::Tiny: '0.036'
+  Module::Build::Tiny: '0.038'
 dynamic_config: 0
-generated_by: 'Dist::Milla version v1.0.5, Dist::Zilla version 5.019, CPAN::Meta::Converter version 2.141520'
+generated_by: 'Dist::Milla version v1.0.8, Dist::Zilla version 5.020, CPAN::Meta::Converter version 2.142690'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -30,9 +30,10 @@ resources:
   bugtracker: https://github.com/mikegrb/WebService-Linode/issues
   homepage: https://github.com/mikegrb/WebService-Linode
   repository: https://github.com/mikegrb/WebService-Linode.git
-version: '0.21'
+version: '0.24'
 x_contributors:
+  - 'Kevan Benson <kentrak@gmail.com>'
+  - 'Mike Greb <mikegrb@cpan.org>'
   - 'Neil Bowers <neil@bowers.com>'
   - 'Stan Schwertly <stan@schwertly.com>'
-  - 'mikegrb <michael@thegrebs.com>'
   - 'trevorparker <trevor@trevorparker.com>'
@@ -16,8 +16,34 @@ Constructor
     For documentation of possible arguments to the constructor, see
     WebService::Linode::Base.
 
+Batch requests
+    Each of the Linode API methods below may optionally be prefixed with
+    QUEUE_ to add that request to a queue to be processed later in one or
+    more batch requests which can be processed by calling
+    send_queued_requests. For example:
+
+        my @linode_ids = () # Get your linode ids through normal methods
+        my @responses = map { $api->linode_ip_list( linodeid=>$_ ) } @linode_ids;
+
+    Can be reduced to a single request:
+
+        my @linode_ids = () # Get your linode ids through normal methods
+        $api->QUEUE_linode_ip_list( linodeid=>$_ ) for @linode_ids;
+        my @responses = $api->send_queued_requests; # One api request
+
+    See WebService::Linode::Base for additional queue management methods.
+
+   send_queued_requests
+    Send queued batch requests, returns list of responses.
+
 Methods from the Linode API
    avail_datacenters
+   avail_nodebalancers
+   avail_distributions
+    Optional Parameters:
+
+    *   distributionid
+
    avail_kernels
     Optional Parameters:
 
@@ -30,7 +56,6 @@ Methods from the Linode API
 
     *   planid
 
-   avail_nodebalancers
    avail_stackscripts
     Optional Parameters:
 
@@ -40,22 +65,15 @@ Methods from the Linode API
 
     *   keywords
 
-   avail_distributions
+   domain_list
     Optional Parameters:
 
-    *   distributionid
-
-   domain_delete
-    Required Parameters:
-
     *   domainid
 
-   domain_create
+   domain_update
     Required Parameters:
 
-    *   domain
-
-    *   type
+    *   domainid
 
     Optional Parameters:
 
@@ -63,6 +81,8 @@ Methods from the Linode API
 
     *   description
 
+    *   domain
+
     *   expire_sec
 
     *   lpm_displaygroup
@@ -79,10 +99,14 @@ Methods from the Linode API
 
     *   ttl_sec
 
-   domain_update
+    *   type
+
+   domain_create
     Required Parameters:
 
-    *   domainid
+    *   domain
+
+    *   type
 
     Optional Parameters:
 
@@ -90,8 +114,6 @@ Methods from the Linode API
 
     *   description
 
-    *   domain
-
     *   expire_sec
 
     *   lpm_displaygroup
@@ -108,29 +130,20 @@ Methods from the Linode API
 
     *   ttl_sec
 
-    *   type
-
-   domain_list
-    Optional Parameters:
+   domain_delete
+    Required Parameters:
 
     *   domainid
 
-   domain_resource_delete
+   domain_resource_update
     Required Parameters:
 
-    *   domainid
-
     *   resourceid
 
-   domain_resource_create
-    Required Parameters:
+    Optional Parameters:
 
     *   domainid
 
-    *   type
-
-    Optional Parameters:
-
     *   name
 
     *   port
@@ -154,15 +167,22 @@ Methods from the Linode API
 
     *   resourceid
 
-   domain_resource_update
+   domain_resource_delete
     Required Parameters:
 
+    *   domainid
+
     *   resourceid
 
-    Optional Parameters:
+   domain_resource_create
+    Required Parameters:
 
     *   domainid
 
+    *   type
+
+    Optional Parameters:
+
     *   name
 
     *   port
@@ -177,19 +197,22 @@ Methods from the Linode API
 
     *   weight
 
-   linode_webconsoletoken
+   linode_resize
     Required Parameters:
 
     *   linodeid
 
-   linode_delete
-    Required Parameters:
+    *   planid
+
+   linode_list
+    Optional Parameters:
 
     *   linodeid
 
-    Optional Parameters:
+   linode_mutate
+    Required Parameters:
 
-    *   skipchecks
+    *   linodeid
 
    linode_boot
     Required Parameters:
@@ -211,11 +234,19 @@ Methods from the Linode API
 
     *   paymentterm
 
-   linode_shutdown
+   linode_clone
     Required Parameters:
 
+    *   datacenterid
+
     *   linodeid
 
+    *   planid
+
+    Optional Parameters:
+
+    *   paymentterm
+
    linode_update
     Required Parameters:
 
@@ -261,44 +292,33 @@ Methods from the Linode API
 
     *   watchdog
 
-   linode_resize
-    Required Parameters:
-
-    *   linodeid
-
-    *   planid
-
-   linode_mutate
+   linode_webconsoletoken
     Required Parameters:
 
     *   linodeid
 
-   linode_clone
+   linode_reboot
     Required Parameters:
 
-    *   datacenterid
-
     *   linodeid
 
-    *   planid
-
     Optional Parameters:
 
-    *   paymentterm
+    *   configid
 
-   linode_list
-    Optional Parameters:
+   linode_shutdown
+    Required Parameters:
 
     *   linodeid
 
-   linode_reboot
+   linode_delete
     Required Parameters:
 
     *   linodeid
 
     Optional Parameters:
 
-    *   configid
+    *   skipchecks
 
    linode_config_delete
     Required Parameters:
@@ -384,6 +404,21 @@ Methods from the Linode API
 
     *   configid
 
+   linode_disk_createfromimage
+    Required Parameters:
+
+    *   imageid
+
+    *   linodeid
+
+    Optional Parameters:
+
+    *   rootpass
+
+    *   rootsshkey
+
+    *   size
+
    linode_disk_duplicate
     Required Parameters:
 
@@ -391,7 +426,20 @@ Methods from the Linode API
 
     *   linodeid
 
-   linode_disk_createfromdistribution
+   linode_disk_update
+    Required Parameters:
+
+    *   diskid
+
+    Optional Parameters:
+
+    *   isreadonly
+
+    *   label
+
+    *   linodeid
+
+   linode_disk_createfromstackscript
     Required Parameters:
 
     *   distributionid
@@ -404,48 +452,53 @@ Methods from the Linode API
 
     *   size
 
+    *   stackscriptid
+
+    *   stackscriptudfresponses
+
     Optional Parameters:
 
     *   rootsshkey
 
-   linode_disk_list
+   linode_disk_imagize
     Required Parameters:
 
+    *   diskid
+
     *   linodeid
 
     Optional Parameters:
 
-    *   diskid
+    *   description
 
-   linode_disk_create
+    *   label
+
+   linode_disk_delete
     Required Parameters:
 
-    *   label
+    *   diskid
 
     *   linodeid
 
-    *   size
-
-    *   type
+   linode_disk_resize
+    Required Parameters:
 
-    Optional Parameters:
+    *   diskid
 
-    *   fromdistributionid
+    *   linodeid
 
-    *   isreadonly
+    *   size
 
-    *   rootpass
+   linode_disk_list
+    Required Parameters:
 
-    *   rootsshkey
+    *   linodeid
 
-   linode_disk_delete
-    Required Parameters:
+    Optional Parameters:
 
     *   diskid
 
-    *   linodeid
-
-   linode_disk_createfromstackscript
+   linode_disk_createfromdistribution
     Required Parameters:
 
     *   distributionid
@@ -458,40 +511,30 @@ Methods from the Linode API
 
     *   size
 
-    *   stackscriptid
-
-    *   stackscriptudfresponses
-
     Optional Parameters:
 
     *   rootsshkey
 
-   linode_disk_resize
+   linode_disk_create
     Required Parameters:
 
-    *   diskid
+    *   label
 
     *   linodeid
 
     *   size
 
-   linode_disk_update
-    Required Parameters:
-
-    *   diskid
+    *   type
 
     Optional Parameters:
 
-    *   isreadonly
-
-    *   label
+    *   fromdistributionid
 
-    *   linodeid
+    *   isreadonly
 
-   linode_ip_addpublic
-    Required Parameters:
+    *   rootpass
 
-    *   linodeid
+    *   rootsshkey
 
    linode_ip_setrdns
     Required Parameters:
@@ -500,6 +543,17 @@ Methods from the Linode API
 
     *   ipaddressid
 
+   linode_ip_swap
+    Required Parameters:
+
+    *   ipaddressid
+
+    Optional Parameters:
+
+    *   tolinodeid
+
+    *   withipaddressid
+
    linode_ip_addprivate
     Required Parameters:
 
@@ -514,16 +568,10 @@ Methods from the Linode API
 
     *   ipaddressid
 
-   linode_ip_swap
+   linode_ip_addpublic
     Required Parameters:
 
-    *   ipaddressid
-
-    Optional Parameters:
-
-    *   tolinodeid
-
-    *   withipaddressid
+    *   linodeid
 
    linode_job_list
     Required Parameters:
@@ -536,6 +584,11 @@ Methods from the Linode API
 
     *   pendingonly
 
+   stackscript_list
+    Optional Parameters:
+
+    *   stackscriptid
+
    stackscript_update
     Required Parameters:
 
@@ -555,11 +608,6 @@ Methods from the Linode API
 
     *   script
 
-   stackscript_list
-    Optional Parameters:
-
-    *   stackscriptid
-
    stackscript_create
     Required Parameters:
 
@@ -620,15 +668,6 @@ Methods from the Linode API
 
     *   stickiness
 
-   nodebalancer_config_list
-    Required Parameters:
-
-    *   nodebalancerid
-
-    Optional Parameters:
-
-    *   configid
-
    nodebalancer_config_update
     Required Parameters:
 
@@ -660,10 +699,14 @@ Methods from the Linode API
 
     *   stickiness
 
-   nodebalancer_node_delete
+   nodebalancer_config_list
     Required Parameters:
 
-    *   nodeid
+    *   nodebalancerid
+
+    Optional Parameters:
+
+    *   configid
 
    nodebalancer_node_create
     Required Parameters:
@@ -680,6 +723,20 @@ Methods from the Linode API
 
     *   weight
 
+   nodebalancer_node_delete
+    Required Parameters:
+
+    *   nodeid
+
+   nodebalancer_node_list
+    Required Parameters:
+
+    *   configid
+
+    Optional Parameters:
+
+    *   nodeid
+
    nodebalancer_node_update
     Required Parameters:
 
@@ -695,15 +752,6 @@ Methods from the Linode API
 
     *   weight
 
-   nodebalancer_node_list
-    Required Parameters:
-
-    *   configid
-
-    Optional Parameters:
-
-    *   nodeid
-
    user_getapikey
     Required Parameters:
 
@@ -720,12 +768,12 @@ Methods from the Linode API
     *   token
 
 AUTHORS
-    *   Michael Greb, "<mgreb@linode.com>"
+    *   Michael Greb, "<michael@thegrebs.com>"
 
-    *   Stan "The Man" Schwertly "<stan@linode.com>"
+    *   Stan "The Man" Schwertly "<stan@schwertly.com>"
 
 COPYRIGHT & LICENSE
-    Copyright 2008-2009 Linode, LLC, all rights reserved.
+    Copyright 2008-2014 Michael Greb, all rights reserved.
 
     This program is free software; you can redistribute it and/or modify it
     under the same terms as Perl itself.
@@ -6,3 +6,8 @@ requires 'LWP'; # for cpants :<
 on test => sub {
     requires 'Test::More', '0.88';
 };
+
+on 'develop' => sub {
+  requires 'Test::Pod';
+  requires 'Test::Pod::Coverage';
+};
@@ -15,7 +15,7 @@ WebService::Linode::Base - Perl Interface to the Linode.com API.
 
 =cut
 
-our $VERSION = '0.21';
+our $VERSION = '0.24';
 our $err;
 our $errstr;
 
@@ -38,6 +38,9 @@ sub new {
     $self->{_ua}->agent("WebService::Linode::Base/$WebService::Linode::Base::VERSION ");
     $self->{_ua}->agent($args{useragent}) if $args{useragent};
 
+    # set up storage for queued requests
+    $self->{_batch_queue} = [];
+
     bless $self, $package;
     return $self;
 }
@@ -68,27 +71,54 @@ sub send_request {
     return $self->{_ua}->post( $self->{_apiurl}, content => { %args } );
 }
 
+sub queue_request {
+    my ($self, %args) = @_;
+    my $queue = $self->{_batch_queue};
+
+    $self->_debug(10, "Queueing request for batch: " . join(' ' , %args));
+    push @$queue, \%args;
+
+    # return current number of items in the queue
+    return scalar @$queue;
+}
+
+sub list_queue {
+    my $self = shift;
+    my $queue = $self->{_batch_queue};
+    return @$queue;
+}
+
+sub clear_queue {
+    my $self = shift;
+    my $queue = $self->{_batch_queue};
+    @$queue = ();
+    return @$queue;
+}
+
+sub process_queue {
+    my ($self,$maxitems) = @_;
+    my $queue = $self->{_batch_queue};
+    # Default to processing the entire queue, cap at queue length
+    $maxitems = @$queue if not defined $maxitems or $maxitems > @$queue;
+
+    my @todo = splice @$queue, 0, $maxitems;
+    my $batch_json = to_json( \@todo );
+
+    return $self->do_request( api_action=>'batch', api_requestArray=>$batch_json );
+}
+
 sub parse_response {
     my $self = shift;
     my $response = shift;
 
     if ( $response->content =~ m|ERRORARRAY|i ) {
+        $self->_debug(10, "Received response: " . $response->content );
         my $json = from_json( $response->content );
-        if (scalar( @{ $json->{ERRORARRAY} } ) == 0
-            || ( scalar( @{ $json->{ERRORARRAY} } ) == 1
-                && $json->{ERRORARRAY}->[0]->{ERRORCODE} == 0 ) )
-        {   return $json->{DATA};
+        if ( ref $json eq 'ARRAY' ) {
+            return map { $self->_parse_api_response_data( $_ ) } @$json;
         }
         else {
-            # TODO this only returns the first error from the API
-
-            my $msg
-                = "API Error "
-                . $json->{ERRORARRAY}->[0]->{ERRORCODE} . ": "
-                . $json->{ERRORARRAY}->[0]->{ERRORMESSAGE};
-
-            $self->_error( $json->{ERRORARRAY}->[0]->{ERRORCODE}, $msg );
-            return;
+            return $self->_parse_api_response_data( $json );
         }
     }
     elsif ( $response->status_line ) {
@@ -101,6 +131,27 @@ sub parse_response {
     }
 }
 
+sub _parse_api_response_data {
+    my $self = shift;
+    my $rdata = shift;
+
+    my $errors = $rdata->{ERRORARRAY};
+    if ( not $errors or ref $errors ne 'ARRAY' ) {
+        $self->_error( -1, 'Invalid response: ERRORARRAY missing or invalid' );
+        return;
+    }
+
+    return $rdata->{DATA} if @$errors == 0;
+    return $rdata->{DATA} if @$errors == 1 and $errors->[0]{ERRORCODE} == 0;
+
+    # If we've reached here, there's an error to report
+    # TODO this only returns the first error from the API
+    my $error = $rdata->{ERRORARRAY}->[0];
+    my $msg = "API Error $error->{ERRORCODE}: $error->{ERRORMESSAGE}";
+    $self->_error( $error->{ERRORCODE}, $msg );
+    return;
+}
+
 sub _lc_keys {
     my ($self, $hashref) = @_;
 
@@ -179,6 +230,25 @@ response returning just the DATA section.
 Executes the send_request method, parses the response with the parse_response
 method and returns the data.
 
+=head2 queue_request
+
+Takes same arguments as send_request, but queues the request to be handled by
+a single batch request later.
+
+=head2 list_queue
+
+Returns list of queued requests.
+
+=head2 clear_queue
+
+Clears batch request queue.
+
+=head2 process_queue
+
+Sends queued items in a batch request.  Takes an optional number of items to
+send in the batch request, defaulting to all queued requests.  Returns an api
+reponse for each batch item.
+
 =head2 apikey
 
 Takes one optional argument, an apikey that if passed replaces the key
@@ -239,7 +309,7 @@ L<http://search.cpan.org/dist/WebService-Linode>
 
 =head1 COPYRIGHT & LICENSE
 
-Copyright 2008-2009 Linode, LLC, all rights reserved.
+Copyright 2008-2014 Michael Greb, all rights reserved.
 
 This program is free software; you can redistribute it and/or modify it
 under the same terms as Perl itself.
@@ -12,7 +12,7 @@ WebService::Linode::DNS - Deprecated Perl Interface to the Linode.com API DNS me
 
 =cut
 
-our $VERSION = '0.21';
+our $VERSION = '0.24';
 our @ISA = ("WebService::Linode::Base");
 
 sub getDomainIDbyName {
@@ -335,7 +335,7 @@ L<http://search.cpan.org/dist/WebService-Linode>
 
 =head1 COPYRIGHT & LICENSE
 
-Copyright 2008-2009 Linode, LLC, all rights reserved.
+Copyright 2008-2014 Michael Greb, all rights reserved.
 
 This program is free software; you can redistribute it and/or modify it
 under the same terms as Perl itself.
@@ -9,7 +9,7 @@ use Carp;
 use List::Util qw(first);
 use WebService::Linode::Base;
 
-our $VERSION = '0.21';
+our $VERSION = '0.24';
 our @ISA     = ("WebService::Linode::Base");
 our $AUTOLOAD;
 
@@ -43,6 +43,10 @@ my %validation = (
         list => [ [ 'domainid' ], [ 'resourceid' ] ],
         update => [ [ 'resourceid' ], [qw( domainid name port priority protocol target ttl_sec weight )] ],
     },
+    image => {
+        delete => [ [ 'imageid' ], [] ],
+        list => [ [], [ 'imageid', 'pending' ] ],
+    },
     linode => {
         boot => [ [ 'linodeid' ], [ 'configid' ] ],
         clone => [ [qw( datacenterid linodeid planid )], [ 'paymentterm' ] ],
@@ -65,9 +69,11 @@ my %validation = (
     linode_disk => {
         create => [ [qw( label linodeid size type )], [qw( fromdistributionid isreadonly rootpass rootsshkey )] ],
         createfromdistribution => [ [qw( distributionid label linodeid rootpass size )], [ 'rootsshkey' ] ],
+        createfromimage => [ [ 'imageid', 'linodeid' ], [qw( rootpass rootsshkey size )] ],
         createfromstackscript => [ [qw( distributionid label linodeid rootpass size stackscriptid stackscriptudfresponses )], [ 'rootsshkey' ] ],
         delete => [ [ 'diskid', 'linodeid' ], [] ],
         duplicate => [ [ 'diskid', 'linodeid' ], [] ],
+        imagize => [ [ 'diskid', 'linodeid' ], [ 'description', 'label' ] ],
         list => [ [ 'linodeid' ], [ 'diskid' ] ],
         resize => [ [qw( diskid linodeid size )], [] ],
         update => [ [ 'diskid' ], [qw( isreadonly label linodeid )] ],
@@ -117,8 +123,8 @@ my %validation = (
 sub AUTOLOAD {
     ( my $name = $AUTOLOAD ) =~ s/.+:://;
     return if $name eq 'DESTROY';
-    if ( $name =~ m/^(.*?)_([^_]+)$/ ) {
-        my ( $thing, $action ) = ( $1, $2 );
+    if ( $name =~ m/^(QUEUE_)?(.*?)_([^_]+)$/ ) {
+        my ( $queue, $thing, $action ) = ( $1, $2, $3 );
         if ( exists $validation{$thing} && exists $validation{$thing}{$action} )
         {   no strict 'refs';
             *{$AUTOLOAD} = sub {
@@ -139,6 +145,7 @@ sub AUTOLOAD {
                     }
                 }
                 ( my $apiAction = "${thing}_${action}" ) =~ s/_/./g;
+                return $self->queue_request( api_action => $apiAction, %args ) if $queue;
                 my $data = $self->do_request( api_action => $apiAction, %args );
                 return [ map { $self->_lc_keys($_) } @$data ]
                     if ref $data eq 'ARRAY';
@@ -156,6 +163,29 @@ sub AUTOLOAD {
     croak "Undefined subroutine \&$AUTOLOAD called";
 }
 
+sub send_queued_requests {
+    my $self = shift;
+    my $items = shift;
+
+    if ( $self->list_queue == 0 ) {
+        $self->_error( -1, "No queued items to send" );
+        return;
+    }
+
+    my @responses;
+    for my $data ( $self->process_queue( $items ) ) {
+        if ( ref $data eq 'ARRAY' ) {
+            push @responses, [ map { $self->_lc_keys($_) } @$data ];
+        } elsif( ref $data eq 'HASH' ) {
+            push @responses, $self->_lc_keys($data);
+        } else {
+            push @responses, $data;
+        }
+    }
+
+    return @responses;
+}
+
 'mmm, cake';
 __END__
 
@@ -179,10 +209,44 @@ same.  For additional information see L<http://www.linode.com/api/>
 For documentation of possible arguments to the constructor, see
 L<WebService::Linode::Base>.
 
+=head1 Batch requests
+
+Each of the Linode API methods below may optionally be prefixed with QUEUE_
+to add that request to a queue to be processed later in one or more batch
+requests which can be processed by calling send_queued_requests.
+For example:
+
+    my @linode_ids = () # Get your linode ids through normal methods
+    my @responses = map { $api->linode_ip_list( linodeid=>$_ ) } @linode_ids;
+
+Can be reduced to a single request:
+
+    my @linode_ids = () # Get your linode ids through normal methods
+    $api->QUEUE_linode_ip_list( linodeid=>$_ ) for @linode_ids;
+    my @responses = $api->send_queued_requests; # One api request
+
+See L<WebService::Linode::Base> for additional queue management methods.
+
+=head3 send_queued_requests
+
+Send queued batch requests, returns list of responses.
+
 =head1 Methods from the Linode API
 
 =head3 avail_datacenters
 
+=head3 avail_nodebalancers
+
+=head3 avail_distributions
+
+Optional Parameters:
+
+=over 4
+
+=item * distributionid
+
+=back
+
 =head3 avail_kernels
 
 Optional Parameters:
@@ -205,8 +269,6 @@ Optional Parameters:
 
 =back
 
-=head3 avail_nodebalancers
-
 =head3 avail_stackscripts
 
 Optional Parameters:
@@ -221,35 +283,23 @@ Optional Parameters:
 
 =back
 
-=head3 avail_distributions
+=head3 domain_list
 
 Optional Parameters:
 
 =over 4
 
-=item * distributionid
-
-=back
-
-=head3 domain_delete
-
-Required Parameters:
-
-=over 4
-
 =item * domainid
 
 =back
 
-=head3 domain_create
+=head3 domain_update
 
 Required Parameters:
 
 =over 4
 
-=item * domain
-
-=item * type
+=item * domainid
 
 =back
 
@@ -261,6 +311,8 @@ Optional Parameters:
 
 =item * description
 
+=item * domain
+
 =item * expire_sec
 
 =item * lpm_displaygroup
@@ -277,15 +329,19 @@ Optional Parameters:
 
 =item * ttl_sec
 
+=item * type
+
 =back
 
-=head3 domain_update
+=head3 domain_create
 
 Required Parameters:
 
 =over 4
 
-=item * domainid
+=item * domain
+
+=item * type
 
 =back
 
@@ -297,8 +353,6 @@ Optional Parameters:
 
 =item * description
 
-=item * domain
-
 =item * expire_sec
 
 =item * lpm_displaygroup
@@ -315,13 +369,11 @@ Optional Parameters:
 
 =item * ttl_sec
 
-=item * type
-
 =back
 
-=head3 domain_list
+=head3 domain_delete
 
-Optional Parameters:
+Required Parameters:
 
 =over 4
 
@@ -329,34 +381,22 @@ Optional Parameters:
 
 =back
 
-=head3 domain_resource_delete
+=head3 domain_resource_update
 
 Required Parameters:
 
 =over 4
 
-=item * domainid
-
 =item * resourceid
 
 =back
 
-=head3 domain_resource_create
-
-Required Parameters:
+Optional Parameters:
 
 =over 4
 
 =item * domainid
 
-=item * type
-
-=back
-
-Optional Parameters:
-
-=over 4
-
 =item * name
 
 =item * port
@@ -391,22 +431,34 @@ Optional Parameters:
 
 =back
 
-=head3 domain_resource_update
+=head3 domain_resource_delete
 
 Required Parameters:
 
 =over 4
 
+=item * domainid
+
 =item * resourceid
 
 =back
 
-Optional Parameters:
+=head3 domain_resource_create
+
+Required Parameters:
 
 =over 4
 
 =item * domainid
 
+=item * type
+
+=back
+
+Optional Parameters:
+
+=over 4
+
 =item * name
 
 =item * port
@@ -423,7 +475,7 @@ Optional Parameters:
 
 =back
 
-=head3 linode_webconsoletoken
+=head3 linode_resize
 
 Required Parameters:
 
@@ -431,11 +483,13 @@ Required Parameters:
 
 =item * linodeid
 
+=item * planid
+
 =back
 
-=head3 linode_delete
+=head3 linode_list
 
-Required Parameters:
+Optional Parameters:
 
 =over 4
 
@@ -443,11 +497,13 @@ Required Parameters:
 
 =back
 
-Optional Parameters:
+=head3 linode_mutate
+
+Required Parameters:
 
 =over 4
 
-=item * skipchecks
+=item * linodeid
 
 =back
 
@@ -489,14 +545,26 @@ Optional Parameters:
 
 =back
 
-=head3 linode_shutdown
+=head3 linode_clone
 
 Required Parameters:
 
 =over 4
 
+=item * datacenterid
+
 =item * linodeid
 
+=item * planid
+
+=back
+
+Optional Parameters:
+
+=over 4
+
+=item * paymentterm
+
 =back
 
 =head3 linode_update
@@ -553,19 +621,7 @@ Optional Parameters:
 
 =back
 
-=head3 linode_resize
-
-Required Parameters:
-
-=over 4
-
-=item * linodeid
-
-=item * planid
-
-=back
-
-=head3 linode_mutate
+=head3 linode_webconsoletoken
 
 Required Parameters:
 
@@ -575,31 +631,27 @@ Required Parameters:
 
 =back
 
-=head3 linode_clone
+=head3 linode_reboot
 
 Required Parameters:
 
 =over 4
 
-=item * datacenterid
-
 =item * linodeid
 
-=item * planid
-
 =back
 
 Optional Parameters:
 
 =over 4
 
-=item * paymentterm
+=item * configid
 
 =back
 
-=head3 linode_list
+=head3 linode_shutdown
 
-Optional Parameters:
+Required Parameters:
 
 =over 4
 
@@ -607,7 +659,7 @@ Optional Parameters:
 
 =back
 
-=head3 linode_reboot
+=head3 linode_delete
 
 Required Parameters:
 
@@ -621,7 +673,7 @@ Optional Parameters:
 
 =over 4
 
-=item * configid
+=item * skipchecks
 
 =back
 
@@ -741,6 +793,30 @@ Optional Parameters:
 
 =back
 
+=head3 linode_disk_createfromimage
+
+Required Parameters:
+
+=over 4
+
+=item * imageid
+
+=item * linodeid
+
+=back
+
+Optional Parameters:
+
+=over 4
+
+=item * rootpass
+
+=item * rootsshkey
+
+=item * size
+
+=back
+
 =head3 linode_disk_duplicate
 
 Required Parameters:
@@ -753,7 +829,29 @@ Required Parameters:
 
 =back
 
-=head3 linode_disk_createfromdistribution
+=head3 linode_disk_update
+
+Required Parameters:
+
+=over 4
+
+=item * diskid
+
+=back
+
+Optional Parameters:
+
+=over 4
+
+=item * isreadonly
+
+=item * label
+
+=item * linodeid
+
+=back
+
+=head3 linode_disk_createfromstackscript
 
 Required Parameters:
 
@@ -769,6 +867,10 @@ Required Parameters:
 
 =item * size
 
+=item * stackscriptid
+
+=item * stackscriptudfresponses
+
 =back
 
 Optional Parameters:
@@ -779,12 +881,14 @@ Optional Parameters:
 
 =back
 
-=head3 linode_disk_list
+=head3 linode_disk_imagize
 
 Required Parameters:
 
 =over 4
 
+=item * diskid
+
 =item * linodeid
 
 =back
@@ -793,53 +897,57 @@ Optional Parameters:
 
 =over 4
 
-=item * diskid
+=item * description
+
+=item * label
 
 =back
 
-=head3 linode_disk_create
+=head3 linode_disk_delete
 
 Required Parameters:
 
 =over 4
 
-=item * label
+=item * diskid
 
 =item * linodeid
 
-=item * size
-
-=item * type
-
 =back
 
-Optional Parameters:
+=head3 linode_disk_resize
 
-=over 4
+Required Parameters:
 
-=item * fromdistributionid
+=over 4
 
-=item * isreadonly
+=item * diskid
 
-=item * rootpass
+=item * linodeid
 
-=item * rootsshkey
+=item * size
 
 =back
 
-=head3 linode_disk_delete
+=head3 linode_disk_list
 
 Required Parameters:
 
 =over 4
 
-=item * diskid
-
 =item * linodeid
 
 =back
 
-=head3 linode_disk_createfromstackscript
+Optional Parameters:
+
+=over 4
+
+=item * diskid
+
+=back
+
+=head3 linode_disk_createfromdistribution
 
 Required Parameters:
 
@@ -855,10 +963,6 @@ Required Parameters:
 
 =item * size
 
-=item * stackscriptid
-
-=item * stackscriptudfresponses
-
 =back
 
 Optional Parameters:
@@ -869,27 +973,19 @@ Optional Parameters:
 
 =back
 
-=head3 linode_disk_resize
+=head3 linode_disk_create
 
 Required Parameters:
 
 =over 4
 
-=item * diskid
+=item * label
 
 =item * linodeid
 
 =item * size
 
-=back
-
-=head3 linode_disk_update
-
-Required Parameters:
-
-=over 4
-
-=item * diskid
+=item * type
 
 =back
 
@@ -897,36 +993,48 @@ Optional Parameters:
 
 =over 4
 
+=item * fromdistributionid
+
 =item * isreadonly
 
-=item * label
+=item * rootpass
 
-=item * linodeid
+=item * rootsshkey
 
 =back
 
-=head3 linode_ip_addpublic
+=head3 linode_ip_setrdns
 
 Required Parameters:
 
 =over 4
 
-=item * linodeid
+=item * hostname
+
+=item * ipaddressid
 
 =back
 
-=head3 linode_ip_setrdns
+=head3 linode_ip_swap
 
 Required Parameters:
 
 =over 4
 
-=item * hostname
-
 =item * ipaddressid
 
 =back
 
+Optional Parameters:
+
+=over 4
+
+=item * tolinodeid
+
+=item * withipaddressid
+
+=back
+
 =head3 linode_ip_addprivate
 
 Required Parameters:
@@ -955,23 +1063,13 @@ Optional Parameters:
 
 =back
 
-=head3 linode_ip_swap
+=head3 linode_ip_addpublic
 
 Required Parameters:
 
 =over 4
 
-=item * ipaddressid
-
-=back
-
-Optional Parameters:
-
-=over 4
-
-=item * tolinodeid
-
-=item * withipaddressid
+=item * linodeid
 
 =back
 
@@ -995,6 +1093,16 @@ Optional Parameters:
 
 =back
 
+=head3 stackscript_list
+
+Optional Parameters:
+
+=over 4
+
+=item * stackscriptid
+
+=back
+
 =head3 stackscript_update
 
 Required Parameters:
@@ -1023,16 +1131,6 @@ Optional Parameters:
 
 =back
 
-=head3 stackscript_list
-
-Optional Parameters:
-
-=over 4
-
-=item * stackscriptid
-
-=back
-
 =head3 stackscript_create
 
 Required Parameters:
@@ -1121,24 +1219,6 @@ Optional Parameters:
 
 =back
 
-=head3 nodebalancer_config_list
-
-Required Parameters:
-
-=over 4
-
-=item * nodebalancerid
-
-=back
-
-Optional Parameters:
-
-=over 4
-
-=item * configid
-
-=back
-
 =head3 nodebalancer_config_update
 
 Required Parameters:
@@ -1179,13 +1259,21 @@ Optional Parameters:
 
 =back
 
-=head3 nodebalancer_node_delete
+=head3 nodebalancer_config_list
 
 Required Parameters:
 
 =over 4
 
-=item * nodeid
+=item * nodebalancerid
+
+=back
+
+Optional Parameters:
+
+=over 4
+
+=item * configid
 
 =back
 
@@ -1213,7 +1301,7 @@ Optional Parameters:
 
 =back
 
-=head3 nodebalancer_node_update
+=head3 nodebalancer_node_delete
 
 Required Parameters:
 
@@ -1223,27 +1311,31 @@ Required Parameters:
 
 =back
 
-Optional Parameters:
+=head3 nodebalancer_node_list
+
+Required Parameters:
 
 =over 4
 
-=item * address
+=item * configid
 
-=item * label
+=back
 
-=item * mode
+Optional Parameters:
 
-=item * weight
+=over 4
+
+=item * nodeid
 
 =back
 
-=head3 nodebalancer_node_list
+=head3 nodebalancer_node_update
 
 Required Parameters:
 
 =over 4
 
-=item * configid
+=item * nodeid
 
 =back
 
@@ -1251,7 +1343,13 @@ Optional Parameters:
 
 =over 4
 
-=item * nodeid
+=item * address
+
+=item * label
+
+=item * mode
+
+=item * weight
 
 =back
 
@@ -1283,15 +1381,15 @@ Optional Parameters:
 
 =over
 
-=item * Michael Greb, C<< <mgreb@linode.com> >>
+=item * Michael Greb, C<< <michael@thegrebs.com> >>
 
-=item * Stan "The Man" Schwertly C<< <stan@linode.com> >>
+=item * Stan "The Man" Schwertly C<< <stan@schwertly.com> >>
 
 =back
 
 =head1 COPYRIGHT & LICENSE
 
-Copyright 2008-2009 Linode, LLC, all rights reserved.
+Copyright 2008-2014 Michael Greb, all rights reserved.
 
 This program is free software; you can redistribute it and/or modify it
 under the same terms as Perl itself.
@@ -6,7 +6,12 @@ BEGIN {
   }
 }
 
-# this test was generated with Dist::Zilla::Plugin::Test::Kwalitee 2.07
+# this test was generated with Dist::Zilla::Plugin::Test::Kwalitee 2.11
 use strict;
 use warnings;
-use Test::Kwalitee;
+use Test::More 0.88;
+use Test::Kwalitee 1.21 'kwalitee_ok';
+
+kwalitee_ok();
+
+done_testing;