Skip to content

Instantly share code, notes, and snippets.

@dylemma
Created April 16, 2015 04:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dylemma/0f9a7c55a10dcd5c5be4 to your computer and use it in GitHub Desktop.
Save dylemma/0f9a7c55a10dcd5c5be4 to your computer and use it in GitHub Desktop.
Example of an implicit SessionManager implementation
/*
* Mock version of a Database Session
*/
class Session {
println("opened session")
def close() = println("closed session")
}
/*
* Mimic Slick's `database.withSession` method
*/
object Database {
def withSession[T](f: Session => T) = {
val s = new Session
try { f(s) } finally { s.close() }
}
}
/*
* SessionManager trait, implementations, and companion
*/
trait SessionManager extends Any {
def withSession[T](f: Session => T): T
}
class ExistingSessionManager(val s: Session) extends AnyVal with SessionManager {
def withSession[T](f: Session => T) = f(s)
}
object GlobalSessionManager extends SessionManager {
def withSession[T](f: Session => T) = Database.withSession(f)
}
object SessionManager {
implicit def getSessionManager(implicit s: Session = null): SessionManager = {
if (s == null) GlobalSessionManager else new ExistingSessionManager(s)
}
}
case class User(name: String)
trait AccessLayer {
def getUser(name: String)(implicit sm: SessionManager): Option[User]
}
object DatabaseAccessLayer extends AccessLayer {
def getUser(name: String)(implicit sm: SessionManager): Option[User] = sm.withSession { implicit session =>
// pretend actual database queries happen here
Some(User(name))
}
}
class CachingAccessLayer(delegate: AccessLayer) extends AccessLayer {
val cache = collection.mutable.Map.empty[String, User]
def getUser(name: String)(implicit sm: SessionManager): Option[User] = {
// don't worry about thread safety, it's only an example!
cache.get(name) orElse {
val result = delegate getUser name
for (user <- result) cache(name) = user
result
}
}
}
class LoggingAccessLayer(delegate: AccessLayer) extends AccessLayer {
def getUser(name: String)(implicit sm: SessionManager): Option[User] = {
println(s"calling getUser($name)")
delegate getUser name
}
}
/* Output:
first lookup
calling getUser(dylemma)
opened session
closed session
second lookup
calling getUser(dylemma)
group lookup
opened session
calling getUser(alice)
calling getUser(bob)
calling getUser(carlos)
closed session
*/
object ExampleMain extends App {
val accessLayer = new LoggingAccessLayer(new CachingAccessLayer(DatabaseAccessLayer))
// cache miss: database opens a session
println("first lookup")
accessLayer.getUser("dylemma")
println
// cache hit: no session
println("second lookup")
accessLayer.getUser("dylemma")
println
// one session reused for each access
println("group lookup")
Database.withSession { implicit session =>
accessLayer.getUser("alice")
accessLayer.getUser("bob")
accessLayer.getUser("carlos")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment