├── chapter06
├── data
│ ├── .keep
│ ├── manual.pdf
│ ├── strategy.jpg
│ └── multipart-message.data
├── project
│ ├── build.properties
│ ├── plugins.sbt
│ └── build.scala
├── .gitignore
└── src
│ ├── main
│ ├── webapp
│ │ └── WEB-INF
│ │ │ ├── layouts
│ │ │ └── default.scaml
│ │ │ ├── web.xml
│ │ │ └── views
│ │ │ └── index.scaml
│ ├── scala
│ │ ├── ScalatraBootstrap.scala
│ │ └── org
│ │ │ └── scalatra
│ │ │ └── book
│ │ │ └── chapter06
│ │ │ └── store.scala
│ └── resources
│ │ └── logback.xml
│ └── test
│ └── scala
│ └── org
│ └── scalatra
│ └── book
│ └── chapter06
│ └── FilesSpec.scala
├── chapter13
├── src
│ ├── main
│ │ ├── webapp
│ │ │ ├── img
│ │ │ │ └── .gitkeep
│ │ │ ├── WEB-INF
│ │ │ │ ├── templates
│ │ │ │ │ └── views
│ │ │ │ │ │ ├── hackers
│ │ │ │ │ │ ├── index.ssp
│ │ │ │ │ │ ├── show.ssp
│ │ │ │ │ │ └── new.ssp
│ │ │ │ │ │ └── sessions
│ │ │ │ │ │ └── new.ssp
│ │ │ │ └── web.xml
│ │ │ └── js
│ │ │ │ └── foundation
│ │ │ │ └── foundation.alerts.js
│ │ ├── scala
│ │ │ ├── com
│ │ │ │ └── constructiveproof
│ │ │ │ │ └── hackertracker
│ │ │ │ │ ├── HackersSwagger.scala
│ │ │ │ │ ├── stacks
│ │ │ │ │ ├── HackerCoreStack.scala
│ │ │ │ │ ├── ApiStack.scala
│ │ │ │ │ └── BrowserStack.scala
│ │ │ │ │ ├── auth
│ │ │ │ │ ├── utils
│ │ │ │ │ │ └── HmacUtils.scala
│ │ │ │ │ ├── strategies
│ │ │ │ │ │ └── OurBasicAuthStrategy.scala
│ │ │ │ │ ├── AuthenticationSupport.scala
│ │ │ │ │ └── OurBasicAuthenticationSupport.scala
│ │ │ │ │ ├── init
│ │ │ │ │ ├── DatabaseSessionSupport.scala
│ │ │ │ │ └── DatabaseInit.scala
│ │ │ │ │ ├── DatabaseSetupController.scala
│ │ │ │ │ ├── SessionsController.scala
│ │ │ │ │ ├── HackersController.scala
│ │ │ │ │ └── models
│ │ │ │ │ └── Models.scala
│ │ │ └── ScalatraBootstrap.scala
│ │ └── resources
│ │ │ └── logback.xml
│ └── test
│ │ └── scala
│ │ └── com
│ │ └── constructiveproof
│ │ └── hackertracker
│ │ └── HackersControllerSpec.scala
├── project
│ ├── build.properties
│ └── plugins.sbt
├── README.md
└── .gitignore
├── chapter01
├── project
│ ├── build.properties
│ └── plugins.sbt
├── src
│ ├── main
│ │ ├── webapp
│ │ │ └── WEB-INF
│ │ │ │ ├── templates
│ │ │ │ ├── views
│ │ │ │ │ └── hello-scalate.jade
│ │ │ │ └── layouts
│ │ │ │ │ └── default.jade
│ │ │ │ └── web.xml
│ │ ├── scala
│ │ │ ├── ScalatraBootstrap.scala
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── app
│ │ │ │ ├── MyScalatraServlet.scala
│ │ │ │ └── MyScalatraWebAppStack.scala
│ │ └── resources
│ │ │ └── logback.xml
│ └── test
│ │ └── scala
│ │ └── com
│ │ └── example
│ │ └── app
│ │ └── MyScalatraServletSpec.scala
└── README.md
├── chapter03
├── project
│ ├── build.properties
│ ├── plugins.sbt
│ └── build.scala
├── src
│ ├── main
│ │ ├── webapp
│ │ │ └── WEB-INF
│ │ │ │ ├── views
│ │ │ │ └── hello-scalate.scaml
│ │ │ │ ├── layouts
│ │ │ │ └── default.scaml
│ │ │ │ └── web.xml
│ │ ├── scala
│ │ │ ├── org
│ │ │ │ └── scalatra
│ │ │ │ │ └── book
│ │ │ │ │ └── chapter03
│ │ │ │ │ ├── Album.scala
│ │ │ │ │ └── Artist.scala
│ │ │ └── ScalatraBootstrap.scala
│ │ └── resources
│ │ │ └── logback.xml
│ └── test
│ │ └── scala
│ │ └── org
│ │ └── scalatra
│ │ └── book
│ │ └── chapter03
│ │ └── RecordStoreSpec.scala
└── README.md
├── chapter05
├── project
│ ├── build.properties
│ ├── plugins.sbt
│ └── build.scala
├── .gitignore
├── README.md
└── src
│ ├── main
│ ├── scala
│ │ ├── ScalatraBootstrap.scala
│ │ └── org
│ │ │ └── scalatra
│ │ │ └── book
│ │ │ └── chapter06
│ │ │ ├── MyJsonpRoutes.scala
│ │ │ ├── MyJsonApp.scala
│ │ │ ├── recipes.scala
│ │ │ ├── MyFoodRoutes.scala
│ │ │ ├── json4s_basics.scala
│ │ │ ├── MyJsonScalazRoutes.scala
│ │ │ ├── MyJsonRoutes.scala
│ │ │ └── json4s_custom_serializers.scala
│ ├── resources
│ │ └── logback.xml
│ └── webapp
│ │ └── WEB-INF
│ │ └── web.xml
│ └── test
│ └── scala
│ └── org
│ └── scalatra
│ └── book
│ └── chapter06
│ └── MyJsonAppSpec.scala
├── chapter07
├── project
│ ├── build.properties
│ ├── plugins.sbt
│ └── build.scala
├── src
│ └── main
│ │ ├── webapp
│ │ └── WEB-INF
│ │ │ ├── layouts
│ │ │ └── default.scaml
│ │ │ ├── views
│ │ │ ├── greeter_dry.scaml
│ │ │ └── greeter.scaml
│ │ │ └── web.xml
│ │ ├── scala
│ │ ├── ScalatraBootstrap.scala
│ │ └── org
│ │ │ └── scalatra
│ │ │ └── book
│ │ │ └── chapter07
│ │ │ ├── GreeterServlet.scala
│ │ │ └── MyScalatraWebappStack.scala
│ │ └── resources
│ │ └── logback.xml
└── README.md
├── chapter08
├── project
│ ├── build.properties
│ ├── plugins.sbt
│ └── build.scala
├── README.md
└── src
│ ├── main
│ ├── scala
│ │ ├── org
│ │ │ └── scalatra
│ │ │ │ └── book
│ │ │ │ └── chapter08
│ │ │ │ ├── MyScalatraServlet.scala
│ │ │ │ ├── FoodServlet.scala
│ │ │ │ └── NukeLauncherServlet.scala
│ │ └── ScalatraBootstrap.scala
│ ├── resources
│ │ └── logback.xml
│ └── webapp
│ │ └── WEB-INF
│ │ └── web.xml
│ └── test
│ └── scala
│ └── org
│ └── scalatra
│ └── book
│ └── chapter08
│ ├── JsonBodySupport.scala
│ ├── MyScalatraServletWordSpec.scala
│ ├── MyScalatraServletSpec.scala
│ ├── FoodServletWordSpec.scala
│ ├── FoodServletSpec.scala
│ └── NukeLauncherSpec.scala
├── chapter09
├── project
│ ├── build.properties
│ └── plugins.sbt
├── src
│ ├── main
│ │ ├── webapp
│ │ │ ├── static.txt
│ │ │ └── WEB-INF
│ │ │ │ ├── templates
│ │ │ │ └── views
│ │ │ │ │ └── hello-scalate.jade
│ │ │ │ └── web.xml
│ │ ├── resources
│ │ │ ├── application.conf
│ │ │ └── logback.xml
│ │ └── scala
│ │ │ ├── org
│ │ │ └── scalatra
│ │ │ │ └── book
│ │ │ │ └── chapter09
│ │ │ │ ├── UrlShortener.scala
│ │ │ │ ├── Chapter09.scala
│ │ │ │ └── AppConfig.scala
│ │ │ └── ScalatraBootstrap.scala
│ └── test
│ │ └── scala
│ │ └── org
│ │ └── scalatra
│ │ └── book
│ │ └── chapter09
│ │ └── Chapter09Spec.scala
└── .gitignore
├── chapter02
├── project
│ ├── build.properties
│ └── plugins.sbt
├── src
│ ├── main
│ │ ├── webapp
│ │ │ ├── img
│ │ │ │ ├── glyphicons-halflings.png
│ │ │ │ └── glyphicons-halflings-white.png
│ │ │ └── WEB-INF
│ │ │ │ ├── templates
│ │ │ │ ├── views
│ │ │ │ │ └── pages
│ │ │ │ │ │ └── show.ssp
│ │ │ │ └── layouts
│ │ │ │ │ └── default.ssp
│ │ │ │ └── web.xml
│ │ ├── scala
│ │ │ ├── ScalatraBootstrap.scala
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── cms
│ │ │ │ └── ScalatraCmsStack.scala
│ │ └── resources
│ │ │ └── logback.xml
│ └── test
│ │ └── scala
│ │ └── com
│ │ └── example
│ │ └── cms
│ │ └── PagesControllerSpec.scala
├── README.md
└── .gitignore
├── chapter04
├── project
│ ├── build.properties
│ └── plugins.sbt
├── src
│ ├── main
│ │ ├── webapp
│ │ │ └── WEB-INF
│ │ │ │ ├── templates
│ │ │ │ ├── views
│ │ │ │ │ └── hello-scalate.jade
│ │ │ │ └── layouts
│ │ │ │ │ └── default.jade
│ │ │ │ └── web.xml
│ │ ├── scala
│ │ │ ├── ScalatraBootstrap.scala
│ │ │ └── com
│ │ │ │ └── constructiveproof
│ │ │ │ └── hackertracker
│ │ │ │ ├── CookiesExample.scala
│ │ │ │ ├── GateController.scala
│ │ │ │ └── HackerTrackerStack.scala
│ │ └── resources
│ │ │ └── logback.xml
│ └── test
│ │ └── scala
│ │ └── com
│ │ └── constructiveproof
│ │ └── hackertracker
│ │ └── HackersControllerSpec.scala
├── README.md
└── .gitignore
├── chapter09-sbt
├── project
│ ├── build.properties
│ └── plugins.sbt
├── src
│ ├── main
│ │ ├── webapp
│ │ │ ├── static.txt
│ │ │ └── WEB-INF
│ │ │ │ └── web.xml
│ │ ├── resources
│ │ │ ├── application.conf
│ │ │ └── logback.xml
│ │ └── scala
│ │ │ ├── org
│ │ │ └── scalatra
│ │ │ │ └── book
│ │ │ │ └── chapter09
│ │ │ │ ├── UrlShortener.scala
│ │ │ │ ├── Chapter09.scala
│ │ │ │ └── AppConfig.scala
│ │ │ └── ScalatraBootstrap.scala
│ └── test
│ │ └── scala
│ │ └── org
│ │ └── scalatra
│ │ └── book
│ │ └── chapter09
│ │ └── Chapter09Spec.scala
├── build.sbt
└── .gitignore
├── chapter10
├── project
│ ├── build.properties
│ ├── plugins.sbt
│ └── build.scala
├── .gitignore
└── src
│ ├── main
│ ├── webapp
│ │ └── WEB-INF
│ │ │ ├── views
│ │ │ ├── areas.jade
│ │ │ ├── area.jade
│ │ │ └── routes.jade
│ │ │ ├── layouts
│ │ │ └── default.jade
│ │ │ └── web.xml
│ ├── scala
│ │ ├── org
│ │ │ └── scalatra
│ │ │ │ └── book
│ │ │ │ └── chapter10
│ │ │ │ ├── domain.scala
│ │ │ │ └── scalaz.scala
│ │ └── ScalatraBootstrap.scala
│ └── resources
│ │ └── logback.xml
│ └── test
│ └── scala
│ └── org
│ └── scalatra
│ └── book
│ └── chapter10
│ └── AppSpec.scala
├── chapter12
├── project
│ ├── build.properties
│ └── plugins.sbt
├── src
│ ├── main
│ │ ├── webapp
│ │ │ └── WEB-INF
│ │ │ │ ├── templates
│ │ │ │ ├── views
│ │ │ │ │ └── hello-scalate.jade
│ │ │ │ └── layouts
│ │ │ │ │ └── default.jade
│ │ │ │ └── web.xml
│ │ ├── resources
│ │ │ └── logback.xml
│ │ └── scala
│ │ │ ├── com
│ │ │ └── constructiveproof
│ │ │ │ └── crawler
│ │ │ │ ├── AkkaCrawler.scala
│ │ │ │ ├── CrawlerStack.scala
│ │ │ │ ├── actors
│ │ │ │ └── GrabActor.scala
│ │ │ │ ├── SparkExampleController.scala
│ │ │ │ └── CrawlController.scala
│ │ │ └── ScalatraBootstrap.scala
│ └── test
│ │ └── scala
│ │ └── com
│ │ └── constructiveproof
│ │ └── crawler
│ │ └── AkkaCrawlerSpec.scala
├── README.md
└── .gitignore
├── chapter07-twirl
├── project
│ ├── build.properties
│ ├── plugins.sbt
│ └── build.scala
├── README.md
└── src
│ ├── main
│ ├── scala
│ │ ├── ScalatraBootstrap.scala
│ │ └── org
│ │ │ └── scalatra
│ │ │ └── book
│ │ │ └── chapter07
│ │ │ └── GreeterServlet.scala
│ ├── resources
│ │ └── logback.xml
│ ├── twirl
│ │ └── greeting.scala.html
│ └── webapp
│ │ └── WEB-INF
│ │ └── web.xml
│ └── test
│ └── scala
│ └── org
│ └── scalatra
│ └── book
│ └── chapter07
│ └── GreeterServletSpec.scala
├── chapter09-docker
├── src
│ ├── main
│ │ ├── webapp
│ │ │ ├── static.txt
│ │ │ └── WEB-INF
│ │ │ │ ├── templates
│ │ │ │ └── views
│ │ │ │ │ └── hello-scalate.jade
│ │ │ │ └── web.xml
│ │ ├── scala
│ │ │ ├── org
│ │ │ │ └── scalatra
│ │ │ │ │ └── book
│ │ │ │ │ └── chapter09
│ │ │ │ │ ├── UrlShortener.scala
│ │ │ │ │ └── Chapter09.scala
│ │ │ ├── ScalatraBootstrap.scala
│ │ │ └── ScalatraLauncher.scala
│ │ └── resources
│ │ │ ├── application.conf
│ │ │ └── logback.xml
│ └── test
│ │ └── scala
│ │ └── org
│ │ └── scalatra
│ │ └── book
│ │ └── chapter09
│ │ └── Chapter09Spec.scala
├── project
│ ├── build.properties
│ └── plugins.sbt
├── stop.sh
├── README.md
├── start.sh
├── conf
│ ├── application.conf
│ └── logback.xml
└── .gitignore
├── chapter09-sbtweb
├── src
│ ├── main
│ │ ├── public
│ │ │ ├── static.txt
│ │ │ ├── WEB-INF
│ │ │ │ ├── templates
│ │ │ │ │ └── views
│ │ │ │ │ │ └── hello-scalate.jade
│ │ │ │ └── web.xml
│ │ │ └── index.html
│ │ ├── assets
│ │ │ └── css
│ │ │ │ ├── bars
│ │ │ │ └── bar.less
│ │ │ │ └── main.less
│ │ ├── resources
│ │ │ ├── application.conf
│ │ │ └── logback.xml
│ │ └── scala
│ │ │ ├── org
│ │ │ └── scalatra
│ │ │ │ └── book
│ │ │ │ └── chapter09
│ │ │ │ ├── UrlShortener.scala
│ │ │ │ ├── Chapter09.scala
│ │ │ │ └── AppConfig.scala
│ │ │ └── ScalatraBootstrap.scala
│ └── test
│ │ └── scala
│ │ └── org
│ │ └── scalatra
│ │ └── book
│ │ └── chapter09
│ │ └── Chapter09Spec.scala
├── project
│ ├── build.properties
│ └── plugins.sbt
├── README.md
└── .gitignore
├── chapter09-standalone
├── project
│ ├── build.properties
│ └── plugins.sbt
├── src
│ ├── main
│ │ ├── webapp
│ │ │ ├── static.txt
│ │ │ └── WEB-INF
│ │ │ │ ├── templates
│ │ │ │ └── views
│ │ │ │ │ └── hello-scalate.jade
│ │ │ │ └── web.xml
│ │ ├── scala
│ │ │ ├── org
│ │ │ │ └── scalatra
│ │ │ │ │ └── book
│ │ │ │ │ └── chapter09
│ │ │ │ │ ├── UrlShortener.scala
│ │ │ │ │ └── Chapter09.scala
│ │ │ ├── ScalatraBootstrap.scala
│ │ │ └── ScalatraLauncher.scala
│ │ └── resources
│ │ │ ├── application.conf
│ │ │ ├── logback.xml
│ │ │ └── logback.production.xml
│ └── test
│ │ └── scala
│ │ └── org
│ │ └── scalatra
│ │ └── book
│ │ └── chapter09
│ │ └── Chapter09Spec.scala
├── README.md
└── .gitignore
├── comments-collector
├── project
│ ├── build.properties
│ └── plugins.sbt
├── src
│ ├── main
│ │ ├── webapp
│ │ │ ├── css
│ │ │ │ └── site.css
│ │ │ └── WEB-INF
│ │ │ │ ├── web.xml
│ │ │ │ ├── views
│ │ │ │ ├── comments.scaml
│ │ │ │ └── index.scaml
│ │ │ │ └── layouts
│ │ │ │ └── default.scaml
│ │ ├── scala
│ │ │ ├── comments
│ │ │ │ ├── scalaz.scala
│ │ │ │ ├── data.scala
│ │ │ │ └── frontend.scala
│ │ │ └── ScalatraBootstrap.scala
│ │ └── resources
│ │ │ └── logback.xml
│ └── test
│ │ └── scala
│ │ └── comments
│ │ └── CommentsServletSpec.scala
├── README.md
└── .gitignore
├── chapter11
├── 1-hacker-tracker-unprotected
│ ├── src
│ │ ├── main
│ │ │ ├── webapp
│ │ │ │ ├── img
│ │ │ │ │ └── .gitkeep
│ │ │ │ ├── WEB-INF
│ │ │ │ │ ├── templates
│ │ │ │ │ │ └── views
│ │ │ │ │ │ │ └── hackers
│ │ │ │ │ │ │ ├── index.ssp
│ │ │ │ │ │ │ ├── show.ssp
│ │ │ │ │ │ │ └── new.ssp
│ │ │ │ │ └── web.xml
│ │ │ │ └── js
│ │ │ │ │ └── foundation
│ │ │ │ │ └── foundation.alerts.js
│ │ │ ├── resources
│ │ │ │ └── logback.xml
│ │ │ └── scala
│ │ │ │ ├── ScalatraBootstrap.scala
│ │ │ │ └── com
│ │ │ │ └── constructiveproof
│ │ │ │ └── hackertracker
│ │ │ │ ├── init
│ │ │ │ ├── DatabaseSessionSupport.scala
│ │ │ │ └── DatabaseInit.scala
│ │ │ │ ├── DatabaseSetupController.scala
│ │ │ │ ├── HackerTrackerStack.scala
│ │ │ │ ├── HackersController.scala
│ │ │ │ └── models
│ │ │ │ └── Models.scala
│ │ └── test
│ │ │ └── scala
│ │ │ └── com
│ │ │ └── constructiveproof
│ │ │ └── hackertracker
│ │ │ └── HackersControllerSpec.scala
│ ├── project
│ │ ├── build.properties
│ │ └── plugins.sbt
│ ├── README.md
│ └── .gitignore
└── 2-hacker-tracker-protected
│ ├── src
│ ├── main
│ │ ├── webapp
│ │ │ ├── img
│ │ │ │ └── .gitkeep
│ │ │ ├── WEB-INF
│ │ │ │ ├── templates
│ │ │ │ │ └── views
│ │ │ │ │ │ ├── hackers
│ │ │ │ │ │ ├── index.ssp
│ │ │ │ │ │ ├── show.ssp
│ │ │ │ │ │ └── new.ssp
│ │ │ │ │ │ └── sessions
│ │ │ │ │ │ └── new.ssp
│ │ │ │ └── web.xml
│ │ │ └── js
│ │ │ │ └── foundation
│ │ │ │ └── foundation.alerts.js
│ │ ├── resources
│ │ │ └── logback.xml
│ │ └── scala
│ │ │ ├── ScalatraBootstrap.scala
│ │ │ └── com
│ │ │ └── constructiveproof
│ │ │ └── hackertracker
│ │ │ ├── init
│ │ │ ├── DatabaseSessionSupport.scala
│ │ │ └── DatabaseInit.scala
│ │ │ ├── auth
│ │ │ ├── strategies
│ │ │ │ └── OurBasicAuthStrategy.scala
│ │ │ ├── AuthenticationSupport.scala
│ │ │ └── OurBasicAuthenticationSupport.scala
│ │ │ ├── HackerTrackerStack.scala
│ │ │ ├── DatabaseSetupController.scala
│ │ │ ├── SessionsController.scala
│ │ │ └── models
│ │ │ └── Models.scala
│ └── test
│ │ └── scala
│ │ └── com
│ │ └── constructiveproof
│ │ └── hackertracker
│ │ └── HackersControllerSpec.scala
│ ├── project
│ ├── build.properties
│ └── plugins.sbt
│ ├── README.md
│ └── .gitignore
├── .gitignore
└── README.md
/chapter06/data/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/chapter13/src/main/webapp/img/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/chapter01/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
--------------------------------------------------------------------------------
/chapter03/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
--------------------------------------------------------------------------------
/chapter05/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
--------------------------------------------------------------------------------
/chapter06/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
--------------------------------------------------------------------------------
/chapter07/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
--------------------------------------------------------------------------------
/chapter08/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
--------------------------------------------------------------------------------
/chapter09/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
--------------------------------------------------------------------------------
/chapter02/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/chapter04/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/chapter09-sbt/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
--------------------------------------------------------------------------------
/chapter09/src/main/webapp/static.txt:
--------------------------------------------------------------------------------
1 | this is static text!
--------------------------------------------------------------------------------
/chapter10/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/chapter12/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/chapter13/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/chapter07-twirl/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/chapter09-docker/src/main/webapp/static.txt:
--------------------------------------------------------------------------------
1 | this is static text!
--------------------------------------------------------------------------------
/chapter09-sbt/src/main/webapp/static.txt:
--------------------------------------------------------------------------------
1 | this is static text!
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/public/static.txt:
--------------------------------------------------------------------------------
1 | this is static text!
--------------------------------------------------------------------------------
/chapter09-docker/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/chapter09-sbtweb/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/chapter09-standalone/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/chapter09-standalone/src/main/webapp/static.txt:
--------------------------------------------------------------------------------
1 | this is static text!
--------------------------------------------------------------------------------
/comments-collector/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/webapp/img/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/webapp/img/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/chapter05/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | target/
3 | /.lib/
4 | .idea
5 | .idea_modules
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
--------------------------------------------------------------------------------
/chapter05/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/assets/css/bars/bar.less:
--------------------------------------------------------------------------------
1 | .fnord {
2 | font-size: 42pt;
3 | }
--------------------------------------------------------------------------------
/chapter09/src/main/webapp/WEB-INF/templates/views/hello-scalate.jade:
--------------------------------------------------------------------------------
1 | p= "Hello, Scalate!"
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.8
2 |
--------------------------------------------------------------------------------
/chapter06/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | project/project
3 | project/target
4 |
5 | .idea
6 |
7 |
--------------------------------------------------------------------------------
/chapter08/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
2 |
--------------------------------------------------------------------------------
/chapter10/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | project/project
3 | project/target
4 |
5 | .idea
6 |
7 |
--------------------------------------------------------------------------------
/chapter10/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
2 |
--------------------------------------------------------------------------------
/chapter09-docker/src/main/webapp/WEB-INF/templates/views/hello-scalate.jade:
--------------------------------------------------------------------------------
1 | p= "Hello, Scalate!"
--------------------------------------------------------------------------------
/chapter09-standalone/src/main/webapp/WEB-INF/templates/views/hello-scalate.jade:
--------------------------------------------------------------------------------
1 | p= "Hello, Scalate!"
--------------------------------------------------------------------------------
/chapter06/data/manual.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scalatra/scalatra-in-action/HEAD/chapter06/data/manual.pdf
--------------------------------------------------------------------------------
/chapter06/data/strategy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scalatra/scalatra-in-action/HEAD/chapter06/data/strategy.jpg
--------------------------------------------------------------------------------
/chapter09-docker/stop.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | docker stop chapter09-standalone
4 |
5 | docker rm chapter09-standalone
6 |
7 |
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/assets/css/main.less:
--------------------------------------------------------------------------------
1 | @import "bars/bar";
2 |
3 | .foo {
4 | .bar {
5 | color: blue;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/chapter07-twirl/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
2 |
3 | addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.0.4")
4 |
--------------------------------------------------------------------------------
/chapter02/src/main/webapp/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scalatra/scalatra-in-action/HEAD/chapter02/src/main/webapp/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/chapter01/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
--------------------------------------------------------------------------------
/chapter02/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
--------------------------------------------------------------------------------
/chapter03/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
--------------------------------------------------------------------------------
/chapter04/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
--------------------------------------------------------------------------------
/chapter06/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
--------------------------------------------------------------------------------
/chapter07/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
--------------------------------------------------------------------------------
/chapter12/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
--------------------------------------------------------------------------------
/chapter13/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
--------------------------------------------------------------------------------
/chapter02/src/main/webapp/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scalatra/scalatra-in-action/HEAD/chapter02/src/main/webapp/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/chapter09-sbt/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "2.0.4")
4 |
--------------------------------------------------------------------------------
/comments-collector/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.4.2")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
--------------------------------------------------------------------------------
/chapter03/src/main/webapp/WEB-INF/views/hello-scalate.scaml:
--------------------------------------------------------------------------------
1 | - attributes("title") = "Scalatra: a tiny, Sinatra-like web framework for Scala"
2 | - attributes("headline") = "Welcome to Scalatra"
3 |
4 | Hello, Scalate!
--------------------------------------------------------------------------------
/chapter05/README.md:
--------------------------------------------------------------------------------
1 | # Sample code chapter 5 #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ sbt
7 | > container:start
8 | ```
9 |
10 | Open [http://localhost:8080/](http://localhost:8080/) in your browser.
11 |
--------------------------------------------------------------------------------
/chapter01/src/main/webapp/WEB-INF/templates/views/hello-scalate.jade:
--------------------------------------------------------------------------------
1 | - attributes("title") = "Scalatra: a tiny, Sinatra-like web framework for Scala"
2 | - attributes("headline") = "Welcome to Scalatra"
3 |
4 | p= "Hello, Scalate!"
--------------------------------------------------------------------------------
/chapter04/src/main/webapp/WEB-INF/templates/views/hello-scalate.jade:
--------------------------------------------------------------------------------
1 | - attributes("title") = "Scalatra: a tiny, Sinatra-like web framework for Scala"
2 | - attributes("headline") = "Welcome to Scalatra"
3 |
4 | p= "Hello, Scalate!"
--------------------------------------------------------------------------------
/chapter12/src/main/webapp/WEB-INF/templates/views/hello-scalate.jade:
--------------------------------------------------------------------------------
1 | - attributes("title") = "Scalatra: a tiny, Sinatra-like web framework for Scala"
2 | - attributes("headline") = "Welcome to Scalatra"
3 |
4 | p= "Hello, Scalate!"
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/public/WEB-INF/templates/views/hello-scalate.jade:
--------------------------------------------------------------------------------
1 | - attributes("title") = "Scalatra: a tiny, Sinatra-like web framework for Scala"
2 | - attributes("headline") = "Welcome to Scalatra"
3 |
4 | p= "Hello, Scalate!"
5 |
--------------------------------------------------------------------------------
/chapter09-docker/README.md:
--------------------------------------------------------------------------------
1 | # Sample code chapter 9 #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ sbt
7 | > container:start
8 | > ~web-stage
9 | ```
10 |
11 | Open [http://localhost:8080/](http://localhost:8080/) in your browser.
12 |
--------------------------------------------------------------------------------
/chapter09-sbtweb/README.md:
--------------------------------------------------------------------------------
1 | # Sample code chapter 9 #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ sbt
7 | > container:start
8 | > ~web-stage
9 | ```
10 |
11 | Open [http://localhost:8080/](http://localhost:8080/) in your browser.
12 |
--------------------------------------------------------------------------------
/chapter09-standalone/README.md:
--------------------------------------------------------------------------------
1 | # Sample code chapter 9 #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ sbt
7 | > container:start
8 | > ~web-stage
9 | ```
10 |
11 | Open [http://localhost:8080/](http://localhost:8080/) in your browser.
12 |
--------------------------------------------------------------------------------
/chapter09/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
5 | addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "2.0.4")
6 |
7 |
--------------------------------------------------------------------------------
/chapter07/src/main/webapp/WEB-INF/layouts/default.scaml:
--------------------------------------------------------------------------------
1 | !!! 5
2 | -@ val body: String
3 | -@ val title: String = "foo"
4 | %html
5 | %head
6 | %link(type="text/css" href="/css/style.css" rel="stylesheet")
7 | %title= title
8 | %body
9 | != body
--------------------------------------------------------------------------------
/chapter09-standalone/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
5 | addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "2.0.4")
6 |
7 |
--------------------------------------------------------------------------------
/chapter03/src/main/webapp/WEB-INF/layouts/default.scaml:
--------------------------------------------------------------------------------
1 | -@ val title: String
2 | -@ val headline: String = title
3 | -@ val body: String
4 |
5 | !!!
6 | %html
7 | %head
8 | %title= title
9 | %body
10 | #content
11 | %h1= headline
12 | != body
13 |
--------------------------------------------------------------------------------
/chapter01/src/main/webapp/WEB-INF/templates/layouts/default.jade:
--------------------------------------------------------------------------------
1 | -@ val title: String
2 | -@ val headline: String = title
3 | -@ val body: String
4 |
5 | !!!
6 | html
7 | head
8 | title= title
9 | body
10 | #content
11 | h1= headline
12 | != body
13 |
--------------------------------------------------------------------------------
/chapter03/README.md:
--------------------------------------------------------------------------------
1 | # Sample code chapter 5 #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ sbt
7 | > container:start
8 | > browse
9 | ```
10 |
11 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
12 |
--------------------------------------------------------------------------------
/chapter04/src/main/webapp/WEB-INF/templates/layouts/default.jade:
--------------------------------------------------------------------------------
1 | -@ val title: String
2 | -@ val headline: String = title
3 | -@ val body: String
4 |
5 | !!!
6 | html
7 | head
8 | title= title
9 | body
10 | #content
11 | h1= headline
12 | != body
13 |
--------------------------------------------------------------------------------
/chapter07/README.md:
--------------------------------------------------------------------------------
1 | # Sample code chapter 5 #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ sbt
7 | > container:start
8 | > browse
9 | ```
10 |
11 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
12 |
--------------------------------------------------------------------------------
/chapter08/README.md:
--------------------------------------------------------------------------------
1 | # Sample code chapter 5 #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ sbt
7 | > container:start
8 | > browse
9 | ```
10 |
11 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
12 |
--------------------------------------------------------------------------------
/chapter09-docker/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BASE=$(dirname $(readlink -f $0))
4 |
5 | docker run -d \
6 | -v $BASE/data:/app/data \
7 | -v $BASE/conf:/app/conf:ro \
8 | -p 8080:80 \
9 | --name chapter09-standalone \
10 | org.scalatra/chapter09-docker
11 |
12 |
--------------------------------------------------------------------------------
/chapter12/src/main/webapp/WEB-INF/templates/layouts/default.jade:
--------------------------------------------------------------------------------
1 | -@ val title: String
2 | -@ val headline: String = title
3 | -@ val body: String
4 |
5 | !!!
6 | html
7 | head
8 | title= title
9 | body
10 | #content
11 | h1= headline
12 | != body
13 |
--------------------------------------------------------------------------------
/chapter07-twirl/README.md:
--------------------------------------------------------------------------------
1 | # Sample code chapter 5 #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ sbt
7 | > container:start
8 | > browse
9 | ```
10 |
11 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
12 |
--------------------------------------------------------------------------------
/chapter02/src/main/webapp/WEB-INF/templates/views/pages/show.ssp:
--------------------------------------------------------------------------------
1 | <%@ import val page: com.example.cms.Page %>
2 |
3 |
4 |
5 |
<%= page.title %>
6 |
<%= page.summary %>
7 |
<%= page.body %>
8 |
9 |
--------------------------------------------------------------------------------
/chapter06/data/multipart-message.data:
--------------------------------------------------------------------------------
1 |
2 | --a93f5485f279c0
3 | content-disposition: form-data; name="sample"; filename="foobar.txt"
4 |
5 | FOOBAZ
6 | --a93f5485f279c0
7 | content-disposition: form-data; name="description"
8 |
9 | A document about foos.
10 | --a93f5485f279c0--
11 |
--------------------------------------------------------------------------------
/chapter13/src/main/webapp/WEB-INF/templates/views/hackers/index.ssp:
--------------------------------------------------------------------------------
1 | <% import com.constructiveproof.hackertracker.models.Hacker %>
2 | <% import org.squeryl.Query %>
3 | <%@ val allHackers:Query[Hacker] %>
4 |
5 | #for(hacker <- allHackers.toList)
6 | <%= hacker.firstName %>
7 | #end
--------------------------------------------------------------------------------
/chapter02/README.md:
--------------------------------------------------------------------------------
1 | # Scalatra CMS #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ cd Scalatra_CMS
7 | $ ./sbt
8 | > container:start
9 | > browse
10 | ```
11 |
12 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
13 |
--------------------------------------------------------------------------------
/chapter04/README.md:
--------------------------------------------------------------------------------
1 | # Hacker Tracker #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ cd Hacker_Tracker
7 | $ ./sbt
8 | > container:start
9 | > browse
10 | ```
11 |
12 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
13 |
--------------------------------------------------------------------------------
/chapter12/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 12 - Crawler #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ cd chapter12
7 | $ ./sbt
8 | > container:start
9 | > browse
10 | ```
11 |
12 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
13 |
--------------------------------------------------------------------------------
/chapter13/README.md:
--------------------------------------------------------------------------------
1 | # Hacker Tracker #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ cd Hacker_Tracker
7 | $ ./sbt
8 | > container:start
9 | > browse
10 | ```
11 |
12 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
13 |
--------------------------------------------------------------------------------
/chapter01/README.md:
--------------------------------------------------------------------------------
1 | # My Scalatra Web App #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ cd My_Scalatra_Web_App
7 | $ ./sbt
8 | > container:start
9 | > browse
10 | ```
11 |
12 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
13 |
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hello Scalatra
6 |
7 |
8 |
9 |
10 | Hello, world!
11 |
12 |
13 |
--------------------------------------------------------------------------------
/chapter01/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import com.example.app._
2 | import org.scalatra._
3 | import javax.servlet.ServletContext
4 |
5 | class ScalatraBootstrap extends LifeCycle {
6 | override def init(context: ServletContext) {
7 | context.mount(new MyScalatraServlet, "/*")
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/chapter02/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import com.example.cms._
2 | import org.scalatra._
3 | import javax.servlet.ServletContext
4 |
5 | class ScalatraBootstrap extends LifeCycle {
6 | override def init(context: ServletContext) {
7 | context.mount(new PagesController, "/*")
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/webapp/WEB-INF/templates/views/hackers/index.ssp:
--------------------------------------------------------------------------------
1 | <% import com.constructiveproof.hackertracker.models.Hacker %>
2 | <% import org.squeryl.Query %>
3 | <%@ val allHackers:Query[Hacker] %>
4 |
5 | #for(hacker <- allHackers.toList)
6 | <%= hacker.firstName %>
7 | #end
--------------------------------------------------------------------------------
/chapter03/src/main/scala/org/scalatra/book/chapter03/Album.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter03
2 |
3 | import scala.xml.Node
4 | import scala.collection.concurrent.TrieMap
5 |
6 | object Albums {
7 | def findByName(artist: String, name: String) =
8 | "Optional exercise: extend model to albums"
9 | }
10 |
--------------------------------------------------------------------------------
/chapter09-docker/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
2 |
3 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
4 |
5 | addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "2.0.4")
6 |
7 | addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.2.0")
8 |
9 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/webapp/WEB-INF/templates/views/hackers/index.ssp:
--------------------------------------------------------------------------------
1 | <% import com.constructiveproof.hackertracker.models.Hacker %>
2 | <% import org.squeryl.Query %>
3 | <%@ val allHackers:Query[Hacker] %>
4 |
5 | #for(hacker <- allHackers.toList)
6 | <%= hacker.firstName %>
7 | #end
--------------------------------------------------------------------------------
/chapter07/src/main/webapp/WEB-INF/views/greeter_dry.scaml:
--------------------------------------------------------------------------------
1 | -@ val whom: String
2 | -@ val lucky: List[Int]
3 | - attributes("title") = "Hello, "+whom
4 |
5 | %h1 Congratulations
6 | %p You've created your first Scalate view, #{whom}.
7 | %p Your lucky numbers are:
8 | %ul
9 | - for (number <- lucky)
10 | %li #{number}
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/README.md:
--------------------------------------------------------------------------------
1 | # Hacker Tracker #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ cd Hacker_Tracker
7 | $ ./sbt
8 | > container:start
9 | > browse
10 | ```
11 |
12 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
13 |
--------------------------------------------------------------------------------
/chapter07/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra._
2 | import javax.servlet.ServletContext
3 |
4 | import org.scalatra.book.chapter07._
5 |
6 | class ScalatraBootstrap extends LifeCycle {
7 | override def init(context: ServletContext) {
8 | context.mount(new GreeterServlet, "/*")
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/README.md:
--------------------------------------------------------------------------------
1 | # Hacker Tracker #
2 |
3 | ## Build & Run ##
4 |
5 | ```sh
6 | $ cd Hacker_Tracker
7 | $ ./sbt
8 | > container:start
9 | > browse
10 | ```
11 |
12 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
13 |
--------------------------------------------------------------------------------
/chapter05/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra._
2 | import javax.servlet.ServletContext
3 |
4 | import org.scalatra.book.chapter06.MyJsonApp
5 |
6 | class ScalatraBootstrap extends LifeCycle {
7 | override def init(context: ServletContext) {
8 | context.mount(new MyJsonApp, "/*")
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/chapter05/src/main/scala/org/scalatra/book/chapter06/MyJsonpRoutes.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter06
2 |
3 | import org.scalatra._
4 | import org.scalatra.json._
5 |
6 | trait MyJsonpRoutes extends ScalatraBase with JacksonJsonSupport {
7 |
8 | override def jsonpCallbackParameterNames = Seq("jsonp")
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/chapter07-twirl/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra._
2 | import javax.servlet.ServletContext
3 |
4 | import org.scalatra.book.chapter07._
5 |
6 | class ScalatraBootstrap extends LifeCycle {
7 | override def init(context: ServletContext) {
8 | context.mount(new GreeterServlet, "/*")
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/chapter09/src/main/resources/application.conf:
--------------------------------------------------------------------------------
1 |
2 | webBase = "http://dev.example.org"
3 |
4 | // one of: development, test, staging, production
5 | environment = "staging"
6 |
7 | email {
8 | user = "user@example.com"
9 | password = "mypassword"
10 | host = "smtp.example.com"
11 | sender = "User "
12 | }
--------------------------------------------------------------------------------
/chapter09-sbt/src/main/resources/application.conf:
--------------------------------------------------------------------------------
1 |
2 | webBase = "http://dev.example.org"
3 |
4 | // one of: development, test, staging, production
5 | environment = "staging"
6 |
7 | email {
8 | user = "user@example.com"
9 | password = "mypassword"
10 | host = "smtp.example.com"
11 | sender = "User "
12 | }
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/resources/application.conf:
--------------------------------------------------------------------------------
1 |
2 | webBase = "http://dev.example.org"
3 |
4 | // one of: development, test, staging, production
5 | environment = "staging"
6 |
7 | email {
8 | user = "user@example.com"
9 | password = "mypassword"
10 | host = "smtp.example.com"
11 | sender = "User "
12 | }
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/HackersSwagger.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import org.scalatra.ScalatraServlet
4 | import org.scalatra.swagger.{JacksonSwaggerBase, Swagger}
5 |
6 | class HackersSwagger(implicit val swagger: Swagger)
7 | extends ScalatraServlet with JacksonSwaggerBase
--------------------------------------------------------------------------------
/comments-collector/src/main/webapp/css/site.css:
--------------------------------------------------------------------------------
1 |
2 | @media (min-width: 768px) {
3 | .container {
4 | max-width: 730px;
5 | }
6 | }
7 |
8 | .header {
9 | border-bottom: 1px solid #e5e5e5;
10 | margin-bottom: 2em;
11 | }
12 |
13 | .comment span.title {
14 | font-weight: bold;
15 | text-decoration: underline;
16 | }
17 |
--------------------------------------------------------------------------------
/chapter03/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra._
2 | import javax.servlet.ServletContext
3 |
4 | import org.scalatra.book.chapter03.RecordStore
5 |
6 | class ScalatraBootstrap extends LifeCycle {
7 | override def init(context: ServletContext) {
8 | context.mount(new RecordStore("/srv/media"), "/*")
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/chapter09/src/main/scala/org/scalatra/book/chapter09/UrlShortener.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | object UrlShortener {
4 | val chars = ('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')
5 | def randChar = chars(scala.util.Random.nextInt(chars.size))
6 |
7 | def nextFreeToken = (1 to 8).foldLeft("")((acc, _) => acc + randChar)
8 | }
9 |
--------------------------------------------------------------------------------
/chapter08/src/main/scala/org/scalatra/book/chapter08/MyScalatraServlet.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter08
2 |
3 | import org.scalatra.ScalatraServlet
4 |
5 | class MyScalatraServlet extends ScalatraServlet {
6 | get("/") {
7 |
8 |
9 | Hi, world!
10 |
11 |
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/chapter09-sbt/src/main/scala/org/scalatra/book/chapter09/UrlShortener.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | object UrlShortener {
4 | val chars = ('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')
5 | def randChar = chars(scala.util.Random.nextInt(chars.size))
6 |
7 | def nextFreeToken = (1 to 8).foldLeft("")((acc, _) => acc + randChar)
8 | }
9 |
--------------------------------------------------------------------------------
/chapter09-docker/src/main/scala/org/scalatra/book/chapter09/UrlShortener.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | object UrlShortener {
4 | val chars = ('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')
5 | def randChar = chars(scala.util.Random.nextInt(chars.size))
6 |
7 | def nextFreeToken = (1 to 8).foldLeft("")((acc, _) => acc + randChar)
8 | }
9 |
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/scala/org/scalatra/book/chapter09/UrlShortener.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | object UrlShortener {
4 | val chars = ('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')
5 | def randChar = chars(scala.util.Random.nextInt(chars.size))
6 |
7 | def nextFreeToken = (1 to 8).foldLeft("")((acc, _) => acc + randChar)
8 | }
9 |
--------------------------------------------------------------------------------
/chapter09-standalone/src/main/scala/org/scalatra/book/chapter09/UrlShortener.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | object UrlShortener {
4 | val chars = ('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')
5 | def randChar = chars(scala.util.Random.nextInt(chars.size))
6 |
7 | def nextFreeToken = (1 to 8).foldLeft("")((acc, _) => acc + randChar)
8 | }
9 |
--------------------------------------------------------------------------------
/chapter13/src/main/webapp/WEB-INF/templates/views/hackers/show.ssp:
--------------------------------------------------------------------------------
1 | <% import com.constructiveproof.hackertracker.models.Hacker %>
2 | <% import org.squeryl.Query %>
3 |
4 | <%@ val hacker:Hacker %>
5 |
6 |
7 | <%= hacker.firstName %> <%= hacker.lastName %> born <%= hacker.birthYear.toString %>
8 |
9 | “<%= hacker.motto %>”
--------------------------------------------------------------------------------
/chapter08/src/test/scala/org/scalatra/book/chapter08/JsonBodySupport.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter08
2 |
3 | import org.scalatra.test._
4 | import org.scalatra.test.specs2._
5 |
6 | import org.json4s.JValue
7 | import org.json4s.jackson.JsonMethods
8 |
9 | trait JsonBodySupport { self: ScalatraTests =>
10 | def jsonBody: JValue = JsonMethods.parse(body)
11 | }
12 |
--------------------------------------------------------------------------------
/chapter06/src/main/webapp/WEB-INF/layouts/default.scaml:
--------------------------------------------------------------------------------
1 | -@ val title: String
2 | -@ val body: String
3 |
4 | !!!
5 | %html
6 | %head
7 | %title= title
8 | %link(rel="stylesheet" href={uri("css/bootstrap.min.css")})
9 | %body
10 |
11 |
12 | .container
13 | .row
14 | .col-lg-12
15 | %h2 Document Storage
16 |
17 | != body
18 |
19 |
20 |
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/stacks/HackerCoreStack.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.stacks
2 |
3 | import org.scalatra.{MethodOverride, ScalatraServlet}
4 | import com.constructiveproof.hackertracker.init.DatabaseSessionSupport
5 |
6 | trait HackerCoreStack extends ScalatraServlet with DatabaseSessionSupport with MethodOverride {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/chapter09-docker/conf/application.conf:
--------------------------------------------------------------------------------
1 |
2 | port = 80
3 |
4 | webBase = "http://www.example.org"
5 |
6 | assetsDirectory = "/app/webapp"
7 |
8 | // one of: development, test, staging, production
9 | environment = "production"
10 |
11 | email {
12 | user = "user@example.com"
13 | password = "mypassword"
14 | host = "smtp.example.com"
15 | sender = "User "
16 | }
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/webapp/WEB-INF/templates/views/hackers/show.ssp:
--------------------------------------------------------------------------------
1 | <% import com.constructiveproof.hackertracker.models.Hacker %>
2 | <% import org.squeryl.Query %>
3 |
4 | <%@ val hacker:Hacker %>
5 |
6 |
7 | <%= hacker.firstName %> <%= hacker.lastName %> born <%= hacker.birthYear.toString %>
8 |
9 | “<%= hacker.motto %>”
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/webapp/WEB-INF/templates/views/hackers/show.ssp:
--------------------------------------------------------------------------------
1 | <% import com.constructiveproof.hackertracker.models.Hacker %>
2 | <% import org.squeryl.Query %>
3 |
4 | <%@ val hacker:Hacker %>
5 |
6 |
7 | <%= hacker.firstName %> <%= hacker.lastName %> born <%= hacker.birthYear.toString %>
8 |
9 | “<%= hacker.motto %>”
--------------------------------------------------------------------------------
/chapter05/src/main/scala/org/scalatra/book/chapter06/MyJsonApp.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter06
2 |
3 | import org.json4s._
4 | import org.scalatra._
5 |
6 | class MyJsonApp extends ScalatraServlet with MyJsonRoutes with MyJsonScalazRoutes with MyFoodRoutes with MyJsonpRoutes {
7 |
8 | implicit lazy val jsonFormats = DefaultFormats +
9 | new NutritionFactsSerializer
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/comments-collector/README.md:
--------------------------------------------------------------------------------
1 | # Comments collector #
2 |
3 | ## Install MongoDB
4 |
5 | ### Linux
6 |
7 | $ sudo apt-get install mongodb
8 |
9 | ## Build & Run ##
10 |
11 | ```sh
12 | $ cd comments_collector
13 | $ ./sbt
14 | > container:start
15 | > browse
16 |
17 | If `browse` doesn't launch your browser, manually open [http://localhost:8080/](http://localhost:8080/) in your browser.
18 |
--------------------------------------------------------------------------------
/chapter05/src/main/scala/org/scalatra/book/chapter06/recipes.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter06
2 |
3 | case class Recipe(title: String, details: RecipeDetails,
4 | ingredients: List[IngredientLine], steps: List[String])
5 |
6 | case class RecipeDetails(cuisine: String, vegetarian: Boolean,
7 | diet: Option[String])
8 |
9 | case class IngredientLine(label: String, quantity: String)
10 |
11 |
--------------------------------------------------------------------------------
/chapter01/src/main/scala/com/example/app/MyScalatraServlet.scala:
--------------------------------------------------------------------------------
1 | package com.example.app
2 |
3 | import org.scalatra._
4 | import scalate.ScalateSupport
5 |
6 | class MyScalatraServlet extends MyScalatraWebAppStack {
7 |
8 | get("/") {
9 |
10 |
11 | Hello, world!
12 | Say hello to Scalate.
13 |
14 |
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/chapter09-standalone/src/main/resources/application.conf:
--------------------------------------------------------------------------------
1 |
2 | port = 8080
3 | webBase = "http://dev.example.org"
4 |
5 | assetsDirectory = "target/webapp" // or webapp in production
6 | environment = "staging" // one of: development, test, staging, production
7 |
8 | email {
9 | user = "user@example.com"
10 | password = "mypassword"
11 | host = "smtp.example.com"
12 | sender = "User "
13 | }
--------------------------------------------------------------------------------
/chapter07/src/main/webapp/WEB-INF/views/greeter.scaml:
--------------------------------------------------------------------------------
1 | !!! 5
2 | -@ val whom: String
3 | -@ val lucky: List[Int]
4 | %html
5 | %head
6 | %link(type="text/css" href="/css/style.css" rel="stylesheet")
7 | %title Hello, #{whom}
8 | %body
9 | %h1 Congratulations
10 | %p You've created your first Scalate view, #{whom}.
11 | %p Your lucky numbers are:
12 | %ul
13 | - for (number <- lucky)
14 | %li #{number}
--------------------------------------------------------------------------------
/chapter04/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import com.constructiveproof.hackertracker._
2 | import org.scalatra._
3 | import javax.servlet.ServletContext
4 |
5 | class ScalatraBootstrap extends LifeCycle {
6 | override def init(context: ServletContext) {
7 | context.mount(new CookiesExample, "/cookies/*")
8 | context.mount(new GateController, "/grail/*")
9 | context.mount(new HackersController, "/*")
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/chapter07-twirl/src/main/scala/org/scalatra/book/chapter07/GreeterServlet.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter07
2 |
3 | import org.scalatra.ScalatraServlet
4 |
5 | class GreeterServlet extends ScalatraServlet {
6 | get("/greet/:whom") {
7 | contentType = "text/html"
8 | val lucky =
9 | for (i <- (1 to 5).toList)
10 | yield util.Random.nextInt(48) + 1
11 | html.greeting(params("whom"), lucky)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/chapter09-docker/src/main/resources/application.conf:
--------------------------------------------------------------------------------
1 |
2 | port = 8080
3 |
4 | webBase = "http://dev.example.org"
5 |
6 | assetsDirectory = "target/webapp" // or /app/webapp in production
7 | environment = "staging" // one of: development, test, staging, production
8 |
9 | email {
10 | user = "user@example.com"
11 | password = "mypassword"
12 | host = "smtp.example.com"
13 | sender = "User "
14 | }
15 |
--------------------------------------------------------------------------------
/chapter10/src/main/webapp/WEB-INF/views/areas.jade:
--------------------------------------------------------------------------------
1 | - import org.scalatra.book.chapter10._
2 | -@ val areas: Seq[Area]
3 |
4 | - attributes("title") = "Areas"
5 |
6 | .row
7 | .col-md-8
8 | - for (area <- areas)
9 | .panel.panel-default
10 | .panel-heading
11 | | Area: #{area.name}
12 | a.pull-right(href={"/areas/" + area.id}) show
13 | .panel-body
14 | div= area.description
15 |
16 |
--------------------------------------------------------------------------------
/chapter08/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra._
2 | import javax.servlet.ServletContext
3 |
4 | import org.scalatra.book.chapter08._
5 |
6 | class ScalatraBootstrap extends LifeCycle {
7 | override def init(context: ServletContext) {
8 | context.mount(new FoodServlet, "/*")
9 | context.mount(new MyScalatraServlet, "/*")
10 | context.mount(new NukeLauncherServlet(RealNukeLauncher), "/nuke/*")
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/chapter05/src/test/scala/org/scalatra/book/chapter06/MyJsonAppSpec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter06
2 |
3 | import org.scalatest.FunSuite
4 | import org.scalatra.test.scalatest.ScalatraSuite
5 |
6 | class MyJsonAppSpec extends FunSuite with ScalatraSuite {
7 |
8 | addServlet(classOf[MyJsonApp], "/*")
9 |
10 | test("Getting foods") {
11 | get("/foods/foo_bar") {
12 | status should equal(200)
13 | }
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/chapter06/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra._
2 | import javax.servlet.ServletContext
3 |
4 | import org.scalatra.book.chapter06.{DocumentStore, DocumentsApp}
5 |
6 | class ScalatraBootstrap extends LifeCycle {
7 |
8 | override def init(context: ServletContext) {
9 |
10 | val store = DocumentStore("data")
11 |
12 | val app = new DocumentsApp(store)
13 |
14 | context.mount(app, "/*")
15 |
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/chapter10/src/main/webapp/WEB-INF/layouts/default.jade:
--------------------------------------------------------------------------------
1 | -@ val title: String
2 | -@ val body: String
3 |
4 | !!!
5 | html
6 | head
7 | title= title
8 | link(rel="stylesheet" href="/css/bootstrap.min.css")
9 | body
10 |
11 | .container
12 | .row
13 | .col-md-8
14 | p
15 | ul.nav.nav-pills
16 | li
17 | a(href="/") Home
18 |
19 | h2= title
20 |
21 | != body
--------------------------------------------------------------------------------
/chapter03/src/test/scala/org/scalatra/book/chapter03/RecordStoreSpec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter03
2 |
3 | import org.scalatest.FunSuite
4 | import org.scalatra.test.scalatest.ScalatraSuite
5 |
6 | class RecordStoreSpec extends FunSuite with ScalatraSuite {
7 |
8 | addServlet(new RecordStore("/"), "/*")
9 |
10 | test("Getting artists") {
11 | get("/artists/?") {
12 | status should equal(200)
13 | }
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/chapter07-twirl/src/test/scala/org/scalatra/book/chapter07/GreeterServletSpec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter07
2 |
3 | import org.scalatest.FunSuite
4 | import org.scalatra.test.scalatest.ScalatraSuite
5 |
6 | class GreeterServletSpec extends FunSuite with ScalatraSuite {
7 | addServlet(classOf[GreeterServlet], "/*")
8 |
9 | test("simple get") {
10 | get("/greet/dave") {
11 | status should equal(200)
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/chapter09-sbt/src/main/scala/org/scalatra/book/chapter09/Chapter09.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | import org.scalatra.ScalatraServlet
4 |
5 | class Chapter09(appConfig: AppConfig) extends ScalatraServlet {
6 |
7 | get("/") {
8 | f"Greetings! (isDevelopment = ${isDevelopmentMode}})"
9 | }
10 |
11 | get("/shorten-url") {
12 | val token = UrlShortener.nextFreeToken
13 | f"${appConfig.webBase}/$token"
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/chapter10/src/main/scala/org/scalatra/book/chapter10/domain.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter10
2 |
3 | case class Area(
4 | id: Int,
5 | name: String,
6 | location: String,
7 | latitude: Double,
8 | longitude: Double,
9 | description: String)
10 |
11 | case class Route(
12 | id: Int,
13 | areaId: Int,
14 | mountainName: Option[String],
15 | routeName: String,
16 | latitude: Double,
17 | longitude: Double,
18 | description: String)
19 |
--------------------------------------------------------------------------------
/comments-collector/src/main/scala/comments/scalaz.scala:
--------------------------------------------------------------------------------
1 | package comments
2 |
3 | import org.scalatra._
4 |
5 | import scalaz._, Scalaz._
6 |
7 | trait ScalazSupport extends ScalatraBase {
8 |
9 | // be able to handle scalaz' \/ as return value, simply unwrap the value from the container
10 | override def renderPipeline: RenderPipeline = ({
11 | case \/-(r) => r
12 | case -\/(l) => l
13 | }: RenderPipeline) orElse super.renderPipeline
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/chapter10/src/main/webapp/WEB-INF/views/area.jade:
--------------------------------------------------------------------------------
1 | - import org.scalatra.book.chapter10._
2 | -@ val area: Area
3 | -@ val routes: Seq[Route]
4 | - attributes("title") = area.name
5 |
6 | .row
7 | .col-lg-8
8 | h4 Routes
9 | - for (route <- routes)
10 | .panel.panel-default
11 | .panel-heading= route.routeName
12 | .panel-body
13 | p Description: #{route.description}
14 | p Lat: #{route.latitude}°, Long: #{route.longitude}°
15 |
16 |
--------------------------------------------------------------------------------
/chapter13/src/main/webapp/WEB-INF/templates/views/hackers/new.ssp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/chapter09/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra.book.chapter09._
2 | import org.scalatra._
3 |
4 | import javax.servlet.ServletContext
5 |
6 | class ScalatraBootstrap extends LifeCycle {
7 |
8 | override def init(context: ServletContext) {
9 |
10 | val conf = AppConfig.load
11 | sys.props(org.scalatra.EnvironmentKey) = AppEnvironment.asString(conf.env)
12 |
13 | val app = new Chapter09(conf)
14 | context.mount(app, "/*")
15 |
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/chapter01/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter02/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter03/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter04/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter05/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter06/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter07/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter08/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter09-sbt/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra.book.chapter09._
2 | import org.scalatra._
3 |
4 | import javax.servlet.ServletContext
5 |
6 | class ScalatraBootstrap extends LifeCycle {
7 |
8 | override def init(context: ServletContext) {
9 |
10 | val conf = AppConfig.load
11 | sys.props(org.scalatra.EnvironmentKey) = AppEnvironment.asString(conf.env)
12 |
13 | val app = new Chapter09(conf)
14 | context.mount(app, "/*")
15 |
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/chapter09/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter10/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter10/src/main/scala/org/scalatra/book/chapter10/scalaz.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter10
2 |
3 | import org.scalatra._
4 |
5 | import scalaz._, Scalaz._
6 |
7 | trait ScalazSupport extends ScalatraBase {
8 |
9 | // be able to handle scalaz' \/ as return value, simply unwrap the value from the container
10 | override def renderPipeline: RenderPipeline = ({
11 | case \/-(r) => r
12 | case -\/(l) => l
13 | }: RenderPipeline) orElse super.renderPipeline
14 |
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/chapter12/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter13/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter07/src/main/scala/org/scalatra/book/chapter07/GreeterServlet.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter07
2 |
3 | import org.scalatra.ScalatraServlet
4 |
5 | class GreeterServlet extends MyScalatraWebappStack {
6 | get("/greet/:whom") {
7 | contentType = "text/html"
8 | val lucky =
9 | for (i <- (1 to 5).toList)
10 | yield util.Random.nextInt(48) + 1
11 | layoutTemplate("greeter_dry.html",
12 | "whom" -> params("whom"),
13 | "lucky" -> lucky)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/chapter09-docker/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra.book.chapter09._
2 | import org.scalatra.LifeCycle
3 |
4 | import javax.servlet.ServletContext
5 |
6 | class ScalatraBootstrap extends LifeCycle {
7 |
8 | override def init(context: ServletContext) {
9 |
10 | val conf = AppConfig.load
11 | sys.props(org.scalatra.EnvironmentKey) = AppEnvironment.asString(conf.env)
12 |
13 | val app = new Chapter09(conf)
14 | context.mount(app, "/*")
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/chapter09-sbt/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra.book.chapter09._
2 | import org.scalatra.LifeCycle
3 |
4 | import javax.servlet.ServletContext
5 |
6 | class ScalatraBootstrap extends LifeCycle {
7 |
8 | override def init(context: ServletContext) {
9 |
10 | val conf = AppConfig.load
11 | sys.props(org.scalatra.EnvironmentKey) = AppEnvironment.asString(conf.env)
12 |
13 | val app = new Chapter09(conf)
14 | context.mount(app, "/*")
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/chapter09-standalone/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra.book.chapter09._
2 | import org.scalatra.LifeCycle
3 |
4 | import javax.servlet.ServletContext
5 |
6 | class ScalatraBootstrap extends LifeCycle {
7 |
8 | override def init(context: ServletContext) {
9 |
10 | val conf = AppConfig.load
11 | sys.props(org.scalatra.EnvironmentKey) = AppEnvironment.asString(conf.env)
12 |
13 | val app = new Chapter09(conf)
14 | context.mount(app, "/*")
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/chapter07-twirl/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter07-twirl/src/main/twirl/greeting.scala.html:
--------------------------------------------------------------------------------
1 | @(whom: String, lucky: List[Int])
2 |
3 |
4 |
5 |
6 | Hello, @whom
7 |
8 |
9 | Congratulations
10 | You've created your first Twirl view, @whom.
11 | Your lucky numbers are:
12 |
13 | @for(number <- lucky) {
14 | - @number
15 | }
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/chapter09-docker/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/comments-collector/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter09-sbtweb/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | resolvers += Resolver.typesafeRepo("releases")
2 |
3 | addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.5.0")
4 |
5 | addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.5.1")
6 |
7 | addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "2.0.4")
8 |
9 | addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.7")
10 |
11 | addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6")
12 |
13 | addSbtPlugin("com.slidingautonomy.sbt" % "sbt-filter" % "1.0.1")
14 |
15 |
--------------------------------------------------------------------------------
/chapter09-standalone/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/webapp/WEB-INF/templates/views/hackers/new.ssp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/webapp/WEB-INF/templates/views/hackers/new.ssp:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/chapter13/src/main/webapp/WEB-INF/templates/views/sessions/new.ssp:
--------------------------------------------------------------------------------
1 | Please login
2 |
3 |
--------------------------------------------------------------------------------
/chapter04/src/main/scala/com/constructiveproof/hackertracker/CookiesExample.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import org.scalatra._
4 |
5 | class CookiesExample extends ScalatraServlet {
6 |
7 | get("/") {
8 | val previous = cookies.get("counter") match {
9 | case Some(v) => v.toInt
10 | case None => 0
11 | }
12 | cookies.update("counter", (previous+1).toString)
13 |
14 | Hi, you have been on this page {previous} times already
15 |
16 | }
17 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | lib_managed/
3 | project/boot/
4 | project/build/target/
5 | project/plugins/project/build.properties
6 | src_managed/
7 | target/
8 | *.eml
9 | *.iml
10 | *.ipr
11 | *.iws
12 | .*.sw?
13 | .idea
14 | .DS_Store
15 | .ensime
16 | .target
17 |
18 | # paulp script #
19 | /.lib/
20 |
21 | # eclipse artifacts
22 | .settings
23 | .project
24 | .classpath
25 | .cache
26 | .ensime_lucene
27 |
28 | # sublime artifacts
29 | *.sublime-*
30 | atlassian-ide-plugin.xml
31 | .lib
32 |
33 | .history
34 |
35 | .jvmopts
36 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/webapp/WEB-INF/templates/views/sessions/new.ssp:
--------------------------------------------------------------------------------
1 | Please login
2 |
3 |
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/stacks/ApiStack.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.stacks
2 |
3 | import org.scalatra.json.JacksonJsonSupport
4 | import org.scalatra.swagger.JacksonSwaggerBase
5 | import com.constructiveproof.hackertracker.auth.ApiAuthenticationSupport
6 | import org.json4s.DefaultFormats
7 |
8 | trait ApiStack extends HackerCoreStack with ApiAuthenticationSupport
9 | with JacksonJsonSupport {
10 |
11 | override protected implicit lazy val jsonFormats = DefaultFormats
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/chapter02/src/main/scala/com/example/cms/ScalatraCmsStack.scala:
--------------------------------------------------------------------------------
1 | package com.example.cms
2 |
3 | import org.scalatra._
4 | import scalate.ScalateSupport
5 |
6 | trait ScalatraCmsStack extends ScalatraServlet with ScalateSupport {
7 |
8 | notFound {
9 | // remove content type in case it was set through an action
10 | contentType = null
11 | // Try to render a ScalateTemplate if no route matched
12 | findTemplate(requestPath) map { path =>
13 | contentType = "text/html"
14 | layoutTemplate(path)
15 | } orElse serveStaticResource() getOrElse resourceNotFound()
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/chapter08/src/main/scala/org/scalatra/book/chapter08/FoodServlet.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter08
2 |
3 | import org.json4s.DefaultFormats
4 | import org.json4s.JsonDSL._
5 | import org.scalatra.ScalatraServlet
6 | import org.scalatra.json._
7 |
8 | class FoodServlet extends ScalatraServlet with JacksonJsonSupport {
9 |
10 | implicit lazy val jsonFormats = DefaultFormats
11 |
12 | get("/foods/potatoes") {
13 | val productJson =
14 | ("name" -> "potatoes") ~
15 | ("fairTrade" -> true) ~
16 | ("tags" -> List("vegetable", "tuber"))
17 |
18 | productJson
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/chapter01/src/test/scala/com/example/app/MyScalatraServletSpec.scala:
--------------------------------------------------------------------------------
1 | package com.example.app
2 |
3 | import org.scalatra.test.specs2._
4 |
5 | // For more on Specs2, see http://etorreborre.github.com/specs2/guide/org.specs2.guide.QuickStart.html
6 | class MyScalatraServletSpec extends ScalatraSpec { def is =
7 | "GET / on MyScalatraServlet" ^
8 | "should return status 200" ! root200^
9 | end
10 |
11 | addServlet(classOf[MyScalatraServlet], "/*")
12 |
13 | def root200 = get("/") {
14 | status must_== 200
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/chapter01/src/main/scala/com/example/app/MyScalatraWebAppStack.scala:
--------------------------------------------------------------------------------
1 | package com.example.app
2 |
3 | import org.scalatra._
4 | import scalate.ScalateSupport
5 |
6 | trait MyScalatraWebAppStack extends ScalatraServlet with ScalateSupport {
7 |
8 | notFound {
9 | // remove content type in case it was set through an action
10 | contentType = null
11 | // Try to render a ScalateTemplate if no route matched
12 | findTemplate(requestPath) map { path =>
13 | contentType = "text/html"
14 | layoutTemplate(path)
15 | } orElse serveStaticResource() getOrElse resourceNotFound()
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import com.constructiveproof.hackertracker._
2 | import com.constructiveproof.hackertracker.init.DatabaseInit
3 | import org.scalatra._
4 | import javax.servlet.ServletContext
5 |
6 | class ScalatraBootstrap extends LifeCycle with DatabaseInit {
7 |
8 | override def init(context: ServletContext) {
9 | configureDb()
10 | context.mount(new HackersController, "/hackers")
11 | context.mount(new DatabaseSetupController, "/database")
12 | }
13 |
14 | override def destroy(context:ServletContext) {
15 | closeDbConnection()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/chapter04/src/main/scala/com/constructiveproof/hackertracker/GateController.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import org.scalatra.ScalatraServlet
4 |
5 | class GateController extends ScalatraServlet {
6 |
7 | before() {
8 | if (params("name") == "Arthur") {
9 | halt(status = 403,
10 | reason = "Forbidden",
11 | headers = Map("X-Your-Mother-Was-A" -> "hamster",
12 | "X-And-Your-Father-Smelt-Of" -> "Elderberries"),
13 | body = Go away or I shall taunt you a second time!
)
14 | }
15 | }
16 |
17 | get("/") {
18 | "the holy grail!"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/chapter07/src/main/scala/org/scalatra/book/chapter07/MyScalatraWebappStack.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter07
2 |
3 | import org.scalatra._
4 | import scalate.ScalateSupport
5 |
6 | trait MyScalatraWebappStack extends ScalatraServlet with ScalateSupport {
7 | notFound {
8 | // remove content type in case it was set through an action
9 | contentType = null
10 | // Try to render a ScalateTemplate if no route matched
11 | findTemplate(requestPath) map { path =>
12 | contentType = "text/html"
13 | layoutTemplate(path)
14 | } orElse serveStaticResource() getOrElse resourceNotFound()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/chapter06/src/test/scala/org/scalatra/book/chapter06/FilesSpec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter06
2 |
3 | import org.scalatra.test.specs2._
4 |
5 | // For more on Specs2, see http://etorreborre.github.com/specs2/guide/org.specs2.guide.QuickStart.html
6 | class FilesSpec extends ScalatraSpec { def is =
7 | "GET / on DocumentsApp" ^
8 | "should return status 200" ! root200^
9 | end
10 |
11 | val store = DocumentStore("data")
12 |
13 | val app = new DocumentsApp(store)
14 |
15 | addServlet(app, "/*")
16 |
17 | def root200 = get("/") {
18 | status must_== 200
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/chapter09-sbt/build.sbt:
--------------------------------------------------------------------------------
1 |
2 | organization := "org.scalatra"
3 | name := "Chapter 9"
4 | version := "0.1.0-SNAPSHOT"
5 | scalaVersion := "2.11.6"
6 |
7 | fork in Test := true
8 |
9 | val ScalatraVersion = "2.4.0"
10 |
11 | libraryDependencies ++= Seq(
12 | "org.scalatra" %% "scalatra" % ScalatraVersion,
13 | "org.scalatra" %% "scalatra-specs2" % ScalatraVersion % "test",
14 | "com.typesafe" % "config" % "1.2.1",
15 | "ch.qos.logback" % "logback-classic" % "1.1.3" % "runtime",
16 | "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided"
17 | )
18 |
19 | enablePlugins(JettyPlugin)
20 |
21 | containerPort in Jetty := 8090
22 |
--------------------------------------------------------------------------------
/chapter10/src/main/webapp/WEB-INF/views/routes.jade:
--------------------------------------------------------------------------------
1 | - import org.scalatra.book.chapter10._
2 | -@ val area: Area
3 | -@ val routes: Seq[Route]
4 | - attributes("title") = area.name
5 |
6 | .row
7 | .col-lg-12
8 | h2 Routes
9 |
10 | - for (route <- routes)
11 | .col-lg-12.comment
12 | span.title= route.routeName
13 | p= route.description
14 |
15 | .col-lg-12
16 | h2 Post comment
17 | form(method="post")
18 | label
19 | Title
20 | input(type="text" name="title")
21 | br
22 | label
23 | Body
24 | textarea(name="body")
25 | br
26 | input(type="submit" value="Submit")
--------------------------------------------------------------------------------
/chapter02/src/test/scala/com/example/cms/PagesControllerSpec.scala:
--------------------------------------------------------------------------------
1 | package com.example.cms
2 |
3 | import org.scalatra.test.specs2._
4 |
5 | class PagesControllerSpec extends ScalatraSpec { def is =
6 | "GET /pages/:slug on PagesController" ^
7 | "should return status 200" ! pagesWork^
8 | "shows the word 'Bacon' in the body" ! containsBacon^
9 | end
10 |
11 | addServlet(classOf[PagesController], "/*")
12 |
13 | def pagesWork = get("/pages/bacon-ipsum") {
14 | status must_== 200
15 | }
16 |
17 | def containsBacon = get("/pages/bacon-ipsum") {
18 | body must contain("Bacon")
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/chapter04/src/test/scala/com/constructiveproof/hackertracker/HackersControllerSpec.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import org.scalatra.test.specs2._
4 |
5 | // For more on Specs2, see http://etorreborre.github.com/specs2/guide/org.specs2.guide.QuickStart.html
6 | class HackersControllerSpec extends ScalatraSpec { def is =
7 | "GET / on HackersController" ^
8 | "should return status 200" ! root200^
9 | end
10 |
11 | addServlet(classOf[HackersController], "/*")
12 |
13 | def root200 = get("/") {
14 | status must_== 200
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This repository contains the sample code for the Scalatra in Action book.
2 |
3 | Each chapter has a code listing. The instructions for building the listings
4 | boils down to:
5 |
6 | * have a JDK installed
7 | * change into the top-level directory of the application (e.g. `cd chapter02/scalachat`).
8 | * make sure the file `sbt` is executable (i.e. `chmod +x sbt` on *nix), or that you've installed sbt if you're on Windows.
9 | * run sbt: `./sbt` (on *nix) or `sbt` (on Windows). This will download a large number of dependencies.
10 | * type `container:start` at the sbt prompt
11 | * visit the running application in your browser, at http://localhost:8008
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chapter07/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
12 | org.scalatra.servlet.ScalatraListener
13 |
14 |
15 |
--------------------------------------------------------------------------------
/chapter08/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
12 | org.scalatra.servlet.ScalatraListener
13 |
14 |
15 |
--------------------------------------------------------------------------------
/chapter04/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter07-twirl/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
12 | org.scalatra.servlet.ScalatraListener
13 |
14 |
15 |
--------------------------------------------------------------------------------
/chapter09/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter13/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter09-sbt/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import com.constructiveproof.hackertracker._
2 | import com.constructiveproof.hackertracker.init.DatabaseInit
3 | import org.scalatra._
4 | import javax.servlet.ServletContext
5 |
6 | class ScalatraBootstrap extends LifeCycle with DatabaseInit {
7 |
8 | override def init(context: ServletContext) {
9 | configureDb()
10 | context.mount(new HackersController, "/hackers")
11 | context.mount(new DatabaseSetupController, "/database")
12 | context.mount(new SessionsController, "/sessions")
13 | }
14 |
15 | override def destroy(context:ServletContext) {
16 | closeDbConnection()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/chapter02/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter09-docker/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter09-standalone/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter12/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/stacks/BrowserStack.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.stacks
2 |
3 | import org.scalatra._
4 | import org.scalatra.scalate.ScalateSupport
5 |
6 | trait BrowserStack extends HackerCoreStack with ScalateSupport
7 | with FlashMapSupport {
8 |
9 | notFound {
10 | // remove content type in case it was set through an action
11 | contentType = null
12 | // Try to render a ScalateTemplate if no route matched
13 | findTemplate(requestPath) map { path =>
14 | contentType = "text/html"
15 | layoutTemplate(path)
16 | } orElse serveStaticResource() getOrElse resourceNotFound()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/public/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter12/src/test/scala/com/constructiveproof/crawler/AkkaCrawlerSpec.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.crawler
2 |
3 | import akka.actor.{Props, ActorSystem}
4 | import com.constructiveproof.crawler.actors.GrabActor
5 | import org.scalatest.FunSuite
6 | import org.scalatra.test.scalatest.ScalatraSuite
7 |
8 | class AkkaCrawlerSpec extends FunSuite with ScalatraSuite {
9 |
10 | val system = ActorSystem()
11 | val grabActor = system.actorOf(Props[GrabActor])
12 |
13 | addServlet(new AkkaCrawler(system, grabActor), "/*")
14 |
15 | test("Simple get") {
16 | get("/", Map("url" -> "http://www.google.com")) {
17 | status should equal(200)
18 | }
19 | }
20 |
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/chapter05/src/main/scala/org/scalatra/book/chapter06/MyFoodRoutes.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter06
2 |
3 | import org.scalatra._
4 | import org.scalatra.json._
5 |
6 | import org.json4s._
7 |
8 | trait MyFoodRoutes extends ScalatraBase with JacksonJsonSupport {
9 |
10 | get("/foods/foo_bar/facts") {
11 | val facts = NutritionFacts(
12 | Energy(2050), Carbohydrate(36.2), Fat(33.9), Protein(7.9))
13 |
14 | val factsJson = Extraction.decompose(facts)
15 |
16 | factsJson
17 | }
18 |
19 | post("/foods/:name/facts") {
20 | val facts = parsedBody.extractOpt[NutritionFacts]
21 | println(f"updated facts: $facts")
22 | }
23 |
24 | }
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/comments-collector/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter05/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/chapter10/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/chapter08/src/main/scala/org/scalatra/book/chapter08/NukeLauncherServlet.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter08
2 |
3 | import org.scalatra._
4 |
5 | class NukeLauncherServlet(launcher: NukeLauncher) extends ScalatraServlet {
6 | val NuclearCode = "password123"
7 |
8 | post("/launch") {
9 | if (params("code") == NuclearCode)
10 | launcher.launch()
11 | else
12 | Forbidden()
13 | }
14 | }
15 |
16 | trait NukeLauncher {
17 | def launch(): Unit
18 | }
19 |
20 | object RealNukeLauncher extends NukeLauncher {
21 | def launch(): Unit = ???
22 | }
23 |
24 | class StubNukeLauncher extends NukeLauncher {
25 | var isLaunched = false
26 | def launch(): Unit = isLaunched = true
27 | }
28 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter01/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 | org.scalatra.servlet.ScalatraListener
14 |
15 |
16 |
--------------------------------------------------------------------------------
/chapter09-docker/conf/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 | /app/data/logs/app.log
10 |
11 |
12 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/comments-collector/src/main/webapp/WEB-INF/views/comments.scaml:
--------------------------------------------------------------------------------
1 | - import comments._
2 | -@ val url: String
3 | -@ val comments: Seq[Comment]
4 | - attributes("title") = s"Comments for ${url}"
5 |
6 | .row
7 | .col-lg-12
8 | %h2
9 | Comments for
10 | %a(href={url})= url
11 |
12 |
13 | - for (comment <- comments)
14 | .col-lg-12.comment
15 | %span.title= comment.title
16 | %p= comment.body
17 |
18 | .col-lg-12
19 | %h2 Post comment
20 | %form(method="post")
21 | %label
22 | Title
23 | %input(type="text" name="title")
24 | %br
25 | %label
26 | Body
27 | %textarea(name="body")
28 | %br
29 | %input(type="submit" value="Submit")
--------------------------------------------------------------------------------
/chapter08/src/test/scala/org/scalatra/book/chapter08/MyScalatraServletWordSpec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter08
2 |
3 | import org.scalatra.test.scalatest._
4 |
5 | class MyScalatraServletWordSpec extends ScalatraWordSpec {
6 | addServlet(classOf[MyScalatraServlet], "/*")
7 |
8 | "GET / on MyScalatraServlet" must {
9 | "return status 200" in {
10 | get("/") {
11 | status should equal(200)
12 | }
13 | }
14 |
15 | "be HTML" in {
16 | get("/") {
17 | header("Content-Type") should startWith("text/html;")
18 | }
19 | }
20 |
21 | "should say \"Hi, world!\"" in {
22 | get("/") {
23 | body should include("Hi")
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/auth/utils/HmacUtils.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.auth.utils
2 |
3 | import javax.crypto.Mac
4 | import javax.crypto.spec.SecretKeySpec
5 | import sun.misc.BASE64Encoder
6 |
7 | object HmacUtils {
8 |
9 | def verify(secretKey: String, signMe: String, hmac: String): Boolean = {
10 | sign(secretKey, signMe) == hmac
11 | }
12 |
13 | def sign(secretKey: String, signMe: String): String = {
14 | val secret = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1")
15 | val mac = Mac.getInstance("HmacSHA1")
16 | mac.init(secret)
17 | val hmac = mac.doFinal(signMe.getBytes)
18 | new BASE64Encoder().encode(hmac)
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/chapter09-standalone/src/main/resources/logback.production.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 | logs/app.log
10 |
11 |
12 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/comments-collector/src/main/webapp/WEB-INF/views/index.scaml:
--------------------------------------------------------------------------------
1 | - import comments._
2 | - import java.net.URLEncoder.encode
3 | -@ val urls: Seq[String]
4 |
5 | :!javascript
6 | $(function() {
7 | $("#create-comment").click(function() {
8 | var url = $("#url").val();
9 | location.href = "#{uri("/")}" + encodeURIComponent(url);
10 | });
11 | })
12 |
13 | .row
14 | .col-lg-12
15 | .jumbotron
16 | %h2 Welcome
17 | %p This is the web interface for the comments collector application. Here you can view existing comments and also create new ones.
18 | %p
19 | Enter url:
20 | %input#url(type="text" value="http://www.google.com")
21 | %a#create-comment.btn.btn-lg.btn-primary(href="#" role="button") Create a comment »
22 |
23 |
24 |
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/init/DatabaseSessionSupport.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.init
2 |
3 | import org.squeryl.Session
4 | import org.squeryl.SessionFactory
5 | import org.scalatra._
6 |
7 | object DatabaseSessionSupport {
8 | val key = {
9 | val n = getClass.getName
10 | if (n.endsWith("$")) n.dropRight(1) else n
11 | }
12 | }
13 |
14 | trait DatabaseSessionSupport { this: ScalatraBase =>
15 | import DatabaseSessionSupport._
16 |
17 | def dbSession = request.get(key).orNull.asInstanceOf[Session]
18 |
19 | before() {
20 | request(key) = SessionFactory.newSession
21 | dbSession.bindToCurrentThread
22 | }
23 |
24 | after() {
25 | dbSession.close
26 | dbSession.unbindFromCurrentThread
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/scala/org/scalatra/book/chapter09/Chapter09.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | import org.scalatra.ScalatraServlet
4 | import org.scalatra.scalate.ScalateSupport
5 |
6 | class Chapter09(appConfig: AppConfig) extends ScalatraServlet with ScalateSupport {
7 |
8 | get("/shorten-url") {
9 | val token = UrlShortener.nextFreeToken
10 | f"${appConfig.webBase}/$token"
11 | }
12 |
13 | notFound {
14 | // remove content type in case it was set through an action
15 | contentType = null
16 | // Try to render a ScalateTemplate if no route matched
17 | findTemplate(requestPath) map { path =>
18 | contentType = "text/html"
19 | layoutTemplate(path)
20 | } orElse serveStaticResource() getOrElse resourceNotFound()
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/chapter12/src/main/scala/com/constructiveproof/crawler/AkkaCrawler.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.crawler
2 |
3 | import java.net.URL
4 |
5 | import akka.actor.{ActorRef, ActorSystem}
6 | import akka.pattern.ask
7 | import akka.util.Timeout
8 | import org.scalatra.{AsyncResult, FutureSupport}
9 |
10 | import scala.concurrent.ExecutionContext
11 | import scala.concurrent.duration._
12 |
13 | class AkkaCrawler(system: ActorSystem, grabActor: ActorRef) extends CrawlerStack with FutureSupport {
14 |
15 | protected implicit def executor: ExecutionContext = system.dispatcher
16 |
17 | implicit val defaultTimeout = new Timeout(2 seconds)
18 |
19 | get("/") {
20 | contentType = "text/html"
21 | new AsyncResult {
22 | val is = grabActor ? new URL(params("url"))
23 | }
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/chapter08/src/test/scala/org/scalatra/book/chapter08/MyScalatraServletSpec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter08
2 |
3 | import org.scalatra.test.specs2._
4 |
5 | class MyScalatraServletSpec extends ScalatraSpec { def is =
6 | "GET / on MyScalatraServlet" ^
7 | "should return status 200" ! root200^
8 | "should be HTML" ! rootHtml^
9 | "should say \"Hi, world!\"" ! rootHi^
10 | end
11 |
12 | addServlet(classOf[MyScalatraServlet], "/*")
13 |
14 | def root200 = get("/") {
15 | status must_== 200
16 | }
17 |
18 | def rootHtml = get("/") {
19 | header("Content-Type") must startWith("text/html;")
20 | }
21 |
22 | def rootHi = get("/") {
23 | body must contain("Hi, world!")
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/scala/com/constructiveproof/hackertracker/init/DatabaseSessionSupport.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.init
2 |
3 | import org.squeryl.Session
4 | import org.squeryl.SessionFactory
5 | import org.scalatra._
6 |
7 | object DatabaseSessionSupport {
8 | val key = {
9 | val n = getClass.getName
10 | if (n.endsWith("$")) n.dropRight(1) else n
11 | }
12 | }
13 |
14 | trait DatabaseSessionSupport { this: ScalatraBase =>
15 | import DatabaseSessionSupport._
16 |
17 | def dbSession = request.get(key).orNull.asInstanceOf[Session]
18 |
19 | before() {
20 | request(key) = SessionFactory.newSession
21 | dbSession.bindToCurrentThread
22 | }
23 |
24 | after() {
25 | dbSession.close
26 | dbSession.unbindFromCurrentThread
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/scala/com/constructiveproof/hackertracker/init/DatabaseSessionSupport.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.init
2 |
3 | import org.squeryl.Session
4 | import org.squeryl.SessionFactory
5 | import org.scalatra._
6 |
7 | object DatabaseSessionSupport {
8 | val key = {
9 | val n = getClass.getName
10 | if (n.endsWith("$")) n.dropRight(1) else n
11 | }
12 | }
13 |
14 | trait DatabaseSessionSupport { this: ScalatraBase =>
15 | import DatabaseSessionSupport._
16 |
17 | def dbSession = request.get(key).orNull.asInstanceOf[Session]
18 |
19 | before() {
20 | request(key) = SessionFactory.newSession
21 | dbSession.bindToCurrentThread
22 | }
23 |
24 | after() {
25 | dbSession.close
26 | dbSession.unbindFromCurrentThread
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/chapter12/src/main/scala/com/constructiveproof/crawler/CrawlerStack.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.crawler
2 |
3 | import org.scalatra._
4 | import scalate.ScalateSupport
5 | import org.fusesource.scalate.{ TemplateEngine, Binding }
6 | import org.fusesource.scalate.layout.DefaultLayoutStrategy
7 | import javax.servlet.http.HttpServletRequest
8 | import collection.mutable
9 |
10 | trait CrawlerStack extends ScalatraServlet with ScalateSupport {
11 |
12 | notFound {
13 | // remove content type in case it was set through an action
14 | contentType = null
15 | // Try to render a ScalateTemplate if no route matched
16 | findTemplate(requestPath) map { path =>
17 | contentType = "text/html"
18 | layoutTemplate(path)
19 | } orElse serveStaticResource() getOrElse resourceNotFound()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/chapter09-sbt/src/test/scala/org/scalatra/book/chapter09/Chapter09Spec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | import org.scalatra.test.specs2._
4 |
5 | class Chapter09Spec extends MutableScalatraSpec {
6 |
7 | val conf = AppConfig.load
8 | sys.props(org.scalatra.EnvironmentKey) = AppEnvironment.asString(conf.env)
9 |
10 | addServlet(new Chapter09(conf), "/*")
11 |
12 | "/ should should execute an action" in {
13 | get("/") {
14 | status must_== 200
15 | }
16 | }
17 |
18 | "/shorten-url should should execute an action" in {
19 | get("/shorten-url") {
20 | status must_== 200
21 | }
22 | }
23 |
24 | "/static.txt should return static file" in {
25 | get("/static.txt") {
26 | status must_== 200
27 | body must_== "this is static text!"
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/chapter12/src/main/scala/com/constructiveproof/crawler/actors/GrabActor.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.crawler.actors
2 |
3 | import java.net.URL
4 | import java.nio.charset.StandardCharsets
5 |
6 | import akka.actor.Actor
7 |
8 | import scala.io.Source
9 |
10 |
11 | class GrabActor extends Actor {
12 |
13 | def receive = {
14 | case url: URL => evaluate(url)
15 | case _ => sender ! "That wasn't a URL."
16 | }
17 |
18 | def evaluate(url: URL) = {
19 | val content = Source.fromURL(
20 | url, StandardCharsets.UTF_8.name()
21 | ).mkString
22 |
23 | content.contains("Akka") match {
24 | case true => sender ! "It's an Akka-related site, very cool."
25 | case false => sender ! "No Akka here, you've made some sort of " +
26 | "mistake in your reading choices."
27 | }
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/chapter13/src/test/scala/com/constructiveproof/hackertracker/HackersControllerSpec.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import com.constructiveproof.hackertracker.init.DatabaseInit
4 | import com.constructiveproof.hackertracker.models.Db
5 | import org.scalatest.{BeforeAndAfter, BeforeAndAfterAll, FunSuite}
6 | import org.scalatra.test.scalatest.ScalatraSuite
7 |
8 | class HackersControllerSpec extends FunSuite with ScalatraSuite with DatabaseInit with BeforeAndAfter with BeforeAndAfterAll {
9 |
10 | addServlet(classOf[HackersController], "/*")
11 |
12 | before {
13 | configureDb()
14 | Db.init
15 | }
16 |
17 | after {
18 | closeDbConnection()
19 | }
20 |
21 | test("simple get") {
22 | get("/new") {
23 | status should equal(302) // It's protected. Redirect!
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/chapter04/src/main/scala/com/constructiveproof/hackertracker/HackerTrackerStack.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import org.scalatra._
4 | import scalate.ScalateSupport
5 | import org.fusesource.scalate.{ TemplateEngine, Binding }
6 | import org.fusesource.scalate.layout.DefaultLayoutStrategy
7 | import javax.servlet.http.HttpServletRequest
8 | import collection.mutable
9 |
10 | trait HackerTrackerStack extends ScalatraServlet with ScalateSupport {
11 |
12 | notFound {
13 | // remove content type in case it was set through an action
14 | contentType = null
15 | // Try to render a ScalateTemplate if no route matched
16 | findTemplate(requestPath) map { path =>
17 | contentType = "text/html"
18 | layoutTemplate(path)
19 | } orElse serveStaticResource() getOrElse resourceNotFound()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/chapter12/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import _root_.akka.actor.{Props, ActorSystem}
2 | import com.constructiveproof.crawler._
3 | import com.constructiveproof.crawler.actors.GrabActor
4 | import org.apache.spark.SparkContext
5 | import org.scalatra._
6 | import javax.servlet.ServletContext
7 |
8 | class ScalatraBootstrap extends LifeCycle {
9 |
10 | val sc = new SparkContext("local[8]", "Spark Demo")
11 |
12 | override def init(context: ServletContext) {
13 | val system = ActorSystem()
14 | val grabActor = system.actorOf(Props[GrabActor])
15 |
16 | context.mount(new CrawlController, "/*")
17 | context.mount(new AkkaCrawler(system, grabActor), "/akka/*")
18 | context.mount(new SparkExampleController(sc), "/spark/*")
19 | }
20 |
21 | override def destroy(context: ServletContext) {
22 | sc.stop()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/chapter08/src/test/scala/org/scalatra/book/chapter08/FoodServletWordSpec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter08
2 |
3 | import org.json4s.JString
4 | import org.scalatra.test.scalatest._
5 |
6 | class FoodServletWordSpec extends ScalatraWordSpec
7 | with JsonBodySupport {
8 |
9 | addServlet(classOf[FoodServlet], "/*")
10 |
11 | "GET /foods/potatoes on FoodServlet" must {
12 | "return status 200" in {
13 | get("/foods/potatoes") {
14 | status should equal(200)
15 | }
16 | }
17 |
18 | "be JSON" in {
19 | get("/foods/potatoes") {
20 | header("Content-Type") should startWith("application/json;")
21 | }
22 | }
23 |
24 | "should have name potatoes" in {
25 | get("/foods/potatoes") {
26 | jsonBody \ "name" should equal(JString("potatoes"))
27 | }
28 | }
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/chapter05/src/main/scala/org/scalatra/book/chapter06/json4s_basics.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter06
2 |
3 | import org.json4s._
4 | import org.json4s.JsonDSL._
5 |
6 | object json4s_basics {
7 |
8 | val fooBar = JObject(
9 | "label" -> JString("Foo bar"),
10 | "fairTrade" -> JBool(true),
11 | "tags" -> JArray(List(JString("bio"), JString("chocolate"))))
12 |
13 | // fooBar: org.json4s.JValue = JObject(List((label,JString(Foo bar)), ...
14 |
15 | import org.json4s.jackson.JsonMethods.parse
16 |
17 | val txt =
18 | """{
19 | | "tags": ["bio","chocolate"],
20 | | "label": "Foo bar",
21 | | "fairTrade": true
22 | |}""".stripMargin
23 |
24 | val parsed = parse(txt)
25 | // parsed: org.json4s.JValue = JObject(List((label,JString(Foo bar)), ...
26 |
27 | fooBar == parsed
28 | // res11: Boolean = true
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/test/scala/com/constructiveproof/hackertracker/HackersControllerSpec.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import com.constructiveproof.hackertracker.init.DatabaseInit
4 | import com.constructiveproof.hackertracker.models.Db
5 | import org.scalatest.{BeforeAndAfter, BeforeAndAfterAll, FunSuite}
6 | import org.scalatra.test.scalatest.ScalatraSuite
7 |
8 | class HackersControllerSpec extends FunSuite with ScalatraSuite with DatabaseInit with BeforeAndAfter with BeforeAndAfterAll {
9 |
10 | addServlet(classOf[HackersController], "/*")
11 |
12 | before {
13 | wipeDb
14 | configureDb()
15 | Db.init
16 | }
17 |
18 | after {
19 | closeDbConnection()
20 | }
21 |
22 | test("simple get") {
23 | get("/new") {
24 | status should equal(302) // It's protected. Redirect!
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/test/scala/com/constructiveproof/hackertracker/HackersControllerSpec.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import java.io.File
4 |
5 | import com.constructiveproof.hackertracker.init.DatabaseInit
6 | import com.constructiveproof.hackertracker.models.Db
7 | import org.scalatest.{BeforeAndAfter, BeforeAndAfterAll, FunSuite}
8 | import org.scalatra.test.scalatest.ScalatraSuite
9 |
10 | class HackersControllerSpec extends FunSuite with ScalatraSuite with DatabaseInit with BeforeAndAfter with BeforeAndAfterAll {
11 |
12 | addServlet(classOf[HackersController], "/*")
13 |
14 | before {
15 | wipeDb
16 | configureDb()
17 | Db.init
18 | }
19 |
20 | after {
21 | closeDbConnection()
22 | }
23 |
24 | test("simple get") {
25 | get("/new") {
26 | status should equal(200)
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/chapter09/src/main/scala/org/scalatra/book/chapter09/Chapter09.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | import org.scalatra.ScalatraServlet
4 | import org.scalatra.scalate.ScalateSupport
5 |
6 | class Chapter09(appConfig: AppConfig) extends ScalatraServlet with ScalateSupport {
7 |
8 | get("/") {
9 | f"Greetings! (isDevelopment = ${isDevelopmentMode}})"
10 | }
11 |
12 | get("/shorten-url") {
13 | val token = UrlShortener.nextFreeToken
14 | f"${appConfig.webBase}/$token"
15 | }
16 |
17 | notFound {
18 | // remove content type in case it was set through an action
19 | contentType = null
20 | // Try to render a ScalateTemplate if no route matched
21 | findTemplate(requestPath) map { path =>
22 | contentType = "text/html"
23 | layoutTemplate(path)
24 | } orElse serveStaticResource() getOrElse resourceNotFound()
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/chapter09-docker/src/main/scala/org/scalatra/book/chapter09/Chapter09.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | import org.scalatra.ScalatraServlet
4 | import org.scalatra.scalate.ScalateSupport
5 |
6 | class Chapter09(appConfig: AppConfig) extends ScalatraServlet with ScalateSupport {
7 |
8 | get("/") {
9 | f"Greetings! (isDevelopment = ${isDevelopmentMode})"
10 | }
11 |
12 | get("/shorten-url") {
13 | val token = UrlShortener.nextFreeToken
14 | f"${appConfig.webBase}/$token"
15 | }
16 |
17 | notFound {
18 | // remove content type in case it was set through an action
19 | contentType = null
20 | // Try to render a ScalateTemplate if no route matched
21 | findTemplate(requestPath) map { path =>
22 | contentType = "text/html"
23 | layoutTemplate(path)
24 | } orElse serveStaticResource() getOrElse resourceNotFound()
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/chapter09-standalone/src/main/scala/org/scalatra/book/chapter09/Chapter09.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | import org.scalatra.ScalatraServlet
4 | import org.scalatra.scalate.ScalateSupport
5 |
6 | class Chapter09(appConfig: AppConfig) extends ScalatraServlet with ScalateSupport {
7 |
8 | get("/") {
9 | f"Greetings! (isDevelopment = ${isDevelopmentMode}})"
10 | }
11 |
12 | get("/shorten-url") {
13 | val token = UrlShortener.nextFreeToken
14 | f"${appConfig.webBase}/$token"
15 | }
16 |
17 | notFound {
18 | // remove content type in case it was set through an action
19 | contentType = null
20 | // Try to render a ScalateTemplate if no route matched
21 | findTemplate(requestPath) map { path =>
22 | contentType = "text/html"
23 | layoutTemplate(path)
24 | } orElse serveStaticResource() getOrElse resourceNotFound()
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/chapter10/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import javax.servlet.ServletContext
2 |
3 | import org.scalatra._
4 | import org.scalatra.book.chapter10.{Chapter10App, DbSetup}
5 | import slick.driver.H2Driver.api._
6 |
7 | import scala.concurrent.Await
8 | import scala.concurrent.duration.Duration
9 |
10 | class ScalatraBootstrap extends LifeCycle {
11 |
12 | val jdbcUrl = "jdbc:h2:mem:chapter10;DB_CLOSE_DELAY=-1"
13 | val jdbcDriverClass = "org.h2.Driver"
14 | val db = Database.forURL(jdbcUrl, driver = jdbcDriverClass)
15 |
16 | val app = new Chapter10App(db)
17 |
18 | override def init(context: ServletContext): Unit = {
19 |
20 | val res = db.run(DbSetup.createDatabase)
21 |
22 | Await.result(res, Duration(5, "seconds"))
23 |
24 | context.mount(app, "/*")
25 |
26 | }
27 |
28 | override def destroy(context: ServletContext): Unit = {
29 | db.close()
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/chapter09-docker/src/main/scala/ScalatraLauncher.scala:
--------------------------------------------------------------------------------
1 | import org.eclipse.jetty.server.Server
2 |
3 | import org.eclipse.jetty.server._
4 | import org.eclipse.jetty.webapp.WebAppContext
5 | import org.scalatra.book.chapter09.AppConfig
6 | import org.scalatra.servlet.ScalatraListener
7 |
8 | object ScalatraLauncher extends App {
9 |
10 | val appConfig = AppConfig.load
11 |
12 | val server = new Server
13 | server.setStopTimeout(5000)
14 | server.setStopAtShutdown(true)
15 |
16 | val connector = new ServerConnector(server)
17 | connector.setPort(appConfig.port)
18 |
19 | server.addConnector(connector)
20 |
21 | val webAppContext = new WebAppContext
22 | webAppContext.setContextPath("/")
23 | webAppContext.setResourceBase(appConfig.assetsDirectory)
24 | webAppContext.setEventListeners(Array(new ScalatraListener))
25 | server.setHandler(webAppContext)
26 |
27 | server.start
28 | server.join
29 |
30 | }
--------------------------------------------------------------------------------
/chapter09-standalone/src/main/scala/ScalatraLauncher.scala:
--------------------------------------------------------------------------------
1 | import org.eclipse.jetty.server.{Server, ServerConnector}
2 | import org.eclipse.jetty.webapp.WebAppContext
3 | import org.scalatra.book.chapter09.AppConfig
4 | import org.scalatra.servlet.ScalatraListener
5 |
6 | object ScalatraLauncher extends App {
7 |
8 | val conf = AppConfig.load
9 |
10 | val server = new Server
11 | server.setStopTimeout(5000)
12 | server.setStopAtShutdown(true)
13 |
14 | val connector = new ServerConnector(server)
15 | connector.setHost("127.0.0.1")
16 | connector.setPort(conf.port)
17 |
18 | server.addConnector(connector)
19 |
20 | val webAppContext = new WebAppContext
21 | webAppContext.setContextPath("/")
22 | webAppContext.setResourceBase(conf.assetsDirectory)
23 | webAppContext.setEventListeners(Array(new ScalatraListener))
24 | server.setHandler(webAppContext)
25 |
26 | server.start
27 | server.join
28 |
29 | }
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/auth/strategies/OurBasicAuthStrategy.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.auth.strategies
2 |
3 | import org.scalatra.auth.strategy.{BasicAuthStrategy}
4 | import org.scalatra.{ScalatraBase}
5 | import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
6 | import com.constructiveproof.hackertracker.models.User
7 |
8 | class OurBasicAuthStrategy(protected override val app: ScalatraBase, realm: String)
9 | extends BasicAuthStrategy[User](app, realm) {
10 |
11 | protected def validate(userName: String, password: String)(implicit request: HttpServletRequest, response: HttpServletResponse): Option[User] = {
12 | if(userName == "scalatra" && password == "scalatra") Some(User("scalatra"))
13 | else None
14 | }
15 |
16 | protected def getUserId(user: User)(implicit request: HttpServletRequest, response: HttpServletResponse): String = user.id
17 | }
18 |
19 |
20 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/scala/com/constructiveproof/hackertracker/DatabaseSetupController.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import com.constructiveproof.hackertracker.models.Db
4 |
5 | class DatabaseSetupController extends HackerTrackerStack {
6 |
7 |
8 | before() {
9 | contentType = "text/html"
10 | }
11 |
12 | /**
13 | * Create a new database.
14 | */
15 | get("/create") {
16 | Db.init
17 | flash("notice") = "Database created"
18 | redirect("/hackers/new")
19 | }
20 |
21 | /** *
22 | * Print the schema definition for our database out to the console.
23 | */
24 | get("/dump-schema") {
25 | Db.printDdl
26 | "Take a look in your running console and you'll see the data definition list printout."
27 | }
28 |
29 | /**
30 | * Whack the database.
31 | */
32 | get("/drop") {
33 | Db.drop
34 | "The database has been dropped."
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/chapter08/src/test/scala/org/scalatra/book/chapter08/FoodServletSpec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter08
2 |
3 | import org.scalatra.test.specs2._
4 |
5 | import org.json4s._
6 | import org.json4s.jackson.JsonMethods
7 |
8 | class FoodServletSpec extends ScalatraSpec with JsonBodySupport {
9 | def is = s2"""
10 | GET /foods/potatoes on FoodServlet
11 | should return status 200 $potatoesOk
12 | should be JSON $potatoesJson
13 | should contain name potatoes $potatoesName
14 | """
15 |
16 | addServlet(classOf[FoodServlet], "/*")
17 |
18 | def potatoesOk = get("/foods/potatoes") {
19 | status must_== 200
20 | }
21 |
22 | def potatoesJson = get("/foods/potatoes") {
23 | header("Content-Type") must startWith ("application/json;")
24 | }
25 |
26 | def potatoesName = get("/foods/potatoes") {
27 | jsonBody \ "name" must_== JString("potatoes")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/chapter03/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
17 |
18 | org.scalatra.servlet.ScalatraListener
19 |
20 |
21 |
22 | 1024
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/chapter05/src/main/scala/org/scalatra/book/chapter06/MyJsonScalazRoutes.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter06
2 |
3 | import org.json4s._
4 | import org.scalatra._
5 | import org.scalatra.json._
6 |
7 | import scalaz._, Scalaz._
8 |
9 | trait MyJsonScalazRoutes extends ScalatraBase with JacksonJsonSupport {
10 |
11 | // be able to handle scalaz' \/ as return value, simply unwrap the value from the container
12 | override def renderPipeline: RenderPipeline = ({
13 | case \/-(r) => r
14 | case -\/(l) => l
15 | }: RenderPipeline) orElse super.renderPipeline
16 |
17 | post("/foods_alt") {
18 |
19 | for {
20 | label <- (parsedBody \ "label").extractOpt[String] \/> BadRequest()
21 | fairTrade <- (parsedBody \ "fairTrade").extractOpt[Boolean] \/> BadRequest()
22 | tags <- (parsedBody \ "tags").extractOpt[List[String]] \/> BadRequest()
23 | } yield (label, fairTrade, tags)
24 |
25 | }
26 |
27 | }
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/scala/com/constructiveproof/hackertracker/auth/strategies/OurBasicAuthStrategy.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.auth.strategies
2 |
3 | import org.scalatra.auth.strategy.{BasicAuthStrategy}
4 | import org.scalatra.{ScalatraBase}
5 | import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
6 | import com.constructiveproof.hackertracker.models.User
7 |
8 | class OurBasicAuthStrategy(protected override val app: ScalatraBase, realm: String)
9 | extends BasicAuthStrategy[User](app, realm) {
10 |
11 | protected def validate(username: String, password: String)(implicit request: HttpServletRequest, response: HttpServletResponse): Option[User] = {
12 | if(username == "scalatra" && password == "scalatra") Some(User("scalatra"))
13 | else None
14 | }
15 |
16 | protected def getUserId(user: User)(implicit request: HttpServletRequest, response: HttpServletResponse): String = user.id
17 | }
18 |
19 |
20 |
--------------------------------------------------------------------------------
/chapter12/src/main/scala/com/constructiveproof/crawler/SparkExampleController.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.crawler
2 |
3 | import org.apache.spark.SparkContext
4 | import org.scalatra.FutureSupport
5 | import scala.concurrent.{Future, ExecutionContext}
6 |
7 | class SparkExampleController(sc: SparkContext) extends CrawlerStack with FutureSupport {
8 |
9 | protected implicit def executor: ExecutionContext = ExecutionContext.global
10 |
11 | get("/count/:word") {
12 | val word = params("word")
13 | Future {
14 | val occurrenceCount = WordCounter.count(word, sc)
15 | s"Found $occurrenceCount occurrences of $word"
16 | }
17 | }
18 |
19 | }
20 |
21 | object WordCounter {
22 |
23 | def count(word: String, sc: SparkContext) = {
24 | val data = "/path/to/data.csv" // <-- Change this to match your file location and name
25 | val lines = sc.textFile(data)
26 | lines.filter(line => line.contains(word)).cache().count()
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/chapter06/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 | org.scalatra.servlet.ScalatraListener
19 |
20 |
21 |
22 | 1024
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/chapter05/src/main/scala/org/scalatra/book/chapter06/MyJsonRoutes.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter06
2 |
3 | import org.scalatra._
4 | import org.scalatra.json._
5 |
6 | import org.json4s._
7 | import org.json4s.JsonDSL._
8 |
9 | trait MyJsonRoutes extends ScalatraBase with JacksonJsonSupport {
10 |
11 | get("/foods/foo_bar") {
12 | val productJson =
13 | ("label" -> "Foo bar") ~
14 | ("fairTrade" -> true) ~
15 | ("tags" -> List("bio", "chocolate"))
16 |
17 | productJson
18 | }
19 |
20 | post("/foods") {
21 |
22 | def parseProduct(jv: JValue): (String, Boolean, List[String]) = {
23 | val label = (jv \ "label").extract[String]
24 | val fairTrade = (jv \ "fairTrade").extract[Boolean]
25 | val tags = (jv \ "tags").extract[List[String]]
26 |
27 | (label, fairTrade, tags)
28 | }
29 |
30 | val product = parseProduct(parsedBody)
31 | println(product)
32 | }
33 |
34 | }
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/chapter06/src/main/webapp/WEB-INF/views/index.scaml:
--------------------------------------------------------------------------------
1 | - import org.scalatra.book.chapter06.Document
2 |
3 | - attributes("title") = "DocumentStorage"
4 | -@ val files: Seq[Document]
5 |
6 | .row
7 | .col-lg-6
8 | %h4 Download a document
9 | %table.table
10 | %tr
11 | %th #
12 | %th Name
13 | %th Description
14 | %th Download
15 | -for (doc <- files)
16 | %tr
17 | %td= doc.id
18 | %td= doc.name
19 | %td= doc.description
20 | %td
21 | %a(href={uri("/documents/" + doc.id)}) download
22 |
23 | .col-lg-3
24 | %h4 Create a new document
25 | %form(enctype="multipart/form-data" method="post" action="/documents")
26 | %div.form-group
27 | %label File:
28 | %input(type="file" name="file")
29 | %div.form-group
30 | %label Description:
31 | %input.form-control(type="text" name="description" value="")
32 | %input.btn.btn-default(type="submit")
33 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/scala/com/constructiveproof/hackertracker/HackerTrackerStack.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import org.scalatra._
4 | import scalate.ScalateSupport
5 | import org.fusesource.scalate.TemplateEngine
6 | import org.fusesource.scalate.layout.DefaultLayoutStrategy
7 | import javax.servlet.http.HttpServletRequest
8 | import collection.mutable
9 | import com.constructiveproof.hackertracker.init.DatabaseSessionSupport
10 |
11 | trait HackerTrackerStack extends ScalatraServlet with ScalateSupport
12 | with DatabaseSessionSupport with FlashMapSupport with MethodOverride {
13 |
14 | notFound {
15 | // remove content type in case it was set through an action
16 | contentType = null
17 | // Try to render a ScalateTemplate if no route matched
18 | findTemplate(requestPath) map { path =>
19 | contentType = "text/html"
20 | layoutTemplate(path)
21 | } orElse serveStaticResource() getOrElse resourceNotFound()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/scala/com/constructiveproof/hackertracker/HackerTrackerStack.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import org.scalatra._
4 | import scalate.ScalateSupport
5 | import org.fusesource.scalate.TemplateEngine
6 | import org.fusesource.scalate.layout.DefaultLayoutStrategy
7 | import javax.servlet.http.HttpServletRequest
8 | import collection.mutable
9 | import com.constructiveproof.hackertracker.init.DatabaseSessionSupport
10 |
11 | trait HackerTrackerStack extends ScalatraServlet with ScalateSupport
12 | with DatabaseSessionSupport with FlashMapSupport with MethodOverride {
13 |
14 | notFound {
15 | // remove content type in case it was set through an action
16 | contentType = null
17 | // Try to render a ScalateTemplate if no route matched
18 | findTemplate(requestPath) map { path =>
19 | contentType = "text/html"
20 | layoutTemplate(path)
21 | } orElse serveStaticResource() getOrElse resourceNotFound()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/chapter08/src/test/scala/org/scalatra/book/chapter08/NukeLauncherSpec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter08
2 |
3 | import org.scalatra.test.specs2._
4 | import org.specs2.mutable.After
5 |
6 | class NukeLauncherSpec extends MutableScalatraSpec with After {
7 | sequential
8 |
9 | val stubLauncher = new StubNukeLauncher
10 | addServlet(new NukeLauncherServlet(stubLauncher), "/*")
11 |
12 | def after: Any = stubLauncher.isLaunched = false
13 |
14 | def launch[A](code: String)(f: => A): A = post("/launch", "code" -> code) { f }
15 |
16 | "The wrong pass code" should {
17 | "respond with forbidden" in {
18 | launch("wrong") {
19 | status must_== 403
20 | }
21 | }
22 |
23 | "not launch the nukes" in {
24 | launch("wrong") {
25 | stubLauncher.isLaunched must_== false
26 | }
27 | }
28 | }
29 |
30 | "The right pass code" should {
31 | "launch the nukes" in {
32 | launch("password123") {
33 | stubLauncher.isLaunched must_== true
34 | }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/chapter09/src/test/scala/org/scalatra/book/chapter09/Chapter09Spec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | import org.scalatra.test.specs2._
4 |
5 | class Chapter09Spec extends MutableScalatraSpec {
6 |
7 | val conf = AppConfig.load
8 | sys.props(org.scalatra.EnvironmentKey) = AppEnvironment.asString(conf.env)
9 |
10 | addServlet(new Chapter09(conf), "/*")
11 |
12 | "/ should should execute an action" in {
13 | get("/") {
14 | status must_== 200
15 | }
16 | }
17 |
18 | "/shorten-url should should execute an action" in {
19 | get("/shorten-url") {
20 | status must_== 200
21 | }
22 | }
23 |
24 | "/static.txt should return static file" in {
25 | get("/static.txt") {
26 | status must_== 200
27 | body must_== "this is static text!"
28 | }
29 | }
30 |
31 | "/hello-scalate should render a template" in {
32 | get("/hello-scalate") {
33 | status must_== 200
34 | body must_==
35 | """Hello, Scalate!
36 | |""".stripMargin
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/comments-collector/src/main/scala/comments/data.scala:
--------------------------------------------------------------------------------
1 | package comments
2 |
3 | import com.mongodb.casbah.Imports._
4 | import org.bson.types.ObjectId
5 |
6 | case class Comment(id: String, url: String, title: String, body: String)
7 |
8 | case class CommentsRepository(collection: MongoCollection) {
9 |
10 | def create(url: String, title: String, body: String): String = {
11 | val m = MongoDBObject("url" -> url, "title" -> title, "body" -> body)
12 | collection += m
13 | m.getAs[ObjectId]("_id").get.toString
14 | }
15 |
16 | def findAll: List[Comment] = {
17 | collection.find.toList flatMap toComment
18 | }
19 |
20 | def findByUrl(url: String): List[Comment] = {
21 | collection.find(MongoDBObject("url" -> url)).toList flatMap toComment
22 | }
23 |
24 | protected def toComment(db: DBObject): Option[Comment] = for {
25 | id <- db.getAs[ObjectId]("_id")
26 | u <- db.getAs[String]("url")
27 | t <- db.getAs[String]("title")
28 | b <- db.getAs[String]("body")
29 | } yield Comment(id.toString, u, t, b)
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/chapter09-docker/src/test/scala/org/scalatra/book/chapter09/Chapter09Spec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | import org.scalatra.test.specs2._
4 |
5 | class Chapter09Spec extends MutableScalatraSpec {
6 |
7 | val conf = AppConfig.load
8 | sys.props(org.scalatra.EnvironmentKey) = AppEnvironment.asString(conf.env)
9 |
10 | addServlet(new Chapter09(conf), "/*")
11 |
12 | "/ should should execute an action" in {
13 | get("/") {
14 | status must_== 200
15 | }
16 | }
17 |
18 | "/shorten-url should should execute an action" in {
19 | get("/shorten-url") {
20 | status must_== 200
21 | }
22 | }
23 |
24 | "/static.txt should return static file" in {
25 | get("/static.txt") {
26 | status must_== 200
27 | body must_== "this is static text!"
28 | }
29 | }
30 |
31 | "/hello-scalate should render a template" in {
32 | get("/hello-scalate") {
33 | status must_== 200
34 | body must_==
35 | """Hello, Scalate!
36 | |""".stripMargin
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/chapter09-standalone/src/test/scala/org/scalatra/book/chapter09/Chapter09Spec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | import org.scalatra.test.specs2._
4 |
5 | class Chapter09Spec extends MutableScalatraSpec {
6 |
7 | val conf = AppConfig.load
8 | sys.props(org.scalatra.EnvironmentKey) = AppEnvironment.asString(conf.env)
9 |
10 | addServlet(new Chapter09(conf), "/*")
11 |
12 | "/ should should execute an action" in {
13 | get("/") {
14 | status must_== 200
15 | }
16 | }
17 |
18 | "/shorten-url should should execute an action" in {
19 | get("/shorten-url") {
20 | status must_== 200
21 | }
22 | }
23 |
24 | "/static.txt should return static file" in {
25 | get("/static.txt") {
26 | status must_== 200
27 | body must_== "this is static text!"
28 | }
29 | }
30 |
31 | "/hello-scalate should render a template" in {
32 | get("/hello-scalate") {
33 | status must_== 200
34 | body must_==
35 | """Hello, Scalate!
36 | |""".stripMargin
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/chapter02/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
--------------------------------------------------------------------------------
/chapter04/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
--------------------------------------------------------------------------------
/chapter09/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/scala/com/constructiveproof/hackertracker/DatabaseSetupController.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import com.constructiveproof.hackertracker.models.Db
4 | import com.constructiveproof.hackertracker.auth.OurBasicAuthenticationSupport
5 |
6 | class DatabaseSetupController extends HackerTrackerStack with OurBasicAuthenticationSupport {
7 |
8 |
9 | before() {
10 | contentType = "text/html"
11 | basicAuth
12 | }
13 |
14 | /**
15 | * Create a new database.
16 | */
17 | get("/create") {
18 | Db.init
19 | flash("notice") = "Database created!"
20 | redirect("/hackers/new")
21 | }
22 |
23 | /** *
24 | * Print the schema definition for our database out to the console.
25 | */
26 | get("/dump-schema") {
27 | Db.printDdl
28 | "Take a look in your running console and you'll see the data definition list printout."
29 | }
30 |
31 | /**
32 | * Whack the database.
33 | */
34 | get("/drop") {
35 | Db.drop
36 | "The database has been dropped."
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/chapter12/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
--------------------------------------------------------------------------------
/chapter13/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
--------------------------------------------------------------------------------
/chapter09-sbt/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
--------------------------------------------------------------------------------
/chapter09-sbtweb/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
--------------------------------------------------------------------------------
/chapter09-standalone/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
--------------------------------------------------------------------------------
/comments-collector/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
--------------------------------------------------------------------------------
/chapter09-docker/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
71 | data/logs/*
72 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/.gitignore:
--------------------------------------------------------------------------------
1 | ## generic files to ignore
2 | *~
3 | *.lock
4 | *.DS_Store
5 | *.swp
6 | *.out
7 |
8 | # rails specific
9 | *.sqlite3
10 | config/database.yml
11 | log/*
12 | tmp/*
13 |
14 | # java specific
15 | *.class
16 |
17 | # python specific
18 | *.pyc
19 |
20 | # xcode/iphone specific
21 | build/*
22 | *.pbxuser
23 | *.mode2v3
24 | *.mode1v3
25 | *.perspective
26 | *.perspectivev3
27 | *~.nib
28 |
29 | # akka specific
30 | logs/*
31 |
32 | # sbt specific
33 | target/
34 | project/boot
35 | lib_managed/*
36 | project/build/target
37 | project/build/lib_managed
38 | project/build/src_managed
39 | project/plugins/lib_managed
40 | project/plugins/target
41 | project/plugins/src_managed
42 | project/plugins/project
43 |
44 | core/lib_managed
45 | core/target
46 | pubsub/lib_managed
47 | pubsub/target
48 |
49 | # eclipse specific
50 | .metadata
51 | jrebel.lic
52 | .settings
53 | .classpath
54 | .project
55 |
56 | .ensime*
57 | *.sublime-*
58 | .cache
59 |
60 | # intellij
61 | *.eml
62 | *.iml
63 | *.ipr
64 | *.iws
65 | .*.sw?
66 | .idea
67 |
68 | # paulp script
69 | /.lib/
70 |
--------------------------------------------------------------------------------
/chapter12/src/main/scala/com/constructiveproof/crawler/CrawlController.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.crawler
2 |
3 | import java.net.URL
4 | import java.nio.charset.StandardCharsets
5 |
6 | import org.scalatra._
7 |
8 | import scala.concurrent.{ExecutionContext, Future}
9 | import scala.io.Source
10 |
11 | class CrawlController extends CrawlerStack with FutureSupport {
12 |
13 | protected implicit def executor = ExecutionContext.global
14 |
15 | get("/") {
16 | contentType = "text/html"
17 | new AsyncResult {
18 | val is =
19 | Grabber.evaluate(new URL(params("url")))
20 | }
21 | }
22 |
23 | }
24 |
25 | object Grabber {
26 | def evaluate(url: URL)(implicit ctx: ExecutionContext): Future[String] = {
27 | Future {
28 | val content = Source.fromURL(
29 | url, StandardCharsets.UTF_8.name()
30 | ).mkString
31 | content.contains("Scala") match {
32 | case true => "It's a Scala site, very cool."
33 | case false => "Whoops, you've made some sort " +
34 | "of mistake in your reading choices."
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/DatabaseSetupController.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import com.constructiveproof.hackertracker.auth.OurBasicAuthenticationSupport
4 | import com.constructiveproof.hackertracker.models.Db
5 | import com.constructiveproof.hackertracker.stacks.BrowserStack
6 |
7 | class DatabaseSetupController extends BrowserStack with OurBasicAuthenticationSupport {
8 |
9 |
10 | before() {
11 | contentType = "text/html"
12 | basicAuth
13 | }
14 |
15 | /**
16 | * Create a new database.
17 | */
18 | get("/create") {
19 | Db.create
20 | flash("notice") = "Database created!"
21 | redirect("/hackers/new")
22 | }
23 |
24 | /** *
25 | * Print the schema definition for our database out to the console.
26 | */
27 | get("/dump-schema") {
28 | Db.printDdl
29 | "Take a look in your running console and you'll see the data definition list printout."
30 | }
31 |
32 | /**
33 | * Whack the database.
34 | */
35 | get("/drop") {
36 | Db.drop
37 | "The database has been dropped."
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/comments-collector/src/test/scala/comments/CommentsServletSpec.scala:
--------------------------------------------------------------------------------
1 | package comments
2 |
3 | import org.scalatra.test.specs2._
4 |
5 | import org.scalatra.swagger._
6 | import com.mongodb.casbah.Imports._
7 |
8 | // For more on Specs2, see http://etorreborre.github.com/specs2/guide/org.specs2.guide.QuickStart.html
9 | class CommentsServletSpec extends ScalatraSpec { def is =
10 | "GET / on CommentsServlet" ^
11 | "should return status 200" ! root200^
12 | end
13 |
14 | implicit val apiInfo = new ApiInfo("The comments API",
15 | "Docs for the comments API",
16 | "http://www.manning.com/carrero2/",
17 | "Ross", "MIT", "http://scalatra.org")
18 |
19 | implicit val swagger = new Swagger("1.0", "1", apiInfo)
20 |
21 | val mongoClient = MongoClient()
22 | val mongoColl = mongoClient("comments_collector")("comments")
23 | val comments = CommentsRepository(mongoColl)
24 |
25 | addServlet(new CommentsApi(comments), "/api/*")
26 |
27 | def root200 = get("/api/comments") {
28 | status must_== 200
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/chapter06/project/build.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | import Keys._
3 |
4 | import org.scalatra.sbt._
5 | import com.earldouglas.xwp.JettyPlugin
6 |
7 | object Chapter06Build extends Build {
8 |
9 | val Organization = "org.scalatra"
10 | val Name = "file-upload"
11 | val Version = "0.1.0-SNAPSHOT"
12 | val ScalaVersion = "2.11.6"
13 | val ScalatraVersion = "2.4.0"
14 |
15 | lazy val project = Project (
16 | Name,
17 | file("."),
18 | settings = Defaults.defaultConfigs ++ ScalatraPlugin.scalatraSettings ++ Seq(
19 | organization := Organization,
20 | name := Name,
21 | version := Version,
22 | scalaVersion := ScalaVersion,
23 | fork in Test := true,
24 | libraryDependencies ++= Seq(
25 | "org.scalatra" %% "scalatra" % ScalatraVersion,
26 | "org.scalatra" %% "scalatra-scalate" % ScalatraVersion,
27 | "org.scalatra" %% "scalatra-specs2" % ScalatraVersion % "test",
28 | "ch.qos.logback" % "logback-classic" % "1.0.6" % "runtime",
29 | "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided"
30 | )
31 | )
32 | ).enablePlugins(JettyPlugin)
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/comments-collector/src/main/scala/comments/frontend.scala:
--------------------------------------------------------------------------------
1 | package comments
2 |
3 | import org.scalatra._
4 | import org.scalatra.scalate.ScalateSupport
5 | import java.net.URLEncoder
6 |
7 | import scalaz._, Scalaz._
8 |
9 | class CommentsFrontend(commentsRepo: CommentsRepository) extends ScalatraServlet with ScalateSupport with ScalazSupport {
10 | get("/") {
11 | val urls = commentsRepo.findAll.groupBy(_.url).keys.toSeq.sorted
12 | layoutTemplate("index", "title" -> "Articles with comments", "urls" -> urls)
13 | }
14 |
15 | get("/:url") {
16 | val url = params("url")
17 | val urls = commentsRepo.findAll.groupBy(_.url).keys.toSeq.sorted
18 | layoutTemplate("comments", "urls" -> urls, "url" -> url, "comments" -> commentsRepo.findByUrl(url))
19 | }
20 |
21 | post("/:url") {
22 | for {
23 | url <- params("url").right
24 | title <- params.get("title") \/> BadRequest("title is required")
25 | body <- params.get("body") \/> BadRequest("body is required")
26 | } yield {
27 | commentsRepo.create(url, title, body)
28 | Found(s"${routeBasePath}/${URLEncoder.encode(url, "utf-8")}")
29 | }
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/chapter03/src/main/scala/org/scalatra/book/chapter03/Artist.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter03
2 |
3 | import scala.xml.Node
4 | import scala.collection.concurrent.TrieMap
5 |
6 | case class Artist(name: String, nationality: String, isActive: Boolean) {
7 | def toXml: Node =
8 |
9 | {name}
10 | {nationality}
11 | {isActive}
12 |
13 |
14 | def toJson: String = ??? // Left as an exercise to the JSON chapter.
15 | }
16 |
17 | object Artist {
18 | // URLs are cleaner without spaces.
19 | def fromParam(name: String) = name.replace('_', ' ')
20 |
21 | private val db = TrieMap.empty[String, Artist]
22 |
23 | def find(name: String): Option[Artist] = db.get(fromParam(name))
24 |
25 | def fetchAll(): List[Artist] = db.values.toList
26 |
27 | def exists(name: String): Boolean = db.contains(fromParam(name))
28 |
29 | def save(artist: Artist): Option[Artist] = db.put(artist.name, artist)
30 |
31 | def update(artist: Artist): Option[Artist] = db.put(artist.name, artist)
32 |
33 | def delete(name: String): Option[Artist] = db.remove(fromParam(name))
34 | }
--------------------------------------------------------------------------------
/chapter07-twirl/project/build.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | import Keys._
3 |
4 | import org.scalatra.sbt.ScalatraPlugin
5 | import com.earldouglas.xwp.JettyPlugin
6 | import play.twirl.sbt.SbtTwirl
7 |
8 | object Chapter07TwirlBuild extends Build {
9 | val Organization = "org.scalatra"
10 | val Name = "views-twirl"
11 | val Version = "0.1.0-SNAPSHOT"
12 | val ScalaVersion = "2.11.6"
13 | val ScalatraVersion = "2.4.0"
14 |
15 | lazy val project = Project (
16 | Name,
17 | file("."),
18 | settings = Seq(
19 | organization := Organization,
20 | name := Name,
21 | version := Version,
22 | scalaVersion := ScalaVersion,
23 | fork in Test := true,
24 | resolvers += Classpaths.typesafeReleases,
25 | libraryDependencies ++= Seq(
26 | "org.scalatra" %% "scalatra" % ScalatraVersion,
27 | "org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
28 | "ch.qos.logback" % "logback-classic" % "1.1.3" % "runtime",
29 | "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided"
30 | )
31 | )
32 | ).settings(ScalatraPlugin.scalatraSettings:_*)
33 | .enablePlugins(SbtTwirl, JettyPlugin)
34 | }
35 |
--------------------------------------------------------------------------------
/comments-collector/src/main/webapp/WEB-INF/layouts/default.scaml:
--------------------------------------------------------------------------------
1 | - response.setContentType("text/html")
2 | - import java.net.URLEncoder.encode
3 | -@ val title: String
4 | -@ val body: String
5 | -@ val urls: Seq[String]
6 |
7 | !!!
8 | %html
9 | %head
10 | %title= title
11 | %link(rel="stylesheet" href={uri("/css/bootstrap.min.css")})
12 | %link(rel="stylesheet" href={uri("/css/site.css")})
13 | %script(src={uri("https://code.jquery.com/jquery-1.10.2.min.js")})
14 | %script(src={uri("/js/bootstrap.min.js")})
15 | %body
16 |
17 | .container
18 | .header
19 | %ul.nav.nav-pills.pull-right
20 | %li
21 | %a(href={uri("/")}) Home
22 | %li.dropdown
23 | %a(href="#" class="dropdown-toggle" data-toggle="dropdown") Explore
24 |
25 | %ul.dropdown-menu
26 | - if (urls.size > 0)
27 | - for (url <- urls)
28 | %li
29 | %a(href={uri("/"+encode(url, "utf-8"))})= url
30 | - else
31 | %li.dropdown-header No comments yet
32 |
33 | %h2 comments-collector
34 |
35 | != body
36 |
37 |
38 |
--------------------------------------------------------------------------------
/chapter07/project/build.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | import Keys._
3 |
4 | import org.scalatra.sbt.ScalatraPlugin
5 | import com.earldouglas.xwp.JettyPlugin
6 |
7 | object Chapter07Build extends Build {
8 | val Organization = "org.scalatra"
9 | val Name = "views-scalate"
10 | val Version = "0.1.0-SNAPSHOT"
11 | val ScalaVersion = "2.11.6"
12 | val ScalatraVersion = "2.4.0"
13 |
14 | lazy val project = Project (
15 | Name,
16 | file("."),
17 | settings = Defaults.defaultConfigs ++ ScalatraPlugin.scalatraSettings ++ Seq(
18 | organization := Organization,
19 | name := Name,
20 | version := Version,
21 | scalaVersion := ScalaVersion,
22 | fork in Test := true,
23 | resolvers += Classpaths.typesafeReleases,
24 | libraryDependencies ++= Seq(
25 | "org.scalatra" %% "scalatra" % ScalatraVersion,
26 | "org.scalatra" %% "scalatra-scalate" % ScalatraVersion,
27 | "org.scalatra" %% "scalatra-specs2" % ScalatraVersion % "test",
28 | "ch.qos.logback" % "logback-classic" % "1.1.3" % "runtime",
29 | "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided"
30 | )
31 | )
32 | ).enablePlugins(JettyPlugin)
33 | }
34 |
--------------------------------------------------------------------------------
/chapter13/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import com.constructiveproof.hackertracker._
2 | import com.constructiveproof.hackertracker.init.DatabaseInit
3 | import javax.servlet.ServletContext
4 | import org.scalatra.LifeCycle
5 | import org.scalatra.swagger.{ApiInfo, Swagger}
6 |
7 | class ScalatraBootstrap extends LifeCycle with DatabaseInit {
8 |
9 | implicit val apiInfo = new ApiInfo(
10 | "The HackerTracker API",
11 | "Docs for the HackerTracker API",
12 | "http://www.constructiveproof.com/hacker-tracker/tos.html",
13 | "apiteam@constructiveproof.com",
14 | "MIT",
15 | "http://opensource.org/licenses/MIT")
16 |
17 | implicit val swagger = new Swagger("1.2", "1.0.0", apiInfo)
18 |
19 | override def init(context: ServletContext) {
20 | configureDb()
21 | context.mount(new ApiController, "/hackers-api", "hackers-api")
22 | context.mount(new HackersSwagger, "/api-docs")
23 | context.mount(new DatabaseSetupController, "/database")
24 | context.mount(new HackersController, "/hackers")
25 | context.mount(new SessionsController, "/sessions")
26 | }
27 |
28 | override def destroy(context: ServletContext) {
29 | closeDbConnection()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/chapter05/src/main/scala/org/scalatra/book/chapter06/json4s_custom_serializers.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter06
2 |
3 | import org.json4s._
4 | import org.json4s.JsonDSL._
5 |
6 | sealed trait Fact
7 | case class Energy(value: Int) extends Fact
8 | case class Carbohydrate(value: Double) extends Fact
9 | case class Fat(value: Double) extends Fact
10 | case class Protein(value: Double) extends Fact
11 |
12 | case class NutritionFacts(
13 | energy: Energy,
14 | carbohydrate: Carbohydrate,
15 | fat: Fat,
16 | protein: Protein)
17 |
18 | class NutritionFactsSerializer
19 | extends CustomSerializer[NutritionFacts](implicit formats => ({
20 | case jv: JValue =>
21 | val e = (jv \ "energy").extract[Int]
22 | val c = (jv \ "carbohydrate").extract[Double]
23 | val f = (jv \ "fat").extract[Double]
24 | val p = (jv \ "protein").extract[Double]
25 |
26 | NutritionFacts(
27 | Energy(e), Carbohydrate(c), Fat(f), Protein(p))
28 | }, {
29 | case facts: NutritionFacts =>
30 | ("energy" -> facts.energy.value) ~
31 | ("carbohydrate" -> facts.carbohydrate.value) ~
32 | ("fat" -> facts.fat.value) ~
33 | ("protein" -> facts.protein.value)
34 | }))
35 |
36 |
--------------------------------------------------------------------------------
/chapter02/src/main/webapp/WEB-INF/templates/layouts/default.ssp:
--------------------------------------------------------------------------------
1 | <%@ val body:String %>
2 |
3 |
4 | Scalatra CMS
5 |
6 |
7 |
11 |
12 |
13 |
31 |
32 | <%= unescape(body) %>
33 |
34 |
35 |
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/init/DatabaseInit.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.init
2 |
3 | import com.mchange.v2.c3p0.ComboPooledDataSource
4 | import org.squeryl.adapters.{H2Adapter, MySQLAdapter}
5 | import org.squeryl.Session
6 | import org.squeryl.SessionFactory
7 | import org.slf4j.LoggerFactory
8 |
9 | trait DatabaseInit {
10 | val logger = LoggerFactory.getLogger(getClass)
11 |
12 | val databaseUsername = "root"
13 | val databasePassword = ""
14 | val databaseConnection = "jdbc:h2:mem:hackerTracker"
15 |
16 | var cpds = new ComboPooledDataSource
17 |
18 | def configureDb() {
19 | cpds.setDriverClass("org.h2.Driver")
20 | cpds.setJdbcUrl(databaseConnection)
21 | cpds.setUser(databaseUsername)
22 | cpds.setPassword(databasePassword)
23 |
24 | cpds.setMinPoolSize(1)
25 | cpds.setAcquireIncrement(1)
26 | cpds.setMaxPoolSize(50)
27 |
28 | SessionFactory.concreteFactory = Some(() => connection)
29 |
30 | def connection = {
31 | logger.info("Creating connection with c3po connection pool")
32 | Session.create(cpds.getConnection, new H2Adapter)
33 | }
34 | }
35 |
36 | def closeDbConnection() {
37 | logger.info("Closing c3po connection pool")
38 | cpds.close()
39 | }
40 | }
--------------------------------------------------------------------------------
/chapter03/project/build.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | import Keys._
3 |
4 | import org.scalatra.sbt.ScalatraPlugin
5 |
6 | import com.earldouglas.xwp.JettyPlugin
7 |
8 | object Chapter03Build extends Build {
9 | val Organization = "org.scalatra"
10 | val Name = "record-store"
11 | val Version = "0.1.0-SNAPSHOT"
12 | val ScalaVersion = "2.11.6"
13 | val ScalatraVersion = "2.4.0"
14 |
15 | lazy val project = Project (
16 | Name,
17 | file("."),
18 | settings = Defaults.defaultConfigs ++ ScalatraPlugin.scalatraSettings ++ Seq(
19 | organization := Organization,
20 | name := Name,
21 | version := Version,
22 | scalaVersion := ScalaVersion,
23 | fork in Test := true,
24 | resolvers += Classpaths.typesafeReleases,
25 | resolvers += "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases",
26 | libraryDependencies ++= Seq(
27 | "org.scalatra" %% "scalatra" % ScalatraVersion,
28 | "org.scalatra" %% "scalatra-scalate" % ScalatraVersion,
29 | "org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
30 | "ch.qos.logback" % "logback-classic" % "1.1.3" % "runtime",
31 | "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided"
32 | )
33 | )
34 | ).enablePlugins(JettyPlugin)
35 | }
36 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/scala/com/constructiveproof/hackertracker/init/DatabaseInit.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.init
2 |
3 | import java.io.File
4 |
5 | import com.mchange.v2.c3p0.ComboPooledDataSource
6 | import org.apache.commons.io.FileUtils
7 | import org.slf4j.LoggerFactory
8 | import org.squeryl.adapters.H2Adapter
9 | import org.squeryl.{Session, SessionFactory}
10 |
11 | trait DatabaseInit {
12 | val logger = LoggerFactory.getLogger(getClass)
13 |
14 | val dbDir = "/tmp/db"
15 | val dbPath = dbDir + "/hackertracker.db"
16 | val databaseConnection = "jdbc:h2:file:" + dbPath
17 |
18 | var cpds = new ComboPooledDataSource
19 |
20 | def configureDb() {
21 | cpds.setDriverClass("org.h2.Driver")
22 | cpds.setJdbcUrl(databaseConnection)
23 |
24 | cpds.setMinPoolSize(1)
25 | cpds.setAcquireIncrement(1)
26 | cpds.setMaxPoolSize(50)
27 |
28 | SessionFactory.concreteFactory = Some(() => connection)
29 |
30 | def connection = {
31 | logger.info("Creating connection with c3po connection pool")
32 | Session.create(cpds.getConnection, new H2Adapter)
33 | }
34 | }
35 |
36 | def closeDbConnection() {
37 | logger.info("Closing c3po connection pool")
38 | cpds.close()
39 | }
40 |
41 | def wipeDb = {
42 | FileUtils.deleteDirectory(new File(dbDir))
43 | }
44 | }
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/scala/com/constructiveproof/hackertracker/init/DatabaseInit.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.init
2 |
3 | import java.io.File
4 |
5 | import com.mchange.v2.c3p0.ComboPooledDataSource
6 | import org.apache.commons.io.FileUtils
7 | import org.slf4j.LoggerFactory
8 | import org.squeryl.{Session, SessionFactory}
9 | import org.squeryl.adapters.H2Adapter
10 |
11 | trait DatabaseInit {
12 | val logger = LoggerFactory.getLogger(getClass)
13 |
14 | val dbDir = "/tmp/db"
15 | val dbPath = dbDir + "/hackertracker.db"
16 | val databaseConnection = "jdbc:h2:file:" + dbPath
17 |
18 | var cpds = new ComboPooledDataSource
19 |
20 | def configureDb() {
21 | cpds.setDriverClass("org.h2.Driver")
22 | cpds.setJdbcUrl(databaseConnection)
23 |
24 | cpds.setMinPoolSize(1)
25 | cpds.setAcquireIncrement(1)
26 | cpds.setMaxPoolSize(50)
27 |
28 | SessionFactory.concreteFactory = Some(() => connection)
29 |
30 | def connection = {
31 | logger.info("Creating connection with c3po connection pool")
32 | Session.create(cpds.getConnection, new H2Adapter)
33 | }
34 | }
35 |
36 | def closeDbConnection() {
37 | logger.info("Closing c3po connection pool")
38 | cpds.close()
39 | }
40 |
41 | def wipeDb = {
42 | FileUtils.deleteDirectory(new File(dbDir))
43 | }
44 | }
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/auth/AuthenticationSupport.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.auth
2 |
3 | import com.constructiveproof.hackertracker.auth.strategies.{RememberMeStrategy, UserPasswordStrategy}
4 | import com.constructiveproof.hackertracker.models.User
5 | import org.scalatra.ScalatraBase
6 | import org.scalatra.auth.{ScentryConfig, ScentrySupport}
7 | import org.slf4j.LoggerFactory
8 |
9 | trait AuthenticationSupport extends ScalatraBase with ScentrySupport[User] {
10 | self: ScalatraBase =>
11 |
12 | val logger = LoggerFactory.getLogger(getClass)
13 |
14 |
15 | protected val scentryConfig = (new ScentryConfig {}).asInstanceOf[ScentryConfiguration]
16 |
17 | protected def fromSession = { case id: String => User(id) }
18 | protected def toSession = { case usr: User => usr.id }
19 |
20 | protected def requireLogin() = {
21 | if(!isAuthenticated) {
22 | redirect("/sessions/new")
23 | }
24 | }
25 |
26 |
27 | override protected def configureScentry = {
28 | scentry.unauthenticated {
29 | scentry.strategies("UserPassword").unauthenticated()
30 | }
31 | }
32 |
33 | override protected def registerAuthStrategies = {
34 | scentry.register("UserPassword", app => new UserPasswordStrategy(app))
35 | scentry.register("RememberMe", app => new RememberMeStrategy(app))
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/chapter08/project/build.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra.sbt.ScalatraPlugin
2 | import com.earldouglas.xwp.JettyPlugin
3 | import sbt._
4 | import Keys._
5 |
6 |
7 | object Chapter08Build extends Build {
8 | val Organization = "org.scalatra"
9 | val Name = "testing"
10 | val Version = "0.1.0-SNAPSHOT"
11 | val ScalaVersion = "2.11.6"
12 | val ScalatraVersion = "2.4.0"
13 |
14 | lazy val project = Project (
15 | Name,
16 | file("."),
17 | settings = Defaults.defaultConfigs ++ ScalatraPlugin.scalatraSettings ++ Seq(
18 | organization := Organization,
19 | name := Name,
20 | version := Version,
21 | scalaVersion := ScalaVersion,
22 | fork in Test := true,
23 | resolvers += Classpaths.typesafeReleases,
24 | resolvers += "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases",
25 | libraryDependencies ++= Seq(
26 | "org.scalatra" %% "scalatra" % ScalatraVersion,
27 | "org.scalatra" %% "scalatra-json" % ScalatraVersion,
28 | "org.scalatra" %% "scalatra-specs2" % ScalatraVersion % "test",
29 | "org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
30 | "org.json4s" %% "json4s-jackson" % "3.3.0",
31 | "ch.qos.logback" % "logback-classic" % "1.1.3" % "runtime",
32 | "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided"
33 | )
34 | )
35 | ).enablePlugins(JettyPlugin)
36 | }
37 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/scala/com/constructiveproof/hackertracker/SessionsController.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import com.constructiveproof.hackertracker.models.{Hacker}
4 | import com.constructiveproof.hackertracker.auth.AuthenticationSupport
5 |
6 |
7 | class SessionsController extends HackerTrackerStack with AuthenticationSupport {
8 |
9 | before("/new") {
10 | logger.info("SessionsController: checking whether to run RememberMeStrategy: " + !isAuthenticated)
11 |
12 | if(!isAuthenticated) {
13 | scentry.authenticate("RememberMe")
14 | }
15 | }
16 |
17 | get("/new") {
18 | if (isAuthenticated) redirect("/hackers")
19 |
20 | contentType="text/html"
21 | ssp("/sessions/new", "allHackers" -> Hacker.all, "authenticated" -> isAuthenticated)
22 | }
23 |
24 | post("/") {
25 | scentry.authenticate()
26 |
27 | if (isAuthenticated) {
28 | redirect("/hackers")
29 | }else{
30 | redirect("/sessions/new")
31 | }
32 | }
33 |
34 | /**
35 | * Any action that has side-effects on the server should not be a GET (a DELETE would
36 | * be preferable here), but I'm going to make this a GET in order to avoid starting a discussion
37 | * of unobtrusive JavaScript and the creation of DELETE links at this point.
38 | */
39 | get("/destroy") {
40 | scentry.logout()
41 | redirect("/hackers")
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/chapter10/src/test/scala/org/scalatra/book/chapter10/AppSpec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter10
2 |
3 | import slick.driver.H2Driver.api._
4 | import org.scalatest.{BeforeAndAfter, FunSuite}
5 | import org.scalatra.test.scalatest.ScalatraSuite
6 |
7 | import scala.concurrent.Await
8 | import scala.concurrent.duration.Duration
9 |
10 | class AppSpec extends FunSuite with ScalatraSuite with BeforeAndAfter {
11 |
12 | val jdbcUrl = "jdbc:h2:mem:chapter10;DB_CLOSE_DELAY=-1"
13 | val jdbcDriverClass = "org.h2.Driver"
14 | val db = Database.forURL(jdbcUrl, driver = jdbcDriverClass)
15 | val res = db.run(DbSetup.createDatabase)
16 |
17 | Await.result(res, Duration(2, "seconds"))
18 |
19 | addServlet(new Chapter10App(db), "/*")
20 |
21 | test("get all areas") {
22 | get("/areas") {
23 | status should equal(200)
24 | }
25 | }
26 |
27 | test("get one area") {
28 | get("/areas/2") {
29 | status should equal(200)
30 | }
31 | }
32 |
33 | test("modify a route") {
34 | put("/routes/1?routeName=foo&description=bar") {
35 | status should equal(200)
36 | }
37 |
38 | put("/routes/100?routeName=foo&description=bar") {
39 | status should equal(404)
40 | }
41 | }
42 |
43 | test("delete a route") {
44 | delete("/routes/1") {
45 | status should equal(200)
46 | }
47 |
48 | delete("/routes/1") {
49 | status should equal(404)
50 | }
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/SessionsController.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import com.constructiveproof.hackertracker.auth.AuthenticationSupport
4 | import com.constructiveproof.hackertracker.models.Hacker
5 | import com.constructiveproof.hackertracker.stacks.BrowserStack
6 |
7 | class SessionsController extends BrowserStack with AuthenticationSupport {
8 |
9 | before("/new") {
10 | logger.info("SessionsController: checking whether to run RememberMeStrategy: " + !isAuthenticated)
11 |
12 | if(!isAuthenticated) {
13 | scentry.authenticate("RememberMe")
14 | }
15 | }
16 |
17 | get("/new") {
18 | if (isAuthenticated) redirect("/hackers")
19 |
20 | contentType="text/html"
21 | ssp("/sessions/new", "allHackers" -> Hacker.all, "authenticated" -> isAuthenticated)
22 | }
23 |
24 | post("/") {
25 | scentry.authenticate()
26 |
27 | if (isAuthenticated) {
28 | redirect("/hackers")
29 | }else{
30 | redirect("/sessions/new")
31 | }
32 | }
33 |
34 | /**
35 | * Any action that has side-effects on the server should not be a GET (a DELETE would
36 | * be preferable here), but I'm going to make this a GET in order to avoid starting a discussion
37 | * of unobtrusive JavaScript and the creation of DELETE links at this point.
38 | */
39 | get("/destroy") {
40 | scentry.logout()
41 | redirect("/hackers")
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/scala/com/constructiveproof/hackertracker/auth/AuthenticationSupport.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.auth
2 |
3 | import org.scalatra.ScalatraBase
4 | import org.scalatra.auth.{ScentryConfig, ScentrySupport}
5 | import com.constructiveproof.hackertracker.auth.strategies.UserPasswordStrategy
6 | import com.constructiveproof.hackertracker.models.User
7 | import com.constructiveproof.hackertracker.auth.strategies.RememberMeStrategy
8 | import org.slf4j.LoggerFactory
9 |
10 | trait AuthenticationSupport extends ScalatraBase with ScentrySupport[User] {
11 | self: ScalatraBase =>
12 |
13 | val logger = LoggerFactory.getLogger(getClass)
14 |
15 |
16 | protected val scentryConfig = (new ScentryConfig {}).asInstanceOf[ScentryConfiguration]
17 |
18 | protected def fromSession = { case id: String => User(id) }
19 | protected def toSession = { case usr: User => usr.id }
20 |
21 | protected def requireLogin() = {
22 | if(!isAuthenticated) {
23 | redirect("/sessions/new")
24 | }
25 | }
26 |
27 |
28 | override protected def configureScentry = {
29 | scentry.unauthenticated {
30 | scentry.strategies("UserPassword").unauthenticated()
31 | }
32 | }
33 |
34 | override protected def registerAuthStrategies = {
35 | scentry.register("UserPassword", app => new UserPasswordStrategy(app))
36 | scentry.register("RememberMe", app => new RememberMeStrategy(app))
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/auth/OurBasicAuthenticationSupport.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.auth
2 |
3 | import org.scalatra.auth.{ScentryConfig, ScentrySupport}
4 | import org.scalatra.auth.strategy.BasicAuthSupport
5 | import org.scalatra.ScalatraBase
6 | import com.constructiveproof.hackertracker.auth.strategies.OurBasicAuthStrategy
7 | import com.constructiveproof.hackertracker.models.User
8 |
9 | /**
10 | * Mix this trait into any controller and it'll require HTTP Basic Authentication using the OurBasicAuthStrategy.
11 | *
12 | * To protect individual routes, just add "basicAuth" at the top of the action, or use a before() filter to protect the whole controller.
13 | */
14 | trait OurBasicAuthenticationSupport extends ScentrySupport[User] with BasicAuthSupport[User] {
15 | self: ScalatraBase =>
16 |
17 | val realm = "Scalatra Basic Auth Example"
18 |
19 | protected def fromSession = { case id: String => User(id) }
20 | protected def toSession = { case usr: User => usr.id }
21 |
22 | protected val scentryConfig = (new ScentryConfig {}).asInstanceOf[ScentryConfiguration]
23 |
24 |
25 | override protected def configureScentry = {
26 | scentry.unauthenticated {
27 | scentry.strategies("Basic").unauthenticated()
28 | }
29 | }
30 |
31 | override protected def registerAuthStrategies = {
32 | scentry.register("Basic", app => new OurBasicAuthStrategy(app, realm))
33 | }
34 |
35 | }
36 |
37 |
38 |
--------------------------------------------------------------------------------
/chapter05/project/build.scala:
--------------------------------------------------------------------------------
1 | import org.scalatra.sbt.ScalatraPlugin
2 | import com.earldouglas.xwp.JettyPlugin
3 | import sbt._
4 | import Keys._
5 |
6 | object Chapter05Build extends Build {
7 |
8 | val Organization = "org.scalatra"
9 | val Name = "foods"
10 | val Version = "0.1.0-SNAPSHOT"
11 | val ScalaVersion = "2.11.6"
12 | val ScalatraVersion = "2.4.0"
13 |
14 | val mySettings = Defaults.defaultConfigs ++
15 | ScalatraPlugin.scalatraWithDist ++
16 | Seq(
17 | organization := Organization,
18 | name := Name,
19 | version := Version,
20 | scalaVersion := ScalaVersion,
21 | fork in Test := true,
22 | resolvers += Classpaths.typesafeReleases,
23 | resolvers += "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases",
24 | libraryDependencies ++= Seq(
25 | "org.scalatra" %% "scalatra" % ScalatraVersion,
26 | "org.scalatra" %% "scalatra-scalate" % ScalatraVersion,
27 | "org.scalatra" %% "scalatra-json" % ScalatraVersion,
28 | "org.json4s" %% "json4s-jackson" % "3.3.0",
29 | "org.scalaz" %% "scalaz-core" % "7.1.2",
30 | "org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
31 | "ch.qos.logback" % "logback-classic" % "1.1.3" % "runtime",
32 | "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided"
33 | )
34 | )
35 |
36 | lazy val project = Project(Name, file("."))
37 | .enablePlugins(JettyPlugin)
38 | .settings(mySettings:_*)
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/scala/com/constructiveproof/hackertracker/auth/OurBasicAuthenticationSupport.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.auth
2 |
3 | import org.scalatra.auth.{ScentryConfig, ScentrySupport}
4 | import org.scalatra.auth.strategy.BasicAuthSupport
5 | import org.scalatra.ScalatraBase
6 | import com.constructiveproof.hackertracker.auth.strategies.OurBasicAuthStrategy
7 | import com.constructiveproof.hackertracker.models.User
8 |
9 | /**
10 | * Mix this trait into any controller and it'll require HTTP Basic Authentication using the OurBasicAuthStrategy.
11 | *
12 | * To protect individual routes, just add "basicAuth" at the top of the action, or use a before() filter to protect the whole controller.
13 | */
14 | trait OurBasicAuthenticationSupport extends ScentrySupport[User] with BasicAuthSupport[User] {
15 | self: ScalatraBase =>
16 |
17 | val realm = "Scalatra Basic Auth Example"
18 |
19 | protected def fromSession = { case id: String => User(id) }
20 | protected def toSession = { case usr: User => usr.id }
21 |
22 | protected val scentryConfig = (new ScentryConfig {}).asInstanceOf[ScentryConfiguration]
23 |
24 |
25 | override protected def configureScentry = {
26 | scentry.unauthenticated {
27 | scentry.strategies("Basic").unauthenticated()
28 | }
29 | }
30 |
31 | override protected def registerAuthStrategies = {
32 | scentry.register("Basic", app => new OurBasicAuthStrategy(app, realm))
33 | }
34 |
35 | }
36 |
37 |
38 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/scala/com/constructiveproof/hackertracker/HackersController.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import com.constructiveproof.hackertracker.models.Hacker
4 | import org.scalatra._
5 |
6 | class HackersController extends HackerTrackerStack {
7 |
8 | before() {
9 | contentType = "text/html"
10 | }
11 |
12 | /**
13 | * Show all hackers.
14 | */
15 | get("/") {
16 | ssp("/hackers/index", "allHackers" -> Hacker.all)
17 | }
18 |
19 | /**
20 | * Show a specific hacker.
21 | */
22 | get("/:id") {
23 | val id = params.getAs[Int]("id").getOrElse(0)
24 | val hacker = Hacker.get(id)
25 | ssp("/hackers/show", "hacker" -> hacker, "allHackers" -> Hacker.all)
26 | }
27 |
28 | /**
29 | * Display a form for creating a new hacker.
30 | */
31 | get("/new") {
32 | ssp("/hackers/new", "allHackers" -> Hacker.all)
33 | }
34 |
35 |
36 | /**
37 | * Create a new hacker in the database.
38 | */
39 | post("/") {
40 | val firstName = params("firstname")
41 | val lastName = params("lastname")
42 | val motto = params("motto")
43 | val birthYear = params.getAs[Int]("birthyear").getOrElse(
44 | halt(BadRequest("Please provide a year of birth.")))
45 |
46 | val hacker = new Hacker(0, firstName, lastName, motto, birthYear)
47 |
48 | if(Hacker.create(hacker)) {
49 | flash("notice") = "Hacker successfully persisted."
50 | redirect("/hackers/" + hacker.id)
51 | }
52 | }
53 |
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/comments-collector/src/main/scala/ScalatraBootstrap.scala:
--------------------------------------------------------------------------------
1 | import comments._
2 |
3 | import org.scalatra._
4 | import org.scalatra.servlet._
5 | import org.scalatra.swagger._
6 |
7 | import com.mongodb.casbah.Imports._
8 |
9 | import javax.servlet.ServletContext
10 | import javax.servlet.http.HttpServlet
11 |
12 | class ScalatraBootstrap extends LifeCycle {
13 |
14 | // create an implicit instance of ApiInfo which publishes additional information
15 | implicit val apiInfo = new ApiInfo("The comments API",
16 | "Docs for the comments API",
17 | "http://www.manning.com/carrero2/",
18 | "Ross", "MIT", "http://scalatra.org")
19 |
20 | // create an implicit instance of Swagger which is passed to both servlets
21 | implicit val swagger = new Swagger("1.0", "1", apiInfo)
22 |
23 | // create a mongodb client and collection
24 | val mongoClient = MongoClient()
25 | val mongoColl = mongoClient("comments_collector")("comments")
26 |
27 | override def init(context: ServletContext) {
28 |
29 | // create a comments repository using the mongo collection
30 | val comments = CommentsRepository(mongoColl)
31 |
32 | // mount the api + swagger docs
33 | context.mount(new CommentsApi(comments), "/api", "api")
34 | context.mount(new CommentsApiDoc(), "/api-docs")
35 |
36 | // mount the html frontend
37 | context.mount(new CommentsFrontend(comments), "/")
38 |
39 | }
40 |
41 | override def destroy(context: ServletContext) {
42 |
43 | // shutdown the mongo client
44 | mongoClient.close
45 |
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/chapter10/project/build.scala:
--------------------------------------------------------------------------------
1 | import sbt._
2 | import Keys._
3 | import org.scalatra.sbt._
4 | import com.earldouglas.xwp.JettyPlugin
5 |
6 | object Chapter10Build extends Build {
7 |
8 | val Organization = "org.scalatra"
9 | val Name = "climbing-routes"
10 | val Version = "0.1.0-SNAPSHOT"
11 | val ScalaVersion = "2.11.7"
12 | val ScalatraVersion = "2.4.0"
13 |
14 | val mySettings = Defaults.defaultConfigs ++
15 | ScalatraPlugin.scalatraSettings ++
16 | Seq(
17 | organization := Organization,
18 | name := Name,
19 | version := Version,
20 | scalaVersion := ScalaVersion,
21 | fork in Test := true,
22 | resolvers += Classpaths.typesafeReleases,
23 | resolvers += "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases",
24 | libraryDependencies ++= Seq(
25 | "org.scalatra" %% "scalatra" % ScalatraVersion,
26 | "org.scalatra" %% "scalatra-scalate" % ScalatraVersion,
27 | "org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
28 | "org.scalaz" %% "scalaz-core" % "7.1.2",
29 | "ch.qos.logback" % "logback-classic" % "1.1.3" % "runtime",
30 | "com.typesafe.slick" %% "slick" % "3.0.0",
31 | "com.h2database" % "h2" % "1.4.187",
32 | "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
33 | "org.scala-lang" % "scala-compiler" % ScalaVersion
34 | )
35 | )
36 |
37 | lazy val project = Project("climbing-routes", file("."))
38 | .settings(mySettings:_*)
39 | .enablePlugins(JettyPlugin)
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/test/scala/org/scalatra/book/chapter09/Chapter09Spec.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | import org.eclipse.jetty.servlet.ServletContextHandler
4 | import org.scalatra.test.specs2._
5 |
6 | class Chapter09Spec extends MutableScalatraSpec {
7 |
8 | val conf = AppConfig.load
9 | sys.props(org.scalatra.EnvironmentKey) = AppEnvironment.asString(conf.env)
10 |
11 | // use target/web/stage as resourceBase
12 | override lazy val servletContextHandler = {
13 | val handler = new ServletContextHandler(ServletContextHandler.SESSIONS)
14 | handler.setContextPath(contextPath)
15 | handler.setResourceBase("target/web/stage")
16 | handler
17 | }
18 |
19 | addServlet(new Chapter09(conf), "/*")
20 |
21 | "/ should should execute an action" in {
22 | get("/") {
23 | status must_== 200
24 | }
25 | }
26 |
27 | "/shorten-url should should execute an action" in {
28 | get("/shorten-url") {
29 | status must_== 200
30 | }
31 | }
32 |
33 | "/static.txt should return static file" in {
34 | get("/static.txt") {
35 | status must_== 200
36 | body must_== "this is static text!"
37 | }
38 | }
39 |
40 | "/hello-scalate should render a template" in {
41 | get("/hello-scalate") {
42 | status must_== 200
43 | body must_==
44 | """Hello, Scalate!
45 | |""".stripMargin
46 | }
47 | }
48 |
49 | "/css/main.css should return a CSS file" in {
50 | get("/css/main.css") {
51 | status must_== 200
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/chapter13/src/main/webapp/js/foundation/foundation.alerts.js:
--------------------------------------------------------------------------------
1 | /*jslint unparam: true, browser: true, indent: 2 */
2 |
3 | ;(function ($, window, document, undefined) {
4 | 'use strict';
5 |
6 | Foundation.libs.alerts = {
7 | name : 'alerts',
8 |
9 | version : '4.3.2',
10 |
11 | settings : {
12 | animation: 'fadeOut',
13 | speed: 300, // fade out speed
14 | callback: function (){}
15 | },
16 |
17 | init : function (scope, method, options) {
18 | this.scope = scope || this.scope;
19 | Foundation.inherit(this, 'data_options');
20 |
21 | if (typeof method === 'object') {
22 | $.extend(true, this.settings, method);
23 | }
24 |
25 | if (typeof method !== 'string') {
26 | if (!this.settings.init) { this.events(); }
27 |
28 | return this.settings.init;
29 | } else {
30 | return this[method].call(this, options);
31 | }
32 | },
33 |
34 | events : function () {
35 | var self = this;
36 |
37 | $(this.scope).on('click.fndtn.alerts', '[data-alert] a.close', function (e) {
38 | var alertBox = $(this).closest("[data-alert]"),
39 | settings = $.extend({}, self.settings, self.data_options(alertBox));
40 |
41 | e.preventDefault();
42 | alertBox[settings.animation](settings.speed, function () {
43 | $(this).remove();
44 | settings.callback();
45 | });
46 | });
47 |
48 | this.settings.init = true;
49 | },
50 |
51 | off : function () {
52 | $(this.scope).off('.fndtn.alerts');
53 | },
54 |
55 | reflow : function () {}
56 | };
57 | }(Foundation.zj, this, this.document));
58 |
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/scala/com/constructiveproof/hackertracker/models/Models.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.models
2 |
3 | import org.squeryl.PrimitiveTypeMode._
4 | import org.squeryl.Schema
5 | import org.squeryl.KeyedEntity
6 | import org.squeryl.PersistenceStatus
7 |
8 | /**
9 | * A hacker in the tracker.
10 | */
11 | case class Hacker(val id: Long, val firstName: String, val lastName: String, val motto: String, val birthYear: Int) extends SquerylRecord {
12 | def this() = this(0, "Foo", "McBar", "It's better to ask forgiveness than permission", 1950)
13 | }
14 |
15 | /**
16 | * The BlogDb object acts as a cross between a Dao and a Schema definition file.
17 | */
18 | object Db extends Schema {
19 |
20 | def init = {
21 | inTransaction {
22 | Db.create
23 | }
24 | }
25 |
26 | val hackers = table[Hacker]("hackers")
27 | on(hackers)(a => declare(
28 | a.id is(autoIncremented)))
29 | }
30 |
31 | object Hacker {
32 | def create(hacker:Hacker):Boolean = {
33 | inTransaction {
34 | val result = Db.hackers.insert(hacker)
35 | if(result.isPersisted) {
36 | true
37 | } else {
38 | false
39 | }
40 | }
41 | }
42 |
43 | def all = {
44 | from(Db.hackers)(select(_))
45 | }
46 |
47 | def get(id: Long) = {
48 | Db.hackers.where(h => h.id === id).single
49 | }
50 | }
51 |
52 | /**
53 | * This trait is just a way to aggregate our model style across multiple
54 | * models so that we have a single point of change if we want to add
55 | * anything to our model behaviour
56 | */
57 | trait SquerylRecord extends KeyedEntity[Long] with PersistenceStatus {
58 |
59 | }
--------------------------------------------------------------------------------
/chapter11/1-hacker-tracker-unprotected/src/main/webapp/js/foundation/foundation.alerts.js:
--------------------------------------------------------------------------------
1 | /*jslint unparam: true, browser: true, indent: 2 */
2 |
3 | ;(function ($, window, document, undefined) {
4 | 'use strict';
5 |
6 | Foundation.libs.alerts = {
7 | name : 'alerts',
8 |
9 | version : '4.3.2',
10 |
11 | settings : {
12 | animation: 'fadeOut',
13 | speed: 300, // fade out speed
14 | callback: function (){}
15 | },
16 |
17 | init : function (scope, method, options) {
18 | this.scope = scope || this.scope;
19 | Foundation.inherit(this, 'data_options');
20 |
21 | if (typeof method === 'object') {
22 | $.extend(true, this.settings, method);
23 | }
24 |
25 | if (typeof method !== 'string') {
26 | if (!this.settings.init) { this.events(); }
27 |
28 | return this.settings.init;
29 | } else {
30 | return this[method].call(this, options);
31 | }
32 | },
33 |
34 | events : function () {
35 | var self = this;
36 |
37 | $(this.scope).on('click.fndtn.alerts', '[data-alert] a.close', function (e) {
38 | var alertBox = $(this).closest("[data-alert]"),
39 | settings = $.extend({}, self.settings, self.data_options(alertBox));
40 |
41 | e.preventDefault();
42 | alertBox[settings.animation](settings.speed, function () {
43 | $(this).remove();
44 | settings.callback();
45 | });
46 | });
47 |
48 | this.settings.init = true;
49 | },
50 |
51 | off : function () {
52 | $(this.scope).off('.fndtn.alerts');
53 | },
54 |
55 | reflow : function () {}
56 | };
57 | }(Foundation.zj, this, this.document));
58 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/webapp/js/foundation/foundation.alerts.js:
--------------------------------------------------------------------------------
1 | /*jslint unparam: true, browser: true, indent: 2 */
2 |
3 | ;(function ($, window, document, undefined) {
4 | 'use strict';
5 |
6 | Foundation.libs.alerts = {
7 | name : 'alerts',
8 |
9 | version : '4.3.2',
10 |
11 | settings : {
12 | animation: 'fadeOut',
13 | speed: 300, // fade out speed
14 | callback: function (){}
15 | },
16 |
17 | init : function (scope, method, options) {
18 | this.scope = scope || this.scope;
19 | Foundation.inherit(this, 'data_options');
20 |
21 | if (typeof method === 'object') {
22 | $.extend(true, this.settings, method);
23 | }
24 |
25 | if (typeof method !== 'string') {
26 | if (!this.settings.init) { this.events(); }
27 |
28 | return this.settings.init;
29 | } else {
30 | return this[method].call(this, options);
31 | }
32 | },
33 |
34 | events : function () {
35 | var self = this;
36 |
37 | $(this.scope).on('click.fndtn.alerts', '[data-alert] a.close', function (e) {
38 | var alertBox = $(this).closest("[data-alert]"),
39 | settings = $.extend({}, self.settings, self.data_options(alertBox));
40 |
41 | e.preventDefault();
42 | alertBox[settings.animation](settings.speed, function () {
43 | $(this).remove();
44 | settings.callback();
45 | });
46 | });
47 |
48 | this.settings.init = true;
49 | },
50 |
51 | off : function () {
52 | $(this.scope).off('.fndtn.alerts');
53 | },
54 |
55 | reflow : function () {}
56 | };
57 | }(Foundation.zj, this, this.document));
58 |
--------------------------------------------------------------------------------
/chapter09/src/main/scala/org/scalatra/book/chapter09/AppConfig.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 |
4 | import com.typesafe.config.ConfigFactory
5 |
6 | case class AppConfig(
7 | webBase: String,
8 | env: AppEnvironment,
9 | mailConfig: MailConfig) {
10 |
11 | def isProduction = env == Production
12 | def isDevelopment = env == Development
13 | }
14 |
15 | case class MailConfig(
16 | user: String,
17 | password: String,
18 | host: String,
19 | sender: String)
20 |
21 | sealed trait AppEnvironment
22 | case object Development extends AppEnvironment
23 | case object Staging extends AppEnvironment
24 | case object Test extends AppEnvironment
25 | case object Production extends AppEnvironment
26 |
27 | object AppEnvironment {
28 | def fromString(s: String): AppEnvironment = {
29 | s match {
30 | case "development" => Development
31 | case "staging" => Staging
32 | case "test" => Test
33 | case "production" => Production
34 | }
35 | }
36 |
37 | def asString(s: AppEnvironment): String = {
38 | s match {
39 | case Development => "development"
40 | case Staging => "staging"
41 | case Test => "test"
42 | case Production => "production"
43 | }
44 | }
45 | }
46 |
47 | object AppConfig {
48 | def load: AppConfig = {
49 | val cfg = ConfigFactory.load
50 |
51 | val webBase = cfg.getString("webBase")
52 | val env = AppEnvironment.fromString(cfg.getString("environment"))
53 | val mailConfig = MailConfig(
54 | cfg.getString("email.user"),
55 | cfg.getString("email.password"),
56 | cfg.getString("email.host"),
57 | cfg.getString("email.sender"))
58 |
59 | AppConfig(webBase, env, mailConfig)
60 | }
61 | }
--------------------------------------------------------------------------------
/chapter09-sbt/src/main/scala/org/scalatra/book/chapter09/AppConfig.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 |
4 | import com.typesafe.config.ConfigFactory
5 |
6 | case class AppConfig(
7 | webBase: String,
8 | env: AppEnvironment,
9 | mailConfig: MailConfig) {
10 |
11 | def isProduction = env == Production
12 | def isDevelopment = env == Development
13 | }
14 |
15 | case class MailConfig(
16 | user: String,
17 | password: String,
18 | host: String,
19 | sender: String)
20 |
21 | sealed trait AppEnvironment
22 | case object Development extends AppEnvironment
23 | case object Staging extends AppEnvironment
24 | case object Test extends AppEnvironment
25 | case object Production extends AppEnvironment
26 |
27 | object AppEnvironment {
28 | def fromString(s: String): AppEnvironment = {
29 | s match {
30 | case "development" => Development
31 | case "staging" => Staging
32 | case "test" => Test
33 | case "production" => Production
34 | }
35 | }
36 |
37 | def asString(s: AppEnvironment): String = {
38 | s match {
39 | case Development => "development"
40 | case Staging => "staging"
41 | case Test => "test"
42 | case Production => "production"
43 | }
44 | }
45 | }
46 |
47 | object AppConfig {
48 | def load: AppConfig = {
49 | val cfg = ConfigFactory.load
50 |
51 | val webBase = cfg.getString("webBase")
52 | val env = AppEnvironment.fromString(cfg.getString("environment"))
53 | val mailConfig = MailConfig(
54 | cfg.getString("email.user"),
55 | cfg.getString("email.password"),
56 | cfg.getString("email.host"),
57 | cfg.getString("email.sender"))
58 |
59 | AppConfig(webBase, env, mailConfig)
60 | }
61 | }
--------------------------------------------------------------------------------
/chapter09-sbtweb/src/main/scala/org/scalatra/book/chapter09/AppConfig.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter09
2 |
3 | import com.typesafe.config.ConfigFactory
4 |
5 | case class AppConfig(
6 | webBase: String,
7 | env: AppEnvironment,
8 | mailConfig: MailConfig) {
9 |
10 | def isProduction = env == Production
11 | def isDevelopment = env == Development
12 | }
13 |
14 | case class MailConfig(
15 | user: String,
16 | password: String,
17 | host: String,
18 | sender: String)
19 |
20 | sealed trait AppEnvironment
21 | case object Development extends AppEnvironment
22 | case object Staging extends AppEnvironment
23 | case object Test extends AppEnvironment
24 | case object Production extends AppEnvironment
25 |
26 | object AppEnvironment {
27 | def fromString(s: String): AppEnvironment = {
28 | s match {
29 | case "development" => Development
30 | case "staging" => Staging
31 | case "test" => Test
32 | case "production" => Production
33 | }
34 | }
35 |
36 | def asString(s: AppEnvironment): String = {
37 | s match {
38 | case Development => "development"
39 | case Staging => "staging"
40 | case Test => "test"
41 | case Production => "production"
42 | }
43 | }
44 | }
45 |
46 | object AppConfig {
47 | def load: AppConfig = {
48 | val cfg = ConfigFactory.load
49 |
50 | val webBase = cfg.getString("webBase")
51 | val env = AppEnvironment.fromString(cfg.getString("environment"))
52 | val mailConfig = MailConfig(
53 | cfg.getString("email.user"),
54 | cfg.getString("email.password"),
55 | cfg.getString("email.host"),
56 | cfg.getString("email.sender"))
57 |
58 | AppConfig(webBase, env, mailConfig)
59 | }
60 | }
--------------------------------------------------------------------------------
/chapter06/src/main/scala/org/scalatra/book/chapter06/store.scala:
--------------------------------------------------------------------------------
1 | package org.scalatra.book.chapter06
2 |
3 | import java.io.{File, FileOutputStream, InputStream, OutputStream}
4 | import java.util.concurrent.atomic.AtomicLong
5 |
6 | case class Document(id: Long, name: String, contentType: Option[String], description: String)
7 |
8 | // simple document store
9 | // - an index of documents exists in-memory
10 | // - the documents are saved as files on the filesystem
11 | case class DocumentStore(base: String) {
12 |
13 | private val fileNameIndex = collection.concurrent.TrieMap[Long, Document]()
14 |
15 | private val idCounter = new AtomicLong(0)
16 |
17 | // adds a new document
18 | def add(name: String, in: InputStream, contentType: Option[String], description: String): Long = {
19 | val id = idCounter.getAndIncrement
20 | val out = new FileOutputStream(getFile(id))
21 | // Files.copy(in, out)
22 | copyStream(in, out)
23 | fileNameIndex(id) = Document(id, name, contentType, description)
24 | id
25 | }
26 |
27 | // a sequence of all documents
28 | def list: Seq[Document] = {
29 | fileNameIndex.values.toSeq
30 | }
31 |
32 | // a document for a given id
33 | def getDocument(id: Long): Option[Document] = {
34 | fileNameIndex.get(id)
35 | }
36 |
37 | // a file for a given id
38 | def getFile(id: Long): File = new File(f"$base/$id")
39 |
40 | // writes an input stream to an output stream
41 | private def copyStream(input: InputStream, output: OutputStream) {
42 | val buffer = Array.ofDim[Byte](1024)
43 | var bytesRead: Int = 0
44 | while (bytesRead != -1) {
45 | bytesRead = input.read(buffer)
46 | if (bytesRead > 0) output.write(buffer, 0, bytesRead)
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/HackersController.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker
2 |
3 | import com.constructiveproof.hackertracker.auth.AuthenticationSupport
4 | import com.constructiveproof.hackertracker.models.Hacker
5 | import com.constructiveproof.hackertracker.stacks.BrowserStack
6 | import org.scalatra._
7 |
8 | class HackersController extends BrowserStack with AuthenticationSupport {
9 |
10 | before() {
11 | contentType = "text/html"
12 | }
13 |
14 | /**
15 | * Show all hackers.
16 | */
17 | get("/") {
18 | ssp("/hackers/index", "allHackers" -> Hacker.all, "authenticated" -> isAuthenticated)
19 | }
20 |
21 | /**
22 | * Show a specific hacker.
23 | */
24 | get("/:id") {
25 | val id = params.getAs[Int]("id").getOrElse(0)
26 | val hacker = Hacker.get(id)
27 | ssp("/hackers/show", "hacker" -> hacker, "allHackers" -> Hacker.all, "authenticated" -> isAuthenticated)
28 | }
29 |
30 | /**
31 | * Display a form for creating a new hacker.
32 | */
33 | get("/new") {
34 | requireLogin
35 | ssp("/hackers/new", "allHackers" -> Hacker.all, "authenticated" -> isAuthenticated)
36 | }
37 |
38 |
39 | /**
40 | * Create a new hacker in the database.
41 | */
42 | post("/") {
43 | requireLogin
44 | val firstName = params("firstname")
45 | val lastName = params("lastname")
46 | val motto = params("motto")
47 | val birthYear = params.getAs[Int]("birthyear").getOrElse(
48 | halt(BadRequest("Please provide a year of birth.")))
49 |
50 | val hacker = new Hacker(0, firstName, lastName, motto, birthYear)
51 |
52 | if(Hacker.create(hacker)) {
53 | flash("notice") = "Hacker successfully persisted."
54 | redirect("/hackers/" + hacker.id)
55 | }
56 | }
57 |
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/chapter11/2-hacker-tracker-protected/src/main/scala/com/constructiveproof/hackertracker/models/Models.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.models
2 |
3 | import org.squeryl.PrimitiveTypeMode._
4 | import org.squeryl.Schema
5 | import org.squeryl.KeyedEntity
6 | import org.squeryl.PersistenceStatus
7 |
8 |
9 | case class User(val id:String) {
10 | def forgetMe() = {
11 | println("Destroying token in datastore")
12 | }
13 | }
14 |
15 | /**
16 | * A hacker in the tracker.
17 | */
18 | case class Hacker(val id: Long, val firstName: String, val lastName: String, val motto: String, val birthYear: Int) extends SquerylRecord {
19 | def this() = this(0, "Foo", "McBar", "It's better to ask forgiveness than permission", 1950)
20 | }
21 |
22 | /**
23 | * The BlogDb object acts as a cross between a Dao and a Schema definition file.
24 | */
25 | object Db extends Schema {
26 |
27 | def init = {
28 | inTransaction {
29 | Db.create
30 | }
31 | }
32 |
33 | val hackers = table[Hacker]("hackers")
34 | on(hackers)(a => declare(
35 | a.id is(autoIncremented)))
36 | }
37 |
38 | object Hacker {
39 | def create(hacker:Hacker):Boolean = {
40 | inTransaction {
41 | val result = Db.hackers.insert(hacker)
42 | if(result.isPersisted) {
43 | true
44 | } else {
45 | false
46 | }
47 | }
48 | }
49 |
50 | def all = {
51 | from(Db.hackers)(select(_))
52 | }
53 |
54 | def get(id: Long) = {
55 | Db.hackers.where(h => h.id === id).single
56 | }
57 | }
58 |
59 | /**
60 | * This trait is just a way to aggregate our model style across multiple
61 | * models so that we have a single point of change if we want to add
62 | * anything to our model behaviour
63 | */
64 | trait SquerylRecord extends KeyedEntity[Long] with PersistenceStatus {
65 |
66 | }
--------------------------------------------------------------------------------
/chapter13/src/main/scala/com/constructiveproof/hackertracker/models/Models.scala:
--------------------------------------------------------------------------------
1 | package com.constructiveproof.hackertracker.models
2 |
3 | import org.squeryl.{KeyedEntity, PersistenceStatus, Schema}
4 | import org.squeryl.PrimitiveTypeMode._
5 |
6 | case class User(val id: String) {
7 | def forgetMe() = {
8 | println("Destroying token in datastore")
9 | }
10 | }
11 |
12 | /**
13 | * A hacker in the tracker.
14 | */
15 | case class Hacker(val id: Long, val firstName: String, val lastName: String, val motto: String, val birthYear: Int) extends SquerylRecord {
16 | def this() = this(0, "Foo", "McBar", "It's better to ask forgiveness than permission", 1950)
17 | }
18 |
19 |
20 | /**
21 | * The BlogDb object acts as a cross between a Dao and a Schema definition file.
22 | */
23 | object Db extends Schema {
24 |
25 | def init = {
26 | inTransaction {
27 | Db.create
28 | }
29 | }
30 |
31 | val hackers = table[Hacker]("hackers")
32 | on(hackers)(a => declare(
33 | a.id is (autoIncremented)))
34 | }
35 |
36 | object Hacker {
37 | def create(hacker: Hacker): Boolean = {
38 | inTransaction {
39 | val result = Db.hackers.insert(hacker)
40 | if (result.isPersisted) {
41 | true
42 | } else {
43 | false
44 | }
45 | }
46 | }
47 |
48 | def all = {
49 | from(Db.hackers)(select(_))
50 | }
51 |
52 | def get(id: Long) = {
53 | Db.hackers.where(h => h.id === id).single
54 | }
55 |
56 | def destroy(id: Long) = {
57 | Db.hackers.delete(id)
58 | }
59 | }
60 |
61 | /**
62 | * This trait is just a way to aggregate our model style across multiple
63 | * models so that we have a single point of change if we want to add
64 | * anything to our model behaviour
65 | */
66 | trait SquerylRecord extends KeyedEntity[Long] with PersistenceStatus {
67 |
68 | }
--------------------------------------------------------------------------------