<commit_before>/**
 * Module dependencies
 */

var fields = require('./fields'),
    widgets = require('./widgets'),
    utils = require('./utils'),
    Field = fields.Field;


var Type = exports.Type = function (name, options) {
    if (typeof name !== 'string') {
        throw new Error('First argument must be the type name');
    }
    this.name = name;
    this.permissions = options.permissions || {};
    this.allow_extra_fields = options.allow_extra_fields || false;

    this.fields = options.fields || {};
    // TODO: set hidden widget, default value and validate
    this.fields.type = fields.string({
        default_value: name,
        widget: widgets.hidden(),
        validators: [function (doc, value) {
            if (value !== name) {
                throw new Error('Unexpected value for type');
            }
        }]
    });

};

Type.prototype.validate = function (doc) {
    var required_errors = exports.checkRequired(this.fields, doc, []);
    var validation_errors = exports.validateFields(
        this.fields, doc, doc, [], this.allow_extra_fields
    );
    return required_errors.concat(validation_errors);
};


/**
 * Iterates over fields, checking for an associated value if the field is
 * required.
 *
 * @param {Object} fields
 * @param {Object} values
 * @param {Array} path
 * @return {Array}
 */

exports.checkRequired = function (fields, values, path) {
    var errors = [];

    for (var k in fields) {
        if (fields.hasOwnProperty(k)) {
            var f = fields[k];
            if (f instanceof Field && f.required) {
                if (!values.hasOwnProperty(k)) {
                    var err = new Error('Required field');
                    err.field = path.concat([k]);
                    errors.push(err);
                }
            }
            else if (utils.isArray(f)) {
                if (f[0] instanceof Field && f[0].required) {
                    if (!values.hasOwnProperty(k)) {
                        var err2 = new Error('Required field');
                        err2.field = path.concat([k]);
                        errors.push(err2);
                    }
                }
                else {
                    // recurse through sub-objects
                    var subvals = values.hasOwnProperty(k) ? values[k]: {};
                    errors = errors.concat(exports.checkRequired(
                        f[0], subvals, path.concat([k])
                    ));
                }
            }
            else {
                // recurse through sub-objects in the type's schema to find
                // more fields
                var subvals2 = values.hasOwnProperty(k) ? values[k]: {};
                errors = errors.concat(exports.checkRequired(
                    f, subvals2, path.concat([k])
                ));
            }
        }
    }

    return errors;
};


/**
 * Iterates over values and checks against field validators, recursing through
 * sub-objects. Returns an array of validation errors, or an empty array if
 * valid.
 *
 * @param {Object} fields
 * @param {Object} values
 * @param {Object} doc
 * @param {Array} path
 * @param {Boolean} allow_extra
 * @return {Array}
 */

exports.validateFields = function (fields, values, doc, path, allow_extra) {
    var errors = [];<commit_msg>add _id and _rev fields to types<commit_after>/**
 * Module dependencies
 */

var fields = require('./fields'),
    widgets = require('./widgets'),
    utils = require('./utils'),
    Field = fields.Field;


var Type = exports.Type = function (name, options) {
    if (typeof name !== 'string') {
        throw new Error('First argument must be the type name');
    }
    this.name = name;
    this.permissions = options.permissions || {};
    this.allow_extra_fields = options.allow_extra_fields || false;

    this.fields = {
        _id: fields.string({
            required: false,
            omit_empty: true,
            widget: widgets.hidden()
        }),
        _rev: fields.string({
            required: false,
            omit_empty: true,
            widget: widgets.hidden(),
        }),
        type: fields.string({
            default_value: name,
            widget: widgets.hidden(),
            validators: [function (doc, value) {
                if (value !== name) {
                    throw new Error('Unexpected value for type');
                }
            }]
        })
    };
    if (options.fields) {
        for (var k in options.fields) {
            if (options.fields.hasOwnProperty(k)) {
                this.fields[k] = options.fields[k];
            }
        }
    }
};

Type.prototype.validate = function (doc) {
    var required_errors = exports.checkRequired(this.fields, doc, []);
    var validation_errors = exports.validateFields(
        this.fields, doc, doc, [], this.allow_extra_fields
    );
    return required_errors.concat(validation_errors);
};


/**
 * Iterates over fields, checking for an associated value if the field is
 * required.
 *
 * @param {Object} fields
 * @param {Object} values
 * @param {Array} path
 * @return {Array}
 */

exports.checkRequired = function (fields, values, path) {
    var errors = [];

    for (var k in fields) {
        if (fields.hasOwnProperty(k)) {
            var f = fields[k];
            var f_path = path.concat([k]);
            if (f instanceof Field) {
                if (f.required) {
                    if (!values.hasOwnProperty(k)) {
                        var err = new Error('Required field');
                        err.field = f_path;
                        errors.push(err);
                    }
                }
            }
            else if (utils.isArray(f)) {
                if (f[0] instanceof Field && f[0].required) {
                    if (!values.hasOwnProperty(k)) {
                        var err2 = new Error('Required field');
                        err2.field = f_path;
                        errors.push(err2);
                    }
                }
                else {
                    // recurse through sub-objects
                    var subvals = values.hasOwnProperty(k) ? values[k]: {};
                    errors = errors.concat(exports.checkRequired(
                        f[0], subvals, f_path
                    ));
                }
            }
            else {
                // recurse through sub-objects in the type's schema to find
                // more fields
                var subvals2 = values.hasOwnProperty(k) ? values[k]: {};
                errors = errors.concat(exports.checkRequired(
                    f, subvals2, f_path
                ));
            }
        }
    }

    return errors;
};


/**
 * Iterates over values and checks against field validators, recursing through
 * sub-objects. Returns an array of validation errors, or an empty array if
 * valid.
 *
 * @param {Object} fields
 * @param {Object} values
 * @param {Object} doc
 * @param {Array} path
 * @param {Boolean} allow_extra
 * @return {Array}
 */

exports.validateFields = function (fields, values, doc, path, allow_extra) {
    var errors = [];
{'fraction_non_alphanumeric': 0.119960668633235, 'fraction_numerical': 0.0022475066722854333, 'mean_word_length': 1.6399703374119392, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7284073', 'n_tokens_mistral': 1905, 'n_tokens_neox': 1840, 'n_words': 512}