From 073daa718c603798ad64296912f44a2d79561719 Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Sat, 1 Oct 2016 01:45:48 +0530 Subject: [PATCH] feat(migrations): add support for making db actions this.db will allow to execute database actions using the database provider Closes #53 --- src/Migrations/Mixins/Migrate.js | 22 +++++++++- src/Schema/proxyHandler.js | 20 +++++++++ test/unit/migrations.spec.js | 70 ++++++++++++++++++++++++++++++-- 3 files changed, 107 insertions(+), 5 deletions(-) diff --git a/src/Migrations/Mixins/Migrate.js b/src/Migrations/Mixins/Migrate.js index 4d67b815..8e5372fc 100644 --- a/src/Migrations/Mixins/Migrate.js +++ b/src/Migrations/Mixins/Migrate.js @@ -12,6 +12,7 @@ const _ = require('lodash') const CatLog = require('cat-log') const cf = require('co-functional') +const co = require('co') const CE = require('../../Exceptions') const logger = new CatLog('adonis:lucid') @@ -55,7 +56,24 @@ Migrate._translateActions = function (schemaInstance, direction) { */ Migrate._callSchemaActions = function (defination, connection) { const builder = this.database.connection(connection).schema - return builder[defination.action](defination.key, this._wrapSchemaCallback(defination.callback)) + /** + * Custom check to allow access the database provider and + * do custom stuff with support for co-routines. + */ + if (defination.action === 'db') { + return co.wrap(function * () { + return yield defination.callback(this.database) + }.bind(this)) + } + + /** + * co.wrap returns a function which returns a promise, so we + * need to wrap schema method inside a function to keep + * the API stable. + */ + return (function () { + return builder[defination.action](defination.key, this._wrapSchemaCallback(defination.callback)) + }.bind(this)) } /** @@ -160,7 +178,7 @@ Migrate._executeMigrations = function * (migrations, direction, batchNumber) { */ Migrate._executeActions = function * (actions, file, direction, batchNumber) { yield cf.forEachSerial(function * (action) { - return yield action + return yield action() }, _.flatten(actions)) /** diff --git a/src/Schema/proxyHandler.js b/src/Schema/proxyHandler.js index 49cbc55b..da19d58c 100644 --- a/src/Schema/proxyHandler.js +++ b/src/Schema/proxyHandler.js @@ -51,6 +51,26 @@ proxyHandler.get = function (target, name) { if (Object.keys(aliases).indexOf(name) > -1) { name = aliases[name] } + + /** + * Trying to be more explicit here by adding the if clause + * on the name instead of checking the existence on + * callback and when key is a function but that will not + * be clear that this custom behaviour is required for + * this.db only. + * + * @example + * this.db(function () {}) + * + * @alternate-check + * if (!callback && typeof (key) === 'function') { + * // not explicit + * } + */ + if (name === 'db') { + callback = key + key = null + } target.actions.push({key, callback, action: name}) } } diff --git a/test/unit/migrations.spec.js b/test/unit/migrations.spec.js index 19b93221..aa3b3e0c 100644 --- a/test/unit/migrations.spec.js +++ b/test/unit/migrations.spec.js @@ -10,12 +10,13 @@ */ /* global describe, it, before,after */ -const Migrations = require('../../src/Migrations') -const Database = require('../../src/Database') -const Schema = require('../../src/Schema') const _ = require('lodash') const Ioc = require('adonis-fold').Ioc const chai = require('chai') +const cf = require('co-functional') +const Migrations = require('../../src/Migrations') +const Database = require('../../src/Database') +const Schema = require('../../src/Schema') const filesFixtures = require('./fixtures/files') const config = require('./helpers/config') const expect = chai.expect @@ -731,6 +732,7 @@ describe('Migrations', function () { const migrationsCompleted = yield runner.database.table('adonis_migrations') expect(migrationsCompleted).to.be.an('array') expect(migrationsCompleted).to.have.length(0) + yield runner.database.schema.dropTable('adonis_migrations') }) it('should return the sql output for rollback', function * () { @@ -755,5 +757,67 @@ describe('Migrations', function () { expect(response).to.have.length(1) expect(response[0].file).to.equal('2015-01-20') expect(response[0].queries).to.be.an('array') + yield runner.database.schema.dropTable('users') + yield runner.database.schema.dropTable('adonis_migrations') + }) + + it('should be able to access the database provider using this.db', function * () { + const Runner = new Migrations(Database, Config) + const runner = new Runner() + let db = null + class Users extends Schema { + up () { + this.db(function * (database) { + db = database + }) + } + } + const migrations = {'2016-04-20': Users} + yield runner.up(migrations) + expect(db.table).to.be.a('function') + yield runner.database.schema.dropTable('adonis_migrations') + }) + + it('should be able to migrate a table data to a different column @fun', function * () { + const Runner = new Migrations(Database, Config) + const runner = new Runner() + class Users extends Schema { + up () { + this.create('users', (table) => { + table.increments() + table.string('username') + }) + + this.db(function * (database) { + yield database.table('users').insert([{username: 'foo'}, {username: 'bar'}]) + }) + } + } + + class UsersMigrate extends Schema { + up () { + this.table('users', (table) => { + table.string('uname') + }) + + this.db(function * (database) { + const usernames = yield database.table('users').pluck('username') + yield cf.forEach(function * (username) { + yield database.table('users').where('username', username).update('uname', username) + }, usernames) + }) + + this.table('users', (table) => { + table.dropColumn('username') + }) + } + } + + const migrations = {'2016-04-20': Users, '2016-10-30': UsersMigrate} + yield runner.up(migrations) + const users = yield Database.table('users').pluck('uname') + expect(users).deep.equal(['foo', 'bar']) + yield runner.database.schema.dropTable('users') + yield runner.database.schema.dropTable('adonis_migrations') }) })