├── .gitignore ├── README.md ├── chapter0 ├── .gitignore ├── LICENSE ├── README ├── activator ├── activator-launch-1.2.12.jar ├── activator.bat ├── app │ ├── controllers │ │ └── Application.scala │ └── views │ │ ├── index.scala.html │ │ └── main.scala.html ├── build.sbt ├── conf │ ├── application.conf │ └── routes ├── init_precompiled.sh ├── project │ ├── build.properties │ └── plugins.sbt ├── public │ ├── images │ │ └── favicon.png │ ├── javascripts │ │ └── hello.js │ └── stylesheets │ │ └── main.css ├── run_precompiled.sh └── test │ ├── ApplicationSpec.scala │ └── IntegrationSpec.scala ├── chapter1 ├── .gitignore ├── LICENSE ├── README ├── activator ├── activator-launch-1.2.12.jar ├── activator.bat ├── app │ ├── controllers │ │ ├── Application.scala │ │ └── FavouriteStudios.scala │ ├── models │ │ └── FavouriteStudio.scala │ └── views │ │ ├── index.scala.html │ │ └── main.scala.html ├── build.sbt ├── conf │ ├── application.conf │ └── routes ├── init_precompiled.sh ├── project │ ├── build.properties │ └── plugins.sbt ├── public │ ├── images │ │ └── favicon.png │ ├── javascripts │ │ └── hello.js │ └── stylesheets │ │ └── main.css ├── run_precompiled.sh └── test │ ├── ApplicationSpec.scala │ └── IntegrationSpec.scala ├── chapter2.1 ├── .gitignore ├── LICENSE ├── README ├── activator ├── activator-launch-1.2.12.jar ├── activator.bat ├── app │ ├── controllers │ │ ├── Application.scala │ │ └── FavouriteStudios.scala │ ├── models │ │ ├── FavouriteStudio.scala │ │ └── dao │ │ │ └── FavouriteStudioDAO.scala │ └── views │ │ ├── index.scala.html │ │ └── main.scala.html ├── build.sbt ├── conf │ ├── application.conf │ └── routes ├── init_precompiled.sh ├── project │ ├── build.properties │ └── plugins.sbt ├── public │ ├── images │ │ └── favicon.png │ ├── javascripts │ │ └── hello.js │ └── stylesheets │ │ └── main.css ├── run_precompiled.sh └── test │ ├── ApplicationSpec.scala │ └── IntegrationSpec.scala ├── chapter2 ├── .gitignore ├── LICENSE ├── README ├── activator ├── activator-launch-1.2.12.jar ├── activator.bat ├── app │ ├── controllers │ │ ├── Application.scala │ │ └── FavouriteStudios.scala │ ├── models │ │ ├── FavouriteStudio.scala │ │ └── dao │ │ │ └── FavouriteStudioDAO.scala │ └── views │ │ ├── index.scala.html │ │ └── main.scala.html ├── build.sbt ├── conf │ ├── application.conf │ └── routes ├── init_precompiled.sh ├── project │ ├── build.properties │ └── plugins.sbt ├── public │ ├── images │ │ └── favicon.png │ ├── javascripts │ │ └── hello.js │ └── stylesheets │ │ └── main.css ├── run_precompiled.sh └── test │ ├── ApplicationSpec.scala │ └── IntegrationSpec.scala ├── chapter3 ├── .gitignore ├── LICENSE ├── README ├── activator ├── activator-launch-1.2.12.jar ├── activator.bat ├── app │ ├── controllers │ │ ├── Application.scala │ │ └── FavouriteStudios.scala │ ├── models │ │ ├── FavouriteStudio.scala │ │ └── dao │ │ │ └── FavouriteStudioDAO.scala │ └── views │ │ ├── index.scala.html │ │ └── main.scala.html ├── build.sbt ├── conf │ ├── application.conf │ └── routes ├── init_precompiled.sh ├── project │ ├── build.properties │ └── plugins.sbt ├── public │ ├── images │ │ └── favicon.png │ ├── javascripts │ │ └── hello.js │ └── stylesheets │ │ └── main.css ├── run_precompiled.sh └── test │ ├── ApplicationSpec.scala │ └── IntegrationSpec.scala ├── chapter4 ├── .gitignore ├── LICENSE ├── README ├── activator ├── activator-launch-1.2.12.jar ├── activator.bat ├── app │ ├── actors │ │ └── NotificationActor.scala │ ├── controllers │ │ ├── Application.scala │ │ └── FavouriteStudios.scala │ ├── models │ │ ├── FavouriteStudio.scala │ │ └── dao │ │ │ └── FavouriteStudioDAO.scala │ └── views │ │ ├── index.scala.html │ │ └── main.scala.html ├── build.sbt ├── conf │ ├── application.conf │ └── routes ├── init_precompiled.sh ├── project │ ├── build.properties │ └── plugins.sbt ├── public │ ├── images │ │ └── favicon.png │ ├── javascripts │ │ └── hello.js │ └── stylesheets │ │ └── main.css ├── run_precompiled.sh └── test │ ├── ApplicationSpec.scala │ └── IntegrationSpec.scala ├── chapter5 ├── .gitignore ├── LICENSE ├── README ├── activator ├── activator-launch-1.2.12.jar ├── activator.bat ├── app │ ├── actors │ │ └── NotificationActor.scala │ ├── controllers │ │ ├── Application.scala │ │ └── FavouriteStudios.scala │ ├── models │ │ ├── FavouriteNotification.scala │ │ ├── FavouriteStudio.scala │ │ ├── Friend.scala │ │ └── dao │ │ │ ├── FavouriteStudioDAO.scala │ │ │ └── FriendDAO.scala │ └── views │ │ ├── index.scala.html │ │ └── main.scala.html ├── build.sbt ├── conf │ ├── application.conf │ └── routes ├── init_precompiled.sh ├── project │ ├── build.properties │ └── plugins.sbt ├── public │ ├── images │ │ └── favicon.png │ ├── javascripts │ │ └── hello.js │ └── stylesheets │ │ └── main.css ├── run_precompiled.sh └── test │ ├── ApplicationSpec.scala │ └── IntegrationSpec.scala ├── demo-script.txt ├── friend-service ├── .gitignore ├── LICENSE ├── README ├── activator ├── activator-launch-1.2.12.jar ├── activator.bat ├── app │ ├── controllers │ │ ├── Application.scala │ │ └── Friends.scala │ └── views │ │ ├── index.scala.html │ │ └── main.scala.html ├── build.sbt ├── conf │ ├── application.conf │ └── routes ├── project │ ├── build.properties │ └── plugins.sbt ├── public │ ├── images │ │ └── favicon.png │ ├── javascripts │ │ └── hello.js │ └── stylesheets │ │ └── main.css └── test │ ├── ApplicationSpec.scala │ └── IntegrationSpec.scala ├── init_demos.sh ├── mysql ├── db_clean.sql ├── db_init.sql ├── db_schema.sql └── init.sh └── precompile.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # applicative-demo 2 | Demo code for my talk at applicative conf. http://applicative.acm.org/speaker-TobySullivan.html 3 | 4 | Check out the slides at https://tobyjsullivan.github.io/applicative-slides 5 | 6 | #### Troubleshooting 7 | If classes aren't loading properly in IntelliJ, try File >> Invalidate Caches / Restart 8 | -------------------------------------------------------------------------------- /chapter0/.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | project/target 4 | target 5 | tmp 6 | .history 7 | dist 8 | /.idea 9 | /*.iml 10 | /out 11 | /.idea_modules 12 | /.classpath 13 | /.project 14 | /RUNNING_PID 15 | /.settings 16 | -------------------------------------------------------------------------------- /chapter0/LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 8 | language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /chapter0/README: -------------------------------------------------------------------------------- 1 | This is your new Play application 2 | ================================= 3 | 4 | This file will be packaged with your application, when using `activator dist`. 5 | -------------------------------------------------------------------------------- /chapter0/activator: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### ------------------------------- ### 4 | ### Helper methods for BASH scripts ### 5 | ### ------------------------------- ### 6 | 7 | realpath () { 8 | ( 9 | TARGET_FILE="$1" 10 | 11 | cd $(dirname "$TARGET_FILE") 12 | TARGET_FILE=$(basename "$TARGET_FILE") 13 | 14 | COUNT=0 15 | while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ] 16 | do 17 | TARGET_FILE=$(readlink "$TARGET_FILE") 18 | cd $(dirname "$TARGET_FILE") 19 | TARGET_FILE=$(basename "$TARGET_FILE") 20 | COUNT=$(($COUNT + 1)) 21 | done 22 | 23 | if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then 24 | cd "$TARGET_FILE" 25 | TARGET_FILEPATH= 26 | else 27 | TARGET_FILEPATH=/$TARGET_FILE 28 | fi 29 | 30 | # make sure we grab the actual windows path, instead of cygwin's path. 31 | if ! is_cygwin; then 32 | echo "$(pwd -P)/$TARGET_FILE" 33 | else 34 | echo $(cygwinpath "$(pwd -P)/$TARGET_FILE") 35 | fi 36 | ) 37 | } 38 | 39 | # TODO - Do we need to detect msys? 40 | 41 | # Uses uname to detect if we're in the odd cygwin environment. 42 | is_cygwin() { 43 | local os=$(uname -s) 44 | case "$os" in 45 | CYGWIN*) return 0 ;; 46 | *) return 1 ;; 47 | esac 48 | } 49 | 50 | # This can fix cygwin style /cygdrive paths so we get the 51 | # windows style paths. 52 | cygwinpath() { 53 | local file="$1" 54 | if is_cygwin; then 55 | echo $(cygpath -w $file) 56 | else 57 | echo $file 58 | fi 59 | } 60 | 61 | # Make something URI friendly 62 | make_url() { 63 | url="$1" 64 | local nospaces=${url// /%20} 65 | if is_cygwin; then 66 | echo "/${nospaces//\\//}" 67 | else 68 | echo "$nospaces" 69 | fi 70 | } 71 | 72 | # Detect if we should use JAVA_HOME or just try PATH. 73 | get_java_cmd() { 74 | if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then 75 | echo "$JAVA_HOME/bin/java" 76 | else 77 | echo "java" 78 | fi 79 | } 80 | 81 | echoerr () { 82 | echo 1>&2 "$@" 83 | } 84 | vlog () { 85 | [[ $verbose || $debug ]] && echoerr "$@" 86 | } 87 | dlog () { 88 | [[ $debug ]] && echoerr "$@" 89 | } 90 | execRunner () { 91 | # print the arguments one to a line, quoting any containing spaces 92 | [[ $verbose || $debug ]] && echo "# Executing command line:" && { 93 | for arg; do 94 | if printf "%s\n" "$arg" | grep -q ' '; then 95 | printf "\"%s\"\n" "$arg" 96 | else 97 | printf "%s\n" "$arg" 98 | fi 99 | done 100 | echo "" 101 | } 102 | 103 | exec "$@" 104 | } 105 | addJava () { 106 | dlog "[addJava] arg = '$1'" 107 | java_args=( "${java_args[@]}" "$1" ) 108 | } 109 | addApp () { 110 | dlog "[addApp] arg = '$1'" 111 | sbt_commands=( "${app_commands[@]}" "$1" ) 112 | } 113 | addResidual () { 114 | dlog "[residual] arg = '$1'" 115 | residual_args=( "${residual_args[@]}" "$1" ) 116 | } 117 | addDebugger () { 118 | addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" 119 | } 120 | addConfigOpts () { 121 | dlog "[addConfigOpts] arg = '$*'" 122 | for item in $* 123 | do 124 | addJava "$item" 125 | done 126 | } 127 | # a ham-fisted attempt to move some memory settings in concert 128 | # so they need not be messed around with individually. 129 | get_mem_opts () { 130 | local mem=${1:-1024} 131 | local meta=$(( $mem / 4 )) 132 | (( $meta > 256 )) || meta=256 133 | (( $meta < 1024 )) || meta=1024 134 | 135 | # default is to set memory options but this can be overridden by code section below 136 | memopts="-Xms${mem}m -Xmx${mem}m" 137 | if [[ "${java_version}" > "1.8" ]]; then 138 | extmemopts="-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=${meta}m" 139 | else 140 | extmemopts="-XX:PermSize=64m -XX:MaxPermSize=${meta}m" 141 | fi 142 | 143 | if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]] || [[ "${java_opts}" == *-XX:MaxMetaspaceSize* ]]; then 144 | # if we detect any of these settings in ${java_opts} we need to NOT output our settings. 145 | # The reason is the Xms/Xmx, if they don't line up, cause errors. 146 | memopts="" 147 | extmemopts="" 148 | fi 149 | 150 | echo "${memopts} ${extmemopts}" 151 | } 152 | require_arg () { 153 | local type="$1" 154 | local opt="$2" 155 | local arg="$3" 156 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then 157 | die "$opt requires <$type> argument" 158 | fi 159 | } 160 | is_function_defined() { 161 | declare -f "$1" > /dev/null 162 | } 163 | 164 | # If we're *not* running in a terminal, and we don't have any arguments, then we need to add the 'ui' parameter 165 | detect_terminal_for_ui() { 166 | [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && { 167 | addResidual "ui" 168 | } 169 | # SPECIAL TEST FOR MAC 170 | [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && { 171 | echo "Detected MAC OSX launched script...." 172 | echo "Swapping to UI" 173 | addResidual "ui" 174 | } 175 | } 176 | 177 | # Processes incoming arguments and places them in appropriate global variables. called by the run method. 178 | process_args () { 179 | while [[ $# -gt 0 ]]; do 180 | case "$1" in 181 | -h|-help) usage; exit 1 ;; 182 | -v|-verbose) verbose=1 && shift ;; 183 | -d|-debug) debug=1 && shift ;; 184 | -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;; 185 | -jvm-debug) 186 | if echo "$2" | grep -E ^[0-9]+$ > /dev/null; then 187 | addDebugger "$2" && shift 188 | else 189 | addDebugger 9999 190 | fi 191 | shift ;; 192 | -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;; 193 | -D*) addJava "$1" && shift ;; 194 | -J*) addJava "${1:2}" && shift ;; 195 | *) addResidual "$1" && shift ;; 196 | esac 197 | done 198 | 199 | is_function_defined process_my_args && { 200 | myargs=("${residual_args[@]}") 201 | residual_args=() 202 | process_my_args "${myargs[@]}" 203 | } 204 | } 205 | 206 | # Actually runs the script. 207 | run() { 208 | # TODO - check for sane environment 209 | 210 | # process the combined args, then reset "$@" to the residuals 211 | process_args "$@" 212 | detect_terminal_for_ui 213 | set -- "${residual_args[@]}" 214 | argumentCount=$# 215 | 216 | #check for jline terminal fixes on cygwin 217 | if is_cygwin; then 218 | stty -icanon min 1 -echo > /dev/null 2>&1 219 | addJava "-Djline.terminal=jline.UnixTerminal" 220 | addJava "-Dsbt.cygwin=true" 221 | fi 222 | 223 | # run sbt 224 | execRunner "$java_cmd" \ 225 | "-Dactivator.home=$(make_url "$activator_home")" \ 226 | $(get_mem_opts $app_mem) \ 227 | ${java_opts[@]} \ 228 | ${java_args[@]} \ 229 | -jar "$app_launcher" \ 230 | "${app_commands[@]}" \ 231 | "${residual_args[@]}" 232 | 233 | local exit_code=$? 234 | if is_cygwin; then 235 | stty icanon echo > /dev/null 2>&1 236 | fi 237 | exit $exit_code 238 | } 239 | 240 | # Loads a configuration file full of default command line options for this script. 241 | loadConfigFile() { 242 | cat "$1" | sed '/^\#/d' 243 | } 244 | 245 | ### ------------------------------- ### 246 | ### Start of customized settings ### 247 | ### ------------------------------- ### 248 | usage() { 249 | cat < [options] 251 | 252 | Command: 253 | ui Start the Activator UI 254 | new [name] [template-id] Create a new project with [name] using template [template-id] 255 | list-templates Print all available template names 256 | -h | -help Print this message 257 | 258 | Options: 259 | -v | -verbose Make this runner chattier 260 | -d | -debug Set sbt log level to debug 261 | -mem Set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem)) 262 | -jvm-debug Turn on JVM debugging, open at the given port. 263 | 264 | # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) 265 | -java-home Alternate JAVA_HOME 266 | 267 | # jvm options and output control 268 | -Dkey=val Pass -Dkey=val directly to the java runtime 269 | -J-X Pass option -X directly to the java runtime 270 | (-J is stripped) 271 | 272 | # environment variables (read from context) 273 | JAVA_OPTS Environment variable, if unset uses "" 274 | SBT_OPTS Environment variable, if unset uses "" 275 | ACTIVATOR_OPTS Environment variable, if unset uses "" 276 | 277 | In the case of duplicated or conflicting options, the order above 278 | shows precedence: environment variables lowest, command line options highest. 279 | EOM 280 | } 281 | 282 | ### ------------------------------- ### 283 | ### Main script ### 284 | ### ------------------------------- ### 285 | 286 | declare -a residual_args 287 | declare -a java_args 288 | declare -a app_commands 289 | declare -r real_script_path="$(realpath "$0")" 290 | declare -r activator_home="$(realpath "$(dirname "$real_script_path")")" 291 | declare -r app_version="1.2.12" 292 | 293 | declare -r app_launcher="${activator_home}/activator-launch-${app_version}.jar" 294 | declare -r script_name=activator 295 | declare -r java_cmd=$(get_java_cmd) 296 | declare -r java_opts=( "${ACTIVATOR_OPTS[@]}" "${SBT_OPTS[@]}" "${JAVA_OPTS[@]}" "${java_opts[@]}" ) 297 | userhome="$HOME" 298 | if is_cygwin; then 299 | # cygwin sets home to something f-d up, set to real windows homedir 300 | userhome="$USERPROFILE" 301 | fi 302 | declare -r activator_user_home_dir="${userhome}/.activator" 303 | declare -r java_opts_config_home="${activator_user_home_dir}/activatorconfig.txt" 304 | declare -r java_opts_config_version="${activator_user_home_dir}/${app_version}/activatorconfig.txt" 305 | 306 | # Now check to see if it's a good enough version 307 | declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}') 308 | if [[ "$java_version" == "" ]]; then 309 | echo 310 | echo No java installations was detected. 311 | echo Please go to http://www.java.com/getjava/ and download 312 | echo 313 | exit 1 314 | elif [[ ! "$java_version" > "1.6" ]]; then 315 | echo 316 | echo The java installation you have is not up to date 317 | echo Activator requires at least version 1.6+, you have 318 | echo version $java_version 319 | echo 320 | echo Please go to http://www.java.com/getjava/ and download 321 | echo a valid Java Runtime and install before running Activator. 322 | echo 323 | exit 1 324 | fi 325 | 326 | # if configuration files exist, prepend their contents to the java args so it can be processed by this runner 327 | # a "versioned" config trumps one on the top level 328 | if [[ -f "$java_opts_config_version" ]]; then 329 | addConfigOpts $(loadConfigFile "$java_opts_config_version") 330 | elif [[ -f "$java_opts_config_home" ]]; then 331 | addConfigOpts $(loadConfigFile "$java_opts_config_home") 332 | fi 333 | 334 | run "$@" 335 | -------------------------------------------------------------------------------- /chapter0/activator-launch-1.2.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter0/activator-launch-1.2.12.jar -------------------------------------------------------------------------------- /chapter0/activator.bat: -------------------------------------------------------------------------------- 1 | @REM activator launcher script 2 | @REM 3 | @REM Envioronment: 4 | @REM JAVA_HOME - location of a JDK home dir (optional if java on path) 5 | @REM CFG_OPTS - JVM options (optional) 6 | @REM Configuration: 7 | @REM activatorconfig.txt found in the ACTIVATOR_HOME or ACTIVATOR_HOME/ACTIVATOR_VERSION 8 | @setlocal enabledelayedexpansion 9 | 10 | @echo off 11 | 12 | set "var1=%~1" 13 | if defined var1 ( 14 | if "%var1%"=="help" ( 15 | echo. 16 | echo Usage activator [options] [command] 17 | echo. 18 | echo Commands: 19 | echo ui Start the Activator UI 20 | echo new [name] [template-id] Create a new project with [name] using template [template-id] 21 | echo list-templates Print all available template names 22 | echo help Print this message 23 | echo. 24 | echo Options: 25 | echo -jvm-debug [port] Turn on JVM debugging, open at the given port. Defaults to 9999 if no port given. 26 | echo. 27 | echo Environment variables ^(read from context^): 28 | echo JAVA_OPTS Environment variable, if unset uses "" 29 | echo SBT_OPTS Environment variable, if unset uses "" 30 | echo ACTIVATOR_OPTS Environment variable, if unset uses "" 31 | echo. 32 | goto :end 33 | ) 34 | ) 35 | 36 | if "%ACTIVATOR_HOME%"=="" ( 37 | set "ACTIVATOR_HOME=%~dp0" 38 | @REM remove trailing "\" from path 39 | set ACTIVATOR_HOME=!ACTIVATOR_HOME:~0,-1! 40 | ) 41 | 42 | set ERROR_CODE=0 43 | set APP_VERSION=1.2.12 44 | set ACTIVATOR_LAUNCH_JAR=activator-launch-%APP_VERSION%.jar 45 | 46 | rem Detect if we were double clicked, although theoretically A user could 47 | rem manually run cmd /c 48 | for %%x in (%cmdcmdline%) do if %%~x==/c set DOUBLECLICKED=1 49 | 50 | rem FIRST we load a config file of extra options (if there is one) 51 | set "CFG_FILE_HOME=%UserProfile%\.activator\activatorconfig.txt" 52 | set "CFG_FILE_VERSION=%UserProfile%\.activator\%APP_VERSION%\activatorconfig.txt" 53 | set CFG_OPTS= 54 | if exist %CFG_FILE_VERSION% ( 55 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_VERSION%") DO ( 56 | set DO_NOT_REUSE_ME=%%i 57 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 58 | rem CFG_OPTS, otherwise it remains "" for this loop. 59 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 60 | ) 61 | ) 62 | if "%CFG_OPTS%"=="" ( 63 | if exist %CFG_FILE_HOME% ( 64 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_HOME%") DO ( 65 | set DO_NOT_REUSE_ME=%%i 66 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 67 | rem CFG_OPTS, otherwise it remains "" for this loop. 68 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 69 | ) 70 | ) 71 | ) 72 | 73 | rem We use the value of the JAVACMD environment variable if defined 74 | set _JAVACMD=%JAVACMD% 75 | 76 | if "%_JAVACMD%"=="" ( 77 | if not "%JAVA_HOME%"=="" ( 78 | if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe" 79 | 80 | rem if there is a java home set we make sure it is the first picked up when invoking 'java' 81 | SET "PATH=%JAVA_HOME%\bin;%PATH%" 82 | ) 83 | ) 84 | 85 | if "%_JAVACMD%"=="" set _JAVACMD=java 86 | 87 | rem Detect if this java is ok to use. 88 | for /F %%j in ('"%_JAVACMD%" -version 2^>^&1') do ( 89 | if %%~j==Java set JAVAINSTALLED=1 90 | ) 91 | 92 | rem Detect the same thing about javac 93 | if "%_JAVACCMD%"=="" ( 94 | if not "%JAVA_HOME%"=="" ( 95 | if exist "%JAVA_HOME%\bin\javac.exe" set "_JAVACCMD=%JAVA_HOME%\bin\javac.exe" 96 | ) 97 | ) 98 | if "%_JAVACCMD%"=="" set _JAVACCMD=javac 99 | for /F %%j in ('"%_JAVACCMD%" -version 2^>^&1') do ( 100 | if %%~j==javac set JAVACINSTALLED=1 101 | ) 102 | 103 | rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style 104 | set JAVAOK=true 105 | if not defined JAVAINSTALLED set JAVAOK=false 106 | if not defined JAVACINSTALLED set JAVAOK=false 107 | 108 | if "%JAVAOK%"=="false" ( 109 | echo. 110 | echo A Java JDK is not installed or can't be found. 111 | if not "%JAVA_HOME%"=="" ( 112 | echo JAVA_HOME = "%JAVA_HOME%" 113 | ) 114 | echo. 115 | echo Please go to 116 | echo http://www.oracle.com/technetwork/java/javase/downloads/index.html 117 | echo and download a valid Java JDK and install before running Activator. 118 | echo. 119 | echo If you think this message is in error, please check 120 | echo your environment variables to see if "java.exe" and "javac.exe" are 121 | echo available via JAVA_HOME or PATH. 122 | echo. 123 | if defined DOUBLECLICKED pause 124 | exit /B 1 125 | ) 126 | 127 | rem Check what Java version is being used to determine what memory options to use 128 | for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( 129 | set JAVA_VERSION=%%g 130 | ) 131 | 132 | rem Strips away the " characters 133 | set JAVA_VERSION=%JAVA_VERSION:"=% 134 | 135 | rem TODO Check if there are existing mem settings in JAVA_OPTS/CFG_OPTS and use those instead of the below 136 | for /f "delims=. tokens=1-3" %%v in ("%JAVA_VERSION%") do ( 137 | set MAJOR=%%v 138 | set MINOR=%%w 139 | set BUILD=%%x 140 | 141 | set META_SIZE=-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=256M 142 | if "!MINOR!" LSS "8" ( 143 | set META_SIZE=-XX:PermSize=64M -XX:MaxPermSize=256M 144 | ) 145 | 146 | set MEM_OPTS=!META_SIZE! 147 | ) 148 | 149 | rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config. 150 | set _JAVA_OPTS=%JAVA_OPTS% 151 | if "%_JAVA_OPTS%"=="" set _JAVA_OPTS=%CFG_OPTS% 152 | 153 | set DEBUG_OPTS= 154 | 155 | rem Loop through the arguments, building remaining args in args variable 156 | set args= 157 | :argsloop 158 | if not "%~1"=="" ( 159 | rem Checks if the argument contains "-D" and if true, adds argument 1 with 2 and puts an equal sign between them. 160 | rem This is done since batch considers "=" to be a delimiter so we need to circumvent this behavior with a small hack. 161 | set arg1=%~1 162 | if "!arg1:~0,2!"=="-D" ( 163 | set "args=%args% "%~1"="%~2"" 164 | shift 165 | shift 166 | goto argsloop 167 | ) 168 | 169 | if "%~1"=="-jvm-debug" ( 170 | if not "%~2"=="" ( 171 | rem This piece of magic somehow checks that an argument is a number 172 | for /F "delims=0123456789" %%i in ("%~2") do ( 173 | set var="%%i" 174 | ) 175 | if defined var ( 176 | rem Not a number, assume no argument given and default to 9999 177 | set JPDA_PORT=9999 178 | ) else ( 179 | rem Port was given, shift arguments 180 | set JPDA_PORT=%~2 181 | shift 182 | ) 183 | ) else ( 184 | set JPDA_PORT=9999 185 | ) 186 | shift 187 | 188 | set DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=!JPDA_PORT! 189 | goto argsloop 190 | ) 191 | rem else 192 | set "args=%args% "%~1"" 193 | shift 194 | goto argsloop 195 | ) 196 | 197 | :run 198 | 199 | if "!args!"=="" ( 200 | if defined DOUBLECLICKED ( 201 | set CMDS="ui" 202 | ) else set CMDS=!args! 203 | ) else set CMDS=!args! 204 | 205 | rem We add a / in front, so we get file:///C: instead of file://C: 206 | rem Java considers the later a UNC path. 207 | rem We also attempt a solid effort at making it URI friendly. 208 | rem We don't even bother with UNC paths. 209 | set JAVA_FRIENDLY_HOME_1=/!ACTIVATOR_HOME:\=/! 210 | set JAVA_FRIENDLY_HOME=/!JAVA_FRIENDLY_HOME_1: =%%20! 211 | 212 | rem Checks if the command contains spaces to know if it should be wrapped in quotes or not 213 | set NON_SPACED_CMD=%_JAVACMD: =% 214 | if "%_JAVACMD%"=="%NON_SPACED_CMD%" %_JAVACMD% %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 215 | if NOT "%_JAVACMD%"=="%NON_SPACED_CMD%" "%_JAVACMD%" %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 216 | 217 | if ERRORLEVEL 1 goto error 218 | goto end 219 | 220 | :error 221 | set ERROR_CODE=1 222 | 223 | :end 224 | 225 | @endlocal 226 | 227 | exit /B %ERROR_CODE% 228 | -------------------------------------------------------------------------------- /chapter0/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api._ 4 | import play.api.mvc._ 5 | 6 | object Application extends Controller { 7 | 8 | def index = Action { 9 | Ok(views.html.index("Your new application is ready.")) 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /chapter0/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @main("Welcome to Play") { 4 | 5 | @play20.welcome(message) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /chapter0/app/views/main.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | 7 | @title 8 | 9 | 10 | 11 | 12 | 13 | @content 14 | 15 | 16 | -------------------------------------------------------------------------------- /chapter0/build.sbt: -------------------------------------------------------------------------------- 1 | name := """chapter0-demo""" 2 | 3 | version := "1.0" 4 | 5 | lazy val root = (project in file(".")).enablePlugins(PlayScala) 6 | 7 | doc in Compile <<= target.map(_ / "none") 8 | 9 | scalaVersion := "2.11.1" 10 | 11 | libraryDependencies ++= Seq( 12 | jdbc, 13 | anorm, 14 | cache, 15 | ws 16 | ) 17 | -------------------------------------------------------------------------------- /chapter0/conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # 8 | # This must be changed for production, but we recommend not changing it in this file. 9 | # 10 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. 11 | application.secret="Ev3V[cSVEMI0[<>86u^:9jahK6Bdc@" 12 | 13 | # The application languages 14 | # ~~~~~ 15 | application.langs="en" 16 | 17 | # Global object class 18 | # ~~~~~ 19 | # Define the Global object class for this application. 20 | # Default to Global in the root package. 21 | # application.global=Global 22 | 23 | # Router 24 | # ~~~~~ 25 | # Define the Router object to use for this application. 26 | # This router will be looked up first when the application is starting up, 27 | # so make sure this is the entry point. 28 | # Furthermore, it's assumed your route file is named properly. 29 | # So for an application router like `my.application.Router`, 30 | # you may need to define a router file `conf/my.application.routes`. 31 | # Default to Routes in the root package (and conf/routes) 32 | # application.router=my.application.Routes 33 | 34 | # Database configuration 35 | # ~~~~~ 36 | # You can declare as many datasources as you want. 37 | # By convention, the default datasource is named `default` 38 | # 39 | # db.default.driver=org.h2.Driver 40 | # db.default.url="jdbc:h2:mem:play" 41 | # db.default.user=sa 42 | # db.default.password="" 43 | 44 | # Evolutions 45 | # ~~~~~ 46 | # You can disable evolutions if needed 47 | # evolutionplugin=disabled 48 | 49 | # Logger 50 | # ~~~~~ 51 | # You can also configure logback (http://logback.qos.ch/), 52 | # by providing an application-logger.xml file in the conf directory. 53 | 54 | # Root logger: 55 | logger.root=ERROR 56 | 57 | # Logger used by the framework: 58 | logger.play=INFO 59 | 60 | # Logger provided to your application: 61 | logger.application=DEBUG 62 | 63 | -------------------------------------------------------------------------------- /chapter0/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # Home page 6 | GET / controllers.Application.index 7 | 8 | # Map static resources from the /public folder to the /assets URL path 9 | GET /assets/*file controllers.Assets.at(path="/public", file) 10 | -------------------------------------------------------------------------------- /chapter0/init_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Clean out dist 4 | rm -rf target/universal/* 5 | 6 | # Compile distributable 7 | sbt dist 8 | 9 | # Unzip components 10 | pushd target/universal 11 | unzip chapter0-demo-1.0.zip 12 | popd 13 | -------------------------------------------------------------------------------- /chapter0/project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Sat Feb 21 15:10:19 PST 2015 3 | template.uuid=d2d450ff-ffb2-4c93-9354-c3b15fc933ea 4 | sbt.version=0.13.5 5 | -------------------------------------------------------------------------------- /chapter0/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 2 | 3 | // The Play plugin 4 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8") 5 | 6 | // web plugins 7 | 8 | addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") 9 | 10 | addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.0") 11 | 12 | addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.1") 13 | 14 | addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.1") 15 | 16 | addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.0.0") 17 | 18 | addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.0.0") 19 | -------------------------------------------------------------------------------- /chapter0/public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter0/public/images/favicon.png -------------------------------------------------------------------------------- /chapter0/public/javascripts/hello.js: -------------------------------------------------------------------------------- 1 | if (window.console) { 2 | console.log("Welcome to your Play application's JavaScript!"); 3 | } -------------------------------------------------------------------------------- /chapter0/public/stylesheets/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter0/public/stylesheets/main.css -------------------------------------------------------------------------------- /chapter0/run_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Run 4 | pushd target/universal/chapter0-demo-1.0/bin 5 | ./chapter0-demo 6 | popd 7 | -------------------------------------------------------------------------------- /chapter0/test/ApplicationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * Add your spec here. 10 | * You can mock out a whole application including requests, plugins etc. 11 | * For more information, consult the wiki. 12 | */ 13 | @RunWith(classOf[JUnitRunner]) 14 | class ApplicationSpec extends Specification { 15 | 16 | "Application" should { 17 | 18 | "send 404 on a bad request" in new WithApplication{ 19 | route(FakeRequest(GET, "/boum")) must beNone 20 | } 21 | 22 | "render the index page" in new WithApplication{ 23 | val home = route(FakeRequest(GET, "/")).get 24 | 25 | status(home) must equalTo(OK) 26 | contentType(home) must beSome.which(_ == "text/html") 27 | contentAsString(home) must contain ("Your new application is ready.") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter0/test/IntegrationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * add your integration spec here. 10 | * An integration test will fire up a whole play application in a real (or headless) browser 11 | */ 12 | @RunWith(classOf[JUnitRunner]) 13 | class IntegrationSpec extends Specification { 14 | 15 | "Application" should { 16 | 17 | "work from within a browser" in new WithBrowser { 18 | 19 | browser.goTo("http://localhost:" + port) 20 | 21 | browser.pageSource must contain("Your new application is ready.") 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter1/.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | project/target 4 | target 5 | tmp 6 | .history 7 | dist 8 | /.idea 9 | /*.iml 10 | /out 11 | /.idea_modules 12 | /.classpath 13 | /.project 14 | /RUNNING_PID 15 | /.settings 16 | -------------------------------------------------------------------------------- /chapter1/LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 8 | language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /chapter1/README: -------------------------------------------------------------------------------- 1 | This is your new Play application 2 | ================================= 3 | 4 | This file will be packaged with your application, when using `activator dist`. 5 | -------------------------------------------------------------------------------- /chapter1/activator: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### ------------------------------- ### 4 | ### Helper methods for BASH scripts ### 5 | ### ------------------------------- ### 6 | 7 | realpath () { 8 | ( 9 | TARGET_FILE="$1" 10 | 11 | cd $(dirname "$TARGET_FILE") 12 | TARGET_FILE=$(basename "$TARGET_FILE") 13 | 14 | COUNT=0 15 | while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ] 16 | do 17 | TARGET_FILE=$(readlink "$TARGET_FILE") 18 | cd $(dirname "$TARGET_FILE") 19 | TARGET_FILE=$(basename "$TARGET_FILE") 20 | COUNT=$(($COUNT + 1)) 21 | done 22 | 23 | if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then 24 | cd "$TARGET_FILE" 25 | TARGET_FILEPATH= 26 | else 27 | TARGET_FILEPATH=/$TARGET_FILE 28 | fi 29 | 30 | # make sure we grab the actual windows path, instead of cygwin's path. 31 | if ! is_cygwin; then 32 | echo "$(pwd -P)/$TARGET_FILE" 33 | else 34 | echo $(cygwinpath "$(pwd -P)/$TARGET_FILE") 35 | fi 36 | ) 37 | } 38 | 39 | # TODO - Do we need to detect msys? 40 | 41 | # Uses uname to detect if we're in the odd cygwin environment. 42 | is_cygwin() { 43 | local os=$(uname -s) 44 | case "$os" in 45 | CYGWIN*) return 0 ;; 46 | *) return 1 ;; 47 | esac 48 | } 49 | 50 | # This can fix cygwin style /cygdrive paths so we get the 51 | # windows style paths. 52 | cygwinpath() { 53 | local file="$1" 54 | if is_cygwin; then 55 | echo $(cygpath -w $file) 56 | else 57 | echo $file 58 | fi 59 | } 60 | 61 | # Make something URI friendly 62 | make_url() { 63 | url="$1" 64 | local nospaces=${url// /%20} 65 | if is_cygwin; then 66 | echo "/${nospaces//\\//}" 67 | else 68 | echo "$nospaces" 69 | fi 70 | } 71 | 72 | # Detect if we should use JAVA_HOME or just try PATH. 73 | get_java_cmd() { 74 | if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then 75 | echo "$JAVA_HOME/bin/java" 76 | else 77 | echo "java" 78 | fi 79 | } 80 | 81 | echoerr () { 82 | echo 1>&2 "$@" 83 | } 84 | vlog () { 85 | [[ $verbose || $debug ]] && echoerr "$@" 86 | } 87 | dlog () { 88 | [[ $debug ]] && echoerr "$@" 89 | } 90 | execRunner () { 91 | # print the arguments one to a line, quoting any containing spaces 92 | [[ $verbose || $debug ]] && echo "# Executing command line:" && { 93 | for arg; do 94 | if printf "%s\n" "$arg" | grep -q ' '; then 95 | printf "\"%s\"\n" "$arg" 96 | else 97 | printf "%s\n" "$arg" 98 | fi 99 | done 100 | echo "" 101 | } 102 | 103 | exec "$@" 104 | } 105 | addJava () { 106 | dlog "[addJava] arg = '$1'" 107 | java_args=( "${java_args[@]}" "$1" ) 108 | } 109 | addApp () { 110 | dlog "[addApp] arg = '$1'" 111 | sbt_commands=( "${app_commands[@]}" "$1" ) 112 | } 113 | addResidual () { 114 | dlog "[residual] arg = '$1'" 115 | residual_args=( "${residual_args[@]}" "$1" ) 116 | } 117 | addDebugger () { 118 | addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" 119 | } 120 | addConfigOpts () { 121 | dlog "[addConfigOpts] arg = '$*'" 122 | for item in $* 123 | do 124 | addJava "$item" 125 | done 126 | } 127 | # a ham-fisted attempt to move some memory settings in concert 128 | # so they need not be messed around with individually. 129 | get_mem_opts () { 130 | local mem=${1:-1024} 131 | local meta=$(( $mem / 4 )) 132 | (( $meta > 256 )) || meta=256 133 | (( $meta < 1024 )) || meta=1024 134 | 135 | # default is to set memory options but this can be overridden by code section below 136 | memopts="-Xms${mem}m -Xmx${mem}m" 137 | if [[ "${java_version}" > "1.8" ]]; then 138 | extmemopts="-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=${meta}m" 139 | else 140 | extmemopts="-XX:PermSize=64m -XX:MaxPermSize=${meta}m" 141 | fi 142 | 143 | if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]] || [[ "${java_opts}" == *-XX:MaxMetaspaceSize* ]]; then 144 | # if we detect any of these settings in ${java_opts} we need to NOT output our settings. 145 | # The reason is the Xms/Xmx, if they don't line up, cause errors. 146 | memopts="" 147 | extmemopts="" 148 | fi 149 | 150 | echo "${memopts} ${extmemopts}" 151 | } 152 | require_arg () { 153 | local type="$1" 154 | local opt="$2" 155 | local arg="$3" 156 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then 157 | die "$opt requires <$type> argument" 158 | fi 159 | } 160 | is_function_defined() { 161 | declare -f "$1" > /dev/null 162 | } 163 | 164 | # If we're *not* running in a terminal, and we don't have any arguments, then we need to add the 'ui' parameter 165 | detect_terminal_for_ui() { 166 | [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && { 167 | addResidual "ui" 168 | } 169 | # SPECIAL TEST FOR MAC 170 | [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && { 171 | echo "Detected MAC OSX launched script...." 172 | echo "Swapping to UI" 173 | addResidual "ui" 174 | } 175 | } 176 | 177 | # Processes incoming arguments and places them in appropriate global variables. called by the run method. 178 | process_args () { 179 | while [[ $# -gt 0 ]]; do 180 | case "$1" in 181 | -h|-help) usage; exit 1 ;; 182 | -v|-verbose) verbose=1 && shift ;; 183 | -d|-debug) debug=1 && shift ;; 184 | -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;; 185 | -jvm-debug) 186 | if echo "$2" | grep -E ^[0-9]+$ > /dev/null; then 187 | addDebugger "$2" && shift 188 | else 189 | addDebugger 9999 190 | fi 191 | shift ;; 192 | -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;; 193 | -D*) addJava "$1" && shift ;; 194 | -J*) addJava "${1:2}" && shift ;; 195 | *) addResidual "$1" && shift ;; 196 | esac 197 | done 198 | 199 | is_function_defined process_my_args && { 200 | myargs=("${residual_args[@]}") 201 | residual_args=() 202 | process_my_args "${myargs[@]}" 203 | } 204 | } 205 | 206 | # Actually runs the script. 207 | run() { 208 | # TODO - check for sane environment 209 | 210 | # process the combined args, then reset "$@" to the residuals 211 | process_args "$@" 212 | detect_terminal_for_ui 213 | set -- "${residual_args[@]}" 214 | argumentCount=$# 215 | 216 | #check for jline terminal fixes on cygwin 217 | if is_cygwin; then 218 | stty -icanon min 1 -echo > /dev/null 2>&1 219 | addJava "-Djline.terminal=jline.UnixTerminal" 220 | addJava "-Dsbt.cygwin=true" 221 | fi 222 | 223 | # run sbt 224 | execRunner "$java_cmd" \ 225 | "-Dactivator.home=$(make_url "$activator_home")" \ 226 | $(get_mem_opts $app_mem) \ 227 | ${java_opts[@]} \ 228 | ${java_args[@]} \ 229 | -jar "$app_launcher" \ 230 | "${app_commands[@]}" \ 231 | "${residual_args[@]}" 232 | 233 | local exit_code=$? 234 | if is_cygwin; then 235 | stty icanon echo > /dev/null 2>&1 236 | fi 237 | exit $exit_code 238 | } 239 | 240 | # Loads a configuration file full of default command line options for this script. 241 | loadConfigFile() { 242 | cat "$1" | sed '/^\#/d' 243 | } 244 | 245 | ### ------------------------------- ### 246 | ### Start of customized settings ### 247 | ### ------------------------------- ### 248 | usage() { 249 | cat < [options] 251 | 252 | Command: 253 | ui Start the Activator UI 254 | new [name] [template-id] Create a new project with [name] using template [template-id] 255 | list-templates Print all available template names 256 | -h | -help Print this message 257 | 258 | Options: 259 | -v | -verbose Make this runner chattier 260 | -d | -debug Set sbt log level to debug 261 | -mem Set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem)) 262 | -jvm-debug Turn on JVM debugging, open at the given port. 263 | 264 | # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) 265 | -java-home Alternate JAVA_HOME 266 | 267 | # jvm options and output control 268 | -Dkey=val Pass -Dkey=val directly to the java runtime 269 | -J-X Pass option -X directly to the java runtime 270 | (-J is stripped) 271 | 272 | # environment variables (read from context) 273 | JAVA_OPTS Environment variable, if unset uses "" 274 | SBT_OPTS Environment variable, if unset uses "" 275 | ACTIVATOR_OPTS Environment variable, if unset uses "" 276 | 277 | In the case of duplicated or conflicting options, the order above 278 | shows precedence: environment variables lowest, command line options highest. 279 | EOM 280 | } 281 | 282 | ### ------------------------------- ### 283 | ### Main script ### 284 | ### ------------------------------- ### 285 | 286 | declare -a residual_args 287 | declare -a java_args 288 | declare -a app_commands 289 | declare -r real_script_path="$(realpath "$0")" 290 | declare -r activator_home="$(realpath "$(dirname "$real_script_path")")" 291 | declare -r app_version="1.2.12" 292 | 293 | declare -r app_launcher="${activator_home}/activator-launch-${app_version}.jar" 294 | declare -r script_name=activator 295 | declare -r java_cmd=$(get_java_cmd) 296 | declare -r java_opts=( "${ACTIVATOR_OPTS[@]}" "${SBT_OPTS[@]}" "${JAVA_OPTS[@]}" "${java_opts[@]}" ) 297 | userhome="$HOME" 298 | if is_cygwin; then 299 | # cygwin sets home to something f-d up, set to real windows homedir 300 | userhome="$USERPROFILE" 301 | fi 302 | declare -r activator_user_home_dir="${userhome}/.activator" 303 | declare -r java_opts_config_home="${activator_user_home_dir}/activatorconfig.txt" 304 | declare -r java_opts_config_version="${activator_user_home_dir}/${app_version}/activatorconfig.txt" 305 | 306 | # Now check to see if it's a good enough version 307 | declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}') 308 | if [[ "$java_version" == "" ]]; then 309 | echo 310 | echo No java installations was detected. 311 | echo Please go to http://www.java.com/getjava/ and download 312 | echo 313 | exit 1 314 | elif [[ ! "$java_version" > "1.6" ]]; then 315 | echo 316 | echo The java installation you have is not up to date 317 | echo Activator requires at least version 1.6+, you have 318 | echo version $java_version 319 | echo 320 | echo Please go to http://www.java.com/getjava/ and download 321 | echo a valid Java Runtime and install before running Activator. 322 | echo 323 | exit 1 324 | fi 325 | 326 | # if configuration files exist, prepend their contents to the java args so it can be processed by this runner 327 | # a "versioned" config trumps one on the top level 328 | if [[ -f "$java_opts_config_version" ]]; then 329 | addConfigOpts $(loadConfigFile "$java_opts_config_version") 330 | elif [[ -f "$java_opts_config_home" ]]; then 331 | addConfigOpts $(loadConfigFile "$java_opts_config_home") 332 | fi 333 | 334 | run "$@" 335 | -------------------------------------------------------------------------------- /chapter1/activator-launch-1.2.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter1/activator-launch-1.2.12.jar -------------------------------------------------------------------------------- /chapter1/activator.bat: -------------------------------------------------------------------------------- 1 | @REM activator launcher script 2 | @REM 3 | @REM Envioronment: 4 | @REM JAVA_HOME - location of a JDK home dir (optional if java on path) 5 | @REM CFG_OPTS - JVM options (optional) 6 | @REM Configuration: 7 | @REM activatorconfig.txt found in the ACTIVATOR_HOME or ACTIVATOR_HOME/ACTIVATOR_VERSION 8 | @setlocal enabledelayedexpansion 9 | 10 | @echo off 11 | 12 | set "var1=%~1" 13 | if defined var1 ( 14 | if "%var1%"=="help" ( 15 | echo. 16 | echo Usage activator [options] [command] 17 | echo. 18 | echo Commands: 19 | echo ui Start the Activator UI 20 | echo new [name] [template-id] Create a new project with [name] using template [template-id] 21 | echo list-templates Print all available template names 22 | echo help Print this message 23 | echo. 24 | echo Options: 25 | echo -jvm-debug [port] Turn on JVM debugging, open at the given port. Defaults to 9999 if no port given. 26 | echo. 27 | echo Environment variables ^(read from context^): 28 | echo JAVA_OPTS Environment variable, if unset uses "" 29 | echo SBT_OPTS Environment variable, if unset uses "" 30 | echo ACTIVATOR_OPTS Environment variable, if unset uses "" 31 | echo. 32 | goto :end 33 | ) 34 | ) 35 | 36 | if "%ACTIVATOR_HOME%"=="" ( 37 | set "ACTIVATOR_HOME=%~dp0" 38 | @REM remove trailing "\" from path 39 | set ACTIVATOR_HOME=!ACTIVATOR_HOME:~0,-1! 40 | ) 41 | 42 | set ERROR_CODE=0 43 | set APP_VERSION=1.2.12 44 | set ACTIVATOR_LAUNCH_JAR=activator-launch-%APP_VERSION%.jar 45 | 46 | rem Detect if we were double clicked, although theoretically A user could 47 | rem manually run cmd /c 48 | for %%x in (%cmdcmdline%) do if %%~x==/c set DOUBLECLICKED=1 49 | 50 | rem FIRST we load a config file of extra options (if there is one) 51 | set "CFG_FILE_HOME=%UserProfile%\.activator\activatorconfig.txt" 52 | set "CFG_FILE_VERSION=%UserProfile%\.activator\%APP_VERSION%\activatorconfig.txt" 53 | set CFG_OPTS= 54 | if exist %CFG_FILE_VERSION% ( 55 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_VERSION%") DO ( 56 | set DO_NOT_REUSE_ME=%%i 57 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 58 | rem CFG_OPTS, otherwise it remains "" for this loop. 59 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 60 | ) 61 | ) 62 | if "%CFG_OPTS%"=="" ( 63 | if exist %CFG_FILE_HOME% ( 64 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_HOME%") DO ( 65 | set DO_NOT_REUSE_ME=%%i 66 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 67 | rem CFG_OPTS, otherwise it remains "" for this loop. 68 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 69 | ) 70 | ) 71 | ) 72 | 73 | rem We use the value of the JAVACMD environment variable if defined 74 | set _JAVACMD=%JAVACMD% 75 | 76 | if "%_JAVACMD%"=="" ( 77 | if not "%JAVA_HOME%"=="" ( 78 | if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe" 79 | 80 | rem if there is a java home set we make sure it is the first picked up when invoking 'java' 81 | SET "PATH=%JAVA_HOME%\bin;%PATH%" 82 | ) 83 | ) 84 | 85 | if "%_JAVACMD%"=="" set _JAVACMD=java 86 | 87 | rem Detect if this java is ok to use. 88 | for /F %%j in ('"%_JAVACMD%" -version 2^>^&1') do ( 89 | if %%~j==Java set JAVAINSTALLED=1 90 | ) 91 | 92 | rem Detect the same thing about javac 93 | if "%_JAVACCMD%"=="" ( 94 | if not "%JAVA_HOME%"=="" ( 95 | if exist "%JAVA_HOME%\bin\javac.exe" set "_JAVACCMD=%JAVA_HOME%\bin\javac.exe" 96 | ) 97 | ) 98 | if "%_JAVACCMD%"=="" set _JAVACCMD=javac 99 | for /F %%j in ('"%_JAVACCMD%" -version 2^>^&1') do ( 100 | if %%~j==javac set JAVACINSTALLED=1 101 | ) 102 | 103 | rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style 104 | set JAVAOK=true 105 | if not defined JAVAINSTALLED set JAVAOK=false 106 | if not defined JAVACINSTALLED set JAVAOK=false 107 | 108 | if "%JAVAOK%"=="false" ( 109 | echo. 110 | echo A Java JDK is not installed or can't be found. 111 | if not "%JAVA_HOME%"=="" ( 112 | echo JAVA_HOME = "%JAVA_HOME%" 113 | ) 114 | echo. 115 | echo Please go to 116 | echo http://www.oracle.com/technetwork/java/javase/downloads/index.html 117 | echo and download a valid Java JDK and install before running Activator. 118 | echo. 119 | echo If you think this message is in error, please check 120 | echo your environment variables to see if "java.exe" and "javac.exe" are 121 | echo available via JAVA_HOME or PATH. 122 | echo. 123 | if defined DOUBLECLICKED pause 124 | exit /B 1 125 | ) 126 | 127 | rem Check what Java version is being used to determine what memory options to use 128 | for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( 129 | set JAVA_VERSION=%%g 130 | ) 131 | 132 | rem Strips away the " characters 133 | set JAVA_VERSION=%JAVA_VERSION:"=% 134 | 135 | rem TODO Check if there are existing mem settings in JAVA_OPTS/CFG_OPTS and use those instead of the below 136 | for /f "delims=. tokens=1-3" %%v in ("%JAVA_VERSION%") do ( 137 | set MAJOR=%%v 138 | set MINOR=%%w 139 | set BUILD=%%x 140 | 141 | set META_SIZE=-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=256M 142 | if "!MINOR!" LSS "8" ( 143 | set META_SIZE=-XX:PermSize=64M -XX:MaxPermSize=256M 144 | ) 145 | 146 | set MEM_OPTS=!META_SIZE! 147 | ) 148 | 149 | rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config. 150 | set _JAVA_OPTS=%JAVA_OPTS% 151 | if "%_JAVA_OPTS%"=="" set _JAVA_OPTS=%CFG_OPTS% 152 | 153 | set DEBUG_OPTS= 154 | 155 | rem Loop through the arguments, building remaining args in args variable 156 | set args= 157 | :argsloop 158 | if not "%~1"=="" ( 159 | rem Checks if the argument contains "-D" and if true, adds argument 1 with 2 and puts an equal sign between them. 160 | rem This is done since batch considers "=" to be a delimiter so we need to circumvent this behavior with a small hack. 161 | set arg1=%~1 162 | if "!arg1:~0,2!"=="-D" ( 163 | set "args=%args% "%~1"="%~2"" 164 | shift 165 | shift 166 | goto argsloop 167 | ) 168 | 169 | if "%~1"=="-jvm-debug" ( 170 | if not "%~2"=="" ( 171 | rem This piece of magic somehow checks that an argument is a number 172 | for /F "delims=0123456789" %%i in ("%~2") do ( 173 | set var="%%i" 174 | ) 175 | if defined var ( 176 | rem Not a number, assume no argument given and default to 9999 177 | set JPDA_PORT=9999 178 | ) else ( 179 | rem Port was given, shift arguments 180 | set JPDA_PORT=%~2 181 | shift 182 | ) 183 | ) else ( 184 | set JPDA_PORT=9999 185 | ) 186 | shift 187 | 188 | set DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=!JPDA_PORT! 189 | goto argsloop 190 | ) 191 | rem else 192 | set "args=%args% "%~1"" 193 | shift 194 | goto argsloop 195 | ) 196 | 197 | :run 198 | 199 | if "!args!"=="" ( 200 | if defined DOUBLECLICKED ( 201 | set CMDS="ui" 202 | ) else set CMDS=!args! 203 | ) else set CMDS=!args! 204 | 205 | rem We add a / in front, so we get file:///C: instead of file://C: 206 | rem Java considers the later a UNC path. 207 | rem We also attempt a solid effort at making it URI friendly. 208 | rem We don't even bother with UNC paths. 209 | set JAVA_FRIENDLY_HOME_1=/!ACTIVATOR_HOME:\=/! 210 | set JAVA_FRIENDLY_HOME=/!JAVA_FRIENDLY_HOME_1: =%%20! 211 | 212 | rem Checks if the command contains spaces to know if it should be wrapped in quotes or not 213 | set NON_SPACED_CMD=%_JAVACMD: =% 214 | if "%_JAVACMD%"=="%NON_SPACED_CMD%" %_JAVACMD% %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 215 | if NOT "%_JAVACMD%"=="%NON_SPACED_CMD%" "%_JAVACMD%" %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 216 | 217 | if ERRORLEVEL 1 goto error 218 | goto end 219 | 220 | :error 221 | set ERROR_CODE=1 222 | 223 | :end 224 | 225 | @endlocal 226 | 227 | exit /B %ERROR_CODE% 228 | -------------------------------------------------------------------------------- /chapter1/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api._ 4 | import play.api.mvc._ 5 | 6 | object Application extends Controller { 7 | 8 | def index = Action { 9 | Ok(views.html.index("Your new application is ready.")) 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /chapter1/app/controllers/FavouriteStudios.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import models.FavouriteStudio 4 | import models.FavouriteStudio._ 5 | import play.api.libs.json.Json 6 | import play.api.mvc._ 7 | 8 | object FavouriteStudios extends Controller { 9 | def add(userId: Int, studioId: Int) = Action { 10 | // TODO: Add favourite 11 | 12 | Ok(Json.obj("result" -> FavouriteStudio(userId, studioId))) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chapter1/app/models/FavouriteStudio.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import play.api.libs.functional.syntax._ 4 | import play.api.libs.json._ 5 | 6 | object FavouriteStudio { 7 | implicit val favouriteStudioWrites: Writes[FavouriteStudio] = ( 8 | (JsPath \ "userId").write[Int] and 9 | (JsPath \ "studioId").write[Int] 10 | )(unlift(FavouriteStudio.unapply)) 11 | } 12 | 13 | case class FavouriteStudio(userId: Int, studioId: Int) 14 | -------------------------------------------------------------------------------- /chapter1/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @main("Welcome to Play") { 4 | 5 | @play20.welcome(message) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /chapter1/app/views/main.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | 7 | @title 8 | 9 | 10 | 11 | 12 | 13 | @content 14 | 15 | 16 | -------------------------------------------------------------------------------- /chapter1/build.sbt: -------------------------------------------------------------------------------- 1 | name := """chapter1-demo""" 2 | 3 | version := "1.0" 4 | 5 | lazy val root = (project in file(".")).enablePlugins(PlayScala) 6 | 7 | doc in Compile <<= target.map(_ / "none") 8 | 9 | scalaVersion := "2.11.1" 10 | 11 | libraryDependencies ++= Seq( 12 | jdbc, 13 | anorm, 14 | cache, 15 | ws 16 | ) 17 | -------------------------------------------------------------------------------- /chapter1/conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # 8 | # This must be changed for production, but we recommend not changing it in this file. 9 | # 10 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. 11 | application.secret="/u9PM[cJc=h>y=xGcV1W?<58iGchSJM7wXnfZ::qFTS" 12 | 13 | # The application languages 14 | # ~~~~~ 15 | application.langs="en" 16 | 17 | # Global object class 18 | # ~~~~~ 19 | # Define the Global object class for this application. 20 | # Default to Global in the root package. 21 | # application.global=Global 22 | 23 | # Router 24 | # ~~~~~ 25 | # Define the Router object to use for this application. 26 | # This router will be looked up first when the application is starting up, 27 | # so make sure this is the entry point. 28 | # Furthermore, it's assumed your route file is named properly. 29 | # So for an application router like `my.application.Router`, 30 | # you may need to define a router file `conf/my.application.routes`. 31 | # Default to Routes in the root package (and conf/routes) 32 | # application.router=my.application.Routes 33 | 34 | # Database configuration 35 | # ~~~~~ 36 | # You can declare as many datasources as you want. 37 | # By convention, the default datasource is named `default` 38 | # 39 | # db.default.driver=org.h2.Driver 40 | # db.default.url="jdbc:h2:mem:play" 41 | # db.default.user=sa 42 | # db.default.password="" 43 | 44 | # Evolutions 45 | # ~~~~~ 46 | # You can disable evolutions if needed 47 | # evolutionplugin=disabled 48 | 49 | # Logger 50 | # ~~~~~ 51 | # You can also configure logback (http://logback.qos.ch/), 52 | # by providing an application-logger.xml file in the conf directory. 53 | 54 | # Root logger: 55 | logger.root=ERROR 56 | 57 | # Logger used by the framework: 58 | logger.play=INFO 59 | 60 | # Logger provided to your application: 61 | logger.application=DEBUG 62 | 63 | -------------------------------------------------------------------------------- /chapter1/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # API Endpoints 6 | POST /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.add(userId: Int, studioId: Int) 7 | 8 | # Home page 9 | GET / controllers.Application.index 10 | 11 | # Map static resources from the /public folder to the /assets URL path 12 | GET /assets/*file controllers.Assets.at(path="/public", file) 13 | -------------------------------------------------------------------------------- /chapter1/init_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Clean out dist 4 | rm -rf target/universal/* 5 | 6 | # Compile distributable 7 | sbt dist 8 | 9 | # Unzip components 10 | pushd target/universal 11 | unzip chapter1-demo-1.0.zip 12 | popd 13 | -------------------------------------------------------------------------------- /chapter1/project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Sat Feb 21 15:26:55 PST 2015 3 | template.uuid=d2d450ff-ffb2-4c93-9354-c3b15fc933ea 4 | sbt.version=0.13.5 5 | -------------------------------------------------------------------------------- /chapter1/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 2 | 3 | // The Play plugin 4 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8") 5 | 6 | // web plugins 7 | 8 | addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") 9 | 10 | addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.0") 11 | 12 | addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.1") 13 | 14 | addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.1") 15 | 16 | addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.0.0") 17 | 18 | addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.0.0") 19 | -------------------------------------------------------------------------------- /chapter1/public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter1/public/images/favicon.png -------------------------------------------------------------------------------- /chapter1/public/javascripts/hello.js: -------------------------------------------------------------------------------- 1 | if (window.console) { 2 | console.log("Welcome to your Play application's JavaScript!"); 3 | } -------------------------------------------------------------------------------- /chapter1/public/stylesheets/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter1/public/stylesheets/main.css -------------------------------------------------------------------------------- /chapter1/run_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Run 4 | pushd target/universal/chapter1-demo-1.0/bin 5 | ./chapter1-demo 6 | popd 7 | -------------------------------------------------------------------------------- /chapter1/test/ApplicationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * Add your spec here. 10 | * You can mock out a whole application including requests, plugins etc. 11 | * For more information, consult the wiki. 12 | */ 13 | @RunWith(classOf[JUnitRunner]) 14 | class ApplicationSpec extends Specification { 15 | 16 | "Application" should { 17 | 18 | "send 404 on a bad request" in new WithApplication{ 19 | route(FakeRequest(GET, "/boum")) must beNone 20 | } 21 | 22 | "render the index page" in new WithApplication{ 23 | val home = route(FakeRequest(GET, "/")).get 24 | 25 | status(home) must equalTo(OK) 26 | contentType(home) must beSome.which(_ == "text/html") 27 | contentAsString(home) must contain ("Your new application is ready.") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter1/test/IntegrationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * add your integration spec here. 10 | * An integration test will fire up a whole play application in a real (or headless) browser 11 | */ 12 | @RunWith(classOf[JUnitRunner]) 13 | class IntegrationSpec extends Specification { 14 | 15 | "Application" should { 16 | 17 | "work from within a browser" in new WithBrowser { 18 | 19 | browser.goTo("http://localhost:" + port) 20 | 21 | browser.pageSource must contain("Your new application is ready.") 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter2.1/.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | project/target 4 | target 5 | tmp 6 | .history 7 | dist 8 | /.idea 9 | /*.iml 10 | /out 11 | /.idea_modules 12 | /.classpath 13 | /.project 14 | /RUNNING_PID 15 | /.settings 16 | -------------------------------------------------------------------------------- /chapter2.1/LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 8 | language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /chapter2.1/README: -------------------------------------------------------------------------------- 1 | This is your new Play application 2 | ================================= 3 | 4 | This file will be packaged with your application, when using `activator dist`. 5 | -------------------------------------------------------------------------------- /chapter2.1/activator: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### ------------------------------- ### 4 | ### Helper methods for BASH scripts ### 5 | ### ------------------------------- ### 6 | 7 | realpath () { 8 | ( 9 | TARGET_FILE="$1" 10 | 11 | cd $(dirname "$TARGET_FILE") 12 | TARGET_FILE=$(basename "$TARGET_FILE") 13 | 14 | COUNT=0 15 | while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ] 16 | do 17 | TARGET_FILE=$(readlink "$TARGET_FILE") 18 | cd $(dirname "$TARGET_FILE") 19 | TARGET_FILE=$(basename "$TARGET_FILE") 20 | COUNT=$(($COUNT + 1)) 21 | done 22 | 23 | if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then 24 | cd "$TARGET_FILE" 25 | TARGET_FILEPATH= 26 | else 27 | TARGET_FILEPATH=/$TARGET_FILE 28 | fi 29 | 30 | # make sure we grab the actual windows path, instead of cygwin's path. 31 | if ! is_cygwin; then 32 | echo "$(pwd -P)/$TARGET_FILE" 33 | else 34 | echo $(cygwinpath "$(pwd -P)/$TARGET_FILE") 35 | fi 36 | ) 37 | } 38 | 39 | # TODO - Do we need to detect msys? 40 | 41 | # Uses uname to detect if we're in the odd cygwin environment. 42 | is_cygwin() { 43 | local os=$(uname -s) 44 | case "$os" in 45 | CYGWIN*) return 0 ;; 46 | *) return 1 ;; 47 | esac 48 | } 49 | 50 | # This can fix cygwin style /cygdrive paths so we get the 51 | # windows style paths. 52 | cygwinpath() { 53 | local file="$1" 54 | if is_cygwin; then 55 | echo $(cygpath -w $file) 56 | else 57 | echo $file 58 | fi 59 | } 60 | 61 | # Make something URI friendly 62 | make_url() { 63 | url="$1" 64 | local nospaces=${url// /%20} 65 | if is_cygwin; then 66 | echo "/${nospaces//\\//}" 67 | else 68 | echo "$nospaces" 69 | fi 70 | } 71 | 72 | # Detect if we should use JAVA_HOME or just try PATH. 73 | get_java_cmd() { 74 | if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then 75 | echo "$JAVA_HOME/bin/java" 76 | else 77 | echo "java" 78 | fi 79 | } 80 | 81 | echoerr () { 82 | echo 1>&2 "$@" 83 | } 84 | vlog () { 85 | [[ $verbose || $debug ]] && echoerr "$@" 86 | } 87 | dlog () { 88 | [[ $debug ]] && echoerr "$@" 89 | } 90 | execRunner () { 91 | # print the arguments one to a line, quoting any containing spaces 92 | [[ $verbose || $debug ]] && echo "# Executing command line:" && { 93 | for arg; do 94 | if printf "%s\n" "$arg" | grep -q ' '; then 95 | printf "\"%s\"\n" "$arg" 96 | else 97 | printf "%s\n" "$arg" 98 | fi 99 | done 100 | echo "" 101 | } 102 | 103 | exec "$@" 104 | } 105 | addJava () { 106 | dlog "[addJava] arg = '$1'" 107 | java_args=( "${java_args[@]}" "$1" ) 108 | } 109 | addApp () { 110 | dlog "[addApp] arg = '$1'" 111 | sbt_commands=( "${app_commands[@]}" "$1" ) 112 | } 113 | addResidual () { 114 | dlog "[residual] arg = '$1'" 115 | residual_args=( "${residual_args[@]}" "$1" ) 116 | } 117 | addDebugger () { 118 | addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" 119 | } 120 | addConfigOpts () { 121 | dlog "[addConfigOpts] arg = '$*'" 122 | for item in $* 123 | do 124 | addJava "$item" 125 | done 126 | } 127 | # a ham-fisted attempt to move some memory settings in concert 128 | # so they need not be messed around with individually. 129 | get_mem_opts () { 130 | local mem=${1:-1024} 131 | local meta=$(( $mem / 4 )) 132 | (( $meta > 256 )) || meta=256 133 | (( $meta < 1024 )) || meta=1024 134 | 135 | # default is to set memory options but this can be overridden by code section below 136 | memopts="-Xms${mem}m -Xmx${mem}m" 137 | if [[ "${java_version}" > "1.8" ]]; then 138 | extmemopts="-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=${meta}m" 139 | else 140 | extmemopts="-XX:PermSize=64m -XX:MaxPermSize=${meta}m" 141 | fi 142 | 143 | if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]] || [[ "${java_opts}" == *-XX:MaxMetaspaceSize* ]]; then 144 | # if we detect any of these settings in ${java_opts} we need to NOT output our settings. 145 | # The reason is the Xms/Xmx, if they don't line up, cause errors. 146 | memopts="" 147 | extmemopts="" 148 | fi 149 | 150 | echo "${memopts} ${extmemopts}" 151 | } 152 | require_arg () { 153 | local type="$1" 154 | local opt="$2" 155 | local arg="$3" 156 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then 157 | die "$opt requires <$type> argument" 158 | fi 159 | } 160 | is_function_defined() { 161 | declare -f "$1" > /dev/null 162 | } 163 | 164 | # If we're *not* running in a terminal, and we don't have any arguments, then we need to add the 'ui' parameter 165 | detect_terminal_for_ui() { 166 | [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && { 167 | addResidual "ui" 168 | } 169 | # SPECIAL TEST FOR MAC 170 | [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && { 171 | echo "Detected MAC OSX launched script...." 172 | echo "Swapping to UI" 173 | addResidual "ui" 174 | } 175 | } 176 | 177 | # Processes incoming arguments and places them in appropriate global variables. called by the run method. 178 | process_args () { 179 | while [[ $# -gt 0 ]]; do 180 | case "$1" in 181 | -h|-help) usage; exit 1 ;; 182 | -v|-verbose) verbose=1 && shift ;; 183 | -d|-debug) debug=1 && shift ;; 184 | -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;; 185 | -jvm-debug) 186 | if echo "$2" | grep -E ^[0-9]+$ > /dev/null; then 187 | addDebugger "$2" && shift 188 | else 189 | addDebugger 9999 190 | fi 191 | shift ;; 192 | -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;; 193 | -D*) addJava "$1" && shift ;; 194 | -J*) addJava "${1:2}" && shift ;; 195 | *) addResidual "$1" && shift ;; 196 | esac 197 | done 198 | 199 | is_function_defined process_my_args && { 200 | myargs=("${residual_args[@]}") 201 | residual_args=() 202 | process_my_args "${myargs[@]}" 203 | } 204 | } 205 | 206 | # Actually runs the script. 207 | run() { 208 | # TODO - check for sane environment 209 | 210 | # process the combined args, then reset "$@" to the residuals 211 | process_args "$@" 212 | detect_terminal_for_ui 213 | set -- "${residual_args[@]}" 214 | argumentCount=$# 215 | 216 | #check for jline terminal fixes on cygwin 217 | if is_cygwin; then 218 | stty -icanon min 1 -echo > /dev/null 2>&1 219 | addJava "-Djline.terminal=jline.UnixTerminal" 220 | addJava "-Dsbt.cygwin=true" 221 | fi 222 | 223 | # run sbt 224 | execRunner "$java_cmd" \ 225 | "-Dactivator.home=$(make_url "$activator_home")" \ 226 | $(get_mem_opts $app_mem) \ 227 | ${java_opts[@]} \ 228 | ${java_args[@]} \ 229 | -jar "$app_launcher" \ 230 | "${app_commands[@]}" \ 231 | "${residual_args[@]}" 232 | 233 | local exit_code=$? 234 | if is_cygwin; then 235 | stty icanon echo > /dev/null 2>&1 236 | fi 237 | exit $exit_code 238 | } 239 | 240 | # Loads a configuration file full of default command line options for this script. 241 | loadConfigFile() { 242 | cat "$1" | sed '/^\#/d' 243 | } 244 | 245 | ### ------------------------------- ### 246 | ### Start of customized settings ### 247 | ### ------------------------------- ### 248 | usage() { 249 | cat < [options] 251 | 252 | Command: 253 | ui Start the Activator UI 254 | new [name] [template-id] Create a new project with [name] using template [template-id] 255 | list-templates Print all available template names 256 | -h | -help Print this message 257 | 258 | Options: 259 | -v | -verbose Make this runner chattier 260 | -d | -debug Set sbt log level to debug 261 | -mem Set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem)) 262 | -jvm-debug Turn on JVM debugging, open at the given port. 263 | 264 | # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) 265 | -java-home Alternate JAVA_HOME 266 | 267 | # jvm options and output control 268 | -Dkey=val Pass -Dkey=val directly to the java runtime 269 | -J-X Pass option -X directly to the java runtime 270 | (-J is stripped) 271 | 272 | # environment variables (read from context) 273 | JAVA_OPTS Environment variable, if unset uses "" 274 | SBT_OPTS Environment variable, if unset uses "" 275 | ACTIVATOR_OPTS Environment variable, if unset uses "" 276 | 277 | In the case of duplicated or conflicting options, the order above 278 | shows precedence: environment variables lowest, command line options highest. 279 | EOM 280 | } 281 | 282 | ### ------------------------------- ### 283 | ### Main script ### 284 | ### ------------------------------- ### 285 | 286 | declare -a residual_args 287 | declare -a java_args 288 | declare -a app_commands 289 | declare -r real_script_path="$(realpath "$0")" 290 | declare -r activator_home="$(realpath "$(dirname "$real_script_path")")" 291 | declare -r app_version="1.2.12" 292 | 293 | declare -r app_launcher="${activator_home}/activator-launch-${app_version}.jar" 294 | declare -r script_name=activator 295 | declare -r java_cmd=$(get_java_cmd) 296 | declare -r java_opts=( "${ACTIVATOR_OPTS[@]}" "${SBT_OPTS[@]}" "${JAVA_OPTS[@]}" "${java_opts[@]}" ) 297 | userhome="$HOME" 298 | if is_cygwin; then 299 | # cygwin sets home to something f-d up, set to real windows homedir 300 | userhome="$USERPROFILE" 301 | fi 302 | declare -r activator_user_home_dir="${userhome}/.activator" 303 | declare -r java_opts_config_home="${activator_user_home_dir}/activatorconfig.txt" 304 | declare -r java_opts_config_version="${activator_user_home_dir}/${app_version}/activatorconfig.txt" 305 | 306 | # Now check to see if it's a good enough version 307 | declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}') 308 | if [[ "$java_version" == "" ]]; then 309 | echo 310 | echo No java installations was detected. 311 | echo Please go to http://www.java.com/getjava/ and download 312 | echo 313 | exit 1 314 | elif [[ ! "$java_version" > "1.6" ]]; then 315 | echo 316 | echo The java installation you have is not up to date 317 | echo Activator requires at least version 1.6+, you have 318 | echo version $java_version 319 | echo 320 | echo Please go to http://www.java.com/getjava/ and download 321 | echo a valid Java Runtime and install before running Activator. 322 | echo 323 | exit 1 324 | fi 325 | 326 | # if configuration files exist, prepend their contents to the java args so it can be processed by this runner 327 | # a "versioned" config trumps one on the top level 328 | if [[ -f "$java_opts_config_version" ]]; then 329 | addConfigOpts $(loadConfigFile "$java_opts_config_version") 330 | elif [[ -f "$java_opts_config_home" ]]; then 331 | addConfigOpts $(loadConfigFile "$java_opts_config_home") 332 | fi 333 | 334 | run "$@" 335 | -------------------------------------------------------------------------------- /chapter2.1/activator-launch-1.2.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter2.1/activator-launch-1.2.12.jar -------------------------------------------------------------------------------- /chapter2.1/activator.bat: -------------------------------------------------------------------------------- 1 | @REM activator launcher script 2 | @REM 3 | @REM Envioronment: 4 | @REM JAVA_HOME - location of a JDK home dir (optional if java on path) 5 | @REM CFG_OPTS - JVM options (optional) 6 | @REM Configuration: 7 | @REM activatorconfig.txt found in the ACTIVATOR_HOME or ACTIVATOR_HOME/ACTIVATOR_VERSION 8 | @setlocal enabledelayedexpansion 9 | 10 | @echo off 11 | 12 | set "var1=%~1" 13 | if defined var1 ( 14 | if "%var1%"=="help" ( 15 | echo. 16 | echo Usage activator [options] [command] 17 | echo. 18 | echo Commands: 19 | echo ui Start the Activator UI 20 | echo new [name] [template-id] Create a new project with [name] using template [template-id] 21 | echo list-templates Print all available template names 22 | echo help Print this message 23 | echo. 24 | echo Options: 25 | echo -jvm-debug [port] Turn on JVM debugging, open at the given port. Defaults to 9999 if no port given. 26 | echo. 27 | echo Environment variables ^(read from context^): 28 | echo JAVA_OPTS Environment variable, if unset uses "" 29 | echo SBT_OPTS Environment variable, if unset uses "" 30 | echo ACTIVATOR_OPTS Environment variable, if unset uses "" 31 | echo. 32 | goto :end 33 | ) 34 | ) 35 | 36 | if "%ACTIVATOR_HOME%"=="" ( 37 | set "ACTIVATOR_HOME=%~dp0" 38 | @REM remove trailing "\" from path 39 | set ACTIVATOR_HOME=!ACTIVATOR_HOME:~0,-1! 40 | ) 41 | 42 | set ERROR_CODE=0 43 | set APP_VERSION=1.2.12 44 | set ACTIVATOR_LAUNCH_JAR=activator-launch-%APP_VERSION%.jar 45 | 46 | rem Detect if we were double clicked, although theoretically A user could 47 | rem manually run cmd /c 48 | for %%x in (%cmdcmdline%) do if %%~x==/c set DOUBLECLICKED=1 49 | 50 | rem FIRST we load a config file of extra options (if there is one) 51 | set "CFG_FILE_HOME=%UserProfile%\.activator\activatorconfig.txt" 52 | set "CFG_FILE_VERSION=%UserProfile%\.activator\%APP_VERSION%\activatorconfig.txt" 53 | set CFG_OPTS= 54 | if exist %CFG_FILE_VERSION% ( 55 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_VERSION%") DO ( 56 | set DO_NOT_REUSE_ME=%%i 57 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 58 | rem CFG_OPTS, otherwise it remains "" for this loop. 59 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 60 | ) 61 | ) 62 | if "%CFG_OPTS%"=="" ( 63 | if exist %CFG_FILE_HOME% ( 64 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_HOME%") DO ( 65 | set DO_NOT_REUSE_ME=%%i 66 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 67 | rem CFG_OPTS, otherwise it remains "" for this loop. 68 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 69 | ) 70 | ) 71 | ) 72 | 73 | rem We use the value of the JAVACMD environment variable if defined 74 | set _JAVACMD=%JAVACMD% 75 | 76 | if "%_JAVACMD%"=="" ( 77 | if not "%JAVA_HOME%"=="" ( 78 | if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe" 79 | 80 | rem if there is a java home set we make sure it is the first picked up when invoking 'java' 81 | SET "PATH=%JAVA_HOME%\bin;%PATH%" 82 | ) 83 | ) 84 | 85 | if "%_JAVACMD%"=="" set _JAVACMD=java 86 | 87 | rem Detect if this java is ok to use. 88 | for /F %%j in ('"%_JAVACMD%" -version 2^>^&1') do ( 89 | if %%~j==Java set JAVAINSTALLED=1 90 | ) 91 | 92 | rem Detect the same thing about javac 93 | if "%_JAVACCMD%"=="" ( 94 | if not "%JAVA_HOME%"=="" ( 95 | if exist "%JAVA_HOME%\bin\javac.exe" set "_JAVACCMD=%JAVA_HOME%\bin\javac.exe" 96 | ) 97 | ) 98 | if "%_JAVACCMD%"=="" set _JAVACCMD=javac 99 | for /F %%j in ('"%_JAVACCMD%" -version 2^>^&1') do ( 100 | if %%~j==javac set JAVACINSTALLED=1 101 | ) 102 | 103 | rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style 104 | set JAVAOK=true 105 | if not defined JAVAINSTALLED set JAVAOK=false 106 | if not defined JAVACINSTALLED set JAVAOK=false 107 | 108 | if "%JAVAOK%"=="false" ( 109 | echo. 110 | echo A Java JDK is not installed or can't be found. 111 | if not "%JAVA_HOME%"=="" ( 112 | echo JAVA_HOME = "%JAVA_HOME%" 113 | ) 114 | echo. 115 | echo Please go to 116 | echo http://www.oracle.com/technetwork/java/javase/downloads/index.html 117 | echo and download a valid Java JDK and install before running Activator. 118 | echo. 119 | echo If you think this message is in error, please check 120 | echo your environment variables to see if "java.exe" and "javac.exe" are 121 | echo available via JAVA_HOME or PATH. 122 | echo. 123 | if defined DOUBLECLICKED pause 124 | exit /B 1 125 | ) 126 | 127 | rem Check what Java version is being used to determine what memory options to use 128 | for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( 129 | set JAVA_VERSION=%%g 130 | ) 131 | 132 | rem Strips away the " characters 133 | set JAVA_VERSION=%JAVA_VERSION:"=% 134 | 135 | rem TODO Check if there are existing mem settings in JAVA_OPTS/CFG_OPTS and use those instead of the below 136 | for /f "delims=. tokens=1-3" %%v in ("%JAVA_VERSION%") do ( 137 | set MAJOR=%%v 138 | set MINOR=%%w 139 | set BUILD=%%x 140 | 141 | set META_SIZE=-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=256M 142 | if "!MINOR!" LSS "8" ( 143 | set META_SIZE=-XX:PermSize=64M -XX:MaxPermSize=256M 144 | ) 145 | 146 | set MEM_OPTS=!META_SIZE! 147 | ) 148 | 149 | rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config. 150 | set _JAVA_OPTS=%JAVA_OPTS% 151 | if "%_JAVA_OPTS%"=="" set _JAVA_OPTS=%CFG_OPTS% 152 | 153 | set DEBUG_OPTS= 154 | 155 | rem Loop through the arguments, building remaining args in args variable 156 | set args= 157 | :argsloop 158 | if not "%~1"=="" ( 159 | rem Checks if the argument contains "-D" and if true, adds argument 1 with 2 and puts an equal sign between them. 160 | rem This is done since batch considers "=" to be a delimiter so we need to circumvent this behavior with a small hack. 161 | set arg1=%~1 162 | if "!arg1:~0,2!"=="-D" ( 163 | set "args=%args% "%~1"="%~2"" 164 | shift 165 | shift 166 | goto argsloop 167 | ) 168 | 169 | if "%~1"=="-jvm-debug" ( 170 | if not "%~2"=="" ( 171 | rem This piece of magic somehow checks that an argument is a number 172 | for /F "delims=0123456789" %%i in ("%~2") do ( 173 | set var="%%i" 174 | ) 175 | if defined var ( 176 | rem Not a number, assume no argument given and default to 9999 177 | set JPDA_PORT=9999 178 | ) else ( 179 | rem Port was given, shift arguments 180 | set JPDA_PORT=%~2 181 | shift 182 | ) 183 | ) else ( 184 | set JPDA_PORT=9999 185 | ) 186 | shift 187 | 188 | set DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=!JPDA_PORT! 189 | goto argsloop 190 | ) 191 | rem else 192 | set "args=%args% "%~1"" 193 | shift 194 | goto argsloop 195 | ) 196 | 197 | :run 198 | 199 | if "!args!"=="" ( 200 | if defined DOUBLECLICKED ( 201 | set CMDS="ui" 202 | ) else set CMDS=!args! 203 | ) else set CMDS=!args! 204 | 205 | rem We add a / in front, so we get file:///C: instead of file://C: 206 | rem Java considers the later a UNC path. 207 | rem We also attempt a solid effort at making it URI friendly. 208 | rem We don't even bother with UNC paths. 209 | set JAVA_FRIENDLY_HOME_1=/!ACTIVATOR_HOME:\=/! 210 | set JAVA_FRIENDLY_HOME=/!JAVA_FRIENDLY_HOME_1: =%%20! 211 | 212 | rem Checks if the command contains spaces to know if it should be wrapped in quotes or not 213 | set NON_SPACED_CMD=%_JAVACMD: =% 214 | if "%_JAVACMD%"=="%NON_SPACED_CMD%" %_JAVACMD% %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 215 | if NOT "%_JAVACMD%"=="%NON_SPACED_CMD%" "%_JAVACMD%" %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 216 | 217 | if ERRORLEVEL 1 goto error 218 | goto end 219 | 220 | :error 221 | set ERROR_CODE=1 222 | 223 | :end 224 | 225 | @endlocal 226 | 227 | exit /B %ERROR_CODE% 228 | -------------------------------------------------------------------------------- /chapter2.1/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api._ 4 | import play.api.mvc._ 5 | 6 | object Application extends Controller { 7 | 8 | def index = Action { 9 | Ok(views.html.index("Your new application is ready.")) 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /chapter2.1/app/controllers/FavouriteStudios.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import models.FavouriteStudio 4 | import models.FavouriteStudio._ 5 | import play.api.libs.json.Json 6 | import play.api.mvc._ 7 | 8 | object FavouriteStudios extends Controller { 9 | def add(userId: Int, studioId: Int) = Action { 10 | val favourite = FavouriteStudio.addFavourite(userId, studioId) 11 | 12 | Ok(Json.obj("result" -> favourite)) 13 | } 14 | 15 | def remove(userId: Int, studioId: Int) = Action { 16 | FavouriteStudio.delete(userId, studioId) 17 | 18 | Ok(Json.obj("result" -> Json.obj())) 19 | } 20 | 21 | def find(userId: Int, studioId: Int) = Action { 22 | val oFavourite = FavouriteStudio.find(userId, studioId) 23 | 24 | oFavourite match { 25 | case None => NotFound(Json.obj("error" -> "NOT_FOUND")) 26 | case Some(favourite) => Ok(Json.obj("result" -> favourite)) 27 | } 28 | } 29 | 30 | def findAll(userId: Int) = Action { 31 | val allFavourites = FavouriteStudio.findAllByUser(userId) 32 | 33 | Ok(Json.obj("result" -> allFavourites)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /chapter2.1/app/models/FavouriteStudio.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import models.dao.FavouriteStudioDAO 4 | import play.api.libs.functional.syntax._ 5 | import play.api.libs.json._ 6 | 7 | object FavouriteStudio { 8 | implicit val favouriteStudioWrites: Writes[FavouriteStudio] = ( 9 | (JsPath \ "userId").write[Int] and 10 | (JsPath \ "studioId").write[Int] 11 | )(unlift(FavouriteStudio.unapply)) 12 | 13 | def addFavourite(userId: Int, studioId: Int): FavouriteStudio = { 14 | val favourite = FavouriteStudio(userId, studioId) 15 | 16 | FavouriteStudioDAO.create(favourite) 17 | 18 | favourite 19 | } 20 | 21 | def delete(userId: Int, studioId: Int) = 22 | FavouriteStudioDAO.delete(FavouriteStudio(userId, studioId)) 23 | 24 | def findAllByUser(userId: Int): List[FavouriteStudio] = 25 | FavouriteStudioDAO.index(userId) 26 | 27 | def find(userId: Int, studioId: Int): Option[FavouriteStudio] = { 28 | val favourite = FavouriteStudio(userId, studioId) 29 | 30 | if (FavouriteStudioDAO.exists(favourite)) 31 | Some(favourite) 32 | else 33 | None 34 | } 35 | } 36 | 37 | case class FavouriteStudio(userId: Int, studioId: Int) 38 | -------------------------------------------------------------------------------- /chapter2.1/app/models/dao/FavouriteStudioDAO.scala: -------------------------------------------------------------------------------- 1 | package models.dao 2 | 3 | import anorm._ 4 | import models.FavouriteStudio 5 | import play.api.db.DB 6 | import play.api.Play.current 7 | 8 | object FavouriteStudioDAO { 9 | def create(favourite: FavouriteStudio) = { 10 | DB.withConnection { implicit c => 11 | SQL( 12 | """ 13 | | INSERT IGNORE INTO `favouriteStudio` (`userId`, `studioId`) 14 | | VALUES 15 | | ({userId}, {studioId}); 16 | """.stripMargin).on( 17 | "userId" -> favourite.userId, 18 | "studioId" -> favourite.studioId 19 | ).executeInsert() 20 | } 21 | } 22 | 23 | def delete(favourite: FavouriteStudio) = { 24 | DB.withConnection { implicit c => 25 | SQL( 26 | """ 27 | | DELETE FROM `favouriteStudio` 28 | | WHERE `userId`={userId} AND `studioId`={studioId} 29 | | LIMIT 1; 30 | """.stripMargin).on( 31 | "userId" -> favourite.userId, 32 | "studioId" -> favourite.studioId 33 | ).executeUpdate() 34 | } 35 | } 36 | 37 | def exists(favourite: FavouriteStudio): Boolean = { 38 | DB.withConnection { implicit c => 39 | val result = SQL( 40 | """ 41 | | SELECT COUNT(*) as numMatches 42 | | FROM `favouriteStudio` 43 | | WHERE `userId`={userId} AND `studioId`={studioId}; 44 | """.stripMargin).on( 45 | "userId" -> favourite.userId, 46 | "studioId" -> favourite.studioId 47 | ).apply().head 48 | 49 | result[Int]("numMatches") != 0 50 | } 51 | } 52 | 53 | def index(userId: Int): List[FavouriteStudio] = { 54 | DB.withConnection { implicit c => 55 | val results = SQL( 56 | """ 57 | | SELECT `userId`, `studioId` 58 | | FROM `favouriteStudio` 59 | | WHERE `userId`={userId}; 60 | """.stripMargin).on( 61 | "userId" -> userId 62 | ).apply() 63 | 64 | results.map { row => 65 | FavouriteStudio(row[Int]("userId"), row[Int]("studioId")) 66 | }.force.toList 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /chapter2.1/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @main("Welcome to Play") { 4 | 5 | @play20.welcome(message) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /chapter2.1/app/views/main.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | 7 | @title 8 | 9 | 10 | 11 | 12 | 13 | @content 14 | 15 | 16 | -------------------------------------------------------------------------------- /chapter2.1/build.sbt: -------------------------------------------------------------------------------- 1 | name := """chapter2.1-demo""" 2 | 3 | version := "1.0" 4 | 5 | lazy val root = (project in file(".")).enablePlugins(PlayScala) 6 | 7 | doc in Compile <<= target.map(_ / "none") 8 | 9 | scalaVersion := "2.11.1" 10 | 11 | libraryDependencies ++= Seq( 12 | jdbc, 13 | anorm, 14 | cache, 15 | ws, 16 | "mysql" % "mysql-connector-java" % "5.1.18" 17 | ) 18 | -------------------------------------------------------------------------------- /chapter2.1/conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # 8 | # This must be changed for production, but we recommend not changing it in this file. 9 | # 10 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. 11 | application.secret="/u9PM[cJc=h>y=xGcV1W?<58iGchSJM7wXnfZ::qFTS" 12 | 13 | # The application languages 14 | # ~~~~~ 15 | application.langs="en" 16 | 17 | # Global object class 18 | # ~~~~~ 19 | # Define the Global object class for this application. 20 | # Default to Global in the root package. 21 | # application.global=Global 22 | 23 | # Router 24 | # ~~~~~ 25 | # Define the Router object to use for this application. 26 | # This router will be looked up first when the application is starting up, 27 | # so make sure this is the entry point. 28 | # Furthermore, it's assumed your route file is named properly. 29 | # So for an application router like `my.application.Router`, 30 | # you may need to define a router file `conf/my.application.routes`. 31 | # Default to Routes in the root package (and conf/routes) 32 | # application.router=my.application.Routes 33 | 34 | # Database configuration 35 | # ~~~~~ 36 | # You can declare as many datasources as you want. 37 | # By convention, the default datasource is named `default` 38 | # 39 | db.default.driver= com.mysql.jdbc.Driver 40 | db.default.url="jdbc:mysql://127.0.0.1:3306/favourites-svc" 41 | db.default.user="favourites-svc" 42 | db.default.password="1e0OZH5a9PCJ0Anj11ia0Wmy" 43 | 44 | # Evolutions 45 | # ~~~~~ 46 | # You can disable evolutions if needed 47 | # evolutionplugin=disabled 48 | 49 | # Logger 50 | # ~~~~~ 51 | # You can also configure logback (http://logback.qos.ch/), 52 | # by providing an application-logger.xml file in the conf directory. 53 | 54 | # Root logger: 55 | logger.root=ERROR 56 | 57 | # Logger used by the framework: 58 | logger.play=INFO 59 | 60 | # Logger provided to your application: 61 | logger.application=DEBUG 62 | 63 | -------------------------------------------------------------------------------- /chapter2.1/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # API Endpoints 6 | POST /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.add(userId: Int, studioId: Int) 7 | DELETE /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.remove(userId: Int, studioId: Int) 8 | GET /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.find(userId: Int, studioId: Int) 9 | GET /users/:userId/favouriteStudios controllers.FavouriteStudios.findAll(userId: Int) 10 | 11 | # Home page 12 | GET / controllers.Application.index 13 | 14 | # Map static resources from the /public folder to the /assets URL path 15 | GET /assets/*file controllers.Assets.at(path="/public", file) 16 | -------------------------------------------------------------------------------- /chapter2.1/init_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Clean out dist 4 | rm -rf target/universal/* 5 | 6 | # Compile distributable 7 | sbt dist 8 | 9 | # Unzip components 10 | pushd target/universal 11 | unzip chapter2-1-demo-1.0.zip 12 | popd 13 | -------------------------------------------------------------------------------- /chapter2.1/project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Sat Feb 21 15:26:55 PST 2015 3 | template.uuid=d2d450ff-ffb2-4c93-9354-c3b15fc933ea 4 | sbt.version=0.13.5 5 | -------------------------------------------------------------------------------- /chapter2.1/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 2 | 3 | // The Play plugin 4 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8") 5 | 6 | // web plugins 7 | 8 | addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") 9 | 10 | addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.0") 11 | 12 | addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.1") 13 | 14 | addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.1") 15 | 16 | addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.0.0") 17 | 18 | addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.0.0") 19 | -------------------------------------------------------------------------------- /chapter2.1/public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter2.1/public/images/favicon.png -------------------------------------------------------------------------------- /chapter2.1/public/javascripts/hello.js: -------------------------------------------------------------------------------- 1 | if (window.console) { 2 | console.log("Welcome to your Play application's JavaScript!"); 3 | } -------------------------------------------------------------------------------- /chapter2.1/public/stylesheets/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter2.1/public/stylesheets/main.css -------------------------------------------------------------------------------- /chapter2.1/run_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Run 4 | pushd target/universal/chapter2-1-demo-1.0/bin 5 | ./chapter2-1-demo 6 | popd 7 | -------------------------------------------------------------------------------- /chapter2.1/test/ApplicationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * Add your spec here. 10 | * You can mock out a whole application including requests, plugins etc. 11 | * For more information, consult the wiki. 12 | */ 13 | @RunWith(classOf[JUnitRunner]) 14 | class ApplicationSpec extends Specification { 15 | 16 | "Application" should { 17 | 18 | "send 404 on a bad request" in new WithApplication{ 19 | route(FakeRequest(GET, "/boum")) must beNone 20 | } 21 | 22 | "render the index page" in new WithApplication{ 23 | val home = route(FakeRequest(GET, "/")).get 24 | 25 | status(home) must equalTo(OK) 26 | contentType(home) must beSome.which(_ == "text/html") 27 | contentAsString(home) must contain ("Your new application is ready.") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter2.1/test/IntegrationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * add your integration spec here. 10 | * An integration test will fire up a whole play application in a real (or headless) browser 11 | */ 12 | @RunWith(classOf[JUnitRunner]) 13 | class IntegrationSpec extends Specification { 14 | 15 | "Application" should { 16 | 17 | "work from within a browser" in new WithBrowser { 18 | 19 | browser.goTo("http://localhost:" + port) 20 | 21 | browser.pageSource must contain("Your new application is ready.") 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter2/.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | project/target 4 | target 5 | tmp 6 | .history 7 | dist 8 | /.idea 9 | /*.iml 10 | /out 11 | /.idea_modules 12 | /.classpath 13 | /.project 14 | /RUNNING_PID 15 | /.settings 16 | -------------------------------------------------------------------------------- /chapter2/LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 8 | language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /chapter2/README: -------------------------------------------------------------------------------- 1 | This is your new Play application 2 | ================================= 3 | 4 | This file will be packaged with your application, when using `activator dist`. 5 | -------------------------------------------------------------------------------- /chapter2/activator: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### ------------------------------- ### 4 | ### Helper methods for BASH scripts ### 5 | ### ------------------------------- ### 6 | 7 | realpath () { 8 | ( 9 | TARGET_FILE="$1" 10 | 11 | cd $(dirname "$TARGET_FILE") 12 | TARGET_FILE=$(basename "$TARGET_FILE") 13 | 14 | COUNT=0 15 | while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ] 16 | do 17 | TARGET_FILE=$(readlink "$TARGET_FILE") 18 | cd $(dirname "$TARGET_FILE") 19 | TARGET_FILE=$(basename "$TARGET_FILE") 20 | COUNT=$(($COUNT + 1)) 21 | done 22 | 23 | if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then 24 | cd "$TARGET_FILE" 25 | TARGET_FILEPATH= 26 | else 27 | TARGET_FILEPATH=/$TARGET_FILE 28 | fi 29 | 30 | # make sure we grab the actual windows path, instead of cygwin's path. 31 | if ! is_cygwin; then 32 | echo "$(pwd -P)/$TARGET_FILE" 33 | else 34 | echo $(cygwinpath "$(pwd -P)/$TARGET_FILE") 35 | fi 36 | ) 37 | } 38 | 39 | # TODO - Do we need to detect msys? 40 | 41 | # Uses uname to detect if we're in the odd cygwin environment. 42 | is_cygwin() { 43 | local os=$(uname -s) 44 | case "$os" in 45 | CYGWIN*) return 0 ;; 46 | *) return 1 ;; 47 | esac 48 | } 49 | 50 | # This can fix cygwin style /cygdrive paths so we get the 51 | # windows style paths. 52 | cygwinpath() { 53 | local file="$1" 54 | if is_cygwin; then 55 | echo $(cygpath -w $file) 56 | else 57 | echo $file 58 | fi 59 | } 60 | 61 | # Make something URI friendly 62 | make_url() { 63 | url="$1" 64 | local nospaces=${url// /%20} 65 | if is_cygwin; then 66 | echo "/${nospaces//\\//}" 67 | else 68 | echo "$nospaces" 69 | fi 70 | } 71 | 72 | # Detect if we should use JAVA_HOME or just try PATH. 73 | get_java_cmd() { 74 | if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then 75 | echo "$JAVA_HOME/bin/java" 76 | else 77 | echo "java" 78 | fi 79 | } 80 | 81 | echoerr () { 82 | echo 1>&2 "$@" 83 | } 84 | vlog () { 85 | [[ $verbose || $debug ]] && echoerr "$@" 86 | } 87 | dlog () { 88 | [[ $debug ]] && echoerr "$@" 89 | } 90 | execRunner () { 91 | # print the arguments one to a line, quoting any containing spaces 92 | [[ $verbose || $debug ]] && echo "# Executing command line:" && { 93 | for arg; do 94 | if printf "%s\n" "$arg" | grep -q ' '; then 95 | printf "\"%s\"\n" "$arg" 96 | else 97 | printf "%s\n" "$arg" 98 | fi 99 | done 100 | echo "" 101 | } 102 | 103 | exec "$@" 104 | } 105 | addJava () { 106 | dlog "[addJava] arg = '$1'" 107 | java_args=( "${java_args[@]}" "$1" ) 108 | } 109 | addApp () { 110 | dlog "[addApp] arg = '$1'" 111 | sbt_commands=( "${app_commands[@]}" "$1" ) 112 | } 113 | addResidual () { 114 | dlog "[residual] arg = '$1'" 115 | residual_args=( "${residual_args[@]}" "$1" ) 116 | } 117 | addDebugger () { 118 | addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" 119 | } 120 | addConfigOpts () { 121 | dlog "[addConfigOpts] arg = '$*'" 122 | for item in $* 123 | do 124 | addJava "$item" 125 | done 126 | } 127 | # a ham-fisted attempt to move some memory settings in concert 128 | # so they need not be messed around with individually. 129 | get_mem_opts () { 130 | local mem=${1:-1024} 131 | local meta=$(( $mem / 4 )) 132 | (( $meta > 256 )) || meta=256 133 | (( $meta < 1024 )) || meta=1024 134 | 135 | # default is to set memory options but this can be overridden by code section below 136 | memopts="-Xms${mem}m -Xmx${mem}m" 137 | if [[ "${java_version}" > "1.8" ]]; then 138 | extmemopts="-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=${meta}m" 139 | else 140 | extmemopts="-XX:PermSize=64m -XX:MaxPermSize=${meta}m" 141 | fi 142 | 143 | if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]] || [[ "${java_opts}" == *-XX:MaxMetaspaceSize* ]]; then 144 | # if we detect any of these settings in ${java_opts} we need to NOT output our settings. 145 | # The reason is the Xms/Xmx, if they don't line up, cause errors. 146 | memopts="" 147 | extmemopts="" 148 | fi 149 | 150 | echo "${memopts} ${extmemopts}" 151 | } 152 | require_arg () { 153 | local type="$1" 154 | local opt="$2" 155 | local arg="$3" 156 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then 157 | die "$opt requires <$type> argument" 158 | fi 159 | } 160 | is_function_defined() { 161 | declare -f "$1" > /dev/null 162 | } 163 | 164 | # If we're *not* running in a terminal, and we don't have any arguments, then we need to add the 'ui' parameter 165 | detect_terminal_for_ui() { 166 | [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && { 167 | addResidual "ui" 168 | } 169 | # SPECIAL TEST FOR MAC 170 | [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && { 171 | echo "Detected MAC OSX launched script...." 172 | echo "Swapping to UI" 173 | addResidual "ui" 174 | } 175 | } 176 | 177 | # Processes incoming arguments and places them in appropriate global variables. called by the run method. 178 | process_args () { 179 | while [[ $# -gt 0 ]]; do 180 | case "$1" in 181 | -h|-help) usage; exit 1 ;; 182 | -v|-verbose) verbose=1 && shift ;; 183 | -d|-debug) debug=1 && shift ;; 184 | -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;; 185 | -jvm-debug) 186 | if echo "$2" | grep -E ^[0-9]+$ > /dev/null; then 187 | addDebugger "$2" && shift 188 | else 189 | addDebugger 9999 190 | fi 191 | shift ;; 192 | -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;; 193 | -D*) addJava "$1" && shift ;; 194 | -J*) addJava "${1:2}" && shift ;; 195 | *) addResidual "$1" && shift ;; 196 | esac 197 | done 198 | 199 | is_function_defined process_my_args && { 200 | myargs=("${residual_args[@]}") 201 | residual_args=() 202 | process_my_args "${myargs[@]}" 203 | } 204 | } 205 | 206 | # Actually runs the script. 207 | run() { 208 | # TODO - check for sane environment 209 | 210 | # process the combined args, then reset "$@" to the residuals 211 | process_args "$@" 212 | detect_terminal_for_ui 213 | set -- "${residual_args[@]}" 214 | argumentCount=$# 215 | 216 | #check for jline terminal fixes on cygwin 217 | if is_cygwin; then 218 | stty -icanon min 1 -echo > /dev/null 2>&1 219 | addJava "-Djline.terminal=jline.UnixTerminal" 220 | addJava "-Dsbt.cygwin=true" 221 | fi 222 | 223 | # run sbt 224 | execRunner "$java_cmd" \ 225 | "-Dactivator.home=$(make_url "$activator_home")" \ 226 | $(get_mem_opts $app_mem) \ 227 | ${java_opts[@]} \ 228 | ${java_args[@]} \ 229 | -jar "$app_launcher" \ 230 | "${app_commands[@]}" \ 231 | "${residual_args[@]}" 232 | 233 | local exit_code=$? 234 | if is_cygwin; then 235 | stty icanon echo > /dev/null 2>&1 236 | fi 237 | exit $exit_code 238 | } 239 | 240 | # Loads a configuration file full of default command line options for this script. 241 | loadConfigFile() { 242 | cat "$1" | sed '/^\#/d' 243 | } 244 | 245 | ### ------------------------------- ### 246 | ### Start of customized settings ### 247 | ### ------------------------------- ### 248 | usage() { 249 | cat < [options] 251 | 252 | Command: 253 | ui Start the Activator UI 254 | new [name] [template-id] Create a new project with [name] using template [template-id] 255 | list-templates Print all available template names 256 | -h | -help Print this message 257 | 258 | Options: 259 | -v | -verbose Make this runner chattier 260 | -d | -debug Set sbt log level to debug 261 | -mem Set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem)) 262 | -jvm-debug Turn on JVM debugging, open at the given port. 263 | 264 | # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) 265 | -java-home Alternate JAVA_HOME 266 | 267 | # jvm options and output control 268 | -Dkey=val Pass -Dkey=val directly to the java runtime 269 | -J-X Pass option -X directly to the java runtime 270 | (-J is stripped) 271 | 272 | # environment variables (read from context) 273 | JAVA_OPTS Environment variable, if unset uses "" 274 | SBT_OPTS Environment variable, if unset uses "" 275 | ACTIVATOR_OPTS Environment variable, if unset uses "" 276 | 277 | In the case of duplicated or conflicting options, the order above 278 | shows precedence: environment variables lowest, command line options highest. 279 | EOM 280 | } 281 | 282 | ### ------------------------------- ### 283 | ### Main script ### 284 | ### ------------------------------- ### 285 | 286 | declare -a residual_args 287 | declare -a java_args 288 | declare -a app_commands 289 | declare -r real_script_path="$(realpath "$0")" 290 | declare -r activator_home="$(realpath "$(dirname "$real_script_path")")" 291 | declare -r app_version="1.2.12" 292 | 293 | declare -r app_launcher="${activator_home}/activator-launch-${app_version}.jar" 294 | declare -r script_name=activator 295 | declare -r java_cmd=$(get_java_cmd) 296 | declare -r java_opts=( "${ACTIVATOR_OPTS[@]}" "${SBT_OPTS[@]}" "${JAVA_OPTS[@]}" "${java_opts[@]}" ) 297 | userhome="$HOME" 298 | if is_cygwin; then 299 | # cygwin sets home to something f-d up, set to real windows homedir 300 | userhome="$USERPROFILE" 301 | fi 302 | declare -r activator_user_home_dir="${userhome}/.activator" 303 | declare -r java_opts_config_home="${activator_user_home_dir}/activatorconfig.txt" 304 | declare -r java_opts_config_version="${activator_user_home_dir}/${app_version}/activatorconfig.txt" 305 | 306 | # Now check to see if it's a good enough version 307 | declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}') 308 | if [[ "$java_version" == "" ]]; then 309 | echo 310 | echo No java installations was detected. 311 | echo Please go to http://www.java.com/getjava/ and download 312 | echo 313 | exit 1 314 | elif [[ ! "$java_version" > "1.6" ]]; then 315 | echo 316 | echo The java installation you have is not up to date 317 | echo Activator requires at least version 1.6+, you have 318 | echo version $java_version 319 | echo 320 | echo Please go to http://www.java.com/getjava/ and download 321 | echo a valid Java Runtime and install before running Activator. 322 | echo 323 | exit 1 324 | fi 325 | 326 | # if configuration files exist, prepend their contents to the java args so it can be processed by this runner 327 | # a "versioned" config trumps one on the top level 328 | if [[ -f "$java_opts_config_version" ]]; then 329 | addConfigOpts $(loadConfigFile "$java_opts_config_version") 330 | elif [[ -f "$java_opts_config_home" ]]; then 331 | addConfigOpts $(loadConfigFile "$java_opts_config_home") 332 | fi 333 | 334 | run "$@" 335 | -------------------------------------------------------------------------------- /chapter2/activator-launch-1.2.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter2/activator-launch-1.2.12.jar -------------------------------------------------------------------------------- /chapter2/activator.bat: -------------------------------------------------------------------------------- 1 | @REM activator launcher script 2 | @REM 3 | @REM Envioronment: 4 | @REM JAVA_HOME - location of a JDK home dir (optional if java on path) 5 | @REM CFG_OPTS - JVM options (optional) 6 | @REM Configuration: 7 | @REM activatorconfig.txt found in the ACTIVATOR_HOME or ACTIVATOR_HOME/ACTIVATOR_VERSION 8 | @setlocal enabledelayedexpansion 9 | 10 | @echo off 11 | 12 | set "var1=%~1" 13 | if defined var1 ( 14 | if "%var1%"=="help" ( 15 | echo. 16 | echo Usage activator [options] [command] 17 | echo. 18 | echo Commands: 19 | echo ui Start the Activator UI 20 | echo new [name] [template-id] Create a new project with [name] using template [template-id] 21 | echo list-templates Print all available template names 22 | echo help Print this message 23 | echo. 24 | echo Options: 25 | echo -jvm-debug [port] Turn on JVM debugging, open at the given port. Defaults to 9999 if no port given. 26 | echo. 27 | echo Environment variables ^(read from context^): 28 | echo JAVA_OPTS Environment variable, if unset uses "" 29 | echo SBT_OPTS Environment variable, if unset uses "" 30 | echo ACTIVATOR_OPTS Environment variable, if unset uses "" 31 | echo. 32 | goto :end 33 | ) 34 | ) 35 | 36 | if "%ACTIVATOR_HOME%"=="" ( 37 | set "ACTIVATOR_HOME=%~dp0" 38 | @REM remove trailing "\" from path 39 | set ACTIVATOR_HOME=!ACTIVATOR_HOME:~0,-1! 40 | ) 41 | 42 | set ERROR_CODE=0 43 | set APP_VERSION=1.2.12 44 | set ACTIVATOR_LAUNCH_JAR=activator-launch-%APP_VERSION%.jar 45 | 46 | rem Detect if we were double clicked, although theoretically A user could 47 | rem manually run cmd /c 48 | for %%x in (%cmdcmdline%) do if %%~x==/c set DOUBLECLICKED=1 49 | 50 | rem FIRST we load a config file of extra options (if there is one) 51 | set "CFG_FILE_HOME=%UserProfile%\.activator\activatorconfig.txt" 52 | set "CFG_FILE_VERSION=%UserProfile%\.activator\%APP_VERSION%\activatorconfig.txt" 53 | set CFG_OPTS= 54 | if exist %CFG_FILE_VERSION% ( 55 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_VERSION%") DO ( 56 | set DO_NOT_REUSE_ME=%%i 57 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 58 | rem CFG_OPTS, otherwise it remains "" for this loop. 59 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 60 | ) 61 | ) 62 | if "%CFG_OPTS%"=="" ( 63 | if exist %CFG_FILE_HOME% ( 64 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_HOME%") DO ( 65 | set DO_NOT_REUSE_ME=%%i 66 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 67 | rem CFG_OPTS, otherwise it remains "" for this loop. 68 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 69 | ) 70 | ) 71 | ) 72 | 73 | rem We use the value of the JAVACMD environment variable if defined 74 | set _JAVACMD=%JAVACMD% 75 | 76 | if "%_JAVACMD%"=="" ( 77 | if not "%JAVA_HOME%"=="" ( 78 | if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe" 79 | 80 | rem if there is a java home set we make sure it is the first picked up when invoking 'java' 81 | SET "PATH=%JAVA_HOME%\bin;%PATH%" 82 | ) 83 | ) 84 | 85 | if "%_JAVACMD%"=="" set _JAVACMD=java 86 | 87 | rem Detect if this java is ok to use. 88 | for /F %%j in ('"%_JAVACMD%" -version 2^>^&1') do ( 89 | if %%~j==Java set JAVAINSTALLED=1 90 | ) 91 | 92 | rem Detect the same thing about javac 93 | if "%_JAVACCMD%"=="" ( 94 | if not "%JAVA_HOME%"=="" ( 95 | if exist "%JAVA_HOME%\bin\javac.exe" set "_JAVACCMD=%JAVA_HOME%\bin\javac.exe" 96 | ) 97 | ) 98 | if "%_JAVACCMD%"=="" set _JAVACCMD=javac 99 | for /F %%j in ('"%_JAVACCMD%" -version 2^>^&1') do ( 100 | if %%~j==javac set JAVACINSTALLED=1 101 | ) 102 | 103 | rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style 104 | set JAVAOK=true 105 | if not defined JAVAINSTALLED set JAVAOK=false 106 | if not defined JAVACINSTALLED set JAVAOK=false 107 | 108 | if "%JAVAOK%"=="false" ( 109 | echo. 110 | echo A Java JDK is not installed or can't be found. 111 | if not "%JAVA_HOME%"=="" ( 112 | echo JAVA_HOME = "%JAVA_HOME%" 113 | ) 114 | echo. 115 | echo Please go to 116 | echo http://www.oracle.com/technetwork/java/javase/downloads/index.html 117 | echo and download a valid Java JDK and install before running Activator. 118 | echo. 119 | echo If you think this message is in error, please check 120 | echo your environment variables to see if "java.exe" and "javac.exe" are 121 | echo available via JAVA_HOME or PATH. 122 | echo. 123 | if defined DOUBLECLICKED pause 124 | exit /B 1 125 | ) 126 | 127 | rem Check what Java version is being used to determine what memory options to use 128 | for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( 129 | set JAVA_VERSION=%%g 130 | ) 131 | 132 | rem Strips away the " characters 133 | set JAVA_VERSION=%JAVA_VERSION:"=% 134 | 135 | rem TODO Check if there are existing mem settings in JAVA_OPTS/CFG_OPTS and use those instead of the below 136 | for /f "delims=. tokens=1-3" %%v in ("%JAVA_VERSION%") do ( 137 | set MAJOR=%%v 138 | set MINOR=%%w 139 | set BUILD=%%x 140 | 141 | set META_SIZE=-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=256M 142 | if "!MINOR!" LSS "8" ( 143 | set META_SIZE=-XX:PermSize=64M -XX:MaxPermSize=256M 144 | ) 145 | 146 | set MEM_OPTS=!META_SIZE! 147 | ) 148 | 149 | rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config. 150 | set _JAVA_OPTS=%JAVA_OPTS% 151 | if "%_JAVA_OPTS%"=="" set _JAVA_OPTS=%CFG_OPTS% 152 | 153 | set DEBUG_OPTS= 154 | 155 | rem Loop through the arguments, building remaining args in args variable 156 | set args= 157 | :argsloop 158 | if not "%~1"=="" ( 159 | rem Checks if the argument contains "-D" and if true, adds argument 1 with 2 and puts an equal sign between them. 160 | rem This is done since batch considers "=" to be a delimiter so we need to circumvent this behavior with a small hack. 161 | set arg1=%~1 162 | if "!arg1:~0,2!"=="-D" ( 163 | set "args=%args% "%~1"="%~2"" 164 | shift 165 | shift 166 | goto argsloop 167 | ) 168 | 169 | if "%~1"=="-jvm-debug" ( 170 | if not "%~2"=="" ( 171 | rem This piece of magic somehow checks that an argument is a number 172 | for /F "delims=0123456789" %%i in ("%~2") do ( 173 | set var="%%i" 174 | ) 175 | if defined var ( 176 | rem Not a number, assume no argument given and default to 9999 177 | set JPDA_PORT=9999 178 | ) else ( 179 | rem Port was given, shift arguments 180 | set JPDA_PORT=%~2 181 | shift 182 | ) 183 | ) else ( 184 | set JPDA_PORT=9999 185 | ) 186 | shift 187 | 188 | set DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=!JPDA_PORT! 189 | goto argsloop 190 | ) 191 | rem else 192 | set "args=%args% "%~1"" 193 | shift 194 | goto argsloop 195 | ) 196 | 197 | :run 198 | 199 | if "!args!"=="" ( 200 | if defined DOUBLECLICKED ( 201 | set CMDS="ui" 202 | ) else set CMDS=!args! 203 | ) else set CMDS=!args! 204 | 205 | rem We add a / in front, so we get file:///C: instead of file://C: 206 | rem Java considers the later a UNC path. 207 | rem We also attempt a solid effort at making it URI friendly. 208 | rem We don't even bother with UNC paths. 209 | set JAVA_FRIENDLY_HOME_1=/!ACTIVATOR_HOME:\=/! 210 | set JAVA_FRIENDLY_HOME=/!JAVA_FRIENDLY_HOME_1: =%%20! 211 | 212 | rem Checks if the command contains spaces to know if it should be wrapped in quotes or not 213 | set NON_SPACED_CMD=%_JAVACMD: =% 214 | if "%_JAVACMD%"=="%NON_SPACED_CMD%" %_JAVACMD% %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 215 | if NOT "%_JAVACMD%"=="%NON_SPACED_CMD%" "%_JAVACMD%" %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 216 | 217 | if ERRORLEVEL 1 goto error 218 | goto end 219 | 220 | :error 221 | set ERROR_CODE=1 222 | 223 | :end 224 | 225 | @endlocal 226 | 227 | exit /B %ERROR_CODE% 228 | -------------------------------------------------------------------------------- /chapter2/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api._ 4 | import play.api.mvc._ 5 | 6 | object Application extends Controller { 7 | 8 | def index = Action { 9 | Ok(views.html.index("Your new application is ready.")) 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /chapter2/app/controllers/FavouriteStudios.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import models.FavouriteStudio 4 | import models.FavouriteStudio._ 5 | import play.api.libs.json.Json 6 | import play.api.mvc._ 7 | 8 | object FavouriteStudios extends Controller { 9 | def add(userId: Int, studioId: Int) = Action { 10 | val favourite = FavouriteStudio.addFavourite(userId, studioId) 11 | 12 | Ok(Json.obj("result" -> favourite)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chapter2/app/models/FavouriteStudio.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import models.dao.FavouriteStudioDAO 4 | import play.api.libs.functional.syntax._ 5 | import play.api.libs.json._ 6 | 7 | object FavouriteStudio { 8 | implicit val favouriteStudioWrites: Writes[FavouriteStudio] = ( 9 | (JsPath \ "userId").write[Int] and 10 | (JsPath \ "studioId").write[Int] 11 | )(unlift(FavouriteStudio.unapply)) 12 | 13 | def addFavourite(userId: Int, studioId: Int): FavouriteStudio = { 14 | val favourite = FavouriteStudio(userId, studioId) 15 | 16 | FavouriteStudioDAO.create(favourite) 17 | 18 | favourite 19 | } 20 | } 21 | 22 | case class FavouriteStudio(userId: Int, studioId: Int) 23 | -------------------------------------------------------------------------------- /chapter2/app/models/dao/FavouriteStudioDAO.scala: -------------------------------------------------------------------------------- 1 | package models.dao 2 | 3 | import anorm._ 4 | import models.FavouriteStudio 5 | import play.api.db.DB 6 | import play.api.Play.current 7 | 8 | object FavouriteStudioDAO { 9 | def create(favourite: FavouriteStudio) = { 10 | DB.withConnection { implicit c => 11 | SQL( 12 | """ 13 | | INSERT IGNORE INTO `favouriteStudio` (`userId`, `studioId`) 14 | | VALUES 15 | | ({userId}, {studioId}); 16 | """.stripMargin).on( 17 | "userId" -> favourite.userId, 18 | "studioId" -> favourite.studioId 19 | ).executeInsert() 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chapter2/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @main("Welcome to Play") { 4 | 5 | @play20.welcome(message) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /chapter2/app/views/main.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | 7 | @title 8 | 9 | 10 | 11 | 12 | 13 | @content 14 | 15 | 16 | -------------------------------------------------------------------------------- /chapter2/build.sbt: -------------------------------------------------------------------------------- 1 | name := """chapter2-demo""" 2 | 3 | version := "1.0" 4 | 5 | lazy val root = (project in file(".")).enablePlugins(PlayScala) 6 | 7 | doc in Compile <<= target.map(_ / "none") 8 | 9 | scalaVersion := "2.11.1" 10 | 11 | libraryDependencies ++= Seq( 12 | jdbc, 13 | anorm, 14 | cache, 15 | ws, 16 | "mysql" % "mysql-connector-java" % "5.1.18" 17 | ) 18 | -------------------------------------------------------------------------------- /chapter2/conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # 8 | # This must be changed for production, but we recommend not changing it in this file. 9 | # 10 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. 11 | application.secret="/u9PM[cJc=h>y=xGcV1W?<58iGchSJM7wXnfZ::qFTS" 12 | 13 | # The application languages 14 | # ~~~~~ 15 | application.langs="en" 16 | 17 | # Global object class 18 | # ~~~~~ 19 | # Define the Global object class for this application. 20 | # Default to Global in the root package. 21 | # application.global=Global 22 | 23 | # Router 24 | # ~~~~~ 25 | # Define the Router object to use for this application. 26 | # This router will be looked up first when the application is starting up, 27 | # so make sure this is the entry point. 28 | # Furthermore, it's assumed your route file is named properly. 29 | # So for an application router like `my.application.Router`, 30 | # you may need to define a router file `conf/my.application.routes`. 31 | # Default to Routes in the root package (and conf/routes) 32 | # application.router=my.application.Routes 33 | 34 | # Database configuration 35 | # ~~~~~ 36 | # You can declare as many datasources as you want. 37 | # By convention, the default datasource is named `default` 38 | # 39 | db.default.driver= com.mysql.jdbc.Driver 40 | db.default.url="jdbc:mysql://127.0.0.1:3306/favourites-svc" 41 | db.default.user="favourites-svc" 42 | db.default.password="1e0OZH5a9PCJ0Anj11ia0Wmy" 43 | 44 | # Evolutions 45 | # ~~~~~ 46 | # You can disable evolutions if needed 47 | # evolutionplugin=disabled 48 | 49 | # Logger 50 | # ~~~~~ 51 | # You can also configure logback (http://logback.qos.ch/), 52 | # by providing an application-logger.xml file in the conf directory. 53 | 54 | # Root logger: 55 | logger.root=ERROR 56 | 57 | # Logger used by the framework: 58 | logger.play=INFO 59 | 60 | # Logger provided to your application: 61 | logger.application=DEBUG 62 | 63 | -------------------------------------------------------------------------------- /chapter2/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # API Endpoints 6 | POST /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.add(userId: Int, studioId: Int) 7 | 8 | # Home page 9 | GET / controllers.Application.index 10 | 11 | # Map static resources from the /public folder to the /assets URL path 12 | GET /assets/*file controllers.Assets.at(path="/public", file) 13 | -------------------------------------------------------------------------------- /chapter2/init_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Clean out dist 4 | rm -rf target/universal/* 5 | 6 | # Compile distributable 7 | sbt dist 8 | 9 | # Unzip components 10 | pushd target/universal 11 | unzip chapter2-demo-1.0.zip 12 | popd 13 | -------------------------------------------------------------------------------- /chapter2/project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Sat Feb 21 15:26:55 PST 2015 3 | template.uuid=d2d450ff-ffb2-4c93-9354-c3b15fc933ea 4 | sbt.version=0.13.5 5 | -------------------------------------------------------------------------------- /chapter2/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 2 | 3 | // The Play plugin 4 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8") 5 | 6 | // web plugins 7 | 8 | addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") 9 | 10 | addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.0") 11 | 12 | addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.1") 13 | 14 | addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.1") 15 | 16 | addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.0.0") 17 | 18 | addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.0.0") 19 | -------------------------------------------------------------------------------- /chapter2/public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter2/public/images/favicon.png -------------------------------------------------------------------------------- /chapter2/public/javascripts/hello.js: -------------------------------------------------------------------------------- 1 | if (window.console) { 2 | console.log("Welcome to your Play application's JavaScript!"); 3 | } -------------------------------------------------------------------------------- /chapter2/public/stylesheets/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter2/public/stylesheets/main.css -------------------------------------------------------------------------------- /chapter2/run_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Run 4 | pushd target/universal/chapter2-demo-1.0/bin 5 | ./chapter2-demo 6 | popd 7 | -------------------------------------------------------------------------------- /chapter2/test/ApplicationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * Add your spec here. 10 | * You can mock out a whole application including requests, plugins etc. 11 | * For more information, consult the wiki. 12 | */ 13 | @RunWith(classOf[JUnitRunner]) 14 | class ApplicationSpec extends Specification { 15 | 16 | "Application" should { 17 | 18 | "send 404 on a bad request" in new WithApplication{ 19 | route(FakeRequest(GET, "/boum")) must beNone 20 | } 21 | 22 | "render the index page" in new WithApplication{ 23 | val home = route(FakeRequest(GET, "/")).get 24 | 25 | status(home) must equalTo(OK) 26 | contentType(home) must beSome.which(_ == "text/html") 27 | contentAsString(home) must contain ("Your new application is ready.") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter2/test/IntegrationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * add your integration spec here. 10 | * An integration test will fire up a whole play application in a real (or headless) browser 11 | */ 12 | @RunWith(classOf[JUnitRunner]) 13 | class IntegrationSpec extends Specification { 14 | 15 | "Application" should { 16 | 17 | "work from within a browser" in new WithBrowser { 18 | 19 | browser.goTo("http://localhost:" + port) 20 | 21 | browser.pageSource must contain("Your new application is ready.") 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter3/.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | project/target 4 | target 5 | tmp 6 | .history 7 | dist 8 | /.idea 9 | /*.iml 10 | /out 11 | /.idea_modules 12 | /.classpath 13 | /.project 14 | /RUNNING_PID 15 | /.settings 16 | -------------------------------------------------------------------------------- /chapter3/LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 8 | language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /chapter3/README: -------------------------------------------------------------------------------- 1 | This is your new Play application 2 | ================================= 3 | 4 | This file will be packaged with your application, when using `activator dist`. 5 | -------------------------------------------------------------------------------- /chapter3/activator-launch-1.2.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter3/activator-launch-1.2.12.jar -------------------------------------------------------------------------------- /chapter3/activator.bat: -------------------------------------------------------------------------------- 1 | @REM activator launcher script 2 | @REM 3 | @REM Envioronment: 4 | @REM JAVA_HOME - location of a JDK home dir (optional if java on path) 5 | @REM CFG_OPTS - JVM options (optional) 6 | @REM Configuration: 7 | @REM activatorconfig.txt found in the ACTIVATOR_HOME or ACTIVATOR_HOME/ACTIVATOR_VERSION 8 | @setlocal enabledelayedexpansion 9 | 10 | @echo off 11 | 12 | set "var1=%~1" 13 | if defined var1 ( 14 | if "%var1%"=="help" ( 15 | echo. 16 | echo Usage activator [options] [command] 17 | echo. 18 | echo Commands: 19 | echo ui Start the Activator UI 20 | echo new [name] [template-id] Create a new project with [name] using template [template-id] 21 | echo list-templates Print all available template names 22 | echo help Print this message 23 | echo. 24 | echo Options: 25 | echo -jvm-debug [port] Turn on JVM debugging, open at the given port. Defaults to 9999 if no port given. 26 | echo. 27 | echo Environment variables ^(read from context^): 28 | echo JAVA_OPTS Environment variable, if unset uses "" 29 | echo SBT_OPTS Environment variable, if unset uses "" 30 | echo ACTIVATOR_OPTS Environment variable, if unset uses "" 31 | echo. 32 | goto :end 33 | ) 34 | ) 35 | 36 | if "%ACTIVATOR_HOME%"=="" ( 37 | set "ACTIVATOR_HOME=%~dp0" 38 | @REM remove trailing "\" from path 39 | set ACTIVATOR_HOME=!ACTIVATOR_HOME:~0,-1! 40 | ) 41 | 42 | set ERROR_CODE=0 43 | set APP_VERSION=1.2.12 44 | set ACTIVATOR_LAUNCH_JAR=activator-launch-%APP_VERSION%.jar 45 | 46 | rem Detect if we were double clicked, although theoretically A user could 47 | rem manually run cmd /c 48 | for %%x in (%cmdcmdline%) do if %%~x==/c set DOUBLECLICKED=1 49 | 50 | rem FIRST we load a config file of extra options (if there is one) 51 | set "CFG_FILE_HOME=%UserProfile%\.activator\activatorconfig.txt" 52 | set "CFG_FILE_VERSION=%UserProfile%\.activator\%APP_VERSION%\activatorconfig.txt" 53 | set CFG_OPTS= 54 | if exist %CFG_FILE_VERSION% ( 55 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_VERSION%") DO ( 56 | set DO_NOT_REUSE_ME=%%i 57 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 58 | rem CFG_OPTS, otherwise it remains "" for this loop. 59 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 60 | ) 61 | ) 62 | if "%CFG_OPTS%"=="" ( 63 | if exist %CFG_FILE_HOME% ( 64 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_HOME%") DO ( 65 | set DO_NOT_REUSE_ME=%%i 66 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 67 | rem CFG_OPTS, otherwise it remains "" for this loop. 68 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 69 | ) 70 | ) 71 | ) 72 | 73 | rem We use the value of the JAVACMD environment variable if defined 74 | set _JAVACMD=%JAVACMD% 75 | 76 | if "%_JAVACMD%"=="" ( 77 | if not "%JAVA_HOME%"=="" ( 78 | if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe" 79 | 80 | rem if there is a java home set we make sure it is the first picked up when invoking 'java' 81 | SET "PATH=%JAVA_HOME%\bin;%PATH%" 82 | ) 83 | ) 84 | 85 | if "%_JAVACMD%"=="" set _JAVACMD=java 86 | 87 | rem Detect if this java is ok to use. 88 | for /F %%j in ('"%_JAVACMD%" -version 2^>^&1') do ( 89 | if %%~j==Java set JAVAINSTALLED=1 90 | ) 91 | 92 | rem Detect the same thing about javac 93 | if "%_JAVACCMD%"=="" ( 94 | if not "%JAVA_HOME%"=="" ( 95 | if exist "%JAVA_HOME%\bin\javac.exe" set "_JAVACCMD=%JAVA_HOME%\bin\javac.exe" 96 | ) 97 | ) 98 | if "%_JAVACCMD%"=="" set _JAVACCMD=javac 99 | for /F %%j in ('"%_JAVACCMD%" -version 2^>^&1') do ( 100 | if %%~j==javac set JAVACINSTALLED=1 101 | ) 102 | 103 | rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style 104 | set JAVAOK=true 105 | if not defined JAVAINSTALLED set JAVAOK=false 106 | if not defined JAVACINSTALLED set JAVAOK=false 107 | 108 | if "%JAVAOK%"=="false" ( 109 | echo. 110 | echo A Java JDK is not installed or can't be found. 111 | if not "%JAVA_HOME%"=="" ( 112 | echo JAVA_HOME = "%JAVA_HOME%" 113 | ) 114 | echo. 115 | echo Please go to 116 | echo http://www.oracle.com/technetwork/java/javase/downloads/index.html 117 | echo and download a valid Java JDK and install before running Activator. 118 | echo. 119 | echo If you think this message is in error, please check 120 | echo your environment variables to see if "java.exe" and "javac.exe" are 121 | echo available via JAVA_HOME or PATH. 122 | echo. 123 | if defined DOUBLECLICKED pause 124 | exit /B 1 125 | ) 126 | 127 | rem Check what Java version is being used to determine what memory options to use 128 | for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( 129 | set JAVA_VERSION=%%g 130 | ) 131 | 132 | rem Strips away the " characters 133 | set JAVA_VERSION=%JAVA_VERSION:"=% 134 | 135 | rem TODO Check if there are existing mem settings in JAVA_OPTS/CFG_OPTS and use those instead of the below 136 | for /f "delims=. tokens=1-3" %%v in ("%JAVA_VERSION%") do ( 137 | set MAJOR=%%v 138 | set MINOR=%%w 139 | set BUILD=%%x 140 | 141 | set META_SIZE=-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=256M 142 | if "!MINOR!" LSS "8" ( 143 | set META_SIZE=-XX:PermSize=64M -XX:MaxPermSize=256M 144 | ) 145 | 146 | set MEM_OPTS=!META_SIZE! 147 | ) 148 | 149 | rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config. 150 | set _JAVA_OPTS=%JAVA_OPTS% 151 | if "%_JAVA_OPTS%"=="" set _JAVA_OPTS=%CFG_OPTS% 152 | 153 | set DEBUG_OPTS= 154 | 155 | rem Loop through the arguments, building remaining args in args variable 156 | set args= 157 | :argsloop 158 | if not "%~1"=="" ( 159 | rem Checks if the argument contains "-D" and if true, adds argument 1 with 2 and puts an equal sign between them. 160 | rem This is done since batch considers "=" to be a delimiter so we need to circumvent this behavior with a small hack. 161 | set arg1=%~1 162 | if "!arg1:~0,2!"=="-D" ( 163 | set "args=%args% "%~1"="%~2"" 164 | shift 165 | shift 166 | goto argsloop 167 | ) 168 | 169 | if "%~1"=="-jvm-debug" ( 170 | if not "%~2"=="" ( 171 | rem This piece of magic somehow checks that an argument is a number 172 | for /F "delims=0123456789" %%i in ("%~2") do ( 173 | set var="%%i" 174 | ) 175 | if defined var ( 176 | rem Not a number, assume no argument given and default to 9999 177 | set JPDA_PORT=9999 178 | ) else ( 179 | rem Port was given, shift arguments 180 | set JPDA_PORT=%~2 181 | shift 182 | ) 183 | ) else ( 184 | set JPDA_PORT=9999 185 | ) 186 | shift 187 | 188 | set DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=!JPDA_PORT! 189 | goto argsloop 190 | ) 191 | rem else 192 | set "args=%args% "%~1"" 193 | shift 194 | goto argsloop 195 | ) 196 | 197 | :run 198 | 199 | if "!args!"=="" ( 200 | if defined DOUBLECLICKED ( 201 | set CMDS="ui" 202 | ) else set CMDS=!args! 203 | ) else set CMDS=!args! 204 | 205 | rem We add a / in front, so we get file:///C: instead of file://C: 206 | rem Java considers the later a UNC path. 207 | rem We also attempt a solid effort at making it URI friendly. 208 | rem We don't even bother with UNC paths. 209 | set JAVA_FRIENDLY_HOME_1=/!ACTIVATOR_HOME:\=/! 210 | set JAVA_FRIENDLY_HOME=/!JAVA_FRIENDLY_HOME_1: =%%20! 211 | 212 | rem Checks if the command contains spaces to know if it should be wrapped in quotes or not 213 | set NON_SPACED_CMD=%_JAVACMD: =% 214 | if "%_JAVACMD%"=="%NON_SPACED_CMD%" %_JAVACMD% %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 215 | if NOT "%_JAVACMD%"=="%NON_SPACED_CMD%" "%_JAVACMD%" %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 216 | 217 | if ERRORLEVEL 1 goto error 218 | goto end 219 | 220 | :error 221 | set ERROR_CODE=1 222 | 223 | :end 224 | 225 | @endlocal 226 | 227 | exit /B %ERROR_CODE% 228 | -------------------------------------------------------------------------------- /chapter3/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api._ 4 | import play.api.mvc._ 5 | 6 | object Application extends Controller { 7 | 8 | def index = Action { 9 | Ok(views.html.index("Your new application is ready.")) 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /chapter3/app/controllers/FavouriteStudios.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import models.FavouriteStudio 4 | import models.FavouriteStudio._ 5 | import play.api.cache.{Cached, Cache} 6 | import play.api.libs.json.Json 7 | import play.api.mvc._ 8 | import play.api.Play.current 9 | 10 | object FavouriteStudios extends Controller { 11 | private def clearCaches(userId: Int, studioId: Int) = 12 | List( 13 | "find_"+userId+"_"+studioId, 14 | "findAll_"+userId 15 | ).map { key => 16 | Cache.remove(key) 17 | } 18 | 19 | def add(userId: Int, studioId: Int) = Action { 20 | val favourite = FavouriteStudio.addFavourite(userId, studioId) 21 | 22 | clearCaches(userId, studioId) 23 | 24 | Ok(Json.obj("result" -> favourite)) 25 | } 26 | 27 | def remove(userId: Int, studioId: Int) = Action { 28 | FavouriteStudio.delete(userId, studioId) 29 | 30 | clearCaches(userId, studioId) 31 | 32 | Ok(Json.obj("result" -> Json.obj())) 33 | } 34 | 35 | def find(userId: Int, studioId: Int) = Cached("find_"+userId+"_"+studioId) { 36 | Action { 37 | val oFavourite = FavouriteStudio.find(userId, studioId) 38 | 39 | oFavourite match { 40 | case None => NotFound(Json.obj("error" -> "NOT_FOUND")) 41 | case Some(favourite) => Ok(Json.obj("result" -> favourite)) 42 | } 43 | } 44 | } 45 | 46 | def findAll(userId: Int) = Cached("findAll_"+userId) { 47 | Action { 48 | val allFavourites = FavouriteStudio.findAllByUser(userId) 49 | 50 | Ok(Json.obj("result" -> allFavourites)) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /chapter3/app/models/FavouriteStudio.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import models.dao.FavouriteStudioDAO 4 | import play.api.libs.functional.syntax._ 5 | import play.api.libs.json._ 6 | 7 | object FavouriteStudio { 8 | implicit val favouriteStudioWrites: Writes[FavouriteStudio] = ( 9 | (JsPath \ "userId").write[Int] and 10 | (JsPath \ "studioId").write[Int] 11 | )(unlift(FavouriteStudio.unapply)) 12 | 13 | def addFavourite(userId: Int, studioId: Int): FavouriteStudio = { 14 | val favourite = FavouriteStudio(userId, studioId) 15 | 16 | FavouriteStudioDAO.create(favourite) 17 | 18 | favourite 19 | } 20 | 21 | def delete(userId: Int, studioId: Int) = 22 | FavouriteStudioDAO.delete(FavouriteStudio(userId, studioId)) 23 | 24 | def findAllByUser(userId: Int): List[FavouriteStudio] = 25 | FavouriteStudioDAO.index(userId) 26 | 27 | def find(userId: Int, studioId: Int): Option[FavouriteStudio] = { 28 | val favourite = FavouriteStudio(userId, studioId) 29 | 30 | if (FavouriteStudioDAO.exists(favourite)) 31 | Some(favourite) 32 | else 33 | None 34 | } 35 | } 36 | 37 | case class FavouriteStudio(userId: Int, studioId: Int) 38 | -------------------------------------------------------------------------------- /chapter3/app/models/dao/FavouriteStudioDAO.scala: -------------------------------------------------------------------------------- 1 | package models.dao 2 | 3 | import anorm._ 4 | import models.FavouriteStudio 5 | import play.api.db.DB 6 | import play.api.Play.current 7 | 8 | object FavouriteStudioDAO { 9 | def create(favourite: FavouriteStudio) = { 10 | DB.withConnection { implicit c => 11 | SQL( 12 | """ 13 | | INSERT IGNORE INTO `favouriteStudio` (`userId`, `studioId`) 14 | | VALUES 15 | | ({userId}, {studioId}); 16 | """.stripMargin).on( 17 | "userId" -> favourite.userId, 18 | "studioId" -> favourite.studioId 19 | ).executeInsert() 20 | } 21 | } 22 | 23 | def delete(favourite: FavouriteStudio) = { 24 | DB.withConnection { implicit c => 25 | SQL( 26 | """ 27 | | DELETE FROM `favouriteStudio` 28 | | WHERE `userId`={userId} AND `studioId`={studioId} 29 | | LIMIT 1; 30 | """.stripMargin).on( 31 | "userId" -> favourite.userId, 32 | "studioId" -> favourite.studioId 33 | ).executeUpdate() 34 | } 35 | } 36 | 37 | def exists(favourite: FavouriteStudio): Boolean = { 38 | DB.withConnection { implicit c => 39 | val result = SQL( 40 | """ 41 | | SELECT COUNT(*) as numMatches 42 | | FROM `favouriteStudio` 43 | | WHERE `userId`={userId} AND `studioId`={studioId}; 44 | """.stripMargin).on( 45 | "userId" -> favourite.userId, 46 | "studioId" -> favourite.studioId 47 | ).apply().head 48 | 49 | result[Int]("numMatches") != 0 50 | } 51 | } 52 | 53 | def index(userId: Int): List[FavouriteStudio] = { 54 | DB.withConnection { implicit c => 55 | val results = SQL( 56 | """ 57 | | SELECT `userId`, `studioId` 58 | | FROM `favouriteStudio` 59 | | WHERE `userId`={userId}; 60 | """.stripMargin).on( 61 | "userId" -> userId 62 | ).apply() 63 | 64 | results.map { row => 65 | FavouriteStudio(row[Int]("userId"), row[Int]("studioId")) 66 | }.force.toList 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /chapter3/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @main("Welcome to Play") { 4 | 5 | @play20.welcome(message) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /chapter3/app/views/main.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | 7 | @title 8 | 9 | 10 | 11 | 12 | 13 | @content 14 | 15 | 16 | -------------------------------------------------------------------------------- /chapter3/build.sbt: -------------------------------------------------------------------------------- 1 | name := """chapter3-demo""" 2 | 3 | version := "1.0" 4 | 5 | lazy val root = (project in file(".")).enablePlugins(PlayScala) 6 | 7 | doc in Compile <<= target.map(_ / "none") 8 | 9 | scalaVersion := "2.11.1" 10 | 11 | libraryDependencies ++= Seq( 12 | jdbc, 13 | anorm, 14 | cache, 15 | ws, 16 | "mysql" % "mysql-connector-java" % "5.1.18" 17 | ) 18 | -------------------------------------------------------------------------------- /chapter3/conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # 8 | # This must be changed for production, but we recommend not changing it in this file. 9 | # 10 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. 11 | application.secret="/u9PM[cJc=h>y=xGcV1W?<58iGchSJM7wXnfZ::qFTS" 12 | 13 | # The application languages 14 | # ~~~~~ 15 | application.langs="en" 16 | 17 | # Global object class 18 | # ~~~~~ 19 | # Define the Global object class for this application. 20 | # Default to Global in the root package. 21 | # application.global=Global 22 | 23 | # Router 24 | # ~~~~~ 25 | # Define the Router object to use for this application. 26 | # This router will be looked up first when the application is starting up, 27 | # so make sure this is the entry point. 28 | # Furthermore, it's assumed your route file is named properly. 29 | # So for an application router like `my.application.Router`, 30 | # you may need to define a router file `conf/my.application.routes`. 31 | # Default to Routes in the root package (and conf/routes) 32 | # application.router=my.application.Routes 33 | 34 | # Database configuration 35 | # ~~~~~ 36 | # You can declare as many datasources as you want. 37 | # By convention, the default datasource is named `default` 38 | # 39 | db.default.driver= com.mysql.jdbc.Driver 40 | db.default.url="jdbc:mysql://127.0.0.1:3306/favourites-svc" 41 | db.default.user="favourites-svc" 42 | db.default.password="1e0OZH5a9PCJ0Anj11ia0Wmy" 43 | 44 | # Evolutions 45 | # ~~~~~ 46 | # You can disable evolutions if needed 47 | # evolutionplugin=disabled 48 | 49 | # Logger 50 | # ~~~~~ 51 | # You can also configure logback (http://logback.qos.ch/), 52 | # by providing an application-logger.xml file in the conf directory. 53 | 54 | # Root logger: 55 | logger.root=ERROR 56 | 57 | # Logger used by the framework: 58 | logger.play=INFO 59 | 60 | # Logger provided to your application: 61 | logger.application=DEBUG 62 | 63 | -------------------------------------------------------------------------------- /chapter3/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # API Endpoints 6 | POST /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.add(userId: Int, studioId: Int) 7 | DELETE /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.remove(userId: Int, studioId: Int) 8 | GET /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.find(userId: Int, studioId: Int) 9 | GET /users/:userId/favouriteStudios controllers.FavouriteStudios.findAll(userId: Int) 10 | 11 | # Home page 12 | GET / controllers.Application.index 13 | 14 | # Map static resources from the /public folder to the /assets URL path 15 | GET /assets/*file controllers.Assets.at(path="/public", file) 16 | -------------------------------------------------------------------------------- /chapter3/init_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Clean out dist 4 | rm -rf target/universal/* 5 | 6 | # Compile distributable 7 | sbt dist 8 | 9 | # Unzip components 10 | pushd target/universal 11 | unzip chapter3-demo-1.0.zip 12 | popd 13 | -------------------------------------------------------------------------------- /chapter3/project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Sat Feb 21 15:26:55 PST 2015 3 | template.uuid=d2d450ff-ffb2-4c93-9354-c3b15fc933ea 4 | sbt.version=0.13.5 5 | -------------------------------------------------------------------------------- /chapter3/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 2 | 3 | // The Play plugin 4 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8") 5 | 6 | // web plugins 7 | 8 | addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") 9 | 10 | addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.0") 11 | 12 | addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.1") 13 | 14 | addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.1") 15 | 16 | addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.0.0") 17 | 18 | addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.0.0") 19 | -------------------------------------------------------------------------------- /chapter3/public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter3/public/images/favicon.png -------------------------------------------------------------------------------- /chapter3/public/javascripts/hello.js: -------------------------------------------------------------------------------- 1 | if (window.console) { 2 | console.log("Welcome to your Play application's JavaScript!"); 3 | } -------------------------------------------------------------------------------- /chapter3/public/stylesheets/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter3/public/stylesheets/main.css -------------------------------------------------------------------------------- /chapter3/run_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Run 4 | pushd target/universal/chapter3-demo-1.0/bin 5 | ./chapter3-demo 6 | popd 7 | -------------------------------------------------------------------------------- /chapter3/test/ApplicationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * Add your spec here. 10 | * You can mock out a whole application including requests, plugins etc. 11 | * For more information, consult the wiki. 12 | */ 13 | @RunWith(classOf[JUnitRunner]) 14 | class ApplicationSpec extends Specification { 15 | 16 | "Application" should { 17 | 18 | "send 404 on a bad request" in new WithApplication{ 19 | route(FakeRequest(GET, "/boum")) must beNone 20 | } 21 | 22 | "render the index page" in new WithApplication{ 23 | val home = route(FakeRequest(GET, "/")).get 24 | 25 | status(home) must equalTo(OK) 26 | contentType(home) must beSome.which(_ == "text/html") 27 | contentAsString(home) must contain ("Your new application is ready.") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter3/test/IntegrationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * add your integration spec here. 10 | * An integration test will fire up a whole play application in a real (or headless) browser 11 | */ 12 | @RunWith(classOf[JUnitRunner]) 13 | class IntegrationSpec extends Specification { 14 | 15 | "Application" should { 16 | 17 | "work from within a browser" in new WithBrowser { 18 | 19 | browser.goTo("http://localhost:" + port) 20 | 21 | browser.pageSource must contain("Your new application is ready.") 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter4/.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | project/target 4 | target 5 | tmp 6 | .history 7 | dist 8 | /.idea 9 | /*.iml 10 | /out 11 | /.idea_modules 12 | /.classpath 13 | /.project 14 | /RUNNING_PID 15 | /.settings 16 | -------------------------------------------------------------------------------- /chapter4/LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 8 | language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /chapter4/README: -------------------------------------------------------------------------------- 1 | This is your new Play application 2 | ================================= 3 | 4 | This file will be packaged with your application, when using `activator dist`. 5 | -------------------------------------------------------------------------------- /chapter4/activator-launch-1.2.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter4/activator-launch-1.2.12.jar -------------------------------------------------------------------------------- /chapter4/activator.bat: -------------------------------------------------------------------------------- 1 | @REM activator launcher script 2 | @REM 3 | @REM Envioronment: 4 | @REM JAVA_HOME - location of a JDK home dir (optional if java on path) 5 | @REM CFG_OPTS - JVM options (optional) 6 | @REM Configuration: 7 | @REM activatorconfig.txt found in the ACTIVATOR_HOME or ACTIVATOR_HOME/ACTIVATOR_VERSION 8 | @setlocal enabledelayedexpansion 9 | 10 | @echo off 11 | 12 | set "var1=%~1" 13 | if defined var1 ( 14 | if "%var1%"=="help" ( 15 | echo. 16 | echo Usage activator [options] [command] 17 | echo. 18 | echo Commands: 19 | echo ui Start the Activator UI 20 | echo new [name] [template-id] Create a new project with [name] using template [template-id] 21 | echo list-templates Print all available template names 22 | echo help Print this message 23 | echo. 24 | echo Options: 25 | echo -jvm-debug [port] Turn on JVM debugging, open at the given port. Defaults to 9999 if no port given. 26 | echo. 27 | echo Environment variables ^(read from context^): 28 | echo JAVA_OPTS Environment variable, if unset uses "" 29 | echo SBT_OPTS Environment variable, if unset uses "" 30 | echo ACTIVATOR_OPTS Environment variable, if unset uses "" 31 | echo. 32 | goto :end 33 | ) 34 | ) 35 | 36 | if "%ACTIVATOR_HOME%"=="" ( 37 | set "ACTIVATOR_HOME=%~dp0" 38 | @REM remove trailing "\" from path 39 | set ACTIVATOR_HOME=!ACTIVATOR_HOME:~0,-1! 40 | ) 41 | 42 | set ERROR_CODE=0 43 | set APP_VERSION=1.2.12 44 | set ACTIVATOR_LAUNCH_JAR=activator-launch-%APP_VERSION%.jar 45 | 46 | rem Detect if we were double clicked, although theoretically A user could 47 | rem manually run cmd /c 48 | for %%x in (%cmdcmdline%) do if %%~x==/c set DOUBLECLICKED=1 49 | 50 | rem FIRST we load a config file of extra options (if there is one) 51 | set "CFG_FILE_HOME=%UserProfile%\.activator\activatorconfig.txt" 52 | set "CFG_FILE_VERSION=%UserProfile%\.activator\%APP_VERSION%\activatorconfig.txt" 53 | set CFG_OPTS= 54 | if exist %CFG_FILE_VERSION% ( 55 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_VERSION%") DO ( 56 | set DO_NOT_REUSE_ME=%%i 57 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 58 | rem CFG_OPTS, otherwise it remains "" for this loop. 59 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 60 | ) 61 | ) 62 | if "%CFG_OPTS%"=="" ( 63 | if exist %CFG_FILE_HOME% ( 64 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_HOME%") DO ( 65 | set DO_NOT_REUSE_ME=%%i 66 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 67 | rem CFG_OPTS, otherwise it remains "" for this loop. 68 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 69 | ) 70 | ) 71 | ) 72 | 73 | rem We use the value of the JAVACMD environment variable if defined 74 | set _JAVACMD=%JAVACMD% 75 | 76 | if "%_JAVACMD%"=="" ( 77 | if not "%JAVA_HOME%"=="" ( 78 | if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe" 79 | 80 | rem if there is a java home set we make sure it is the first picked up when invoking 'java' 81 | SET "PATH=%JAVA_HOME%\bin;%PATH%" 82 | ) 83 | ) 84 | 85 | if "%_JAVACMD%"=="" set _JAVACMD=java 86 | 87 | rem Detect if this java is ok to use. 88 | for /F %%j in ('"%_JAVACMD%" -version 2^>^&1') do ( 89 | if %%~j==Java set JAVAINSTALLED=1 90 | ) 91 | 92 | rem Detect the same thing about javac 93 | if "%_JAVACCMD%"=="" ( 94 | if not "%JAVA_HOME%"=="" ( 95 | if exist "%JAVA_HOME%\bin\javac.exe" set "_JAVACCMD=%JAVA_HOME%\bin\javac.exe" 96 | ) 97 | ) 98 | if "%_JAVACCMD%"=="" set _JAVACCMD=javac 99 | for /F %%j in ('"%_JAVACCMD%" -version 2^>^&1') do ( 100 | if %%~j==javac set JAVACINSTALLED=1 101 | ) 102 | 103 | rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style 104 | set JAVAOK=true 105 | if not defined JAVAINSTALLED set JAVAOK=false 106 | if not defined JAVACINSTALLED set JAVAOK=false 107 | 108 | if "%JAVAOK%"=="false" ( 109 | echo. 110 | echo A Java JDK is not installed or can't be found. 111 | if not "%JAVA_HOME%"=="" ( 112 | echo JAVA_HOME = "%JAVA_HOME%" 113 | ) 114 | echo. 115 | echo Please go to 116 | echo http://www.oracle.com/technetwork/java/javase/downloads/index.html 117 | echo and download a valid Java JDK and install before running Activator. 118 | echo. 119 | echo If you think this message is in error, please check 120 | echo your environment variables to see if "java.exe" and "javac.exe" are 121 | echo available via JAVA_HOME or PATH. 122 | echo. 123 | if defined DOUBLECLICKED pause 124 | exit /B 1 125 | ) 126 | 127 | rem Check what Java version is being used to determine what memory options to use 128 | for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( 129 | set JAVA_VERSION=%%g 130 | ) 131 | 132 | rem Strips away the " characters 133 | set JAVA_VERSION=%JAVA_VERSION:"=% 134 | 135 | rem TODO Check if there are existing mem settings in JAVA_OPTS/CFG_OPTS and use those instead of the below 136 | for /f "delims=. tokens=1-3" %%v in ("%JAVA_VERSION%") do ( 137 | set MAJOR=%%v 138 | set MINOR=%%w 139 | set BUILD=%%x 140 | 141 | set META_SIZE=-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=256M 142 | if "!MINOR!" LSS "8" ( 143 | set META_SIZE=-XX:PermSize=64M -XX:MaxPermSize=256M 144 | ) 145 | 146 | set MEM_OPTS=!META_SIZE! 147 | ) 148 | 149 | rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config. 150 | set _JAVA_OPTS=%JAVA_OPTS% 151 | if "%_JAVA_OPTS%"=="" set _JAVA_OPTS=%CFG_OPTS% 152 | 153 | set DEBUG_OPTS= 154 | 155 | rem Loop through the arguments, building remaining args in args variable 156 | set args= 157 | :argsloop 158 | if not "%~1"=="" ( 159 | rem Checks if the argument contains "-D" and if true, adds argument 1 with 2 and puts an equal sign between them. 160 | rem This is done since batch considers "=" to be a delimiter so we need to circumvent this behavior with a small hack. 161 | set arg1=%~1 162 | if "!arg1:~0,2!"=="-D" ( 163 | set "args=%args% "%~1"="%~2"" 164 | shift 165 | shift 166 | goto argsloop 167 | ) 168 | 169 | if "%~1"=="-jvm-debug" ( 170 | if not "%~2"=="" ( 171 | rem This piece of magic somehow checks that an argument is a number 172 | for /F "delims=0123456789" %%i in ("%~2") do ( 173 | set var="%%i" 174 | ) 175 | if defined var ( 176 | rem Not a number, assume no argument given and default to 9999 177 | set JPDA_PORT=9999 178 | ) else ( 179 | rem Port was given, shift arguments 180 | set JPDA_PORT=%~2 181 | shift 182 | ) 183 | ) else ( 184 | set JPDA_PORT=9999 185 | ) 186 | shift 187 | 188 | set DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=!JPDA_PORT! 189 | goto argsloop 190 | ) 191 | rem else 192 | set "args=%args% "%~1"" 193 | shift 194 | goto argsloop 195 | ) 196 | 197 | :run 198 | 199 | if "!args!"=="" ( 200 | if defined DOUBLECLICKED ( 201 | set CMDS="ui" 202 | ) else set CMDS=!args! 203 | ) else set CMDS=!args! 204 | 205 | rem We add a / in front, so we get file:///C: instead of file://C: 206 | rem Java considers the later a UNC path. 207 | rem We also attempt a solid effort at making it URI friendly. 208 | rem We don't even bother with UNC paths. 209 | set JAVA_FRIENDLY_HOME_1=/!ACTIVATOR_HOME:\=/! 210 | set JAVA_FRIENDLY_HOME=/!JAVA_FRIENDLY_HOME_1: =%%20! 211 | 212 | rem Checks if the command contains spaces to know if it should be wrapped in quotes or not 213 | set NON_SPACED_CMD=%_JAVACMD: =% 214 | if "%_JAVACMD%"=="%NON_SPACED_CMD%" %_JAVACMD% %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 215 | if NOT "%_JAVACMD%"=="%NON_SPACED_CMD%" "%_JAVACMD%" %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 216 | 217 | if ERRORLEVEL 1 goto error 218 | goto end 219 | 220 | :error 221 | set ERROR_CODE=1 222 | 223 | :end 224 | 225 | @endlocal 226 | 227 | exit /B %ERROR_CODE% 228 | -------------------------------------------------------------------------------- /chapter4/app/actors/NotificationActor.scala: -------------------------------------------------------------------------------- 1 | package actors 2 | 3 | import akka.actor.{Props, Actor} 4 | import models.FavouriteStudio 5 | 6 | object NotificationActor { 7 | def props: Props = Props(new NotificationActor) 8 | } 9 | 10 | class NotificationActor extends Actor { 11 | def receive = { 12 | case favourite: FavouriteStudio => 13 | notifyFriendsOfFavourite(favourite) 14 | } 15 | 16 | private def notifyFriendsOfFavourite(favourite: FavouriteStudio): Unit = { 17 | // TODO: Lookup friends 18 | 19 | // TODO: Send a push notification to each friend 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /chapter4/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api._ 4 | import play.api.mvc._ 5 | 6 | object Application extends Controller { 7 | 8 | def index = Action { 9 | Ok(views.html.index("Your new application is ready.")) 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /chapter4/app/controllers/FavouriteStudios.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import models.FavouriteStudio 4 | import models.FavouriteStudio._ 5 | import play.api.cache.{Cached, Cache} 6 | import play.api.libs.json.Json 7 | import play.api.mvc._ 8 | import play.api.Play.current 9 | 10 | object FavouriteStudios extends Controller { 11 | private def clearCaches(userId: Int, studioId: Int) = 12 | List( 13 | "find_"+userId+"_"+studioId, 14 | "findAll_"+userId 15 | ).map { key => 16 | Cache.remove(key) 17 | } 18 | 19 | def add(userId: Int, studioId: Int) = Action { 20 | val favourite = FavouriteStudio.addFavourite(userId, studioId) 21 | 22 | clearCaches(userId, studioId) 23 | 24 | Ok(Json.obj("result" -> favourite)) 25 | } 26 | 27 | def remove(userId: Int, studioId: Int) = Action { 28 | FavouriteStudio.delete(userId, studioId) 29 | 30 | clearCaches(userId, studioId) 31 | 32 | Ok(Json.obj("result" -> Json.obj())) 33 | } 34 | 35 | def find(userId: Int, studioId: Int) = Cached("find_"+userId+"_"+studioId) { 36 | Action { 37 | val oFavourite = FavouriteStudio.find(userId, studioId) 38 | 39 | oFavourite match { 40 | case None => NotFound(Json.obj("error" -> "NOT_FOUND")) 41 | case Some(favourite) => Ok(Json.obj("result" -> favourite)) 42 | } 43 | } 44 | } 45 | 46 | def findAll(userId: Int) = Cached("findAll_"+userId) { 47 | Action { 48 | val allFavourites = FavouriteStudio.findAllByUser(userId) 49 | 50 | Ok(Json.obj("result" -> allFavourites)) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /chapter4/app/models/FavouriteStudio.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import actors.NotificationActor 4 | import models.dao.FavouriteStudioDAO 5 | import play.api.libs.concurrent.Akka 6 | import play.api.libs.functional.syntax._ 7 | import play.api.libs.json._ 8 | 9 | import play.api.Play.current 10 | 11 | object FavouriteStudio { 12 | lazy val notificationActor = Akka.system.actorOf(NotificationActor.props) 13 | 14 | implicit val favouriteStudioWrites: Writes[FavouriteStudio] = ( 15 | (JsPath \ "userId").write[Int] and 16 | (JsPath \ "studioId").write[Int] 17 | )(unlift(FavouriteStudio.unapply)) 18 | 19 | def addFavourite(userId: Int, studioId: Int): FavouriteStudio = { 20 | val favourite = FavouriteStudio(userId, studioId) 21 | 22 | FavouriteStudioDAO.create(favourite) 23 | 24 | notificationActor ! favourite 25 | 26 | favourite 27 | } 28 | 29 | def delete(userId: Int, studioId: Int) = 30 | FavouriteStudioDAO.delete(FavouriteStudio(userId, studioId)) 31 | 32 | def findAllByUser(userId: Int): List[FavouriteStudio] = 33 | FavouriteStudioDAO.index(userId) 34 | 35 | def find(userId: Int, studioId: Int): Option[FavouriteStudio] = { 36 | val favourite = FavouriteStudio(userId, studioId) 37 | 38 | if (FavouriteStudioDAO.exists(favourite)) 39 | Some(favourite) 40 | else 41 | None 42 | } 43 | } 44 | 45 | case class FavouriteStudio(userId: Int, studioId: Int) 46 | -------------------------------------------------------------------------------- /chapter4/app/models/dao/FavouriteStudioDAO.scala: -------------------------------------------------------------------------------- 1 | package models.dao 2 | 3 | import anorm._ 4 | import models.FavouriteStudio 5 | import play.api.db.DB 6 | import play.api.Play.current 7 | 8 | object FavouriteStudioDAO { 9 | def create(favourite: FavouriteStudio) = { 10 | DB.withConnection { implicit c => 11 | SQL( 12 | """ 13 | | INSERT IGNORE INTO `favouriteStudio` (`userId`, `studioId`) 14 | | VALUES 15 | | ({userId}, {studioId}); 16 | """.stripMargin).on( 17 | "userId" -> favourite.userId, 18 | "studioId" -> favourite.studioId 19 | ).executeInsert() 20 | } 21 | } 22 | 23 | def delete(favourite: FavouriteStudio) = { 24 | DB.withConnection { implicit c => 25 | SQL( 26 | """ 27 | | DELETE FROM `favouriteStudio` 28 | | WHERE `userId`={userId} AND `studioId`={studioId} 29 | | LIMIT 1; 30 | """.stripMargin).on( 31 | "userId" -> favourite.userId, 32 | "studioId" -> favourite.studioId 33 | ).executeUpdate() 34 | } 35 | } 36 | 37 | def exists(favourite: FavouriteStudio): Boolean = { 38 | DB.withConnection { implicit c => 39 | val result = SQL( 40 | """ 41 | | SELECT COUNT(*) as numMatches 42 | | FROM `favouriteStudio` 43 | | WHERE `userId`={userId} AND `studioId`={studioId}; 44 | """.stripMargin).on( 45 | "userId" -> favourite.userId, 46 | "studioId" -> favourite.studioId 47 | ).apply().head 48 | 49 | result[Int]("numMatches") != 0 50 | } 51 | } 52 | 53 | def index(userId: Int): List[FavouriteStudio] = { 54 | DB.withConnection { implicit c => 55 | val results = SQL( 56 | """ 57 | | SELECT `userId`, `studioId` 58 | | FROM `favouriteStudio` 59 | | WHERE `userId`={userId}; 60 | """.stripMargin).on( 61 | "userId" -> userId 62 | ).apply() 63 | 64 | results.map { row => 65 | FavouriteStudio(row[Int]("userId"), row[Int]("studioId")) 66 | }.force.toList 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /chapter4/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @main("Welcome to Play") { 4 | 5 | @play20.welcome(message) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /chapter4/app/views/main.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | 7 | @title 8 | 9 | 10 | 11 | 12 | 13 | @content 14 | 15 | 16 | -------------------------------------------------------------------------------- /chapter4/build.sbt: -------------------------------------------------------------------------------- 1 | name := """chapter4-demo""" 2 | 3 | version := "1.0" 4 | 5 | lazy val root = (project in file(".")).enablePlugins(PlayScala) 6 | 7 | doc in Compile <<= target.map(_ / "none") 8 | 9 | scalaVersion := "2.11.1" 10 | 11 | libraryDependencies ++= Seq( 12 | jdbc, 13 | anorm, 14 | cache, 15 | ws, 16 | "mysql" % "mysql-connector-java" % "5.1.18" 17 | ) 18 | -------------------------------------------------------------------------------- /chapter4/conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # 8 | # This must be changed for production, but we recommend not changing it in this file. 9 | # 10 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. 11 | application.secret="/u9PM[cJc=h>y=xGcV1W?<58iGchSJM7wXnfZ::qFTS" 12 | 13 | # The application languages 14 | # ~~~~~ 15 | application.langs="en" 16 | 17 | # Global object class 18 | # ~~~~~ 19 | # Define the Global object class for this application. 20 | # Default to Global in the root package. 21 | # application.global=Global 22 | 23 | # Router 24 | # ~~~~~ 25 | # Define the Router object to use for this application. 26 | # This router will be looked up first when the application is starting up, 27 | # so make sure this is the entry point. 28 | # Furthermore, it's assumed your route file is named properly. 29 | # So for an application router like `my.application.Router`, 30 | # you may need to define a router file `conf/my.application.routes`. 31 | # Default to Routes in the root package (and conf/routes) 32 | # application.router=my.application.Routes 33 | 34 | # Database configuration 35 | # ~~~~~ 36 | # You can declare as many datasources as you want. 37 | # By convention, the default datasource is named `default` 38 | # 39 | db.default.driver= com.mysql.jdbc.Driver 40 | db.default.url="jdbc:mysql://127.0.0.1:3306/favourites-svc" 41 | db.default.user="favourites-svc" 42 | db.default.password="1e0OZH5a9PCJ0Anj11ia0Wmy" 43 | 44 | # Evolutions 45 | # ~~~~~ 46 | # You can disable evolutions if needed 47 | # evolutionplugin=disabled 48 | 49 | # Logger 50 | # ~~~~~ 51 | # You can also configure logback (http://logback.qos.ch/), 52 | # by providing an application-logger.xml file in the conf directory. 53 | 54 | # Root logger: 55 | logger.root=ERROR 56 | 57 | # Logger used by the framework: 58 | logger.play=INFO 59 | 60 | # Logger provided to your application: 61 | logger.application=DEBUG 62 | 63 | -------------------------------------------------------------------------------- /chapter4/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # API Endpoints 6 | POST /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.add(userId: Int, studioId: Int) 7 | DELETE /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.remove(userId: Int, studioId: Int) 8 | GET /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.find(userId: Int, studioId: Int) 9 | GET /users/:userId/favouriteStudios controllers.FavouriteStudios.findAll(userId: Int) 10 | 11 | # Home page 12 | GET / controllers.Application.index 13 | 14 | # Map static resources from the /public folder to the /assets URL path 15 | GET /assets/*file controllers.Assets.at(path="/public", file) 16 | -------------------------------------------------------------------------------- /chapter4/init_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Clean out dist 4 | rm -rf target/universal/* 5 | 6 | # Compile distributable 7 | sbt dist 8 | 9 | # Unzip components 10 | pushd target/universal 11 | unzip chapter4-demo-1.0.zip 12 | popd 13 | -------------------------------------------------------------------------------- /chapter4/project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Sat Feb 21 15:26:55 PST 2015 3 | template.uuid=d2d450ff-ffb2-4c93-9354-c3b15fc933ea 4 | sbt.version=0.13.5 5 | -------------------------------------------------------------------------------- /chapter4/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 2 | 3 | // The Play plugin 4 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8") 5 | 6 | // web plugins 7 | 8 | addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") 9 | 10 | addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.0") 11 | 12 | addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.1") 13 | 14 | addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.1") 15 | 16 | addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.0.0") 17 | 18 | addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.0.0") 19 | -------------------------------------------------------------------------------- /chapter4/public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter4/public/images/favicon.png -------------------------------------------------------------------------------- /chapter4/public/javascripts/hello.js: -------------------------------------------------------------------------------- 1 | if (window.console) { 2 | console.log("Welcome to your Play application's JavaScript!"); 3 | } -------------------------------------------------------------------------------- /chapter4/public/stylesheets/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter4/public/stylesheets/main.css -------------------------------------------------------------------------------- /chapter4/run_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Run 4 | pushd target/universal/chapter4-demo-1.0/bin 5 | ./chapter4-demo 6 | popd 7 | -------------------------------------------------------------------------------- /chapter4/test/ApplicationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * Add your spec here. 10 | * You can mock out a whole application including requests, plugins etc. 11 | * For more information, consult the wiki. 12 | */ 13 | @RunWith(classOf[JUnitRunner]) 14 | class ApplicationSpec extends Specification { 15 | 16 | "Application" should { 17 | 18 | "send 404 on a bad request" in new WithApplication{ 19 | route(FakeRequest(GET, "/boum")) must beNone 20 | } 21 | 22 | "render the index page" in new WithApplication{ 23 | val home = route(FakeRequest(GET, "/")).get 24 | 25 | status(home) must equalTo(OK) 26 | contentType(home) must beSome.which(_ == "text/html") 27 | contentAsString(home) must contain ("Your new application is ready.") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter4/test/IntegrationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * add your integration spec here. 10 | * An integration test will fire up a whole play application in a real (or headless) browser 11 | */ 12 | @RunWith(classOf[JUnitRunner]) 13 | class IntegrationSpec extends Specification { 14 | 15 | "Application" should { 16 | 17 | "work from within a browser" in new WithBrowser { 18 | 19 | browser.goTo("http://localhost:" + port) 20 | 21 | browser.pageSource must contain("Your new application is ready.") 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter5/.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | project/target 4 | target 5 | tmp 6 | .history 7 | dist 8 | /.idea 9 | /*.iml 10 | /out 11 | /.idea_modules 12 | /.classpath 13 | /.project 14 | /RUNNING_PID 15 | /.settings 16 | -------------------------------------------------------------------------------- /chapter5/LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 8 | language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /chapter5/README: -------------------------------------------------------------------------------- 1 | This is your new Play application 2 | ================================= 3 | 4 | This file will be packaged with your application, when using `activator dist`. 5 | -------------------------------------------------------------------------------- /chapter5/activator-launch-1.2.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter5/activator-launch-1.2.12.jar -------------------------------------------------------------------------------- /chapter5/activator.bat: -------------------------------------------------------------------------------- 1 | @REM activator launcher script 2 | @REM 3 | @REM Envioronment: 4 | @REM JAVA_HOME - location of a JDK home dir (optional if java on path) 5 | @REM CFG_OPTS - JVM options (optional) 6 | @REM Configuration: 7 | @REM activatorconfig.txt found in the ACTIVATOR_HOME or ACTIVATOR_HOME/ACTIVATOR_VERSION 8 | @setlocal enabledelayedexpansion 9 | 10 | @echo off 11 | 12 | set "var1=%~1" 13 | if defined var1 ( 14 | if "%var1%"=="help" ( 15 | echo. 16 | echo Usage activator [options] [command] 17 | echo. 18 | echo Commands: 19 | echo ui Start the Activator UI 20 | echo new [name] [template-id] Create a new project with [name] using template [template-id] 21 | echo list-templates Print all available template names 22 | echo help Print this message 23 | echo. 24 | echo Options: 25 | echo -jvm-debug [port] Turn on JVM debugging, open at the given port. Defaults to 9999 if no port given. 26 | echo. 27 | echo Environment variables ^(read from context^): 28 | echo JAVA_OPTS Environment variable, if unset uses "" 29 | echo SBT_OPTS Environment variable, if unset uses "" 30 | echo ACTIVATOR_OPTS Environment variable, if unset uses "" 31 | echo. 32 | goto :end 33 | ) 34 | ) 35 | 36 | if "%ACTIVATOR_HOME%"=="" ( 37 | set "ACTIVATOR_HOME=%~dp0" 38 | @REM remove trailing "\" from path 39 | set ACTIVATOR_HOME=!ACTIVATOR_HOME:~0,-1! 40 | ) 41 | 42 | set ERROR_CODE=0 43 | set APP_VERSION=1.2.12 44 | set ACTIVATOR_LAUNCH_JAR=activator-launch-%APP_VERSION%.jar 45 | 46 | rem Detect if we were double clicked, although theoretically A user could 47 | rem manually run cmd /c 48 | for %%x in (%cmdcmdline%) do if %%~x==/c set DOUBLECLICKED=1 49 | 50 | rem FIRST we load a config file of extra options (if there is one) 51 | set "CFG_FILE_HOME=%UserProfile%\.activator\activatorconfig.txt" 52 | set "CFG_FILE_VERSION=%UserProfile%\.activator\%APP_VERSION%\activatorconfig.txt" 53 | set CFG_OPTS= 54 | if exist %CFG_FILE_VERSION% ( 55 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_VERSION%") DO ( 56 | set DO_NOT_REUSE_ME=%%i 57 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 58 | rem CFG_OPTS, otherwise it remains "" for this loop. 59 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 60 | ) 61 | ) 62 | if "%CFG_OPTS%"=="" ( 63 | if exist %CFG_FILE_HOME% ( 64 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_HOME%") DO ( 65 | set DO_NOT_REUSE_ME=%%i 66 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 67 | rem CFG_OPTS, otherwise it remains "" for this loop. 68 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 69 | ) 70 | ) 71 | ) 72 | 73 | rem We use the value of the JAVACMD environment variable if defined 74 | set _JAVACMD=%JAVACMD% 75 | 76 | if "%_JAVACMD%"=="" ( 77 | if not "%JAVA_HOME%"=="" ( 78 | if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe" 79 | 80 | rem if there is a java home set we make sure it is the first picked up when invoking 'java' 81 | SET "PATH=%JAVA_HOME%\bin;%PATH%" 82 | ) 83 | ) 84 | 85 | if "%_JAVACMD%"=="" set _JAVACMD=java 86 | 87 | rem Detect if this java is ok to use. 88 | for /F %%j in ('"%_JAVACMD%" -version 2^>^&1') do ( 89 | if %%~j==Java set JAVAINSTALLED=1 90 | ) 91 | 92 | rem Detect the same thing about javac 93 | if "%_JAVACCMD%"=="" ( 94 | if not "%JAVA_HOME%"=="" ( 95 | if exist "%JAVA_HOME%\bin\javac.exe" set "_JAVACCMD=%JAVA_HOME%\bin\javac.exe" 96 | ) 97 | ) 98 | if "%_JAVACCMD%"=="" set _JAVACCMD=javac 99 | for /F %%j in ('"%_JAVACCMD%" -version 2^>^&1') do ( 100 | if %%~j==javac set JAVACINSTALLED=1 101 | ) 102 | 103 | rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style 104 | set JAVAOK=true 105 | if not defined JAVAINSTALLED set JAVAOK=false 106 | if not defined JAVACINSTALLED set JAVAOK=false 107 | 108 | if "%JAVAOK%"=="false" ( 109 | echo. 110 | echo A Java JDK is not installed or can't be found. 111 | if not "%JAVA_HOME%"=="" ( 112 | echo JAVA_HOME = "%JAVA_HOME%" 113 | ) 114 | echo. 115 | echo Please go to 116 | echo http://www.oracle.com/technetwork/java/javase/downloads/index.html 117 | echo and download a valid Java JDK and install before running Activator. 118 | echo. 119 | echo If you think this message is in error, please check 120 | echo your environment variables to see if "java.exe" and "javac.exe" are 121 | echo available via JAVA_HOME or PATH. 122 | echo. 123 | if defined DOUBLECLICKED pause 124 | exit /B 1 125 | ) 126 | 127 | rem Check what Java version is being used to determine what memory options to use 128 | for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( 129 | set JAVA_VERSION=%%g 130 | ) 131 | 132 | rem Strips away the " characters 133 | set JAVA_VERSION=%JAVA_VERSION:"=% 134 | 135 | rem TODO Check if there are existing mem settings in JAVA_OPTS/CFG_OPTS and use those instead of the below 136 | for /f "delims=. tokens=1-3" %%v in ("%JAVA_VERSION%") do ( 137 | set MAJOR=%%v 138 | set MINOR=%%w 139 | set BUILD=%%x 140 | 141 | set META_SIZE=-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=256M 142 | if "!MINOR!" LSS "8" ( 143 | set META_SIZE=-XX:PermSize=64M -XX:MaxPermSize=256M 144 | ) 145 | 146 | set MEM_OPTS=!META_SIZE! 147 | ) 148 | 149 | rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config. 150 | set _JAVA_OPTS=%JAVA_OPTS% 151 | if "%_JAVA_OPTS%"=="" set _JAVA_OPTS=%CFG_OPTS% 152 | 153 | set DEBUG_OPTS= 154 | 155 | rem Loop through the arguments, building remaining args in args variable 156 | set args= 157 | :argsloop 158 | if not "%~1"=="" ( 159 | rem Checks if the argument contains "-D" and if true, adds argument 1 with 2 and puts an equal sign between them. 160 | rem This is done since batch considers "=" to be a delimiter so we need to circumvent this behavior with a small hack. 161 | set arg1=%~1 162 | if "!arg1:~0,2!"=="-D" ( 163 | set "args=%args% "%~1"="%~2"" 164 | shift 165 | shift 166 | goto argsloop 167 | ) 168 | 169 | if "%~1"=="-jvm-debug" ( 170 | if not "%~2"=="" ( 171 | rem This piece of magic somehow checks that an argument is a number 172 | for /F "delims=0123456789" %%i in ("%~2") do ( 173 | set var="%%i" 174 | ) 175 | if defined var ( 176 | rem Not a number, assume no argument given and default to 9999 177 | set JPDA_PORT=9999 178 | ) else ( 179 | rem Port was given, shift arguments 180 | set JPDA_PORT=%~2 181 | shift 182 | ) 183 | ) else ( 184 | set JPDA_PORT=9999 185 | ) 186 | shift 187 | 188 | set DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=!JPDA_PORT! 189 | goto argsloop 190 | ) 191 | rem else 192 | set "args=%args% "%~1"" 193 | shift 194 | goto argsloop 195 | ) 196 | 197 | :run 198 | 199 | if "!args!"=="" ( 200 | if defined DOUBLECLICKED ( 201 | set CMDS="ui" 202 | ) else set CMDS=!args! 203 | ) else set CMDS=!args! 204 | 205 | rem We add a / in front, so we get file:///C: instead of file://C: 206 | rem Java considers the later a UNC path. 207 | rem We also attempt a solid effort at making it URI friendly. 208 | rem We don't even bother with UNC paths. 209 | set JAVA_FRIENDLY_HOME_1=/!ACTIVATOR_HOME:\=/! 210 | set JAVA_FRIENDLY_HOME=/!JAVA_FRIENDLY_HOME_1: =%%20! 211 | 212 | rem Checks if the command contains spaces to know if it should be wrapped in quotes or not 213 | set NON_SPACED_CMD=%_JAVACMD: =% 214 | if "%_JAVACMD%"=="%NON_SPACED_CMD%" %_JAVACMD% %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 215 | if NOT "%_JAVACMD%"=="%NON_SPACED_CMD%" "%_JAVACMD%" %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 216 | 217 | if ERRORLEVEL 1 goto error 218 | goto end 219 | 220 | :error 221 | set ERROR_CODE=1 222 | 223 | :end 224 | 225 | @endlocal 226 | 227 | exit /B %ERROR_CODE% 228 | -------------------------------------------------------------------------------- /chapter5/app/actors/NotificationActor.scala: -------------------------------------------------------------------------------- 1 | package actors 2 | 3 | import akka.actor.{Props, Actor} 4 | import models.{FavouriteNotification, Friend, FavouriteStudio} 5 | 6 | import scala.concurrent.ExecutionContext.Implicits.global 7 | 8 | object NotificationActor { 9 | def props: Props = Props(new NotificationActor) 10 | } 11 | 12 | class NotificationActor extends Actor { 13 | def receive = { 14 | case favourite: FavouriteStudio => 15 | notifyFriendsOfFavourite(favourite) 16 | } 17 | 18 | private def notifyFriendsOfFavourite(favourite: FavouriteStudio): Unit = { 19 | // Lookup friends 20 | val fFriends = Friend.findAllFriends(favourite.userId) 21 | 22 | // Send a push notification to each friend 23 | for ( 24 | friends <- fFriends; 25 | friend <- friends; 26 | notification = FavouriteNotification(friend, favourite) 27 | ) { 28 | notification.send() 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /chapter5/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api._ 4 | import play.api.mvc._ 5 | 6 | object Application extends Controller { 7 | 8 | def index = Action { 9 | Ok(views.html.index("Your new application is ready.")) 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /chapter5/app/controllers/FavouriteStudios.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import models.FavouriteStudio 4 | import models.FavouriteStudio._ 5 | import play.api.cache.{Cached, Cache} 6 | import play.api.libs.json.Json 7 | import play.api.mvc._ 8 | import play.api.Play.current 9 | 10 | object FavouriteStudios extends Controller { 11 | private def clearCaches(userId: Int, studioId: Int) = 12 | List( 13 | "find_"+userId+"_"+studioId, 14 | "findAll_"+userId 15 | ).map { key => 16 | Cache.remove(key) 17 | } 18 | 19 | def add(userId: Int, studioId: Int) = Action { 20 | val favourite = FavouriteStudio.addFavourite(userId, studioId) 21 | 22 | clearCaches(userId, studioId) 23 | 24 | Ok(Json.obj("result" -> favourite)) 25 | } 26 | 27 | def remove(userId: Int, studioId: Int) = Action { 28 | FavouriteStudio.delete(userId, studioId) 29 | 30 | clearCaches(userId, studioId) 31 | 32 | Ok(Json.obj("result" -> Json.obj())) 33 | } 34 | 35 | def find(userId: Int, studioId: Int) = Cached("find_"+userId+"_"+studioId) { 36 | Action { 37 | val oFavourite = FavouriteStudio.find(userId, studioId) 38 | 39 | oFavourite match { 40 | case None => NotFound(Json.obj("error" -> "NOT_FOUND")) 41 | case Some(favourite) => Ok(Json.obj("result" -> favourite)) 42 | } 43 | } 44 | } 45 | 46 | def findAll(userId: Int) = Cached("findAll_"+userId) { 47 | Action { 48 | val allFavourites = FavouriteStudio.findAllByUser(userId) 49 | 50 | Ok(Json.obj("result" -> allFavourites)) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /chapter5/app/models/FavouriteNotification.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | case class FavouriteNotification(recipient: Friend, favourite: FavouriteStudio) { 4 | def send(): Unit = { 5 | // TODO: Send a notification to Amazon SNS 6 | println("SENDING PUSH NOTIFICATION TO: "+recipient.friendId) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chapter5/app/models/FavouriteStudio.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import actors.NotificationActor 4 | import models.dao.FavouriteStudioDAO 5 | import play.api.libs.concurrent.Akka 6 | import play.api.libs.functional.syntax._ 7 | import play.api.libs.json._ 8 | 9 | import play.api.Play.current 10 | 11 | object FavouriteStudio { 12 | val notificationActor = Akka.system.actorOf(NotificationActor.props) 13 | 14 | implicit val favouriteStudioWrites: Writes[FavouriteStudio] = ( 15 | (JsPath \ "userId").write[Int] and 16 | (JsPath \ "studioId").write[Int] 17 | )(unlift(FavouriteStudio.unapply)) 18 | 19 | def addFavourite(userId: Int, studioId: Int): FavouriteStudio = { 20 | val favourite = FavouriteStudio(userId, studioId) 21 | 22 | FavouriteStudioDAO.create(favourite) 23 | 24 | notificationActor ! favourite 25 | 26 | favourite 27 | } 28 | 29 | def delete(userId: Int, studioId: Int) = 30 | FavouriteStudioDAO.delete(FavouriteStudio(userId, studioId)) 31 | 32 | def findAllByUser(userId: Int): List[FavouriteStudio] = 33 | FavouriteStudioDAO.index(userId) 34 | 35 | def find(userId: Int, studioId: Int): Option[FavouriteStudio] = { 36 | val favourite = FavouriteStudio(userId, studioId) 37 | 38 | if (FavouriteStudioDAO.exists(favourite)) 39 | Some(favourite) 40 | else 41 | None 42 | } 43 | } 44 | 45 | case class FavouriteStudio(userId: Int, studioId: Int) 46 | -------------------------------------------------------------------------------- /chapter5/app/models/Friend.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import models.dao.FriendDAO 4 | 5 | import scala.concurrent._ 6 | 7 | object Friend { 8 | def findAllFriends(userId: Int)(implicit ec: ExecutionContext): Future[Set[Friend]] = 9 | FriendDAO.index(userId).map(_.toSet) 10 | } 11 | 12 | case class Friend(friendId: Int) 13 | -------------------------------------------------------------------------------- /chapter5/app/models/dao/FavouriteStudioDAO.scala: -------------------------------------------------------------------------------- 1 | package models.dao 2 | 3 | import anorm._ 4 | import models.FavouriteStudio 5 | import play.api.db.DB 6 | import play.api.Play.current 7 | 8 | object FavouriteStudioDAO { 9 | def create(favourite: FavouriteStudio) = { 10 | DB.withConnection { implicit c => 11 | SQL( 12 | """ 13 | | INSERT IGNORE INTO `favouriteStudio` (`userId`, `studioId`) 14 | | VALUES 15 | | ({userId}, {studioId}); 16 | """.stripMargin).on( 17 | "userId" -> favourite.userId, 18 | "studioId" -> favourite.studioId 19 | ).executeInsert() 20 | } 21 | } 22 | 23 | def delete(favourite: FavouriteStudio) = { 24 | DB.withConnection { implicit c => 25 | SQL( 26 | """ 27 | | DELETE FROM `favouriteStudio` 28 | | WHERE `userId`={userId} AND `studioId`={studioId} 29 | | LIMIT 1; 30 | """.stripMargin).on( 31 | "userId" -> favourite.userId, 32 | "studioId" -> favourite.studioId 33 | ).executeUpdate() 34 | } 35 | } 36 | 37 | def exists(favourite: FavouriteStudio): Boolean = { 38 | DB.withConnection { implicit c => 39 | val result = SQL( 40 | """ 41 | | SELECT COUNT(*) as numMatches 42 | | FROM `favouriteStudio` 43 | | WHERE `userId`={userId} AND `studioId`={studioId}; 44 | """.stripMargin).on( 45 | "userId" -> favourite.userId, 46 | "studioId" -> favourite.studioId 47 | ).apply().head 48 | 49 | result[Int]("numMatches") != 0 50 | } 51 | } 52 | 53 | def index(userId: Int): List[FavouriteStudio] = { 54 | DB.withConnection { implicit c => 55 | val results = SQL( 56 | """ 57 | | SELECT `userId`, `studioId` 58 | | FROM `favouriteStudio` 59 | | WHERE `userId`={userId}; 60 | """.stripMargin).on( 61 | "userId" -> userId 62 | ).apply() 63 | 64 | results.map { row => 65 | FavouriteStudio(row[Int]("userId"), row[Int]("studioId")) 66 | }.force.toList 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /chapter5/app/models/dao/FriendDAO.scala: -------------------------------------------------------------------------------- 1 | package models.dao 2 | 3 | import models.Friend 4 | import play.api.Play 5 | import play.api.Play.current 6 | import play.api.libs.ws._ 7 | 8 | import scala.concurrent._ 9 | 10 | object FriendDAO { 11 | val friendsServiceUrl = Play.current.configuration.getString("service.backend.url").get + "/friends" 12 | 13 | /** 14 | * Fetches a list of friends associated with the given user ID 15 | * @param userId The ID of the user to find friends of 16 | * @return A list of friends associated with the specified user 17 | */ 18 | def index(userId: Int)(implicit ec: ExecutionContext): Future[List[Friend]] = { 19 | val holder: WSRequestHolder = WS.url(friendsServiceUrl) 20 | .withQueryString("userId" -> userId.toString) 21 | 22 | // Execute the web service request and get back a future of a response 23 | val fResponse = holder.get() 24 | 25 | fResponse.map { response => 26 | // The response will be JSON so parse out the list of friends' IDs 27 | val friendIds: List[Int] = (response.json \ "result" \\ "friendId").map(_.as[Int]).toList 28 | 29 | // Map the list of IDs to a list of friends 30 | friendIds.map(Friend.apply) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter5/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @main("Welcome to Play") { 4 | 5 | @play20.welcome(message) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /chapter5/app/views/main.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | 7 | @title 8 | 9 | 10 | 11 | 12 | 13 | @content 14 | 15 | 16 | -------------------------------------------------------------------------------- /chapter5/build.sbt: -------------------------------------------------------------------------------- 1 | name := """chapter5-demo""" 2 | 3 | version := "1.0" 4 | 5 | lazy val root = (project in file(".")).enablePlugins(PlayScala) 6 | 7 | doc in Compile <<= target.map(_ / "none") 8 | 9 | scalaVersion := "2.11.1" 10 | 11 | libraryDependencies ++= Seq( 12 | jdbc, 13 | anorm, 14 | cache, 15 | ws, 16 | "mysql" % "mysql-connector-java" % "5.1.18" 17 | ) 18 | -------------------------------------------------------------------------------- /chapter5/conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # 8 | # This must be changed for production, but we recommend not changing it in this file. 9 | # 10 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. 11 | application.secret="/u9PM[cJc=h>y=xGcV1W?<58iGchSJM7wXnfZ::qFTS" 12 | 13 | # The application languages 14 | # ~~~~~ 15 | application.langs="en" 16 | 17 | # Global object class 18 | # ~~~~~ 19 | # Define the Global object class for this application. 20 | # Default to Global in the root package. 21 | # application.global=Global 22 | 23 | # Router 24 | # ~~~~~ 25 | # Define the Router object to use for this application. 26 | # This router will be looked up first when the application is starting up, 27 | # so make sure this is the entry point. 28 | # Furthermore, it's assumed your route file is named properly. 29 | # So for an application router like `my.application.Router`, 30 | # you may need to define a router file `conf/my.application.routes`. 31 | # Default to Routes in the root package (and conf/routes) 32 | # application.router=my.application.Routes 33 | 34 | # Database configuration 35 | # ~~~~~ 36 | # You can declare as many datasources as you want. 37 | # By convention, the default datasource is named `default` 38 | # 39 | db.default.driver= com.mysql.jdbc.Driver 40 | db.default.url="jdbc:mysql://127.0.0.1:3306/favourites-svc" 41 | db.default.user="favourites-svc" 42 | db.default.password="1e0OZH5a9PCJ0Anj11ia0Wmy" 43 | 44 | # Evolutions 45 | # ~~~~~ 46 | # You can disable evolutions if needed 47 | # evolutionplugin=disabled 48 | 49 | # Logger 50 | # ~~~~~ 51 | # You can also configure logback (http://logback.qos.ch/), 52 | # by providing an application-logger.xml file in the conf directory. 53 | 54 | # Root logger: 55 | logger.root=ERROR 56 | 57 | # Logger used by the framework: 58 | logger.play=INFO 59 | 60 | # Logger provided to your application: 61 | logger.application=DEBUG 62 | 63 | service.backend.url="http://localhost:9001" 64 | 65 | -------------------------------------------------------------------------------- /chapter5/conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # API Endpoints 6 | POST /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.add(userId: Int, studioId: Int) 7 | DELETE /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.remove(userId: Int, studioId: Int) 8 | GET /users/:userId/favouriteStudios/:studioId controllers.FavouriteStudios.find(userId: Int, studioId: Int) 9 | GET /users/:userId/favouriteStudios controllers.FavouriteStudios.findAll(userId: Int) 10 | 11 | # Home page 12 | GET / controllers.Application.index 13 | 14 | # Map static resources from the /public folder to the /assets URL path 15 | GET /assets/*file controllers.Assets.at(path="/public", file) 16 | -------------------------------------------------------------------------------- /chapter5/init_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Clean out dist 4 | rm -rf target/universal/* 5 | 6 | # Compile distributable 7 | sbt dist 8 | 9 | # Unzip components 10 | pushd target/universal 11 | unzip chapter5-demo-1.0.zip 12 | popd 13 | -------------------------------------------------------------------------------- /chapter5/project/build.properties: -------------------------------------------------------------------------------- 1 | #Activator-generated Properties 2 | #Sat Feb 21 15:26:55 PST 2015 3 | template.uuid=d2d450ff-ffb2-4c93-9354-c3b15fc933ea 4 | sbt.version=0.13.5 5 | -------------------------------------------------------------------------------- /chapter5/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 2 | 3 | // The Play plugin 4 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.8") 5 | 6 | // web plugins 7 | 8 | addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0") 9 | 10 | addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.0") 11 | 12 | addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.1") 13 | 14 | addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.1") 15 | 16 | addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.0.0") 17 | 18 | addSbtPlugin("com.typesafe.sbt" % "sbt-mocha" % "1.0.0") 19 | -------------------------------------------------------------------------------- /chapter5/public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter5/public/images/favicon.png -------------------------------------------------------------------------------- /chapter5/public/javascripts/hello.js: -------------------------------------------------------------------------------- 1 | if (window.console) { 2 | console.log("Welcome to your Play application's JavaScript!"); 3 | } -------------------------------------------------------------------------------- /chapter5/public/stylesheets/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/chapter5/public/stylesheets/main.css -------------------------------------------------------------------------------- /chapter5/run_precompiled.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Run 4 | pushd target/universal/chapter5-demo-1.0/bin 5 | ./chapter5-demo 6 | popd 7 | -------------------------------------------------------------------------------- /chapter5/test/ApplicationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * Add your spec here. 10 | * You can mock out a whole application including requests, plugins etc. 11 | * For more information, consult the wiki. 12 | */ 13 | @RunWith(classOf[JUnitRunner]) 14 | class ApplicationSpec extends Specification { 15 | 16 | "Application" should { 17 | 18 | "send 404 on a bad request" in new WithApplication{ 19 | route(FakeRequest(GET, "/boum")) must beNone 20 | } 21 | 22 | "render the index page" in new WithApplication{ 23 | val home = route(FakeRequest(GET, "/")).get 24 | 25 | status(home) must equalTo(OK) 26 | contentType(home) must beSome.which(_ == "text/html") 27 | contentAsString(home) must contain ("Your new application is ready.") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter5/test/IntegrationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * add your integration spec here. 10 | * An integration test will fire up a whole play application in a real (or headless) browser 11 | */ 12 | @RunWith(classOf[JUnitRunner]) 13 | class IntegrationSpec extends Specification { 14 | 15 | "Application" should { 16 | 17 | "work from within a browser" in new WithBrowser { 18 | 19 | browser.goTo("http://localhost:" + port) 20 | 21 | browser.pageSource must contain("Your new application is ready.") 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demo-script.txt: -------------------------------------------------------------------------------- 1 | # Chapter 0 2 | (In browser) http://localhost:9000 3 | 4 | # Chapter 1 5 | curl -X POST http://localhost:9000/users/198374913/favouriteStudios/8621349 6 | 7 | # Chapter 2 8 | curl -X POST http://localhost:9000/users/234985987/favouriteStudios/9174714 9 | 10 | # Chapter 2.1 11 | curl -X POST http://localhost:9000/users/234985987/favouriteStudios/1283479 12 | curl http://localhost:9000/users/234985987/favouriteStudios 13 | curl -v http://localhost:9000/users/234985987/favouriteStudios/9174714 14 | curl -v -X DELETE http://localhost:9000/users/234985987/favouriteStudios/9174714 15 | curl -v http://localhost:9000/users/234985987/favouriteStudios/9174714 16 | 17 | # Chapter 3 18 | curl -X POST http://localhost:9000/users/234985987/favouriteStudios/3097147 19 | time curl http://localhost:9000/users/234985987/favouriteStudios 20 | time curl http://localhost:9000/users/234985987/favouriteStudios 21 | time curl http://localhost:9000/users/234985987/favouriteStudios/3097147 22 | time curl http://localhost:9000/users/234985987/favouriteStudios/3097147 23 | 24 | # Chapter 4 25 | No demo possible atm... 26 | 27 | # Chapter 5 28 | curl -X POST http://localhost:9000/users/234985987/favouriteStudios/5297402 29 | 30 | -------------------------------------------------------------------------------- /friend-service/.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | project/target 4 | target 5 | tmp 6 | .history 7 | dist 8 | /.idea 9 | /*.iml 10 | /out 11 | /.idea_modules 12 | /.classpath 13 | /.project 14 | /RUNNING_PID 15 | /.settings 16 | -------------------------------------------------------------------------------- /friend-service/LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed under the Apache 2 license, quoted below. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with 4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 5 | 6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific 8 | language governing permissions and limitations under the License. -------------------------------------------------------------------------------- /friend-service/README: -------------------------------------------------------------------------------- 1 | This is your new Play application 2 | ================================= 3 | 4 | This file will be packaged with your application, when using `activator dist`. 5 | -------------------------------------------------------------------------------- /friend-service/activator-launch-1.2.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobyjsullivan/applicative-demo/764fc6edfb7482153eeee7dfc89eb5a8b5de17a4/friend-service/activator-launch-1.2.12.jar -------------------------------------------------------------------------------- /friend-service/activator.bat: -------------------------------------------------------------------------------- 1 | @REM activator launcher script 2 | @REM 3 | @REM Envioronment: 4 | @REM JAVA_HOME - location of a JDK home dir (optional if java on path) 5 | @REM CFG_OPTS - JVM options (optional) 6 | @REM Configuration: 7 | @REM activatorconfig.txt found in the ACTIVATOR_HOME or ACTIVATOR_HOME/ACTIVATOR_VERSION 8 | @setlocal enabledelayedexpansion 9 | 10 | @echo off 11 | 12 | set "var1=%~1" 13 | if defined var1 ( 14 | if "%var1%"=="help" ( 15 | echo. 16 | echo Usage activator [options] [command] 17 | echo. 18 | echo Commands: 19 | echo ui Start the Activator UI 20 | echo new [name] [template-id] Create a new project with [name] using template [template-id] 21 | echo list-templates Print all available template names 22 | echo help Print this message 23 | echo. 24 | echo Options: 25 | echo -jvm-debug [port] Turn on JVM debugging, open at the given port. Defaults to 9999 if no port given. 26 | echo. 27 | echo Environment variables ^(read from context^): 28 | echo JAVA_OPTS Environment variable, if unset uses "" 29 | echo SBT_OPTS Environment variable, if unset uses "" 30 | echo ACTIVATOR_OPTS Environment variable, if unset uses "" 31 | echo. 32 | goto :end 33 | ) 34 | ) 35 | 36 | if "%ACTIVATOR_HOME%"=="" ( 37 | set "ACTIVATOR_HOME=%~dp0" 38 | @REM remove trailing "\" from path 39 | set ACTIVATOR_HOME=!ACTIVATOR_HOME:~0,-1! 40 | ) 41 | 42 | set ERROR_CODE=0 43 | set APP_VERSION=1.2.12 44 | set ACTIVATOR_LAUNCH_JAR=activator-launch-%APP_VERSION%.jar 45 | 46 | rem Detect if we were double clicked, although theoretically A user could 47 | rem manually run cmd /c 48 | for %%x in (%cmdcmdline%) do if %%~x==/c set DOUBLECLICKED=1 49 | 50 | rem FIRST we load a config file of extra options (if there is one) 51 | set "CFG_FILE_HOME=%UserProfile%\.activator\activatorconfig.txt" 52 | set "CFG_FILE_VERSION=%UserProfile%\.activator\%APP_VERSION%\activatorconfig.txt" 53 | set CFG_OPTS= 54 | if exist %CFG_FILE_VERSION% ( 55 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_VERSION%") DO ( 56 | set DO_NOT_REUSE_ME=%%i 57 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 58 | rem CFG_OPTS, otherwise it remains "" for this loop. 59 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 60 | ) 61 | ) 62 | if "%CFG_OPTS%"=="" ( 63 | if exist %CFG_FILE_HOME% ( 64 | FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%CFG_FILE_HOME%") DO ( 65 | set DO_NOT_REUSE_ME=%%i 66 | rem ZOMG (Part #2) WE use !! here to delay the expansion of 67 | rem CFG_OPTS, otherwise it remains "" for this loop. 68 | set CFG_OPTS=!CFG_OPTS! !DO_NOT_REUSE_ME! 69 | ) 70 | ) 71 | ) 72 | 73 | rem We use the value of the JAVACMD environment variable if defined 74 | set _JAVACMD=%JAVACMD% 75 | 76 | if "%_JAVACMD%"=="" ( 77 | if not "%JAVA_HOME%"=="" ( 78 | if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe" 79 | 80 | rem if there is a java home set we make sure it is the first picked up when invoking 'java' 81 | SET "PATH=%JAVA_HOME%\bin;%PATH%" 82 | ) 83 | ) 84 | 85 | if "%_JAVACMD%"=="" set _JAVACMD=java 86 | 87 | rem Detect if this java is ok to use. 88 | for /F %%j in ('"%_JAVACMD%" -version 2^>^&1') do ( 89 | if %%~j==Java set JAVAINSTALLED=1 90 | ) 91 | 92 | rem Detect the same thing about javac 93 | if "%_JAVACCMD%"=="" ( 94 | if not "%JAVA_HOME%"=="" ( 95 | if exist "%JAVA_HOME%\bin\javac.exe" set "_JAVACCMD=%JAVA_HOME%\bin\javac.exe" 96 | ) 97 | ) 98 | if "%_JAVACCMD%"=="" set _JAVACCMD=javac 99 | for /F %%j in ('"%_JAVACCMD%" -version 2^>^&1') do ( 100 | if %%~j==javac set JAVACINSTALLED=1 101 | ) 102 | 103 | rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style 104 | set JAVAOK=true 105 | if not defined JAVAINSTALLED set JAVAOK=false 106 | if not defined JAVACINSTALLED set JAVAOK=false 107 | 108 | if "%JAVAOK%"=="false" ( 109 | echo. 110 | echo A Java JDK is not installed or can't be found. 111 | if not "%JAVA_HOME%"=="" ( 112 | echo JAVA_HOME = "%JAVA_HOME%" 113 | ) 114 | echo. 115 | echo Please go to 116 | echo http://www.oracle.com/technetwork/java/javase/downloads/index.html 117 | echo and download a valid Java JDK and install before running Activator. 118 | echo. 119 | echo If you think this message is in error, please check 120 | echo your environment variables to see if "java.exe" and "javac.exe" are 121 | echo available via JAVA_HOME or PATH. 122 | echo. 123 | if defined DOUBLECLICKED pause 124 | exit /B 1 125 | ) 126 | 127 | rem Check what Java version is being used to determine what memory options to use 128 | for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( 129 | set JAVA_VERSION=%%g 130 | ) 131 | 132 | rem Strips away the " characters 133 | set JAVA_VERSION=%JAVA_VERSION:"=% 134 | 135 | rem TODO Check if there are existing mem settings in JAVA_OPTS/CFG_OPTS and use those instead of the below 136 | for /f "delims=. tokens=1-3" %%v in ("%JAVA_VERSION%") do ( 137 | set MAJOR=%%v 138 | set MINOR=%%w 139 | set BUILD=%%x 140 | 141 | set META_SIZE=-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=256M 142 | if "!MINOR!" LSS "8" ( 143 | set META_SIZE=-XX:PermSize=64M -XX:MaxPermSize=256M 144 | ) 145 | 146 | set MEM_OPTS=!META_SIZE! 147 | ) 148 | 149 | rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config. 150 | set _JAVA_OPTS=%JAVA_OPTS% 151 | if "%_JAVA_OPTS%"=="" set _JAVA_OPTS=%CFG_OPTS% 152 | 153 | set DEBUG_OPTS= 154 | 155 | rem Loop through the arguments, building remaining args in args variable 156 | set args= 157 | :argsloop 158 | if not "%~1"=="" ( 159 | rem Checks if the argument contains "-D" and if true, adds argument 1 with 2 and puts an equal sign between them. 160 | rem This is done since batch considers "=" to be a delimiter so we need to circumvent this behavior with a small hack. 161 | set arg1=%~1 162 | if "!arg1:~0,2!"=="-D" ( 163 | set "args=%args% "%~1"="%~2"" 164 | shift 165 | shift 166 | goto argsloop 167 | ) 168 | 169 | if "%~1"=="-jvm-debug" ( 170 | if not "%~2"=="" ( 171 | rem This piece of magic somehow checks that an argument is a number 172 | for /F "delims=0123456789" %%i in ("%~2") do ( 173 | set var="%%i" 174 | ) 175 | if defined var ( 176 | rem Not a number, assume no argument given and default to 9999 177 | set JPDA_PORT=9999 178 | ) else ( 179 | rem Port was given, shift arguments 180 | set JPDA_PORT=%~2 181 | shift 182 | ) 183 | ) else ( 184 | set JPDA_PORT=9999 185 | ) 186 | shift 187 | 188 | set DEBUG_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=!JPDA_PORT! 189 | goto argsloop 190 | ) 191 | rem else 192 | set "args=%args% "%~1"" 193 | shift 194 | goto argsloop 195 | ) 196 | 197 | :run 198 | 199 | if "!args!"=="" ( 200 | if defined DOUBLECLICKED ( 201 | set CMDS="ui" 202 | ) else set CMDS=!args! 203 | ) else set CMDS=!args! 204 | 205 | rem We add a / in front, so we get file:///C: instead of file://C: 206 | rem Java considers the later a UNC path. 207 | rem We also attempt a solid effort at making it URI friendly. 208 | rem We don't even bother with UNC paths. 209 | set JAVA_FRIENDLY_HOME_1=/!ACTIVATOR_HOME:\=/! 210 | set JAVA_FRIENDLY_HOME=/!JAVA_FRIENDLY_HOME_1: =%%20! 211 | 212 | rem Checks if the command contains spaces to know if it should be wrapped in quotes or not 213 | set NON_SPACED_CMD=%_JAVACMD: =% 214 | if "%_JAVACMD%"=="%NON_SPACED_CMD%" %_JAVACMD% %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 215 | if NOT "%_JAVACMD%"=="%NON_SPACED_CMD%" "%_JAVACMD%" %DEBUG_OPTS% %MEM_OPTS% %ACTIVATOR_OPTS% %SBT_OPTS% %_JAVA_OPTS% "-Dactivator.home=%JAVA_FRIENDLY_HOME%" -jar "%ACTIVATOR_HOME%\%ACTIVATOR_LAUNCH_JAR%" %CMDS% 216 | 217 | if ERRORLEVEL 1 goto error 218 | goto end 219 | 220 | :error 221 | set ERROR_CODE=1 222 | 223 | :end 224 | 225 | @endlocal 226 | 227 | exit /B %ERROR_CODE% 228 | -------------------------------------------------------------------------------- /friend-service/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api._ 4 | import play.api.mvc._ 5 | 6 | object Application extends Controller { 7 | 8 | def index = Action { 9 | Ok(views.html.index("Your new application is ready.")) 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /friend-service/app/controllers/Friends.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api.libs.json.{JsArray, Json} 4 | import play.api.mvc.{Action, Controller} 5 | 6 | import scala.concurrent.Future 7 | import scala.concurrent.ExecutionContext.Implicits.global 8 | 9 | object Friends extends Controller { 10 | def index(userId: Int) = Action.async { 11 | val friendIds = Seq( 12 | userId + 319380, 13 | userId - 24389, 14 | userId + 13502, 15 | userId + 19374, 16 | userId - 51093 17 | ) 18 | 19 | val jsFriendIds = friendIds.map(friendId => Json.obj("friendId" -> friendId)) 20 | Future { 21 | // Artificial latency for demo purposes 22 | Thread.sleep(3000L) 23 | 24 | Ok(Json.obj( 25 | "result" -> JsArray(jsFriendIds) 26 | )) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /friend-service/app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(message: String) 2 | 3 | @main("Welcome to Play") { 4 | 5 | @play20.welcome(message) 6 | 7 | } 8 | -------------------------------------------------------------------------------- /friend-service/app/views/main.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | 7 | @title 8 | 9 | 10 | 11 | 12 | 13 | @content 14 | 15 | 16 | -------------------------------------------------------------------------------- /friend-service/build.sbt: -------------------------------------------------------------------------------- 1 | name := """friend-service""" 2 | 3 | version := "1.0-SNAPSHOT" 4 | 5 | lazy val root = (project in file(".")).enablePlugins(PlayScala) 6 | 7 | scalaVersion := "2.11.1" 8 | 9 | libraryDependencies ++= Seq( 10 | jdbc, 11 | anorm, 12 | cache, 13 | ws 14 | ) 15 | -------------------------------------------------------------------------------- /friend-service/conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # 8 | # This must be changed for production, but we recommend not changing it in this file. 9 | # 10 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. 11 | application.secret="UM0gvJjOt=