Skip to content

Commit

Permalink
Merge pull request #8 from dtop/feature/RC7
Browse files Browse the repository at this point in the history
Feature/rc7
  • Loading branch information
Danilo Topalovic committed Mar 1, 2016
2 parents 836e1d7 + 070d51d commit 3bfa51e
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 26 deletions.
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ disabled_rules: # rule identifiers to exclude from running
- function_body_length
- leading_whitespace
- trailing_whitespace
- valid_docs
excluded: # paths to ignore during linting. overridden by `included`.
- Carthage
- Pods
Expand Down
25 changes: 18 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[![Compatibility](https://img.shields.io/badge/Swift-2.1-blue.svg)](https://developer.apple.com/swift)
[![DependencyManagement](https://img.shields.io/badge/CocoaPods-Compatible-brightgreen.svg)](https://cocoapods.org)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/dtop/SwiftValidate/master/LICENSE)
[![codebeat badge](https://codebeat.co/badges/e6b1e8da-2b4a-46b9-ad1d-9bdd057fbb58)](https://codebeat.co/projects/github-com-dtop-swiftvalidate)
[![GitHub release](https://img.shields.io/github/release/dtop/SwiftValidate.svg)](https://github.com/dtop/SwiftValidate)
##### enhanced validation for swift

Expand Down Expand Up @@ -45,7 +46,7 @@ See also the [ExampleProject]
## Requirements

* iOS 8.0+
* XCode 7.0+
* Xcode 7.0+

## Installation

Expand All @@ -62,6 +63,13 @@ pod 'SwiftValidate'
```

Of course you can use it in [Carthage] as well

```
github "dtop/SwiftValidate"
```

## Usage

All validators are usable completely on their own but one of the main advantages of `SwiftValidate` is that the validators are chainable.
Expand Down Expand Up @@ -154,6 +162,7 @@ see the extraction of the ValidationIteratorTests:


let validationResult = validationIterator.validate(formResults)
let cityInError = validationIterator.isInError("city")

```

Expand Down Expand Up @@ -248,12 +257,13 @@ Validates a given email address

**Configuration**

| value | type | default | description |
|------------------------|:----:|---------|---------------------------------------------------------|
| `allowNil` | Bool | true | value an be nil |
| `validateLocalPart` | Bool | true | the local part of the mail address will be validated |
| `validateHostnamePart` | Bool | true | the hostname part of the mail address will be validated |
| `strict` | Bool | true | the length of the parts will also be validated |
| value | type | default | description |
|--------------------------|:----:|---------|---------------------------------------------------------|
| `allowNil` | Bool | true | value an be nil |
| `validateLocalPart` | Bool | true | the local part of the mail address will be validated |
| `validateHostnamePart` | Bool | true | the hostname part of the mail address will be validated |
| `validateTopLevelDomain` | Bool | true | the address has to have a topleveldomain |
| `strict` | Bool | true | the length of the parts will also be validated |

**Error Messages**

Expand Down Expand Up @@ -510,6 +520,7 @@ class MyGenericValidator<TYPE where TYPE: Equatable>: ValidatorProtocol {
[Eureka]: https://github.com/xmartlabs/Eureka
[Zend\Validate]: https://github.com/zendframework/zend-validator
[CocoaPods]: https://cocoapods.org
[Carthage]: https://github.com/Carthage/Carthage
[WiKi]: https://github.com/dtop/SwiftValidate/wiki
[ExampleProject]: https://github.com/dtop/swift-validate-example

Expand Down
16 changes: 16 additions & 0 deletions validate/ValidationIterator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,22 @@ public class ValidationIterator {
return nil
}

/**
Returns if the given key is in error
- parameter key: the key
- returns: true if in error
*/
public func isInError(key key: String) -> Bool {

if let _ = self.errors[key] {
return true
}

return false
}

/**
Validates the value against the chain
Expand Down
6 changes: 1 addition & 5 deletions validate/Validators/ValidatorBetween.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,7 @@ public class ValidatorBetween<TYPE: SignedNumberType>: ValidatorProtocol {

if let numVal = Double(value) {

guard let minVal = NumberConverter<TYPE>.toDouble(self.minValue) else {
throw NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "internal error - could not convert to double"])
}

guard let maxVal = NumberConverter<TYPE>.toDouble(self.maxValue) else {
guard let minVal = NumberConverter<TYPE>.toDouble(self.minValue), let maxVal = NumberConverter<TYPE>.toDouble(self.maxValue) else {
throw NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "internal error - could not convert to double"])
}

Expand Down
17 changes: 9 additions & 8 deletions validate/Validators/ValidatorDateBetween.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ public class ValidatorDateBetween: BaseValidator, ValidatorProtocol {

// MARK: comparison funcs

private let compareAscExclusive = { (left: NSDate, right: NSDate) -> Bool in return left.compare(right) == .OrderedDescending }
private let compareAscInclusive = { (left: NSDate, right: NSDate) -> Bool in let result = left.compare(right); return result == .OrderedDescending || result == .OrderedSame }

private let compareDescExclusive = { (left: NSDate, right: NSDate) -> Bool in return left.compare(right) == .OrderedAscending }
private let compareDescInclusive = { (left: NSDate, right: NSDate) -> Bool in let result = left.compare(right); return result == .OrderedAscending || result == .OrderedSame }
private let compare = {
(left: NSDate, right: NSDate, expect: NSComparisonResult, inclusive: Bool) -> Bool in

let result = left.compare(right)
return (inclusive) ? result == expect || result == .OrderedSame : result == expect
}

// MARK: methods

Expand Down Expand Up @@ -69,9 +70,9 @@ public class ValidatorDateBetween: BaseValidator, ValidatorProtocol {

if let date = try self.parseDate(value) {

let leftOk = (self.minInclusive) ? self.compareAscInclusive(date, self.min) : self.compareAscExclusive(date, self.min)
let rightOk = (self.maxInclusive) ? self.compareDescInclusive(date, self.max) : self.compareDescExclusive(date, self.max)
let leftOk = self.compare(date, self.min, .OrderedDescending, self.minInclusive)
let rightOk = self.compare(date, self.max, .OrderedAscending, self.maxInclusive)

if !leftOk || !rightOk {

return self.returnError(self.errorMessageNotBetween)
Expand Down
39 changes: 34 additions & 5 deletions validate/Validators/ValidatorEmail.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public class ValidatorEmail: BaseValidator, ValidatorProtocol {
/// Validates the hostname part of the mail addr (after @)
public var validateHostnamePart: Bool = true

/// Validates if a tld is present
public var validateToplevelDomain: Bool = true

/// strict check
public var strict: Bool = true

Expand Down Expand Up @@ -69,14 +72,21 @@ public class ValidatorEmail: BaseValidator, ValidatorProtocol {
return true
}

if !self.validateHostnamePart && self.validateToplevelDomain {
throw NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Topleveldomain cannot be validated without the hostname"])
}

if let strVal = value as? String {

if !self.validateMailFormat(strVal) {
return false
}

let partial = self.splitEmailAddress(strVal)
let valid = ((self.doValidateLocalPart(partial.local) || !self.validateLocalPart) && (self.doValidateHostnamePart(partial.hostname) || !self.validateHostnamePart))
guard let partial = self.splitEmailAddress(strVal) else {
return false
}

let valid = ((self.doValidateLocalPart(partial.local) || !self.validateLocalPart) && (self.doValidateHostnamePart(partial.hostname) || !self.validateHostnamePart))

return valid
}
Expand Down Expand Up @@ -123,14 +133,28 @@ public class ValidatorEmail: BaseValidator, ValidatorProtocol {
return false
}

let charset = NSCharacterSet.URLHostAllowedCharacterSet()
var result: Bool = false

if self.validateToplevelDomain {
do {

let pattern = "\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$"
let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions(rawValue: 0))

let x = regex.numberOfMatchesInString(part, options: NSMatchingOptions(rawValue: 0), range: NSRange(location: 0, length: part.characters.count))
result = x > 0
} catch _ {
result = false
}
}

let charset = NSCharacterSet.URLHostAllowedCharacterSet()
if let _ = part.rangeOfCharacterFromSet(charset.invertedSet) {

return self.returnError(self.errorMessageInvalidHostnamePart)
}

return true
return result
}

/**
Expand Down Expand Up @@ -167,9 +191,14 @@ public class ValidatorEmail: BaseValidator, ValidatorProtocol {
- returns: the tupel
*/
private func splitEmailAddress(address: String) -> (local: String, hostname: String) {
private func splitEmailAddress(address: String) -> (local: String, hostname: String)? {

let parts = address.characters.split { $0 == "@" }.map(String.init)

if parts.count != 2 {
return nil
}

return (parts[0], parts[1])
}

Expand Down
4 changes: 3 additions & 1 deletion validate/Validators/ValidatorRegex.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ public class ValidatorRegex: BaseValidator, ValidatorProtocol {
if let strVal = value as? String {

let regex = try NSRegularExpression(pattern: self.pattern, options: self.options)
let result = regex.numberOfMatchesInString(strVal, options: self.matchingOptions, range: NSMakeRange(0, strVal.characters.count)) > 0

let range = NSRange(location: 0, length: strVal.characters.count)
let result = regex.numberOfMatchesInString(strVal, options: self.matchingOptions, range: range) > 0

if !result {

Expand Down
3 changes: 3 additions & 0 deletions validateTests/ValidationIteratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ class ValidationIteratorTests: XCTestCase {
validationResult = validationIterator.validate(formResults)
XCTAssertFalse(validationResult)

let fieldInError = validationIterator.isInError(key: "city")
XCTAssertTrue(fieldInError)

validationIterator.resultForUnknownKeys = true

// must not fail (unknown fields are ok)
Expand Down
65 changes: 65 additions & 0 deletions validateTests/ValidatorEmailTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,69 @@ class ValidatorEmailTests: XCTestCase {
XCTAssertEqual("no valid string value given", error.localizedDescription)
}
}

func testValidatorCanHandleInsufficientInput() {

let validator = ValidatorEmail() {
$0.strict = true
$0.validateHostnamePart = true
$0.validateLocalPart = true
}

do {

let result = try validator.validate("a@", context: nil)
XCTAssertFalse(result)

} catch _ {
XCTAssert(false)
}
}

func testCanHandleMissingTld() {

let validator = ValidatorEmail() {
$0.strict = true
$0.validateHostnamePart = true
$0.validateLocalPart = true
$0.validateToplevelDomain = true
}

var result: Bool = false
let vMail = "foo@example.org"
let iMail = "foo@example"

do {

result = try validator.validate(vMail, context: nil)
XCTAssertTrue(result)

result = try validator.validate(iMail, context: nil)
XCTAssertFalse(result)

} catch _ {
XCTAssert(false)
}
}

func testCanHandleMissconfig() {

let validator = ValidatorEmail() {
$0.strict = true
$0.validateHostnamePart = false
$0.validateLocalPart = true
$0.validateToplevelDomain = true
}

let iMail = "foo@example"

do {

_ = try validator.validate(iMail, context: nil)
XCTAssert(false)

} catch _ {
XCTAssert(true)
}
}
}

0 comments on commit 3bfa51e

Please sign in to comment.