<commit_before>'use strict';

const common = require('x2node-common');

/**
 * Regular expression for parsing property value type specifications.
 *
 * @private
 * @type {RegExp}
 */
const VALUE_TYPE_RE = new RegExp(
	'^(?:(string|number|boolean|datetime|object)' +
		'|(ref)\\((\\S+)\\))(?:\\[\\]|\\{\\})?$'
);

/**
 * Get invalid property definition error.
 *
 * @private
 * @param {module:x2node-records~PropertyDescriptor} propDesc Property
 * descriptor.
 * @param {string} msg Error message.
 * @returns {module:x2node-common.X2UsageError} Error to throw.
 */
function invalidPropDef(propDesc, msg) {
	return new common.X2UsageError(
		'Property ' + propDesc.container.nestedPath + propDesc.name +
			' of record type ' + String(propDesc.container.recordTypeName) +
			' has invalid definition: ' + msg);
}

/**
 * Create object factory.
 *
 * @private
 * @param {Object} objDef Object definition.
 * @returns {Function} The factory function.
 */
function createObjectFactory(objDef) {
	return (
		objDef.factory ? objDef.factory :
			function() {
				return new Object();
			}
	);
}

/**
 * Subtype property marker.
 *
 * @private
 */
const SUBTYPE = Symbol('SUBTYPE');

/**
 * Extension that implements the core functionaity.
 *
 * @private
 * @memberof module:x2node-records
 * @inner
 * @implements {module:x2node-records.Extension}
 */
class CoreExtention {

	// extend properties containers
	extendPropertiesContainer(ctx, container) {

		// create records factory
		container._factory = createObjectFactory(container.definition);

		// find the id property
		ctx.onContainerComplete(container => {
			let idPropName;
			container.allPropertyNames.forEach(propName => {
				if (container.getPropertyDesc(propName).isId()) {
					if (idPropName)
						throw new common.X2UsageError(
							'Record type ' + String(container.recordTypeName) + (
								container.nestedPath.length > 0 ?
									' property ' + container.nestedPath : '') +
								' has more than one id property.');
					idPropName = propName;
				}
			});
			if (idPropName)
				container._idPropName = idPropName;
		});

		// record type specific logic
		if (container.nestedPath.length === 0) {

			// add id validation if record type
			ctx.onLibraryValidation(() => {
				if (!container.idPropertyName)
					throw new common.X2UsageError(
						'Record type ' + String(container.recordTypeName) +
							' does not have an id property.');
			});

			// add properties and methods to the descriptor:

			/**
			 * Create new record instance of this type.
			 *
			 * @function module:x2node-records~RecordTypeDescriptor#newRecord
			 * @returns {Object} New record instance.
			 */
			container.newRecord = function() {
				return this._factory.call(this);
			};
		}

		// return the container
		return container;
	}

	// extend property descriptors
	extendPropertyDescriptor(ctx, propDesc) {

		// get the definition
		const propDef = propDesc.definition;

		// parse the value type
		let match = VALUE_TYPE_RE.exec(propDef.valueType);
		if (match === null)
			throw invalidPropDef(
				propDesc, 'unknown value type or invalid value type syntax.');
		propDesc._scalarValueType = match.find((val, ind) => ((ind > 0) && val));

		// determine whether scalar, array or map
		propDesc._isArray = propDef.valueType.endsWith('[]');
		propDesc._isMap = propDef.valueType.endsWith('{}');
		propDesc._isScalar = (!propDesc._isArray && !propDesc._isMap);

		// determine if optional
		propDesc._optional = (
			propDef.optional === undefined ?
				!propDesc._isScalar : propDef.optional);

		// determine if id property
		propDesc._isId = (propDef.role === 'id');
		if (propDesc._isId && !(propDesc._isScalar && (
			propDesc._scalarValueType === 'number' ||
				propDesc._scalarValueType === 'string')))
			throw invalidPropDef(
				propDesc, 'id property may only be a scalar string or' +
					' a number.');

		// determine if polymorph object
		propDesc._isPolymorphObject = (propDef.subtypes !== undefined);
		if (propDesc._isPolymorphObject) {
			propDesc._typePropertyName = propDef.typePropertyName;
			if (!propDesc._typePropertyName)
				throw invalidPropDef(
					propDesc, 'missing typePropertyName attribute.');
		}

		// process reference property
		if (propDesc._scalarValueType === 'ref') {

			// extract and validate reference target record type(s)
			const refTargets = match[3].split('|');

			// determine if polymorphic
			propDesc._isPolymorphRef = (refTargets.length > 1);
			if (propDesc._isPolymorphRef) {

				propDesc._scalarValueType = 'object';

				const supertypeDef = Object.create(propDef);
				supertypeDef.properties = {};
				propDesc._subtypes = new Array();
				refTargets.forEach(refTarget => {
					propDesc._subtypes.push(refTarget);
					supertypeDef.properties[refTarget] = {
						[SUBTYPE]: true,
						valueType: 'ref(' + refTarget + ')',
						optional: true
					};
				});

				propDesc._nestedProperties = ctx.createPropertiesContainer(
					propDesc, supertypeDef);

				// TODO: process id and or map key properties

				propDesc._factory = createObjectFactory({});

			} else {

				propDesc._refTarget = refTargets[0];

				ctx.onLibraryComplete(recordTypes => {
					if (!recordTypes.hasRecordType(propDesc.refTarget))
						throw invalidPropDef(
							propDesc, 'unknown reference target record type.');
					propDesc._nestedProperties = recordTypes.getRecordTypeDesc(
						propDesc.refTarget);
				});
			}
		}

		// process nested object property
		if ((propDesc._scalarValueType === 'object') &&
			!propDesc._isPolymorphRef) {

			// get/create nested properties container
			if (propDesc._viewOfDesc) {

				propDesc._nestedProperties = viewOfDesc._nestedProperties;

			} else if (propDesc._isPolymorphObject) {

				const supertypeDef = Object.create(propDef);
				supertypeDef.properties = (
					propDef.properties ? Object.create(propDef.properties) : {}
				);
				supertypeDef.subtypes = null;
				propDesc._subtypes = new Array();
				for (let subtypeName in propDef.subtypes) {
					propDesc._subtypes.push(subtypeName);
					const subtypePropDef = Object.create(
						propDef.subtypes[subtypeName]);
					subtypePropDef[SUBTYPE] = true;
					subtypePropDef.typePropertyName = propDesc._typePropertyName;
					subtypePropDef.valueType = 'object';
					subtypePropDef.optional = true;
					supertypeDef.properties[subtypeName] = subtypePropDef;
				}

				propDesc._nestedProperties = ctx.createPropertiesContainer(
					propDesc, supertypeDef);

			} else {

				if (!propDef.properties)
					throw invalidPropDef(propDesc, 'missing properties.');

				propDesc._nestedProperties = ctx.createPropertiesContainer(
					propDesc, propDef);
			}

			// create object factory
			propDesc._factory = createObjectFactory(propDef);
		}

		// create nested properties container for simple values collection
		if ((propDesc._scalarValueType !== 'object') &&
			(propDesc._scalarValueType !== 'ref')) {
			if (propDesc._isScalar)
				propDesc._nestedProperties = null;
			else
				propDesc._nestedProperties = ctx.createPropertiesContainer(
					propDesc, {
						properties: {
							'value': {
								valueType: propDesc._scalarValueType
							}
						}
					});
		}

		// process subtype property
		if (propDef[SUBTYPE]) {
			propDesc._subtype = true;
			propDesc._typePropertyName = propDef.typePropertyName;
		}

		// add properties and methods to the descriptor:

		/**
		 * Scalar value type of the property. One of "string", "number",
		 * "boolean", "datetime", "object" or "ref".
		 *
		 * @member {string} module:x2node-records~PropertyDescriptor#scalarValueType
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'scalarValueType', {
			get() { return this._scalarValueType; }
		});

		/**
		 * Tell if the property is scalar.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isScalar
		 * @returns {boolean} true if the property is scalar.
		 */
		propDesc.isScalar = function() { return this._isScalar; };

		/**
		 * Tell if the property is an array.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isArray
		 * @returns {boolean} true if the property is an array.
		 */
		propDesc.isArray = function() { return this._isArray; };

		/**
		 * Tell if the property is a map.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isMap
		 * @returns {boolean} true if the property is a map.
		 */
		propDesc.isMap = function() { return this._isMap; };

		/**
		 * true if the property is optional.
		 *
		 * @member {boolean} module:x2node-records~PropertyDescriptor#optional
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'optional', {
			get() { return this._optional; }
		});

		/**
		 * Tell if the property is a record or nested object id.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isId
		 * @returns {boolean} true if the property is an id.
		 */
		propDesc.isId = function() { return this._isId; };

		/**
		 * Tell if the property is polymorphic nested object.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isPolymorphObject
		 * @returns {boolean} true if the property is a polymorphic
		 * nested object.
		 */
		propDesc.isPolymorphObject =
			function() { return this._isPolymorphObject; };

		/**
		 * Tell if the property is polymorphic reference.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isPolymorphRef
		 * @returns {boolean} true if the property is a polymorphic
		 * reference.
		 */
		propDesc.isPolymorphRef = function() { return this._isPolymorphRef; };

		/**
		 * Tell if the property is polymorphic nested object or reference.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isPolymorph
		 * @returns {boolean} true if the property is a polymorphic
		 * nested object or reference.
		 */
		propDesc.isPolymorph = function() {
			return (this._isPolymorphObject || this._isPolymorphRef);
		};

		/**
		 * Tell if the property is a pseudo-property for a subtype in a
		 * polymorphic nested object or reference container.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isSubtype
		 * @returns {boolean} true if the property is a subtype
		 * property (can be either nested object or a reference).
		 */
		propDesc.isSubtype = function() {

			return this._subtype;
		};

		/**
		 * For a polymorphic nested object property, name of the property used as
		 * the concrete type discriminator. The same is made available on the
		 * subtype pseudo-properties (those that have isSubtype()
		 * return true).
		 *
		 * @member {string} module:x2node-records~PropertyDescriptor#typePropertyName
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'typePropertyName', {
			get() { return this._typePropertyName; }
		});

		/**
		 * Tell if the property is a reference (scalarValueType is
		 * "ref").
		 *
		 * @function module:x2node-records~PropertyDescriptor#isRef
		 * @returns {boolean} true if the property is a reference.
		 */
		propDesc.isRef =
			function() { return (this._scalarValueType === 'ref'); };

		/**
		 * Name of the target record type for a reference property.
		 *
		 * @member {string} module:x2node-records~PropertyDescriptor#refTarget
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'refTarget', {
			get() { return this._refTarget; }
		});

		/**
		 * For a nested object property (scalarValueType is
		 * "object"), the descriptors of the nested properties. For a reference
		 * property (scalarValueType is "ref"), the target record
		 * type descriptor. For a non-object, non-reference property, if it is
		 * not scalar the container describes a single property named "value"
		 * with the scalar value type. If it is scalar, the
		 * nestedProperties value is null
		 *
		 * @member {?module:x2node-records~PropertiesContainer} module:x2node-records~PropertyDescriptor#nestedProperties
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'nestedProperties', {
			get() { return this._nestedProperties; }
		});

		/**
		 * For a polymorphic nested object property, the list of subtype names.
		 * For a polymorphic reference property, the list of possible referred
		 * record type names.
		 *
		 * @member {string[]} module:x2node-records~PropertyDescriptor#subtypes
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'subtypes', {
			get() { return this._subtypes; }
		});

		/**
		 * Create new nested object instance for a nested object property.
		 *
		 * @function module:x2node-records~PropertyDescriptor#newObject
		 * @returns {Object} New nested object instance.
		 */
		propDesc.newObject = function() {
			return this._factory.call(this);
		};

		// return the descriptor
		return propDesc;
	}
}

