Skip to content

Commit 5d65cb7

Browse files
committed
refactor(files): First write down of the walker and separating file API
Refers to #215 Signed-off-by: Tobias Gurtzick <[email protected]>
1 parent 5450eb4 commit 5d65cb7

File tree

2 files changed

+295
-0
lines changed

2 files changed

+295
-0
lines changed

lib/file.js

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
'use strict';
2+
3+
var fs = require('fs');
4+
var path = require('path');
5+
var log = require('db-migrate-shared').log;
6+
var inflection = require('inflection');
7+
var Promise = require('bluebird');
8+
var lpad = require('db-migrate-shared').util.lpad;
9+
10+
function formatPath (dir, name) {
11+
return path.join(dir, name);
12+
}
13+
14+
function formatName (title, date) {
15+
return formatDate(date) + '-' + formatTitle(title);
16+
}
17+
18+
function formatDate (date) {
19+
return [
20+
date.getUTCFullYear(),
21+
lpad(date.getUTCMonth() + 1, '0', 2),
22+
lpad(date.getUTCDate(), '0', 2),
23+
lpad(date.getUTCHours(), '0', 2),
24+
lpad(date.getUTCMinutes(), '0', 2),
25+
lpad(date.getUTCSeconds(), '0', 2)
26+
].join('');
27+
}
28+
29+
function formatTitle (title) {
30+
return inflection.dasherize(title);
31+
}
32+
33+
function parseDate (name) {
34+
var date = new Date();
35+
var match = name.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})-[^.]+/);
36+
date.setUTCFullYear(match[1]);
37+
date.setUTCDate(match[3]);
38+
date.setUTCMonth(match[2] - 1);
39+
date.setUTCHours(match[4]);
40+
date.setUTCMinutes(match[5]);
41+
date.setUTCSeconds(match[6]);
42+
return date;
43+
}
44+
45+
function parseTitle (name) {
46+
var match = name.match(/\d{14}-([^.]+)/);
47+
var dashed = match[1];
48+
return inflection.humanize(dashed, true);
49+
}
50+
51+
var filesRegEx = /\.js$/;
52+
53+
var File = {
54+
init: function () {
55+
if (arguments.length >= 3) {
56+
this.title = arguments[0];
57+
this.date = arguments[2];
58+
this.name = this.formatName(this.title, this.date);
59+
this.path = this.formatPath(arguments[1], this.name);
60+
this.templateType = arguments[3];
61+
this.internals = arguments[4];
62+
} else if (arguments.length === 2) {
63+
this.path = arguments[0];
64+
this.name = this.parseName(this.path);
65+
this.date = this.parseDate(this.name);
66+
this.title = this.parseTitle(this.name);
67+
this.internals = arguments[1];
68+
}
69+
70+
this._super(this.internals);
71+
},
72+
73+
parseName: function (path) {
74+
var match = path.match(/(\d{14}-[^.]+)(?:\.*?)?/);
75+
return match[1];
76+
},
77+
78+
parseTitle: parseTitle,
79+
parseDate: parseDate,
80+
formatTitle: formatTitle,
81+
formatPath: formatPath,
82+
formatName: formatName
83+
};
84+
85+
File.registerHook = function (Plugin, prefix, internals) {
86+
var plugin = Plugin.hook(prefix + ':hook:require');
87+
internals.parser = internals.parser || {
88+
filesRegEx: filesRegEx,
89+
extensions: 'js'
90+
};
91+
92+
if (!plugin) {
93+
return Promise.resolve(null);
94+
}
95+
96+
return Promise.resolve(plugin)
97+
.map(function (plugin) {
98+
return plugin[prefix + ':hook:require']();
99+
})
100+
.each(function (parser) {
101+
if (parser && parser.extensions) {
102+
internals.parser.extensions =
103+
internals.parser.extensions + '|' + parser.extensions;
104+
}
105+
})
106+
.then(function () {
107+
internals.parser.filesRegEx = new RegExp(
108+
'\\.(' + internals.parser.extensions + ')$'
109+
);
110+
111+
return internals.parser;
112+
});
113+
};
114+
115+
File.loadFromFileystem = function (dir, type, internals) {
116+
log.verbose('loading ' + type + ' from dir', dir);
117+
118+
return fs
119+
.readdirAsync(dir)
120+
.filter(function (files) {
121+
return internals.parser.filesRegEx.test(files);
122+
})
123+
.then(function (files) {
124+
return files.sort();
125+
})
126+
.map(function (file) {
127+
return new File(path.join(dir, file), internals);
128+
});
129+
};
130+
131+
File.loadFromDatabase = function (dir, type, loader, internals) {
132+
log.verbose('loading ' + type + ' from database');
133+
return loader()
134+
.catch(function (err) {
135+
if (internals.dryRun) {
136+
return [];
137+
} else {
138+
return Promise.reject(err);
139+
}
140+
})
141+
.filter(function (result) {
142+
return (
143+
result.name.substr(0, result.name.lastIndexOf('/')) ===
144+
internals.matching
145+
);
146+
})
147+
.map(function (result) {
148+
return new File(path.join(dir, result.name), internals);
149+
});
150+
};
151+
152+
Promise.promisifyAll(fs);
153+
154+
module.exports = File;

