Skip to content

Commit

Permalink
Add kales generate migration task (#14)
Browse files Browse the repository at this point in the history
* Add kales generate migration task

* Couple of bug fixes

* Another nit
  • Loading branch information
felipecsl committed Feb 25, 2019
1 parent cc4b790 commit 847ed62
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 25 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ then open `http://localhost:8080` on your browser.
## Download

```
implementation 'com.felipecsl.kales:kales:0.0.1-SNAPSHOT'`
implementation 'com.felipecsl.kales:kales:0.0.1-SNAPSHOT'
```

Snapshots of the development version are available in
Expand Down
38 changes: 29 additions & 9 deletions kales-cli/src/main/kotlin/kales/cli/Cli.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import com.github.ajalt.clikt.core.subcommands
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.multiple
import com.github.ajalt.clikt.parameters.types.file
import kales.cli.task.DbMigrateTask
import kales.cli.task.GenerateControllerTask
import kales.cli.task.NewCommandTask
import kales.cli.task.*
import java.io.File

class Cli : CliktCommand() {
Expand Down Expand Up @@ -43,8 +41,16 @@ class DbMigrate : CliktCommand(name = "db:migrate", help = """
Migrate the database
""".trimIndent()) {
override fun run() {
val workingDir = File(System.getProperty("user.dir"))
DbMigrateTask(workingDir).run()
DbMigrateTask(workingDir()).run()
}
}

// TODO: Add support for different KALES_ENV (development, production, test, etc)
class DbCreate : CliktCommand(name = "db:create", help = """
Creates the database from resources/database.yml
""".trimIndent()) {
override fun run() {
DbCreateTask(workingDir()).run()
}
}

Expand All @@ -58,14 +64,28 @@ class GenerateController : CliktCommand(name = "controller", help = """
private val actions by argument().multiple()

override fun run() {
val workingDir = File(System.getProperty("user.dir"))
GenerateControllerTask(workingDir, name, actions.toSet()).run()
GenerateControllerTask(workingDir(), name, actions.toSet()).run()
}
}

class GenerateMigration : CliktCommand(name = "migration", help ="""
Stubs out a new database migration. Pass the migration name CamelCased.
A migration class is generated in db/migrate prefixed by a timestamp of the current date and time.
""".trimIndent()) {
private val migrationName by argument()

override fun run() {
GenerateMigrationTask(workingDir(), migrationName).run()
}
}

fun main(args: Array<String>) {
val generateCommand = Generate()
.subcommands(GenerateController())
Cli().subcommands(New(), generateCommand, DbMigrate())
.subcommands(GenerateMigration())
Cli().subcommands(New(), generateCommand, DbMigrate(), DbCreate())
.main(args)
}
}

fun workingDir() = File(System.getProperty("user.dir"))
9 changes: 9 additions & 0 deletions kales-cli/src/main/kotlin/kales/cli/task/DbCreateTask.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package kales.cli.task

import java.io.File

class DbCreateTask(workingDir: File) : KalesContextualTask(workingDir) {
override fun run() {
TODO("Not implemented yet")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@ class GenerateControllerTask(
""".trimIndent())
}
val controllersDir = File(appDirectory, "controllers")
writeControllerClassFile(controllersDir, controllerName, appPackageName)
writeControllerClassFile(controllersDir, controllerName)
}

private fun writeControllerClassFile(
controllersDir: File,
controllerName: String,
appPackageName: String
controllerName: String
) {
val file = buildFileSpec(controllerName, appPackageName)
val file = buildFileSpec(controllerName)
val outputPath = controllersDir.toPath().resolve("$controllerName.kt")
ByteArrayOutputStream().use { baos ->
OutputStreamWriter(baos, StandardCharsets.UTF_8).use { writer ->
Expand All @@ -51,7 +50,7 @@ class GenerateControllerTask(
}
}

private fun buildFileSpec(controllerName: String, appPackageName: String): FileSpec {
private fun buildFileSpec(controllerName: String): FileSpec {
val controllerTypeSpec = TypeSpec.classBuilder(controllerName)
.primaryConstructor(FunSpec.constructorBuilder()
.addParameter("call", ApplicationCall::class)
Expand Down
48 changes: 48 additions & 0 deletions kales-cli/src/main/kotlin/kales/cli/task/GenerateMigrationTask.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package kales.cli.task

import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.TypeSpec
import kales.cli.writeTextWithLogging
import kales.migrations.Migration
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.OutputStreamWriter
import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.util.*

class GenerateMigrationTask(
workingDir: File,
private val migrationClassName: String,
private val dateProvider: () -> Date = { Date() }
) : KalesContextualTask(workingDir) {
override fun run() {
val timestamp = SimpleDateFormat("yyyyMMddhhmmss").format(dateProvider())
val migrationTypeSpec = TypeSpec.classBuilder(migrationClassName)
.superclass(Migration::class)
.addFunction(FunSpec.builder("up")
.addModifiers(KModifier.OVERRIDE)
.build())
.addFunction(FunSpec.builder("down")
.addModifiers(KModifier.OVERRIDE)
.build())
.build()
val fileSpec = FileSpec.builder("$appPackageName.db.migrate", migrationClassName)
.addType(migrationTypeSpec)
.build()
val outputPath = dbMigrateDir.toPath().resolve("M${timestamp}_$migrationClassName.kt")
ByteArrayOutputStream().use { baos ->
OutputStreamWriter(baos, StandardCharsets.UTF_8).use { writer ->
fileSpec.writeTo(writer)
}
// We need to instantiate the migration class name at the bottom of the migration file so that
// the Kotlin script engine will return that type from eval() and be able to execute the
// migration. Without that, eval() returns `null` and we cannot run it. There must be a better
// way to do this...
val finalText = "$baos\n$migrationClassName()\n"
outputPath.toFile().writeTextWithLogging(finalText)
}
}
}
11 changes: 4 additions & 7 deletions kales-cli/src/main/kotlin/kales/cli/task/KalesContextualTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,15 @@ abstract class KalesContextualTask(protected val applicationRootDir: File) : Kal
protected val appDirectory = findAppDirectory()
?: throw UsageError("Unable to find the `app` sources directory")

/** returns "com.example.foo.app" */
/** Returns the path to the migrations directory */
protected val dbMigrateDir = File(appDirectory.parentFile, relativePathFor("db", "migrate"))

/** returns "com.example.foo" */
protected val appPackageName =
recursivelyDetermineAppPackageName(appDirectory, appDirectory)
?.split("/")
?.joinToString(".")

/** Returns "com.example.foo" */
internal val packageName = PackageName.parse(appPackageNameOrThrow()).parentPackage

private fun appPackageNameOrThrow() = appPackageName
?: throw IllegalStateException("appPackageName == null")

/** Returns a File pointing to the application app/`type` directory or null if none found */
private fun findAppDirectory(): File? {
return kotlinDir.childDirectories()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package kales.cli.task

import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.io.File
import java.text.SimpleDateFormat
import java.util.*

class GenerateMigrationTaskTest {
@get:Rule val tempDir = TemporaryFolder()

@Test fun `test create migration`() {
val root = tempDir.root
val appName = "com.example.testapp"
val date = Date()
val timestamp = SimpleDateFormat("yyyyMMddhhmmss").format(date)
NewCommandTask(root, appName).run()
GenerateMigrationTask(root, "CreateFooBar") { date }.run()
val dbMigrateDir = File(root, "src/main/kotlin/com/example/testapp/db/migrate")
val migrationFile = File(dbMigrateDir, "M${timestamp}_CreateFooBar.kt")
assertThat(dbMigrateDir.listFiles().toList()).containsExactly(migrationFile)
assertThat(migrationFile.readText()).isEqualTo("""
package com.example.testapp.db.migrate
import kales.migrations.Migration
class CreateFooBar : Migration() {
override fun up() {
}
override fun down() {
}
}
CreateFooBar()
""".trimIndent())
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package kales.sample.db.migrate

import com.improve_future.harmonica.core.AbstractMigration
import kales.migrations.Migration

class CreatePostsMigration : AbstractMigration() {
class M20190224204400_CreatePostsMigration : Migration() {
override fun up() {
createTable("posts") {
varchar(columnName = "title", nullable = false)
Expand Down
2 changes: 1 addition & 1 deletion sampleapp/src/main/resources/database.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
development:
adapter: postgresql
host: localhost
database: norp_development
database: kales_sampleapp_development
username: felipecsl
password:

0 comments on commit 847ed62

Please sign in to comment.