Статьи

Представляем Accord: нормальная библиотека валидации для Scala

Accord — это библиотека валидации Scala с открытым исходным кодом (лицензированная Apache), разработанная в Wix. Он размещен на GitHub, и вы можете раскошелиться на него; Поделитесь с нами вашими мыслями!

Почему другая валидационная библиотека

Когда мы переходили с Java на Scala, мы начали сталкиваться с существующими библиотеками проверки, а именно JSR 303 и Spring Validation. Несмотря на то, что для Scala написано несколько платформ валидации, в частности скалаза с его функциями валидации, после оценки мы остались недовольны и в итоге разработали собственную. Если вам интересно, есть больше предыстории и сравнений с существующими фреймворками в вики проекта на GitHub.

Так как же это выглядит?

Валидатор типа определяется путем предоставления набора правил проверки через DSL:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
import com.wix.accord.dsl._    // Import the validator DSL
 
case class Person( firstName: String, lastName: String )
case class Classroom( teacher: Person, students: Seq[ Person ] )
 
implicit val personValidator = validator[ Person ] { p =>
  p.firstName is notEmpty                   // The expression being validated is resolved automatically, see below
  p.lastName as "last name" is notEmpty     // You can also explicitly describe the expression being validated
}
 
implicit val classValidator = validator[ Classroom ] { c =>
  c.teacher is valid        // Implicitly relies on personValidator!
  c.students.each is valid
  c.students have size > 0
}

Затем вы можете выполнить валидаторы свободно и получить результат обратно. Результат ошибки включает в себя соответствующие нарушения:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
scala> val validPerson = Person( "Wernher", "von Braun" )
validPerson: Person = Person(Wernher,von Braun)
 
scala> validate( validPerson )
res0: com.wix.accord.Result = Success
 
scala> val invalidPerson = Person( "", "No First Name" )
invalidPerson: Person = Person(,No First Name)
 
scala> validate( invalidPerson )
res1: com.wix.accord.Result = Failure(List(RuleViolation(,must not be empty,firstName)))
 
scala> val explicitDescription = Person( "No Last Name", "" )
explicitDescription: Person = Person(No Last Name,)
 
scala> validate( explicitDescription )
res2: com.wix.accord.Result = Failure(List(RuleViolation(,must not be empty,last name)))
 
scala> val invalidClassroom = Classroom( Person( "Alfred", "Aho" ), Seq.empty )
invalidClassroom: Classroom = Classroom(Person(Alfred,Aho),List())
 
scala> validate( invalidClassroom )
res3: com.wix.accord.Result = Failure(List(RuleViolation(List(),has size 0, expected more than 0,students)))

Цели дизайна

Аккорд был разработан для удовлетворения четырех основных целей проектирования:

  • Минималистичный : обеспечить минимальную функциональность, необходимую для решения проблемной области. Любая расширенная функциональность поставляется в отдельном модуле и удовлетворяет тем же целям проектирования.
  • Простой : Обеспечьте очень простой и тонкий API для всех четырех категорий сайтов вызовов (определение валидатора, определение комбинатора, выполнение валидатора и обработка результатов).
  • Автономный : Уменьшите или полностью исключите внешние зависимости, где это возможно.
  • Интегрированность : предоставление расширений для интеграции с общими библиотеками и использование простых точек интеграции, где это возможно.

Первый промежуточный выпуск (0.1) уже включает существенный набор комбинаторов (терминология Accord для дискретных правил валидации, например, IsEmpty или IsNotNull), краткий DSL для определения валидаторов, совпадения результатов для ScalaTest и Specs² и средства интеграции для Spring Validation .

Синтаксис Accord специально разработан, чтобы избежать пользовательских строк в API (включая scala.Symbol s). На практике это означает, что он не использует отражение во время выполнения и, кроме того, может автоматически генерировать описания проверяемых выражений. В приведенном выше примере вы можете видеть, что RuleViolations могут включать как неявные (как в firstName ), так и явные (как в lastName ) описания; эта функция позволяет чрезвычайно краткие правила проверки без ущерба для читаемости возникающих нарушений.