lib/walker.js

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
var dbmUtil = require('db-migrate-shared').util;
2+
var log = require('db-migrate-shared').log;
3+
var Promise = require('bluebird');
4+
var File = require('./file.js');
5+
6+
// Not sure what will happen to this yet
7+
function SeedLink(driver, internals) {
8+
this.seeder = require('./seeder.js')(
9+
driver,
10+
internals.argv['vcseeder-dir'],
11+
true,
12+
internals
13+
);
14+
this.internals = internals;
15+
this.links = [];
16+
}
17+
18+
var Walker = function(driver, directory, interface, empty, intern) {
19+
this.driver = dbmUtil.reduceToInterface(driver, interface);
20+
this._driver = driver;
21+
this.directory = directory;
22+
this.internals = intern;
23+
24+
// keep it until we decide how we do the cross linking
25+
if (intern.linked === false) {
26+
this.seedLink = new SeedLink(driver, intern);
27+
intern.linked = true;
28+
}
29+
};
30+
31+
Walker.prototype = {
32+
createMigrationsTable: function(callback) {
33+
this._driver.createMigrationsTable(callback);
34+
},
35+
36+
writeMigrationRecord: function(migration, callback) {
37+
function onComplete(err) {
38+
if (err) {
39+
log.error(this.prefix + migration.name, err);
40+
} else {
41+
log.info(this.prefix + 'Processed', migration.name);
42+
}
43+
callback(err);
44+
}
45+
this._driver.addMigrationRecord(
46+
this.internals.matching + '/' + migration.name,
47+
onComplete
48+
);
49+
},
50+
51+
deleteMigrationRecord: function(migration, callback) {
52+
function onComplete(err) {
53+
if (err) {
54+
log.error(this.prefix + migration.name, err);
55+
} else {
56+
log.info(this.prefix + 'Processed', migration.name);
57+
}
58+
callback(err);
59+
}
60+
this._driver.deleteMigration(
61+
this.internals.matching + '/' + migration.name,
62+
function(err) {
63+
if (!this.internals.matching) {
64+
this._driver.deleteMigration(migration.name, onComplete);
65+
} else {
66+
onComplete.apply(err);
67+
}
68+
}.bind(this)
69+
);
70+
},
71+
72+
sync: function(options, callback) {
73+
return Migration.loadFromDatabase(
74+
this.directory,
75+
this._driver,
76+
this.internals
77+
)
78+
.then(completedMigrations => {
79+
var mode = dbmUtil.syncMode(
80+
completedMigrations,
81+
funcOrOpts.destination
82+
);
83+
if (mode === 1) {
84+
log.info(this.prefix + 'Syncing upwards.');
85+
return this.up(options);
86+
} else {
87+
log.info(this.prefix + 'Syncing downwards.');
88+
return this.down(options);
89+
}
90+
})
91+
.nodeify(callback);
92+
},
93+
94+
up: function({ partialName, count }, callback) {
95+
return Promise.all([
96+
File.loadFromFilesystem(this.directory, this.internals),
97+
File.loadFromDatabase(this.directory, this._driver, this.internals)
98+
])
99+
.then(function(allMigrations, completedMigrations) {
100+
var toRun = dbmUtil.filterUp(
101+
allMigrations,
102+
completedMigrations,
103+
partialName,
104+
count
105+
);
106+
107+
if (toRun.length === 0) {
108+
log.info(this.prefix + 'Nothing to run');
109+
}
110+
111+
return toRun;
112+
})
113+
.each(function(migration) {
114+
log.verbose(this.prefix + 'preparing to run up:', migration.name);
115+
var version = migration._meta.version || 1;
116+
require('./executors/versioned/v' + version).up(this.driver, migration);
117+
})
118+
.nodeify(callback);
119+
},
120+
121+
down: function({ partialName, count }, callback) {
122+
return File.loadFromDatabase(this.directory, this._driver, this.internals)
123+
.then(completedMigrations => {
124+
let toRun = dbmUtil.filterDown(completedMigrations, partialName, count);
125+
126+
if (toRun.length === 0) {
127+
log.info(this.prefix + 'Nothing to run');
128+
}
129+
130+
return toRun;
131+
})
132+
.each(migration => {
133+
log.verbose(this.prefix + 'preparing to run down:', migration.name);
134+
let version = migration._meta.version || 1;
135+
require('./executors/versioned/v' + version).up(this.driver, migration);
136+
})
137+
.nodeify(callback);
138+
}
139+
};
140+
141+
module.exports = Walker;

0 commit comments

Comments
 (0)