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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/README.md +36 -0
- data/Rakefile +2 -0
- data/lib/generators/persistence/install_generator.rb +22 -0
- data/lib/generators/persistence/templates/application.js +10 -0
- data/lib/persistence-rails.rb +1 -0
- data/lib/persistence/rails.rb +8 -0
- data/lib/persistence/rails/engine.rb +6 -0
- data/lib/persistence/rails/version.rb +5 -0
- data/persistence-rails.gemspec +17 -0
- data/vendor/assets/javascript/persistence.all.js +16 -0
- data/vendor/assets/javascript/persistence.core.js +2419 -0
- data/vendor/assets/javascript/persistence.jquery.js +103 -0
- data/vendor/assets/javascript/persistence.jquery.mobile.js +256 -0
- data/vendor/assets/javascript/persistence.js +5 -0
- data/vendor/assets/javascript/persistence.migrations.js +303 -0
- data/vendor/assets/javascript/persistence.pool.js +47 -0
- data/vendor/assets/javascript/persistence.search.js +293 -0
- data/vendor/assets/javascript/persistence.store.appengine.js +412 -0
- data/vendor/assets/javascript/persistence.store.config.js +29 -0
- data/vendor/assets/javascript/persistence.store.memory.js +239 -0
- data/vendor/assets/javascript/persistence.store.mysql.js +127 -0
- data/vendor/assets/javascript/persistence.store.sql.js +900 -0
- data/vendor/assets/javascript/persistence.store.sqlite.js +123 -0
- data/vendor/assets/javascript/persistence.store.sqlite3.js +124 -0
- data/vendor/assets/javascript/persistence.store.titanium.js +193 -0
- data/vendor/assets/javascript/persistence.store.websql.js +218 -0
- data/vendor/assets/javascript/persistence.sync.js +353 -0
- 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,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
|
+
}());
|