diff options
author | Stewart Stewart <stewinsalot@gmail.com> | 2016-11-22 13:49:22 -0500 |
---|---|---|
committer | Stewart Stewart <stewinsalot@gmail.com> | 2016-11-22 13:49:22 -0500 |
commit | f917786aee0b55e2a9cf7c1ecb7b5b3f25160ab9 (patch) | |
tree | b3c160fd3b9b81fec386b2ffca6a95ace13d7de0 /src | |
parent | 484948d3aee517091eb513fd459201ed96211fc5 (diff) | |
download | slick-codegen-plugin-f917786aee0b55e2a9cf7c1ecb7b5b3f25160ab9.tar.gz slick-codegen-plugin-f917786aee0b55e2a9cf7c1ecb7b5b3f25160ab9.tar.bz2 slick-codegen-plugin-f917786aee0b55e2a9cf7c1ecb7b5b3f25160ab9.zip |
add sbt-settings and lint/format
Diffstat (limited to 'src')
-rw-r--r-- | src/main/scala/CodegenPlugin.scala | 30 | ||||
-rw-r--r-- | src/main/scala/NamespacedCodegen.scala | 196 |
2 files changed, 149 insertions, 77 deletions
diff --git a/src/main/scala/CodegenPlugin.scala b/src/main/scala/CodegenPlugin.scala index 98d832c..f9351c1 100644 --- a/src/main/scala/CodegenPlugin.scala +++ b/src/main/scala/CodegenPlugin.scala @@ -7,14 +7,26 @@ object CodegenPlugin extends AutoPlugin { type TableColumn = (String, String) object autoImport { - lazy val codegenURI = SettingKey[String]("codegen-uri", "uri for the database configuration") - lazy val codegenPackage = SettingKey[String]("codegen-package", "package in which to place generated code") - lazy val codegenOutputPath = SettingKey[String]("codegen-output-path", "directory to with the generated code will be written") - lazy val codegenSchemaWhitelist = SettingKey[List[String]]("codegen-schema-whitelist", "schemas and tables to process") - lazy val codegenForeignKeys = SettingKey[Map[TableColumn, TableColumn]]("codegen-foreign-keys", "foreign key references to data models add manually") - lazy val codegenSchemaBaseClassParts = SettingKey[List[String]]("codegen-schema-base-class-parts", "parts inherited by each generated schema object") - - lazy val slickCodeGenTask = TaskKey[Unit]("gen-tables", "generate the table definitions") + lazy val codegenURI = + SettingKey[String]("codegen-uri", "uri for the database configuration") + lazy val codegenPackage = SettingKey[String]( + "codegen-package", + "package in which to place generated code") + lazy val codegenOutputPath = SettingKey[String]( + "codegen-output-path", + "directory to with the generated code will be written") + lazy val codegenSchemaWhitelist = SettingKey[List[String]]( + "codegen-schema-whitelist", + "schemas and tables to process") + lazy val codegenForeignKeys = SettingKey[Map[TableColumn, TableColumn]]( + "codegen-foreign-keys", + "foreign key references to data models add manually") + lazy val codegenSchemaBaseClassParts = SettingKey[List[String]]( + "codegen-schema-base-class-parts", + "parts inherited by each generated schema object") + + lazy val slickCodeGenTask = + TaskKey[Unit]("gen-tables", "generate the table definitions") } @@ -39,8 +51,6 @@ object CodegenPlugin extends AutoPlugin { ) } }.value - ) - } diff --git a/src/main/scala/NamespacedCodegen.scala b/src/main/scala/NamespacedCodegen.scala index 41905f0..dd2567f 100644 --- a/src/main/scala/NamespacedCodegen.scala +++ b/src/main/scala/NamespacedCodegen.scala @@ -1,12 +1,15 @@ import java.net.URI -import java.io.{BufferedWriter, File, FileWriter} import java.nio.file.Paths import scala.concurrent.Await import scala.concurrent.duration.Duration import scala.concurrent.ExecutionContext.Implicits.global import slick.backend.DatabaseConfig -import slick.codegen.{OutputHelpers, SourceCodeGenerator, StringGeneratorHelpers} +import slick.codegen.{ + OutputHelpers, + SourceCodeGenerator, + StringGeneratorHelpers +} import slick.dbio.DBIO import slick.driver.JdbcProfile import slick.jdbc.meta.MTable @@ -15,20 +18,36 @@ import slick.model.{Column, Model, Table} object Generator { - - def run(uri: URI, pkg: String, schemaNames: Option[List[String]], outputPath: String, manualForeignKeys: Map[(String, String), (String, String)], schemaBaseClass: String) = { - val dc: DatabaseConfig[JdbcProfile] = DatabaseConfig.forURI[JdbcProfile](uri) - val parsedSchemasOpt: Option[Map[String, List[String]]] = schemaNames.map(SchemaParser.parse) - val dbModel: Model = Await.result(dc.db.run(SchemaParser.createModel(dc.driver, parsedSchemasOpt)), Duration.Inf) - - val generator = new Generator(uri, pkg, dbModel, outputPath, manualForeignKeys, schemaBaseClass) - val generatedCode = generator.code - parsedSchemasOpt.getOrElse(Map()).keys.map(schemaName => FileHelpers.schemaOutputPath(outputPath, schemaName)) + def run(uri: URI, + pkg: String, + schemaNames: Option[List[String]], + outputPath: String, + manualForeignKeys: Map[(String, String), (String, String)], + schemaBaseClass: String) = { + val dc: DatabaseConfig[JdbcProfile] = + DatabaseConfig.forURI[JdbcProfile](uri) + val parsedSchemasOpt: Option[Map[String, List[String]]] = + schemaNames.map(SchemaParser.parse) + val dbModel: Model = Await.result( + dc.db.run(SchemaParser.createModel(dc.driver, parsedSchemasOpt)), + Duration.Inf) + + val generator = new Generator(uri, + pkg, + dbModel, + outputPath, + manualForeignKeys, + schemaBaseClass) + parsedSchemasOpt + .getOrElse(Map()) + .keys + .map(schemaName => FileHelpers.schemaOutputPath(outputPath, schemaName)) } } -class PackageNameGenerator(pkg: String, dbModel: Model) extends SourceCodeGenerator(dbModel) { +class PackageNameGenerator(pkg: String, dbModel: Model) + extends SourceCodeGenerator(dbModel) { override def code: String = s""" |// format: OFF @@ -38,7 +57,6 @@ class PackageNameGenerator(pkg: String, dbModel: Model) extends SourceCodeGenera |""".stripMargin } - class ImportGenerator(dbModel: Model) extends SourceCodeGenerator(dbModel) { val baseImports: String = s""" @@ -64,10 +82,18 @@ class ImportGenerator(dbModel: Model) extends SourceCodeGenerator(dbModel) { |""".stripMargin else "" - override def code: String = baseImports + hlistImports + plainSqlMapperImports + override def code: String = + baseImports + hlistImports + plainSqlMapperImports } -class Generator(uri: URI, pkg: String, dbModel: Model, outputPath: String, manualForeignKeys: Map[(String, String), (String, String)], schemaBaseClass: String) extends SourceCodeGenerator(dbModel) with OutputHelpers { +class Generator(uri: URI, + pkg: String, + dbModel: Model, + outputPath: String, + manualForeignKeys: Map[(String, String), (String, String)], + schemaBaseClass: String) + extends SourceCodeGenerator(dbModel) + with OutputHelpers { val packageName = new PackageNameGenerator(pkg, dbModel).code val allImports: String = new ImportGenerator(dbModel).code @@ -76,15 +102,19 @@ class Generator(uri: URI, pkg: String, dbModel: Model, outputPath: String, manua val sortedSchemaTables: List[(String, Seq[TableDef])] = tables .groupBy(t => t.model.name.schema.getOrElse("`public`")) - .toList.sortBy(_._1) - + .toList + .sortBy(_._1) val schemata: String = sortedSchemaTables.map { case (schemaName, tableDefs) => - val tableCode = tableDefs.sortBy(_.model.name.table).map(_.code.mkString("\n")) .mkString("\n\n") + val tableCode = tableDefs + .sortBy(_.model.name.table) + .map(_.code.mkString("\n")) + .mkString("\n\n") val generatedSchema = s""" |object ${schemaName} extends $schemaBaseClass { - | override val database = xyz.driver.core.database.Database.fromConfig("${uri.getFragment()}") + | override val database = xyz.driver.core.database.Database.fromConfig("${uri + .getFragment()}") | import database.profile.api._ | ${tableCode} | @@ -104,11 +134,11 @@ class Generator(uri: URI, pkg: String, dbModel: Model, outputPath: String, manua allImports + schemata } - override def Table = new Table(_) { table => // need this in order to use our own TableClass generator - override def definitions = Seq[Def]( EntityTypeRef, PlainSqlMapper, TableClassRef, TableValue ) + override def definitions = + Seq[Def](EntityTypeRef, PlainSqlMapper, TableClassRef, TableValue) def TableClassRef = new TableClass() { // We disable the option mapping, as it is a bit more complex to support and we don't appear to need it @@ -122,37 +152,44 @@ class Generator(uri: URI, pkg: String, dbModel: Model, outputPath: String, manua override def mappingEnabled: Boolean = true // create case class from colums - override def factory: String = - if(!hlistEnabled) super.factory - else { - val args = columns.zipWithIndex.map("a"+_._2) - val hlist = args.mkString("::") + ":: HNil" - val hlistType = columns.map(_.actualType).mkString("::") + ":: HNil.type" - s"((h : $hlistType) => h match {case $hlist => ${TableClass.elementType}(${args.mkString(",")})})" - } + override def factory: String = + if (!hlistEnabled) super.factory + else { + val args = columns.zipWithIndex.map("a" + _._2) + val hlist = args.mkString("::") + ":: HNil" + val hlistType = columns + .map(_.actualType) + .mkString("::") + ":: HNil.type" + s"((h : $hlistType) => h match {case $hlist => ${TableClass.elementType}(${args.mkString(",")})})" + } // from case class create columns override def extractor: String = - if(!hlistEnabled) super.extractor - else s"(a : ${TableClass.elementType}) => Some(" + columns.map("a."+_.name ).mkString("::") + ":: HNil)" - + if (!hlistEnabled) super.extractor + else + s"(a : ${TableClass.elementType}) => Some(" + columns + .map("a." + _.name) + .mkString("::") + ":: HNil)" def EntityTypeRef = new EntityTypeDef { - override def code: String = (if (classEnabled) "final " else "") + super.code + override def code: String = + (if (classEnabled) "final " else "") + super.code } - override def Column = new Column(_) { - column => + override def Column = new Column(_) { column => - val manualReferences = SchemaParser.references(dbModel, manualForeignKeys) + val manualReferences = + SchemaParser.references(dbModel, manualForeignKeys) // work out the destination of the foreign key - def derefColumn(table: sModel.Table, column: sModel.Column): (sModel.Table, sModel.Column) = { - val referencedColumn: Seq[(sModel.Table, sModel.Column)] = table.foreignKeys - .filter(tableFk => tableFk.referencingColumns.forall(_ == column)) - .filter(columnFk => columnFk.referencedColumns.length == 1) - .flatMap(_.referencedColumns - .map(c => (dbModel.tablesByName(c.table), c))) + def derefColumn(table: sModel.Table, + column: sModel.Column): (sModel.Table, sModel.Column) = { + val referencedColumn: Seq[(sModel.Table, sModel.Column)] = + table.foreignKeys + .filter(tableFk => tableFk.referencingColumns.forall(_ == column)) + .filter(columnFk => columnFk.referencedColumns.length == 1) + .flatMap(_.referencedColumns.map(c => + (dbModel.tablesByName(c.table), c))) assert(referencedColumn.distinct.length <= 1, referencedColumn) referencedColumn.headOption @@ -164,13 +201,15 @@ class Generator(uri: URI, pkg: String, dbModel: Model, outputPath: String, manua // re-write ids, and time types override def rawType: String = { val (t, c) = derefColumn(table.model, column.model) - if (c.options.contains(slick.ast.ColumnOption.PrimaryKey)) TypeGenerator.idType(pkg, t) - else model.tpe match { - // TODO: There should be a way to add adhoc custom time mappings - case "java.sql.Time" => "xyz.driver.core.time.Time" - case "java.sql.Timestamp" => "xyz.driver.core.time.Time" - case _ => super.rawType - } + if (c.options.contains(slick.ast.ColumnOption.PrimaryKey)) + TypeGenerator.idType(pkg, t) + else + model.tpe match { + // TODO: There should be a way to add adhoc custom time mappings + case "java.sql.Time" => "xyz.driver.core.time.Time" + case "java.sql.Timestamp" => "xyz.driver.core.time.Time" + case _ => super.rawType + } } } @@ -178,12 +217,22 @@ class Generator(uri: URI, pkg: String, dbModel: Model, outputPath: String, manua override def code = { val fkColumns = compoundValue(referencingColumns.map(_.name)) val qualifier = - if (referencedTable.model.name.schema == referencingTable.model.name.schema) "" - else referencedTable.model.name.schema.fold("")(sname => s"$pkg.$sname.") + if (referencedTable.model.name.schema == referencingTable.model.name.schema) + "" + else + referencedTable.model.name.schema.fold("")(sname => + s"$pkg.$sname.") val qualifiedName = qualifier + referencedTable.TableValue.name - val pkColumns = compoundValue(referencedColumns.map(c => s"r.${c.name}${if (!c.model.nullable && referencingColumns.forall(_.model.nullable)) ".?" else ""}")) - val fkName = referencingColumns.map(_.name).flatMap(_.split("_")).map(_.capitalize).mkString.uncapitalize + "Fk" + val pkColumns = compoundValue(referencedColumns.map(c => + s"r.${c.name}${if (!c.model.nullable && referencingColumns.forall(_.model.nullable)) ".?" + else ""}")) + val fkName = referencingColumns + .map(_.name) + .flatMap(_.split("_")) + .map(_.capitalize) + .mkString + .uncapitalize + "Fk" s"""lazy val $fkName = foreignKey("$dbName", $fkColumns, $qualifiedName)(r => $pkColumns, onUpdate=$onUpdate, onDelete=$onDelete)""" } } @@ -193,32 +242,46 @@ class Generator(uri: URI, pkg: String, dbModel: Model, outputPath: String, manua } object SchemaParser { - def references(dbModel: Model, tcMappings: Map[(String, String), (String, String)]): Map[(String, String), (Table, Column)] = { - def getTableColumn(tc: (String, String)) : (Table, Column) = { + def references(dbModel: Model, + tcMappings: Map[(String, String), (String, String)]) + : Map[(String, String), (Table, Column)] = { + def getTableColumn(tc: (String, String)): (Table, Column) = { val (tableName, columnName) = tc - val table = dbModel.tables.find(_.name.asString == tableName) + val table = dbModel.tables + .find(_.name.asString == tableName) .getOrElse(throw new RuntimeException("No table " + tableName)) - val column = table.columns.find(_.name == columnName) - .getOrElse(throw new RuntimeException("No column " + columnName + " in table " + tableName)) + val column = table.columns + .find(_.name == columnName) + .getOrElse(throw new RuntimeException( + "No column " + columnName + " in table " + tableName)) (table, column) } - tcMappings.map{case (from, to) => ({getTableColumn(from); from}, getTableColumn(to))} + tcMappings.map { + case (from, to) => ({ getTableColumn(from); from }, getTableColumn(to)) + } } def parse(schemaTableNames: List[String]): Map[String, List[String]] = - schemaTableNames.map(_.split('.')) + schemaTableNames + .map(_.split('.')) .groupBy(_.head) .mapValues(_.flatMap(_.tail)) - def createModel(jdbcProfile: JdbcProfile, mappedSchemasOpt: Option[Map[String, List[String]]]): DBIO[Model] = { + def createModel( + jdbcProfile: JdbcProfile, + mappedSchemasOpt: Option[Map[String, List[String]]]): DBIO[Model] = { val allTables: DBIO[Vector[MTable]] = MTable.getTables - val filteredTables = mappedSchemasOpt.map(mappedSchemas => - allTables.map( - (tables: Vector[MTable]) => tables.filter(table => - table.name.schema.flatMap(mappedSchemas.get).exists(ts => - ts.isEmpty || ts.contains(table.name.name))))) + val filteredTables = mappedSchemasOpt.map( + mappedSchemas => + allTables.map( + (tables: Vector[MTable]) => + tables.filter( + table => + table.name.schema + .flatMap(mappedSchemas.get) + .exists(ts => ts.isEmpty || ts.contains(table.name.name))))) jdbcProfile.createModel(filteredTables orElse Some(allTables)) } @@ -236,7 +299,6 @@ object TypeGenerator extends StringGeneratorHelpers { } } - object FileHelpers { def schemaOutputPath(path: String, schemaName: String): String = Paths.get(path, s"${schemaName}.scala").toAbsolutePath().toString() |