Play vs Rails: a developer's notes

4,691 views
Skip to first unread message

Adam Hooper

unread,
Oct 15, 2013, 3:20:47 PM10/15/13
to play-fr...@googlegroups.com
I'm a full-stack developer who ends up doing more front-end work than most. I've used Rails for several years and Play for 16 months. Here's a comparison of Rails 3.1 with Play 2.1: hopefully the feedback can help improve Play.

TL;DR:

Play is less mature than Rails. Compared to Rails, it has strengths and weaknesses. For most future projects, I will be using Rails, because today Rails lets me build more features with less hassle.

A feature-by-feature comparison:

* Deployment

Play _should_ win here: jars are great, and the docs give lots of deployment options.

But Play uses Rhino to compile RequireJS files, so the compilation step is mercilessly slow. (This would be different on a website that doesn't use much client-side code.)

In my experience, Rails is a huge hassle when setting up a server environment; Play is more hassle during each deploy (only because deployment is so slow). As a result, Play deployment turns into an actual _thing_. With Play, I'll ask myself, "do I really need to deploy this important bugfix right now, or can it wait a week?" while on Rails, I see "git push && cap staging deploy" as a sensible shell alias.

Play could improve by making RequireJS compilation fast (probably by ditching Rhino).

Winner: Rails

* SBT

This deserves its own category. There's a whole level of hurt in Play that simply doesn't come about in Rails.

Both Rails and Play have satisfactory workflows for adding dependencies. After dependencies, though....

In theory Rails uses rake, but one rarely edits Rakefiles or even _runs_ rake in practice.

In Rails, tweaking the build involves editing a gem's configuration (in a separate file) or, with crappy add-ons, monkey-patching a gem (in a separate file). Key gems' configuration DSLs are clearly documented and _don't_ make me memorize quasi-random symbols like ":=", "++=", "<++=", .... And my Rails test suite doesn't crash with PermGen errors.

SBT is far too complicated and it crashes with PermGen errors when I keep "play ~test" open all day. That's more a critique of the Scala ecosystem than of Play in particular, but it hurts my efficiency when I develop with Play.

Winner: Rails

* Compiled vs Interpreted code (alternatively: "Scala vs Ruby")

I prefer compiled code in _theory_, but in _practice_ interpreted code lets me work faster. In Play it takes several seconds to run unit tests after any code changes, and it takes several seconds to refresh a page after I tweak a view.

A typical day of TDD might involve a bunch of cycles of this (using "play ~test-only [Spec]"):

1. [1 minute average] write or alter some test code (often five minutes, but more often five seconds, as I fix typos)
2. [15 seconds average] wait to see test result
3. [1 minute average] write or alter the code under test (often five minutes, but more often five seconds, as I fix typos)
4. [15 seconds average] wait to see test result

Result: when I'm coding at full efficiency with Play, I'm waiting 20 per cent of the time. On my Rails (or NodeJS) projects, I'm only waiting one or two per cent of the time.

This is frustrating. It makes me less inclined to write tests, which in turn makes my code less maintainable. Is this for a few milliseconds' speedup in production? That's premature optimization.

With that in mind, which programming language should one choose for a web framework? The main distinctions:

1. Scala+Play and Ruby+Rails take similar quantities of similarly-elegant code.
2. Rails is faster to test (and therefore develop)
3. Play is faster to run in production

... 2 matters every day; 3 only matters during optimization, at which point Rails has solutions.

Winner: Rails

* Asset management (JavaScript/CSS/Images/etc)

I'm a front-end developer. My day-to-day interactions with Play are fraught with frustration:

- It takes two minutes to tweak _one byte_ on a decent-sized, Bootstrap-based stylesheet suite on my netbook. _Two minutes_. On Rails, the same tweak would take _one second_.

- On Play, I can't avoid Google's Closure Compiler. I use CoffeeScript, so I don't need lint checking, yet it wastes a second or two every page refresh.

- Rails uses asset fingerprinting (e.g., "styles-908e25f4bf641868d8683022a5b62f54.css") and Play doesn't. To my knowledge, fingerprinting is the industry standard: it makes caching and cache invalidation seamless. Web pages load faster, CDNs are easy to update, and server load is reduced. http://guides.rubyonrails.org/asset_pipeline.html

- Rails fingerprints images, PDFs and other assets, too -- not just CSS/JS.

- Rails lets you reference image routes from stylesheets and JavaScript; it lets you reference stylesheet routes from JavaScript, etc.

- Rails comes with Sass by default instead of Less. That's a marginal win for Rails, since Sass has some nifty features the Less developers deemed undesirable. (Loops, for instance, are a pain in the neck with Less.)

My top suggestion: Play should ditch Rhino for something faster. Rhino is way, way, way too slow. Rhino is unusable in development. Rhino is the biggest reason I fear Play.

After that, Play still has a ways to go to catch up with Rails. The docs at http://guides.rubyonrails.org/asset_pipeline.html describe exactly what I'd expect a mature framework to support. (Lest you think Rails has tainted my judgement, consider that other asset managers have the same features: Yeoman and Jammit spring to mind, and there are mature, documented Grunt plugins for each of the features I've listed.)

I have implemented hacks:

- to quickly run CoffeeScript unit tests (using NodeJS): <https://github.com/overview/overview-server/tree/master/test/assets/javascripts/autotest>

- to circumvent Play when compiling Less files in development (using lessc): <https://github.com/overview/overview-server/blob/master/auto/compile-less-on-linux.sh> and <https://github.com/overview/overview-server/blob/51b08add7811a9c058ca355dbd4e61060c5468e4/project/Build.scala#L149-L153>. (After editing Less files, wait one or two seconds before refreshing the page or the new styles won't appear. This is error-prone, but it's better than waiting two minutes.)

Winner: Rails

* Asynchronicity

Play makes asynchronous code understandable. Rails either tries to hide it and sometimes doesn't support it.

I really appreciate Play's clarity and simplicity, as asynchronous code is important at a certain stage of development. It's simpler and more efficient than threading or forking.

Play's Iteratees are powerful and confusing; I admire Play for keeping them at the perfect level of abstraction so I don't need to know anything about them when I'm starting out.

Winner: Play

* Models

Here, Play has a huge opportunity.

Rails is opinionated: ActiveRecord or bust.

Play is un-opinionated. Unfortunately, Java's and Scala's database-y libraries are. We tried Ebean and quickly outgrew its feature set. We tried anorm and needed to use string concatenation to construct dynamic SQL strings. We switched to Squeryl and spent ages wrestling with its type-safe DSL.

Most Scala libraries we've used separate data objects from storage/retrieval methods. That's great ... for people who want to switch to another storage/retrieval library. See the irony? ;)

I haven't tried Slick; while it might be the great soon, I don't think it's ready for us today. It looks like there's some boiler-plate code involved, which means we'd need boiler-plate tests. Ick.

While ActiveRecord is a pain, its query interface -- arel <https://github.com/rails/arel> -- is a dream. And its _default_ models are very simple, too: they require practically zero code. It's acceptable in practice.

I think Play should push ahead with Slick, in an opinionated manner: make it the default, iron out the wrinkly bits, and make the default use case as near to a one-liner as possible.

Winner: Rails (for today ... but not for long?)

* SQL Migrations/Evolutions

Rails migrations seem sensible: mostly code, plus SQL when you need it. You decide when to apply migrations: you can run them from the command-line with Rake.

Play evolutions are straight SQL, which makes for lots of boilerplate code.

Worse: there seems to be no way to test !downs.

Even worse: with Play, you can't deploy your evolutions with a single command. This leads to contortions on production servers during deployment.

And worse still: with Play, you can't *edit* evolutions without causing devastation on production. (1. Add a new feature with an accompanying evolution that adds a new table; 2. Deploy the feature; 3. Fix a typo in a comment in the evolution. 4. Deploy at a later date. You'll lose all data from the table on production.)

Play should add helpers akin to Rails', determine how to test evolutions, and provide a way to apply evolutions separately from server code.

Winner: Rails

* Forms

Rails has only one thing going for it when it comes to forms: it's CSRF-secure by default, because encourages and documents form helpers exclusively (as opposed to form HTML). This also applies to its AJAX helpers. (Rails comes with some JavaScript code to send CSRF-secure AJAX requests.)

But Rails' zest for permissively parsing form data has led to numerous security vulnerabilities, and in practice even the non-vulnerabilities can cause server errors. Play, on the other hand, has wonderful Form objects that convert from POST/GET variables to models and back. These objects are easy to test, and they completely avoid the problems Rails has with invalid input. All this greatness for a few lines of code -- it's totally worth it.

Play's only downside is that CSRF protection isn't the default. The common case is documented, at least.

Play could improve by documenting how to send CSRF-protected AJAX requests, and maybe adding a some HTML/JS helpers. (My solution is similar to Rails': a global JavaScript variable.)

Winner: Play

* Sessions (cookies, flash, etc.)

Play and Rails behave near-identically, so there's no winner here.

* Testing

Clear winner: Rails+RSpec. For every single line of test code.

First, dissecting <http://www.playframework.com/documentation/2.2.x/ScalaTest>, compared to Rails, there are subtle problems both in documentation and implementation:

- specs2 has nice matchers, but its DSL causes problems. For instance, if I import Squeryl, then I get two "in" methods; then, when there's a compilation error, Scala complains about "in" having two meanings, rather than complaining about the test in question.

- Scala tests take a while to compile. (It seems ironic to me that specs2 is optimized around concurrency. It _should_ be optimized around the real time-waster: compilation speed.)

- Play's documentation suggests dependency injection (without calling it so, nit), but the documentation is incomplete: it doesn't offer any suggestions for how to test AnormUserRepository.

- The suggestion for unit testing controllers is a good one, and it should be fleshed out. Tests like the one listed are crucial in practice, but the documentation doesn't explain _why_ this is so: why does the "should be valid" test use TestController instead of ExampleController? (I know the reason, but I can't see how a newcomer would learn that reason by reading that page.)


- FluentLenium is frustrating, because its return values are frequently null. If the tests pass, great! If the tests fail, they're hard to debug because one doesn't expect nulls in Scala. I need to remember to type Option() everywhere I call FluentLenium code ... which gets old.

- It would be handy to include FluentLenium (or a more Scala-ish library that has yet to be created) by default when testing templates.

- The documentation suggests using an H2 database. That is _usually_ a terrible idea, because H2 is so different from [database X] and those differences _usually_ matter. Play should nix this H2 suggestion entirely (it will only work for a minority of users, none of them novices), and instead document how to configure Play to use a test database for tests.

- Play has no documentation about setting up a database for tests. This is a Hard Problem that Rails has solved. In unit tests, Rails runs BEGIN TRANSACTION+ROLLBACK for all tests. In integration tests, things are more complicated.

- Play's WithApplication calls are frustrating. In RSpec, the "describe" blocks (equivalent to Play's "should" blocks) hold setup/teardown/around blocks, _not_ the individual tests. In my experience, that makes it easier to write tests.

- RSpec allows nesting "describe" blocks. That, plus per-block setup/teardown/around, means it doesn't need Specs2's icky "in new WithApplication" clauses. In practice, I find those clauses to be a subtle source of error: they're not where I _expect_ erroneous code to appear, because in Scala the key logic usually goes on the _left_ side of a line of code. I tend to try lots of other things before noticing I've made an error over there.

- RSpec makes mocking and dependency injection a bit easier, because Ruby objects are so malleable. Specs2 has Mockito, which is a start... but Mockito defaults to null return values, which are confusing in Scala.

- Play is missing documentation: How do I test Forms? BodyParsers? Filters? Etc.

- Why is testing on its own page in Play's documentation? I write my tests before I write my code, so the "how to test templates" code should come before the "how to write templates" code, and the "how to test controllers" code should come before the "how to write controllers" code.... (In other words: even though not everybody uses TDD or BDD, the Play framework should support and encourage it.)

My opinion: Specs2 is more complicated than RSpec, and that makes it more difficult (and hence more time-consuming) to write tests. There are things Specs2 does that RSpec doesn't, and there are things Specs2 does better than RSpec ... but those aren't as important as the things RSpec does that Specs2 doesn't, or the things RSpec does better than Specs2. Play would be easier (and faster) to use if it had a super-simple test framework with Specs2's matchers, RSpec's "describe" blocks, Mockito, and lots of opinionated helpers for testing various kinds of classes.

As for integration testing: were I coding my project from scratch, I'd write my integration tests in RSpec+Capybara (or RSpec+Cucumber+Capybara). I'm still tempted to do so, even for Play projects. The test suite is better. (I've often pondered whether it's possible, with JRuby, to port _all_ unit tests over to RSpec.)

Winner: Rails+RSpec

* Internationalization

The feature sets are identical and both implementations are usable and documented. Rails has a couple of pros:

- Rails has more documentation

- Rails uses a Yaml message file, which enforces namespacing

On the other hand, I can see how others would prefer Play because it uses Java MessageFormat strings in the standard MessageFormat files. Those strings are cryptic and the format is badly documented, but they _are_ standardized: maybe there are good tools for editing them?

In my particular project, I took advantage of the Messages format to translate some messages in JavaScript. See my JavaScript i18n function:

- It only supports a few features, and those are all unit-tested: <https://github.com/overview/overview-server/blob/master/test/assets/javascripts/specs/i18n-spec.coffee>

That was more useful for our purposes than <https://github.com/julienrf/play-jsmessages>.

Winner: Both

* Routing

A typical Play project's routes file is more verbose than a typical Rails project's, because Rails has a standardized notion of a "resource". Rails also provides a convention for action names.

On the other hand, I like the way Play extracts paramaters. In Play, required query parameters are handled perfectly; in Rails, controller code needs to check their types (and if it doesn't, it can lead to security vulnerabilities).

Winner: Both

* Authorization/Authentication

On Rails I use Devise, a template for an industry-standard login setup. Devise includes HTML templates, database models and so forth.

With Play, nothing compares.


It took weeks, as I had to research best practices for password-reset protocols, registration email phrasing, and so forth. And my implementation doesn't use tokens, so it's not as secure as what I would have had on day one with Rails.

I'm aware of <https://github.com/t2v/play2-auth>, and we started off with it before writing our own. It doesn't do all the things Devise does: for instance, sending registration emails. Those things are key, and it takes a lot of research to figure them all out.

Winner: Rails

* JSON

Play needs more code than Rails to accomplish the same thing. For instance, in Rails you can just call ".to_json" on any object and it'll do exactly what you'd expect.

As for parsing: I think Play does the best it can, considering it's type-safe. But Rails _isn't_ type-safe, so it takes less code to express the same thing.

Winner: Rails

MY TOP FIVE RECOMMENDATIONS FOR PLAY

I'm often a front-end developer, so my views are skewed....

If I were king of the world, I'd have Play:

1. Ditch Rhino and get a fast JavaScript engine for asset compilation. I can't see myself using Play for any new project until this happens.

2. Encourage developers to write tests first and code second. That means changes to documentation, helper classes, database integration, and perhaps a different DSL than specs2's.

3. Make Slick great and make Play opinionated about using Slick. Do what ActiveRecord does right: it makes the easy stuff easy and the hard stuff manageable. (To me, Play has no risk of doing what ActiveRecord does _wrong_, so Play can really shine here.)

4. Refocus asset management: there are industry standards for developing, bundling and serving JavaScript, CSS and image assets, and Play should follow them. Rails (Sprockets), Jammit and Yeoman showcase the standard features.

5. Work really hard making tests and code compile more quickly. At the very least, write documentation that helps developers write faster-to-compile code. For instance, can Scala code compile appreciably faster if I declare the types of my variables or if I (somehow) disable implicit variables? Or better yet, is there a way to run the tests with an interpreter rather than a compiler? Or to only recompile a single test at a time? Would it be productive to write core code in (gasp) Java for a compile-time win?

TAKE-AWAY THOUGHTS

I realize, after writing this, that Rails wins out an awful lot. I think that's mostly because it's a more mature framework.

In my 16 months with Play, I probably spent more than 40 per cent of my time doing things that I would not have needed to do with Rails. Put another way, I feel I was 40 per cent less productive than I should have been.

Many Rails wins are simply because Rails has better plugins. Rails doesn't ship with RSpec or Devise, for instance, but those plugins are part of the Rails universe. With Play I needed to code re-implementations or workarounds. I hope the necessary libraries come to Play soon.

Rails has a far, far faster development cycle. A faster "A. write tests; B. write code" cycle. Faster bundling. Templates and gems for testing all sorts of typical classes. Faster mocking (because classes are malleable). Less code (more conventions). Sure, a Play project responds to requests more quickly in production ... but that's rarely more important than development speed. (Indeed, with a faster development cycle it's faster to optimize the requests that matter.)

Play is a great tool for a wide range of tasks (streaming-heavy websites, for instance). In particular, its asynchronous nature is more intuitive than NodeJS's and more powerful than Rails'. I'm convinced Play will continue to improve at the speedy pace it has shown up to now.

Through Play (and Scala) I've gained an appreciation of static typing and immutable objects that will improve my future code.

So long, Play. It's been interesting.

Enjoy life,
Adam

Philip Johnson

unread,
Oct 15, 2013, 3:56:37 PM10/15/13
to play-fr...@googlegroups.com
Fantastic, articulate, informative analysis.   If your recommendations are not acted upon, I believe Play will be doomed to life as a "special case" web framework. 

Philip

Adam Hooper

unread,
Oct 15, 2013, 6:47:48 PM10/15/13
to play-fr...@googlegroups.com
Whoops, I forgot an obvious one:

* Templates

- anti-Play: you need to write imports. It's also annoying that imports come below arguments: argument types need to be fully-qualified. This makes the whole template system feel like a bit of a hack.

- pro-Rails (ERB): there are useful "helper" variables. For instance, "request" is available to all views, _without_ requiring a parameter (an implicit parameter, no less). And view-specific functions can be defined in a separate file, where they belong. It _feels_ like there are exactly the right global variables when you're writing a view.

- pro-Rails: there are nifty helper functions like link_to(), and it's obvious where more can be inserted. (Play provides a few, such as form helpers; but it's notably lacking link_to() and url_for(). Play also has a "templatesImport" sbt key, but that's far less developer-friendly: first, the docs end up in the wrong place; second, the developer needs to learn how to edit Build.scala. Why not create a helpful default, similar to app/Global.scala?)

- pro-Play: static compilation. This is a big win. Many Rails shops don't test their views, because view tests seem to have high cost (frequent edits) and low rewards (many tests just mirror the code they're testing). So Rails views can sometimes end up with bugs a compiler would catch, such as undeclared variables, unclosed tags, etc. Play avoids this problem.

Play can improve by tastefully adding useful helpers (such as url_for()) and letting developers create global helpers without altering Build.scala. (Bonus points: remove the ability to define a function in a template, and instead automatically import an appropriately-named non-template helper, as Rails does.)

I _would_ declare a tie, except ... Rails has Haml (http://haml.info/), which replaces ERB. Haml is far more concise and far less error-prone. (Unit tests usually fail to test the entire structure; with Haml, the structure is plain to see.)

Winner: Rails

Enjoy life,
Adam

Mike Bryant

unread,
Oct 15, 2013, 6:57:35 PM10/15/13
to play-fr...@googlegroups.com
Excellent analysis. I did one major project in Rails circa RoR 2.0 (and not since for many years) but I thoroughly agree with many of your points. However, with many years of Django in between to flesh out my dynamic-framework perspective, I think you're understating some of Play's best points:
  • The benefits of type-safe, compiled templates and routes. This might be a little bit contentious - since a lot of people (including myself, before I "sub-projected" my project) have a problem with the compile times it involves - but I've found it just a massive time-saver overall, compared to writing tests and manual testing. Compiling templates and routes just makes lots of testing you'd otherwise have to do redundant, which means less code to maintain. I've found it's a safely net which is hard to do without now I've become accustomed to it. Variants of null pointer errors - "Nil/None has no such method" etc - in templates simply don't exist for me now. This is my NUMBER ONE Play bonus.
  • Async support (and support for long running async operations) is a constant pain-point in so many frameworks. Play *really* shines here, and its strengths will only become more relevant over time. I say this with much regard for things like Celery, which is great, but the common-case for async is much better handled with Play's Akka integration.
  • Deployment: personally I use a quicky fabric script to deploy my app after "play clean stage" and I'm not sure how it could possibly be much easier (though Capistrano is cool). I don't use much frontend-related tooling though so I'm probably missing your Rhino pain.
That said, I'm totally in agreement with you on many points. Specs2 can be confusing and obtuse. SBT can be maddening and slow, and give completely incomprehensible errors. Turnaround time for changes can be horribly slow if you don't have incremental compilation working properly. There's nothing comparable on the Java side to the sheer ease of ActiveRecord for common DB cases. Play is clearly less mature than Rails, and, as a consequence, making breaking changes more often, which can be frustrating if you have lots of code already.

Eric Torreborre

unread,
Oct 15, 2013, 7:16:31 PM10/15/13
to play-fr...@googlegroups.com
Hi Adam,

Thanks for such a detailed feedback.

Being the specs2 author I'll try to answer the specs2 points:

>  specs2 has nice matchers, but its DSL causes problems. For instance, if I import Squeryl, then I get two "in" methods; then, when there's a compilation error, Scala complains about "in" having two meanings, rather than complaining about the test in question.

You can deactivate this by mixing-in a trait:

trait NoInExample extends org.specs2.mutable.FragmentsBuilder {
  override def inExample(s: String): InExample = super.inExample(s)
}

or you can use `>>` as an alias for `in`.

> Scala tests take a while to compile. (It seems ironic to me that specs2 is optimized around concurrency. It _should_ be optimized around the real time-waster: compilation speed.)

True. This has been slightly improved with specs2 2.0 (0.5s per spec file) and this eventually just might be the trade-off to accept for having a compiler like scalac. Also I think that what you wish to optimize varies. Sometimes you wish your code to compile faster, sometimes you wish the whole code-build-test cycle to be fast.

Also there is a "batteries included" approach to specs2. When you extend `Specification` you get most of the matchers and implicit conversions which may slow down compilation times. It is however possible to build your own `Spec` trait with only the things you generally use:

abstract class MySpec extends 
  BaseSpecification 
    with mutable.FragmentsBuilder
    with mutable.ArgumentsArgs with mutable.ArgumentsShortcuts // for sequential
    with AnyMatchers with MustExpectations with ThrownExpectations // for 1 must_== 1

I haven't done any benchmarking yet showing huge improvements but you can read Bill Venner's informative post on the subject.

Note also that ScalaTest takes the opposite approach than specs2 w.r.t. implicits. The base trait has no conversions and you need to stack every functionality you wish to use.

> Play's WithApplication calls are frustrating. In RSpec, the "describe" blocks (equivalent to Play's "should" blocks) hold setup/teardown/around blocks, _not_ the individual tests. In my experience, that makes it easier to write tests. 

You can this in specs2 by using implicit values:

import org.specs2._
import execute._
import org.specs2.specification.Context

class TestSpec extends mutable.Specification { sequential
  "block 1 should have its own setup" >> {
    implicit val around1 = around("around 1")
    example(1)
    example(2)
  }
  "block 2 should have its own setup" >> {
    implicit val around2 = around("around 2")
    example(3)
    example(4)
  }

  def around(name: String) = MyAround(name)
  case class MyAround(name: String) extends mutable.Around {
    def around[R : AsResult](r: =>R) = {
      println(name+" setup")
      AsResult(r)
    }
  }

  def example(i: Int)(implicit c: Context) = "example "+i >> { i.pp; ok }
}


> RSpec allows nesting "describe" blocks. That, plus per-block setup/teardown/around, means it doesn't need Specs2's icky "in new WithApplication" clauses. 

In specs2 you can nest blocks as well and use traits inheritance to stack the contexts:

import org.specs2._
import execute._
import org.specs2.specification.Context

class TestSpec extends mutable.Specification { sequential
  "nested blocks should have their own setup" >> {
    "nested 1" >> {
      implicit val around1 = nestedAround("around nested 1")
      example(1)
      example(2)
    }
    "nested 2" >> {
      implicit val around2 = nestedAround("around nested 2")
      example(3)
      example(4)
    }
  }

  def nestedAround(name: String) = SpecialisedAround(name)

  trait BaseAround extends mutable.Around {
    def around[R : AsResult](r: =>R) = {
      println(name+" base setup")
      AsResult(r)
    }
    def name: String
  }

  case class SpecialisedAround(name: String) extends BaseAround {
    override def around[R : AsResult](r: =>R) = {
      println(name+" specialised setup")
      super.around(r)
    }
  }

  def example(i: Int)(implicit c: Context) = "example "+i >> { i.pp; ok }

}

In practice, I find those clauses to be a subtle source of error: they're not where I _expect_ erroneous code to appear, because in Scala the key logic usually goes on the _left_ side of a line of code. I tend to try lots of other things before noticing I've made an error over there.

I don't understand this. Can you give a concrete example please?

> My opinion: Specs2 is more complicated than RSpec, and that makes it more difficult (and hence more time-consuming) to write tests. There are things Specs2 does that RSpec doesn't, and there are things Specs2 does better than RSpec ... but those aren't as important as the things RSpec does that Specs2 doesn't, or the things RSpec does better than Specs2.

For my information, what do you like in specs2 that RSpec doesn't have? And besides the describe/block setup of RSpec (+ compilation times), what are the other things you miss in specs2?

> Play would be easier (and faster) to use if it had a super-simple test framework with Specs2's matchers, RSpec's "describe" blocks, Mockito, and lots of opinionated helpers for testing various kinds of classes.

Maybe you can try using JUnit with specs2 JUnitMatchers (but that doesn't give you blocks)? Or maybe specsy (looks like what you're after)?

Thanks,

Eric.

Adam Hooper

unread,
Oct 15, 2013, 7:29:21 PM10/15/13
to play-fr...@googlegroups.com
On Tue, Oct 15, 2013 at 6:57 PM, Mike Bryant <mike...@gmail.com> wrote:
> I think you're understating some of Play's
> best points:
>
> The benefits of type-safe, compiled templates and routes.

I did say I like Play's routes for that reason (and Rails' for others).

You're absolutely right that I forgot about type-safe templates. I
just recently wrote an email to correct that.

> Async support (and support for long running async operations) is a constant
> pain-point in so many frameworks. Play *really* shines here, and its
> strengths will only become more relevant over time.

Absolutely ... but don't assume other frameworks are waiting around.
NodeJS, for instance, isn't even a framework and it's async from the
ground up. Great solutions _will_ arrive for Rails (and a great
framework will arrive for NodeJS).

Play does shine, but don't overstate its advantage. Even if
unoptimized Play can handle five times as much traffic as unoptimized
Rails with the same task, is that a reason to use Play? From a
gain/loss perspective: if each Play developer loses 15% of his/her
time waiting for the framework during development, then from a certain
point of view, those two months a year gain the project some code
optimization. For a $100k/yr developer, that's $15k/yr optimizing. On
Rails, you could hire the same developer for only ten months out of
the year and spend the other $15k/yr buying three m1.xlarge EC2
instances. (Or you could get the developer to optimize the _slow_
parts of the application instead of implicitly optimizing _all_ parts;
shouldn't it take fewer than two months per year to reach target
performance levels?)

Put another way: instead of hiring six Play developers, consider
hiring five Rails developers and spending $70k/yr on hardware. You'll
get the same features and save $10k/yr.

Sure, this logic fails at the "that doesn't scale" threshold; but why
not wait until you hit the problem, then solve it? Once you hit
scaling problems, you can probably afford more developers. Twitter is
a perfect example of this: it started with Rails and then switched
piecemeal to Scala.

