Skip to content

Instantly share code, notes, and snippets.

@cvogt
Created August 17, 2016 19:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cvogt/8fc07d624d1660ad09f6b973074300c2 to your computer and use it in GitHub Desktop.
Save cvogt/8fc07d624d1660ad09f6b973074300c2 to your computer and use it in GitHub Desktop.
Scalable code-generation
import sbt._
import Keys._
import Tests._
object stagedBuild extends Build {
lazy val mainProject = Project(
id="main",
base=file("."),
settings = sharedSettings ++ Seq(
slick <<= codeGenTask, // register manual sbt command
sourceGenerators in Compile <+= codeGenTask // register automatic code generation on every compile, remove for only manual use
)
).dependsOn( codegenProject )
/** codegen project containing the customized code generator */
lazy val codegenProject = Project(
id="codegen",
base=file("codegen"),
settings = sharedSettings
)
// shared sbt config between main project and codegen project
val sharedSettings = Project.defaultSettings ++ Seq(
scalaVersion := "2.11.7"
)
// code generation task that calls the customized code generator
lazy val slick = TaskKey[Seq[File]]("codegen")
lazy val codeGenTask = (sourceManaged, dependencyClasspath in Compile, runner in Compile, streams) map { (dir, cp, r, s) =>
val outputDir = (dir / "generated").getPath // place generated files in sbt's managed sources folder
toError(r.run("demo.CodeGen", cp.files, Array(outputDir), s.log))
val fname = outputDir + "/demo/Generated.scala"
Seq(file(fname))
}
}
package demo
trait Type{
def scalaName: String
def mongooseName: String
}
object StringType extends Type{
def scalaName = "String"
def mongooseName = "String"
}
object IntType extends Type{
def scalaName = "Int"
def mongooseName = "Integer"
}
case class Entity(
mongoName: String,
scalaName: String,
fields: Map[String, Type],
parents: Seq[String],
body: String
)
object Model{
val traits = List( "Foo", "Bar" )
val entities = List(
Entity(
mongoName = "person",
scalaName = "Person",
fields = Map(
"name" -> StringType,
"age" -> IntType
),
Seq("Foo","Bar")
)
)
}
case class CodeGenerator(model: Model.type){
import model._
case class ClassGenerator( entity: Entity ){
def parents = if(entity.parents.isEmpty)"" else ("extends " ++ entity.parents.mkString(" with "))
def generate = (
"""
package demo
"""
++
traits.map(t => "sealed trait "+t).mkString("\n")
++
s"""
/** maps to mongo collection ${entity.mongoName} */
case class ${entity.scalaName}("""++entity.fields.map{case(name, tpe) => name++": "++tpe.scalaName}.mkString(", ")++s""") $parents{
$body
}
object ${entity.scalaName}{
implicit def jsonFormat: Format[${entity.scalaName}] = Json.format
}
"""
)
}
def generate = entities.map(ClassGenerator(_).generate).mkString
}
object CodeGen{
def main(args: Array[String]) = {
//val outputDir = args.head
val output: String = CodeGenerator(Model).generate
println(output)
}
}
@yadavan88
Copy link

I was trying this generator. It was at first giving error that the path doesn't exist. So I created the path, including an empty file. Then the error is not coming, but nothing is getting generated in the file.
println(output) is however printing the generated code.
I am new to the custom sbt tasks, so not sure what is going wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment