-
-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add query bean support #64
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,8 @@ | |
|
||
docs/conf | ||
|
||
/.idea | ||
|
||
# sbt specific | ||
dist/* | ||
target/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,18 @@ lazy val docs = project | |
) | ||
.settings(PlayEbean.unscopedSettings: _*) | ||
.settings(inConfig(Test)(Seq( | ||
playEbeanQueryGenerate := false, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I'd name these just "ebean..." instead of "playEbean...". It's much more concise that way. The query beans also don't have anything to do with Play really, so leaving Play out of the name would make it easier to refactor this into a more general sbt plugin in the future |
||
playEbeanQueryEnhance := false, | ||
playEbeanQueryDestDirectory := "app", | ||
playEbeanQueryResourceDirectory := "conf", | ||
playEbeanQueryModelsPackage := "models", | ||
playEbeanQueryModelsQueryModificationPackage := Set("models/query"), | ||
playEbeanQueryGenerateFinder := true, | ||
playEbeanQueryGenerateFinderField := true, | ||
playEbeanQueryGeneratePublicWhereField := true, | ||
playEbeanQueryGenerateAopStyle := true, | ||
playEbeanQueryArgs := "", | ||
playEbeanQueryProcessPackages := None, | ||
playEbeanModels := Seq("javaguide.ebean.*"), | ||
manipulateBytecode <<= PlayEbean.ebeanEnhance | ||
)): _*) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,11 @@ package play.ebean.sbt | |
import java.net.URLClassLoader | ||
|
||
import com.typesafe.play.sbt.enhancer.PlayEnhancer | ||
import org.avaje.ebean.typequery.generator.{Generator, GeneratorConfig} | ||
import sbt.Keys._ | ||
import sbt._ | ||
import sbt.inc._ | ||
import scala.collection.JavaConverters._ | ||
|
||
import scala.util.control.NonFatal | ||
|
||
|
@@ -16,6 +18,19 @@ object PlayEbean extends AutoPlugin { | |
val playEbeanVersion = settingKey[String]("The version of Play ebean that should be added to the library dependencies.") | ||
val playEbeanDebugLevel = settingKey[Int]("The debug level to use for the ebean agent. The higher, the more debug is output, with 9 being the most. -1 turns debugging off.") | ||
val playEbeanAgentArgs = taskKey[Map[String, String]]("The arguments to pass to the agent.") | ||
|
||
val playEbeanQueryGenerate = settingKey[Boolean]("Generate Query Beans from model classes. Default false.") | ||
val playEbeanQueryEnhance = settingKey[Boolean]("Enhance Query Beans from model classes. Defaults to false") | ||
val playEbeanQueryDestDirectory = settingKey[String]("Target directory for generated classes. Defaults to app ") | ||
val playEbeanQueryResourceDirectory = settingKey[String]("Resource directory to read configuration. Defaults to conf") | ||
val playEbeanQueryModelsPackage = settingKey[String]("Directory of models to scan to build query beans") | ||
val playEbeanQueryModelsQueryModificationPackage = settingKey[Set[String]]("Directories of matching query objects to rewrite field access to use getters. Defaults to [model/query]") | ||
val playEbeanQueryGenerateFinder = settingKey[Boolean]("Generate finder objects") | ||
val playEbeanQueryGenerateFinderField = settingKey[Boolean]("Modify models to add finder field") | ||
val playEbeanQueryGeneratePublicWhereField = settingKey[Boolean]("Public finder field") | ||
val playEbeanQueryGenerateAopStyle = settingKey[Boolean]("Use AOP style generation. Default true") | ||
val playEbeanQueryArgs = settingKey[String]("Args for generation, useful for logging / debugging generation ") | ||
val playEbeanQueryProcessPackages = settingKey[Option[String]]("Change to alter the initial package for scanning for model classes. By default views all") | ||
} | ||
|
||
import autoImport._ | ||
|
@@ -25,7 +40,24 @@ object PlayEbean extends AutoPlugin { | |
|
||
override def trigger = noTrigger | ||
|
||
override def projectSettings = inConfig(Compile)(scopedSettings) ++ unscopedSettings | ||
override def projectSettings = { | ||
val querySettings = Seq( | ||
playEbeanQueryGenerate := false, | ||
playEbeanQueryEnhance := false, | ||
playEbeanQueryDestDirectory := "app", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if this should go to SBT's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it needs to go into a managed source directory, assuming it's a Java file. There's actually a better way to configure directories. Take a look at the Twirl sbt plugin to see how it does it: Concretely here are some steps:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well I'm not sure about that. When you're writing a twirl template, you only have one canonical source file: the .scala.html that the user actively works with to fulfil their tasks. This is compiled in to a mostly unreadable scala file that is used internally by Play that is then compiled down to an actual class file by the compiler. This use case is different : if you peruse the example project, you will see that they have simply checked the generated source code into source control and more importantly they have modified the query beans after the initial generation to add additional functionality to them. If we place it within the managed source directory like this (by default), we are essentially limiting users to only using the autogenerated functionality which is the opposite intent of what I was trying to achieve (based upon the Ebean examples). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @andrewl102 can you share a link to the example project you're talking about? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/ebean-orm/avaje-ebeanorm-examples . The a-basic directory contains an example of usage of query beans. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, sounds like the thing to do then is put the query beans into a managed source directory as Rich suggested. That's how SBT plugins would typically handle this from what I've seen. We could potentially invoke the code generator twice. Once to generate the never-modified query beans into the managed source directory and once to generate the usually modified Finders into the regular source directory. For simplicity though maybe it's easiest to just generate the query beans and forget about the Finders. The Finders seem rather simple compared to the query beans, so most of the value in the code generation is in generating the query beans, so that seems good enough for a v1 of this feature. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be useful, after reading the previous comments I thought perhaps the way to go was finders -> source control and regular beans -> managed sources. If I ran it twice I'd also have to purge the generated query classes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think for simplicity we just don't give the user the ability to generate Finders for now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've spent some time on this and hit some severe problems. The query generation code requires the model classes already be compiled. By moving it to a generated source, we now have the condition where we need the sources for the models to be compiled before we trigger the query generation phase. It might be possible to solve this but it's way beyond the difficulty I had originally intended. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for being MIA and not reviewing over the holidays. That's a good point you bring up. Forget that idea then. Can you add a comment to the code explaining why we're not using managed sources? Don't want someone else trying to go down that path in the future without knowing what they're getting into. I think if we're not going to use managed sources then this is probably better as a task so that you have to type the task name on the console to trigger it. That way we're not automatically overwriting code you have checked in. Hopefully that will be a much easier change to make. |
||
playEbeanQueryResourceDirectory := "conf", | ||
playEbeanQueryModelsPackage := "models", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think users should be able to configure this or the generator must respect models packages even if it is not using the default There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be configurable. |
||
playEbeanQueryModelsQueryModificationPackage := Set("models/query"), | ||
playEbeanQueryGenerateFinder := true, | ||
playEbeanQueryGenerateFinderField := true, | ||
playEbeanQueryGeneratePublicWhereField := true, | ||
playEbeanQueryGenerateAopStyle := true, | ||
playEbeanQueryArgs := "", | ||
playEbeanQueryProcessPackages := None | ||
) | ||
|
||
inConfig(Compile)(scopedSettings) ++ unscopedSettings ++ querySettings | ||
} | ||
|
||
def scopedSettings = Seq( | ||
playEbeanModels <<= configuredEbeanModels, | ||
|
@@ -61,13 +93,36 @@ object PlayEbean extends AutoPlugin { | |
|
||
import com.avaje.ebean.enhance.agent._ | ||
import com.avaje.ebean.enhance.ant._ | ||
|
||
val transformer = new Transformer(classpath, agentArgsString) | ||
|
||
val fileTransform = new OfflineFileTransform(transformer, classLoader, classes.getAbsolutePath) | ||
|
||
try { | ||
if(playEbeanQueryGenerate.value) { | ||
val config = new GeneratorConfig() | ||
config.setClassesDirectory(classes.getAbsolutePath) | ||
config.setDestDirectory(playEbeanQueryDestDirectory.value) | ||
config.setDestResourceDirectory(playEbeanQueryResourceDirectory.value) | ||
|
||
config.setEntityBeanPackage(playEbeanQueryModelsPackage.value) | ||
config.setAddFinderWherePublic(playEbeanQueryGeneratePublicWhereField.value) | ||
config.setAopStyle(playEbeanQueryGenerateAopStyle.value) | ||
|
||
val generator: Generator = new Generator(config) | ||
generator.generateQueryBeans() | ||
if (playEbeanQueryGenerateFinder.value) { | ||
generator.generateFinders() | ||
} | ||
if (playEbeanQueryGenerateFinderField.value) { | ||
generator.modifyEntityBeansAddFinderField() | ||
} | ||
} | ||
|
||
val transformer = new Transformer(classpath, agentArgsString) | ||
val fileTransform = new OfflineFileTransform(transformer, classLoader, classes.getAbsolutePath) | ||
fileTransform.process(playEbeanModels.value.mkString(",")) | ||
if(playEbeanQueryEnhance.value) { | ||
val queryTransform = new org.avaje.ebean.typequery.agent.Transformer(playEbeanQueryArgs.value, classLoader, playEbeanQueryModelsQueryModificationPackage.value.asJava) | ||
val fileQueryTransform = new org.avaje.ebean.typequery.agent.offline.OfflineFileTransform(queryTransform, classLoader, classes.getAbsolutePath) | ||
//Defaults to null, like the Maven plugin | ||
fileQueryTransform.process(playEbeanQueryProcessPackages.value.orNull) | ||
} | ||
} catch { | ||
case NonFatal(_) => | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the latest is 1.5.3