diff options
-rw-r--r-- | src/main/scala/NamespacedCodegen.scala | 124 |
1 files changed, 51 insertions, 73 deletions
diff --git a/src/main/scala/NamespacedCodegen.scala b/src/main/scala/NamespacedCodegen.scala index bdda76a..44826fe 100644 --- a/src/main/scala/NamespacedCodegen.scala +++ b/src/main/scala/NamespacedCodegen.scala @@ -1,18 +1,20 @@ -import slick.dbio.{NoStream, DBIOAction} +import java.io.{FileWriter, File} +import java.net.URI import scala.concurrent.Await +import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration.Duration import scala.reflect.runtime.currentMirror + import slick.ast.ColumnOption +import slick.backend.DatabaseConfig +import slick.codegen.{AbstractGenerator, SourceCodeGenerator} +import slick.dbio.{DBIO, DBIOAction, NoStream} import slick.driver.JdbcProfile import slick.jdbc.meta.MTable -import slick.codegen.{AbstractGenerator, SourceCodeGenerator} -import slick.model._ import slick.{model => m} -import scala.concurrent.ExecutionContext.Implicits.global - -import java.io.File -import java.io.FileWriter +import slick.model.{Column, Model, Table} +import slick.util.ConfigExtensionMethods.configExtensionMethods // NamespacedCodegen handles tables within schemas by namespacing them // within objects here @@ -20,24 +22,17 @@ import java.io.FileWriter // generator places the relevant generated classes into separate // objects--a "a" object, and a "b" object) object NamespacedCodegen { - def parseSchemaList(schemaTableNames: List[String]): Map[String, List[String]] = { - val (tables, schemas) = schemaTableNames.partition(_.contains(".")) - val mappedSchemas = schemas.map(_ -> List()).toMap - val mappedTables = tables.groupBy(_.split("\\.")(0)).map { - case (key, value) => (key, value.map(_.split("\\.")(1)).asInstanceOf[List[String]]) - } - - mappedSchemas ++ mappedTables - } - - import slick.dbio.DBIO - import slick.model.Model + def parseSchemaList(schemaTableNames: List[String]): Map[String, List[String]] = + schemaTableNames.map(_.split('.')) + .groupBy(_.head) + .mapValues(_.flatMap(_.tail)) + .toMap def createFilteredModel(driver: JdbcProfile, mappedSchemas: Map[String, List[String]]): DBIO[Model] = driver.createModel(Some( - MTable.getTables.map(_.filter((t: MTable) => mappedSchemas - .get(t.name.schema.getOrElse("")) - .fold(false)(ts => ts.isEmpty || ts.contains(t.name.name)))))) + MTable.getTables.map(_.filter((t: MTable) => + t.name.schema.flatMap(mappedSchemas.get).exists(tables => + tables.isEmpty || tables.contains(t.name.name)))))) def references(dbModel: Model, tcMappings: Map[(String, String), (String, String)]): Map[(String, String), (Table, Column)] = { def getTableColumn(tc: (String, String)) : (Table, Column) = { @@ -52,11 +47,6 @@ object NamespacedCodegen { tcMappings.map{case (from, to) => ({getTableColumn(from); from}, getTableColumn(to))} } - - import java.net.URI - import slick.backend.DatabaseConfig - import slick.util.ConfigExtensionMethods.configExtensionMethods - def run( uri: URI, pkg: String, @@ -74,18 +64,21 @@ object NamespacedCodegen { val manualReferences = references(dbModel, manualForeignKeys) def codegen(typeFile: Boolean) = new SourceCodeGenerator(dbModel){ - def derefColumn(table: m.Table, column: m.Column): (m.Table, m.Column) = - (table.foreignKeys.toList - .filter(_.referencingColumns.forall(_ == column)) - .flatMap(fk => - fk.referencedColumns match { - case Seq(c) => dbModel.tablesByName.get(fk.referencedTable).map{(_, c)} - case _ => None - }) ++ - manualReferences.get((table.name.asString, column.name))) - .headOption + + def derefColumn(table: m.Table, column: m.Column): (m.Table, m.Column) = { + val referencedColumn: Seq[(m.Table, m.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 + .orElse(manualReferences.get((table.name.asString, column.name))) .map((derefColumn _).tupled) .getOrElse((table, column)) + } // Is this compatible with ***REMOVED*** Id? How do we make it generic? def idType(t: m.Table) : String = @@ -97,14 +90,13 @@ object NamespacedCodegen { // Why can't we simply re-use? var imports = - // acyclic is unnecessary in generic projects - //if (typeFile) "import acyclic.file\nimport dbmodels.rows\n" - //else "import slick.model.ForeignKeyAction\n" + - "import rows._\n" + + "import dbmodels.rows\n" + ( if(tables.exists(_.hlistEnabled)){ - "import slick.collection.heterogeneous._\n"+ - "import slick.collection.heterogeneous.syntax._\n" + "import slick.collection.heterogeneous._\n" + + "import slick.collection.heterogeneous.syntax._\n" + + "import com.drivergrp.core._\n" + + "import com.drivergrp.core.database._\n" } else "" ) + ( if(tables.exists(_.PlainSqlMapper.enabled)){ @@ -113,35 +105,22 @@ object NamespacedCodegen { } else "" ) + "\n\n" // We didn't copy ddl though + val sortedSchemaTables: List[(String, Seq[TableDef])] = tables + .groupBy(t => t.model.name.schema.getOrElse("`public`")) + .toList.sortBy(_._1) - val bySchema = tables.groupBy(t => { - t.model.name.schema - }) + val schemata: String = sortedSchemaTables.map { + case (schemaName, tables) => + val tableCode = tables + .sortBy(_.model.name.table) + .map(_.code.mkString("\n")) + .mkString("\n\n") + indent(s"object $schemaName extends CoreDBMappers {\n$tableCode")+"\n}\n" + }.mkString("\n\n") - val schemaFor = (schema: String) => { - bySchema(Option(schema)).sortBy(_.model.name.table).map( - _.code.mkString("\n") // TODO explore here - ).mkString("\n\n") - } + val mapperTrait: String = """trait CoreDBMappers extends com.drivergrp.core.database.IdColumnTypes { override val database = com.drivergrp.core.database.Database.fromConfig("slick.db.default") }""" - val schemata = mappedSchemas.keys.toList.sorted.map( - s => indent("object" + " " + s + " {\n" + schemaFor(s)) + "\n}\n" - ).mkString("\n\n") - - val idType = - if (typeFile)// Should not be defined here. - """|case class Id[T](v: Int) - |""".stripMargin - else - // This should be in a separate Implicits trait - """|implicit def idTypeMapper[A] : BaseColumnType[Id[A]] = - | MappedColumnType.base[Id[A], Int](_.v, Id(_)) - |import play.api.mvc.PathBindable - |implicit def idPathBindable[A] : PathBindable[Id[A]] = implicitly[PathBindable[Int]].transform[Id[A]](Id(_),_.v) - |""".stripMargin - //pathbindable is play specific - // Id works only with labdash Id - imports + idType + schemata + imports + mapperTrait + "\n\n" + schemata } // This is overridden to output classfiles elsewhere @@ -239,9 +218,8 @@ object NamespacedCodegen { else model.tpe match { // how does this type work out? // There should be a way to add adhoc custom time mappings - case "java.sql.Date" => "tools.Date" - case "java.sql.Time" => "tools.Time" - case "java.sql.Timestamp" => "tools.Time" + case "java.sql.Time" => "com.drivergrp.core.time.Time" + case "java.sql.Timestamp" => "com.drivergrp.core.time.Time" case _ => super.rawType } } @@ -255,7 +233,7 @@ object NamespacedCodegen { fw.write(c) fw.close() } - val disableScalariform = "filename/ format: OFF\n" + val disableScalariform = "// filename/ format: OFF\n" val tablesSource = codegen(false).packageCode(slickDriver, pkg, "Tables", None) val rowsSource = s"package $pkg.rows\n\n" + codegen(true).code |