Skip to content

nyampass/jerk

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Jerk – template engine for Play 2

forked from https://github.com/branaway/japid42.

The latest version: see the end of this file for version information

Compatibility: Play 2.1.x, 2.2.x
Note: One must use Jerk version 0.9.16 or later with Play 2.2.1 or later.

Note: version 0.7.4 and older is compatible with 2.0.4. Version 0.9.9.1 or earlier is compatible with Play2.1.×.

About

Jerk is a native Java based template engine for Java programmers using Play2.
It can also be used in any application that need an advanced templating solution.

  1. Very fast in reloading changed views. You simply change the views and refresh your browser to get it right away in a blink instead of taking a coffee break. Japid manages its own view compilation and class loading. Changes in the Japid views are isolated from the Play reloading mechanism.
  2. Full featured. It at least matches the feature set of the built-in Scala based template engine.
  3. Excellent runtime performance.
  4. Java-based, thus very friendly to Java programmers.
  5. The core Japid has been used in production for a long time and is very robust.
  6. Japid views compile lazy and compile on demand, unnoticed.
  7. It integrates with Play2 seamlessly, in the meantime can be used stand-alone with any Java applications.

Features

- freely mixing Java code in templates.
- layout inheritance
- tags and local snippet
- controller action invocations from within views
- implicit objects available in views: request, response, session, flash, lang
- all sorts of directives
- support Play2 forms, fields, data binding and form validations.
- cache with action invocation, wither using the Cached annotation or timeout spec in `a directive
- session.authentityToken to protect app from forged form submission attach.
- render data to dynamic templates in memory
- support JDK6 & 7.
- fully distribute-able in jar files.

New features since version 0.9.5:

- It comes with a Play router implementation that use a mixture of annotation and convention to do away with the route files of typical Play applications. See below for detail.

Credit: the work is derived from Peter Hausel’s little nice project hosted here: https://github.com/pk11/play-jaxrs. The differences are explained later.

Usage

For a full sample application with Japid, see: http://github.com/branaway/computer-japid

Please also check out the JapidSample project in the samples directory for an example.

Basically you’ll need to do three things:

0. Get the dependencies

- declare Japid in the project/Build.scala and add the local repository resolver:


//…
val appDependencies = Seq(
javaCore,
cache, // not needed for working with Play 2.1.x
“japid42” % “japid42_2.10” % “{the latest version}” // use 0.9.9.1 if you want to use Japid routing with Play 2.1.1 or earlier
)

val main = PlayProject(appName, appVersion, appDependencies).settings( resolvers += Resolver.url(“My GitHub Play Repository”, url(“http://branaway.github.io/releases/”))(Resolver.ivyStylePatterns) )

//…

Note: adjust the version number accordingly.

1. Initialize Japid in Global.java in the app directory:

public class Global extends cn.bran.play.GlobalSettingsWithJapid{}

Note:

- There are a few things that can be customized here. Here is a list of them. Hopefully they are self-explanatory.

	addImport(String imp)
	addImport(Class<?> cls)
	addImportStatic(String imp)
	addImportStatic(Class<?> cls)
	setKeepJavaFiles(boolean keepJava)
	setLogVerbose(boolean verb)
	setTemplateRoot(String... root)
	setRefreshInterval(int i)
	setCacheResponse(boolean c)
	setPresentErrorInHtml(boolean presentErrorInHtml)
	setEnableJITCachePersistence(boolean enableJITCachePersistence)

Please see the computer-japid (https://github.com/branaway/computer-japid) example for a real-world Global class definition.

2. Extend cn.bran.play.JapidController to create your own controllers and use the renderJapid(…) etc to render the views.

package controllers;

import play.mvc.Result;
import cn.bran.play.JapidController;

public class Application extends JapidController {
  
  public static Result index() {
	  return renderJapid("cool");
  }
}

3. Create the Japid view script “{my app root}/japidroot/japidviews/Application/index.html”:

@(String m)
Hello, $m!

4. Hit http://localhost:9000/ to see Japid at work!

Releasing Your Application

When an app is ready to be distributed, you’ll need to modify the build.scala to prepare your project for distribution. There are two things needed to be done:

  1. include a directive in the build.scala to specify where the Japid root directory is, so the Japid scripts are included in the generated jar file. Note, you’ll want to leave it commented out in dev mode, since it would trigger app reloading when a Japid script is changed.
    
        val main = play.Project(appName, appVersion, Seq()).settings(
            ...
            ,unmanagedResourceDirectories in Compile  <+= baseDirectory( _ / "japidroot" )
        )
    
  2. (optionally, to avoid some warning messages) set the japid root to null explicitly in your global settings file, so the Japid engine will pick up the Japid scripts in the jars.
    
        public class Global extends GlobalSettingsWithJapid {
            @Override
            public void onStartJapid() {
                if (_app.isDev())
                    setTemplateRoot("japidroot");
                else
                    setTemplateRoot((String[])null); // scan class only
            }
        }
    

This applies to using Japid in sub-projects settings too.

Using Japid in Sub-projects

In Build.scala, you may have a sub-project defined like this:


    val appDependencies = Seq(
      "japid42" % "japid42_2.10" % "{the version number}"
    )

val foo = PlayProject(“foo”, “0.1”, appDependencies, path = file(“modules/foo”)) val main = PlayProject(appName, appVersion, appDependencies).settings( resolvers += Resolver.url(“Japid on Github”, url(“http://branaway.github.io/releases/”))(Resolver.ivyStylePatterns) ).dependsOn(foo)

Obviously you have a sub-project located in modules/foo. For the system to know the Japid scripts in the sub-project, you’ll need to tell Japid the location, using the JapidRenderer.setTemplateRoot(String…) method, in the global initialization method:


public class Global extends cn.bran.play.GlobalSettingsWithJapid {
	@Override
	public void onStartJapid() {
		setTemplateRoot("japidroot", "modules/foo/japidroot");
	}
}

In the above example, the first argument is the location of the Japid scripts in the parent project. The second argument is the root for the Japid scripts in the sub-project. All paths are relative to the root of the master project.

The computer-japid(http://github.com/branaway/computer-japid) sample application demos this feature.

Use Japid Router to replace the Play2 routing mechanism (experimental but quite usable)

Note: version 0.9.5 supports Play 2.1.1 while version 0.9.6+ supports Play 2.1.2, and version 0.9.11 supports Play 2.2.×.

Every typical Play applications come with a route file in the conf directory. The conf file is compiled to multiple scala files when the app is loaded or reloaded.

Two issues with the approach:

  1. Compiling the route file is slow. The multiple scala files derived from the route file take a long time a compile. Any change in the route file triggers this lengthy process which is a productivity killer in development.
  2. Maintaining the route file is annoying. Every time one adds a new method or change the signature of an action method, one must go searching the long routing table in the route file to adjust the mapping properly. Another productivity killer.

The newly introduced Japid Router takes care of these by using some conventions with minimum Annotations.

These are the steps to take advantage of the feature:

1. Enable the Japid Router in the global settings:

public class Global extends GlobalSettingsWithJapid {
	@Override
	public void onStartJapid() {
		//...
		setUseJapidRouting(true); // not required since 0.9.11
	}
}

2. Use a few annotations in the controllers:

package controllers;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;

import play.mvc.Result;
import cn.bran.play.JapidController;

@Path("") // an empty value means to use the controller name (minus the leading "controller." part) as the first segment of the URL path
public class Jax extends JapidController {
	// takes HTTP GET. the path is "yahoo"
	@GET
	public static Result yahoo() {
		return ok("news!");
	}

	@GET
	@Path("/glass") // explicit path
	public static Result g() {
		return ok(renderJapid("ggff"));
	}

	// take any HTTP method
	// explicit path following JAX-RS convention
	@Path("/book/{isbn:[0-9]+}/{chapter:[abc]}")
	public static Result getBook(@PathParam("isbn") Integer id, @PathParam("chapter") String chap, @QueryParam("note") String note) {
		return ok("got book: " + id + "/" + chap + ": " + note);
	}

	// take any HTTP method
	// auto-routing based on positional parameter mapping
	public static Result bo(int id, String chap, @QueryParam("note") String note) {
		return ok("got bo: " + id + "/" + chap + ": " + note);
	}
}

Note: the Japid routing can actually co-exist with the traditional route file. The Japid router falls back to the default route file in case no route is matched.

The @GET, @POST, @PUT, @DELETE, @HEAD, @OPTIONS, @Path, @PathParam and @QueryParam are standard JAX-RS annotations. But the resemblance to JAX-RS ends there.

The auto-routing convention

When an action method carries no JAX-RS @Path annotation, the routing takes a set of conventional rules:

  1. @PathParam CANNOT be used with the parameters whereas @QueryParam can still be used.
  2. A URL is composed in multiple segments separated by a “/”.
  3. The first segment is the controller’s class name, minus the “controller.” part, plus the name of the method to invoke.
  4. The rest of the URL path is mapped to the parameters of the action method by position, i.e., the first segment is mapped to the first none-@QueryParam-annotated parameter and so on. Basically the URL is composed to invoke the action method as in PASCAL convention. An example:

package controllers;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import play.mvc.Result;
@Path("") // the path to this controller is the name of the class minus the "controller." part
public class Jax extends Controller {
	public static Result book(int id, String chap, @QueryParam("note") String note) {
		return ok("got book: " + id + "/" + chap + ": " + note);
	}
}

To invoke the book action, the URL must be compose similar to /Jax.book/123/chapter2?note=mynote. The id parameter takes 123 as the value. The chap parameter takes “chapter2” as the value. The argument to the “note” parameter gets its value from the query string in the name of “note”. The convention can be summarized as

http://myhost/{app name}/{controller name without leading "controllers."}.{method name}/{parameter 1}/{parameter 2}/...?{query param 1}=...&{query param 2}=...

Note: the app name segment is defined by an instance of @ApplicationPath with the Global class.

Credit

As mentioned before, the Japid router is inspired by Peter Hausel’s project at “https://github.com/pk11/play-jaxrs”. I have made a few interesting features on top of it.

  1. Automatic argument type conversion was added.
  2. Regex format of path parameters are enforced.
  3. The use HTTP method annotation, @Path, @PathParam are all optional, and in that case we use some conventions to route the HTTP requests, as explained above.
  4. The implementation is mostly Java based.

Use Japid in any Java applications

First of all, you grab the japid jar from the target/scala-2.10 folder and two other dependencies from the libs4plainjava folder.

Then the following code snippet is the simplest form of using Japid in any plain Java applications:

	// do this once, using true to set dev mode to reload Japid script changes, false otherwise. 
JapidRenderer.init(true) 
	// now render some data to a Japid script "{app name}/japidroot/japidviews/hello.html"
RenderResult rr = JapidRenderer.renderWith("japidviews/hello.html", "John");
	// rr.toString() outputs what you want to see

By default the intermediary Java files derived from the Japid scripts are saved along with the scripts. Use JapidRenderer.setKeepJavaFiles(false) to keep them in the memory only.

The following is a code snippet that demonstrate using Japid with a Servlet:

public class S2 extends HttpServlet {

	public void init(ServletConfig config) throws ServletException {
		String p = config.getServletContext().getRealPath("WEB-INF/japidroot");
		JapidRenderer.setTemplateRoot(p);
		JapidRenderer.init(true);
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Person p = new Person(); p.name = "Nadal";
		RenderResult rr = JapidRenderer.render(p);
		response.getWriter().println(rr.toString());
	}

}

Documentation

The “computer-japid” sample project contains a README file that explains most of the Japid usage. Please refer to it.

For detailed Japid grammar please refer to the Japid 1 manual before I write something formal:

https://github.com/branaway/Japid

Sample applications

Hacking Jerk in Eclipse

1. Git clone the Jerk project to your local computer.
2. Build it and publish it to your local repository: “play publish-local”, where the play is the Play2 binary.

Note:

- I’m using sbt 0.12.2 locally. Match the sbt version in the “project/build.properties” file to yours .
- Also check and match the sbt-plugin version in the “project/plugins.sbt” file to your local version.

About

A full feaured Java-based template engine for Play2

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Java 69.6%
  • JavaScript 27.4%
  • CSS 2.6%
  • Scala 0.4%