// export the class
module.exports = CoreExtention;
<commit_msg>Check that shared polymorph props do not collide with subtypes.<commit_after>'use strict';

const common = require('x2node-common');

/**
 * Regular expression for parsing property value type specifications.
 *
 * @private
 * @type {RegExp}
 */
const VALUE_TYPE_RE = new RegExp(
	'^(?:(string|number|boolean|datetime|object)' +
		'|(ref)\\((\\S+)\\))(?:\\[\\]|\\{\\})?$'
);

/**
 * Get invalid property definition error.
 *
 * @private
 * @param {module:x2node-records~PropertyDescriptor} propDesc Property
 * descriptor.
 * @param {string} msg Error message.
 * @returns {module:x2node-common.X2UsageError} Error to throw.
 */
function invalidPropDef(propDesc, msg) {
	return new common.X2UsageError(
		'Property ' + propDesc.container.nestedPath + propDesc.name +
			' of record type ' + String(propDesc.container.recordTypeName) +
			' has invalid definition: ' + msg);
}

/**
 * Create object factory.
 *
 * @private
 * @param {Object} objDef Object definition.
 * @returns {Function} The factory function.
 */
function createObjectFactory(objDef) {
	return (
		objDef.factory ? objDef.factory :
			function() {
				return new Object();
			}
	);
}

/**
 * Subtype property marker.
 *
 * @private
 */
const SUBTYPE = Symbol('SUBTYPE');

/**
 * Extension that implements the core functionaity.
 *
 * @private
 * @memberof module:x2node-records
 * @inner
 * @implements {module:x2node-records.Extension}
 */
class CoreExtention {

	// extend properties containers
	extendPropertiesContainer(ctx, container) {

		// create records factory
		container._factory = createObjectFactory(container.definition);

		// find the id property
		ctx.onContainerComplete(container => {
			let idPropName;
			container.allPropertyNames.forEach(propName => {
				if (container.getPropertyDesc(propName).isId()) {
					if (idPropName)
						throw new common.X2UsageError(
							'Record type ' + String(container.recordTypeName) + (
								container.nestedPath.length > 0 ?
									' property ' + container.nestedPath : '') +
								' has more than one id property.');
					idPropName = propName;
				}
			});
			if (idPropName)
				container._idPropName = idPropName;
		});

		// record type specific logic
		if (container.nestedPath.length === 0) {

			// add id validation if record type
			ctx.onLibraryValidation(() => {
				if (!container.idPropertyName)
					throw new common.X2UsageError(
						'Record type ' + String(container.recordTypeName) +
							' does not have an id property.');
			});

			// add properties and methods to the descriptor:

			/**
			 * Create new record instance of this type.
			 *
			 * @function module:x2node-records~RecordTypeDescriptor#newRecord
			 * @returns {Object} New record instance.
			 */
			container.newRecord = function() {
				return this._factory.call(this);
			};
		}

		// return the container
		return container;
	}

