mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
c10dd32ad7
Former-commit-id: 350eafb0f70f8433e394e103ff93fa332ee00a05
177 lines
4.5 KiB
Go
177 lines
4.5 KiB
Go
// file: repositories/movie_repository.go
|
|
|
|
package repositories
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
|
|
"github.com/kataras/iris/v12/_examples/dependency-injection/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[uint64]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[uint64]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 uint64
|
|
// 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)
|
|
}
|