Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 434 lines (263 sloc) 24.808 kb
1263ebb7 »
2012-06-06 updates and article
1 Getting Started with Play 2, Scala and Squeryl
2 ==============================================
3
4 Intro to Play 2
5 ---------------
6
7 Play 2 is unique among the many web frameworks available today because it was designed with a stateless architecture.  This allows it to be a very lightweight framework that consumes little resources, such as memory and cpu.  Being stateless also allows it to easily scale horizontally, something that is critical in today's cloud architectures.   The other unique feature of the architecture is that it was designed to handle long lived connections, such as websockets and comet, by using a fully asynchronous HTTP programming model.
8
9 The other unique feature of Play 2 is that it is completely self-contained, which makes it well suited for deployment to cloud application platforms like Heroku.  That means deployment of Play applications is simple and avoids the typical environment inconsistency issues that we've experienced with container-based approaches.
10
11 Many of the newer web frameworks like Ruby on Rails, Grails, and Django are very productive at first because of dynamic typing.  Play provides the same quick iterations and rapid development that dynamic language frameworks have, but preserves the benefits of statically typed languages Java and Scala languages.  This allows the compiler help the developer but not get in the way of productivity.
12
13
14 Intro to Squeryl
15 ----------------
16
17 The best technology to use for integrating with a database is heavily debated.  Luckily we have a number of options for data persistence in the Java and Scala ecosystems.  The default database mapping tool that Play 2 uses is Anorm (Anorm is Not an Object Relational Mapper). As the name implies, Anorm does not do automatic mapping between the object and relational models.  Instead the developer writes native SQL and manually maps relational data to objects.  This approach does have advantages, namely the ability to hand tune the raw queries that are executed.  The Play developers argue that SQL is a great DSL for talking to relational databases and abstracting away the SQL layer may cause you to give up a lot of power and flexibility.
18
19 An alternative to Anorm for data persistence in Scala is Squeryl.  In contrast to Anorm, Squeryl is more like hibernate and provides object-relational mapping.  Squeryl provides a type-safe DSL for talking to databases.  Squeryl also allows you to explicitly control the granularity of data objects that are retrieved.  This provides an elegant solution to the N+1 problem common with traditional ORM, like Hibernate.
20
21 Since Squeryl is not the default persistence library in Play 2 it does require some additional configuration and setup.  Database evolutions must be managed manually and the database connection needs to be created on initial startup.  Transactions must also be explicitly defined when calling the database in the controller.
22
23 Setup Squeryl in Play 2
24 -----------------------
25
26 If you don't already have a Play 2 project, then create a new one after installing Play 2:
27
28 play new mysquerylapp
29
30 Chose Scala as the language for the project.
31
32 The Squeryl library needs to be added to the Play project.  We will also add the Postgres JDBC driver as a dependency because later we will deploy this project on the cloud with Heroku and the default database on Heroku is Postgres.
33
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
34 Edit the `project/Build.scala` file and update the dependencies:
1263ebb7 »
2012-06-06 updates and article
35
36 val appDependencies = Seq(
6c612060 »
2012-06-08 fix sql
37 "org.squeryl" %% "squeryl" % "0.9.5-2",
1263ebb7 »
2012-06-06 updates and article
38 "postgresql" % "postgresql" % "9.1-901-1.jdbc4"
39 )
40
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
41
42 Then if you are using an Eclipse or IntelliJ Play can automatically create the project for you using either:
43
82d7031b »
2012-06-08 Walked through all of the code from scratch and updated the article w…
44 play idea
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
45
46 or
47
82d7031b »
2012-06-08 Walked through all of the code from scratch and updated the article w…
48 play eclipsify
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
49
50 Note that we created the project files after updating the dependencies, so the projects would be configured with the required libraries. If the dependencies change in the future, just re-run the commands to create the projects.
51
1263ebb7 »
2012-06-06 updates and article
52 You can now start the application from within your project's root directory:
53
54 play ~run
55
384e6012 »
2012-06-08 minor fixes
56 Verify that the server is running by opening the following URL in your browser:
57 [http://localhost:9000](http://localhost:9000)
1263ebb7 »
2012-06-06 updates and article
58
59 For local testing we will use an in-memory "h2" database.  To setup Play to use that database, edit the `conf/application.conf` file and uncomment or add the following lines:
60
61 db.default.driver=org.h2.Driver
62 db.default.url="jdbc:h2:mem:play"
63
64 The final setup step is to provide Squeryl a database connection but we still want to use the standard Play configuration system to get the database connection information.   This is easily done by adding a `Global` class that can hook into the startup phase of the Play application lifecycle.  Create a new file named `app/Global.scala` containing:
65
66 import org.squeryl.adapters.{H2Adapter, PostgreSqlAdapter}
67 import org.squeryl.internals.DatabaseAdapter
68 import org.squeryl.{Session, SessionFactory}
69 import play.api.db.DB
70 import play.api.GlobalSettings
71
72 import play.api.Application
73
74 object Global extends GlobalSettings {
75
76 override def onStart(app: Application) {
77 SessionFactory.concreteFactory = app.configuration.getString("db.default.driver") match {
78 case Some("org.h2.Driver") => Some(() => getSession(new H2Adapter, app))
79 case Some("org.postgresql.Driver") => Some(() => getSession(new PostgreSqlAdapter, app))
80 case _ => sys.error("Database driver must be either org.h2.Driver or org.postgresql.Driver")
81 }
82 }
83
84 def getSession(adapter:DatabaseAdapter, app: Application) = Session.create(DB.getConnection()(app), adapter)
85
86 }
87
88 On application startup the `db.default.driver` configuration parameter will be used to determine which driver to use to setup the database connection. A new connection will be made and stored in Squeryl's SessionFactory.
89
384e6012 »
2012-06-08 minor fixes
90 If you reload the [http://localhost:9000](http://localhost:9000) webpage in your browser, everything should still be working and you should see the following message in the Play STDOUT log:
1263ebb7 »
2012-06-06 updates and article
91
92 [info] play - database [default] connected at jdbc:h2:mem:play
93
94
95 Create an Entity
96 ----------------
97
98 Now lets create a simple entity object that will be used to persist data into the database. Create a new file named `app/models/Bar.scala` containing:
99
100 package models
101
102 import org.squeryl.{Schema, KeyedEntity}
103
104 case class Bar(name: Option[String]) extends KeyedEntity[Long] {
105 val id: Long = 0
106 }
107
108 object AppDB extends Schema {
109 val barTable = table[Bar]("bar")
110 }
111
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
112 This is a very simple entity that will store a list of `Bar` objects. Each `Bar` has a name and an `id` property for the primary key. The case class in Scala is immutable and basically super charges a class adding a number of syntactic conveniences.  It also allows it to be used for pattern matching which can be quite handy when matching form values returned from the client. The `AppDB` object is an instance of the `Schema` that Squeryl will map into the database. In this case we are only defining one table that will be called `bar` in the database.  Because the schema is defined as an object, it makes it a singleton instance.
1263ebb7 »
2012-06-06 updates and article
113
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
114 With Squeryl you can programatically create the database schema by calling the `AppDB.create` method. However, a better approach is to manually create the SQL scripts (what play calls evolutions scripts). Play will then to track your database schema evolutions by checking the database schema against these SQL scripts. When Play detects the schema is out of date it, it will suggest applying this SQL script. It only does this while in DEV mode, in PROD mode it applies the script before starting the application. This allows us to have full control over the changes in our database schema and version the schema changes in case a rollback needs to happen.
115
116 Create a new file named `conf/evolutions/default/1.sql` containing:
1263ebb7 »
2012-06-06 updates and article
117
118 # --- First database schema
119
120 # --- !Ups
121
6c612060 »
2012-06-08 fix sql
122 create sequence s_bar_id;
123
1263ebb7 »
2012-06-06 updates and article
124 create table bar (
6c612060 »
2012-06-08 fix sql
125 id bigint DEFAULT nextval('s_bar_id'),
126 name varchar(128)
1263ebb7 »
2012-06-06 updates and article
127 );
128
6c612060 »
2012-06-08 fix sql
129
1263ebb7 »
2012-06-06 updates and article
130 # --- !Downs
131
6c612060 »
2012-06-08 fix sql
132 drop table bar;
133 drop sequence s_bar_id;
1263ebb7 »
2012-06-06 updates and article
134
135 This simple SQL script has two sections: "Ups" and "Downs". The "Ups" section brings the database schema "up" to this version. The "Downs" section takes the database down from this version. Play will apply the database schema changes in order based on their names. If you need to change the schema after you've deployed and done an evolution to `1.sql` then you'd create a `2.sql` file containing your changes.
136
384e6012 »
2012-06-08 minor fixes
137 If you reload the [http://localhost:9000](http://localhost:9000) webpage you will now see that Play is asking you if you want to apply the database evolutions. Click the `Apply this script now!` button to run the "Ups" for `1.sql` on your local in-memory database.
1263ebb7 »
2012-06-06 updates and article
138
139 Test the Model
140 --------------
141
fe895493 »
2012-07-24 fix typos
142 The testing support in Play 2 is very powerful and fits well with the Test Driven Development style. Play 2 with Scala uses [specs2](http://etorreborre.github.com/specs2/) as the default for testing but we prefer [ScalaTest](http://scalatest.org). Lets create a simple test for the `Bar` model object. Start by adding the ScalaTest dependency to the project and modifying the `testOptions` setting. Update the `project/Build.scala` file to contain:
7281ad47 »
2012-06-19 update for ScalaTest
143
144 val appDependencies = Seq(
145 "org.scalatest" %% "scalatest" % "1.8" % "test",
146 "org.squeryl" %% "squeryl" % "0.9.5-2",
147 "postgresql" % "postgresql" % "9.1-901-1.jdbc4"
148 )
149
150 val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
151 testOptions in Test := Nil
152 // Add your own project settings here
153 )
154
155 Now create a new file named `test/BarSpec.scala` containing.
08385c0b »
2012-06-07 add tests
156
157 import models.{AppDB, Bar}
158
7281ad47 »
2012-06-19 update for ScalaTest
159 import org.scalatest.FlatSpec
160 import org.scalatest.matchers.ShouldMatchers
161
08385c0b »
2012-06-07 add tests
162 import org.squeryl.PrimitiveTypeMode.inTransaction
163
164 import play.api.test._
165 import play.api.test.Helpers._
166
7281ad47 »
2012-06-19 update for ScalaTest
167 class BarSpec extends FlatSpec with ShouldMatchers {
08385c0b »
2012-06-07 add tests
168
7281ad47 »
2012-06-19 update for ScalaTest
169 "A Bar" should "be creatable" in {
170 running(FakeApplication(additionalConfiguration = inMemoryDatabase())) {
171 inTransaction {
172 val bar = AppDB.barTable insert Bar(Some("foo"))
173 bar.id should not equal(0)
08385c0b »
2012-06-07 add tests
174 }
175 }
176 }
177
178 }
179
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
180 This test uses a `FakeApplication` with an in-memory database to run the test. By using the `FakeApplication` the Squeryl database connection will be configured using the `Global` object that was created earlier. The body of the test simply creates a new instance of `Bar` and tests that the `id` is not equal to zero. This happens in a Squeryl transaction. Different from Play 1, test are run from the command line using:
08385c0b »
2012-06-07 add tests
181
182 play test
183
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
184 If the tests worked, then you should see the following message in the Play STDOUT log:
185
744b01ac »
2012-06-08 Walked through all of the code from scratch and updated the article w…
186 [info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
187
08385c0b »
2012-06-07 add tests
188 If you'd like to have the tests run whenever the source changes then run:
189
190 play ~test
191
7281ad47 »
2012-06-19 update for ScalaTest
192 You can keep both the `~run` and `~test` commands running in the background. This allows you to quickly test the application from both programmatic unit / functional tests and from manual browser tests.
1263ebb7 »
2012-06-06 updates and article
193
194
195 Creating Bars From a Web Form
196 -----------------------------
197
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
198 Now lets add a basic web UI for creating new `Bar` objects. Note that this code will not compile until this entire section is completed.
199
200 First update the `app/controllers/Application.scala` file to contain:
1263ebb7 »
2012-06-06 updates and article
201
202 package controllers
203
204 import play.api.mvc._
205
206 import com.codahale.jerkson.Json
207 import play.api.data.Form
208 import play.api.data.Forms.{mapping, text, optional}
209
210 import org.squeryl.PrimitiveTypeMode._
211 import models.{AppDB, Bar}
212
213
214 object Application extends Controller {
215
216 val barForm = Form(
217 mapping(
218 "name" -> optional(text)
219 )(Bar.apply)(Bar.unapply)
220 )
221
222 def index = Action {
223 Ok(views.html.index(barForm))
224 }
225
226 def addBar = Action { implicit request =>
227 barForm.bindFromRequest.value map { bar =>
228 inTransaction(AppDB.barTable insert bar)
229 Redirect(routes.Application.index())
230 } getOrElse BadRequest
231 }
232
233 }
234
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
235 The `barForm` provides a mapping from a request parameter named `name` to the `name` property on the `Bar` case class (via it's constructor). The `index` method has been updated to pass an instance of the `barForm` into the `index` template. We will update that template next. The `addBar` method binds the request parameters into an object named `bar` then in a transaction the `bar` is inserted into the database. Because Squeryl is not integrated into the Play Framework, database transactions need to be explicitly started using the `inTransaction` Squeryl function. Then the user is redirected back to the index page. If the request parameters could not be mapped to a `Bar` using the `barForm` then a `BadRequest` error is returned.
1263ebb7 »
2012-06-06 updates and article
236
237 Now we need to update the `app/views/index.scala.html` template to contain:
238
239 @(form: play.api.data.Form[Bar])
240
241 @main("Welcome to Play 2.0") {
242
243 @helper.form(action = routes.Application.addBar) {
244 @helper.inputText(form("name"))
245 <input type="submit"/>
246 }
247
248 }
249
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
250 The template now takes a `Form[Bar]` parameter which is passed from the `index` method on the `Application` controller. Then in the body of the template a new HTML form is rendered using Play 2's form helper. The form contains an HTML field for the `name` and a submit button. Notice that the action of the form points to the route to the `Application` controller's `addBar` method.
251
252 If you look in the console window at this point you will see the error "value addBar is not a member of controllers.ReverseApplication". This is because the route file is compiled and the view is checked for a valid route. But we haven't created a route yet, so edit the `conf/routes` file and add a new line with the following:
1263ebb7 »
2012-06-06 updates and article
253
254 POST /bars controllers.Application.addBar
255
256 This creates a HTTP route that maps `POST` requests for the `/bars` URL to the `addBar` method.
257
384e6012 »
2012-06-08 minor fixes
258 Now refresh [http://localhost:9000](http://localhost:9000) in your browser and you should see the very basic form for adding new `Bar` objects. If successful, after adding a new `Bar` the browser should just redirect back to the index page.
1263ebb7 »
2012-06-06 updates and article
259
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
260 Now that you have it working lets take a look back at the controller code and get a better understanding of how everything works. To understand what the `addBar` method is doing it is helpful to first understand how the `implicit` keyword informs the compiler where to find the value from the surrounding scope. In Scala the `implicit` keyword can be used either as an `implicit` function parameter or an `implicit` object conversion. The two are quite different but both relate to how Scala resolves the definition. In this case, `implicit` is used when you call one or more functions and need to pass the same value to all functions. This strategy is useful in constructing APIs so that users do not have to always be explicit about what parameters are used, but rely on default values instead.
1263ebb7 »
2012-06-06 updates and article
261
262 In the case of `addBar` we specify request to be `implicit` because the `barForm.bindFromRequest` method takes a` play.api.mvc.Request` parameter that we no longer need to pass explicitly. For reference, here is the method definition for the `Form.bindToRequest` method:
263
264 def bindFromRequest()(implicit request: play.api.mvc.Request[_]): Form[T] = {...}
265
266 The `bindFromRequest` returns a `Form` object. In the `addBar` method we call the `value` method on that `Form` instance which returns an `Option[Bar]` in this case. Then calling `map` gets the `Bar` if it could be created from the form mapping, otherwise the `getOrElse` statement returns the `BadRequest` error. When the `Bar` object can be created it is saved to the database in a transaction. <todo: explain the squeryl insert>
267
268 Now that you have a good understanding of how to map request parameters to objects and save those objects with Squeryl lets write a test for the new `addBar` controller method.
269
270
271 Test Adding Bars
272 ----------------
273
08385c0b »
2012-06-07 add tests
274 Create a new test for the `addBar` controller method by creating a new file named `test/ApplicationSpec.scala` containing:
275
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
276 import controllers.routes
08385c0b »
2012-06-07 add tests
277 import models.{AppDB, Bar}
278
7281ad47 »
2012-06-19 update for ScalaTest
279 import org.scalatest.FlatSpec
280 import org.scalatest.matchers.ShouldMatchers
281
08385c0b »
2012-06-07 add tests
282 import org.squeryl.PrimitiveTypeMode.inTransaction
283
7281ad47 »
2012-06-19 update for ScalaTest
284 import play.api.http.ContentTypes.JSON
08385c0b »
2012-06-07 add tests
285 import play.api.test._
286 import play.api.test.Helpers._
287
7281ad47 »
2012-06-19 update for ScalaTest
288 class ApplicationSpec extends FlatSpec with ShouldMatchers {
08385c0b »
2012-06-07 add tests
289
7281ad47 »
2012-06-19 update for ScalaTest
290 "A request to the addBar action" should "respond" in {
08385c0b »
2012-06-07 add tests
291 running(FakeApplication(additionalConfiguration = inMemoryDatabase())) {
292 val result = controllers.Application.addBar(FakeRequest().withFormUrlEncodedBody("name" -> "FooBar"))
7281ad47 »
2012-06-19 update for ScalaTest
293 status(result) should equal (SEE_OTHER)
294 redirectLocation(result) should equal (Some(routes.Application.index.url))
08385c0b »
2012-06-07 add tests
295 }
296 }
7281ad47 »
2012-06-19 update for ScalaTest
297
08385c0b »
2012-06-07 add tests
298 }
1263ebb7 »
2012-06-06 updates and article
299
dbc9bec5 »
2012-06-25 minor clarification to section "Test Adding Bars"
300 This functional test uses a `FakeApplication` with an in-memory database. The test makes a request to the `addBar` method on the `Application` controller with a form parameter named `name` and a value of `FooBar`. Since success in this method is simply a redirect to the `index` page the status is checked to be `SEE_OTHER` (HTTP 303 status code) and the redirect location is checked to be the URL of the `index` page. Run this test with either `play test` or `play ~test` if you'd like to keep running tests when your code changes.
1263ebb7 »
2012-06-06 updates and article
301
302
303 Get Bars as JSON
304 ----------------
305
306 Now lets add a RESTful service to the application that will return all of the `Bar` objects as JSON serialized data. Start by adding a new method to the `app/controllers/Application.scala` file:
307
308 def getBars = Action {
309 val json = inTransaction {
310 val bars = from(AppDB.barTable)(barTable =>
311 select(barTable)
312 )
313 Json.generate(bars)
314 }
315 Ok(json).as(JSON)
316 }
317
318 The `getBars` method fetches the `Bar` objects from the database using Squeryl and then creates a JSON representation of the list of `Bar` objects and returns the JSON data.
319
320 Now add a new route to the `conf/routes` file:
321
322 GET /bars controllers.Application.getBars
323
324 This maps `GET` requests for `/bars` to the `getBars` method.
325
326 Try this out in your browser by loading:
384e6012 »
2012-06-08 minor fixes
327 [http://localhost:9000/bars](http://localhost:9000/bars)
1263ebb7 »
2012-06-06 updates and article
328
329 You should see a list of the `Bar` objects you've created serialized as JSON.
330
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
331 As mentioned previously, the transaction needs to be explicityl started with the `inTransaction` Squeryl function, even when selecting values from the database. Then within the bounds of the transaction, all of the `Bar` entities are retrieved from the database.
1263ebb7 »
2012-06-06 updates and article
332
36f995f8 »
2012-06-25 minor clarification to section "Test Adding Bars"
333 The syntax of the query demonstrates the power of Squeryl’s type-safe query language and the power of Scala to create DSLs.  The `from` function takes the type-safe reference to the table as the first parameter. This is like the SQL `from` keyword. The second parameter is a function that takes the table to query as a parameter and then specifies what to do on that table, in this case a `select`. The `from` returns an iterable object which is set to the `bars` immutable val. Then the `Json.generate` method iterates through the `bars` retrieved from the database and returns them. The `json` val is then returned in a `Ok` (HTTP 200 status code response) with the content type set to `application/json` (the value of `JSON`).
1263ebb7 »
2012-06-06 updates and article
334
335
336 Test JSON Service
337 -----------------
338
08385c0b »
2012-06-07 add tests
339 Now lets update the `test/ApplicationSpec.scala` test to have a new test for the JSON service. Add the following:
340
7281ad47 »
2012-06-19 update for ScalaTest
341 "A request to the getBars Action" should "respond with data" in {
08385c0b »
2012-06-07 add tests
342 running(FakeApplication(additionalConfiguration = inMemoryDatabase())) {
343 inTransaction(AppDB.barTable insert Bar(Some("foo")))
7281ad47 »
2012-06-19 update for ScalaTest
344
08385c0b »
2012-06-07 add tests
345 val result = controllers.Application.getBars(FakeRequest())
7281ad47 »
2012-06-19 update for ScalaTest
346 status(result) should equal (OK)
347 contentAsString(result) should include ("foo")
08385c0b »
2012-06-07 add tests
348 }
349 }
350
351 Again this functional test uses a `FakeApplication` and an in-memory database. It then creates a new `Bar` in the database and makes a request to the `getBars` method on the `Application` controller. The response is tested to be `OK` (HTTP 200 status code) and to contain the name of the `Bar` that was created. Like before run this test either `play test` or `play ~test` and you should now have three tests that pass.
1263ebb7 »
2012-06-06 updates and article
352
353
354 Display the Bars with CoffeeScript and jQuery
355 ---------------------------------------------
356
821d0fd6 »
2012-06-07 doc updates
357 Now that we have a RESTful JSON service to get the list of `Bar` objects, lets use some CoffeeScript and jQuery to fetch and display them on the `index` page. One of the new features in Play 2 is the asset compiler that can compile CoffeeScript to JavaScript, syntax check JavaScript, minify JavaScript, and compile LESS to CSS.
358
359 Create a new file named `app/assets/javascripts/index.coffee` that contains:
360
361 $ ->
362 $.get "/bars", (data) ->
363 $.each data, (index, item) ->
364 $("#bars").append $("<li>").text item.name
365
1009e6b7 »
2012-06-08 Walked through all of the code from scratch and updated the article w…
366 This CoffeeScript uses jQuery to make a `get` request to `/bars` and then iterates through each `bar` and adds it to the element on the page with an id of `bars`. Now lets update the `app/views/index.scala.html` template to load this script and provide the `bars` element on the page. Add the follow into the top part of the `main` section of the template :
821d0fd6 »
2012-06-07 doc updates
367
368 <script src="@routes.Assets.at("javascripts/index.min.js")" type="text/javascript"></script>
369 <ul id="bars"></ul>
370
384e6012 »
2012-06-08 minor fixes
371 Notice that `src` of the script uses the `routes.Assets.at` function to get the URL to the `javascripts/index.min.js` file. Yet, that file doesn't exist. Play's asset compiler knows that it needs to create that minified file from compiling the `index.coffee` file. Load the [http://localhost:9000](http://localhost:9000) webpage again, create a new `Bar` and you should see it displayed on the webpage.
1263ebb7 »
2012-06-06 updates and article
372
373
374 Deploy on Heroku
375 ----------------
376
85ac0f3f »
2012-06-07 add Heroku section
377 Heroku is a Polyglot Cloud Application Platform that provides a place to run Play 2 apps on the cloud. To deploy this application on Heroku, follow these steps:
378
74323e41 »
2012-06-07 fix formatting
379 1. First create a new file in the root directory named `Procfile` that contains:
85ac0f3f »
2012-06-07 add Heroku section
380
384e6012 »
2012-06-08 minor fixes
381 web: target/start -Dhttp.port=${PORT} -DapplyEvolutions.default=true -Ddb.default.driver=org.postgresql.Driver -Ddb.default.url=${DATABASE_URL} ${JAVA_OPTS}
85ac0f3f »
2012-06-07 add Heroku section
382
383 That tells Heroku how to start the Play application.
384
74323e41 »
2012-06-07 fix formatting
385 2. Heroku uses Git as the way to transfer files to Heroku. [Install Git](http://git-scm.com/download You will need to create a new git) if you don't already have it. Then from the root directory of your project create a new Git repository for this project, add your files and commit them:
85ac0f3f »
2012-06-07 add Heroku section
386
387 git init
388 git add .
389 git commit -m init
390
821d0fd6 »
2012-06-07 doc updates
391 3. The Heroku Toolbelt is a command line interface to Heroku. [Install the Heroku Toolbelt](http://toolbelt.heroku.com).
85ac0f3f »
2012-06-07 add Heroku section
392
821d0fd6 »
2012-06-07 doc updates
393 4. [Signup for a Heroku account](http://heroku.com/signup).
85ac0f3f »
2012-06-07 add Heroku section
394
74323e41 »
2012-06-07 fix formatting
395 5. Login to Heroku from the command line:
85ac0f3f »
2012-06-07 add Heroku section
396
397 heroku login
398
399 This will walk you through setting up an SSH key for Git and associating the key with your Heroku account.
400
74323e41 »
2012-06-07 fix formatting
401 6. Provision a new application on Heroku:
85ac0f3f »
2012-06-07 add Heroku section
402
403 heroku create --stack cedar
404
74323e41 »
2012-06-07 fix formatting
405 7. Now push this applicaiton to Heroku:
85ac0f3f »
2012-06-07 add Heroku section
406
407 git push heroku master
408
409 Heroku will build the app with SBT and then run it on a [dyno](https://devcenter.heroku.com/articles/dynos).
410
74323e41 »
2012-06-07 fix formatting
411 8. Open the application, now running on the cloud, in your browser:
85ac0f3f »
2012-06-07 add Heroku section
412
413 heroku open
414
415 Congrats! Your Play 2 app is now running on the cloud!
1263ebb7 »
2012-06-06 updates and article
416
417
418 Further Learning
419 ----------------
420
821d0fd6 »
2012-06-07 doc updates
421 All of the source code for this project can be found on GitHub:
422 [https://github.com/jamesward/play2bars/blob/scala-squeryl](https://github.com/jamesward/play2bars/blob/scala-squeryl)
423
424 When you local Play web server is running you can access Play's local documentation at:
425 [http://localhost:9000/@documentation](http://localhost:9000/@documentation)
426
ca77236f »
2012-06-07 fix formatting
427 You can also find the Play documentation at:
821d0fd6 »
2012-06-07 doc updates
428 [http://www.playframework.org/documentation](http://www.playframework.org/documentation)
429
ca77236f »
2012-06-07 fix formatting
430 To learn more about Heroku, visit the Heroku Dev Center:
821d0fd6 »
2012-06-07 doc updates
431 [http://devcenter.heroku.com](http://devcenter.heroku.com)
432
fe895493 »
2012-07-24 fix typos
433 We hope this was helpful, please let us know if you have any questions or problems.
Something went wrong with that request. Please try again.