Search code examples
scalaplayframeworkspecs2

Creating an anonymous class from an abstract class in Scala - desugaring code


Scala noob here.

I don't understand the following piece of code, taken from a Play application's integration test (Scala):

package workflows.admin

import play.api.test._

class SignInSpec extends PlaySpecification {

  "An activated user" should {
    "be able to sign in to the admin console" in new WithBrowser(webDriver = WebDriverFactory(FIREFOX)) {
      // some matchers here ...
      true
    }
  }
}

As far as I understand, the example creates a new anonymous class from the abstract class WithBrowser and instantiates it. The instance will receive the (named) constructor parameter webDriver.

The problem is that I don't understand what's going on here when looking into WithBrowser:

abstract class WithBrowser[WEBDRIVER <: WebDriver](
    val webDriver: WebDriver = WebDriverFactory(Helpers.HTMLUNIT),
    val app: Application = GuiceApplicationBuilder().build(),
    val port: Int = Helpers.testServerPort) extends Around with Scope {

  def this(
    webDriver: Class[WEBDRIVER],
    app: Application,
    port: Int) = this(WebDriverFactory(webDriver), app, port)

  implicit def implicitApp: Application = app
  implicit def implicitPort: Port = port

  lazy val browser: TestBrowser = TestBrowser(webDriver, Some("http://localhost:" + port))

  override def around[T: AsResult](t: => T): Result = {
    try {
      Helpers.running(TestServer(port, app))(AsResult.effectively(t))
    } finally {
      browser.quit()
    }
  }
}

I have two questions:

  1. WithBrowser is a generic abstract class with a type parameter WEBDRIVER, but the type parameter is not declared in the example. Instead, the anonymous class' instance receives this information using the named constructor parameter webDriver. What's the missing link between the type parameter and the constructor parameter ?
  2. The example declares a block of code (it just returns true) and this block of code is the test itself. But where does that code go in the anonymous class ? WithBrowser extends Around and overrides the around function, which executes the code block, but I don't understand how the generated anonymous class moves the given example code block into around.

Any help is highly appreciated.


Solution

    1. Scala can infer type parameters. Since none of the passed parameters' type is somehow related to the type parameter and would thereby restrict the type to be inferred, the scala compiler will just infer it as type Nothing.

    2. The code block would normally be the constructor of the class, but this is a special case. The Around class does extend the scala.DelayedInit interface. This will rewrite the code within the constructor to a call of the delayedInit function. The code that would actually be the constructor is passed as call-by-name parameter to this function. This value (actually wrapped in a org.specs2.execute.Result.resultOrSuccess call) is the parameter passed to the around function.

    Imagine the Around class like this:

    class Around extends DelayedInit {
      def delayedInit(f: => Unit): Unit = around(f)
      def around(f: => Unit): Unit
    }
    

    Let's say you will now inherit the around class:

    class Foo extends Around {
      println("Hello World")
    }
    

    The above gets rewritten to this:

    class Foo extends Around {
      delayedInit(println("Hello World"))
    }
    

    If my explanation was not clear enough or you want to know more implementation details:
    - Here is Around and the DelayedInit documentation.