Skip to content

Instantly share code, notes, and snippets.

@skinner
Last active January 3, 2016 16:09
Show Gist options
  • Save skinner/8487618 to your computer and use it in GitHub Desktop.
Save skinner/8487618 to your computer and use it in GitHub Desktop.
custom code generator for Slick 2.0.0-RC1 that puts tables in schemas into separate objects
package codegen
import scala.reflect.runtime.currentMirror
import scala.slick.driver.JdbcProfile
import scala.slick.jdbc.meta.{MTable, createModel}
import scala.slick.model.codegen.SourceCodeGenerator
import scala.slick.model.{Model, Table}
import java.io.File
import java.io.FileWriter
// NamespacedCodegen handles tables within schemas by namespacing them
// within objects here
// (e.g., table a.foo and table b.foo can co-exist, because this code
// generator places the relevant generated classes into separate
// objects--a "a" object, and a "b" object)
object NamespacedCodegen {
def main(args: Array[String]) = {
args.toList match {
case List(slickDriver, jdbcDriver, url, outputDir, pkg, schemaList, fname) => {
val driver: JdbcProfile = {
val module = currentMirror.staticModule(slickDriver)
val reflectedModule = currentMirror.reflectModule(module)
val driver = reflectedModule.instance.asInstanceOf[JdbcProfile]
driver
}
val schemas = schemaList.split(",").map({
case "" => None
case (name: String) => Some(name)
}).toSet
var model = driver.simple.Database
.forURL(url, driver = jdbcDriver)
.withSession { implicit session =>
val filteredTables = driver.getTables.list.filter(
(t: MTable) => schemas.contains(t.name.schema)
)
createModel(filteredTables, driver)
}
val codegen = new scala.slick.model.codegen.SourceCodeGenerator(model){
override def code = {
//imports is copied right out of
//scala.slick.model.codegen.AbstractSourceCodeGenerator
var imports =
"import scala.slick.model.ForeignKeyAction\n" +
( if(tables.exists(_.hlistEnabled)){
"import scala.slick.collection.heterogenous._\n"+
"import scala.slick.collection.heterogenous.syntax._\n"
} else ""
) +
( if(tables.exists(_.PlainSqlMapper.enabled)){
"import scala.slick.jdbc.{GetResult => GR}\n"+
"// NOTE: GetResult mappers for plain SQL are only generated for tables where Slick knows how to map the types of all columns.\n"
} else ""
) + "\n\n"
val bySchema = tables.groupBy(t => {
t.model.name.schema
})
val schemaFor = (schema: Option[String]) => {
bySchema(schema).sortBy(_.model.name.table).map(
_.code.mkString("\n")
).mkString("\n\n")
}
val schemata = schemas.toArray.sorted.map(
schema => schema match {
case Some(schemaName) => {
indent(
"object " + schemaName + " {\n" + schemaFor(schema)
) + "\n}\n"
}
case None => {
schemaFor(schema)
}
}
).mkString("\n\n")
imports + schemata
}
override def Table = new Table(_) {
table =>
// customize foreign keys
override def ForeignKey = new ForeignKey(_) {
override def code = {
val fkColumns = compound(referencingColumns.map(_.name))
// Add the schema name to qualify the referenced table name if:
// 1. it's in a different schema from referencingTable, and
// 2. it's not None
val qualifier = if (referencedTable.model.name.schema
!= referencingTable.model.name.schema) {
referencedTable.model.name.schema match {
case Some(schema) => schema + "."
case None => ""
}
} else {
""
}
val qualifiedName = qualifier + referencedTable.TableValue.name
val pkColumns = compound(referencedColumns.map(c => s"r.${c.name}"))
s"""val $name = foreignKey("$dbName", $fkColumns, $qualifiedName)(r => $pkColumns, onUpdate=${onUpdate}, onDelete=${onDelete})"""
}
}
}
}
(new File(outputDir)).mkdirs()
val fw = new FileWriter(fname)
fw.write(codegen.packageCode(slickDriver, pkg))
fw.close()
}
case _ => {
println("""
Usage: NamespacedCodegen.main(Array( slickDriver, jdbcDriver, url, outputFolder, pkg, schemaList, fileName ))
slickDriver: Fully qualified name of Slick driver class, e.g. 'scala.slick.driver.PostgresDriver'
jdbcDriver: Fully qualified name of jdbc driver class, e.g. 'org.postgresql.Driver'
url: jdbc url, e.g. 'jdbc:postgresql://localhost/test'
outputFolder: Place where the package folder structure should be put
pkg: Scala package the generated code should be placed in
schemaList: string with comma-separated list of schemas to include
fName: name of output file
""".trim)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment