1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-11 05:55:57 +00:00

create a new package, name it as hero, I was thinking super or superb but hero is better name for what it does - the goal is to split the new 'mvc handlers' from the mvc system because they are not the same, users should know that they can use these type of rich binded handlers without controllers as well, like a normal handler and that I implemented here, the old files exist on the mvc package but will be removed at the next commit, I have to decide if we want type aliases for Result or no

Former-commit-id: cb775edc72bedc88aeab4c5a6de6bfc6bd56fae2
This commit is contained in:
Gerasimos (Makis) Maropoulos
2017-12-25 20:05:32 +02:00
parent 4ab889da5f
commit 46505f62db
43 changed files with 2680 additions and 20 deletions

View File

@@ -0,0 +1,176 @@
// file: repositories/movie_repository.go
package repositories
import (
"errors"
"sync"
"github.com/kataras/iris/_examples/hero/overview/datamodels"
)
// Query represents the visitor and action queries.
type Query func(datamodels.Movie) bool
// MovieRepository handles the basic operations of a movie entity/model.
// It's an interface in order to be testable, i.e a memory movie repository or
// a connected to an sql database.
type MovieRepository interface {
Exec(query Query, action Query, limit int, mode int) (ok bool)
Select(query Query) (movie datamodels.Movie, found bool)
SelectMany(query Query, limit int) (results []datamodels.Movie)
InsertOrUpdate(movie datamodels.Movie) (updatedMovie datamodels.Movie, err error)
Delete(query Query, limit int) (deleted bool)
}
// NewMovieRepository returns a new movie memory-based repository,
// the one and only repository type in our example.
func NewMovieRepository(source map[int64]datamodels.Movie) MovieRepository {
return &movieMemoryRepository{source: source}
}
// movieMemoryRepository is a "MovieRepository"
// which manages the movies using the memory data source (map).
type movieMemoryRepository struct {
source map[int64]datamodels.Movie
mu sync.RWMutex
}
const (
// ReadOnlyMode will RLock(read) the data .
ReadOnlyMode = iota
// ReadWriteMode will Lock(read/write) the data.
ReadWriteMode
)
func (r *movieMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) {
loops := 0
if mode == ReadOnlyMode {
r.mu.RLock()
defer r.mu.RUnlock()
} else {
r.mu.Lock()
defer r.mu.Unlock()
}
for _, movie := range r.source {
ok = query(movie)
if ok {
if action(movie) {
loops++
if actionLimit >= loops {
break // break
}
}
}
}
return
}
// Select receives a query function
// which is fired for every single movie model inside
// our imaginary data source.
// When that function returns true then it stops the iteration.
//
// It returns the query's return last known "found" value
// and the last known movie model
// to help callers to reduce the LOC.
//
// It's actually a simple but very clever prototype function
// I'm using everywhere since I firstly think of it,
// hope you'll find it very useful as well.
func (r *movieMemoryRepository) Select(query Query) (movie datamodels.Movie, found bool) {
found = r.Exec(query, func(m datamodels.Movie) bool {
movie = m
return true
}, 1, ReadOnlyMode)
// set an empty datamodels.Movie if not found at all.
if !found {
movie = datamodels.Movie{}
}
return
}
// SelectMany same as Select but returns one or more datamodels.Movie as a slice.
// If limit <=0 then it returns everything.
func (r *movieMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.Movie) {
r.Exec(query, func(m datamodels.Movie) bool {
results = append(results, m)
return true
}, limit, ReadOnlyMode)
return
}
// InsertOrUpdate adds or updates a movie to the (memory) storage.
//
// Returns the new movie and an error if any.
func (r *movieMemoryRepository) InsertOrUpdate(movie datamodels.Movie) (datamodels.Movie, error) {
id := movie.ID
if id == 0 { // Create new action
var lastID int64
// find the biggest ID in order to not have duplications
// in productions apps you can use a third-party
// library to generate a UUID as string.
r.mu.RLock()
for _, item := range r.source {
if item.ID > lastID {
lastID = item.ID
}
}
r.mu.RUnlock()
id = lastID + 1
movie.ID = id
// map-specific thing
r.mu.Lock()
r.source[id] = movie
r.mu.Unlock()
return movie, nil
}
// Update action based on the movie.ID,
// here we will allow updating the poster and genre if not empty.
// Alternatively we could do pure replace instead:
// r.source[id] = movie
// and comment the code below;
current, exists := r.Select(func(m datamodels.Movie) bool {
return m.ID == id
})
if !exists { // ID is not a real one, return an error.
return datamodels.Movie{}, errors.New("failed to update a nonexistent movie")
}
// or comment these and r.source[id] = m for pure replace
if movie.Poster != "" {
current.Poster = movie.Poster
}
if movie.Genre != "" {
current.Genre = movie.Genre
}
// map-specific thing
r.mu.Lock()
r.source[id] = current
r.mu.Unlock()
return movie, nil
}
func (r *movieMemoryRepository) Delete(query Query, limit int) bool {
return r.Exec(query, func(m datamodels.Movie) bool {
delete(r.source, m.ID)
return true
}, limit, ReadWriteMode)
}