Skip to content

Instantly share code, notes, and snippets.

@tinoadams
Created May 31, 2012 16:01
Show Gist options
  • Save tinoadams/2844392 to your computer and use it in GitHub Desktop.
Save tinoadams/2844392 to your computer and use it in GitHub Desktop.
"Emulating" C# using keyword in Scala
package utils.using
import java.sql.Connection
import javax.persistence.EntityManager
/**
* Declares an object that can be used.
*/
trait Usable[+T] {
def use[R](body: (T) => R): R
}
/**
* Defines a generic container for objects that can be used and closed.
* When use is invoked it will execute the body and close the object afterwards.
*/
abstract case class Closeable[+T](content: T) extends Usable[T] {
override def use[R](body: (T) => R): R =
try {
body(content)
}
finally {
close
}
def close: Unit
}
/**
* Package object which provides the "using" method.
* Also contains some predefined implicit conversions for common types:
* - java.sql.Connection
* - java.io.Closeable
* - scala.io.BufferedSource
* - java.sql.PreparedStatement
* - javax.persistence.EntityManager
*/
object `package` {
/**
* Sets auto commit to false and executes the body.
* On successful execution the transaction is committed
* otherwise it will be rolled back.
* The connection is closed after use.
*/
implicit def SQLConnection2Useable[X <: Connection](instance: X) = new Usable[X] {
def use[R](body: (X) => R): R = {
try {
instance.setAutoCommit(false)
val result = body(instance)
instance.commit()
result
}
catch {
case e =>
instance.rollback()
throw e
}
finally {
instance.close
}
}
}
implicit def IOCloseable2Useable[X <: java.io.Closeable](instance: X) = new Closeable(instance) {
def close = instance.close
}
implicit def scalaioBufferedSource2Useable[X <: scala.io.BufferedSource](instance: X) = new Closeable(instance) {
def close = instance.close
}
implicit def javasqlPreparedStatement2Useable[X <: java.sql.PreparedStatement](instance: X) = new Closeable(instance) {
def close = instance.close
}
/**
* Starts a transaction or fails if it is already started.
* When the body is executed successfully the transaction is committed
* otherwise it will be rolled back.
* The entity manager is closed after use.
*/
implicit def javaxpersistenceEntityManager2Useable[X <: EntityManager](instance: X) = new Usable[X] {
def use[R](body: (X) => R): R = {
val tx = instance.getTransaction
if (tx.isActive())
throw new Exception("Transaction is active. Unable to nest transactions.")
try {
tx.begin()
val result = body(instance)
tx.commit()
result
}
catch {
case e =>
tx.rollback()
throw e
}
finally {
instance.close
}
}
}
/**
* Uses the useable with the given body.
*/
def using[W, T](useables: Usable[W])(body: (W) => T): T = useables.use(body)
/**
* Nested use of all given useables:
*
* <pre>
* val result: Int = using(u1,u2) {
* println("body")
* 123
* }
* </pre>
*
* Will effectively do:
*
* <pre>
* val result: Int = using(u1) {
* using(u2) {
* println("body")
* 123
* }
* }
* </pre>
*/
def using[T](useables: Usable[_]*)(body: => T): T = {
// the deepest (last element in useables) "use" method should invoke "body" - "body _" passes "body" without invoking it
val callChain = useables.foldRight(body _) {
case (useable, nestedCall) =>
// returns a function that calls the current usable "use" method with in turn executes the "nestedCall"
() => useable.use(_ => nestedCall())
}
// invoke the top function of the nested call chain
callChain()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment