persistence-rails 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/README.md +36 -0
  4. data/Rakefile +2 -0
  5. data/lib/generators/persistence/install_generator.rb +22 -0
  6. data/lib/generators/persistence/templates/application.js +10 -0
  7. data/lib/persistence-rails.rb +1 -0
  8. data/lib/persistence/rails.rb +8 -0
  9. data/lib/persistence/rails/engine.rb +6 -0
  10. data/lib/persistence/rails/version.rb +5 -0
  11. data/persistence-rails.gemspec +17 -0
  12. data/vendor/assets/javascript/persistence.all.js +16 -0
  13. data/vendor/assets/javascript/persistence.core.js +2419 -0
  14. data/vendor/assets/javascript/persistence.jquery.js +103 -0
  15. data/vendor/assets/javascript/persistence.jquery.mobile.js +256 -0
  16. data/vendor/assets/javascript/persistence.js +5 -0
  17. data/vendor/assets/javascript/persistence.migrations.js +303 -0
  18. data/vendor/assets/javascript/persistence.pool.js +47 -0
  19. data/vendor/assets/javascript/persistence.search.js +293 -0
  20. data/vendor/assets/javascript/persistence.store.appengine.js +412 -0
  21. data/vendor/assets/javascript/persistence.store.config.js +29 -0
  22. data/vendor/assets/javascript/persistence.store.memory.js +239 -0
  23. data/vendor/assets/javascript/persistence.store.mysql.js +127 -0
  24. data/vendor/assets/javascript/persistence.store.sql.js +900 -0
  25. data/vendor/assets/javascript/persistence.store.sqlite.js +123 -0
  26. data/vendor/assets/javascript/persistence.store.sqlite3.js +124 -0
  27. data/vendor/assets/javascript/persistence.store.titanium.js +193 -0
  28. data/vendor/assets/javascript/persistence.store.websql.js +218 -0
  29. data/vendor/assets/javascript/persistence.sync.js +353 -0
  30. metadata +76 -0
