Trying to create Scalate Plugin and getting "not found: value render"

319 views
Skip to first unread message

Matt Raible

unread,
Nov 3, 2011, 11:28:20 AM11/3/11
to play-framework
Hello all,

I'm trying to turn the following Scalate Trait into a Plugin.

import play.mvc.{Scope, Http}

trait Scalate {

def render(args: (Symbol, Any)*) = {
var template = Scope.RenderArgs.current().get("template")
if (template == null) {
template = Http.Request.current().action.replace(".", "/")
}

renderTemplate(template.toString, args: _*);
}

def renderTemplate(template: String, args: (Symbol, Any)*) = {
ScalateTemplate(template).render(args: _*);
}
}

With this trait, I had the following in ScalateTemplate.scala:

case class Template(name: String) {

def render(args: (Symbol, Any)*) = {
val argsMap = populateRenderArgs(args: _*)

scalateEngine.layout(name + scalateType, argsMap)
}
}

def apply(template: String) = Template(template)

To turn this into a plugin, I copied ScalateTemplate into a module
project and modified the Template class to be as follows:

case class Template(name: String) {

def render(args: Map[String, Any]): String = {
scalateEngine.layout(name + scalateType, args)
}
}

I created the following Plugin to call ScalateTemplate:

class Plugin extends PlayPlugin {

var templateLoader:PlayPlugin = null

override def loadTemplate(file: VirtualFile): Template = {
if (null == templateLoader) return new FancyTemplate(file)
return templateLoader.loadTemplate(file);
}

case class FancyTemplate(file: VirtualFile) extends Template {
var template:String = null

def apply(file: VirtualFile) {
this.name = file.getName
this.source = file.getRealFile.getAbsolutePath
this.template =
Scope.RenderArgs.current().get("template").toString
if (this.template == null) {
this.template = Http.Request.current().action.replace(".",
"/")
}
}

override def compile(): Unit = {}
override def render(): String = {
ScalateTemplate(template).render(Map())
}
override def internalRender(args: java.util.Map[String, AnyRef]):
String = {
import scala.collection.JavaConversions._

ScalateTemplate(template).render(args.toMap)
}
}
}

However, when I try to use it, I get:

