michaelminter
2/4/2013 - 7:20 PM

A Javascript class object that acts like Rails' ActiveRecord with localStorage.

A Javascript class object that acts like Rails' ActiveRecord with localStorage.

///////////////////////////////////////////////////////////////////////////////
//
// Simulate an activerecord-like model class
// in general JavaScript is a class-less language. Everything is an object.
// http://www.phpied.com/3-ways-to-define-a-javascript-class/
//
///////////////////////////////////////////////////////////////////////////////

// ## Data
// Objects should be indexed by id
// {"149":{"patient_related":false,"lock_version":0,"uuid":"918497e0-29a4-012f-1aca-005056af31ef","updated_at":"2012-01-25T11:04:29-06:00","facility_id":13,"priority_id":108,"job_type_id":269,"description":"Light burned out","created_at":"2012-01-25T11:04:29-06:00","id":149},"150":{"patient_related":true,"lock_version":0,"uuid":"9194e050-29a4-012f-1acb-005056af31ef","updated_at":"2012-01-25T11:04:29-06:00","facility_id":13,"priority_id":109,"job_type_id":272,"description":"Toilet stopped up","created_at":"2012-01-25T11:04:29-06:00","id":150},"151":{"patient_related":true,"lock_version":0,"uuid":"919607f0-29a4-012f-1acc-005056af31ef","updated_at":"2012-01-25T11:04:29-06:00","facility_id":13,"priority_id":109,"job_type_id":272,"description":"Drain clogged","created_at":"2012-01-25T11:04:29-06:00","id":151},"152":{"patient_related":true,"lock_version":0,"uuid":"91970ba0-29a4-012f-1acd-005056af31ef","updated_at":"2012-01-25T11:04:29-06:00","facility_id":13,"priority_id":108,"job_type_id":267,"description":"Room too cold","created_at":"2012-01-25T11:04:29-06:00","id":152},"153":{"patient_related":true,"lock_version":0,"uuid":"919801c0-29a4-012f-1ace-005056af31ef","updated_at":"2012-01-25T11:04:29-06:00","facility_id":13,"priority_id":108,"job_type_id":267,"description":"Room too hot","created_at":"2012-01-25T11:04:29-06:00","id":153},"154":{"patient_related":false,"lock_version":0,"uuid":"9198f860-29a4-012f-1acf-005056af31ef","updated_at":"2013-01-03T21:04:05-06:00","facility_id":13,"priority_id":109,"job_type_id":283,"description":"Door won't close","created_at":"2012-01-25T11:04:29-06:00","id":154},"155":{"patient_related":false,"lock_version":0,"uuid":"9199e740-29a4-012f-1ad0-005056af31ef","updated_at":"2012-01-25T11:04:29-06:00","facility_id":13,"priority_id":108,"job_type_id":269,"description":"Power out","created_at":"2012-01-25T11:04:29-06:00","id":155},"156":{"patient_related":true,"lock_version":0,"uuid":"919ad520-29a4-012f-1ad1-005056af31ef","updated_at":"2012-01-25T11:04:29-06:00","facility_id":13,"priority_id":108,"job_type_id":283,"description":"TV is broken","created_at":"2012-01-25T11:04:29-06:00","id":156},"157":{"patient_related":true,"lock_version":0,"uuid":"919bc420-29a4-012f-1ad2-005056af31ef","updated_at":"2012-01-25T11:04:29-06:00","facility_id":13,"priority_id":108,"job_type_id":272,"description":"No hot water","created_at":"2012-01-25T11:04:29-06:00","id":157},"158":{"patient_related":true,"lock_version":0,"uuid":"919cb3f0-29a4-012f-1ad3-005056af31ef","updated_at":"2012-01-25T11:04:29-06:00","facility_id":13,"priority_id":108,"job_type_id":283,"description":"TV remote does not work","created_at":"2012-01-25T11:04:29-06:00","id":158},"159":{"patient_related":false,"lock_version":0,"uuid":"919da3f0-29a4-012f-1ad4-005056af31ef","updated_at":"2012-01-25T11:04:29-06:00","facility_id":13,"priority_id":109,"job_type_id":272,"description":"Water leak","created_at":"2012-01-25T11:04:29-06:00","id":159},"652":{"patient_related":false,"lock_version":0,"uuid":"da2dede0-3839-0130-1c8c-065142d3bcb8","updated_at":"2013-01-03T19:13:36-06:00","facility_id":13,"priority_id":108,"job_type_id":283,"description":"Empty cardboard bailer","created_at":"2013-01-03T19:13:20-06:00","id":652},"653":{"patient_related":true,"lock_version":0,"uuid":"ffe96d10-3839-0130-1c90-065142d3bcb8","updated_at":"2013-01-03T19:14:23-06:00","facility_id":13,"priority_id":108,"job_type_id":267,"description":"Ice machine not working","created_at":"2013-01-03T19:14:23-06:00","id":653},"654":{"patient_related":false,"lock_version":0,"uuid":"2f7b3830-383a-0130-1c99-065142d3bcb8","updated_at":"2013-01-03T19:15:43-06:00","facility_id":13,"priority_id":107,"job_type_id":267,"description":"Defrost refridgerator","created_at":"2013-01-03T19:15:43-06:00","id":654},"655":{"patient_related":false,"lock_version":0,"uuid":"8fb19ba0-383a-0130-1ca0-065142d3bcb8","updated_at":"2013-01-03T19:18:25-06:00","facility_id":13,"priority_id":107,"job_type_id":283,"description":"Cabinet broken","created_at":"2013-01-03T19:18:25-06:00","id":655},"656":{"patient_related":false,"lock_version":0,"uuid":"dfd7c390-3848-0130-6525-065142f28507","updated_at":"2013-01-03T21:01:05-06:00","facility_id":13,"priority_id":107,"job_type_id":283,"description":"Remove bed","created_at":"2013-01-03T21:00:52-06:00","id":656},"657":{"patient_related":true,"lock_version":0,"uuid":"f5de0a10-3848-0130-652b-065142f28507","updated_at":"2013-01-03T21:01:36-06:00","facility_id":13,"priority_id":109,"job_type_id":283,"description":"Need bed","created_at":"2013-01-03T21:01:29-06:00","id":657},"658":{"patient_related":true,"lock_version":0,"uuid":"2923eae0-3849-0130-6531-065142f28507","updated_at":"2013-01-03T21:02:55-06:00","facility_id":13,"priority_id":109,"job_type_id":272,"description":"Toilet overflowing","created_at":"2013-01-03T21:02:55-06:00","id":658},"659":{"patient_related":false,"lock_version":0,"uuid":"e3e490e0-3851-0130-6653-065142f28507","updated_at":"2013-01-03T22:05:24-06:00","facility_id":13,"priority_id":109,"job_type_id":283,"description":"Noise","created_at":"2013-01-03T22:05:24-06:00","id":659}}

