{"action":"closed","number":139,"pull_request":{"url":"https://api.github.com/repos/metosin/spec-tools/pulls/139","id":223121703,"node_id":"MDExOlB1bGxSZXF1ZXN0MjIzMTIxNzAz","html_url":"https://github.com/metosin/spec-tools/pull/139","diff_url":"https://github.com/metosin/spec-tools/pull/139.diff","patch_url":"https://github.com/metosin/spec-tools/pull/139.patch","issue_url":"https://api.github.com/repos/metosin/spec-tools/issues/139","number":139,"state":"closed","locked":false,"title":"Coercion","user":{"login":"ikitommi","id":567532,"node_id":"MDQ6VXNlcjU2NzUzMg==","avatar_url":"https://avatars1.githubusercontent.com/u/567532?v=4","gravatar_id":"","url":"https://api.github.com/users/ikitommi","html_url":"https://github.com/ikitommi","followers_url":"https://api.github.com/users/ikitommi/followers","following_url":"https://api.github.com/users/ikitommi/following{/other_user}","gists_url":"https://api.github.com/users/ikitommi/gists{/gist_id}","starred_url":"https://api.github.com/users/ikitommi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ikitommi/subscriptions","organizations_url":"https://api.github.com/users/ikitommi/orgs","repos_url":"https://api.github.com/users/ikitommi/repos","events_url":"https://api.github.com/users/ikitommi/events{/privacy}","received_events_url":"https://api.github.com/users/ikitommi/received_events","type":"User","site_admin":false},"body":"FIxes #65.\r\n\r\n* New `st/coerce` function to coerce a value using form parsing and spec transformers. Can only walk over simple specs, and doesn't require any wrapping of specs. Inspired by [spec-coerce](https://github.com/wilkerlucio/spec-coerce).\r\n\r\n```clj\r\n(deftest coercion-test\r\n  (testing \"predicates\"\r\n    (is (= 1 (st/coerce int? \"1\" st/string-transformer)))\r\n    (is (= \"1\" (st/coerce int? \"1\" st/json-transformer)))\r\n    (is (= :user/kikka (st/coerce keyword? \"user/kikka\" st/string-transformer))))\r\n  (testing \"s/and\"\r\n    (is (= 1 (st/coerce (s/and int? keyword?) \"1\" st/string-transformer)))\r\n    (is (= :1 (st/coerce (s/and keyword? int?) \"1\" st/string-transformer))))\r\n  (testing \"s/or\"\r\n    (is (= 1 (st/coerce (s/or :int int? :keyword keyword?) \"1\" st/string-transformer)))\r\n    (is (= :1 (st/coerce (s/or :keyword keyword? :int int?) \"1\" st/string-transformer))))\r\n  (testing \"s/coll-of\"\r\n    (is (= #{1 2 3} (st/coerce (s/coll-of int? :into #{}) [\"1\" 2 \"3\"] st/string-transformer)))\r\n    (is (= #{\"1\" 2 \"3\"} (st/coerce (s/coll-of int? :into #{}) [\"1\" 2 \"3\"] st/json-transformer)))\r\n    (is (= [:1 2 :3] (st/coerce (s/coll-of keyword?) [\"1\" 2 \"3\"] st/string-transformer)))\r\n    (is (= ::invalid (st/coerce (s/coll-of keyword?) ::invalid st/string-transformer))))\r\n  (testing \"s/keys\"\r\n    (is (= {:c1 1, ::c2 :kikka} (st/coerce (s/keys :req-un [::c1]) {:c1 \"1\", ::c2 \"kikka\"} st/string-transformer)))\r\n    (is (= {:c1 1, ::c2 :kikka} (st/coerce (s/keys :req-un [(and ::c1 ::c2)]) {:c1 \"1\", ::c2 \"kikka\"} st/string-transformer)))\r\n    (is (= {:c1 \"1\", ::c2 :kikka} (st/coerce (s/keys :req-un [::c1]) {:c1 \"1\", ::c2 \"kikka\"} st/json-transformer)))\r\n    (is (= ::invalid (st/coerce (s/keys :req-un [::c1]) ::invalid st/json-transformer))))\r\n  (testing \"s/map-of\"\r\n    (is (= {1 :abba, 2 :jabba} (st/coerce (s/map-of int? keyword?) {\"1\" \"abba\", \"2\" \"jabba\"} st/string-transformer)))\r\n    (is (= {\"1\" :abba, \"2\" :jabba} (st/coerce (s/map-of int? keyword?) {\"1\" \"abba\", \"2\" \"jabba\"} st/json-transformer)))\r\n    (is (= ::invalid (st/coerce (s/map-of int? keyword?) ::invalid st/json-transformer))))\r\n  (testing \"s/nillable\"\r\n    (is (= 1 (st/coerce (s/nilable int?) \"1\" st/string-transformer)))\r\n    (is (= nil (st/coerce (s/nilable int?) nil st/string-transformer))))\r\n  (testing \"s/every\"\r\n    (is (= [1] (st/coerce (s/every int?) [\"1\"] st/string-transformer))))\r\n  (testing \"composed\"\r\n    (let [spec (s/nilable\r\n                 (s/nilable\r\n                   (s/map-of\r\n                     keyword?\r\n                     (s/or :keys (s/keys :req-un [::c1])\r\n                           :ks (s/coll-of (s/and int?) :into #{})))))\r\n          value {\"keys\" {:c1 \"1\" ::c2 \"kikka\"}\r\n                 \"keys2\" {:c1 true}\r\n                 \"ints\" [1 \"1\" \"invalid\" \"3\"]}]\r\n      (is (= {:keys {:c1 1 ::c2 :kikka}\r\n              :keys2 {:c1 true}\r\n              :ints #{1 \"invalid\" 3}}\r\n             (st/coerce spec value st/string-transformer)))\r\n      (is (= {:keys {:c1 \"1\" ::c2 :kikka}\r\n              :keys2 {:c1 true}\r\n              :ints #{1 \"1\" \"invalid\" \"3\"}}\r\n             (st/coerce spec value st/json-transformer))))))\r\n```\r\n\r\n* `st/decode` first tries to use `st/coerce`, then the old conforming-based approach\r\n* **BREAKING**: enchanced parsing results from `spec-tools.parse/parse-spec`:\r\n  * all parse result keys have been qualified:\r\n    * `:keys` => `::parse/keys`\r\n    * `:keys/req` => `::parse/keys-req`\r\n    * `:keys/opt` => `::parse/keys-opt`\r\n  * new parser keys `::parse/items`, `::parse/item`, `::parse/key` and `::parse/value`\r\n  * `s/and` and `s/or` are parsed into composite types:\r\n  \r\n```clj\r\n(testing \"s/or\"\r\n  (is (= {::parse/items [{:spec int?, :type :long} {:spec keyword?, :type :keyword}]\r\n          :type [:or [:long :keyword]]}\r\n         (parse/parse-spec (s/or :int int? :keyword keyword?)))))\r\n(testing \"s/and\"\r\n  (is (= {::parse/items [{:spec int?, :type :long} {:spec keyword?, :type :keyword}]\r\n          :type [:and [:long :keyword]]}\r\n         (parse/parse-spec (s/and int? keyword?)))))\r\n```","created_at":"2018-10-16T05:44:27Z","updated_at":"2018-10-18T08:31:45Z","closed_at":"2018-10-18T08:31:45Z","merged_at":"2018-10-18T08:31:45Z","merge_commit_sha":"eb07480493be290370c1b20c427d45dd14ca7889","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/metosin/spec-tools/pulls/139/commits","review_comments_url":"https://api.github.com/repos/metosin/spec-tools/pulls/139/comments","review_comment_url":"https://api.github.com/repos/metosin/spec-tools/pulls/comments{/number}","comments_url":"https://api.github.com/repos/metosin/spec-tools/issues/139/comments","statuses_url":"https://api.github.com/repos/metosin/spec-tools/statuses/cf0840886a9fac619d4a5a76c9a28b6cd9f2902f","head":{"label":"metosin:coercion","ref":"coercion","sha":"cf0840886a9fac619d4a5a76c9a28b6cd9f2902f","user":{"login":"metosin","id":1569418,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE1Njk0MTg=","avatar_url":"https://avatars1.githubusercontent.com/u/1569418?v=4","gravatar_id":"","url":"https://api.github.com/users/metosin","html_url":"https://github.com/metosin","followers_url":"https://api.github.com/users/metosin/followers","following_url":"https://api.github.com/users/metosin/following{/other_user}","gists_url":"https://api.github.com/users/metosin/gists{/gist_id}","starred_url":"https://api.github.com/users/metosin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/metosin/subscriptions","organizations_url":"https://api.github.com/users/metosin/orgs","repos_url":"https://api.github.com/users/metosin/repos","events_url":"https://api.github.com/users/metosin/events{/privacy}","received_events_url":"https://api.github.com/users/metosin/received_events","type":"Organization","site_admin":false},"repo":{"id":61716978,"node_id":"MDEwOlJlcG9zaXRvcnk2MTcxNjk3OA==","name":"spec-tools","full_name":"metosin/spec-tools","private":false,"owner":{"login":"metosin","id":1569418,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE1Njk0MTg=","avatar_url":"https://avatars1.githubusercontent.com/u/1569418?v=4","gravatar_id":"","url":"https://api.github.com/users/metosin","html_url":"https://github.com/metosin","followers_url":"https://api.github.com/users/metosin/followers","following_url":"https://api.github.com/users/metosin/following{/other_user}","gists_url":"https://api.github.com/users/metosin/gists{/gist_id}","starred_url":"https://api.github.com/users/metosin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/metosin/subscriptions","organizations_url":"https://api.github.com/users/metosin/orgs","repos_url":"https://api.github.com/users/metosin/repos","events_url":"https://api.github.com/users/metosin/events{/privacy}","received_events_url":"https://api.github.com/users/metosin/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/metosin/spec-tools","description":"Clojure(Script) tools for clojure.spec","fork":false,"url":"https://api.github.com/repos/metosin/spec-tools","forks_url":"https://api.github.com/repos/metosin/spec-tools/forks","keys_url":"https://api.github.com/repos/metosin/spec-tools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/metosin/spec-tools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/metosin/spec-tools/teams","hooks_url":"https://api.github.com/repos/metosin/spec-tools/hooks","issue_events_url":"https://api.github.com/repos/metosin/spec-tools/issues/events{/number}","events_url":"https://api.github.com/repos/metosin/spec-tools/events","assignees_url":"https://api.github.com/repos/metosin/spec-tools/assignees{/user}","branches_url":"https://api.github.com/repos/metosin/spec-tools/branches{/branch}","tags_url":"https://api.github.com/repos/metosin/spec-tools/tags","blobs_url":"https://api.github.com/repos/metosin/spec-tools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/metosin/spec-tools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/metosin/spec-tools/git/refs{/sha}","trees_url":"https://api.github.com/repos/metosin/spec-tools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/metosin/spec-tools/statuses/{sha}","languages_url":"https://api.github.com/repos/metosin/spec-tools/languages","stargazers_url":"https://api.github.com/repos/metosin/spec-tools/stargazers","contributors_url":"https://api.github.com/repos/metosin/spec-tools/contributors","subscribers_url":"https://api.github.com/repos/metosin/spec-tools/subscribers","subscription_url":"https://api.github.com/repos/metosin/spec-tools/subscription","commits_url":"https://api.github.com/repos/metosin/spec-tools/commits{/sha}","git_commits_url":"https://api.github.com/repos/metosin/spec-tools/git/commits{/sha}","comments_url":"https://api.github.com/repos/metosin/spec-tools/comments{/number}","issue_comment_url":"https://api.github.com/repos/metosin/spec-tools/issues/comments{/number}","contents_url":"https://api.github.com/repos/metosin/spec-tools/contents/{+path}","compare_url":"https://api.github.com/repos/metosin/spec-tools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/metosin/spec-tools/merges","archive_url":"https://api.github.com/repos/metosin/spec-tools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/metosin/spec-tools/downloads","issues_url":"https://api.github.com/repos/metosin/spec-tools/issues{/number}","pulls_url":"https://api.github.com/repos/metosin/spec-tools/pulls{/number}","milestones_url":"https://api.github.com/repos/metosin/spec-tools/milestones{/number}","notifications_url":"https://api.github.com/repos/metosin/spec-tools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/metosin/spec-tools/labels{/name}","releases_url":"https://api.github.com/repos/metosin/spec-tools/releases{/id}","deployments_url":"https://api.github.com/repos/metosin/spec-tools/deployments","created_at":"2016-06-22T12:23:25Z","updated_at":"2018-10-18T03:53:00Z","pushed_at":"2018-10-18T08:31:45Z","git_url":"git://github.com/metosin/spec-tools.git","ssh_url":"git@github.com:metosin/spec-tools.git","clone_url":"https://github.com/metosin/spec-tools.git","svn_url":"https://github.com/metosin/spec-tools","homepage":"","size":672,"stargazers_count":328,"watchers_count":328,"language":"Clojure","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":34,"mirror_url":null,"archived":false,"open_issues_count":30,"license":{"key":"epl-1.0","name":"Eclipse Public License 1.0","spdx_id":"EPL-1.0","url":"https://api.github.com/licenses/epl-1.0","node_id":"MDc6TGljZW5zZTc="},"forks":34,"open_issues":30,"watchers":328,"default_branch":"master"}},"base":{"label":"metosin:master","ref":"master","sha":"42eb131291306c7f4426db9a55b614ed9d7bf47f","user":{"login":"metosin","id":1569418,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE1Njk0MTg=","avatar_url":"https://avatars1.githubusercontent.com/u/1569418?v=4","gravatar_id":"","url":"https://api.github.com/users/metosin","html_url":"https://github.com/metosin","followers_url":"https://api.github.com/users/metosin/followers","following_url":"https://api.github.com/users/metosin/following{/other_user}","gists_url":"https://api.github.com/users/metosin/gists{/gist_id}","starred_url":"https://api.github.com/users/metosin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/metosin/subscriptions","organizations_url":"https://api.github.com/users/metosin/orgs","repos_url":"https://api.github.com/users/metosin/repos","events_url":"https://api.github.com/users/metosin/events{/privacy}","received_events_url":"https://api.github.com/users/metosin/received_events","type":"Organization","site_admin":false},"repo":{"id":61716978,"node_id":"MDEwOlJlcG9zaXRvcnk2MTcxNjk3OA==","name":"spec-tools","full_name":"metosin/spec-tools","private":false,"owner":{"login":"metosin","id":1569418,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE1Njk0MTg=","avatar_url":"https://avatars1.githubusercontent.com/u/1569418?v=4","gravatar_id":"","url":"https://api.github.com/users/metosin","html_url":"https://github.com/metosin","followers_url":"https://api.github.com/users/metosin/followers","following_url":"https://api.github.com/users/metosin/following{/other_user}","gists_url":"https://api.github.com/users/metosin/gists{/gist_id}","starred_url":"https://api.github.com/users/metosin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/metosin/subscriptions","organizations_url":"https://api.github.com/users/metosin/orgs","repos_url":"https://api.github.com/users/metosin/repos","events_url":"https://api.github.com/users/metosin/events{/privacy}","received_events_url":"https://api.github.com/users/metosin/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/metosin/spec-tools","description":"Clojure(Script) tools for clojure.spec","fork":false,"url":"https://api.github.com/repos/metosin/spec-tools","forks_url":"https://api.github.com/repos/metosin/spec-tools/forks","keys_url":"https://api.github.com/repos/metosin/spec-tools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/metosin/spec-tools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/metosin/spec-tools/teams","hooks_url":"https://api.github.com/repos/metosin/spec-tools/hooks","issue_events_url":"https://api.github.com/repos/metosin/spec-tools/issues/events{/number}","events_url":"https://api.github.com/repos/metosin/spec-tools/events","assignees_url":"https://api.github.com/repos/metosin/spec-tools/assignees{/user}","branches_url":"https://api.github.com/repos/metosin/spec-tools/branches{/branch}","tags_url":"https://api.github.com/repos/metosin/spec-tools/tags","blobs_url":"https://api.github.com/repos/metosin/spec-tools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/metosin/spec-tools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/metosin/spec-tools/git/refs{/sha}","trees_url":"https://api.github.com/repos/metosin/spec-tools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/metosin/spec-tools/statuses/{sha}","languages_url":"https://api.github.com/repos/metosin/spec-tools/languages","stargazers_url":"https://api.github.com/repos/metosin/spec-tools/stargazers","contributors_url":"https://api.github.com/repos/metosin/spec-tools/contributors","subscribers_url":"https://api.github.com/repos/metosin/spec-tools/subscribers","subscription_url":"https://api.github.com/repos/metosin/spec-tools/subscription","commits_url":"https://api.github.com/repos/metosin/spec-tools/commits{/sha}","git_commits_url":"https://api.github.com/repos/metosin/spec-tools/git/commits{/sha}","comments_url":"https://api.github.com/repos/metosin/spec-tools/comments{/number}","issue_comment_url":"https://api.github.com/repos/metosin/spec-tools/issues/comments{/number}","contents_url":"https://api.github.com/repos/metosin/spec-tools/contents/{+path}","compare_url":"https://api.github.com/repos/metosin/spec-tools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/metosin/spec-tools/merges","archive_url":"https://api.github.com/repos/metosin/spec-tools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/metosin/spec-tools/downloads","issues_url":"https://api.github.com/repos/metosin/spec-tools/issues{/number}","pulls_url":"https://api.github.com/repos/metosin/spec-tools/pulls{/number}","milestones_url":"https://api.github.com/repos/metosin/spec-tools/milestones{/number}","notifications_url":"https://api.github.com/repos/metosin/spec-tools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/metosin/spec-tools/labels{/name}","releases_url":"https://api.github.com/repos/metosin/spec-tools/releases{/id}","deployments_url":"https://api.github.com/repos/metosin/spec-tools/deployments","created_at":"2016-06-22T12:23:25Z","updated_at":"2018-10-18T03:53:00Z","pushed_at":"2018-10-18T08:31:45Z","git_url":"git://github.com/metosin/spec-tools.git","ssh_url":"git@github.com:metosin/spec-tools.git","clone_url":"https://github.com/metosin/spec-tools.git","svn_url":"https://github.com/metosin/spec-tools","homepage":"","size":672,"stargazers_count":328,"watchers_count":328,"language":"Clojure","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":34,"mirror_url":null,"archived":false,"open_issues_count":30,"license":{"key":"epl-1.0","name":"Eclipse Public License 1.0","spdx_id":"EPL-1.0","url":"https://api.github.com/licenses/epl-1.0","node_id":"MDc6TGljZW5zZTc="},"forks":34,"open_issues":30,"watchers":328,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/metosin/spec-tools/pulls/139"},"html":{"href":"https://github.com/metosin/spec-tools/pull/139"},"issue":{"href":"https://api.github.com/repos/metosin/spec-tools/issues/139"},"comments":{"href":"https://api.github.com/repos/metosin/spec-tools/issues/139/comments"},"review_comments":{"href":"https://api.github.com/repos/metosin/spec-tools/pulls/139/comments"},"review_comment":{"href":"https://api.github.com/repos/metosin/spec-tools/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/metosin/spec-tools/pulls/139/commits"},"statuses":{"href":"https://api.github.com/repos/metosin/spec-tools/statuses/cf0840886a9fac619d4a5a76c9a28b6cd9f2902f"}},"author_association":"MEMBER","merged":true,"mergeable":null,"rebaseable":null,"mergeable_state":"unknown","merged_by":{"login":"ikitommi","id":567532,"node_id":"MDQ6VXNlcjU2NzUzMg==","avatar_url":"https://avatars1.githubusercontent.com/u/567532?v=4","gravatar_id":"","url":"https://api.github.com/users/ikitommi","html_url":"https://github.com/ikitommi","followers_url":"https://api.github.com/users/ikitommi/followers","following_url":"https://api.github.com/users/ikitommi/following{/other_user}","gists_url":"https://api.github.com/users/ikitommi/gists{/gist_id}","starred_url":"https://api.github.com/users/ikitommi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ikitommi/subscriptions","organizations_url":"https://api.github.com/users/ikitommi/orgs","repos_url":"https://api.github.com/users/ikitommi/repos","events_url":"https://api.github.com/users/ikitommi/events{/privacy}","received_events_url":"https://api.github.com/users/ikitommi/received_events","type":"User","site_admin":false},"comments":6,"review_comments":0,"maintainer_can_modify":false,"commits":6,"additions":493,"deletions":102,"changed_files":10}}
{"actor":{"display_login":"ikitommi"}}