diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..044bad5c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,24 @@
+*Switch to the Preview tab to read this message in the Github issue form.*
+
+The project lacks resources to answer questions regarding general usage and problems occurring from misconfiguration and or improper use. That's why we kindly ask you to following the following guidelines when considering opening an issue.
+
+**Before opening an issue:**
+
+* Please **carefully read the README** and make sure that your question isn't already answered in the documentation.
+* Please **review [the configuration methods](https://github.com/mgonto/restangular#configuring-restangular) and [methods description](https://github.com/mgonto/restangular#methods-description)** once more.
+* Please **search the existing issue list** to see if there's already an issue **open or closed** that solves your problem.
+* Please **search for and/or ask your question on [StackOverflow](http://stackoverflow.com/search?q=restangular)**. You will get answers more quickly there than here.
+* Please **debug your problem long and hard** enough to make sure the issue is in Restangular and not in your code and/or backend.
+
+**When opening an issue:**
+
+* Please **create a live Plunker** (fork [this Plunker](http://plnkr.co/edit/26Heuv5F6hUgxpNWNTee?p=info)) demonstrating the problem, use [httpbin](https://httpbin.org/) or [JSONPlaceholder](https://jsonplaceholder.typicode.com/) or similar as backend if needed.
+* Please **write your issue in a clear and succint manner**, so that all the details of your issue are easy to understand and fully explained.
+* Please **enclose your code in [fenced blocks](https://help.github.com/articles/creating-and-highlighting-code-blocks/)** for readability.
+
+The maintainers may opt to close issues that don't follow these guidelines.
+
+**Also remember:**
+
+* Pull requests fixing problems are highly appreciated ;)
+
diff --git a/.gitignore b/.gitignore
index 79787e35..e8a68eb0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ results
lib
node_modules
components
+coverage
bower_components
npm-debug.log
.idea
diff --git a/.travis.yml b/.travis.yml
index ad563755..20b0f596 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,11 @@
language: node_js
node_js:
- - "0.10"
+ - "7.0"
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- 'npm install -g bower grunt-cli'
- 'bower install --config.interactive=false'
+
+script: grunt travis
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7420402c..b768c53e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,22 +1,77 @@
-1.5.2 / 2016-02-15
-==================
-
- * Change _.contains to _.includes for compatability with lodash >= 4.0
-
-1.5.1 / 2015-04-03
-==================
-
- * Release 1.5.0
- * Updated zip
- * Merge branch 'master' of github.com:mgonto/restangular
- * Merge pull request #1081 from rajeshwarpatlolla/develop
- * Merge pull request #1079 from wching/master
- * change in README file
- * url modified for 'Chain methods together to easily build complex requests'
- * Update README.md
- * Update README.md
-
-1.5.0 / 2015-04-03
-==================
+
+## 1.6.1 (2017-01-06)
+
+### Bug fixes
+
+* fix(copy) Collections are now copied/cloned properly ([c92b138](https://github.com/mgonto/restangular/commit/c92b138))
+* fix(copy) Copying collections now correctly sets route, fromServer and parent on the copy ([7fd668b](https://github.com/mgonto/restangular/commit/7fd668b))
+* fix(elementTransformer) matchTransformer now doesn't throw if route is undefined ([fb242ae](https://github.com/mgonto/restangular/commit/fb242ae))
+
+### Docs
+
+* chore(docs): update contribution guidelines ([c49ca45](https://github.com/mgonto/restangular/commit/c49ca45))
+* fix(docs): add link to david-dm.org from badge ([2bfb745](https://github.com/mgonto/restangular/commit/2bfb745))
+* chore(docs) Add new example production site ([2596035](https://github.com/mgonto/restangular/commit/2596035))
+* chore(docs) Add note about pull requests and github preview tab ([6883075](https://github.com/mgonto/restangular/commit/6883075))
+* chore(docs) Apply automatic formatting to code and spec ([bc16122](https://github.com/mgonto/restangular/commit/bc16122))
+* chore(docs) Reformat changelog, add unreleased section ([8bfa685](https://github.com/mgonto/restangular/commit/8bfa685))
+* chore(docs) Update issue guidelines to include StackOverflow as source for solutions to problems ([34b0e9a](https://github.com/mgonto/restangular/commit/34b0e9a))
+* chore(docs) Update link to demo Plunker, rephrase ([7c30615](https://github.com/mgonto/restangular/commit/7c30615))
+* chore(test) fix jshint errors in spec file ([1a988cb](https://github.com/mgonto/restangular/commit/1a988cb))
+* feat(docs) Add FAQ about cancelling request ([8552c51](https://github.com/mgonto/restangular/commit/8552c51)), closes [#926](https://github.com/mgonto/restangular/issues/926) [#1145](https://github.com/mgonto/restangular/issues/1145) [#1377](https://github.com/mgonto/restangular/issues/1377) [#1391](https://github.com/mgonto/restangular/issues/1391)
+
+### Other
+
+* chore(changelog): upgrade package and config ([58caacd](https://github.com/mgonto/restangular/commit/58caacd))
+* chore(dependencies): Update lodash version to ~4.17.0 as in unit tests ([e0b68a0](https://github.com/mgonto/restangular/commit/e0b68a0))
+* chore(deps): upgrade dev dependencies, fix tests (#1450) ([b583197](https://github.com/mgonto/restangular/commit/b583197)), closes [#1450](https://github.com/mgonto/restangular/issues/1450)
+* chore(travis): change travis script and include coveralls ([ca9856a](https://github.com/mgonto/restangular/commit/ca9856a))
+* test(coverage): add coverage and coveralls.io integration ([fdd5de6](https://github.com/mgonto/restangular/commit/fdd5de6))
+* Update dist files ([7c245a2](https://github.com/mgonto/restangular/commit/7c245a2))
+
+
+## 1.6.0 (2016-12-25)
+
+* Url now supports unescaped suffix (0350bcd)
+* Added Restangular Plunkr example (c4ef002)
+* Now id can be a nested property (a94228b)
+* Add withHttpConfig to objects created with .service (e8f7295)
+* Add support for angularjs dependency injection using commonjs require syntax (f02db83)
+* Fix missing 'get' in decoupled service (8096ce1)
+* Avoid restangularizing an undefined element in restangularizeCollecti onAndElements. (0f8b562)
+* Fixes #1167: Extend condition to treat '0, which as a falsy value currently fails, as a valid ID (95ea231)
+* Add customPatch method (01297fe)
+* Added UMD snippet (caab5e6)
+* Support BaseUrl with athority without schema (5f3eacb)
+* Add ability to restangularize a collection with fromServer set (51066ec)
+* Add configuration option to use plain() by default (94ffaf0)
+* Fix fromServer param while copying (b53f4b6)
+* Rename CONTRIBUTE.md to CONTRIBUTING.md in accordance with GitHub's spec (c17df47)
+* Remove moot `version` property from bower.json (1a585f3)
+* Add more realistic POST response for accounts, with id (#943) (11fb475)
+* Added context/explanation of when to use JSONP. (fec9b27)
+* Add regexp matching for route to element transformers (#1430) (de8f561)
+
+
+## 1.5.2 (2016-02-15)
+
+* Change \_.contains to \_.includes for compatability with lodash >= 4.0
+
+
+## 1.5.1 (2015-04-03)
+
+* Release 1.5.0
+* Updated zip
+* Merge branch 'master' of github.com:mgonto/restangular
+* Merge pull request #1081 from rajeshwarpatlolla/develop
+* Merge pull request #1079 from wching/master
+* change in README file
+* url modified for 'Chain methods together to easily build complex requests'
+* Update README.md
+* Update README.md
+
+
+## 1.5.0 (2015-04-03)
+
* Tons of bug fixes
* Upgraded Lodash to 1.3.0
diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md
deleted file mode 100644
index 8ccfef3b..00000000
--- a/CONTRIBUTE.md
+++ /dev/null
@@ -1,31 +0,0 @@
-##Install env
-
-In order to contribute just git clone the repository and then run:
-
-```
-git clone git@github.com:mgonto/restangular.git
-cd restangular
-npm install grunt-cli --global
-npm install
-```
-
-Be sure to have PhantomJS installed as Karma tests use it. Otherwise, in Mac just run
-
-```
-brew install phantomjs
-```
-
-All changes must be done in src/restangular.js
-
-##Branching
-
-Please submit a Pull Request or create issues for anything you want :). If your code is super small, nobody will blame
-you for doing it from master to master. Otherwise, please try to create a branch `features/[name of your awesome feature]`.
-
-##Testing and styling
-
-Before submiting any PR, you should run `grunt test` to validate your didn't break anything. If you just added a new
-feature, please consider also adding tests for it. And when you're done with your code, run `grunt jshint` to check
-if you code follow the same simple coding design as the rest of the project.
-
-Thanks!
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..59451f29
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,67 @@
+# Issues
+
+Read the [issue guidlines](.github/ISSUE_TEMPLATE.md) before opening an issue!
+
+# How to submit PRs
+
+## Install env
+
+In order to contribute just create your own [fork](https://help.github.com/articles/fork-a-repo/)
+of the repository and then run:
+
+```
+git clone git@github.com:/restangular.git
+cd restangular
+npm install grunt-cli --global
+npm install
+```
+
+## Create a branch
+
+Create a branch for your code changes
+
+`git checkout -b my_awesome_fix`
+
+## Write tests
+
+When making changes to the code, please always **write a test case** for
+your topic before making the change. Watch the test fail, then
+implement the change and watch the test succeed.
+
+Tests are run with `grunt test`.
+
+## Keep the style
+
+When you're done with your code, run `grunt jshint` to check
+if you code follow the same simple coding design as the rest of the project.
+Consider integrating `jshint` in your editor to get real time feedback on your
+style as you're coding.
+
+## Commit message format
+
+Please write your commit messages in the [angular conventional changelog](https://github.com/conventional-changelog/conventional-changelog-angular/blob/master/convention.md) format. This will help
+us to keep a decent [CHANGELOG](CHANGELOG.md) with minimum effort. Check previous commit
+messages for examples.
+
+## Update docs
+
+If your code change includes new features, please include an update to the [README.md](README.md)
+explaining your feature.
+
+**Don't**, however, generate distribution files (the files in [dist](dist)). This will be done
+on a regular basis by maintainers as PRs are merged.
+
+## Squash commits
+
+Please consider squasing the commits in your topic branch into a single commit including
+all changes needed. This will make the PR cleaner and the change history more easy to follow
+after the PR has been merged. Also, the CHANGELOG will make more sense.
+
+Look [here](https://ariejan.net/2011/07/05/git-squash-your-latests-commits-into-one/) and
+[here](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits) for how to.
+
+## Sumbit the PR
+
+Now you're ready to [open a PR](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
+
+Thanks!
diff --git a/Gruntfile.js b/Gruntfile.js
index 20cbe953..13420589 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -85,9 +85,45 @@ module.exports = function(grunt) {
autoWatch: true
}
},
- changelog: {
+ coveralls: {
+ // Options relevant to all targets
options: {
- dest: 'CHANGELOG.md'
+ // When true, grunt-coveralls will only print a warning rather than
+ // an error, to prevent CI builds from failing unnecessarily (e.g. if
+ // coveralls.io is down). Optional, defaults to false.
+ force: false
+ },
+
+ restangular: {
+ // LCOV coverage file (can be string, glob or array)
+ src: 'coverage/**/lcov.info',
+ options: {
+ // Any options for just this target
+ }
+ },
+ },
+ conventionalChangelog: {
+ options: {
+ changelogOpts: {
+ // conventional-changelog options go here
+ outputUnreleased: true,
+ // preset: 'angular'
+ },
+ context: {
+ // context goes here
+ },
+ gitRawCommitsOpts: {
+ // git-raw-commits options go here
+ },
+ parserOpts: {
+ // conventional-commits-parser options go here
+ },
+ writerOpts: {
+ // conventional-changelog-writer options go here
+ }
+ },
+ release: {
+ src: 'CHANGELOG.md'
}
}
});
@@ -111,6 +147,8 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-zip');
+ grunt.loadNpmTasks('grunt-coveralls');
+
// Default task.
grunt.registerTask('default', ['build']);
@@ -122,7 +160,9 @@ module.exports = function(grunt) {
grunt.registerTask('test-debug', ['karma:debug']);
- grunt.registerTask('travis', ['karma:travis', 'karma:travisUnderscore']);
+ grunt.registerTask('travis', ['karma:travis', 'karma:travisUnderscore', 'coveralls']);
+
+ grunt.registerTask('changelog', ['conventionalChangelog']);
// Provides the "bump" task.
grunt.registerTask('bump', 'Increment version number', function() {
@@ -142,7 +182,6 @@ module.exports = function(grunt) {
grunt.file.write(file, JSON.stringify(json, null, ' '));
}
updateFile('package.json');
- updateFile('bower.json');
grunt.log.ok('Version bumped to ' + version);
});
diff --git a/MAINTENANCE.md b/MAINTENANCE.md
new file mode 100644
index 00000000..6d219897
--- /dev/null
+++ b/MAINTENANCE.md
@@ -0,0 +1,45 @@
+# Restangular maintenance policy
+
+Restangular follows the [Semantic Versioning](http://semver.org/) for its releases:
+`(Major).(Minor).(Patch)`.
+
+- **Patch number**: When backwards compatible bug fixes are introduced that fix
+ incorrect behavior.
+- **Minor version**: When new, backwards compatible functionality is introduced
+ to the public API or a minor feature is introduced, or when a set of smaller
+ features is rolled out.
+- **Major version**: Whenever there is something significant or any backwards
+ incompatible changes are introduced to the public API.
+
+The current stable release will receive security patches and bug fixes
+(eg. `1.6.0` -> `1.6.1`). Feature releases will mark the next supported stable
+release where the minor version is increased numerically by increments of one
+(eg. `1.6.3 -> 1.7.0`).
+
+We encourage everyone to run the latest stable release.
+
+# Notes for maintainers
+
+Following are a set of guidelines and checklists for maintaining the
+[main repository](https://github.com/mgonto/restangular).
+
+## Keeping master usable
+
+In order for developers to use the bleeding edge master version in their projects,
+we'll need to keep the dist files up to date. After merging PRs that include mentionable
+changes, please update the dist files `grunt build` and the CHANGELOG `grunt changelog`.
+The CHANGELOG will have a section called `Unreleased` for changes not yet included in
+any release. This is the place for
+
+## Releasing
+
+Follow this checklist for publishing a new release.
+
+- [ ] Bump version `grunt bump --type=patch/minor/major`
+- [ ] Create changelog `grunt changelog` (open it in editor and make sure its sensible)
+- [ ] Make dist files `grunt build`
+- [ ] Commit changes `git add dist package.json CHANGELOG.md` and `git commit -m "chore(release): release 1.6.1"`
+- [ ] Tag release `git tag -a -m "Version 1.6.1" 1.6.1`
+- [ ] Push everything `git push` and `git push --tags`
+- [ ] Publish to NPM `npm publish`
+- [ ] Create a [new release](https://github.com/mgonto/restangular/releases) on GitHub, entering the version changelog as body
diff --git a/README.md b/README.md
index 5f6c37f8..aea15823 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,23 @@
-#Restangular
+# Restangular
[](https://travis-ci.org/mgonto/restangular)
+[](https://coveralls.io/github/mgonto/restangular?branch=master)
+[](https://david-dm.org/mgonto/restangular/?type=dev)
+[](https://snyk.io/test/github/mgonto/restangular)
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=martin%40gon%2eto&lc=US&item_name=Martin%20Gontovnikas¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted "Donate once-off to this project using Paypal")
[](https://www.gittip.com/mgonto/)
-Restangular is an AngularJS service that simplifies common GET, POST, DELETE, and UPDATE requests with a minimum of client code.
-It's a perfect fit for any WebApp that consumes data from a RESTful API.
+Restangular is an AngularJS service that simplifies common GET, POST, DELETE, and UPDATE requests with a minimum of client code. It's a perfect fit for any WebApp that consumes data from a RESTful API.
-**Check out a, [live demo on plunkr](http://plnkr.co/edit/d6yDka?p=preview).** It uses the same example as the official [Angular Javascript Project](http://angularjs.org/#wire-up-a-backend)... but Restangularized!
+**Note This version of Restangular [only supports Angular 1](#supported-angular-versions). For an Angular 2+ version of Restangular, check out [ngx-restangular](https://github.com/2muchcoffeecom/ngx-restangular).** It's a separate project with different maintainers, so issues regarding ngx-restangular should be reported [over there](https://github.com/2muchcoffeecom/ngx-restangular/issues) :wink:
-You can also **check out [a video introduction of a talk I gave at Devoxx France](http://www.parleys.com/play/535a189ee4b0c5ba17d43455/chapter1/about)** about Restangular
+Learn Restangular! Try the [live demo on plunkr](http://plnkr.co/edit/8qrGeE?p=preview). It uses the same example as the official [Angular Javascript Project](http://angularjs.org/#wire-up-a-backend), but with Restangular! Or watch [a video introduction of a talk I gave at Devoxx France](http://www.parleys.com/play/535a189ee4b0c5ba17d43455/chapter1/about) about Restangular.
-#Table of contents
+
+# Table of contents
- [Restangular](#restangular)
- [Differences with $resource](#differences-with-resource)
@@ -41,6 +44,7 @@ You can also **check out [a video introduction of a talk I gave at Devoxx France
- [setRequestInterceptor](#setrequestinterceptor)
- [addRequestInterceptor](#addrequestinterceptor)
- [setFullRequestInterceptor](#setfullrequestinterceptor)
+ - [addFullRequestInterceptor](#addfullrequestinterceptor)
- [setErrorInterceptor](#seterrorinterceptor)
- [setRestangularFields](#setrestangularfields)
- [setMethodOverriders](#setmethodoverriders)
@@ -83,6 +87,7 @@ You can also **check out [a video introduction of a talk I gave at Devoxx France
- [How can I access the unrestangularized element as well as the restangularized one?](#how-can-i-access-the-unrestangularized-element-as-well-as-the-restangularized-one)
- [Restangular fails with status code 0](#restangular-fails-with-status-code-0)
- [Why does this depend on Lodash / Underscore?](#why-does-this-depend-on-lodash--underscore)
+ - [How do I cancel a request?](#how-do-i-cancel-a-request)
- [Supported Angular versions](#supported-angular-versions)
- [Server Frameworks](#server-frameworks)
- [Releases Notes](#releases-notes)
@@ -133,7 +138,7 @@ $scope.user.one('messages', 123).one('from', 123).getList('unread');
**[Back to top](#table-of-contents)**
-#How do I add this to my project?
+## How do I add this to my project?
You can download this by:
@@ -144,13 +149,13 @@ You can download this by:
````html
-
-
+
+
````
**[Back to top](#table-of-contents)**
-#Dependencies
+## Dependencies
Restangular depends on Angular and Lodash (or Underscore).
@@ -163,10 +168,12 @@ Each time, there're more Production WebApps using `Restangular`. If your webapp
* **Life360** is using Restangular to build the WebApp version of their platform
* **Thomson Reuters** is using Restangular for the new Webapp they've built
* **Quran.com** is using Restangular for their alpha/beta app and soon to be main site
+* **[Worldcampus.co](http://www.worldcampus.co)** is using Restangular for their beta international students social network.
+* **[ENTSO-E Transparency Platform](https://transparency.entsoe.eu)**
**[Back to top](#table-of-contents)**
-#Starter Guide
+# Starter Guide
## Quick Configuration (For Lazy Readers)
This is all you need to start using all the basic Restangular features.
@@ -370,7 +377,7 @@ This is a hook. After each element has been "restangularized" (Added the new met
**I favor the usage of `addElementTransformer` instead of `onElemRestangularized` whenever possible as the implementation is much cleaner.**
-This callback is a function that has 3 parameters:
+This callback is a function that has 4 parameters:
* **elem**: The element that has just been restangularized. Can be a collection or a single element.
* **isCollection**: Boolean indicating if this is a collection or a single element.
@@ -459,7 +466,7 @@ Restangular.setErrorInterceptor(function(response, deferred, responseHandler) {
#### setRestangularFields
-Restangular required 3 fields for every "Restangularized" element. These are:
+Restangular requires 7 fields for every "Restangularized" element. These are:
* id: Id of the element. Default: id
* route: Name of the route of this element. Default: route
@@ -671,7 +678,8 @@ app.controller('MainCtrl', function(Restangular, BingRestangular) {
### Decoupled Restangular Service
-There're some times where you want to use Restangular but you don't want to expose Restangular object anywhere. For those cases, you can actually use the `service` feature of Restangular.
+There're some times where you want to use Restangular but you don't want to expose Restangular object anywhere.
+For those cases, you can actually use the `service` feature of Restangular.
Let's see how it works:
@@ -682,6 +690,7 @@ module.factory('Users', function(Restangular) {
});
// In your controller you inject Users
+Users.get(2) // GET to /users/2
Users.one(2).get() // GET to /users/2
Users.post({data}) // POST to /users
@@ -694,6 +703,21 @@ Users.getList().then(function(users) {
})
````
+You can also use ```withHttpConfig``` on objects created by ```Restangular.service```.
+
+```js
+var personService = Restangular.service('person');
+var entity = personService.withHttpConfig({transformRequest: function(data) {
+ data.fullname = data.firstname + ' ' + data.lastname;
+ return JSON.stringify(data);
+}}).post({
+ "lastname": "Mueller",
+ "firstname": "Gerd"
+}).then(function(resp) {
+ console.log(resp);
+});
+```
+
We can also use Nested RESTful resources with this:
````js
@@ -715,8 +739,8 @@ These are the methods that can be called on the Restangular object.
* **oneUrl(route, url)**: This will create a new Restangular object that is just a pointer to one element with the specified URL.
* **allUrl(route, url)**: This creates a Restangular object that is just a pointer to a list at the specified URL.
* **copy(fromElement)**: This will create a copy of the from element so that we can modify the copied one.
-* **restangularizeElement(parent, element, route, queryParams)**: Restangularizes a new element
-* **restangularizeCollection(parent, element, route, queryParams)**: Restangularizes a new collection
+* **restangularizeElement(parent, element, route, fromServer, collection, reqParams)**: Restangularizes a new element
+* **restangularizeCollection(parent, element, route, fromServer, reqParams)**: Restangularizes a new collection
**[Back to top](#table-of-contents)**
@@ -747,7 +771,7 @@ These are the methods that can be called on the Restangular object.
### Collection methods
* **getList([queryParams, headers]): Gets itself again (Remember this is a collection)**.
-* **get([id]): Gets one item from the collection by id**.
+* **get(id): Gets one item from the collection by id**.
* **post(elementToPost, [queryParams, headers])**: Creates a new element of this collection.
* **head([queryParams, headers])**: Does a HEAD
* **trace: ([queryParams, headers])**: Does a TRACE
@@ -792,7 +816,7 @@ All custom methods have an alias where you replace `custom` by `do`. For example
**[Back to top](#table-of-contents)**
## Copying elements
-Before modifying an object, we sometimes want to copy it and then modify the copied object. We can't use `angular.copy` for this because it'll not change the `this` binded in the functions we add to the object. In this cases, you must use `Restangular.copy(fromElement)`.
+Before modifying an object, we sometimes want to copy it and then modify the copied object. We can't use `angular.copy` for this because it'll not change the `this` bound in the functions we add to the object. In this cases, you must use `Restangular.copy(fromElement)`.
**[Back to top](#table-of-contents)**
@@ -1287,9 +1311,27 @@ So, why not use it? If you've never heard of them, by using Restangular, you cou
**[Back to top](#table-of-contents)**
+#### How do I cancel a request?
+
+Sometimes you may wish to cancel a request, this is how you would do it:
+
+```
+var canceler = $q.defer();
+Restangular.all('users').withHttpConfig({timeout: canceler.promise}).get();
+canceler.resolve(); // cancels the request
+```
+
+This is a little counterintuitive, so let me explain. Restangular is built on top of `$http`, which takes a timeout parameter. As per the $http docs:
+
+ timeout in milliseconds, or promise that should abort the request when resolved.
+
+Resolving the promise (canceler in this case), will cancel the request.
+
+**[Back to top](#table-of-contents)**
+
# Supported Angular versions
-Restangular supports all angular versions including 1.0.X, 1.1.X and 1.2.X (1.2.4 being the current at the time)
+Restangular supports all Angular versions from 1.0.X - 1.5.X
Also, when using Restangular with version >= 1.1.4, in case you're using Restangular inside a callback not handled by Angular, you have to wrap the whole request with `$scope.apply` to make it work or you need to run one extra `$digest` manually. Check out https://github.com/mgonto/restangular/issues/71
diff --git a/bower.json b/bower.json
index cf036a0b..ee38a810 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,5 @@
{
"name": "restangular",
- "version": "1.5.2",
"main": "./dist/restangular.js",
"description": "Restful Resources service for AngularJS apps",
"repository": {
@@ -8,7 +7,7 @@
"url": "git://github.com/mgonto/restangular.git"
},
"dependencies": {
- "lodash": ">=1.3.0",
+ "lodash": "~4.17.0",
"angular": "~1.x"
},
"ignore": [
@@ -16,4 +15,4 @@
"components",
"lib"
]
-}
\ No newline at end of file
+}
diff --git a/dist/restangular.js b/dist/restangular.js
index dd2e369e..b59b25f9 100644
--- a/dist/restangular.js
+++ b/dist/restangular.js
@@ -1,1355 +1,1452 @@
/**
* Restful Resources service for AngularJS apps
- * @version v1.5.2 - 2016-02-08 * @link https://github.com/mgonto/restangular
+ * @version v1.6.1 - 2017-01-06 * @link https://github.com/mgonto/restangular
* @author Martin Gontovnikas
* @license MIT License, http://www.opensource.org/licenses/MIT
- */(function() {
-
-var restangular = angular.module('restangular', []);
-
-restangular.provider('Restangular', function() {
- // Configuration
- var Configurer = {};
- Configurer.init = function(object, config) {
- object.configuration = config;
-
- /**
- * Those are HTTP safe methods for which there is no need to pass any data with the request.
- */
- var safeMethods= ['get', 'head', 'options', 'trace', 'getlist'];
- config.isSafe = function(operation) {
- return _.includes(safeMethods, operation.toLowerCase());
- };
+ */(function(root, factory) {
+ /* global define, require */
+ // https://github.com/umdjs/umd/blob/master/templates/returnExports.js
+ if (typeof define === 'function' && define.amd) {
+ define(['lodash', 'angular'], factory);
+ } else if (typeof module === 'object' && module.exports) {
+ module.exports = factory(require('lodash'), require('angular'));
+ } else {
+ // No global export, Restangular will register itself as Angular.js module
+ factory(root._, root.angular);
+ }
+}(this, function(_, angular) {
+
+ var restangular = angular.module('restangular', []);
+
+ restangular.provider('Restangular', function() {
+ // Configuration
+ var Configurer = {};
+ Configurer.init = function(object, config) {
+ object.configuration = config;
+
+ /**
+ * Those are HTTP safe methods for which there is no need to pass any data with the request.
+ */
+ var safeMethods = ['get', 'head', 'options', 'trace', 'getlist'];
+ config.isSafe = function(operation) {
+ return _.includes(safeMethods, operation.toLowerCase());
+ };
- var absolutePattern = /^https?:\/\//i;
- config.isAbsoluteUrl = function(string) {
- return _.isUndefined(config.absoluteUrl) || _.isNull(config.absoluteUrl) ?
- string && absolutePattern.test(string) :
- config.absoluteUrl;
- };
+ var absolutePattern = /^https?:\/\//i;
+ config.isAbsoluteUrl = function(string) {
+ return _.isUndefined(config.absoluteUrl) || _.isNull(config.absoluteUrl) ?
+ string && absolutePattern.test(string) :
+ config.absoluteUrl;
+ };
- config.absoluteUrl = _.isUndefined(config.absoluteUrl) ? true : config.absoluteUrl;
- object.setSelfLinkAbsoluteUrl = function(value) {
- config.absoluteUrl = value;
- };
- /**
- * This is the BaseURL to be used with Restangular
- */
- config.baseUrl = _.isUndefined(config.baseUrl) ? '' : config.baseUrl;
- object.setBaseUrl = function(newBaseUrl) {
- config.baseUrl = /\/$/.test(newBaseUrl) ?
- newBaseUrl.substring(0, newBaseUrl.length-1) :
- newBaseUrl;
- return this;
- };
+ config.absoluteUrl = _.isUndefined(config.absoluteUrl) ? true : config.absoluteUrl;
+ object.setSelfLinkAbsoluteUrl = function(value) {
+ config.absoluteUrl = value;
+ };
+ /**
+ * This is the BaseURL to be used with Restangular
+ */
+ config.baseUrl = _.isUndefined(config.baseUrl) ? '' : config.baseUrl;
+ object.setBaseUrl = function(newBaseUrl) {
+ config.baseUrl = /\/$/.test(newBaseUrl) ?
+ newBaseUrl.substring(0, newBaseUrl.length - 1) :
+ newBaseUrl;
+ return this;
+ };
- /**
- * Sets the extra fields to keep from the parents
- */
- config.extraFields = config.extraFields || [];
- object.setExtraFields = function(newExtraFields) {
- config.extraFields = newExtraFields;
- return this;
- };
+ /**
+ * Sets the extra fields to keep from the parents
+ */
+ config.extraFields = config.extraFields || [];
+ object.setExtraFields = function(newExtraFields) {
+ config.extraFields = newExtraFields;
+ return this;
+ };
- /**
- * Some default $http parameter to be used in EVERY call
- **/
- config.defaultHttpFields = config.defaultHttpFields || {};
- object.setDefaultHttpFields = function(values) {
- config.defaultHttpFields = values;
- return this;
- };
+ /**
+ * Some default $http parameter to be used in EVERY call
+ **/
+ config.defaultHttpFields = config.defaultHttpFields || {};
+ object.setDefaultHttpFields = function(values) {
+ config.defaultHttpFields = values;
+ return this;
+ };
- config.withHttpValues = function(httpLocalConfig, obj) {
- return _.defaults(obj, httpLocalConfig, config.defaultHttpFields);
- };
+ /**
+ * Always return plain data, no restangularized object
+ **/
+ config.plainByDefault = config.plainByDefault || false;
+ object.setPlainByDefault = function(value) {
+ config.plainByDefault = value === true ? true : false;
+ return this;
+ };
- config.encodeIds = _.isUndefined(config.encodeIds) ? true : config.encodeIds;
- object.setEncodeIds = function(encode) {
- config.encodeIds = encode;
- };
+ config.withHttpValues = function(httpLocalConfig, obj) {
+ return _.defaults(obj, httpLocalConfig, config.defaultHttpFields);
+ };
- config.defaultRequestParams = config.defaultRequestParams || {
- get: {},
- post: {},
- put: {},
- remove: {},
- common: {}
- };
+ config.encodeIds = _.isUndefined(config.encodeIds) ? true : config.encodeIds;
+ object.setEncodeIds = function(encode) {
+ config.encodeIds = encode;
+ };
+
+ config.defaultRequestParams = config.defaultRequestParams || {
+ get: {},
+ post: {},
+ put: {},
+ remove: {},
+ common: {}
+ };
- object.setDefaultRequestParams = function(param1, param2) {
- var methods = [],
+ object.setDefaultRequestParams = function(param1, param2) {
+ var methods = [],
params = param2 || param1;
- if (!_.isUndefined(param2)) {
- if (_.isArray(param1)) {
- methods = param1;
+ if (!_.isUndefined(param2)) {
+ if (_.isArray(param1)) {
+ methods = param1;
+ } else {
+ methods.push(param1);
+ }
} else {
- methods.push(param1);
+ methods.push('common');
}
- } else {
- methods.push('common');
- }
- _.each(methods, function (method) {
- config.defaultRequestParams[method] = params;
- });
- return this;
- };
+ _.each(methods, function(method) {
+ config.defaultRequestParams[method] = params;
+ });
+ return this;
+ };
- object.requestParams = config.defaultRequestParams;
+ object.requestParams = config.defaultRequestParams;
- config.defaultHeaders = config.defaultHeaders || {};
- object.setDefaultHeaders = function(headers) {
- config.defaultHeaders = headers;
- object.defaultHeaders = config.defaultHeaders;
- return this;
- };
-
- object.defaultHeaders = config.defaultHeaders;
+ config.defaultHeaders = config.defaultHeaders || {};
+ object.setDefaultHeaders = function(headers) {
+ config.defaultHeaders = headers;
+ object.defaultHeaders = config.defaultHeaders;
+ return this;
+ };
- /**
- * Method overriders will set which methods are sent via POST with an X-HTTP-Method-Override
- **/
- config.methodOverriders = config.methodOverriders || [];
- object.setMethodOverriders = function(values) {
- var overriders = _.extend([], values);
- if (config.isOverridenMethod('delete', overriders)) {
- overriders.push('remove');
- }
- config.methodOverriders = overriders;
- return this;
- };
+ object.defaultHeaders = config.defaultHeaders;
- config.jsonp = _.isUndefined(config.jsonp) ? false : config.jsonp;
- object.setJsonp = function(active) {
- config.jsonp = active;
- };
+ /**
+ * Method overriders will set which methods are sent via POST with an X-HTTP-Method-Override
+ **/
+ config.methodOverriders = config.methodOverriders || [];
+ object.setMethodOverriders = function(values) {
+ var overriders = _.extend([], values);
+ if (config.isOverridenMethod('delete', overriders)) {
+ overriders.push('remove');
+ }
+ config.methodOverriders = overriders;
+ return this;
+ };
- config.isOverridenMethod = function(method, values) {
- var search = values || config.methodOverriders;
- return !_.isUndefined(_.find(search, function(one) {
- return one.toLowerCase() === method.toLowerCase();
- }));
- };
+ config.jsonp = _.isUndefined(config.jsonp) ? false : config.jsonp;
+ object.setJsonp = function(active) {
+ config.jsonp = active;
+ };
- /**
- * Sets the URL creator type. For now, only Path is created. In the future we'll have queryParams
- **/
- config.urlCreator = config.urlCreator || 'path';
- object.setUrlCreator = function(name) {
- if (!_.has(config.urlCreatorFactory, name)) {
- throw new Error('URL Path selected isn\'t valid');
- }
+ config.isOverridenMethod = function(method, values) {
+ var search = values || config.methodOverriders;
+ return !_.isUndefined(_.find(search, function(one) {
+ return one.toLowerCase() === method.toLowerCase();
+ }));
+ };
- config.urlCreator = name;
- return this;
- };
+ /**
+ * Sets the URL creator type. For now, only Path is created. In the future we'll have queryParams
+ **/
+ config.urlCreator = config.urlCreator || 'path';
+ object.setUrlCreator = function(name) {
+ if (!_.has(config.urlCreatorFactory, name)) {
+ throw new Error('URL Path selected isn\'t valid');
+ }
- /**
- * You can set the restangular fields here. The 3 required fields for Restangular are:
- *
- * id: Id of the element
- * route: name of the route of this element
- * parentResource: the reference to the parent resource
- *
- * All of this fields except for id, are handled (and created) by Restangular. By default,
- * the field values will be id, route and parentResource respectively
- */
- config.restangularFields = config.restangularFields || {
- id: 'id',
- route: 'route',
- parentResource: 'parentResource',
- restangularCollection: 'restangularCollection',
- cannonicalId: '__cannonicalId',
- etag: 'restangularEtag',
- selfLink: 'href',
- get: 'get',
- getList: 'getList',
- put: 'put',
- post: 'post',
- remove: 'remove',
- head: 'head',
- trace: 'trace',
- options: 'options',
- patch: 'patch',
- getRestangularUrl: 'getRestangularUrl',
- getRequestedUrl: 'getRequestedUrl',
- putElement: 'putElement',
- addRestangularMethod: 'addRestangularMethod',
- getParentList: 'getParentList',
- clone: 'clone',
- ids: 'ids',
- httpConfig: '_$httpConfig',
- reqParams: 'reqParams',
- one: 'one',
- all: 'all',
- several: 'several',
- oneUrl: 'oneUrl',
- allUrl: 'allUrl',
- customPUT: 'customPUT',
- customPOST: 'customPOST',
- customDELETE: 'customDELETE',
- customGET: 'customGET',
- customGETLIST: 'customGETLIST',
- customOperation: 'customOperation',
- doPUT: 'doPUT',
- doPOST: 'doPOST',
- doDELETE: 'doDELETE',
- doGET: 'doGET',
- doGETLIST: 'doGETLIST',
- fromServer: 'fromServer',
- withConfig: 'withConfig',
- withHttpConfig: 'withHttpConfig',
- singleOne: 'singleOne',
- plain: 'plain',
- save: 'save',
- restangularized: 'restangularized'
- };
- object.setRestangularFields = function(resFields) {
- config.restangularFields =
- _.extend(config.restangularFields, resFields);
- return this;
- };
+ config.urlCreator = name;
+ return this;
+ };
- config.isRestangularized = function(obj) {
- return !!obj[config.restangularFields.restangularized];
- };
+ /**
+ * You can set the restangular fields here. The 3 required fields for Restangular are:
+ *
+ * id: Id of the element
+ * route: name of the route of this element
+ * parentResource: the reference to the parent resource
+ *
+ * All of this fields except for id, are handled (and created) by Restangular. By default,
+ * the field values will be id, route and parentResource respectively
+ */
+ config.restangularFields = config.restangularFields || {
+ id: 'id',
+ route: 'route',
+ parentResource: 'parentResource',
+ restangularCollection: 'restangularCollection',
+ cannonicalId: '__cannonicalId',
+ etag: 'restangularEtag',
+ selfLink: 'href',
+ get: 'get',
+ getList: 'getList',
+ put: 'put',
+ post: 'post',
+ remove: 'remove',
+ head: 'head',
+ trace: 'trace',
+ options: 'options',
+ patch: 'patch',
+ getRestangularUrl: 'getRestangularUrl',
+ getRequestedUrl: 'getRequestedUrl',
+ putElement: 'putElement',
+ addRestangularMethod: 'addRestangularMethod',
+ getParentList: 'getParentList',
+ clone: 'clone',
+ ids: 'ids',
+ httpConfig: '_$httpConfig',
+ reqParams: 'reqParams',
+ one: 'one',
+ all: 'all',
+ several: 'several',
+ oneUrl: 'oneUrl',
+ allUrl: 'allUrl',
+ customPUT: 'customPUT',
+ customPATCH: 'customPATCH',
+ customPOST: 'customPOST',
+ customDELETE: 'customDELETE',
+ customGET: 'customGET',
+ customGETLIST: 'customGETLIST',
+ customOperation: 'customOperation',
+ doPUT: 'doPUT',
+ doPATCH: 'doPATCH',
+ doPOST: 'doPOST',
+ doDELETE: 'doDELETE',
+ doGET: 'doGET',
+ doGETLIST: 'doGETLIST',
+ fromServer: 'fromServer',
+ withConfig: 'withConfig',
+ withHttpConfig: 'withHttpConfig',
+ singleOne: 'singleOne',
+ plain: 'plain',
+ save: 'save',
+ restangularized: 'restangularized'
+ };
+ object.setRestangularFields = function(resFields) {
+ config.restangularFields =
+ _.extend(config.restangularFields, resFields);
+ return this;
+ };
- config.setFieldToElem = function(field, elem, value) {
- var properties = field.split('.');
- var idValue = elem;
- _.each(_.initial(properties), function(prop) {
- idValue[prop] = {};
- idValue = idValue[prop];
- });
- idValue[_.last(properties)] = value;
- return this;
- };
+ config.isRestangularized = function(obj) {
+ return !!obj[config.restangularFields.restangularized];
+ };
- config.getFieldFromElem = function(field, elem) {
- var properties = field.split('.');
- var idValue = elem;
- _.each(properties, function(prop) {
- if (idValue) {
+ config.setFieldToElem = function(field, elem, value) {
+ var properties = field.split('.');
+ var idValue = elem;
+ _.each(_.initial(properties), function(prop) {
+ idValue[prop] = {};
idValue = idValue[prop];
- }
- });
- return angular.copy(idValue);
- };
-
- config.setIdToElem = function(elem, id /*, route */) {
- config.setFieldToElem(config.restangularFields.id, elem, id);
- return this;
- };
+ });
+ idValue[_.last(properties)] = value;
+ return this;
+ };
- config.getIdFromElem = function(elem) {
- return config.getFieldFromElem(config.restangularFields.id, elem);
- };
+ config.getFieldFromElem = function(field, elem) {
+ var properties = field.split('.');
+ var idValue = elem;
+ _.each(properties, function(prop) {
+ if (idValue) {
+ idValue = idValue[prop];
+ }
+ });
+ return angular.copy(idValue);
+ };
- config.isValidId = function(elemId) {
- return '' !== elemId && !_.isUndefined(elemId) && !_.isNull(elemId);
- };
+ config.setIdToElem = function(elem, id /*, route */ ) {
+ config.setFieldToElem(config.restangularFields.id, elem, id);
+ return this;
+ };
- config.setUrlToElem = function(elem, url /*, route */) {
- config.setFieldToElem(config.restangularFields.selfLink, elem, url);
- return this;
- };
+ config.getIdFromElem = function(elem) {
+ return config.getFieldFromElem(config.restangularFields.id, elem);
+ };
- config.getUrlFromElem = function(elem) {
- return config.getFieldFromElem(config.restangularFields.selfLink, elem);
- };
+ config.isValidId = function(elemId) {
+ return '' !== elemId && !_.isUndefined(elemId) && !_.isNull(elemId);
+ };
- config.useCannonicalId = _.isUndefined(config.useCannonicalId) ? false : config.useCannonicalId;
- object.setUseCannonicalId = function(value) {
- config.useCannonicalId = value;
- return this;
- };
+ config.setUrlToElem = function(elem, url /*, route */ ) {
+ config.setFieldToElem(config.restangularFields.selfLink, elem, url);
+ return this;
+ };
- config.getCannonicalIdFromElem = function(elem) {
- var cannonicalId = elem[config.restangularFields.cannonicalId];
- var actualId = config.isValidId(cannonicalId) ? cannonicalId : config.getIdFromElem(elem);
- return actualId;
- };
+ config.getUrlFromElem = function(elem) {
+ return config.getFieldFromElem(config.restangularFields.selfLink, elem);
+ };
- /**
- * Sets the Response parser. This is used in case your response isn't directly the data.
- * For example if you have a response like {meta: {'meta'}, data: {name: 'Gonto'}}
- * you can extract this data which is the one that needs wrapping
- *
- * The ResponseExtractor is a function that receives the response and the method executed.
- */
+ config.useCannonicalId = _.isUndefined(config.useCannonicalId) ? false : config.useCannonicalId;
+ object.setUseCannonicalId = function(value) {
+ config.useCannonicalId = value;
+ return this;
+ };
- config.responseInterceptors = config.responseInterceptors || [];
+ config.getCannonicalIdFromElem = function(elem) {
+ var cannonicalId = elem[config.restangularFields.cannonicalId];
+ var actualId = config.isValidId(cannonicalId) ? cannonicalId : config.getIdFromElem(elem);
+ return actualId;
+ };
- config.defaultResponseInterceptor = function(data /*, operation, what, url, response, deferred */) {
- return data;
- };
+ /**
+ * Sets the Response parser. This is used in case your response isn't directly the data.
+ * For example if you have a response like {meta: {'meta'}, data: {name: 'Gonto'}}
+ * you can extract this data which is the one that needs wrapping
+ *
+ * The ResponseExtractor is a function that receives the response and the method executed.
+ */
- config.responseExtractor = function(data, operation, what, url, response, deferred) {
- var interceptors = angular.copy(config.responseInterceptors);
- interceptors.push(config.defaultResponseInterceptor);
- var theData = data;
- _.each(interceptors, function(interceptor) {
- theData = interceptor(theData, operation,
- what, url, response, deferred);
- });
- return theData;
- };
+ config.responseInterceptors = config.responseInterceptors || [];
- object.addResponseInterceptor = function(extractor) {
- config.responseInterceptors.push(extractor);
- return this;
- };
+ config.defaultResponseInterceptor = function(data /*, operation, what, url, response, deferred */ ) {
+ return data;
+ };
- config.errorInterceptors = config.errorInterceptors || [];
- object.addErrorInterceptor = function(interceptor) {
- config.errorInterceptors.push(interceptor);
- return this;
- };
+ config.responseExtractor = function(data, operation, what, url, response, deferred) {
+ var interceptors = angular.copy(config.responseInterceptors);
+ interceptors.push(config.defaultResponseInterceptor);
+ var theData = data;
+ _.each(interceptors, function(interceptor) {
+ theData = interceptor(theData, operation,
+ what, url, response, deferred);
+ });
+ return theData;
+ };
- object.setResponseInterceptor = object.addResponseInterceptor;
- object.setResponseExtractor = object.addResponseInterceptor;
- object.setErrorInterceptor = object.addErrorInterceptor;
+ object.addResponseInterceptor = function(extractor) {
+ config.responseInterceptors.push(extractor);
+ return this;
+ };
- /**
- * Response interceptor is called just before resolving promises.
- */
+ config.errorInterceptors = config.errorInterceptors || [];
+ object.addErrorInterceptor = function(interceptor) {
+ config.errorInterceptors.push(interceptor);
+ return this;
+ };
+ object.setResponseInterceptor = object.addResponseInterceptor;
+ object.setResponseExtractor = object.addResponseInterceptor;
+ object.setErrorInterceptor = object.addErrorInterceptor;
- /**
- * Request interceptor is called before sending an object to the server.
- */
- config.requestInterceptors = config.requestInterceptors || [];
+ /**
+ * Response interceptor is called just before resolving promises.
+ */
- config.defaultInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
- return {
- element: element,
- headers: headers,
- params: params,
- httpConfig: httpConfig
- };
- };
- config.fullRequestInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
- var interceptors = angular.copy(config.requestInterceptors);
- var defaultRequest = config.defaultInterceptor(element, operation, path, url, headers, params, httpConfig);
- return _.reduce(interceptors, function(request, interceptor) {
- return _.extend(request, interceptor(request.element, operation,
- path, url, request.headers, request.params, request.httpConfig));
- }, defaultRequest);
- };
+ /**
+ * Request interceptor is called before sending an object to the server.
+ */
+ config.requestInterceptors = config.requestInterceptors || [];
- object.addRequestInterceptor = function(interceptor) {
- config.requestInterceptors.push(function(elem, operation, path, url, headers, params, httpConfig) {
+ config.defaultInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
return {
+ element: element,
headers: headers,
params: params,
- element: interceptor(elem, operation, path, url),
httpConfig: httpConfig
};
- });
- return this;
- };
+ };
- object.setRequestInterceptor = object.addRequestInterceptor;
+ config.fullRequestInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
+ var interceptors = angular.copy(config.requestInterceptors);
+ var defaultRequest = config.defaultInterceptor(element, operation, path, url, headers, params, httpConfig);
+ return _.reduce(interceptors, function(request, interceptor) {
+ return _.extend(request, interceptor(request.element, operation,
+ path, url, request.headers, request.params, request.httpConfig));
+ }, defaultRequest);
+ };
- object.addFullRequestInterceptor = function(interceptor) {
- config.requestInterceptors.push(interceptor);
- return this;
- };
+ object.addRequestInterceptor = function(interceptor) {
+ config.requestInterceptors.push(function(elem, operation, path, url, headers, params, httpConfig) {
+ return {
+ headers: headers,
+ params: params,
+ element: interceptor(elem, operation, path, url),
+ httpConfig: httpConfig
+ };
+ });
+ return this;
+ };
- object.setFullRequestInterceptor = object.addFullRequestInterceptor;
+ object.setRequestInterceptor = object.addRequestInterceptor;
- config.onBeforeElemRestangularized = config.onBeforeElemRestangularized || function(elem) {
- return elem;
- };
- object.setOnBeforeElemRestangularized = function(post) {
- config.onBeforeElemRestangularized = post;
- return this;
- };
+ object.addFullRequestInterceptor = function(interceptor) {
+ config.requestInterceptors.push(interceptor);
+ return this;
+ };
- object.setRestangularizePromiseInterceptor = function(interceptor) {
- config.restangularizePromiseInterceptor = interceptor;
- return this;
- };
+ object.setFullRequestInterceptor = object.addFullRequestInterceptor;
- /**
- * This method is called after an element has been "Restangularized".
- *
- * It receives the element, a boolean indicating if it's an element or a collection
- * and the name of the model
- *
- */
- config.onElemRestangularized = config.onElemRestangularized || function(elem) {
- return elem;
- };
- object.setOnElemRestangularized = function(post) {
- config.onElemRestangularized = post;
- return this;
- };
+ config.onBeforeElemRestangularized = config.onBeforeElemRestangularized || function(elem) {
+ return elem;
+ };
+ object.setOnBeforeElemRestangularized = function(post) {
+ config.onBeforeElemRestangularized = post;
+ return this;
+ };
- config.shouldSaveParent = config.shouldSaveParent || function() {
- return true;
- };
- object.setParentless = function(values) {
- if (_.isArray(values)) {
- config.shouldSaveParent = function(route) {
- return !_.includes(values, route);
- };
- } else if (_.isBoolean(values)) {
- config.shouldSaveParent = function() {
- return !values;
- };
- }
- return this;
- };
+ object.setRestangularizePromiseInterceptor = function(interceptor) {
+ config.restangularizePromiseInterceptor = interceptor;
+ return this;
+ };
- /**
- * This lets you set a suffix to every request.
- *
- * For example, if your api requires that for JSon requests you do /users/123.json, you can set that
- * in here.
- *
- *
- * By default, the suffix is null
- */
- config.suffix = _.isUndefined(config.suffix) ? null : config.suffix;
- object.setRequestSuffix = function(newSuffix) {
- config.suffix = newSuffix;
- return this;
- };
+ /**
+ * This method is called after an element has been "Restangularized".
+ *
+ * It receives the element, a boolean indicating if it's an element or a collection
+ * and the name of the model
+ *
+ */
+ config.onElemRestangularized = config.onElemRestangularized || function(elem) {
+ return elem;
+ };
+ object.setOnElemRestangularized = function(post) {
+ config.onElemRestangularized = post;
+ return this;
+ };
- /**
- * Add element transformers for certain routes.
- */
- config.transformers = config.transformers || {};
- object.addElementTransformer = function(type, secondArg, thirdArg) {
- var isCollection = null;
- var transformer = null;
- if (arguments.length === 2) {
- transformer = secondArg;
- } else {
- transformer = thirdArg;
- isCollection = secondArg;
- }
+ config.shouldSaveParent = config.shouldSaveParent || function() {
+ return true;
+ };
+ object.setParentless = function(values) {
+ if (_.isArray(values)) {
+ config.shouldSaveParent = function(route) {
+ return !_.includes(values, route);
+ };
+ } else if (_.isBoolean(values)) {
+ config.shouldSaveParent = function() {
+ return !values;
+ };
+ }
+ return this;
+ };
- var typeTransformers = config.transformers[type];
- if (!typeTransformers) {
- typeTransformers = config.transformers[type] = [];
- }
+ /**
+ * This lets you set a suffix to every request.
+ *
+ * For example, if your api requires that for JSon requests you do /users/123.json, you can set that
+ * in here.
+ *
+ *
+ * By default, the suffix is null
+ */
+ config.suffix = _.isUndefined(config.suffix) ? null : config.suffix;
+ object.setRequestSuffix = function(newSuffix) {
+ config.suffix = newSuffix;
+ return this;
+ };
- typeTransformers.push(function(coll, elem) {
- if (_.isNull(isCollection) || (coll === isCollection)) {
- return transformer(elem);
+ /**
+ * Add element transformers for certain routes.
+ */
+ config.transformers = config.transformers || {};
+ config.matchTransformers = config.matchTransformers || [];
+ object.addElementTransformer = function(type, secondArg, thirdArg) {
+ var isCollection = null;
+ var transformer = null;
+ if (arguments.length === 2) {
+ transformer = secondArg;
+ } else {
+ transformer = thirdArg;
+ isCollection = secondArg;
}
- return elem;
- });
- return object;
- };
+ var transformerFn = function(coll, elem) {
+ if (_.isNull(isCollection) || (coll === isCollection)) {
+ return transformer(elem);
+ }
+ return elem;
+ };
- object.extendCollection = function(route, fn) {
- return object.addElementTransformer(route, true, fn);
- };
+ if (_.isRegExp(type)) {
+ config.matchTransformers.push({
+ regexp: type,
+ transformer: transformerFn
+ });
+ } else {
+ if (!config.transformers[type]) {
+ config.transformers[type] = [];
+ }
+ config.transformers[type].push(transformerFn);
+ }
- object.extendModel = function(route, fn) {
- return object.addElementTransformer(route, false, fn);
- };
+ return object;
+ };
- config.transformElem = function(elem, isCollection, route, Restangular, force) {
- if (!force && !config.transformLocalElements && !elem[config.restangularFields.fromServer]) {
- return elem;
- }
- var typeTransformers = config.transformers[route];
- var changedElem = elem;
- if (typeTransformers) {
- _.each(typeTransformers, function(transformer) {
- changedElem = transformer(isCollection, changedElem);
- });
- }
- return config.onElemRestangularized(changedElem, isCollection, route, Restangular);
- };
+ object.extendCollection = function(route, fn) {
+ return object.addElementTransformer(route, true, fn);
+ };
- config.transformLocalElements = _.isUndefined(config.transformLocalElements) ?
- false :
- config.transformLocalElements;
+ object.extendModel = function(route, fn) {
+ return object.addElementTransformer(route, false, fn);
+ };
- object.setTransformOnlyServerElements = function(active) {
- config.transformLocalElements = !active;
- };
+ config.transformElem = function(elem, isCollection, route, Restangular, force) {
+ if (!force && !config.transformLocalElements && !elem[config.restangularFields.fromServer]) {
+ return elem;
+ }
- config.fullResponse = _.isUndefined(config.fullResponse) ? false : config.fullResponse;
- object.setFullResponse = function(full) {
- config.fullResponse = full;
- return this;
- };
+ var changedElem = elem;
+ var matchTransformers = config.matchTransformers;
+ if (matchTransformers) {
+ _.each(matchTransformers, function(transformer) {
+ if (transformer.regexp.test(route)) {
+ changedElem = transformer.transformer(isCollection, changedElem);
+ }
+ });
+ }
+
+ var typeTransformers = config.transformers[route];
+ if (typeTransformers) {
+ _.each(typeTransformers, function(transformer) {
+ changedElem = transformer(isCollection, changedElem);
+ });
+ }
+ return config.onElemRestangularized(changedElem, isCollection, route, Restangular);
+ };
- //Internal values and functions
- config.urlCreatorFactory = {};
+ config.transformLocalElements = _.isUndefined(config.transformLocalElements) ?
+ false :
+ config.transformLocalElements;
- /**
- * Base URL Creator. Base prototype for everything related to it
- **/
+ object.setTransformOnlyServerElements = function(active) {
+ config.transformLocalElements = !active;
+ };
- var BaseCreator = function() {
- };
+ config.fullResponse = _.isUndefined(config.fullResponse) ? false : config.fullResponse;
+ object.setFullResponse = function(full) {
+ config.fullResponse = full;
+ return this;
+ };
- BaseCreator.prototype.setConfig = function(config) {
- this.config = config;
- return this;
- };
- BaseCreator.prototype.parentsArray = function(current) {
- var parents = [];
- while(current) {
- parents.push(current);
- current = current[this.config.restangularFields.parentResource];
- }
- return parents.reverse();
- };
+ //Internal values and functions
+ config.urlCreatorFactory = {};
+
+ /**
+ * Base URL Creator. Base prototype for everything related to it
+ **/
- function RestangularResource(config, $http, url, configurer) {
- var resource = {};
- _.each(_.keys(configurer), function(key) {
- var value = configurer[key];
+ var BaseCreator = function() {};
- // Add default parameters
- value.params = _.extend({}, value.params, config.defaultRequestParams[value.method.toLowerCase()]);
- // We don't want the ? if no params are there
- if (_.isEmpty(value.params)) {
- delete value.params;
+ BaseCreator.prototype.setConfig = function(config) {
+ this.config = config;
+ return this;
+ };
+
+ BaseCreator.prototype.parentsArray = function(current) {
+ var parents = [];
+ while (current) {
+ parents.push(current);
+ current = current[this.config.restangularFields.parentResource];
}
+ return parents.reverse();
+ };
- if (config.isSafe(value.method)) {
+ function RestangularResource(config, $http, url, configurer) {
+ var resource = {};
+ _.each(_.keys(configurer), function(key) {
+ var value = configurer[key];
- resource[key] = function() {
- return $http(_.extend(value, {
- url: url
- }));
- };
+ // Add default parameters
+ value.params = _.extend({}, value.params, config.defaultRequestParams[value.method.toLowerCase()]);
+ // We don't want the ? if no params are there
+ if (_.isEmpty(value.params)) {
+ delete value.params;
+ }
- } else {
+ if (config.isSafe(value.method)) {
- resource[key] = function(data) {
- return $http(_.extend(value, {
- url: url,
- data: data
- }));
- };
+ resource[key] = function() {
+ return $http(_.extend(value, {
+ url: url
+ }));
+ };
- }
- });
+ } else {
+
+ resource[key] = function(data) {
+ return $http(_.extend(value, {
+ url: url,
+ data: data
+ }));
+ };
- return resource;
- }
+ }
+ });
- BaseCreator.prototype.resource = function(current, $http, localHttpConfig, callHeaders, callParams, what, etag,operation) {
+ return resource;
+ }
- var params = _.defaults(callParams || {}, this.config.defaultRequestParams.common);
- var headers = _.defaults(callHeaders || {}, this.config.defaultHeaders);
+ BaseCreator.prototype.resource = function(current, $http, localHttpConfig, callHeaders, callParams, what, etag, operation) {
- if (etag) {
- if (!config.isSafe(operation)) {
- headers['If-Match'] = etag;
- } else {
- headers['If-None-Match'] = etag;
+ var params = _.defaults(callParams || {}, this.config.defaultRequestParams.common);
+ var headers = _.defaults(callHeaders || {}, this.config.defaultHeaders);
+
+ if (etag) {
+ if (!config.isSafe(operation)) {
+ headers['If-Match'] = etag;
+ } else {
+ headers['If-None-Match'] = etag;
+ }
}
- }
- var url = this.base(current);
+ var url = this.base(current);
- if (what) {
- var add = '';
- if (!/\/$/.test(url)) {
- add += '/';
+ if (what || what === 0) {
+ var add = '';
+ if (!/\/$/.test(url)) {
+ add += '/';
+ }
+ add += what;
+ url += add;
}
- add += what;
- url += add;
- }
- if (this.config.suffix &&
- url.indexOf(this.config.suffix, url.length - this.config.suffix.length) === -1 &&
- !this.config.getUrlFromElem(current)) {
+ if (this.config.suffix &&
+ url.indexOf(this.config.suffix, url.length - this.config.suffix.length) === -1 &&
+ !this.config.getUrlFromElem(current)) {
url += this.config.suffix;
- }
-
- current[this.config.restangularFields.httpConfig] = undefined;
+ }
- return RestangularResource(this.config, $http, url, {
- getList: this.config.withHttpValues(localHttpConfig,
- {method: 'GET',
- params: params,
- headers: headers}),
+ current[this.config.restangularFields.httpConfig] = undefined;
- get: this.config.withHttpValues(localHttpConfig,
- {method: 'GET',
- params: params,
- headers: headers}),
+ return RestangularResource(this.config, $http, url, {
+ getList: this.config.withHttpValues(localHttpConfig, {
+ method: 'GET',
+ params: params,
+ headers: headers
+ }),
- jsonp: this.config.withHttpValues(localHttpConfig,
- {method: 'jsonp',
- params: params,
- headers: headers}),
+ get: this.config.withHttpValues(localHttpConfig, {
+ method: 'GET',
+ params: params,
+ headers: headers
+ }),
- put: this.config.withHttpValues(localHttpConfig,
- {method: 'PUT',
- params: params,
- headers: headers}),
+ jsonp: this.config.withHttpValues(localHttpConfig, {
+ method: 'jsonp',
+ params: params,
+ headers: headers
+ }),
- post: this.config.withHttpValues(localHttpConfig,
- {method: 'POST',
- params: params,
- headers: headers}),
+ put: this.config.withHttpValues(localHttpConfig, {
+ method: 'PUT',
+ params: params,
+ headers: headers
+ }),
- remove: this.config.withHttpValues(localHttpConfig,
- {method: 'DELETE',
- params: params,
- headers: headers}),
+ post: this.config.withHttpValues(localHttpConfig, {
+ method: 'POST',
+ params: params,
+ headers: headers
+ }),
- head: this.config.withHttpValues(localHttpConfig,
- {method: 'HEAD',
- params: params,
- headers: headers}),
+ remove: this.config.withHttpValues(localHttpConfig, {
+ method: 'DELETE',
+ params: params,
+ headers: headers
+ }),
- trace: this.config.withHttpValues(localHttpConfig,
- {method: 'TRACE',
- params: params,
- headers: headers}),
+ head: this.config.withHttpValues(localHttpConfig, {
+ method: 'HEAD',
+ params: params,
+ headers: headers
+ }),
- options: this.config.withHttpValues(localHttpConfig,
- {method: 'OPTIONS',
- params: params,
- headers: headers}),
+ trace: this.config.withHttpValues(localHttpConfig, {
+ method: 'TRACE',
+ params: params,
+ headers: headers
+ }),
- patch: this.config.withHttpValues(localHttpConfig,
- {method: 'PATCH',
- params: params,
- headers: headers})
- });
- };
+ options: this.config.withHttpValues(localHttpConfig, {
+ method: 'OPTIONS',
+ params: params,
+ headers: headers
+ }),
- /**
- * This is the Path URL creator. It uses Path to show Hierarchy in the Rest API.
- * This means that if you have an Account that then has a set of Buildings, a URL to a building
- * would be /accounts/123/buildings/456
- **/
- var Path = function() {
- };
+ patch: this.config.withHttpValues(localHttpConfig, {
+ method: 'PATCH',
+ params: params,
+ headers: headers
+ })
+ });
+ };
- Path.prototype = new BaseCreator();
+ /**
+ * This is the Path URL creator. It uses Path to show Hierarchy in the Rest API.
+ * This means that if you have an Account that then has a set of Buildings, a URL to a building
+ * would be /accounts/123/buildings/456
+ **/
+ var Path = function() {};
- Path.prototype.normalizeUrl = function (url){
- var parts = /(http[s]?:\/\/)?(.*)?/.exec(url);
- parts[2] = parts[2].replace(/[\\\/]+/g, '/');
- return (typeof parts[1] !== 'undefined')? parts[1] + parts[2] : parts[2];
- };
+ Path.prototype = new BaseCreator();
- Path.prototype.base = function(current) {
- var __this = this;
- return _.reduce(this.parentsArray(current), function(acum, elem) {
- var elemUrl;
- var elemSelfLink = __this.config.getUrlFromElem(elem);
- if (elemSelfLink) {
- if (__this.config.isAbsoluteUrl(elemSelfLink)) {
- return elemSelfLink;
- } else {
- elemUrl = elemSelfLink;
- }
- } else {
- elemUrl = elem[__this.config.restangularFields.route];
+ Path.prototype.normalizeUrl = function(url) {
+ var parts = /((?:http[s]?:)?\/\/)?(.*)?/.exec(url);
+ parts[2] = parts[2].replace(/[\\\/]+/g, '/');
+ return (typeof parts[1] !== 'undefined') ? parts[1] + parts[2] : parts[2];
+ };
- if (elem[__this.config.restangularFields.restangularCollection]) {
- var ids = elem[__this.config.restangularFields.ids];
- if (ids) {
- elemUrl += '/' + ids.join(',');
- }
- } else {
- var elemId;
- if (__this.config.useCannonicalId) {
- elemId = __this.config.getCannonicalIdFromElem(elem);
+ Path.prototype.base = function(current) {
+ var __this = this;
+ return _.reduce(this.parentsArray(current), function(acum, elem) {
+ var elemUrl;
+ var elemSelfLink = __this.config.getUrlFromElem(elem);
+ if (elemSelfLink) {
+ if (__this.config.isAbsoluteUrl(elemSelfLink)) {
+ return elemSelfLink;
} else {
- elemId = __this.config.getIdFromElem(elem);
+ elemUrl = elemSelfLink;
}
+ } else {
+ elemUrl = elem[__this.config.restangularFields.route];
- if (config.isValidId(elemId) && !elem.singleOne) {
- elemUrl += '/' + (__this.config.encodeIds ? encodeURIComponent(elemId) : elemId);
+ if (elem[__this.config.restangularFields.restangularCollection]) {
+ var ids = elem[__this.config.restangularFields.ids];
+ if (ids) {
+ elemUrl += '/' + ids.join(',');
+ }
+ } else {
+ var elemId;
+ if (__this.config.useCannonicalId) {
+ elemId = __this.config.getCannonicalIdFromElem(elem);
+ } else {
+ elemId = __this.config.getIdFromElem(elem);
+ }
+
+ if (config.isValidId(elemId) && !elem.singleOne) {
+ elemUrl += '/' + (__this.config.encodeIds ? encodeURIComponent(elemId) : elemId);
+ }
}
}
- }
- acum = acum.replace(/\/$/, '') + '/' + elemUrl;
- return __this.normalizeUrl(acum);
+ acum = acum.replace(/\/$/, '') + '/' + elemUrl;
+ return __this.normalizeUrl(acum);
- }, this.config.baseUrl);
- };
+ }, this.config.baseUrl);
+ };
- Path.prototype.fetchUrl = function(current, what) {
- var baseUrl = this.base(current);
- if (what) {
- baseUrl += '/' + what;
- }
- return baseUrl;
- };
+ Path.prototype.fetchUrl = function(current, what) {
+ var baseUrl = this.base(current);
+ if (what) {
+ baseUrl += '/' + what;
+ }
+ return baseUrl;
+ };
- Path.prototype.fetchRequestedUrl = function(current, what) {
- var url = this.fetchUrl(current, what);
- var params = current[config.restangularFields.reqParams];
-
- // From here on and until the end of fetchRequestedUrl,
- // the code has been kindly borrowed from angular.js
- // The reason for such code bloating is coherence:
- // If the user were to use this for cache management, the
- // serialization of parameters would need to be identical
- // to the one done by angular for cache keys to match.
- function sortedKeys(obj) {
- var keys = [];
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- keys.push(key);
+ Path.prototype.fetchRequestedUrl = function(current, what) {
+ var url = this.fetchUrl(current, what);
+ var params = current[config.restangularFields.reqParams];
+
+ // From here on and until the end of fetchRequestedUrl,
+ // the code has been kindly borrowed from angular.js
+ // The reason for such code bloating is coherence:
+ // If the user were to use this for cache management, the
+ // serialization of parameters would need to be identical
+ // to the one done by angular for cache keys to match.
+ function sortedKeys(obj) {
+ var keys = [];
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ keys.push(key);
+ }
}
+ return keys.sort();
}
- return keys.sort();
- }
- function forEachSorted(obj, iterator, context) {
- var keys = sortedKeys(obj);
- for ( var i = 0; i < keys.length; i++) {
- iterator.call(context, obj[keys[i]], keys[i]);
+ function forEachSorted(obj, iterator, context) {
+ var keys = sortedKeys(obj);
+ for (var i = 0; i < keys.length; i++) {
+ iterator.call(context, obj[keys[i]], keys[i]);
+ }
+ return keys;
}
- return keys;
- }
- function encodeUriQuery(val, pctEncodeSpaces) {
- return encodeURIComponent(val).
- replace(/%40/gi, '@').
- replace(/%3A/gi, ':').
- replace(/%24/g, '$').
- replace(/%2C/gi, ',').
- replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
- }
-
- if (!params) { return url + (this.config.suffix || ''); }
+ function encodeUriQuery(val, pctEncodeSpaces) {
+ return encodeURIComponent(val).
+ replace(/%40/gi, '@').
+ replace(/%3A/gi, ':').
+ replace(/%24/g, '$').
+ replace(/%2C/gi, ',').
+ replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
+ }
- var parts = [];
- forEachSorted(params, function(value, key) {
- if (value === null || value === undefined) { return; }
- if (!angular.isArray(value)) { value = [value]; }
+ if (!params) {
+ return url + (this.config.suffix || '');
+ }
- angular.forEach(value, function(v) {
- if (angular.isObject(v)) {
- v = angular.toJson(v);
+ var parts = [];
+ forEachSorted(params, function(value, key) {
+ if (value === null || value === undefined) {
+ return;
}
- parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(v));
+ if (!angular.isArray(value)) {
+ value = [value];
+ }
+
+ angular.forEach(value, function(v) {
+ if (angular.isObject(v)) {
+ v = angular.toJson(v);
+ }
+ parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(v));
+ });
});
- });
- return url + (this.config.suffix || '') + ((url.indexOf('?') === -1) ? '?' : '&') + parts.join('&');
+ return url + (this.config.suffix || '') + ((url.indexOf('?') === -1) ? '?' : '&') + parts.join('&');
+ };
+
+ config.urlCreatorFactory.path = Path;
};
- config.urlCreatorFactory.path = Path;
- };
+ var globalConfiguration = {};
- var globalConfiguration = {};
+ Configurer.init(this, globalConfiguration);
- Configurer.init(this, globalConfiguration);
+ this.$get = ['$http', '$q', function($http, $q) {
- this.$get = ['$http', '$q', function($http, $q) {
+ function createServiceForConfiguration(config) {
+ var service = {};
- function createServiceForConfiguration(config) {
- var service = {};
+ var urlHandler = new config.urlCreatorFactory[config.urlCreator]();
+ urlHandler.setConfig(config);
- var urlHandler = new config.urlCreatorFactory[config.urlCreator]();
- urlHandler.setConfig(config);
+ function restangularizeBase(parent, elem, route, reqParams, fromServer) {
+ elem[config.restangularFields.route] = route;
+ elem[config.restangularFields.getRestangularUrl] = _.bind(urlHandler.fetchUrl, urlHandler, elem);
+ elem[config.restangularFields.getRequestedUrl] = _.bind(urlHandler.fetchRequestedUrl, urlHandler, elem);
+ elem[config.restangularFields.addRestangularMethod] = _.bind(addRestangularMethodFunction, elem);
+ elem[config.restangularFields.clone] = _.bind(copyRestangularizedElement, elem, elem);
+ elem[config.restangularFields.reqParams] = _.isEmpty(reqParams) ? null : reqParams;
+ elem[config.restangularFields.withHttpConfig] = _.bind(withHttpConfig, elem);
+ elem[config.restangularFields.plain] = _.bind(stripRestangular, elem, elem);
- function restangularizeBase(parent, elem, route, reqParams, fromServer) {
- elem[config.restangularFields.route] = route;
- elem[config.restangularFields.getRestangularUrl] = _.bind(urlHandler.fetchUrl, urlHandler, elem);
- elem[config.restangularFields.getRequestedUrl] = _.bind(urlHandler.fetchRequestedUrl, urlHandler, elem);
- elem[config.restangularFields.addRestangularMethod] = _.bind(addRestangularMethodFunction, elem);
- elem[config.restangularFields.clone] = _.bind(copyRestangularizedElement, elem, elem);
- elem[config.restangularFields.reqParams] = _.isEmpty(reqParams) ? null : reqParams;
- elem[config.restangularFields.withHttpConfig] = _.bind(withHttpConfig, elem);
- elem[config.restangularFields.plain] = _.bind(stripRestangular, elem, elem);
+ // Tag element as restangularized
+ elem[config.restangularFields.restangularized] = true;
- // Tag element as restangularized
- elem[config.restangularFields.restangularized] = true;
+ // RequestLess connection
+ elem[config.restangularFields.one] = _.bind(one, elem, elem);
+ elem[config.restangularFields.all] = _.bind(all, elem, elem);
+ elem[config.restangularFields.several] = _.bind(several, elem, elem);
+ elem[config.restangularFields.oneUrl] = _.bind(oneUrl, elem, elem);
+ elem[config.restangularFields.allUrl] = _.bind(allUrl, elem, elem);
- // RequestLess connection
- elem[config.restangularFields.one] = _.bind(one, elem, elem);
- elem[config.restangularFields.all] = _.bind(all, elem, elem);
- elem[config.restangularFields.several] = _.bind(several, elem, elem);
- elem[config.restangularFields.oneUrl] = _.bind(oneUrl, elem, elem);
- elem[config.restangularFields.allUrl] = _.bind(allUrl, elem, elem);
+ elem[config.restangularFields.fromServer] = !!fromServer;
- elem[config.restangularFields.fromServer] = !!fromServer;
+ if (parent && config.shouldSaveParent(route)) {
+ var parentId = config.getIdFromElem(parent);
+ var parentUrl = config.getUrlFromElem(parent);
- if (parent && config.shouldSaveParent(route)) {
- var parentId = config.getIdFromElem(parent);
- var parentUrl = config.getUrlFromElem(parent);
+ var restangularFieldsForParent = _.union(
+ _.values(_.pick(config.restangularFields, ['route', 'singleOne', 'parentResource'])),
+ config.extraFields
+ );
+ var parentResource = _.pick(parent, restangularFieldsForParent);
- var restangularFieldsForParent = _.union(
- _.values( _.pick(config.restangularFields, ['route', 'singleOne', 'parentResource']) ),
- config.extraFields
- );
- var parentResource = _.pick(parent, restangularFieldsForParent);
+ if (config.isValidId(parentId)) {
+ config.setIdToElem(parentResource, parentId, route);
+ }
+ if (config.isValidId(parentUrl)) {
+ config.setUrlToElem(parentResource, parentUrl, route);
+ }
- if (config.isValidId(parentId)) {
- config.setIdToElem(parentResource, parentId, route);
- }
- if (config.isValidId(parentUrl)) {
- config.setUrlToElem(parentResource, parentUrl, route);
+ elem[config.restangularFields.parentResource] = parentResource;
+ } else {
+ elem[config.restangularFields.parentResource] = null;
}
-
- elem[config.restangularFields.parentResource] = parentResource;
- } else {
- elem[config.restangularFields.parentResource] = null;
+ return elem;
}
- return elem;
- }
- function one(parent, route, id, singleOne) {
- var error;
- if (_.isNumber(route) || _.isNumber(parent)) {
- error = 'You\'re creating a Restangular entity with the number ';
- error += 'instead of the route or the parent. For example, you can\'t call .one(12).';
- throw new Error(error);
- }
- if (_.isUndefined(route)) {
- error = 'You\'re creating a Restangular entity either without the path. ';
- error += 'For example you can\'t call .one(). Please check if your arguments are valid.';
- throw new Error(error);
+ function one(parent, route, id, singleOne) {
+ var error;
+ if (_.isNumber(route) || _.isNumber(parent)) {
+ error = 'You\'re creating a Restangular entity with the number ';
+ error += 'instead of the route or the parent. For example, you can\'t call .one(12).';
+ throw new Error(error);
+ }
+ if (_.isUndefined(route)) {
+ error = 'You\'re creating a Restangular entity either without the path. ';
+ error += 'For example you can\'t call .one(). Please check if your arguments are valid.';
+ throw new Error(error);
+ }
+ var elem = {};
+ config.setIdToElem(elem, id, route);
+ config.setFieldToElem(config.restangularFields.singleOne, elem, singleOne);
+ return restangularizeElem(parent, elem, route, false);
}
- var elem = {};
- config.setIdToElem(elem, id, route);
- config.setFieldToElem(config.restangularFields.singleOne, elem, singleOne);
- return restangularizeElem(parent, elem , route, false);
- }
- function all(parent, route) {
- return restangularizeCollection(parent, [] , route, false);
- }
+ function all(parent, route) {
+ return restangularizeCollection(parent, [], route, false);
+ }
- function several(parent, route /*, ids */) {
- var collection = [];
- collection[config.restangularFields.ids] = Array.prototype.splice.call(arguments, 2);
- return restangularizeCollection(parent, collection , route, false);
- }
+ function several(parent, route /*, ids */ ) {
+ var collection = [];
+ collection[config.restangularFields.ids] = Array.prototype.splice.call(arguments, 2);
+ return restangularizeCollection(parent, collection, route, false);
+ }
- function oneUrl(parent, route, url) {
- if (!route) {
- throw new Error('Route is mandatory when creating new Restangular objects.');
+ function oneUrl(parent, route, url) {
+ if (!route) {
+ throw new Error('Route is mandatory when creating new Restangular objects.');
+ }
+ var elem = {};
+ config.setUrlToElem(elem, url, route);
+ return restangularizeElem(parent, elem, route, false);
}
- var elem = {};
- config.setUrlToElem(elem, url, route);
- return restangularizeElem(parent, elem , route, false);
- }
- function allUrl(parent, route, url) {
- if (!route) {
- throw new Error('Route is mandatory when creating new Restangular objects.');
+ function allUrl(parent, route, url) {
+ if (!route) {
+ throw new Error('Route is mandatory when creating new Restangular objects.');
+ }
+ var elem = {};
+ config.setUrlToElem(elem, url, route);
+ return restangularizeCollection(parent, elem, route, false);
}
- var elem = {};
- config.setUrlToElem(elem, url, route);
- return restangularizeCollection(parent, elem , route, false);
- }
- // Promises
- function restangularizePromise(promise, isCollection, valueToFill) {
- promise.call = _.bind(promiseCall, promise);
- promise.get = _.bind(promiseGet, promise);
- promise[config.restangularFields.restangularCollection] = isCollection;
- if (isCollection) {
+ // Promises
+ function restangularizePromise(promise, isCollection, valueToFill) {
+ promise.call = _.bind(promiseCall, promise);
+ promise.get = _.bind(promiseGet, promise);
+ promise[config.restangularFields.restangularCollection] = isCollection;
+ if (isCollection) {
promise.push = _.bind(promiseCall, promise, 'push');
+ }
+ promise.$object = valueToFill;
+ if (config.restangularizePromiseInterceptor) {
+ config.restangularizePromiseInterceptor(promise);
+ }
+ return promise;
}
- promise.$object = valueToFill;
- if (config.restangularizePromiseInterceptor) {
- config.restangularizePromiseInterceptor(promise);
- }
- return promise;
- }
-
- function promiseCall(method) {
- var deferred = $q.defer();
- var callArgs = arguments;
- var filledValue = {};
- this.then(function(val) {
- var params = Array.prototype.slice.call(callArgs, 1);
- var func = val[method];
- func.apply(val, params);
- filledValue = val;
- deferred.resolve(val);
- });
- return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
- }
-
- function promiseGet(what) {
- var deferred = $q.defer();
- var filledValue = {};
- this.then(function(val) {
- filledValue = val[what];
- deferred.resolve(filledValue);
- });
- return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
- }
-
- function resolvePromise(deferred, response, data, filledValue) {
- _.extend(filledValue, data);
-
- // Trigger the full response interceptor.
- if (config.fullResponse) {
- return deferred.resolve(_.extend(response, {
- data: data
- }));
- } else {
- deferred.resolve(data);
- }
- }
-
- // Elements
- function stripRestangular(elem) {
- if (_.isArray(elem)) {
- var array = [];
- _.each(elem, function(value) {
- array.push(config.isRestangularized(value) ? stripRestangular(value) : value);
+ function promiseCall(method) {
+ var deferred = $q.defer();
+ var callArgs = arguments;
+ var filledValue = {};
+ this.then(function(val) {
+ var params = Array.prototype.slice.call(callArgs, 1);
+ var func = val[method];
+ func.apply(val, params);
+ filledValue = val;
+ deferred.resolve(val);
});
- return array;
- } else {
- return _.omit(elem, _.values(_.omit(config.restangularFields, 'id')));
+ return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
}
- }
-
- function addCustomOperation(elem) {
- elem[config.restangularFields.customOperation] = _.bind(customFunction, elem);
- _.each(['put', 'post', 'get', 'delete'], function(oper) {
- _.each(['do', 'custom'], function(alias) {
- var callOperation = oper === 'delete' ? 'remove' : oper;
- var name = alias + oper.toUpperCase();
- var callFunction;
- if (callOperation !== 'put' && callOperation !== 'post') {
- callFunction = customFunction;
- } else {
- callFunction = function(operation, elem, path, params, headers) {
- return _.bind(customFunction, this)(operation, path, params, headers, elem);
- };
- }
- elem[name] = _.bind(callFunction, elem, callOperation);
+ function promiseGet(what) {
+ var deferred = $q.defer();
+ var filledValue = {};
+ this.then(function(val) {
+ filledValue = val[what];
+ deferred.resolve(filledValue);
});
- });
- elem[config.restangularFields.customGETLIST] = _.bind(fetchFunction, elem);
- elem[config.restangularFields.doGETLIST] = elem[config.restangularFields.customGETLIST];
- }
+ return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
+ }
- function copyRestangularizedElement(fromElement, toElement) {
- var copiedElement = angular.copy(fromElement, toElement);
- return restangularizeElem(copiedElement[config.restangularFields.parentResource],
- copiedElement, copiedElement[config.restangularFields.route], true);
- }
+ function resolvePromise(deferred, response, data, filledValue) {
+ _.extend(filledValue, data);
- function restangularizeElem(parent, element, route, fromServer, collection, reqParams) {
- var elem = config.onBeforeElemRestangularized(element, false, route);
+ // Trigger the full response interceptor.
+ if (config.fullResponse) {
+ return deferred.resolve(_.extend(response, {
+ data: data
+ }));
+ } else {
+ deferred.resolve(data);
+ }
+ }
- var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
- if (config.useCannonicalId) {
- localElem[config.restangularFields.cannonicalId] = config.getIdFromElem(localElem);
+ // Elements
+ function stripRestangular(elem) {
+ if (_.isArray(elem)) {
+ var array = [];
+ _.each(elem, function(value) {
+ array.push(config.isRestangularized(value) ? stripRestangular(value) : value);
+ });
+ return array;
+ } else {
+ return _.omit(elem, _.values(_.omit(config.restangularFields, 'id')));
+ }
}
- if (collection) {
- localElem[config.restangularFields.getParentList] = function() {
- return collection;
+ function addCustomOperation(elem) {
+ elem[config.restangularFields.customOperation] = _.bind(customFunction, elem);
+ var requestMethods = {
+ get: customFunction,
+ delete: customFunction
};
+ _.each(['put', 'patch', 'post'], function(name) {
+ requestMethods[name] = function(operation, elem, path, params, headers) {
+ return _.bind(customFunction, this)(operation, path, params, headers, elem);
+ };
+ });
+ _.each(requestMethods, function(requestFunc, name) {
+ var callOperation = name === 'delete' ? 'remove' : name;
+ _.each(['do', 'custom'], function(alias) {
+ elem[alias + name.toUpperCase()] = _.bind(requestFunc, elem, callOperation);
+ });
+ });
+ elem[config.restangularFields.customGETLIST] = _.bind(fetchFunction, elem);
+ elem[config.restangularFields.doGETLIST] = elem[config.restangularFields.customGETLIST];
}
- localElem[config.restangularFields.restangularCollection] = false;
- localElem[config.restangularFields.get] = _.bind(getFunction, localElem);
- localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem);
- localElem[config.restangularFields.put] = _.bind(putFunction, localElem);
- localElem[config.restangularFields.post] = _.bind(postFunction, localElem);
- localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
- localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
- localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
- localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
- localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
- localElem[config.restangularFields.save] = _.bind(save, localElem);
-
- addCustomOperation(localElem);
- return config.transformElem(localElem, false, route, service, true);
- }
+ function copyRestangularizedElement(element) {
+ var copiedElement = angular.copy(element);
+
+ // check if we're dealing with a collection (i.e. an array)
+ // and restangularize the element using the proper restangularizer,
+ // element / collection
+ if (_.isArray(element)) {
+ return restangularizeCollection(
+ element[config.restangularFields.parentResource],
+ copiedElement,
+ element[config.restangularFields.route],
+ element[config.restangularFields.fromServer],
+ element[config.restangularFields.reqParams]
+ );
+ }
- function restangularizeCollection(parent, element, route, fromServer, reqParams) {
- var elem = config.onBeforeElemRestangularized(element, true, route);
-
- var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
- localElem[config.restangularFields.restangularCollection] = true;
- localElem[config.restangularFields.post] = _.bind(postFunction, localElem, null);
- localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
- localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
- localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
- localElem[config.restangularFields.putElement] = _.bind(putElementFunction, localElem);
- localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
- localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
- localElem[config.restangularFields.get] = _.bind(getById, localElem);
- localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem, null);
-
- addCustomOperation(localElem);
- return config.transformElem(localElem, true, route, service, true);
- }
+ // not a collection, restangularize it as an element
+ return restangularizeElem(
+ element[config.restangularFields.parentResource],
+ copiedElement,
+ element[config.restangularFields.route],
+ element[config.restangularFields.fromServer],
+ element[config.restangularFields.restangularCollection],
+ element[config.restangularFields.reqParams]
+ );
+ }
- function restangularizeCollectionAndElements(parent, element, route) {
- var collection = restangularizeCollection(parent, element, route, false);
- _.each(collection, function(elem) {
- restangularizeElem(parent, elem, route, false);
- });
- return collection;
- }
+ function restangularizeElem(parent, element, route, fromServer, collection, reqParams) {
+ var elem = config.onBeforeElemRestangularized(element, false, route);
- function getById(id, reqParams, headers){
- return this.customGET(id.toString(), reqParams, headers);
- }
+ var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
- function putElementFunction(idx, params, headers) {
- var __this = this;
- var elemToPut = this[idx];
- var deferred = $q.defer();
- var filledArray = [];
- filledArray = config.transformElem(filledArray, true, elemToPut[config.restangularFields.route], service);
- elemToPut.put(params, headers).then(function(serverElem) {
- var newArray = copyRestangularizedElement(__this);
- newArray[idx] = serverElem;
- filledArray = newArray;
- deferred.resolve(newArray);
- }, function(response) {
- deferred.reject(response);
- });
+ if (config.useCannonicalId) {
+ localElem[config.restangularFields.cannonicalId] = config.getIdFromElem(localElem);
+ }
- return restangularizePromise(deferred.promise, true, filledArray);
- }
+ if (collection) {
+ localElem[config.restangularFields.getParentList] = function() {
+ return collection;
+ };
+ }
- function parseResponse(resData, operation, route, fetchUrl, response, deferred) {
- var data = config.responseExtractor(resData, operation, route, fetchUrl, response, deferred);
- var etag = response.headers('ETag');
- if (data && etag) {
- data[config.restangularFields.etag] = etag;
+ localElem[config.restangularFields.restangularCollection] = false;
+ localElem[config.restangularFields.get] = _.bind(getFunction, localElem);
+ localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem);
+ localElem[config.restangularFields.put] = _.bind(putFunction, localElem);
+ localElem[config.restangularFields.post] = _.bind(postFunction, localElem);
+ localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
+ localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
+ localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
+ localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
+ localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
+ localElem[config.restangularFields.save] = _.bind(save, localElem);
+
+ addCustomOperation(localElem);
+ return config.transformElem(localElem, false, route, service, true);
}
- return data;
- }
+ function restangularizeCollection(parent, element, route, fromServer, reqParams) {
+ var elem = config.onBeforeElemRestangularized(element, true, route);
+
+ var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
+ localElem[config.restangularFields.restangularCollection] = true;
+ localElem[config.restangularFields.post] = _.bind(postFunction, localElem, null);
+ localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
+ localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
+ localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
+ localElem[config.restangularFields.putElement] = _.bind(putElementFunction, localElem);
+ localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
+ localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
+ localElem[config.restangularFields.get] = _.bind(getById, localElem);
+ localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem, null);
+
+ addCustomOperation(localElem);
+ return config.transformElem(localElem, true, route, service, true);
+ }
- function fetchFunction(what, reqParams, headers) {
- var __this = this;
- var deferred = $q.defer();
- var operation = 'getList';
- var url = urlHandler.fetchUrl(this, what);
- var whatFetched = what || __this[config.restangularFields.route];
+ function restangularizeCollectionAndElements(parent, element, route, fromServer) {
+ var collection = restangularizeCollection(parent, element, route, fromServer);
+ _.each(collection, function(elem) {
+ if (elem) {
+ restangularizeElem(parent, elem, route, fromServer);
+ }
+ });
+ return collection;
+ }
- var request = config.fullRequestInterceptor(null, operation,
- whatFetched, url, headers || {}, reqParams || {}, this[config.restangularFields.httpConfig] || {});
+ function getById(id, reqParams, headers) {
+ return this.customGET(id.toString(), reqParams, headers);
+ }
- var filledArray = [];
- filledArray = config.transformElem(filledArray, true, whatFetched, service);
+ function putElementFunction(idx, params, headers) {
+ var __this = this;
+ var elemToPut = this[idx];
+ var deferred = $q.defer();
+ var filledArray = [];
+ filledArray = config.transformElem(filledArray, true, elemToPut[config.restangularFields.route], service);
+ elemToPut.put(params, headers).then(function(serverElem) {
+ var newArray = copyRestangularizedElement(__this);
+ newArray[idx] = serverElem;
+ filledArray = newArray;
+ deferred.resolve(newArray);
+ }, function(response) {
+ deferred.reject(response);
+ });
- var method = 'getList';
+ return restangularizePromise(deferred.promise, true, filledArray);
+ }
- if (config.jsonp) {
- method = 'jsonp';
+ function parseResponse(resData, operation, route, fetchUrl, response, deferred) {
+ var data = config.responseExtractor(resData, operation, route, fetchUrl, response, deferred);
+ var etag = response.headers('ETag');
+ if (data && etag) {
+ data[config.restangularFields.etag] = etag;
+ }
+ return data;
}
- var okCallback = function(response) {
- var resData = response.data;
- var fullParams = response.config.params;
- var data = parseResponse(resData, operation, whatFetched, url, response, deferred);
- // support empty response for getList() calls (some APIs respond with 204 and empty body)
- if (_.isUndefined(data) || '' === data) {
- data = [];
- }
- if (!_.isArray(data)) {
- throw new Error('Response for getList SHOULD be an array and not an object or something else');
- }
- var processedData = _.map(data, function(elem) {
- if (!__this[config.restangularFields.restangularCollection]) {
- return restangularizeElem(__this, elem, what, true, data);
- } else {
- return restangularizeElem(__this[config.restangularFields.parentResource],
- elem, __this[config.restangularFields.route], true, data);
- }
- });
+ function fetchFunction(what, reqParams, headers) {
+ var __this = this;
+ var deferred = $q.defer();
+ var operation = 'getList';
+ var url = urlHandler.fetchUrl(this, what);
+ var whatFetched = what || __this[config.restangularFields.route];
- processedData = _.extend(data, processedData);
-
- if (!__this[config.restangularFields.restangularCollection]) {
- resolvePromise(
- deferred,
- response,
- restangularizeCollection(
- __this,
- processedData,
- what,
- true,
- fullParams
- ),
- filledArray
- );
- } else {
- resolvePromise(
- deferred,
- response,
- restangularizeCollection(
- __this[config.restangularFields.parentResource],
- processedData,
- __this[config.restangularFields.route],
- true,
- fullParams
- ),
- filledArray
- );
- }
- };
+ var request = config.fullRequestInterceptor(null, operation,
+ whatFetched, url, headers || {}, reqParams || {}, this[config.restangularFields.httpConfig] || {});
- urlHandler.resource(this, $http, request.httpConfig, request.headers, request.params, what,
- this[config.restangularFields.etag], operation)[method]().then(okCallback, function error(response) {
- if (response.status === 304 && __this[config.restangularFields.restangularCollection]) {
- resolvePromise(deferred, response, __this, filledArray);
- } else if ( _.every(config.errorInterceptors, function(cb) { return cb(response, deferred, okCallback) !== false; }) ) {
- // triggered if no callback returns false
- deferred.reject(response);
+ var filledArray = [];
+ filledArray = config.transformElem(filledArray, true, whatFetched, service);
+
+ var method = 'getList';
+
+ if (config.jsonp) {
+ method = 'jsonp';
}
- });
- return restangularizePromise(deferred.promise, true, filledArray);
- }
+ var okCallback = function(response) {
+ var resData = response.data;
+ var fullParams = response.config.params;
+ var data = parseResponse(resData, operation, whatFetched, url, response, deferred);
- function withHttpConfig(httpConfig) {
- this[config.restangularFields.httpConfig] = httpConfig;
- return this;
- }
+ // support empty response for getList() calls (some APIs respond with 204 and empty body)
+ if (_.isUndefined(data) || '' === data) {
+ data = [];
+ }
+ if (!_.isArray(data)) {
+ throw new Error('Response for getList SHOULD be an array and not an object or something else');
+ }
- function save(params, headers) {
- if (this[config.restangularFields.fromServer]) {
- return this[config.restangularFields.put](params, headers);
- } else {
- return _.bind(elemFunction, this)('post', undefined, params, undefined, headers);
- }
- }
+ if (true === config.plainByDefault) {
+ return resolvePromise(deferred, response, data, filledArray);
+ }
- function elemFunction(operation, what, params, obj, headers) {
- var __this = this;
- var deferred = $q.defer();
- var resParams = params || {};
- var route = what || this[config.restangularFields.route];
- var fetchUrl = urlHandler.fetchUrl(this, what);
+ var processedData = _.map(data, function(elem) {
+ if (!__this[config.restangularFields.restangularCollection]) {
+ return restangularizeElem(__this, elem, what, true, data);
+ } else {
+ return restangularizeElem(__this[config.restangularFields.parentResource],
+ elem, __this[config.restangularFields.route], true, data);
+ }
+ });
- var callObj = obj || this;
- // fallback to etag on restangular object (since for custom methods we probably don't explicitly specify the etag field)
- var etag = callObj[config.restangularFields.etag] || (operation !== 'post' ? this[config.restangularFields.etag] : null);
+ processedData = _.extend(data, processedData);
- if (_.isObject(callObj) && config.isRestangularized(callObj)) {
- callObj = stripRestangular(callObj);
- }
- var request = config.fullRequestInterceptor(callObj, operation, route, fetchUrl,
- headers || {}, resParams || {}, this[config.restangularFields.httpConfig] || {});
-
- var filledObject = {};
- filledObject = config.transformElem(filledObject, false, route, service);
-
- var okCallback = function(response) {
- var resData = response.data;
- var fullParams = response.config.params;
- var elem = parseResponse(resData, operation, route, fetchUrl, response, deferred);
- if (elem) {
- var data;
- if (operation === 'post' && !__this[config.restangularFields.restangularCollection]) {
- data = restangularizeElem(
- __this[config.restangularFields.parentResource],
- elem,
- route,
- true,
- null,
- fullParams
+ if (!__this[config.restangularFields.restangularCollection]) {
+ resolvePromise(
+ deferred,
+ response,
+ restangularizeCollection(
+ __this,
+ processedData,
+ what,
+ true,
+ fullParams
+ ),
+ filledArray
);
- resolvePromise(deferred, response, data, filledObject);
} else {
- data = restangularizeElem(
- __this[config.restangularFields.parentResource],
- elem,
- __this[config.restangularFields.route],
- true,
- null,
- fullParams
+ resolvePromise(
+ deferred,
+ response,
+ restangularizeCollection(
+ __this[config.restangularFields.parentResource],
+ processedData,
+ __this[config.restangularFields.route],
+ true,
+ fullParams
+ ),
+ filledArray
);
+ }
+ };
- data[config.restangularFields.singleOne] = __this[config.restangularFields.singleOne];
- resolvePromise(deferred, response, data, filledObject);
+ urlHandler.resource(this, $http, request.httpConfig, request.headers, request.params, what,
+ this[config.restangularFields.etag], operation)[method]().then(okCallback, function error(response) {
+ if (response.status === 304 && __this[config.restangularFields.restangularCollection]) {
+ resolvePromise(deferred, response, __this, filledArray);
+ } else if (_.every(config.errorInterceptors, function(cb) {
+ return cb(response, deferred, okCallback) !== false;
+ })) {
+ // triggered if no callback returns false
+ deferred.reject(response);
}
+ });
+
+ return restangularizePromise(deferred.promise, true, filledArray);
+ }
+
+ function withHttpConfig(httpConfig) {
+ this[config.restangularFields.httpConfig] = httpConfig;
+ return this;
+ }
+ function save(params, headers) {
+ if (this[config.restangularFields.fromServer]) {
+ return this[config.restangularFields.put](params, headers);
} else {
- resolvePromise(deferred, response, undefined, filledObject);
+ return _.bind(elemFunction, this)('post', undefined, params, undefined, headers);
}
- };
+ }
- var errorCallback = function(response) {
- if (response.status === 304 && config.isSafe(operation)) {
- resolvePromise(deferred, response, __this, filledObject);
- } else if ( _.every(config.errorInterceptors, function(cb) { return cb(response, deferred, okCallback) !== false; }) ) {
- // triggered if no callback returns false
- deferred.reject(response);
+ function elemFunction(operation, what, params, obj, headers) {
+ var __this = this;
+ var deferred = $q.defer();
+ var resParams = params || {};
+ var route = what || this[config.restangularFields.route];
+ var fetchUrl = urlHandler.fetchUrl(this, what);
+
+ var callObj = obj || this;
+ // fallback to etag on restangular object (since for custom methods we probably don't explicitly specify the etag field)
+ var etag = callObj[config.restangularFields.etag] || (operation !== 'post' ? this[config.restangularFields.etag] : null);
+
+ if (_.isObject(callObj) && config.isRestangularized(callObj)) {
+ callObj = stripRestangular(callObj);
}
- };
- // Overriding HTTP Method
- var callOperation = operation;
- var callHeaders = _.extend({}, request.headers);
- var isOverrideOperation = config.isOverridenMethod(operation);
- if (isOverrideOperation) {
- callOperation = 'post';
- callHeaders = _.extend(callHeaders, {'X-HTTP-Method-Override': operation === 'remove' ? 'DELETE' : operation.toUpperCase()});
- } else if (config.jsonp && callOperation === 'get') {
- callOperation = 'jsonp';
- }
+ var request = config.fullRequestInterceptor(callObj, operation, route, fetchUrl,
+ headers || {}, resParams || {}, this[config.restangularFields.httpConfig] || {});
+
+ var filledObject = {};
+ filledObject = config.transformElem(filledObject, false, route, service);
+
+ var okCallback = function(response) {
+ var resData = response.data;
+ var fullParams = response.config.params;
+ var elem = parseResponse(resData, operation, route, fetchUrl, response, deferred);
+
+ // accept 0 as response
+ if (elem !== null && elem !== undefined && elem !== '') {
+ var data;
+
+ if (true === config.plainByDefault) {
+ return resolvePromise(deferred, response, elem, filledObject);
+ }
+
+ if (operation === 'post' && !__this[config.restangularFields.restangularCollection]) {
+ data = restangularizeElem(
+ __this[config.restangularFields.parentResource],
+ elem,
+ route,
+ true,
+ null,
+ fullParams
+ );
+ resolvePromise(deferred, response, data, filledObject);
+ } else {
+ data = restangularizeElem(
+ __this[config.restangularFields.parentResource],
+ elem,
+ __this[config.restangularFields.route],
+ true,
+ null,
+ fullParams
+ );
+
+ data[config.restangularFields.singleOne] = __this[config.restangularFields.singleOne];
+ resolvePromise(deferred, response, data, filledObject);
+ }
- if (config.isSafe(operation)) {
+ } else {
+ resolvePromise(deferred, response, undefined, filledObject);
+ }
+ };
+
+ var errorCallback = function(response) {
+ if (response.status === 304 && config.isSafe(operation)) {
+ resolvePromise(deferred, response, __this, filledObject);
+ } else if (_.every(config.errorInterceptors, function(cb) {
+ return cb(response, deferred, okCallback) !== false;
+ })) {
+ // triggered if no callback returns false
+ deferred.reject(response);
+ }
+ };
+ // Overriding HTTP Method
+ var callOperation = operation;
+ var callHeaders = _.extend({}, request.headers);
+ var isOverrideOperation = config.isOverridenMethod(operation);
if (isOverrideOperation) {
- urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
- what, etag, callOperation)[callOperation]({}).then(okCallback, errorCallback);
+ callOperation = 'post';
+ callHeaders = _.extend(callHeaders, {
+ 'X-HTTP-Method-Override': operation === 'remove' ? 'DELETE' : operation.toUpperCase()
+ });
+ } else if (config.jsonp && callOperation === 'get') {
+ callOperation = 'jsonp';
+ }
+
+ if (config.isSafe(operation)) {
+ if (isOverrideOperation) {
+ urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
+ what, etag, callOperation)[callOperation]({}).then(okCallback, errorCallback);
+ } else {
+ urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
+ what, etag, callOperation)[callOperation]().then(okCallback, errorCallback);
+ }
} else {
urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
- what, etag, callOperation)[callOperation]().then(okCallback, errorCallback);
+ what, etag, callOperation)[callOperation](request.element).then(okCallback, errorCallback);
}
- } else {
- urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
- what, etag, callOperation)[callOperation](request.element).then(okCallback, errorCallback);
- }
- return restangularizePromise(deferred.promise, false, filledObject);
- }
-
- function getFunction(params, headers) {
- return _.bind(elemFunction, this)('get', undefined, params, undefined, headers);
- }
+ return restangularizePromise(deferred.promise, false, filledObject);
+ }
- function deleteFunction(params, headers) {
- return _.bind(elemFunction, this)('remove', undefined, params, undefined, headers);
- }
+ function getFunction(params, headers) {
+ return _.bind(elemFunction, this)('get', undefined, params, undefined, headers);
+ }
- function putFunction(params, headers) {
- return _.bind(elemFunction, this)('put', undefined, params, undefined, headers);
- }
+ function deleteFunction(params, headers) {
+ return _.bind(elemFunction, this)('remove', undefined, params, undefined, headers);
+ }
- function postFunction(what, elem, params, headers) {
- return _.bind(elemFunction, this)('post', what, params, elem, headers);
- }
+ function putFunction(params, headers) {
+ return _.bind(elemFunction, this)('put', undefined, params, undefined, headers);
+ }
- function headFunction(params, headers) {
- return _.bind(elemFunction, this)('head', undefined, params, undefined, headers);
- }
+ function postFunction(what, elem, params, headers) {
+ return _.bind(elemFunction, this)('post', what, params, elem, headers);
+ }
- function traceFunction(params, headers) {
- return _.bind(elemFunction, this)('trace', undefined, params, undefined, headers);
- }
+ function headFunction(params, headers) {
+ return _.bind(elemFunction, this)('head', undefined, params, undefined, headers);
+ }
- function optionsFunction(params, headers) {
- return _.bind(elemFunction, this)('options', undefined, params, undefined, headers);
- }
+ function traceFunction(params, headers) {
+ return _.bind(elemFunction, this)('trace', undefined, params, undefined, headers);
+ }
- function patchFunction(elem, params, headers) {
- return _.bind(elemFunction, this)('patch', undefined, params, elem, headers);
- }
+ function optionsFunction(params, headers) {
+ return _.bind(elemFunction, this)('options', undefined, params, undefined, headers);
+ }
- function customFunction(operation, path, params, headers, elem) {
- return _.bind(elemFunction, this)(operation, path, params, elem, headers);
- }
+ function patchFunction(elem, params, headers) {
+ return _.bind(elemFunction, this)('patch', undefined, params, elem, headers);
+ }
- function addRestangularMethodFunction(name, operation, path, defaultParams, defaultHeaders, defaultElem) {
- var bindedFunction;
- if (operation === 'getList') {
- bindedFunction = _.bind(fetchFunction, this, path);
- } else {
- bindedFunction = _.bind(customFunction, this, operation, path);
+ function customFunction(operation, path, params, headers, elem) {
+ return _.bind(elemFunction, this)(operation, path, params, elem, headers);
}
- var createdFunction = function(params, headers, elem) {
- var callParams = _.defaults({
- params: params,
- headers: headers,
- elem: elem
- }, {
- params: defaultParams,
- headers: defaultHeaders,
- elem: defaultElem
- });
- return bindedFunction(callParams.params, callParams.headers, callParams.elem);
- };
+ function addRestangularMethodFunction(name, operation, path, defaultParams, defaultHeaders, defaultElem) {
+ var bindedFunction;
+ if (operation === 'getList') {
+ bindedFunction = _.bind(fetchFunction, this, path);
+ } else {
+ bindedFunction = _.bind(customFunction, this, operation, path);
+ }
- if (config.isSafe(operation)) {
- this[name] = createdFunction;
- } else {
- this[name] = function(elem, params, headers) {
- return createdFunction(params, headers, elem);
+ var createdFunction = function(params, headers, elem) {
+ var callParams = _.defaults({
+ params: params,
+ headers: headers,
+ elem: elem
+ }, {
+ params: defaultParams,
+ headers: defaultHeaders,
+ elem: defaultElem
+ });
+ return bindedFunction(callParams.params, callParams.headers, callParams.elem);
};
- }
- }
- function withConfigurationFunction(configurer) {
- var newConfig = angular.copy(_.omit(config, 'configuration'));
- Configurer.init(newConfig, newConfig);
- configurer(newConfig);
- return createServiceForConfiguration(newConfig);
- }
-
- function toService(route, parent) {
- var knownCollectionMethods = _.values(config.restangularFields);
- var serv = {};
- var collection = (parent || service).all(route);
- serv.one = _.bind(one, (parent || service), parent, route);
- serv.post = _.bind(collection.post, collection);
- serv.getList = _.bind(collection.getList, collection);
-
- for (var prop in collection) {
- if (collection.hasOwnProperty(prop) && _.isFunction(collection[prop]) && !_.includes(knownCollectionMethods, prop)) {
- serv[prop] = _.bind(collection[prop], collection);
+ if (config.isSafe(operation)) {
+ this[name] = createdFunction;
+ } else {
+ this[name] = function(elem, params, headers) {
+ return createdFunction(params, headers, elem);
+ };
}
}
- return serv;
- }
+ function withConfigurationFunction(configurer) {
+ var newConfig = angular.copy(_.omit(config, 'configuration'));
+ Configurer.init(newConfig, newConfig);
+ configurer(newConfig);
+ return createServiceForConfiguration(newConfig);
+ }
+
+ function toService(route, parent) {
+ var knownCollectionMethods = _.values(config.restangularFields);
+ var serv = {};
+ var collection = (parent || service).all(route);
+ serv.one = _.bind(one, (parent || service), parent, route);
+ serv.post = _.bind(collection.post, collection);
+ serv.getList = _.bind(collection.getList, collection);
+ serv.withHttpConfig = _.bind(collection.withHttpConfig, collection);
+ serv.get = _.bind(collection.get, collection);
+
+ for (var prop in collection) {
+ if (collection.hasOwnProperty(prop) && _.isFunction(collection[prop]) && !_.includes(knownCollectionMethods, prop)) {
+ serv[prop] = _.bind(collection[prop], collection);
+ }
+ }
+ return serv;
+ }
- Configurer.init(service, config);
- service.copy = _.bind(copyRestangularizedElement, service);
+ Configurer.init(service, config);
- service.service = _.bind(toService, service);
+ service.copy = _.bind(copyRestangularizedElement, service);
- service.withConfig = _.bind(withConfigurationFunction, service);
+ service.service = _.bind(toService, service);
- service.one = _.bind(one, service, null);
+ service.withConfig = _.bind(withConfigurationFunction, service);
- service.all = _.bind(all, service, null);
+ service.one = _.bind(one, service, null);
- service.several = _.bind(several, service, null);
+ service.all = _.bind(all, service, null);
- service.oneUrl = _.bind(oneUrl, service, null);
+ service.several = _.bind(several, service, null);
- service.allUrl = _.bind(allUrl, service, null);
+ service.oneUrl = _.bind(oneUrl, service, null);
- service.stripRestangular = _.bind(stripRestangular, service);
+ service.allUrl = _.bind(allUrl, service, null);
- service.restangularizeElement = _.bind(restangularizeElem, service);
+ service.stripRestangular = _.bind(stripRestangular, service);
- service.restangularizeCollection = _.bind(restangularizeCollectionAndElements, service);
+ service.restangularizeElement = _.bind(restangularizeElem, service);
- return service;
- }
+ service.restangularizeCollection = _.bind(restangularizeCollectionAndElements, service);
- return createServiceForConfiguration(globalConfiguration);
- }];
-});
+ return service;
+ }
-})();
+ return createServiceForConfiguration(globalConfiguration);
+ }];
+ });
+ return restangular.name;
+}));
diff --git a/dist/restangular.min.js b/dist/restangular.min.js
index f0599d58..729f8aad 100644
--- a/dist/restangular.min.js
+++ b/dist/restangular.min.js
@@ -1,6 +1,6 @@
/**
* Restful Resources service for AngularJS apps
- * @version v1.5.2 - 2016-02-08 * @link https://github.com/mgonto/restangular
+ * @version v1.6.1 - 2017-01-06 * @link https://github.com/mgonto/restangular
* @author Martin Gontovnikas
* @license MIT License, http://www.opensource.org/licenses/MIT
- */!function(){var a=angular.module("restangular",[]);a.provider("Restangular",function(){var a={};a.init=function(a,b){function c(a,b,c,d){var e={};return _.each(_.keys(d),function(f){var g=d[f];g.params=_.extend({},g.params,a.defaultRequestParams[g.method.toLowerCase()]),_.isEmpty(g.params)&&delete g.params,a.isSafe(g.method)?e[f]=function(){return b(_.extend(g,{url:c}))}:e[f]=function(a){return b(_.extend(g,{url:c,data:a}))}}),e}a.configuration=b;var d=["get","head","options","trace","getlist"];b.isSafe=function(a){return _.includes(d,a.toLowerCase())};var e=/^https?:\/\//i;b.isAbsoluteUrl=function(a){return _.isUndefined(b.absoluteUrl)||_.isNull(b.absoluteUrl)?a&&e.test(a):b.absoluteUrl},b.absoluteUrl=_.isUndefined(b.absoluteUrl)?!0:b.absoluteUrl,a.setSelfLinkAbsoluteUrl=function(a){b.absoluteUrl=a},b.baseUrl=_.isUndefined(b.baseUrl)?"":b.baseUrl,a.setBaseUrl=function(a){return b.baseUrl=/\/$/.test(a)?a.substring(0,a.length-1):a,this},b.extraFields=b.extraFields||[],a.setExtraFields=function(a){return b.extraFields=a,this},b.defaultHttpFields=b.defaultHttpFields||{},a.setDefaultHttpFields=function(a){return b.defaultHttpFields=a,this},b.withHttpValues=function(a,c){return _.defaults(c,a,b.defaultHttpFields)},b.encodeIds=_.isUndefined(b.encodeIds)?!0:b.encodeIds,a.setEncodeIds=function(a){b.encodeIds=a},b.defaultRequestParams=b.defaultRequestParams||{get:{},post:{},put:{},remove:{},common:{}},a.setDefaultRequestParams=function(a,c){var d=[],e=c||a;return _.isUndefined(c)?d.push("common"):_.isArray(a)?d=a:d.push(a),_.each(d,function(a){b.defaultRequestParams[a]=e}),this},a.requestParams=b.defaultRequestParams,b.defaultHeaders=b.defaultHeaders||{},a.setDefaultHeaders=function(c){return b.defaultHeaders=c,a.defaultHeaders=b.defaultHeaders,this},a.defaultHeaders=b.defaultHeaders,b.methodOverriders=b.methodOverriders||[],a.setMethodOverriders=function(a){var c=_.extend([],a);return b.isOverridenMethod("delete",c)&&c.push("remove"),b.methodOverriders=c,this},b.jsonp=_.isUndefined(b.jsonp)?!1:b.jsonp,a.setJsonp=function(a){b.jsonp=a},b.isOverridenMethod=function(a,c){var d=c||b.methodOverriders;return!_.isUndefined(_.find(d,function(b){return b.toLowerCase()===a.toLowerCase()}))},b.urlCreator=b.urlCreator||"path",a.setUrlCreator=function(a){if(!_.has(b.urlCreatorFactory,a))throw new Error("URL Path selected isn't valid");return b.urlCreator=a,this},b.restangularFields=b.restangularFields||{id:"id",route:"route",parentResource:"parentResource",restangularCollection:"restangularCollection",cannonicalId:"__cannonicalId",etag:"restangularEtag",selfLink:"href",get:"get",getList:"getList",put:"put",post:"post",remove:"remove",head:"head",trace:"trace",options:"options",patch:"patch",getRestangularUrl:"getRestangularUrl",getRequestedUrl:"getRequestedUrl",putElement:"putElement",addRestangularMethod:"addRestangularMethod",getParentList:"getParentList",clone:"clone",ids:"ids",httpConfig:"_$httpConfig",reqParams:"reqParams",one:"one",all:"all",several:"several",oneUrl:"oneUrl",allUrl:"allUrl",customPUT:"customPUT",customPOST:"customPOST",customDELETE:"customDELETE",customGET:"customGET",customGETLIST:"customGETLIST",customOperation:"customOperation",doPUT:"doPUT",doPOST:"doPOST",doDELETE:"doDELETE",doGET:"doGET",doGETLIST:"doGETLIST",fromServer:"fromServer",withConfig:"withConfig",withHttpConfig:"withHttpConfig",singleOne:"singleOne",plain:"plain",save:"save",restangularized:"restangularized"},a.setRestangularFields=function(a){return b.restangularFields=_.extend(b.restangularFields,a),this},b.isRestangularized=function(a){return!!a[b.restangularFields.restangularized]},b.setFieldToElem=function(a,b,c){var d=a.split("."),e=b;return _.each(_.initial(d),function(a){e[a]={},e=e[a]}),e[_.last(d)]=c,this},b.getFieldFromElem=function(a,b){var c=a.split("."),d=b;return _.each(c,function(a){d&&(d=d[a])}),angular.copy(d)},b.setIdToElem=function(a,c){return b.setFieldToElem(b.restangularFields.id,a,c),this},b.getIdFromElem=function(a){return b.getFieldFromElem(b.restangularFields.id,a)},b.isValidId=function(a){return""!==a&&!_.isUndefined(a)&&!_.isNull(a)},b.setUrlToElem=function(a,c){return b.setFieldToElem(b.restangularFields.selfLink,a,c),this},b.getUrlFromElem=function(a){return b.getFieldFromElem(b.restangularFields.selfLink,a)},b.useCannonicalId=_.isUndefined(b.useCannonicalId)?!1:b.useCannonicalId,a.setUseCannonicalId=function(a){return b.useCannonicalId=a,this},b.getCannonicalIdFromElem=function(a){var c=a[b.restangularFields.cannonicalId],d=b.isValidId(c)?c:b.getIdFromElem(a);return d},b.responseInterceptors=b.responseInterceptors||[],b.defaultResponseInterceptor=function(a){return a},b.responseExtractor=function(a,c,d,e,f,g){var h=angular.copy(b.responseInterceptors);h.push(b.defaultResponseInterceptor);var i=a;return _.each(h,function(a){i=a(i,c,d,e,f,g)}),i},a.addResponseInterceptor=function(a){return b.responseInterceptors.push(a),this},b.errorInterceptors=b.errorInterceptors||[],a.addErrorInterceptor=function(a){return b.errorInterceptors.push(a),this},a.setResponseInterceptor=a.addResponseInterceptor,a.setResponseExtractor=a.addResponseInterceptor,a.setErrorInterceptor=a.addErrorInterceptor,b.requestInterceptors=b.requestInterceptors||[],b.defaultInterceptor=function(a,b,c,d,e,f,g){return{element:a,headers:e,params:f,httpConfig:g}},b.fullRequestInterceptor=function(a,c,d,e,f,g,h){var i=angular.copy(b.requestInterceptors),j=b.defaultInterceptor(a,c,d,e,f,g,h);return _.reduce(i,function(a,b){return _.extend(a,b(a.element,c,d,e,a.headers,a.params,a.httpConfig))},j)},a.addRequestInterceptor=function(a){return b.requestInterceptors.push(function(b,c,d,e,f,g,h){return{headers:f,params:g,element:a(b,c,d,e),httpConfig:h}}),this},a.setRequestInterceptor=a.addRequestInterceptor,a.addFullRequestInterceptor=function(a){return b.requestInterceptors.push(a),this},a.setFullRequestInterceptor=a.addFullRequestInterceptor,b.onBeforeElemRestangularized=b.onBeforeElemRestangularized||function(a){return a},a.setOnBeforeElemRestangularized=function(a){return b.onBeforeElemRestangularized=a,this},a.setRestangularizePromiseInterceptor=function(a){return b.restangularizePromiseInterceptor=a,this},b.onElemRestangularized=b.onElemRestangularized||function(a){return a},a.setOnElemRestangularized=function(a){return b.onElemRestangularized=a,this},b.shouldSaveParent=b.shouldSaveParent||function(){return!0},a.setParentless=function(a){return _.isArray(a)?b.shouldSaveParent=function(b){return!_.includes(a,b)}:_.isBoolean(a)&&(b.shouldSaveParent=function(){return!a}),this},b.suffix=_.isUndefined(b.suffix)?null:b.suffix,a.setRequestSuffix=function(a){return b.suffix=a,this},b.transformers=b.transformers||{},a.addElementTransformer=function(c,d,e){var f=null,g=null;2===arguments.length?g=d:(g=e,f=d);var h=b.transformers[c];return h||(h=b.transformers[c]=[]),h.push(function(a,b){return _.isNull(f)||a===f?g(b):b}),a},a.extendCollection=function(b,c){return a.addElementTransformer(b,!0,c)},a.extendModel=function(b,c){return a.addElementTransformer(b,!1,c)},b.transformElem=function(a,c,d,e,f){if(!f&&!b.transformLocalElements&&!a[b.restangularFields.fromServer])return a;var g=b.transformers[d],h=a;return g&&_.each(g,function(a){h=a(c,h)}),b.onElemRestangularized(h,c,d,e)},b.transformLocalElements=_.isUndefined(b.transformLocalElements)?!1:b.transformLocalElements,a.setTransformOnlyServerElements=function(a){b.transformLocalElements=!a},b.fullResponse=_.isUndefined(b.fullResponse)?!1:b.fullResponse,a.setFullResponse=function(a){return b.fullResponse=a,this},b.urlCreatorFactory={};var f=function(){};f.prototype.setConfig=function(a){return this.config=a,this},f.prototype.parentsArray=function(a){for(var b=[];a;)b.push(a),a=a[this.config.restangularFields.parentResource];return b.reverse()},f.prototype.resource=function(a,d,e,f,g,h,i,j){var k=_.defaults(g||{},this.config.defaultRequestParams.common),l=_.defaults(f||{},this.config.defaultHeaders);i&&(b.isSafe(j)?l["If-None-Match"]=i:l["If-Match"]=i);var m=this.base(a);if(h){var n="";/\/$/.test(m)||(n+="/"),n+=h,m+=n}return this.config.suffix&&-1===m.indexOf(this.config.suffix,m.length-this.config.suffix.length)&&!this.config.getUrlFromElem(a)&&(m+=this.config.suffix),a[this.config.restangularFields.httpConfig]=void 0,c(this.config,d,m,{getList:this.config.withHttpValues(e,{method:"GET",params:k,headers:l}),get:this.config.withHttpValues(e,{method:"GET",params:k,headers:l}),jsonp:this.config.withHttpValues(e,{method:"jsonp",params:k,headers:l}),put:this.config.withHttpValues(e,{method:"PUT",params:k,headers:l}),post:this.config.withHttpValues(e,{method:"POST",params:k,headers:l}),remove:this.config.withHttpValues(e,{method:"DELETE",params:k,headers:l}),head:this.config.withHttpValues(e,{method:"HEAD",params:k,headers:l}),trace:this.config.withHttpValues(e,{method:"TRACE",params:k,headers:l}),options:this.config.withHttpValues(e,{method:"OPTIONS",params:k,headers:l}),patch:this.config.withHttpValues(e,{method:"PATCH",params:k,headers:l})})};var g=function(){};g.prototype=new f,g.prototype.normalizeUrl=function(a){var b=/(http[s]?:\/\/)?(.*)?/.exec(a);return b[2]=b[2].replace(/[\\\/]+/g,"/"),"undefined"!=typeof b[1]?b[1]+b[2]:b[2]},g.prototype.base=function(a){var c=this;return _.reduce(this.parentsArray(a),function(a,d){var e,f=c.config.getUrlFromElem(d);if(f){if(c.config.isAbsoluteUrl(f))return f;e=f}else if(e=d[c.config.restangularFields.route],d[c.config.restangularFields.restangularCollection]){var g=d[c.config.restangularFields.ids];g&&(e+="/"+g.join(","))}else{var h;h=c.config.useCannonicalId?c.config.getCannonicalIdFromElem(d):c.config.getIdFromElem(d),b.isValidId(h)&&!d.singleOne&&(e+="/"+(c.config.encodeIds?encodeURIComponent(h):h))}return a=a.replace(/\/$/,"")+"/"+e,c.normalizeUrl(a)},this.config.baseUrl)},g.prototype.fetchUrl=function(a,b){var c=this.base(a);return b&&(c+="/"+b),c},g.prototype.fetchRequestedUrl=function(a,c){function d(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b.sort()}function e(a,b,c){for(var e=d(a),f=0;f",
"repository": {
@@ -29,35 +29,35 @@
}
],
"dependencies": {
- "lodash": ">=1.3.0"
+ "lodash": "~4.17.0"
},
"peerDependencies": {
"angular": ">=1.3.12"
},
"devDependencies": {
"angular-mocks": "^1.4.8",
- "grunt": "^0.4.5",
- "grunt-cli": ">= 0.1.7",
+ "grunt": "^1.0.0",
+ "grunt-bower": "*",
+ "grunt-bower-task": "*",
+ "grunt-cli": "^1.2.0",
"grunt-contrib-concat": "*",
"grunt-contrib-jshint": "*",
"grunt-contrib-uglify": "*",
- "grunt-bower": "*",
- "grunt-bower-task": "*",
+ "grunt-conventional-changelog": "latest",
+ "grunt-coveralls": "^1.0.1",
"grunt-karma": "latest",
- "grunt-conventional-changelog": "0.0.12",
"grunt-zip": "*",
- "karma": "^0.13.19",
- "karma-chrome-launcher": "~0.1.2",
- "karma-firefox-launcher": "~0.1.3",
- "karma-jasmine": "~0.1.5",
- "karma-mocha-reporter": "0.2.8",
- "karma-jasmine": "~0.1.5",
- "karma-chrome-launcher": "~0.1.2",
- "karma-phantomjs-launcher": "~0.1.2",
- "karma-firefox-launcher": "~0.1.3"
+ "jasmine-core": "^2.5.2",
+ "karma": "^1.3.0",
+ "karma-chrome-launcher": "~2.0.0",
+ "karma-coverage": "^1.1.1",
+ "karma-firefox-launcher": "~v1.0.0",
+ "karma-jasmine": "^1.1.0",
+ "karma-mocha-reporter": "^2.2.0",
+ "karma-phantomjs-launcher": "~v1.0.2"
},
"scripts": {
"test": "grunt test --verbose"
},
"license": "MIT"
-}
+}
\ No newline at end of file
diff --git a/src/restangular.js b/src/restangular.js
index 5362b84c..ff7d5e24 100644
--- a/src/restangular.js
+++ b/src/restangular.js
@@ -1,4 +1,5 @@
-(function (root, factory) {
+(function(root, factory) {
+ /* global define, require */
// https://github.com/umdjs/umd/blob/master/templates/returnExports.js
if (typeof define === 'function' && define.amd) {
define(['lodash', 'angular'], factory);
@@ -8,1373 +9,1439 @@
// No global export, Restangular will register itself as Angular.js module
factory(root._, root.angular);
}
-}(this, function (_, angular) {
-
-var restangular = angular.module('restangular', []);
-
-restangular.provider('Restangular', function() {
- // Configuration
- var Configurer = {};
- Configurer.init = function(object, config) {
- object.configuration = config;
-
- /**
- * Those are HTTP safe methods for which there is no need to pass any data with the request.
- */
- var safeMethods= ['get', 'head', 'options', 'trace', 'getlist'];
- config.isSafe = function(operation) {
- return _.includes(safeMethods, operation.toLowerCase());
- };
+}(this, function(_, angular) {
+
+ var restangular = angular.module('restangular', []);
+
+ restangular.provider('Restangular', function() {
+ // Configuration
+ var Configurer = {};
+ Configurer.init = function(object, config) {
+ object.configuration = config;
+
+ /**
+ * Those are HTTP safe methods for which there is no need to pass any data with the request.
+ */
+ var safeMethods = ['get', 'head', 'options', 'trace', 'getlist'];
+ config.isSafe = function(operation) {
+ return _.includes(safeMethods, operation.toLowerCase());
+ };
- var absolutePattern = /^https?:\/\//i;
- config.isAbsoluteUrl = function(string) {
- return _.isUndefined(config.absoluteUrl) || _.isNull(config.absoluteUrl) ?
- string && absolutePattern.test(string) :
- config.absoluteUrl;
- };
+ var absolutePattern = /^https?:\/\//i;
+ config.isAbsoluteUrl = function(string) {
+ return _.isUndefined(config.absoluteUrl) || _.isNull(config.absoluteUrl) ?
+ string && absolutePattern.test(string) :
+ config.absoluteUrl;
+ };
- config.absoluteUrl = _.isUndefined(config.absoluteUrl) ? true : config.absoluteUrl;
- object.setSelfLinkAbsoluteUrl = function(value) {
- config.absoluteUrl = value;
- };
- /**
- * This is the BaseURL to be used with Restangular
- */
- config.baseUrl = _.isUndefined(config.baseUrl) ? '' : config.baseUrl;
- object.setBaseUrl = function(newBaseUrl) {
- config.baseUrl = /\/$/.test(newBaseUrl) ?
- newBaseUrl.substring(0, newBaseUrl.length-1) :
- newBaseUrl;
- return this;
- };
+ config.absoluteUrl = _.isUndefined(config.absoluteUrl) ? true : config.absoluteUrl;
+ object.setSelfLinkAbsoluteUrl = function(value) {
+ config.absoluteUrl = value;
+ };
+ /**
+ * This is the BaseURL to be used with Restangular
+ */
+ config.baseUrl = _.isUndefined(config.baseUrl) ? '' : config.baseUrl;
+ object.setBaseUrl = function(newBaseUrl) {
+ config.baseUrl = /\/$/.test(newBaseUrl) ?
+ newBaseUrl.substring(0, newBaseUrl.length - 1) :
+ newBaseUrl;
+ return this;
+ };
- /**
- * Sets the extra fields to keep from the parents
- */
- config.extraFields = config.extraFields || [];
- object.setExtraFields = function(newExtraFields) {
- config.extraFields = newExtraFields;
- return this;
- };
+ /**
+ * Sets the extra fields to keep from the parents
+ */
+ config.extraFields = config.extraFields || [];
+ object.setExtraFields = function(newExtraFields) {
+ config.extraFields = newExtraFields;
+ return this;
+ };
- /**
- * Some default $http parameter to be used in EVERY call
- **/
- config.defaultHttpFields = config.defaultHttpFields || {};
- object.setDefaultHttpFields = function(values) {
- config.defaultHttpFields = values;
- return this;
- };
-
- /**
- * Always return plain data, no restangularized object
- **/
- config.plainByDefault = config.plainByDefault || false;
- object.setPlainByDefault = function(value) {
- config.plainByDefault = value === true ? true : false;
- return this;
- }
-
- config.withHttpValues = function(httpLocalConfig, obj) {
- return _.defaults(obj, httpLocalConfig, config.defaultHttpFields);
- };
+ /**
+ * Some default $http parameter to be used in EVERY call
+ **/
+ config.defaultHttpFields = config.defaultHttpFields || {};
+ object.setDefaultHttpFields = function(values) {
+ config.defaultHttpFields = values;
+ return this;
+ };
- config.encodeIds = _.isUndefined(config.encodeIds) ? true : config.encodeIds;
- object.setEncodeIds = function(encode) {
- config.encodeIds = encode;
- };
+ /**
+ * Always return plain data, no restangularized object
+ **/
+ config.plainByDefault = config.plainByDefault || false;
+ object.setPlainByDefault = function(value) {
+ config.plainByDefault = value === true ? true : false;
+ return this;
+ };
- config.defaultRequestParams = config.defaultRequestParams || {
- get: {},
- post: {},
- put: {},
- remove: {},
- common: {}
- };
+ config.withHttpValues = function(httpLocalConfig, obj) {
+ return _.defaults(obj, httpLocalConfig, config.defaultHttpFields);
+ };
- object.setDefaultRequestParams = function(param1, param2) {
- var methods = [],
+ config.encodeIds = _.isUndefined(config.encodeIds) ? true : config.encodeIds;
+ object.setEncodeIds = function(encode) {
+ config.encodeIds = encode;
+ };
+
+ config.defaultRequestParams = config.defaultRequestParams || {
+ get: {},
+ post: {},
+ put: {},
+ remove: {},
+ common: {}
+ };
+
+ object.setDefaultRequestParams = function(param1, param2) {
+ var methods = [],
params = param2 || param1;
- if (!_.isUndefined(param2)) {
- if (_.isArray(param1)) {
- methods = param1;
+ if (!_.isUndefined(param2)) {
+ if (_.isArray(param1)) {
+ methods = param1;
+ } else {
+ methods.push(param1);
+ }
} else {
- methods.push(param1);
+ methods.push('common');
}
- } else {
- methods.push('common');
- }
- _.each(methods, function (method) {
- config.defaultRequestParams[method] = params;
- });
- return this;
- };
-
- object.requestParams = config.defaultRequestParams;
+ _.each(methods, function(method) {
+ config.defaultRequestParams[method] = params;
+ });
+ return this;
+ };
- config.defaultHeaders = config.defaultHeaders || {};
- object.setDefaultHeaders = function(headers) {
- config.defaultHeaders = headers;
- object.defaultHeaders = config.defaultHeaders;
- return this;
- };
+ object.requestParams = config.defaultRequestParams;
- object.defaultHeaders = config.defaultHeaders;
+ config.defaultHeaders = config.defaultHeaders || {};
+ object.setDefaultHeaders = function(headers) {
+ config.defaultHeaders = headers;
+ object.defaultHeaders = config.defaultHeaders;
+ return this;
+ };
- /**
- * Method overriders will set which methods are sent via POST with an X-HTTP-Method-Override
- **/
- config.methodOverriders = config.methodOverriders || [];
- object.setMethodOverriders = function(values) {
- var overriders = _.extend([], values);
- if (config.isOverridenMethod('delete', overriders)) {
- overriders.push('remove');
- }
- config.methodOverriders = overriders;
- return this;
- };
+ object.defaultHeaders = config.defaultHeaders;
- config.jsonp = _.isUndefined(config.jsonp) ? false : config.jsonp;
- object.setJsonp = function(active) {
- config.jsonp = active;
- };
+ /**
+ * Method overriders will set which methods are sent via POST with an X-HTTP-Method-Override
+ **/
+ config.methodOverriders = config.methodOverriders || [];
+ object.setMethodOverriders = function(values) {
+ var overriders = _.extend([], values);
+ if (config.isOverridenMethod('delete', overriders)) {
+ overriders.push('remove');
+ }
+ config.methodOverriders = overriders;
+ return this;
+ };
- config.isOverridenMethod = function(method, values) {
- var search = values || config.methodOverriders;
- return !_.isUndefined(_.find(search, function(one) {
- return one.toLowerCase() === method.toLowerCase();
- }));
- };
+ config.jsonp = _.isUndefined(config.jsonp) ? false : config.jsonp;
+ object.setJsonp = function(active) {
+ config.jsonp = active;
+ };
- /**
- * Sets the URL creator type. For now, only Path is created. In the future we'll have queryParams
- **/
- config.urlCreator = config.urlCreator || 'path';
- object.setUrlCreator = function(name) {
- if (!_.has(config.urlCreatorFactory, name)) {
- throw new Error('URL Path selected isn\'t valid');
- }
+ config.isOverridenMethod = function(method, values) {
+ var search = values || config.methodOverriders;
+ return !_.isUndefined(_.find(search, function(one) {
+ return one.toLowerCase() === method.toLowerCase();
+ }));
+ };
- config.urlCreator = name;
- return this;
- };
+ /**
+ * Sets the URL creator type. For now, only Path is created. In the future we'll have queryParams
+ **/
+ config.urlCreator = config.urlCreator || 'path';
+ object.setUrlCreator = function(name) {
+ if (!_.has(config.urlCreatorFactory, name)) {
+ throw new Error('URL Path selected isn\'t valid');
+ }
- /**
- * You can set the restangular fields here. The 3 required fields for Restangular are:
- *
- * id: Id of the element
- * route: name of the route of this element
- * parentResource: the reference to the parent resource
- *
- * All of this fields except for id, are handled (and created) by Restangular. By default,
- * the field values will be id, route and parentResource respectively
- */
- config.restangularFields = config.restangularFields || {
- id: 'id',
- route: 'route',
- parentResource: 'parentResource',
- restangularCollection: 'restangularCollection',
- cannonicalId: '__cannonicalId',
- etag: 'restangularEtag',
- selfLink: 'href',
- get: 'get',
- getList: 'getList',
- put: 'put',
- post: 'post',
- remove: 'remove',
- head: 'head',
- trace: 'trace',
- options: 'options',
- patch: 'patch',
- getRestangularUrl: 'getRestangularUrl',
- getRequestedUrl: 'getRequestedUrl',
- putElement: 'putElement',
- addRestangularMethod: 'addRestangularMethod',
- getParentList: 'getParentList',
- clone: 'clone',
- ids: 'ids',
- httpConfig: '_$httpConfig',
- reqParams: 'reqParams',
- one: 'one',
- all: 'all',
- several: 'several',
- oneUrl: 'oneUrl',
- allUrl: 'allUrl',
- customPUT: 'customPUT',
- customPATCH: 'customPATCH',
- customPOST: 'customPOST',
- customDELETE: 'customDELETE',
- customGET: 'customGET',
- customGETLIST: 'customGETLIST',
- customOperation: 'customOperation',
- doPUT: 'doPUT',
- doPATCH: 'doPATCH',
- doPOST: 'doPOST',
- doDELETE: 'doDELETE',
- doGET: 'doGET',
- doGETLIST: 'doGETLIST',
- fromServer: 'fromServer',
- withConfig: 'withConfig',
- withHttpConfig: 'withHttpConfig',
- singleOne: 'singleOne',
- plain: 'plain',
- save: 'save',
- restangularized: 'restangularized'
- };
- object.setRestangularFields = function(resFields) {
- config.restangularFields =
- _.extend(config.restangularFields, resFields);
- return this;
- };
+ config.urlCreator = name;
+ return this;
+ };
- config.isRestangularized = function(obj) {
- return !!obj[config.restangularFields.restangularized];
- };
+ /**
+ * You can set the restangular fields here. The 3 required fields for Restangular are:
+ *
+ * id: Id of the element
+ * route: name of the route of this element
+ * parentResource: the reference to the parent resource
+ *
+ * All of this fields except for id, are handled (and created) by Restangular. By default,
+ * the field values will be id, route and parentResource respectively
+ */
+ config.restangularFields = config.restangularFields || {
+ id: 'id',
+ route: 'route',
+ parentResource: 'parentResource',
+ restangularCollection: 'restangularCollection',
+ cannonicalId: '__cannonicalId',
+ etag: 'restangularEtag',
+ selfLink: 'href',
+ get: 'get',
+ getList: 'getList',
+ put: 'put',
+ post: 'post',
+ remove: 'remove',
+ head: 'head',
+ trace: 'trace',
+ options: 'options',
+ patch: 'patch',
+ getRestangularUrl: 'getRestangularUrl',
+ getRequestedUrl: 'getRequestedUrl',
+ putElement: 'putElement',
+ addRestangularMethod: 'addRestangularMethod',
+ getParentList: 'getParentList',
+ clone: 'clone',
+ ids: 'ids',
+ httpConfig: '_$httpConfig',
+ reqParams: 'reqParams',
+ one: 'one',
+ all: 'all',
+ several: 'several',
+ oneUrl: 'oneUrl',
+ allUrl: 'allUrl',
+ customPUT: 'customPUT',
+ customPATCH: 'customPATCH',
+ customPOST: 'customPOST',
+ customDELETE: 'customDELETE',
+ customGET: 'customGET',
+ customGETLIST: 'customGETLIST',
+ customOperation: 'customOperation',
+ doPUT: 'doPUT',
+ doPATCH: 'doPATCH',
+ doPOST: 'doPOST',
+ doDELETE: 'doDELETE',
+ doGET: 'doGET',
+ doGETLIST: 'doGETLIST',
+ fromServer: 'fromServer',
+ withConfig: 'withConfig',
+ withHttpConfig: 'withHttpConfig',
+ singleOne: 'singleOne',
+ plain: 'plain',
+ save: 'save',
+ restangularized: 'restangularized'
+ };
+ object.setRestangularFields = function(resFields) {
+ config.restangularFields =
+ _.extend(config.restangularFields, resFields);
+ return this;
+ };
- config.setFieldToElem = function(field, elem, value) {
- var properties = field.split('.');
- var idValue = elem;
- _.each(_.initial(properties), function(prop) {
- idValue[prop] = {};
- idValue = idValue[prop];
- });
- idValue[_.last(properties)] = value;
- return this;
- };
+ config.isRestangularized = function(obj) {
+ return !!obj[config.restangularFields.restangularized];
+ };
- config.getFieldFromElem = function(field, elem) {
- var properties = field.split('.');
- var idValue = elem;
- _.each(properties, function(prop) {
- if (idValue) {
+ config.setFieldToElem = function(field, elem, value) {
+ var properties = field.split('.');
+ var idValue = elem;
+ _.each(_.initial(properties), function(prop) {
+ idValue[prop] = {};
idValue = idValue[prop];
- }
- });
- return angular.copy(idValue);
- };
-
- config.setIdToElem = function(elem, id /*, route */) {
- config.setFieldToElem(config.restangularFields.id, elem, id);
- return this;
- };
+ });
+ idValue[_.last(properties)] = value;
+ return this;
+ };
- config.getIdFromElem = function(elem) {
- return config.getFieldFromElem(config.restangularFields.id, elem);
- };
+ config.getFieldFromElem = function(field, elem) {
+ var properties = field.split('.');
+ var idValue = elem;
+ _.each(properties, function(prop) {
+ if (idValue) {
+ idValue = idValue[prop];
+ }
+ });
+ return angular.copy(idValue);
+ };
- config.isValidId = function(elemId) {
- return '' !== elemId && !_.isUndefined(elemId) && !_.isNull(elemId);
- };
+ config.setIdToElem = function(elem, id /*, route */ ) {
+ config.setFieldToElem(config.restangularFields.id, elem, id);
+ return this;
+ };
- config.setUrlToElem = function(elem, url /*, route */) {
- config.setFieldToElem(config.restangularFields.selfLink, elem, url);
- return this;
- };
+ config.getIdFromElem = function(elem) {
+ return config.getFieldFromElem(config.restangularFields.id, elem);
+ };
- config.getUrlFromElem = function(elem) {
- return config.getFieldFromElem(config.restangularFields.selfLink, elem);
- };
+ config.isValidId = function(elemId) {
+ return '' !== elemId && !_.isUndefined(elemId) && !_.isNull(elemId);
+ };
- config.useCannonicalId = _.isUndefined(config.useCannonicalId) ? false : config.useCannonicalId;
- object.setUseCannonicalId = function(value) {
- config.useCannonicalId = value;
- return this;
- };
+ config.setUrlToElem = function(elem, url /*, route */ ) {
+ config.setFieldToElem(config.restangularFields.selfLink, elem, url);
+ return this;
+ };
- config.getCannonicalIdFromElem = function(elem) {
- var cannonicalId = elem[config.restangularFields.cannonicalId];
- var actualId = config.isValidId(cannonicalId) ? cannonicalId : config.getIdFromElem(elem);
- return actualId;
- };
+ config.getUrlFromElem = function(elem) {
+ return config.getFieldFromElem(config.restangularFields.selfLink, elem);
+ };
- /**
- * Sets the Response parser. This is used in case your response isn't directly the data.
- * For example if you have a response like {meta: {'meta'}, data: {name: 'Gonto'}}
- * you can extract this data which is the one that needs wrapping
- *
- * The ResponseExtractor is a function that receives the response and the method executed.
- */
+ config.useCannonicalId = _.isUndefined(config.useCannonicalId) ? false : config.useCannonicalId;
+ object.setUseCannonicalId = function(value) {
+ config.useCannonicalId = value;
+ return this;
+ };
- config.responseInterceptors = config.responseInterceptors || [];
+ config.getCannonicalIdFromElem = function(elem) {
+ var cannonicalId = elem[config.restangularFields.cannonicalId];
+ var actualId = config.isValidId(cannonicalId) ? cannonicalId : config.getIdFromElem(elem);
+ return actualId;
+ };
- config.defaultResponseInterceptor = function(data /*, operation, what, url, response, deferred */) {
- return data;
- };
+ /**
+ * Sets the Response parser. This is used in case your response isn't directly the data.
+ * For example if you have a response like {meta: {'meta'}, data: {name: 'Gonto'}}
+ * you can extract this data which is the one that needs wrapping
+ *
+ * The ResponseExtractor is a function that receives the response and the method executed.
+ */
- config.responseExtractor = function(data, operation, what, url, response, deferred) {
- var interceptors = angular.copy(config.responseInterceptors);
- interceptors.push(config.defaultResponseInterceptor);
- var theData = data;
- _.each(interceptors, function(interceptor) {
- theData = interceptor(theData, operation,
- what, url, response, deferred);
- });
- return theData;
- };
+ config.responseInterceptors = config.responseInterceptors || [];
- object.addResponseInterceptor = function(extractor) {
- config.responseInterceptors.push(extractor);
- return this;
- };
+ config.defaultResponseInterceptor = function(data /*, operation, what, url, response, deferred */ ) {
+ return data;
+ };
- config.errorInterceptors = config.errorInterceptors || [];
- object.addErrorInterceptor = function(interceptor) {
- config.errorInterceptors.push(interceptor);
- return this;
- };
+ config.responseExtractor = function(data, operation, what, url, response, deferred) {
+ var interceptors = angular.copy(config.responseInterceptors);
+ interceptors.push(config.defaultResponseInterceptor);
+ var theData = data;
+ _.each(interceptors, function(interceptor) {
+ theData = interceptor(theData, operation,
+ what, url, response, deferred);
+ });
+ return theData;
+ };
- object.setResponseInterceptor = object.addResponseInterceptor;
- object.setResponseExtractor = object.addResponseInterceptor;
- object.setErrorInterceptor = object.addErrorInterceptor;
+ object.addResponseInterceptor = function(extractor) {
+ config.responseInterceptors.push(extractor);
+ return this;
+ };
- /**
- * Response interceptor is called just before resolving promises.
- */
+ config.errorInterceptors = config.errorInterceptors || [];
+ object.addErrorInterceptor = function(interceptor) {
+ config.errorInterceptors.push(interceptor);
+ return this;
+ };
+ object.setResponseInterceptor = object.addResponseInterceptor;
+ object.setResponseExtractor = object.addResponseInterceptor;
+ object.setErrorInterceptor = object.addErrorInterceptor;
- /**
- * Request interceptor is called before sending an object to the server.
- */
- config.requestInterceptors = config.requestInterceptors || [];
+ /**
+ * Response interceptor is called just before resolving promises.
+ */
- config.defaultInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
- return {
- element: element,
- headers: headers,
- params: params,
- httpConfig: httpConfig
- };
- };
- config.fullRequestInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
- var interceptors = angular.copy(config.requestInterceptors);
- var defaultRequest = config.defaultInterceptor(element, operation, path, url, headers, params, httpConfig);
- return _.reduce(interceptors, function(request, interceptor) {
- return _.extend(request, interceptor(request.element, operation,
- path, url, request.headers, request.params, request.httpConfig));
- }, defaultRequest);
- };
+ /**
+ * Request interceptor is called before sending an object to the server.
+ */
+ config.requestInterceptors = config.requestInterceptors || [];
- object.addRequestInterceptor = function(interceptor) {
- config.requestInterceptors.push(function(elem, operation, path, url, headers, params, httpConfig) {
+ config.defaultInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
return {
+ element: element,
headers: headers,
params: params,
- element: interceptor(elem, operation, path, url),
httpConfig: httpConfig
};
- });
- return this;
- };
+ };
- object.setRequestInterceptor = object.addRequestInterceptor;
+ config.fullRequestInterceptor = function(element, operation, path, url, headers, params, httpConfig) {
+ var interceptors = angular.copy(config.requestInterceptors);
+ var defaultRequest = config.defaultInterceptor(element, operation, path, url, headers, params, httpConfig);
+ return _.reduce(interceptors, function(request, interceptor) {
+ return _.extend(request, interceptor(request.element, operation,
+ path, url, request.headers, request.params, request.httpConfig));
+ }, defaultRequest);
+ };
- object.addFullRequestInterceptor = function(interceptor) {
- config.requestInterceptors.push(interceptor);
- return this;
- };
+ object.addRequestInterceptor = function(interceptor) {
+ config.requestInterceptors.push(function(elem, operation, path, url, headers, params, httpConfig) {
+ return {
+ headers: headers,
+ params: params,
+ element: interceptor(elem, operation, path, url),
+ httpConfig: httpConfig
+ };
+ });
+ return this;
+ };
- object.setFullRequestInterceptor = object.addFullRequestInterceptor;
+ object.setRequestInterceptor = object.addRequestInterceptor;
- config.onBeforeElemRestangularized = config.onBeforeElemRestangularized || function(elem) {
- return elem;
- };
- object.setOnBeforeElemRestangularized = function(post) {
- config.onBeforeElemRestangularized = post;
- return this;
- };
+ object.addFullRequestInterceptor = function(interceptor) {
+ config.requestInterceptors.push(interceptor);
+ return this;
+ };
- object.setRestangularizePromiseInterceptor = function(interceptor) {
- config.restangularizePromiseInterceptor = interceptor;
- return this;
- };
+ object.setFullRequestInterceptor = object.addFullRequestInterceptor;
- /**
- * This method is called after an element has been "Restangularized".
- *
- * It receives the element, a boolean indicating if it's an element or a collection
- * and the name of the model
- *
- */
- config.onElemRestangularized = config.onElemRestangularized || function(elem) {
- return elem;
- };
- object.setOnElemRestangularized = function(post) {
- config.onElemRestangularized = post;
- return this;
- };
+ config.onBeforeElemRestangularized = config.onBeforeElemRestangularized || function(elem) {
+ return elem;
+ };
+ object.setOnBeforeElemRestangularized = function(post) {
+ config.onBeforeElemRestangularized = post;
+ return this;
+ };
- config.shouldSaveParent = config.shouldSaveParent || function() {
- return true;
- };
- object.setParentless = function(values) {
- if (_.isArray(values)) {
- config.shouldSaveParent = function(route) {
- return !_.includes(values, route);
- };
- } else if (_.isBoolean(values)) {
- config.shouldSaveParent = function() {
- return !values;
- };
- }
- return this;
- };
+ object.setRestangularizePromiseInterceptor = function(interceptor) {
+ config.restangularizePromiseInterceptor = interceptor;
+ return this;
+ };
- /**
- * This lets you set a suffix to every request.
- *
- * For example, if your api requires that for JSon requests you do /users/123.json, you can set that
- * in here.
- *
- *
- * By default, the suffix is null
- */
- config.suffix = _.isUndefined(config.suffix) ? null : config.suffix;
- object.setRequestSuffix = function(newSuffix) {
- config.suffix = newSuffix;
- return this;
- };
+ /**
+ * This method is called after an element has been "Restangularized".
+ *
+ * It receives the element, a boolean indicating if it's an element or a collection
+ * and the name of the model
+ *
+ */
+ config.onElemRestangularized = config.onElemRestangularized || function(elem) {
+ return elem;
+ };
+ object.setOnElemRestangularized = function(post) {
+ config.onElemRestangularized = post;
+ return this;
+ };
- /**
- * Add element transformers for certain routes.
- */
- config.transformers = config.transformers || {};
- object.addElementTransformer = function(type, secondArg, thirdArg) {
- var isCollection = null;
- var transformer = null;
- if (arguments.length === 2) {
- transformer = secondArg;
- } else {
- transformer = thirdArg;
- isCollection = secondArg;
- }
+ config.shouldSaveParent = config.shouldSaveParent || function() {
+ return true;
+ };
+ object.setParentless = function(values) {
+ if (_.isArray(values)) {
+ config.shouldSaveParent = function(route) {
+ return !_.includes(values, route);
+ };
+ } else if (_.isBoolean(values)) {
+ config.shouldSaveParent = function() {
+ return !values;
+ };
+ }
+ return this;
+ };
- var typeTransformers = config.transformers[type];
- if (!typeTransformers) {
- typeTransformers = config.transformers[type] = [];
- }
+ /**
+ * This lets you set a suffix to every request.
+ *
+ * For example, if your api requires that for JSon requests you do /users/123.json, you can set that
+ * in here.
+ *
+ *
+ * By default, the suffix is null
+ */
+ config.suffix = _.isUndefined(config.suffix) ? null : config.suffix;
+ object.setRequestSuffix = function(newSuffix) {
+ config.suffix = newSuffix;
+ return this;
+ };
- typeTransformers.push(function(coll, elem) {
- if (_.isNull(isCollection) || (coll === isCollection)) {
- return transformer(elem);
+ /**
+ * Add element transformers for certain routes.
+ */
+ config.transformers = config.transformers || {};
+ config.matchTransformers = config.matchTransformers || [];
+ object.addElementTransformer = function(type, secondArg, thirdArg) {
+ var isCollection = null;
+ var transformer = null;
+ if (arguments.length === 2) {
+ transformer = secondArg;
+ } else {
+ transformer = thirdArg;
+ isCollection = secondArg;
}
- return elem;
- });
- return object;
- };
+ var transformerFn = function(coll, elem) {
+ if (_.isNull(isCollection) || (coll === isCollection)) {
+ return transformer(elem);
+ }
+ return elem;
+ };
- object.extendCollection = function(route, fn) {
- return object.addElementTransformer(route, true, fn);
- };
+ if (_.isRegExp(type)) {
+ config.matchTransformers.push({
+ regexp: type,
+ transformer: transformerFn
+ });
+ } else {
+ if (!config.transformers[type]) {
+ config.transformers[type] = [];
+ }
+ config.transformers[type].push(transformerFn);
+ }
- object.extendModel = function(route, fn) {
- return object.addElementTransformer(route, false, fn);
- };
+ return object;
+ };
- config.transformElem = function(elem, isCollection, route, Restangular, force) {
- if (!force && !config.transformLocalElements && !elem[config.restangularFields.fromServer]) {
- return elem;
- }
- var typeTransformers = config.transformers[route];
- var changedElem = elem;
- if (typeTransformers) {
- _.each(typeTransformers, function(transformer) {
- changedElem = transformer(isCollection, changedElem);
- });
- }
- return config.onElemRestangularized(changedElem, isCollection, route, Restangular);
- };
+ object.extendCollection = function(route, fn) {
+ return object.addElementTransformer(route, true, fn);
+ };
- config.transformLocalElements = _.isUndefined(config.transformLocalElements) ?
- false :
- config.transformLocalElements;
+ object.extendModel = function(route, fn) {
+ return object.addElementTransformer(route, false, fn);
+ };
- object.setTransformOnlyServerElements = function(active) {
- config.transformLocalElements = !active;
- };
+ config.transformElem = function(elem, isCollection, route, Restangular, force) {
+ if (!force && !config.transformLocalElements && !elem[config.restangularFields.fromServer]) {
+ return elem;
+ }
- config.fullResponse = _.isUndefined(config.fullResponse) ? false : config.fullResponse;
- object.setFullResponse = function(full) {
- config.fullResponse = full;
- return this;
- };
+ var changedElem = elem;
+ var matchTransformers = config.matchTransformers;
+ if (matchTransformers) {
+ _.each(matchTransformers, function(transformer) {
+ if (transformer.regexp.test(route)) {
+ changedElem = transformer.transformer(isCollection, changedElem);
+ }
+ });
+ }
+
+ var typeTransformers = config.transformers[route];
+ if (typeTransformers) {
+ _.each(typeTransformers, function(transformer) {
+ changedElem = transformer(isCollection, changedElem);
+ });
+ }
+ return config.onElemRestangularized(changedElem, isCollection, route, Restangular);
+ };
- //Internal values and functions
- config.urlCreatorFactory = {};
+ config.transformLocalElements = _.isUndefined(config.transformLocalElements) ?
+ false :
+ config.transformLocalElements;
- /**
- * Base URL Creator. Base prototype for everything related to it
- **/
+ object.setTransformOnlyServerElements = function(active) {
+ config.transformLocalElements = !active;
+ };
- var BaseCreator = function() {
- };
+ config.fullResponse = _.isUndefined(config.fullResponse) ? false : config.fullResponse;
+ object.setFullResponse = function(full) {
+ config.fullResponse = full;
+ return this;
+ };
- BaseCreator.prototype.setConfig = function(config) {
- this.config = config;
- return this;
- };
- BaseCreator.prototype.parentsArray = function(current) {
- var parents = [];
- while(current) {
- parents.push(current);
- current = current[this.config.restangularFields.parentResource];
- }
- return parents.reverse();
- };
+ //Internal values and functions
+ config.urlCreatorFactory = {};
+
+ /**
+ * Base URL Creator. Base prototype for everything related to it
+ **/
+
+ var BaseCreator = function() {};
- function RestangularResource(config, $http, url, configurer) {
- var resource = {};
- _.each(_.keys(configurer), function(key) {
- var value = configurer[key];
+ BaseCreator.prototype.setConfig = function(config) {
+ this.config = config;
+ return this;
+ };
- // Add default parameters
- value.params = _.extend({}, value.params, config.defaultRequestParams[value.method.toLowerCase()]);
- // We don't want the ? if no params are there
- if (_.isEmpty(value.params)) {
- delete value.params;
+ BaseCreator.prototype.parentsArray = function(current) {
+ var parents = [];
+ while (current) {
+ parents.push(current);
+ current = current[this.config.restangularFields.parentResource];
}
+ return parents.reverse();
+ };
- if (config.isSafe(value.method)) {
+ function RestangularResource(config, $http, url, configurer) {
+ var resource = {};
+ _.each(_.keys(configurer), function(key) {
+ var value = configurer[key];
- resource[key] = function() {
- return $http(_.extend(value, {
- url: url
- }));
- };
+ // Add default parameters
+ value.params = _.extend({}, value.params, config.defaultRequestParams[value.method.toLowerCase()]);
+ // We don't want the ? if no params are there
+ if (_.isEmpty(value.params)) {
+ delete value.params;
+ }
- } else {
+ if (config.isSafe(value.method)) {
- resource[key] = function(data) {
- return $http(_.extend(value, {
- url: url,
- data: data
- }));
- };
+ resource[key] = function() {
+ return $http(_.extend(value, {
+ url: url
+ }));
+ };
- }
- });
+ } else {
- return resource;
- }
+ resource[key] = function(data) {
+ return $http(_.extend(value, {
+ url: url,
+ data: data
+ }));
+ };
- BaseCreator.prototype.resource = function(current, $http, localHttpConfig, callHeaders, callParams, what, etag,operation) {
+ }
+ });
- var params = _.defaults(callParams || {}, this.config.defaultRequestParams.common);
- var headers = _.defaults(callHeaders || {}, this.config.defaultHeaders);
+ return resource;
+ }
- if (etag) {
- if (!config.isSafe(operation)) {
- headers['If-Match'] = etag;
- } else {
- headers['If-None-Match'] = etag;
+ BaseCreator.prototype.resource = function(current, $http, localHttpConfig, callHeaders, callParams, what, etag, operation) {
+
+ var params = _.defaults(callParams || {}, this.config.defaultRequestParams.common);
+ var headers = _.defaults(callHeaders || {}, this.config.defaultHeaders);
+
+ if (etag) {
+ if (!config.isSafe(operation)) {
+ headers['If-Match'] = etag;
+ } else {
+ headers['If-None-Match'] = etag;
+ }
}
- }
- var url = this.base(current);
+ var url = this.base(current);
- if (what) {
- var add = '';
- if (!/\/$/.test(url)) {
- add += '/';
+ if (what || what === 0) {
+ var add = '';
+ if (!/\/$/.test(url)) {
+ add += '/';
+ }
+ add += what;
+ url += add;
}
- add += what;
- url += add;
- }
- if (this.config.suffix &&
- url.indexOf(this.config.suffix, url.length - this.config.suffix.length) === -1 &&
- !this.config.getUrlFromElem(current)) {
+ if (this.config.suffix &&
+ url.indexOf(this.config.suffix, url.length - this.config.suffix.length) === -1 &&
+ !this.config.getUrlFromElem(current)) {
url += this.config.suffix;
- }
-
- current[this.config.restangularFields.httpConfig] = undefined;
+ }
- return RestangularResource(this.config, $http, url, {
- getList: this.config.withHttpValues(localHttpConfig,
- {method: 'GET',
- params: params,
- headers: headers}),
+ current[this.config.restangularFields.httpConfig] = undefined;
- get: this.config.withHttpValues(localHttpConfig,
- {method: 'GET',
- params: params,
- headers: headers}),
+ return RestangularResource(this.config, $http, url, {
+ getList: this.config.withHttpValues(localHttpConfig, {
+ method: 'GET',
+ params: params,
+ headers: headers
+ }),
- jsonp: this.config.withHttpValues(localHttpConfig,
- {method: 'jsonp',
- params: params,
- headers: headers}),
+ get: this.config.withHttpValues(localHttpConfig, {
+ method: 'GET',
+ params: params,
+ headers: headers
+ }),
- put: this.config.withHttpValues(localHttpConfig,
- {method: 'PUT',
- params: params,
- headers: headers}),
+ jsonp: this.config.withHttpValues(localHttpConfig, {
+ method: 'jsonp',
+ params: params,
+ headers: headers
+ }),
- post: this.config.withHttpValues(localHttpConfig,
- {method: 'POST',
- params: params,
- headers: headers}),
+ put: this.config.withHttpValues(localHttpConfig, {
+ method: 'PUT',
+ params: params,
+ headers: headers
+ }),
- remove: this.config.withHttpValues(localHttpConfig,
- {method: 'DELETE',
- params: params,
- headers: headers}),
+ post: this.config.withHttpValues(localHttpConfig, {
+ method: 'POST',
+ params: params,
+ headers: headers
+ }),
- head: this.config.withHttpValues(localHttpConfig,
- {method: 'HEAD',
- params: params,
- headers: headers}),
+ remove: this.config.withHttpValues(localHttpConfig, {
+ method: 'DELETE',
+ params: params,
+ headers: headers
+ }),
- trace: this.config.withHttpValues(localHttpConfig,
- {method: 'TRACE',
- params: params,
- headers: headers}),
+ head: this.config.withHttpValues(localHttpConfig, {
+ method: 'HEAD',
+ params: params,
+ headers: headers
+ }),
- options: this.config.withHttpValues(localHttpConfig,
- {method: 'OPTIONS',
- params: params,
- headers: headers}),
+ trace: this.config.withHttpValues(localHttpConfig, {
+ method: 'TRACE',
+ params: params,
+ headers: headers
+ }),
- patch: this.config.withHttpValues(localHttpConfig,
- {method: 'PATCH',
- params: params,
- headers: headers})
- });
- };
+ options: this.config.withHttpValues(localHttpConfig, {
+ method: 'OPTIONS',
+ params: params,
+ headers: headers
+ }),
- /**
- * This is the Path URL creator. It uses Path to show Hierarchy in the Rest API.
- * This means that if you have an Account that then has a set of Buildings, a URL to a building
- * would be /accounts/123/buildings/456
- **/
- var Path = function() {
- };
+ patch: this.config.withHttpValues(localHttpConfig, {
+ method: 'PATCH',
+ params: params,
+ headers: headers
+ })
+ });
+ };
- Path.prototype = new BaseCreator();
+ /**
+ * This is the Path URL creator. It uses Path to show Hierarchy in the Rest API.
+ * This means that if you have an Account that then has a set of Buildings, a URL to a building
+ * would be /accounts/123/buildings/456
+ **/
+ var Path = function() {};
- Path.prototype.normalizeUrl = function (url){
- var parts = /((?:http[s]?:)?\/\/)?(.*)?/.exec(url);
- parts[2] = parts[2].replace(/[\\\/]+/g, '/');
- return (typeof parts[1] !== 'undefined')? parts[1] + parts[2] : parts[2];
- };
+ Path.prototype = new BaseCreator();
- Path.prototype.base = function(current) {
- var __this = this;
- return _.reduce(this.parentsArray(current), function(acum, elem) {
- var elemUrl;
- var elemSelfLink = __this.config.getUrlFromElem(elem);
- if (elemSelfLink) {
- if (__this.config.isAbsoluteUrl(elemSelfLink)) {
- return elemSelfLink;
- } else {
- elemUrl = elemSelfLink;
- }
- } else {
- elemUrl = elem[__this.config.restangularFields.route];
+ Path.prototype.normalizeUrl = function(url) {
+ var parts = /((?:http[s]?:)?\/\/)?(.*)?/.exec(url);
+ parts[2] = parts[2].replace(/[\\\/]+/g, '/');
+ return (typeof parts[1] !== 'undefined') ? parts[1] + parts[2] : parts[2];
+ };
- if (elem[__this.config.restangularFields.restangularCollection]) {
- var ids = elem[__this.config.restangularFields.ids];
- if (ids) {
- elemUrl += '/' + ids.join(',');
- }
- } else {
- var elemId;
- if (__this.config.useCannonicalId) {
- elemId = __this.config.getCannonicalIdFromElem(elem);
+ Path.prototype.base = function(current) {
+ var __this = this;
+ return _.reduce(this.parentsArray(current), function(acum, elem) {
+ var elemUrl;
+ var elemSelfLink = __this.config.getUrlFromElem(elem);
+ if (elemSelfLink) {
+ if (__this.config.isAbsoluteUrl(elemSelfLink)) {
+ return elemSelfLink;
} else {
- elemId = __this.config.getIdFromElem(elem);
+ elemUrl = elemSelfLink;
}
+ } else {
+ elemUrl = elem[__this.config.restangularFields.route];
- if (config.isValidId(elemId) && !elem.singleOne) {
- elemUrl += '/' + (__this.config.encodeIds ? encodeURIComponent(elemId) : elemId);
+ if (elem[__this.config.restangularFields.restangularCollection]) {
+ var ids = elem[__this.config.restangularFields.ids];
+ if (ids) {
+ elemUrl += '/' + ids.join(',');
+ }
+ } else {
+ var elemId;
+ if (__this.config.useCannonicalId) {
+ elemId = __this.config.getCannonicalIdFromElem(elem);
+ } else {
+ elemId = __this.config.getIdFromElem(elem);
+ }
+
+ if (config.isValidId(elemId) && !elem.singleOne) {
+ elemUrl += '/' + (__this.config.encodeIds ? encodeURIComponent(elemId) : elemId);
+ }
}
}
- }
- acum = acum.replace(/\/$/, '') + '/' + elemUrl;
- return __this.normalizeUrl(acum);
+ acum = acum.replace(/\/$/, '') + '/' + elemUrl;
+ return __this.normalizeUrl(acum);
- }, this.config.baseUrl);
- };
+ }, this.config.baseUrl);
+ };
- Path.prototype.fetchUrl = function(current, what) {
- var baseUrl = this.base(current);
- if (what) {
- baseUrl += '/' + what;
- }
- return baseUrl;
- };
+ Path.prototype.fetchUrl = function(current, what) {
+ var baseUrl = this.base(current);
+ if (what) {
+ baseUrl += '/' + what;
+ }
+ return baseUrl;
+ };
- Path.prototype.fetchRequestedUrl = function(current, what) {
- var url = this.fetchUrl(current, what);
- var params = current[config.restangularFields.reqParams];
-
- // From here on and until the end of fetchRequestedUrl,
- // the code has been kindly borrowed from angular.js
- // The reason for such code bloating is coherence:
- // If the user were to use this for cache management, the
- // serialization of parameters would need to be identical
- // to the one done by angular for cache keys to match.
- function sortedKeys(obj) {
- var keys = [];
- for (var key in obj) {
- if (obj.hasOwnProperty(key)) {
- keys.push(key);
+ Path.prototype.fetchRequestedUrl = function(current, what) {
+ var url = this.fetchUrl(current, what);
+ var params = current[config.restangularFields.reqParams];
+
+ // From here on and until the end of fetchRequestedUrl,
+ // the code has been kindly borrowed from angular.js
+ // The reason for such code bloating is coherence:
+ // If the user were to use this for cache management, the
+ // serialization of parameters would need to be identical
+ // to the one done by angular for cache keys to match.
+ function sortedKeys(obj) {
+ var keys = [];
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ keys.push(key);
+ }
}
+ return keys.sort();
}
- return keys.sort();
- }
- function forEachSorted(obj, iterator, context) {
- var keys = sortedKeys(obj);
- for ( var i = 0; i < keys.length; i++) {
- iterator.call(context, obj[keys[i]], keys[i]);
+ function forEachSorted(obj, iterator, context) {
+ var keys = sortedKeys(obj);
+ for (var i = 0; i < keys.length; i++) {
+ iterator.call(context, obj[keys[i]], keys[i]);
+ }
+ return keys;
}
- return keys;
- }
-
- function encodeUriQuery(val, pctEncodeSpaces) {
- return encodeURIComponent(val).
- replace(/%40/gi, '@').
- replace(/%3A/gi, ':').
- replace(/%24/g, '$').
- replace(/%2C/gi, ',').
- replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
- }
- if (!params) { return url + (this.config.suffix || ''); }
+ function encodeUriQuery(val, pctEncodeSpaces) {
+ return encodeURIComponent(val).
+ replace(/%40/gi, '@').
+ replace(/%3A/gi, ':').
+ replace(/%24/g, '$').
+ replace(/%2C/gi, ',').
+ replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
+ }
- var parts = [];
- forEachSorted(params, function(value, key) {
- if (value === null || value === undefined) { return; }
- if (!angular.isArray(value)) { value = [value]; }
+ if (!params) {
+ return url + (this.config.suffix || '');
+ }
- angular.forEach(value, function(v) {
- if (angular.isObject(v)) {
- v = angular.toJson(v);
+ var parts = [];
+ forEachSorted(params, function(value, key) {
+ if (value === null || value === undefined) {
+ return;
+ }
+ if (!angular.isArray(value)) {
+ value = [value];
}
- parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(v));
+
+ angular.forEach(value, function(v) {
+ if (angular.isObject(v)) {
+ v = angular.toJson(v);
+ }
+ parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(v));
+ });
});
- });
- return url + (this.config.suffix || '') + ((url.indexOf('?') === -1) ? '?' : '&') + parts.join('&');
+ return url + (this.config.suffix || '') + ((url.indexOf('?') === -1) ? '?' : '&') + parts.join('&');
+ };
+
+ config.urlCreatorFactory.path = Path;
};
- config.urlCreatorFactory.path = Path;
- };
+ var globalConfiguration = {};
- var globalConfiguration = {};
+ Configurer.init(this, globalConfiguration);
- Configurer.init(this, globalConfiguration);
+ this.$get = ['$http', '$q', function($http, $q) {
- this.$get = ['$http', '$q', function($http, $q) {
+ function createServiceForConfiguration(config) {
+ var service = {};
- function createServiceForConfiguration(config) {
- var service = {};
+ var urlHandler = new config.urlCreatorFactory[config.urlCreator]();
+ urlHandler.setConfig(config);
- var urlHandler = new config.urlCreatorFactory[config.urlCreator]();
- urlHandler.setConfig(config);
+ function restangularizeBase(parent, elem, route, reqParams, fromServer) {
+ elem[config.restangularFields.route] = route;
+ elem[config.restangularFields.getRestangularUrl] = _.bind(urlHandler.fetchUrl, urlHandler, elem);
+ elem[config.restangularFields.getRequestedUrl] = _.bind(urlHandler.fetchRequestedUrl, urlHandler, elem);
+ elem[config.restangularFields.addRestangularMethod] = _.bind(addRestangularMethodFunction, elem);
+ elem[config.restangularFields.clone] = _.bind(copyRestangularizedElement, elem, elem);
+ elem[config.restangularFields.reqParams] = _.isEmpty(reqParams) ? null : reqParams;
+ elem[config.restangularFields.withHttpConfig] = _.bind(withHttpConfig, elem);
+ elem[config.restangularFields.plain] = _.bind(stripRestangular, elem, elem);
- function restangularizeBase(parent, elem, route, reqParams, fromServer) {
- elem[config.restangularFields.route] = route;
- elem[config.restangularFields.getRestangularUrl] = _.bind(urlHandler.fetchUrl, urlHandler, elem);
- elem[config.restangularFields.getRequestedUrl] = _.bind(urlHandler.fetchRequestedUrl, urlHandler, elem);
- elem[config.restangularFields.addRestangularMethod] = _.bind(addRestangularMethodFunction, elem);
- elem[config.restangularFields.clone] = _.bind(copyRestangularizedElement, elem, elem);
- elem[config.restangularFields.reqParams] = _.isEmpty(reqParams) ? null : reqParams;
- elem[config.restangularFields.withHttpConfig] = _.bind(withHttpConfig, elem);
- elem[config.restangularFields.plain] = _.bind(stripRestangular, elem, elem);
+ // Tag element as restangularized
+ elem[config.restangularFields.restangularized] = true;
- // Tag element as restangularized
- elem[config.restangularFields.restangularized] = true;
+ // RequestLess connection
+ elem[config.restangularFields.one] = _.bind(one, elem, elem);
+ elem[config.restangularFields.all] = _.bind(all, elem, elem);
+ elem[config.restangularFields.several] = _.bind(several, elem, elem);
+ elem[config.restangularFields.oneUrl] = _.bind(oneUrl, elem, elem);
+ elem[config.restangularFields.allUrl] = _.bind(allUrl, elem, elem);
- // RequestLess connection
- elem[config.restangularFields.one] = _.bind(one, elem, elem);
- elem[config.restangularFields.all] = _.bind(all, elem, elem);
- elem[config.restangularFields.several] = _.bind(several, elem, elem);
- elem[config.restangularFields.oneUrl] = _.bind(oneUrl, elem, elem);
- elem[config.restangularFields.allUrl] = _.bind(allUrl, elem, elem);
+ elem[config.restangularFields.fromServer] = !!fromServer;
- elem[config.restangularFields.fromServer] = !!fromServer;
+ if (parent && config.shouldSaveParent(route)) {
+ var parentId = config.getIdFromElem(parent);
+ var parentUrl = config.getUrlFromElem(parent);
- if (parent && config.shouldSaveParent(route)) {
- var parentId = config.getIdFromElem(parent);
- var parentUrl = config.getUrlFromElem(parent);
+ var restangularFieldsForParent = _.union(
+ _.values(_.pick(config.restangularFields, ['route', 'singleOne', 'parentResource'])),
+ config.extraFields
+ );
+ var parentResource = _.pick(parent, restangularFieldsForParent);
- var restangularFieldsForParent = _.union(
- _.values( _.pick(config.restangularFields, ['route', 'singleOne', 'parentResource']) ),
- config.extraFields
- );
- var parentResource = _.pick(parent, restangularFieldsForParent);
+ if (config.isValidId(parentId)) {
+ config.setIdToElem(parentResource, parentId, route);
+ }
+ if (config.isValidId(parentUrl)) {
+ config.setUrlToElem(parentResource, parentUrl, route);
+ }
- if (config.isValidId(parentId)) {
- config.setIdToElem(parentResource, parentId, route);
- }
- if (config.isValidId(parentUrl)) {
- config.setUrlToElem(parentResource, parentUrl, route);
+ elem[config.restangularFields.parentResource] = parentResource;
+ } else {
+ elem[config.restangularFields.parentResource] = null;
}
-
- elem[config.restangularFields.parentResource] = parentResource;
- } else {
- elem[config.restangularFields.parentResource] = null;
+ return elem;
}
- return elem;
- }
- function one(parent, route, id, singleOne) {
- var error;
- if (_.isNumber(route) || _.isNumber(parent)) {
- error = 'You\'re creating a Restangular entity with the number ';
- error += 'instead of the route or the parent. For example, you can\'t call .one(12).';
- throw new Error(error);
- }
- if (_.isUndefined(route)) {
- error = 'You\'re creating a Restangular entity either without the path. ';
- error += 'For example you can\'t call .one(). Please check if your arguments are valid.';
- throw new Error(error);
+ function one(parent, route, id, singleOne) {
+ var error;
+ if (_.isNumber(route) || _.isNumber(parent)) {
+ error = 'You\'re creating a Restangular entity with the number ';
+ error += 'instead of the route or the parent. For example, you can\'t call .one(12).';
+ throw new Error(error);
+ }
+ if (_.isUndefined(route)) {
+ error = 'You\'re creating a Restangular entity either without the path. ';
+ error += 'For example you can\'t call .one(). Please check if your arguments are valid.';
+ throw new Error(error);
+ }
+ var elem = {};
+ config.setIdToElem(elem, id, route);
+ config.setFieldToElem(config.restangularFields.singleOne, elem, singleOne);
+ return restangularizeElem(parent, elem, route, false);
}
- var elem = {};
- config.setIdToElem(elem, id, route);
- config.setFieldToElem(config.restangularFields.singleOne, elem, singleOne);
- return restangularizeElem(parent, elem , route, false);
- }
- function all(parent, route) {
- return restangularizeCollection(parent, [] , route, false);
- }
+ function all(parent, route) {
+ return restangularizeCollection(parent, [], route, false);
+ }
- function several(parent, route /*, ids */) {
- var collection = [];
- collection[config.restangularFields.ids] = Array.prototype.splice.call(arguments, 2);
- return restangularizeCollection(parent, collection , route, false);
- }
+ function several(parent, route /*, ids */ ) {
+ var collection = [];
+ collection[config.restangularFields.ids] = Array.prototype.splice.call(arguments, 2);
+ return restangularizeCollection(parent, collection, route, false);
+ }
- function oneUrl(parent, route, url) {
- if (!route) {
- throw new Error('Route is mandatory when creating new Restangular objects.');
+ function oneUrl(parent, route, url) {
+ if (!route) {
+ throw new Error('Route is mandatory when creating new Restangular objects.');
+ }
+ var elem = {};
+ config.setUrlToElem(elem, url, route);
+ return restangularizeElem(parent, elem, route, false);
}
- var elem = {};
- config.setUrlToElem(elem, url, route);
- return restangularizeElem(parent, elem , route, false);
- }
- function allUrl(parent, route, url) {
- if (!route) {
- throw new Error('Route is mandatory when creating new Restangular objects.');
+ function allUrl(parent, route, url) {
+ if (!route) {
+ throw new Error('Route is mandatory when creating new Restangular objects.');
+ }
+ var elem = {};
+ config.setUrlToElem(elem, url, route);
+ return restangularizeCollection(parent, elem, route, false);
}
- var elem = {};
- config.setUrlToElem(elem, url, route);
- return restangularizeCollection(parent, elem , route, false);
- }
- // Promises
- function restangularizePromise(promise, isCollection, valueToFill) {
- promise.call = _.bind(promiseCall, promise);
- promise.get = _.bind(promiseGet, promise);
- promise[config.restangularFields.restangularCollection] = isCollection;
- if (isCollection) {
+ // Promises
+ function restangularizePromise(promise, isCollection, valueToFill) {
+ promise.call = _.bind(promiseCall, promise);
+ promise.get = _.bind(promiseGet, promise);
+ promise[config.restangularFields.restangularCollection] = isCollection;
+ if (isCollection) {
promise.push = _.bind(promiseCall, promise, 'push');
+ }
+ promise.$object = valueToFill;
+ if (config.restangularizePromiseInterceptor) {
+ config.restangularizePromiseInterceptor(promise);
+ }
+ return promise;
}
- promise.$object = valueToFill;
- if (config.restangularizePromiseInterceptor) {
- config.restangularizePromiseInterceptor(promise);
- }
- return promise;
- }
-
- function promiseCall(method) {
- var deferred = $q.defer();
- var callArgs = arguments;
- var filledValue = {};
- this.then(function(val) {
- var params = Array.prototype.slice.call(callArgs, 1);
- var func = val[method];
- func.apply(val, params);
- filledValue = val;
- deferred.resolve(val);
- });
- return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
- }
-
- function promiseGet(what) {
- var deferred = $q.defer();
- var filledValue = {};
- this.then(function(val) {
- filledValue = val[what];
- deferred.resolve(filledValue);
- });
- return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
- }
-
- function resolvePromise(deferred, response, data, filledValue) {
- _.extend(filledValue, data);
-
- // Trigger the full response interceptor.
- if (config.fullResponse) {
- return deferred.resolve(_.extend(response, {
- data: data
- }));
- } else {
- deferred.resolve(data);
- }
- }
-
- // Elements
- function stripRestangular(elem) {
- if (_.isArray(elem)) {
- var array = [];
- _.each(elem, function(value) {
- array.push(config.isRestangularized(value) ? stripRestangular(value) : value);
+ function promiseCall(method) {
+ var deferred = $q.defer();
+ var callArgs = arguments;
+ var filledValue = {};
+ this.then(function(val) {
+ var params = Array.prototype.slice.call(callArgs, 1);
+ var func = val[method];
+ func.apply(val, params);
+ filledValue = val;
+ deferred.resolve(val);
});
- return array;
- } else {
- return _.omit(elem, _.values(_.omit(config.restangularFields, 'id')));
+ return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
}
- }
- function addCustomOperation(elem) {
- elem[config.restangularFields.customOperation] = _.bind(customFunction, elem);
- var requestMethods = { get: customFunction, delete: customFunction };
- _.each(['put', 'patch', 'post'], function(name) {
- requestMethods[name] = function(operation, elem, path, params, headers) {
- return _.bind(customFunction, this)(operation, path, params, headers, elem);
- };
- });
- _.each(requestMethods, function(requestFunc, name) {
- var callOperation = name === 'delete' ? 'remove' : name;
- _.each(['do', 'custom'], function(alias) {
- elem[alias + name.toUpperCase()] = _.bind(requestFunc, elem, callOperation);
+ function promiseGet(what) {
+ var deferred = $q.defer();
+ var filledValue = {};
+ this.then(function(val) {
+ filledValue = val[what];
+ deferred.resolve(filledValue);
});
- });
- elem[config.restangularFields.customGETLIST] = _.bind(fetchFunction, elem);
- elem[config.restangularFields.doGETLIST] = elem[config.restangularFields.customGETLIST];
- }
+ return restangularizePromise(deferred.promise, this[config.restangularFields.restangularCollection], filledValue);
+ }
- function copyRestangularizedElement(fromElement, toElement) {
- var copiedElement = angular.copy(fromElement, toElement);
- return restangularizeElem(copiedElement[config.restangularFields.parentResource],
- copiedElement, copiedElement[config.restangularFields.route], true);
- }
+ function resolvePromise(deferred, response, data, filledValue) {
+ _.extend(filledValue, data);
- function restangularizeElem(parent, element, route, fromServer, collection, reqParams) {
- var elem = config.onBeforeElemRestangularized(element, false, route);
+ // Trigger the full response interceptor.
+ if (config.fullResponse) {
+ return deferred.resolve(_.extend(response, {
+ data: data
+ }));
+ } else {
+ deferred.resolve(data);
+ }
+ }
- var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
- if (config.useCannonicalId) {
- localElem[config.restangularFields.cannonicalId] = config.getIdFromElem(localElem);
+ // Elements
+ function stripRestangular(elem) {
+ if (_.isArray(elem)) {
+ var array = [];
+ _.each(elem, function(value) {
+ array.push(config.isRestangularized(value) ? stripRestangular(value) : value);
+ });
+ return array;
+ } else {
+ return _.omit(elem, _.values(_.omit(config.restangularFields, 'id')));
+ }
}
- if (collection) {
- localElem[config.restangularFields.getParentList] = function() {
- return collection;
+ function addCustomOperation(elem) {
+ elem[config.restangularFields.customOperation] = _.bind(customFunction, elem);
+ var requestMethods = {
+ get: customFunction,
+ delete: customFunction
};
+ _.each(['put', 'patch', 'post'], function(name) {
+ requestMethods[name] = function(operation, elem, path, params, headers) {
+ return _.bind(customFunction, this)(operation, path, params, headers, elem);
+ };
+ });
+ _.each(requestMethods, function(requestFunc, name) {
+ var callOperation = name === 'delete' ? 'remove' : name;
+ _.each(['do', 'custom'], function(alias) {
+ elem[config.restangularFields[alias + name.toUpperCase()]] = _.bind(requestFunc, elem, callOperation);
+ });
+ });
+ elem[config.restangularFields.customGETLIST] = _.bind(fetchFunction, elem);
+ elem[config.restangularFields.doGETLIST] = elem[config.restangularFields.customGETLIST];
}
- localElem[config.restangularFields.restangularCollection] = false;
- localElem[config.restangularFields.get] = _.bind(getFunction, localElem);
- localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem);
- localElem[config.restangularFields.put] = _.bind(putFunction, localElem);
- localElem[config.restangularFields.post] = _.bind(postFunction, localElem);
- localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
- localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
- localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
- localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
- localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
- localElem[config.restangularFields.save] = _.bind(save, localElem);
-
- addCustomOperation(localElem);
- return config.transformElem(localElem, false, route, service, true);
- }
-
- function restangularizeCollection(parent, element, route, fromServer, reqParams) {
- var elem = config.onBeforeElemRestangularized(element, true, route);
-
- var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
- localElem[config.restangularFields.restangularCollection] = true;
- localElem[config.restangularFields.post] = _.bind(postFunction, localElem, null);
- localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
- localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
- localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
- localElem[config.restangularFields.putElement] = _.bind(putElementFunction, localElem);
- localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
- localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
- localElem[config.restangularFields.get] = _.bind(getById, localElem);
- localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem, null);
-
- addCustomOperation(localElem);
- return config.transformElem(localElem, true, route, service, true);
- }
-
- function restangularizeCollectionAndElements(parent, element, route) {
- var collection = restangularizeCollection(parent, element, route, false);
- _.each(collection, function(elem) {
- if (elem) {
- restangularizeElem(parent, elem, route, false);
+ function copyRestangularizedElement(element) {
+ var copiedElement = angular.copy(element);
+
+ // check if we're dealing with a collection (i.e. an array)
+ // and restangularize the element using the proper restangularizer,
+ // element / collection
+ if (_.isArray(element)) {
+ return restangularizeCollection(
+ element[config.restangularFields.parentResource],
+ copiedElement,
+ element[config.restangularFields.route],
+ element[config.restangularFields.fromServer],
+ element[config.restangularFields.reqParams]
+ );
}
- });
- return collection;
- }
-
- function getById(id, reqParams, headers){
- return this.customGET(id.toString(), reqParams, headers);
- }
- function putElementFunction(idx, params, headers) {
- var __this = this;
- var elemToPut = this[idx];
- var deferred = $q.defer();
- var filledArray = [];
- filledArray = config.transformElem(filledArray, true, elemToPut[config.restangularFields.route], service);
- elemToPut.put(params, headers).then(function(serverElem) {
- var newArray = copyRestangularizedElement(__this);
- newArray[idx] = serverElem;
- filledArray = newArray;
- deferred.resolve(newArray);
- }, function(response) {
- deferred.reject(response);
- });
-
- return restangularizePromise(deferred.promise, true, filledArray);
- }
-
- function parseResponse(resData, operation, route, fetchUrl, response, deferred) {
- var data = config.responseExtractor(resData, operation, route, fetchUrl, response, deferred);
- var etag = response.headers('ETag');
- if (data && etag) {
- data[config.restangularFields.etag] = etag;
+ // not a collection, restangularize it as an element
+ return restangularizeElem(
+ element[config.restangularFields.parentResource],
+ copiedElement,
+ element[config.restangularFields.route],
+ element[config.restangularFields.fromServer],
+ element[config.restangularFields.restangularCollection],
+ element[config.restangularFields.reqParams]
+ );
}
- return data;
- }
+ function restangularizeElem(parent, element, route, fromServer, collection, reqParams) {
+ var elem = config.onBeforeElemRestangularized(element, false, route);
- function fetchFunction(what, reqParams, headers) {
- var __this = this;
- var deferred = $q.defer();
- var operation = 'getList';
- var url = urlHandler.fetchUrl(this, what);
- var whatFetched = what || __this[config.restangularFields.route];
+ var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
- var request = config.fullRequestInterceptor(null, operation,
- whatFetched, url, headers || {}, reqParams || {}, this[config.restangularFields.httpConfig] || {});
-
- var filledArray = [];
- filledArray = config.transformElem(filledArray, true, whatFetched, service);
+ if (config.useCannonicalId) {
+ localElem[config.restangularFields.cannonicalId] = config.getIdFromElem(localElem);
+ }
- var method = 'getList';
+ if (collection) {
+ localElem[config.restangularFields.getParentList] = function() {
+ return collection;
+ };
+ }
- if (config.jsonp) {
- method = 'jsonp';
+ localElem[config.restangularFields.restangularCollection] = false;
+ localElem[config.restangularFields.get] = _.bind(getFunction, localElem);
+ localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem);
+ localElem[config.restangularFields.put] = _.bind(putFunction, localElem);
+ localElem[config.restangularFields.post] = _.bind(postFunction, localElem);
+ localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
+ localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
+ localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
+ localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
+ localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
+ localElem[config.restangularFields.save] = _.bind(save, localElem);
+
+ addCustomOperation(localElem);
+ return config.transformElem(localElem, false, route, service, true);
}
- var okCallback = function(response) {
- var resData = response.data;
- var fullParams = response.config.params;
- var data = parseResponse(resData, operation, whatFetched, url, response, deferred);
-
- // support empty response for getList() calls (some APIs respond with 204 and empty body)
- if (_.isUndefined(data) || '' === data) {
- data = [];
- }
- if (!_.isArray(data)) {
- throw new Error('Response for getList SHOULD be an array and not an object or something else');
- }
-
- if (true === config.plainByDefault) {
- return resolvePromise(deferred, response, data, filledArray);
- }
+ function restangularizeCollection(parent, element, route, fromServer, reqParams) {
+ var elem = config.onBeforeElemRestangularized(element, true, route);
+
+ var localElem = restangularizeBase(parent, elem, route, reqParams, fromServer);
+ localElem[config.restangularFields.restangularCollection] = true;
+ localElem[config.restangularFields.post] = _.bind(postFunction, localElem, null);
+ localElem[config.restangularFields.remove] = _.bind(deleteFunction, localElem);
+ localElem[config.restangularFields.head] = _.bind(headFunction, localElem);
+ localElem[config.restangularFields.trace] = _.bind(traceFunction, localElem);
+ localElem[config.restangularFields.putElement] = _.bind(putElementFunction, localElem);
+ localElem[config.restangularFields.options] = _.bind(optionsFunction, localElem);
+ localElem[config.restangularFields.patch] = _.bind(patchFunction, localElem);
+ localElem[config.restangularFields.get] = _.bind(getById, localElem);
+ localElem[config.restangularFields.getList] = _.bind(fetchFunction, localElem, null);
+
+ addCustomOperation(localElem);
+ return config.transformElem(localElem, true, route, service, true);
+ }
- var processedData = _.map(data, function(elem) {
- if (!__this[config.restangularFields.restangularCollection]) {
- return restangularizeElem(__this, elem, what, true, data);
- } else {
- return restangularizeElem(__this[config.restangularFields.parentResource],
- elem, __this[config.restangularFields.route], true, data);
+ function restangularizeCollectionAndElements(parent, element, route, fromServer) {
+ var collection = restangularizeCollection(parent, element, route, fromServer);
+ _.each(collection, function(elem) {
+ if (elem) {
+ restangularizeElem(parent, elem, route, fromServer);
}
});
+ return collection;
+ }
- processedData = _.extend(data, processedData);
-
- if (!__this[config.restangularFields.restangularCollection]) {
- resolvePromise(
- deferred,
- response,
- restangularizeCollection(
- __this,
- processedData,
- what,
- true,
- fullParams
- ),
- filledArray
- );
- } else {
- resolvePromise(
- deferred,
- response,
- restangularizeCollection(
- __this[config.restangularFields.parentResource],
- processedData,
- __this[config.restangularFields.route],
- true,
- fullParams
- ),
- filledArray
- );
- }
- };
+ function getById(id, reqParams, headers) {
+ return this.customGET(id.toString(), reqParams, headers);
+ }
- urlHandler.resource(this, $http, request.httpConfig, request.headers, request.params, what,
- this[config.restangularFields.etag], operation)[method]().then(okCallback, function error(response) {
- if (response.status === 304 && __this[config.restangularFields.restangularCollection]) {
- resolvePromise(deferred, response, __this, filledArray);
- } else if ( _.every(config.errorInterceptors, function(cb) { return cb(response, deferred, okCallback) !== false; }) ) {
- // triggered if no callback returns false
+ function putElementFunction(idx, params, headers) {
+ var __this = this;
+ var elemToPut = this[idx];
+ var deferred = $q.defer();
+ var filledArray = [];
+ filledArray = config.transformElem(filledArray, true, elemToPut[config.restangularFields.route], service);
+ elemToPut.put(params, headers).then(function(serverElem) {
+ var newArray = copyRestangularizedElement(__this);
+ newArray[idx] = serverElem;
+ filledArray = newArray;
+ deferred.resolve(newArray);
+ }, function(response) {
deferred.reject(response);
- }
- });
-
- return restangularizePromise(deferred.promise, true, filledArray);
- }
+ });
- function withHttpConfig(httpConfig) {
- this[config.restangularFields.httpConfig] = httpConfig;
- return this;
- }
+ return restangularizePromise(deferred.promise, true, filledArray);
+ }
- function save(params, headers) {
- if (this[config.restangularFields.fromServer]) {
- return this[config.restangularFields.put](params, headers);
- } else {
- return _.bind(elemFunction, this)('post', undefined, params, undefined, headers);
+ function parseResponse(resData, operation, route, fetchUrl, response, deferred) {
+ var data = config.responseExtractor(resData, operation, route, fetchUrl, response, deferred);
+ var etag = response.headers('ETag');
+ if (data && etag) {
+ data[config.restangularFields.etag] = etag;
+ }
+ return data;
}
- }
- function elemFunction(operation, what, params, obj, headers) {
- var __this = this;
- var deferred = $q.defer();
- var resParams = params || {};
- var route = what || this[config.restangularFields.route];
- var fetchUrl = urlHandler.fetchUrl(this, what);
- var callObj = obj || this;
- // fallback to etag on restangular object (since for custom methods we probably don't explicitly specify the etag field)
- var etag = callObj[config.restangularFields.etag] || (operation !== 'post' ? this[config.restangularFields.etag] : null);
+ function fetchFunction(what, reqParams, headers) {
+ var __this = this;
+ var deferred = $q.defer();
+ var operation = 'getList';
+ var url = urlHandler.fetchUrl(this, what);
+ var whatFetched = what || __this[config.restangularFields.route];
- if (_.isObject(callObj) && config.isRestangularized(callObj)) {
- callObj = stripRestangular(callObj);
- }
- var request = config.fullRequestInterceptor(callObj, operation, route, fetchUrl,
- headers || {}, resParams || {}, this[config.restangularFields.httpConfig] || {});
+ var request = config.fullRequestInterceptor(null, operation,
+ whatFetched, url, headers || {}, reqParams || {}, this[config.restangularFields.httpConfig] || {});
+
+ var filledArray = [];
+ filledArray = config.transformElem(filledArray, true, whatFetched, service);
- var filledObject = {};
- filledObject = config.transformElem(filledObject, false, route, service);
+ var method = 'getList';
- var okCallback = function(response) {
- var resData = response.data;
- var fullParams = response.config.params;
- var elem = parseResponse(resData, operation, route, fetchUrl, response, deferred);
+ if (config.jsonp) {
+ method = 'jsonp';
+ }
- if (elem) {
- var data;
+ var okCallback = function(response) {
+ var resData = response.data;
+ var fullParams = response.config.params;
+ var data = parseResponse(resData, operation, whatFetched, url, response, deferred);
+
+ // support empty response for getList() calls (some APIs respond with 204 and empty body)
+ if (_.isUndefined(data) || '' === data) {
+ data = [];
+ }
+ if (!_.isArray(data)) {
+ throw new Error('Response for getList SHOULD be an array and not an object or something else');
+ }
if (true === config.plainByDefault) {
- return resolvePromise(deferred, response, elem, filledObject);
+ return resolvePromise(deferred, response, data, filledArray);
}
- if (operation === 'post' && !__this[config.restangularFields.restangularCollection]) {
- data = restangularizeElem(
- __this[config.restangularFields.parentResource],
- elem,
- route,
- true,
- null,
- fullParams
+ var processedData = _.map(data, function(elem) {
+ if (!__this[config.restangularFields.restangularCollection]) {
+ return restangularizeElem(__this, elem, what, true, data);
+ } else {
+ return restangularizeElem(__this[config.restangularFields.parentResource],
+ elem, __this[config.restangularFields.route], true, data);
+ }
+ });
+
+ processedData = _.extend(data, processedData);
+
+ if (!__this[config.restangularFields.restangularCollection]) {
+ resolvePromise(
+ deferred,
+ response,
+ restangularizeCollection(
+ __this,
+ processedData,
+ what,
+ true,
+ fullParams
+ ),
+ filledArray
);
- resolvePromise(deferred, response, data, filledObject);
} else {
- data = restangularizeElem(
- __this[config.restangularFields.parentResource],
- elem,
- __this[config.restangularFields.route],
- true,
- null,
- fullParams
+ resolvePromise(
+ deferred,
+ response,
+ restangularizeCollection(
+ __this[config.restangularFields.parentResource],
+ processedData,
+ __this[config.restangularFields.route],
+ true,
+ fullParams
+ ),
+ filledArray
);
+ }
+ };
- data[config.restangularFields.singleOne] = __this[config.restangularFields.singleOne];
- resolvePromise(deferred, response, data, filledObject);
+ urlHandler.resource(this, $http, request.httpConfig, request.headers, request.params, what,
+ this[config.restangularFields.etag], operation)[method]().then(okCallback, function error(response) {
+ if (response.status === 304 && __this[config.restangularFields.restangularCollection]) {
+ resolvePromise(deferred, response, __this, filledArray);
+ } else if (_.every(config.errorInterceptors, function(cb) {
+ return cb(response, deferred, okCallback) !== false;
+ })) {
+ // triggered if no callback returns false
+ deferred.reject(response);
}
+ });
+
+ return restangularizePromise(deferred.promise, true, filledArray);
+ }
+ function withHttpConfig(httpConfig) {
+ this[config.restangularFields.httpConfig] = httpConfig;
+ return this;
+ }
+
+ function save(params, headers) {
+ if (this[config.restangularFields.fromServer]) {
+ return this[config.restangularFields.put](params, headers);
} else {
- resolvePromise(deferred, response, undefined, filledObject);
+ return _.bind(elemFunction, this)('post', undefined, params, undefined, headers);
}
- };
+ }
- var errorCallback = function(response) {
- if (response.status === 304 && config.isSafe(operation)) {
- resolvePromise(deferred, response, __this, filledObject);
- } else if ( _.every(config.errorInterceptors, function(cb) { return cb(response, deferred, okCallback) !== false; }) ) {
- // triggered if no callback returns false
- deferred.reject(response);
+ function elemFunction(operation, what, params, obj, headers) {
+ var __this = this;
+ var deferred = $q.defer();
+ var resParams = params || {};
+ var route = what || this[config.restangularFields.route];
+ var fetchUrl = urlHandler.fetchUrl(this, what);
+
+ var callObj = obj || this;
+ // fallback to etag on restangular object (since for custom methods we probably don't explicitly specify the etag field)
+ var etag = callObj[config.restangularFields.etag] || (operation !== 'post' ? this[config.restangularFields.etag] : null);
+
+ if (_.isObject(callObj) && config.isRestangularized(callObj)) {
+ callObj = stripRestangular(callObj);
}
- };
- // Overriding HTTP Method
- var callOperation = operation;
- var callHeaders = _.extend({}, request.headers);
- var isOverrideOperation = config.isOverridenMethod(operation);
- if (isOverrideOperation) {
- callOperation = 'post';
- callHeaders = _.extend(callHeaders, {'X-HTTP-Method-Override': operation === 'remove' ? 'DELETE' : operation.toUpperCase()});
- } else if (config.jsonp && callOperation === 'get') {
- callOperation = 'jsonp';
- }
+ var request = config.fullRequestInterceptor(callObj, operation, route, fetchUrl,
+ headers || {}, resParams || {}, this[config.restangularFields.httpConfig] || {});
+
+ var filledObject = {};
+ filledObject = config.transformElem(filledObject, false, route, service);
+
+ var okCallback = function(response) {
+ var resData = response.data;
+ var fullParams = response.config.params;
+ var elem = parseResponse(resData, operation, route, fetchUrl, response, deferred);
+
+ // accept 0 as response
+ if (elem !== null && elem !== undefined && elem !== '') {
+ var data;
+
+ if (true === config.plainByDefault) {
+ return resolvePromise(deferred, response, elem, filledObject);
+ }
+
+ if (operation === 'post' && !__this[config.restangularFields.restangularCollection]) {
+ data = restangularizeElem(
+ __this[config.restangularFields.parentResource],
+ elem,
+ route,
+ true,
+ null,
+ fullParams
+ );
+ resolvePromise(deferred, response, data, filledObject);
+ } else {
+ data = restangularizeElem(
+ __this[config.restangularFields.parentResource],
+ elem,
+ __this[config.restangularFields.route],
+ true,
+ null,
+ fullParams
+ );
+
+ data[config.restangularFields.singleOne] = __this[config.restangularFields.singleOne];
+ resolvePromise(deferred, response, data, filledObject);
+ }
+
+ } else {
+ resolvePromise(deferred, response, undefined, filledObject);
+ }
+ };
- if (config.isSafe(operation)) {
+ var errorCallback = function(response) {
+ if (response.status === 304 && config.isSafe(operation)) {
+ resolvePromise(deferred, response, __this, filledObject);
+ } else if (_.every(config.errorInterceptors, function(cb) {
+ return cb(response, deferred, okCallback) !== false;
+ })) {
+ // triggered if no callback returns false
+ deferred.reject(response);
+ }
+ };
+ // Overriding HTTP Method
+ var callOperation = operation;
+ var callHeaders = _.extend({}, request.headers);
+ var isOverrideOperation = config.isOverridenMethod(operation);
if (isOverrideOperation) {
- urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
- what, etag, callOperation)[callOperation]({}).then(okCallback, errorCallback);
+ callOperation = 'post';
+ callHeaders = _.extend(callHeaders, {
+ 'X-HTTP-Method-Override': operation === 'remove' ? 'DELETE' : operation.toUpperCase()
+ });
+ } else if (config.jsonp && callOperation === 'get') {
+ callOperation = 'jsonp';
+ }
+
+ if (config.isSafe(operation)) {
+ if (isOverrideOperation) {
+ urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
+ what, etag, callOperation)[callOperation]({}).then(okCallback, errorCallback);
+ } else {
+ urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
+ what, etag, callOperation)[callOperation]().then(okCallback, errorCallback);
+ }
} else {
urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
- what, etag, callOperation)[callOperation]().then(okCallback, errorCallback);
+ what, etag, callOperation)[callOperation](request.element).then(okCallback, errorCallback);
}
- } else {
- urlHandler.resource(this, $http, request.httpConfig, callHeaders, request.params,
- what, etag, callOperation)[callOperation](request.element).then(okCallback, errorCallback);
- }
-
- return restangularizePromise(deferred.promise, false, filledObject);
- }
- function getFunction(params, headers) {
- return _.bind(elemFunction, this)('get', undefined, params, undefined, headers);
- }
+ return restangularizePromise(deferred.promise, false, filledObject);
+ }
- function deleteFunction(params, headers) {
- return _.bind(elemFunction, this)('remove', undefined, params, undefined, headers);
- }
+ function getFunction(params, headers) {
+ return _.bind(elemFunction, this)('get', undefined, params, undefined, headers);
+ }
- function putFunction(params, headers) {
- return _.bind(elemFunction, this)('put', undefined, params, undefined, headers);
- }
+ function deleteFunction(params, headers) {
+ return _.bind(elemFunction, this)('remove', undefined, params, undefined, headers);
+ }
- function postFunction(what, elem, params, headers) {
- return _.bind(elemFunction, this)('post', what, params, elem, headers);
- }
+ function putFunction(params, headers) {
+ return _.bind(elemFunction, this)('put', undefined, params, undefined, headers);
+ }
- function headFunction(params, headers) {
- return _.bind(elemFunction, this)('head', undefined, params, undefined, headers);
- }
+ function postFunction(what, elem, params, headers) {
+ return _.bind(elemFunction, this)('post', what, params, elem, headers);
+ }
- function traceFunction(params, headers) {
- return _.bind(elemFunction, this)('trace', undefined, params, undefined, headers);
- }
+ function headFunction(params, headers) {
+ return _.bind(elemFunction, this)('head', undefined, params, undefined, headers);
+ }
- function optionsFunction(params, headers) {
- return _.bind(elemFunction, this)('options', undefined, params, undefined, headers);
- }
+ function traceFunction(params, headers) {
+ return _.bind(elemFunction, this)('trace', undefined, params, undefined, headers);
+ }
- function patchFunction(elem, params, headers) {
- return _.bind(elemFunction, this)('patch', undefined, params, elem, headers);
- }
+ function optionsFunction(params, headers) {
+ return _.bind(elemFunction, this)('options', undefined, params, undefined, headers);
+ }
- function customFunction(operation, path, params, headers, elem) {
- return _.bind(elemFunction, this)(operation, path, params, elem, headers);
- }
+ function patchFunction(elem, params, headers) {
+ return _.bind(elemFunction, this)('patch', undefined, params, elem, headers);
+ }
- function addRestangularMethodFunction(name, operation, path, defaultParams, defaultHeaders, defaultElem) {
- var bindedFunction;
- if (operation === 'getList') {
- bindedFunction = _.bind(fetchFunction, this, path);
- } else {
- bindedFunction = _.bind(customFunction, this, operation, path);
+ function customFunction(operation, path, params, headers, elem) {
+ return _.bind(elemFunction, this)(operation, path, params, elem, headers);
}
- var createdFunction = function(params, headers, elem) {
- var callParams = _.defaults({
- params: params,
- headers: headers,
- elem: elem
- }, {
- params: defaultParams,
- headers: defaultHeaders,
- elem: defaultElem
- });
- return bindedFunction(callParams.params, callParams.headers, callParams.elem);
- };
+ function addRestangularMethodFunction(name, operation, path, defaultParams, defaultHeaders, defaultElem) {
+ var bindedFunction;
+ if (operation === 'getList') {
+ bindedFunction = _.bind(fetchFunction, this, path);
+ } else {
+ bindedFunction = _.bind(customFunction, this, operation, path);
+ }
- if (config.isSafe(operation)) {
- this[name] = createdFunction;
- } else {
- this[name] = function(elem, params, headers) {
- return createdFunction(params, headers, elem);
+ var createdFunction = function(params, headers, elem) {
+ var callParams = _.defaults({
+ params: params,
+ headers: headers,
+ elem: elem
+ }, {
+ params: defaultParams,
+ headers: defaultHeaders,
+ elem: defaultElem
+ });
+ return bindedFunction(callParams.params, callParams.headers, callParams.elem);
};
+
+ if (config.isSafe(operation)) {
+ this[name] = createdFunction;
+ } else {
+ this[name] = function(elem, params, headers) {
+ return createdFunction(params, headers, elem);
+ };
+ }
}
- }
- function withConfigurationFunction(configurer) {
- var newConfig = angular.copy(_.omit(config, 'configuration'));
- Configurer.init(newConfig, newConfig);
- configurer(newConfig);
- return createServiceForConfiguration(newConfig);
- }
+ function withConfigurationFunction(configurer) {
+ var newConfig = angular.copy(_.omit(config, 'configuration'));
+ Configurer.init(newConfig, newConfig);
+ configurer(newConfig);
+ return createServiceForConfiguration(newConfig);
+ }
- function toService(route, parent) {
- var knownCollectionMethods = _.values(config.restangularFields);
- var serv = {};
- var collection = (parent || service).all(route);
- serv.one = _.bind(one, (parent || service), parent, route);
- serv.post = _.bind(collection.post, collection);
- serv.getList = _.bind(collection.getList, collection);
-
- for (var prop in collection) {
- if (collection.hasOwnProperty(prop) && _.isFunction(collection[prop]) && !_.includes(knownCollectionMethods, prop)) {
- serv[prop] = _.bind(collection[prop], collection);
+ function toService(route, parent) {
+ var knownCollectionMethods = _.values(config.restangularFields);
+ var serv = {};
+ var collection = (parent || service).all(route);
+ serv.one = _.bind(one, (parent || service), parent, route);
+ serv.post = _.bind(collection.post, collection);
+ serv.getList = _.bind(collection.getList, collection);
+ serv.withHttpConfig = _.bind(collection.withHttpConfig, collection);
+ serv.get = _.bind(collection.get, collection);
+
+ for (var prop in collection) {
+ if (collection.hasOwnProperty(prop) && _.isFunction(collection[prop]) && !_.includes(knownCollectionMethods, prop)) {
+ serv[prop] = _.bind(collection[prop], collection);
+ }
}
- }
- return serv;
- }
+ return serv;
+ }
- Configurer.init(service, config);
+ Configurer.init(service, config);
- service.copy = _.bind(copyRestangularizedElement, service);
+ service.copy = _.bind(copyRestangularizedElement, service);
- service.service = _.bind(toService, service);
+ service.service = _.bind(toService, service);
- service.withConfig = _.bind(withConfigurationFunction, service);
+ service.withConfig = _.bind(withConfigurationFunction, service);
- service.one = _.bind(one, service, null);
+ service.one = _.bind(one, service, null);
- service.all = _.bind(all, service, null);
+ service.all = _.bind(all, service, null);
- service.several = _.bind(several, service, null);
+ service.several = _.bind(several, service, null);
- service.oneUrl = _.bind(oneUrl, service, null);
+ service.oneUrl = _.bind(oneUrl, service, null);
- service.allUrl = _.bind(allUrl, service, null);
+ service.allUrl = _.bind(allUrl, service, null);
- service.stripRestangular = _.bind(stripRestangular, service);
+ service.stripRestangular = _.bind(stripRestangular, service);
- service.restangularizeElement = _.bind(restangularizeElem, service);
+ service.restangularizeElement = _.bind(restangularizeElem, service);
- service.restangularizeCollection = _.bind(restangularizeCollectionAndElements, service);
+ service.restangularizeCollection = _.bind(restangularizeCollectionAndElements, service);
- return service;
- }
+ return service;
+ }
- return createServiceForConfiguration(globalConfiguration);
- }];
-});
+ return createServiceForConfiguration(globalConfiguration);
+ }];
+ });
return restangular.name;
}));
diff --git a/test/restangularSpec.js b/test/restangularSpec.js
index 218b40e0..fd989819 100644
--- a/test/restangularSpec.js
+++ b/test/restangularSpec.js
@@ -1,201 +1,317 @@
-describe("Restangular", function() {
+/* global describe, beforeEach, afterEach, it, expect, spyOn, jasmine */
+/* jshint unused: false */
+describe('Restangular', function () {
// API
- var Restangular, $httpBackend;
- var accountsModel, restangularAccounts, restangularAccount0, restangularAccount1;
- var accountsHalModel;
- var messages, newAccount;
+ var Restangular, $httpBackend, testData,
+ restangularAccounts, restangularAccount0, restangularAccount1;
// Load required modules
- beforeEach(angular.mock.module("restangular"));
-
- // Init HTTP mock backend and Restangular resources
- beforeEach(inject(function($injector) {
- // Model
- accountsModel = [
- {id: 0, user: "Martin ", amount: 42, transactions: []},
- {id: 1, user: "Paul", amount: 3.1416, transactions: [{from: "Martin", amount: 3, id: 0}, {from: "Anonymous", amount: 0.1416, id:1}]}
- ];
-
- // HAL model (http://stateless.co/hal_specification.html)
- accountsHalModel = [
- {id: 0, user: "Martin", amount: 42, transaction: [], _links: {self: "/accountsHAL/martin"}},
- {id: 1, user: "Paul", amount: 3.1416, transaction: [
- {from: "Martin", amount: 3, id: 0, _links: {self: "/accountsHAL/paul/transactions/0"}},
- {from: "Anonymous", amount: 0.1416, id: 1, _links: {self: "/accountsHAL/paul/transactions/1"}}
- ], _links: {self: "/accountsHAL/paul"}}
- ];
-
- infoModel = {
- id: 0, text: "Some additional account information"
- }
-
- newAccount = {id: 44, user: "First User", amount: 45, transactions: []};
-
- messages = [{id: 23, name: "Gonto"}, {id: 45, name: "John"}]
-
- accountsDoSomethingModel = { result: 1 };
-
- $httpBackend = $injector.get("$httpBackend");
-
- $httpBackend.when("HEAD", "/accounts").respond();
- $httpBackend.when("TRACE", "/accounts").respond();
- $httpBackend.when("OPTIONS", "/accounts").respond();
-
- $httpBackend.whenGET("/accounts").respond(accountsModel);
- $httpBackend.whenGET("/accounts/do-something").respond(accountsDoSomethingModel);
- $httpBackend.whenJSONP("/accounts").respond(accountsModel);
- $httpBackend.whenGET("/accounts/0,1").respond(accountsModel);
- $httpBackend.whenGET("/accounts/messages").respond(messages);
- $httpBackend.whenGET("/accounts/1/message").respond(messages[0]);
- $httpBackend.whenGET("/accounts/1/messages").respond(messages);
- $httpBackend.whenGET("/accounts/0").respond(accountsModel[0]);
- $httpBackend.whenGET("/accounts/1").respond(accountsModel[1]);
- $httpBackend.whenJSONP("/accounts/1").respond(accountsModel[1]);
- $httpBackend.whenGET("/accounts/1/transactions").respond(accountsModel[1].transactions);
- $httpBackend.whenGET("/accounts/1/transactions/1").respond(accountsModel[1].transactions[1]);
-
- $httpBackend.whenGET("/info").respond(infoModel);
- $httpBackend.whenGET("/accounts/1/info").respond(infoModel);
- $httpBackend.whenPUT("/info").respond(function(method, url, data) {
- return [200, data, ""];
- });
-
- $httpBackend.whenGET("/accountsHAL").respond(accountsHalModel);
- $httpBackend.whenPUT("/accountsHAL/martin").respond(function(method, url, data) {
- accountsHalModel[0] = angular.fromJson(data);
- return [200, data, ""];
- });
-
- // Full URL
- $httpBackend.whenGET('http://accounts.com/all').respond(accountsModel);
-
- $httpBackend.whenPOST("/accounts").respond(function(method, url, data, headers) {
- var newData = angular.fromJson(data);
- newData.fromServer = true;
- return [201, JSON.stringify(newData), ""];
- });
-
- $httpBackend.whenPOST("/accounts/1/transactions").respond(function(method, url, data, headers) {
- return [201, "", ""];
- });
-
- $httpBackend.whenDELETE("/accounts/1/transactions/1").respond(function(method, url, data, headers) {
- return [200, "", ""];
- });
-
- $httpBackend.whenDELETE("/accounts/1").respond(function(method, url, data, headers) {
- return [200, "", ""];
- });
-
- $httpBackend.whenPOST("/accounts/1").respond(function(method, url, data, headers) {
- return [200, "", ""];
- });
+ beforeEach(function () {
+ // Load restangular module
+ angular.mock.module('restangular');
- $httpBackend.whenPUT("/accounts/1").respond(function(method, url, data, headers) {
- accountsModel[1] = angular.fromJson(data);
- return [201, data, ""];
+ // Get references to modules from the injector
+ angular.mock.inject(function ($injector) {
+ $httpBackend = $injector.get('$httpBackend');
+ Restangular = $injector.get('Restangular');
});
- $httpBackend.whenGET("/error").respond(function() {
- return [500, {}, ""];
- });
+ // Restangularize a few demo accounts
+ restangularAccounts = Restangular.all('accounts');
+ restangularAccount0 = Restangular.one('accounts', 0);
+ restangularAccount1 = Restangular.one('accounts', 1);
- $httpBackend.whenPOST("/customs").respond(function(method, url, data, headers) {
- if (JSON.parse(data).one) {
- return [201, "", ""];
- } else {
- return [400, "", ""];
- }
- });
+ // Create testdata for our tests
+ testData = {
+ // Model
+ accountsModel: [{
+ id: 0,
+ user: 'Martin ',
+ amount: 42,
+ transactions: []
+ }, {
+ id: 1,
+ user: 'Paul',
+ amount: 3.1416,
+ transactions: [{
+ from: 'Martin',
+ amount: 3,
+ id: 0
+ }, {
+ from: 'Anonymous',
+ amount: 0.1416,
+ id: 1
+ }]
+ }],
+
+ nextAccountId: 22,
+
+ // HAL model (http://stateless.co/hal_specification.html)
+ accountsHalModel: [{
+ id: 0,
+ user: 'Martin',
+ amount: 42,
+ transaction: [],
+ _links: {
+ self: '/accountsHAL/martin'
+ }
+ }, {
+ id: 1,
+ user: 'Paul',
+ amount: 3.1416,
+ transaction: [{
+ from: 'Martin',
+ amount: 3,
+ id: 0,
+ _links: {
+ self: '/accountsHAL/paul/transactions/0'
+ }
+ }, {
+ from: 'Anonymous',
+ amount: 0.1416,
+ id: 1,
+ _links: {
+ self: '/accountsHAL/paul/transactions/1'
+ }
+ }],
+ _links: {
+ self: '/accountsHAL/paul'
+ }
+ }],
+
+ infoModel: {
+ id: 0,
+ text: 'Some additional account information'
+ },
- // return the status code given
- // e.g.: /error/404 returns 404 Not Found
- var urlRegex = /\/error\/(\d{3})/;
- $httpBackend.whenGET(urlRegex).respond(function(method, url, data, headers) {
- return [url.match(urlRegex)[1], {}, ""];
- });
+ newAccount: {
+ user: 'First User',
+ amount: 45,
+ transactions: []
+ },
- Restangular = $injector.get("Restangular");
- restangularAccounts = Restangular.all("accounts");
- restangularAccount0 = Restangular.one("accounts", 0);
- restangularAccount1 = Restangular.one("accounts", 1);
+ messages: [{
+ id: 23,
+ name: 'Gonto'
+ }, {
+ id: 45,
+ name: 'John'
+ }],
+ accountsDoSomethingModel: {
+ result: 1
+ },
- // Another API for testing
- customers = [
- {
+ // Another API for testing
+ customers: [{
id: 0,
- name: "Alice",
+ name: 'Alice',
status: 'active',
credit: 4000.0
- },
- {
+ }, {
id: 1,
- name: "Bob",
+ name: 'Bob',
status: 'active',
credit: 4000.0
- },
- {
+ }, {
id: 2,
- name: "Carl",
+ name: 'Carl',
status: 'active',
credit: 4000.0
- }
- ];
- publications = [
- {
+ }],
+ publications: [{
id: 1,
- title: "Sample",
- content: "Rich data",
+ title: 'Sample',
+ content: 'Rich data',
tags: [
'science',
'chemistry'
]
+ }],
+ newCustomer: {
+ id: 3,
+ name: 'New',
+ status: 'active',
+ credit: 4000.0
}
- ];
- newCustomer = {
- id: 3,
- name: "New",
- status: 'active',
- credit: 4000.0
};
- $httpBackend.whenGET("/customers/").respond(customers);
- $httpBackend.whenGET("http://localhost:8080/customers/").respond(customers);
- $httpBackend.whenGET("api.new.domain/customers/").respond(customers);
- $httpBackend.whenGET("/customers/?active=true").respond(customers);
- $httpBackend.whenGET("/customers/publications/?tags=chemistry").respond(publications);
- $httpBackend.whenPUT("/customers/0").respond(function (method, url, data) {
- customers[0] = angular.fromJson(data);
- return [200, data, ""];
+
+ // Set up backend responses
+ $httpBackend.when('HEAD', '/accounts').respond();
+ $httpBackend.when('TRACE', '/accounts').respond();
+ $httpBackend.when('OPTIONS', '/accounts').respond();
+
+ // CRUD /accounts
+ $httpBackend.whenGET('/accounts').respond(testData.accountsModel);
+ $httpBackend.whenJSONP('/accounts').respond(testData.accountsModel);
+ $httpBackend.whenPOST('/accounts').respond(function (method, url, data, headers) {
+ var newData = angular.fromJson(data);
+ newData.fromServer = true;
+ newData.id = testData.nextAccountId;
+ return [201, JSON.stringify(newData), ''];
+ });
+ $httpBackend.whenGET('/accounts/do-something').respond(testData.accountsDoSomethingModel);
+ $httpBackend.whenGET('/accounts/search/byOwner').respond(testData.accountsModel);
+
+ // CRUD /accounts/{id}
+ $httpBackend.whenGET('/accounts/0,1').respond(testData.accountsModel);
+ $httpBackend.whenGET('/accounts/messages').respond(testData.messages);
+ $httpBackend.whenGET('/accounts/1/message').respond(testData.messages[0]);
+ $httpBackend.whenGET('/accounts/1/messages').respond(testData.messages);
+ $httpBackend.whenGET('/accounts/0').respond(testData.accountsModel[0]);
+ $httpBackend.whenGET('/accounts/1').respond(testData.accountsModel[1]);
+ $httpBackend.whenJSONP('/accounts/1').respond(testData.accountsModel[1]);
+ $httpBackend.whenGET('/accounts/1/transactions').respond(testData.accountsModel[1].transactions);
+ $httpBackend.whenGET('/accounts/1/transactions/1').respond(testData.accountsModel[1].transactions[1]);
+ $httpBackend.whenPOST('/accounts/1/transactions').respond(function (method, url, data, headers) {
+ return [201, '', ''];
+ });
+ $httpBackend.whenDELETE('/accounts/1/transactions/1').respond(function (method, url, data, headers) {
+ return [200, '', ''];
+ });
+ $httpBackend.whenDELETE('/accounts/1').respond(function (method, url, data, headers) {
+ return [200, '', ''];
+ });
+ $httpBackend.whenPOST('/accounts/1').respond(function (method, url, data, headers) {
+ return [200, '', ''];
+ });
+ $httpBackend.whenPUT('/accounts/1').respond(function (method, url, data, headers) {
+ testData.accountsModel[1] = angular.fromJson(data);
+ return [201, data, ''];
+ });
+
+
+ $httpBackend.whenGET('/info').respond(testData.infoModel);
+ $httpBackend.whenGET('/accounts/1/info').respond(testData.infoModel);
+ $httpBackend.whenPUT('/info').respond(function (method, url, data) {
+ return [200, data, ''];
+ });
+
+ $httpBackend.whenGET('/accountsHAL').respond(testData.accountsHalModel);
+ $httpBackend.whenPUT('/accountsHAL/martin').respond(function (method, url, data) {
+ testData.accountsHalModel[0] = angular.fromJson(data);
+ return [200, data, ''];
+ });
+
+ // Full URL
+ $httpBackend.whenGET('http://accounts.com/all').respond(testData.accountsModel);
+
+ $httpBackend.whenGET('/error').respond(function () {
+ return [500, {}, ''];
+ });
+
+ $httpBackend.whenGET('/misc/zero').respond(function () {
+ return [200, 0, ''];
+ });
+
+ // return the status code given
+ // e.g.: /error/404 returns 404 Not Found
+ var urlRegex = /\/error\/(\d{3})/;
+ $httpBackend.whenGET(urlRegex).respond(function (method, url, data, headers) {
+ return [url.match(urlRegex)[1], {}, ''];
+ });
+
+ $httpBackend.whenGET('/customers/').respond(testData.customers);
+ $httpBackend.whenGET('http://localhost:8080/customers/').respond(testData.customers);
+ $httpBackend.whenGET('api.new.domain/customers/').respond(testData.customers);
+ $httpBackend.whenGET('/customers/?active=true').respond(testData.customers);
+ $httpBackend.whenGET('/customers/publications/?tags=chemistry').respond(testData.publications);
+ $httpBackend.whenPUT('/customers/0').respond(function (method, url, data) {
+ testData.customers[0] = angular.fromJson(data);
+ return [200, data, ''];
});
- $httpBackend.whenPOST("/customers/").respond(function (method, url, data, headers) {
+ $httpBackend.whenPOST('/customers/').respond(function (method, url, data, headers) {
var newData = angular.fromJson(data);
newData.fromServer = true;
- return [201, JSON.stringify(newData), ""];
+ return [201, JSON.stringify(newData), ''];
});
- }));
+ }); // END OF BEFOREEACH
- afterEach(function() {
+ afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
- describe("Interceptors", function() {
- it("Should add multiple request and response interceptors", function() {
- Restangular.addRequestInterceptor(function(elem) {
+ describe('stripRestangular', function () {
+ // We test stripRestangular by saving objects and checking
+ // the data received by the backend in the POST request.
+ // StripRestangular is used to remove Restangular's methods
+ // from the restangularized POST data (element), but not from raw data.
+
+ it('should not strip Restangular properties from raw POST data', function () {
+ // https://github.com/mgonto/restangular/issues/374
+ var restangularFields = Restangular.configuration.restangularFields;
+ // create an object whose keys are the values of the restangularFields
+ var postData = {};
+ _.each(restangularFields, function (value, key) {
+ postData.value = value;
+ });
+ // we don't want our post data to be treated as a restangularized object
+ postData.restangularized = false;
+ // when posting, restangular shouldn't remove any of our properties
+ var expectedData = angular.copy(postData);
+
+ $httpBackend.expectPOST('/accounts/1/merge', expectedData).respond(200);
+ var parent = Restangular.restangularizeElement(null, {id: 1}, 'accounts', true);
+ parent.post('merge', postData);
+ $httpBackend.flush();
+ });
+
+ it('should not strip "original" Restangular properties in restangularized POST data when overriding restangularFields', function () {
+ // https://github.com/mgonto/restangular/issues/374
+ // Here, we want to post a data object with fields
+ // that normally are used by Restangular, such that save, ids, options etc.
+ // We do that by taking each field in restangularFields and overriding it
+ // with something else, making restangular use different properties
+ // for its internal properties and functions, freeing the original ones
+ // for use in our data object
+
+ // these are the original field names used by restangular
+ var restangularFields = Restangular.configuration.restangularFields;
+ // create an object whose keys are the values of the restangularFields
+ // i.e. {save: "save", clone: "clone", doPOST: "doPOST", ...}
+ var postData = {};
+ _.each(restangularFields, function (value, key) {
+ postData.value = value;
+ });
+ // we expect the http service to get all of these "original" properties in the data object
+ var expectedData = angular.copy(postData);
+
+ // Override the field names used internally by Restangular,
+ // the new config will be something like
+ // {id: '_id', save: '_save', clone: '_clone', ...}
+ var newFieldConfig = {};
+ _.each(restangularFields, function (value, key) {
+ newFieldConfig[key] = '_' + key;
+ });
+ Restangular.setRestangularFields(newFieldConfig);
+
+ // Restangularize the data as an element to save
+ var parent = Restangular.restangularizeElement(null, postData, 'accounts', false);
+
+ // make the POST and check the posted data
+ $httpBackend.expectPOST('/accounts', expectedData).respond(200);
+ parent._save(); // we've overriden the save method as _save
+ $httpBackend.flush();
+ });
+ });
+
+ describe('Interceptors', function () {
+ it('Should add multiple request and response interceptors', function () {
+ Restangular.addRequestInterceptor(function (elem) {
var elemCopy = angular.copy(elem);
elemCopy.firstRequestInterceptor = true;
return elemCopy;
});
- Restangular.addRequestInterceptor(function(elem) {
+ Restangular.addRequestInterceptor(function (elem) {
expect(elem.firstRequestInterceptor).toBeDefined();
var elemCopy = angular.copy(elem);
elemCopy.secondRequestInterceptor = true;
return elemCopy;
});
- Restangular.addFullRequestInterceptor(function(elem) {
+ Restangular.addFullRequestInterceptor(function (elem) {
expect(elem.firstRequestInterceptor).toBeDefined();
expect(elem.secondRequestInterceptor).toBeDefined();
var elemCopy = angular.copy(elem);
@@ -205,138 +321,142 @@ describe("Restangular", function() {
};
});
- Restangular.addResponseInterceptor(function(elem) {
+ Restangular.addResponseInterceptor(function (elem) {
var elemCopy = angular.copy(elem);
elemCopy.firstResponseInterceptor = true;
return elemCopy;
});
- Restangular.addResponseInterceptor(function(elem) {
+ Restangular.addResponseInterceptor(function (elem) {
expect(elem.firstResponseInterceptor).toBeDefined();
var elemCopy = angular.copy(elem);
elemCopy.secondResponseInterceptor = true;
return elemCopy;
});
- $httpBackend.whenPOST("/list").respond(function(method, url, data, headers) {
+ $httpBackend.whenPOST('/list').respond(function (method, url, data, headers) {
var elem = angular.fromJson(data);
expect(elem.firstRequestInterceptor).toBeDefined();
expect(elem.secondRequestInterceptor).toBeDefined();
expect(elem.thirdRequestInterceptor).toBeDefined();
- return [200, elem, ""];
+ return [200, elem, ''];
});
$httpBackend.expectPOST('/list');
- Restangular.all('list').post({name: "Gonto"}).then(function(elem) {
+ Restangular.all('list').post({
+ name: 'Gonto'
+ }).then(function (elem) {
expect(elem.firstResponseInterceptor).toBeDefined();
expect(elem.secondResponseInterceptor).toBeDefined();
- });
+ });
- $httpBackend.flush();
+ $httpBackend.flush();
});
- it("Should add multiple error interceptors", function() {
- $httpBackend.expectGET("/error");
+ it('Should add multiple error interceptors', function () {
+ $httpBackend.expectGET('/error');
- var CallbackManager = function() {};
- CallbackManager.successCallback = function() {
+ var CallbackManager = function () {};
+ CallbackManager.successCallback = function () {
expect(CallbackManager.successCallback).not.toHaveBeenCalled();
};
- CallbackManager.errorCallback = function() {
+ CallbackManager.errorCallback = function () {
expect(CallbackManager.firstErrorInterceptor).toHaveBeenCalled();
expect(CallbackManager.secondErrorInterceptor).toHaveBeenCalled();
};
- CallbackManager.firstErrorInterceptor = function() {};
- CallbackManager.secondErrorInterceptor = function() {};
+ CallbackManager.firstErrorInterceptor = function () {};
+ CallbackManager.secondErrorInterceptor = function () {};
- spyOn(CallbackManager, "successCallback").andCallThrough();
- spyOn(CallbackManager, "firstErrorInterceptor").andCallThrough();
- spyOn(CallbackManager, "secondErrorInterceptor").andCallThrough();
+ spyOn(CallbackManager, 'successCallback').and.callThrough();
+ spyOn(CallbackManager, 'firstErrorInterceptor').and.callThrough();
+ spyOn(CallbackManager, 'secondErrorInterceptor').and.callThrough();
Restangular.addErrorInterceptor(CallbackManager.firstErrorInterceptor);
Restangular.addErrorInterceptor(CallbackManager.secondErrorInterceptor);
- Restangular.all("error").getList()
+ Restangular.all('error').getList()
.then(CallbackManager.successCallback)
.catch(CallbackManager.errorCallback);
$httpBackend.flush();
});
- it("Should add multiple error interceptors but don't reject the promise if one of them returns false", function() {
- $httpBackend.expectGET("/error");
+ it('Should add multiple error interceptors but don\'t reject the promise if one of them returns false', function () {
+ $httpBackend.expectGET('/error');
- var CallbackManager = function() {};
- CallbackManager.successCallback = function() {
+ var CallbackManager = function () {};
+ CallbackManager.successCallback = function () {
expect(CallbackManager.successCallback).not.toHaveBeenCalled();
};
- CallbackManager.errorCallback = function() {
+ CallbackManager.errorCallback = function () {
expect(CallbackManager.errorCallback).not.toHaveBeenCalled();
};
- CallbackManager.firstErrorInterceptor = function() {
+ CallbackManager.firstErrorInterceptor = function () {
return true;
};
- CallbackManager.secondErrorInterceptor = function() {
+ CallbackManager.secondErrorInterceptor = function () {
return false; // prevent promise to be rejected
};
- spyOn(CallbackManager, "successCallback").andCallThrough();
- spyOn(CallbackManager, "errorCallback").andCallThrough();
+ spyOn(CallbackManager, 'successCallback').and.callThrough();
+ spyOn(CallbackManager, 'errorCallback').and.callThrough();
Restangular.addErrorInterceptor(CallbackManager.firstErrorInterceptor);
Restangular.addErrorInterceptor(CallbackManager.secondErrorInterceptor);
- Restangular.all("error").getList()
+ Restangular.all('error').getList()
.then(CallbackManager.successCallback)
.catch(CallbackManager.errorCallback);
$httpBackend.flush();
});
- it("Should add multiple error interceptors for a single get too", function() {
- $httpBackend.expectGET("/error/404");
+ it('Should add multiple error interceptors for a single get too', function () {
+ $httpBackend.expectGET('/error/404');
- var CallbackManager = function() {};
- CallbackManager.successCallback = function() {
+ var CallbackManager = function () {};
+ CallbackManager.successCallback = function () {
expect(CallbackManager.successCallback).not.toHaveBeenCalled();
};
- CallbackManager.errorCallback = function() {
+ CallbackManager.errorCallback = function () {
expect(CallbackManager.firstErrorInterceptor).toHaveBeenCalled();
expect(CallbackManager.secondErrorInterceptor).toHaveBeenCalled();
};
- CallbackManager.firstErrorInterceptor = function(response) {
+ CallbackManager.firstErrorInterceptor = function (response) {
expect(Number(response.status)).toEqual(404);
};
- CallbackManager.secondErrorInterceptor = function() {};
+ CallbackManager.secondErrorInterceptor = function () {};
- spyOn(CallbackManager, "successCallback").andCallThrough();
- spyOn(CallbackManager, "firstErrorInterceptor").andCallThrough();
- spyOn(CallbackManager, "secondErrorInterceptor").andCallThrough();
+ spyOn(CallbackManager, 'successCallback').and.callThrough();
+ spyOn(CallbackManager, 'firstErrorInterceptor').and.callThrough();
+ spyOn(CallbackManager, 'secondErrorInterceptor').and.callThrough();
Restangular.addErrorInterceptor(CallbackManager.firstErrorInterceptor);
Restangular.addErrorInterceptor(CallbackManager.secondErrorInterceptor);
- Restangular.one("error", 404).get()
- .then(CallbackManager.successCallback)
- .catch(CallbackManager.errorCallback);
+ Restangular.one('error', 404).get()
+ .then(CallbackManager.successCallback)
+ .catch(CallbackManager.errorCallback);
$httpBackend.flush();
});
});
- describe("Transformers", function() {
- it("Should decorate element both on server and local by default", function() {
+ describe('Transformers', function () {
+ it('Should decorate element both on server and local by default', function () {
- Restangular.extendModel('accounts', function(account) {
- account.extended = function() {return true;}
+ Restangular.extendModel('accounts', function (account) {
+ account.extended = function () {
+ return true;
+ };
return account;
});
- Restangular.one('accounts', 1).get().then(function(account) {
+ Restangular.one('accounts', 1).get().then(function (account) {
expect(account.extended).toBeDefined();
});
@@ -349,9 +469,9 @@ describe("Restangular", function() {
});
- describe("With Suffix", function() {
- it("shouldn't add suffix to getRestangularUrl", function() {
- var suffixRestangular = Restangular.withConfig(function(RestangularConfigurer) {
+ describe('With Suffix', function () {
+ it('shouldn\'t add suffix to getRestangularUrl', function () {
+ var suffixRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRequestSuffix('.json');
});
var collection = suffixRestangular.all('accounts');
@@ -359,8 +479,8 @@ describe("Restangular", function() {
expect(collection.one('1').getRestangularUrl()).toBe('/accounts/1');
});
- it("should add suffix to getRequestedUrl", function() {
- var suffixRestangular = Restangular.withConfig(function(RestangularConfigurer) {
+ it('should add suffix to getRequestedUrl', function () {
+ var suffixRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRequestSuffix('.json');
});
var collection = suffixRestangular.all('accounts');
@@ -368,8 +488,8 @@ describe("Restangular", function() {
expect(collection.one('1').getRequestedUrl()).toBe('/accounts/1.json');
});
- it("should add suffix to request", function() {
- var suffixRestangular = Restangular.withConfig(function(RestangularConfigurer) {
+ it('should add suffix to request', function () {
+ var suffixRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRequestSuffix('.json');
});
var collection = suffixRestangular.all('accounts');
@@ -380,8 +500,8 @@ describe("Restangular", function() {
$httpBackend.flush();
});
- it("shouldn't add suffix to allUrl", function() {
- var suffixRestangular = Restangular.withConfig(function(RestangularConfigurer) {
+ it('shouldn\'t add suffix to allUrl', function () {
+ var suffixRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRequestSuffix('.json');
});
$httpBackend.expectGET('http://accounts.com/all');
@@ -390,8 +510,8 @@ describe("Restangular", function() {
});
});
- describe("JSONp", function() {
- it("should work for get", function() {
+ describe('JSONp', function () {
+ it('should work for get', function () {
Restangular.setJsonp(true);
Restangular.one('accounts', 1).get();
@@ -399,7 +519,7 @@ describe("Restangular", function() {
$httpBackend.flush();
});
- it("should work for getList", function() {
+ it('should work for getList', function () {
Restangular.setJsonp(true);
Restangular.all('accounts').getList();
@@ -407,9 +527,12 @@ describe("Restangular", function() {
$httpBackend.flush();
});
- it("shouldn't override post", function() {
+ it('shouldn\'t override post', function () {
Restangular.setJsonp(true);
- restangularAccounts.post({id: 2, user: "Someone"});
+ restangularAccounts.post({
+ id: 2,
+ user: 'Someone'
+ });
$httpBackend.expectPOST('/accounts').respond(201, '');
$httpBackend.flush();
@@ -418,9 +541,9 @@ describe("Restangular", function() {
});
- describe("Local data", function() {
- it("Should restangularize a collection OK", function() {
- var collection = angular.copy(accountsModel);
+ describe('Local data', function () {
+ it('Should restangularize a collection OK', function () {
+ var collection = angular.copy(testData.accountsModel);
Restangular.restangularizeCollection(null, collection, 'accounts');
@@ -432,8 +555,8 @@ describe("Restangular", function() {
});
- it("Should restangularize a function with arguments OK", function() {
- var collection = function(a, b) { };
+ it('Should restangularize a function with arguments OK', function () {
+ var collection = function (a, b) {};
Restangular.restangularizeCollection(null, collection, 'accounts');
@@ -441,13 +564,35 @@ describe("Restangular", function() {
expect(collection.getRestangularUrl()).toBe('/accounts');
});
+
+ it('should have fromServer set when restangularizeElement is called with that param', function () {
+ var element = Restangular.restangularizeElement(null, {}, 'accounts', true);
+ expect(element.fromServer).toEqual(true);
+
+ element = Restangular.restangularizeElement(null, {}, 'accounts', false);
+ expect(element.fromServer).toEqual(false);
+
+ element = Restangular.restangularizeElement(null, {}, 'accounts');
+ expect(element.fromServer).toEqual(false);
+ });
+
+ it('should have fromServer set when restangularizeCollection is called with that param', function () {
+ var collection = Restangular.restangularizeCollection(null, [{}], 'accounts', true);
+ expect(collection[0].fromServer).toEqual(true);
+
+ collection = Restangular.restangularizeCollection(null, [{}], 'accounts', false);
+ expect(collection[0].fromServer).toEqual(false);
+
+ collection = Restangular.restangularizeCollection(null, [{}], 'accounts');
+ expect(collection[0].fromServer).toEqual(false);
+ });
});
- describe("restangularizePromiseIntercept", function() {
- it("should be invoked by restangularizePromise", function() {
+ describe('restangularizePromiseIntercept', function () {
+ it('should be invoked by restangularizePromise', function () {
var calledWithPromise;
- Restangular.setRestangularizePromiseInterceptor(function(promise) {
+ Restangular.setRestangularizePromiseInterceptor(function (promise) {
calledWithPromise = promise;
promise.$object.$custom = true;
@@ -462,8 +607,8 @@ describe("Restangular", function() {
});
});
- describe("$object", function() {
- it("Should work for single get", function() {
+ describe('$object', function () {
+ it('Should work for single get', function () {
var promise = Restangular.one('accounts', 1).get();
var obj = promise.$object;
expect(obj).toBeDefined();
@@ -474,9 +619,11 @@ describe("Restangular", function() {
expect(obj.amount).toEqual(3.1416);
});
- it("Shouldn't be restangularized by default", function() {
- Restangular.extendModel('accounts', function(account) {
- account.extended = function() {return true;}
+ it('Shouldn\'t be restangularized by default', function () {
+ Restangular.extendModel('accounts', function (account) {
+ account.extended = function () {
+ return true;
+ };
return account;
});
@@ -488,7 +635,7 @@ describe("Restangular", function() {
$httpBackend.flush();
});
- it("Should work for single get", function() {
+ it('Should work for single get', function () {
var promise = Restangular.all('accounts').getList();
var list = promise.$object;
expect(list).toBeDefined();
@@ -501,106 +648,120 @@ describe("Restangular", function() {
});
});
- describe("ALL", function() {
- it("getList() should return an array of items", function() {
- restangularAccounts.getList().then(function(accounts) {
- expect(Restangular.stripRestangular(accounts)).toEqual(Restangular.stripRestangular(accountsModel));
+ describe('ALL', function () {
+ it('getList() should return an array of items', function () {
+ restangularAccounts.getList().then(function (accounts) {
+ expect(Restangular.stripRestangular(accounts)).toEqual(Restangular.stripRestangular(testData.accountsModel));
});
$httpBackend.flush();
});
- it("several getList() should return an array of items", function() {
+ it('several getList() should return an array of items', function () {
$httpBackend.expectGET('/accounts/0,1');
- Restangular.several("accounts", 0, 1).getList().then(function(accounts) {
- expect(Restangular.stripRestangular(accounts)).toEqual(Restangular.stripRestangular(accountsModel));
+ Restangular.several('accounts', 0, 1).getList().then(function (accounts) {
+ expect(Restangular.stripRestangular(accounts)).toEqual(Restangular.stripRestangular(testData.accountsModel));
});
$httpBackend.flush();
});
- it("several remove() should work", function() {
- $httpBackend.expectDELETE('/accounts/0,1').respond([200, "", ""]);
- Restangular.several("accounts", 0, 1).remove();
+ it('several remove() should work', function () {
+ $httpBackend.expectDELETE('/accounts/0,1').respond([200, '', '']);
+ Restangular.several('accounts', 0, 1).remove();
$httpBackend.flush();
});
- it("get(id) should return the item with given id", function() {
- restangularAccounts.get(0).then(function(account) {
- expect(Restangular.stripRestangular(account)).toEqual(Restangular.stripRestangular(accountsModel[0]));
+ it('get(id) should return the item with given id', function () {
+ restangularAccounts.get(0).then(function (account) {
+ expect(Restangular.stripRestangular(account)).toEqual(Restangular.stripRestangular(testData.accountsModel[0]));
});
$httpBackend.flush();
});
- it('uses all to get the list without parameters', function() {
+ it('uses all to get the list without parameters', function () {
Restangular.one('accounts', 1).all('messages').getList();
$httpBackend.expectGET('/accounts/1/messages');
$httpBackend.flush();
});
- it("Custom GET methods should work", function() {
- restangularAccounts.customGETLIST("messages").then(function(msgs) {
- expect(Restangular.stripRestangular(msgs)).toEqual(Restangular.stripRestangular(messages));
+ it('Custom GET methods should work', function () {
+ restangularAccounts.customGETLIST('messages').then(function (msgs) {
+ expect(Restangular.stripRestangular(msgs)).toEqual(Restangular.stripRestangular(testData.messages));
});
$httpBackend.flush();
});
- it("post() should add a new item", function() {
- restangularAccounts.post({id: 2, user: "Someone"}).then(function() {
- expect(accountsModel.length).toEqual(2);
+ it('post() should add a new item', function () {
+ restangularAccounts.post({
+ id: 2,
+ user: 'Someone'
+ }).then(function () {
+ expect(testData.accountsModel.length).toEqual(2);
});
$httpBackend.expectPOST('/accounts').respond(201, '');
$httpBackend.flush();
});
- it("customPOST() should add a new item", function() {
- restangularAccounts.customPOST({id: 2, user: "Someone"}).then(function() {
- expect(accountsModel.length).toEqual(2);
+ it('customPOST() should add a new item', function () {
+ restangularAccounts.customPOST({
+ id: 2,
+ user: 'Someone'
+ }).then(function () {
+ expect(testData.accountsModel.length).toEqual(2);
});
$httpBackend.expectPOST('/accounts').respond(201, '');
$httpBackend.flush();
});
- it("post() should work with arrays", function() {
- Restangular.all('places').post([{name: "Gonto"}, {name: 'John'}]).then(function(value) {
- expect(value.length).toEqual(2);
- });
+ it('post() should work with arrays', function () {
+ Restangular.all('places').post([{
+ name: 'Gonto'
+ }, {
+ name: 'John'
+ }]).then(function (value) {
+ expect(value.length).toEqual(2);
+ });
- $httpBackend.expectPOST('/places').respond(function(method, url, data, headers) {
- return [201, angular.fromJson(data), ""];
- });
+ $httpBackend.expectPOST('/places').respond(function (method, url, data, headers) {
+ return [201, angular.fromJson(data), ''];
+ });
- $httpBackend.flush();
- });
+ $httpBackend.flush();
+ });
- it("post() should add a new item with data and return the data from the server", function() {
- restangularAccounts.post(newAccount).then(function(added) {
- expect(added.fromServer).toEqual(true);
- expect(added.id).toEqual(newAccount.id);
- });
+ it('post() should add a new item with data and return the data from the server', function () {
+ restangularAccounts.post(testData.newAccount).then(function (added) {
+ expect(added.fromServer).toEqual(true);
+ expect(added.id).toEqual(testData.nextAccountId);
+ expect(added.user).toEqual(testData.newAccount.user);
+ });
- $httpBackend.expectPOST('/accounts');
- $httpBackend.flush();
- });
+ $httpBackend.expectPOST('/accounts');
+ $httpBackend.flush();
+ });
- it("Doing a post and then other operation (delete) should call right URLs", function() {
- restangularAccounts.post(newAccount).then(function(added) {
+ it('Doing a post and then other operation (delete) should call right URLs', function () {
+ restangularAccounts.post(testData.newAccount).then(function (added) {
added.remove();
- $httpBackend.expectDELETE('/accounts/44').respond(201, '');
+ $httpBackend.expectDELETE('/accounts/' + testData.nextAccountId).respond(201, '');
});
$httpBackend.flush();
});
- it("Doing a post to a server that returns no element will return undefined", function() {
- restangularAccounts.getList().then(function(accounts) {
- var newTransaction = {id: 1, name: "Gonto"};
- accounts[1].post('transactions', newTransaction).then(function(transaction) {
+ it('Doing a post to a server that returns no element will return undefined', function () {
+ restangularAccounts.getList().then(function (accounts) {
+ var newTransaction = {
+ id: 1,
+ name: 'Gonto'
+ };
+ accounts[1].post('transactions', newTransaction).then(function (transaction) {
expect(transaction).toBeUndefined();
});
});
@@ -608,64 +769,74 @@ describe("Restangular", function() {
$httpBackend.flush();
});
- it("head() should safely return", function() {
- restangularAccounts.head().then(function() {
+ it('head() should safely return', function () {
+ restangularAccounts.head().then(function () {
expect(true).toBe(true);
});
$httpBackend.flush();
});
- it("trace() should safely return", function() {
- restangularAccounts.trace().then(function() {
+ it('trace() should safely return', function () {
+ restangularAccounts.trace().then(function () {
expect(true).toBe(true);
});
$httpBackend.flush();
});
- it("customPUT should work", function() {
- $httpBackend.expectPUT('/accounts/hey').respond(accountsModel);
- restangularAccounts.customPUT({key: 'value'}, 'hey');
+ it('customPUT should work', function () {
+ $httpBackend.expectPUT('/accounts/hey').respond(testData.accountsModel);
+ restangularAccounts.customPUT({
+ key: 'value'
+ }, 'hey');
$httpBackend.flush();
});
- it("customPATCH should work", function() {
- var data = { foo: 'bar' };
- $httpBackend.expectPATCH('/accounts/hey', data).respond(accountsModel);
+ it('customPATCH should work', function () {
+ var data = {
+ foo: 'bar'
+ };
+ $httpBackend.expectPATCH('/accounts/hey', data).respond(testData.accountsModel);
restangularAccounts.customPATCH(data, 'hey');
$httpBackend.flush();
});
- it("options() should safely return", function() {
- restangularAccounts.options().then(function() {
+ it('options() should safely return', function () {
+ restangularAccounts.options().then(function () {
expect(true).toBe(true);
});
$httpBackend.flush();
});
- it("getList() should correctly handle params after customDELETE", function() {
- $httpBackend.expectGET('/accounts?foo=1').respond(accountsModel);
- restangularAccounts.getList({foo: 1}).then(function(){
+ it('getList() should correctly handle params after customDELETE', function () {
+ $httpBackend.expectGET('/accounts?foo=1').respond(testData.accountsModel);
+ restangularAccounts.getList({
+ foo: 1
+ }).then(function () {
$httpBackend.expectDELETE('/accounts?id=1').respond(201, '');
- return restangularAccounts.customDELETE('', {id: 1});
- }).then(function() {
- $httpBackend.expectGET('/accounts?foo=1').respond(accountsModel);
- return restangularAccounts.getList({foo: 1});
- }).then(function(accounts) {
- expect(Restangular.stripRestangular(accounts)).toEqual(Restangular.stripRestangular(accountsModel));
+ return restangularAccounts.customDELETE('', {
+ id: 1
+ });
+ }).then(function () {
+ $httpBackend.expectGET('/accounts?foo=1').respond(testData.accountsModel);
+ return restangularAccounts.getList({
+ foo: 1
});
+ }).then(function (accounts) {
+ expect(Restangular.stripRestangular(accounts)).toEqual(Restangular.stripRestangular(testData.accountsModel));
+ });
$httpBackend.flush();
});
});
- describe("Scoped Service", function() {
+ describe('Scoped Service', function () {
- it("should correctly work", function() {
+ it('should correctly work', function () {
var Accounts = Restangular.service('accounts');
- Accounts.post(newAccount);
+ Accounts.post(testData.newAccount);
Accounts.one(0).get();
Accounts.getList();
@@ -673,23 +844,25 @@ describe("Restangular", function() {
$httpBackend.expectGET('/accounts/0');
$httpBackend.expectGET('/accounts');
$httpBackend.flush();
- });
+ });
- it("should correctly work with children", function() {
+ it('should correctly work with children', function () {
var Transactions = Restangular.service('transactions', restangularAccount1);
- Transactions.post(newAccount);
+ Transactions.post(testData.newAccount);
Transactions.one(1).get();
Transactions.getList();
+ Transactions.get(1);
$httpBackend.expectPOST('/accounts/1/transactions');
$httpBackend.expectGET('/accounts/1/transactions/1');
$httpBackend.expectGET('/accounts/1/transactions');
+ $httpBackend.expectGET('/accounts/1/transactions/1');
$httpBackend.flush();
- });
+ });
- it("should add custom collection method added with withConfig", function() {
- var Accounts = Restangular.withConfig(function(RestangularConfigurer) {
- RestangularConfigurer.addElementTransformer('accounts', true, function(worker) {
+ it('should add custom collection method added with withConfig', function () {
+ var Accounts = Restangular.withConfig(function (RestangularConfigurer) {
+ RestangularConfigurer.addElementTransformer('accounts', true, function (worker) {
worker.addRestangularMethod('doSomething', 'get', 'do-something');
return worker;
});
@@ -698,31 +871,44 @@ describe("Restangular", function() {
expect(Accounts.doSomething).toBeDefined();
expect(_.isFunction(Accounts.doSomething)).toBeTruthy();
- Accounts.post(newAccount);
+ Accounts.post(testData.newAccount);
Accounts.one(0).get();
Accounts.getList();
Accounts.doSomething();
+ Accounts.get(0);
$httpBackend.expectPOST('/accounts');
$httpBackend.expectGET('/accounts/0');
$httpBackend.expectGET('/accounts');
$httpBackend.expectGET('/accounts/do-something');
+ $httpBackend.expectGET('/accounts/0');
$httpBackend.flush();
});
+
+ it('should provide a one-off $http configuration method', function () {
+ var Accounts = Restangular.service('accounts');
+ Accounts.withHttpConfig({
+ transformRequest: angular.identity
+ });
+ Accounts.post(testData.newAccount);
+ $httpBackend.expectPOST('/accounts');
+ $httpBackend.flush();
+ });
+
});
- describe("ONE", function() {
- it("get() should return a JSON item", function() {
- restangularAccount1.get().then(function(account) {
+ describe('ONE', function () {
+ it('get() should return a JSON item', function () {
+ restangularAccount1.get().then(function (account) {
expect(Restangular.stripRestangular(account))
- .toEqual(Restangular.stripRestangular(accountsModel[1]));
+ .toEqual(Restangular.stripRestangular(testData.accountsModel[1]));
});
$httpBackend.flush();
});
- it("Should save as put correctly", function() {
- restangularAccount1.get().then(function(account) {
+ it('Should save as put correctly', function () {
+ restangularAccount1.get().then(function (account) {
$httpBackend.expectPUT('/accounts/1');
account.put();
});
@@ -730,65 +916,65 @@ describe("Restangular", function() {
$httpBackend.flush();
});
- it("Should save as post correctly", function() {
+ it('Should save as post correctly', function () {
var account1 = angular.copy(restangularAccount1);
$httpBackend.expectPOST('/accounts/1');
- account1.name = "Hey";
+ account1.name = 'Hey';
account1.save();
$httpBackend.flush();
});
- it("Should keep route property when element is created", function() {
+ it('Should keep route property when element is created', function () {
var account1 = Restangular.restangularizeElement(null, {}, 'accounts');
$httpBackend.expectPOST('/accounts');
$httpBackend.expectPUT('/accounts/1');
- account1.name = "Hey";
- account1.save().then(function(accountFromServer) {
+ account1.name = 'Hey';
+ account1.save().then(function (accountFromServer) {
accountFromServer.id = 1;
return accountFromServer.save();
- }).then(function(accountFromServer2) {
+ }).then(function (accountFromServer2) {
expect(accountFromServer2.route).toBe(account1.route);
});
- $httpBackend.flush()
+ $httpBackend.flush();
});
- it("Should make RequestLess connections with one", function() {
- restangularAccount1.one("transactions", 1).get().then(function(transaction) {
+ it('Should make RequestLess connections with one', function () {
+ restangularAccount1.one('transactions', 1).get().then(function (transaction) {
expect(Restangular.stripRestangular(transaction))
- .toEqual(Restangular.stripRestangular(accountsModel[1].transactions[1]));
+ .toEqual(Restangular.stripRestangular(testData.accountsModel[1].transactions[1]));
});
$httpBackend.flush();
});
- it("Should make RequestLess connections with all", function() {
- restangularAccount1.all("transactions").getList().then(function(transactions) {
+ it('Should make RequestLess connections with all', function () {
+ restangularAccount1.all('transactions').getList().then(function (transactions) {
expect(Restangular.stripRestangular(transactions))
- .toEqual(Restangular.stripRestangular(accountsModel[1].transactions));
+ .toEqual(Restangular.stripRestangular(testData.accountsModel[1].transactions));
});
$httpBackend.flush();
});
- it("Custom GET methods should work", function() {
- restangularAccount1.customGET("message").then(function(msg) {
- expect(Restangular.stripRestangular(msg)).toEqual(Restangular.stripRestangular(messages[0]));
+ it('Custom GET methods should work', function () {
+ restangularAccount1.customGET('message').then(function (msg) {
+ expect(Restangular.stripRestangular(msg)).toEqual(Restangular.stripRestangular(testData.messages[0]));
});
$httpBackend.flush();
});
- it("put() should update the value", function() {
- restangularAccount1.get().then(function(account) {
+ it('put() should update the value', function () {
+ restangularAccount1.get().then(function (account) {
account.amount = 1.618;
- account.put().then(function(newAc) {
- expect(accountsModel[1].amount).toEqual(1.618);
+ account.put().then(function (newAc) {
+ expect(testData.accountsModel[1].amount).toEqual(1.618);
newAc.remove();
- $httpBackend.expectDELETE("/accounts/1");
+ $httpBackend.expectDELETE('/accounts/1');
});
- $httpBackend.expectPUT("/accounts/1");
+ $httpBackend.expectPUT('/accounts/1');
});
@@ -796,11 +982,11 @@ describe("Restangular", function() {
$httpBackend.flush();
});
- it("should return an array when accessing a subvalue", function() {
- restangularAccount1.get().then(function(account) {
- account.getList("transactions").then(function(transactions) {
+ it('should return an array when accessing a subvalue', function () {
+ restangularAccount1.get().then(function (account) {
+ account.getList('transactions').then(function (transactions) {
expect(Restangular.stripRestangular(transactions))
- .toEqual(Restangular.stripRestangular(accountsModel[1].transactions));
+ .toEqual(Restangular.stripRestangular(testData.accountsModel[1].transactions));
});
});
@@ -808,103 +994,303 @@ describe("Restangular", function() {
});
});
- describe("COPY", function() {
- it("should copy an object and 'this' should reference the copied object", function() {
- var copiedAccount = Restangular.copy(accountsModel[0]);
+ describe('COPY', function () {
+ it('should copy an object and "this" should reference the copied object', function () {
+ var copiedAccount = Restangular.copy(testData.accountsModel[0]);
var that;
- copiedAccount.user = "Copied string";
- expect(copiedAccount).not.toBe(accountsModel[0]);
+ copiedAccount.user = 'Copied string';
+ expect(copiedAccount).not.toBe(testData.accountsModel[0]);
// create a spy for one of the methods to capture the value of 'this'
- spyOn(copiedAccount, 'getRestangularUrl').andCallFake(function() {
+ spyOn(copiedAccount, 'getRestangularUrl').and.callFake(function () {
that = this;
});
copiedAccount.getRestangularUrl(); // invoke the method we are spying on
expect(that).toBe(copiedAccount);
});
- });
- describe("getRestangularUrl", function() {
- it("should return the generated URL when you chain Restangular methods together", function() {
- var restangularSpaces = Restangular.one("accounts",123).one("buildings", 456).all("spaces");
- expect(restangularSpaces.getRestangularUrl()).toEqual("/accounts/123/buildings/456/spaces");
+ it('should copy an object and "fromServer" param should be the same with the copied object', function () {
+ var responseHandler = jasmine.createSpy();
+
+ // with fromServer=true
+ restangularAccount1.get().then(responseHandler);
+ $httpBackend.flush();
+ var account = responseHandler.calls.argsFor(0)[0],
+ copiedAccount = Restangular.copy(account);
+ expect(account.fromServer).toEqual(true);
+ expect(copiedAccount.fromServer).toEqual(true);
+
+ // with fromServer=false
+ account = Restangular.one('accounts', 123),
+ copiedAccount = Restangular.copy(account);
+ expect(account.fromServer).toEqual(false);
+ expect(copiedAccount.fromServer).toEqual(false);
+ });
+
+ it('should copy a collection and "fromServer" param should stay the same', function () {
+ var responseHandler = jasmine.createSpy();
+
+ // with collections, fromServer=false
+ var accounts = Restangular.all('accounts'),
+ copiedAccounts = Restangular.copy(accounts);
+ expect(accounts.fromServer).toEqual(false);
+ expect(copiedAccounts.fromServer).toEqual(false);
+
+ // with collections, fromServer = true;
+ restangularAccounts.getList().then(responseHandler);
+ $httpBackend.flush();
+ accounts = responseHandler.calls.argsFor(0)[0],
+ copiedAccounts = Restangular.copy(accounts);
+ expect(accounts.fromServer).toEqual(true);
+ expect(copiedAccounts.fromServer).toEqual(true);
+ });
+
+ it('should copy an object and "route" param should be the same in the copied object', function () {
+ // for element
+ var account = Restangular.one('accounts', 123),
+ copiedAccount = Restangular.copy(account);
+ expect(account.route).toEqual(copiedAccount.route);
+
+ // for collection
+ var accounts = Restangular.all('accounts'),
+ copiedAccounts = Restangular.copy(accounts);
+ expect(accounts.route).toEqual(copiedAccounts.route);
+ });
+
+ it('should copy an object and the parent property should stay the same', function () {
+ // element
+ var user = Restangular.one('account', 12).one('user', 14),
+ userCopy = Restangular.copy(user);
+ expect(user.parentResource.route).toEqual('account');
+ expect(userCopy.parentResource.route).toEqual('account');
+
+ // collection
+ var users = Restangular.one('account', 12).all('users'),
+ usersCopy = Restangular.copy(users);
+ expect(user.parentResource.route).toEqual('account');
+ expect(usersCopy.parentResource.route).toEqual('account');
});
});
- describe("getRestangularUrl with useCannonicalId set to true", function() {
- it("should return the generated URL when you chain Restangular methods together", function() {
- var R = Restangular.withConfig(function(config) {
- config.setUseCannonicalId(true);
+ describe('getRestangularUrl', function () {
+ it('should get the URL for the current object', function () {
+ var element = Restangular.one('accounts', 123);
+ expect(element.getRestangularUrl()).toEqual('/accounts/123');
+ });
+ it('should not include query parameters', function () {
+ var responseHandler = jasmine.createSpy(), element;
+ $httpBackend.expectGET('/accounts/123?query=params').respond({id: 123, name: 'account123'});
+ Restangular.one('accounts', 123).get({query: 'params'}).then(responseHandler);
+ $httpBackend.flush();
+
+ element = responseHandler.calls.argsFor(0)[0];
+ expect(element.getRestangularUrl()).toEqual('/accounts/123');
+ });
+ it('should be the same for the built resource as for the fetched resource', function () {
+ var responseHandler = jasmine.createSpy(), element, resource;
+ $httpBackend.expectGET('/accounts/123').respond({id: 123, name: 'Account 123'});
+ resource = Restangular.one('accounts', 123);
+ resource.get().then(responseHandler);
+ $httpBackend.flush();
+
+ element = responseHandler.calls.argsFor(0)[0];
+ expect(resource.getRestangularUrl()).toEqual('/accounts/123');
+ expect(element.getRestangularUrl()).toEqual('/accounts/123');
+ });
+ it('should use the id from the response, not the request', function () {
+ var responseHandler = jasmine.createSpy(), element, resource;
+ $httpBackend.expectGET('/accounts/123').respond({id: 444, name: 'Account 444'});
+ resource = Restangular.one('accounts', 123);
+ resource.get().then(responseHandler);
+ $httpBackend.flush();
+
+ element = responseHandler.calls.argsFor(0)[0];
+ expect(resource.getRestangularUrl()).toEqual('/accounts/123');
+ expect(element.getRestangularUrl()).toEqual('/accounts/444');
+ });
+ it('should have an empty id in the URL if the response id is empty', function () {
+ // https://github.com/mgonto/restangular/issues/1421
+ var responseHandler = jasmine.createSpy(), element, resource;
+ $httpBackend.expectGET('/accounts/123').respond({name: 'Account 444'});
+ resource = Restangular.one('accounts', 123);
+ resource.get().then(responseHandler);
+ $httpBackend.flush();
+
+ element = responseHandler.calls.argsFor(0)[0];
+ expect(resource.getRestangularUrl()).toEqual('/accounts/123');
+ expect(element.getRestangularUrl()).toEqual('/accounts');
+ });
+ it('should return the generated URL for PUTed elements', function () {
+ var responseHandler = jasmine.createSpy(), element;
+ $httpBackend.expectPUT('/accounts/123').respond({id: 123, name: 'Account 123'});
+ Restangular.one('accounts', 123).put().then(responseHandler);
+ $httpBackend.flush();
+
+ element = responseHandler.calls.argsFor(0)[0];
+ expect(element.getRestangularUrl()).toEqual('/accounts/123');
+ });
+ it('should return the generated URL for POSTed elements', function () {
+ var responseHandler = jasmine.createSpy(), element;
+ $httpBackend.expectPOST('/accounts').respond({id: 123, name: 'Account 123'});
+ Restangular.restangularizeElement(null, {name: 'Account 123'}, 'accounts', false, false).save().then(responseHandler);
+ $httpBackend.flush();
+
+ element = responseHandler.calls.argsFor(0)[0];
+ expect(element.getRestangularUrl()).toEqual('/accounts/123');
+ });
+ it('should return the generated URL when you chain Restangular methods together', function () {
+ var restangularSpaces = Restangular.one('accounts', 123).one('buildings', 456).all('spaces');
+ expect(restangularSpaces.getRestangularUrl()).toEqual('/accounts/123/buildings/456/spaces');
+ });
+
+ describe('with useCannonicalId set to true', function () {
+ it('should return the generated URL when you chain Restangular methods together', function () {
+ var R = Restangular.withConfig(function (config) {
+ config.setUseCannonicalId(true);
+ });
+ var restangularSpaces = R.one('accounts', 123).one('buildings', 456).all('spaces');
+ expect(restangularSpaces.getRestangularUrl()).toEqual('/accounts/123/buildings/456/spaces');
});
- var restangularSpaces = R.one("accounts",123).one("buildings", 456).all("spaces");
- expect(restangularSpaces.getRestangularUrl()).toEqual("/accounts/123/buildings/456/spaces");
});
});
-
- describe("addElementTransformer", function() {
- it("should allow for a custom method to be placed at the collection level", function() {
+ describe('addElementTransformer', function () {
+ it('should allow for a custom method to be placed at the collection level', function () {
var accountsPromise;
- Restangular.addElementTransformer('accounts', true, function(collection) {
- collection.totalAmount = function() {};
- return collection;
+ Restangular.addElementTransformer('accounts', true, function (collection) {
+ collection.totalAmount = function () {};
+ return collection;
});
accountsPromise = Restangular.all('accounts').getList();
- accountsPromise.then(function(accounts) {
- expect(typeof accounts.totalAmount).toEqual("function");
+ accountsPromise.then(function (accounts) {
+ expect(typeof accounts.totalAmount).toEqual('function');
});
$httpBackend.flush();
});
- it("should allow for a custom method to be placed at the model level when one model is requested", function() {
+ it('should allow for a custom method to be placed at the model level when one model is requested', function () {
var accountPromise;
- Restangular.addElementTransformer('accounts', false, function(model) {
- model.prettifyAmount = function() {};
- return model;
+ Restangular.addElementTransformer('accounts', false, function (model) {
+ model.prettifyAmount = function () {};
+ return model;
});
accountPromise = Restangular.one('accounts', 1).get();
- accountPromise.then(function(account) {
- expect(typeof account.prettifyAmount).toEqual("function");
+ accountPromise.then(function (account) {
+ expect(typeof account.prettifyAmount).toEqual('function');
+ });
+
+ $httpBackend.flush();
+ });
+
+ it('should allow for a custom method to be placed at the model level when several models are requested', function () {
+ var accountsPromise;
+
+ Restangular.addElementTransformer('accounts', false, function (model) {
+ model.prettifyAmount = function () {};
+ return model;
+ });
+
+ accountsPromise = Restangular.all('accounts', 1).getList();
+
+ accountsPromise.then(function (accounts) {
+ accounts.forEach(function (account) {
+ expect(typeof account.prettifyAmount).toEqual('function');
+ });
+ });
+
+ $httpBackend.flush();
+ });
+
+ it('should allow for a custom method to be placed at the collection level using a regexp matching the route', function () {
+ var accountsPromise;
+
+ Restangular.addElementTransformer(/^accounts/, false, function (model) {
+ model.prettifyAmount = function () {};
+ return model;
+ });
+
+ accountsPromise = Restangular.all('accounts/search/byOwner', 1).getList();
+
+ accountsPromise.then(function (accounts) {
+ accounts.forEach(function (account) {
+ expect(typeof account.prettifyAmount).toEqual('function');
+ });
+ });
+
+ $httpBackend.flush();
+ });
+
+ it('should work with cloned collections', function () {
+ var responseHandler = jasmine.createSpy();
+
+ Restangular.addElementTransformer(/^accounts/, true, function (collection) {
+ collection.customThing = 'customValue';
+ return collection;
});
+ Restangular.all('accounts').getList().then(responseHandler);
$httpBackend.flush();
+
+ var accounts = responseHandler.calls.argsFor(0)[0];
+ var accountsCopy = accounts.clone();
+
+ expect(accounts.customThing).toEqual('customValue');
+ expect(accountsCopy.customThing).toEqual('customValue');
});
- it("should allow for a custom method to be placed at the model level when several models are requested", function() {
+ it('should allow for a custom method to be placed at the model level using regexp route when one model is requested', function () {
var accountPromise;
- Restangular.addElementTransformer('accounts', false, function(model) {
- model.prettifyAmount = function() {};
- return model;
+ Restangular.addElementTransformer(/^accounts/, false, function (model) {
+ model.prettifyAmount = function () {};
+ return model;
+ });
+
+ accountPromise = Restangular.one('accounts', 1).get();
+
+ accountPromise.then(function (account) {
+ expect(typeof account.prettifyAmount).toEqual('function');
+ });
+
+ $httpBackend.flush();
+ });
+
+ it('should allow for a custom method to be placed at the model level using regexp when several models are requested', function () {
+ var accountsPromise;
+
+ Restangular.addElementTransformer(/^accounts/, false, function (model) {
+ model.prettifyAmount = function () {};
+ return model;
});
accountsPromise = Restangular.all('accounts', 1).getList();
- accountsPromise.then(function(accounts) {
- accounts.forEach(function(account, index) {
- expect(typeof account.prettifyAmount).toEqual("function");
+ accountsPromise.then(function (accounts) {
+ accounts.forEach(function (account) {
+ expect(typeof account.prettifyAmount).toEqual('function');
});
});
$httpBackend.flush();
});
+
});
- describe("extendCollection", function() {
- it("should be an alias for a specific invocation of addElementTransformer", function() {
+ describe('extendCollection', function () {
+ it('should be an alias for a specific invocation of addElementTransformer', function () {
var spy = spyOn(Restangular, 'addElementTransformer');
- var fn = function(collection) {
- collection.totalAmount = function() {};
+ var fn = function (collection) {
+ collection.totalAmount = function () {};
return collection;
};
@@ -914,12 +1300,12 @@ describe("Restangular", function() {
});
});
- describe("extendModel", function() {
- it("should be an alias for a specific invocation of addElementTransformer", function() {
+ describe('extendModel', function () {
+ it('should be an alias for a specific invocation of addElementTransformer', function () {
var spy = spyOn(Restangular, 'addElementTransformer');
- var fn = function(model) {
- model.prettifyAmount = function() {};
+ var fn = function (model) {
+ model.prettifyAmount = function () {};
return model;
};
@@ -929,16 +1315,18 @@ describe("Restangular", function() {
});
});
- describe("headers", function() {
- it("should return defaultHeaders", function() {
- var defaultHeaders = {testheader:'header value'};
+ describe('headers', function () {
+ it('should return defaultHeaders', function () {
+ var defaultHeaders = {
+ testheader: 'header value'
+ };
Restangular.setDefaultHeaders(defaultHeaders);
expect(Restangular.defaultHeaders).toEqual(defaultHeaders);
});
- it("should pass uppercase methods in X-HTTP-Method-Override", function() {
- Restangular.setMethodOverriders(["put"]);
- $httpBackend.expectPOST('/overriders/1').respond(function(method, url, data, headers) {
+ it('should pass uppercase methods in X-HTTP-Method-Override', function () {
+ Restangular.setMethodOverriders(['put']);
+ $httpBackend.expectPOST('/overriders/1').respond(function (method, url, data, headers) {
expect(headers['X-HTTP-Method-Override']).toBe('PUT');
return {};
});
@@ -947,18 +1335,24 @@ describe("Restangular", function() {
});
});
- describe("defaultRequestParams", function() {
- it("should return defaultRequestParams", function() {
- var defaultRequestParams = {param:'value'};
+ describe('defaultRequestParams', function () {
+ it('should return defaultRequestParams', function () {
+ var defaultRequestParams = {
+ param: 'value'
+ };
Restangular.setDefaultRequestParams(defaultRequestParams);
expect(Restangular.requestParams.common).toEqual(defaultRequestParams);
});
- it("should be able to set default params for get, post, put.. methods separately", function() {
- var postParams = {post:'value'},
- putParams = {put:'value'};
+ it('should be able to set default params for get, post, put.. methods separately', function () {
+ var postParams = {
+ post: 'value'
+ },
+ putParams = {
+ put: 'value'
+ };
Restangular.setDefaultRequestParams('post', postParams);
expect(Restangular.requestParams.post).toEqual(postParams);
@@ -969,8 +1363,10 @@ describe("Restangular", function() {
expect(Restangular.requestParams.common).not.toEqual(putParams);
});
- it("should be able to set default params for multiple methods with array", function() {
- var defaultParams = {param:'value'};
+ it('should be able to set default params for multiple methods with array', function () {
+ var defaultParams = {
+ param: 'value'
+ };
Restangular.setDefaultRequestParams(['post', 'put'], defaultParams);
@@ -981,9 +1377,9 @@ describe("Restangular", function() {
});
});
- describe("withConfig", function() {
- it("should create new service with scoped configuration", function() {
- var childRestangular = Restangular.withConfig(function(RestangularConfigurer){
+ describe('withConfig', function () {
+ it('should create new service with scoped configuration', function () {
+ var childRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api/v1');
});
@@ -992,12 +1388,12 @@ describe("Restangular", function() {
});
- it("should allow nested configurations", function() {
- var childRestangular = Restangular.withConfig(function(RestangularConfigurer){
+ it('should allow nested configurations', function () {
+ var childRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('/api/v1');
});
- var grandchildRestangular = childRestangular.withConfig(function(RestangularConfigurer){
+ var grandchildRestangular = childRestangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRequestSuffix('.json');
});
@@ -1012,11 +1408,11 @@ describe("Restangular", function() {
});
});
- describe("Self linking", function() {
- it("Should request the link in HAL format", function() {
- var linkRestangular = Restangular.withConfig(function(RestangularConfigurer) {
+ describe('Self linking', function () {
+ it('Should request the link in HAL format', function () {
+ var linkRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setRestangularFields({
- selfLink: "_links.self"
+ selfLink: '_links.self'
});
});
@@ -1024,74 +1420,78 @@ describe("Restangular", function() {
$httpBackend.flush();
var account = arr[0];
- $httpBackend.expectPUT("/accountsHAL/martin");
- account.name = "Updated";
+ $httpBackend.expectPUT('/accountsHAL/martin');
+ account.name = 'Updated';
account.put();
$httpBackend.flush();
});
});
- describe("Singe one (endpoint not expecting an id)", function() {
- it('does not use the id for single resource GET', function() {
+ describe('Singe one (endpoint not expecting an id)', function () {
+ it('does not use the id for single resource GET', function () {
Restangular.one('info', 0, true).get();
$httpBackend.expectGET('/info');
$httpBackend.flush();
});
- it('getRestangularUrl() returns still the url without id after GET', function() {
- record = Restangular.one('info', 0, true);
- record.get().then(function(data){
- expect(data.getRestangularUrl()).toEqual("/info")
+ it('getRestangularUrl() returns still the url without id after GET', function () {
+ var record = Restangular.one('info', 0, true);
+ record.get().then(function (data) {
+ expect(data.getRestangularUrl()).toEqual('/info');
});
$httpBackend.expectGET('/info');
$httpBackend.flush();
});
- it('does not use the id for single nested resource GET', function() {
- Restangular.one('accounts', 1).one('info', 0, true).get()
+ it('does not use the id for single nested resource GET', function () {
+ Restangular.one('accounts', 1).one('info', 0, true).get();
$httpBackend.expectGET('/accounts/1/info');
$httpBackend.flush();
});
- it('does not use the id for single resource PUT', function() {
+ it('does not use the id for single resource PUT', function () {
Restangular.one('info', 0, true).put();
$httpBackend.expectPUT('/info');
$httpBackend.flush();
});
});
- describe("setSelfLinkAbsoluteUrl", function() {
- it("works", function() {
- var childRestangular = Restangular.withConfig(function(RestangularConfigurer){
+ describe('setSelfLinkAbsoluteUrl', function () {
+ it('works', function () {
+ var childRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setSelfLinkAbsoluteUrl(false);
});
expect(Restangular.configuration.absoluteUrl).toEqual(true);
expect(childRestangular.configuration.absoluteUrl).toEqual(false);
- })
+ });
});
- describe("Misc", function () {
- it("should not strip [one] or [all] key from plain object", function () {
- Restangular.all("customs").customPOST({one: 'I am here', two: 'I am also here'}).then(function () {
- expect(1).toBe(1);
- }, function () {
- expect("Promise").toBe("correctly fulfilled");
+ describe('Misc', function () {
+
+ it('should accept 0 as response', function () {
+ Restangular.one('misc', 'zero').get().then(function (res) {
+ expect(res).toEqual(0);
});
$httpBackend.flush();
});
- it("should not stip non-restangularized elements", function () {
- expect(Restangular.stripRestangular(["test","test2"])).toEqual(["test","test2"]);
+ it('Should accept 0 as a proper id in custom requests', function () {
+ $httpBackend.expectDELETE('/accounts/0').respond(202);
+ Restangular.all('accounts').customDELETE(0);
+ $httpBackend.flush();
});
+
});
- describe("testing normalize url", function () {
+ describe('testing normalize url', function () {
- it("should get a list of objects", function () {
- Restangular.all('customers/').getList().then(function(res){
- res.getList({active: true});
+ it('should get a list of objects', function () {
+ Restangular.all('customers/').getList().then(function (res) {
+ res.getList({
+ active: true
+ });
$httpBackend.expectGET('/customers/?active=true');
//res.getList('publications/', {tags: 'chemistry'});
//$httpBackend.expectGET('/customers/publications/?tags=chemistry');
@@ -1100,93 +1500,217 @@ describe("Restangular", function() {
$httpBackend.flush();
});
- it("should get a list of objects even if the path has extra slashes", function () {
- Restangular.all('customers///').getList().then(function(res){
- res.getList({active: true});
+ it('should get a list of objects even if the path has extra slashes', function () {
+ Restangular.all('customers///').getList().then(function (res) {
+ res.getList({
+ active: true
+ });
$httpBackend.expectGET('/customers/?active=true');
});
$httpBackend.expectGET('/customers/');
$httpBackend.flush();
});
- it("should post with slash at the end", function () {
- Restangular.all('customers/').getList().then(function(res){
- res.post(newCustomer);
+ it('should post with slash at the end', function () {
+ Restangular.all('customers/').getList().then(function (res) {
+ res.post(testData.newCustomer);
$httpBackend.expectPOST('/customers/');
});
$httpBackend.expectGET('/customers/');
$httpBackend.flush();
});
- it("should put with slash at the end", function () {
- Restangular.all('customers/').getList().then(function(customers){
+ it('should put with slash at the end', function () {
+ Restangular.all('customers/').getList().then(function (customers) {
customers[0].put();
$httpBackend.expectPUT('/customers/0');
});
$httpBackend.flush();
});
- it("should return a normalized URL even it has extra slashes", function() {
- var restangularSpaces = Restangular.one("accounts//", 123).one("buildings//", 456).all("spaces///");
- expect(restangularSpaces.getRestangularUrl()).toEqual("/accounts/123/buildings/456/spaces/");
+ it('should return a normalized URL even it has extra slashes', function () {
+ var restangularSpaces = Restangular.one('accounts//', 123).one('buildings//', 456).all('spaces///');
+ expect(restangularSpaces.getRestangularUrl()).toEqual('/accounts/123/buildings/456/spaces/');
});
- it("should create a new service and still working normalized URL", function() {
- var newRes = Restangular.withConfig(function(RestangularConfigurer){
+ it('should create a new service and still working normalized URL', function () {
+ var newRes = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('http://localhost:8080');
});
expect(newRes.configuration.baseUrl).toEqual('http://localhost:8080');
- newRes.all("customers////").getList();
+ newRes.all('customers////').getList();
$httpBackend.expectGET('http://localhost:8080/customers/');
- var newApi = Restangular.withConfig(function(RestangularConfigurer){
+ var newApi = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('api.new.domain');
});
expect(newApi.configuration.baseUrl).toEqual('api.new.domain');
- newApi.all("customers////").getList();
+ newApi.all('customers////').getList();
$httpBackend.expectGET('api.new.domain/customers/');
$httpBackend.flush();
});
- it("Should work with absolute URL with //authority", function() {
- var newRes = Restangular.withConfig(function(RestangularConfigurer){
+ it('Should work with absolute URL with //authority', function () {
+ var newRes = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('//localhost:8080');
});
expect(newRes.configuration.baseUrl).toEqual('//localhost:8080');
- newRes.all("customers////").getList();
+ newRes.all('customers////').getList();
$httpBackend.expectGET('//localhost:8080/customers/').respond([]);
$httpBackend.flush();
});
});
- describe("setPlainByDefault", function() {
-
- it("should not add restangularized methods to response object", function() {
- var newRes = Restangular.withConfig(function(RestangularConfigurer) {
+ describe('setPlainByDefault', function () {
+ var plainByDefaultRestangular;
+
+ beforeEach(function () {
+ plainByDefaultRestangular = Restangular.withConfig(function (RestangularConfigurer) {
RestangularConfigurer.setPlainByDefault(true);
});
+ });
- expect(newRes.configuration.plainByDefault).toEqual(true);
+ it('should set the property on the configuration', function () {
+ expect(plainByDefaultRestangular.configuration.plainByDefault).toEqual(true);
+ });
- newRes.one('accounts',0).get().then(function(account) {
- expect(account).toEqual(accountsModel[0]);
+ it('should not add restangularized methods to response object', function () {
+ plainByDefaultRestangular.one('accounts', 0).get().then(function (account) {
+ expect(account).toEqual(testData.accountsModel[0]);
});
+ $httpBackend.flush();
+ });
+ it('shoud not add restangularized methods to response collection', function () {
+ plainByDefaultRestangular.all('accounts').getList().then(function (accounts) {
+ expect(accounts).toEqual(testData.accountsModel);
+ });
$httpBackend.flush();
});
- it("shoud not add restangularized methods to response collection", function() {
- var newRes = Restangular.withConfig(function(RestangularConfigurer) {
- RestangularConfigurer.setPlainByDefault(true);
+ describe('with ETag', function () {
+ beforeEach(function () {
+ $httpBackend.whenGET('/accounts').respond(
+ testData.accountsModel,
+ {'ETag': 'c11ea3f8-3bfd-4be8-a6a6-501dd831b8a4'}
+ );
+ $httpBackend.whenGET('/accounts/1').respond(
+ testData.accountsModel[1],
+ {'ETag': 'bf79b780-f132-4f44-a9eb-7e6eb4f902b2'}
+ );
+ });
+
+ it('should not add restangularized ETag to response object', function () {
+ plainByDefaultRestangular.one('accounts', 0).get().then(function (account) {
+ expect(account).toEqual(testData.accountsModel[0]);
+ });
+ $httpBackend.flush();
+ });
+
+ it('shoud not add restangularized ETag to response collection', function () {
+ plainByDefaultRestangular.all('accounts').getList().then(function (accounts) {
+ expect(accounts).toEqual(testData.accountsModel);
+ });
+ $httpBackend.flush();
});
+ });
+ });
- newRes.all('accounts').getList().then(function(accounts){
- expect(accounts).toEqual(accountsModel);
+ describe('ETags', function () {
+ beforeEach(function () {
+ $httpBackend.whenGET('/etagAccounts').respond(
+ testData.accountsModel,
+ {'ETag': 'c11ea3f8-3bfd-4be8-a6a6-501dd831b8a4'}
+ );
+ $httpBackend.whenGET('/etagAccounts/1').respond(
+ testData.accountsModel[1],
+ {'ETag': 'bf79b780-f132-4f44-a9eb-7e6eb4f902b2'}
+ );
+ });
+
+ it('should include the ETag in the restangularized element', function () {
+ Restangular.one('etagAccounts', 1).get().then(function (account) {
+ expect(account.restangularEtag).toEqual('bf79b780-f132-4f44-a9eb-7e6eb4f902b2');
+ });
+ $httpBackend.flush();
+ });
+ it('should include the ETag in the restangularized collection', function () {
+ Restangular.all('etagAccounts').getList().then(function (accounts) {
+ expect(accounts.restangularEtag).toEqual('c11ea3f8-3bfd-4be8-a6a6-501dd831b8a4');
});
$httpBackend.flush();
});
+ it('should add the If-Match header on PUT requests', function () {
+ var responseHandler = jasmine.createSpy();
+ Restangular.one('etagAccounts', 1).get().then(responseHandler);
+ $httpBackend.flush();
+
+ var account = responseHandler.calls.argsFor(0)[0];
+ $httpBackend.expect(
+ 'PUT',
+ '/etagAccounts/1',
+ testData.accountsModel[1],
+ function (headers) {
+ return headers['If-Match'] === 'bf79b780-f132-4f44-a9eb-7e6eb4f902b2';
+ }
+ ).respond(200);
+ account.save();
+ $httpBackend.flush();
+ });
+ it('should add the If-Match header on DELETE requests', function () {
+ var responseHandler = jasmine.createSpy();
+ Restangular.one('etagAccounts', 1).get().then(responseHandler);
+ $httpBackend.flush();
+
+ var account = responseHandler.calls.argsFor(0)[0];
+ $httpBackend.expect(
+ 'DELETE',
+ '/etagAccounts/1',
+ testData.accountsModel[1],
+ function (headers) {
+ return headers['If-Match'] === 'bf79b780-f132-4f44-a9eb-7e6eb4f902b2';
+ }
+ ).respond(200);
+ account.remove();
+ $httpBackend.flush();
+ });
+ it('should add the If-None-Match header on GET requests for elements', function () {
+ var responseHandler = jasmine.createSpy();
+ Restangular.one('etagAccounts', 1).get().then(responseHandler);
+ $httpBackend.flush();
+
+ var account = responseHandler.calls.argsFor(0)[0];
+ $httpBackend.expect(
+ 'GET',
+ '/etagAccounts/1',
+ undefined,
+ function (headers) {
+ return headers['If-None-Match'] === 'bf79b780-f132-4f44-a9eb-7e6eb4f902b2';
+ }
+ ).respond(200);
+ account.get();
+ $httpBackend.flush();
+ });
+ it('should add the If-None-Match header on GET requests for collections', function () {
+ var responseHandler = jasmine.createSpy();
+ Restangular.all('etagAccounts').getList().then(responseHandler);
+ $httpBackend.flush();
+
+ var accounts = responseHandler.calls.argsFor(0)[0];
+ $httpBackend.expect(
+ 'GET',
+ '/etagAccounts',
+ undefined,
+ function (headers) {
+ return headers['If-None-Match'] === 'c11ea3f8-3bfd-4be8-a6a6-501dd831b8a4';
+ }
+ ).respond(200);
+ accounts.getList();
+ $httpBackend.flush();
+ });
+
});
});