From e9257fe6a73eedc833cdeed2dc47934eeb2d248b Mon Sep 17 00:00:00 2001 From: Meutel Date: Sun, 6 Aug 2017 18:49:40 +0200 Subject: [PATCH] Factorize go --- bouquins/bouquins.go | 187 ++++++++++++++++++------------------------ bouquins/db.go | 29 ++++++- bouquins/dbauthors.go | 28 +------ bouquins/dbbooks.go | 28 +------ bouquins/dbseries.go | 27 +----- main.go | 31 ++++--- 6 files changed, 137 insertions(+), 193 deletions(-) diff --git a/bouquins/bouquins.go b/bouquins/bouquins.go index a422ce8..552fe27 100644 --- a/bouquins/bouquins.go +++ b/bouquins/bouquins.go @@ -219,12 +219,9 @@ type ReqParams struct { AllWords bool } -func (app *Bouquins) render(res http.ResponseWriter, tpl string, model interface{}) error { - return app.Template.ExecuteTemplate(res, tpl, model) -} +// add functions to templates func TemplatesFunc() *template.Template { - tpl := template.New("") - tpl.Funcs(template.FuncMap{ + return template.New("").Funcs(template.FuncMap{ "humanSize": func(sz int64) string { return datasize.ByteSize(sz).HumanReadable() }, @@ -236,17 +233,26 @@ func TemplatesFunc() *template.Template { return "/calibre/" + url.PathEscape(book.Path) + "/" + url.PathEscape(data.Name) + "." + strings.ToLower(data.Format) }, }) - return tpl } +// output page with template +func (app *Bouquins) render(res http.ResponseWriter, tpl string, model interface{}) error { + return app.Template.ExecuteTemplate(res, tpl, model) +} + +// output as JSON func writeJson(res http.ResponseWriter, model interface{}) error { res.Header().Set("Content-Type", "application/json") enc := json.NewEncoder(res) return enc.Encode(model) } + +// test if JSON requested func isJson(req *http.Request) bool { return req.Header.Get("Accept") == "application/json" } + +// get integer parameter func paramInt(name string, req *http.Request) int { val := req.URL.Query().Get(name) if val == "" { @@ -259,6 +265,8 @@ func paramInt(name string, req *http.Request) int { } return valInt } + +// get order parameter func paramOrder(req *http.Request) string { val := req.URL.Query().Get(PARAM_ORDER) if val == "desc" || val == "asc" { @@ -267,6 +275,7 @@ func paramOrder(req *http.Request) string { return "" } +// get common request parameters func params(req *http.Request) *ReqParams { page, perpage := paramInt(PARAM_PAGE, req), paramInt(PARAM_PERPAGE, req) limit := perpage @@ -283,27 +292,22 @@ func params(req *http.Request) *ReqParams { return &ReqParams{limit, offset, sort, order, terms, false} } -// ROUTES // - -func (app *Bouquins) IndexPage(res http.ResponseWriter, req *http.Request) { - count, err := app.BookCount() - if err != nil { - log.Print(err) +// single element or list elements page +func listOrId(res http.ResponseWriter, req *http.Request, url string, + listFunc func(res http.ResponseWriter, req *http.Request) error, + idFunc func(idParam string, res http.ResponseWriter, req *http.Request) error) error { + if !strings.HasPrefix(req.URL.Path, url) { + return errors.New("Invalid URL") // FIXME 404 } - model := NewIndexModel("", count) - if isJson(req) { - err := writeJson(res, model) - if err != nil { - log.Println(err) - http.Error(res, err.Error(), 500) - } - } else { - err = app.render(res, TPL_INDEX, model) - if err != nil { - log.Println(err) - } + idParam := req.URL.Path[len(url):] + if len(idParam) == 0 { + return listFunc(res, req) } + return idFunc(idParam, res, req) } + +// LIST ELEMENTS PAGES // + func (app *Bouquins) BooksListPage(res http.ResponseWriter, req *http.Request) error { if isJson(req) { books, count, more, err := app.BooksAdv(params(req)) @@ -314,35 +318,6 @@ func (app *Bouquins) BooksListPage(res http.ResponseWriter, req *http.Request) e } return errors.New("Invalid mime") } -func (app *Bouquins) BookPage(idParam string, res http.ResponseWriter, req *http.Request) error { - id, err := strconv.Atoi(idParam) - if err != nil { - return nil - } - book, err := app.BookFull(int64(id)) - if err != nil { - return nil - } - return app.render(res, TPL_BOOKS, &BookModel{*NewBouquinsModel(book.Title, "book"), book}) -} -func (app *Bouquins) BooksPage(res http.ResponseWriter, req *http.Request) { - var err error - var idParam = "" - if strings.HasPrefix(req.URL.Path, URL_BOOKS) { - idParam = req.URL.Path[len(URL_BOOKS):] - } else { - err = errors.New("Invalid URL") // FIXME 404 - } - if len(idParam) == 0 { - err = app.BooksListPage(res, req) - } else { - err = app.BookPage(idParam, res, req) - } - if err != nil { - log.Println(err) - http.Error(res, err.Error(), 500) - } -} func (app *Bouquins) AuthorsListPage(res http.ResponseWriter, req *http.Request) error { if isJson(req) { authors, count, more, err := app.AuthorsAdv(params(req)) @@ -353,6 +328,30 @@ func (app *Bouquins) AuthorsListPage(res http.ResponseWriter, req *http.Request) } return errors.New("Invalid mime") } +func (app *Bouquins) SeriesListPage(res http.ResponseWriter, req *http.Request) error { + if isJson(req) { + series, count, more, err := app.SeriesAdv(params(req)) + if err != nil { + return err + } + return writeJson(res, NewSeriesResultsModel(series, more, count)) + } + return errors.New("Invalid mime") +} + +// SINGLE ELEMENT PAGES // + +func (app *Bouquins) BookPage(idParam string, res http.ResponseWriter, req *http.Request) error { + id, err := strconv.Atoi(idParam) + if err != nil { + return err + } + book, err := app.BookFull(int64(id)) + if err != nil { + return err + } + return app.render(res, TPL_BOOKS, &BookModel{*NewBouquinsModel(book.Title, "book"), book}) +} func (app *Bouquins) AuthorPage(idParam string, res http.ResponseWriter, req *http.Request) error { id, err := strconv.Atoi(idParam) if err != nil { @@ -364,34 +363,6 @@ func (app *Bouquins) AuthorPage(idParam string, res http.ResponseWriter, req *ht } return app.render(res, TPL_AUTHORS, &AuthorModel{*NewBouquinsModel(author.Name, "author"), author}) } -func (app *Bouquins) AuthorsPage(res http.ResponseWriter, req *http.Request) { - var err error - var idParam = "" - if strings.HasPrefix(req.URL.Path, URL_AUTHORS) { - idParam = req.URL.Path[len(URL_AUTHORS):] - } else { - err = errors.New("Invalid URL") // FIXME 404 - } - if len(idParam) == 0 { - err = app.AuthorsListPage(res, req) - } else { - err = app.AuthorPage(idParam, res, req) - } - if err != nil { - log.Println(err) - http.Error(res, err.Error(), 500) - } -} -func (app *Bouquins) SeriesListPage(res http.ResponseWriter, req *http.Request) error { - if isJson(req) { - series, count, more, err := app.SeriesAdv(params(req)) - if err != nil { - return err - } - return writeJson(res, NewSeriesResultsModel(series, more, count)) - } - return errors.New("Invalid mime") -} func (app *Bouquins) SeriePage(idParam string, res http.ResponseWriter, req *http.Request) error { id, err := strconv.Atoi(idParam) if err != nil { @@ -403,34 +374,32 @@ func (app *Bouquins) SeriePage(idParam string, res http.ResponseWriter, req *htt } return app.render(res, TPL_SERIES, &SeriesModel{*NewBouquinsModel(series.Name, "series"), series}) } -func (app *Bouquins) SeriesPage(res http.ResponseWriter, req *http.Request) { - var err error - var idParam = "" - if strings.HasPrefix(req.URL.Path, URL_SERIES) { - idParam = req.URL.Path[len(URL_SERIES):] - } else { - err = errors.New("Invalid URL") // FIXME 404 - } - if len(idParam) == 0 { - err = app.SeriesListPage(res, req) - } else { - err = app.SeriePage(idParam, res, req) - } - if err != nil { - log.Println(err) - http.Error(res, err.Error(), 500) - } + +// ROUTES // + +func (app *Bouquins) BooksPage(res http.ResponseWriter, req *http.Request) error { + return listOrId(res, req, URL_BOOKS, app.BooksListPage, app.BookPage) } -func (app *Bouquins) SearchPage(res http.ResponseWriter, req *http.Request) { - model := NewSearchModel() - err := app.render(res, TPL_SEARCH, model) - if err != nil { - log.Println(err) - } +func (app *Bouquins) AuthorsPage(res http.ResponseWriter, req *http.Request) error { + return listOrId(res, req, URL_AUTHORS, app.AuthorsListPage, app.AuthorPage) } -func (app *Bouquins) AboutPage(res http.ResponseWriter, req *http.Request) { - err := app.render(res, TPL_ABOUT, NewBouquinsModel("A propos", "about")) - if err != nil { - log.Println(err) - } +func (app *Bouquins) SeriesPage(res http.ResponseWriter, req *http.Request) error { + return listOrId(res, req, URL_SERIES, app.SeriesListPage, app.SeriePage) +} +func (app *Bouquins) SearchPage(res http.ResponseWriter, req *http.Request) error { + return app.render(res, TPL_SEARCH, NewSearchModel()) +} +func (app *Bouquins) AboutPage(res http.ResponseWriter, req *http.Request) error { + return app.render(res, TPL_ABOUT, NewBouquinsModel("A propos", "about")) +} +func (app *Bouquins) IndexPage(res http.ResponseWriter, req *http.Request) error { + count, err := app.BookCount() + if err != nil { + return err + } + model := NewIndexModel("", count) + if isJson(req) { + return writeJson(res, model) + } + return app.render(res, TPL_INDEX, model) } diff --git a/bouquins/db.go b/bouquins/db.go index a1c692b..030e0ad 100644 --- a/bouquins/db.go +++ b/bouquins/db.go @@ -172,6 +172,33 @@ type Query struct { Desc bool } +func (app *Bouquins) searchHelper(all bool, terms []string, stub, termExpr, orderExpr string) (*sql.Rows, error) { + query := stub + queryTerms := make([]interface{}, 0, len(terms)) + for i, term := range terms { + queryTerms = append(queryTerms, "%"+term+"%") + query += termExpr + if i < len(terms)-1 && all { + query += STMT_BOOL_AND + } + if i < len(terms)-1 && !all { + query += STMT_BOOL_OR + } + } + query += orderExpr + log.Println("Search:", query) + + stmt, err := app.DB.Prepare(query) + if err != nil { + return nil, err + } + rows, err := stmt.Query(queryTerms...) + if err != nil { + return nil, err + } + return rows, nil +} + // PREPARED STATEMENTS // func (app *Bouquins) PrepareAll() error { errcount := 0 @@ -217,5 +244,5 @@ func (app *Bouquins) psSort(sortNameField string, qt QueryType, sort, order stri // prepared statement without sort func (app *Bouquins) ps(qt QueryType) (*sql.Stmt, error) { - return app.psSortBooks(qt, "", "") + return app.psSort("any", qt, "", "") } diff --git a/bouquins/dbauthors.go b/bouquins/dbauthors.go index 9e50c90..7698864 100644 --- a/bouquins/dbauthors.go +++ b/bouquins/dbauthors.go @@ -2,37 +2,17 @@ package bouquins import ( "database/sql" - "log" ) // SUB QUERIES // func (app *Bouquins) searchAuthors(limit int, terms []string, all bool) ([]*AuthorAdv, int, error) { + rows, err := app.searchHelper(all, terms, STMT_AUTHORS_SEARCH, STMT_SEARCH_TERM_AUTHOR, STMT_SEARCH_ORDER_AUTHORS) + if err != nil { + return nil, 0, err + } authors := make([]*AuthorAdv, 0, limit) count := 0 - query := STMT_AUTHORS_SEARCH - queryTerms := make([]interface{}, 0, len(terms)) - for i, term := range terms { - queryTerms = append(queryTerms, "%"+term+"%") - query += STMT_SEARCH_TERM_AUTHOR - if i < len(terms)-1 && all { - query += STMT_BOOL_AND - } - if i < len(terms)-1 && !all { - query += STMT_BOOL_OR - } - } - query += STMT_SEARCH_ORDER_AUTHORS - log.Println("Search:", query) - - stmt, err := app.DB.Prepare(query) - if err != nil { - return nil, 0, err - } - rows, err := stmt.Query(queryTerms...) - if err != nil { - return nil, 0, err - } defer rows.Close() for rows.Next() { if len(authors) <= limit { diff --git a/bouquins/dbbooks.go b/bouquins/dbbooks.go index 520cb07..59d09eb 100644 --- a/bouquins/dbbooks.go +++ b/bouquins/dbbooks.go @@ -2,7 +2,6 @@ package bouquins import ( "database/sql" - "log" ) // MERGE SUB QUERIES // @@ -16,34 +15,13 @@ func assignAuthorsTagsBooks(books []*BookAdv, authors map[int64][]*Author, tags // SUB QUERIES // func (app *Bouquins) searchBooks(limit int, terms []string, all bool) ([]*BookAdv, int, error) { - books := make([]*BookAdv, 0, limit) - // FIXME factorize searchAuthors,searchSeries - count := 0 - query := STMT_BOOKS0 + STMT_WHERE - queryTerms := make([]interface{}, 0, len(terms)) - for i, term := range terms { - queryTerms = append(queryTerms, "%"+term+"%") - query += STMT_SEARCH_TERM_BOOKS - if i < len(terms)-1 && all { - query += STMT_BOOL_AND - } - if i < len(terms)-1 && !all { - query += STMT_BOOL_OR - } - } - query += STMT_SEARCH_ORDER_BOOKS - log.Println("Search:", query) - - stmt, err := app.DB.Prepare(query) - if err != nil { - return nil, 0, err - } - rows, err := stmt.Query(queryTerms...) + rows, err := app.searchHelper(all, terms, STMT_BOOKS0+STMT_WHERE, STMT_SEARCH_TERM_BOOKS, STMT_SEARCH_ORDER_BOOKS) if err != nil { return nil, 0, err } defer rows.Close() - // FIXME factorize queryBooks + books := make([]*BookAdv, 0, limit) + count := 0 for rows.Next() { if len(books) <= limit { book := new(BookAdv) diff --git a/bouquins/dbseries.go b/bouquins/dbseries.go index 0ae53da..25a28be 100644 --- a/bouquins/dbseries.go +++ b/bouquins/dbseries.go @@ -1,7 +1,5 @@ package bouquins -import "log" - // MERGE SUB QUERIES // func assignAuthorsSeries(series []*SeriesAdv, authors map[int64][]*Author) { @@ -13,32 +11,13 @@ func assignAuthorsSeries(series []*SeriesAdv, authors map[int64][]*Author) { // SUB QUERIES // func (app *Bouquins) searchSeries(limit int, terms []string, all bool) ([]*SeriesAdv, int, error) { - series := make([]*SeriesAdv, 0, limit) - count := 0 - query := STMT_SERIES_SEARCH - queryTerms := make([]interface{}, 0, len(terms)) - for i, term := range terms { - queryTerms = append(queryTerms, "%"+term+"%") - query += STMT_SEARCH_TERM_SERIES - if i < len(terms)-1 && all { - query += STMT_BOOL_AND - } - if i < len(terms)-1 && !all { - query += STMT_BOOL_OR - } - } - query += STMT_SEARCH_ORDER_SERIES - log.Println("Search:", query) - - stmt, err := app.DB.Prepare(query) - if err != nil { - return nil, 0, err - } - rows, err := stmt.Query(queryTerms...) + rows, err := app.searchHelper(all, terms, STMT_SERIES_SEARCH, STMT_SEARCH_TERM_SERIES, STMT_SEARCH_ORDER_SERIES) if err != nil { return nil, 0, err } defer rows.Close() + series := make([]*SeriesAdv, 0, limit) + count := 0 for rows.Next() { if len(series) <= limit { serie := new(SeriesAdv) diff --git a/main.go b/main.go index 98f0b10..edefe6f 100644 --- a/main.go +++ b/main.go @@ -63,10 +63,7 @@ func initApp() *BouquinsConf { if err != nil { log.Fatalln(err) } - app := &Bouquins{ - tpl, - db, - } + app := &Bouquins{tpl, db} err = app.PrepareAll() if err != nil { log.Fatalln(err) @@ -83,13 +80,27 @@ func assets(calibre string) { http.Handle(URL_CALIBRE, http.StripPrefix(URL_CALIBRE, http.FileServer(http.Dir(calibre)))) } +func handle(f func(res http.ResponseWriter, req *http.Request) error) func(res http.ResponseWriter, req *http.Request) { + return func(res http.ResponseWriter, req *http.Request) { + err := f(res, req) + if err != nil { + log.Println(err) + http.Error(res, err.Error(), 500) + } + } +} + +func handleUrl(url string, f func(res http.ResponseWriter, req *http.Request) error) { + http.HandleFunc(url, handle(f)) +} + func router(app *Bouquins) { - http.HandleFunc(URL_INDEX, app.IndexPage) - http.HandleFunc(URL_BOOKS, app.BooksPage) - http.HandleFunc(URL_AUTHORS, app.AuthorsPage) - http.HandleFunc(URL_SERIES, app.SeriesPage) - http.HandleFunc(URL_SEARCH, app.SearchPage) - http.HandleFunc(URL_ABOUT, app.AboutPage) + handleUrl(URL_INDEX, app.IndexPage) + handleUrl(URL_BOOKS, app.BooksPage) + handleUrl(URL_AUTHORS, app.AuthorsPage) + handleUrl(URL_SERIES, app.SeriesPage) + handleUrl(URL_SEARCH, app.SearchPage) + handleUrl(URL_ABOUT, app.AboutPage) } func main() {