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