Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -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"
},
Expand All @@ -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",
Expand Down
158 changes: 148 additions & 10 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Collection src="etc"/> 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 <Collection src="etc"/> 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**

Expand Down Expand Up @@ -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!)

Expand Down Expand Up @@ -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:
Expand All @@ -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"
});
```
Expand All @@ -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();
}
});
```
Expand All @@ -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,
Expand All @@ -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/<videoId>"
}, {
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/<videoId>"
}, {
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

<pre>
Expand Down
22 changes: 11 additions & 11 deletions reste.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
});
}
};
Expand Down Expand Up @@ -447,15 +447,15 @@ 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];
});

if (options.success) options.success(response);

Alloy.Collections[collectionConfig.name].trigger('sync');
reste.Collections[collectionConfig.name].trigger('sync');
}
}
}, (response) => {
Expand Down
73 changes: 73 additions & 0 deletions tests/alloy.test.js
Original file line number Diff line number Diff line change
@@ -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/<videoId>"
}, {
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);
});
});
});
Loading