diff --git a/core/common/src/main/scala/net/liftweb/common/Box.scala b/core/common/src/main/scala/net/liftweb/common/Box.scala index fc09f0ebfa..908e6cb7ef 100644 --- a/core/common/src/main/scala/net/liftweb/common/Box.scala +++ b/core/common/src/main/scala/net/liftweb/common/Box.scala @@ -21,6 +21,41 @@ import scala.reflect.Manifest import java.util.{Iterator => JavaIterator, ArrayList => JavaArrayList} +/** + * Helper class to provide an easy way for converting Lists of Boxes[T] into + * a Box of List[T]. +**/ +case class ListOfBoxes[T](theListOfBoxes: List[Box[T]]) { + /** + * Convert a List of Boxes into a single Box containting a List[T], where T is + * the parameterized type of the Boxes. + * + * This method is useful for those cases where you have a lot of operations being + * executed that all return some Box[T]. You want just a List[T] if all of those + * operations succeeded, but you don't want to have Failures disappear if any were + * present in the list. + * + * If all of the Boxes in the List are Full or Empty, we return a Full box containing + * a List of all of the Full Box values that were present. If any of the Boxes contain + * a Failure, a ParamFailure is returned, containing the original List[Box[T]] as the + * param. + * + * It is worth noting that the size of the list in the resulting Box[List[T]] may not be equal + * to the size of the List[Box[T]] that is fed as Empty values will disappear altogether in the + * conversion. + * + * @param failureErrorMessage The string that should be placed in the message for the Failure. + * @return A Full[List[T]] if no Failures were present. ParamFailure[List[Box[T]]] otherwise. + **/ + def toSingleBox(failureErrorMessage: String): Box[List[T]] = { + if (theListOfBoxes.exists(_.isInstanceOf[Failure])) { + Failure(failureErrorMessage) ~> theListOfBoxes + } else { + Full(theListOfBoxes.flatten) + } + } +} + /** * The bridge from Java to Scala Box */ @@ -46,7 +81,9 @@ class BoxJBridge { * * It also provides implicit methods to transform Option to Box, Box to Iterable, and Box to Option */ -object Box extends BoxTrait +object Box extends BoxTrait { + implicit def listToListOfBoxes[T](boxes: List[Box[T]]) = ListOfBoxes(boxes) +} /** * The Box companion object provides methods to create a Box from: diff --git a/core/common/src/test/scala/net/liftweb/common/BoxSpec.scala b/core/common/src/test/scala/net/liftweb/common/BoxSpec.scala index 76735b6702..d71cc1f63d 100644 --- a/core/common/src/test/scala/net/liftweb/common/BoxSpec.scala +++ b/core/common/src/test/scala/net/liftweb/common/BoxSpec.scala @@ -351,6 +351,29 @@ class BoxSpec extends Specification with ScalaCheck with BoxGenerator { } } + "A List[Box[T]]" should { + "be convertable to a Box[List[T]] when all are Full" in { + val someBoxes: List[Box[String]] = List(Full("bacon"), Full("sammich")) + val singleBox = someBoxes.toSingleBox("Box failed!") + + singleBox must_== Full(List("bacon", "sammich")) + } + + "be convertable to a Box[List[T]] when some are Full and some are Empty" in { + val someBoxes: List[Box[String]] = List(Full("bacon"), Full("sammich"), Empty) + val singleBox = someBoxes.toSingleBox("Box failed!") + + singleBox must_== Full(List("bacon", "sammich")) + } + + "be convertable to a ParamFailure[Box[List[T]]] when any are Failure" in { + val someBoxes: List[Box[String]] = List(Full("bacon"), Full("sammich"), Failure("I HATE BACON")) + val singleBox = someBoxes.toSingleBox("This should be in the param failure.") + + singleBox must_== ParamFailure("This should be in the param failure.", None, None, someBoxes) + } + } + }