// ## Use
// var Workorder = new Model('workorders');

// ### Where
// var date = new Date();
// Workorder.where({name:"MichaelMinter",age:28,created_at:date,admin:true},{created_at:'<'})

function Model(table){
  "use strict";

  if (this.constructor == Model) {
    var _table = localStorage.getObject(table);
    if (_table == null) {
      localStorage.setObject(table, {});
    }
  }

  this.all = function(){
    return localStorage.getObject(table);
  }

  this.find = function(id){
    return localStorage.getObject(table)[id];
  }

  this.first = function(){
    var _table = localStorage.getObject(table);
    for (var o in _table)
    {
      return _table[o];
    }
  }

  this.last = function(){
    var _table  = localStorage.getObject(table);
    var _return = "";
    for (var o in _table)
    {
      _return = _table[o];
    }
    return _return;
  }

  this.where = function(obj,options){
    var _table = localStorage.getObject(table);
    var returnData = {};

    for (var t in _table)
    {
      var checkSum   = [];
      var checkCount = 0;

      for (var o in obj)
      {
        switch(typeof obj[o])
        {
          case 'string':
            // string actually utilizes regex (case insensitive)
            if (_table[t][o] != null && eval('_table[t][o].search(/' + obj[o] + '/i)') > -1)
            {
              checkSum.push(true);
            }
            break;
          case 'number':
            if (_table[t][o] != null && _table[t][o] == obj[o])
            {
              checkSum.push(true);
            }
            break;
          case 'object':
            // object (date) requires option
            var dateLookup = new Date(_table[t][o]).getTime();
            var dateNow    = new Date().getTime();

            // TODO: replace with eval
            switch (options[o])
            {
              case '>':
                if (dateLookup > dateNow)
                {
                  checkSum.push(true);
                }
                break;
              case '<':
                if (dateLookup < dateNow)
                {
                  checkSum.push(true);
                }
                break;
              case '>=':
                if (dateLookup >= dateNow)
                {
                  checkSum.push(true);
                }
                break;
              case '<=':
                if (dateLookup <= dateNow)
                {
                  checkSum.push(true);
                }
                break;
              default:
                if (dateLookup == dateNow)
                {
                  checkSum.push(true);
                }
            }
            break;
          case 'boolean':

            break;
          case 'null':
            if (_table[t][o] == null)
            {
              checkSum.push(true);
            }
            break;
          default:

        }
        checkCount++;
      }
      if (checkSum.length == checkCount && checkSum.indexOf(true) >= 0 && checkSum.indexOf(false) == -1)
      {
        returnData[t] = _table[t];
      }
    }
    return returnData;
  }

  this.create = function(obj){
    var _table = localStorage.getObject(table);
    _table[obj.id] = obj;
    localStorage.setObject(table, _table);
  }

  this.update = function(obj){
    var _table = localStorage.getObject(table);
    _table[obj.id] = obj;
    localStorage.setObject(table, _table);
  }

  this.destroy = function(id){
    var _table = localStorage.getObject(table);
    delete _table[id];
    localStorage.setObject(table, _table);
  }

  this.destroy_all = function(){
    localStorage.setItem(table, '{}');
  }
}

if (window.Storage){
  Storage.prototype.setObject = function(key, value) {
    "use strict";
    this.setItem(key, JSON.stringify(value));
  };

  Storage.prototype.getObject = function(key) {
    "use strict";
    var value = this.getItem(key);
    return value && JSON.parse(value);
  };
} else {
  alert('localStorage is not supported on this browser')
}