Yes, Play's async stuff is spectacular. Is it "I choose Play"
spectacular? That depends on the project.

If I choose Play over Rails, I choose a certain gain (a speed boost in
production) at a certain cost (a drop in development efficiency). Is
that trade-off worth it? For all the projects I've participated in,
the answer in retrospect has been no. The math would change if the
gains were higher (that's hard to achieve, because hardware is cheap
and Play's async stuff is already spectacular) or the costs were lower
(faster testing, faster compilation, fewer lines of code).

Enjoy life,
Adam

--
My Phone (mobile): +1 613 986 3339
My Website: http://adamhooper.com
My Twitter: http://twitter.com/adamhooper

Adam Hooper

unread,
Oct 15, 2013, 8:02:46 PM10/15/13
to play-fr...@googlegroups.com
On Tue, Oct 15, 2013 at 7:16 PM, Eric Torreborre <etorr...@gmail.com> wrote:
> Hi Adam,
>
> Thanks for such a detailed feedback.

Hi Eric,

Thank you for addressing my concerns! If nothing else, you've provided
some great example code I wish I'd seen online earlier :).

My main beef with specs2, of course, is that it's slower because it's
compiled. That said, I'll answer your questions inline :).

> You can deactivate this by mixing-in a trait:
>
> trait NoInExample extends org.specs2.mutable.FragmentsBuilder {
> override def inExample(s: String): InExample = super.inExample(s)
> }
>
> or you can use `>>` as an alias for `in`.

Thank you for your suggestions. In practice, we've resorted to writing
inExample() in all specs that import Squeryl. (I'm really not a fan of
[what C++ folks would call] operator overloading.)

>> Scala tests take a while to compile. (It seems ironic to me that specs2 is
>> optimized around concurrency. It _should_ be optimized around the real
>> time-waster: compilation speed.)
>
> True. This has been slightly improved with specs2 2.0 (0.5s per spec file)
> and this eventually just might be the trade-off to accept for having a
> compiler like scalac.

...

> I haven't done any benchmarking yet showing huge improvements but you can
> read Bill Venner's informative post on the subject.

That is certainly an informative post. Specs2 seems to have a lot of
catching-up to do.

>> Play's WithApplication calls are frustrating. In RSpec, the "describe"
>> blocks (equivalent to Play's "should" blocks) hold setup/teardown/around
>> blocks, _not_ the individual tests. In my experience, that makes it easier
>> to write tests.
>
> You can this in specs2 by using implicit values:
>
> import org.specs2._
> import execute._
> import org.specs2.specification.Context
>
> class TestSpec extends mutable.Specification { sequential
> "block 1 should have its own setup" >> {
> implicit val around1 = around("around 1")
> example(1)
> example(2)
> }
> "block 2 should have its own setup" >> {
> implicit val around2 = around("around 2")
> example(3)
> example(4)
> }
>
> def around(name: String) = MyAround(name)
> case class MyAround(name: String) extends mutable.Around {
> def around[R : AsResult](r: =>R) = {
> println(name+" setup")
> AsResult(r)
> }
> }
>
> def example(i: Int)(implicit c: Context) = "example "+i >> { i.pp; ok }
> }

That's cool (but syntactically awkward).
Nifty. (More thoughts later)

>> In practice, I find those clauses to be a subtle source of error: they're
>> not where I _expect_ erroneous code to appear, because in Scala the key
>> logic usually goes on the _left_ side of a line of code. I tend to try lots
>> of other things before noticing I've made an error over there.
>
> I don't understand this. Can you give a concrete example please?

Sure. Consider:

"Example Page#index" should {
"should be valid" in {
val controller = new TestController()
val result = controller.index()
result must not beNull
}
}
}

... if Page#index depends on some part of my framework, I'll get a
confusing error mesasge until I change "in {" to "in WithApplication
{".

Compare that to RSpec:

describe 'Page#index' do
it 'should be valid' do
controller = TestController.new
result = controller.index()
result.should eq(nil)
end
end

... if _that_ Page#index depends on some part of my framework, I'll
get a confusing error message until I add a "before" or "around"
clause before the test in question.

I did say the difference is subtle. With most code, I expect things to
read left-to-right; I don't expect a crucial part of the code to be to
the _right_ of the word "in".

>> My opinion: Specs2 is more complicated than RSpec, and that makes it more
>> difficult (and hence more time-consuming) to write tests. There are things
>> Specs2 does that RSpec doesn't, and there are things Specs2 does better than
>> RSpec ... but those aren't as important as the things RSpec does that Specs2
>> doesn't, or the things RSpec does better than Specs2.
>
> For my information, what do you like in specs2 that RSpec doesn't have? And
> besides the describe/block setup of RSpec (+ compilation times), what are
> the other things you miss in specs2?

Nice in specs2: traits can be extended. For instance:

trait Trait1 extends Whatever {
val request : Request[AnyContent]
...
}

trait Trait2 extends Trait1 {
override val request = ...
}

rspec has a "let" syntax to mimic this, I've been told, and it sounds
confusing :). (I haven't used RSpec for 16 months now ;).)

Not-nice in specs2: the DSL. When I see ">>" I think "bit-shift". When
I see "in" I think "test if the value is in the set".

Missing in specs2: not really related to RSpec, but ... it would be
really nice if Mockito methods threw NotImplementedError instead of
returning null. I never found a way to do that.

I hope this helps. These are all itsy-bitsy compared to the
compilation times issue, though.

>> Play would be easier (and faster) to use if it had a super-simple test
>> framework with Specs2's matchers, RSpec's "describe" blocks, Mockito, and
>> lots of opinionated helpers for testing various kinds of classes.
>
> Maybe you can try using JUnit with specs2 JUnitMatchers (but that doesn't
> give you blocks)? Or maybe specsy (looks like what you're after)?

We looked at lots of options and decided upon specs2 because That's
What Play Supports. Specs2 may have been the wrong choice; then again,
it may have been the right one: there was lots of good advice on this
list and on Stack Overflow.

But ooh, is it ever complicated! Here's where Rails has a better
approach: its default, Test::Unit, is simple, quick and usable. RSpec,
too, is simple, quick and usable. Guides for both explain how to use
them to test any kind of class you'll need in a Rails project.

Regardless of whether specs2 is the best choice for Play, choosing a
test framework isn't enough. That's just one part of the larger task
of creating the most efficient write-tests -> test -> write-code ->
test cycle possible.

Jxtps

unread,
Oct 15, 2013, 8:38:27 PM10/15/13
to play-fr...@googlegroups.com
I developed in rails for 4 years, then switched to Play, and have been developing in Play since 1.x. You raise many excellent points. A couple of comments:
  • I found refactoring to be a lot easier in Play vs Rails. You sound like you write tests religiously - I was never able to get there. Some parts of the app, yes, anything near complete coverage, no. I'm maintaining an old 2.x Rails app - change is scary.

  • Rails Windows support was abysmal. Just starting our rails app would take 20 seconds if I recall correctly. Page refreshes in dev mode were not fast (though faster than half-the-world recompiles). Yes, that's a Ruby-Rails-Windows issue, and most people in that ecosystem don't use that. But I do.

  • HAML: I have a simple HtmlBuilder base class that let's me crank out HAML-style templates in pure scala. Works like a dream IMHO. Happy to share it. There was no interest when I mentioned it on the Play dev list - until you're hooked it's not interesting ;)

Eric Torreborre

unread,
Oct 15, 2013, 9:11:49 PM10/15/13
to play-fr...@googlegroups.com

Thank you for addressing my concerns! If nothing else, you've provided
some great example code I wish I'd seen online earlier :).

Yes, documentation and examples can certainly be improved.
I see (but I don't really know what to do about it).
 
Not-nice in specs2: the DSL. When I see ">>" I think "bit-shift". When
I see "in" I think "test if the value is in the set".

It is hard to find names pleasing everyone :-). But you can add your own favourite syntax on top if you want, see the MockitoSpec below with `it`.

Missing in specs2: not really related to RSpec, but ... it would be
really nice if Mockito methods threw NotImplementedError instead of
returning null. I never found a way to do that.

You can try that:

class MockitoSpec extends mutable.Specification with Mockito {

  // a different syntax to declare examples
  it ("should throw exceptions on non-stubbed methods") {
    val a = mock[A] // see below the redefinition of `mock`

    // note that we cannot use the specs2 syntax `a.method1 returns "hey"`
    // it is equivalent to the Mockito syntax `when(a.method1).thenReturn("hey")`
    // which doesn't work either
    // so we need to use Mockito's `doReturn` syntax here
    doReturn("hey").when(a).method1

    a.method1 must_== "hey"
    a.method2 must throwA[NotImplementedError]
  }

  trait A {
    def method1: String
    def method2: String
  }

  // implementing the `it` syntax
  def it[R : AsResult](desc: String)(r: =>R) = inExample(desc) >> r

  // don't return `null` on unstubbed methods but throw a NotImplementedError
  override def mock[T : ClassTag] = super.mock[T].defaultAnswer((_: InvocationOnMock) => ???)
}
 
Regardless of whether specs2 is the best choice for Play, choosing a
test framework isn't enough. That's just one part of the larger task
of creating the most efficient write-tests -> test -> write-code ->
test cycle possible.

Sure, and I hope this is going to be optimised even more in the future.

Cheers,

Eric.

Yevgeniy Brikman

unread,
Oct 16, 2013, 1:39:35 AM10/16/13
to play-fr...@googlegroups.com
This is an excellent analysis. I don't agree with all of your conclusions, but I wish all discussions about web frameworks were as mature and well thought out. I believe most of your top recommendations are spot on and the Play community would benefit from following them.

A few thoughts I'd like to toss out:
  1. I don't think you're giving type safety nearly enough consideration. It's not just for correctness, but also developer productivity. 
    1. There is no doubt you can refresh a page or rerun a test faster in Rails. However, I've found that you have to refresh the page and rerun the tests much more often because it takes many more tries to get things right. 
    2. The type system - which in Play includes the Scala code, routes files, templates, and build system - catches a huge number of errors. In Rails, you are the type system.
    3. If you use an IDE while you code, the IDE catches errors while you type; and lets not forget all the other benefits of an IDE, including auto complete, codegen, refactor help, debugging, code browsing, find usages, etc. The importance of this grows exponentially as a project gets older and larger. 
    4. You can mitigate some of these problems in Rails (or any dynamic language) with a large suite of tests. However, as this suite grows, the tests take longer to run, mitigating some of Rails' advantage. Worse yet, they impose a very large maintenance overhead, because you have to constantly update those tests. 
    5. IMO, Rails sets the bar for iteration speed on a small project; but I cannot overstate how much slower it becomes for a project that's over a year old and has had several dozen developers touch it. With no no type safety and no IDE support, figuring out how to use someone else's Ruby code or refactoring a large, old Rails app can be very difficult.
  2. 100% agree on SBT in terms of its API. It has an extremely steep learning curve: the rampant use of operators and implicits makes the code very tough to follow; there are a lot of docs, but for some reason, it's very tough to get the "big picture" from them of how to go about making changes; the source code is no help either, as its also a mess of operator overloading and implicits. SBT 0.13 made some API improvements by using macros, but it's still confusing. 
  3. Play apps certainly take longer to build, but you get something in exchange for the build time (all the compile time checks) and the output is an artifact that's trivial to deploy and run (./start). On the other hand, with Rails, the combination of RVM (or is rbenv the rage these days?) and bundler has been a nightmare on every single OS I've tried. I've followed the Rails docs as closely as humanly possible on a huge variety of operating systems (Ubuntu, Fedora, RHEL6, Windows XP, Windows 7, OSX), and each one left me dealing with a number of non-trivial errors with cryptic error messages, most often related to missing .h files and MySQL libraries. That said, if you can use a standard cloud deployment, such as Heroku, they all have Rails setups ready-to-go, so Rails is a huge win in that department.
  4. I'd also add a note about performance: you're right that in most cases, it doesn't matter. But when it does, you really don't want to be using Rails. And it's not premature optimization: Play performs remarkably well out of the box. Many Rails guides, on the other hand, recommend all sorts of caching solutions for even a mild amount of traffic ("there are only two hard problems in computer science..."). Even weirder, most Rails users are strangely unfamiliar with the Ruby threading model, don't know about the GIL, and don't know how to setup multiple Rails processes to use all CPU cores, so they hit Rails bottlenecks with hilarious low traffic numbers; perhaps the docs here need work. Finally, Play's support for NIO is a huge performance win for service oriented architectures, where servers spend most of their time waiting on I/O.
Obviously, all the above comes from my personal experience using Rails and Play in a relatively high traffic environment, with large teams, and large codebases, so it won't apply to everyone. 

Thanks again for starting a good discussion.

virtualeyes

unread,
Oct 16, 2013, 1:49:57 AM10/16/13
to play-fr...@googlegroups.com
Great write up.

Now, I will wave my magic want and instantly alleviate your assets problem:
// Build.scala
lazy val aaMain
= play.Project(...).settings(
 
...
  lessEntryPoints
:= Nil,
  coffeescriptEntryPoints
:= Nil,
  javascriptEntryPoints
:= Nil
)

Install Bower + GruntJS (or Yeoman if that's how you roll), and voila, snappy assets compilation completely independent of slow scala compilation (if you want grunt process tied into play> run,test,compile then search this group for Grunt + Angular, a Play user posted the Build.scala code for that).

I think you've highlighted a lot of Rails' strengths, and not many of its weaknesses ;-)

I'd suggest testing what should be tested and not 100% test coverage as one is obliged to strive for in a dynamic language backed framework.

There's a tradeoff for type safety, certainly, the development experience will be less rapid fire than blazing away in TextEdit, but, on the other hand you'll know, as you code, exactly what it is that you're working with; that is, in my book, a huge WIN.

As for the database story in Scala, you've covered the main players, but did miss Activate, probably the closest AR equivalent in Scala. Apparently it's highly performant (blowing away Slick at present if these numbers are to be trusted) and typesafe. Slick is in flux, they're ironing out some of the rough edges, but will likely be the standard moving forward, IMO.

As for cross sub project routing, I created a couple of build tasks to address this for scala and javascript based routes:
https://github.com/godenji/play-reverse-router
https://github.com/godenji/play-reverse-router-js

Add your voice to the dependency-free reverse routing Play issue request:
https://github.com/playframework/playframework/issues/1390

Ideally we'll have a framework supported routing solution in Play 2.3/2.4, the more Play-ers requesting this feature the more likely it is to happen.

Thanks for the Rails/Play comparison, quite revealing.

diwa

unread,
Oct 16, 2013, 4:05:33 AM10/16/13
to play-fr...@googlegroups.com
Thanks for a well-reasoned and troll-free analysis.
In the middle of this year, my team had to decide on a new platform across node, .NET, spring, rails and play and we ended up choosing play (thanks in no small measure to the wonderful summary at http://www.slideshare.net/brikis98/the-play-framework-at-linkedin/115 )

We found that play+scala+akka promised us the responsiveness of node, the conciseness of ruby, the productivity of rails and the modularity & typesafety of .NET/Spring
Our main pain points have been poor dev experience for SPAs, impossibility of effective TDD & lack of love for sub-modules (apart from issues due to the newness of the framework like maturity, small ecosystem and evolving best-practices)

We deal with the issues your raised in the following way

(1) & (4) -Client Side Dev - We have moved completely to play-yeoman and use the grunt and bower to power our client. I would love to see play-core embrace this plugin and offer this for SPAs. The difference in development productivity is dramatic

(3) Slick - We use very bullish on slick even if we are worried at the sporadic progress of 2.0 series. We use play-slick and would really love some macros to reduce the boilerplate

(5) Tests - This is the topic which gives us most grief and disappointment with play. There is hardly any discussion at a meaningful level about testing as we are still stuck at selenium version mismatches, specs2 mixin conflicts etc. 
               In rails, during tests, the app is started and the database created once for the entire test run. During each test, a transaction is started and rolled back. Hence we are used to sub-second response time (with preloaders like spring/zeus)
              In play, the implicit expectation is that a new fakeapp is started for every individual test which greatly slows down the test suite. FakeApp is not restartable probably due to bonecp issues. Also the JVM is forked for every test run. So we have a minimum turnaround time of 5-6 seconds for the simplest change.
              To mitigate this, we are trying the following
             1. Implemented a service layer as a pure scala library and test it independent of play (and no jvm forking)
             2. Trying hard to keep our controllers not dependent on the running application so that we can write the controller tests without a running fakeapplication
             3. Use ~testQuick in sbt
             4. Experimenting with scalatest for improving compile times but however we are not seeing much gains with TDD as the jvm forking tax is the main time sink and there is also a much smaller community using it with play

I am also quite thankful to the community and this mailing list is a life-saver

Regards,
Diwa

Eric Torreborre

unread,
Oct 16, 2013, 8:55:06 AM10/16/13
to play-fr...@googlegroups.com
Hi Diwa,

> specs2 mixin conflicts

This can generally be solved especially if this is about "deactivating" implicits. Were there some remaining issues?

Do you think that Play should come with a more specialised Spec trait just having the right amount of implicits and/or context setting?

Thanks,

Eric.

diwa

unread,
Oct 16, 2013, 11:46:26 AM10/16/13
to play-fr...@googlegroups.com
Hi Eric,

  At the outset, i must say that you have been the one of the most friendly, helpful and responsive open source maintainers I have come across and thanks a lot for that. The speed at which you implement improvements amazes me (recent examples are macro matchers, modules refactoring)

   I love several aspects of specs2 like the vast matcher libraries, laser focus on BDD, flexibility with around matchers/examples, mockito sugar, data-tables etc. I have even used DependencyMatchers to enforce the application layering

   My point was that most of the discussions around testing are still around the low level bootstrapping issues and not around BDD, best practices, new approaches etc.(Play meanwhile has a palatable base trait - https://github.com/playframework/playframework/blob/master/framework/src/play-test/src/main/scala/play/api/test/PlaySpecification.scala)

  My main gripes about specs2 are around the following topics
   1. Long TDD turnaround - In a TDD session, we do a large number of small changes which result in incremental compiles and running a small set of tests. So the individual compilation times are important. I will take your advice and build out a stripped down base trait.
   2. Docs tuned for Library testing vs Web App testing - I have a nagging feeling that a lot of specs2 documentation is tuned for testing libraries/components but not really web applications. There is not a lot of helpful documentation/samples available about managing database create/drop, wrapping transactions around tests, seed/fixture data handling, etc. (I do know that specs2 is immensely powerful and can do all of this but I am having to learn most of this by piecing together your mails to the play and specs2 mailing lists, your tweets and gists)
   3. Complexity of Acceptance tests - Acceptance tests are a real differentiator for specs2 where its functional roots shine through. I would love to write  UI-driven acceptance tests in given-when-then style wrapping around a fluentlenium DSL and page model like i could in rails with cucumber and capybara. However I don't see much examples for this or for more complex cases and i am scared that i might drown in stuff like shapeless. For ex. I have some long scenarios where givens can appear after whens and thens and i think it is not possible to implement currently.

  There are several other things in the play-land that stop the true power of specs2 shining through
  1. Sequential Tests only  - Since we cannot run tests in parallel, the functional design of specs2 is not buying us much (https://groups.google.com/forum/#!topic/play-framework/aqh3mldsUyc)
  2. JVM forking for tests - This adds an overhead of 3-4s even if a single test has to be run. In addition, this swallows logging and makes debugging difficult
  3. Non-restartability of FakeApplication - This means that instead of having a single fakeapp which is started and stopped in each test, we need to create a new fake app per test. I am dealing with this by trying to write most of my tests without needing a running play application (http://eng.42go.com/speed-up-play-tests-direct/)

  So in effect I feel that I am paying all the tax for specs2 without reaping all the benefits possible.

Regards
Diwa

Adam Hooper

unread,
Oct 16, 2013, 12:29:26 PM10/16/13
to play-fr...@googlegroups.com
On Wednesday, October 16, 2013 1:39:35 AM UTC-4, Yevgeniy Brikman wrote:
  1. I don't think you're giving type safety nearly enough consideration. It's not just for correctness, but also developer productivity.
I _prefer_ Scala over Ruby. I _like_ type safety. But it doesn't change the engineering process: the tasks are the same, regardless of programming language, and in my experience I can achieve my requirements more speedily with Rails.
  1. I'd also add a note about performance: you're right that in most cases, it doesn't matter. But when it does, you really don't want to be using Rails. And it's not premature optimization: Play performs remarkably well out of the box. Many Rails guides, on the other hand, recommend all sorts of caching solutions for even a mild amount of traffic ("there are only two hard problems in computer science..."). Even weirder, most Rails users are strangely unfamiliar with the Ruby threading model, don't know about the GIL, and don't know how to setup multiple Rails processes to use all CPU cores, so they hit Rails bottlenecks with hilarious low traffic numbers; perhaps the docs here need work. Finally, Play's support for NIO is a huge performance win for service oriented architectures, where servers spend most of their time waiting on I/O.
First, do you mind if I ignore out a few arguments? For instance, that Ruby programmers are less experienced than Scala programmers; that Rails server architecture is qualitatively different from Play server architecture; that Rails howtos are worse than Play howtos; that threading models make a huge difference (Play uses one thread per processor, plus background threads; Rails uses one process per processor, plus background processes). Websites' architectures don't change much: it's load balancer -> web server -> [cache or] framework -> [cache or] my code -> [cache or] databases, services, etc., whether you're using Play or Rails. And there's plenty of bad advice out there for newbie Play developers.

Fundamentally, Play's performance advantage over Rails is that it's event-driven. And that's great. (Unless you're using JDBC, that is; if you are, Play probably won't give you a significant performance advantage....)

But I really want to stress the point we agree on: in most cases, it doesn't matter. For an engineer, if the target is 100 req/s, Rails serves 100 req/s and Play serves 1,500 req/s, then both frameworks are equivalent with regards to performance.

Rephrased: sure, NoSQL > SQL, threading > processes, async > synchronous, bytecode > interpreted, and so on, I agree ... with provisos. I agree in theory. I agree every employee should understand these things. I agree every team should _consider_ scalability throughout a project, if only to categorize the concern as technical debt. I agree it's enjoyable to craft more solid solutions. But in my experience (most of the websites I code involve prototyping and quick iterations), the benefits of NoSQL/threading/async/bytecode haven't been worth the cost at the start-up stage.

I love Play's premise: that it's lowering the cost. It certainly is. However, in my experience, the cost is still too high for most new projects.

Play needs to keep doing what it's doing. For now, I'm still recommend Rails for most projects.

There are exceptions: I imagine the Guardian is one, and I trust that LinkedIn is another. However, in both cases Play is the "rewrite framework," not the original framework, and that's a whole other story because so many requirements have solidified and performance requirements are higher and relatively well-defined. In other words: to some extent, migrating to Play _is_ optimization.

A thought exercise: it's an alternate 2002, in which Play 2.2 and Rails 4.0 exist. You and your team are going to invent LinkedIn. You know other companies are trying to do the same thing, and they have the same choice of frameworks you do. Do you choose Rails or Play? I'd choose Rails, because judging from past experience I can whip out more features in the same amount of time with Rails, giving me an edge over my Play-using competitors. I can always switch to Play ten years down the road after I've pummelled my competition....

(Queue a brainstorm idea: Play+JRuby, an event-driven framework that lets me write tests and prototype code in Ruby then lets me optimize by rewriting a class at a time in Scala. Fast development cycle, fast code ... best of both worlds.)

Enjoy life,
Adam

Eric Torreborre

unread,
Oct 16, 2013, 6:52:06 PM10/16/13
to play-fr...@googlegroups.com
build out a stripped down base trait.

Make sure to make it an abstract class and not a trait. Otherwise you will incur a 0.5s recompilation time per file (as measured on specs2 codebase)

> 1. Long TDD turnaround - In a TDD session, we do a large number of small changes which result in incremental compiles and running a small set of tests. So the individual compilation times are important. I will take your advice and build out a stripped down base trait.

I need to work out a SpecLite abstract class that would give the bare minimum implicits and benchmark against the full 'Specification' one in order to see what the real difference.

>   2. Docs tuned for Library testing vs Web App testing - I have a nagging feeling that a lot of specs2 documentation is tuned for testing libraries/components but not really web applications. There is not a lot of helpful documentation/samples available about managing database create/drop, wrapping transactions around tests, seed/fixture data handling, etc. (I do know that specs2 is immensely powerful and can do all of this but I am having to learn most of this by piecing together your mails to the play and specs2 mailing lists, your tweets and gists)

This is absolutely true and the main reason for this is that I'm not developing web apps at work. I was hoping that some members in the Play community (or the play team) would write this chapter of the book. I promise to do something about it if I come to web development (unfortunately not likely in the next 6 months).

>   3. Complexity of Acceptance tests - Acceptance tests are a real differentiator for specs2 where its functional roots shine through. I would love to write  UI-driven acceptance tests in given-when-then style wrapping around a fluentlenium DSL and page model like i could in rails with cucumber and capybara. However I don't see much examples for this or for more complex cases and i am scared that i might drown in stuff like shapeless. For ex. I have some long scenarios where givens can appear after whens and thens and i think it is not possible to implement currently.

The current status of GWT is the result of trying to implement it without using variables and keeping type-safety. I am not sure if this is really possible to do something simpler given those constraints. On the other hand I might be losing the perspective here that we are talking about a webapp that is probably talking to a backend persisting data. So effectively that backend serves as a huge variable. In that case we should be able to describe a Scenario as:

class GWTWebSpec extends Specification with GWT with StandardRegexStepParsers { def is = s2"""

A given-when-then example for a calculator webapp ${calculator.start}
Given the following number: 1
And a second number: 2
And a third number: 6
When I use this operator: +
Then I should get: 9
And it should be >: 0 ${calculator.end}

Now do one last given for the cleanup ${reset.start}
reset to: 0 ${reset.end}

"""
  val anOperator = readAs(".*: (.)$").and((s: String) => s)

  val calculator =
    Scenario("calculator").
      given(anInt)((i: Int) => saveFirstNumber(i)).
      given(anInt)((i: Int) => saveSecondNumber(i)).
      given(anInt)((i: Int) => saveThirdNumber(i)).
      when(anOperator) { (op: String) => val (i,j,k) = (getFirst, getSecond, getThird); saveResult(if (op == "+") (i+j+k) else (i*j*k)) }.
      andThen(anInt)(resultMustBeOk).
      andThen(anInt) { case expected => getResult must be_>(expected) }
def resultMustBeOk = (expected: Int) => getResult === expected

  val reset =
    Scenario("reset the calculator").
    given(anInt)((i: Int) => resetTo(i))

} 

There is no shapeless involved in there and I think this satisfies 2 other constraints:
  
  - the ability to reuse extractors (anInt, anOperator)
  - the ability to reuse functions on those values (resultMustBeOk)

Note also that I added another scenario with just a given step after the main scenario.

Again the deficiencies of this style in specs2 probably come from the fact that I haven't had to eat my own dog food!

 There are several other things in the play-land that stop the true power of specs2 shining through

You're not the only one to voice those concerns apparently. Let's hope that the Play team will be as reactive as the framework :-).

Christopher Hunt

unread,
Oct 16, 2013, 9:44:15 PM10/16/13
to play-fr...@googlegroups.com
Hi Adam,

Thank you so much for your honest and comprehensive feedback. I think that the responses on this thread have also been great and so I'll avoid covering points that others have made.

On Wednesday, 16 October 2013 06:20:47 UTC+11, Adam Hooper wrote:
MY TOP FIVE RECOMMENDATIONS FOR PLAY

I'm often a front-end developer, so my views are skewed....

If I were king of the world, I'd have Play:
Note: you are king of our world - you represent the Play community. That may sound trite, but feedback like this is what continues to improve Play.
 

1. Ditch Rhino and get a fast JavaScript engine for asset compilation. I can't see myself using Play for any new project until this happens.
This is an incredibly important point that you raise and I'm thrilled to be able to report that Play 2.3 will be overhauling its approach to JavaScript tooling.

We will be providing high performance alternatives to Rhino and are able to do this given a new library of ours that leverages the WebDriver protocol. Please note that the library is a work in progress, but the essential idea is that the library is called on to natively execute JavaScript and HTML documents in a browser; be it Chrome, IE or the headless PhantomJs (and many more). 

Many browsers already support the WebDriver protocol (which is hoped to become a W3C standard). HtmlUnit also supports the WebDriver architecture and will facilitate Play's out-of-box experience (in-JVM is important to us for using Play out of the box by not requiring additional downloads). HtmlUnit provides an HTML DOM to Rhino, and I suspect that HtmlUnit will support Nashorn in the future, possibly even for JDK7 (Nashorn is part of JDK 8, but there is already a port to JDK 7).

Our goal is to utilise the WebDriver architecture so that JavaScript concerns are removed out of play-core. As a proof of concept we have now developed a jslint plugin for use from sbt, and therefore Play. CoffeeScript, LESS, RequireJs optimisation, unit testing with QUnit and/or Jasmine and Closure Compilation will all be factored out of play-core and re-implemented as sbt plugins that use our new WebDriver library.

Incidentally the WebDriver library we are developing focuses on re-implementing the Selenium WebDriver library found here. The main reason we have re-written the client side of the WebDriver protocol is to achieve a reactive architecture that permits a high degree of parallelism. If your machine has 4 cores and there are 4 js files to lint (for example) then all 4 will be linted in parallel; and the same goes for the other js plugins that we'll write).

The performance of JS in Play's build cycle sounded like your primary concern, so I hope that this allays that.
 

2. Encourage developers to write tests first and code second. That means changes to documentation, helper classes, database integration, and perhaps a different DSL than specs2's.
Our documentation continues to evolve and I hope that this is felt by the community. We always welcome pull requests of course and appreciate improvements to the documentation.
 

3. Make Slick great and make Play opinionated about using Slick. Do what ActiveRecord does right: it makes the easy stuff easy and the hard stuff manageable. (To me, Play has no risk of doing what ActiveRecord does _wrong_, so Play can really shine here.)
We will be accepting the play-slick project into master very shortly.
 

4. Refocus asset management: there are industry standards for developing, bundling and serving JavaScript, CSS and image assets, and Play should follow them. Rails (Sprockets), Jammit and Yeoman showcase the standard features.
 
There was some discussion on this, and it came up again recently:


I'm pretty confident that you'll see improvements in 2.3 around here.

> 5. Work really hard making tests and code compile more quickly. At the very least, write documentation that helps developers write faster-to-compile code.
> For instance, can Scala code compile appreciably faster if I declare the types of my variables or if I (somehow) disable implicit variables? Or better yet, is
> there a way to run the tests with an interpreter rather than a compiler? Or to only recompile a single test at a time? Would it be productive to write core code
> in (gasp) Java for a compile-time win?

You'll see major improvements here with the next release of sbt which will introduce an experimental incremental compilation step. We're very optimistic about this feature for Play. IDE support also continues to evolve rapidly. Finally I think that it is fair to say that the Scala team continually make advances in terms of scalac.

Thanks again for your comprehensive post.

Kind regards,
Christopher

Steven Marcus

unread,
Oct 16, 2013, 11:04:15 PM10/16/13
to play-fr...@googlegroups.com
Can you comment on whether the acquisation of the spray.io project/team by Typesafe will impact Play 2.3 ?

I think Akka gaining http support via Spray is a great thing in general. But I'm a little concerned that "Play 3 based on akka-http" will be incompatible in the same way that Play 2 was a break from Play 1...

tia
Steven Marcus



Christopher Hunt

unread,
Oct 16, 2013, 11:24:01 PM10/16/13
to play-fr...@googlegroups.com
On 17/10/2013, at 2:04 PM, Steven Marcus <steven...@gmail.com> wrote:

Can you comment on whether the acquisation of the spray.io project/team by Typesafe will impact Play 2.3 ?

I think Akka gaining http support via Spray is a great thing in general. But I'm a little concerned that "Play 3 based on akka-http" will be incompatible in the same way that Play 2 was a break from Play 1…

I think that there should be a separate topic for this, so please start one up if you require further information. To be honest we don't yet know if akka-http will be available within Play 2.3's timeline, but if it is then it'll probably be offered as an experimental option while Netty remains the default.

Kind regards,
Christopher

-- 
Christopher Hunt
Senior Engineer
Typesafe – Build Reactive Apps on the JVM!

Twitter: @huntchr

Maxime

unread,
Oct 17, 2013, 5:15:26 AM10/17/13
to play-fr...@googlegroups.com
As for the Less files with bootstrap. Play compiles all less files but the ones that start with an _underscore (that can only be used in import).
To improve Less compilation, you can rename all bootstrap (but bootstrap.less) with an _underscore, or move the bootstrap directory out of /app/assets/ and only use imports.

This is not a solution, but a workaround.

Adam Hooper

unread,
Oct 17, 2013, 9:22:50 AM10/17/13
to play-fr...@googlegroups.com
On Thursday, October 17, 2013 5:15:26 AM UTC-4, Maxime wrote:
As for the Less files with bootstrap. Play compiles all less files but the ones that start with an _underscore (that can only be used in import).
To improve Less compilation, you can rename all bootstrap (but bootstrap.less) with an _underscore, or move the bootstrap directory out of /app/assets/ and only use imports.

This is not a solution, but a workaround.

Hi Maxime,

For the record: I was already doing this :). Or at least, I was overriding lessEntryPoints to include a single .less file.

Enjoy life,
Adam 

Adam Hooper

unread,
Oct 17, 2013, 9:38:35 AM10/17/13
to play-fr...@googlegroups.com
On Wednesday, October 16, 2013 9:44:15 PM UTC-4, Christopher Hunt wrote:
Hi Adam,

Thank you so much for your honest and comprehensive feedback. I think that the responses on this thread have also been great and so I'll avoid covering points that others have made.

Hi Christopher,

I'm glad Typesafe (along with the community in general) is receiving my comments so constructively. This is definitely way cool.
 
The performance of JS in Play's build cycle sounded like your primary concern, so I hope that this allays that.

I wouldn't call the performance of JS in Play's build cycle my _primary_ concern. It was very important to me, yes; the reason I put it at #1 is that I assumed it had the best benefit/cost ratio for Play.

Encouraging test-driven development is probably my primary concern, but I assume that's much more gradual and difficult and involves more trade-offs and discussions. That's why I listed (what I assume to be) the more tangible aspects at #2 and the more experimental stuff at #5.

It's extremely encouraging to see that Play is already evolving in a direction that will help it compete with Rails. I can't wait to see the magic Play holds when I check back in on it in the years to come. I can't wait for the comparison to boil down to: "Play: statically typed, 10 times faster; Rails: dynamically typed, 10 times slower."

Enjoy life,
Adam
Message has been deleted

Kui Zou

unread,
Oct 18, 2013, 9:31:56 AM10/18/13
to play-fr...@googlegroups.com
In terms of asset fingerprinting, please check this repo PlayFrameworkAssetsDemo.

The output of assets path is like:

Best regards

Guillaume Bort

unread,
Oct 18, 2013, 10:04:07 AM10/18/13
to play-fr...@googlegroups.com
You don't need to fingerprint a file name that already contain a version number... jquery-1.9.0.min.js will always be jquery-1.9.0.min.js and will never change. You can already cache it infinitely.

And your solution doesn't really solve the real problem of fingerprinting that is that you have to pre-process al the HTML/JS/CSS files to replace the real file names with their fingerprint versions.

As I already posted in this group before, I think that the best way to solve this just to add a version prefix to your asset path:

# Public resources                                    
GET    /...:version/*file                       controllers.Application.asset(version, file)

No magic, no pre or post processing, no need to have all the static resources processed by the build system. You can then set your cache-control to an infinite value, and still, at each new version of your application you get a clean refresh of all these static resources.

This is the way we did it for prismic.io, if you check any of our website or application page you will see that all our static assets are prefixed by a version number:

...
<link rel="icon" type="image/png" href="/...5aaf90e/images/favicon.png">
<link rel="stylesheet" media="screen" href="/...5aaf90e/stylesheets/www.min.css">
...



--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--

James Ward

unread,
Oct 18, 2013, 10:50:08 AM10/18/13
to play-fr...@googlegroups.com
We do the same thing as prismic.io for typesafe.com but we generate a
new version on startup. It's not optimal because it won't work on
multiple nodes and it means that with every restart users must fetch new
resources. But optimizing that would be easy (we could use a config
param instead). Here is the Asset controller wrapper we use to
accomplish this:

object StaticAssets extends Controller {

val versionStamp: String = new Date().getTime.toString + "/"

def at(file: String) = CustomNotFound {
val actualFile = file.replaceAll(versionStamp, "")
Assets.at("/public", actualFile)
}

def getUrl(file: String) = {
val versionedFile = versionStamp + file
Play.configuration.getString("contenturl") match {
case Some(contentUrl) => contentUrl +
controllers.routes.StaticAssets.at(versionedFile).url
case None => controllers.routes.StaticAssets.at(versionedFile)
}
}

}


We have CloudFront in front of the app so that all of the static content
for production comes from a CDN. If you want to learn more about how to
set that up, check out:
http://www.jamesward.com/2012/08/08/edge-caching-with-play2-heroku-cloudfront

-James


On 10/18/2013 08:04 AM, Guillaume Bort wrote:
> You don't need to fingerprint a file name that already contain a version
> number... jquery-1.9.0.min.js will always be jquery-1.9.0.min.js and
> will never change. You can already cache it infinitely.
>
> And your solution doesn't really solve the real problem of
> fingerprinting that is that you have to pre-process al the HTML/JS/CSS
> files to replace the real file names with their fingerprint versions.
>
> As I already posted in this group before, I think that the best way to
> solve this just to add a version prefix to your asset path:
>
> # Public resources
> GET /...:version/*file
> controllers.Application.asset(version, file)
>
> No magic, no pre or post processing, no need to have all the static
> resources processed by the build system. You can then set your
> cache-control to an infinite value, and still, at each new version of
> your application you get a clean refresh of all these static resources.
>
> This is the way we did it for prismic.io <http://prismic.io>, if you
> check any of our website or application page you will see that all our
> static assets are prefixed by a version number:
>
> ...
> <link rel="icon" type="image/png" href="/...5aaf90e/images/favicon.png">
> <link rel="stylesheet" media="screen"
> href="/...5aaf90e/stylesheets/www.min.css">
>
> ...
>
>
>
> On Fri, Oct 18, 2013 at 3:31 PM, Kui Zou <zouk...@gmail.com
> <mailto:zouk...@gmail.com>> wrote:
>
> In terms of asset fingerprinting, please check this repo
> PlayFrameworkAssetsDemo
> <https://github.com/zoukui/PlayFrameworkAssetsDemo>.
>
> The output of assets path is like:
>
>
> http://static.example.com/__javascripts/jquery-1.9.0.min.__v5c6aec8.js <http://static.example.com/javascripts/jquery-1.9.0.min.v5c6aec8.js>
>
> You can also change it to other formats.
>
> Best regards
>
> On Wednesday, October 16, 2013 3:20:47 AM UTC+8, Adam Hooper wrote:
>
>
> ** Asset management (JavaScript/CSS/Images/etc)*
>
> I'm a front-end developer. My day-to-day interactions with Play
> are fraught with frustration:
>
> - It takes two minutes to tweak _one byte_ on a decent-sized,
> Bootstrap-based stylesheet suite on my netbook. _Two minutes_.
> On Rails, the same tweak would take _one second_.
>
> - On Play, I can't avoid Google's Closure Compiler. I use
> CoffeeScript, so I don't need lint checking, yet it wastes a
> second or two every page refresh.
>
> - Rails uses asset fingerprinting (e.g.,
> "styles-__908e25f4bf641868d8683022a5b62f__54.css") and Play
> doesn't. To my knowledge, fingerprinting is the industry
> standard: it makes caching and cache invalidation seamless. Web
> pages load faster, CDNs are easy to update, and server load is
> reduced. http://guides.rubyonrails.org/__asset_pipeline.html
> <http://guides.rubyonrails.org/asset_pipeline.html>
>
> - Rails fingerprints images, PDFs and other assets, too -- not
> just CSS/JS.
>
> - Rails lets you reference image routes from stylesheets and
> JavaScript; it lets you reference stylesheet routes from
> JavaScript, etc.
>
> - Rails comes with Sass by default instead of Less. That's a
> marginal win for Rails, since Sass has some nifty features the
> Less developers deemed undesirable. (Loops, for instance, are a
> pain in the neck with Less.)
>
> My top suggestion: Play should ditch Rhino for something faster.
> Rhino is way, way, way too slow. Rhino is unusable in
> development. Rhino is the biggest reason I fear Play.
>
> After that, Play still has a ways to go to catch up with Rails.
> The docs at http://guides.rubyonrails.org/__asset_pipeline.html
> <http://guides.rubyonrails.org/asset_pipeline.html> describe
> exactly what I'd expect a mature framework to support. (Lest you
> think Rails has tainted my judgement, consider that other asset
> managers have the same features: Yeoman and Jammit spring to
> mind, and there are mature, documented Grunt plugins for each of
> the features I've listed.)
>
> I have implemented hacks:
>
> - to quickly run CoffeeScript unit tests (using NodeJS):
> <https://github.com/overview/__overview-server/tree/master/__test/assets/javascripts/__autotest
> <https://github.com/overview/overview-server/tree/master/test/assets/javascripts/autotest>>
>
> - to circumvent Play when compiling Less files in development
> (using lessc):
> <https://github.com/overview/__overview-server/blob/master/__auto/compile-less-on-linux.sh
> <https://github.com/overview/overview-server/blob/master/auto/compile-less-on-linux.sh>>
> and
> <https://github.com/overview/__overview-server/blob/__51b08add7811a9c058ca355dbd4e61__060c5468e4/project/Build.__scala#L149-L153
> <https://github.com/overview/overview-server/blob/51b08add7811a9c058ca355dbd4e61060c5468e4/project/Build.scala#L149-L153>>.
> (After editing Less files, wait one or two seconds before
> refreshing the page or the new styles won't appear. This is
> error-prone, but it's better than waiting two minutes.)
>
> /Winner: Rails/
>
> --
> You received this message because you are subscribed to the Google
> Groups "play-framework" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to play-framewor...@googlegroups.com
> <mailto:play-framework%2Bunsu...@googlegroups.com>.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>
>
>
> --
> Guillaume Bort, @guillaumebort <http://twitter.com/guillaumebort> �
> http://guillaume.bort.fr

Adam Hooper

unread,
Oct 18, 2013, 11:56:47 AM10/18/13
to play-fr...@googlegroups.com
On Fri, Oct 18, 2013 at 10:50 AM, James Ward <james...@typesafe.com> wrote:
> We do the same thing as prismic.io for typesafe.com but we generate a new
> version on startup. It's not optimal because it won't work on multiple
> nodes and it means that with every restart users must fetch new resources.
> But optimizing that would be easy (we could use a config param instead).
>
> We have CloudFront in front of the app so that all of the static content for
> production comes from a CDN. If you want to learn more about how to set
> that up, check out:
> http://www.jamesward.com/2012/08/08/edge-caching-with-play2-heroku-cloudfront

This sounds like a plausible solution. It raises lots of questions, though:

* Why isn't it the default?

* Does it work during a rolling deploy: are the old versions of the
file still available _as old versions_, even if they've never been
requested by any clients?

* Is it easy to integrate with S3, a separate front-end server, or
other file servers? (With Rails it's a single command,
https://github.com/rumblelabs/asset_sync)

* Does it work on images, too, not just JS/CSS?

Play provides several seemingly reasonable solutions for serving
assets. But which is best? Will it work for everyone's needs? Does it
handle (almost) all reasonable uses? Is it easy to understand? ...
It's time for Play to become opinionated, so that developers don't
need to spend time researching the answers to all these questions.

Kui Zou

unread,
Oct 18, 2013, 12:10:41 PM10/18/13
to play-fr...@googlegroups.com
You may misunderstand my post. The file name is always jquery-1.9.0.min.js, I did not change it.
The version string is generated by git command.

Best regards,

James Ward

unread,
Oct 18, 2013, 3:53:36 PM10/18/13
to play-fr...@googlegroups.com
On 10/18/2013 09:56 AM, Adam Hooper wrote:
> On Fri, Oct 18, 2013 at 10:50 AM, James Ward <james...@typesafe.com> wrote:
>> We do the same thing as prismic.io for typesafe.com but we generate a new
>> version on startup. It's not optimal because it won't work on multiple
>> nodes and it means that with every restart users must fetch new resources.
>> But optimizing that would be easy (we could use a config param instead).
>>
>> We have CloudFront in front of the app so that all of the static content for
>> production comes from a CDN. If you want to learn more about how to set
>> that up, check out:
>> http://www.jamesward.com/2012/08/08/edge-caching-with-play2-heroku-cloudfront
>
> This sounds like a plausible solution. It raises lots of questions, though:
>
> * Why isn't it the default?

Having something like this built-in is definitely something we should
consider.

> * Does it work during a rolling deploy: are the old versions of the
> file still available _as old versions_, even if they've never been
> requested by any clients?80007

This implementation doesn't support maintaining the old versions. It
also bumps the version for all static assets whether or not they
actually changed. So it's not perfect.

> * Is it easy to integrate with S3, a separate front-end server, or
> other file servers? (With Rails it's a single command,
> https://github.com/rumblelabs/asset_sync)

My preference is to keep the assets in the app to avoid an additional
deployment step and then put the caching proxy / cdn in front of the
app. There are probably some pretty easy ways to make asset deployment
simple. I just haven't looked into it.

> * Does it work on images, too, not just JS/CSS?

It works for any static asset.
Reply all
Reply to author
Forward
0 new messages