Skip to content

Instantly share code, notes, and snippets.

@TGOlson
Last active August 29, 2015 14:17
Show Gist options
  • Select an option

  • Save TGOlson/8d0bb174c7f5862cd83f to your computer and use it in GitHub Desktop.

Select an option

Save TGOlson/8d0bb174c7f5862cd83f to your computer and use it in GitHub Desktop.
Graph utility with property getter monad
var R = require('ramda');
// [k] -> {k: a, load: fn} -> a
var getPath = R.curry(function(propList, entity) {
return getPathFromPropList(propList, entity);
});
var getPathFromPropList = R.curry(function(propList, entity) {
var loadPattern = convertPropListToLoadPattern(propList);
return entity.load(loadPattern)
.then(reduceFromPropListAndReturnValue(propList));
});
// String -> {k: a, load: fn} -> a
var getPathFromPropString = R.curry(function(propList, entity) {
return getPathFromPropList(propList.split('/'), entity);
});
// [k] -> {k: v}
function convertPropListToLoadPattern(propList) {
return R.reduceRight(R.flip(makeObject), true, propList);
}
// k -> a -> {k: a}
function makeObject(property, value) {
return R.assoc(property, value, {});
}
function reduceFromPropListAndReturnValue(propList) {
return R.compose(R.func('getValue'), reduceFromPropList(propList));
}
// [k] -> {k: a} -> a
var reduceFromPropList = R.curry(function(propList, obj) {
return R.reduce(R.func('get'), PropertyGetter(obj), propList);
});
function PropertyGetter(value) {
return {
get : R.partialRight(get, value),
map : R.partialRight(map, value),
getValue : R.always(value)
};
}
// k -> {k: a} -> PropertyGetter(a)
function get(property, obj) {
return map(R.prop(property), obj);
}
// (k -> a) -> {k: a} -> PropertyGetter(a)
function map(callback, obj) {
return mapToPropertyGetter(callback)(obj);
}
var mapToPropertyGetter = function(callback) {
return R.cond(
[R.isNil, composeToPropertyGetter(R.always(undefined))],
[R.is(Array), composeToPropertyGetter(R.chain(callback))],
[R.T, composeToPropertyGetter(callback)]
);
};
function composeToPropertyGetter(fn) {
return R.compose(PropertyGetter, fn);
}
module.exports = {
getPath : getPath,
getPathFromPropList : getPathFromPropList
};
var Q = require('q'),
R = require('ramda');
var Graph = require('./junk');
describe('Graph', function() {
function load(loadPattern) {
return Q.when(this);
}
function entityArray(array) {
array.load = load;
return array;
}
var entity = {
title: 'Something',
load: load,
isActive: true,
maybeData: null,
totalCount: 5,
creator: {
name: 'Dude',
load: load
},
scores: entityArray([
{
points: 1,
votes: ['a', 'b']
},
{
points: 2,
votes: ['c']
},
{
points: 3,
votes: ['d']
}
])
};
function getPath(path, entity) {
return Graph.getPath(path, entity)
.catch(console.log.bind(console));
}
var expectResultWithMatcher = R.curry(function(matcher, expected, actual) {
R.func(matcher, expect(actual), expected);
});
var expectResultToBe = expectResultWithMatcher('toBe');
var expectResultToEqual = expectResultWithMatcher('toEqual');
var expectResultToBeUndefined = expectResultWithMatcher('toBe', undefined);
describe('getPath', function() {
it('should be curried', function(done) {
var getTitle = Graph.getPath(['title']);
getTitle(entity)
.then(expectResultToBe('Something'))
.finally(done);
});
it('should return a single property', function(done) {
getPath(['title'], entity)
.then(expectResultToBe('Something'))
.finally(done);
});
it('should return a undefined if a property does not exist', function(done) {
getPath(['thisIsNotARealProperty'], entity)
.then(expectResultToBeUndefined)
.finally(done);
});
it('should return an entity property', function(done) {
getPath(['creator'], entity)
.then(expectResultToBe(entity.creator))
.finally(done);
});
it('should return a nested entity property', function(done) {
getPath(['creator', 'name'], entity)
.then(expectResultToBe('Dude'))
.finally(done);
});
it('should return a undefined if a path builds off a simple data type', function(done) {
getPath(['title', 'notARealProperty'], entity)
.then(expectResultToBeUndefined)
.finally(done);
});
it('should return a undefined if a path builds off a boolean data type', function(done) {
getPath(['isActive', 'notARealProperty'], entity)
.then(expectResultToBeUndefined)
.finally(done);
});
it('should return a undefined if a path builds off a falsey data type', function(done) {
getPath(['maybeData', 'notARealProperty'], entity)
.then(expectResultToBeUndefined)
.finally(done);
});
it('should return a undefined if a path builds off a number data type', function(done) {
getPath(['totalCount', 'notARealProperty'], entity)
.then(expectResultToBeUndefined)
.finally(done);
});
it('should return a undefined if a path does not exist', function(done) {
getPath(['thisIsNot', 'aRealProperty'], entity)
.then(expectResultToBeUndefined)
.finally(done);
});
it('should return an array entity', function(done) {
getPath(['scores'], entity)
.then(expectResultToBe(entity.scores))
.finally(done);
});
it('should return mapped array properties', function(done) {
getPath(['scores', 'points'], entity)
.then(expectResultToEqual([1, 2, 3]))
.finally(done);
});
it('should return nested array properties', function(done) {
getPath(['scores', 'votes'], entity)
.then(expectResultToEqual(['a', 'b', 'c', 'd']))
.finally(done);
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment