onionskin/onionskin

View on GitHub
src/onionskin/item.js

Summary

Maintainability
B
5 hrs
Test Coverage
'use strict';

var Promise = require('bluebird');

function Item(key, pool) {
  this.key = key;
  this.pool = pool;
  this.save = (function (that) {
    var _save = that.save;
    return function () {
      return _save.apply(that, arguments);
    };
  })(this);

  this._unload_();
}

Item.CP_NONE = 1;
Item.CP_OLD = 2;
Item.CP_PRECOMPUTE = 4;
Item.CP_VALUE = 8;

Item.prototype._unload_ = function () {
  this.value = null;
  this.expiration = false;
  this.locked = undefined;
};

Item.prototype._load_ = function () {
  var that = this;

  return new Promise(function (resolve) {
    var promises = that.pool.drivers.map(function (d) {
      return d.get(that.key).then(function (data) {
        if (data !== null) {
          resolve(data);
        }
      });
    });

    Promise.all(promises).then(function () {
      resolve(null);
    });
  });
};

Item.prototype._write_ = function () {
  var that = this;

  return new Promise(function (resolve) {
    that.pool.drivers.reverse().forEach(function (d) {
      d.put(that.key, that.value, that.expiration).then(resolve);
    });
  });
};

Item._calculateExpiration_ = function (expiration) {
  if (typeof(expiration) === 'number') {
    expiration *= 1000;
    expiration += Date.now();
  } else if (expiration instanceof Date) {
    expiration = expiration.getTime();
  }

  return expiration;
};

Item.prototype.get = function (cachePolicy, policyData) {
  var that = this;
  this.cachePolicy = cachePolicy || Item.CP_NONE;
  this.policyData = policyData;

  function load(resolve) {
    that._load_().then(function (data) {
      if (data) {
        that.value = data.value;
        that.expiration = data.expiration;
        data = typeof data.value === 'undefined' ? null : data.value;
      }

      resolve(data);
    });
  }

  return new Promise(function (resolve) {
    if (that.cachePolicy & Item.CP_VALUE) {
      that.isLocked().then(function (locked) {
        if (locked) {
          resolve(that.policyData);
        } else {
          load(resolve);
        }
      });
    } else {
      load(resolve);
    }
  });
};

// alias for better syntax on Pool#get
Item.prototype.save = function (value, expiration) {
  return this.set(value, expiration).then(function () {
    return value;
  });
};

Item.prototype.set = function (value, expiration) {
  this._unload_();

  this.expiration = Item._calculateExpiration_(expiration);
  this.value = value;

  this.unlock();
  return this._write_();
};

Item.prototype.isMiss = function () {
  var that = this;

  var isMissed = function (locked, resolve) {
    var miss;

    if (locked && (that.cachePolicy & Item.CP_OLD)) {
      miss = false;
    } else if (!locked &&
               (that.cachePolicy & Item.CP_PRECOMPUTE) &&
               that.policyData * 1000 >= that.expiration - Date.now()) {
      miss =  true;
    } else {
      miss = that.value === null ||
        typeof(that.expiration) === 'number' &&
        that.expiration < Date.now();
    }

    resolve(miss);
  };

  return new Promise(function (resolve) {
    if (that.locked !== undefined) {
      isMissed(that.locked, resolve);
    } else {
      that.isLocked().then(function (locked) {
       isMissed(locked, resolve);
      });
    }
  });
};

Item.prototype.clear = function () {
  var that = this;
  this._unload_();

  return new Promise(function (resolve) {
    that.pool.drivers.forEach(function (driver) {
      driver.delete(that.key).then(resolve);
    });
  });
};

Item.prototype.lock = function () {
  if (this.locked === true) {
    return Promise.cast(this.locked);
  }

  this.locked = true;
  var that = this;

  return new Promise(function (resolve) {
    that.pool.drivers.forEach(function (d) {
      d.lock(that.key).then(resolve);
    });
  });
};

Item.prototype.isLocked = function () {
  if (this.locked !== undefined) {
    return Promise.cast(this.locked);
  }

  var that = this;
  return new Promise(function (resolve) {
    var promises = that.pool.drivers.map(function (d) {
      return d.isLocked(that.key).then(function (locked) {
        if (locked) {
          that.locked = locked;
          resolve(locked);
        }
      });
    });

    Promise.all(promises).then(function () {
      that.locked = that.locked || false;
      resolve(that.locked);
    });
  });
};

Item.prototype.unlock = function () {
  if (this.locked === false) {
    return Promise.cast();
  }

  this.locked = false;
  var that = this;

  return new Promise(function (resolve) {
    that.pool.drivers.forEach(function (d) {
      d.unlock(that.key).then(resolve);
    });
  });
};

module.exports = Item;