Skip to content

Commit

Permalink
Make stamp factory to accept refs not props. Make the props safely me…
Browse files Browse the repository at this point in the history
…rged into the refs when possible.
  • Loading branch information
koresar committed Jun 9, 2015
1 parent 0bfa98e commit 30acfa3
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 26 deletions.
45 changes: 42 additions & 3 deletions mixer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ var forOwn = require('lodash/object/forOwn');
var forIn = require('lodash/object/forIn');
var deepClone = require('lodash/lang/cloneDeep');
var isObject = require('lodash/lang/isObject');
var isUndefined = require('lodash/lang/isUndefined');
var isFunction = require('lodash/lang/isFunction');

/**
* Creates mixin functions of all kinds.
* @param {object} opts Options.
* @param {function(object, string)} opts.filter Function which filters value and key.
* @param {function(object, object)} opts.filter Function which filters value and key.
* @param {boolean} opts.chain Loop through prototype properties too.
* @param {function(object)} opts.getTarget Converts an object object to a target.
* @param {function(object, object)} opts.getValue Converts src and dst values to a new value.
Expand All @@ -30,11 +31,14 @@ var mixer = function (opts) {
target = opts.getTarget ? opts.getTarget(target) : target;

This comment has been minimized.

Copy link
@unstoppablecarl

unstoppablecarl Jun 9, 2015

Contributor

use vars for opts to decrease cognitive load

    var filter = opts.filter,
    getValue = opts.getValue;

use target instead of this


var mergeValue = function mergeValue(val, key) {
if (opts.filter && !opts.filter(val, key)) {
if (opts.filter && !opts.filter(val, this[key])) {

This comment has been minimized.

Copy link
@unstoppablecarl

unstoppablecarl Jun 9, 2015

Contributor
  var targetVal = target[key];
  if (filter && !filter(val, targetVal)) {
    return;
  }

  if(getValue){
    targetVal = getValue(val, targetVal);
  }
  target[key] = targetVal;
return;
}

this[key] = opts.getValue ? opts.getValue(val, this[key]) : val;
if (this[key] === 'should not be merged') {
console.log('got ya');
}
};

while (++i < n) {
Expand All @@ -51,7 +55,6 @@ var mixer = function (opts) {
};

var merge = mixer({
getTarget: deepClone,
/* jshint ignore:start */
getValue: mergeSourceToTarget
/* jshint ignore:end */
Expand Down Expand Up @@ -94,6 +97,32 @@ module.exports.mixinChainFunctions = mixer({
*/
module.exports.merge = merge;


var mergeUnique = mixer({
filter: function (srcVal, targetVal) {
return isUndefined(targetVal) || (isObject(srcVal) && isObject(targetVal));
},
/* jshint ignore:start */
getValue: mergeUniqueSourceToTarget
/* jshint ignore:end */
});

function mergeUniqueSourceToTarget(srcVal, targetVal) {
if ((isObject(srcVal) && isObject(targetVal))) {
// inception, deep merge objects
return mergeUnique(targetVal, srcVal);
} else {
// make sure arrays, regexp, date, objects are cloned
return deepClone(srcVal);
}
}

/**
* Just like regular object merge, but do not override destination object data.
*/
module.exports.mergeUnique = mergeUnique;


/**
* merge objects including prototype chain properties.
*/
Expand All @@ -103,3 +132,13 @@ module.exports.mergeChainNonFunctions = mixer({
getValue: mergeSourceToTarget,
chain: true
});

/**
* merge unique properties of objects including prototype chain properties.
*/
module.exports.mergeChainNonFunctions = mixer({
filter: function (val) { return !isFunction(val); },
getTarget: deepClone,
getValue: mergeUniqueSourceToTarget,
chain: true
});
10 changes: 6 additions & 4 deletions stampit.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
var forEach = require('lodash/collection/forEach');
var map = require('lodash/collection/map');
var forOwn = require('lodash/object/forOwn');
var deepClone = require('lodash/lang/cloneDeep');
var isFunction = require('lodash/lang/isFunction');
var isArray = Array.isArray;
var isObject = require('lodash/lang/isObject');
Expand Down Expand Up @@ -126,9 +125,12 @@ stampit = function stampit(options) {
addStatic(fixed, options.static);
}

var factory = function Factory(properties, args) {
properties = properties ? mixer.merge({}, fixed.props, properties) : deepClone(fixed.props);
var instance = mixer.mixin(create(fixed.methods), fixed.refs, properties); // props are taking over refs
var factory = function Factory(refs, args) {
//refs = refs ? mixer.merge({}, fixed.props, refs) : deepClone(fixed.props);
//var instance = mixer.mixin(create(fixed.methods), fixed.refs, refs);

var instance = mixer.mixin(create(fixed.methods), fixed.refs, refs); // props are merged into refs
mixer.mergeUnique(instance, fixed.props);

if (fixed.init.length > 0) {
args = slice(arguments, 1, arguments.length);
Expand Down
33 changes: 14 additions & 19 deletions test/props-safety.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,22 @@ test('Stamp props deep cloned for object created', function (t) {
t.end();
});

test('stamp(props) deep merge into object created', function (t) {
var deep = { foo: 'foo', bar: 'bar' };
var stamp1 = stampit().props({ deep: deep, foo: 'foo', bar: 'bar' });
var stamp2 = stampit({ props: { deep: deep, foo: 'foo', bar: 'bar' } });
test('stamp(refs) deep merges props into refs', function (t) {
var deepInProps = { deepProp1: 'should not be merged', deepProp2: 'merge me!' };
var stamp1 = stampit().props({ deep: deepInProps, shallow1: 'should not be merged', shallow2: 'merge me!' });
var stamp2 = stampit({ props: { deep: deepInProps, shallow1: 'should not be merged', shallow2: 'merge me!' } });

var deep2 = { foo: 'override', baz: 'baz' };
var o1 = stamp1({ deep: deep2, foo: 'override', baz: 'baz' });
var o2 = stamp2({ deep: deep2, foo: 'override', baz: 'baz' });
var o1 = stamp1({ deep: { deepProp1: 'leave me as is' }, shallow1: 'leave me as is' });
var o2 = stamp2({ deep: { deepProp1: 'leave me as is' }, shallow1: 'leave me as is' });

t.equal(o1.foo, 'override');
t.equal(o1.bar, 'bar');
t.equal(o1.baz, 'baz');
t.equal(o2.foo, 'override');
t.equal(o2.bar, 'bar');
t.equal(o2.baz, 'baz');
t.equal(o1.deep.foo, 'override');
t.equal(o1.deep.bar, 'bar');
t.equal(o1.deep.baz, 'baz');
t.equal(o2.deep.foo, 'override');
t.equal(o2.deep.bar, 'bar');
t.equal(o2.deep.baz, 'baz');
t.equal(o1.shallow1, 'leave me as is');

This comment has been minimized.

Copy link
@ericelliott

ericelliott Jun 10, 2015

Contributor

Please supply english descriptions explaining what each comparison is testing for.

There are some good examples here, such as 'Deep property in refs should not be touched by props'.

This comment has been minimized.

Copy link
@koresar

koresar Jun 10, 2015

Author Member

👍

t.equal(o1.shallow2, 'merge me!');
t.equal(o2.shallow1, 'leave me as is');
t.equal(o2.shallow2, 'merge me!');
t.equal(o1.deep.deepProp1, 'leave me as is', 'Deep property in refs should not be touched by props');
t.equal(o1.deep.deepProp2, 'merge me!');
t.equal(o2.deep.deepProp1, 'leave me as is', 'Deep property in refs should not be touched by props');
t.equal(o2.deep.deepProp2, 'merge me!');

t.end();
});
Expand Down

0 comments on commit 30acfa3

Please sign in to comment.