Skip to content

Instantly share code, notes, and snippets.

@symbiont-eric-torreborre
Created August 2, 2019 06:45
Show Gist options
  • Save symbiont-eric-torreborre/7a94f513347a05c38981b7214d7c973d to your computer and use it in GitHub Desktop.
Save symbiont-eric-torreborre/7a94f513347a05c38981b7214d7c973d to your computer and use it in GitHub Desktop.
api for global resources in specs2
// This describe a more or less ideal API for global resources management
// This trait allows to insert setup/teardown actions guaranteed to execute only once
// and making sure that the teardown action will always happen at the end of the
// specification execution
trait GlobalBeforeAfter extends SpecificationStructure with FragmentsFactory {
def globalBefore: Fragment
def globalAfter: Fragment
override def map(fs: =>Fragments): Fragments = super.map(fs).prepend(
Seq(globalBefore.memoize, fragmentFactory.markAs(AlwaysTag))).
andFinally(globalAfter.memoize)
}
// Then a user can declare its own trait for accessing a specific resource
trait MyDatabase extends GlobalBeforeAfter {
lazy val connection: IO[Connection] = newConnection
def globalBefore =
step(connection.unsafePerformIO)
def globalAfter =
step(connection.unsafePerformIO.kill)
}
// and use it in several specifications
class MySpec extends Specification with MyDatabase { def is = s2"""
the database can be used across several specifications and only created once $createOnce
the database will be shutdown safely when the last specification has finished executing $terminateDatabase
"""
def createOnce = todo
def terminateDatabase = todo
}
// The 2 major difficulties are:
//
// 1. the memoization of actions across several specification runs.
//
// If this is too hard to do we can require the user to define an object for creating a resource
// then it will be shared across specifications once started
object MyDatabase {
lazy val connection: Connection = newConnection.unsafePerformIO
}
// and we can require the teardown function to succeed even if the connection has already been closed
def globalAfter =
step(if (connection.isOpen) connection.kill)
// 2. sbt executes specifications one after the other (they are defined as "Tasks").
// However the output of a task execution can be other tasks. So I think it is possible to use this
// mechanism to collect finalizers for each executed tasks and eventually execute all the finalizers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment