Last active
August 29, 2015 14:17
-
-
Save TGOlson/8d0bb174c7f5862cd83f to your computer and use it in GitHub Desktop.
Graph utility with property getter monad
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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