Created
May 31, 2012 16:01
-
-
Save tinoadams/2844392 to your computer and use it in GitHub Desktop.
"Emulating" C# using keyword in Scala
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
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