Skip to content

Commit

Permalink
Merge pull request #1 from integreat-io/feature/pipes
Browse files Browse the repository at this point in the history
Feature/pipes
  • Loading branch information
kjellmorten authored Sep 17, 2018
2 parents ee3d74a + 6245b22 commit 62a70bc
Show file tree
Hide file tree
Showing 50 changed files with 3,515 additions and 4,514 deletions.
815 changes: 583 additions & 232 deletions README.md

Large diffs are not rendered by default.

3,337 changes: 827 additions & 2,510 deletions package-lock.json

Large diffs are not rendered by default.

17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "map-transform",
"version": "0.1.6",
"version": "0.2.0",
"description": "Map and transform objects with mapping definitions",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -58,20 +58,21 @@
},
"homepage": "https://github.com/integreat-io/map-transform#readme",
"dependencies": {
"map-any": "^0.1.1",
"crocks": "^0.10.1",
"map-any": "^0.1.2",
"ramda": "^0.25.0"
},
"devDependencies": {
"@types/deep-freeze": "^0.1.1",
"@types/ramda": "^0.25.36",
"ava": "1.0.0-beta.6",
"@types/ramda": "^0.25.38",
"ava": "1.0.0-beta.8",
"babel-preset-env": "^1.7.0",
"coveralls": "^3.0.2",
"deep-freeze": "0.0.1",
"nyc": "^12.0.2",
"ts-node": "^7.0.0",
"nyc": "^13.0.1",
"ts-node": "^7.0.1",
"tslint": "^5.11.0",
"tslint-config-standard": "^7.1.0",
"typescript": "^3.0.1"
"tslint-config-standard": "^8.0.1",
"typescript": "^3.0.3"
}
}
97 changes: 97 additions & 0 deletions src/funcs/alt-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import test from 'ava'
import { State } from '../types'

import alt from './alt'

// Helpers

const getUser = (state: State) => ({ ...state, value: (state.value as any).user })

// Tests

test('should set alt value when value is undefined', (t) => {
const state = {
root: { user: 'johnf' },
context: { user: 'johnf' },
value: undefined
}
const expected = {
root: { user: 'johnf' },
context: { user: 'johnf' },
value: 'johnf'
}

const ret = alt(getUser)(state)

t.deepEqual(ret, expected)
})

test('should do nothing when value is set', (t) => {
const state = {
root: { user: 'johnf' },
context: { user: 'johnf' },
value: 'maryk'
}
const expected = {
root: { user: 'johnf' },
context: { user: 'johnf' },
value: 'maryk'
}

const ret = alt(getUser)(state)

t.deepEqual(ret, expected)
})

test('should do nothing when onlyMapped is true', (t) => {
const state = {
root: { user: 'johnf' },
context: { user: 'johnf' },
value: undefined,
onlyMapped: true
}
const expected = {
root: { user: 'johnf' },
context: { user: 'johnf' },
value: undefined,
onlyMapped: true
}

const ret = alt(getUser)(state)

t.deepEqual(ret, expected)
})

test('should treat string as path', (t) => {
const state = {
root: { user: 'johnf' },
context: { user: 'johnf' },
value: undefined
}
const expected = {
root: { user: 'johnf' },
context: { user: 'johnf' },
value: 'johnf'
}

const ret = alt('user')(state)

t.deepEqual(ret, expected)
})

test('should treat array as map pipe', (t) => {
const state = {
root: { user: 'johnf' },
context: { user: 'johnf' },
value: undefined
}
const expected = {
root: { user: 'johnf' },
context: { user: 'johnf' },
value: 'johnf'
}

const ret = alt(['user'])(state)

t.deepEqual(ret, expected)
})
23 changes: 23 additions & 0 deletions src/funcs/alt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as mapAny from 'map-any'
import { State, Data, MapFunction, MapDefinition } from '../types'
import { setStateValue, getStateValue } from '../utils/stateHelpers'
import { mapFunctionFromDef } from '../utils/definitionHelpers'

const getOne = (context: Data | Data[], index?: number) =>
(typeof index === 'undefined' || !Array.isArray(context)) ? context : context[index]

const getValueOrDefault = (state: State, runAlt: MapFunction) => (value: Data, index?: number) =>
(typeof value === 'undefined')
? getStateValue(runAlt({ ...state, value: getOne(state.context, index) }))
: value

export default function alt (fn: MapDefinition): MapFunction {
const runAlt = mapFunctionFromDef(fn)

return (state: State) => (state.onlyMapped)
? state
: setStateValue(
state,
mapAny(getValueOrDefault(state, runAlt), state.value)
)
}
117 changes: 117 additions & 0 deletions src/funcs/directionals-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import test from 'ava'
import { MapFunction } from '../types'

import { fwd, rev } from './directionals'

// Helpers

const upperCase = (str: string | any) => (typeof str === 'string') ? str.toUpperCase() : str
const upper: MapFunction = (state) => ({ ...state, value: upperCase(state.value) })

// Tests -- forward