/Users/mraible/dev/play-more/app/controllers/Home.scala:10: not found:
value render
render('athlete -> athlete)
^
/Users/mraible/dev/play-more/app/controllers/Home.scala:14: not found:
value render
render()
^
/Users/mraible/dev/play-more/app/controllers/Home.scala:18: not found:
value render
render()
^
three errors found

Any ideas?

Thanks,

Matt

Guillaume Bort

unread,
Nov 3, 2011, 3:13:22 PM11/3/11
to play-fr...@googlegroups.com
The ScalaController type doesn't define any render method right?

The problem is that in Play 1.2, the Plugin API was designed for the
Java API, and then we started to experiment in a separate module with
scala. So most methods of the Plugin API are not relevant for the
Scala API.

Btw, that's why we started Play 2.0 to clean all this stuff and have 2
API at the same level with a proper Plugin mechanism.

In the meantime you should just provide a trait in your jade module.
It is probably the easiest and cleanest way to handle it.

> --
> You received this message because you are subscribed to the Google Groups "play-framework" group.
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.
>
>

--
Guillaume Bort, http://guillaume.bort.fr

Matt Raible

unread,
Nov 4, 2011, 1:14:25 PM11/4/11
to play-fr...@googlegroups.com
Copying my trait to a plugin solved the problem. However, I'd also like to try and override the default behavior (rending a Template with Template() instead of render()). I've gotten close, but can't figure out how to turn a java.util.Map into (Symbol, Any).

override def compile(): Unit = {}

override def render(args: java.util.Map[String, AnyRef]): String = {
internalRender(args)


}
override def internalRender(args: java.util.Map[String, AnyRef]): String = {

// todo: Convert from a [String, AnyRef] to (Symbol, Any)
// ScalateTemplate(template).render(args)
""
}

Anyone know how to do this conversion?

Thanks,

Matt

Matt Hildebrand

unread,
Nov 4, 2011, 1:27:56 PM11/4/11
to play-fr...@googlegroups.com
Here is one possibility:

def convertMap(in: java.util.Map[String, AnyRef]): Map[Symbol, Any] = {
  import scala.collection.JavaConversions._

  Map.empty ++ (in map { pair => (Symbol(pair._1) -> pair._2.asInstanceOf[Any]) })

Matt Raible

unread,
Nov 4, 2011, 3:29:38 PM11/4/11
to play-fr...@googlegroups.com
Nope:

   [scalac] /Users/mraible/dev/play-scalate/src/play/modules/scalate/Plugin.scala:36: error: type mismatch;
   [scalac]  found   : scala.collection.immutable.Map[Symbol,Any]
   [scalac]  required: String
   [scalac]       Map.empty ++ (args map { pair => (Symbol(pair._1) -> pair._2.asInstanceOf[Any]) })
   [scalac]                 ^
   [scalac] one error found

BUILD FAILED
/Users/mraible/dev/play-scalate/build.xml:52: Compile failed with 1 error; see the compiler error output for details.

Guillaume Bort

unread,
Nov 4, 2011, 3:35:02 PM11/4/11
to play-fr...@googlegroups.com
Have you tried scala.collection.JavaConverters ?

Matt Raible

unread,
Nov 4, 2011, 3:38:03 PM11/4/11
to play-fr...@googlegroups.com
I feel like I've tried everything. I spent several hours last night trying everything I could think of. Of course, it was from 12-3 am, so it could be something very simple that I missed. ;)

Matt Hildebrand

unread,
Nov 4, 2011, 3:43:08 PM11/4/11
to play-fr...@googlegroups.com
The compiler error suggests that you actually want a String.  Perhaps you forgot to render the template using the Map[Symbol, Any]?

Your question was apparently asking how to convert a java.util.Map[String, AnyRef] to a Map[Symbol, Any], and that is what the snippet I sent does -- see below.  Perhaps I misunderstood the question.

scala> val m = new java.util.HashMap[String, AnyRef]
m: java.util.HashMap[String,AnyRef] = {}

scala> m.put("a", "123")
res0: AnyRef = null

scala> m.put("b", "456")
res1: AnyRef = null

scala> def convertMap(in: java.util.Map[String, AnyRef]): Map[Symbol, Any] = {
     |   import scala.collection.JavaConversions._
     |   Map.empty ++ (in map { pair => (Symbol(pair._1) -> pair._2.asInstanceOf[Any]) })
     | }
convertMap: (in: java.util.Map[String,AnyRef])Map[Symbol,Any]

scala> convertMap(m)
res2: Map[Symbol,Any] = Map('a -> 123, 'b -> 456)

Matt Raible

unread,
Nov 4, 2011, 3:53:39 PM11/4/11
to play-fr...@googlegroups.com
The render method I'm trying to call takes the following:

def render(args: (Symbol, Any)*)

To be honest, I'm not sure if this is a Map or a Seq. 

Matt Hildebrand

unread,
Nov 4, 2011, 4:23:16 PM11/4/11
to play-fr...@googlegroups.com
On Fri, Nov 4, 2011 at 3:53 PM, Matt Raible <mra...@gmail.com> wrote:
The render method I'm trying to call takes the following:

def render(args: (Symbol, Any)*)

In that case, you need a Seq[(Symbol, Any)], not a Map[Symbol, Any] or String.  How about this, then?

render(convertMap(theMapToConvert).toSeq :_*)

The * in the render method's parameter list means "zero or more" (Symbol, Any) tuples -- in other words, it's Scala syntax for varargs.  The :_* at the call site means "don't pass this collection as a single parameter, but rather pass each element that it contains as its own, separate parameter to the varargs method I'm calling".

Cheers,
-Matt

Matt Raible

unread,
Nov 4, 2011, 5:13:11 PM11/4/11
to play-fr...@googlegroups.com
Could this be failing because I'm using Scala 2.8.1?

Here's my code, which is what I believe you're suggesting:

    override def internalRender(args: java.util.Map[String, AnyRef]): String = {
      var map:Map[Symbol, Any] = convertMap(args);
      ScalateTemplate(template).render(map.toSeq :_*)
    }
    
    def convertMap(in: java.util.Map[String, AnyRef]): Map[Symbol, Any] = {
      import scala.collection.JavaConversions._
      
      Map.empty ++ (in map { pair => (Symbol(pair._1) -> pair._2.asInstanceOf[Any]) })
    }

The result is:

   [scalac] /Users/mraible/dev/play-scalate/src/play/modules/scalate/Plugin.scala:36: error: type mismatch;
   [scalac]  found   : Any
   [scalac]  required: String
   [scalac]       ScalateTemplate(template).render(map.toSeq :_*)
   [scalac]                                                                     ^
   [scalac] one error found

The ^ in my console points to the opening paren before map.toSeq

Reply all
Reply to author
Forward
0 new messages