	// extend property descriptors
	extendPropertyDescriptor(ctx, propDesc) {

		// get the definition
		const propDef = propDesc.definition;

		// parse the value type
		let match = VALUE_TYPE_RE.exec(propDef.valueType);
		if (match === null)
			throw invalidPropDef(
				propDesc, 'unknown value type or invalid value type syntax.');
		propDesc._scalarValueType = match.find((val, ind) => ((ind > 0) && val));

		// determine whether scalar, array or map
		propDesc._isArray = propDef.valueType.endsWith('[]');
		propDesc._isMap = propDef.valueType.endsWith('{}');
		propDesc._isScalar = (!propDesc._isArray && !propDesc._isMap);

		// determine if optional
		propDesc._optional = (
			propDef.optional === undefined ?
				!propDesc._isScalar : propDef.optional);

		// determine if id property
		propDesc._isId = (propDef.role === 'id');
		if (propDesc._isId && !(propDesc._isScalar && (
			propDesc._scalarValueType === 'number' ||
				propDesc._scalarValueType === 'string')))
			throw invalidPropDef(
				propDesc, 'id property may only be a scalar string or' +
					' a number.');

		// determine if polymorph object
		propDesc._isPolymorphObject = (propDef.subtypes !== undefined);
		if (propDesc._isPolymorphObject) {
			propDesc._typePropertyName = propDef.typePropertyName;
			if (!propDesc._typePropertyName)
				throw invalidPropDef(
					propDesc, 'missing typePropertyName attribute.');
		}

		// process reference property
		if (propDesc._scalarValueType === 'ref') {

			// extract and validate reference target record type(s)
			const refTargets = match[3].split('|');

			// determine if polymorphic
			propDesc._isPolymorphRef = (refTargets.length > 1);
			if (propDesc._isPolymorphRef) {

				propDesc._scalarValueType = 'object';

				const supertypeDef = Object.create(propDef);
				supertypeDef.properties = {};
				propDesc._subtypes = new Array();
				refTargets.forEach(refTarget => {
					propDesc._subtypes.push(refTarget);
					supertypeDef.properties[refTarget] = {
						[SUBTYPE]: true,
						valueType: 'ref(' + refTarget + ')',
						optional: true
					};
				});
				propDesc._nestedProperties = ctx.createPropertiesContainer(
					propDesc, supertypeDef);

				propDesc._factory = createObjectFactory({});

			} else {

				propDesc._refTarget = refTargets[0];

				ctx.onLibraryComplete(recordTypes => {
					if (!recordTypes.hasRecordType(propDesc.refTarget))
						throw invalidPropDef(
							propDesc, 'unknown reference target record type.');
					propDesc._nestedProperties = recordTypes.getRecordTypeDesc(
						propDesc.refTarget);
				});
			}
		}

		// process nested object property
		if ((propDesc._scalarValueType === 'object') &&
			!propDesc._isPolymorphRef) {

			// get/create nested properties container
			if (propDesc._viewOfDesc) {

				propDesc._nestedProperties = viewOfDesc._nestedProperties;

			} else if (propDesc._isPolymorphObject) {

				const supertypeDef = Object.create(propDef);
				supertypeDef.properties = (
					propDef.properties ? Object.create(propDef.properties) : {}
				);
				supertypeDef.subtypes = null;
				propDesc._subtypes = new Array();
				for (let subtypeName in propDef.subtypes) {
					propDesc._subtypes.push(subtypeName);
					const subtypePropDef = Object.create(
						propDef.subtypes[subtypeName]);
					subtypePropDef[SUBTYPE] = true;
					subtypePropDef.typePropertyName = propDesc._typePropertyName;
					subtypePropDef.valueType = 'object';
					subtypePropDef.optional = true;
					if (supertypeDef.properties[subtypeName] !== undefined)
						throw invalidPropDef(
							propDesc, 'shared property may not have same name' +
								' as a subtype.');
					supertypeDef.properties[subtypeName] = subtypePropDef;
				}

				propDesc._nestedProperties = ctx.createPropertiesContainer(
					propDesc, supertypeDef);

			} else {

				if (!propDef.properties)
					throw invalidPropDef(propDesc, 'missing properties.');

				propDesc._nestedProperties = ctx.createPropertiesContainer(
					propDesc, propDef);
			}

			// create object factory
			propDesc._factory = createObjectFactory(propDef);
		}

		// create nested properties container for simple values collection
		if ((propDesc._scalarValueType !== 'object') &&
			(propDesc._scalarValueType !== 'ref')) {
			if (propDesc._isScalar)
				propDesc._nestedProperties = null;
			else
				propDesc._nestedProperties = ctx.createPropertiesContainer(
					propDesc, {
						properties: {
							'value': {
								valueType: propDesc._scalarValueType
							}
						}
					});
		}

		// process subtype property
		if (propDef[SUBTYPE]) {
			propDesc._subtype = true;
			propDesc._typePropertyName = propDef.typePropertyName;
		}

		// add properties and methods to the descriptor:

		/**
		 * Scalar value type of the property. One of "string", "number",
		 * "boolean", "datetime", "object" or "ref".
		 *
		 * @member {string} module:x2node-records~PropertyDescriptor#scalarValueType
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'scalarValueType', {
			get() { return this._scalarValueType; }
		});

		/**
		 * Tell if the property is scalar.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isScalar
		 * @returns {boolean} true if the property is scalar.
		 */
		propDesc.isScalar = function() { return this._isScalar; };

		/**
		 * Tell if the property is an array.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isArray
		 * @returns {boolean} true if the property is an array.
		 */
		propDesc.isArray = function() { return this._isArray; };

		/**
		 * Tell if the property is a map.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isMap
		 * @returns {boolean} true if the property is a map.
		 */
		propDesc.isMap = function() { return this._isMap; };

		/**
		 * true if the property is optional.
		 *
		 * @member {boolean} module:x2node-records~PropertyDescriptor#optional
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'optional', {
			get() { return this._optional; }
		});

		/**
		 * Tell if the property is a record or nested object id.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isId
		 * @returns {boolean} true if the property is an id.
		 */
		propDesc.isId = function() { return this._isId; };

		/**
		 * Tell if the property is polymorphic nested object.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isPolymorphObject
		 * @returns {boolean} true if the property is a polymorphic
		 * nested object.
		 */
		propDesc.isPolymorphObject =
			function() { return this._isPolymorphObject; };

		/**
		 * Tell if the property is polymorphic reference.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isPolymorphRef
		 * @returns {boolean} true if the property is a polymorphic
		 * reference.
		 */
		propDesc.isPolymorphRef = function() { return this._isPolymorphRef; };

		/**
		 * Tell if the property is polymorphic nested object or reference.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isPolymorph
		 * @returns {boolean} true if the property is a polymorphic
		 * nested object or reference.
		 */
		propDesc.isPolymorph = function() {
			return (this._isPolymorphObject || this._isPolymorphRef);
		};

		/**
		 * Tell if the property is a pseudo-property for a subtype in a
		 * polymorphic nested object or reference container.
		 *
		 * @function module:x2node-records~PropertyDescriptor#isSubtype
		 * @returns {boolean} true if the property is a subtype
		 * property (can be either nested object or a reference).
		 */
		propDesc.isSubtype = function() {

			return this._subtype;
		};

		/**
		 * For a polymorphic nested object property, name of the property used as
		 * the concrete type discriminator. The same is made available on the
		 * subtype pseudo-properties (those that have isSubtype()
		 * return true).
		 *
		 * @member {string} module:x2node-records~PropertyDescriptor#typePropertyName
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'typePropertyName', {
			get() { return this._typePropertyName; }
		});

		/**
		 * Tell if the property is a reference (scalarValueType is
		 * "ref").
		 *
		 * @function module:x2node-records~PropertyDescriptor#isRef
		 * @returns {boolean} true if the property is a reference.
		 */
		propDesc.isRef =
			function() { return (this._scalarValueType === 'ref'); };

		/**
		 * Name of the target record type for a reference property.
		 *
		 * @member {string} module:x2node-records~PropertyDescriptor#refTarget
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'refTarget', {
			get() { return this._refTarget; }
		});

		/**
		 * For a nested object property (scalarValueType is
		 * "object"), the descriptors of the nested properties. For a reference
		 * property (scalarValueType is "ref"), the target record
		 * type descriptor. For a non-object, non-reference property, if it is
		 * not scalar the container describes a single property named "value"
		 * with the scalar value type. If it is scalar, the
		 * nestedProperties value is null
		 *
		 * @member {?module:x2node-records~PropertiesContainer} module:x2node-records~PropertyDescriptor#nestedProperties
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'nestedProperties', {
			get() { return this._nestedProperties; }
		});

		/**
		 * For a polymorphic nested object property, the list of subtype names.
		 * For a polymorphic reference property, the list of possible referred
		 * record type names.
		 *
		 * @member {string[]} module:x2node-records~PropertyDescriptor#subtypes
		 * @readonly
		 */
		Object.defineProperty(propDesc, 'subtypes', {
			get() { return this._subtypes; }
		});

		/**
		 * Create new nested object instance for a nested object property.
		 *
		 * @function module:x2node-records~PropertyDescriptor#newObject
		 * @returns {Object} New nested object instance.
		 */
		propDesc.newObject = function() {
			return this._factory.call(this);
		};

		// return the descriptor
		return propDesc;
	}
}

// export the class
module.exports = CoreExtention;
<|endoftext|>


<commit_before>const libob = require('libobject');

exports.module = (loader, subject, predicate, object) => {
    console.log('Load module:', object);
    //loader.scope.mod(object, (err, module) => {
    //    console.log(err, module);
    //});
};

exports.instance = (loader, subject, predicate, object) => {
    console.log('Create and load instance:', object);
    // check if instance is cached
    // load instance
};

exports.event = (loader, subject, predicate, object) => {
    //console.log('Create event', object, '\n');
};

exports.emit = (loader, subject, predicate, object) => {
    let instance = object;
    let event = object;
    loader.scope.flow(object);
};

exports.sequence = (loader, subject, predicate, object) => {
    //console.log('Create new sequence', object, '\n');
};

exports.dataHandler = (loader, subject, predicate, object) => {
    //console.log('Create handler and add to seqence', object, '\n');
    // check if seqence exists
    // 0 -> wait for sequence
    // 1 -> check if handler exists
    //      0 -> wait for handler
    //      1 -> check if instance exists
    //           0 -> wait and try again on instance ready
    //           1 -> get method ref and pass it to handler
    //console.log('get method ref', object, '\n');
};

exports.streamHandler = (loader, subject, predicate, object) => {
    console.log('Create handler and add to seqence', object, '\n');
};

exports.eventHandler = (loader, subject, predicate, object) => {
    // check if sequence exists
    // 0 -> wait for sequence
    // 1 -> check if handler exists
    //      0 -> wait for handler
    //      1 -> emit flow event and pass it to handler
    console.log('Get event stream and pipe to sequence', object, '\n');
};

exports.object = (loader, subject, predicate, object) => {
   // console.log('Build object', object, 'of type', predicate, 'attach to', subject, '\n');
};
        /*if (err || (!composition || !composition.module) || !roleAccess(composition, options.session)) {
            return loader.emit('error', err || new Error('Flow.load: No access or invalid composition'));
        }

        // emit flow events on load
        if (composition.load) {
            composition.load.forEach((event) => {
                scope.flow(event).end(options);
            });
        }

        // get cached module
        if (scope.modules[composition.module]) {
            build(scope, loader, composition, scope.modules[composition.module]);
            return;
        }

        loader.once('module', (module) => {
            scope.modules[composition.module] = module;
            build(scope, loader, composition, module);
        });

        if (scope.modules[composition.module] === null) {
            return;
        }

        scope.modules[composition.module] = null;
        scope.mod(composition.module, options, (err, module) => {

            if (err) {
                return loader.emit('error', err);
            }

            loader.emit('module', module);
        });
    });
}

function roleAccess (item, role) {

    var roles = item && (item._roles || item.roles || {}) || {};
    role = role && role.role ? role.role : role;

    if (roles['*'] || (typeof role === 'string' && roles[role])) {
        return true;
    }
}

function build (scope, loader, config, module) {

    // create new flow emitter
    var instance = Object.create(module);
    instance.flow = scope.flow.bind(null, config.name);
    instance._name = config.name;
    instance._reset = scope.reset;
    instance._config = config.config || {};

    // extend loader
    loader.module = config.module;
    loader.name = config.name;
    loader.roles = config.roles || {};
    loader.flow = config.flow || {};
    loader.parsed = {};

    var ready = function (err, data) {

        if (err) {
            return loader.emit('error', err);
        }

        // mark instance as ready
        loader.inst = instance;
        loader.ready = true;
        loader.emit('ready', loader);
    };

    // init loader instance
    instance.init ? instance.init(config.config || {}, ready) : ready();
}*/