test('should apply function when not rev', (t) => {
const state = {
root: { title: 'Entry 1' },
context: { title: 'Entry 1' },
value: 'Entry 1',
rev: false
}
const expected = {
root: { title: 'Entry 1' },
context: { title: 'Entry 1' },
value: 'ENTRY 1',
rev: false
}

const ret = fwd(upper)(state)

t.deepEqual(ret, expected)
})

test('should not apply function when rev', (t) => {
const state = {
root: { title: 'Entry 1' },
context: { title: 'Entry 1' },
value: 'Entry 1',
rev: true
}
const expected = {
root: { title: 'Entry 1' },
context: { title: 'Entry 1' },
value: 'Entry 1',
rev: true
}

const ret = fwd(upper)(state)

t.deepEqual(ret, expected)
})

test('should treat string as get path in fwd', (t) => {
const state = {
root: { title: 'Entry 1' },
context: { title: 'Entry 1' },
value: { title: 'Entry 1' },
rev: false
}
const expectedValue = 'Entry 1'

const ret = fwd('title')(state)

t.deepEqual(ret.value, expectedValue)
})

// Tests -- reverse

test('should apply function when rev', (t) => {
const state = {
root: { title: 'Entry 1' },
context: { title: 'Entry 1' },
value: 'Entry 1',
rev: true
}
const expected = {
root: { title: 'Entry 1' },
context: { title: 'Entry 1' },
value: 'ENTRY 1',
rev: true
}

const ret = rev(upper)(state)

t.deepEqual(ret, expected)
})

test('should not apply function when fwd', (t) => {
const state = {
root: { title: 'Entry 1' },
context: { title: 'Entry 1' },
value: 'Entry 1',
rev: false
}
const expected = {
root: { title: 'Entry 1' },
context: { title: 'Entry 1' },
value: 'Entry 1',
rev: false
}

const ret = rev(upper)(state)

t.deepEqual(ret, expected)
})

test('should treat string as get path in rev', (t) => {
const state = {
root: 'Entry 1',
context: 'Entry 1',
value: 'Entry 1',
rev: true
}
const expectedValue = { title: 'Entry 1' }

const ret = rev('title')(state)

t.deepEqual(ret.value, expectedValue)
})
14 changes: 14 additions & 0 deletions src/funcs/directionals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { MapDefinition, MapFunction } from '../types'
import { mapFunctionFromDef } from '../utils/definitionHelpers'

const applyInDirection = (def: MapDefinition, rev: boolean): MapFunction => {
const fn = mapFunctionFromDef(def)
return (state) => (rev ? state.rev : !state.rev) ? fn(state) : state
}
export function fwd (def: MapDefinition): MapFunction {
return applyInDirection(def, false)
}

export function rev (def: MapDefinition): MapFunction {
return applyInDirection(def, true)
}
67 changes: 67 additions & 0 deletions src/funcs/filter-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import test from 'ava'

import filter, { FilterFunction } from './filter'

// Helpers

const beginsWithA: FilterFunction = (str) => (typeof str === 'string') ? str.startsWith('A') : false

// Tests

test('should set value to undefined when filter returns false', (t) => {
const state = {
root: { title: 'Other entry' },
context: { title: 'Other entry' },
value: 'Other entry'
}
const expected = {
root: { title: 'Other entry' },
context: { title: 'Other entry' },
value: undefined
}

const ret = filter(beginsWithA)(state)

t.deepEqual(ret, expected)
})

test('should not touch value when filter returns true', (t) => {
const state = {
root: { title: 'An entry' },
context: { title: 'An entry' },
value: 'An entry'
}

const ret = filter(beginsWithA)(state)

t.deepEqual(ret, state)
})

test('should remove values in array when filter returns false', (t) => {
const state = {
root: { users: ['John F', 'Andy'] },
context: { users: ['John F', 'Andy'] },
value: ['John F', 'Andy']
}
const expected = {
root: { users: ['John F', 'Andy'] },
context: { users: ['John F', 'Andy'] },
value: ['Andy']
}

const ret = filter(beginsWithA)(state)

t.deepEqual(ret, expected)
})

test('should not touch value when filter is not a function', (t) => {
const state = {
root: { title: 'An entry' },
context: { title: 'An entry' },
value: 'An entry'
}

const ret = filter('notallowed' as any)(state)

t.deepEqual(ret, state)
})
27 changes: 27 additions & 0 deletions src/funcs/filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { compose, unless, always, ifElse, filter as filterR, identity } from 'ramda'
import { MapFunction, Data, State } from '../types'
import { getStateValue } from '../utils/stateHelpers'

export interface FilterFunction {
(data: Data): boolean
}

export default function filter (fn: FilterFunction): MapFunction {
if (typeof fn !== 'function') {
return identity
}

const runFilter = compose(
ifElse(
Array.isArray,
filterR(fn),
unless(fn, always(undefined))
),
getStateValue
)

return (state: State) => ({
...state,
value: runFilter(state)
})
}
Loading

0 comments on commit 62a70bc

Please sign in to comment.