version 1.2.0

@seratch seratch released this Jul 22, 2014

Skinny Framework 1.2.0 is out.

APIs are basically compatible. If you don't have some extensions that depend on LocaleFeature, RequestScopeFeature and SkinnySessionFilter methods, your app will work fine without changing your code.

http://skinny-framework.org/

Topics

Angular Server Side Support

angularjs

By using AngularXHRServerFeature trait, your controllers will be Angular.js friendly JSON API provider.

 class ArticlesController extends SkinnyApiResource with AngularXHRServerFeature {
   // Enable Angular's XSRF protection
   protectFromForgery()

   // JSON request body will be merged into Scalatra params automatically
   // ...

 }

And your ngResource is like this:

 app.factory('Article', ['$resource', function($resource) {
   return $resource('/api/articles/:id.json', {}, {});
 }]);

See in detail here:

http://skinny-framework.org/documentation/angular.html

FutureOpsFeature

Scalatra's org.scalatra.DynamicScope has thread-local HttpServletRequest object and it is implicitly accessed everywhere whenever you use Scalatra DSLs.

So working with Scalatra DSLs within Future blocks is not easy because we must avoid accessing implicit thread-local request because accessing it from other threads causes NullPointerException.

Now we provide FutureOpsFeature trait and the trait is out-of-the-box when you use SkinnyController. This trait provides futureWithRequest and awaitFutures blocks.

import service._
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import javax.servlet.http.HttpServletRequest

class DashboardController extends ApplicationController {

  val adminUserService = new AdminUserService
  val accessService = new AccessLogService
  val alertService = new AlertService
  val ops = DashboardOps(this)

  def index = {
    val scope: scala.collection.concurrent.Map[String, Any] = requestScope()

    awaitFutures(5.seconds)(
      // simply define operation inside of this controller
      futureWithRequest { implicit req =>
        //set("hourlyStats", accessService.getHourlyStatsForGraph(new LocalDate))
        // [error] example/src/main/scala/controller/DashboardController.scala:43: ambiguous implicit values:
        // [error]  both value req of type javax.servlet.http.HttpServletRequest
        // [error]  and method request in class DashboardController of type => javax.servlet.http.HttpServletRequest

        set("hourlyStats", accessService.getHourlyStatsForGraph(new LocalDate))(req)
      },

      // separate operation to outside of this controller
      futureWithRequest(req => ops.setCurrentUser(req)),

      // just use Future directly
      Future {
        // When using Future directly, you must be aware of Scalatra's DynamicScope's thread local request.
        // In this case, you cannot use request here. requestScope, session and so on 
        // should be captured outside of this Future block.
        scope.update("alerts", alertService.findAll())
      }
    )
    render("/dashboard/index")
  }

  private[controller] def currentUserId(implicit req: HttpServletRequest): Option[Long] = session(req).getAs[Long]("userId")
}

case class DashboardOps(controller: DashboardController) {
  def setCurrentUser(implicit req: HttpServletRequest) = {
    // implicit request is not ambiguous here
    val userId = controller.currentUserId.getOrElse(controller.halt(401))
    controller.set("currentUser" -> controller.adminUserService.getCurrentUser(userId))
  }
}

See in detail here:

http://skinny-framework.org/documentation/controller-and-routes.html#awaiting-several-futures-within-action-method

Changes

  • [framework] Smooth async support (FutureOpsFeature)
  • [framework] LocaleFeature, RequestScopeFeature and SkinnySessionFilter API changes to accept implicit request param
  • [framework] RequestScopeFeature#requestScopeA is deprecated
  • [framework] Fix SkinnySessionInitializer's possible NPE on Jetty
  • [framework] Fix haltWithBody for SkinnyApiController ignore status param
  • [framework] update/delete API responds as HTML when resource not found
  • [framework] Seamless Angular Server Side API support
  • [framework] JSONParamsAutoBinderFeature to merge JSON request body to params