All Projects → krzemin → Octopus

krzemin / Octopus

Licence: apache-2.0
Scala library for boilerplate-free validation

Programming Languages

scala
5932 projects
scalaz
18 projects

Projects that are alternatives of or similar to Octopus

Monkeytail
Macro derived validation for cats.data.Validated
Stars: ✭ 87 (-36.96%)
Mutual labels:  cats, validation
Express Typescript Boilerplate
A delightful way to building a RESTful API with NodeJs & TypeScript by @w3tecch
Stars: ✭ 2,293 (+1561.59%)
Mutual labels:  validation, boilerplate
Webpack Pug Scss Boilerplate
📂 Webpack 4 based boilerplate for building apps
Stars: ✭ 135 (-2.17%)
Mutual labels:  boilerplate
Redux React Starter
DEPRECATED use the new https://github.com/didierfranc/react-webpack-4
Stars: ✭ 137 (-0.72%)
Mutual labels:  boilerplate
Hugrid
Hugrid (Hugo+grid) is a simple grid theme for Hugo. It's a kind of boilerplate to perform anyone or anything quickly. Portfolio, collection, bookmarks, contacts and so on.
Stars: ✭ 136 (-1.45%)
Mutual labels:  boilerplate
Subzero Starter Kit
Starter Kit and tooling for authoring GraphQL/REST API backends with subZero
Stars: ✭ 136 (-1.45%)
Mutual labels:  boilerplate
Next Js Blog Boilerplate
🚀 Nextjs Blog Boilerplate is starter code for your blog based on Next framework. ⚡️ Made with Nextjs, TypeScript, ESLint, Prettier, PostCSS, Tailwind CSS.
Stars: ✭ 134 (-2.9%)
Mutual labels:  boilerplate
React Pages Boilerplate
Deliver react + react-router application to gh-pages
Stars: ✭ 134 (-2.9%)
Mutual labels:  boilerplate
Forgjs
ForgJs is a javascript lightweight object validator. Go check the Quick start section and start coding with love
Stars: ✭ 1,687 (+1122.46%)
Mutual labels:  validation
Framework
Strongly-typed JavaScript object with support for validation and error handling.
Stars: ✭ 136 (-1.45%)
Mutual labels:  validation
Generator Create Redux App
Add redux, emotion-js and other useful libraries like react-router in top of create-react-app
Stars: ✭ 137 (-0.72%)
Mutual labels:  boilerplate
Validatetor
Android library for fast and simple string validation
Stars: ✭ 136 (-1.45%)
Mutual labels:  validation
Pandasschema
A validation library for Pandas data frames using user-friendly schemas
Stars: ✭ 135 (-2.17%)
Mutual labels:  validation
Canoe
Functional Telegram Bot API for Scala
Stars: ✭ 137 (-0.72%)
Mutual labels:  cats
React Native Boilerplate
A React Native boilerplate with Expo, Redux, React Navigation, Styled Components and some 💕 included.
Stars: ✭ 135 (-2.17%)
Mutual labels:  boilerplate
Automatic Gatsbyjs App Landing Page
Automatic GatsbyJS App Landing Page - Automatically generate iOS app landing page using GatsbyJS
Stars: ✭ 137 (-0.72%)
Mutual labels:  boilerplate
Aura.filter
Validate and sanitize arrays and objects.
Stars: ✭ 134 (-2.9%)
Mutual labels:  validation
Vue Monorepo Boilerplate
Vue Fullstack App Monorepo Boilerplate
Stars: ✭ 136 (-1.45%)
Mutual labels:  boilerplate
Swift Project Template
Script to easily create an iOS project base code!
Stars: ✭ 136 (-1.45%)
Mutual labels:  boilerplate
Node Convict
Featureful configuration management library for Node.js
Stars: ✭ 1,855 (+1244.2%)
Mutual labels:  validation

Octopus

CI build Maven Central codecov.io License

Octopus is a Scala library for boilerplate-free validation.

It defines Validator[T] type-class, provide composable DSL for defining validation rules for user-defined type and can automatically derive validators for case classes, tuples, sealed hierarchies and various standard Scala types by composing other defined or derived validators.

Example

Let's consider example business domain.

case class UserId(id: Int) extends AnyVal

case class Email(address: String) extends AnyVal

case class PostalCode(code: String) extends AnyVal

case class Address(street: String,
                   postalCode: PostalCode,
                   city: String)

case class User(id: UserId,
                email: Email,
                address: Address)

Let's define validation rules as implicit type class instances.

// Usually you want to put them into companion objects
// or group them together in a module.

import octopus.dsl._

implicit val userIdValidator: Validator[UserId] = Validator[UserId]
  .rule(_.id > 0, "must be positive number")

implicit val emailValidator: Validator[Email] = Validator[Email]
  .rule(_.address.nonEmpty, "must not be empty")
  .rule(_.address.contains("@"), "must contain @")
  .rule(_.address.split('@').last.contains("."), "must contain . after @")

implicit val potalCodeValidator: Validator[PostalCode] = Validator[PostalCode]
  .ruleVC((_: String).length == 5, "must be of length 5")
  .ruleVC((_: String).forall(_.isDigit), "must contain only digits")

implicit val addressValidator: Validator[Address] = Validator
  .derived[Address] // derives default validator for Address
  .rule(_.city, (_: String).nonEmpty, "must not be empty")
  .rule(_.street, (_: String).nonEmpty, "must not be empty")

Then, having validator instances imported, we can validate our bigger case classes for free, without any additional boilerplate!

import octopus.syntax._

val user1 = User(
  UserId(1),
  Email("[email protected]"),
  Address("Love Street", PostalCode("12345"), "Los Angeles")
)

val user2 = User(
  UserId(0),
  Email("[email protected]"),
  Address("", PostalCode("qqqqqq"), "   ")
)

user1.isValid // : Boolean = true

user1.validate.toEither // : Either[octopus.ValidationError, User] = Right(user1)

user2.isValid // : Boolean = false

user2.validate.toFieldErrMapping
// : List[(String, String)] = List(
//     (id,must be positive number), 
//     (email,must contain . after @),
//     (address.postalCode,must be of length 5), 
//     (address.postalCode,must contain only digits),
//     (address.street,must not be empty)
// )

Getting started

Octopus is currently available for Scala 2.11, 2.12, 2.13 and Scala.js.

To get started with SBT, add following line to your build.sbt:

libraryDependencies += "com.github.krzemin" %% "octopus" % "0.4.1"

Or if you are using Scala.js:

libraryDependencies += "com.github.krzemin" %%% "octopus" % "0.4.1"

Integration with Cats / Scalaz

There are available additional modules that simplify integration with Cats and Scalaz validation types.

Cats

If you want to integrate with Cats, simply add following line to build.sbt:

libraryDependencies += "com.github.krzemin" %%% "octopus-cats" % "0.4.1"

Having this dependency on classpath, you can use

import octopus.syntax._
import octopus.cats._

user1.validate.toValidatedNel // : ValidatedNel[octopus.ValidationError, User] = Valid(user1)

user2.validate.toValidatedNel // : ValidatedNel[octopus.ValidationError, User] = Invalid(NonEmptyList(...))

See integration test suite for more information.

Scalaz

Alternatively, if you want similar integration with Scalaz, add following line to build.sbt:

libraryDependencies += "com.github.krzemin" %%% "octopus-scalaz" % "0.4.1"

See integration test suite for reference.

Asynchronous validators

Sometimes validation rules are more complex in sense that they can't be decided locally by only looking at object value, but they require some external context like querying service or database. Therefore, Octopus has support for asynchronous predicates, that instead of T => Boolean, are defined in terms of T => Future[Boolean]. The same as with normal validation predicates, full derivation is also supported for asynchronous validators. Look at the example below to get better insight:

trait EmailService {
  def isEmailTaken(email: String): Future[Boolean]
  def doesDomainExists(email: String): Future[Boolean]
}

class AsyncValidators(emailService: EmailService) {

  implicit val emailAsyncValidator: AsyncValidator[Email] =
    Validator
      .derived[Email] // (1)
      .async.ruleVC(emailService.isEmailTaken, "email is already taken by someone else") // (2)
      .async.rule(_.address, emailService.doesDomainExists, "domain does not exists") // (3)
}

val asyncValidators = new AsyncValidators(...)

import asyncValidators._ // (4)

Email("[email protected]").isValidAsync // Success(false): Future[Boolean]
Email("[email protected]").validateAsync
  .map(_.toFieldErrMapping)
  // Success(List(("", "must contain . after @"), ("", "domain does not exists"))): Future[List[(String, String)]


val user1 = User(
  UserId(1),
  Email("[email protected]"),
  Address("Love Street", PostalCode("12345"), "Los Angeles")
)

user1.validateAsync
  .map(_.toFieldErrMapping) // Success(List("email", "email is already taken by someone else")): Future[List[(String, String)]

Comments:

  • (1) we are requesting to derive usual validator for Email type
  • (2) by prepending rule with async keyword we can define validator rule with asynchronous predicate that lifts our validator to AsyncValidator[Email]
  • (3) we are adding next asynchronous validation rule
  • (4) we are importing instances for asynchronous validators into current scope so that later we can use .isValidAsync/.validateAsync extension methods.

Using other monad

When working with asynchronous validators DSL, by default you define rules and obtain results in scala Future. However you're not particularly tied to it.

We provide bridge instances for internal AppError (which resembles applicative functor with error handling capabilities) for cats ApplicativeError and scalaz MonadError. Any wrapper type M[_] for which you already have those instances, will work. Otherwise, you need to define your own instance for AppError and make sure it's in implicit scope when using rule dsl.

Example integration with cats.effect.IO:

  import octopus.async.cats.implicits._
  import cats.effect.IO

  trait EmailService {
    def isEmailTaken(email: String): IO[Boolean]
    def doesDomainExists(email: String): IO[Boolean]
  }

  implicit val emailAsyncValidator: AsyncValidatorM[IO, Email] =
    Validator
      .derived[Email]
      .asyncM[IO].ruleVC(emailService.isEmailTaken, "email is already taken by someone else") // (2)
      .async.rule(_.address, emailService.doesDomainExists, "domain does not exists") // (3)

  Email("[email protected]").isValidAsync // IO[Boolean]

FAQ

How it's different that Cats/Scalaz validation data types?

The main difference between Octopus and Cats/Scalaz validation types is an approach to composability of validations.

Cats/Scalaz validations are kind of disjunction types that hold successfully validated value or some validation error(s). They can be composed usually with simple combinators or applicative builder syntax. When having lot of case classes with many fields and you want to compose validators of their fields, you have to do this manually which results with rather lot amount of boilerplate code.

Octopus approach is a bit different. The Validator[T] type-class holds a function that will perform validation at some point of time. There are actually two levels of composability:

  • validation rules for single type - can be composed using provided DSL,
  • field validators that composes and create case class validator - that is achieved automatically by using type-class derivation mechanism; still you can override validation rules for certain types in local contexts.

License

Copyright 2016-2020 Piotr Krzemiński

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.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].