You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by sn...@apache.org on 2016/10/24 13:06:11 UTC

[26/83] [abbrv] usergrid git commit: Moving older SDKs to a difference location and updating main README to link to new SDK locations.

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/Makefile.PL
----------------------------------------------------------------------
diff --git a/sdks/other/perl/Makefile.PL b/sdks/other/perl/Makefile.PL
new file mode 100644
index 0000000..6ab9874
--- /dev/null
+++ b/sdks/other/perl/Makefile.PL
@@ -0,0 +1,41 @@
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+
+use 5.008;
+
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+  LICENSE           => 'apache',
+  AUTHOR            => 'Anuradha Weeraman <anuradha@cpan.org',
+  ABSTRACT          => 'Client API for Apache Usergrid',
+  NAME              => 'Usergrid::Client',
+  VERSION_FROM      => 'lib/Usergrid/Client.pm',
+  PREREQ_PM => {
+    'Moose'                => 0,
+    'JSON'                 => 0,
+    'REST::Client'         => 0,
+    'URI::Template'        => 0,
+    'Log::Log4perl'        => 0,
+    'namespace::autoclean' => 0
+  },
+  BUILD_REQUIRES    => {
+    'Test::More'    => '0.98'
+  }
+);

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/README.md
----------------------------------------------------------------------
diff --git a/sdks/other/perl/README.md b/sdks/other/perl/README.md
new file mode 100644
index 0000000..61069cc
--- /dev/null
+++ b/sdks/other/perl/README.md
@@ -0,0 +1,341 @@
+# Usergrid Perl Client ![CI status](https://travis-ci.org/aweeraman/usergrid-perl-sdk.svg?branch=master "CI Status")
+
+Usergrid::Client provides an easy to use Perl API for Apache Usergrid.
+
+## Quickstart
+
+Install Usergrid::Client from CPAN:
+
+    $ sudo cpan Usergrid::Client
+
+Write a perl script that uses the Perl API to talk to Usergrid. Here's an example:
+
+```perl
+#!/usr/bin/perl
+use Usergrid::Client;
+
+# Create a client object for Usergrid that's used for subsequent activity
+my $client = Usergrid::Client->new(
+  organization => 'test-organization',
+  application  => 'test-app',
+  api_url      => 'http://localhost:8080',
+  trace        => 0
+);
+
+# Logs the user in. The security token is maintained by the library in memory
+$client->login('johndoe', 'Johndoe123$');
+
+# Add two entities to the "books" collection
+$client->add_entity("books", { name => "Ulysses", author => "James Joyce" });
+$client->add_entity("books", { name => "Neuromancer", author => "William Gibson" });
+
+# Retrieve a handle to the collection
+my $books = $client->get_collection("books");
+
+# Add a new attribute for quantity in stock
+while ($books->has_next_entity()) {
+  my $book = $books->get_next_entity();
+
+  print "Name: "   . $book->get('name')   . ", ";
+  print "Author: " . $book->get('author') . "\n";
+
+  # Create a new attribute and update the entity
+  $book->set("in-stock", 0);
+  $client->update_entity($book);
+}
+```
+
+## What is Apache Usergrid
+
+Usergrid is an open-source Backend-as-a-Service (\u201cBaaS\u201d or \u201cmBaaS\u201d) composed of
+an integrated distributed NoSQL database, application layer and client tier with
+SDKs for developers looking to rapidly build web and/or mobile applications.
+It provides elementary services (user registration & management, data storage,
+file storage, queues) and retrieval features (full text search, geolocation
+search, joins) to power common app features.
+
+It is a multi-tenant system designed for deployment to public cloud environments
+(such as Amazon Web Services, Rackspace, etc.) or to run on traditional server
+infrastructures so that anyone can run their own private BaaS deployment.
+
+For architects and back-end teams, it aims to provide a distributed, easily
+extendable, operationally predictable and highly scalable solution. For
+front-end developers, it aims to simplify the development process by enabling
+them to rapidly build and operate mobile and web applications without requiring
+backend expertise.
+
+Source: [Usergrid Documentation](https://usergrid.apache.org/docs/)
+
+For more information, visit [http://www.usergrid.org](http://www.usergrid.org)
+
+## Installation
+
+### Prerequisites
+Usergrid::Client depends on the following modules which can be installed
+from CPAN as shown below:
+
+    $ sudo cpan install Moose
+    $ sudo cpan install JSON
+    $ sudo cpan install REST::Client
+    $ sudo cpan install URI::Template
+    $ sudo cpan install Log::Log4perl
+    $ sudo cpan install namespace::autoclean
+
+### Build and install
+
+    $ perl Build.PL
+    $ ./Build
+    $ ./Build test
+    $ sudo ./Build install
+
+### For legacy users on older versions of Perl
+
+    $ perl Makefile.PL
+    $ make
+    $ make test
+    $ sudo make install
+
+## Usage
+
+### Getting started
+
+In order to login to Usergrid using the API, create a Usergrid::Client object
+as shown below and invoke the login function.
+
+```perl
+# Create the client object that will be used for all subsequent requests
+my $client = Usergrid::Client->new(
+  organization => $organization,
+  application  => $application,
+  api_url      => $api_url
+);
+
+$client->login($username, $password);
+```
+
+For troubleshooting the requests and responses, tracing can be enabled, which
+will log all requests and responses to standard output.
+
+```perl
+# Create the client object that will be used for all subsequent requests
+my $client = Usergrid::Client->new(
+  organization => $organization,
+  application  => $application,
+  api_url      => $api_url,
+  trace        => 1
+);
+```
+
+To get more details on the API, read the following perldocs:
+
+    Usergrid::Client
+    Usergrid::Collection
+    Usergrid::Entity
+
+### Entities
+
+Creating and updating an entity is easy. Here's how:
+
+```perl
+$book = $client->add_entity("books", { name => "Neuromancer", author => "William Gibson" });
+$book->set('genre', 'Cyberpunk');
+
+$client->update_entity($book);
+```
+
+Querying an entity can be done by UUID:
+
+```perl
+$book = $client->get_entity("books", $uuid);
+```
+
+or by name:
+
+```perl
+$book = $client->get_entity("books", "Neuromancer");
+```
+
+Similarly, an entity can be deleted by UUID or by name:
+
+```perl
+$client->delete_entity_by_id("books", $uuid_or_name);
+
+# An entity can be also deleted by passing an entity object
+$client->delete_entity($entity);
+```
+
+### Collections
+
+A collection can be retrieved as shown below:
+
+```perl
+$collection = $client->get_collection("books");
+
+# Outputs the number of records in the collection
+print "$collection->count()\n";
+```
+
+To iterate over the collection:
+
+```perl
+while ($collection->has_next_entity()) {
+  $book = $collection->get_next_entity();
+  print "$book->get('name')\n";
+}
+```
+
+Note that by default a collection returns a maximum of 10 records per page. This
+can be overridden when retrieving the collection as shown below:
+
+```perl
+$collection = $client->get_collection("books", 30);
+
+# Retrieve the first book in the collection's current page
+$first_book = $collection->get_first_entity();
+
+# Retrieve the last book in the collection's current page
+$last_book  = $collection->get_last_entity();
+```
+
+To navigate the pages in the collection:
+
+```perl
+$collection->get_next_page();
+
+$collection->get_prev_page();
+```
+
+Both of the above return FALSE if the end or the beginning of the collection
+is reached.
+
+When iterating through a collection, the auto_page attribute can be set
+to allow the collection to transparently fetch the next page when iterating.
+
+```perl
+$collection = $client->get_collection("books");
+
+$collection->auto_page(1);
+
+while ($collection->has_next_entity()) {
+  my $entity = $collection->get_next_entity();
+  # do something
+}
+```
+
+### Querying & Batch Updates
+
+Collections can be queried using a SQL-like query language for greater control
+over the data set that is returned.
+
+```perl
+$collection = $client->query_collection("books", "select * where genre = 'Cyberpunk'", $limit );
+```
+
+Queries can also be used when deleting collections:
+
+```perl
+$collection = $client->delete_collection("books", "select * where genre = 'Cyberpunk'", $limit);
+```
+
+If the $limit is omitted in the above method calls, a default of 10 is assumed.
+
+A collection can be batch updated as shown below:
+
+```perl
+$client->update_collection("books", { in_stock => 1 });
+```
+
+A query can be used to fine-tune the update:
+
+```perl
+$client->update_collection("books", { in_stock => 0 }, "select * where genre = 'Cyberpunk'", $limit);
+```
+
+Similarly, entities can be deleted in batch:
+
+```perl
+$client->delete_collection("books", "select * where genre = 'Novel'", $limit);
+```
+
+### Entity Connections
+
+Connections can be created between entities through relationships as shown below:
+
+```perl
+$book1 = $client->add_entity("books", { name => "Neuromancer", author => "William Gibson" });
+$book2 = $client->add_entity("books", { name => "Count Zero", author => "William Gibson" });
+$book3 = $client->add_entity("books", { name => "Mona Lisa Overdrive", author => "William Gibson" });
+
+$client->connect_entities($book1, "similar_to", $book2);
+$client->connect_entities($book1, "similar_to", $book3);
+```
+
+They can also be queried just like any other collection:
+
+```perl
+$collection = $client->query_connections($book1, "similar_to");
+
+# Queries and limits can also be passed in
+$collection = $client->query_connections($book1, "similar_to", $query, $limit);
+```
+
+To delete a connection:
+
+```perl
+$client->disconnect_entities($book1, "similar_to", $book2);
+```
+
+### Code Coverage
+
+Code coverage reporting requires Devel::Cover module which can be
+installed from CPAN as shown:
+
+    $ sudo cpan install Devel::Cover
+
+For generating reports on code coverage:
+
+    $ ./Build testcover
+
+The generated report artifacts are located in cover_db/.
+
+## Release notes
+
+### 0.22
+
+* Auto paging for collections
+
+### 0.21
+
+* Documentation updates
+
+### 0.2
+
+* Creating, querying and deleting entity connections
+* Bi-directional collection pagination
+
+### 0.11
+
+* Added namespace::autoclean.pm as a dependency to fix build break on some
+  platforms
+
+### 0.1
+
+* Initial release
+* Application and admin authentication
+* Support for collections and entities (CRUD & queries)
+
+## License
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+the ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/examples/books.pl
----------------------------------------------------------------------
diff --git a/sdks/other/perl/examples/books.pl b/sdks/other/perl/examples/books.pl
new file mode 100644
index 0000000..842d378
--- /dev/null
+++ b/sdks/other/perl/examples/books.pl
@@ -0,0 +1,48 @@
+#!/usr/bin/perl
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use Usergrid::Client;
+
+# Create a client object for Usergrid that's used for subsequent activity
+my $client = Usergrid::Client->new(
+  organization => 'test-organization',
+  application  => 'test-app',
+  api_url      => 'http://localhost:8080',
+  trace        => 0
+);
+
+# Logs the user in. The security token is maintained by the library in memory
+$client->login('johndoe', 'Johndoe123$');
+
+# Add two entities to the "books" collection
+$client->add_entity("books", { name => "Ulysses", author => "James Joyce" });
+$client->add_entity("books", { name => "Neuromancer", author => "William Gibson" });
+
+# Retrieve a handle to the collection
+my $books = $client->get_collection("books");
+
+# Add a new attribute for quantity in stock
+while ($books->has_next_entity()) {
+  my $book = $books->get_next_entity();
+
+  print "Name: "   . $book->get('name')   . ", ";
+  print "Author: " . $book->get('author') . "\n";
+
+  # Create a new attribute and update the entity
+  $book->set("in-stock", 0);
+  $client->update_entity($book);
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/lib/Usergrid/Client.pm
----------------------------------------------------------------------
diff --git a/sdks/other/perl/lib/Usergrid/Client.pm b/sdks/other/perl/lib/Usergrid/Client.pm
new file mode 100644
index 0000000..ccb276b
--- /dev/null
+++ b/sdks/other/perl/lib/Usergrid/Client.pm
@@ -0,0 +1,492 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+package Usergrid::Client;
+
+use Moose;
+use namespace::autoclean;
+use Usergrid::Entity;
+use Usergrid::Collection;
+
+with (
+  'Usergrid::Request',
+);
+
+our $VERSION = '0.22';
+
+=head1 NAME
+
+Usergrid::Client - Usergrid Perl Client
+
+=head1 SYNOPSIS
+
+  use Usergrid::Client;
+
+  my $client = Usergrid::Client->new(
+    organization => 'test-organization',
+    application  => 'test-app',
+    api_url      => 'http://localhost:8080'
+  );
+
+  $client->login('johndoe', 'Johndoe123$');
+
+  $client->add_entity("books", { name => "Ulysses" });
+  $client->add_entity("books", { name => "Neuromancer" });
+
+  my $books = $client->get_collection("books");
+
+  while ($books->has_next_entity()) {
+    my $book = $books->get_next_entity();
+
+    print "Name: "   . $book->get('name')   . ", ";
+    print "Author: " . $book->get('author') . "\n";
+
+    $book->set("in-stock", 0);
+    $client->update_entity($book);
+  }
+
+=head1 DESCRIPTION
+
+Usergrid::Client provides an easy to use Perl API for Apache Usergrid.
+
+=head1 WHAT IS APACHE USERGRID
+
+Usergrid is an open-source Backend-as-a-Service ("BaaS" or "mBaaS") composed of
+an integrated distributed NoSQL database, application layer and client tier with
+SDKs for developers looking to rapidly build web and/or mobile applications.
+It provides elementary services (user registration & management, data storage,
+file storage, queues) and retrieval features (full text search, geolocation
+search, joins) to power common app features.
+
+It is a multi-tenant system designed for deployment to public cloud environments
+(such as Amazon Web Services, Rackspace, etc.) or to run on traditional server
+infrastructures so that anyone can run their own private BaaS deployment.
+
+For architects and back-end teams, it aims to provide a distributed, easily
+extendable, operationally predictable and highly scalable solution. For
+front-end developers, it aims to simplify the development process by enabling
+them to rapidly build and operate mobile and web applications without requiring
+backend expertise.
+
+Source: L<https://usergrid.apache.org/docs/>
+
+For more information, visit L<http://www.usergrid.org>
+
+=head1 ATTRIBUTES
+
+The following attributes are made available via the L<Usergrid::Request> role:
+
+=over 4
+
+=item organization (String)
+
+Organization name
+
+=item application (String)
+
+Application name
+
+=item api_url (String)
+
+URL of the Usergrid instance
+
+=item trace (Boolean)
+
+Enable/disable request and response tracing for debugging and troubleshooting
+(Optional)
+
+=back
+
+=head1 METHODS
+
+The following methods are provided in this API for interacting with the Apache
+Usergrid backend.
+
+=head2 Authentication
+
+=over 4
+
+=item login ( $username, $password )
+
+Performs application user authentication. Returns the user token for the
+logged in user. The token is also kept in memory and used for subsequent
+authentication of requests.
+
+=cut
+sub login {
+  my ($self, $username, $password) = @_;
+
+  my %request = (
+    grant_type => "password",
+    username   => $username,
+    password   => $password
+  );
+
+  my $uri = URI::Template
+    ->new('/{organization}/{application}/token')
+    ->process(
+      organization => $self->organization,
+      application  => $self->application
+  );
+
+  my $token = $self->POST($uri, \%request);
+
+  $self->user_token($token);
+
+  return $self->user_token;
+}
+
+=item admin_login ( $username, $password )
+
+Performs admin user authentication. Returns the user token for the
+logged in user. The token is also kept in memory and used for subsequent
+authentication of requests.
+
+=cut
+sub management_login {
+  my ($self, $username, $password) = @_;
+
+  my %request = (
+    grant_type => "password",
+    username   => $username,
+    password   => $password
+  );
+
+  my $token = $self->POST('/management/token', \%request);
+
+  $self->user_token($token);
+
+  return $self->user_token;
+}
+
+=back
+
+=head2 Entities
+
+This section covers some of the entity level methods available in the API.
+Entities form one of the basic building blocks of Usergrid and is analogous to
+a row in an RDBMS table.
+
+=over 4
+
+=item add_entity ( $collection, \%entity )
+
+Creates a new entity within the specified collection. Returns a L<Usergrid::Entity>
+for the newly added entity.
+
+=cut
+sub add_entity {
+  my ($self, $collection, $entity) = @_;
+
+  my $uri = URI::Template
+    ->new('/{organization}/{application}/{collection}')
+    ->process(
+      organization => $self->organization,
+      application  => $self->application,
+      collection   => $collection
+  );
+
+  return Usergrid::Entity->new( object => $self->POST($uri, $entity));
+}
+
+=item update_entity ( L<Usergrid::Entity> )
+
+Saves changes to the given entity. Returns the updated L<Usergrid::Entity>.
+
+=cut
+sub update_entity {
+  my ($self, $entity) = @_;
+
+  my $uri = URI::Template
+    ->new('/{organization}/{application}/{collection}/{uuid}')
+    ->process(
+      organization => $self->organization,
+      application  => $self->application,
+      collection   => $entity->get('type'),
+      uuid         => $entity->get('uuid')
+  );
+
+  return Usergrid::Entity->new( object => $self->PUT($uri,
+    $entity->object->{'entities'}[0]) );
+}
+
+=item get_entity ( $collection, $id )
+
+Returns a L<Usergrid::Entity> identified by either UUID or name.
+If the entity does not exist, the method returns FALSE.
+
+=cut
+sub get_entity {
+  my ($self, $collection, $id_or_name) = @_;
+
+  my $uri = URI::Template
+    ->new('/{organization}/{application}/{collection}/{id_or_name}')
+    ->process(
+      organization => $self->organization,
+      application  => $self->application,
+      collection   => $collection,
+      id_or_name   => $id_or_name
+  );
+
+  my $response = $self->GET($uri);
+
+  return undef if (! $response);
+
+  return Usergrid::Entity->new( object => $response );
+}
+
+=item delete_entity_by_id ( $collection, $id )
+
+Deletes an entity from the collection identified by either UUID or name. Returns
+a L<Usergrid::Entity> of the deleted entity.
+
+=cut
+sub delete_entity_by_id {
+  my ($self, $collection, $id_or_name) = @_;
+
+  my $uri = URI::Template
+    ->new('/{organization}/{application}/{collection}/{id}')
+    ->process(
+      organization => $self->organization,
+      application  => $self->application,
+      collection   => $collection,
+      id           => $id_or_name
+  );
+
+  return Usergrid::Entity->new( object => $self->DELETE($uri) );
+}
+
+=item delete_entity ( L<Usergrid::Entity> )
+
+Deletes the specified L<Usergrid::Entity>. Returns an instance of the deleted
+L<Usergrid::Entity> if successful.
+
+=cut
+sub delete_entity {
+  my ($self, $entity) = @_;
+
+  my $uri = URI::Template
+    ->new('/{organization}/{application}/{collection}/{uuid}')
+    ->process(
+      organization => $self->organization,
+      application  => $self->application,
+      collection   => $entity->get('type'),
+      uuid         => $entity->get('uuid')
+  );
+
+  return Usergrid::Entity->new( object => $self->DELETE($uri) );
+}
+
+=item connect_entities ( L<Usergrid::Entity>, $relationship, L<Usergrid::Entity> )
+
+Creates a connection from Entity #1 to Entity #2 signified by the relationship.
+The first entity in this relationship is identified as the connecting entity and
+the second as the connected entity. The relationship is a string that signifies
+the type of connection. This returns a L<Usergrid::Entity> of the connecting
+entity.
+
+=cut
+sub connect_entities {
+  my ($self, $connecting, $relationship, $connected) = @_;
+
+  my $uri = URI::Template
+    ->new ('/{organization}/{application}/{connecting_collection}/{connecting}/{relationship}/{connected_collection}/{connected}')
+    ->process(
+      organization          => $self->organization,
+      application           => $self->application,
+      connecting_collection => $connecting->get('type'),
+      connecting            => $connecting->get('uuid'),
+      relationship          => $relationship,
+      connected_collection  => $connected->get('type'),
+      connected             => $connected->get('uuid')
+    );
+
+    return Usergrid::Entity->new ( object => $self->POST($uri));
+}
+
+=item disconnect_entities ( L<Usergrid::Entity>, $relationship, L<Usergrid::Entity> )
+
+Removes the connection between the two entities signified by the relationship.
+This does not affect the entities in any other way apart from the removal of the
+connection that is depicted by the relationship. This returns a L<Usergrid::Entity>
+of the connecting entity with the given relationship removed.
+
+=cut
+sub disconnect_entities {
+my ($self, $connecting, $relationship, $connected) = @_;
+
+my $uri = URI::Template
+  ->new ('/{organization}/{application}/{connecting_collection}/{connecting}/{relationship}/{connected_collection}/{connected}')
+  ->process(
+    organization          => $self->organization,
+    application           => $self->application,
+    connecting_collection => $connecting->get('type'),
+    connecting            => $connecting->get('uuid'),
+    relationship          => $relationship,
+    connected_collection  => $connected->get('type'),
+    connected             => $connected->get('uuid')
+  );
+
+  return Usergrid::Entity->new ( object => $self->DELETE($uri));
+}
+
+=back
+
+=head2 Collections
+
+This section covers the methods related to retrieving and working with
+collections in the Usergrid API. Collections contains groups of entities and is
+analogous to a table in an RDBMS.
+
+=over 4
+
+=item get_collection ( $collection, [ $limit ] )
+
+Returns a L<Usergrid::Collection> with the list of entities up to the maximum
+specified limit, which is 10 if not provided.
+
+=cut
+sub get_collection {
+  my ($self, $collection, $limit) = @_;
+
+  my $uri = URI::Template
+    ->new('/{organization}/{application}/{collection}?limit={limit}')
+    ->process(
+      organization => $self->organization,
+      application  => $self->application,
+      collection   => $collection,
+      limit        => ( defined $limit ) ? $limit: 10
+  );
+
+  return $self->_collection($self->GET($uri), $uri);
+}
+
+=item update_collection ( $collection, \%attributes, [ $query ], [ $limit ] )
+
+Updates all the entities in the specified collection with the provided attributes.
+Optionally pass in the SQL-like query to narrow the scope of the objects that
+are affected. This also supports specifying a limit to restrict the maximum number of
+records that are updated. If not specified, the limit defaults to 10 entities.
+
+=cut
+sub update_collection {
+  my ($self, $collection, $properties, $query, $limit) = @_;
+
+  my $uri = URI::Template
+    ->new('/{organization}/{application}/{collection}/?limit={limit}&ql={query}')
+    ->process(
+      organization => $self->organization,
+      application  => $self->application,
+      collection   => $collection,
+      limit        => ( defined $limit ) ? $limit : 10,
+      query        => ( defined $query ) ? $query : undef
+  );
+
+  return $self->_collection($self->PUT($uri, $properties), $uri);
+}
+
+=item delete_collection ( $collection, [ $query ], [ $limit ] )
+
+Batch delete entities in the specified collection. Optionally pass in a SQL-like
+query to narrow the scope of the objects that are affected and a limit to restrict
+the maximum number of records that are deleted. If not specified, the limit
+defaults to 10 entities.
+
+=cut
+sub delete_collection {
+  my ($self, $collection, $query, $limit) = @_;
+
+  my $uri = URI::Template
+    ->new('/{organization}/{application}/{collection}/?limit={limit}&ql={query}')
+    ->process(
+      organization => $self->organization,
+      application  => $self->application,
+      collection   => $collection,
+      limit        => ( defined $limit ) ? $limit : 10,
+      query        => ( defined $query ) ? $query : undef
+  );
+
+  return $self->_collection($self->DELETE($uri), $uri);
+}
+
+=item query_collection ( $collection, $query, [ $limit ] )
+
+Queries all the entities in the specified collection using a SQL-like query string.
+This also supports specifying a limit to restrict the maximum number of
+records that are returned. If not specified, the limit defaults to 10 entities.
+
+=cut
+sub query_collection {
+  my ($self, $collection, $query, $limit) = @_;
+
+  my $uri = URI::Template
+    ->new('/{organization}/{application}/{collection}?limit={limit}&ql={ql}')
+    ->process(
+      organization => $self->organization,
+      application  => $self->application,
+      collection   => $collection,
+      limit        => ( defined $limit ) ? $limit : 10,
+      ql           => $query
+  );
+
+  return $self->_collection($self->GET($uri), $uri);
+}
+
+=item query_connections ( $entity, $relationship, [ $query ], [ $limit ] )
+
+Returns a collection of entities for the given relationship, optionally filtered
+by a SQL-like query and limited to the maximum number of records specified. If no limit
+is provided, a default of 10 entities is assumed.
+
+=cut
+sub query_connections {
+  my ($self, $entity, $relationship, $query, $limit) = @_;
+
+  my $uri = URI::Template
+    ->new('/{organization}/{application}/{collection}/{id}/{relationship}?limit={limit}&ql={ql}')
+    ->process(
+      organization => $self->organization,
+      application  => $self->application,
+      collection   => $entity->get('type'),
+      id           => $entity->get('uuid'),
+      relationship => $relationship,
+      limit        => ( defined $limit ) ? $limit : 10,
+      ql           => $query
+  );
+
+  return $self->_collection($self->GET($uri), $uri);
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
+__END__
+
+=back
+
+=head1 SEE ALSO
+
+L<Usergrid::Collection>, L<Usergrid::Entity>, L<Usergrid::Request>
+
+=head1 LICENSE
+
+This software is distributed under the Apache 2 license.
+
+=head1 AUTHOR
+
+Anuradha Weeraman <an...@cpan.org>
+
+=cut

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/lib/Usergrid/Collection.pm
----------------------------------------------------------------------
diff --git a/sdks/other/perl/lib/Usergrid/Collection.pm b/sdks/other/perl/lib/Usergrid/Collection.pm
new file mode 100644
index 0000000..8d64ce6
--- /dev/null
+++ b/sdks/other/perl/lib/Usergrid/Collection.pm
@@ -0,0 +1,212 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+package Usergrid::Collection;
+
+use Moose;
+use namespace::autoclean;
+
+with (
+  'Usergrid::Request',
+);
+
+my @stack;
+
+=head1 NAME
+
+Usergrid::Collection - a Usergrid collection
+
+=head1 DESCRIPTION
+
+Encapsulates Usergrid collections and provides methods for iterating and paging
+through them.
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item object
+
+A hash reference with the collection data
+
+=item uri
+
+The URI from which this collection was retrieved
+
+=item auto_page
+
+When set, the collection will automatically move to the next page when iterating
+through the collection
+
+=back
+
+=cut
+has 'object'      => ( is => 'rw', required => 1 );
+has 'uri'         => ( is => 'rw', required => 1 );
+has 'auto_page'   => ( is => 'rw', isa => 'Bool' );
+has 'iterator'    => ( is => 'rw', isa => 'Int', default => sub { -1 } );
+
+=head1 METHODS
+
+=over 4
+
+=item has_next_entity
+
+Returns true if there's another entity available during iteration.
+
+=cut
+sub has_next_entity {
+  my $self = shift;
+  my $next = $self->iterator + 1;
+  if ( $self->auto_page && $next >= $self->count()) {
+    return $self->get_next_page();
+  }
+  return ($next >= 0 && $next < $self->count());
+}
+
+=item get_next_entity
+
+Returns the next available L<Usergrid::Entity>. If there's no entity available
+to return, it returns a FALSE. If the auto_page attribute it set, the next page
+is automatically fetched and the next entity is returned.
+
+=cut
+sub get_next_entity {
+  my $self = shift;
+  if ($self->has_next_entity()) {
+    $self->iterator ($self->iterator + 1);
+    return Usergrid::Entity->new ( object => $self->object->{'entities'}[$self->iterator] );
+  }
+  return undef;
+}
+
+=item count
+
+Returns the count of the items in the collection.
+
+=cut
+sub count {
+  my $self = shift;
+  return scalar @{$self->object->{'entities'}};
+}
+
+=item reset_iterator
+
+Rewinds the iterator back to the beginning.
+
+=cut
+sub reset_iterator {
+  my $self = shift;
+  $self->iterator (-1);
+}
+
+=item get_first_entity
+
+Returns the first entity in the collection. This is only applicable for the
+current page of the collection.
+
+=cut
+sub get_first_entity {
+  my $self = shift;
+  return ($self->count() > 0) ? Usergrid::Entity->new (
+    object => $self->object->{'entities'}[0] ) : undef;
+}
+
+=item get_last_entity
+
+Returns the last entity in the collection. This is only applicable for the
+current page of the collection.
+
+=cut
+sub get_last_entity {
+  my $self = shift;
+  return ($self->count() > 0) ? Usergrid::Entity->new (
+    object => $self->object->{'entities'}[$self->count() - 1] ) : undef;
+
+}
+
+=item get_next_page
+
+Fetches the next page in the collection. Returns false when there are no more reults.
+
+=cut
+sub get_next_page {
+  my $self = shift;
+
+  my $csr = $self->object->{'cursor'};
+
+  my $object = $self->GET($self->uri . "&cursor=". $csr);
+
+  if ($object->{'count'} > 0) {
+    push @stack, "1" if (scalar @stack == 0);
+    push @stack, $csr;
+
+    $self->object( $object );
+    $self->reset_iterator();
+
+    return $self;
+  }
+
+  0;
+}
+
+=item get_prev_page
+
+Fetches the previous page in the collection. Returns false when there are no more reults.
+
+=cut
+sub get_prev_page {
+  my $self = shift;
+  my $object;
+
+  if (scalar @stack > 0) {
+    my $csr = pop @stack;
+
+    if ($csr eq "1") {
+      $object = $self->GET($self->uri);
+    } else {
+      $object = $self->GET($self->uri . "&cursor=" . $csr);
+    }
+
+    $self->object( $object );
+    $self->reset_iterator();
+
+    return $self;
+  }
+
+  0;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
+__END__
+
+=back
+
+=head1 SEE ALSO
+
+L<Usergrid::Client>, L<Usergrid::Entity>, L<Usergrid::Request>
+
+=head1 LICENSE
+
+This software is distributed under the Apache 2 license.
+
+=head1 AUTHOR
+
+Anuradha Weeraman <an...@cpan.org>
+
+=cut

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/lib/Usergrid/Entity.pm
----------------------------------------------------------------------
diff --git a/sdks/other/perl/lib/Usergrid/Entity.pm b/sdks/other/perl/lib/Usergrid/Entity.pm
new file mode 100644
index 0000000..051e9b6
--- /dev/null
+++ b/sdks/other/perl/lib/Usergrid/Entity.pm
@@ -0,0 +1,92 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+package Usergrid::Entity;
+
+use Moose;
+use namespace::autoclean;
+
+=head1 NAME
+
+Usergrid::Entity - a Usergrid entity
+
+=head1 DESCRIPTION
+
+Encapsulates Usergrid entities and provides methods for accessing the underlying
+data.
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item object
+
+A hash reference with the entity data
+
+=back
+
+=cut
+has 'object'      => ( is => 'rw', required => 1);
+
+=head1 METHODS
+
+=over 4
+
+=item get ( $attribute_name )
+
+Returns the value of the specified attribute.
+
+=cut
+sub get {
+  my ($self, $key) = @_;
+  return $self->object->{$key} if (defined $self->object->{$key});
+  return $self->object->{'entities'}[0]->{$key};
+}
+
+=item set ( $attribute_name, $value )
+
+Sets the value of the specified attribute.
+
+=cut
+sub set {
+  my ($self, $key, $value) = @_;
+  if (defined $self->object->{$key}) {
+    $self->object->{$key} = $value;
+    return;
+  }
+  $self->object->{'entities'}[0]->{$key} = $value;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
+__END__
+
+=back
+
+=head1 SEE ALSO
+
+L<Usergrid::Client>, L<Usergrid::Collection>, L<Usergrid::Request>
+
+=head1 LICENSE
+
+This software is distributed under the Apache 2 license.
+
+=head1 AUTHOR
+
+Anuradha Weeraman <an...@cpan.org>
+
+=cut

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/lib/Usergrid/Request.pm
----------------------------------------------------------------------
diff --git a/sdks/other/perl/lib/Usergrid/Request.pm b/sdks/other/perl/lib/Usergrid/Request.pm
new file mode 100644
index 0000000..3768039
--- /dev/null
+++ b/sdks/other/perl/lib/Usergrid/Request.pm
@@ -0,0 +1,249 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+package Usergrid::Request;
+
+use Moose::Role;
+use namespace::autoclean;
+use Carp qw(confess);
+use Log::Log4perl qw(:easy);
+use REST::Client;
+use URI::Template;
+use JSON;
+
+my $json = JSON->new->allow_nonref;
+
+=head1 NAME
+
+Usergrid::Request - Role that provides HTTP invocation and utility methods
+
+=head1 DESCRIPTION
+
+This is a Role that is applied to L<Usergrid::Client> and L<Usergrid::Collection>
+which provides methods that relate to HTTP invocation as well as some utility
+functions.
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item organization (String)
+
+Organization name
+
+=item application (String)
+
+Application name
+
+=item api_url (String)
+
+URL of the Usergrid instance
+
+=item trace (Boolean)
+
+Enable/disable request and response tracing for debugging and troubleshooting
+(Optional)
+
+=cut
+has 'organization'  => ( is => 'rw', isa => 'Str', required => 1);
+has 'application'   => ( is => 'rw', isa => 'Str', required => 1);
+has 'api_url'       => ( is => 'rw', isa => 'Str', required => 1);
+
+has 'trace'         => ( is => 'rw', isa => 'Bool', trigger => \&_enable_tracing);
+
+has 'user_token'    => ( is => 'rw');
+
+# internal method
+sub _is_token_required {
+  my ($self, $resource) = @_;
+  return 0 if $resource =~ m/\/management\/token/;
+  1;
+}
+
+# internal method
+sub _api_request {
+  my ($self, $method, $resource, $request) = @_;
+
+  $self->trace_message("$method $resource");
+  $self->trace_message("REQUEST: " . $self->prettify($request)) if ($request);
+
+  my $client = REST::Client->new();
+  $client->setHost($self->api_url);
+
+  if ($self->_is_token_required($resource) == 1 && defined $self->user_token) {
+     $client->addHeader('Authorization',
+        'Bearer ' . $self->user_token->{'access_token'});
+  }
+
+  my $json_req = $self->json_encode($request) if ($request);
+
+  $client->DELETE($resource)          if ($method eq 'DELETE');
+  $client->GET($resource)             if ($method eq 'GET');
+  $client->POST($resource, $json_req) if ($method eq 'POST');
+  $client->PUT($resource, $json_req)  if ($method eq 'PUT');
+
+  my $response = $client->responseContent();
+
+  $self->trace_message("RESPONSE: " . $self->prettify($response)) if ($response);
+
+  return undef if ($client->responseCode() eq "404");
+
+  confess "Bad request"  if ($client->responseCode() eq "400");
+  confess "Unauthorized" if ($client->responseCode() eq "401");
+  confess "Forbidden"    if ($client->responseCode() eq "403");
+  confess "Server error" if ($client->responseCode() eq "500");
+
+  return $self->json_decode($response);
+}
+
+# internal method
+sub _enable_tracing {
+  my ($self, $state, $old_state) = @_;
+  if ($state) {
+    Log::Log4perl::easy_init($DEBUG);
+    our $logger = Log::Log4perl->get_logger();
+  }
+}
+
+# internal method
+sub _collection {
+  my ($self, $object, $uri) = @_;
+
+  return Usergrid::Collection->new (
+    object       => $object,
+    uri          => $uri,
+    organization => $self->organization,
+    application  => $self->application,
+    api_url      => $self->api_url,
+    trace        => $self->trace,
+    user_token   => $self->user_token
+  );
+}
+
+=back
+
+=head1 METHODS
+
+=head2 HTTP Invocation Methods
+
+=over 4
+
+=item DELETE ( $resource )
+
+Invokes HTTP DELETE on the specified resource.
+
+=cut
+sub DELETE {
+  my ($self, $resource) = @_;
+  $self->_api_request('DELETE', $resource);
+}
+
+=item GET ( $resource )
+
+Invokes HTTP GET on the specified resource.
+
+=cut
+sub GET {
+  my ($self, $resource) = @_;
+  $self->_api_request('GET', $resource);
+}
+
+=item POST ( $resource, \%request )
+
+Invokes HTTP POST on the specified resource and passes in the payload
+for the request.
+
+=cut
+sub POST {
+  my ($self, $resource, $request) = @_;
+  $self->_api_request('POST', $resource, $request);
+}
+
+=item PUT ( $resource, \%request )
+
+Invokes HTTP PUT on the specified resource and passes in the payload
+for the request.
+
+=cut
+sub PUT {
+  my ($self, $resource, $request) = @_;
+  $self->_api_request('PUT', $resource, $request);
+}
+
+=back
+
+=head2 Utility Methods
+
+=over 4
+
+=item trace_message ( $message )
+
+Utility method to log a message to console if tracing is enabled.
+
+=cut
+sub trace_message {
+  my ($self, $message) = @_;
+  $Usergrid::Request::logger->debug($message) if (defined $Usergrid::Request::logger);
+}
+
+=item prettify ( $message, \%object )
+
+Returns a prettified string representation hash reference.
+
+=cut
+sub prettify {
+  my ($self, $json_obj) = @_;
+  return $json->pretty->encode($json_obj);
+}
+
+=item json_encode ( \%hashref )
+
+Returns a JSON object from a hash reference.
+
+=cut
+sub json_encode {
+  my ($self, $json_obj) = @_;
+  $json->encode($json_obj);
+}
+
+=item json_decode ( $json_object )
+
+Returns a hash reference from a JSON object.
+
+=cut
+sub json_decode {
+  my ($self, $json_obj) = @_;
+  $json->decode($json_obj);
+}
+
+1;
+
+__END__
+
+=back
+
+=head1 SEE ALSO
+
+L<Usergrid::Client>, L<Usergrid::Collection>, L<Usergrid::Entity>
+
+=head1 LICENSE
+
+This software is distributed under the Apache 2 license.
+
+=head1 AUTHOR
+
+Anuradha Weeraman <an...@cpan.org>
+
+=cut

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/01_init.t
----------------------------------------------------------------------
diff --git a/sdks/other/perl/t/01_init.t b/sdks/other/perl/t/01_init.t
new file mode 100644
index 0000000..c410155
--- /dev/null
+++ b/sdks/other/perl/t/01_init.t
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+
+BEGIN {
+  use_ok 'Usergrid::Client'     || print "Bail out!\n";
+  use_ok 'Usergrid::Entity'     || print "Bail out!\n";
+  use_ok 'Usergrid::Collection' || print "Bail out!\n";
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/02_login.t
----------------------------------------------------------------------
diff --git a/sdks/other/perl/t/02_login.t b/sdks/other/perl/t/02_login.t
new file mode 100644
index 0000000..8397b92
--- /dev/null
+++ b/sdks/other/perl/t/02_login.t
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+use Usergrid::Client;
+use IO::Socket::INET;
+use Test::More;
+
+# TEST DATA
+our $hostname        = 'localhost';
+our $port            = '8080';
+our $api_url         = "http://$hostname:$port";
+our $organization    = 'test-organization';
+our $application     = 'test-app';
+our $username        = 'testuser';
+our $password        = 'Testuser123$';
+###########
+
+if (_check_port($hostname, $port)) {
+  plan tests => 1;
+} else {
+  plan skip_all => "server $api_url not reachable"
+}
+
+sub _check_port {
+  my ($hostname, $port) = @_;
+  new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port,
+    Proto => 'tcp' ) || return 0;
+  return 1;
+}
+
+my ($user, $token);
+
+# Create the client object that will be used for all subsequent requests
+my $client = Usergrid::Client->new(
+  organization => $organization,
+  application  => $application,
+  api_url      => $api_url,
+  trace        => 0
+);
+
+# Create a test user
+$user = $client->add_entity("users", { username=>$username, password=>$password });
+
+$token = $client->login($username, $password);
+
+ok ( $token->{user}->{username} eq $username, "user logged in" );
+
+$client->delete_entity($user);

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/03_update.t
----------------------------------------------------------------------
diff --git a/sdks/other/perl/t/03_update.t b/sdks/other/perl/t/03_update.t
new file mode 100644
index 0000000..b05a260
--- /dev/null
+++ b/sdks/other/perl/t/03_update.t
@@ -0,0 +1,102 @@
+#!/usr/bin/perl
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+use Usergrid::Client;
+use IO::Socket::INET;
+use Test::More;
+
+# TEST DATA
+my $hostname        = 'localhost';
+my $port            = '8080';
+my $api_url         = "http://$hostname:$port";
+my $organization    = 'test-organization';
+my $application     = 'test-app';
+my $username        = 'testuser';
+my $password        = 'Testuser123$';
+###########
+
+if (_check_port($hostname, $port)) {
+  plan tests => 7;
+} else {
+  plan skip_all => "server $api_url not reachable"
+}
+
+sub _check_port {
+  my ($hostname, $port) = @_;
+  new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port,
+    Proto => 'tcp' ) || return 0;
+  return 1;
+}
+
+my ($user, $token, $book, $deleted_book);
+
+# Create the client object that will be used for all subsequent requests
+my $client = Usergrid::Client->new(
+  organization => $organization,
+  application  => $application,
+  api_url      => $api_url,
+  trace        => 0
+);
+
+# Create a test user
+$user = $client->add_entity("users", { username=>$username, password=>$password });
+
+$token = $client->login($username, $password);
+
+eval {
+
+  ok ( $token->{user}->{username} eq $username, "user logged in" );
+
+  $book = $client->add_entity("books", { name => "Ulysses", author => "James Joyce" });
+
+  ok ( $book->get('author') eq "James Joyce", "check entity creation" );
+
+  $book->set('genre', 'Modernist');
+
+  $book = $client->update_entity($book);
+
+  ok ( $book->get('genre') eq "Modernist", "check for new attribute" );
+
+  $book->set('genre', 'Novel');
+
+  $book = $client->update_entity($book);
+
+  ok ( $book->get('genre') eq "Novel", "check for updated attribute" );
+
+  $book = $client->get_entity("books", $book->get('uuid'));
+
+  ok ( $book->get('genre') eq "Novel", "check again for updated attribute by uuid" );
+
+  $book = $client->get_entity("books", "Ulysses");
+
+  ok ( $book->get('name') eq 'Ulysses', "get object by name") ;
+
+  $book = $client->delete_entity_by_id("books", $book->get('uuid'));
+
+  $deleted_book = $client->get_entity("books", $book->get('uuid'));
+
+  ok ( (! defined $deleted_book), "deleted book cannot be found" );
+
+};
+
+diag($@) if $@;
+
+# Cleanup
+$client->delete_entity($book);
+$client->delete_entity($user);

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/04_collection.t
----------------------------------------------------------------------
diff --git a/sdks/other/perl/t/04_collection.t b/sdks/other/perl/t/04_collection.t
new file mode 100644
index 0000000..d823ced
--- /dev/null
+++ b/sdks/other/perl/t/04_collection.t
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+use Usergrid::Client;
+use IO::Socket::INET;
+use Test::More;
+
+# TEST DATA
+my $hostname        = 'localhost';
+my $port            = '8080';
+my $api_url         = "http://$hostname:$port";
+my $organization    = 'test-organization';
+my $application     = 'test-app';
+my $username        = 'testuser';
+my $password        = 'Testuser123$';
+###########
+
+if (_check_port($hostname, $port)) {
+  plan tests => 10;
+} else {
+  plan skip_all => "server $api_url not reachable"
+}
+
+sub _check_port {
+  my ($hostname, $port) = @_;
+  new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port,
+    Proto => 'tcp' ) || return 0;
+  return 1;
+}
+
+my ($user, $token, $book, $collection);
+
+# Create the client object that will be used for all subsequent requests
+my $client = Usergrid::Client->new(
+  organization => $organization,
+  application  => $application,
+  api_url      => $api_url,
+  trace        => 0
+);
+
+# Create a test user
+$user = $client->add_entity("users", { username=>$username, password=>$password });
+
+$token = $client->login($username, $password);
+
+eval {
+
+  $collection = $client->get_collection("books");
+
+  ok ( $collection->count() == 0, "count must be initially zero" );
+
+  $client->add_entity("books", { name => "Ulysses", author => "James Joyce" });
+  $client->add_entity("books", { name => "Neuromancer", author => "William Gibson" });
+  $client->add_entity("books", { name => "On the Road", author => "Jack Kerouac" });
+  $client->add_entity("books", { name => "Ubik", author => "Philip K. Dick" });
+  $client->add_entity("books", { name => "Reef", author => "Romesh Gunasekera" });
+
+  $collection = $client->get_collection("books");
+
+  ok ( $collection->count() == 5, "count must now be five" );
+
+  while ($collection->has_next_entity()) {
+    $book = $collection->get_next_entity();
+    ok ( length($book->get('name')) > 3, "check the book titles" );
+  }
+
+  $collection->reset_iterator();
+
+  ok ( $collection->iterator == -1, "iterator must be reset" );
+
+  ok ( $collection->count() == 5, "count must be five" );
+
+  while ($collection->has_next_entity()) {
+    $book = $collection->get_next_entity();
+    $client->delete_entity($book);
+  }
+
+  $collection = $client->get_collection("books");
+
+  ok ( $collection->count() == 0, "count must now be again zero" );
+
+};
+
+diag($@) if $@;
+
+# Cleanup
+$collection = $client->delete_collection("books", undef, 30);
+$client->delete_entity($user);

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/05_query.t
----------------------------------------------------------------------
diff --git a/sdks/other/perl/t/05_query.t b/sdks/other/perl/t/05_query.t
new file mode 100644
index 0000000..40e3f1b
--- /dev/null
+++ b/sdks/other/perl/t/05_query.t
@@ -0,0 +1,108 @@
+#!/usr/bin/perl
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+use Usergrid::Client;
+use IO::Socket::INET;
+use Test::More;
+
+# TEST DATA
+my $hostname        = 'localhost';
+my $port            = '8080';
+my $api_url         = "http://$hostname:$port";
+my $organization    = 'test-organization';
+my $application     = 'test-app';
+my $username        = 'testuser';
+my $password        = 'Testuser123$';
+###########
+
+if (_check_port($hostname, $port)) {
+  plan tests => 8;
+} else {
+  plan skip_all => "server $api_url not reachable"
+}
+
+sub _check_port {
+  my ($hostname, $port) = @_;
+  new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port,
+    Proto => 'tcp' ) || return 0;
+  return 1;
+}
+
+my ($user, $token, $book, $collection, $subset);
+
+# Create the client object that will be used for all subsequent requests
+my $client = Usergrid::Client->new(
+  organization => $organization,
+  application  => $application,
+  api_url      => $api_url,
+  trace        => 0
+);
+
+# Create a test user
+$user = $client->add_entity("users", { username=>$username, password=>$password });
+
+$token = $client->login($username, $password);
+
+eval {
+
+  $collection = $client->get_collection("books");
+
+  ok ( $collection->count() == 0, "count must be initially zero" );
+
+  for (my $i = 0; $i < 30; $i++) {
+    $client->add_entity("books", { name => "book $i", index => $i });
+  }
+
+  $collection = $client->get_collection("books", 30);
+
+  ok ( $collection->count() == 30, "count must now be 30" );
+
+  $book = $collection->get_first_entity();
+
+  ok ( $book->get('index') eq '0', "first index should be 0");
+
+  $book = $collection->get_last_entity();
+
+  ok ( $book->get('index') eq '29', "last index should be 29");
+
+  $subset = $client->query_collection("books", "select * where index = '5'", 15 );
+
+  ok ( $subset->count() == 1, "subset is 1" );
+  ok ( $subset->object->{'params'}->{'limit'}[0] eq '15', "check limit override" );
+
+  $book = $subset->get_next_entity();
+
+  ok ( $book->get('index') eq '5', "query returned the fifth book" );
+
+  while ($collection->has_next_entity()) {
+    $book = $collection->get_next_entity();
+    $client->delete_entity($book);
+  }
+
+  $collection = $client->get_collection("books");
+
+  ok ( $collection->count() == 0, "count must now be again zero" );
+
+};
+
+diag($@) if $@;
+
+# Cleanup
+$collection = $client->delete_collection("books", undef, 30);
+$client->delete_entity($user);

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/06_batch_update.t
----------------------------------------------------------------------
diff --git a/sdks/other/perl/t/06_batch_update.t b/sdks/other/perl/t/06_batch_update.t
new file mode 100644
index 0000000..04ff11d
--- /dev/null
+++ b/sdks/other/perl/t/06_batch_update.t
@@ -0,0 +1,107 @@
+#!/usr/bin/perl
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+use Usergrid::Client;
+use IO::Socket::INET;
+use Test::More;
+
+# TEST DATA
+my $hostname        = 'localhost';
+my $port            = '8080';
+my $api_url         = "http://$hostname:$port";
+my $organization    = 'test-organization';
+my $application     = 'test-app';
+my $username        = 'testuser';
+my $password        = 'Testuser123$';
+###########
+
+if (_check_port($hostname, $port)) {
+  plan tests => 34;
+} else {
+  plan skip_all => "server $api_url not reachable"
+}
+
+sub _check_port {
+  my ($hostname, $port) = @_;
+  new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port,
+    Proto => 'tcp' ) || return 0;
+  return 1;
+}
+
+my ($user, $token, $book, $collection, $count);
+
+# Create the client object that will be used for all subsequent requests
+my $client = Usergrid::Client->new(
+  organization => $organization,
+  application  => $application,
+  api_url      => $api_url,
+  trace        => 0
+);
+
+# Create a test user
+$user = $client->add_entity("users", { username=>$username, password=>$password });
+
+$token = $client->login($username, $password);
+
+eval {
+
+  $collection = $client->get_collection("books");
+
+  ok ( $collection->count() == 0, "count must be initially zero" );
+
+  for (my $i = 0; $i < 30; $i++) {
+    $client->add_entity("books", { name => "book $i", index => $i });
+  }
+
+  $collection = $client->get_collection("books", 30);
+
+  ok ( $collection->count() == 30, "count must now be 30" );
+
+  $client->update_collection("books", { in_stock => 1 });
+
+  $collection = $client->get_collection("books", 30);
+
+  while ($collection->has_next_entity()) {
+    $book = $collection->get_next_entity();
+    ok ( $book->get('in_stock') == 1 );
+  }
+
+  $client->update_collection("books", { in_stock => 0 }, "select * where index = '1' or index = '2' or index = '3' or index = '4' or index = '5'");
+
+  $collection = $client->get_collection("books", 30);
+
+  while ($collection->has_next_entity()) {
+    $book = $collection->get_next_entity();
+    $count++ if ($book->get('index') =~ /[12345]/ && $book->get('in_stock') == 0);
+    $client->delete_entity($book);
+  }
+
+  ok ( $count == 5, "batch update only 5 entities" );
+
+  $collection = $client->get_collection("books");
+
+  ok ( $collection->count() == 0, "count must now be again zero" );
+
+};
+
+diag($@) if $@;
+
+# Cleanup
+$collection = $client->delete_collection("books", undef, 30);
+$client->delete_entity($user);

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/07_batch_delete.t
----------------------------------------------------------------------
diff --git a/sdks/other/perl/t/07_batch_delete.t b/sdks/other/perl/t/07_batch_delete.t
new file mode 100644
index 0000000..2ed6d72
--- /dev/null
+++ b/sdks/other/perl/t/07_batch_delete.t
@@ -0,0 +1,87 @@
+#!/usr/bin/perl
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+use Usergrid::Client;
+use IO::Socket::INET;
+use Test::More;
+
+# TEST DATA
+my $hostname        = 'localhost';
+my $port            = '8080';
+my $api_url         = "http://$hostname:$port";
+my $organization    = 'test-organization';
+my $application     = 'test-app';
+my $username        = 'testuser';
+my $password        = 'Testuser123$';
+###########
+
+if (_check_port($hostname, $port)) {
+  plan tests => 3;
+} else {
+  plan skip_all => "server $api_url not reachable"
+}
+
+sub _check_port {
+  my ($hostname, $port) = @_;
+  new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port,
+    Proto => 'tcp' ) || return 0;
+  return 1;
+}
+
+my ($user, $token, $book, $collection, $count);
+
+# Create the client object that will be used for all subsequent requests
+my $client = Usergrid::Client->new(
+  organization => $organization,
+  application  => $application,
+  api_url      => $api_url,
+  trace        => 0
+);
+
+# Create a test user
+$user = $client->add_entity("users", { username=>$username, password=>$password });
+
+$token = $client->login($username, $password);
+
+eval {
+
+  $collection = $client->get_collection("books");
+
+  ok ( $collection->count() == 0, "count must be initially zero" );
+
+  for (my $i = 0; $i < 30; $i++) {
+    $client->add_entity("books", { name => "book $i", index => $i });
+  }
+
+  $collection = $client->get_collection("books", 30);
+
+  ok ( $collection->count() == 30, "count must now be 30" );
+
+  $client->delete_collection("books", "select * where index = '1' or index = '2' or index = '3' or index = '4' or index = '5'");
+
+  $collection = $client->get_collection("books", 30);
+
+  ok ( $collection->count() == 25, "deleted 5 entities" );
+};
+
+diag($@) if $@;
+
+# Cleanup
+$collection = $client->delete_collection("books", undef, 30);
+$client->delete_entity($user);

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/08_connections.t
----------------------------------------------------------------------
diff --git a/sdks/other/perl/t/08_connections.t b/sdks/other/perl/t/08_connections.t
new file mode 100644
index 0000000..35550e0
--- /dev/null
+++ b/sdks/other/perl/t/08_connections.t
@@ -0,0 +1,94 @@
+#!/usr/bin/perl
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+use Usergrid::Client;
+use IO::Socket::INET;
+use Test::More;
+
+# TEST DATA
+my $hostname        = 'localhost';
+my $port            = '8080';
+my $api_url         = "http://$hostname:$port";
+my $organization    = 'test-organization';
+my $application     = 'test-app';
+my $username        = 'testuser';
+my $password        = 'Testuser123$';
+###########
+
+if (_check_port($hostname, $port)) {
+  plan tests => 5;
+} else {
+  plan skip_all => "server $api_url not reachable"
+}
+
+sub _check_port {
+  my ($hostname, $port) = @_;
+  new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port,
+    Proto => 'tcp' ) || return 0;
+  return 1;
+}
+
+my ($user, $token, $book, $book1, $book2, $book3, $collection);
+
+# Create the client object that will be used for all subsequent requests
+my $client = Usergrid::Client->new(
+  organization => $organization,
+  application  => $application,
+  api_url      => $api_url,
+  trace        => 0
+);
+
+# Create a test user
+$user = $client->add_entity("users", { username=>$username, password=>$password });
+
+$token = $client->login($username, $password);
+
+eval {
+  $book1 = $client->add_entity("books", { name => "Neuromancer", author => "William Gibson" });
+  $book2 = $client->add_entity("books", { name => "Count Zero", author => "William Gibson" });
+  $book3 = $client->add_entity("books", { name => "Mona Lisa Overdrive", author => "William Gibson" });
+
+  $client->connect_entities($book1, "similar_to", $book2);
+  $client->connect_entities($book1, "similar_to", $book3);
+
+  $collection = $client->query_connections($book1, "similar_to");
+
+  ok ( $collection->count == 2, "two connections must exist" );
+
+  while ($collection->has_next_entity()) {
+    $book = $collection->get_next_entity();
+    ok ( $book->get('name') eq 'Count Zero' || $book->get('name') eq ('Mona Lisa Overdrive'), "check connections");
+  }
+
+  $client->disconnect_entities($book1, "similar_to", $book2);
+
+  $collection = $client->query_connections($book1, "similar_to");
+
+  ok ( $collection->count() == 1 );
+
+  $book = $collection->get_next_entity();
+
+  ok ( $book->get('name') eq 'Mona Lisa Overdrive', "check remaining connection");
+};
+
+diag($@) if $@;
+
+# Cleanup
+$client->delete_collection("books", undef, 10);
+$client->delete_entity($user);

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/09_paging.t
----------------------------------------------------------------------
diff --git a/sdks/other/perl/t/09_paging.t b/sdks/other/perl/t/09_paging.t
new file mode 100644
index 0000000..9a38a7f
--- /dev/null
+++ b/sdks/other/perl/t/09_paging.t
@@ -0,0 +1,140 @@
+#!/usr/bin/perl
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+use Usergrid::Client;
+use IO::Socket::INET;
+use Test::More;
+
+# TEST DATA
+my $hostname        = 'localhost';
+my $port            = '8080';
+my $api_url         = "http://$hostname:$port";
+my $organization    = 'test-organization';
+my $application     = 'test-app';
+my $username        = 'testuser';
+my $password        = 'Testuser123$';
+###########
+
+if (_check_port($hostname, $port)) {
+  plan tests => 24;
+} else {
+  plan skip_all => "server $api_url not reachable"
+}
+
+sub _check_port {
+  my ($hostname, $port) = @_;
+  new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port,
+    Proto => 'tcp' ) || return 0;
+  return 1;
+}
+
+my ($user, $token, $book, $collection, $subset, $count);
+
+# Create the client object that will be used for all subsequent requests
+my $client = Usergrid::Client->new(
+  organization => $organization,
+  application  => $application,
+  api_url      => $api_url,
+  trace        => 0
+);
+
+# Create a test user
+$user = $client->add_entity("users", { username=>$username, password=>$password });
+
+$token = $client->login($username, $password);
+
+eval {
+
+  $collection = $client->get_collection("books");
+
+  ok ( $collection->count() == 0, "count must be initially zero" );
+
+  for (my $i = 0; $i < 30; $i++) {
+    $client->add_entity("books", { name => "book $i", index => $i });
+  }
+
+  $collection = $client->get_collection("books", 30);
+
+  ok ( $collection->count() == 30, "count must now be 30" );
+
+  $collection = $client->get_collection("books", 10);
+
+  ok ( $collection->count == 10, "checking first page count" );
+  ok ( $collection->get_first_entity()->get('name') eq 'book 0', "Check first entity of first page");
+  ok ( $collection->get_last_entity()->get('name')  eq 'book 9', "Check last entity of first page");
+
+  $collection->get_next_page();
+
+  ok ( $collection->count == 10, "checking second page count" );
+  ok ( $collection->get_first_entity()->get('name') eq 'book 10', "Check first entity of second page");
+  ok ( $collection->get_last_entity()->get('name')  eq 'book 19', "Check last entity of second page");
+
+  $collection->get_next_page();
+
+  ok ( $collection->count == 10, "checking third page count" );
+  ok ( $collection->get_first_entity()->get('name') eq 'book 20', "Check first entity of third page");
+  ok ( $collection->get_last_entity()->get('name')  eq 'book 29', "Check last entity of third page");
+
+  if (! $collection->get_next_page()) {
+    pass ( "no more results" );
+  }
+
+  $collection->get_prev_page();
+
+  ok ( $collection->count == 10, "checking third page count" );
+  ok ( $collection->get_first_entity()->get('name') eq 'book 20', "Check first entity of third page in reverse");
+  ok ( $collection->get_last_entity()->get('name')  eq 'book 29', "Check last entity of third page in reverse");
+
+  $collection->get_prev_page();
+
+  ok ( $collection->count == 10, "checking second page count" );
+  ok ( $collection->get_first_entity()->get('name') eq 'book 10', "Check first entity of second page in reverse");
+  ok ( $collection->get_last_entity()->get('name')  eq 'book 19', "Check last entity of second page in reverse");
+
+  $collection->get_prev_page();
+
+  ok ( $collection->count == 10, "checking first page count" );
+  ok ( $collection->get_first_entity()->get('name') eq 'book 0', "Check first entity of first page in reverse");
+  ok ( $collection->get_last_entity()->get('name')  eq 'book 9', "Check last entity of first page in reverse");
+
+  if (! $collection->get_prev_page()) {
+    pass ( "no more results in reverse" );
+  }
+
+  $count = 0;
+  $collection = $client->get_collection("books", 10);
+  do {
+    $count += $collection->count();
+  } while ($collection->get_next_page());
+
+  ok ( $count == 30, "should return 30 entities in forward" );
+
+  $count = 0;
+  while ($collection->get_prev_page()) {
+    $count += $collection->count();
+  }
+
+  ok ( $count == 30, "should return 30 entities in reverse" );
+};
+
+diag($@) if $@;
+
+# Cleanup
+$client->delete_collection("books", undef, 30);
+$client->delete_entity($user);

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/perl/t/10_auto_paging.t
----------------------------------------------------------------------
diff --git a/sdks/other/perl/t/10_auto_paging.t b/sdks/other/perl/t/10_auto_paging.t
new file mode 100644
index 0000000..5ed98f7
--- /dev/null
+++ b/sdks/other/perl/t/10_auto_paging.t
@@ -0,0 +1,94 @@
+#!/usr/bin/perl
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+use strict;
+use warnings;
+use Usergrid::Client;
+use IO::Socket::INET;
+use Test::More;
+
+# TEST DATA
+my $hostname        = 'localhost';
+my $port            = '8080';
+my $api_url         = "http://$hostname:$port";
+my $organization    = 'test-organization';
+my $application     = 'test-app';
+my $username        = 'testuser';
+my $password        = 'Testuser123$';
+###########
+
+if (_check_port($hostname, $port)) {
+  plan tests => 3;
+} else {
+  plan skip_all => "server $api_url not reachable"
+}
+
+sub _check_port {
+  my ($hostname, $port) = @_;
+  new IO::Socket::INET ( PeerAddr => $hostname, PeerPort => $port,
+    Proto => 'tcp' ) || return 0;
+  return 1;
+}
+
+my ($user, $token, $book, $collection, $subset, $count);
+
+# Create the client object that will be used for all subsequent requests
+my $client = Usergrid::Client->new(
+  organization => $organization,
+  application  => $application,
+  api_url      => $api_url,
+  trace        => 0
+);
+
+# Create a test user
+$user = $client->add_entity("users", { username=>$username, password=>$password });
+
+$token = $client->login($username, $password);
+
+eval {
+
+  $collection = $client->get_collection("books");
+
+  ok ( $collection->count() == 0, "count must be initially zero" );
+
+  for (my $i = 0; $i < 30; $i++) {
+    $client->add_entity("books", { name => "book $i", index => $i });
+  }
+
+  $collection = $client->get_collection("books");
+
+  ok ( $collection->get_last_entity()->get('name') eq 'book 9', "only return 10 entites per page by default");
+
+  $collection = $client->get_collection("books");
+
+  $collection->auto_page(1);
+
+  $count = 0;
+
+  while ($collection->has_next_entity()) {
+    $count += 1;
+    my $entity = $collection->get_next_entity();
+  }
+
+  ok ( $count == 30, "should return 30 entities in when auto paging" );
+};
+
+diag($@) if $@;
+
+# Cleanup
+$client->delete_collection("books", undef, 30);
+$client->delete_entity($user);

http://git-wip-us.apache.org/repos/asf/usergrid/blob/867060fa/sdks/other/php/LICENSE
----------------------------------------------------------------------
diff --git a/sdks/other/php/LICENSE b/sdks/other/php/LICENSE
new file mode 100644
index 0000000..e06d208
--- /dev/null
+++ b/sdks/other/php/LICENSE
@@ -0,0 +1,202 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+