Date post: | 07-May-2015 |
Category: |
Technology |
Upload: | sebastian-nozzi |
View: | 762 times |
Download: | 1 times |
à la
Sebastian Nozzi
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
Ruby
Let’s talk about
“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.”
Let’s talk about
“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.”
How Ruby is (self) perceived
• Heavily inspired from Rails
• Fun. Productive. Save + Reload.
• Convention over Configuration.
• Routing. REST. MVC...
• ... but I missed some things ...
ActiveRecord
db:migrate
Cucumber + Capybara
Anorm? Slick?
Evolutions?
Specs?
Rails’ vs. Play’s default offerings
not “railsy” enough
ActiveRecord
db:migrate
Cucumber + Capybara
ActiveRecord (for Scala)
Flyway
Cucumber-JVM + Fluentlenium
More “Rails-like” alternatives
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
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
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
Play Integration
object Global extends GlobalSettings { override def onStart(app: Application) {
if(!Play.isTest) { val flyway = new Flyway() // .. get values from Play’s config ... flyway.setDataSource(url, user, password) flyway.setInitOnMigrate(true) flyway.migrate() } Tables.initialize(...) // ActiveRecord }}
Triggering the Migrations
object Global extends GlobalSettings { override def onStart(app: Application) {
if(!Play.isTest) { val flyway = new Flyway() // .. get values from Play’s config ... flyway.setDataSource(url, user, password) flyway.setInitOnMigrate(true) flyway.migrate() } Tables.initialize(...) // ActiveRecord }}
Initializing ActiveRecord
SQL-based Migrations
package db.migration
class V1_03__CreateSomePosts extends JdbcMigration {
override def migrate(ignoredConnection: Connection) { ... ... ... ... ... }
}
Code-based Migrations
package db.migration
class V1_03__CreateSomePosts extends JdbcMigration {
override def migrate(ignoredConnection: Connection) { User.findBy("username", "Homer").foreach { homer => homer.posts << Post("I'm hungry") homer.posts << Post("I should go to Moe's") homer.posts << Post("Or order some Pizza") } }
}
Code-based Migrations
+ Fluentlenium
• Write in plain English
• Separation of specification / implementation
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"
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
• Part of Play2
• DSL wrapping Selenium
• PhantomJS for headless testing
Fluentlenium
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) => ... ... ...}
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
Conclusions
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
Some Thoughts
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 ;-)
Thank you