Skip to content

Instantly share code, notes, and snippets.

@Arakaki
Created December 24, 2013 09:36
Show Gist options
  • Save Arakaki/8110863 to your computer and use it in GitHub Desktop.
Save Arakaki/8110863 to your computer and use it in GitHub Desktop.
Skinny reverse engineer task support.
package skinny.task.generator
import scalikejdbc.config.{ DBsWithEnv, TypesafeConfigReaderWithEnv }
import scalikejdbc._
import java.sql.{ Types => JavaSqlTypes }
import scalikejdbc.mapper._
import java.util.Locale.{ ENGLISH => en }
object ReverseEngineering extends ReverseEngineering
/**
* Reverse Engineering Task.
*/
trait ReverseEngineering {
private[this] def showUsage = {
println(""" Usage: sbt "task/run db:reverse_engineering env db_name scheme table_name view_template_name resources resource" """)
println("")
}
def run(args: List[String]) {
args match {
case env :: dbName :: scheme :: tableName :: viewTemplate :: resources :: resource :: Nil => scaffold(env, dbName, scheme, tableName, viewTemplate, resources, resource)
case env :: dbName :: scheme :: tableName :: resources :: resource :: Nil => scaffold(env, dbName, scheme, tableName, "ssp", resources, resource)
case _ => showUsage
}
}
def scaffold(env: String, dbName: String, scheme: String, tableName: String, viewTemplate: String, resources: String, resource: String) {
val settings = loadDbSettings(env, dbName)
settings match {
case Some(settings) =>
Class.forName(settings.driverName)
val model = Model(settings.url, settings.user, settings.password)
val table = model.table(scheme, tableName).orElse(None)
table match {
case Some(table) =>
val params = (resources :: resource :: Nil) ++ columnParameter(table)
viewTemplate match {
case "ssp" => ScaffoldSspGenerator.run(params)
case "scaml" => ScaffoldScamlGenerator.run(params)
case "jade" => ScaffoldJadeGenerator.run(params)
case _ => showUsage
}
case None => showUsage
}
case None => showUsage
}
}
def columnParameter(table: Table) = {
table.allColumns.map { column =>
val columnInScala = ColumnInScala(column)
s"${columnInScala.nameInScala}:${columnInScala.typeInScala}"
}
}
def loadDbSettings(env: String, dbName: String): Option[JDBCSettings] = {
DBsWithEnv(env).setupAll()
if (!TypesafeConfigReaderWithEnv(env).dbNames.isEmpty) {
Some(TypesafeConfigReaderWithEnv(env).readJDBCSettings(Symbol(dbName)))
} else {
None
}
}
case class ColumnInScala(underlying: Column) {
lazy val nameInScala: String = {
val camelCase: String = toCamelCase(underlying.name)
camelCase.head.toLower + camelCase.tail
}
private def toCamelCase(s: String): String = s.split("_").toList.foldLeft("") {
(camelCaseString, part) =>
camelCaseString + toProperCase(part)
}
private def toProperCase(s: String): String = {
if (s == null || s.trim.size == 0) ""
else s.substring(0, 1).toUpperCase(en) + s.substring(1).toLowerCase(en)
}
object TypeName {
val Any = "Any"
val AnyArray = "Array[Any]"
val ByteArray = "Array[Byte]"
val Long = "Long"
val Boolean = "Boolean"
val DateTime = "DateTime"
val LocalDate = "LocalDate"
val LocalTime = "LocalTime"
val String = "String"
val Byte = "Byte"
val Int = "Int"
val Short = "Short"
val Float = "Float"
val Double = "Double"
val Blob = "Blob"
val Clob = "Clob"
val Ref = "Ref"
val Struct = "Struct"
val BigDecimal = "BigDecimal" // scala.math.BigDecimal
}
lazy val rawTypeInScala: String = underlying.dataType match {
case JavaSqlTypes.ARRAY => TypeName.AnyArray
case JavaSqlTypes.BIGINT => TypeName.Long
case JavaSqlTypes.BINARY => TypeName.ByteArray
case JavaSqlTypes.BIT => TypeName.Boolean
case JavaSqlTypes.BLOB => TypeName.Blob
case JavaSqlTypes.BOOLEAN => TypeName.Boolean
case JavaSqlTypes.CHAR => TypeName.String
case JavaSqlTypes.CLOB => TypeName.Clob
case JavaSqlTypes.DATALINK => TypeName.Any
case JavaSqlTypes.DATE => TypeName.LocalDate
case JavaSqlTypes.DECIMAL => TypeName.BigDecimal
case JavaSqlTypes.DISTINCT => TypeName.Any
case JavaSqlTypes.DOUBLE => TypeName.Double
case JavaSqlTypes.FLOAT => TypeName.Float
case JavaSqlTypes.INTEGER => TypeName.Int
case JavaSqlTypes.JAVA_OBJECT => TypeName.Any
case JavaSqlTypes.LONGVARBINARY => TypeName.ByteArray
case JavaSqlTypes.LONGVARCHAR => TypeName.String
case JavaSqlTypes.NULL => TypeName.Any
case JavaSqlTypes.NUMERIC => TypeName.BigDecimal
case JavaSqlTypes.OTHER => TypeName.Any
case JavaSqlTypes.REAL => TypeName.Float
case JavaSqlTypes.REF => TypeName.Ref
case JavaSqlTypes.SMALLINT => TypeName.Short
case JavaSqlTypes.STRUCT => TypeName.Struct
case JavaSqlTypes.TIME => TypeName.LocalTime
case JavaSqlTypes.TIMESTAMP => TypeName.DateTime
case JavaSqlTypes.TINYINT => TypeName.Byte
case JavaSqlTypes.VARBINARY => TypeName.ByteArray
case JavaSqlTypes.VARCHAR => TypeName.String
case _ => TypeName.Any
}
lazy val typeInScala: String = {
if (underlying.isNotNull) rawTypeInScala
else "Option[" + rawTypeInScala + "]"
}
lazy val extractorName: String = underlying.dataType match {
case JavaSqlTypes.ARRAY => "array"
case JavaSqlTypes.BIGINT => "long"
case JavaSqlTypes.BINARY => "bytes"
case JavaSqlTypes.BIT => "boolean"
case JavaSqlTypes.BLOB => "blob"
case JavaSqlTypes.BOOLEAN => "boolean"
case JavaSqlTypes.CHAR => "string"
case JavaSqlTypes.CLOB => "clob"
case JavaSqlTypes.DATALINK => "any"
case JavaSqlTypes.DATE => "date"
case JavaSqlTypes.DECIMAL => "bigDecimal"
case JavaSqlTypes.DISTINCT => "any"
case JavaSqlTypes.DOUBLE => "double"
case JavaSqlTypes.FLOAT => "float"
case JavaSqlTypes.INTEGER => "int"
case JavaSqlTypes.JAVA_OBJECT => "any"
case JavaSqlTypes.LONGVARBINARY => "bytes"
case JavaSqlTypes.LONGVARCHAR => "string"
case JavaSqlTypes.NULL => "any"
case JavaSqlTypes.NUMERIC => "bigDecimal"
case JavaSqlTypes.OTHER => "any"
case JavaSqlTypes.REAL => "float"
case JavaSqlTypes.REF => "ref"
case JavaSqlTypes.SMALLINT => "short"
case JavaSqlTypes.STRUCT => "any"
case JavaSqlTypes.TIME => "time"
case JavaSqlTypes.TIMESTAMP => "timestamp"
case JavaSqlTypes.TINYINT => "byte"
case JavaSqlTypes.VARBINARY => "bytes"
case JavaSqlTypes.VARCHAR => "string"
case _ => "any"
}
}
}
package skinny.task
import skinny.task.generator._
import skinny.dbmigration.DBMigration
import skinny.SkinnyEnv
/**
* Task launcher.
*/
object TaskLauncher {
/**
* Registered tasks.
*/
private[this] val tasks = new scala.collection.mutable.HashMap[String, (List[String]) => Unit]
// built-in tasks
register("generate:controller", (params) => ControllerGenerator.run(params))
register("generate:model", (params) => ModelGenerator.run(params))
register("generate:migration", (params) => DBMigrationFileGenerator.run(params))
register("generate:scaffold", (params) => ScaffoldSspGenerator.run(params))
register("generate:scaffold:ssp", (params) => ScaffoldSspGenerator.run(params))
register("generate:scaffold:scaml", (params) => ScaffoldScamlGenerator.run(params))
register("generate:scaffold:jade", (params) => ScaffoldJadeGenerator.run(params))
register("db:migrate", {
case env :: dbName :: rest => DBMigration.migrate(env, dbName)
case params => DBMigration.migrate(params.headOption.getOrElse(SkinnyEnv.Development))
})
register("db:reverse_engineering", (params) => ReverseEngineering.run(params))
def register(name: String, runner: (List[String]) => Unit) = tasks.update(name, runner)
def showUsage = {
println(
s"""
| Usage: sbt "task/run [task] [options...]
|
|${tasks.map(t => " " + t._1).mkString("\n")}
|
|""".stripMargin)
}
def main(args: Array[String]) {
args.toList match {
case name :: parameters =>
tasks.get(name).map(_.apply(parameters)).getOrElse(println(s"Task for ${name} not found."))
case _ => showUsage
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment