All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalactic.TrySugar.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2001-2013 Artima, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.scalactic

import scala.util.Try
import scala.util.Failure
import annotation.tailrec
import exceptions.ValidationFailedException

/**
 * Trait providing an implicit class that adds a toOr method to
 * Try, which converts Success to Good,
 * and Failure to Bad, as well as a validating method,
 * which takes one or more validation functions and returns either the
 * same Try if either the Try had already failed or its value
 * passes all the functions, or [[org.scalactic.exceptions.ValidationFailedException `ValidationFailedException`]] containing an error message
 * describing the first validation that failed.
 *
 * 

* Here's an example validation method, which passes if the given Int is evenly * divisible by 10 (i.e., the result will be [[org.scalactic.Pass Pass]]). If the value does not pass * this test, the result is a [[org.scalactic.Fail Fail]] containing a helpful error message string. *

* *
 * scala> import org.scalactic._
 * import org.scalactic._
 *
 * scala> import TrySugar._
 * import TrySugar._
 *
 * scala> import scala.util.Try
 * import scala.util.Try
 *
 * scala> def isRound(i: Int): Validation[ErrorMessage] =
 *      |   if (i % 10 == 0) Pass else Fail(i + " was not a round number")
 * isRound: (i: Int)org.scalactic.Validation[org.scalactic.ErrorMessage]
 * 
* *

* Validation will be attempted on a successful Try. If the validation succeeds, the * resulting Try will be the same successful Try with the same value. (A * "validation" only transforms the Try if the validation fails, otherwise it is the * same Try. The only difference is its value has now been proven valid.) * In the following example, a successful Try[Int] with the value 100 * passes the validation (which checks whether 100 is evenly divisible by 10), therefore * the result of the validating call is the same successful Try * with the same value. *

* *
 * scala> val try100 = Try(100)
 * try100: scala.util.Try[Int] = Success(100)
 *
 * scala> val round100 = try100.validating(isRound)
 * round100: scala.util.Try[Int] = Success(100)
 * 
* *

* If validation fails, the successful Try will be transformed into a failed one, with * a ValidationFailedException that contains the error message * returned by the validation function. In the following example, 42 fails the validation because it * is not evenly divisible by 10: *

* *
 * scala> val try42 = Try(42)
 * try42: scala.util.Try[Int] = Success(42)
 *
 * scala> val round42 = try42.validating(isRound)
 * round42: scala.util.Try[Int] = Failure(org.scalactic.exceptions.ValidationFailedException: 42 was not a round number)
 * 
* *

* If validating is called on a failed Try, it just returns the same failed Try: *

* *
 * scala> val tryEx = Try[Int] { throw new Exception("oops!") }
 * tryEx: scala.util.Try[Int] = Failure(java.lang.Exception: oops!)
 *
 * scala> val roundEx = tryEx.validating(isRound)
 * roundEx: scala.util.Try[Int] = Failure(java.lang.Exception: oops!)
 * 
* *

* The validating method accepts one or more validation functions. If you * pass more than one, they will be tried in order up until the first failure, whose * error message will appear in the ValidationFailedException. In other words, * validating will short circuit at the first error and return that. It * will not accumulate errors. For example, the following validation will short circuit * after the isDivBy3 function fails: *

* *
 * scala> def isDivBy3(i: Int): Validation[ErrorMessage] =
 *      |   if (i % 3 == 0) Pass else Fail(i + " was not divisible by 3")
 * isDivBy3: (i: Int)org.scalactic.Validation[org.scalactic.ErrorMessage]
 *
 * scala> def isAnswerToLifeTheUniverseAndEverything(i: Int): Validation[ErrorMessage] =
 *      |   if (i == 42) Pass else Fail(i + " did not equal 42")
 * isAnswerToLifeTheUniverseAndEverything: (i: Int)org.scalactic.Validation[org.scalactic.ErrorMessage]
 *
 * scala> try100.validating(isRound, isDivBy3, isAnswerToLifeTheUniverseAndEverything)
 * res0: scala.util.Try[Int] = Failure(org.scalactic.exceptions.ValidationFailedException: 100 was not divisible by 3)
 * 
* *

* Here are some examples of the toOr method: *

* *
 * scala> try100.toOr
 * res1: org.scalactic.Or[Int,Throwable] = Good(100)
 *
 * scala> tryEx.toOr
 * res2: org.scalactic.Or[Int,Throwable] = Bad(java.lang.Exception: oops!)
 * 
*/ trait TrySugar { /** * Implicit class that adds a toOr method to * Try, which converts Success to Good, * and Failure to Bad, as well as a * validation method, which takes one or more functions that validate * the Future's value. * *

* See the main documentation for trait [[org.scalactic.TrySugar `TrySugar`]] for more detail and examples. *

* * @param theTry the Try to which to add toOr and validating methods. */ implicit class Tryizer[T](theTry: Try[T]) { /** * Converts a Try to an Or, with * Success becoming Good and * Failure becoming Bad. */ def toOr: T Or Throwable = Or.from(theTry) /** * Validates a Try using the passed validation functions. * *

* See the main documentation for trait [[org.scalactic.TrySugar `TrySugar`]] for more detail and examples. *

* * @param first the first validation function to apply * @param rest the subsequent validation functions to apply, if any * @return a "validated" Try, either a Try with the same value, or * if validation failed, a failed Try containing a ValidationFailedException. */ def validating(hd: T => Validation[ErrorMessage], tl: (T => Validation[ErrorMessage])*): Try[T] = { theTry.flatMap { (o: T) => TrySugar.passOrFirstFail(o, hd :: tl.toList) match { case Pass => theTry case Fail(errorMessage) => Failure(ValidationFailedException(errorMessage)) } } } } } /** * Companion object for TrySugar enabling its members to be * imported as an alternative to mixing them in. */ object TrySugar extends TrySugar { @tailrec private[scalactic] def passOrFirstFail[T, E](o: T, fs: List[T => Validation[E]]): Validation[E] = { fs match { case Nil => Pass case head :: tail => head(o) match { case Pass => passOrFirstFail(o, tail) case firstFail => firstFail } } } } /* import org.scalactic._ import TrySugar._ import scala.util.Try def isRound(i: Int): Validation[ErrorMessage] = if (i % 10 == 0) Pass else Fail(i + " was not a round number") val try100 = Try(100) val round100 = try100.validating(isRound) val try42 = Try(42) val round42 = try42.validating(isRound) val tryEx = Try[Int] { throw new Exception("oops!") } val roundEx = tryEx.validating(isRound) def isDivBy3(i: Int): Validation[ErrorMessage] = if (i % 3 == 0) Pass else Fail(i + " was not divisible by 3") def isAnswerToLifeTheUniverseAndEverything(i: Int): Validation[ErrorMessage] = if (i == 42) Pass else Fail(i + "did not equal 42") try100.validating(isRound, isDivBy3, isAnswerToLifeTheUniverseAndEverything) */




© 2015 - 2024 Weber Informatics LLC | Privacy Policy