Skip to content

Instantly share code, notes, and snippets.

@furf
Created July 30, 2009 14:42
Show Gist options
  • Save furf/158722 to your computer and use it in GitHub Desktop.
Save furf/158722 to your computer and use it in GitHub Desktop.
function Transmogrifier (map) {
var EMPTY = '',
SOURCE = 's',
TARGET = 't',
DOT = '.',
EQUALS = '=',
OBJECT = '{}',
BREAK = ';\n',
NUMERIC_INDEX_REGEXP = /\.(\d+)/g,
modifiers = [];
function makeSource (map /*, root */) {
var root = arguments[1] || EMPTY,
rootDot = (root !== EMPTY) ? root + DOT : EMPTY,
src = [],
prop,
val,
i;
// Open function
if (root === EMPTY) {
src.push('var ', TARGET, EQUALS, OBJECT, BREAK);
// Render nested object
} else {
src.push(TARGET, DOT, root, EQUALS, OBJECT, BREAK);
}
// Iterate map properties
for (prop in map) {
if (map.hasOwnProperty(prop)) {
val = map[prop];
// Traverse nested objects
if (typeof val === 'object') {
src.push(makeSource(val, prop));
// Render dot-delimited properties and modifiers
} else {
// Render left-hand assignment
src.push(TARGET, DOT, rootDot, prop, EQUALS);
// Render modifier
if (typeof val === 'function') {
modifiers[prop] = val;
src.push('this.modifiers.', prop, '(', SOURCE, ')', BREAK);
// Render dot-delimited property as gated assignment
} else {
val = val.split(DOT);
for (i = val.length - 1; i >= 0; --i) {
val[i] = SOURCE + DOT + val.slice(0, i + 1).join(DOT);
}
src.push(val.join('&&').replace(NUMERIC_INDEX_REGEXP, '[$1]'), BREAK);
}
}
}
}
// Close function
if (root === EMPTY) {
src.push('return ', TARGET, BREAK);
}
return src.join(EMPTY);
}
this.transmogrify = new Function(SOURCE, makeSource(map));
this.modifiers = modifiers;
}
function Transmogrifier(a){var b="",j="s",e="t",g=".",i="=",c="{}",k=";\n",d=/\.(\d+)/g,h=[];function f(o){var m=arguments[1]||b,l=(m!==b)?m+g:b,q=[],r,p,n;if(m===b){q.push("var ",e,i,c,k);}else{q.push(e,g,m,i,c,k);}for(r in o){if(o.hasOwnProperty(r)){p=o[r];if(typeof p==="object"){q.push(f(p,r));}else{q.push(e,g,l,r,i);if(typeof p==="function"){h[r]=p;q.push("this.modifiers.",r,"(",j,")",k);}else{p=p.split(g);for(n=p.length-1;n>=0;--n){p[n]=j+g+p.slice(0,n+1).join(g);}q.push(p.join("&&").replace(d,"[$1]"),k);}}}}if(m===b){q.push("return ",e,k);}return q.join(b);}this.transmogrify=new Function(j,f(a));this.modifiers=h;}
var contactMap = {
firstName: 'invoice.billing.fname',
lastName: 'invoice.billing.lname',
billingAddress: {
undefined: 'foo.bar',
job: 'invoice.billing.jobs.2.name',
address: function (obj) {
var b = obj.invoice.billing;
return b.city + ', ' +
b.state + ' ' +
b.country;
}
},
getFullName: function (obj) {
return function getFullName () {
return this.firstName + ' ' + this.lastName;
};
}
},
contactTransmogrifier = new Transmogrifier(contactMap),
contact = {
invoice: {
billing: {
fname: 'Dave',
lname: 'Furfero',
city: 'Harlem',
state: 'NY',
country: 'USA',
jobs: [
{name:'Live Nation'},
{name:'Time, Inc.'},
{name:'MLB'}
]
}
}
};
console.group('source');
console.log(contactTransmogrifier.transmogrify.toString());
console.groupEnd('source');
console.group('input');
console.dir(contact);
console.groupEnd('input');
console.group('output');
console.dir(contactTransmogrifier.transmogrify(contact));
console.groupEnd('output');
console.group('time');
var r = stress(function contactTransmogrifierTest() {
contactTransmogrifier.transmogrify(contact);
}); // stress
console.log(r.each, 'ms');
console.groupEnd('time');
var p = contactTransmogrifier.transmogrify(contact);
console.log(p.getFullName());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment