persistence-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }());