add missing functionality to Ox.api (positions requests and enums)
This commit is contained in:
parent
7b4002b340
commit
f3ff4b791d
2 changed files with 164 additions and 42 deletions
|
@ -2,16 +2,20 @@
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.api <f> Turns an array into a list API
|
Ox.api <f> Turns an array into a list API
|
||||||
(items) -> <f> List API
|
(items, options) -> <f> List API
|
||||||
(items, keys) -> <f> List API
|
items <[o]> An array of objects (key/value stores)
|
||||||
items <[o]> An array of objects
|
options <o> Options object
|
||||||
keys <[s]> Array of keys to be included in totals
|
enums <o> Enumerables, for example <code>{size: ['S', 'M', 'L', 'XL']}</code>
|
||||||
|
sums <[s]> List of keys to be included in totals
|
||||||
|
unique <s|'id'> The name of the unique key
|
||||||
<script>
|
<script>
|
||||||
Ox.test.api = Ox.api([
|
Ox.test.api = Ox.api([
|
||||||
{id: 'foo', n: 2},
|
{id: 'foo', n: 2},
|
||||||
{id: 'bar', n: 2},
|
{id: 'bar', n: 2},
|
||||||
{id: 'baz', n: 1}
|
{id: 'baz', n: 1}
|
||||||
], ['n']);
|
], {
|
||||||
|
sums: ['n']
|
||||||
|
});
|
||||||
Ox.test.apiResults = {
|
Ox.test.apiResults = {
|
||||||
0: Ox.test.api(),
|
0: Ox.test.api(),
|
||||||
1: Ox.test.api({
|
1: Ox.test.api({
|
||||||
|
@ -25,8 +29,8 @@ Ox.api <f> Turns an array into a list API
|
||||||
keys: [],
|
keys: [],
|
||||||
query: {
|
query: {
|
||||||
conditions: [
|
conditions: [
|
||||||
{key: 'id', value: 'f', operator: '!^'},
|
{key: 'id', operator: '!^', value: 'f'},
|
||||||
{key: 'n', value: 1, operator: '>'}
|
{key: 'n', operator: '>', value: 1}
|
||||||
],
|
],
|
||||||
operator: '&'
|
operator: '&'
|
||||||
}
|
}
|
||||||
|
@ -35,8 +39,8 @@ Ox.api <f> Turns an array into a list API
|
||||||
keys: [],
|
keys: [],
|
||||||
query: {
|
query: {
|
||||||
conditions: [
|
conditions: [
|
||||||
{key: 'id', value: 'O', operator: '='},
|
{key: 'id', operator: '=', value: 'O'},
|
||||||
{key: 'n', value: [1, 2], operator: '='}
|
{key: 'n', operator: '=', value: [1, 2]}
|
||||||
],
|
],
|
||||||
operator: '|'
|
operator: '|'
|
||||||
},
|
},
|
||||||
|
@ -46,11 +50,11 @@ Ox.api <f> Turns an array into a list API
|
||||||
keys: [],
|
keys: [],
|
||||||
query: {
|
query: {
|
||||||
conditions: [
|
conditions: [
|
||||||
{key: 'id', value: 'f', operator: '='},
|
{key: 'id', operator: '=', value: 'f'},
|
||||||
{
|
{
|
||||||
conditions: [
|
conditions: [
|
||||||
{key: 'id', value: 'a', operator: '='},
|
{key: 'id', operator: '=', value: 'a'},
|
||||||
{key: 'id', value: 'z', operator: '='}
|
{key: 'id', operator: '=', value: 'z'}
|
||||||
],
|
],
|
||||||
operator: '&'
|
operator: '&'
|
||||||
}
|
}
|
||||||
|
@ -63,8 +67,28 @@ Ox.api <f> Turns an array into a list API
|
||||||
keys: [],
|
keys: [],
|
||||||
range: [1, 2],
|
range: [1, 2],
|
||||||
sort: ['+id']
|
sort: ['+id']
|
||||||
|
}),
|
||||||
|
7: Ox.test.api({
|
||||||
|
positions: ['foo', 'bar'],
|
||||||
|
sort: ['+id']
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
Ox.test.api = Ox.api([
|
||||||
|
{i: 0, size: 'S'},
|
||||||
|
{i: 1, size: 'M'},
|
||||||
|
{i: 2, size: 'L'}
|
||||||
|
], {
|
||||||
|
enums: {size: ['S', 'M', 'L']},
|
||||||
|
unique: 'i'
|
||||||
|
});
|
||||||
|
Ox.test.apiResults[8] = Ox.test.api({
|
||||||
|
keys: ['size'],
|
||||||
|
query: {
|
||||||
|
conditions: [{key: 'size', operator: '>=', value: 'M'}],
|
||||||
|
operator: '&'
|
||||||
|
},
|
||||||
|
sort: [{key: 'size', operator: '-'}]
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
> Ox.test.apiResults[0].data
|
> Ox.test.apiResults[0].data
|
||||||
{items: 3, n: 5}
|
{items: 3, n: 5}
|
||||||
|
@ -80,9 +104,67 @@ Ox.api <f> Turns an array into a list API
|
||||||
{items: [{id: 'baz', n: 1}, {id: 'foo', n: 2}]}
|
{items: [{id: 'baz', n: 1}, {id: 'foo', n: 2}]}
|
||||||
> Ox.test.apiResults[6].data
|
> Ox.test.apiResults[6].data
|
||||||
{items: [{id: 'baz', n: 1}]}
|
{items: [{id: 'baz', n: 1}]}
|
||||||
|
> Ox.test.apiResults[7].data
|
||||||
|
{positions: {foo: 2, bar: 0}}
|
||||||
|
> Ox.test.apiResults[8].data
|
||||||
|
{items: [{i: 2, size: 'L'}, {i: 1, size: 'M'}]}
|
||||||
@*/
|
@*/
|
||||||
Ox.api = function(items, keys) {
|
Ox.api = function(items, options) {
|
||||||
keys = keys || [];
|
|
||||||
|
var enums = options.enums ? parseEnums(options.enums) : {},
|
||||||
|
sums = options.sums || [],
|
||||||
|
unique = options.unique || 'id';
|
||||||
|
|
||||||
|
function parseEnums(enums) {
|
||||||
|
// make enumerable strings lowercase
|
||||||
|
return Ox.map(enums, function(values) {
|
||||||
|
return values.map(function(value) {
|
||||||
|
return value.toLowerCase();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseConditions(conditions) {
|
||||||
|
// make string values lowercase,
|
||||||
|
// and replace enumerable strings used with the
|
||||||
|
// <, !<, <=, !<=, >, !>, >= or !>= operator
|
||||||
|
// with their index
|
||||||
|
return conditions.map(function(condition) {
|
||||||
|
var key = condition.key,
|
||||||
|
operator = condition.operator,
|
||||||
|
values = Ox.toArray(condition.value);
|
||||||
|
if (condition.conditions) {
|
||||||
|
condition.conditions = parseConditions(condition.conditions);
|
||||||
|
} else {
|
||||||
|
values = values.map(function(value) {
|
||||||
|
if (Ox.isString(value)) {
|
||||||
|
value = value.toLowerCase();
|
||||||
|
}
|
||||||
|
if (enums[key] && (
|
||||||
|
operator.indexOf('<') > -1
|
||||||
|
|| operator.indexOf('>') > -1
|
||||||
|
)) {
|
||||||
|
value = enums[key].indexOf(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
condition.value = Ox.isArray(condition.value)
|
||||||
|
? values : values[0];
|
||||||
|
}
|
||||||
|
return condition;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSort(sort) {
|
||||||
|
// translate 'foo' to {key: 'foo', operator: '+'}
|
||||||
|
return sort.map(function(sort) {
|
||||||
|
return Ox.isString(sort) ? {
|
||||||
|
key: sort.replace(/^[\+\-]/, ''),
|
||||||
|
operator: sort[0] == '-' ? '-' : '+'
|
||||||
|
} : sort;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function testCondition(item, condition) {
|
function testCondition(item, condition) {
|
||||||
var key = condition.key,
|
var key = condition.key,
|
||||||
operator = condition.operator.replace('!', ''),
|
operator = condition.operator.replace('!', ''),
|
||||||
|
@ -97,20 +179,24 @@ Ox.api = function(items, keys) {
|
||||||
},
|
},
|
||||||
'==': function(a, b) { return a === b; },
|
'==': function(a, b) { return a === b; },
|
||||||
'<': function(a, b) { return a < b; },
|
'<': function(a, b) { return a < b; },
|
||||||
'>': function(a, b) { return a > b; },
|
|
||||||
'<=': function(a, b) { return a <= b; },
|
'<=': function(a, b) { return a <= b; },
|
||||||
|
'>': function(a, b) { return a > b; },
|
||||||
'>=': function(a, b) { return a >= b; },
|
'>=': function(a, b) { return a >= b; },
|
||||||
'^': function(a, b) { return Ox.starts(a, b); },
|
'^': function(a, b) { return Ox.starts(a, b); },
|
||||||
'$': function(a, b) { return Ox.ends(a, b); },
|
'$': function(a, b) { return Ox.ends(a, b); },
|
||||||
};
|
};
|
||||||
if (Ox.isString(value)) {
|
|
||||||
value = value.toLowerCase();
|
|
||||||
}
|
|
||||||
if (Ox.isString(itemValue)) {
|
if (Ox.isString(itemValue)) {
|
||||||
itemValue = itemValue.toLowerCase();
|
itemValue = itemValue.toLowerCase();
|
||||||
}
|
}
|
||||||
|
if (enums[key] && (
|
||||||
|
operator.indexOf('<') > -1
|
||||||
|
|| operator.indexOf('>') > -1
|
||||||
|
)) {
|
||||||
|
itemValue = enums[key].indexOf(itemValue);
|
||||||
|
}
|
||||||
return test[operator](itemValue, value) == !not;
|
return test[operator](itemValue, value) == !not;
|
||||||
}
|
}
|
||||||
|
|
||||||
function testQuery(item, query) {
|
function testQuery(item, query) {
|
||||||
var match = true;
|
var match = true;
|
||||||
Ox.forEach(query.conditions, function(condition) {
|
Ox.forEach(query.conditions, function(condition) {
|
||||||
|
@ -128,22 +214,55 @@ Ox.api = function(items, keys) {
|
||||||
});
|
});
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
return function(options) {
|
return function(options) {
|
||||||
var data,
|
var data,
|
||||||
result = {data: {}, status: {code: 200, text: 'ok'}};
|
result = {data: {}, status: {code: 200, text: 'ok'}},
|
||||||
|
sort = {};
|
||||||
options = options || {};
|
options = options || {};
|
||||||
if (options.query) {
|
if (options.query) {
|
||||||
|
// find
|
||||||
|
options.query.conditions = parseConditions(options.query.conditions);
|
||||||
result.data.items = items.filter(function(item) {
|
result.data.items = items.filter(function(item) {
|
||||||
return testQuery(item, options.query);
|
return testQuery(item, options.query);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
result.data.items = Ox.clone(items);
|
result.data.items = Ox.clone(items);
|
||||||
}
|
}
|
||||||
if (options.keys) {
|
if (options.sort) {
|
||||||
if (options.sort) {
|
// sort
|
||||||
result.data.items = Ox.sortBy(result.data.items, options.sort);
|
options.sort = parseSort(options.sort);
|
||||||
}
|
Ox.forEach(enums, function(values, key) {
|
||||||
|
sort[key] = function(value) {
|
||||||
|
return values.indexOf(value.toLowerCase());
|
||||||
|
};
|
||||||
|
})
|
||||||
|
result.data.items = Ox.sortBy(result.data.items, options.sort, sort);
|
||||||
|
}
|
||||||
|
if (options.positions) {
|
||||||
|
// return positions
|
||||||
|
data = {positions: {}};
|
||||||
|
options.positions.forEach(function(id) {
|
||||||
|
data.positions[id] = Ox.getIndex(result.data.items, unique, id)
|
||||||
|
});
|
||||||
|
result.data = data;
|
||||||
|
} else if (!options.keys) {
|
||||||
|
// return totals
|
||||||
|
data = {};
|
||||||
|
sums.forEach(function(key) {
|
||||||
|
data[key] = Ox.sum(result.data.items.map(function(item) {
|
||||||
|
return item[key];
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
data.items = result.data.items.length;
|
||||||
|
result.data = data;
|
||||||
|
} else {
|
||||||
|
// return items
|
||||||
if (!Ox.isEmpty(options.keys)) {
|
if (!Ox.isEmpty(options.keys)) {
|
||||||
|
// filter keys
|
||||||
|
if (options.keys.indexOf(unique) == -1) {
|
||||||
|
options.keys.push(unique);
|
||||||
|
}
|
||||||
result.data.items = result.data.items.map(function(item) {
|
result.data.items = result.data.items.map(function(item) {
|
||||||
var ret = {};
|
var ret = {};
|
||||||
options.keys.forEach(function(key) {
|
options.keys.forEach(function(key) {
|
||||||
|
@ -153,21 +272,15 @@ Ox.api = function(items, keys) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (options.range) {
|
if (options.range) {
|
||||||
|
// apply range
|
||||||
result.data.items = Ox.sub(
|
result.data.items = Ox.sub(
|
||||||
result.data.items, options.range[0], options.range[1]
|
result.data.items, options.range[0], options.range[1]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
data = {items: result.data.items.length};
|
|
||||||
keys.forEach(function(key) {
|
|
||||||
data[key] = Ox.sum(result.data.items.map(function(item) {
|
|
||||||
return item[key];
|
|
||||||
}));
|
|
||||||
})
|
|
||||||
result.data = data;
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
|
@ -259,8 +372,11 @@ Ox.range = function() {
|
||||||
len, matches = {}, sort = {};
|
len, matches = {}, sort = {};
|
||||||
// find leading numbers
|
// find leading numbers
|
||||||
arr.forEach(function(val, i) {
|
arr.forEach(function(val, i) {
|
||||||
var match = /^\d+/.exec(arr_[i]);
|
var match;
|
||||||
matches[val] = match ? match[0] : '';
|
if (Ox.isString(val)) {
|
||||||
|
match = /^\d+/.exec(arr_[i]);
|
||||||
|
matches[val] = match ? match[0] : '';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// get length of longest leading number
|
// get length of longest leading number
|
||||||
len = Ox.max(Ox.map(matches, function(val) {
|
len = Ox.max(Ox.map(matches, function(val) {
|
||||||
|
@ -269,13 +385,15 @@ Ox.range = function() {
|
||||||
// pad leading numbers, make lowercase,
|
// pad leading numbers, make lowercase,
|
||||||
// and remove leading non-word characters
|
// and remove leading non-word characters
|
||||||
arr.forEach(function(val, i) {
|
arr.forEach(function(val, i) {
|
||||||
sort[val] = (
|
sort[val] = Ox.isString(arr_[i])
|
||||||
matches[val]
|
? (
|
||||||
? arr_[i].toString().replace(
|
matches[val]
|
||||||
matches[val], Ox.pad(matches[val], len)
|
? arr_[i].replace(
|
||||||
)
|
matches[val], Ox.pad(matches[val], len)
|
||||||
: arr_[i]
|
)
|
||||||
).toLowerCase().replace(/^\W+/, '');
|
: arr_[i]
|
||||||
|
).toLowerCase().replace(/^\W+/, '')
|
||||||
|
: arr_[i];
|
||||||
});
|
});
|
||||||
return sort;
|
return sort;
|
||||||
}
|
}
|
||||||
|
@ -320,10 +438,10 @@ Ox.range = function() {
|
||||||
Ox.sortBy = function(arr, by, fn) {
|
Ox.sortBy = function(arr, by, fn) {
|
||||||
var length = by.length, values = {};
|
var length = by.length, values = {};
|
||||||
by = by.map(function(v) {
|
by = by.map(function(v) {
|
||||||
return {
|
return Ox.isString(v) ? {
|
||||||
key: v.replace(/^[\+\-]/, ''),
|
key: v.replace(/^[\+\-]/, ''),
|
||||||
operator: v[0] == '-' ? '-' : '+'
|
operator: v[0] == '-' ? '-' : '+'
|
||||||
};
|
} : v;
|
||||||
});
|
});
|
||||||
fn = fn || {};
|
fn = fn || {};
|
||||||
by.map(function(v) {
|
by.map(function(v) {
|
||||||
|
|
|
@ -46,6 +46,8 @@ Ox.endsWith <f> Checks if a string ends with a given substring
|
||||||
@*/
|
@*/
|
||||||
Ox.ends = Ox.endsWith = function(str, sub) {
|
Ox.ends = Ox.endsWith = function(str, sub) {
|
||||||
// fixme: rename to ends
|
// fixme: rename to ends
|
||||||
|
str = str.toString();
|
||||||
|
sub = sub.toString();
|
||||||
return str.substr(str.length - sub.length) == sub;
|
return str.substr(str.length - sub.length) == sub;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -427,6 +429,8 @@ Ox.startsWith <f> Checks if a string starts with a given substring
|
||||||
@*/
|
@*/
|
||||||
Ox.starts = Ox.startsWith = function(str, sub) {
|
Ox.starts = Ox.startsWith = function(str, sub) {
|
||||||
// fixme: rename to starts
|
// fixme: rename to starts
|
||||||
|
str = str.toString();
|
||||||
|
sub = sub.toString();
|
||||||
return str.substr(0, sub.length) == sub;
|
return str.substr(0, sub.length) == sub;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue