All Projects → nsagora → peppermint

nsagora / peppermint

Licence: MIT License
Declarative data validation framework, written in Swift

Programming Languages

swift
15916 projects

Projects that are alternatives of or similar to peppermint

Validation
The most awesome validation engine ever created for PHP
Stars: ✭ 5,484 (+14721.62%)
Mutual labels:  validation, validation-engine
validation-components
A collection of common validation predicates for ValidationToolkit framework
Stars: ✭ 26 (-29.73%)
Mutual labels:  validation, validation-engine
filter
Go语言的数据过滤包,由 数据输入、格式化、校验、输出 几个部份组成。
Stars: ✭ 22 (-40.54%)
Mutual labels:  validation
fqdn
RFC-compliant FQDN validation and manipulation for Python.
Stars: ✭ 23 (-37.84%)
Mutual labels:  validation
openapi-lint-vscode
OpenAPI 2.0/3.0.x intellisense, validator, linter, converter and resolver extension for Visual Studio Code
Stars: ✭ 47 (+27.03%)
Mutual labels:  validation
frames-android
Checkout API Client, Payment Form UI and Utilities
Stars: ✭ 26 (-29.73%)
Mutual labels:  validation
Fore
Fore - declarative programming with web components
Stars: ✭ 34 (-8.11%)
Mutual labels:  validation
pyvaru
Rule based data validation library for python 3.
Stars: ✭ 17 (-54.05%)
Mutual labels:  validation
fastify-response-validation
A simple plugin that enables response validation for Fastify.
Stars: ✭ 20 (-45.95%)
Mutual labels:  validation
formalizer
React hooks based form validation made for humans.
Stars: ✭ 12 (-67.57%)
Mutual labels:  validation
ng2-multi-step-wizard-ui-router1
Series 3: Tutorials on creating an Angular 2 Multi-Step Wizard using UI-Router 1.0 and TypeScript 2.0.10
Stars: ✭ 33 (-10.81%)
Mutual labels:  validation
ember-changeset-conditional-validations
Conditional validations for ember-changeset-validations
Stars: ✭ 26 (-29.73%)
Mutual labels:  validation
unity-asset-validator
The Asset Validator is an editor tool for validating assets in the project and in scenes.
Stars: ✭ 30 (-18.92%)
Mutual labels:  validation
cnpj
🇧🇷 Format, validate and generate CNPJ numbers in Node & Deno
Stars: ✭ 26 (-29.73%)
Mutual labels:  validation
datalize
Parameter, query, form data validation and filtering for NodeJS.
Stars: ✭ 55 (+48.65%)
Mutual labels:  validation
liquibase-linter
Quality control for your Liquibase scripts
Stars: ✭ 15 (-59.46%)
Mutual labels:  validation
phone
Validate phone number format
Stars: ✭ 63 (+70.27%)
Mutual labels:  validation
validatedb
Validate on a table in a DB, using dbplyr
Stars: ✭ 15 (-59.46%)
Mutual labels:  validation
dockerfile-utils
A library and command line interface for formatting and linting Dockerfiles.
Stars: ✭ 17 (-54.05%)
Mutual labels:  validation
python-valid8
Yet another validation lib ;). Provides tools for general-purpose variable validation, function inputs/outputs validation as well as class fields validation. All entry points raise consistent ValidationError including all contextual details, with dynamic inheritance of ValueError/TypeError as appropriate.
Stars: ✭ 24 (-35.14%)
Mutual labels:  validation

Peppermint badge-version

badge-build-macos badge-build-linux badge-codecov badge-docs badge-license badge-twitter

  1. Introduction
  2. Requirements
  3. Installation
  4. Usage Examples
  5. Contribute
  6. Meta

Introduction

let constraint = TypeConstraint<Account, Account.Error> {
    KeyPathConstraint(\.username) {
        BlockConstraint {
            $0.count >= 5
        } errorBuilder: {
            .username
        }
    }
    KeyPathConstraint(\.password) {
        GroupConstraint(.all) {
            PredicateConstraint {
                .characterSet(.lowercaseLetters, mode: .inclusive)
            } errorBuilder: {
                .password(.missingLowercase)
            }
            PredicateConstraint{
                .characterSet(.uppercaseLetters, mode: .inclusive)
            } errorBuilder: {
                .password(.missingUppercase)
            }
            PredicateConstraint {
                .characterSet(.decimalDigits, mode: .inclusive)
            } errorBuilder: {
                .password(.missingDigits)
            }
            PredicateConstraint {
                .characterSet(CharacterSet(charactersIn: "!?@#$%^&*()|\\/<>,.~`_+-="), mode: .inclusive)
            } errorBuilder: {
                .password(.missingSpecialChars)
            }
            PredicateConstraint {
                .length(min: 8)
            }  errorBuilder: {
                .password(.tooShort)
            }
        }
    }
    BlockConstraint {
        $0.password == $0.passwordConfirmation
    } errorBuilder: {
        .password(.confirmationMismatch)
    }
    KeyPathConstraint(\.email) {
        PredicateConstraint(.email, error: .email)
    }
    KeyPathConstraint(\.age) {
        PredicateConstraint(.range(min: 14), error: .underAge)
    }
    KeyPathConstraint(\.website) {
        PredicateConstraint(.url, error: .website)
            .optional()
    }
}

let result = constraint.evaluate(with: account)
switch result {
case .success:
    handleSuccess()
case .failure(let summary):
    handleErrors(summary.errors)
}

Peppermint is a declarative and lightweight data validation framework.

At the core of it, there are 2 principles:

  • Empower composition.
  • Embrace standard library.

Every project is unique in it's own challenges and it's great when we can focus on solving them instead of spending our time on boilerplate tasks.

With this idea in mind, the framework follows the Protocol Oriented Programming paradigm and was designed from a small set of protocols and structures that can easily be composed to fit your project needs. Thus, you can think of Peppermint as an adjustable wrench more than a Swiss knife.

Since validation can take place at many levels, Peppermint is available on iOS, macOS, tvOS, watchOS and native Swift projects, such as server-side apps.

Requirements

  • Swift 4.2+
  • iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 8.1+

Installation

Peppermint is available only through Swift Package Manager.

Swift Package Manager

You can add Peppermint to your project in Xcode by going to File > Swift Packages > Add Package Dependency.

Or, if you want to use it as a dependency to your own package, you can add it to your Package.swift file:

import PackageDescription

let package = Package(
    name: "YOUR_PROJECT_NAME",
    targets: [],
    dependencies: [
        .Package(url: "https://github.com/nsagora/peppermint", majorVersion: 1),
    ]
)

Usage example

For a comprehensive list of examples try out the Examples.playground:

  1. Download the repository locally on your machine
  2. Open the project in Xcode
  3. Select the Examples playground from the Project navigator

The Peppermint framework is compact and offers you the foundation you need to build data validation around your project needs. In addition, it includes a set of common validation predicates and constraints that most projects can benefit off.

Predicates

The Predicate represents the core protocol and has the role to evaluate if an input matches on a given validation condition.

At the core of Peppermint there are the following two predicates, which allows you to compose predicates specific to the project needs:

BlockPredicate
let predicate = BlockPredicate<String> { $0.characters.count > 2 }
predicate.evaluate(with: "a") // returns false
predicate.evaluate(with: "abc") // returns true
RegexPredicate
let predicate = RegexPredicate(expression: "^[a-z]$")
predicate.evaluate(with: "a") // returns true
predicate.evaluate(with: "5") // returns false
predicate.evaluate(with: "ab") // returns false

In addition, the framework offers a set of common validation predicates that your project can benefit of:

EmailPredicate
let predicate = EmailPredicate()
predicate.evaluate(with: "hello@") // returns false
predicate.evaluate(with: "[email protected]") // returns true
predicate.evaluate(with: "[email protected]") // returns true
URLPredicate
let predicate = URLPredicate()
predicate.evaluate(with: "http://www.url.com") // returns true
predicate.evaluate(with: "http:\\www.url.com") // returns false
RangePredicate
let predicate = let range = RangePredicate(10...20)
predicate.evaluate(with: 15) // returns true
predicate.evaluate(with: 21) // returns false
LengthPredicate
let predicate = LengthPredicate<String>(min: 5)
predicate.evaluate(with: "abcde")   // returns true
predicate.evaluate(with: "abcd")    // returns false

On top of that, developers can build more advanced or complex predicates by extending the Predicate protocol, and/ or by composing or decorating the existing predicates:

Custom Predicate
public struct CustomPredicate: Predicate {

    public typealias InputType = String

    private let custom: String

    public init(custom: String) {
        self.custom = custom
    }

    public func evaluate(with input: String) -> Bool {
        return input == custom
    }
}

let predicate = CustomPredicate(custom: "alphabet")
predicate.evaluate(with: "alp") // returns false
predicate.evaluate(with: "alpha") // returns false
predicate.evaluate(with: "alphabet") // returns true

Constraints

Predicate Constraint

A PredicateConstraint represents a data type that links a Predicate to an Error, in order to provide useful feedback for the end users.

PredicateConstraint
let constraint = PredicateConstraint<String, MyError>(.email, error: .invalid)

let result = constraint.evaluate(with: "[email protected]")
switch result {
case .valid:
    print("Hi there 👋!")
case .invalid(let summary):
    print("Oh, I was expecting a valid email address!")
}  // prints "Hi there 👋!"
enum MyError: Error {
    case invalid
}

Block Constraint

A BlockConstraint represents a data type that links a custom validation closure to an Error that describes why the evaluation has failed. It's a shortcut of a PredicateConstraint that is initialised with a BlockPredicate.

BlockConstraint
let constraint = BlockConstraint<Int, MyError> {
    $0 % 2 == 0
} errorBuilder: {
    .magicNumber
}

constraint.evaluate(with: 3)
enum Failure: MyError {
    case magicNumber
}

Group Constraint

A GroupConstraint represents a composition of constraints that allows the evaluation to be made on:

  • all constraints
  • or any of the constraints

To provide context, a GroupConstraint allows us to constraint a piece of data as being required and also as being a valid email.

GroupConstraintAn example of a registration form, whereby users are prompted to enter a strong password. This process typically entails some form of validation, but the logic itself is often unstructured and spread out through a view controller.

Peppermint seeks instead to consolidate, standardise, and make explicit the logic that is being used to validate user input. To this end, the below example demonstrates construction of a full GroupConstraint object that can be used to enforce requirements on the user's password data:

var passwordConstraint = GroupConstraint<String, Form.Password>(.all) {
    PredicateConstraint {
        .characterSet(.lowercaseLetters, mode: .loose)
    } errorBuilder: {
        .missingLowercase
    }
    PredicateConstraint{
        .characterSet(.uppercaseLetters, mode: .loose)
    } errorBuilder: {
        .missingUppercase
    }
    PredicateConstraint {
        .characterSet(.decimalDigits, mode: .loose)
    } errorBuilder: {
        .missingDigits
    }
    PredicateConstraint {
        .characterSet(CharacterSet(charactersIn: "!?@#$%^&*()|\\/<>,.~`_+-="), mode: .loose)
    } errorBuilder: {
        .missingSpecialChars
    }
    PredicateConstraint {
        .length(min: 8)
    }  errorBuilder: {
        .minLength(8)
    }
}

let password = "3nGuard!"
let result = passwordConstraint.evaluate(with: password)

switch result {
case .success:
    print("Wow, that's a 💪 password!")
case .failure(let summary):
    print(summary.errors.map({$0.localizedDescription}))
} // prints "Wow, that's a 💪 password!"

From above, we see that once we've constructed the passwordConstraint, we're simply calling evaluate(with:) to get our evaluation Result. This contains a Summary that can be handled as we please.

Contribute

We would love you for the contribution to Peppermint, check the LICENSE file for more info.

Meta

This project is developed and maintained by the members of iOS NSAgora, the community of iOS Developers of Iași, Romania.

Distributed under the MIT license. See LICENSE for more information.

[https://github.com/nsagora/peppermint]

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].