From 7c9e4a3b7b2b1da9b01c44b6dc436553911d838a Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Wed, 6 Apr 2016 15:57:36 +0800 Subject: [PATCH 1/4] Add multiAddOrUpdate --- src/model.js | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/model.js b/src/model.js index b460acc..dc01afa 100644 --- a/src/model.js +++ b/src/model.js @@ -84,6 +84,53 @@ class Model { return this.model.rows; } + // multi add or update + async multiAddOrUpdate(data, uniques) { + await this.initModel(); + for (var key in data) { + + // Update + + var existingRowIndex = -1; + + for (var i = 0; i < this.model.totalrows; i++) { + var isFound = true; + uniques.forEach((u_attr)=>{ + if (this.model.rows[i][u_attr] != data[u_attr]) { + isFound = false; + } + }); + + if (isFound) { + existingRowIndex = i; + break; + } + } + + if (existingRowIndex != -1) { + this.model.rows[existingRowIndex] = data; + continue; + } + + // Add + + var value = data[key]; + var autoinc = this.model.autoinc++; + if (this.model.rows[autoinc]) { + return Util.error("ReactNativeStore error: Storage already contains _id '" + autoinc + "'"); + } + if (value._id) { + return Util.error("ReactNativeStore error: Don't need _id with add method"); + } + value._id = autoinc; + this.model.rows[autoinc] = value; + this.model.totalrows++; + } + this.database[this.modelName] = this.model; + await AsyncStorage.setItem(this.dbName, JSON.stringify(this.database)); + return this.model.rows; + } + // update async update(data, filter) { await this.initModel(); From 8ecfb312bd92fab4b81b39dc0bd01074a4c0363c Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Wed, 6 Apr 2016 16:07:43 +0800 Subject: [PATCH 2/4] Add lib --- .gitignore | 2 - lib/__tests__/filter_test.js | 274 +++++++++++++ lib/__tests__/mockStorage.js | 42 ++ lib/__tests__/model_test.js | 348 +++++++++++++++++ lib/__tests__/store_test.js | 87 +++++ lib/filter.js | 144 +++++++ lib/index.js | 8 + lib/migrations.js | 10 + lib/model.js | 724 +++++++++++++++++++++++++++++++++++ lib/store.js | 198 ++++++++++ lib/util.js | 10 + package.json | 2 +- 12 files changed, 1846 insertions(+), 3 deletions(-) create mode 100644 lib/__tests__/filter_test.js create mode 100644 lib/__tests__/mockStorage.js create mode 100644 lib/__tests__/model_test.js create mode 100644 lib/__tests__/store_test.js create mode 100644 lib/filter.js create mode 100644 lib/index.js create mode 100644 lib/migrations.js create mode 100644 lib/model.js create mode 100644 lib/store.js create mode 100644 lib/util.js diff --git a/.gitignore b/.gitignore index 89e8234..87d6313 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,3 @@ psd thumb sketch coverage - -lib diff --git a/lib/__tests__/filter_test.js b/lib/__tests__/filter_test.js new file mode 100644 index 0000000..079f739 --- /dev/null +++ b/lib/__tests__/filter_test.js @@ -0,0 +1,274 @@ +'use strict'; + +jest.dontMock('../filter.js'); + +var testDataSet = { + '1': { + _id: 1, + name: 'j', + price: 3, + location: { + name: 'USA', + coords: { + lat: 123, + lng: 123 + } + } + }, + '2': { + _id: 2, + name: 'a', + price: 4, + location: { + name: 'USA', + coords: { + lat: 123, + lng: 123 + } + } + }, + '3': { + _id: 3, + name: 'v', + price: 1, + location: { + name: 'USA', + coords: { + lat: 123, + lng: 123 + } + } + }, + '4': { + _id: 4, + name: 'a', + price: 2, + location: { + name: 'USA', + coords: { + lat: 123, + lng: 123 + } + } + }, + '5': { + _id: 5, + name: 's', + price: 1, + location: { + name: 'EU', + coords: { + lat: 423, + lng: 123 + } + } + }, + '6': { + _id: 6, + name: 'c', + price: 1, + location: { + name: 'EU', + coords: { + lat: 423, + lng: 123 + } + } + }, + '7': { + _id: 7, + name: 'r', + price: 7, + location: { + name: 'EU', + coords: { + lat: 423, + lng: 123 + } + } + }, + '8': { + _id: 8, + name: 'i', + price: 9, + location: { + name: 'Outer Space', + coords: { + lat: 999, + lng: 999 + } + } + }, + '9': { + _id: 9, + name: 'p', + price: 4, + location: { + name: 'InterGalatic Space', + coords: { + lat: 9001, + lng: 42 + } + } + }, + '10': { + _id: 10, + name: 't', + price: 999, + location: { + name: 'Outside', + coords: { + lat: -1, + lng: 0 + } + } + } +}; + +describe('filter Tests', function () { + var Filter; + + beforeEach(function () { + var Filter_ = require('../filter.js'); + Filter = new Filter_(); + }); + + it('should filter using find', function () { + var filter = { + where: { + or: [{ + price: { + between: [0, 5] + }, + location: { + name: 'EU' + } + }, { + location: { + name: { + regexp: 'space' + } + } + }] + }, + fields: { + name: false + }, + order: { + price: 'ASC' + } + }; + var results = Filter.apply(testDataSet, filter); + expect(results.length).toEqual(4); + var expected = [{ + _id: 5, + price: 1, + location: { + name: 'EU', + coords: { + lat: 423, + lng: 123 + } + } + }, { + _id: 6, + price: 1, + location: { + name: 'EU', + coords: { + lat: 423, + lng: 123 + } + } + }, { + _id: 9, + price: 4, + location: { + name: 'InterGalatic Space', + coords: { + lat: 9001, + lng: 42 + } + } + }, { + _id: 8, + price: 9, + location: { + name: 'Outer Space', + coords: { + lat: 999, + lng: 999 + } + } + }]; + expect(results).toEqual(expected); + }); + + it('should filter using findById', function () { + var findById = { + where: { + _id: 3 + } + }; + var results = Filter.apply(testDataSet, findById); + var expected = [{ + _id: 3, + name: 'v', + price: 1, + location: { + name: 'USA', + coords: { + lat: 123, + lng: 123 + } + } + }]; + expect(results).toEqual(expected); + }); + + it('should filter entries lexicographically', function () { + var lexiFind = { + where: { + name: { + lte: 'f' + } + } + }; + var results = Filter.apply(testDataSet, lexiFind); + var expected = [{ + _id: 2, + name: 'a', + price: 4, + location: { + name: 'USA', + coords: { + lat: 123, + lng: 123 + } + } + }, { + _id: 4, + name: 'a', + price: 2, + location: { + name: 'USA', + coords: { + lat: 123, + lng: 123 + } + } + }, { + _id: 6, + name: 'c', + price: 1, + location: { + name: 'EU', + coords: { + lat: 423, + lng: 123 + } + } + }]; + expect(results).toEqual(expected); + }); +}); \ No newline at end of file diff --git a/lib/__tests__/mockStorage.js b/lib/__tests__/mockStorage.js new file mode 100644 index 0000000..21bf7dd --- /dev/null +++ b/lib/__tests__/mockStorage.js @@ -0,0 +1,42 @@ +"use strict"; +// Replicate resolve and reject values from: +// https://github.com/facebook/react-native/blob/master/Libraries/Storage/AsyncStorage.js +// Haven't implemented callbacks as not yet used in react-native-store +// Haven't implemented multiGet/Set as not yet used in react-native-store + +var cache = {}; +var mock = { + setItem: jest.genMockFunction().mockImplementation(function (key, value) { + return new Promise(function (resolve, reject) { + if (typeof key !== 'string' || typeof value !== 'string') reject(new Error('key and value must be string')); + resolve(cache[key] = value); + }); + }), + getItem: jest.genMockFunction().mockImplementation(function (key) { + return new Promise(function (resolve, reject) { + if (cache.hasOwnProperty(key)) resolve(cache[key]); + resolve(null); + }); + }), + removeItem: jest.genMockFunction().mockImplementation(function (key) { + return new Promise(function (resolve, reject) { + if (cache.hasOwnProperty(key)) resolve(delete cache[key]); + reject('No such item!'); + }); + }), + clear: jest.genMockFunction().mockImplementation(function () { + return new Promise(function (resolve, reject) { + resolve(cache = {}); + }); + }), + getAllKeys: jest.genMockFunction().mockImplementation(function () { + return new Promise(function (resolve, reject) { + resolve(Object.keys(cache)); + }); + }), + _forceClear: function _forceClear() { + cache = {}; + } +}; + +module.exports = mock; \ No newline at end of file diff --git a/lib/__tests__/model_test.js b/lib/__tests__/model_test.js new file mode 100644 index 0000000..eec5e5e --- /dev/null +++ b/lib/__tests__/model_test.js @@ -0,0 +1,348 @@ +'use strict'; + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { return step("next", value); }, function (err) { return step("throw", err); }); } } return step("next"); }); }; } + +jest.dontMock('../filter.js'); +jest.dontMock('../model.js'); +var astore = require.requireActual('./mockStorage.js'); +jest.setMock('react-native', { + AsyncStorage: astore +}); + +// To view log of any syncstorage calls, use inside code: +// console.log('set calls', astore.setItem.mock.calls) + +describe('model Tests', function () { + var _this = this; + + var Model; + + beforeEach(function () { + var Model_ = require('../model.js'); + Model = new Model_('modelName', 'dbName'); + }); + + afterEach(function () { + astore._forceClear(); + }); + + pit('should test create database', function () { + return Model.getDatabase().then(function (resp) { + expect(resp).toEqual({}); + expect(astore.getItem).toBeCalled(); + expect(astore.setItem).toBeCalledWith('dbName', '{}'); + }); + }); + + pit('should add the data to AsyncStorage', function () { + return Model.add({ + foo: 'bar' + }).then(function (resp) { + expect(resp).toEqual({ + _id: 1, + foo: 'bar' + }); + var dbJson = '{"modelName":{"totalrows":1,"autoinc":2,"rows":{"1":{"foo":"bar","_id":1}}}}'; + expect(astore.setItem).toBeCalledWith('dbName', dbJson); + }); + }); + + pit('should test findById', function () { + return Model.findById(3).then(function (resp) { + expect(resp).toEqual(null); + }); + }); + + pit('should destroy the model', function () { + return Model.add({ + foo: 'bar' + }).then(function (resp) { + Model.destroy(); + }).then(function (resp) { + expect(astore.removeItem).toBeCalledWith('dbName'); + }); + }); + + pit('should update existing rows on filter', _asyncToGenerator(regeneratorRuntime.mark(function _callee() { + var testData, resp, expected, dbJson; + return regeneratorRuntime.wrap(function _callee$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + testData = [{ + foo: 0, + bar: 0, + foobar: 'foobar' + }, { + foo: 0, + bar: 1, + foobar: 'foobar' + }, { + foo: 1, + bar: 0, + foobar: 'foo' + }, { + foo: 1, + bar: 1, + foobar: 'foobar' + }]; + _context.next = 3; + return Model.multiAdd(testData); + + case 3: + _context.next = 5; + return Model.update({ + foobar: 'bar' + }, { + where: { + bar: 1 + } + }); + + case 5: + resp = _context.sent; + expected = [{ + _id: 2, + foo: 0, + bar: 1, + foobar: 'bar' + }, { + _id: 4, + foo: 1, + bar: 1, + foobar: 'bar' + }]; + + expect(resp).toEqual(expected); + dbJson = { + "modelName": { + "totalrows": 4, + "autoinc": 5, + "rows": { + 1: { + "foo": 0, + "bar": 0, + "foobar": "foobar", + "_id": 1 + }, + 2: { + "foo": 0, + "bar": 1, + "foobar": "bar", + "_id": 2 + }, + 3: { + "foo": 1, + "bar": 0, + "foobar": "foo", + "_id": 3 + }, + 4: { + "foo": 1, + "bar": 1, + "foobar": "bar", + "_id": 4 + } + } + } + }; + + expect(astore.setItem).toBeCalledWith('dbName', JSON.stringify(dbJson)); + + case 10: + case 'end': + return _context.stop(); + } + } + }, _callee, _this); + }))); + + pit('should update row with given id', _asyncToGenerator(regeneratorRuntime.mark(function _callee2() { + var testData, resp, expected; + return regeneratorRuntime.wrap(function _callee2$(_context2) { + while (1) { + switch (_context2.prev = _context2.next) { + case 0: + testData = [{ + foo: 0, + bar: 0, + foobar: 'foobar' + }, { + foo: 0, + bar: 1, + foobar: 'foobar' + }, { + foo: 1, + bar: 0, + foobar: 'foo' + }, { + foo: 1, + bar: 1, + foobar: 'foobar' + }]; + _context2.next = 3; + return Model.multiAdd(testData); + + case 3: + _context2.next = 5; + return Model.updateById({ + foobar: 'barfoo' + }, 2); + + case 5: + resp = _context2.sent; + expected = { + _id: 2, + foo: 0, + bar: 1, + foobar: 'barfoo' + }; + + expect(resp).toEqual(expected); + + case 8: + case 'end': + return _context2.stop(); + } + } + }, _callee2, _this); + }))); + + pit('should remove rows based on filter', _asyncToGenerator(regeneratorRuntime.mark(function _callee3() { + var testData, resp, dbJson; + return regeneratorRuntime.wrap(function _callee3$(_context3) { + while (1) { + switch (_context3.prev = _context3.next) { + case 0: + testData = [{ + foo: 0, + bar: 0, + foobar: 'foobar' + }, { + foo: 0, + bar: 1, + foobar: 'foobar' + }, { + foo: 1, + bar: 0, + foobar: 'foo' + }, { + foo: 1, + bar: 1, + foobar: 'foobar' + }]; + _context3.next = 3; + return Model.multiAdd(testData); + + case 3: + astore.setItem.mockClear(); + _context3.next = 6; + return Model.remove({ + where: { + foo: 1 + } + }); + + case 6: + resp = _context3.sent; + dbJson = { + "modelName": { + "totalrows": 2, + "autoinc": 5, + "rows": { + 1: { + "foo": 0, + "bar": 0, + "foobar": "foobar", + "_id": 1 + }, + 2: { + "foo": 0, + "bar": 1, + "foobar": "foobar", + "_id": 2 + } + } + } + }; + + expect(astore.setItem).toBeCalledWith('dbName', JSON.stringify(dbJson)); + + case 9: + case 'end': + return _context3.stop(); + } + } + }, _callee3, _this); + }))); + + pit('should remove rows based on id', _asyncToGenerator(regeneratorRuntime.mark(function _callee4() { + var testData, resp, dbJson; + return regeneratorRuntime.wrap(function _callee4$(_context4) { + while (1) { + switch (_context4.prev = _context4.next) { + case 0: + testData = [{ + foo: 0, + bar: 0, + foobar: 'foobar' + }, { + foo: 0, + bar: 1, + foobar: 'foobar' + }, { + foo: 1, + bar: 0, + foobar: 'foo' + }, { + foo: 1, + bar: 1, + foobar: 'foobar' + }]; + _context4.next = 3; + return Model.multiAdd(testData); + + case 3: + astore.setItem.mockClear(); + _context4.next = 6; + return Model.removeById(1); + + case 6: + resp = _context4.sent; + dbJson = { + "modelName": { + "totalrows": 3, + "autoinc": 5, + "rows": { + 2: { + "foo": 0, + "bar": 1, + "foobar": "foobar", + "_id": 2 + }, + 3: { + "foo": 1, + "bar": 0, + "foobar": "foo", + "_id": 3 + }, + 4: { + "foo": 1, + "bar": 1, + "foobar": "foobar", + "_id": 4 + } + } + } + }; + + expect(astore.setItem).toBeCalledWith('dbName', JSON.stringify(dbJson)); + + case 9: + case 'end': + return _context4.stop(); + } + } + }, _callee4, _this); + }))); +}); \ No newline at end of file diff --git a/lib/__tests__/store_test.js b/lib/__tests__/store_test.js new file mode 100644 index 0000000..5b04f1a --- /dev/null +++ b/lib/__tests__/store_test.js @@ -0,0 +1,87 @@ +'use strict'; + +jest.dontMock('../model'); +jest.dontMock('../store'); +var astore = require.requireActual('./mockStorage.js'); +jest.setMock('react-native', { AsyncStorage: astore }); + +describe('store Tests', function () { + var Store; + + beforeEach(function () { + var Store_ = require('../store'); + Store = new Store_({ dbName: 'react-native-store' }); + }); + + afterEach(function () { + astore._forceClear(); + }); + + pit('should create model', function () { + var model = Store.model('newModel'); + return model.add({ foo: 'bar' }).then(function () { + return astore.getAllKeys(); + }).then(function (keys) { + return expect(keys).toEqual(['react-native-store']); + }); + }); + + pit('should clear only react-native-store created keys', function () { + astore.setItem('SomeOtherLibrary', 'Foobar'); + var model = Store.model('newModel'); + return model.add({ foo: 'bar' }).then(function () { + return astore.getAllKeys(); + }).then(function (keys) { + return expect(keys).toEqual(['SomeOtherLibrary', 'react-native-store']); + }).then(function () { + return Store.clear(); + }).then(astore.getAllKeys).then(function (keys) { + return expect(keys).toEqual(['SomeOtherLibrary']); + }); + }); + + pit('should run migrations', function () { + var migrations = require('../migrations'); + migrations.push({ + version: 0.2, + perform: jest.genMockFunction() + }); + return Store.migrate().then(function () { + expect(migrations[0].perform).toBeCalled(); + }); + }); + + pit('should run partial migrations', function () { + var migrations = require('../migrations'); + migrations.push({ + version: 0.2, + perform: jest.genMockFunction() + }); + migrations.push({ + version: 0.3, + perform: jest.genMockFunction() + }); + astore.setItem('react-native-store_version', '0.2'); + return Store.migrate().then(function () { + expect(migrations[0].perform).not.toBeCalled(); + expect(migrations[1].perform).toBeCalled(); + }); + }); + + pit('should not run migrations', function () { + var migrations = require('../migrations'); + migrations.push({ + version: 0.2, + perform: jest.genMockFunction() + }); + migrations.push({ + version: 0.3, + perform: jest.genMockFunction() + }); + astore.setItem('react-native-store_version', '0.3'); + return Store.migrate().then(function () { + expect(migrations[0].perform).not.toBeCalled(); + expect(migrations[1].perform).not.toBeCalled(); + }); + }); +}); \ No newline at end of file diff --git a/lib/filter.js b/lib/filter.js new file mode 100644 index 0000000..dd62211 --- /dev/null +++ b/lib/filter.js @@ -0,0 +1,144 @@ +'use strict'; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Filter = function () { + function Filter() { + _classCallCheck(this, Filter); + + this.comparisonOperators = ['gt', 'gte', 'lt', 'lte', 'between', 'inq', 'nin', 'regexp']; + this.logicalOperators = ['and', 'or']; + } + + _createClass(Filter, [{ + key: 'apply', + value: function apply(set, settings) { + var where = settings.where || null; + var fields = settings.fields || {}; + var order = settings.order || {}; + var offset = settings.offset || 0; + var limit = settings.limit; + var result = []; + //Elements that satisfy where filter + if (where != null) { + for (var i in set) { + var element = set[i]; + //Empty filter includes everything + if (this.evaluate(where, element)) result.push(this.applyFieldsFilter(element, fields)); + } + } else { + //result needs to be in an array, set can be an object + //so you cant just result = set + for (var i in set) { + var element = set[i]; + result.push(this.applyFieldsFilter(element, fields)); + } + } + //Found a lot of conflicting info on whether Array.sort() is stable, + //but in testing so far it seems to be. + //Reverse the arrays of keys to get the desired weight + var orderKeys = Object.keys(order).reverse(); + for (var key in orderKeys) { + //If for some reason the value of the order is not ASC or DESC, + //sort by ASC + var greater = order[orderKeys[key]] === 'DESC' ? -1 : 1; + var lesser = greater * -1; + var keySort = function keySort(a, b) { + if (a[orderKeys[key]] < b[orderKeys[key]]) return lesser; + if (a[orderKeys[key]] > b[orderKeys[key]]) return greater; + if (a[orderKeys[key]] == b[orderKeys[key]]) return 0; + }; + result.sort(keySort); + } + //Apply limit and offset filters through results.slice(offset, offset + limit) + if (typeof limit === 'number' && limit > 0) return result.slice(offset, offset + limit);else if (offset !== 0) return result.slice(offset); + return result; + } + }, { + key: 'evaluate', + value: function evaluate(filter, element) { + var filterKeys = Object.keys(filter); + if ((typeof filter === 'undefined' ? 'undefined' : _typeof(filter)) == 'object') { + for (var i in filterKeys) { + var key = filterKeys[i]; + //key is either a property name, or logical operator + if (this.logicalOperators.indexOf(key) > -1) { + if (!this.evaluateLogicalOperator(key, filter[key], element)) return false; + } else if (this.comparisonOperators.indexOf(key) > -1) { + if (!this.evaluateComparisonOperator(key, filter[key], element)) return false; + } else if (_typeof(filter[key]) == 'object') { + if (!this.evaluate(filter[key], element[key])) return false; + } else if (filter[key] != element[key]) return false; + } + return true; + } + //It technically should never reach here, but just to be safe + return a == b; + } + }, { + key: 'evaluateLogicalOperator', + value: function evaluateLogicalOperator(operator, filter, element) { + if (operator == 'and') { + for (var i in filter) { + var comp = filter[i]; + if (!this.evaluate(comp, element)) return false; + } + return true; + } else if (operator == 'or') { + for (var i in filter) { + var comp = filter[i]; + if (this.evaluate(comp, element)) return true; + } + } + return false; + } + }, { + key: 'evaluateComparisonOperator', + value: function evaluateComparisonOperator(operator, filter, element) { + if (operator == 'neq') return element != filter;else if (operator == 'gt') return element > filter;else if (operator == 'gte') return element >= filter;else if (operator == 'lt') return element < filter;else if (operator == 'lte') return element <= filter;else if (operator == 'between' && filter[0] != null && filter[1] != null) return element >= filter[0] && element <= filter[1];else if (operator == 'inq') return filter.indexOf(element) > -1 ? true : false;else if (operator == 'nin') return filter.indexOf(element) > -1 ? false : true;else if (operator == 'regexp') { + var regexper = (typeof filter === 'undefined' ? 'undefined' : _typeof(filter)) == 'object' ? filter : new RegExp(filter, 'i'); + return regexper.exec(element) == null ? false : true; + } + return false; + } + }, { + key: 'applyFieldsFilter', + value: function applyFieldsFilter(element, fields) { + //Fields filter will exclude all keys from the element for which there + //is a corresponding key in the fields object with a value of false. + //However, if one key in the fields filter has a value of true, all + //keys in element that do not have a corresponding key with a value of + //true will be excluded. + //If strict = false, the former behavior occurs, if strict = true, the + //latter behavior occurs + var strict = false; + var result = {}; + var fieldKeys = Object.keys(fields); + var elementKeys = Object.keys(element); + for (var key in fieldKeys) { + if (fields[fieldKeys[key]] === true) strict = true; + } + //NOTE: This is only for react-native-storage, which needs a _id key + //to function correctly. If we are on strict mode, we must add in the + //_id key, if we are not, we much make sure that there is no false + //value for it + if (strict) fields._id = true;else delete fields._id; + for (var key in elementKeys) { + //Applying the above described behavior. Add the property if + //we are being strict and the filter contains the key, or if + //we are not being strict and our filter does not want to remove + //the property. + if (strict && fields[elementKeys[key]] === true || !strict && !(fields[elementKeys[key]] === false)) result[elementKeys[key]] = element[elementKeys[key]]; + } + return result; + } + }]); + + return Filter; +}(); + +module.exports = Filter; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..5bc669e --- /dev/null +++ b/lib/index.js @@ -0,0 +1,8 @@ +'use strict'; + +var Store = require('./store.js'); +var store = new Store({ + dbName: "react-native-store" +}); + +module.exports = store; \ No newline at end of file diff --git a/lib/migrations.js b/lib/migrations.js new file mode 100644 index 0000000..d3f275c --- /dev/null +++ b/lib/migrations.js @@ -0,0 +1,10 @@ +"use strict"; + +var Migrations = [{ + version: 0.2, + perform: function perform() { + // perform actions here. + } +}]; + +module.exports = Migrations; \ No newline at end of file diff --git a/lib/model.js b/lib/model.js new file mode 100644 index 0000000..1a11d2b --- /dev/null +++ b/lib/model.js @@ -0,0 +1,724 @@ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { return step("next", value); }, function (err) { return step("throw", err); }); } } return step("next"); }); }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var AsyncStorage = require('react-native').AsyncStorage; +var Util = require('./util.js'); +var Filter = require('./filter.js'); + +var Model = function () { + function Model(modelName, dbName) { + _classCallCheck(this, Model); + + this.dbName = dbName; + this.modelName = modelName; + this.offset = 0; + this.limit = 10; + this.modelFilter = new Filter(); + } + + _createClass(Model, [{ + key: 'createDatabase', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee() { + return regeneratorRuntime.wrap(function _callee$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + _context.next = 2; + return AsyncStorage.setItem(this.dbName, JSON.stringify({})); + + case 2: + return _context.abrupt('return', this.getDatabase()); + + case 3: + case 'end': + return _context.stop(); + } + } + }, _callee, this); + })); + + function createDatabase() { + return ref.apply(this, arguments); + } + + return createDatabase; + }() + }, { + key: 'getDatabase', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee2() { + var database; + return regeneratorRuntime.wrap(function _callee2$(_context2) { + while (1) { + switch (_context2.prev = _context2.next) { + case 0: + _context2.next = 2; + return AsyncStorage.getItem(this.dbName); + + case 2: + database = _context2.sent; + + if (!database) { + _context2.next = 7; + break; + } + + return _context2.abrupt('return', Object.assign({}, JSON.parse(database))); + + case 7: + return _context2.abrupt('return', this.createDatabase()); + + case 8: + case 'end': + return _context2.stop(); + } + } + }, _callee2, this); + })); + + function getDatabase() { + return ref.apply(this, arguments); + } + + return getDatabase; + }() + }, { + key: 'initModel', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee3() { + return regeneratorRuntime.wrap(function _callee3$(_context3) { + while (1) { + switch (_context3.prev = _context3.next) { + case 0: + _context3.next = 2; + return this.getDatabase(); + + case 2: + this.database = _context3.sent; + + this.model = this.database[this.modelName] ? this.database[this.modelName] : { + 'totalrows': 0, + 'autoinc': 1, + 'rows': {} + }; + this.database[this.modelName] = this.database[this.modelName] || this.model; + + case 5: + case 'end': + return _context3.stop(); + } + } + }, _callee3, this); + })); + + function initModel() { + return ref.apply(this, arguments); + } + + return initModel; + }() + + //destroy + + }, { + key: 'destroy', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee4() { + var database; + return regeneratorRuntime.wrap(function _callee4$(_context4) { + while (1) { + switch (_context4.prev = _context4.next) { + case 0: + _context4.next = 2; + return AsyncStorage.getItem(this.dbName); + + case 2: + database = _context4.sent; + + if (!database) { + _context4.next = 9; + break; + } + + _context4.next = 6; + return AsyncStorage.removeItem(this.dbName); + + case 6: + _context4.t0 = _context4.sent; + _context4.next = 10; + break; + + case 9: + _context4.t0 = null; + + case 10: + return _context4.abrupt('return', _context4.t0); + + case 11: + case 'end': + return _context4.stop(); + } + } + }, _callee4, this); + })); + + function destroy() { + return ref.apply(this, arguments); + } + + return destroy; + }() + + // add + + }, { + key: 'add', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee5(data) { + var autoinc; + return regeneratorRuntime.wrap(function _callee5$(_context5) { + while (1) { + switch (_context5.prev = _context5.next) { + case 0: + _context5.next = 2; + return this.initModel(); + + case 2: + autoinc = this.model.autoinc++; + + if (!this.model.rows[autoinc]) { + _context5.next = 5; + break; + } + + return _context5.abrupt('return', Util.error("ReactNativeStore error: Storage already contains _id '" + autoinc + "'")); + + case 5: + if (!data._id) { + _context5.next = 7; + break; + } + + return _context5.abrupt('return', Util.error("ReactNativeStore error: Don't need _id with add method")); + + case 7: + data._id = autoinc; + this.model.rows[autoinc] = data; + this.model.totalrows++; + this.database[this.modelName] = this.model; + _context5.next = 13; + return AsyncStorage.setItem(this.dbName, JSON.stringify(this.database)); + + case 13: + return _context5.abrupt('return', this.model.rows[data._id]); + + case 14: + case 'end': + return _context5.stop(); + } + } + }, _callee5, this); + })); + + function add(_x) { + return ref.apply(this, arguments); + } + + return add; + }() + + // multi add + + }, { + key: 'multiAdd', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee6(data) { + var key, value, autoinc; + return regeneratorRuntime.wrap(function _callee6$(_context6) { + while (1) { + switch (_context6.prev = _context6.next) { + case 0: + _context6.next = 2; + return this.initModel(); + + case 2: + _context6.t0 = regeneratorRuntime.keys(data); + + case 3: + if ((_context6.t1 = _context6.t0()).done) { + _context6.next = 16; + break; + } + + key = _context6.t1.value; + value = data[key]; + autoinc = this.model.autoinc++; + + if (!this.model.rows[autoinc]) { + _context6.next = 9; + break; + } + + return _context6.abrupt('return', Util.error("ReactNativeStore error: Storage already contains _id '" + autoinc + "'")); + + case 9: + if (!value._id) { + _context6.next = 11; + break; + } + + return _context6.abrupt('return', Util.error("ReactNativeStore error: Don't need _id with add method")); + + case 11: + value._id = autoinc; + this.model.rows[autoinc] = value; + this.model.totalrows++; + _context6.next = 3; + break; + + case 16: + this.database[this.modelName] = this.model; + _context6.next = 19; + return AsyncStorage.setItem(this.dbName, JSON.stringify(this.database)); + + case 19: + return _context6.abrupt('return', this.model.rows); + + case 20: + case 'end': + return _context6.stop(); + } + } + }, _callee6, this); + })); + + function multiAdd(_x2) { + return ref.apply(this, arguments); + } + + return multiAdd; + }() + + // multi add or update + + }, { + key: 'multiAddOrUpdate', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee7(data, uniques) { + var _this = this; + + var key, existingRowIndex, i, isFound, value, autoinc; + return regeneratorRuntime.wrap(function _callee7$(_context7) { + while (1) { + switch (_context7.prev = _context7.next) { + case 0: + _context7.next = 2; + return this.initModel(); + + case 2: + _context7.t0 = regeneratorRuntime.keys(data); + + case 3: + if ((_context7.t1 = _context7.t0()).done) { + _context7.next = 30; + break; + } + + key = _context7.t1.value; + + + // Update + + existingRowIndex = -1; + i = 0; + + case 7: + if (!(i < this.model.totalrows)) { + _context7.next = 16; + break; + } + + isFound = true; + + uniques.forEach(function (u_attr) { + if (_this.model.rows[i][u_attr] != data[u_attr]) { + isFound = false; + } + }); + + if (!isFound) { + _context7.next = 13; + break; + } + + existingRowIndex = i; + return _context7.abrupt('break', 16); + + case 13: + i++; + _context7.next = 7; + break; + + case 16: + if (!(existingRowIndex != -1)) { + _context7.next = 19; + break; + } + + this.model.rows[existingRowIndex] = data; + return _context7.abrupt('continue', 3); + + case 19: + + // Add + + value = data[key]; + autoinc = this.model.autoinc++; + + if (!this.model.rows[autoinc]) { + _context7.next = 23; + break; + } + + return _context7.abrupt('return', Util.error("ReactNativeStore error: Storage already contains _id '" + autoinc + "'")); + + case 23: + if (!value._id) { + _context7.next = 25; + break; + } + + return _context7.abrupt('return', Util.error("ReactNativeStore error: Don't need _id with add method")); + + case 25: + value._id = autoinc; + this.model.rows[autoinc] = value; + this.model.totalrows++; + _context7.next = 3; + break; + + case 30: + this.database[this.modelName] = this.model; + _context7.next = 33; + return AsyncStorage.setItem(this.dbName, JSON.stringify(this.database)); + + case 33: + return _context7.abrupt('return', this.model.rows); + + case 34: + case 'end': + return _context7.stop(); + } + } + }, _callee7, this); + })); + + function multiAddOrUpdate(_x3, _x4) { + return ref.apply(this, arguments); + } + + return multiAddOrUpdate; + }() + + // update + + }, { + key: 'update', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee8(data, filter) { + var results, rows, filterResult, row, element, i; + return regeneratorRuntime.wrap(function _callee8$(_context8) { + while (1) { + switch (_context8.prev = _context8.next) { + case 0: + _context8.next = 2; + return this.initModel(); + + case 2: + filter = filter || {}; + if (data._id) delete data._id; + results = []; + rows = this.model["rows"]; + filterResult = this.modelFilter.apply(rows, filter); + _context8.t0 = regeneratorRuntime.keys(rows); + + case 8: + if ((_context8.t1 = _context8.t0()).done) { + _context8.next = 23; + break; + } + + row = _context8.t1.value; + _context8.t2 = regeneratorRuntime.keys(filterResult); + + case 11: + if ((_context8.t3 = _context8.t2()).done) { + _context8.next = 21; + break; + } + + element = _context8.t3.value; + + if (!(rows[row]['_id'] === filterResult[element]['_id'])) { + _context8.next = 19; + break; + } + + for (i in data) { + rows[row][i] = data[i]; + } + results.push(rows[row]); + this.database[this.modelName] = this.model; + _context8.next = 19; + return AsyncStorage.setItem(this.dbName, JSON.stringify(this.database)); + + case 19: + _context8.next = 11; + break; + + case 21: + _context8.next = 8; + break; + + case 23: + return _context8.abrupt('return', results.length ? results : null); + + case 24: + case 'end': + return _context8.stop(); + } + } + }, _callee8, this); + })); + + function update(_x5, _x6) { + return ref.apply(this, arguments); + } + + return update; + }() + + // remove a single entry by id + + }, { + key: 'updateById', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee9(data, id) { + var result; + return regeneratorRuntime.wrap(function _callee9$(_context9) { + while (1) { + switch (_context9.prev = _context9.next) { + case 0: + _context9.next = 2; + return this.update(data, { + where: { + _id: id + } + }); + + case 2: + result = _context9.sent; + return _context9.abrupt('return', result ? result[0] : null); + + case 4: + case 'end': + return _context9.stop(); + } + } + }, _callee9, this); + })); + + function updateById(_x7, _x8) { + return ref.apply(this, arguments); + } + + return updateById; + }() + + // remove + + }, { + key: 'remove', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee10(filter) { + var results, rowsToDelete, rows, filterResult, row, element, i; + return regeneratorRuntime.wrap(function _callee10$(_context10) { + while (1) { + switch (_context10.prev = _context10.next) { + case 0: + _context10.next = 2; + return this.initModel(); + + case 2: + filter = filter || {}; + results = []; + rowsToDelete = []; + rows = this.model["rows"]; + filterResult = this.modelFilter.apply(rows, filter); + + for (row in rows) { + for (element in filterResult) { + if (rows[row]['_id'] === filterResult[element]['_id']) rowsToDelete.push(row); + } + } + for (i in rowsToDelete) { + row = rowsToDelete[i]; + + results.push(this.model["rows"][row]); + delete this.model["rows"][row]; + this.model["totalrows"]--; + } + this.database[this.modelName] = this.model; + _context10.next = 12; + return AsyncStorage.setItem(this.dbName, JSON.stringify(this.database)); + + case 12: + return _context10.abrupt('return', results.length ? results : null); + + case 13: + case 'end': + return _context10.stop(); + } + } + }, _callee10, this); + })); + + function remove(_x9) { + return ref.apply(this, arguments); + } + + return remove; + }() + + // remove a single entry by id + + }, { + key: 'removeById', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee11(id) { + var result; + return regeneratorRuntime.wrap(function _callee11$(_context11) { + while (1) { + switch (_context11.prev = _context11.next) { + case 0: + _context11.next = 2; + return this.remove({ + where: { + _id: id + } + }); + + case 2: + result = _context11.sent; + return _context11.abrupt('return', result ? result[0] : null); + + case 4: + case 'end': + return _context11.stop(); + } + } + }, _callee11, this); + })); + + function removeById(_x10) { + return ref.apply(this, arguments); + } + + return removeById; + }() + + // find + + }, { + key: 'find', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee12(filter) { + var results, rows; + return regeneratorRuntime.wrap(function _callee12$(_context12) { + while (1) { + switch (_context12.prev = _context12.next) { + case 0: + _context12.next = 2; + return this.initModel(); + + case 2: + filter = filter || {}; + results = []; + rows = this.model["rows"]; + + results = this.modelFilter.apply(rows, filter); + return _context12.abrupt('return', results.length ? results : null); + + case 7: + case 'end': + return _context12.stop(); + } + } + }, _callee12, this); + })); + + function find(_x11) { + return ref.apply(this, arguments); + } + + return find; + }() + + // find a single entry by id + + }, { + key: 'findById', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee13(id) { + var result; + return regeneratorRuntime.wrap(function _callee13$(_context13) { + while (1) { + switch (_context13.prev = _context13.next) { + case 0: + _context13.next = 2; + return this.find({ + where: { + _id: id + } + }); + + case 2: + result = _context13.sent; + return _context13.abrupt('return', result ? result[0] : null); + + case 4: + case 'end': + return _context13.stop(); + } + } + }, _callee13, this); + })); + + function findById(_x12) { + return ref.apply(this, arguments); + } + + return findById; + }() + + // get + + }, { + key: 'get', + value: function get(filter) { + filter = filter || {}; + filter.limit = 1; + return this.find(filter); + } + }]); + + return Model; +}(); + +module.exports = Model; \ No newline at end of file diff --git a/lib/store.js b/lib/store.js new file mode 100644 index 0000000..6602d98 --- /dev/null +++ b/lib/store.js @@ -0,0 +1,198 @@ +'use strict'; + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { return step("next", value); }, function (err) { return step("throw", err); }); } } return step("next"); }); }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var AsyncStorage = require('react-native').AsyncStorage; +var Model = require('./model.js'); +var Util = require('./util.js'); + +var Store = function () { + function Store(opts) { + _classCallCheck(this, Store); + + this.dbName = opts.dbName; + } + + _createClass(Store, [{ + key: '_getCurrentVersion', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(versionKey) { + var currentVersion; + return regeneratorRuntime.wrap(function _callee$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + _context.next = 2; + return AsyncStorage.getItem(versionKey); + + case 2: + currentVersion = _context.sent; + + currentVersion = currentVersion || 0; + return _context.abrupt('return', parseFloat(currentVersion)); + + case 5: + case 'end': + return _context.stop(); + } + } + }, _callee, this); + })); + + function _getCurrentVersion(_x) { + return ref.apply(this, arguments); + } + + return _getCurrentVersion; + }() + }, { + key: 'migrate', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee2() { + var migrations, versionKey, currentVersion, target, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, migration; + + return regeneratorRuntime.wrap(function _callee2$(_context2) { + while (1) { + switch (_context2.prev = _context2.next) { + case 0: + migrations = require('./migrations.js'); + versionKey = this.dbName + '_version'; + _context2.next = 4; + return this._getCurrentVersion(versionKey); + + case 4: + currentVersion = _context2.sent; + target = migrations.slice(-1)[0]; + + if (!(currentVersion == target.version)) { + _context2.next = 8; + break; + } + + return _context2.abrupt('return'); + + case 8: + _iteratorNormalCompletion = true; + _didIteratorError = false; + _iteratorError = undefined; + _context2.prev = 11; + _iterator = migrations[Symbol.iterator](); + + case 13: + if (_iteratorNormalCompletion = (_step = _iterator.next()).done) { + _context2.next = 23; + break; + } + + migration = _step.value; + + if (!(migration.version <= currentVersion)) { + _context2.next = 17; + break; + } + + return _context2.abrupt('continue', 20); + + case 17: + migration.perform(); + _context2.next = 20; + return AsyncStorage.setItem(versionKey, migration.version.toString()); + + case 20: + _iteratorNormalCompletion = true; + _context2.next = 13; + break; + + case 23: + _context2.next = 29; + break; + + case 25: + _context2.prev = 25; + _context2.t0 = _context2['catch'](11); + _didIteratorError = true; + _iteratorError = _context2.t0; + + case 29: + _context2.prev = 29; + _context2.prev = 30; + + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + + case 32: + _context2.prev = 32; + + if (!_didIteratorError) { + _context2.next = 35; + break; + } + + throw _iteratorError; + + case 35: + return _context2.finish(32); + + case 36: + return _context2.finish(29); + + case 37: + case 'end': + return _context2.stop(); + } + } + }, _callee2, this, [[11, 25, 29, 37], [30,, 32, 36]]); + })); + + function migrate() { + return ref.apply(this, arguments); + } + + return migrate; + }() + }, { + key: 'model', + value: function model(modelName) { + return new Model(modelName, this.dbName); + } + + // clear store + + }, { + key: 'clear', + value: function () { + var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee3() { + return regeneratorRuntime.wrap(function _callee3$(_context3) { + while (1) { + switch (_context3.prev = _context3.next) { + case 0: + _context3.next = 2; + return AsyncStorage.removeItem(this.dbName); + + case 2: + case 'end': + return _context3.stop(); + } + } + }, _callee3, this); + })); + + function clear() { + return ref.apply(this, arguments); + } + + return clear; + }() + }]); + + return Store; +}(); + +module.exports = Store; + +// Store.model("user").get({ id:1 },{fite}).then().fail(); \ No newline at end of file diff --git a/lib/util.js b/lib/util.js new file mode 100644 index 0000000..e13d1cc --- /dev/null +++ b/lib/util.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = { + log: function log(msg) { + console.log(msg); + }, + error: function error(msg) { + console.error(msg); + } +}; \ No newline at end of file diff --git a/package.json b/package.json index acf6188..8dbd819 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-store", - "version": "0.4.1", + "version": "0.4.2", "description": "A simple database base on react-native AsyncStorage.", "main": "./lib/index.js", "scripts": { From 997dae2e39aa0aca0050cc9f9c49d5365e65d44a Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Wed, 6 Apr 2016 16:20:09 +0800 Subject: [PATCH 3/4] Add new setting --- lib/__tests__/filter_test.js | 274 ------------- lib/__tests__/mockStorage.js | 42 -- lib/__tests__/model_test.js | 348 ----------------- lib/__tests__/store_test.js | 87 ----- lib/filter.js | 144 ------- lib/index.js | 8 - lib/migrations.js | 10 - lib/model.js | 724 ----------------------------------- lib/store.js | 198 ---------- lib/util.js | 10 - package.json | 2 +- 11 files changed, 1 insertion(+), 1846 deletions(-) delete mode 100644 lib/__tests__/filter_test.js delete mode 100644 lib/__tests__/mockStorage.js delete mode 100644 lib/__tests__/model_test.js delete mode 100644 lib/__tests__/store_test.js delete mode 100644 lib/filter.js delete mode 100644 lib/index.js delete mode 100644 lib/migrations.js delete mode 100644 lib/model.js delete mode 100644 lib/store.js delete mode 100644 lib/util.js diff --git a/lib/__tests__/filter_test.js b/lib/__tests__/filter_test.js deleted file mode 100644 index 079f739..0000000 --- a/lib/__tests__/filter_test.js +++ /dev/null @@ -1,274 +0,0 @@ -'use strict'; - -jest.dontMock('../filter.js'); - -var testDataSet = { - '1': { - _id: 1, - name: 'j', - price: 3, - location: { - name: 'USA', - coords: { - lat: 123, - lng: 123 - } - } - }, - '2': { - _id: 2, - name: 'a', - price: 4, - location: { - name: 'USA', - coords: { - lat: 123, - lng: 123 - } - } - }, - '3': { - _id: 3, - name: 'v', - price: 1, - location: { - name: 'USA', - coords: { - lat: 123, - lng: 123 - } - } - }, - '4': { - _id: 4, - name: 'a', - price: 2, - location: { - name: 'USA', - coords: { - lat: 123, - lng: 123 - } - } - }, - '5': { - _id: 5, - name: 's', - price: 1, - location: { - name: 'EU', - coords: { - lat: 423, - lng: 123 - } - } - }, - '6': { - _id: 6, - name: 'c', - price: 1, - location: { - name: 'EU', - coords: { - lat: 423, - lng: 123 - } - } - }, - '7': { - _id: 7, - name: 'r', - price: 7, - location: { - name: 'EU', - coords: { - lat: 423, - lng: 123 - } - } - }, - '8': { - _id: 8, - name: 'i', - price: 9, - location: { - name: 'Outer Space', - coords: { - lat: 999, - lng: 999 - } - } - }, - '9': { - _id: 9, - name: 'p', - price: 4, - location: { - name: 'InterGalatic Space', - coords: { - lat: 9001, - lng: 42 - } - } - }, - '10': { - _id: 10, - name: 't', - price: 999, - location: { - name: 'Outside', - coords: { - lat: -1, - lng: 0 - } - } - } -}; - -describe('filter Tests', function () { - var Filter; - - beforeEach(function () { - var Filter_ = require('../filter.js'); - Filter = new Filter_(); - }); - - it('should filter using find', function () { - var filter = { - where: { - or: [{ - price: { - between: [0, 5] - }, - location: { - name: 'EU' - } - }, { - location: { - name: { - regexp: 'space' - } - } - }] - }, - fields: { - name: false - }, - order: { - price: 'ASC' - } - }; - var results = Filter.apply(testDataSet, filter); - expect(results.length).toEqual(4); - var expected = [{ - _id: 5, - price: 1, - location: { - name: 'EU', - coords: { - lat: 423, - lng: 123 - } - } - }, { - _id: 6, - price: 1, - location: { - name: 'EU', - coords: { - lat: 423, - lng: 123 - } - } - }, { - _id: 9, - price: 4, - location: { - name: 'InterGalatic Space', - coords: { - lat: 9001, - lng: 42 - } - } - }, { - _id: 8, - price: 9, - location: { - name: 'Outer Space', - coords: { - lat: 999, - lng: 999 - } - } - }]; - expect(results).toEqual(expected); - }); - - it('should filter using findById', function () { - var findById = { - where: { - _id: 3 - } - }; - var results = Filter.apply(testDataSet, findById); - var expected = [{ - _id: 3, - name: 'v', - price: 1, - location: { - name: 'USA', - coords: { - lat: 123, - lng: 123 - } - } - }]; - expect(results).toEqual(expected); - }); - - it('should filter entries lexicographically', function () { - var lexiFind = { - where: { - name: { - lte: 'f' - } - } - }; - var results = Filter.apply(testDataSet, lexiFind); - var expected = [{ - _id: 2, - name: 'a', - price: 4, - location: { - name: 'USA', - coords: { - lat: 123, - lng: 123 - } - } - }, { - _id: 4, - name: 'a', - price: 2, - location: { - name: 'USA', - coords: { - lat: 123, - lng: 123 - } - } - }, { - _id: 6, - name: 'c', - price: 1, - location: { - name: 'EU', - coords: { - lat: 423, - lng: 123 - } - } - }]; - expect(results).toEqual(expected); - }); -}); \ No newline at end of file diff --git a/lib/__tests__/mockStorage.js b/lib/__tests__/mockStorage.js deleted file mode 100644 index 21bf7dd..0000000 --- a/lib/__tests__/mockStorage.js +++ /dev/null @@ -1,42 +0,0 @@ -"use strict"; -// Replicate resolve and reject values from: -// https://github.com/facebook/react-native/blob/master/Libraries/Storage/AsyncStorage.js -// Haven't implemented callbacks as not yet used in react-native-store -// Haven't implemented multiGet/Set as not yet used in react-native-store - -var cache = {}; -var mock = { - setItem: jest.genMockFunction().mockImplementation(function (key, value) { - return new Promise(function (resolve, reject) { - if (typeof key !== 'string' || typeof value !== 'string') reject(new Error('key and value must be string')); - resolve(cache[key] = value); - }); - }), - getItem: jest.genMockFunction().mockImplementation(function (key) { - return new Promise(function (resolve, reject) { - if (cache.hasOwnProperty(key)) resolve(cache[key]); - resolve(null); - }); - }), - removeItem: jest.genMockFunction().mockImplementation(function (key) { - return new Promise(function (resolve, reject) { - if (cache.hasOwnProperty(key)) resolve(delete cache[key]); - reject('No such item!'); - }); - }), - clear: jest.genMockFunction().mockImplementation(function () { - return new Promise(function (resolve, reject) { - resolve(cache = {}); - }); - }), - getAllKeys: jest.genMockFunction().mockImplementation(function () { - return new Promise(function (resolve, reject) { - resolve(Object.keys(cache)); - }); - }), - _forceClear: function _forceClear() { - cache = {}; - } -}; - -module.exports = mock; \ No newline at end of file diff --git a/lib/__tests__/model_test.js b/lib/__tests__/model_test.js deleted file mode 100644 index eec5e5e..0000000 --- a/lib/__tests__/model_test.js +++ /dev/null @@ -1,348 +0,0 @@ -'use strict'; - -function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { return step("next", value); }, function (err) { return step("throw", err); }); } } return step("next"); }); }; } - -jest.dontMock('../filter.js'); -jest.dontMock('../model.js'); -var astore = require.requireActual('./mockStorage.js'); -jest.setMock('react-native', { - AsyncStorage: astore -}); - -// To view log of any syncstorage calls, use inside code: -// console.log('set calls', astore.setItem.mock.calls) - -describe('model Tests', function () { - var _this = this; - - var Model; - - beforeEach(function () { - var Model_ = require('../model.js'); - Model = new Model_('modelName', 'dbName'); - }); - - afterEach(function () { - astore._forceClear(); - }); - - pit('should test create database', function () { - return Model.getDatabase().then(function (resp) { - expect(resp).toEqual({}); - expect(astore.getItem).toBeCalled(); - expect(astore.setItem).toBeCalledWith('dbName', '{}'); - }); - }); - - pit('should add the data to AsyncStorage', function () { - return Model.add({ - foo: 'bar' - }).then(function (resp) { - expect(resp).toEqual({ - _id: 1, - foo: 'bar' - }); - var dbJson = '{"modelName":{"totalrows":1,"autoinc":2,"rows":{"1":{"foo":"bar","_id":1}}}}'; - expect(astore.setItem).toBeCalledWith('dbName', dbJson); - }); - }); - - pit('should test findById', function () { - return Model.findById(3).then(function (resp) { - expect(resp).toEqual(null); - }); - }); - - pit('should destroy the model', function () { - return Model.add({ - foo: 'bar' - }).then(function (resp) { - Model.destroy(); - }).then(function (resp) { - expect(astore.removeItem).toBeCalledWith('dbName'); - }); - }); - - pit('should update existing rows on filter', _asyncToGenerator(regeneratorRuntime.mark(function _callee() { - var testData, resp, expected, dbJson; - return regeneratorRuntime.wrap(function _callee$(_context) { - while (1) { - switch (_context.prev = _context.next) { - case 0: - testData = [{ - foo: 0, - bar: 0, - foobar: 'foobar' - }, { - foo: 0, - bar: 1, - foobar: 'foobar' - }, { - foo: 1, - bar: 0, - foobar: 'foo' - }, { - foo: 1, - bar: 1, - foobar: 'foobar' - }]; - _context.next = 3; - return Model.multiAdd(testData); - - case 3: - _context.next = 5; - return Model.update({ - foobar: 'bar' - }, { - where: { - bar: 1 - } - }); - - case 5: - resp = _context.sent; - expected = [{ - _id: 2, - foo: 0, - bar: 1, - foobar: 'bar' - }, { - _id: 4, - foo: 1, - bar: 1, - foobar: 'bar' - }]; - - expect(resp).toEqual(expected); - dbJson = { - "modelName": { - "totalrows": 4, - "autoinc": 5, - "rows": { - 1: { - "foo": 0, - "bar": 0, - "foobar": "foobar", - "_id": 1 - }, - 2: { - "foo": 0, - "bar": 1, - "foobar": "bar", - "_id": 2 - }, - 3: { - "foo": 1, - "bar": 0, - "foobar": "foo", - "_id": 3 - }, - 4: { - "foo": 1, - "bar": 1, - "foobar": "bar", - "_id": 4 - } - } - } - }; - - expect(astore.setItem).toBeCalledWith('dbName', JSON.stringify(dbJson)); - - case 10: - case 'end': - return _context.stop(); - } - } - }, _callee, _this); - }))); - - pit('should update row with given id', _asyncToGenerator(regeneratorRuntime.mark(function _callee2() { - var testData, resp, expected; - return regeneratorRuntime.wrap(function _callee2$(_context2) { - while (1) { - switch (_context2.prev = _context2.next) { - case 0: - testData = [{ - foo: 0, - bar: 0, - foobar: 'foobar' - }, { - foo: 0, - bar: 1, - foobar: 'foobar' - }, { - foo: 1, - bar: 0, - foobar: 'foo' - }, { - foo: 1, - bar: 1, - foobar: 'foobar' - }]; - _context2.next = 3; - return Model.multiAdd(testData); - - case 3: - _context2.next = 5; - return Model.updateById({ - foobar: 'barfoo' - }, 2); - - case 5: - resp = _context2.sent; - expected = { - _id: 2, - foo: 0, - bar: 1, - foobar: 'barfoo' - }; - - expect(resp).toEqual(expected); - - case 8: - case 'end': - return _context2.stop(); - } - } - }, _callee2, _this); - }))); - - pit('should remove rows based on filter', _asyncToGenerator(regeneratorRuntime.mark(function _callee3() { - var testData, resp, dbJson; - return regeneratorRuntime.wrap(function _callee3$(_context3) { - while (1) { - switch (_context3.prev = _context3.next) { - case 0: - testData = [{ - foo: 0, - bar: 0, - foobar: 'foobar' - }, { - foo: 0, - bar: 1, - foobar: 'foobar' - }, { - foo: 1, - bar: 0, - foobar: 'foo' - }, { - foo: 1, - bar: 1, - foobar: 'foobar' - }]; - _context3.next = 3; - return Model.multiAdd(testData); - - case 3: - astore.setItem.mockClear(); - _context3.next = 6; - return Model.remove({ - where: { - foo: 1 - } - }); - - case 6: - resp = _context3.sent; - dbJson = { - "modelName": { - "totalrows": 2, - "autoinc": 5, - "rows": { - 1: { - "foo": 0, - "bar": 0, - "foobar": "foobar", - "_id": 1 - }, - 2: { - "foo": 0, - "bar": 1, - "foobar": "foobar", - "_id": 2 - } - } - } - }; - - expect(astore.setItem).toBeCalledWith('dbName', JSON.stringify(dbJson)); - - case 9: - case 'end': - return _context3.stop(); - } - } - }, _callee3, _this); - }))); - - pit('should remove rows based on id', _asyncToGenerator(regeneratorRuntime.mark(function _callee4() { - var testData, resp, dbJson; - return regeneratorRuntime.wrap(function _callee4$(_context4) { - while (1) { - switch (_context4.prev = _context4.next) { - case 0: - testData = [{ - foo: 0, - bar: 0, - foobar: 'foobar' - }, { - foo: 0, - bar: 1, - foobar: 'foobar' - }, { - foo: 1, - bar: 0, - foobar: 'foo' - }, { - foo: 1, - bar: 1, - foobar: 'foobar' - }]; - _context4.next = 3; - return Model.multiAdd(testData); - - case 3: - astore.setItem.mockClear(); - _context4.next = 6; - return Model.removeById(1); - - case 6: - resp = _context4.sent; - dbJson = { - "modelName": { - "totalrows": 3, - "autoinc": 5, - "rows": { - 2: { - "foo": 0, - "bar": 1, - "foobar": "foobar", - "_id": 2 - }, - 3: { - "foo": 1, - "bar": 0, - "foobar": "foo", - "_id": 3 - }, - 4: { - "foo": 1, - "bar": 1, - "foobar": "foobar", - "_id": 4 - } - } - } - }; - - expect(astore.setItem).toBeCalledWith('dbName', JSON.stringify(dbJson)); - - case 9: - case 'end': - return _context4.stop(); - } - } - }, _callee4, _this); - }))); -}); \ No newline at end of file diff --git a/lib/__tests__/store_test.js b/lib/__tests__/store_test.js deleted file mode 100644 index 5b04f1a..0000000 --- a/lib/__tests__/store_test.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -jest.dontMock('../model'); -jest.dontMock('../store'); -var astore = require.requireActual('./mockStorage.js'); -jest.setMock('react-native', { AsyncStorage: astore }); - -describe('store Tests', function () { - var Store; - - beforeEach(function () { - var Store_ = require('../store'); - Store = new Store_({ dbName: 'react-native-store' }); - }); - - afterEach(function () { - astore._forceClear(); - }); - - pit('should create model', function () { - var model = Store.model('newModel'); - return model.add({ foo: 'bar' }).then(function () { - return astore.getAllKeys(); - }).then(function (keys) { - return expect(keys).toEqual(['react-native-store']); - }); - }); - - pit('should clear only react-native-store created keys', function () { - astore.setItem('SomeOtherLibrary', 'Foobar'); - var model = Store.model('newModel'); - return model.add({ foo: 'bar' }).then(function () { - return astore.getAllKeys(); - }).then(function (keys) { - return expect(keys).toEqual(['SomeOtherLibrary', 'react-native-store']); - }).then(function () { - return Store.clear(); - }).then(astore.getAllKeys).then(function (keys) { - return expect(keys).toEqual(['SomeOtherLibrary']); - }); - }); - - pit('should run migrations', function () { - var migrations = require('../migrations'); - migrations.push({ - version: 0.2, - perform: jest.genMockFunction() - }); - return Store.migrate().then(function () { - expect(migrations[0].perform).toBeCalled(); - }); - }); - - pit('should run partial migrations', function () { - var migrations = require('../migrations'); - migrations.push({ - version: 0.2, - perform: jest.genMockFunction() - }); - migrations.push({ - version: 0.3, - perform: jest.genMockFunction() - }); - astore.setItem('react-native-store_version', '0.2'); - return Store.migrate().then(function () { - expect(migrations[0].perform).not.toBeCalled(); - expect(migrations[1].perform).toBeCalled(); - }); - }); - - pit('should not run migrations', function () { - var migrations = require('../migrations'); - migrations.push({ - version: 0.2, - perform: jest.genMockFunction() - }); - migrations.push({ - version: 0.3, - perform: jest.genMockFunction() - }); - astore.setItem('react-native-store_version', '0.3'); - return Store.migrate().then(function () { - expect(migrations[0].perform).not.toBeCalled(); - expect(migrations[1].perform).not.toBeCalled(); - }); - }); -}); \ No newline at end of file diff --git a/lib/filter.js b/lib/filter.js deleted file mode 100644 index dd62211..0000000 --- a/lib/filter.js +++ /dev/null @@ -1,144 +0,0 @@ -'use strict'; - -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var Filter = function () { - function Filter() { - _classCallCheck(this, Filter); - - this.comparisonOperators = ['gt', 'gte', 'lt', 'lte', 'between', 'inq', 'nin', 'regexp']; - this.logicalOperators = ['and', 'or']; - } - - _createClass(Filter, [{ - key: 'apply', - value: function apply(set, settings) { - var where = settings.where || null; - var fields = settings.fields || {}; - var order = settings.order || {}; - var offset = settings.offset || 0; - var limit = settings.limit; - var result = []; - //Elements that satisfy where filter - if (where != null) { - for (var i in set) { - var element = set[i]; - //Empty filter includes everything - if (this.evaluate(where, element)) result.push(this.applyFieldsFilter(element, fields)); - } - } else { - //result needs to be in an array, set can be an object - //so you cant just result = set - for (var i in set) { - var element = set[i]; - result.push(this.applyFieldsFilter(element, fields)); - } - } - //Found a lot of conflicting info on whether Array.sort() is stable, - //but in testing so far it seems to be. - //Reverse the arrays of keys to get the desired weight - var orderKeys = Object.keys(order).reverse(); - for (var key in orderKeys) { - //If for some reason the value of the order is not ASC or DESC, - //sort by ASC - var greater = order[orderKeys[key]] === 'DESC' ? -1 : 1; - var lesser = greater * -1; - var keySort = function keySort(a, b) { - if (a[orderKeys[key]] < b[orderKeys[key]]) return lesser; - if (a[orderKeys[key]] > b[orderKeys[key]]) return greater; - if (a[orderKeys[key]] == b[orderKeys[key]]) return 0; - }; - result.sort(keySort); - } - //Apply limit and offset filters through results.slice(offset, offset + limit) - if (typeof limit === 'number' && limit > 0) return result.slice(offset, offset + limit);else if (offset !== 0) return result.slice(offset); - return result; - } - }, { - key: 'evaluate', - value: function evaluate(filter, element) { - var filterKeys = Object.keys(filter); - if ((typeof filter === 'undefined' ? 'undefined' : _typeof(filter)) == 'object') { - for (var i in filterKeys) { - var key = filterKeys[i]; - //key is either a property name, or logical operator - if (this.logicalOperators.indexOf(key) > -1) { - if (!this.evaluateLogicalOperator(key, filter[key], element)) return false; - } else if (this.comparisonOperators.indexOf(key) > -1) { - if (!this.evaluateComparisonOperator(key, filter[key], element)) return false; - } else if (_typeof(filter[key]) == 'object') { - if (!this.evaluate(filter[key], element[key])) return false; - } else if (filter[key] != element[key]) return false; - } - return true; - } - //It technically should never reach here, but just to be safe - return a == b; - } - }, { - key: 'evaluateLogicalOperator', - value: function evaluateLogicalOperator(operator, filter, element) { - if (operator == 'and') { - for (var i in filter) { - var comp = filter[i]; - if (!this.evaluate(comp, element)) return false; - } - return true; - } else if (operator == 'or') { - for (var i in filter) { - var comp = filter[i]; - if (this.evaluate(comp, element)) return true; - } - } - return false; - } - }, { - key: 'evaluateComparisonOperator', - value: function evaluateComparisonOperator(operator, filter, element) { - if (operator == 'neq') return element != filter;else if (operator == 'gt') return element > filter;else if (operator == 'gte') return element >= filter;else if (operator == 'lt') return element < filter;else if (operator == 'lte') return element <= filter;else if (operator == 'between' && filter[0] != null && filter[1] != null) return element >= filter[0] && element <= filter[1];else if (operator == 'inq') return filter.indexOf(element) > -1 ? true : false;else if (operator == 'nin') return filter.indexOf(element) > -1 ? false : true;else if (operator == 'regexp') { - var regexper = (typeof filter === 'undefined' ? 'undefined' : _typeof(filter)) == 'object' ? filter : new RegExp(filter, 'i'); - return regexper.exec(element) == null ? false : true; - } - return false; - } - }, { - key: 'applyFieldsFilter', - value: function applyFieldsFilter(element, fields) { - //Fields filter will exclude all keys from the element for which there - //is a corresponding key in the fields object with a value of false. - //However, if one key in the fields filter has a value of true, all - //keys in element that do not have a corresponding key with a value of - //true will be excluded. - //If strict = false, the former behavior occurs, if strict = true, the - //latter behavior occurs - var strict = false; - var result = {}; - var fieldKeys = Object.keys(fields); - var elementKeys = Object.keys(element); - for (var key in fieldKeys) { - if (fields[fieldKeys[key]] === true) strict = true; - } - //NOTE: This is only for react-native-storage, which needs a _id key - //to function correctly. If we are on strict mode, we must add in the - //_id key, if we are not, we much make sure that there is no false - //value for it - if (strict) fields._id = true;else delete fields._id; - for (var key in elementKeys) { - //Applying the above described behavior. Add the property if - //we are being strict and the filter contains the key, or if - //we are not being strict and our filter does not want to remove - //the property. - if (strict && fields[elementKeys[key]] === true || !strict && !(fields[elementKeys[key]] === false)) result[elementKeys[key]] = element[elementKeys[key]]; - } - return result; - } - }]); - - return Filter; -}(); - -module.exports = Filter; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index 5bc669e..0000000 --- a/lib/index.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -var Store = require('./store.js'); -var store = new Store({ - dbName: "react-native-store" -}); - -module.exports = store; \ No newline at end of file diff --git a/lib/migrations.js b/lib/migrations.js deleted file mode 100644 index d3f275c..0000000 --- a/lib/migrations.js +++ /dev/null @@ -1,10 +0,0 @@ -"use strict"; - -var Migrations = [{ - version: 0.2, - perform: function perform() { - // perform actions here. - } -}]; - -module.exports = Migrations; \ No newline at end of file diff --git a/lib/model.js b/lib/model.js deleted file mode 100644 index 1a11d2b..0000000 --- a/lib/model.js +++ /dev/null @@ -1,724 +0,0 @@ -'use strict'; - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { return step("next", value); }, function (err) { return step("throw", err); }); } } return step("next"); }); }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var AsyncStorage = require('react-native').AsyncStorage; -var Util = require('./util.js'); -var Filter = require('./filter.js'); - -var Model = function () { - function Model(modelName, dbName) { - _classCallCheck(this, Model); - - this.dbName = dbName; - this.modelName = modelName; - this.offset = 0; - this.limit = 10; - this.modelFilter = new Filter(); - } - - _createClass(Model, [{ - key: 'createDatabase', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee() { - return regeneratorRuntime.wrap(function _callee$(_context) { - while (1) { - switch (_context.prev = _context.next) { - case 0: - _context.next = 2; - return AsyncStorage.setItem(this.dbName, JSON.stringify({})); - - case 2: - return _context.abrupt('return', this.getDatabase()); - - case 3: - case 'end': - return _context.stop(); - } - } - }, _callee, this); - })); - - function createDatabase() { - return ref.apply(this, arguments); - } - - return createDatabase; - }() - }, { - key: 'getDatabase', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee2() { - var database; - return regeneratorRuntime.wrap(function _callee2$(_context2) { - while (1) { - switch (_context2.prev = _context2.next) { - case 0: - _context2.next = 2; - return AsyncStorage.getItem(this.dbName); - - case 2: - database = _context2.sent; - - if (!database) { - _context2.next = 7; - break; - } - - return _context2.abrupt('return', Object.assign({}, JSON.parse(database))); - - case 7: - return _context2.abrupt('return', this.createDatabase()); - - case 8: - case 'end': - return _context2.stop(); - } - } - }, _callee2, this); - })); - - function getDatabase() { - return ref.apply(this, arguments); - } - - return getDatabase; - }() - }, { - key: 'initModel', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee3() { - return regeneratorRuntime.wrap(function _callee3$(_context3) { - while (1) { - switch (_context3.prev = _context3.next) { - case 0: - _context3.next = 2; - return this.getDatabase(); - - case 2: - this.database = _context3.sent; - - this.model = this.database[this.modelName] ? this.database[this.modelName] : { - 'totalrows': 0, - 'autoinc': 1, - 'rows': {} - }; - this.database[this.modelName] = this.database[this.modelName] || this.model; - - case 5: - case 'end': - return _context3.stop(); - } - } - }, _callee3, this); - })); - - function initModel() { - return ref.apply(this, arguments); - } - - return initModel; - }() - - //destroy - - }, { - key: 'destroy', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee4() { - var database; - return regeneratorRuntime.wrap(function _callee4$(_context4) { - while (1) { - switch (_context4.prev = _context4.next) { - case 0: - _context4.next = 2; - return AsyncStorage.getItem(this.dbName); - - case 2: - database = _context4.sent; - - if (!database) { - _context4.next = 9; - break; - } - - _context4.next = 6; - return AsyncStorage.removeItem(this.dbName); - - case 6: - _context4.t0 = _context4.sent; - _context4.next = 10; - break; - - case 9: - _context4.t0 = null; - - case 10: - return _context4.abrupt('return', _context4.t0); - - case 11: - case 'end': - return _context4.stop(); - } - } - }, _callee4, this); - })); - - function destroy() { - return ref.apply(this, arguments); - } - - return destroy; - }() - - // add - - }, { - key: 'add', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee5(data) { - var autoinc; - return regeneratorRuntime.wrap(function _callee5$(_context5) { - while (1) { - switch (_context5.prev = _context5.next) { - case 0: - _context5.next = 2; - return this.initModel(); - - case 2: - autoinc = this.model.autoinc++; - - if (!this.model.rows[autoinc]) { - _context5.next = 5; - break; - } - - return _context5.abrupt('return', Util.error("ReactNativeStore error: Storage already contains _id '" + autoinc + "'")); - - case 5: - if (!data._id) { - _context5.next = 7; - break; - } - - return _context5.abrupt('return', Util.error("ReactNativeStore error: Don't need _id with add method")); - - case 7: - data._id = autoinc; - this.model.rows[autoinc] = data; - this.model.totalrows++; - this.database[this.modelName] = this.model; - _context5.next = 13; - return AsyncStorage.setItem(this.dbName, JSON.stringify(this.database)); - - case 13: - return _context5.abrupt('return', this.model.rows[data._id]); - - case 14: - case 'end': - return _context5.stop(); - } - } - }, _callee5, this); - })); - - function add(_x) { - return ref.apply(this, arguments); - } - - return add; - }() - - // multi add - - }, { - key: 'multiAdd', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee6(data) { - var key, value, autoinc; - return regeneratorRuntime.wrap(function _callee6$(_context6) { - while (1) { - switch (_context6.prev = _context6.next) { - case 0: - _context6.next = 2; - return this.initModel(); - - case 2: - _context6.t0 = regeneratorRuntime.keys(data); - - case 3: - if ((_context6.t1 = _context6.t0()).done) { - _context6.next = 16; - break; - } - - key = _context6.t1.value; - value = data[key]; - autoinc = this.model.autoinc++; - - if (!this.model.rows[autoinc]) { - _context6.next = 9; - break; - } - - return _context6.abrupt('return', Util.error("ReactNativeStore error: Storage already contains _id '" + autoinc + "'")); - - case 9: - if (!value._id) { - _context6.next = 11; - break; - } - - return _context6.abrupt('return', Util.error("ReactNativeStore error: Don't need _id with add method")); - - case 11: - value._id = autoinc; - this.model.rows[autoinc] = value; - this.model.totalrows++; - _context6.next = 3; - break; - - case 16: - this.database[this.modelName] = this.model; - _context6.next = 19; - return AsyncStorage.setItem(this.dbName, JSON.stringify(this.database)); - - case 19: - return _context6.abrupt('return', this.model.rows); - - case 20: - case 'end': - return _context6.stop(); - } - } - }, _callee6, this); - })); - - function multiAdd(_x2) { - return ref.apply(this, arguments); - } - - return multiAdd; - }() - - // multi add or update - - }, { - key: 'multiAddOrUpdate', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee7(data, uniques) { - var _this = this; - - var key, existingRowIndex, i, isFound, value, autoinc; - return regeneratorRuntime.wrap(function _callee7$(_context7) { - while (1) { - switch (_context7.prev = _context7.next) { - case 0: - _context7.next = 2; - return this.initModel(); - - case 2: - _context7.t0 = regeneratorRuntime.keys(data); - - case 3: - if ((_context7.t1 = _context7.t0()).done) { - _context7.next = 30; - break; - } - - key = _context7.t1.value; - - - // Update - - existingRowIndex = -1; - i = 0; - - case 7: - if (!(i < this.model.totalrows)) { - _context7.next = 16; - break; - } - - isFound = true; - - uniques.forEach(function (u_attr) { - if (_this.model.rows[i][u_attr] != data[u_attr]) { - isFound = false; - } - }); - - if (!isFound) { - _context7.next = 13; - break; - } - - existingRowIndex = i; - return _context7.abrupt('break', 16); - - case 13: - i++; - _context7.next = 7; - break; - - case 16: - if (!(existingRowIndex != -1)) { - _context7.next = 19; - break; - } - - this.model.rows[existingRowIndex] = data; - return _context7.abrupt('continue', 3); - - case 19: - - // Add - - value = data[key]; - autoinc = this.model.autoinc++; - - if (!this.model.rows[autoinc]) { - _context7.next = 23; - break; - } - - return _context7.abrupt('return', Util.error("ReactNativeStore error: Storage already contains _id '" + autoinc + "'")); - - case 23: - if (!value._id) { - _context7.next = 25; - break; - } - - return _context7.abrupt('return', Util.error("ReactNativeStore error: Don't need _id with add method")); - - case 25: - value._id = autoinc; - this.model.rows[autoinc] = value; - this.model.totalrows++; - _context7.next = 3; - break; - - case 30: - this.database[this.modelName] = this.model; - _context7.next = 33; - return AsyncStorage.setItem(this.dbName, JSON.stringify(this.database)); - - case 33: - return _context7.abrupt('return', this.model.rows); - - case 34: - case 'end': - return _context7.stop(); - } - } - }, _callee7, this); - })); - - function multiAddOrUpdate(_x3, _x4) { - return ref.apply(this, arguments); - } - - return multiAddOrUpdate; - }() - - // update - - }, { - key: 'update', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee8(data, filter) { - var results, rows, filterResult, row, element, i; - return regeneratorRuntime.wrap(function _callee8$(_context8) { - while (1) { - switch (_context8.prev = _context8.next) { - case 0: - _context8.next = 2; - return this.initModel(); - - case 2: - filter = filter || {}; - if (data._id) delete data._id; - results = []; - rows = this.model["rows"]; - filterResult = this.modelFilter.apply(rows, filter); - _context8.t0 = regeneratorRuntime.keys(rows); - - case 8: - if ((_context8.t1 = _context8.t0()).done) { - _context8.next = 23; - break; - } - - row = _context8.t1.value; - _context8.t2 = regeneratorRuntime.keys(filterResult); - - case 11: - if ((_context8.t3 = _context8.t2()).done) { - _context8.next = 21; - break; - } - - element = _context8.t3.value; - - if (!(rows[row]['_id'] === filterResult[element]['_id'])) { - _context8.next = 19; - break; - } - - for (i in data) { - rows[row][i] = data[i]; - } - results.push(rows[row]); - this.database[this.modelName] = this.model; - _context8.next = 19; - return AsyncStorage.setItem(this.dbName, JSON.stringify(this.database)); - - case 19: - _context8.next = 11; - break; - - case 21: - _context8.next = 8; - break; - - case 23: - return _context8.abrupt('return', results.length ? results : null); - - case 24: - case 'end': - return _context8.stop(); - } - } - }, _callee8, this); - })); - - function update(_x5, _x6) { - return ref.apply(this, arguments); - } - - return update; - }() - - // remove a single entry by id - - }, { - key: 'updateById', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee9(data, id) { - var result; - return regeneratorRuntime.wrap(function _callee9$(_context9) { - while (1) { - switch (_context9.prev = _context9.next) { - case 0: - _context9.next = 2; - return this.update(data, { - where: { - _id: id - } - }); - - case 2: - result = _context9.sent; - return _context9.abrupt('return', result ? result[0] : null); - - case 4: - case 'end': - return _context9.stop(); - } - } - }, _callee9, this); - })); - - function updateById(_x7, _x8) { - return ref.apply(this, arguments); - } - - return updateById; - }() - - // remove - - }, { - key: 'remove', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee10(filter) { - var results, rowsToDelete, rows, filterResult, row, element, i; - return regeneratorRuntime.wrap(function _callee10$(_context10) { - while (1) { - switch (_context10.prev = _context10.next) { - case 0: - _context10.next = 2; - return this.initModel(); - - case 2: - filter = filter || {}; - results = []; - rowsToDelete = []; - rows = this.model["rows"]; - filterResult = this.modelFilter.apply(rows, filter); - - for (row in rows) { - for (element in filterResult) { - if (rows[row]['_id'] === filterResult[element]['_id']) rowsToDelete.push(row); - } - } - for (i in rowsToDelete) { - row = rowsToDelete[i]; - - results.push(this.model["rows"][row]); - delete this.model["rows"][row]; - this.model["totalrows"]--; - } - this.database[this.modelName] = this.model; - _context10.next = 12; - return AsyncStorage.setItem(this.dbName, JSON.stringify(this.database)); - - case 12: - return _context10.abrupt('return', results.length ? results : null); - - case 13: - case 'end': - return _context10.stop(); - } - } - }, _callee10, this); - })); - - function remove(_x9) { - return ref.apply(this, arguments); - } - - return remove; - }() - - // remove a single entry by id - - }, { - key: 'removeById', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee11(id) { - var result; - return regeneratorRuntime.wrap(function _callee11$(_context11) { - while (1) { - switch (_context11.prev = _context11.next) { - case 0: - _context11.next = 2; - return this.remove({ - where: { - _id: id - } - }); - - case 2: - result = _context11.sent; - return _context11.abrupt('return', result ? result[0] : null); - - case 4: - case 'end': - return _context11.stop(); - } - } - }, _callee11, this); - })); - - function removeById(_x10) { - return ref.apply(this, arguments); - } - - return removeById; - }() - - // find - - }, { - key: 'find', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee12(filter) { - var results, rows; - return regeneratorRuntime.wrap(function _callee12$(_context12) { - while (1) { - switch (_context12.prev = _context12.next) { - case 0: - _context12.next = 2; - return this.initModel(); - - case 2: - filter = filter || {}; - results = []; - rows = this.model["rows"]; - - results = this.modelFilter.apply(rows, filter); - return _context12.abrupt('return', results.length ? results : null); - - case 7: - case 'end': - return _context12.stop(); - } - } - }, _callee12, this); - })); - - function find(_x11) { - return ref.apply(this, arguments); - } - - return find; - }() - - // find a single entry by id - - }, { - key: 'findById', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee13(id) { - var result; - return regeneratorRuntime.wrap(function _callee13$(_context13) { - while (1) { - switch (_context13.prev = _context13.next) { - case 0: - _context13.next = 2; - return this.find({ - where: { - _id: id - } - }); - - case 2: - result = _context13.sent; - return _context13.abrupt('return', result ? result[0] : null); - - case 4: - case 'end': - return _context13.stop(); - } - } - }, _callee13, this); - })); - - function findById(_x12) { - return ref.apply(this, arguments); - } - - return findById; - }() - - // get - - }, { - key: 'get', - value: function get(filter) { - filter = filter || {}; - filter.limit = 1; - return this.find(filter); - } - }]); - - return Model; -}(); - -module.exports = Model; \ No newline at end of file diff --git a/lib/store.js b/lib/store.js deleted file mode 100644 index 6602d98..0000000 --- a/lib/store.js +++ /dev/null @@ -1,198 +0,0 @@ -'use strict'; - -var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - -function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { return step("next", value); }, function (err) { return step("throw", err); }); } } return step("next"); }); }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var AsyncStorage = require('react-native').AsyncStorage; -var Model = require('./model.js'); -var Util = require('./util.js'); - -var Store = function () { - function Store(opts) { - _classCallCheck(this, Store); - - this.dbName = opts.dbName; - } - - _createClass(Store, [{ - key: '_getCurrentVersion', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee(versionKey) { - var currentVersion; - return regeneratorRuntime.wrap(function _callee$(_context) { - while (1) { - switch (_context.prev = _context.next) { - case 0: - _context.next = 2; - return AsyncStorage.getItem(versionKey); - - case 2: - currentVersion = _context.sent; - - currentVersion = currentVersion || 0; - return _context.abrupt('return', parseFloat(currentVersion)); - - case 5: - case 'end': - return _context.stop(); - } - } - }, _callee, this); - })); - - function _getCurrentVersion(_x) { - return ref.apply(this, arguments); - } - - return _getCurrentVersion; - }() - }, { - key: 'migrate', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee2() { - var migrations, versionKey, currentVersion, target, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, migration; - - return regeneratorRuntime.wrap(function _callee2$(_context2) { - while (1) { - switch (_context2.prev = _context2.next) { - case 0: - migrations = require('./migrations.js'); - versionKey = this.dbName + '_version'; - _context2.next = 4; - return this._getCurrentVersion(versionKey); - - case 4: - currentVersion = _context2.sent; - target = migrations.slice(-1)[0]; - - if (!(currentVersion == target.version)) { - _context2.next = 8; - break; - } - - return _context2.abrupt('return'); - - case 8: - _iteratorNormalCompletion = true; - _didIteratorError = false; - _iteratorError = undefined; - _context2.prev = 11; - _iterator = migrations[Symbol.iterator](); - - case 13: - if (_iteratorNormalCompletion = (_step = _iterator.next()).done) { - _context2.next = 23; - break; - } - - migration = _step.value; - - if (!(migration.version <= currentVersion)) { - _context2.next = 17; - break; - } - - return _context2.abrupt('continue', 20); - - case 17: - migration.perform(); - _context2.next = 20; - return AsyncStorage.setItem(versionKey, migration.version.toString()); - - case 20: - _iteratorNormalCompletion = true; - _context2.next = 13; - break; - - case 23: - _context2.next = 29; - break; - - case 25: - _context2.prev = 25; - _context2.t0 = _context2['catch'](11); - _didIteratorError = true; - _iteratorError = _context2.t0; - - case 29: - _context2.prev = 29; - _context2.prev = 30; - - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); - } - - case 32: - _context2.prev = 32; - - if (!_didIteratorError) { - _context2.next = 35; - break; - } - - throw _iteratorError; - - case 35: - return _context2.finish(32); - - case 36: - return _context2.finish(29); - - case 37: - case 'end': - return _context2.stop(); - } - } - }, _callee2, this, [[11, 25, 29, 37], [30,, 32, 36]]); - })); - - function migrate() { - return ref.apply(this, arguments); - } - - return migrate; - }() - }, { - key: 'model', - value: function model(modelName) { - return new Model(modelName, this.dbName); - } - - // clear store - - }, { - key: 'clear', - value: function () { - var ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee3() { - return regeneratorRuntime.wrap(function _callee3$(_context3) { - while (1) { - switch (_context3.prev = _context3.next) { - case 0: - _context3.next = 2; - return AsyncStorage.removeItem(this.dbName); - - case 2: - case 'end': - return _context3.stop(); - } - } - }, _callee3, this); - })); - - function clear() { - return ref.apply(this, arguments); - } - - return clear; - }() - }]); - - return Store; -}(); - -module.exports = Store; - -// Store.model("user").get({ id:1 },{fite}).then().fail(); \ No newline at end of file diff --git a/lib/util.js b/lib/util.js deleted file mode 100644 index e13d1cc..0000000 --- a/lib/util.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - log: function log(msg) { - console.log(msg); - }, - error: function error(msg) { - console.error(msg); - } -}; \ No newline at end of file diff --git a/package.json b/package.json index 8dbd819..e169998 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "react-native-store", "version": "0.4.2", "description": "A simple database base on react-native AsyncStorage.", - "main": "./lib/index.js", + "main": "./src/index.js", "scripts": { "watch": "babel src --watch --presets es2015,stage-0 --out-dir lib", "build": "babel src --presets es2015,stage-0 --out-dir lib", From eda88456ac5034bb929371ad74d19932ddbb0ba3 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Wed, 6 Apr 2016 16:22:28 +0800 Subject: [PATCH 4/4] Add new setting --- .npmignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.npmignore b/.npmignore index 62adcfa..87d6313 100644 --- a/.npmignore +++ b/.npmignore @@ -13,5 +13,3 @@ psd thumb sketch coverage - -src