Skip to content

Commit

Permalink
convert ctx into struct (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
acoshift authored Jun 6, 2018
1 parent 2f5ab4c commit 7cede44
Show file tree
Hide file tree
Showing 13 changed files with 150 additions and 226 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func router(app *hime.App) http.Handler {
}

func logRequestURI(h http.Handler) http.Handler {
return hime.H(func(ctx hime.Context) hime.Result {
return hime.H(func(ctx *hime.Context) hime.Result {
log.Println(ctx.Request().RequestURI)
return h
})
Expand All @@ -86,7 +86,7 @@ func addHeaderRender(h http.Handler) http.Handler {
})
}

func indexHandler(ctx hime.Context) hime.Result {
func indexHandler(ctx *hime.Context) hime.Result {
if ctx.Request().URL.Path != "/" {
return ctx.RedirectTo("index")
}
Expand Down Expand Up @@ -126,7 +126,7 @@ You can also use hime's handler with middleware

```go
func logRequestURI(h http.Handler) http.Handler {
return hime.H(func(ctx hime.Context) hime.Result {
return hime.H(func(ctx *hime.Context) hime.Result {
log.Println(ctx.Request().RequestURI)
return h // hime.Result is http.Handler alias
})
Expand All @@ -137,7 +137,7 @@ Inject data to context

```go
func injectData(h http.Handler) http.Handler {
return hime.H(func(ctx hime.Context) hime.Result {
return hime.H(func(ctx *hime.Context) hime.Result {
ctx.WithValue(ctxKeyData{}, "injected data!")
return h
})
Expand Down Expand Up @@ -192,7 +192,7 @@ func signInHandler(w http.ResponseWriter, r *http.Request) {
with Result

```go
func signInHandler(ctx hime.Context) hime.Result {
func signInHandler(ctx *hime.Context) hime.Result {
username := r.FormValue("username")
if username == "" {
return ctx.Status(http.StatusBadRequest).Error("username required")
Expand All @@ -204,7 +204,7 @@ func signInHandler(ctx hime.Context) hime.Result {
Why not return error, like this...

```go
func signInHandler(ctx hime.Context) error {
func signInHandler(ctx *hime.Context) error {
username := r.FormValue("username")
if username == "" {
return ctx.Status(http.StatusBadRequest).Error("username required")
Expand Down
2 changes: 1 addition & 1 deletion app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestHandler(t *testing.T) {

t.Run("hime", func(t *testing.T) {
app := hime.New().
Handler(hime.H(func(ctx hime.Context) hime.Result {
Handler(hime.H(func(ctx *hime.Context) hime.Result {
return ctx.String("ok")
}))

Expand Down
43 changes: 26 additions & 17 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,24 @@ import (
)

// NewContext creates new hime's context
func NewContext(w http.ResponseWriter, r *http.Request) Context {
func NewContext(w http.ResponseWriter, r *http.Request) *Context {
return newInternalContext(w, r)
}

func newInternalContext(w http.ResponseWriter, r *http.Request) *appContext {
func newInternalContext(w http.ResponseWriter, r *http.Request) *Context {
app, ok := r.Context().Value(ctxKeyApp).(*App)
if !ok {
panic(ErrAppNotFound)
}
return newContext(app, w, r)
}

func newContext(app *App, w http.ResponseWriter, r *http.Request) *appContext {
return &appContext{app, r, w, 0}
func newContext(app *App, w http.ResponseWriter, r *http.Request) *Context {
return &Context{app, r, w, 0}
}

type appContext struct {
// Context is hime context
type Context struct {
app *App
r *http.Request
w http.ResponseWriter
Expand All @@ -32,54 +33,62 @@ type appContext struct {
}

// Deadline implements context.Context
func (ctx *appContext) Deadline() (deadline time.Time, ok bool) {
func (ctx *Context) Deadline() (deadline time.Time, ok bool) {
return ctx.r.Context().Deadline()
}

// Done implements context.Context
func (ctx *appContext) Done() <-chan struct{} {
func (ctx *Context) Done() <-chan struct{} {
return ctx.r.Context().Done()
}

// Err implements context.Context
func (ctx *appContext) Err() error {
func (ctx *Context) Err() error {
return ctx.r.Context().Err()
}

// Value implements context.Context
func (ctx *appContext) Value(key interface{}) interface{} {
func (ctx *Context) Value(key interface{}) interface{} {
return ctx.r.Context().Value(key)
}

func (ctx *appContext) WithContext(nctx context.Context) {
// WithContext sets r to r.WithContext with given context
func (ctx *Context) WithContext(nctx context.Context) {
ctx.r = ctx.r.WithContext(nctx)
}

func (ctx *appContext) WithRequest(r *http.Request) {
// WithRequest overrides request
func (ctx *Context) WithRequest(r *http.Request) {
ctx.r = r
}

func (ctx *appContext) WithResponseWriter(w http.ResponseWriter) {
// WithResponseWriter overrides response writer
func (ctx *Context) WithResponseWriter(w http.ResponseWriter) {
ctx.w = w
}

func (ctx *appContext) WithValue(key interface{}, val interface{}) {
// WithValue calls WithContext with value context
func (ctx *Context) WithValue(key interface{}, val interface{}) {
ctx.WithContext(context.WithValue(ctx.r.Context(), key, val))
}

func (ctx *appContext) Request() *http.Request {
// Request returns request
func (ctx *Context) Request() *http.Request {
return ctx.r
}

func (ctx *appContext) ResponseWriter() http.ResponseWriter {
// ResponseWriter returns response writer
func (ctx *Context) ResponseWriter() http.ResponseWriter {
return ctx.w
}

func (ctx *appContext) Status(code int) Context {
// Status sets response status code
func (ctx *Context) Status(code int) *Context {
ctx.code = code
return ctx
}

func (ctx *appContext) Param(name string, value interface{}) *Param {
// Param is the short-hand for hime.Param
func (ctx *Context) Param(name string, value interface{}) *Param {
return &Param{Name: name, Value: value}
}
4 changes: 2 additions & 2 deletions examples/middlewarevalue/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type ctxKeyData struct{}

func router() http.Handler {
mux := http.NewServeMux()
mux.Handle("/", hime.H(func(ctx hime.Context) hime.Result {
mux.Handle("/", hime.H(func(ctx *hime.Context) hime.Result {
return ctx.String(ctx.Value(ctxKeyData{}).(string))
}))

Expand All @@ -32,7 +32,7 @@ func router() http.Handler {
}

func injectData(h http.Handler) http.Handler {
return hime.H(func(ctx hime.Context) hime.Result {
return hime.H(func(ctx *hime.Context) hime.Result {
ctx.WithValue(ctxKeyData{}, "injected data!")
return h
})
Expand Down
10 changes: 5 additions & 5 deletions examples/net-http/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func router(app *hime.App) http.Handler {
}

func logRequestURI(h http.Handler) http.Handler {
return hime.Wrap(func(ctx hime.Context) hime.Result {
return hime.Wrap(func(ctx *hime.Context) hime.Result {
log.Println(ctx.Request().RequestURI)
return h
})
Expand All @@ -87,7 +87,7 @@ func addHeaderRender(h http.Handler) http.Handler {
})
}

func indexHandler(ctx hime.Context) hime.Result {
func indexHandler(ctx *hime.Context) hime.Result {
if ctx.Request().URL.Path != "/" {
return ctx.RedirectTo("index")
}
Expand All @@ -96,17 +96,17 @@ func indexHandler(ctx hime.Context) hime.Result {
})
}

func aboutHandler(ctx hime.Context) hime.Result {
func aboutHandler(ctx *hime.Context) hime.Result {
return ctx.View("about", nil)
}

func apiJSONHandler(ctx hime.Context) hime.Result {
func apiJSONHandler(ctx *hime.Context) hime.Result {
return ctx.JSON(map[string]interface{}{
"success": "ok",
})
}

func apiJSONErrorHandler(ctx hime.Context) hime.Result {
func apiJSONErrorHandler(ctx *hime.Context) hime.Result {
return ctx.Status(http.StatusBadRequest).JSON(map[string]interface{}{
"error": "bad request",
})
Expand Down
3 changes: 2 additions & 1 deletion global.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func (app *App) Global(key interface{}) interface{} {
return app.globals[key]
}

func (ctx *appContext) Global(key interface{}) interface{} {
// Global returns global value
func (ctx *Context) Global(key interface{}) interface{} {
return ctx.app.Global(key)
}
2 changes: 1 addition & 1 deletion global_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestGlobal(t *testing.T) {
"key1": "value1",
"key2": "value2",
}).
Handler(hime.H(func(ctx hime.Context) hime.Result {
Handler(hime.H(func(ctx *hime.Context) hime.Result {
assert.Equal(t, "value1", ctx.Global("key1"))
assert.Equal(t, "value2", ctx.Global("key2"))
assert.Nil(t, ctx.Global("invalid"))
Expand Down
134 changes: 1 addition & 133 deletions hime.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package hime

import (
"context"
"io"
"mime/multipart"
"net/http"
"net/url"
)

// Routes is the map for route name => path
Expand All @@ -15,141 +11,13 @@ type Routes map[string]string
type Globals map[interface{}]interface{}

// Handler is the hime handler
type Handler func(Context) Result
type Handler func(*Context) Result

// Result is the handler result
type Result http.Handler

// Context is the hime context
type Context interface {
context.Context

WithContext(ctx context.Context)
WithRequest(r *http.Request)
WithResponseWriter(w http.ResponseWriter)
WithValue(key interface{}, val interface{})

// App data

// Route gets route path from given name
Route(name string, params ...interface{}) string

// Global gets value from global storage
Global(key interface{}) interface{}

// HTTP data

// Request returns http.Request from context
Request() *http.Request

// ResponseWrite returns http.ResponseWriter from context
ResponseWriter() http.ResponseWriter

// Status sets response status
Status(code int) Context

// Request functions
ParseForm() error
ParseMultipartForm(maxMemory int64) error
Form() url.Values
PostForm() url.Values

// FromValue functions
FormValue(key string) string
FormValueTrimSpace(key string) string
FormValueTrimSpaceComma(key string) string
FormValueInt(key string) int
FormValueInt64(key string) int64
FormValueFloat32(key string) float32
FormValueFloat64(key string) float64

PostFormValue(key string) string
PostFormValueTrimSpace(key string) string
PostFormValueTrimSpaceComma(key string) string
PostFormValueInt(key string) int
PostFormValueInt64(key string) int64
PostFormValueFloat32(key string) float32
PostFormValueFloat64(key string) float64

FormFile(key string) (multipart.File, *multipart.FileHeader, error)

// FormFileNotEmpty calls r.FormFile but return http.ErrMissingFile if file empty
FormFileNotEmpty(key string) (multipart.File, *multipart.FileHeader, error)

MultipartForm() *multipart.Form
MultipartReader() (*multipart.Reader, error)
Method() string

// Query returns ctx.Request().URL.Query()
Query() url.Values

Param(name string, value interface{}) *Param

// Results

// Nothing does nothing
Nothing() Result

// Redirect redirects to given url
Redirect(url string, params ...interface{}) Result

// SafeRedirect extracts only path from url then redirect
SafeRedirect(url string, params ...interface{}) Result

// RedirectTo redirects to named route
RedirectTo(name string, params ...interface{}) Result

// RedirectToGet redirects to GET method with See Other status code on the current path
RedirectToGet() Result

// RedirectBack redirects back to previous URL
RedirectBack(fallback string) Result

// RedirectBackToGet redirects back to GET method with See Other status code to previous URL
// or fallback to same URL like RedirectToGet
RedirectBackToGet() Result

// SafeRedirectBack redirects back to previous URL using SafeRedirect
SafeRedirectBack(fallback string) Result

// Error wraps http.Error
Error(error string) Result

// NotFound wraps http.NotFound
NotFound() Result

// NoContent renders empty body with http.StatusNoContent
NoContent() Result

// View renders template
View(name string, data interface{}) Result

// JSON renders json
JSON(data interface{}) Result

// String renders string with format
String(format string, a ...interface{}) Result

// StatusText renders String when http.StatusText
StatusText() Result

// CopyFrom copies source into response writer
CopyFrom(src io.Reader) Result

// Bytes renders bytes
Bytes(b []byte) Result

// File renders file
File(name string) Result
}

// Param is the query param when redirect
type Param struct {
Name string
Value interface{}
}

var (
_ = Context(&appContext{})
_ = context.Context(&appContext{})
)
Loading

0 comments on commit 7cede44

Please sign in to comment.