function parse (scope, event, event_id, options, instance) {

    var sequence = scope.events[event_id];

    if (!instance.flow || !instance.flow[event]) {
        return sequence.emit('error', new Error('Flow.parse: Event "' + event + '" not found on instance "' + instance.name + '".'));
    }

    if (!instance.flow[event].d) {
        return sequence.emit('error', new Error('Flow.parse: No data handlers.'));
    }

    var parsedEvent = parseEvent(scope, instance, instance.flow[event].d);
    var count = 0;
    var check = function () {
        if (++count === parsedEvent.l.length) {
            parseSequence(scope, sequence, parsedEvent.s);
        }
    };

    // set instance ref
    sequence.i = instance;

    // set end and error events
    sequence.e = instance.flow[event].e;
    sequence.r = instance.flow[event].r;

    if (parsedEvent.l.length > 0) {
        parsedEvent.l.forEach(function (instName) {
            Load(scope, sequence, instName, options, check);
        });

    } else {
        parseSequence(scope, sequence, parsedEvent.s);
    }
}

function parseEvent (scope, instance, config) {

    var instances = {};
    var sequences = [];
    var pointer = 0;

    config.forEach(function (handler) {

        // resolve handler
        var path = handler;
        var args = {};

        if (handler.constructor === Array) {
            path = handler[0];
            args = handler[1] || {};
        }

        let type = path.substr(0, 1);
        path = path.substr(1);

        // parse path
        let instance_name = instance.name;
        if (path.indexOf('/') > 0) {
            path = path.split('/', 2);
            instance_name = path[0];
            path = path[1];
        }

        if (!scope.instances[instance_name] || !scope.instances[instance_name].ready) {
            instances[instance_name] = 1;
        }

        switch (type) {

            // data handlers
            case ':':
            case '.':

                // create new section if current is a even/stream handler
                if (sequences[pointer] && sequences[pointer][0] > 0) {
                    ++pointer;
                }

                sequences[pointer] = sequences[pointer] || [0, []];
                sequences[pointer][1].push([instance_name, path, args, (type === '.')]);
                return;

            // stream handlers
            case '>':
            case '*':

                sequences.push([(type === '*' ? 1 : 2), [instance_name, path, args]]);

                ++pointer;
                return;
        }

        sequence.emit('error', new Error('Flow.parse: Invalid handler type:', type));
    });

    return {
        l: Object.keys(instances),
        s: sequences
    };
}

// update sequences with method refs
function parseSequence (scope, event, sequences) {

    var getMethodRef = function (handler) {
        handler[0] = scope.instances[handler[0]].inst;

        if (!(handler[1] = libob.path(handler[1], handler[0]))) {
            return event.emit('error', new Error('Flow.parse: Method on instance "' + handler[0]._name + '" not found.'));
        }
    };

    for (var i = 0, l = sequences.length, sequence; i < l; ++i) {
        sequence = sequences[i];

        switch (sequence[0]) {
            case 0:
                sequence[1].forEach(getMethodRef);
                break;
            case 1:
                getMethodRef(sequence[1]);
                break;
            case 2:
                sequence[1][0] = scope.instances[sequence[1][0]].inst;
        }
    }

    event.s = sequences;
    event.ready = true;
    event.emit('ready', event);
}
<commit_msg>wokring on module loading<commit_after>const libob = require('libobject');

exports.module = (loader, subject, predicate, object) => {

    console.log('Load module:', object);

    let scope = loader.scope;

    // get cached module
    let cached_module = scope.cache.get('m:' + object);
    if (cached_module) {
        console.log('Module cached!');
        loader.emit('module', cached_module);
        //build(loader, scope.modules[composition.module]);
        return;
    }

    if (cached_module === null) {
        console.log('Module is loading!', object);
        return;
    }

    scope.cache.set('m:' + object, null);
    scope.mod(object, loader.args, (err, module) => {

        if (err) {
            return loader.emit('error', err);
        }

        loader.emit('module', module);
    });
    //loader.once('module', (module) => {
    //  loader.scope.cache() = module;
    //  build(loader.scope, loader, composition, module);
    //});

    //if (scope.modules[composition.module] === null) {
    //  return;
    //}
};

exports.instance = (loader, subject, predicate, object) => {
    console.log('Create and load instance:', object);
    // check if instance is cached
    // load instance
};

exports.event = (loader, subject, predicate, object) => {
    //console.log('Create event', object, '\n');
};

exports.emit = (loader, subject, predicate, object) => {
    let instance = object;
    let event = object;
    loader.scope.flow(object);
};

exports.sequence = (loader, subject, predicate, object) => {
    //console.log('Create new sequence', object, '\n');
};

exports.dataHandler = (loader, subject, predicate, object) => {
    //console.log('Create handler and add to seqence', object, '\n');
    // check if seqence exists
    // 0 -> wait for sequence
    // 1 -> check if handler exists
    //      0 -> wait for handler
    //      1 -> check if instance exists
    //           0 -> wait and try again on instance ready
    //           1 -> get method ref and pass it to handler
    //console.log('get method ref', object, '\n');
};

exports.streamHandler = (loader, subject, predicate, object) => {
    console.log('Create handler and add to seqence', object, '\n');
};

exports.eventHandler = (loader, subject, predicate, object) => {
    // check if sequence exists
    // 0 -> wait for sequence
    // 1 -> check if handler exists
    //      0 -> wait for handler
    //      1 -> emit flow event and pass it to handler
    console.log('Get event stream and pipe to sequence', object, '\n');
};

exports.object = (loader, subject, predicate, object) => {
   // console.log('Build object', object, 'of type', predicate, 'attach to', subject, '\n');
};
        /*if (err || (!composition || !composition.module) || !roleAccess(composition, options.session)) {
            return loader.emit('error', err || new Error('Flow.load: No access or invalid composition'));
        }

        // emit flow events on load
        if (composition.load) {
            composition.load.forEach((event) => {
                scope.flow(event).end(options);
            });
        }
    });
}

function roleAccess (item, role) {

    var roles = item && (item._roles || item.roles || {}) || {};
    role = role && role.role ? role.role : role;

    if (roles['*'] || (typeof role === 'string' && roles[role])) {
        return true;
    }
}

function build (scope, loader, config, module) {

    // create new flow emitter
    var instance = Object.create(module);
    instance.flow = scope.flow.bind(null, config.name);
    instance._name = config.name;
    instance._reset = scope.reset;
    instance._config = config.config || {};

    // extend loader
    loader.module = config.module;
    loader.name = config.name;
    loader.roles = config.roles || {};
    loader.flow = config.flow || {};
    loader.parsed = {};

    var ready = function (err, data) {

        if (err) {
            return loader.emit('error', err);
        }

        // mark instance as ready
        loader.inst = instance;
        loader.ready = true;
        loader.emit('ready', loader);
    };

    // init loader instance
    instance.init ? instance.init(config.config || {}, ready) : ready();
}*/

function parse (scope, event, event_id, options, instance) {

    var sequence = scope.events[event_id];

    if (!instance.flow || !instance.flow[event]) {
        return sequence.emit('error', new Error('Flow.parse: Event "' + event + '" not found on instance "' + instance.name + '".'));
    }

    if (!instance.flow[event].d) {
        return sequence.emit('error', new Error('Flow.parse: No data handlers.'));
    }

    var parsedEvent = parseEvent(scope, instance, instance.flow[event].d);
    var count = 0;
    var check = function () {
        if (++count === parsedEvent.l.length) {
            parseSequence(scope, sequence, parsedEvent.s);
        }
    };

    // set instance ref
    sequence.i = instance;

    // set end and error events
    sequence.e = instance.flow[event].e;
    sequence.r = instance.flow[event].r;

    if (parsedEvent.l.length > 0) {
        parsedEvent.l.forEach(function (instName) {
            Load(scope, sequence, instName, options, check);
        });

    } else {
        parseSequence(scope, sequence, parsedEvent.s);
    }
}

