Skip to content
Permalink
Browse files

Merge pull request #199 from hanami/revert-196-patch-1

Revert "class methods => instance methods"
  • Loading branch information...
jodosha committed Jun 7, 2016
2 parents 8d8471c + f413ef0 commit ad4d0c88b4c9350a68e128400e5e1d9f422d0241
Showing with 32 additions and 38 deletions.
  1. +1 −1 source/guides/getting-started.md
  2. +31 −37 source/guides/models/repositories.md
@@ -536,7 +536,7 @@ require 'features_helper'
describe 'List books' do
before do
BookRepository.new.clear
BookRepository.clear
BookRepository.new.create(Book.new(title: 'PoEAA', author: 'Martin Fowler'))
BookRepository.new.create(Book.new(title: 'TDD', author: 'Kent Beck'))
@@ -26,23 +26,20 @@ This architecture has several advantages:

## Interface

When a class includes `Hanami::Repository`, its objects will receive the following interface:

* `#persist(entity)` – Create or update an entity
* `#create(entity)` – Create a record for the given entity
* `#update(entity)` – Update the record corresponding to the given entity
* `#delete(entity)` – Delete the record corresponding to the given entity
* `#fetch(raw)` – Fetch raw datasets for the given raw query string (eg. SQL)
* `#execute(raw)` – Execute raw command (eg. SQL)
* `#all` - Fetch all the entities from the collection
* `#find` - Fetch an entity from the collection by its ID
* `#first` - Fetch the first entity from the collection
* `#last` - Fetch the last entity from the collection
* `#clear` - Delete all the records from the collection
* `#query` - Fabricates a query object

Note that these methods are instance methods therefore are called after a new repository object has been created with
`.new`.
When a class includes `Hanami::Repository`, it will receive the following interface:

* `.persist(entity)` – Create or update an entity
* `.create(entity)` – Create a record for the given entity
* `.update(entity)` – Update the record corresponding to the given entity
* `.delete(entity)` – Delete the record corresponding to the given entity
* `.fetch(raw)` – Fetch raw datasets for the given raw query string (eg. SQL)
* `.execute(raw)` – Execute raw command (eg. SQL)
* `.all` - Fetch all the entities from the collection
* `.find` - Fetch an entity from the collection by its ID
* `.first` - Fetch the first entity from the collection
* `.last` - Fetch the last entity from the collection
* `.clear` - Delete all the records from the collection
* `.query` - Fabricates a query object

**A collection is a homogenous set of records.**
It corresponds to a table for a SQL database or to a MongoDB collection.
@@ -55,7 +52,7 @@ This decision forces developers to define intention revealing API, instead of le
Look at the following code:

```ruby
BookRepository.new.where(author_id: 23).order(:published_at).limit(8)
BookRepository.where(author_id: 23).order(:published_at).limit(8)
```

This is **bad** for a variety of reasons:
@@ -78,7 +75,7 @@ There is a better way:
class BookRepository
include Hanami::Repository
def most_recent_by_author(author, limit: 8)
def self.most_recent_by_author(author, limit: 8)
query do
where(author_id: author.id).
order(:published_at)
@@ -99,9 +96,6 @@ This is a **huge improvement**, because:

* If we change the storage, the callers aren't affected.

Now we can get all books from an author ordered by date, and we can also supply an optional limit, with only one method call.
We can do this by calling `BookRepository.new.most_recent_by_author(author, 20)`.

## Raw Queries & Commands

A repository can perform queries and commands by accepting raw query language expressions.
@@ -113,17 +107,17 @@ A repository can perform queries and commands by accepting raw query language ex
class BookRepository
include Hanami::Repository
def raw_all
def self.raw_all
fetch("SELECT * FROM books")
end
def find_all_titles
def self.find_all_titles
fetch("SELECT title FROM books").map do |book|
book[:title]
end
end
def max_price
def self.max_price
result = 0
fetch("SELECT price FROM books") do |book|
@@ -135,7 +129,7 @@ class BookRepository
end
```

When `#fetch` is used, the returning value is NOT a collection of entities (eg. `Book`), BUT an array of hashes that represents the **raw result set**.
When `.fetch` is used, the returning value is NOT a collection of entities (eg. `Book`), BUT an array of hashes that represents the **raw result set**.

<p class="warning">
Do NOT use user input with <code>.fetch</code>, because it makes your app vulnerable to SQL Injection.
@@ -148,13 +142,13 @@ When `#fetch` is used, the returning value is NOT a collection of entities (eg.
class BookRepository
include Hanami::Repository
def reset_download_count
def self.reset_download_count
execute("UPDATE books SET download_count = 0")
end
end
```

To ensure a command/query separation, `#execute` doesn't have a returning value.
To ensure a command/query separation, `.execute` doesn't have a returning value.

<p class="warning">
Do NOT use user input with <code>.execute</code>, because it makes your app vulnerable to SQL Injection.
@@ -169,37 +163,37 @@ Here is an extended example of a repository that uses the SQL adapter.
class BookRepository
include Hanami::Repository
def most_recent_by_author(author, limit: 8)
def self.most_recent_by_author(author, limit: 8)
query do
where(author_id: author.id).
desc(:id).
limit(limit)
end
end
def most_recent_published_by_author(author, limit = 8)
def self.most_recent_published_by_author(author, limit = 8)
most_recent_by_author(author, limit).published
end
def published
def self.published
query do
where(published: true)
end
end
def drafts
def self.drafts
exclude published
end
def rank
def self.rank
published.desc(:comments_count)
end
def best_article_ever
def self.best_article_ever
rank.limit(1)
end
def comments_average
def self.comments_average
query.average(:comments_count)
end
end
@@ -215,7 +209,7 @@ Here is a pagination example:
module Bookshelf
module Repositories
module Pagination
def paginate(limit: 10, offset: 0)
def self.paginate(limit: 10, offset: 0)
query do
limit(limit).offset(offset)
end
@@ -231,7 +225,7 @@ class BookRepository
include Hanami::Repository
include Bookshelf::Repositories::Pagination
def published
def self.published
query do
where(published: true)
end.paginate

0 comments on commit ad4d0c8

Please sign in to comment.
You can’t perform that action at this time.