Skip to content

Commit

Permalink
feat(default_packer): adds support for explicitly setting links and m…
Browse files Browse the repository at this point in the history
…etadata properties to extract.

Also improves property masking so links dont get feed with metadata in certain situations.

Closes #153
  • Loading branch information
iobaixas committed Sep 24, 2014
1 parent e2ea44c commit dcfd37b
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 35 deletions.
71 changes: 38 additions & 33 deletions src/module/support/default-packer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,30 @@

RMModule.factory('DefaultPacker', ['restmod', 'inflector', 'RMPackerCache', function(restmod, inflector, packerCache) {

// process metadata
function processMeta(_meta, _raw, _skip) {
var metaDef = _meta;
if(typeof metaDef === 'string') {
if(metaDef === '.') {
var meta = {};
for(var key in _raw) {
if(_raw.hasOwnProperty(key) && _skip.indexOf(key) === -1) { // skip links and object root if extracting from root.
meta[key] = _raw[key];
}
}
return meta;
} else {
return _raw[metaDef];
function include(_source, _list, _do) {
for(var i = 0, l = _list.length; i < l; i++) {
_do(_list[i], _source[_list[i]]);
}
}

function exclude(_source, _skip, _do) {
for(var key in _source) {
if(_source.hasOwnProperty(key) && _skip.indexOf(key) === -1) {
_do(key, _source[key]);
}
} else if(typeof metaDef === 'function') {
return metaDef(_raw);
}
}

// process links and stores them in the packer cache
function processLinks(_links, _raw, _skip) {
var source = _links === '.' ? _raw : _raw[_links];
if(!source) return;

// feed packer cache
for(var key in source) {
if(source.hasOwnProperty(key) && _skip.indexOf(key) === -1) {
var cache = source[key];
// TODO: check that cache is an array.
packerCache.feed(key, cache);
}
function processFeature(_raw, _name, _feature, _other, _do) {
if(_feature === '.' || _feature === true) {
var skip = [_name];
if(_other) skip.push.apply(skip, angular.isArray(_other) ? _other : [_other]);
exclude(_raw, skip, _do);
} else if(typeof _feature === 'string') {
exclude(_raw[_feature], [], _do);
} else { // links is an array
include(_raw, _feature, _do);
}
}

Expand Down Expand Up @@ -66,7 +58,8 @@ RMModule.factory('DefaultPacker', ['restmod', 'inflector', 'RMPackerCache', func
*
* By default the mixin will look for links to other resources in the 'linked' root property, you
* can change this by setting the jsonLinks variable. To use the root element as link source
* use `jsonLinks: true`. To skip links processing, set it to false.
* use `jsonLinks: '.'`. You can also explicitly select which properties to consider links using an
* array of property names. To skip links processing altogether, set it to false.
*
* Links are expected to use the pluralized version of the name for the referenced model. For example,
* given the following response:
Expand All @@ -88,9 +81,9 @@ RMModule.factory('DefaultPacker', ['restmod', 'inflector', 'RMPackerCache', func
* By default metadata is only captured if it comes in the 'meta' root property. Metadata is then
* stored in the $meta property of the resource being unwrapped.
*
* To change the metadata source property set the jsonMeta property to the desired name, set
* it to '.' to capture the entire raw response or set it to false to skip metadata. It can also be set
* to a function, for custom processsing.
* Just like links, to change the metadata source property set the jsonMeta property to the desired name, set
* it to '.' to capture the entire raw response or set it to false to skip metadata and set it to an array of properties
* to be extract selected properties.
*
* @property {mixed} single The expected single resource wrapper property name
* @property {object} plural The expected collection wrapper property name
Expand All @@ -111,8 +104,20 @@ RMModule.factory('DefaultPacker', ['restmod', 'inflector', 'RMPackerCache', func
name = this.getProperty('jsonRootSingle') || this.getProperty('jsonRoot') || this.getProperty('name');
}

if(meta) _resource.$metadata = processMeta(meta, _raw, [name, links]);
if(links) processLinks(links, _raw, [name]);
if(meta) {
_resource.$metadata = {};
processFeature(_raw, name, meta, links, function(_key, _value) {
_resource.$metadata[_key] = _value;
});
}

if(links) {
processFeature(_raw, name, links, meta, function(_key, _value) {
// TODO: check that cache is an array.
packerCache.feed(_key, _value);
});
}

return _raw[name];
});
});
Expand Down
66 changes: 64 additions & 2 deletions test/default-packer-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ describe('DefaultPacker', function() {

var record = model.$new(1);
record.$unwrap({ bike: { model: 'Slash' } });
expect(record.$metadata).toBeUndefined();
expect(record.$metadata).toEqual({});
});

it('should extract metadata from root if set to dot', function() {
Expand All @@ -101,10 +101,30 @@ describe('DefaultPacker', function() {

var record = model.$new(1);
record.$unwrap({ bike: { model: 'Slash' }, pages: 20 });
expect(record.$metadata.bike).toBeUndefined();
expect(record.$metadata.pages).toBeDefined();
});

it('should skip links and name if set to dot', function() {
var model = restmod.model('/api/bikes').mix('DefaultPacker', {
$config: { jsonMeta: '.' }
});

var record = model.$new(1);
record.$unwrap({ bike: { model: 'Slash' }, linked: { users: [] }, pages: 20 });
expect(record.$metadata.bike).toBeUndefined();
expect(record.$metadata.linked).toBeUndefined();

var model2 = restmod.model('/api/bikes').mix('DefaultPacker', {
$config: { jsonMeta: '.', jsonLinks: ['users', 'parts'] }
});

record = model2.$new(1);
record.$unwrap({ bike: { model: 'Slash' }, users: [], parts: [], pages: 20 });
expect(record.$metadata.bike).toBeUndefined();
expect(record.$metadata.users).toBeUndefined();
expect(record.$metadata.parts).toBeUndefined();
});

it('should skip metadata extraction if set to false', function() {
var model = restmod.model('/api/bikes').mix('DefaultPacker', {
$config: { jsonMeta: false }
Expand All @@ -115,6 +135,18 @@ describe('DefaultPacker', function() {
expect(record.$metadata).toBeUndefined();
});

it('should extract only properties specified in array if array is given', function() {
var model = restmod.model('/api/bikes').mix('DefaultPacker', {
$config: { jsonMeta: ['pages', 'status'] }
});

var record = model.$new(1);
record.$unwrap({ bike: { model: 'Slash' }, pages: 20, status: 'success', pageSz: 20 });
expect(record.$metadata.pages).toBeDefined();
expect(record.$metadata.status).toBeDefined();
expect(record.$metadata.pageSz).toBeUndefined();
});

});

describe('processLinks', function() {
Expand Down Expand Up @@ -162,6 +194,36 @@ describe('DefaultPacker', function() {
expect(packerCache.feed).not.toHaveBeenCalledWith('users', []);
});

it('should skip metadata and name if set to dot', function() {
var model = restmod.model('/api/bikes').mix('DefaultPacker', {
$config: { jsonLinks: '.' }
});

var record = model.$new(1);
record.$unwrap({ bike: { model: 'Slash' }, users: [], meta: {} });
expect(packerCache.feed).not.toHaveBeenCalledWith('meta', {});

var model2 = restmod.model('/api/bikes').mix('DefaultPacker', {
$config: { jsonLinks: '.', jsonMeta: ['pages'] }
});

record = model2.$new(1);
record.$unwrap({ bike: { model: 'Slash' }, users: [], parts: [], pages: 20 });
expect(packerCache.feed).not.toHaveBeenCalledWith('pages', 20);
});

it('should extract only links specified in array if array is given', function() {
var model = restmod.model('/api/bikes').mix('DefaultPacker', {
$config: { jsonLinks: ['users', 'parts'] }
});

var record = model.$new(1);
record.$unwrap({ bike: {}, users: [], parts: [], rides: [] });
expect(packerCache.feed).toHaveBeenCalledWith('users', []);
expect(packerCache.feed).toHaveBeenCalledWith('parts', []);
expect(packerCache.feed).not.toHaveBeenCalledWith('rides', []);
});

});

});

0 comments on commit dcfd37b

Please sign in to comment.