@@ -0,0 +1,103 @@
1
+ //= require persistence.core
2
+
3
+ /**
4
+ * Copyright (c) 2010 Roberto Saccon <rsaccon@gmail.com>
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person
7
+ * obtaining a copy of this software and associated documentation
8
+ * files (the "Software"), to deal in the Software without
9
+ * restriction, including without limitation the rights to use,
10
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the
12
+ * Software is furnished to do so, subject to the following
13
+ * conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be
16
+ * included in all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25
+ * OTHER DEALINGS IN THE SOFTWARE.
26
+ */
27
+
28
+ if (!window.jQuery) {
29
+ throw new Error("jQuery should be loaded before persistence.jquery.js");
30
+ }
31
+
32
+ if (!window.persistence) {
33
+ throw new Error("persistence.js should be loaded before persistence.jquery.js");
34
+ }
35
+
36
+ persistence.jquery = {};
37
+
38
+ /**
39
+ * crossbrowser implementation for entity-property
40
+ */
41
+ persistence.defineProp = function(scope, field, setterCallback, getterCallback) {
42
+ scope[field] = function(value) {
43
+ if (value === undefined) {
44
+ return getterCallback();
45
+ } else {
46
+ setterCallback(value);
47
+ return scope;
48
+ }
49
+ };
50
+ };
51
+
52
+ /**
53
+ * crossbrowser implementation for entity-property setter
54
+ */
55
+ persistence.set = function(scope, fieldName, value) {
56
+ if (persistence.isImmutable(fieldName)) throw new Error("immutable field: "+fieldName);
57
+ scope[fieldName](value);
58
+ return scope;
59
+ };
60
+
61
+ /**
62
+ * crossbrowser implementation for entity-property getter
63
+ */
64
+ persistence.get = function(arg1, arg2) {
65
+ var val = (arguments.length == 1) ? arg1 : arg1[arg2];
66
+ return (typeof val === "function") ? val() : val;
67
+ };
68
+
69
+
70
+ (function($){
71
+ var originalDataMethod = $.fn.data;
72
+
73
+ $.fn.data = function(name, data) {
74
+ if (this[0] && this[0]._session && (this[0]._session === window.persistence)) {
75
+ if (data) {
76
+ this[0][name](data);
77
+ return this;
78
+ } else {
79
+ return this[0][name]();
80
+ }
81
+ } else {
82
+ return originalDataMethod.apply(this, arguments);
83
+ }
84
+ };
85
+
86
+ if (persistence.sync) {
87
+ persistence.sync.getJSON = function(url, success) {
88
+ $.getJSON(url, null, success);
89
+ };
90
+
91
+ persistence.sync.postJSON = function(url, data, success) {
92
+ $.ajax({
93
+ url: url,
94
+ type: 'POST',
95
+ data: data,
96
+ dataType: 'json',
97
+ success: function(response) {
98
+ success(JSON.parse(response));
99
+ }
100
+ });
101
+ };
102
+ }
103
+ })(jQuery);
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Copyright (c) 2010 Roberto Saccon <rsaccon@gmail.com>
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person
5
+ * obtaining a copy of this software and associated documentation
6
+ * files (the "Software"), to deal in the Software without
7
+ * restriction, including without limitation the rights to use,
8
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the
10
+ * Software is furnished to do so, subject to the following
11
+ * conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
+ * OTHER DEALINGS IN THE SOFTWARE.
24
+ */
25
+
26
+ if (!window.persistence.jquery) {
27
+ throw new Error("persistence.jquery.js should be loaded before persistence.jquery.mobile.js");
28
+ }
29
+
30
+ persistence.jquery.mobile = {};
31
+
32
+ (function($){
33
+ var $pjqm = persistence.jquery.mobile;
34
+
35
+ if (window.openDatabase) {
36
+ $pjqm.pageEntityName = "Page";
37
+ $pjqm.imageEntityName = "Image";
38
+ $pjqm.pathField = "path";
39
+ $pjqm.dataField = "data";
40
+
41
+ var originalAjaxMethod = $.ajax;
42
+
43
+ function expand(docPath, srcPath) {
44
+ var basePath = (/\/$/.test(location.pathname) || (location.pathname == "")) ?
45
+ location.pathname :
46
+ location.pathname.substring(0, location.pathname.lastIndexOf("/"));
47
+ if (/^\.\.\//.test(srcPath)) {
48
+ // relative path with upward directory traversal
49
+ var count = 1, splits = docPath.split("/");
50
+ while (/^\.\.\//.test(srcPath)) {
51
+ srcPath = srcPath.substring(3);
52
+ count++;
53
+ }
54
+ return basePath + ((count >= splits.length) ?
55
+ srcPath :
56
+ splits.slice(0, splits.length-count).join("/") + "/" + srcPath);
57
+ } else if (/^\//.test(srcPath)) {
58
+ // absolute path
59
+ return srcPath;
60
+ } else {
61
+ // relative path without directory traversal
62
+ return basePath + docPath + "/" + srcPath;
63
+ }
64
+ }
65
+
66
+ function base64Image(img, type) {
67
+ var canvas = document.createElement("canvas");
68
+ canvas.width = img.width;
69
+ canvas.height = img.height;
70
+
71
+ // Copy the image contents to the canvas
72
+ var ctx = canvas.getContext("2d");
73
+ ctx.drawImage(img, 0, 0);
74
+
75
+ return canvas.toDataURL("image/" + type);
76
+ }
77
+
78
+ // parseUri 1.2.2
79
+ // (c) Steven Levithan <stevenlevithan.com>
80
+ // MIT License
81
+
82
+ var parseUriOptions = {
83
+ strictMode: false,
84
+ key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
85
+ q: {
86
+ name: "queryKey",
87
+ parser: /(?:^|&)([^&=]*)=?([^&]*)/g
88
+ },
89
+ parser: {
90
+ strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
91
+ loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
92
+ }
93
+ };
94
+
95
+ function parseUri (str) {
96
+ var o = parseUriOptions,
97
+ m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
98
+ uri = {},
99
+ i = 14;
100
+
101
+ while (i--) uri[o.key[i]] = m[i] || "";
102
+
103
+ uri[o.q.name] = {};
104
+ uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
105
+ if ($1) uri[o.q.name][$1] = $2;
106
+ });
107
+
108
+ return uri;
109
+ }
110
+
111
+ function getImageType(parsedUri) {
112
+ if (parsedUri.queryKey.type) {
113
+ return parsedUri.queryKey.type;
114
+ } else {
115
+ return (/\.png$/i.test(parsedUri.path)) ? "png" : "jpeg";
116
+ }
117
+ }
118
+
119
+ $.ajax = function(settings) {
120
+ var parsedUrl = parseUri(settings.url);
121
+ var entities = {}, urlPathSegments = parsedUrl.path.split("/");
122
+ if ((settings.type == "post") && (urlPathSegments.length > 1)) {
123
+ var entityName = (urlPathSegments[1].charAt(0).toUpperCase() + urlPathSegments[1].substring(1));
124
+ if (persistence.isDefined(entityName)) {
125
+ var Form = persistence.define(entityName);
126
+
127
+ var persistFormData = function() {
128
+ var obj = {};
129
+ settings.data.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, function ( $0, $1, $2 ) {
130
+ if ($1) {
131
+ obj[$1] = $2;
132
+ }
133
+ });
134
+
135
+ var entity = new Form(obj);
136
+ persistence.add(entity);
137
+ persistence.flush();
138
+ };
139
+
140
+ if (!navigator.hasOwnProperty("onLine") || navigator.onLine) {
141
+ originalAjaxMethod({
142
+ url: settings.url,
143
+ success: function(data) {
144
+ settings.success(data);
145
+ persistFormData();
146
+ },
147
+ error: settings.error
148
+ });
149
+ } else {
150
+ persistFormData();
151
+ }
152
+ } else {
153
+ originalAjaxMethod(settings);
154
+ }
155
+ } else if (persistence.urlExcludeRx && persistence.urlExcludeRx.test(parsedUrl.path)) {
156
+ originalAjaxMethod(settings);
157
+ } else {
158
+ if (persistence.isDefined($pjqm.pageEntityName)) {
159
+ var Page = persistence.define($pjqm.pageEntityName);
160
+ Page.findBy($pjqm.pathField, settings.url, function(page) {
161
+ if (page) {
162
+ //
163
+ // load page and images from persistencejs
164
+ //
165
+ if (settings.success) {
166
+ var pos = 0, countOuter = 0, countInner = 0;
167
+ var inStr = page[$pjqm.dataField](), outStr = "";
168
+ var regExp = /(<img[^>]+src\s*=\s*[\'\"])([^\'\"]+)([\'\"][^>]*>)/ig;
169
+ var replaced = inStr.replace(regExp, function($0, $1, $2, $3, offset) {
170
+ countOuter++;
171
+ if (persistence.isDefined($pjqm.imageEntityName)) {
172
+ var Img = persistence.define($pjqm.imageEntityName);
173
+ Img.findBy($pjqm.pathField, expand(settings.url, $2), function(image){
174
+ countInner++;
175
+ if (image) {
176
+ var imgTagStr = $1 + image[$pjqm.dataField]() + $3;
177
+ outStr += inStr.substring(pos, offset) + imgTagStr;
178
+ pos = offset + imgTagStr.length;
179
+ } else {
180
+ outStr += inStr.substring(pos, offset) + imgTagStr;
181
+ pos = offset;
182
+ }
183
+ if (countInner == countOuter) {
184
+ settings.success(outStr);
185
+ }
186
+ return "";
187
+ });
188
+ } else {
189
+ outStr += inStr.substring(pos, offset) + imgTagStr;
190
+ pos = offset;
191
+ }
192
+ });
193
+ if (replaced == inStr) {
194
+ settings.success(inStr);
195
+ } else if (!persistence.isDefined($pjqm.imageEntityName)) {
196
+ settings.success(outStr);
197
+ };
198
+ }
199
+ } else {
200
+ //
201
+ // ajax-load page and persist page and images
202
+ //
203
+ originalAjaxMethod({
204
+ url: settings.url,
205
+ success: function(data) {
206
+ settings.success(data);
207
+ if (persistence.isDefined($pjqm.pageEntityName)) {
208
+ var entities = [], crawlImages = false;
209
+ var Page = persistence.define($pjqm.pageEntityName);
210
+ if (persistence.isDefined($pjqm.imageEntityName)) {
211
+ var Img = persistence.define($pjqm.imageEntityName), count = 0;
212
+ $("#"+settings.url.replace(/\//g,"\\/").replace(/\./g,"\\.")+" img").each(function(i, img){
213
+ crawlImages = true;
214
+ count++;
215
+ $(img).load(function() {
216
+ var obj = {}, parsedImgSrc = parseUri(img.src);
217
+ obj[$pjqm.pathField] = parsedImgSrc.path;
218
+ obj[$pjqm.dataField] = base64Image(img, getImageType(parsedImgSrc));
219
+ entities.push(new Img(obj));
220
+
221
+ if (crawlImages && (--count == 0)) {
222
+ for (var j=0; j<entities.length; j++) {
223
+ persistence.add(entities[j]);
224
+ }
225
+ persistence.flush();
226
+ }
227
+ });
228
+ $(img).error(function() {
229
+ crawlImages = false;
230
+ });
231
+ });
232
+ }
233
+
234
+ var obj = {};
235
+ obj[$pjqm.pathField] = settings.url;
236
+ obj[$pjqm.dataField] = data;
237
+
238
+ entities.push(new Page(obj));
239
+
240
+ if (!crawlImages) {
241
+ persistence.add(entities[0]);
242
+ persistence.flush();
243
+ }
244
+ }
245
+ },
246
+ error: settings.error
247
+ });
248
+ }
249
+ });
250
+ } else {
251
+ originalAjaxMethod(settings);
252
+ }
253
+ }
254
+ };
255
+ }
256
+ })(jQuery);
@@ -0,0 +1,5 @@
1
+ //= require persistence.core
2
+ //= require persistence.store.sql
3
+ //= require persistence.store.websql
4
+ //= require persistence.sync
5
+ //= require persistence.search
@@ -0,0 +1,303 @@
1
+ //= require persistence.core
2
+
3
+ /**
4
+ * @license
5
+ * Copyright (c) 2010 Fábio Rehm <fgrehm@gmail.com>
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person
8
+ * obtaining a copy of this software and associated documentation
9
+ * files (the "Software"), to deal in the Software without
10
+ * restriction, including without limitation the rights to use,
11
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the
13
+ * Software is furnished to do so, subject to the following
14
+ * conditions:
15
+ *
16
+ * The above copyright notice and this permission notice shall be
17
+ * included in all copies or substantial portions of the Software.
18
+ *
19
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26
+ * OTHER DEALINGS IN THE SOFTWARE.
27
+ */
28
+
29
+ if(!window.persistence) { // persistence.js not loaded!
30
+ throw new Error("persistence.js should be loaded before persistence.migrations.js");
31
+ }
32
+
33
+ (function() {
34
+
35
+ var Migrator = {
36
+ migrations: [],
37
+
38
+ version: function(callback) {
39
+ persistence.transaction(function(t){
40
+ t.executeSql('SELECT current_version FROM schema_version', null, function(result){
41
+ if (result.length == 0) {
42
+ t.executeSql('INSERT INTO schema_version VALUES (0)', null, function(){
43
+ callback(0);
44
+ });
45
+ } else {
46
+ callback(result[0].current_version);
47
+ }
48
+ });
49
+ });
50
+ },
51
+
52
+ setVersion: function(v, callback) {
53
+ persistence.transaction(function(t){
54
+ t.executeSql('UPDATE schema_version SET current_version = ?', [v], function(){
55
+ Migrator._version = v;
56
+ if (callback) callback();
57
+ });
58
+ });
59
+ },
60
+
61
+ setup: function(callback) {
62
+ persistence.transaction(function(t){
63
+ t.executeSql('CREATE TABLE IF NOT EXISTS schema_version (current_version INTEGER)', null, function(){
64
+ // Creates a dummy migration just to force setting schema version when cleaning DB
65
+ Migrator.migration(0, { up: function() { }, down: function() { } });
66
+ if (callback) callback();
67
+ });
68
+ });
69
+ },
70
+
71
+ // Method should only be used for testing
72
+ reset: function(callback) {
73
+ // Creates a dummy migration just to force setting schema version when cleaning DB
74
+ Migrator.migrations = [];
75
+ Migrator.migration(0, { up: function() { }, down: function() { } });
76
+ Migrator.setVersion(0, callback);
77
+ },
78
+
79
+ migration: function(version, actions) {
80
+ Migrator.migrations[version] = new Migration(version, actions);
81
+ return Migrator.migrations[version];
82
+ },
83
+
84
+ migrateUpTo: function(version, callback) {
85
+ var migrationsToRun = [];
86
+
87
+ function migrateOne() {
88
+ var migration = migrationsToRun.pop();
89
+
90
+ if (!migration) callback();
91
+
92
+ migration.up(function(){
93
+ if (migrationsToRun.length > 0) {
94
+ migrateOne();
95
+ } else if (callback) {
96
+ callback();
97
+ }
98
+ });
99
+ }
100
+
101
+ this.version(function(currentVersion){
102
+ for (var v = currentVersion+1; v <= version; v++)
103
+ migrationsToRun.unshift(Migrator.migrations[v]);
104
+
105
+ if (migrationsToRun.length > 0) {
106
+ migrateOne();
107
+ } else if (callback) {
108
+ callback();
109
+ }
110
+ });
111
+ },
112
+
113
+ migrateDownTo: function(version, callback) {
114
+ var migrationsToRun = [];
115
+
116
+ function migrateOne() {
117
+ var migration = migrationsToRun.pop();
118
+
119
+ if (!migration) callback();
120
+
121
+ migration.down(function(){
122
+ if (migrationsToRun.length > 0) {
123
+ migrateOne();
124
+ } else if (callback) {
125
+ callback();
126
+ }
127
+ });
128
+ }
129
+
130
+ this.version(function(currentVersion){
131
+ for (var v = currentVersion; v > version; v--)
132
+ migrationsToRun.unshift(Migrator.migrations[v]);
133
+
134
+ if (migrationsToRun.length > 0) {
135
+ migrateOne();
136
+ } else if (callback) {
137
+ callback();
138
+ }
139
+ });
140
+ },
141
+
142
+ migrate: function(version, callback) {
143
+ if ( arguments.length === 1 ) {
144
+ callback = version;
145
+ version = this.migrations.length-1;
146
+ }
147
+
148
+ this.version(function(curVersion){
149
+ if (curVersion < version)
150
+ Migrator.migrateUpTo(version, callback);
151
+ else if (curVersion > version)
152
+ Migrator.migrateDownTo(version, callback);
153
+ else
154
+ callback();
155
+ });
156
+ }
157
+ }
158
+
159
+ var Migration = function(version, body) {
160
+ this.version = version;
161
+ // TODO check if actions contains up and down methods
162
+ this.body = body;
163
+ this.actions = [];
164
+ };
165
+
166
+ Migration.prototype.executeActions = function(callback, customVersion) {
167
+ var actionsToRun = this.actions;
168
+ var version = (customVersion!==undefined) ? customVersion : this.version;
169
+
170
+ persistence.transaction(function(tx){
171
+ function nextAction() {
172
+ if (actionsToRun.length == 0)
173
+ Migrator.setVersion(version, callback);
174
+ else {
175
+ var action = actionsToRun.pop();
176
+ action(tx, nextAction);
177
+ }
178
+ }
179
+
180
+ nextAction();
181
+ });
182
+ }
183
+
184
+ Migration.prototype.up = function(callback) {
185
+ if (this.body.up) this.body.up.apply(this);
186
+ this.executeActions(callback);
187
+ }
188
+
189
+ Migration.prototype.down = function(callback) {
190
+ if (this.body.down) this.body.down.apply(this);
191
+ this.executeActions(callback, this.version-1);
192
+ }
193
+
194
+ Migration.prototype.createTable = function(tableName, callback) {
195
+ var table = new ColumnsHelper();
196
+
197
+ if (callback) callback(table);
198
+
199
+ var column;
200
+ var sql = 'CREATE TABLE ' + tableName + ' (id VARCHAR(32) PRIMARY KEY';
201
+ while (column = table.columns.pop())
202
+ sql += ', ' + column;
203
+
204
+ this.executeSql(sql + ')');
205
+ }
206
+
207
+ Migration.prototype.dropTable = function(tableName) {
208
+ var sql = 'DROP TABLE ' + tableName;
209
+ this.executeSql(sql);
210
+ }
211
+
212
+ Migration.prototype.addColumn = function(tableName, columnName, columnType) {
213
+ var sql = 'ALTER TABLE ' + tableName + ' ADD ' + columnName + ' ' + columnType;
214
+ this.executeSql(sql);
215
+ }
216
+
217
+ Migration.prototype.removeColumn = function(tableName, columnName) {
218
+ this.action(function(tx, nextCommand){
219
+ var sql = 'select sql from sqlite_master where type = "table" and name == "'+tableName+'"';
220
+ tx.executeSql(sql, null, function(result){
221
+ var columns = new RegExp("CREATE TABLE `\\w+` |\\w+ \\((.+)\\)").exec(result[0].sql)[1].split(', ');
222
+ var selectColumns = [];
223
+ var columnsSql = [];
224
+
225
+ for (var i = 0; i < columns.length; i++) {
226
+ var colName = new RegExp("((`\\w+`)|(\\w+)) .+").exec(columns[i])[1];
227
+ if (colName == columnName) continue;
228
+
229
+ columnsSql.push(columns[i]);
230
+ selectColumns.push(colName);
231
+ }
232
+ columnsSql = columnsSql.join(', ');
233
+ selectColumns = selectColumns.join(', ');
234
+
235
+ var queries = [];
236
+ queries.unshift(["ALTER TABLE " + tableName + " RENAME TO " + tableName + "_bkp;", null]);
237
+ queries.unshift(["CREATE TABLE " + tableName + " (" + columnsSql + ");", null]);
238
+ queries.unshift(["INSERT INTO " + tableName + " SELECT " + selectColumns + " FROM " + tableName + "_bkp;", null]);
239
+ queries.unshift(["DROP TABLE " + tableName + "_bkp;", null]);
240
+
241
+ persistence.executeQueriesSeq(tx, queries, nextCommand);
242
+ });
243
+ });
244
+ }
245
+
246
+ Migration.prototype.addIndex = function(tableName, columnName, unique) {
247
+ var sql = 'CREATE ' + (unique === true ? 'UNIQUE' : '') + ' INDEX ' + tableName + '_' + columnName + ' ON ' + tableName + ' (' + columnName + ')';
248
+ this.executeSql(sql);
249
+ }
250
+
251
+ Migration.prototype.removeIndex = function(tableName, columnName) {
252
+ var sql = 'DROP INDEX ' + tableName + '_' + columnName;
253
+ this.executeSql(sql);
254
+ }
255
+
256
+ Migration.prototype.executeSql = function(sql, args) {
257
+ this.action(function(tx, nextCommand){
258
+ tx.executeSql(sql, args, nextCommand);
259
+ });
260
+ }
261
+
262
+ Migration.prototype.action = function(callback) {
263
+ this.actions.unshift(callback);
264
+ }
265
+
266
+ var ColumnsHelper = function() {
267
+ this.columns = [];
268
+ }
269
+
270
+ ColumnsHelper.prototype.text = function(columnName) {
271
+ this.columns.unshift(columnName + ' TEXT');
272
+ }
273
+
274
+ ColumnsHelper.prototype.integer = function(columnName) {
275
+ this.columns.unshift(columnName + ' INT');
276
+ }
277
+
278
+ ColumnsHelper.prototype.real = function(columnName) {
279
+ this.columns.unshift(columnName + ' REAL');
280
+ }
281
+
282
+ ColumnsHelper.prototype['boolean'] = function(columnName) {
283
+ this.columns.unshift(columnName + ' BOOL');
284
+ }
285
+
286
+ ColumnsHelper.prototype.date = function(columnName) {
287
+ this.columns.unshift(columnName + ' DATE');
288
+ }
289
+
290
+ ColumnsHelper.prototype.json = function(columnName) {
291
+ this.columns.unshift(columnName + ' TEXT');
292
+ }
293
+
294
+ // Makes Migrator and Migration available to tests
295
+ persistence.migrations = {};
296
+ persistence.migrations.Migrator = Migrator;
297
+ persistence.migrations.Migration = Migration;
298
+ persistence.migrations.init = function() { Migrator.setup.apply(Migrator, Array.prototype.slice.call(arguments, 0))};
299
+
300
+ persistence.migrate = function() { Migrator.migrate.apply(Migrator, Array.prototype.slice.call(arguments, 0))};
301
+ persistence.defineMigration = function() { Migrator.migration.apply(Migrator, Array.prototype.slice.call(arguments, 0))};
302
+
303
+ }());