{"action":"created","issue":{"url":"https://api.github.com/repos/hanami/model/issues/292","repository_url":"https://api.github.com/repos/hanami/model","labels_url":"https://api.github.com/repos/hanami/model/issues/292/labels{/name}","comments_url":"https://api.github.com/repos/hanami/model/issues/292/comments","events_url":"https://api.github.com/repos/hanami/model/issues/292/events","html_url":"https://github.com/hanami/model/issues/292","id":130739185,"number":292,"title":"SQL Automapping","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/model/labels/discussion","name":"discussion","color":"207de5"},{"url":"https://api.github.com/repos/hanami/model/labels/feature","name":"feature","color":"bfe5bf"}],"state":"open","locked":false,"assignee":{"login":"hlegius","id":36469,"avatar_url":"https://avatars.githubusercontent.com/u/36469?v=3","gravatar_id":"","url":"https://api.github.com/users/hlegius","html_url":"https://github.com/hlegius","followers_url":"https://api.github.com/users/hlegius/followers","following_url":"https://api.github.com/users/hlegius/following{/other_user}","gists_url":"https://api.github.com/users/hlegius/gists{/gist_id}","starred_url":"https://api.github.com/users/hlegius/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hlegius/subscriptions","organizations_url":"https://api.github.com/users/hlegius/orgs","repos_url":"https://api.github.com/users/hlegius/repos","events_url":"https://api.github.com/users/hlegius/events{/privacy}","received_events_url":"https://api.github.com/users/hlegius/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/hanami/model/milestones/15","html_url":"https://github.com/hanami/model/milestones/v0.7.0","labels_url":"https://api.github.com/repos/hanami/model/milestones/15/labels","id":1555003,"number":15,"title":"v0.7.0","description":"","creator":{"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},"open_issues":6,"closed_issues":6,"state":"open","created_at":"2016-02-01T09:31:49Z","updated_at":"2016-02-29T08:30:55Z","due_on":null,"closed_at":null},"comments":32,"created_at":"2016-02-02T16:37:52Z","updated_at":"2016-03-03T08:57:18Z","closed_at":null,"body":"## Actual\r\n\r\nIn order to add an attribute to an entity, we need to:\r\n\r\n  * add the database column via a migration\r\n  * add the attribute to the entity (via `.attributes`)\r\n  * map the database column with the corresponding attribute\r\n\r\nWhile this is a powerful approach, it's really frustrating to deal with it.\r\n\r\n## Proposal\r\n\r\nEliminate the need of mapping database columns to entities attributes.\r\n\r\nThis is an unnecessary step for brand new applications. With relational databases we can use reflection to return the right data set and have the repositories to know what to do, without an explicit user intervention.\r\n\r\n```ruby\r\nHanami::Model.configure do\r\n  adapter :sql, ENV['DATABASE_URL']\r\n  # look 'ma, no mapping!\r\nend.load!\r\n\r\nbook = BookRepository.new.find(1)\r\n```\r\n\r\n### Entities\r\n\r\nHowever, mapping has a useful role: specify Database to Ruby (and viceversa) coercion rules. Think of a date, it's returned as a string from the low level adapter and thanks to the mapper we're able to get a `Date` instance.\r\n\r\nWe need to solve this problem.\r\n\r\n#### Alternative 1\r\n\r\nBuild a reflection mechanism to automatically resolve coercion rules, starting from database schema.\r\nThis is convenient, but using entities will always require a database connection (even for unit tests).\r\n\r\n```ruby\r\nclass Book\r\n  include Hanami::Entity\r\nend\r\n```\r\n\r\n#### Alternative 2\r\n\r\nBuild a powerful attributes DSL for `Entity`. Something similar to `Virtus` or `Dry::Data`:\r\n\r\n```ruby\r\nclass Book\r\n  include Hanami::Entity\r\n\r\n  attribute :title, String\r\n  attribute :isbn, String\r\nend\r\n```\r\n\r\nThe advantage here is to have entities that are still close to Ruby objects (aka PORO), and they don't need a database connection to be instantiated.\r\n\r\nThis is an improvement, but not DRY as \"Alternative 1\". We can solve this, by empowering the model generator from `hanami` gem.\r\n\r\n```shell\r\n➜ bundle exec hanami generate model book title:string isbn:string\r\n```\r\n\r\nThis command above will generate the entitiy with the typed attributes.\r\n\r\n### Repositories\r\n\r\nThe role of the mapper was to associate a database table to an entity and a repository. Without a mapping, we need to figure out how to associate these elements.\r\n\r\nMy proposal is introduce new conventions:\r\n\r\n  * For a given `BookRepository` the target entity will be `Book` (remove `Repository` suffix)\r\n  * For a given `BookRepository` the target table will be `books` (remove `Repository` suffix, downcase, pluralize)\r\n\r\nBy default this is the code that a developer will see:\r\n\r\n```ruby\r\nclass BookRepository\r\n  include Hanami::Repository\r\nend\r\n```\r\n\r\nExceptions to this rule can be expressed with the following code.\r\n\r\n#### Different Entity Name\r\n\r\n```ruby\r\nclass BookRepository\r\n  include Hanami::Repository\r\n  entity MyBook\r\nend\r\n```\r\n\r\n#### Different Repository Name\r\n\r\n```ruby\r\nclass AlternativeBookRepository\r\n  include Hanami::Repository\r\n  entity Book\r\nend\r\n```\r\n\r\n#### Different Database Table\r\n\r\n```ruby\r\nclass BookRepository\r\n  include Hanami::Repository\r\n  collection :t_books\r\nend\r\n```\r\n\r\n---\r\n\r\n/cc @hanami/core-team @hanami/contributors "},"comment":{"url":"https://api.github.com/repos/hanami/model/issues/comments/191659716","html_url":"https://github.com/hanami/model/issues/292#issuecomment-191659716","issue_url":"https://api.github.com/repos/hanami/model/issues/292","id":191659716,"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},"created_at":"2016-03-03T08:57:17Z","updated_at":"2016-03-03T08:57:17Z","body":"> I don't think anybody tries to say that data mappers are not useful, I'm just saying that having to manually define mappers all the time is a PITA\r\n\r\n@jxxcarlson This is the point :point_up: "}}