{"action":"created","issue":{"url":"https://api.github.com/repos/hanami/validations/issues/98","repository_url":"https://api.github.com/repos/hanami/validations","labels_url":"https://api.github.com/repos/hanami/validations/issues/98/labels{/name}","comments_url":"https://api.github.com/repos/hanami/validations/issues/98/comments","events_url":"https://api.github.com/repos/hanami/validations/issues/98/events","html_url":"https://github.com/hanami/validations/pull/98","id":149771680,"number":98,"title":"Predicates","user":{"login":"jodosha","id":5089,"avatar_url":"https://avatars.githubusercontent.com/u/5089?v=3","gravatar_id":"","url":"https://api.github.com/users/jodosha","html_url":"https://github.com/jodosha","followers_url":"https://api.github.com/users/jodosha/followers","following_url":"https://api.github.com/users/jodosha/following{/other_user}","gists_url":"https://api.github.com/users/jodosha/gists{/gist_id}","starred_url":"https://api.github.com/users/jodosha/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jodosha/subscriptions","organizations_url":"https://api.github.com/users/jodosha/orgs","repos_url":"https://api.github.com/users/jodosha/repos","events_url":"https://api.github.com/users/jodosha/events{/privacy}","received_events_url":"https://api.github.com/users/jodosha/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/hanami/validations/labels/enhancement","name":"enhancement","color":"84b6eb"}],"state":"open","locked":false,"assignee":{"login":"jodosha","id":5089,"avatar_url":"https://avatars.githubusercontent.com/u/5089?v=3","gravatar_id":"","url":"https://api.github.com/users/jodosha","html_url":"https://github.com/jodosha","followers_url":"https://api.github.com/users/jodosha/followers","following_url":"https://api.github.com/users/jodosha/following{/other_user}","gists_url":"https://api.github.com/users/jodosha/gists{/gist_id}","starred_url":"https://api.github.com/users/jodosha/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jodosha/subscriptions","organizations_url":"https://api.github.com/users/jodosha/orgs","repos_url":"https://api.github.com/users/jodosha/repos","events_url":"https://api.github.com/users/jodosha/events{/privacy}","received_events_url":"https://api.github.com/users/jodosha/received_events","type":"User","site_admin":false},"milestone":null,"comments":7,"created_at":"2016-04-20T13:44:07Z","updated_at":"2016-04-21T01:12:58Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/hanami/validations/pulls/98","html_url":"https://github.com/hanami/validations/pull/98","diff_url":"https://github.com/hanami/validations/pull/98.diff","patch_url":"https://github.com/hanami/validations/pull/98.patch"},"body":"## Current State\r\n\r\nThe current API that uses options to define validations suffers from **rigidity**, **poor extensibility**. Developers don't have control on the execution order of the validations (_presence_, then _size_), which caused problems with blank values. Nor they can decide to skip a validation during certain conditions.\r\n\r\n## dry-rb\r\n\r\nTalking with dry-rb Community, I’ve discovered a new and powerful approach.\r\n\r\nIt works with input hashes and lets us to define a set of validation rules **for each** key/value pair. These rules are wrapped by lambdas that can only return `true` or `false` to state that a specific key is valid or not. To do that, we translate business requirements into predicates that are chained together with Ruby _faux boolean logic_ operators (eg. `&` instead of `&&`).\r\n\r\nThink of a signup form. We need to ensure data integrity for the  `name` field with the following rules. It has to be: present **and** a string **and** its size must be greater than 3 chars, but lesser than 64. Here’s the code, **read it aloud** and notice how it perfectly expresses our needs for `name`.\r\n\r\n```ruby\r\nrequire 'dry-validation'\r\n\r\nschema = Dry::Validation.Schema do\r\n  validates(:name) { present? & str? & size?(3..64) }\r\nend\r\nresult = schema.call(name: \"Luca\")\r\nresult.success? # => true\r\n```\r\n\r\nThere is more than that, [`dry-validation`](http://dry-rb.org/gems/dry-validation/) puts emphasis on _type safety_ as a fundamental approach for a reliable process.\r\n\r\nIn the past months, I've experimented with `dry-validation` as backend for our library because it solves most of the problems described above. While `dry-v` is a powerful tool, I decided to take the good parts of it and to not adopt it.\r\n\r\n### API Stability\r\n\r\nThere isn’t a way to wrap `dry-v` API, that means **to expose its public API as our public API**. We’re going to shift our approach from _option based_ to _predicate based_. That is a major breaking change for our Community. If we can’t wrap `dry-v`, **all its breaking changes will become our breaking changes**.\r\n\r\nOne of the main Hanami values is maintenance. We aim to protect developers from low level changes by offering a stable API. For instance `hanami-controller` wraps `rack`.\r\n\r\n### API Control\r\n\r\nThere is more to consider: if we can’t wrap, we can’t control what to expose. Each method name, each method signature, each feature wouldn’t be decided by us.\r\n\r\nThere are things that I prefer to not adopt, for instance the code for _predicate logic_ is represented in memory with a custom AST, which makes impossible to debug it.\r\n\r\n### The Support Problem\r\n\r\n`dry-v` is still young and there are still problems to address. This is fine, but a bug in `dry-v` will become our bug without having the chance to fix it.\r\n\r\n### Too Many Transitive Dependencies\r\n\r\n`dry-v` would introduce 10 transitive dependency gems. Hanami v0.8 will have 20 gems in total for the **whole framework** and brand new projects will have 30 gems, if you consider `capybara`, `rake`, `bundler` et co.\r\n\r\nAdding 10 gems only for validations is too much.\r\n\r\n## Custom Solution\r\n\r\nAfter that long experiment with `dry-v`, I decided to try with a custom solution heavily inspired by it. This _pull request_ is the result of this new direction.\r\n\r\n[**For the API details, please have a look at the updated README.**](https://github.com/hanami/validations/blob/predicates/README.md)\r\n\r\n## The Forking Road\r\n\r\nOnce I announced this decision in chat, there was an argument with the dry-rb Community. According to them, We should use only one validations library in Ruby Community, otherwise it will sink.\r\n\r\nIt’s true that there isn’t unity outside of the Rails world.\r\nWe have low level frameworks that are duplicated across the _Alt Ruby Community_. We have `hanami-view`, dry has `dry-view` and Trailblazer has Cells.\r\n\r\nAt the end of the day, we should choose what’s best for us as maintainers and for our developers.\r\n\r\nFor instance, we’ll probably adopt ROM as backend for `hanami-model`, because ROM is so powerful that we can bear with this maintenance burden. For a validation library it isn’t worth the trouble.\r\n\r\n## A Community Discussion\r\n\r\nI’m reporting all the story here because for openness sake. **I want to hear your voice now**: what’s your technical opinion about this change? What do you think about the _predicate syntax_? Shall we merge this or adopt dry-v?\r\n\r\n---\r\n\r\nCloses #72 #79 #80 #90 \r\n\r\n/cc @hanami/core-team @hanami/contributors"},"comment":{"url":"https://api.github.com/repos/hanami/validations/issues/comments/212679072","html_url":"https://github.com/hanami/validations/pull/98#issuecomment-212679072","issue_url":"https://api.github.com/repos/hanami/validations/issues/98","id":212679072,"user":{"login":"joneslee85","id":135605,"avatar_url":"https://avatars.githubusercontent.com/u/135605?v=3","gravatar_id":"","url":"https://api.github.com/users/joneslee85","html_url":"https://github.com/joneslee85","followers_url":"https://api.github.com/users/joneslee85/followers","following_url":"https://api.github.com/users/joneslee85/following{/other_user}","gists_url":"https://api.github.com/users/joneslee85/gists{/gist_id}","starred_url":"https://api.github.com/users/joneslee85/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/joneslee85/subscriptions","organizations_url":"https://api.github.com/users/joneslee85/orgs","repos_url":"https://api.github.com/users/joneslee85/repos","events_url":"https://api.github.com/users/joneslee85/events{/privacy}","received_events_url":"https://api.github.com/users/joneslee85/received_events","type":"User","site_admin":false},"created_at":"2016-04-21T01:12:58Z","updated_at":"2016-04-21T01:12:58Z","body":"I am in favour of this proposal though I find the predicate syntax a bit magical.\r\n\r\nFor example:\r\n\r\n```ruby\r\nvalidates(:name) { true }\r\n```\r\n\r\nWith the above example, what is the value of the subject `name` inside the predicate block?\r\n\r\nBesides, on the decision of not wrapping dry-v or adopting it, I am also with you on this for I think 1.0 is close and mitigate breaking changes should be place on top for sake of adoption."}}