My thoughts on Play 2.0

1,213 views
Skip to first unread message

Tymon Tobolski

unread,
Feb 10, 2012, 2:16:04 PM2/10/12
to play-framework
I've been using Ruby on Rails framework for four year now, and I've
seen how it changed during that time. Play 2.0 is on its way to become
really nice piece of software. I read play wiki and have some thoughts
about it in contrast to Ruby on Rails.

I'm not saying that play sucks because it's not rails, and that it
should copy everything from it (please don't!). All I want to share is
that some concepts were already proven by many people over the years,
and there is no point in going through that again.


So here are my thoughts:



### Routing

Rails conventions are havily based on RESTful resources. While play
routes are similar to Rails' there are few differencies:
- rails uses dsl written in ruby, play has custom file format (why not
scala dsl?)
- play allows to define one route at a time, like: METHOD PATH ACTION
while rails has some helpers that simplfy common cases

Rails routing syntax basics:

# Simplest match, /users/1 will route to users_controller.show
with param :id = 1
match "/users/:id" => "users#show"

# Instead of 'match', you can specify http method
get "/foo" => "foo#get_bar"
post "/foo" => "foo#post_bar"
# etc...


Resource routes:

resources :users

will generate 7 routes:

GET /users UsersController#index
GET /users/:id UsersController#show
POST /users UsersController#create
PUT /users/:id UsersController#update
DELETE /users/:id UsersController#destroy
GET /users/new UsersController#new
GET /users/:id/edit UsersController#edit

time has shown that people were in fact writing all those routes by
themself, but with different names and paths. Having one standard way
of creating CRUD controller not only cleans up code, but also makes it
easier to maintain. Every Rails programmer knows this convention and
sticks with it, so there are no surprises when looking at other
people's code. Of course, this is not a silver bullet and it is
impossible to build whole application with routes like above.

resources :users do
collection do
get :active
end

member do
get :profile
end
end

leads to:

GET /users/active UsersController#active
GET /users/:id/profile UsersController#profile


Other very useful feature of rails routing is namespacing. Consider
this example:

namespace :api do
resources :users
end

This will create 7 routes as described before, but prefixed with "/
api/" and pointing to Api::UsersController. Again, simple convention
that saves few keystrokes and increases readability. ('namespace'
feature would probably be easier to implement using scala dsl).

Resources can also be nested:

resources :users do
resources :comments
end

Will create:

GET /users/:user_id/comments CommentController#index
GET /users/:user_id/comments/:id CommentController#show
etc...

(from my experience, this is really handy)


Other interesting feature of rails routes are optional parameters:

match "/posts(/:id)" => "posts#show"

This will match both /posts and /posts/5 paths. From play perspective,
this could be simply handled with Option[T] function argument.

Other usage of optional parameters:

match "/users/:id(.:format)" => "users#show"

In Rails, :format default to "html", and is havily used when creating
responses (I'll talk about that later). This route will match e.g. /
users/1 (format=html), /users/1.json (format=json), /users/1.xml
(format=xml) etc.

Btw, why not make routing staticly typed?
If there is a function `def show(id: Int)` it would be only valid
if :id parameter was Int (early validation of incomming requests)

Rails routing is simple yet powerfull, even allowing routes like

match "/some_legacy_route" => redirect("/new_route")


Every modern ruby web framework is based on rack - small alyer that is
interface between various servers and web frameworks. It introduces to
the ruby world concept of middlewares and mountable engines - mulitple
applications running at different path, sharing common data.



### Request parameters

Rails takes care of incommint request body. By default it will parse
it as form data, but if request have Content-Type header it will try
to parse json or xml. On the user side, there is only one nested hash
- `params` and one action can work with all kinds of input data
without having to explicitly define type of request body.



### Views

Play has hardcoded own templating engine, that may be good for java
developers, but surely is "from 2006" for ruby community. At the
beginning, rails used ERB: `<div><%= some_ruby %></div>`. It worked,
but wasn't good enough. Nowedays, after few years of trying different
approaches HAML is happen to be the choice of most rails programmers.
What's most important, rails itself doesn't really care what do you
use. And you can you both erb and haml in one application (or other
templating launguage). Rails simply just look at the filename, and
when it finds something like "index.html.erb" it checks if there is
registered "erb" processor and creates "index.html" file using that
processor. Same think with "index.html.haml" or
"index.html.your_processor". Again, simple convention that simplifies
a lot. You could even have "index.xml.builder" - processed with xml
builder library.

This also applies to assets, but this will be next point of my
message.

Rendering

in rails there are several ways of creating response:

render "template_name" # well, render the template
render :json => {:foo => 1} # render some json, sets correct
Content-Type
render :xml => {:bar => 2} # render some xml, sets correct
Content-Type

One magical method in rails is `respond_to`

def index
posts = get_posts_from_database
respond_with(posts)
end

this one takes care of :format request parameter and will render html,
json, xml or any other registered format - internally rails calls
"to_FORMAT" method on provided object, but I think that could be
resolved with proper typeclasses



### Assets

I've noticed that play 2.0 has hardcoded coffeescript and lesscsss
support. This is similar case as with rendering templates. Rails uses
the same convention: "myapp.js.coffee" -> "myapp.js", "style.css.sass"
-> "style.css". Exactly the same behavior - register processor and
that's it.

Rails 3.1 introduced assets pipeline, that could be simply described
as:

- process all js/css files (with coffee, sass, less, etc)
- put it in one big application.js/css file
- compress!
- attach timestamp to result

In development mode, you could see tens on `<script src="...">` tags,
but in production environment you will see only one with application-
TIMESTAMP_HASH.js - minified (optionally gzipped).

Easy, easy to server, easy to cache.

(It also allows serving assets from 3rd party plugins)



### EOF // posted here: https://gist.github.com/1791857

Tymon Tobolski

unread,
Feb 10, 2012, 3:01:45 PM2/10/12
to play-framework
More detailed examples can be found here: http://guides.rubyonrails.org/

Ben McCann

unread,
Feb 10, 2012, 4:25:22 PM2/10/12
to play-fr...@googlegroups.com
+1 to having some way to generate the 7 routes you get from rails for free without having to write 7 rules.  
I've been writing these rules myself to stick to that convention, but it's a bit tiresome and sometimes someone on the team accidentally writes one which deviates from the convention we're trying to keep.

Guillaume Bort

unread,
Feb 10, 2012, 4:45:33 PM2/10/12
to play-fr...@googlegroups.com
I know rails very well but there is a main difference with Play: Rails
is based on Ruby a dynamic language and Play use either Java or Scala
two statically typed language.

Most of magic features of Rails are possibles because Ruby is dynamic.
Trying to do the same with a statically typed language requires some
black magic (reflection and byte code manipulation), and so makes the
use of a compiler less useful. We don't want to compete with framework
based on dynamic languages in these areas (and actually we tried to
remove most magic in Play 2.0 to make it more robust).

Now regarding the specific points you highlighted:

### Routing

The router is done this way to be fully type checked. But it is then
difficult to generate dynamically some routes. I mean, even if the
router could generate several routes for you, it would fail if there
is no corresponding action methods.

Anyway the router is fully pluggable in Play 2.0, so you can write
something more dynamic looking like the Rails one if you really want
it.

### Request parameters

I think it is way better to give you a DOM structure for XML bodies, a
Json object for Json, and a Hash for url form encoded. Trying to merge
everything in a simple Hash make it finally more complex to use. I
mean, it can only work with simple XML/Json format. Even if it looks
simpler, it eventually become more complex to use with real world XML
or Json structure.

### Views

The Play 2.0 template engine is fully pluggable as well. There is
nothing hard wired. But again I think that having full control over
your HTML is way better. Anyway if you prefer something different you
can easily use it from within a Play application. Scalate provides
Scaml that looks like Haml.

### Assets

Again there is nothing hardcoded. The cofeescript, google closure and
less compiler are just here because there are convenient. You can plug
in everything else you want, or provide a custom way of packaging your
assets. Assets management is integrated into the sbt build system like
any other project resource.

> --
> 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

Tymon Tobolski

unread,
Feb 19, 2012, 6:52:42 PM2/19/12
to play-framework
New router proposal: https://gist.github.com/1866044
> > You received this message because you are subscribed to the...
>
> read more »

Anton Bessonov

unread,
Feb 19, 2012, 7:29:15 PM2/19/12
to play-fr...@googlegroups.com
I like personally Django-way[1] to describe urls. Regex allow very
powerful url-mapping.

Another interesting approach is so called mount points in Wicket[2].

[1] https://docs.djangoproject.com/en/dev/topics/http/urls/

[2] https://cwiki.apache.org/WICKET/url-coding-strategies.html

> --

> You received this message because you are subscribed to the Google
> Groups "play-framework" group.

> To view this discussion on the web visit
> https://groups.google.com/d/msg/play-framework/-/UOG5__LX-yYJ.

Tymon Tobolski

unread,
Feb 19, 2012, 8:31:45 PM2/19/12
to play-framework
[1] - Easily done with custom type and ParamsMatcher[T] like:

case class MyStr(str: String)
implicit def MyStrParamsMatcher = new ParamsMatcher[MyStr] { def
unapply(s: String) = if(s matches someRegex) Some(MyStr(s)) else
None }
implicit def MyStr2String(s: MyStr) = s.str



On Feb 20, 1:29 am, Anton Bessonov <exe...@googlemail.com> wrote:
> I like personally Django-way[1] to describe urls. Regex allow very
> powerful url-mapping.
>
> Another interesting approach is so called mount points in Wicket[2].
>
> [1]https://docs.djangoproject.com/en/dev/topics/http/urls/
>
> [2]https://cwiki.apache.org/WICKET/url-coding-strategies.html
>
> On 10.02.2012 22:25, Ben McCann wrote:
>
>
>
>
>
>
>
> > +1 to having some way to generate the 7 routes you get from rails for
> > free without having to write 7 rules.
> > See section 2.2http://guides.rubyonrails.org/routing.html

Julien Richard-Foy

unread,
Feb 20, 2012, 3:55:09 AM2/20/12
to play-fr...@googlegroups.com
On Mon, Feb 20, 2012 at 12:52 AM, Tymon Tobolski <i...@teamon.eu> wrote:
> New router proposal: https://gist.github.com/1866044

Interesting code, but how do you handle reverse routing?

Tymon Tobolski

unread,
Feb 20, 2012, 6:09:52 AM2/20/12
to play-framework
Right now it is not handled. Current router implementation do not
allow double routes, like:

GET /foo App.foo
GET /bar App.foo

- this will not compile and will break reverse routing.

Noel Welsh

unread,
Feb 20, 2012, 7:28:42 AM2/20/12
to play-fr...@googlegroups.com
Here is one way to do type-safe reversible routing in pure Scala:

http://untyped.com/untyping/2011/10/10/reading-writing-and-the-rest/

The "trick" is to use the HList datastructure, a statically typed homogenous list.

HTH,
N.

Tymon Tobolski

unread,
Feb 20, 2012, 7:51:02 AM2/20/12
to play-framework
Reverse routing has one big drawback - can't create two routes to the
same action. And currently all HList(KList) scala implementations I
know are a bit painful to use.

Imho the problem is still open for discussion.

Anton Bessonov

unread,
Feb 20, 2012, 8:46:03 AM2/20/12
to play-fr...@googlegroups.com
Why do you need this?

Probably you need parameters, aliases or redirects, but not routing to
exact same action.

Tymon Tobolski

unread,
Feb 21, 2012, 2:29:32 AM2/21/12
to play-framework
As I look at my rails apps, there are some cases with few path routing
to the same action. However, in rails it is possible for action to
take e.g int or string as parameter and that wouldn't make any sense
in scala. But, in case like this:

GET /foo Application.foo
GET /bar /foo // definition of alias

it is still impossible to get "/bar" route (reverse routing for
Application.foo will return "/foo")


On Feb 20, 2:46 pm, Anton Bessonov <exe...@googlemail.com> wrote:
> Why do you need this?
>
> Probably you need parameters, aliases or redirects, but not routing to
> exact same action.
>

Tymon Tobolski

unread,
Feb 21, 2012, 4:50:39 AM2/21/12
to play-framework
Reverse routing - https://gist.github.com/1866044

Quite straight forward, especially with resources

few "but"'s
[1] I had weird issues with "object Routes not found", that's why
there is controller.Routing object
[2] Actions in application controller need explicit result type
because they are recursive while actions in object Todos does not
(??)

Julien Richard-Foy

unread,
Feb 21, 2012, 5:18:44 AM2/21/12
to play-fr...@googlegroups.com
You gist is definitely interesting, however I keep thinking that the
current Play 2 external DSL is more convenient than your embedded DSL
because the syntax of the Play 2 routes DSL is less cluttered.

Maybe the “resource” and namespacing concepts will be added after the
2.0 release.

Tymon Tobolski

unread,
Feb 21, 2012, 5:42:03 AM2/21/12
to play-framework
Imho few more keystrokes is low price for whole new range of
possibilities. Look how "resources" are implemented in few lines of
code, as well as reverse routing. Hacking 1200LOC compiler would be
much harder. Also, something like GET on "bar" to redirect("/foo")
requires only something similar to:

def redirect(path: String): () => Handler = Action { redirect(path) }

and that's it.

And do not forget about custom parameter types :)

Tymon Tobolski

unread,
Feb 27, 2012, 7:03:29 AM2/27/12
to play-framework
I just released sbt build - http://github.com/teamon/play-navigator

peter hausel

unread,
Feb 27, 2012, 11:04:01 AM2/27/12
to play-fr...@googlegroups.com
My personal take is that while play-navigator is cool it's just an alternative routing mechanism with its own tradeoffs, so if anything, this should be part of play2's (hopefully growing) ecosystem rather than the core framework.

If we view this project as an extension, then I think Global#onRouteRequest
https://github.com/playframework/Play20/blob/master/framework/src/play/src/main/scala/play/api/Global.scala#L54 should be preferred for wiring (even if it required some extra work over the current hijack-the-routes-namespace solution). Perhaps turning it into a plugin would be useful too.

Just my 2c.
Peter

Tymon Tobolski

unread,
Mar 3, 2012, 10:21:03 PM3/3/12
to play-framework
In case anyone is still interested I added support for multiple
routers: https://github.com/teamon/play-navigator

On Feb 27, 5:04 pm, peter hausel <peter.hau...@gmail.com> wrote:
> My personal take is that while play-navigator is cool it's just an
> alternative routing mechanism with its own tradeoffs, so if anything, this
> should be part of play2's (hopefully growing) ecosystem rather than the
> core framework.
>
> If we view this project as an extension, then I think Global#onRouteRequesthttps://github.com/playframework/Play20/blob/master/framework/src/pla...should
> be preferred for wiring (even if it required some extra work over the
> current hijack-the-routes-namespace solution). Perhaps turning it into a
> plugin would be useful too.
>
> Just my 2c.
> Peter
>
>
>
>
>
>
>
> On Monday, February 27, 2012 7:03:29 AM UTC-5, Tymon Tobolski wrote:
>
> > I just released sbt build -http://github.com/teamon/play-navigator
>
> > On Feb 21, 11:42 am, Tymon Tobolski <i...@teamon.eu> wrote:
> > > Imho few more keystrokes is low price for whole new range of
> > > possibilities. Look how "resources" are implemented in few lines of
> > > code, as well as reverse routing. Hacking 1200LOC compiler would be
> > > much harder. Also, something like GET on "bar" to redirect("/foo")
> > > requires only something similar to:
>
> > > def redirect(path: String): () => Handler = Action { redirect(path) }
>
> > > and that's it.
>
> > > And do not forget about custom parameter types :)
>
> > > On Feb 21, 11:18 am, Julien Richard-Foy <j...@zenexity.com> wrote:
>
> > > > You gist is definitely interesting, however I keep thinking that the
> > > > current Play 2 external DSL is more convenient than your embedded DSL
> > > > because the syntax of the Play 2 routes DSL is less cluttered.
>
> > > > Maybe the “resource” and namespacing concepts will be added after the
> > > > 2.0 release.
> On Monday, February 27, 2012 7:03:29 AM UTC-5, Tymon Tobolski wrote:
>
> > I just released sbt build -http://github.com/teamon/play-navigator
>
> > On Feb 21, 11:42 am, Tymon Tobolski <i...@teamon.eu> wrote:
> > > Imho few more keystrokes is low price for whole new range of
> > > possibilities. Look how "resources" are implemented in few lines of
> > > code, as well as reverse routing. Hacking 1200LOC compiler would be
> > > much harder. Also, something like GET on "bar" to redirect("/foo")
> > > requires only something similar to:
>
> > > def redirect(path: String): () => Handler = Action { redirect(path) }
>
> > > and that's it.
>
> > > And do not forget about custom parameter types :)
>
> > > On Feb 21, 11:18 am, Julien Richard-Foy <j...@zenexity.com> wrote:
>
> > > > You gist is definitely interesting, however I keep thinking that the
> > > > current Play 2 external DSL is more convenient than your embedded DSL
> > > > because the syntax of the Play 2 routes DSL is less cluttered.
>
> > > > Maybe the “resource” and namespacing concepts will be added after the
> > > > 2.0 release.
> On Monday, February 27, 2012 7:03:29 AM UTC-5, Tymon Tobolski wrote:
>
> > I just released sbt build -http://github.com/teamon/play-navigator
>
> > On Feb 21, 11:42 am, Tymon Tobolski <i...@teamon.eu> wrote:
> > > Imho few more keystrokes is low price for whole new range of
> > > possibilities. Look how "resources" are implemented in few lines of
> > > code, as well as reverse routing. Hacking 1200LOC compiler would be
> > > much harder. Also, something like GET on "bar" to redirect("/foo")
> > > requires only something similar to:
>
> > > def redirect(path: String): () => Handler = Action { redirect(path) }
>
> > > and that's it.
>
> > > And do not forget about custom parameter types :)
>
> > > On Feb 21, 11:18 am, Julien Richard-Foy <j...@zenexity.com> wrote:
>
> > > > You gist is definitely interesting, however I keep thinking that the
> > > > current Play 2 external DSL is more convenient than your embedded DSL
> > > > because the syntax of the Play 2 routes DSL is less cluttered.
>
> > > > Maybe the “resource” and namespacing concepts will be added after the
> > > > 2.0 release.
> On Monday, February 27, 2012 7:03:29 AM UTC-5, Tymon Tobolski wrote:
>
> > I just released sbt build -http://github.com/teamon/play-navigator
>
> > On Feb 21, 11:42 am, Tymon Tobolski <i...@teamon.eu> wrote:
> > > Imho few more keystrokes is low price for whole new range of
> > > possibilities. Look how "resources" are implemented in few lines of
> > > code, as well as reverse routing. Hacking 1200LOC compiler would be
> > > much harder. Also, something like GET on "bar" to redirect("/foo")
> > > requires only something similar to:
>
> > > def redirect(path: String): () => Handler = Action { redirect(path) }
>
> > > and that's it.
>
> > > And do not forget about custom parameter types :)
>
> > > On Feb 21, 11:18 am, Julien Richard-Foy <j...@zenexity.com> wrote:
>
> > > > You gist is definitely interesting, however I keep thinking that the
> > > > current Play 2 external DSL is more convenient than your embedded DSL
> > > > because the syntax of the Play 2 routes DSL is less cluttered.
>
> > > > Maybe the “resource” and namespacing concepts will be added after the
> > > > 2.0 release.
> On Monday, February 27, 2012 7:03:29 AM UTC-5, Tymon Tobolski wrote:
>
> > I just released sbt build -http://github.com/teamon/play-navigator
>
> > On Feb 21, 11:42 am, Tymon Tobolski <i...@teamon.eu> wrote:
> > > Imho few more keystrokes is low price for whole new range of
> > > possibilities. Look how "resources" are implemented in few lines of
> > > code, as well as reverse routing. Hacking 1200LOC compiler would be
> > > much harder. Also, something like GET on "bar" to redirect("/foo")
> > > requires only something similar to:
>
> > > def redirect(path: String): () => Handler = Action { redirect(path) }
>
> > > and that's it.
>
> > > And do not forget about custom parameter types :)
>
> > > On Feb 21, 11:18 am, Julien Richard-Foy <j...@zenexity.com> wrote:
>
> > > > You gist is definitely interesting, however I keep thinking that the
> > > > current Play 2 external DSL is more convenient than your embedded DSL
> > > > because the syntax of the Play 2 routes DSL is less cluttered.
>
> > > > Maybe the “resource” and namespacing concepts will be added after the
> > > > 2.0 release.
> On Monday, February 27, 2012 7:03:29 AM UTC-5, Tymon Tobolski wrote:
>
> > I just released sbt build -http://github.com/teamon/play-navigator
>
> > On Feb 21, 11:42 am, Tymon Tobolski <i...@teamon.eu> wrote:
> > > Imho few more keystrokes is low price for whole new range of
> > > possibilities. Look how "resources" are implemented in few lines of
> > > code, as well as reverse routing. Hacking 1200LOC compiler would be
> > > much harder. Also, something like GET on "bar" to redirect("/foo")
> > > requires only something similar to:
>
> > > def redirect(path: String): () => Handler = Action { redirect(path) }
>
> > > and that's it.
>
> > > And do not forget about custom parameter types :)
>
> > > On Feb 21, 11:18 am, Julien Richard-Foy <j...@zenexity.com> wrote:
>
> > > > You gist is definitely interesting, however I keep thinking that the
> > > > current Play 2 external DSL is more convenient than your embedded DSL
> > > > because the syntax of the Play 2 routes DSL is less cluttered.
>
> > > > Maybe the “resource” and namespacing concepts will be added after the
> > > > 2.0 release.
> On Monday, February 27, 2012 7:03:29 AM UTC-5, Tymon Tobolski wrote:
>
> > I just released sbt build -http://github.com/teamon/play-navigator
>
> > On Feb 21, 11:42 am, Tymon Tobolski <i...@teamon.eu> wrote:
> > > Imho few more keystrokes is low price for whole new range of
> > > possibilities. Look how "resources" are implemented in few lines of
> > > code, as well as reverse routing. Hacking 1200LOC compiler would be
> > > much harder. Also, something like GET on "bar" to redirect("/foo")
> > > requires only something similar to:
>
> > > def redirect(path: String): () => Handler = Action { redirect(path) }
>
> > > and that's it.
>
> > > And do not forget about custom parameter types :)
>
> > > On Feb 21, 11:18 am, Julien Richard-Foy <j...@zenexity.com> wrote:
>
> > > > You gist is definitely interesting, however I keep thinking that the
> > > > current Play 2 external DSL is more convenient than your embedded DSL
> > > > because the syntax of the Play 2 routes DSL is less cluttered.
>
> > > > Maybe the “resource” and namespacing concepts will be added after the
> > > > 2.0 release.
> On Monday, February 27, 2012 7:03:29 AM UTC-5, Tymon Tobolski wrote:
>
> > I just released sbt build -http://github.com/teamon/play-navigator
>
> > On Feb 21, 11:42 am, Tymon Tobolski <i...@teamon.eu> wrote:
> > > Imho few more keystrokes is low price for whole new range of
> > > possibilities. Look how "resources" are implemented in few lines of
> > > code, as well as reverse routing. Hacking 1200LOC compiler would be
> > > much harder. Also, something like GET on "bar" to redirect("/foo")
> > > requires only something similar to:
>
> > > def redirect(path: String): () => Handler = Action { redirect(path) }
>
> > > and that's it.
>
> > > And do not forget about custom parameter types :)
>
> > > On Feb 21, 11:18 am, Julien Richard-Foy <j...@zenexity.com> wrote:
>
> > > > You gist is definitely interesting, however I keep thinking that the
> > > > current Play 2 external DSL is more convenient than your embedded DSL
> > > > because the syntax of the Play 2 routes DSL is less cluttered.
>
> > > > Maybe the “resource” and namespacing concepts will be added after the
>
> ...
>
> read more »
Reply all
Reply to author
Forward
0 new messages