angularjs-rails 1.2.14 → 1.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,20 +1,72 @@
1
1
  /**
2
- * @license AngularJS v1.1.5
3
- * (c) 2010-2012 Google, Inc. http://angularjs.org
2
+ * @license AngularJS v1.3.0-beta.3
3
+ * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
6
- (function(window, angular, undefined) {
7
- 'use strict';
6
+ (function(window, angular, undefined) {'use strict';
7
+
8
+ var $resourceMinErr = angular.$$minErr('$resource');
9
+
10
+ // Helper functions and regex to lookup a dotted path on an object
11
+ // stopping at undefined/null. The path must be composed of ASCII
12
+ // identifiers (just like $parse)
13
+ var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;
14
+
15
+ function isValidDottedPath(path) {
16
+ return (path != null && path !== '' && path !== 'hasOwnProperty' &&
17
+ MEMBER_NAME_REGEX.test('.' + path));
18
+ }
19
+
20
+ function lookupDottedPath(obj, path) {
21
+ if (!isValidDottedPath(path)) {
22
+ throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
23
+ }
24
+ var keys = path.split('.');
25
+ for (var i = 0, ii = keys.length; i < ii && obj !== undefined; i++) {
26
+ var key = keys[i];
27
+ obj = (obj !== null) ? obj[key] : undefined;
28
+ }
29
+ return obj;
30
+ }
8
31
 
9
32
  /**
10
- * @ngdoc overview
33
+ * Create a shallow copy of an object and clear other fields from the destination
34
+ */
35
+ function shallowClearAndCopy(src, dst) {
36
+ dst = dst || {};
37
+
38
+ angular.forEach(dst, function(value, key){
39
+ delete dst[key];
40
+ });
41
+
42
+ for (var key in src) {
43
+ if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
44
+ dst[key] = src[key];
45
+ }
46
+ }
47
+
48
+ return dst;
49
+ }
50
+
51
+ /**
52
+ * @ngdoc module
11
53
  * @name ngResource
12
54
  * @description
55
+ *
56
+ * # ngResource
57
+ *
58
+ * The `ngResource` module provides interaction support with RESTful services
59
+ * via the $resource service.
60
+ *
61
+ *
62
+ * <div doc-module-components="ngResource"></div>
63
+ *
64
+ * See {@link ngResource.$resource `$resource`} for usage.
13
65
  */
14
66
 
15
67
  /**
16
- * @ngdoc object
17
- * @name ngResource.$resource
68
+ * @ngdoc service
69
+ * @name $resource
18
70
  * @requires $http
19
71
  *
20
72
  * @description
@@ -24,25 +76,15 @@
24
76
  * The returned resource object has action methods which provide high-level behaviors without
25
77
  * the need to interact with the low level {@link ng.$http $http} service.
26
78
  *
27
- * # Installation
28
- * To use $resource make sure you have included the `angular-resource.js` that comes in Angular
29
- * package. You can also find this file on Google CDN, bower as well as at
30
- * {@link http://code.angularjs.org/ code.angularjs.org}.
31
- *
32
- * Finally load the module in your application:
33
- *
34
- * angular.module('app', ['ngResource']);
35
- *
36
- * and you are ready to get started!
79
+ * Requires the {@link ngResource `ngResource`} module to be installed.
37
80
  *
38
81
  * @param {string} url A parametrized URL template with parameters prefixed by `:` as in
39
82
  * `/user/:username`. If you are using a URL with a port number (e.g.
40
- * `http://example.com:8080/api`), you'll need to escape the colon character before the port
41
- * number, like this: `$resource('http://example.com\\:8080/api')`.
83
+ * `http://example.com:8080/api`), it will be respected.
42
84
  *
43
- * If you are using a url with a suffix, just add the suffix, like this:
44
- * `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')
45
- * or even `$resource('http://example.com/resource/:resource_id.:format')`
85
+ * If you are using a url with a suffix, just add the suffix, like this:
86
+ * `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
87
+ * or even `$resource('http://example.com/resource/:resource_id.:format')`
46
88
  * If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
47
89
  * collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
48
90
  * can escape it with `/\.`.
@@ -60,9 +102,9 @@
60
102
  * If the parameter value is prefixed with `@` then the value of that parameter is extracted from
61
103
  * the data object (useful for non-GET operations).
62
104
  *
63
- * @param {Object.<Object>=} actions Hash with declaration of custom action that should extend the
64
- * default set of resource actions. The declaration should be created in the format of {@link
65
- * ng.$http#Parameters $http.config}:
105
+ * @param {Object.<Object>=} actions Hash with declaration of custom action that should extend
106
+ * the default set of resource actions. The declaration should be created in the format of {@link
107
+ * ng.$http#usage_parameters $http.config}:
66
108
  *
67
109
  * {action1: {method:?, params:?, isArray:?, headers:?, ...},
68
110
  * action2: {method:?, params:?, isArray:?, headers:?, ...},
@@ -70,21 +112,23 @@
70
112
  *
71
113
  * Where:
72
114
  *
73
- * - **`action`** – {string} – The name of action. This name becomes the name of the method on your
74
- * resource object.
75
- * - **`method`** – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`,
76
- * and `JSONP`.
77
- * - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of the
78
- * parameter value is a function, it will be executed every time when a param value needs to be
79
- * obtained for a request (unless the param was overridden).
80
- * - **`url`** – {string} – action specific `url` override. The url templating is supported just like
81
- * for the resource-level urls.
82
- * - **`isArray`** – {boolean=} – If true then the returned object for this action is an array, see
83
- * `returns` section.
84
- * - **`transformRequest`** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
115
+ * - **`action`** – {string} – The name of action. This name becomes the name of the method on
116
+ * your resource object.
117
+ * - **`method`** – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`,
118
+ * `DELETE`, and `JSONP`.
119
+ * - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
120
+ * the parameter value is a function, it will be executed every time when a param value needs to
121
+ * be obtained for a request (unless the param was overridden).
122
+ * - **`url`** – {string} – action specific `url` override. The url templating is supported just
123
+ * like for the resource-level urls.
124
+ * - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
125
+ * see `returns` section.
126
+ * - **`transformRequest`** –
127
+ * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
85
128
  * transform function or an array of such functions. The transform function takes the http
86
129
  * request body and headers and returns its transformed (typically serialized) version.
87
- * - **`transformResponse`** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
130
+ * - **`transformResponse`** –
131
+ * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
88
132
  * transform function or an array of such functions. The transform function takes the http
89
133
  * response body and headers and returns its transformed (typically deserialized) version.
90
134
  * - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
@@ -93,33 +137,38 @@
93
137
  * caching.
94
138
  * - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
95
139
  * should abort the request when resolved.
96
- * - **`withCredentials`** - `{boolean}` - whether to to set the `withCredentials` flag on the
97
- * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
98
- * requests with credentials} for more information.
99
- * - **`responseType`** - `{string}` - see {@link
100
- * https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
140
+ * - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
141
+ * XHR object. See
142
+ * [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
143
+ * for more information.
144
+ * - **`responseType`** - `{string}` - see
145
+ * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
146
+ * - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
147
+ * `response` and `responseError`. Both `response` and `responseError` interceptors get called
148
+ * with `http response` object. See {@link ng.$http $http interceptors}.
101
149
  *
102
150
  * @returns {Object} A resource "class" object with methods for the default set of resource actions
103
151
  * optionally extended with custom `actions`. The default set contains these actions:
104
- *
105
- * { 'get': {method:'GET'},
106
- * 'save': {method:'POST'},
107
- * 'query': {method:'GET', isArray:true},
108
- * 'remove': {method:'DELETE'},
109
- * 'delete': {method:'DELETE'} };
152
+ * ```js
153
+ * { 'get': {method:'GET'},
154
+ * 'save': {method:'POST'},
155
+ * 'query': {method:'GET', isArray:true},
156
+ * 'remove': {method:'DELETE'},
157
+ * 'delete': {method:'DELETE'} };
158
+ * ```
110
159
  *
111
160
  * Calling these methods invoke an {@link ng.$http} with the specified http method,
112
161
  * destination and parameters. When the data is returned from the server then the object is an
113
162
  * instance of the resource class. The actions `save`, `remove` and `delete` are available on it
114
163
  * as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
115
164
  * read, update, delete) on server-side data like this:
116
- * <pre>
117
- var User = $resource('/user/:userId', {userId:'@id'});
118
- var user = User.get({userId:123}, function() {
119
- user.abc = true;
120
- user.$save();
121
- });
122
- </pre>
165
+ * ```js
166
+ * var User = $resource('/user/:userId', {userId:'@id'});
167
+ * var user = User.get({userId:123}, function() {
168
+ * user.abc = true;
169
+ * user.$save();
170
+ * });
171
+ * ```
123
172
  *
124
173
  * It is important to realize that invoking a $resource object method immediately returns an
125
174
  * empty reference (object or array depending on `isArray`). Once the data is returned from the
@@ -127,7 +176,7 @@
127
176
  * usually the resource is assigned to a model which is then rendered by the view. Having an empty
128
177
  * object results in no rendering, once the data arrives from the server then the object is
129
178
  * populated with the data and the view automatically re-renders itself showing the new data. This
130
- * means that in most case one never has to write a callback function for the action methods.
179
+ * means that in most cases one never has to write a callback function for the action methods.
131
180
  *
132
181
  * The action methods on the class object or instance object can be invoked with the following
133
182
  * parameters:
@@ -136,30 +185,34 @@
136
185
  * - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
137
186
  * - non-GET instance actions: `instance.$action([parameters], [success], [error])`
138
187
  *
188
+ * Success callback is called with (value, responseHeaders) arguments. Error callback is called
189
+ * with (httpResponse) argument.
139
190
  *
140
- * The Resource instances and collection have these additional properties:
191
+ * Class actions return empty instance (with additional properties below).
192
+ * Instance actions return promise of the action.
141
193
  *
142
- * - `$then`: the `then` method of a {@link ng.$q promise} derived from the underlying
143
- * {@link ng.$http $http} call.
194
+ * The Resource instances and collection have these additional properties:
144
195
  *
145
- * The success callback for the `$then` method will be resolved if the underlying `$http` requests
146
- * succeeds.
196
+ * - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
197
+ * instance or collection.
147
198
  *
148
- * The success callback is called with a single object which is the {@link ng.$http http response}
149
- * object extended with a new property `resource`. This `resource` property is a reference to the
150
- * result of the resource action resource object or array of resources.
199
+ * On success, the promise is resolved with the same resource instance or collection object,
200
+ * updated with data from server. This makes it easy to use in
201
+ * {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
202
+ * rendering until the resource(s) are loaded.
151
203
  *
152
- * The error callback is called with the {@link ng.$http http response} object when an http
153
- * error occurs.
204
+ * On failure, the promise is resolved with the {@link ng.$http http response} object, without
205
+ * the `resource` property.
154
206
  *
155
- * - `$resolved`: true if the promise has been resolved (either with success or rejection);
156
- * Knowing if the Resource has been resolved is useful in data-binding.
207
+ * - `$resolved`: `true` after first server interaction is completed (either with success or
208
+ * rejection), `false` before that. Knowing if the Resource has been resolved is useful in
209
+ * data-binding.
157
210
  *
158
211
  * @example
159
212
  *
160
213
  * # Credit card resource
161
214
  *
162
- * <pre>
215
+ * ```js
163
216
  // Define CreditCard class
164
217
  var CreditCard = $resource('/user/:userId/card/:cardId',
165
218
  {userId:123, cardId:'@id'}, {
@@ -190,31 +243,32 @@
190
243
  newCard.name = "Mike Smith";
191
244
  newCard.$save();
192
245
  // POST: /user/123/card {number:'0123', name:'Mike Smith'}
193
- // server returns: {id:789, number:'01234', name: 'Mike Smith'};
246
+ // server returns: {id:789, number:'0123', name: 'Mike Smith'};
194
247
  expect(newCard.id).toEqual(789);
195
- * </pre>
248
+ * ```
196
249
  *
197
250
  * The object returned from this function execution is a resource "class" which has "static" method
198
251
  * for each action in the definition.
199
252
  *
200
- * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and `headers`.
253
+ * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
254
+ * `headers`.
201
255
  * When the data is returned from the server then the object is an instance of the resource type and
202
256
  * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
203
257
  * operations (create, read, update, delete) on server-side data.
204
258
 
205
- <pre>
259
+ ```js
206
260
  var User = $resource('/user/:userId', {userId:'@id'});
207
- var user = User.get({userId:123}, function() {
261
+ User.get({userId:123}, function(user) {
208
262
  user.abc = true;
209
263
  user.$save();
210
264
  });
211
- </pre>
265
+ ```
212
266
  *
213
- * It's worth noting that the success callback for `get`, `query` and other method gets passed
267
+ * It's worth noting that the success callback for `get`, `query` and other methods gets passed
214
268
  * in the response that came from the server as well as $http header getter function, so one
215
269
  * could rewrite the above example and get access to http headers as:
216
270
  *
217
- <pre>
271
+ ```js
218
272
  var User = $resource('/user/:userId', {userId:'@id'});
219
273
  User.get({userId:123}, function(u, getResponseHeaders){
220
274
  u.abc = true;
@@ -223,58 +277,50 @@
223
277
  //putResponseHeaders => $http header getter
224
278
  });
225
279
  });
226
- </pre>
227
-
228
- * # Buzz client
229
-
230
- Let's look at what a buzz client created with the `$resource` service looks like:
231
- <doc:example>
232
- <doc:source jsfiddle="false">
233
- <script>
234
- function BuzzController($resource) {
235
- this.userId = 'googlebuzz';
236
- this.Activity = $resource(
237
- 'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
238
- {alt:'json', callback:'JSON_CALLBACK'},
239
- {get:{method:'JSONP', params:{visibility:'@self'}}, replies: {method:'JSONP', params:{visibility:'@self', comments:'@comments'}}}
240
- );
241
- }
242
-
243
- BuzzController.prototype = {
244
- fetch: function() {
245
- this.activities = this.Activity.get({userId:this.userId});
246
- },
247
- expandReplies: function(activity) {
248
- activity.replies = this.Activity.replies({userId:this.userId, activityId:activity.id});
249
- }
250
- };
251
- BuzzController.$inject = ['$resource'];
252
- </script>
253
-
254
- <div ng-controller="BuzzController">
255
- <input ng-model="userId"/>
256
- <button ng-click="fetch()">fetch</button>
257
- <hr/>
258
- <div ng-repeat="item in activities.data.items">
259
- <h1 style="font-size: 15px;">
260
- <img src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
261
- <a href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
262
- <a href ng-click="expandReplies(item)" style="float: right;">Expand replies: {{item.links.replies[0].count}}</a>
263
- </h1>
264
- {{item.object.content | html}}
265
- <div ng-repeat="reply in item.replies.data.items" style="margin-left: 20px;">
266
- <img src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
267
- <a href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>: {{reply.content | html}}
268
- </div>
269
- </div>
270
- </div>
271
- </doc:source>
272
- <doc:scenario>
273
- </doc:scenario>
274
- </doc:example>
280
+ ```
281
+ *
282
+ * You can also access the raw `$http` promise via the `$promise` property on the object returned
283
+ *
284
+ ```
285
+ var User = $resource('/user/:userId', {userId:'@id'});
286
+ User.get({userId:123})
287
+ .$promise.then(function(user) {
288
+ $scope.user = user;
289
+ });
290
+ ```
291
+
292
+ * # Creating a custom 'PUT' request
293
+ * In this example we create a custom method on our resource to make a PUT request
294
+ * ```js
295
+ * var app = angular.module('app', ['ngResource', 'ngRoute']);
296
+ *
297
+ * // Some APIs expect a PUT request in the format URL/object/ID
298
+ * // Here we are creating an 'update' method
299
+ * app.factory('Notes', ['$resource', function($resource) {
300
+ * return $resource('/notes/:id', null,
301
+ * {
302
+ * 'update': { method:'PUT' }
303
+ * });
304
+ * }]);
305
+ *
306
+ * // In our controller we get the ID from the URL using ngRoute and $routeParams
307
+ * // We pass in $routeParams and our Notes factory along with $scope
308
+ * app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
309
+ function($scope, $routeParams, Notes) {
310
+ * // First get a note object from the factory
311
+ * var note = Notes.get({ id:$routeParams.id });
312
+ * $id = note.id;
313
+ *
314
+ * // Now call update passing in the ID first then the object you are updating
315
+ * Notes.update({ id:$id }, note);
316
+ *
317
+ * // This will PUT /notes/ID with the note object in the request payload
318
+ * }]);
319
+ * ```
275
320
  */
