diff --git a/package.json b/package.json index 679bdf4..215bce6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "reste", "version": "1.7.0", - "description": "A JavaScript REST / API helper for Titanium with Alloy Models/Collections support", + "description": "A JavaScript REST / API helper with Models/Collections support", "titaniumManifest": { "guid": "afafe8b0-b93b-771c-a9e5-4e71db81b9ff" }, @@ -12,15 +12,12 @@ "url": "https://github.com/jasonkneen/RESTe.git" }, "keywords": [ - "alloy", "api", "appc-lib", "appc-npm", "appcelerator", "arrow", - "rest", - "titanium", - "titanium-module" + "rest" ], "author": "Jason Kneen", "license": "Apache-2.0", diff --git a/readme.md b/readme.md index acf9c80..3cce8df 100644 --- a/readme.md +++ b/readme.md @@ -395,11 +395,11 @@ The following will clear any cookies from the baseUrl: api.clearCookies(); ``` -## Alloy Collections and Model support +## Collections and Model support RESTe supports collection and model generation. So it supports creating and managing collections and models, binding, and CRUD methods to Create, Update and Delete models. -**NOTE**: If you are using the Alloy Collections and Model Support of RESTe, you should **not** use the Alloy Model / Collection definitions -- so you shouldn't have an app/models folder with models defined. You must also **not** use the notation in the XML -- you *just* use the dataCollection binding in a repeating element. +**NOTE**: If you are using the Collections and Model Support of RESTe, you should **not** use the Alloy Model / Collection definitions -- so you shouldn't have an app/models folder with models defined. You must also **not** use the notation in the XML -- you *just* use the dataCollection binding in a repeating element. You can also now perform transform functions at a global (config) level or locally in a controller / view -- this is really useful if you use Alloy and pass models to views using **$model** @@ -440,7 +440,7 @@ You can also pass an optional transform parameter in the transform function, whi ### Defining methods with models / collections -Using the following config you can configure end points that will still work as normal RESTe methods, but also give you collections and model support for (C)reate, (R)ead, (U)pdate, (D)elete. For Collections I use an array of collections so you can have multiple endpoints configured if different collections using the same model. This enables use of for example, Alloy.Collections.locations (for all locations) and Alloy.Collections.locationsByName (for locations by a specific parameter). +Using the following config you can configure end points that will still work as normal RESTe methods, but also give you collections and model support for (C)reate, (R)ead, (U)pdate, (D)elete. For Collections I use an array of collections so you can have multiple endpoints configured if different collections using the same model. This enables use of for example, Collections.locations (for all locations) and Collections.locationsByName (for locations by a specific parameter). (Ideally this should be more elegant, allowing the single locations collection in this case to be used to filter content but I needed a way to make this API independant and it's the best I can do for now!) @@ -489,7 +489,7 @@ Using the following config you can configure end points that will still work as In the example above, I can refresh the data for a collection by using: ```javascript -Alloy.Collections.locations.fetch(); +Collections.locations.fetch(); ``` and bind it to a tableview as follows: @@ -506,7 +506,7 @@ and bind it to a tableview as follows: You could also send parameters like follows: ```javascript -Alloy.Collections.locationsByName.fetch({ +Collections.locationsByName.fetch({ name: "home" }); ``` @@ -516,13 +516,13 @@ To sort a collection, you need to set the comparator to the collection. Don't do Calling the sort function at any time after the fetch will try to sort. ```js -Alloy.Collections.locations.comparator = function(a, b){ +Collections.locations.comparator = function(a, b){ // do your sorting here, a & b will be models }; -Alloy.Collections.locations.fetch({ +Collections.locations.fetch({ success: function(a,b,c){ - Alloy.Collections.locations.sort(); + Collections.locations.sort(); } }); ``` @@ -535,14 +535,14 @@ RESTe provides a couple of useful helper functions to create new models and coll .createModel(name, attributes) .createCollection(name, array) ``` -Each return either a model, or collection that can then be used with Alloy. +Each return either a model, or collection that can then be used with Backbone. When working with created models, you can define an instance of a model that you've specified in the config, and if that supports CRUD functions, you can pass options when creating, saving, updating and deleting. So for example: ```javascript -var user = Alloy.Globals.reste.createModel("user"); +var user = reste.createModel("user"); user.save({ username: $.email.value, @@ -562,6 +562,144 @@ user.save({ }); ``` +## Using RESTe with React Native and React + +RESTe can also be used with React Native and React. Here are some instructions on how to set it up: + +### React Native + +1. Install RESTe using npm: + +```sh +npm install reste +``` + +2. Import RESTe in your React Native project: + +```javascript +import reste from 'reste'; +``` + +3. Configure RESTe in your project: + +```javascript +const api = new reste(); + +api.config({ + debug: true, + errorsAsObjects: true, + autoValidateParams: false, + validatesSecureCertificate: false, + timeout: 4000, + url: "https://api.parse.com/1/", + requestHeaders: { + "X-Parse-Application-Id": "APPID", + "X-Parse-REST-API-Key": "RESTID", + "Content-Type": "application/json" + }, + methods: [{ + name: "courses", + post: "functions/getCourses", + onError: function(e, callback, globalOnError){ + alert("There was an error getting the courses!"); + } + }, { + name: "getVideos", + get: "classes/videos" + }, { + name: "getVideoById", + get: "classes/videos/" + }, { + name: "addVideo", + post: "classes/videos" + }], + onError: function(e, retry) { + alert("There was an error connecting to the server, check your network connection and retry."); + retry(); + }, + onLoad: function(e, callback) { + callback(e); + } +}); +``` + +4. Use the configured methods in your React Native components: + +```javascript +api.getVideos().then(videos => { + // do stuff with the videos +}).catch(error => { + console.error(error); +}); +``` + +### React + +1. Install RESTe using npm: + +```sh +npm install reste +``` + +2. Import RESTe in your React project: + +```javascript +import reste from 'reste'; +``` + +3. Configure RESTe in your project: + +```javascript +const api = new reste(); + +api.config({ + debug: true, + errorsAsObjects: true, + autoValidateParams: false, + validatesSecureCertificate: false, + timeout: 4000, + url: "https://api.parse.com/1/", + requestHeaders: { + "X-Parse-Application-Id": "APPID", + "X-Parse-REST-API-Key": "RESTID", + "Content-Type": "application/json" + }, + methods: [{ + name: "courses", + post: "functions/getCourses", + onError: function(e, callback, globalOnError){ + alert("There was an error getting the courses!"); + } + }, { + name: "getVideos", + get: "classes/videos" + }, { + name: "getVideoById", + get: "classes/videos/" + }, { + name: "addVideo", + post: "classes/videos" + }], + onError: function(e, retry) { + alert("There was an error connecting to the server, check your network connection and retry."); + retry(); + }, + onLoad: function(e, callback) { + callback(e); + } +}); +``` + +4. Use the configured methods in your React components: + +```javascript +api.getVideos().then(videos => { + // do stuff with the videos +}).catch(error => { + console.error(error); +}); +``` + ## License
diff --git a/reste.js b/reste.js
index 97378a9..acd2ff1 100644
--- a/reste.js
+++ b/reste.js
@@ -377,15 +377,15 @@ function main() {
   };
 
   reste.createCollection = function (name, content) {
-    if (!Alloy.Collections[name]) {
-      Alloy.Collections[name] = new Backbone.Collection();
+    if (!reste.Collections[name]) {
+      reste.Collections[name] = new Backbone.Collection();
     }
 
     if (content instanceof Array) {
-      Alloy.Collections[name].reset(content);
+      reste.Collections[name].reset(content);
 
-      Alloy.Collections[name].fetch = function () {
-        Alloy.Collections[name].trigger('change');
+      reste.Collections[name].fetch = function () {
+        reste.Collections[name].trigger('change');
       };
     } else {
       throw 'No Array specified for createCollection';
@@ -415,10 +415,10 @@ function main() {
 
       if (args.collections) {
         args.collections.forEach((collection) => {
-          Alloy.Collections[collection.name] = Alloy.Collections[collection.name] || new Backbone.Collection();
-          Alloy.Collections[collection.name]._type = args.name;
-          Alloy.Collections[collection.name]._name = collection.name;
-          Alloy.Collections[collection.name].model = model;
+          reste.Collections[collection.name] = reste.Collections[collection.name] || new Backbone.Collection();
+          reste.Collections[collection.name]._type = args.name;
+          reste.Collections[collection.name]._name = collection.name;
+          reste.Collections[collection.name].model = model;
         });
       }
     };
@@ -447,7 +447,7 @@ function main() {
 
               if (options.success) options.success(response[collectionConfig.content]);
 
-              Alloy.Collections[collectionConfig.name].trigger('sync');
+              reste.Collections[collectionConfig.name].trigger('sync');
             } else {
               response.forEach((item) => {
                 item.id = item[modelConfig.id];
@@ -455,7 +455,7 @@ function main() {
 
               if (options.success) options.success(response);
 
-              Alloy.Collections[collectionConfig.name].trigger('sync');
+              reste.Collections[collectionConfig.name].trigger('sync');
             }
           }
         }, (response) => {
diff --git a/tests/alloy.test.js b/tests/alloy.test.js
new file mode 100644
index 0000000..075b415
--- /dev/null
+++ b/tests/alloy.test.js
@@ -0,0 +1,73 @@
+const reste = require('../reste');
+const assert = require('assert');
+
+describe('RESTe Alloy (Titanium) Tests', function() {
+  let api;
+
+  beforeEach(function() {
+    api = new reste();
+    api.config({
+      debug: true,
+      errorsAsObjects: true,
+      autoValidateParams: false,
+      validatesSecureCertificate: false,
+      timeout: 4000,
+      url: "https://api.parse.com/1/",
+      requestHeaders: {
+        "X-Parse-Application-Id": "APPID",
+        "X-Parse-REST-API-Key": "RESTID",
+        "Content-Type": "application/json"
+      },
+      methods: [{
+        name: "courses",
+        post: "functions/getCourses",
+        onError: function(e, callback, globalOnError){
+          console.error("There was an error getting the courses!");
+        }
+      }, {
+        name: "getVideos",
+        get: "classes/videos"
+      }, {
+        name: "getVideoById",
+        get: "classes/videos/"
+      }, {
+        name: "addVideo",
+        post: "classes/videos"
+      }],
+      onError: function(e, retry) {
+        console.error("There was an error connecting to the server, check your network connection and retry.");
+        retry();
+      },
+      onLoad: function(e, callback) {
+        callback(e);
+      }
+    });
+  });
+
+  it('should fetch videos', function(done) {
+    api.getVideos().then(videos => {
+      assert(Array.isArray(videos), 'Expected videos to be an array');
+      done();
+    }).catch(error => {
+      done(error);
+    });
+  });
+
+  it('should fetch video by id', function(done) {
+    api.getVideoById({ videoId: "fUAM4ZFj9X" }).then(video => {
+      assert(video, 'Expected video to be defined');
+      done();
+    }).catch(error => {
+      done(error);
+    });
+  });
+
+  it('should add a video', function(done) {
+    api.addVideo({ body: { categoryId: 1, name: "My Video" } }).then(video => {
+      assert(video, 'Expected video to be defined');
+      done();
+    }).catch(error => {
+      done(error);
+    });
+  });
+});
diff --git a/tests/node.test.js b/tests/node.test.js
new file mode 100644
index 0000000..214f525
--- /dev/null
+++ b/tests/node.test.js
@@ -0,0 +1,73 @@
+const reste = require('../reste');
+const assert = require('assert');
+
+describe('RESTe Node Tests', function() {
+  let api;
+
+  beforeEach(function() {
+    api = new reste();
+    api.config({
+      debug: true,
+      errorsAsObjects: true,
+      autoValidateParams: false,
+      validatesSecureCertificate: false,
+      timeout: 4000,
+      url: "https://api.parse.com/1/",
+      requestHeaders: {
+        "X-Parse-Application-Id": "APPID",
+        "X-Parse-REST-API-Key": "RESTID",
+        "Content-Type": "application/json"
+      },
+      methods: [{
+        name: "courses",
+        post: "functions/getCourses",
+        onError: function(e, callback, globalOnError){
+          console.error("There was an error getting the courses!");
+        }
+      }, {
+        name: "getVideos",
+        get: "classes/videos"
+      }, {
+        name: "getVideoById",
+        get: "classes/videos/"
+      }, {
+        name: "addVideo",
+        post: "classes/videos"
+      }],
+      onError: function(e, retry) {
+        console.error("There was an error connecting to the server, check your network connection and retry.");
+        retry();
+      },
+      onLoad: function(e, callback) {
+        callback(e);
+      }
+    });
+  });
+
+  it('should fetch videos', function(done) {
+    api.getVideos().then(videos => {
+      assert(Array.isArray(videos), 'Expected videos to be an array');
+      done();
+    }).catch(error => {
+      done(error);
+    });
+  });
+
+  it('should fetch video by id', function(done) {
+    api.getVideoById({ videoId: "fUAM4ZFj9X" }).then(video => {
+      assert(video, 'Expected video to be defined');
+      done();
+    }).catch(error => {
+      done(error);
+    });
+  });
+
+  it('should add a video', function(done) {
+    api.addVideo({ body: { categoryId: 1, name: "My Video" } }).then(video => {
+      assert(video, 'Expected video to be defined');
+      done();
+    }).catch(error => {
+      done(error);
+    });
+  });
+});
diff --git a/tests/react.test.js b/tests/react.test.js
new file mode 100644
index 0000000..73c3f69
--- /dev/null
+++ b/tests/react.test.js
@@ -0,0 +1,73 @@
+const reste = require('../reste');
+const assert = require('assert');
+
+describe('RESTe React Tests', function() {
+  let api;
+
+  beforeEach(function() {
+    api = new reste();
+    api.config({
+      debug: true,
+      errorsAsObjects: true,
+      autoValidateParams: false,
+      validatesSecureCertificate: false,
+      timeout: 4000,
+      url: "https://api.parse.com/1/",
+      requestHeaders: {
+        "X-Parse-Application-Id": "APPID",
+        "X-Parse-REST-API-Key": "RESTID",
+        "Content-Type": "application/json"
+      },
+      methods: [{
+        name: "courses",
+        post: "functions/getCourses",
+        onError: function(e, callback, globalOnError){
+          console.error("There was an error getting the courses!");
+        }
+      }, {
+        name: "getVideos",
+        get: "classes/videos"
+      }, {
+        name: "getVideoById",
+        get: "classes/videos/"
+      }, {
+        name: "addVideo",
+        post: "classes/videos"
+      }],
+      onError: function(e, retry) {
+        console.error("There was an error connecting to the server, check your network connection and retry.");
+        retry();
+      },
+      onLoad: function(e, callback) {
+        callback(e);
+      }
+    });
+  });
+
+  it('should fetch videos', function(done) {
+    api.getVideos().then(videos => {
+      assert(Array.isArray(videos), 'Expected videos to be an array');
+      done();
+    }).catch(error => {
+      done(error);
+    });
+  });
+
+  it('should fetch video by id', function(done) {
+    api.getVideoById({ videoId: "fUAM4ZFj9X" }).then(video => {
+      assert(video, 'Expected video to be defined');
+      done();
+    }).catch(error => {
+      done(error);
+    });
+  });
+
+  it('should add a video', function(done) {
+    api.addVideo({ body: { categoryId: 1, name: "My Video" } }).then(video => {
+      assert(video, 'Expected video to be defined');
+      done();
+    }).catch(error => {
+      done(error);
+    });
+  });
+});