Skip to content

Commit fe0d9c7

Browse files
committed
feat: adds the $single method to Model and singleton method to $restmod
* The singleton service is a shorcut to create a single resource with a given object definition. * The $single method allows to create Model instances bound to a given url. Closes platanus#29
1 parent bf5339f commit fe0d9c7

3 files changed

Lines changed: 89 additions & 4 deletions

File tree

src/module/restmod.js

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ angular.module('plRestmod').provider('$restmod', function() {
8484
*/
8585
$get: ['$http', '$q', '$injector', '$parse', '$filter', '$inflector', function($http, $q, $injector, $parse, $filter, $inflector) {
8686

87-
return {
87+
var restmod = {
8888
/**
8989
* @function model
9090
* @memberOf services.$restmod#
@@ -99,6 +99,7 @@ angular.module('plRestmod').provider('$restmod', function() {
9999
model: function(_baseUrl/* , _mix */) {
100100

101101
var masks = {
102+
$type: SyncMask.SYSTEM_ALL,
102103
$scope: SyncMask.SYSTEM_ALL,
103104
$promise: SyncMask.SYSTEM_ALL,
104105
$pending: SyncMask.SYSTEM_ALL,
@@ -260,22 +261,47 @@ angular.module('plRestmod').provider('$restmod', function() {
260261
* static methods are available to generate new instances of a model, for more information
261262
* read the {@link ModelCollection} documentation.
262263
*/
263-
var Model = function(_scope, _pk) {
264+
function Model(_scope, _pk) {
264265

265266
this.$scope = _scope;
266267
this.$pk = _pk;
267268
this.$pending = false;
269+
this.$type = Model;
268270

269271
var tmp;
270272

271273
// apply defaults
272274
for(var i = 0; (tmp = defaults[i]); i++) {
273275
this[tmp[0]] = (typeof tmp[1] === 'function') ? tmp[1].apply(this) : tmp[1];
274276
}
275-
};
277+
}
276278

277279
// Model default behavior:
278280

281+
/**
282+
* @memberof Model
283+
*
284+
* @description Returns a resource bound to a given url, with no parent scope.
285+
*
286+
* This can be used to create singleton resources:
287+
*
288+
* ```javascript
289+
* module('BikeShop', []).factory('Status', function($restmod) {
290+
* return $restmod.model(null).$single('/api/status');
291+
* };)
292+
* ```
293+
*
294+
* @param {string} _url Url to bound resource to.
295+
* @return {Model} new resource instance.
296+
*/
297+
Model.$single = function(_url) {
298+
return new Model({
299+
$urlFor: function() {
300+
return _url;
301+
}
302+
}, '');
303+
};
304+
279305
Model.inferKey = function(_data) {
280306
if(typeof _data === 'object') {
281307
if(typeof _data.id === 'undefined') return null;
@@ -567,7 +593,7 @@ angular.module('plRestmod').provider('$restmod', function() {
567593
});
568594
} else {
569595
// If not bound create.
570-
url = this.$scope.$createUrlFor ? this.$scope.$createUrlFor(this) : this.$scope.$url();
596+
url = (this.$scope.$createUrlFor && this.$scope.$createUrlFor(this)) || (this.$scope.$url && this.$scope.$url());
571597
if(!url) throw new Error('Create is not supported by this resource');
572598
request = { method: 'POST', url: url, data: this.$encode(SyncMask.ENCODE_CREATE) };
573599
callback('before-save', this, request);
@@ -1611,8 +1637,24 @@ angular.module('plRestmod').provider('$restmod', function() {
16111637
*/
16121638
mixin: function(/* mixins */) {
16131639
return { $isAbstract: true, $chain: arraySlice.call(arguments, 0) };
1640+
},
1641+
1642+
/**
1643+
* @method singleton
1644+
* @memberOf services.$restmod#
1645+
*
1646+
* Shorcut method used to create singleton resources. see {@link Model@$single}.
1647+
*
1648+
* @param {string} _url Resource url,
1649+
* @param {mixed} _mixins Mixin chain.
1650+
* @return {object} New resource instance.
1651+
*/
1652+
singleton: function(_url/*, _mixins*/) {
1653+
return restmod.model.apply(this, arguments).$single(_url);
16141654
}
16151655
};
1656+
1657+
return restmod;
16161658
}]
16171659
};
16181660
})

test/model_spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ describe('Restmod model class:', function() {
1212
Bike = $restmod.model('/api/bikes');
1313
}));
1414

15+
describe('$single', function() {
16+
17+
it('should create a resource bound to a given url', function() {
18+
var bike = Bike.$single('/user/bike');
19+
expect(bike.$url()).toEqual('/user/bike');
20+
});
21+
});
22+
1523
describe('$fetch', function() {
1624

1725
it('should call callbacks in proper order', function() {

test/service_spec.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
3+
describe('Restmod model class:', function() {
4+
5+
var $restmod;
6+
7+
beforeEach(module('plRestmod'));
8+
beforeEach(inject(function($injector) {
9+
$restmod = $injector.get('$restmod');
10+
}));
11+
12+
describe('model', function() {
13+
it('should create a resource', function() {
14+
var Bike = $restmod.model('/user/bike', {}, {});
15+
expect(Bike.$baseUrl).toEqual('/user/bike');
16+
expect(Bike.$chain.length).toEqual(2);
17+
});
18+
});
19+
20+
describe('mixin', function() {
21+
it('should create a mixin module', function() {
22+
var Bike = $restmod.mixin({}, {});
23+
expect(Bike.$chain.length).toEqual(2);
24+
});
25+
});
26+
27+
describe('singleton', function() {
28+
it('should create a singleton resource', function() {
29+
var bike = $restmod.singleton('/user/bike', {}, {});
30+
expect(bike.$url()).toEqual('/user/bike');
31+
expect(bike.$type.$chain.length).toEqual(2);
32+
});
33+
});
34+
});
35+

0 commit comments

Comments
 (0)