Created
April 16, 2015 04:26
-
-
Save dylemma/0f9a7c55a10dcd5c5be4 to your computer and use it in GitHub Desktop.
Example of an implicit SessionManager implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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