function parseEvent (scope, instance, config) {

    var instances = {};
    var sequences = [];
    var pointer = 0;

    config.forEach(function (handler) {

        // resolve handler
        var path = handler;
        var args = {};

        if (handler.constructor === Array) {
            path = handler[0];
            args = handler[1] || {};
        }

        let type = path.substr(0, 1);
        path = path.substr(1);

        // parse path
        let instance_name = instance.name;
        if (path.indexOf('/') > 0) {
            path = path.split('/', 2);
            instance_name = path[0];
            path = path[1];
        }

        if (!scope.instances[instance_name] || !scope.instances[instance_name].ready) {
            instances[instance_name] = 1;
        }

        switch (type) {

            // data handlers
            case ':':
            case '.':

                // create new section if current is a even/stream handler
                if (sequences[pointer] && sequences[pointer][0] > 0) {
                    ++pointer;
                }

                sequences[pointer] = sequences[pointer] || [0, []];
                sequences[pointer][1].push([instance_name, path, args, (type === '.')]);
                return;

            // stream handlers
            case '>':
            case '*':

                sequences.push([(type === '*' ? 1 : 2), [instance_name, path, args]]);

                ++pointer;
                return;
        }

        sequence.emit('error', new Error('Flow.parse: Invalid handler type:', type));
    });

    return {
        l: Object.keys(instances),
        s: sequences
    };
}

// update sequences with method refs
function parseSequence (scope, event, sequences) {

    var getMethodRef = function (handler) {
        handler[0] = scope.instances[handler[0]].inst;

        if (!(handler[1] = libob.path(handler[1], handler[0]))) {
            return event.emit('error', new Error('Flow.parse: Method on instance "' + handler[0]._name + '" not found.'));
        }
    };

    for (var i = 0, l = sequences.length, sequence; i < l; ++i) {
        sequence = sequences[i];

        switch (sequence[0]) {
            case 0:
                sequence[1].forEach(getMethodRef);
                break;
            case 1:
                getMethodRef(sequence[1]);
                break;
            case 2:
                sequence[1][0] = scope.instances[sequence[1][0]].inst;
        }
    }

    event.s = sequences;
    event.ready = true;
    event.emit('ready', event);
}
<|endoftext|>


<commit_before>var express = require('express'),
    app = express(),
    fs = require('fs'),
    join = require('path').join,
    cheerio = require('cheerio'),
    concat = require('concat-stream'),
    notfound = require('./middleware/404'),
    reloadFlag = 0;

exports.server = function (dirPath, option, callback) {

    app.configure(function () {
        if(option.log === "true"){
            app.use(express.logger());
        }
        app.use(express.compress());
        app.use(app.router);
        app.use(express.static(__dirname+'/client'));
        app.use(express.static(dirPath));
    });

    app.get('/', function (req, res, next) {
        var filename = option.homepage + ".html",
            path = join(dirPath, filename),
            stream = fs.createReadStream(path, {encoding: 'utf-8'});

        stream.pipe(concat(function (data) {
            var $ = cheerio.load(data),

            // hack for a good fuck!
            injectan = "<script src='svh.js'></script>"
            var sel = $('body').append(injectan);

            if(sel.length === 0){
                $('*').after(injectan);
            }

            res.setHeader('Content-Type', 'text/html');
            res.write($.html());
            res.end();
        }))
    });

    app.get('/reload', function(req, res, next){

        var event = "reload";
        client = {
            "reload" : option.reload,
            "reloadFlag": reloadFlag
        };

        res.writeHead(200, {
            "Content-Type":"text/event-stream",
            "Cache-Control":"no-cache",
            "Connection":"keep-alive"
        });

        var id = (new Date()).toLocaleTimeString();

        res.write("id: " + id + '\n');
        res.write("event: " + event + "\n");
        res.write("data: "+JSON.stringify(client)+"\n\n");
        res.write('retry: 500\n');

        // Reset
        reloadFlag = 0;

        res.end();
    });

    app.use(notfound.index);

    var server = app.listen(option.port, callback);
    return server;
};

exports.setReload = function(data){
    reloadFlag = data;
}
<commit_msg>fix bug from version 0.0.4 - all files serving as text/html<commit_after>var express = require('express'),
    app = express(),
    fs = require('fs'),
    join = require('path').join,
    cheerio = require('cheerio'),
    concat = require('concat-stream'),
    notfound = require('./middleware/404'),
    reloadFlag = 0;

exports.server = function (dirPath, option, callback) {

    app.configure(function () {
        if(option.log === "true"){
            app.use(express.logger());
        }
        app.use(express.compress());
        app.use(app.router);
        app.use(express.static(__dirname+'/client'));
        app.use(express.static(dirPath));
    });

    app.get('/', function (req, res, next) {
        var filename = option.homepage + ".html",
            path = join(dirPath, filename),
            stream = fs.createReadStream(path, {encoding: 'utf-8'});

        stream.pipe(concat(function (data) {
            var $ = cheerio.load(data),

            // hack for a good fuck!
            injectan = "<script src='svh.js'></script>"
            var sel = $('body').append(injectan);

            if(sel.length === 0){
                $('*').after(injectan);
            }

            res.setHeader('Content-Type', 'text/html');
            res.write($.html());
            res.end();
        }))
    });

    app.get('/reload', function(req, res, next){

        var event = "reload";
        client = {
            "reload" : option.reload,
            "reloadFlag": reloadFlag
        };

        res.writeHead(200, {
            "Content-Type":"text/event-stream",
            "Cache-Control":"no-cache",
            "Connection":"keep-alive"
        });

        var id = (new Date()).toLocaleTimeString();

        res.write("id: " + id + '\n');
        res.write("event: " + event + "\n");
        res.write("data: "+JSON.stringify(client)+"\n\n");
        res.write('retry: 500\n');

        // Reset
        reloadFlag = 0;

        res.end();
    });

    app.use(notfound.index);

    var server = app.listen(option.port, callback);
    return server;
};

exports.setReload = function(data){
    reloadFlag = data;
}
<|endoftext|>


<commit_before>module.exports=function(req,res,next){
  res.setHeader("Access-Control-Allow-Origin","*");
  res.setHeader("Access-Control-Allow-Methods","GET");
  res.setHeader("Access-Control-Allow-Headers","X-Requested-With");
  next(req,res);
};
<commit_msg>lib/cors.js : accept opt for configuration<commit_after>module.exports=function(opt){
  opt=opt||{};
  opt.methods=(opt.methods&&opt.methods.length?opt.methods:['GET']).join(',');
  opt.origin=opt.origin||'*';
  opt.headers=opt.headers||'X-Requested-With';

  return function(req,res,next){
    res.setHeader("Access-Control-Allow-Origin",opt.origin);
    res.setHeader("Access-Control-Allow-Methods",opt.methods);
    res.setHeader("Access-Control-Allow-Headers",opt.headers);
    next();
  };
};
<|endoftext|>


<commit_before>'use strict';
const request = require('request');
const CronJob = require('cron').CronJob;
const twilio = require('twilio')('ACb3b6f265ae38c1c167b39b431d210785', 'ece3df3dcd39784d4114096c87554fcc');
let localUrl = 'http://localhost:3000/api/league/';
let herokuUrl = 'https://fake-sports-real-money.herokuapp.com/api/league/';

let overdueCheck = new CronJob('00 * 15 * * *', function() {
  request(herokuUrl, (err, response, html) => {
    let league = JSON.parse(response.body);
    league.forEach((league) => {
      request(herokuUrl + league._id + '/user/overdue', (err,   response, html) => {
        let overdueMembers = JSON.parse(response.body);
        overdueMembers.forEach((member) => {
          twilio.messages.create({
            from: '+13185624276',
            to: member.contact.phone,
            body: 'You are overdue by heroku' + member.name
          }, function(err, data) {
            if (err) {
              console.log(err);
            }
            console.log('sent');
          });
        });
      });
    });
  });
}, null, true, 'America/Los_Angeles');

module.exports = exports = overdueCheck;
<commit_msg>changed cron timer<commit_after>'use strict';
const request = require('request');
const CronJob = require('cron').CronJob;
const twilio = require('twilio')('ACb3b6f265ae38c1c167b39b431d210785', 'ece3df3dcd39784d4114096c87554fcc');
let localUrl = 'http://localhost:3000/api/league/';
let herokuUrl = 'https://fake-sports-real-money.herokuapp.com/api/league/';

let overdueCheck = new CronJob('00 30 15 * * *', function() {
  request(herokuUrl, (err, response, html) => {
    let league = JSON.parse(response.body);
    league.forEach((league) => {
      request(herokuUrl + league._id + '/user/overdue', (err,   response, html) => {
        let overdueMembers = JSON.parse(response.body);
        overdueMembers.forEach((member) => {
          twilio.messages.create({
            from: '+13185624276',
            to: member.contact.phone,
            body: 'You are overdue by heroku' + member.name
          }, function(err, data) {
            if (err) {
              console.log(err);
            }
            console.log('sent' + data);
          });
        });
      });
    });
  });
}, null, true, 'America/Los_Angeles');

module.exports = exports = overdueCheck;
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var Emitter = require('component-emitter');
var stringify = require('./stringify');

/**
 * Expose `Deku`.
 */