276
321
  angular.module('ngResource', ['ng']).
277
- factory('$resource', ['$http', '$parse', function($http, $parse) {
322
+ factory('$resource', ['$http', '$q', function($http, $q) {
323
+
278
324
  var DEFAULT_ACTIONS = {
279
325
  'get': {method:'GET'},
280
326
  'save': {method:'POST'},
@@ -286,10 +332,7 @@ angular.module('ngResource', ['ng']).
286
332
  forEach = angular.forEach,
287
333
  extend = angular.extend,
288
334
  copy = angular.copy,
289
- isFunction = angular.isFunction,
290
- getter = function(obj, path) {
291
- return $parse(path)(obj);
292
- };
335
+ isFunction = angular.isFunction;
293
336
 
294
337
  /**
295
338
  * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
@@ -311,9 +354,9 @@ angular.module('ngResource', ['ng']).
311
354
 
312
355
 
313
356
  /**
314
- * This method is intended for encoding *key* or *value* parts of query component. We need a custom
315
- * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
316
- * encoded per http://tools.ietf.org/html/rfc3986:
357
+ * This method is intended for encoding *key* or *value* parts of query component. We need a
358
+ * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
359
+ * have to be encoded per http://tools.ietf.org/html/rfc3986:
317
360
  * query = *( pchar / "/" / "?" )
318
361
  * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
319
362
  * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
@@ -345,8 +388,12 @@ angular.module('ngResource', ['ng']).
345
388
 
346
389
  var urlParams = self.urlParams = {};
347
390
  forEach(url.split(/\W/), function(param){
348
- if (param && (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
349
- urlParams[param] = true;
391
+ if (param === 'hasOwnProperty') {
392
+ throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
393
+ }
394
+ if (!(new RegExp("^\\d+$").test(param)) && param &&
395
+ (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
396
+ urlParams[param] = true;
350
397
  }
351
398
  });
352
399
  url = url.replace(/\\:/g, ':');
@@ -356,7 +403,9 @@ angular.module('ngResource', ['ng']).
356
403
  val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
357
404
  if (angular.isDefined(val) && val !== null) {
358
405
  encodedVal = encodeUriSegment(val);
359
- url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), encodedVal + "$1");
406
+ url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
407
+ return encodedVal + p1;
408
+ });
360
409
  } else {
361
410
  url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
362
411
  leadingSlashes, tail) {
@@ -370,13 +419,13 @@ angular.module('ngResource', ['ng']).
370
419
  });
371
420
 
372
421
  // strip trailing slashes and set the url
373
- url = url.replace(/\/+$/, '');
422
+ url = url.replace(/\/+$/, '') || '/';
374
423
  // then replace collapse `/.` if found in the last URL path segment before the query
375
424
  // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
376
425
  url = url.replace(/\/\.(?=\w+($|\?))/, '.');
377
426
  // replace escaped `/\.` with `/.`
378
427
  config.url = url.replace(/\/\\\./, '/.');
379
-
428
+
380
429
 
381
430
  // set params - delegate param encoding to $http
382
431
  forEach(params, function(value, key){
@@ -389,7 +438,7 @@ angular.module('ngResource', ['ng']).
389
438
  };
390
439
 
391
440
 
392
- function ResourceFactory(url, paramDefaults, actions) {
441
+ function resourceFactory(url, paramDefaults, actions) {
393
442
  var route = new Route(url);
394
443
 
395
444
  actions = extend({}, DEFAULT_ACTIONS, actions);
@@ -399,25 +448,27 @@ angular.module('ngResource', ['ng']).
399
448
  actionParams = extend({}, paramDefaults, actionParams);
400
449
  forEach(actionParams, function(value, key){
401
450
  if (isFunction(value)) { value = value(); }
402
- ids[key] = value && value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
451
+ ids[key] = value && value.charAt && value.charAt(0) == '@' ?
452
+ lookupDottedPath(data, value.substr(1)) : value;
403
453
  });
404
454
  return ids;
405
455
  }
406
456
 
457
+ function defaultResponseInterceptor(response) {
458
+ return response.resource;
459
+ }
460
+
407
461
  function Resource(value){
408
- copy(value || {}, this);
462
+ shallowClearAndCopy(value || {}, this);
409
463
  }
410
464
 
411
465
  forEach(actions, function(action, name) {
412
- action.method = angular.uppercase(action.method);
413
- var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH';
466
+ var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
467
+
414
468
  Resource[name] = function(a1, a2, a3, a4) {
415
- var params = {};
416
- var data;
417
- var success = noop;
418
- var error = null;
419
- var promise;
469
+ var params = {}, data, success, error;
420
470
 
471
+ /* jshint -W086 */ /* (purposefully fall through case statements) */
421
472
  switch(arguments.length) {
422
473
  case 4:
423
474
  error = a4;
@@ -448,89 +499,108 @@ angular.module('ngResource', ['ng']).
448
499
  break;
449
500
  case 0: break;
450
501
  default:
451
- throw "Expected between 0-4 arguments [params, data, success, error], got " +
452
- arguments.length + " arguments.";
502
+ throw $resourceMinErr('badargs',
503
+ "Expected up to 4 arguments [params, data, success, error], got {0} arguments",
504
+ arguments.length);
453
505
  }
506
+ /* jshint +W086 */ /* (purposefully fall through case statements) */
454
507
 
455
- var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
456
- var httpConfig = {},
457
- promise;
508
+ var isInstanceCall = this instanceof Resource;
509
+ var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
510
+ var httpConfig = {};
511
+ var responseInterceptor = action.interceptor && action.interceptor.response ||
512
+ defaultResponseInterceptor;
513
+ var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
514
+ undefined;
458
515
 
459
516
  forEach(action, function(value, key) {
460
- if (key != 'params' && key != 'isArray' ) {
517
+ if (key != 'params' && key != 'isArray' && key != 'interceptor') {
461
518
  httpConfig[key] = copy(value);
462
519
  }
463
520
  });
464
- httpConfig.data = data;
465
- route.setUrlParams(httpConfig, extend({}, extractParams(data, action.params || {}), params), action.url);
466
521
 
467
- function markResolved() { value.$resolved = true; }
522
+ if (hasBody) httpConfig.data = data;
523
+ route.setUrlParams(httpConfig,
524
+ extend({}, extractParams(data, action.params || {}), params),
525
+ action.url);
468
526
 
469
- promise = $http(httpConfig);
470
- value.$resolved = false;
471
-
472
- promise.then(markResolved, markResolved);
473
- value.$then = promise.then(function(response) {
474
- var data = response.data;
475
- var then = value.$then, resolved = value.$resolved;
527
+ var promise = $http(httpConfig).then(function(response) {
528
+ var data = response.data,
529
+ promise = value.$promise;
476
530
 
477
531
  if (data) {
532
+ // Need to convert action.isArray to boolean in case it is undefined
533
+ // jshint -W018
534
+ if (angular.isArray(data) !== (!!action.isArray)) {
535
+ throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' +
536
+ 'response to contain an {0} but got an {1}',
537
+ action.isArray?'array':'object', angular.isArray(data)?'array':'object');
538
+ }
539
+ // jshint +W018
478
540
  if (action.isArray) {
479
541
  value.length = 0;
480
542
  forEach(data, function(item) {
481
543
  value.push(new Resource(item));
482
544
  });
483
545
  } else {
484
- copy(data, value);
485
- value.$then = then;
486
- value.$resolved = resolved;
546
+ shallowClearAndCopy(data, value);
547
+ value.$promise = promise;
487
548
  }
488
549
  }
489
550
 
490
- (success||noop)(value, response.headers);
551
+ value.$resolved = true;
491
552
 
492
553
  response.resource = value;
554
+
493
555
  return response;
494
- }, error).then;
556
+ }, function(response) {
557
+ value.$resolved = true;
495
558
 
496
- return value;
497
- };
559
+ (error||noop)(response);
498
560
 
561
+ return $q.reject(response);
562
+ });
499
563
 
500
- Resource.prototype['$' + name] = function(a1, a2, a3) {
501
- var params = extractParams(this),
502
- success = noop,
503
- error;
564
+ promise = promise.then(
565
+ function(response) {
566
+ var value = responseInterceptor(response);
567
+ (success||noop)(value, response.headers);
568
+ return value;
569
+ },
570
+ responseErrorInterceptor);
571
+
572
+ if (!isInstanceCall) {
573
+ // we are creating instance / collection
574
+ // - set the initial promise
575
+ // - return the instance / collection
576
+ value.$promise = promise;
577
+ value.$resolved = false;
578
+
579
+ return value;
580
+ }
504
581
 
505
- switch(arguments.length) {
506
- case 3: params = a1; success = a2; error = a3; break;
507
- case 2:
508
- case 1:
509
- if (isFunction(a1)) {
510
- success = a1;
511
- error = a2;
512
- } else {
513
- params = a1;
514
- success = a2 || noop;
515
- }
516
- case 0: break;
517
- default:
518
- throw "Expected between 1-3 arguments [params, success, error], got " +
519
- arguments.length + " arguments.";
582
+ // instance call
583
+ return promise;
584
+ };
585
+
586
+
587
+ Resource.prototype['$' + name] = function(params, success, error) {
588
+ if (isFunction(params)) {
589
+ error = success; success = params; params = {};
520
590
  }
521
- var data = hasBody ? this : undefined;
522
- Resource[name].call(this, params, data, success, error);
591
+ var result = Resource[name].call(this, params, this, success, error);
592
+ return result.$promise || result;
523
593
  };
524
594
  });
525
595
 
526
596
  Resource.bind = function(additionalParamDefaults){
527
- return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
597
+ return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
528
598
  };
529
599
 
530
600
  return Resource;
531
601
  }
532
602
 
533
- return ResourceFactory;
603
+ return resourceFactory;
534
604
  }]);
535
605
 
536
606