My attempts to make my experience developing Play 2 web-applications (in Scala) more Rails-like.
I show 3 frameworks employed that draw nearer to the Ruby/Rails spirit than Play's default offerings.
2. Background
• Working with Java since 2001
• Flirting with Smalltalk all these years
• Involved with Rails since September 2012
• Hooked with Scala since January 2013
• Developing with Play since June 2013
5. “A dynamic, open source
programming language with
a focus on simplicity and
productivity.
It has an elegant syntax
that is natural to read and
easy to write.”
8. “Ruby on Rails is an open-
source web framework that’s
optimized for programmer
happiness and sustainable
productivity.
It lets you write beautiful
code by favoring convention
over configuration.”
10. • Heavily inspired from Rails
• Fun. Productive. Save + Reload.
• Convention over Configuration.
• Routing. REST. MVC...
• ... but I missed some things ...
15. case class User(var username: String) extends ActiveRecord {
lazy val posts = hasMany[Post]
}
case class Post(var text: String) extends ActiveRecord with
Timestamps {
var userId: Long = _
lazy val user = belongsTo[User]
}
Declaring Entities
16. object Tables extends ActiveRecordTables {
val users = table[User]("users")
val posts = table[Post]("posts")
}
object User extends ActiveRecordCompanion[User]
object Post extends ActiveRecordCompanion[Post]
Declaring the Schema
17. val newUser = User(username=“Homer”).create()
val users: List[User] = User.toList
User.findBy(“username”, “Homer”).foreach { user =>
val posts = user.posts.orderBy(_.createdAt desc).toList
...
...
user.posts << Post(“Ohhh donuts!”)
}
Basic Operations
26. • Write in plain English
• Separation of specification / implementation
27. Feature: Posting status updates
The goal of the system is keep co-workers
informed by posting status updates.
Background:
Given that user "manager" exists
And that user "manager" posted
| first day at work |
| meeting people |
| working like crazy |
Scenario: Posts are ordered chronologically (newest on top)
When I go to the posts page of user "manager"
Then the post nr. 1 should contain "working"
And the post nr. 2 should contain "meeting"
And the post nr. 3 should contain "first"
28. When("""^I type "([^"]*)" in the "([^"]*)" field$""") {
(text: String, fieldName: String) =>
...
}
And("""^press "([^"]*)"$""") { (buttonLabel: String) =>
...
...
}
Then("""^I should be on the posts page of "([^"]*)"$""") { (username: String) =>
...
...
...
}
Step Declarations
29. • Part of Play2
• DSL wrapping Selenium
• PhantomJS for headless testing
Fluentlenium
30. When("""^I type "([^"]*)" in the "([^"]*)" field$""") {
(text: String, fieldName: String) =>
...
}
And("""^press "([^"]*)"$""") { (buttonLabel: String) =>
...
...
}
Then("""^I should be on the posts page of "([^"]*)"$""") { (username: String) =>
...
...
...
}
31. When("""^I type "([^"]*)" in the "([^"]*)" field$""") {
(text: String, fieldName: String) =>
browser.fill("*", withName(fieldName)).`with`(text)
}
And("""^press "([^"]*)"$""") { (buttonLabel: String) =>
val button = browser.find("button", withText(buttonLabel))
button.click()
}
Then("""^I should be on the posts page of "([^"]*)"$""") { (username: String) =>
val user = User.findBy("username", username).get
val expectedUrl = controllers.routes.UserActions.posts(user.id).url
driver.getCurrentUrl() should endWith(expectedUrl)
}
Step Implementations
33. Conclusions
• Satisfied with my choices (so far)
• Integration was doable
• Play flexible enough
• to replace some parts
• to let different libraries co-exist
• Reached a more Rails-like experience
35. Some Thoughts
• Programming can (and should!) be fun
• Scala embraces some of that “fun”
• ... but we can do more
• Go check Ruby / Rails
• Let’s steal get inspired from them ;-)