{"action":"created","issue":{"url":"https://api.github.com/repos/hanami/model/issues/334","repository_url":"https://api.github.com/repos/hanami/model","labels_url":"https://api.github.com/repos/hanami/model/issues/334/labels{/name}","comments_url":"https://api.github.com/repos/hanami/model/issues/334/comments","events_url":"https://api.github.com/repos/hanami/model/issues/334/events","html_url":"https://github.com/hanami/model/pull/334","id":177832204,"number":334,"title":"New Engine (ROM)","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":[{"id":80116988,"url":"https://api.github.com/repos/hanami/model/labels/enhancement","name":"enhancement","color":"84b6eb","default":true},{"id":326264990,"url":"https://api.github.com/repos/hanami/model/labels/fix","name":"fix","color":"009800","default":false}],"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},"assignees":[{"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":{"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":12,"closed_issues":15,"state":"open","created_at":"2016-02-01T09:31:49Z","updated_at":"2016-11-08T11:58:30Z","due_on":null,"closed_at":null},"comments":12,"created_at":"2016-09-19T16:30:04Z","updated_at":"2016-11-08T15:44:40Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/hanami/model/pulls/334","html_url":"https://github.com/hanami/model/pull/334","diff_url":"https://github.com/hanami/model/pull/334.diff","patch_url":"https://github.com/hanami/model/pull/334.patch"},"body":"## New Engine\r\n\r\nUse `rom` as low-level engine for `hanami-model`. Introduce new features (automapping for SQL databases, experimental associations), remove old (dirty tracking). Fix old bugs.\r\n### Example\r\n\r\nBefore to dive into the details here's how it looks the basic example:\r\n\r\n``` ruby\r\nrequire 'hanami/model'\r\n\r\nclass User\r\n  include Hanami::Entity\r\n  # no more attributes here\r\nend\r\n\r\nclass UserRepository < Hanami::Repository # inherit instead of include\r\nend\r\n\r\nHanami::Model.configure do\r\n  adapter :sql, ENV['DATABASE_URL']\r\n  # No more mapping\r\nend.load!\r\n\r\nrepository = UserRepository.new\r\nuser       = repository.create(name: 'Luca') # It works with data as primary input type\r\nuser.id # => 1\r\n\r\nuser       = repository.update(user.id, age: 34) # It accepts the primary key and the data\r\nuser.age # => 34\r\n\r\nrepository.delete(user.id) # It accepts the primary key\r\n```\r\n\r\nThis **reduces drastically the boilerplate** to setup an entity and a repository pair.\r\n### Repositories\r\n\r\nKeeping the good work from #299, repositories now expose their CRUD operations at the instance level, not at the class level.\r\n\r\nData (hashes) is the preferred input for CRUD operations. In past we used to pass entities, while this is still possible, it’s suggested to use just data.\r\n\r\nRepositories can access all the relations with instance methods. For instance, from the `BookRepository` we can access `users` relation (database table) with `#users`.\r\n\r\nRepositories are able to _auto infer_ the database schema and use the correct Ruby types. Advanced PostgreSQL types like UUID, Array, JSON(B) and Money are handled natively.\r\n\r\nRepositories can also _auto map_ database columns with entities attributes. In case there is a mismatch between the names, we can define a custom mapping. **NOTE:** manual mapping is optional and should be used only with legacy databases.\r\n\r\n``` ruby\r\n# Basic case\r\nclass UserRepository < Hanami::Repository\r\nend\r\n\r\n# Advanced case for legacy databases\r\nclass OperatorRepository < Hanami::Repository\r\n  # By convention we expect it to work with :operators,\r\n  # but instead we manually define the name\r\n  self.relation = :t_operator\r\n\r\n  # Define a mapping between each entity attribute to the corresponding database column\r\n  mapping do\r\n    attribute :id,   from: :i_operator_id\r\n    attribute :name, from: :s_name\r\n    # …\r\n  end\r\nend\r\n```\r\n\r\nCustom queries can be defined with an intuitive Ruby syntax:\r\n\r\n``` ruby\r\nclass UserRepository < Hanami::Repository\r\n  def by_name(name)\r\n    users.where(name: name).as(:entity)\r\n  end\r\nend\r\n```\r\n### Entities\r\n\r\nWe removed the need to define attributes (via `.attributes`). Now an entity behaves like `OpenStruct`: it accepts a hash as input and exposes getters for it.\r\n\r\nEntities are now immutable. (**NOTE:** This is experimental and it could change before we release 0.7).\r\n### Associations (experimental)\r\n\r\nWe introduced experimental support for associations (only `has_many` at this time).\r\n\r\nWhen we define an association, it does NOT add any method to the public interface. We can access the association internally via `#assoc` and define only the explicit operations that we need for our model domain.\r\n\r\n``` ruby\r\nclass AuthorRepository < Hanami::Repository\r\n  associations do\r\n    has_many :books\r\n  end\r\n\r\n  def find_with_books(id)\r\n    aggregate(:books).where(authors__id: id).as(Author).one\r\n  end\r\n\r\n  def books_for(author)\r\n    assoc(:books, author)\r\n  end\r\n\r\n  def add_book(author, data)\r\n    assoc(:books, author).add(data)\r\n  end\r\n\r\n  def remove_book(author, id)\r\n    assoc(:books, author).remove(id)\r\n  end\r\n\r\n  def delete_books(author)\r\n    assoc(:books, author).delete\r\n  end\r\n\r\n  def delete_on_sales_books(author)\r\n    assoc(:books, author).where(on_sale: true).delete\r\n  end\r\n\r\n  def books_count(author)\r\n    assoc(:books, author).count\r\n  end\r\n\r\n  def on_sales_books_count(author)\r\n    assoc(:books, author).where(on_sale: true).count\r\n  end\r\n\r\n  def find_book(author, id)\r\n    book_for(author, id).one\r\n  end\r\n\r\n  def book_exists?(author, id)\r\n    book_for(author, id).exists?\r\n  end\r\n\r\n  private\r\n\r\n  def book_for(author, id)\r\n    assoc(:books, author).where(id: id)\r\n  end\r\nend\r\n```\r\n\r\n---\r\n\r\nA special thanks goes to @solnic, @flash-gordon, @AMHOL and @timriley for ROM and their tireless support provided during these months of development. You rock! 💚 \r\n\r\n---\r\n\r\nCloses #217 \r\nCloses #237 \r\nCloses #245 \r\nCloses #272 \r\nCloses #273 \r\nCloses #278 \r\nCloses #279 \r\nCloses #285 \r\nCloses #291 \r\nCloses #294 \r\n\r\n---\r\n\r\n/cc @hanami/core-team @hanami/contributors \r\n"},"comment":{"url":"https://api.github.com/repos/hanami/model/issues/comments/259172009","html_url":"https://github.com/hanami/model/pull/334#issuecomment-259172009","issue_url":"https://api.github.com/repos/hanami/model/issues/334","id":259172009,"user":{"login":"beauby","id":2215778,"avatar_url":"https://avatars.githubusercontent.com/u/2215778?v=3","gravatar_id":"","url":"https://api.github.com/users/beauby","html_url":"https://github.com/beauby","followers_url":"https://api.github.com/users/beauby/followers","following_url":"https://api.github.com/users/beauby/following{/other_user}","gists_url":"https://api.github.com/users/beauby/gists{/gist_id}","starred_url":"https://api.github.com/users/beauby/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/beauby/subscriptions","organizations_url":"https://api.github.com/users/beauby/orgs","repos_url":"https://api.github.com/users/beauby/repos","events_url":"https://api.github.com/users/beauby/events{/privacy}","received_events_url":"https://api.github.com/users/beauby/received_events","type":"User","site_admin":false},"created_at":"2016-11-08T15:44:40Z","updated_at":"2016-11-08T15:44:40Z","body":"@jodosha Agreed, but TBH, this seems like a shortcoming on rubocop's side, as the environment should not have any effect on static analysis of code."}}
{"actor":{"display_login":"beauby"}}