2014-10-13 3 views
2

Я борюсь с использованием шаблона экстрактора в определенном прецеденте, где кажется, что он может быть очень мощным.scala extractor pattern для комплексной проверки, но с хорошим выходом ошибки

Я начинаю с ввода карты [String, String], исходящей из веб-запроса. Это либо searchRequest, либо countRequest для нашего api.

SearchRequest имеет ключи

  • запроса (требуется)
  • FromDate (факультативно-дефолт)
  • Todate (факультативно-дефолт)
  • nextToken (необязательный)
  • maxResults (факультативно-дефолтных)

countRequest имеет ключи

  • запроса (обязательно)
  • FromDate (факультативно-дефолт)
  • Todate (опция-дефолт)
  • ведра (опция-дефолт)

Тогда, я хочу, чтобы преобразовать как из них для такой структуры композиционного типа

protected case class CommonQueryRequest(
    originalQuery: String, 
    fromDate: DateTime, 
    toDate: DateTime 
) 

case class SearchQueryRequest(
    commonRequest: CommonQueryRequest, 
    maxResults: Int, 
    nextToken: Option[Long]) 

case class CountRequest(commonRequest: CommonQueryRequest, bucket: String) 

As yo U может видеть, я как бы превращаю строки в DateTimes и Int, Long и т. д. Моя проблема в том, что мне действительно нужны ошибки для недопустимого fromDate или недопустимого формата toDate против недопустимых значений maxResults или недопустимого следующего токена IF.

В то же время мне нужно придерживаться значений по умолчанию (которые зависят от того, является ли это поиском или запросом на подсчет).

Естественно, что с переданной картой вы можете указать поиск по счету, поэтому в первый раз при этом я добавил ключ = «тип» со значением поиска или подсчета, чтобы я мог совместить, по крайней мере, что.

Я даже иду по правильному пути? Я думал, что, возможно, использование совпадения может быть более чистым, чем наша существующая реализация, но, тем не менее, я иду по этому пути, кажется, становится немного уродливым.

спасибо, Дин

ответ

2

Я хотел бы предложить вам взглянуть на scalaz.Validation и ValidationNel. Это отличный способ собрать ошибки проверки, идеально подходящие для проверки запроса ввода.

Вы можете узнать больше о валидации здесь: http://eed3si9n.com/learning-scalaz/Validation.html. Однако в моем примере я использую scalaz 7.1, и он может немного отличаться от того, что описано в этой статье. Однако основная идея остается прежней.

Heres маленький пример для случая использования:

import java.util.NoSuchElementException 

    import org.joda.time.DateTime 
    import org.joda.time.format.DateTimeFormat 

    import scala.util.Try 

    import scalaz.ValidationNel 
    import scalaz.syntax.applicative._ 
    import scalaz.syntax.validation._ 

    type Input = Map[String, String] 
    type Error = String 

    case class CommonQueryRequest(originalQuery: String, 
           fromDate: DateTime, 
           toDate: DateTime) 

    case class SearchQueryRequest(commonRequest: CommonQueryRequest, 
           maxResults: Int, 
           nextToken: Option[Long]) 

    case class CountRequest(commonRequest: CommonQueryRequest, bucket: String) 

    def stringField(field: String)(input: Input): ValidationNel[Error, String] = 
    input.get(field) match { 
     case None => s"Field $field is not defined".failureNel 
     case Some(value) => value.successNel 
    } 


    val dateTimeFormat = DateTimeFormat.fullTime() 

    def dateTimeField(field: String)(input: Input): ValidationNel[Error, DateTime] = 
    Try(dateTimeFormat.parseDateTime(input(field))) recover { 
     case _: NoSuchElementException => DateTime.now() 
    } match { 
     case scala.util.Success(dt) => dt.successNel 
     case scala.util.Failure(err) => err.toString.failureNel 
    } 

    def intField(field: String)(input: Input): ValidationNel[Error, Int] = 
    Try(input(field).toInt) match { 
     case scala.util.Success(i) => i.successNel 
     case scala.util.Failure(err) => err.toString.failureNel 
    } 

    def countRequest(input: Input): ValidationNel[Error, CountRequest] = 
    (
     stringField ("query") (input) |@| 
     dateTimeField("fromDate")(input) |@| 
     dateTimeField("toDate") (input) |@| 
     stringField ("bucket") (input) 
    ) { (query, from, to, bucket) => 
     CountRequest(CommonQueryRequest(query, from, to), bucket) 
    } 


    val validCountReq = Map("query" -> "a", "bucket" -> "c") 
    val badCountReq = Map("fromDate" -> "invalid format", "bucket" -> "c") 

    println(countRequest(validCountReq)) 
    println(countRequest(badCountReq)) 
+0

+1 довольно хороший ответ, но сказаз почему-то не разрешен в наших библиотеках, хотя это дает мне, по крайней мере, хорошие идеи. –

+0

вы можете скопировать код проверки из scalaz в свой проект и притвориться, что это вовсе не скалаз. когда у вас будет 50% скаляз в вашем проекте, вы можете смело представить его как зависимость;) –

0

scalactic выглядит довольно круто, как хорошо, и я могу идти по этому пути (хотя и не уверен, что если мы можем использовать эту библиотеку или нет, но я думаю, что я просто продолжить работу вперед, пока кто-то не скажет «нет»).