<commit_before>'use strict';

/**
 * AMNA: Amazing Mongoose Node.js API
 *
 * @author <NAME>
 * @url https://github.com/NateFerrero/amna
 *
 * AMNA - Cache
 *
 * [TEMPORARY] Using mongo to store cached data,
 *             eventually this should be configurable.
 */
var crypto = require('crypto');
var parseDuration = require('parse-duration');

module.exports = function phase_1(amna, log) {

    var AMNACache = amna.thing.mongoose({
        key: {
            type: String,
            unique: true,
            required: true
        },
        value: {
            type: amna.types.Mixed
        }
    });

    AMNACache.register('amna_cache');
    log('registered thing', 'amna_cache');

    var CacheRecord = function (key) {
        this.key = key;
    };

    CacheRecord.prototype.read = function (maxage, done) {
        if (typeof maxage === 'function') {
            done = maxage;
            maxage = Infinity;
        }
        else if (typeof maxage === 'string') {
            maxage = parseDuration(maxage);
        }
        log('<init> read key', this.key);
        AMNACache.model.findOne({key: this.key}, function (err, doc) {
            if (err) {
                return done(err);
            }
            else if (!doc) {
                return done(null, null);
            }
            else {
                var now = new Date();
                if (now - doc.updatedAt > maxage) {
                    log('<timeout> read key', this.key, now - doc.updatedAt, '>', maxage);
                    return done(null, null); // Cache is too old
                }
                log('<success> read key', this.key);
                return done(null, doc);
            }
        }.bind(this));
    };

    CacheRecord.prototype.save = function (value, done) {
        log('<init> save key', this.key);
        var record = {key: this.key, value: value};
        AMNACache.model.findOrCreate({key: this.key}, record, function (err, doc) {
            if (err) {
                log('<fail> save key', this.key, err);
                return done(err);
            }
            doc.value = value;
            doc.markModified('value');
            doc.save(function (err) {
                if (err) {
                    log('<fail> save key', this.key, err);
                    return done(err);
                }
                log('<success> save key', this.key);
                done(null, doc.value);
            }.bind(this));
        }.bind(this));
    };

    /**
     * Usage:
     *
     * amna.cache(some, key, args).read(function (err, record) { ... });
     * amna.cache(some, key, args).save(value, function (err, record) { ... });
     */
    return function (/* cache key arguments */) {
        var hash = crypto.createHash('sha1');
        Array.prototype.forEach.call(arguments, function (arg) {
            /**
             * This is to prevent two different keys hashing to the same value,
             * such as the string "[]" and the array [].
             * This way the first produces '["[]"]' and the second produces '[[]]'.
             */<commit_msg>Add DecayingSetCacheRecord for sets whose values expire<commit_after>'use strict';

/**
 * AMNA: Amazing Mongoose Node.js API
 *
 * @author <NAME>
 * @url https://github.com/NateFerrero/amna
 *
 * AMNA - Cache
 *
 * [TEMPORARY] Using mongo to store cached data,
 *             eventually this should be configurable.
 */
var crypto = require('crypto');
var parseDuration = require('parse-duration');

module.exports = function phase_1(amna, log) {

    var AMNACache = amna.thing.mongoose({
        key: {
            type: String,
            unique: true,
            required: true
        },
        value: {
            type: amna.types.Mixed
        }
    });

    AMNACache.register('amna_cache');
    log('registered thing', 'amna_cache');

    var CacheRecord = function (key) {
        this.key = key;
    };

    CacheRecord.prototype.decayingSet = function () {
        return new DecayingSetCacheRecord(this.key);
    };

    CacheRecord.prototype.read = function (maxage, done) {
        if (typeof maxage === 'function') {
            done = maxage;
            maxage = Infinity;
        }
        else if (typeof maxage === 'string') {
            maxage = parseDuration(maxage);
        }
        log('<init> read key', this.key);
        AMNACache.model.findOne({key: this.key}, function (err, doc) {
            if (err) {
                return done(err);
            }
            else if (!doc) {
                return done(null, null);
            }
            else {
                var now = new Date();
                if (now - doc.updatedAt > maxage) {
                    log('<timeout> read key', this.key, now - doc.updatedAt, '>', maxage);
                    return done(null, null); // Cache is too old
                }
                log('<success> read key', this.key);
                return done(null, doc);
            }
        }.bind(this));
    };

    CacheRecord.prototype.save = function (value, done) {
        log('<init> save key', this.key);
        var record = {key: this.key, value: value};
        AMNACache.model.findOrCreate({key: this.key}, record, function (err, doc) {
            if (err) {
                log('<fail> save key', this.key, err);
                return done(err);
            }
            doc.value = value;
            doc.markModified('value');
            doc.save(function (err) {
                if (err) {
                    log('<fail> save key', this.key, err);
                    return done(err);
                }
                log('<success> save key', this.key);
                done(null, doc.value);
            }.bind(this));
        }.bind(this));
    };

    var DecayingSetCacheRecord = function (key) {
        this.key = key;
    };

    DecayingSetCacheRecord.prototype.read = function (maxage, done) {
        if (typeof maxage === 'function') {
            done = maxage;
            maxage = Infinity;
        }
        else if (typeof maxage === 'string') {
            maxage = parseDuration(maxage);
        }
        log('<init> read decaying set key', this.key);
        AMNACache.model.findOne({key: this.key}, function (err, doc) {
            if (err) {
                return done(err);
            }
            else if (!doc) {
                return done(null, null);
            }
            else {
                if (!Array.isArray(doc.value)) {
                    doc.value = [];
                }
                var now = new Date();
                doc.value = doc.value.filter(function (item) {
                    return now - item.updatedAt <= maxage;
                });
                log('<success> read decaying set key', this.key);
                return done(null, doc);
            }
        }.bind(this));
    };

    DecayingSetCacheRecord.prototype.save = function (value, done) {
        log('<init> save decaying set key', this.key);
        var record = {key: this.key, value: []};
        AMNACache.model.findOrCreate({key: this.key}, record, function (err, doc) {
            if (err) {
                log('<fail> save decaying set key', this.key, err);
                return done(err);
            }

            if (!Array.isArray(doc.value)) {
                doc.value = [];
            }

            var existing = doc.value.filter(function (item) {
                return item.value === value;
            });

            if (existing) {
                existing.updatedAt = new Date();
            }

            else {
                doc.value.push({
                    updatedAt: new Date(),
                    value: value
                });
            }

            doc.markModified('value');
            doc.save(function (err) {
                if (err) {
                    log('<fail> save decaying set key', this.key, err);
                    return done(err);
                }
                log('<success> save decaying set key', this.key);
                done(null, doc.value);
            }.bind(this));
        }.bind(this));
    };

    /**
     * Usage:
     *
     * amna.cache(some, key, args).read(function (err, record) { ... });
     * amna.cache(some, key, args).save(value, function (err, record) { ... });
     */
    return function (/* cache key arguments */) {
        var hash = crypto.createHash('sha1');
        Array.prototype.forEach.call(arguments, function (arg) {
            /**
             * This is to prevent two different keys hashing to the same value,
             * such as the string "[]" and the array [].
             * This way the first produces '["[]"]' and the second produces '[[]]'.
             */
{'fraction_non_alphanumeric': 0.13658760169588632, 'fraction_numerical': 0.00045834765669760516, 'mean_word_length': 1.459977452085682, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 29, 'https://': 2, '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': '2592506', 'n_tokens_mistral': 2471, 'n_tokens_neox': 2359, 'n_words': 618}