Coverage

100%
155
155
0

lib/abstract.js

100%
102
102
0
LineHitsSource
1'use strict';
2
31var _ = require('underscore');
4
5
6function lcfirst(property) {
766 return [property.charAt(0).toLowerCase(), property.substr(1)].join('');
8}
9
10
11function plucker(data, property) {
1212 return _.object(_.keys(data), _.pluck(data, property));
13}
14
15
16function defineProperty(object, property, value, enumerable) {
17843 return Object.defineProperty(object, property, {
18 enumerable: enumerable || false,
19 configurable: false,
20 writable: false,
21 value: value
22 });
23}
24
25
26function setProperties(object, properties) {
2756 return _.each(properties, function (value, key) {
28560 return defineProperty(object, key, value);
29 });
30}
31
32
33function abstract(namespace, schema, defaults) {
3457 var object = {},
35 setters = {},
36 getters = {},
37 validators = {};
38
3957 defineProperty(object, '_data', {});
4057 defineProperty(object, '_gleam', namespace);
41
42 function set(values) {
4341 if (!_.isObject(values) || _.isArray(values) || _.isFunction(values)) {
444 throw new Error('Values must be an object');
45 }
4637 _.each(values, function (value, key) {
4796 return setKey(key, value, values);
48 });
49 }
50
51 function get() {
526 var data = {};
536 _.each(object._data, function (property, key) {
5415 var value = getKey(key);
5515 if (_.isArray(value)) {
562 data[key] = [];
572 _.each(value, function (item) {
584 if (_.isObject(item) && !_.isEmpty(item._gleam)) {
592 data[key].push(item.get());
60 } else {
612 data[key].push(item);
62 }
63 });
6413 } else if (_.isObject(value) && !_.isEmpty(value._gleam)) {
651 data[key] = value.get();
66 } else {
6712 data[key] = value;
68 }
69 });
706 return data;
71 }
72
73
74 function isValid(key, value, values) {
75100 if (_.isUndefined(values)) {
764 values = {};
774 values[key] = value;
78 }
79100 if (!_.has(schema, key)) {
809 return false;
81 }
8291 if (_.has(validators, key)) {
8320 return validators[key](value, values, object);
84 }
8571 return true;
86 }
87
88
89 function setValue(key, value, values) {
9088 if (_.has(setters, key)) {
913 object._data[key].value = setters[key](value, values, object);
92 } else {
9385 object._data[key].value = value;
94 }
9588 object._data[key].modified = true;
9688 return object;
97 }
98
99
100 function setKey(key, value, values) {
10198 if (isValid(key, value, values)) {
10288 return setValue(key, value, values);
103 }
10410 if (_.has(schema, key)) {
1051 throw new Error('Value [' + value + '] is not valid for [' + namespace + '.' + key + ']');
106 }
1079 return object;
108 }
109
110
111 function getKey(key) {
11233 if (!_.has(object._data, key)) {
1131 throw new Error('Accessing undefined property [' + namespace + '.' + key + ']');
114 }
11532 if (_.has(getters, key)) {
1164 return getters[key](object);
117 }
11828 return object._data[key].value;
119 }
120
121
12257 _.each(schema, function (value, key) {
123236 if (!_.isFunction(value)) {
1241 throw new Error('Entity schema must not have any properties [' + namespace + '.' + key + ']');
125 }
126235 if (key.substr(0, 4) === '_set') {
12715 setters[lcfirst(key.substr(4))] = value;
12815 return;
129 }
130220 if (key.substr(0, 4) === '_get') {
13115 getters[lcfirst(key.substr(4))] = value;
13215 return;
133 }
134205 if (key.substr(0, 9) === '_validate') {
13536 validators[lcfirst(key.substr(9))] = value;
13636 return;
137 }
138
139169 object._data[key] = {
140 initial: value(),
141 modified: false,
142 value: value()
143 };
144
145169 defineProperty(object, key, function (value) {
14618 if (_.isUndefined(value)) {
14716 return getKey(key);
148 }
1492 setKey(key, value);
1502 return object;
151 }, true);
152 });
153
154
155 function getFlat() {
1562 var data = {};
1572 _.each(object._data, function (property, key) {
1589 if (_.isArray(property.value)) {
1592 data[key] = [];
1602 _.each(property.value, function (item) {
1614 if (!_.isObject(item) || _.isEmpty(item._gleam)) {
1622 data[key].push(item);
163 }
164 });
1657 } else if (!_.isObject(property.value) || _.isEmpty(property.value._gleam)) {
1666 data[key] = property.value;
167 }
168 });
1692 return data;
170 }
171
172
173 function toJson() {
1749 var data = {};
1759 _.each(object._data, function (property, key) {
17623 if (_.isArray(property.value)) {
1772 data[key] = [];
1782 _.each(property.value, function (item) {
1794 if (_.isObject(item) && !_.isEmpty(item._gleam)) {
1802 data[key].push(item.toJSON());
181 } else {
1822 data[key].push(item);
183 }
184 });
18521 } else if (_.isObject(property.value) && !_.isEmpty(property.value._gleam)) {
1862 data[key] = property.value.toJSON();
187 } else {
18819 data[key] = property.value;
189 }
190 });
1919 data.__ns = namespace;
1929 return data;
193 }
194
195
19656 setProperties(object, {
197 set: set,
198 get: get,
199 getProperty: getKey,
200 getFlat: getFlat,
201 initial: function () {
2026 return plucker(object._data, 'initial');
203 },
204 modified: function () {
2056 return plucker(object._data, 'modified');
206 },
207 is: function (ns) {
2089 return ns === namespace;
209 },
210 isValid: isValid,
211 toJSON: toJson,
212 toString: function () {
2136 return ['[', 'object', ' ', 'Gleam:', namespace, ']'].join('');
214 }
215 });
216
217
21856 if (defaults) {
21933 set(defaults);
220
221 // Set modified key back to false and fill initial value
22232 _.each(object._data, function (property) {
22395 property.modified = false;
22495 property.initial = property.value;
225 });
226 }
227
228
22955 return object;
230}
231
232
2331module.exports = exports = abstract;
234

