{"action":"created","comment":{"url":"https://api.github.com/repos/qutheory/vapor/pulls/comments/59657643","id":59657643,"diff_hunk":"@@ -0,0 +1,307 @@\n+/**\n+ Validatable struct is used to indicate that a value can be validated.\n+ It requires no special attributes or conformance.\n+ \n+ It's purpose is largely based on convenient code share and by\n+ conforming an object inherits a great deal of syntax to more \n+ conveniently work with validators.\n+ \n+ Naming Conventions\n+\n+ - tested throws -> Self\n+ - passed -> Bool\n+ - validated throws -> Valid<Validator>\n+ \n+ */\n+public protocol Validatable {}\n+\n+// MARK: Conformance\n+\n+extension String: Validatable {}\n+\n+extension Set: Validatable {}\n+extension Array: Validatable {}\n+extension Dictionary: Validatable {}\n+\n+extension Bool: Validatable {}\n+\n+extension Int: Validatable {}\n+extension Int8: Validatable {}\n+extension Int16: Validatable {}\n+extension Int32: Validatable {}\n+extension Int64: Validatable {}\n+\n+extension UInt: Validatable {}\n+extension UInt8: Validatable {}\n+extension UInt16: Validatable {}\n+extension UInt32: Validatable {}\n+extension UInt64: Validatable {}\n+\n+extension Float: Validatable {}\n+extension Double: Validatable {}\n+\n+// MARK: Testing\n+\n+extension Validatable {\n+    /**\n+     Test whether or not the caller passes the given tester\n+\n+     - parameter tester: a closure that might potentially fail testing\n+\n+     - rethrows: the encompassed error of the tester\n+\n+     - returns: self if passed tester\n+     */\n+    public func tested(","path":"Sources/Vapor/Validation/Validatable.swift","position":55,"original_position":55,"commit_id":"82fb0e2af3c78288f3e5c669bd91808a2060924f","original_commit_id":"82fb0e2af3c78288f3e5c669bd91808a2060924f","user":{"login":"houndci-bot","id":6697940,"avatar_url":"https://avatars.githubusercontent.com/u/6697940?v=3","gravatar_id":"","url":"https://api.github.com/users/houndci-bot","html_url":"https://github.com/houndci-bot","followers_url":"https://api.github.com/users/houndci-bot/followers","following_url":"https://api.github.com/users/houndci-bot/following{/other_user}","gists_url":"https://api.github.com/users/houndci-bot/gists{/gist_id}","starred_url":"https://api.github.com/users/houndci-bot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/houndci-bot/subscriptions","organizations_url":"https://api.github.com/users/houndci-bot/orgs","repos_url":"https://api.github.com/users/houndci-bot/repos","events_url":"https://api.github.com/users/houndci-bot/events{/privacy}","received_events_url":"https://api.github.com/users/houndci-bot/received_events","type":"User","site_admin":false},"body":"Valid Docs Violation: Documented declarations should be valid. (valid_docs)","created_at":"2016-04-14T02:56:29Z","updated_at":"2016-04-14T02:56:29Z","html_url":"https://github.com/qutheory/vapor/pull/175#discussion_r59657643","pull_request_url":"https://api.github.com/repos/qutheory/vapor/pulls/175","_links":{"self":{"href":"https://api.github.com/repos/qutheory/vapor/pulls/comments/59657643"},"html":{"href":"https://github.com/qutheory/vapor/pull/175#discussion_r59657643"},"pull_request":{"href":"https://api.github.com/repos/qutheory/vapor/pulls/175"}}},"pull_request":{"url":"https://api.github.com/repos/qutheory/vapor/pulls/175","id":65872817,"html_url":"https://github.com/qutheory/vapor/pull/175","diff_url":"https://github.com/qutheory/vapor/pull/175.diff","patch_url":"https://github.com/qutheory/vapor/pull/175.patch","issue_url":"https://api.github.com/repos/qutheory/vapor/issues/175","number":175,"state":"open","locked":false,"title":"[WIP] Validation","user":{"login":"LoganWright","id":5750489,"avatar_url":"https://avatars.githubusercontent.com/u/5750489?v=3","gravatar_id":"","url":"https://api.github.com/users/LoganWright","html_url":"https://github.com/LoganWright","followers_url":"https://api.github.com/users/LoganWright/followers","following_url":"https://api.github.com/users/LoganWright/following{/other_user}","gists_url":"https://api.github.com/users/LoganWright/gists{/gist_id}","starred_url":"https://api.github.com/users/LoganWright/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LoganWright/subscriptions","organizations_url":"https://api.github.com/users/LoganWright/orgs","repos_url":"https://api.github.com/users/LoganWright/repos","events_url":"https://api.github.com/users/LoganWright/events{/privacy}","received_events_url":"https://api.github.com/users/LoganWright/received_events","type":"User","site_admin":false},"body":"Validation proposal.\r\n\r\nI added some updates that I think achieves some good syntax compromises between type safety, and basic accessors. I think we should discuss a lot of it, but here's the main points.\r\n\r\n## Example\r\n\r\n```Swift\r\nclass UserName: ValidationSuite {\r\n    static func validate(input value: String) -> Bool {\r\n        // composing validators. Can also be done inline\r\n        let evaluation = OnlyAlphanumeric.self\r\n            + StringLength.min(5)\r\n            + StringLength.max(20)\r\n\r\n        return evaluation.validate(value)\r\n    }\r\n}\r\n```\r\n\r\nNow Use\r\n\r\n```Swift\r\nlet username = try possibleName.validated(by: UserName.self)\r\n```\r\n\r\nor w/ type inference\r\n\r\n```Swift\r\nclass User {\r\n    let username: Validated<UserName>\r\n\r\n    init(username: String) throws {\r\n        self.username = try username.validated()\r\n    }\r\n}\r\n```\r\n\r\n## Naming Conventions:\r\n\r\nSome of the conventions I'm trying to stick to are as follows\r\n\r\n### tested\r\n\r\nReturns `Self` type, ie: `String` returns `String`, `Int` returns `Int`, etc. This is useful if one wants to incorporate validators, but not the `Validated<V>` type.\r\n\r\n```Swift\r\n// tested is String\r\nlet tested = try \"someString\".tested(by: ValidatorOne + ValidatorTwo || ValidatorThree)\r\n```\r\n\r\n### passes\r\n\r\nReturns `Bool` whether or not a value is valid. I think I'd prefer to remove this, but I'm not sure if others think it has value.\r\n\r\n### validated\r\n\r\nThis validates the input and creates a `Validated<V>` object that within reason can ensure the validity of the underlying value throughout its life.\r\n\r\n```Swift\r\n// Validated<Username>\r\nlet name = try \"possibleName\".validated(by: Username.self)\r\n// Validated<And<Email, StringLength>>\r\nlet googleEmail = try \"possible@email.com\".validated(by: Email.from(\"google.com\") + StringLength.min(13)) // Pretending google doesn't allow 1 or 2 letter emails (@google.com + 3)\r\n```\r\n\r\n## Composition\r\n\r\nValidators can be easily composed together on the fly w/ logical operators `!`, `||`, and `+` or `&&`. \r\n\r\n```Swift\r\nlet secureEntry = try input.validated(by: StringLenght.min(5) + ContainsNumber.self || ContainsPunctuation.self)\r\n```\r\n\r\n#### Minor Annoyance\r\n\r\nA lot of functions are based on `Validatable`. If a user wants a custom Validator, they need to make the `InputType` conform to `Validatable` with:\r\n\r\n```Swift\r\nextension CustomType: Validatable {}\r\n```\r\nIt's simple to do, but a minor annoyance. This is what allows a flexible syntax w/o redeclaring the same functions for new types. Although it's annoying, I think it's better to the alternative. \r\n\r\n#### Value Safety\r\n\r\nWe had to sacrifice a bit of the value safety in order to have more dynamic validators. I think this is a good mix of the two concepts.\r\n\r\n\\- `ValidationSuite` which uses a class level initializer and can be more authoritative. This can't take parameters, but is a little bit stricter.\r\n\r\n\\- `Validator` initialized instance that takes parameters, but is slightly less safe. For example, `Validated<StringLength>` requires an instance of string length. We can guarantee the value has been validated by length, but we can't know the parameters passed. There is talk of eventually being able to do something like `StringLength<5>`. We should revisit this type when/if that becomes possible\r\n\r\n## Discussion Points\r\n\r\n- [ ] Syntax. Do we like the way this looks\r\n- [ ] Naming Conventions, `tested`, `validated`, `passes`\r\n- [ ] Validatable Syntax. Possible alternatives\r\n- [ ] Operators. Should we include operators?\r\n- [ ] Should we keep them? Should use `+` or `&&` or both? \r\n\r\n","created_at":"2016-04-09T06:29:29Z","updated_at":"2016-04-14T02:56:29Z","closed_at":null,"merged_at":null,"merge_commit_sha":"45376ec4dfd59b39cc18e7cd3f2cf5183b1ec36d","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/qutheory/vapor/pulls/175/commits","review_comments_url":"https://api.github.com/repos/qutheory/vapor/pulls/175/comments","review_comment_url":"https://api.github.com/repos/qutheory/vapor/pulls/comments{/number}","comments_url":"https://api.github.com/repos/qutheory/vapor/issues/175/comments","statuses_url":"https://api.github.com/repos/qutheory/vapor/statuses/82fb0e2af3c78288f3e5c669bd91808a2060924f","head":{"label":"qutheory:validation","ref":"validation","sha":"82fb0e2af3c78288f3e5c669bd91808a2060924f","user":{"login":"qutheory","id":17364220,"avatar_url":"https://avatars.githubusercontent.com/u/17364220?v=3","gravatar_id":"","url":"https://api.github.com/users/qutheory","html_url":"https://github.com/qutheory","followers_url":"https://api.github.com/users/qutheory/followers","following_url":"https://api.github.com/users/qutheory/following{/other_user}","gists_url":"https://api.github.com/users/qutheory/gists{/gist_id}","starred_url":"https://api.github.com/users/qutheory/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/qutheory/subscriptions","organizations_url":"https://api.github.com/users/qutheory/orgs","repos_url":"https://api.github.com/users/qutheory/repos","events_url":"https://api.github.com/users/qutheory/events{/privacy}","received_events_url":"https://api.github.com/users/qutheory/received_events","type":"Organization","site_admin":false},"repo":{"id":49910095,"name":"vapor","full_name":"qutheory/vapor","owner":{"login":"qutheory","id":17364220,"avatar_url":"https://avatars.githubusercontent.com/u/17364220?v=3","gravatar_id":"","url":"https://api.github.com/users/qutheory","html_url":"https://github.com/qutheory","followers_url":"https://api.github.com/users/qutheory/followers","following_url":"https://api.github.com/users/qutheory/following{/other_user}","gists_url":"https://api.github.com/users/qutheory/gists{/gist_id}","starred_url":"https://api.github.com/users/qutheory/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/qutheory/subscriptions","organizations_url":"https://api.github.com/users/qutheory/orgs","repos_url":"https://api.github.com/users/qutheory/repos","events_url":"https://api.github.com/users/qutheory/events{/privacy}","received_events_url":"https://api.github.com/users/qutheory/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/qutheory/vapor","description":"An elegant web framework for Swift >=2.2 that works on OS X and Ubuntu.","fork":false,"url":"https://api.github.com/repos/qutheory/vapor","forks_url":"https://api.github.com/repos/qutheory/vapor/forks","keys_url":"https://api.github.com/repos/qutheory/vapor/keys{/key_id}","collaborators_url":"https://api.github.com/repos/qutheory/vapor/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/qutheory/vapor/teams","hooks_url":"https://api.github.com/repos/qutheory/vapor/hooks","issue_events_url":"https://api.github.com/repos/qutheory/vapor/issues/events{/number}","events_url":"https://api.github.com/repos/qutheory/vapor/events","assignees_url":"https://api.github.com/repos/qutheory/vapor/assignees{/user}","branches_url":"https://api.github.com/repos/qutheory/vapor/branches{/branch}","tags_url":"https://api.github.com/repos/qutheory/vapor/tags","blobs_url":"https://api.github.com/repos/qutheory/vapor/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/qutheory/vapor/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/qutheory/vapor/git/refs{/sha}","trees_url":"https://api.github.com/repos/qutheory/vapor/git/trees{/sha}","statuses_url":"https://api.github.com/repos/qutheory/vapor/statuses/{sha}","languages_url":"https://api.github.com/repos/qutheory/vapor/languages","stargazers_url":"https://api.github.com/repos/qutheory/vapor/stargazers","contributors_url":"https://api.github.com/repos/qutheory/vapor/contributors","subscribers_url":"https://api.github.com/repos/qutheory/vapor/subscribers","subscription_url":"https://api.github.com/repos/qutheory/vapor/subscription","commits_url":"https://api.github.com/repos/qutheory/vapor/commits{/sha}","git_commits_url":"https://api.github.com/repos/qutheory/vapor/git/commits{/sha}","comments_url":"https://api.github.com/repos/qutheory/vapor/comments{/number}","issue_comment_url":"https://api.github.com/repos/qutheory/vapor/issues/comments{/number}","contents_url":"https://api.github.com/repos/qutheory/vapor/contents/{+path}","compare_url":"https://api.github.com/repos/qutheory/vapor/compare/{base}...{head}","merges_url":"https://api.github.com/repos/qutheory/vapor/merges","archive_url":"https://api.github.com/repos/qutheory/vapor/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/qutheory/vapor/downloads","issues_url":"https://api.github.com/repos/qutheory/vapor/issues{/number}","pulls_url":"https://api.github.com/repos/qutheory/vapor/pulls{/number}","milestones_url":"https://api.github.com/repos/qutheory/vapor/milestones{/number}","notifications_url":"https://api.github.com/repos/qutheory/vapor/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/qutheory/vapor/labels{/name}","releases_url":"https://api.github.com/repos/qutheory/vapor/releases{/id}","deployments_url":"https://api.github.com/repos/qutheory/vapor/deployments","created_at":"2016-01-18T22:37:52Z","updated_at":"2016-04-14T02:12:17Z","pushed_at":"2016-04-14T02:56:17Z","git_url":"git://github.com/qutheory/vapor.git","ssh_url":"git@github.com:qutheory/vapor.git","clone_url":"https://github.com/qutheory/vapor.git","svn_url":"https://github.com/qutheory/vapor","homepage":"qutheory.io","size":6374,"stargazers_count":3562,"watchers_count":3562,"language":"Swift","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":170,"mirror_url":null,"open_issues_count":6,"forks":170,"open_issues":6,"watchers":3562,"default_branch":"master"}},"base":{"label":"qutheory:master","ref":"master","sha":"d8174c3b95f33851abaedadeefeea6f9fd545d8c","user":{"login":"qutheory","id":17364220,"avatar_url":"https://avatars.githubusercontent.com/u/17364220?v=3","gravatar_id":"","url":"https://api.github.com/users/qutheory","html_url":"https://github.com/qutheory","followers_url":"https://api.github.com/users/qutheory/followers","following_url":"https://api.github.com/users/qutheory/following{/other_user}","gists_url":"https://api.github.com/users/qutheory/gists{/gist_id}","starred_url":"https://api.github.com/users/qutheory/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/qutheory/subscriptions","organizations_url":"https://api.github.com/users/qutheory/orgs","repos_url":"https://api.github.com/users/qutheory/repos","events_url":"https://api.github.com/users/qutheory/events{/privacy}","received_events_url":"https://api.github.com/users/qutheory/received_events","type":"Organization","site_admin":false},"repo":{"id":49910095,"name":"vapor","full_name":"qutheory/vapor","owner":{"login":"qutheory","id":17364220,"avatar_url":"https://avatars.githubusercontent.com/u/17364220?v=3","gravatar_id":"","url":"https://api.github.com/users/qutheory","html_url":"https://github.com/qutheory","followers_url":"https://api.github.com/users/qutheory/followers","following_url":"https://api.github.com/users/qutheory/following{/other_user}","gists_url":"https://api.github.com/users/qutheory/gists{/gist_id}","starred_url":"https://api.github.com/users/qutheory/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/qutheory/subscriptions","organizations_url":"https://api.github.com/users/qutheory/orgs","repos_url":"https://api.github.com/users/qutheory/repos","events_url":"https://api.github.com/users/qutheory/events{/privacy}","received_events_url":"https://api.github.com/users/qutheory/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/qutheory/vapor","description":"An elegant web framework for Swift >=2.2 that works on OS X and Ubuntu.","fork":false,"url":"https://api.github.com/repos/qutheory/vapor","forks_url":"https://api.github.com/repos/qutheory/vapor/forks","keys_url":"https://api.github.com/repos/qutheory/vapor/keys{/key_id}","collaborators_url":"https://api.github.com/repos/qutheory/vapor/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/qutheory/vapor/teams","hooks_url":"https://api.github.com/repos/qutheory/vapor/hooks","issue_events_url":"https://api.github.com/repos/qutheory/vapor/issues/events{/number}","events_url":"https://api.github.com/repos/qutheory/vapor/events","assignees_url":"https://api.github.com/repos/qutheory/vapor/assignees{/user}","branches_url":"https://api.github.com/repos/qutheory/vapor/branches{/branch}","tags_url":"https://api.github.com/repos/qutheory/vapor/tags","blobs_url":"https://api.github.com/repos/qutheory/vapor/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/qutheory/vapor/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/qutheory/vapor/git/refs{/sha}","trees_url":"https://api.github.com/repos/qutheory/vapor/git/trees{/sha}","statuses_url":"https://api.github.com/repos/qutheory/vapor/statuses/{sha}","languages_url":"https://api.github.com/repos/qutheory/vapor/languages","stargazers_url":"https://api.github.com/repos/qutheory/vapor/stargazers","contributors_url":"https://api.github.com/repos/qutheory/vapor/contributors","subscribers_url":"https://api.github.com/repos/qutheory/vapor/subscribers","subscription_url":"https://api.github.com/repos/qutheory/vapor/subscription","commits_url":"https://api.github.com/repos/qutheory/vapor/commits{/sha}","git_commits_url":"https://api.github.com/repos/qutheory/vapor/git/commits{/sha}","comments_url":"https://api.github.com/repos/qutheory/vapor/comments{/number}","issue_comment_url":"https://api.github.com/repos/qutheory/vapor/issues/comments{/number}","contents_url":"https://api.github.com/repos/qutheory/vapor/contents/{+path}","compare_url":"https://api.github.com/repos/qutheory/vapor/compare/{base}...{head}","merges_url":"https://api.github.com/repos/qutheory/vapor/merges","archive_url":"https://api.github.com/repos/qutheory/vapor/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/qutheory/vapor/downloads","issues_url":"https://api.github.com/repos/qutheory/vapor/issues{/number}","pulls_url":"https://api.github.com/repos/qutheory/vapor/pulls{/number}","milestones_url":"https://api.github.com/repos/qutheory/vapor/milestones{/number}","notifications_url":"https://api.github.com/repos/qutheory/vapor/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/qutheory/vapor/labels{/name}","releases_url":"https://api.github.com/repos/qutheory/vapor/releases{/id}","deployments_url":"https://api.github.com/repos/qutheory/vapor/deployments","created_at":"2016-01-18T22:37:52Z","updated_at":"2016-04-14T02:12:17Z","pushed_at":"2016-04-14T02:56:17Z","git_url":"git://github.com/qutheory/vapor.git","ssh_url":"git@github.com:qutheory/vapor.git","clone_url":"https://github.com/qutheory/vapor.git","svn_url":"https://github.com/qutheory/vapor","homepage":"qutheory.io","size":6374,"stargazers_count":3562,"watchers_count":3562,"language":"Swift","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":170,"mirror_url":null,"open_issues_count":6,"forks":170,"open_issues":6,"watchers":3562,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/qutheory/vapor/pulls/175"},"html":{"href":"https://github.com/qutheory/vapor/pull/175"},"issue":{"href":"https://api.github.com/repos/qutheory/vapor/issues/175"},"comments":{"href":"https://api.github.com/repos/qutheory/vapor/issues/175/comments"},"review_comments":{"href":"https://api.github.com/repos/qutheory/vapor/pulls/175/comments"},"review_comment":{"href":"https://api.github.com/repos/qutheory/vapor/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/qutheory/vapor/pulls/175/commits"},"statuses":{"href":"https://api.github.com/repos/qutheory/vapor/statuses/82fb0e2af3c78288f3e5c669bd91808a2060924f"}}}}