module.exports = Deku;

/**
 * Create a new `Deku`.
 */

function Deku() {
  if (!(this instanceof Deku)) return new Deku;
  this.options = {};
  this.rootIndex = 0;
  this.store = {};
  this.connections = {};
}

/**
 * Mixin `Emitter`.
 */

Emitter(Deku.prototype);

/**
 * Add a plugin
 *
 * @param {Function} plugin
 */

Deku.prototype.use = function(plugin){
  plugin(this);
  return this;
};

/**
 * Send message to the world.
 *
 * @param {String} type
 * @param {Object} data
 */

Deku.prototype.update = function(type, data){
  this.emit(type, data);
};

/**
 * Register data source name.
 *
 * @param {String} name
 */

Deku.prototype.source = function(name, fn){
  this.store[name] = val;
  return this;
};

/**
 * Set an option
 *
 * @param {String} name
 */

Deku.prototype.set = function(name, val){
  this.options[name] = val;
  return this;
};

/**
 * Set value used somewhere in the IO network.
 */

Deku.prototype.value = function(name, data){
  if (this.store[name] === data) return;
  this.store[name] = data; // update global store.
  this.connections[name](data);
};

/**
 * Mount a virtual element.
 *
 * @param {VirtualElement} virtualElement
 */

Deku.prototype.mount = function(virtualElement){
  this.el = virtualElement;
  this.emit('mount', virtualElement);
};

/**
 * Remove the world. Unmount everything.
 */

Deku.prototype.unmount = function(){
  this.el = null;
  this.emit('unmount');
};

/**
 * Render the current element to a string
 *
 * @param {Object} options The rendering options
 */

Deku.prototype.renderString = function(options){
  if (!this.el) return mountError();
  return stringify(this, options);
};

/**
 * Render the current element to the DOM.
 *
 * @param {HTMLElement} container Append to this element
 * @param {Object} options The rendering options
 */

Deku.prototype.render = function(container, options){
  if (!this.el) return mountError();
  if (!render) return DOMError();
  return render(this, container, options);
};

/**
 * Throw an error for a missing mount
 */

function mountError() {
  throw new Error('There is no mounted element. Mount an element first, then call this function.')
}

/**
 * No available DOM.
 */

function DOMError() {
  throw new Error('This environment does not have DOM support.');
}
<commit_msg>Don't store the node<commit_after>
/**
 * Module dependencies.
 */

var Emitter = require('component-emitter');
var stringify = require('./stringify');

/**
 * Expose `Deku`.
 */

module.exports = Deku;

/**
 * Create a new `Deku`.
 */

function Deku() {
  if (!(this instanceof Deku)) return new Deku;
  this.options = {};
  this.rootIndex = 0;
  this.store = {};
  this.connections = {};
}

/**
 * Mixin `Emitter`.
 */

Emitter(Deku.prototype);

/**
 * Add a plugin
 *
 * @param {Function} plugin
 */

Deku.prototype.use = function(plugin){
  plugin(this);
  return this;
};

/**
 * Send message to the world.
 *
 * @param {String} type
 * @param {Object} data
 */

Deku.prototype.update = function(type, data){
  this.emit(type, data);
};

/**
 * Register data source name.
 *
 * @param {String} name
 */

Deku.prototype.source = function(name, fn){
  this.store[name] = val;
  return this;
};

/**
 * Set an option
 *
 * @param {String} name
 */

Deku.prototype.set = function(name, val){
  this.options[name] = val;
  return this;
};

/**
 * Set value used somewhere in the IO network.
 */

Deku.prototype.value = function(name, data){
  if (this.store[name] === data) return;
  this.store[name] = data; // update global store.
  this.connections[name](data);
};

/**
 * Mount a virtual element.
 *
 * @param {VirtualElement} virtualElement
 */

Deku.prototype.mount = function(virtualElement){
  this.emit('mount', virtualElement);
};

/**
 * Remove the world. Unmount everything.
 */

Deku.prototype.unmount = function(){
  this.emit('unmount');
};

/**
 * Render the current element to a string
 *
 * @param {Object} options The rendering options
 */

Deku.prototype.renderString = function(options){
  if (!this.el) return mountError();
  return stringify(this, options);
};

/**
 * Render the current element to the DOM.
 *
 * @param {HTMLElement} container Append to this element
 * @param {Object} options The rendering options
 */

Deku.prototype.render = function(container, options){
  if (!this.el) return mountError();
  if (!render) return DOMError();
  return render(this, container, options);
};

/**
 * Throw an error for a missing mount
 */

function mountError() {
  throw new Error('There is no mounted element. Mount an element first, then call this function.')
}

/**
 * No available DOM.
 */

function DOMError() {
  throw new Error('This environment does not have DOM support.');
}
<|endoftext|>


<commit_before>'use strict'

const Topo = require('toposort-class')

exports.order = function order (rules) {
  const deps = dependencies(rules)
  const nodeps = Object.keys(rules).filter((field) => !deps[field])
  const order = new Topo()

  Object
    .keys(deps)
    .forEach((field) => order.add(field, deps[field]))

  return order.sort().reverse().concat(nodeps)
}

exports.build = dependencies
exports.inverse = inverse

function inverse (dependencies) {
  return Object
    .keys(dependencies)
    .reduce((inversed, field) => {
      const fieldDeps = dependencies[field]

      return fieldDeps
        .reduce((inversed, dependency) => {
          inversed[dependency] = inversed[dependency]
            ? inversed[dependency].concat(field)
            : [field]

          return inversed
        }, inversed)
    }, {})
}

function dependencies (rules) {
  return Object
    .keys(rules)
    .reduce((dependencies, field) => {
      const validators = rules[field]

      validators.forEach((validator) => {
        if (!validator.dependsOn) return

        dependencies[field] = (dependencies[field] || [])
          .concat(validator.dependsOn)

        if (dependencies[validator.dependsOn] &&
            dependencies[validator.dependsOn].indexOf(field) !== -1) {
          throw new Error('Circular dependencies between validators found')
        }
      })

      return dependencies
    }, {})
}
<commit_msg>fix babel/browserify syntaxerror bug<commit_after>'use strict'

const Topo = require('toposort-class')

exports.order = function (rules) {
  const deps = dependencies(rules)
  const nodeps = Object.keys(rules).filter((field) => !deps[field])
  const order = new Topo()

  Object
    .keys(deps)
    .forEach((field) => order.add(field, deps[field]))

  return order.sort().reverse().concat(nodeps)
}

exports.build = dependencies
exports.inverse = inverse

function inverse (dependencies) {
  return Object
    .keys(dependencies)
    .reduce((inversed, field) => {
      const fieldDeps = dependencies[field]

      return fieldDeps
        .reduce((inversed, dependency) => {
          inversed[dependency] = inversed[dependency]
            ? inversed[dependency].concat(field)
            : [field]

          return inversed
        }, inversed)
    }, {})
}

function dependencies (rules) {
  return Object
    .keys(rules)
    .reduce((dependencies, field) => {
      const validators = rules[field]

      validators.forEach((validator) => {
        if (!validator.dependsOn) return

        dependencies[field] = (dependencies[field] || [])
          .concat(validator.dependsOn)

        if (dependencies[validator.dependsOn] &&
            dependencies[validator.dependsOn].indexOf(field) !== -1) {
          throw new Error('Circular dependencies between validators found')
        }
      })

      return dependencies
    }, {})
}
<|endoftext|>


<commit_before>var util = require('util');
var _ = require('lodash');

var h = require('highland');
var elasticsearch = require('elasticsearch');

module.exports = function dest (glob, _opt) {
  glob = glob || {};

  /**
   * Clone the options because ElasticSearch overwrites them:
   */

  var opt = _.clone(_opt || {});

  /**
   * If the caller is hoping to use AWS ElasticSearch rather than a
   * direct instance then insert the signing module:
   */

  if (opt.amazonES) {
    opt.connectionClass = require('http-aws-es');
  }

  /**
   * Return a pipeline for use in non-Highland situations:
   */

  return h.pipeline(function(stream) {
    var _stream = (opt.rateLimit) ? stream.ratelimit(1, opt.rateLimit) : stream;
    var client = new elasticsearch.Client(opt);

    return _stream
      /**
       * Ensure there is some data to index:
       */

      .filter(function(file) {
        return file.data || file.contents;
      })

      /**
       * Change the layout and use default values where necessary:
       */

      .map(function(file) {
        return {
          index: glob.index || file.index || opt.index,
          type: glob.type || (file.data && file.data.type) ||
            opt.type || file.base,
          id: glob.id || file.id || file.path || opt.id,
          body: file.data || JSON.parse(file.contents.toString())
        };
      })

      /**
       * Now push each new object to ES:
       */

      .map(function(file) {
        client.index(file, function(error, response) {
          if (error) {
            throw new Error(util.format('Error writing to ES: %s %s %s', file,
              error, response));
          }
        });
        return file;
      });
  });
};
<commit_msg>ensure elasticsearch client options are cloned<commit_after>var util = require('util');
var _ = require('lodash');