lib/build-sync.js

100%
18
18
0
LineHitsSource
1"use strict";
2
31var path = require('path');
41var _ = require('underscore');
51var fs = require('fs');
61var glob = require('glob');
71var mkdirp = require('mkdirp');
8
9
10function getPaths(root) {
111 var files = {
12 'from-json': {
13 template: path.join(__dirname, 'browser', 'from-json.txt')
14 },
15 'abstract': {
16 template: path.join(__dirname, 'browser', 'abstract.txt'),
17 content: path.join(__dirname, 'abstract.js')
18 }
19 };
201 glob.sync("**/*.js", {cwd: root}).forEach(function (entityPath) {
217 files[entityPath.replace('.js', '')] = {
22 template: path.join(__dirname, 'browser', 'entity.txt'),
23 content: path.join(root, entityPath)
24 };
25 });
261 return files;
27}
28
29
301var removeStrictRegexp = new RegExp('\\s*[\'"]use strict[\'"];\\s*', 'ig');
31
32
33function renderFile(namespace, file, out) {
349 var template = _.template(fs.readFileSync(file.template, 'UTF-8')),
35 content = template({
36 namespace: namespace,
37 content: file.content && fs.readFileSync(file.content, 'UTF-8').replace(removeStrictRegexp, '') || ''
38 }),
39 outDir = path.join(out, path.join.apply(path, _.initial(namespace.split('/')))),
40 outFile = path.join(out, namespace + '.js');
41
429 mkdirp.sync(outDir);
439 fs.writeFileSync(outFile, content);
449 return true;
45}
46
47
48function render(files, out) {
491 return _.reduce(files, function (memo, file, namespace) {
509 return memo && renderFile(namespace, file, out);
51 }, true);
52}
53
54function build(input, out) {
551 return render(getPaths(input), out);
56}
57
581module.exports = exports = build;
59

lib/entity.js

100%
8
8
0
LineHitsSource
1"use strict";
2
31var abstract = require('./abstract');
41var path = require('path');
51var _ = require('underscore');
6
7
8function entity(root, namespace, data) {
959 var schema = require(path.join(root, namespace)).Entity;
1059 if (!_.isObject(schema)) {
112 throw new Error("Entity must be an object");
12 }
1357 return abstract(namespace, schema, data);
14}
15
161module.exports = exports = entity;
17

lib/from-json.js

100%
9
9
0
LineHitsSource
1'use strict';
2
31var _ = require('underscore');
41var entity = require('./entity');
5
6
7function fromJson(root, json) {
811 return JSON.parse(json, function (key, value) {
9103 if (_.isObject(value) && !_.isUndefined(value.__ns)) {
1022 var namespace = value.__ns;
1122 delete value.__ns;
1222 return entity(root, namespace, value);
13 }
1481 return value;
15 });
16}
17
181module.exports = exports = fromJson;
19

lib/index.js

100%
15
15
0
LineHitsSource
1"use strict";
2
31var entity = require('./entity');
41var is = require('./is');
51var fromJson = require('./from-json');
61var buildSync = require('./build-sync');
71var _ = require('underscore');
8
9
10/**
11 * @constructor
12 * @param root
13 * @property _root
14 */
15function Gleam(root) {
166 if (_.isEmpty(root)) {
171 throw new Error('Root must be set');
18 }
195 this._root = root;
20}
21
22
23/**
24 * @param {String} namespace
25 * @param {Object?} data
26 * @return {Entity}
27 */
281Gleam.prototype.entity = function (namespace, data) {
2937 return entity(this._root, namespace, data);
30};
31
32
33/**
34 * @param {String} json
35 * @return {*}
36 */
371Gleam.prototype.fromJson = function (json) {
3811 return fromJson(this._root, json);
39};
40
41
42/**
43 * @function
44 * @param {Object} entity
45 * @param {String} namespace
46 */
471Gleam.is = is;
48
49
50/**
51 * @function
52 * @param {String} input Input directory
53 * @param {String} out Output directory
54 */
551Gleam.buildSync = buildSync;
56
57
581module.exports = exports = Gleam;
59

lib/is.js

100%
3
3
0
LineHitsSource
1"use strict";
2
31var _ = require('underscore');
4
5/**
6 * @api
7 * @static
8 */
9function is(entity, namespace) {
108 return !_.isEmpty(entity) &&
11 _.isObject(entity) &&
12 _.isFunction(entity.is) &&
13 entity.is(namespace);
14}
15
161module.exports = exports = is;
17