This is a fairly technical mail about features that aren't written
yet. Feel free to ignore.
My quest to write a version of the tutorial using Scala brought me to
model objects and tests. Writing a simple entity in Scala is easy, but
I started with a Java entity and a Scala test. The trouble starts when
you want to write User.find(…) and find out that in Scala that doesn't
work. JPASupport.find(…) does work, but doesn't run, because the byte
code magic really wants the find method to belong to User.
What would work for Scala is to declare the User companion object and
make that extend something providing the methods. The problem is: that
something can't be the existing JPASupport class and so will mean a
significant restructuring of the play core. Worse, I don't think it
can be made compatible with Java as long as static methods are involved.
This wouldn't matter if every play application started from scratch,
but the CRUD module in particular knows very intimate details of the
current JPA support. So is there a future for Scala as a module?
A possible way forward that I can see is to define a core API that may
not be the nicest thing to work in from either language, but that is
at least compatible with both. Think plain JPA with something to
provide an entity manager. Core modules like CRUD would then be
rewritten using the new API. The current Java sugar for entities could
be added back in based on the API, but it wouldn't be used for Scala
code. Scala would use different means to create a nice to use API.
Now I could be wrong and see problems that aren't there, or we could
decide this is all too much work for a small project and focus our
energy on other ways to make Play! better. Either way, I'd like to
know what you think.
> Hi Bart, I have not tried it but could not implicit conversions be
> used to simulate find on models?
Oh yes, a nice API for Scala code would probably make tasteful use of
implicit conversions, as well as lots of traits to mix in. My mail was
more about the details of actually making it work.
> just a thought
> Peter
This will call:
public static JPAQuery find(String query, Object... params) {
throw new UnsupportedOperationException("Please annotate your
JPA model with @javax.persistence.Entity annotation.");
}
but what it really should call is added by JPAEnhancer to every model
class:
CtMethod find = CtMethod.make("public static
play.db.jpa.JPASupport.JPAQuery find(String query, Object[] params)
{ javax.persistence.Query q = em().createQuery
(play.db.jpa.JPQLDialect.instance.createFindByQuery(\"" + entityName +
"\", \"" + entityName + "\", query, params)); return new
play.db.jpa.JPASupport.JPAQuery
(play.db.jpa.JPQLDialect.instance.createFindByQuery(\"" + entityName +
"\", \"" + entityName + "\", query, params),
play.db.jpa.JPQLDialect.instance.bindParameters(q,params)); }",
ctClass);
I didn't try it yet, I only reacted to JPASupport.find, but this
evening I'll see if I can make a piece of Scala with a User object
with no methods, yet where User.find will compile.
On Nov 3, 2009, at 11:43, Guillaume Bort wrote:
>
> Does this implicit conversion work (Despite the fact that it will need
> to call some non-existent code) ?
>
> On Tue, Nov 3, 2009 at 12:27 AM, Bart Schuller <Bart.S...@gmail.com
> > wrote:
>>
>>
>> On Nov 3, 2009, at 0:09, phausel wrote:
>>> class RichModel(m:Model) {
>>> def find(query:String,params:java.lang.Object*)=JPASupport.find
>>> (query,params:_*)
>>> }
> The problem is that the User companion object seen by scala, does not
> extends JPASupport.
> So the implicit method will not work. Perhaps if I add a Java
> interface to JPASupport, it could be seen as a scala Trait ?
In Scala, it's very easy to add lots of methods to an object such as
User:
object User extends JPAMethods
where JPAMethods is an interface declaring normal methods, or a Scala
trait where the methods can carry implementations as well.
But for the case where User is a java class, this will not work. I'll
still try the implicit conversion way, because even though User is to
Scala an object with no methods, it should still be possible to give
it an explicit conversion, something like def any2Richmodel(any: AnyRef)
I tried it a little but can't make it work.
For the User class defined in Java, it seems that there is no real
scala companion object:
println(User) ==> value User not found.
> But what would work is mandating that if you use java models with
> scala, you write one scala file in the models directory containing a
> line for every model like this:
>
> object Juser extends Findable
Of course, with all the custom classloading and compiling going on, it
would be possible to have play do this itself.
Still, that leaves writing the trait that implements JPASupport but as
regular methods.
Yes that's a good idea. That allows to work with existing Java object in a very simple way.Btw I think we could make these objects inherit Model directly and use implicits to redefine static methods. If the object is defined in scala, it should work.
No, once we declare the objects to extend something, the implementation can live there as well and we don't need implicits, but it really needs to be written somewhere as normal methods first.
> Ok, fine.
> I will start to move all this code out of the JPAEnhancer...
That looks good. Here's where I got this evening:
Juser is a java model, User is a scala model:
val bob: Juser = find[Juser]("byEmail", "b...@gmail.com").first()
val harry: User = find[User]("byEmail", "ha...@gmail.com").first()
val tom: User = User.find("byEmail", "t...@gmail.com").first()
You'll notice that Juser.find is not in there. I tried, but it looks
like it really isn't possible.
Of course, find[Juser](q, v) is just one possible function-like
syntax, find("User", q, v) is even easier (and would also work as an
alternative java syntax with a static import).
Comments, opinions?