var h = require('highland');
var elasticsearch = require('elasticsearch');

module.exports = function dest (glob, _opt) {
  glob = glob || {};

  /**
   * Clone the options because ElasticSearch overwrites them:
   */

  var opt = _.clone(_opt || {});

  /**
   * If the caller is hoping to use AWS ElasticSearch rather than a
   * direct instance then insert the signing module:
   */

  if (opt.amazonES) {
    opt.connectionClass = require('http-aws-es');
  }

  /**
   * Return a pipeline for use in non-Highland situations:
   */

  return h.pipeline(function(stream) {
    var _stream = (opt.rateLimit) ? stream.ratelimit(1, opt.rateLimit) : stream;
    var client = new elasticsearch.Client(_.clone(opt));

    return _stream
      /**
       * Ensure there is some data to index:
       */

      .filter(function(file) {
        return file.data || file.contents;
      })

      /**
       * Change the layout and use default values where necessary:
       */

      .map(function(file) {
        return {
          index: glob.index || file.index || opt.index,
          type: glob.type || (file.data && file.data.type) ||
            opt.type || file.base,
          id: glob.id || file.id || file.path || opt.id,
          body: file.data || JSON.parse(file.contents.toString())
        };
      })

      /**
       * Now push each new object to ES:
       */

      .map(function(file) {
        client.index(file, function(error, response) {
          if (error) {
            throw new Error(util.format('Error writing to ES: %s %s %s', file,
              error, response));
          }
        });
        return file;
      });
  });
};
<|endoftext|>


<commit_before>'use strict';

var Git = require('nodegit');
var debug = require('debug')('kit:diff');

function getHunks(hunk){
    return hunk.lines()
    .then(function(lines){
        return lines.map(function(line, i){
            var change = ' ';

            if (line.newLineno() == -1) change = '-';
            else if (line.oldLineno() == -1) change = '+';

            return change + line.content().trim();
        });
    })
    .then(function(diff){
        var result = [hunk.header().trim()];
        return result.concat(diff).join('\n');
    });
}

// { oldFile: [Function],
// newFile: [Function],
// size: [Function],
// hunks: [Function],
// status: [Function],
// isUnmodified: [Function],
// isAdded: [Function],
// isDeleted: [Function],
// isModified: [Function],
// isRenamed: [Function],
// isCopied: [Function],
// isIgnored: [Function],
// isUntracked: [Function],
// isTypeChange: [Function] }

function getInfo(patch){
    return new Promise(function(resolve, reject){
        var status;
        if (patch.isUnmodified()) status = 'unmodified';
        else if (patch.isAdded()) status = 'added';
        else if (patch.isDeleted()) status = 'deleted';
        else if (patch.isModified()) status = 'modified';
        else if (patch.isRenamed()) status = 'renamed';
        else if (patch.isCopied()) status = 'copied';
        else if (patch.isIgnored()) status = 'ignored';
        else if (patch.isUntracked()) status = 'untracked';
        else if (patch.isTypeChange()) status = 'typechange';

        resolve(status);
    })
    .then(function(status){
        var file = patch.newFile();

        return {
            'status': status,
            'path': file.path(),
            'size': file.size()
        };
    })
    .then(function(entry){
        debug('%s %s', entry.status, entry.path);
        if (entry.status != 'modified'){
            return entry;
        }

        var oldfile = patch.oldFile();
        if (entry.path != oldfile.path()){
            entry.oldpath = oldfile.path();
        }
        entry.oldsize = oldfile.size();

        return patch.hunks()
        .then(function(hunks){
            return Promise.all(hunks.map(getHunks));
        })
        .then(function(hunks){
            entry.hunks = hunks;
            return entry;
        });
    });
}

module.exports = function(repo){
    return repo.getBranchCommit('master')
    .then(function(commit){
        return commit.getTree();
    })
    .then(function(tree){
        return repo.openIndex();
    })
    .then(function(index){
        return Git.Diff.indexToWorkdir(repo, index, {
            'flags': Git.Diff.OPTION.INCLUDE_UNTRACKED |
                Git.Diff.OPTION.INCLUDE_UNREADABLE |
                Git.Diff.OPTION.SKIP_BINARY_CHECK
        });
    })
    .then(function(diff){
        return diff.patches();
    })
    .then(function(patches){
        if (!patches.length){
            return null;
        }
        return Promise.all(patches.map(getInfo));
    });

};
<commit_msg>cleanup<commit_after>'use strict';

var Git = require('nodegit');
var debug = require('debug')('kit:diff');

function getHunks(hunk){
    return hunk.lines()
    .then(function(lines){
        return lines.map(function(line, i){
            var change = ' ';

            if (line.newLineno() == -1) change = '-';
            else if (line.oldLineno() == -1) change = '+';

            return change + line.content().trim();
        });
    })
    .then(function(diff){
        var result = [hunk.header().trim()];
        return result.concat(diff).join('\n');
    });
}

// { oldFile: [Function],
// newFile: [Function],
// size: [Function],
// hunks: [Function],
// status: [Function],
// isUnmodified: [Function],
// isAdded: [Function],
// isDeleted: [Function],
// isModified: [Function],
// isRenamed: [Function],
// isCopied: [Function],
// isIgnored: [Function],
// isUntracked: [Function],
// isTypeChange: [Function] }

function getInfo(patch){
    return new Promise(function(resolve, reject){
        var status;
        if (patch.isUnmodified()) status = 'unmodified';
        else if (patch.isAdded()) status = 'added';
        else if (patch.isDeleted()) status = 'deleted';
        else if (patch.isModified()) status = 'modified';
        else if (patch.isRenamed()) status = 'renamed';
        else if (patch.isCopied()) status = 'copied';
        else if (patch.isIgnored()) status = 'ignored';
        else if (patch.isUntracked()) status = 'untracked';
        else if (patch.isTypeChange()) status = 'typechange';
        resolve(status);
    })
    .then(function(status){
        var file = patch.newFile();
        return {
            'status': status,
            'path': file.path(),
            'size': file.size()
        };
    })
    .then(function(entry){
        debug('%s %s', entry.status, entry.path);
        if (entry.status != 'modified'){
            return entry;
        }

        var oldfile = patch.oldFile();
        if (entry.path != oldfile.path()){
            entry.oldpath = oldfile.path();
        }
        entry.oldsize = oldfile.size();
        return entry;
    })
    .then(function(entry){
        return patch.hunks()
        .then(function(hunks){
            return Promise.all(hunks.map(getHunks));
        })
        .then(function(hunks){
            entry.hunks = hunks;
            return entry;
        });
    });
}

module.exports = function(repo){
    return repo.getBranchCommit('master')
    .then(function(commit){
        return commit.getTree();
    })
    .then(function(tree){
        return repo.openIndex();
    })
    .then(function(index){
        return Git.Diff.indexToWorkdir(repo, index, {
            'flags': Git.Diff.OPTION.INCLUDE_UNTRACKED |
                Git.Diff.OPTION.INCLUDE_UNREADABLE |
                Git.Diff.OPTION.SKIP_BINARY_CHECK
        });
    })
    .then(function(diff){
        return diff.patches();
    })
    .then(function(patches){
        if (!patches.length){
            return null;
        }
        return Promise.all(patches.map(getInfo));
    });
};
<|endoftext|>


<commit_before>'use strict';

var chalk = require('chalk');
var moment = require('moment');
var open = require('open');
var editor = process.env.EDITOR;
var spawn = require('child_process').spawn;
var path = require('path');
var inquirer = require('inquirer');

module.exports = function(args) {

  var title = args._.join(' ').replace(/ /g, '-') || '';
  var folder = args.f || args.folder || '';
  var draft = args.d || args.draft || '';
  var tag = args.t || args.tag || '';
  var cat = args.c || args.category || '';
  var before = args.b || args.before || '';
  before = before.replace(/\//g, '-');
  var after = args.a || args.after || '';
  after = after.replace(/\//g, '-');

  var gui = args.g || args.gui || !editor;

  // load in the posts before processing them
  this.load().then(function() {
    var sourceDir = this.source_dir;
    var searchDir = sourceDir;
    var editor = process.env.EDITOR;

    var selected;
    var entries;

    Promise.resolve(this.locals.get('posts').sort('-date').toArray()).then(function(posts) {
      var filtered = posts.slice();

      // allow omission of leading underscore or trailing s for the common _drafts and _posts folders
      if (/post|draft/.test(folder)) {
        folder = (/^_/.test(folder)) ? folder : '_' + folder;
        folder = (/s$/.test(folder)) ? folder : folder + 's';
      }

      filtered = (draft) ? filterDrafts(filtered) : filtered;

      filtered = (folder) ? filterFolder(filtered) : filtered;

      filtered = (title) ? filterTitle(filtered) : filtered;

      filtered = (tag) ? filterTag(filtered) : filtered;

      filtered = (cat) ? filterCategory(filtered) : filtered;

      filtered = (before) ? filterBefore(filtered) : filtered;

      filtered = (after) ? filterAfter(filtered) : filtered;

      if (filtered.length == 0) {
        console.log('Sorry, no filtered matched your query. Exiting.');
        process.exit();
      } else if (filtered.length == 1) {

        // no menu necessary if there is only one matching file
        selected = path.join(searchDir, filtered[0].source);
        openFile(selected);
      } else {

        // get a list of entries to put in the menu -- slugs are easy because it shows the subfolder and can easily be put back together with the searchDir to open it
        entries = filtered.map(function(post) {
          var entry = '';
          var folder = post.source.substr(0, post.source.lastIndexOf(path.sep));
          if (post.published) {
            entry = ['[', chalk.gray(post.date.format('MM-DD-YYYY')), '] ', post.title, ' (', chalk.green(folder), ')'].join('');
          } else {
            entry = ['[', chalk.yellow.bgBlack('draft'), '] ', post.title].join('');
          }

          return entry;
        });

        inquirer.prompt([
          {
            type: 'list',
            name: 'file',
            message: 'Select the file you wish to edit.',
            choices: entries,
          },
        ], function(answer) {
          var pos = entries.indexOf(answer.file);
          selected = path.join(sourceDir, filtered[pos].source);
          openFile(selected);
        });
      }

    });

    // filter the posts using a subfolder if supplied
    function filterFolder(posts) {
      var reFolder = new RegExp(folder);
      return posts.filter(function(post) {
        return reFolder.test(post.source.substr(0, post.source.lastIndexOf(path.sep)));
      });
    }

    // filter the posts using a tag if supplied
    function filterTag(posts) {
      var reTag = new RegExp(tag);
      return posts.filter(function(post) {
        return post.tags.data.some(function(postTag) {
          return reTag.test(postTag.name);
        });
      });
    }

    // filter the posts using a category if supplied
    function filterCategory(posts) {
      var reCat = new RegExp(cat);
      return posts.filter(function(post) {
        return post.categories.data.some(function(postCat) {
          return reCat.test(postCat.name);
        });
      });
    }

    // filter out all non-published posts
    function filterDrafts(posts) {
      return posts.filter(function(post) {
        return !post.published;
      });
    }

    // filter the posts using a before date if supplied
    // dates don't make much sense for drafts, so they will be excluded
    function filterBefore(posts) {
      before = moment(before, 'MM-DD-YYYY', true);
      if (!before.isValid()) {
        console.log(chalk.red('Before date is not valid (expecting `MM-DD-YYYY`), ignoring argument.'));
        return posts;
      }

      return posts.filter(function(post) {
        return post.published && moment(post.date).isBefore(before);
      });
    }

    // filter the posts using an after date if supplied
    function filterAfter(posts) {
      after = moment(after, 'MM-DD-YYYY', true);
      if (!after.isValid()) {
        console.log(chalk.red('After date is not valid (expecting `MM-DD-YYYY`), ignoring argument.'));
        return posts;
      }

      return posts.filter(function(post) {
        return post.published && moment(post.date).isAfter(after);
      });
    }

    // filter the posts with the supplied regular expression -- uses the slug to avoid capitalization and spacing shenanigans
    function filterTitle(posts) {
      var reTitle = new RegExp(title, 'i');
      return posts.filter(function(post) {
        return reTitle.test(post.slug);
      });
    }

    // spawn process and open with associated gui or terminal editor
    function openFile(file) {
      if (!editor || gui) {
        open(file);
      } else {
        var edit = spawn(editor, [file], {stdio: 'inherit'});
        edit.on('exit', process.exit);
      }
    }
  }.bind(this));
};
<commit_msg>improved title regexing<commit_after>'use strict';

var chalk = require('chalk');
var moment = require('moment');
var open = require('open');
var editor = process.env.EDITOR;
var spawn = require('child_process').spawn;
var path = require('path');
var inquirer = require('inquirer');

module.exports = function(args) {

  var title = args._ || '';

  var folder = args.f || args.folder || '';
  var draft = args.d || args.draft || '';
  var tag = args.t || args.tag || '';
  var cat = args.c || args.category || '';
  var before = args.b || args.before || '';
  before = before.replace(/\//g, '-');
  var after = args.a || args.after || '';
  after = after.replace(/\//g, '-');

  var gui = args.g || args.gui || !editor;

  // load in the posts before processing them
  this.load().then(function() {
    var sourceDir = this.source_dir;
    var searchDir = sourceDir;
    var editor = process.env.EDITOR;

    var selected;
    var entries;

    Promise.resolve(this.locals.get('posts').sort('-date').toArray()).then(function(posts) {
      var filtered = posts.slice();

      // allow omission of leading underscore or trailing s for the common _drafts and _posts folders
      if (/post|draft/.test(folder)) {
        folder = (/^_/.test(folder)) ? folder : '_' + folder;
        folder = (/s$/.test(folder)) ? folder : folder + 's';
      }

      filtered = (draft) ? filterDrafts(filtered) : filtered;

      filtered = (folder) ? filterFolder(filtered) : filtered;

      filtered = (title) ? filterTitle(filtered) : filtered;

      filtered = (tag) ? filterTag(filtered) : filtered;

      filtered = (cat) ? filterCategory(filtered) : filtered;

      filtered = (before) ? filterBefore(filtered) : filtered;

      filtered = (after) ? filterAfter(filtered) : filtered;

      if (filtered.length == 0) {
        console.log('Sorry, no filtered matched your query. Exiting.');
        process.exit();
      } else if (filtered.length == 1) {

        // no menu necessary if there is only one matching file
        selected = path.join(searchDir, filtered[0].source);
        openFile(selected);
      } else {

        // get a list of entries to put in the menu -- slugs are easy because it shows the subfolder and can easily be put back together with the searchDir to open it
        entries = filtered.map(function(post) {
          var entry = '';
          var folder = post.source.substr(0, post.source.lastIndexOf(path.sep));
          if (post.published) {
            entry = ['[', chalk.gray(post.date.format('MM-DD-YYYY')), '] ', post.title, ' (', chalk.green(folder), ')'].join('');
          } else {
            entry = ['[', chalk.yellow.bgBlack('draft'), '] ', post.title].join('');
          }

          return entry;
        });

        inquirer.prompt([
          {
            type: 'list',
            name: 'file',
            message: 'Select the file you wish to edit.',
            choices: entries,
          },
        ], function(answer) {
          var pos = entries.indexOf(answer.file);
          selected = path.join(sourceDir, filtered[pos].source);
          openFile(selected);
        });
      }

    });

    // filter the posts using a subfolder if supplied
    function filterFolder(posts) {
      var reFolder = new RegExp(folder);
      return posts.filter(function(post) {
        return reFolder.test(post.source.substr(0, post.source.lastIndexOf(path.sep)));
      });
    }

    // filter the posts using a tag if supplied
    function filterTag(posts) {
      var reTag = new RegExp(tag);
      return posts.filter(function(post) {
        return post.tags.data.some(function(postTag) {
          return reTag.test(postTag.name);
        });
      });
    }

    // filter the posts using a category if supplied
    function filterCategory(posts) {
      var reCat = new RegExp(cat);
      return posts.filter(function(post) {
        return post.categories.data.some(function(postCat) {
          return reCat.test(postCat.name);
        });
      });
    }

    // filter out all non-published posts
    function filterDrafts(posts) {
      return posts.filter(function(post) {
        return !post.published;
      });
    }

    // filter the posts using a before date if supplied
    // dates don't make much sense for drafts, so they will be excluded
    function filterBefore(posts) {
      before = moment(before, 'MM-DD-YYYY', true);
      if (!before.isValid()) {
        console.log(chalk.red('Before date is not valid (expecting `MM-DD-YYYY`), ignoring argument.'));
        return posts;
      }

      return posts.filter(function(post) {
        return post.published && moment(post.date).isBefore(before);
      });
    }

    // filter the posts using an after date if supplied
    function filterAfter(posts) {
      after = moment(after, 'MM-DD-YYYY', true);
      if (!after.isValid()) {
        console.log(chalk.red('After date is not valid (expecting `MM-DD-YYYY`), ignoring argument.'));
        return posts;
      }

      return posts.filter(function(post) {
        return post.published && moment(post.date).isAfter(after);
      });
    }

    // filter the posts with the supplied regular expression -- uses the slug to avoid capitalization and spacing shenanigans
    function filterTitle(posts) {
      var reTitle = title.map(function(word) {
        return new RegExp(word, 'i');
      });

      return posts.filter(function(post) {
        return reTitle.every(function(regex) {
          return regex.test(post.title);
        });
      });
    }

    // spawn process and open with associated gui or terminal editor
    function openFile(file) {
      if (!editor || gui) {
        open(file);
      } else {
        var edit = spawn(editor, [file], {stdio: 'inherit'});
        edit.on('exit', process.exit);
      }
    }
  }.bind(this));
};
<|endoftext|>