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 2cd805c553..22e0c03ce0 100644 --- a/core/common/src/main/scala/net/liftweb/common/Box.scala +++ b/core/common/src/main/scala/net/liftweb/common/Box.scala @@ -55,29 +55,43 @@ object Box extends BoxTrait { **/ implicit class ListOfBoxes[T](val theListOfBoxes: List[Box[T]]) extends AnyVal { /** - * Convert a List of Boxes into a single Box containting a List[T], where T is - * the parameterized type of the Boxes. + * Convert a `List` of `Box`es into a single `Box` containting a `List[T]`, + * where `T` is the parameterized type of the `Box`es. * - * 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. + * 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. + * If all of the `Box`es 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 `Box`es contain a `Failure`, a + * `ParamFailure` is returned, containing the original `List[Box[T]]` as + * the param. The `ParamFailure` itself is chained to a `Failure` chain + * containing all of the `Failure` boxes in the list. * - * 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. + * 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. + * @return A `Full[List[T]]` if no `Failure`s were present. `ParamFailure[List[Box[T]]]` otherwise. **/ def toSingleBox(failureErrorMessage: String): Box[List[T]] = { if (theListOfBoxes.exists(_.isInstanceOf[Failure])) { - Failure(failureErrorMessage) ~> theListOfBoxes + val failureChain = + theListOfBoxes.collect { + case fail: Failure => fail + }.reduceRight { (topmostFailure, latestFailure) => + topmostFailure.copy(chain = Full(latestFailure)) + } + + ParamFailure( + failureErrorMessage, + Empty, + Full(failureChain), + theListOfBoxes + ) } else { Full(theListOfBoxes.flatten) } 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 4d60eb25cf..3ea237dc0a 100644 --- a/core/common/src/test/scala/net/liftweb/common/BoxSpec.scala +++ b/core/common/src/test/scala/net/liftweb/common/BoxSpec.scala @@ -365,7 +365,26 @@ class BoxSpec extends Specification with ScalaCheck with BoxGenerator { 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) + singleBox must beLike { + case ParamFailure(message, _, _, _) => + message must_== "This should be in the param failure." + } + } + + "chain the ParamFailure to the failures in the list when any are Failure" in { + val someBoxes: List[Box[String]] = List(Full("bacon"), Failure("I HATE BACON"), Full("sammich"), Failure("MORE BACON FAIL"), Failure("BACON WHY U BACON")) + + val singleBox = someBoxes.toSingleBox("Failure.") + + val expectedChain = + Failure("I HATE BACON", Empty, + Full(Failure("MORE BACON FAIL", Empty, + Full(Failure("BACON WHY U BACON"))))) + + singleBox must beLike { + case ParamFailure(_, _, chain, _) => + chain must_== Full(expectedChain) + } } }