Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Revamp to ignore the db.version number, which is apparently ignored o…

…n Safari.

Instead, a table is created with a single value and that is used instead.  Besides
a slight increase in complexity, there are really no downsides.  Also added debug helpers,
callbacks, updated the example file.  Works on Chrome Linux and Windows Safari, at least.
  • Loading branch information...
commit de8de4da415185ffa59cba77e485f51d85aa63b6 1 parent a16de53
@nanodeath authored
Showing with 164 additions and 55 deletions.
  1. +125 −23 migrator.js
  2. +39 −32 test.html
View
148 migrator.js
@@ -1,25 +1,127 @@
function Migrator(db){
- var migrations = [];
- this.migration = function(number, func){
- migrations[number] = func;
- };
- var doMigration = function(number){
- if(migrations[number]){
- db.changeVersion(db.version, String(number), function(t){
- migrations[number](t);
- }, function(err){
- if(console.error) console.error("Error!: %o (while upgrading to %d)", err, number);
- }, function(){
- doMigration(number+1);
- });
- }
- };
- this.doIt = function(){
- var initialVersion = parseInt(db.version) || 0;
- try {
- doMigration(initialVersion+1);
- } catch(e) {
- if(console.error) console.error(e);
- }
- }
+ // Pending migrations to run
+ var migrations = [];
+ // Callbacks to run when migrations done
+ var whenDone = [];
+
+ var state = 0;
+
+ var MIGRATOR_TABLE = "_migrator_schema";
+
+ // Use this method to actually add a migration.
+ // You'll probably want to start with 1 for the migration number.
+ this.migration = function(number, func){
+ migrations[number] = func;
+ };
+
+ // Execute a given migration by index
+ var doMigration = function(number){
+ if(migrations[number]){
+ db.transaction(function(t){
+ t.executeSql("update " + MIGRATOR_TABLE + " set version = ?", [number], function(t){
+ debug(Migrator.DEBUG_HIGH, "Beginning migration %d", [number]);
+ migrations[number](t);
+ debug(Migrator.DEBUG_HIGH, "Completed migration %d", [number]);
+ doMigration(number+1);
+ }, function(t, err){
+ error("Error!: %o (while upgrading to %s from %s)", err, number);
+ })
+ });
+ } else {
+ debug(Migrator.DEBUG_HIGH, "Migrations complete, executing callbacks.");
+ state = 2;
+ executeWhenDoneCallbacks();
+ }
+ };
+
+ // helper that actually calls doMigration from doIt.
+ var migrateStartingWith = function(ver){
+ state = 1;
+ debug(Migrator.DEBUG_LOW, "Main Migrator starting.");
+
+ try {
+ doMigration(ver+1);
+ } catch(e) {
+ error(e);
+ }
+ };
+
+ this.execute = function(){
+ if(state > 0){
+ throw "Migrator is only valid once -- create a new one if you want to do another migration.";
+ }
+ db.transaction(function(t){
+ t.executeSql("select version from _migrator_schema", [], function(t, res){
+ var rows = res.rows;
+ var version = rows.item(0).version;
+ debug(Migrator.DEBUG_HIGH, "Existing database present, migrating from %d", [version]);
+ migrateStartingWith(version);
+ }, function(t, err){
+ if(err.message.match(/no such table/i)){
+ t.executeSql("create table " + MIGRATOR_TABLE + "(version integer)", [], function(){
+ t.executeSql("insert into " + MIGRATOR_TABLE + " values(0)", [], function(){
+ debug(Migrator.DEBUG_HIGH, "New migration database created...");
+ migrateStartingWith(0);
+ }, function(t, err){
+ error("Unrecoverable error inserting initial version into db: %o", err);
+ });
+ }, function(t, err){
+ error("Unrecoverable error creating version table: %o", err);
+ });
+ } else {
+ error("Unrecoverable error resolving schema version: %o", err);
+ }
+ });
+ });
+
+ return this;
+ };
+
+ // Called when the migration has completed. If the migration has already completed,
+ // executes immediately. Otherwise, waits.
+ this.whenDone = function(func){
+ if(typeof func !== "array"){
+ func = [func];
+ }
+ for(var f in func){
+ whenDone.push(func[f]);
+ }
+ if(state > 1){
+ debug(Migrator.DEBUG_LOW, "Executing 'whenDone' tasks immediately as the migrations have already finished.");
+ executeWhenDoneCallbacks();
+ }
+ };
+
+ var executeWhenDoneCallbacks = function(){
+ for(var f in whenDone){
+ whenDone[f]();
+ }
+ debug(Migrator.DEBUG_LOW, "Callbacks complete.");
+ }
+
+ // Debugging stuff.
+ var log = (window.console && console.log) ? function() { console.log.apply(console, argumentsToArray(arguments)) } : function(){};
+ var error = (window.console && console.error) ? function() { console.error.apply(console, argumentsToArray(arguments)) } : function(){};
+
+ var debugLevel = Migrator.DEBUG_NONE;
+
+ var argumentsToArray = function(args) { return Array.prototype.slice.call(args); };
+ this.setDebugLevel = function(level){
+ debugLevel = level;
+ }
+
+ var debug = function(minLevel, message, args){
+ if(debugLevel >= minLevel){
+ var newArgs = [message];
+ if(args != null) for(var i in args) newArgs.push(args[i]);
+
+ log.apply(null, newArgs);
+ }
+ }
}
+
+// no output, low threshold (lots of output), or high threshold (just log the weird stuff)
+// these might be a little, uh, backwards
+Migrator.DEBUG_NONE = 0;
+Migrator.DEBUG_LOW = 1;
+Migrator.DEBUG_HIGH = 2;
View
71 test.html
@@ -9,26 +9,31 @@
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.js"></script>
<script type="text/javascript" src="migrator.js"></script>
<script type="text/javascript">
- var db = openDatabase("example_db", "", "Example Database", 100000);
- // now db is a database object: http://dev.w3.org/html5/webdatabase/#database
+ if(window.openDatabase){
+ var db = openDatabase("example_db", "", "Example Database", 100000);
+ // now db is a database object: http://dev.w3.org/html5/webdatabase/#database
- var M = new Migrator(db);
- M.migration(1, function(t){
- // t here is a transaction object: http://dev.w3.org/html5/webdatabase/#sqltransaction
- t.executeSql("create table user(id integer primary key, name text)");
- t.executeSql("insert into user(name) values('max')");
- });
- M.migration(2, function(t){
- t.executeSql("alter table user add column phone text");
- t.executeSql("update user set phone = '555-5555' where name == 'max'");
- });
- M.migration(3, function(t){
- t.executeSql("insert into user(name, phone) values('jeremy', '555-1234')");
- t.executeSql("update user set phone = '555-5556' where name == 'max'");
- });
+ var M = new Migrator(db);
+ M.setDebugLevel(Migrator.DEBUG_HIGH);
+ M.migration(1, function(t){
+ // t here is a transaction object: http://dev.w3.org/html5/webdatabase/#sqltransaction
+ t.executeSql("create table user(id integer primary key, name text)");
+ t.executeSql("insert into user(name) values('max')");
+ });
+ M.migration(2, function(t){
+ t.executeSql("alter table user add column phone text");
+ t.executeSql("update user set phone = '555-5555' where name == 'max'");
+ });
+ M.migration(3, function(t){
+ t.executeSql("insert into user(name, phone) values('jeremy', '555-1234')");
+ t.executeSql("update user set phone = '555-5556' where name == 'max'");
+ });
- // This executes the applicable transactions
- M.doIt();
+ // This executes the applicable transactions
+ M.execute();
+ } else {
+ $("p:eq(0)").text("Oops! HTML5 databases are not supported with your browser.");
+ }
</script>
<h1>Address Book</h1>
<p>Here are all the numbers in the address book...</p>
@@ -39,18 +44,20 @@
<th>Phone</th>
</tr>
</table>
- <script type="text/javascript">
- db.readTransaction(function(t){
- t.executeSql("select id, name, phone from user", [], function(t, res){
- var rows = res.rows;
- var addressTable = $("table.address_book");
- for(var i = 0; i < rows.length; i++){
- var row = rows.item(i);
- var domRow = $("<tr><td>"+row.id+"</td><td>"+row.name+"</td><td>"+row.phone+"</td></tr>");
- domRow.appendTo(addressTable);
- }
- });
- });
- </script>
+ <script type="text/javascript">
+ M.whenDone(function(){
+ db.transaction(function(t){
+ t.executeSql("select id, name, phone from user", [], function(t, res){
+ var rows = res.rows;
+ var addressTable = $("table.address_book");
+ for(var i = 0; i < rows.length; i++){
+ var row = rows.item(i);
+ var domRow = $("<tr><td>"+row.id+"</td><td>"+row.name+"</td><td>"+row.phone+"</td></tr>");
+ domRow.appendTo(addressTable);
+ }
+ });
+ });
+ });
+ </script>
</body>
-</html>
+</html>
Please sign in to comment.
Something went wrong with that request. Please try again.