diff --git a/README.md b/README.md index 0fd8a69b..2aafdc7a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > This is the under-development branch. Stay tuned for the upcoming release [v12.2.0](HISTORY.md#Next). -![](https://iris-go.com/images/release.png) Iris version **12.1.8** has been [released](HISTORY.md#su-16-february-2020--v1218)! + ![](https://iris-go.com/images/cli.png) The official [Iris Command Line Interface](https://github.com/kataras/iris-cli) will soon be near you in 2020! @@ -31,7 +31,7 @@ Learn what [others saying about Iris](https://iris-go.com/testimonials/) and **[ ```sh # https://github.com/kataras/iris/wiki/Installation -$ go get github.com/kataras/iris/v12@latest +$ go get github.com/kataras/iris/v12@master # assume the following code in example.go file $ cat example.go ``` @@ -67,7 +67,7 @@ Iris contains extensive and thorough **[wiki](https://github.com/kataras/iris/wi -For a more detailed technical documentation you can head over to our [godocs](https://pkg.go.dev/github.com/kataras/iris/v12@v12.2.0). And for executable code you can always visit the [\_examples](_examples/) repository's subdirectory. +For a more detailed technical documentation you can head over to our [godocs](https://godoc.org/github.com/kataras/iris). And for executable code you can always visit the [./_examples](_examples) repository's subdirectory. ### Do you like to read while traveling? diff --git a/_examples/README.md b/_examples/README.md index b2e5013c..70710e7b 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -182,7 +182,8 @@ * [JWT](dependency-injection/jwt/main.go) * [JWT (iris-contrib)](dependency-injection/jwt/contrib/main.go) * MVC - * [Overview - Repository and Service layers](mvc/overview) + * [Overview](mvc/overview) + * [Repository and Service layers](mvc/repository) * [Hello world](mvc/hello-world/main.go) * [Basic](mvc/basic/main.go) * [Wildcard](mvc/basic/wildcard/main.go) diff --git a/_examples/mvc/overview/controller/greet_controller.go b/_examples/mvc/overview/controller/greet_controller.go new file mode 100644 index 00000000..737bd537 --- /dev/null +++ b/_examples/mvc/overview/controller/greet_controller.go @@ -0,0 +1,23 @@ +package controller + +import ( + "app/model" + "app/service" +) + +// GreetController handles the index. +type GreetController struct { + Service service.GreetService + // Ctx iris.Context +} + +// Get serves [GET] /. +// Query: name +func (c *GreetController) Get(req model.Request) (model.Response, error) { + message, err := c.Service.Say(req.Name) + if err != nil { + return model.Response{}, err + } + + return model.Response{Message: message}, nil +} diff --git a/_examples/mvc/overview/database/database.go b/_examples/mvc/overview/database/database.go new file mode 100644 index 00000000..88f92210 --- /dev/null +++ b/_examples/mvc/overview/database/database.go @@ -0,0 +1,22 @@ +package database + +import ( + "app/environment" +) + +// DB example database interface. +type DB interface { + Exec(q string) error +} + +// NewDB returns a database based on "env". +func NewDB(env environment.Env) DB { + switch env { + case environment.PROD: + return &mysql{} + case environment.DEV: + return &sqlite{} + default: + panic("unknown environment") + } +} diff --git a/_examples/mvc/overview/database/mysql.go b/_examples/mvc/overview/database/mysql.go new file mode 100644 index 00000000..5d14da05 --- /dev/null +++ b/_examples/mvc/overview/database/mysql.go @@ -0,0 +1,10 @@ +package database + +import "fmt" + +type mysql struct{} + +func (db *mysql) Exec(q string) error { + // simulate an error response. + return fmt.Errorf("mysql: not implemented <%s>", q) +} diff --git a/_examples/mvc/overview/database/sqlite.go b/_examples/mvc/overview/database/sqlite.go new file mode 100644 index 00000000..956a82fd --- /dev/null +++ b/_examples/mvc/overview/database/sqlite.go @@ -0,0 +1,5 @@ +package database + +type sqlite struct{} + +func (db *sqlite) Exec(q string) error { return nil } diff --git a/_examples/mvc/overview/environment/environment.go b/_examples/mvc/overview/environment/environment.go new file mode 100644 index 00000000..dd540525 --- /dev/null +++ b/_examples/mvc/overview/environment/environment.go @@ -0,0 +1,10 @@ +package environment + +// Env is the environment type. +type Env string + +// Available environments example. +const ( + PROD Env = "production" + DEV Env = "development" +) diff --git a/_examples/mvc/overview/go.mod b/_examples/mvc/overview/go.mod new file mode 100644 index 00000000..8621348f --- /dev/null +++ b/_examples/mvc/overview/go.mod @@ -0,0 +1,3 @@ +module app + +go 1.14 diff --git a/_examples/mvc/overview/main.go b/_examples/mvc/overview/main.go index 38748134..7e96ad84 100644 --- a/_examples/mvc/overview/main.go +++ b/_examples/mvc/overview/main.go @@ -1,13 +1,10 @@ -// file: main.go - package main import ( - "github.com/kataras/iris/v12/_examples/mvc/overview/datasource" - "github.com/kataras/iris/v12/_examples/mvc/overview/repositories" - "github.com/kataras/iris/v12/_examples/mvc/overview/services" - "github.com/kataras/iris/v12/_examples/mvc/overview/web/controllers" - "github.com/kataras/iris/v12/_examples/mvc/overview/web/middleware" + "app/controller" + "app/database" + "app/environment" + "app/service" "github.com/kataras/iris/v12" "github.com/kataras/iris/v12/mvc" @@ -15,39 +12,66 @@ import ( func main() { app := iris.New() - app.Logger().SetLevel("debug") + mvc.Configure(app.Party("/greet"), setup) - // Load the template files. - app.RegisterView(iris.HTML("./web/views", ".html")) - - // Serve our controllers. - mvc.New(app.Party("/hello")).Handle(new(controllers.HelloController)) - // You can also split the code you write to configure an mvc.Application - // using the `mvc.Configure` method, as shown below. - mvc.Configure(app.Party("/movies"), movies) - - // http://localhost:8080/hello - // http://localhost:8080/hello/iris - // http://localhost:8080/movies - // http://localhost:8080/movies/1 - app.Listen(":8080", iris.WithOptimizations) + // http://localhost:8080/greet?name=kataras + app.Listen(":8080", iris.WithLogLevel("debug")) } -// note the mvc.Application, it's not iris.Application. -func movies(app *mvc.Application) { - // Add the basic authentication(admin:password) middleware - // for the /movies based requests. - app.Router.Use(middleware.BasicAuth) +/* + +-------------------+ + | Env (DEV, PROD) | + +---------+---------+ + | | | + | | | + | | | + DEV | | | PROD +-------------------+---------------------+ | +----------------------+------------------- + | | | + | | | + +---+-----+ +----------------v------------------+ +----+----+ + | sqlite | | NewDB(Env) DB | | mysql | + +---+-----+ +----------------+---+--------------+ +----+----+ + | | | | + | | | | + | | | | + +------------+-----+ +-------------------v---v-----------------+ +----+------+ + | greeterWithLog | | NewGreetService(Env, DB) GreetService | | greeter | + -------------+-----+ +---------------------------+-------------+ +----+------+ + | | | + | | | + | +-----------------------------------------+ | + | | GreetController | | | + | | | | | + | | - Service GreetService <-- | | + | | | | + | +-------------------+---------------------+ | + | | | + | | | + | | | + | +-----------+-----------+ | + | | HTTP Request | | + | +-----------------------+ | + | | /greet?name=kataras | | + | +-----------+-----------+ | + | | | ++------------------+--------+ +------------+------------+ +-------+------------------+ +| model.Response (JSON) | | Response (JSON, error) | | Bad Request | ++---------------------------+ +-------------------------+ +--------------------------+ +| { | | mysql: not implemented | +| "msg": "Hello kataras" | +--------------------------+ +| } | ++---------------------------+ +*/ +func setup(app *mvc.Application) { + // Register Dependencies. + // Tip: A dependency can depend on other dependencies too. + app.Register( + environment.DEV, // DEV, PROD + database.NewDB, // sqlite, mysql + service.NewGreetService, // greeterWithLogging, greeter + ) - // Create our movie repository with some (memory) data from the datasource. - repo := repositories.NewMovieRepository(datasource.Movies) - // Create our movie service, we will bind it to the movie app's dependencies. - movieService := services.NewMovieService(repo) - app.Register(movieService) - - // serve our movies controller. - // Note that you can serve more than one controller - // you can also create child mvc apps using the `movies.Party(relativePath)` or `movies.Clone(app.Party(...))` - // if you want. - app.Handle(new(controllers.MovieController)) + // Register Controllers. + app.Handle(new(controller.GreetController)) } diff --git a/_examples/mvc/overview/model/request.go b/_examples/mvc/overview/model/request.go new file mode 100644 index 00000000..03a826ed --- /dev/null +++ b/_examples/mvc/overview/model/request.go @@ -0,0 +1,6 @@ +package model + +// Request example incoming request. +type Request struct { + Name string `json:"name" url:"name"` +} diff --git a/_examples/mvc/overview/model/response.go b/_examples/mvc/overview/model/response.go new file mode 100644 index 00000000..18e7855f --- /dev/null +++ b/_examples/mvc/overview/model/response.go @@ -0,0 +1,6 @@ +package model + +// Response example server's response. +type Response struct { + Message string `json:"msg"` +} diff --git a/_examples/mvc/overview/service/greet_service.go b/_examples/mvc/overview/service/greet_service.go new file mode 100644 index 00000000..200e8a23 --- /dev/null +++ b/_examples/mvc/overview/service/greet_service.go @@ -0,0 +1,50 @@ +package service + +import ( + "app/database" + "app/environment" + "fmt" +) + +// GreetService example service. +type GreetService interface { + Say(input string) (string, error) +} + +// NewGreetService returns a service backed with a "db" based on "env". +func NewGreetService(env environment.Env, db database.DB) GreetService { + service := &greeter{db: db, prefix: "Hello"} + + switch env { + case environment.PROD: + return service + case environment.DEV: + return &greeterWithLogging{service} + default: + panic("unknown environment") + } +} + +type greeter struct { + prefix string + db database.DB +} + +func (s *greeter) Say(input string) (string, error) { + if err := s.db.Exec("simulate a query..."); err != nil { + return "", err + } + + result := s.prefix + " " + input + return result, nil +} + +type greeterWithLogging struct { + *greeter +} + +func (s *greeterWithLogging) Say(input string) (string, error) { + result, err := s.greeter.Say(input) + fmt.Printf("result: %s\nerror: %v\n", result, err) + return result, err +} diff --git a/_examples/mvc/overview/datamodels/README.md b/_examples/mvc/repository/datamodels/README.md similarity index 100% rename from _examples/mvc/overview/datamodels/README.md rename to _examples/mvc/repository/datamodels/README.md diff --git a/_examples/mvc/overview/datamodels/movie.go b/_examples/mvc/repository/datamodels/movie.go similarity index 100% rename from _examples/mvc/overview/datamodels/movie.go rename to _examples/mvc/repository/datamodels/movie.go diff --git a/_examples/mvc/overview/datasource/README.md b/_examples/mvc/repository/datasource/README.md similarity index 100% rename from _examples/mvc/overview/datasource/README.md rename to _examples/mvc/repository/datasource/README.md diff --git a/_examples/mvc/overview/datasource/movies.go b/_examples/mvc/repository/datasource/movies.go similarity index 92% rename from _examples/mvc/overview/datasource/movies.go rename to _examples/mvc/repository/datasource/movies.go index ca3ad043..518245df 100644 --- a/_examples/mvc/overview/datasource/movies.go +++ b/_examples/mvc/repository/datasource/movies.go @@ -2,7 +2,7 @@ package datasource -import "github.com/kataras/iris/v12/_examples/mvc/overview/datamodels" +import "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" // Movies is our imaginary data source. var Movies = map[int64]datamodels.Movie{ diff --git a/_examples/mvc/overview/folder_structure.png b/_examples/mvc/repository/folder_structure.png similarity index 100% rename from _examples/mvc/overview/folder_structure.png rename to _examples/mvc/repository/folder_structure.png diff --git a/_examples/mvc/repository/main.go b/_examples/mvc/repository/main.go new file mode 100644 index 00000000..1cd430b7 --- /dev/null +++ b/_examples/mvc/repository/main.go @@ -0,0 +1,53 @@ +// file: main.go + +package main + +import ( + "github.com/kataras/iris/v12/_examples/mvc/repository/datasource" + "github.com/kataras/iris/v12/_examples/mvc/repository/repositories" + "github.com/kataras/iris/v12/_examples/mvc/repository/services" + "github.com/kataras/iris/v12/_examples/mvc/repository/web/controllers" + "github.com/kataras/iris/v12/_examples/mvc/repository/web/middleware" + + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/mvc" +) + +func main() { + app := iris.New() + app.Logger().SetLevel("debug") + + // Load the template files. + app.RegisterView(iris.HTML("./web/views", ".html")) + + // Serve our controllers. + mvc.New(app.Party("/hello")).Handle(new(controllers.HelloController)) + // You can also split the code you write to configure an mvc.Application + // using the `mvc.Configure` method, as shown below. + mvc.Configure(app.Party("/movies"), movies) + + // http://localhost:8080/hello + // http://localhost:8080/hello/iris + // http://localhost:8080/movies + // http://localhost:8080/movies/1 + app.Listen(":8080", iris.WithOptimizations) +} + +// note the mvc.Application, it's not iris.Application. +func movies(app *mvc.Application) { + // Add the basic authentication(admin:password) middleware + // for the /movies based requests. + app.Router.Use(middleware.BasicAuth) + + // Create our movie repository with some (memory) data from the datasource. + repo := repositories.NewMovieRepository(datasource.Movies) + // Create our movie service, we will bind it to the movie app's dependencies. + movieService := services.NewMovieService(repo) + app.Register(movieService) + + // serve our movies controller. + // Note that you can serve more than one controller + // you can also create child mvc apps using the `movies.Party(relativePath)` or `movies.Clone(app.Party(...))` + // if you want. + app.Handle(new(controllers.MovieController)) +} diff --git a/_examples/mvc/overview/models/README.md b/_examples/mvc/repository/models/README.md similarity index 79% rename from _examples/mvc/overview/models/README.md rename to _examples/mvc/repository/models/README.md index e940e80d..280664ee 100644 --- a/_examples/mvc/overview/models/README.md +++ b/_examples/mvc/repository/models/README.md @@ -1,20 +1,20 @@ -# Domain Models - -There should be the domain/business-level models. - -Example: - -```go -import "github.com/kataras/iris/v12/_examples/mvc/overview/datamodels" - -type Movie struct { - datamodels.Movie -} - -func (m Movie) Validate() (Movie, error) { - /* do some checks and return an error if that Movie is not valid */ -} -``` - -However, we will use the "datamodels" as the only one models package because +# Domain Models + +There should be the domain/business-level models. + +Example: + +```go +import "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" + +type Movie struct { + datamodels.Movie +} + +func (m Movie) Validate() (Movie, error) { + /* do some checks and return an error if that Movie is not valid */ +} +``` + +However, we will use the "datamodels" as the only one models package because Movie structure we don't need any extra functionality or validation inside it. \ No newline at end of file diff --git a/_examples/mvc/overview/repositories/README.md b/_examples/mvc/repository/repositories/README.md similarity index 74% rename from _examples/mvc/overview/repositories/README.md rename to _examples/mvc/repository/repositories/README.md index bfeff731..6de39915 100644 --- a/_examples/mvc/overview/repositories/README.md +++ b/_examples/mvc/repository/repositories/README.md @@ -1,3 +1,3 @@ -# Repositories - +# Repositories + The package which has direct access to the "datasource" and can manipulate data directly. \ No newline at end of file diff --git a/_examples/mvc/overview/repositories/movie_repository.go b/_examples/mvc/repository/repositories/movie_repository.go similarity index 98% rename from _examples/mvc/overview/repositories/movie_repository.go rename to _examples/mvc/repository/repositories/movie_repository.go index 56dc9dbd..6a79aeee 100644 --- a/_examples/mvc/overview/repositories/movie_repository.go +++ b/_examples/mvc/repository/repositories/movie_repository.go @@ -6,7 +6,7 @@ import ( "errors" "sync" - "github.com/kataras/iris/v12/_examples/mvc/overview/datamodels" + "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" ) // Query represents the visitor and action queries. diff --git a/_examples/mvc/overview/services/README.md b/_examples/mvc/repository/services/README.md similarity index 79% rename from _examples/mvc/overview/services/README.md rename to _examples/mvc/repository/services/README.md index 57208319..520875c9 100644 --- a/_examples/mvc/overview/services/README.md +++ b/_examples/mvc/repository/services/README.md @@ -1,3 +1,3 @@ -# Service Layer - +# Service Layer + The package which has access to call functions from the "repositories" and "models" ("datamodels" only in that simple example). It should contain the domain logic. \ No newline at end of file diff --git a/_examples/mvc/overview/services/movie_service.go b/_examples/mvc/repository/services/movie_service.go similarity index 93% rename from _examples/mvc/overview/services/movie_service.go rename to _examples/mvc/repository/services/movie_service.go index d9973ce7..5e1b0280 100644 --- a/_examples/mvc/overview/services/movie_service.go +++ b/_examples/mvc/repository/services/movie_service.go @@ -3,8 +3,8 @@ package services import ( - "github.com/kataras/iris/v12/_examples/mvc/overview/datamodels" - "github.com/kataras/iris/v12/_examples/mvc/overview/repositories" + "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" + "github.com/kataras/iris/v12/_examples/mvc/repository/repositories" ) // MovieService handles some of the CRUID operations of the movie datamodel. diff --git a/_examples/mvc/overview/web/controllers/hello_controller.go b/_examples/mvc/repository/web/controllers/hello_controller.go similarity index 100% rename from _examples/mvc/overview/web/controllers/hello_controller.go rename to _examples/mvc/repository/web/controllers/hello_controller.go diff --git a/_examples/mvc/overview/web/controllers/movie_controller.go b/_examples/mvc/repository/web/controllers/movie_controller.go similarity index 94% rename from _examples/mvc/overview/web/controllers/movie_controller.go rename to _examples/mvc/repository/web/controllers/movie_controller.go index 157ef142..a800d959 100644 --- a/_examples/mvc/overview/web/controllers/movie_controller.go +++ b/_examples/mvc/repository/web/controllers/movie_controller.go @@ -5,8 +5,8 @@ package controllers import ( "errors" - "github.com/kataras/iris/v12/_examples/mvc/overview/datamodels" - "github.com/kataras/iris/v12/_examples/mvc/overview/services" + "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" + "github.com/kataras/iris/v12/_examples/mvc/repository/services" "github.com/kataras/iris/v12" ) diff --git a/_examples/mvc/overview/web/middleware/basicauth.go b/_examples/mvc/repository/web/middleware/basicauth.go similarity index 100% rename from _examples/mvc/overview/web/middleware/basicauth.go rename to _examples/mvc/repository/web/middleware/basicauth.go diff --git a/_examples/mvc/overview/web/viewmodels/README.md b/_examples/mvc/repository/web/viewmodels/README.md similarity index 93% rename from _examples/mvc/overview/web/viewmodels/README.md rename to _examples/mvc/repository/web/viewmodels/README.md index 181123ea..10520287 100644 --- a/_examples/mvc/overview/web/viewmodels/README.md +++ b/_examples/mvc/repository/web/viewmodels/README.md @@ -1,56 +1,56 @@ -# View Models - -There should be the view models, the structure that the client will be able to see. - -Example: - -```go -import ( - "github.com/kataras/iris/v12/_examples/mvc/overview/datamodels" - - "github.com/kataras/iris/v12" - "github.com/kataras/iris/v12/context" -) - -type Movie struct { - datamodels.Movie -} - -func (m Movie) IsValid() bool { - /* do some checks and return true if it's valid... */ - return m.ID > 0 -} -``` - -Iris is able to convert any custom data Structure into an HTTP Response Dispatcher, -so theoretically, something like the following is permitted if it's really necessary; - -```go -// Dispatch completes the `kataras/iris/mvc#Result` interface. -// Sends a `Movie` as a controlled http response. -// If its ID is zero or less then it returns a 404 not found error -// else it returns its json representation, -// (just like the controller's functions do for custom types by default). -// -// Don't overdo it, the application's logic should not be here. -// It's just one more step of validation before the response, -// simple checks can be added here. -// -// It's just a showcase, -// imagine the potentials this feature gives when designing a bigger application. -// -// This is called where the return value from a controller's method functions -// is type of `Movie`. -// For example the `controllers/movie_controller.go#GetBy`. -func (m Movie) Dispatch(ctx iris.Context) { - if !m.IsValid() { - ctx.NotFound() - return - } - ctx.JSON(m, context.JSON{Indent: " "}) -} -``` - -However, we will use the "datamodels" as the only one models package because -Movie structure doesn't contain any sensitive data, clients are able to see all of its fields +# View Models + +There should be the view models, the structure that the client will be able to see. + +Example: + +```go +import ( + "github.com/kataras/iris/v12/_examples/mvc/repository/datamodels" + + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/context" +) + +type Movie struct { + datamodels.Movie +} + +func (m Movie) IsValid() bool { + /* do some checks and return true if it's valid... */ + return m.ID > 0 +} +``` + +Iris is able to convert any custom data Structure into an HTTP Response Dispatcher, +so theoretically, something like the following is permitted if it's really necessary; + +```go +// Dispatch completes the `kataras/iris/mvc#Result` interface. +// Sends a `Movie` as a controlled http response. +// If its ID is zero or less then it returns a 404 not found error +// else it returns its json representation, +// (just like the controller's functions do for custom types by default). +// +// Don't overdo it, the application's logic should not be here. +// It's just one more step of validation before the response, +// simple checks can be added here. +// +// It's just a showcase, +// imagine the potentials this feature gives when designing a bigger application. +// +// This is called where the return value from a controller's method functions +// is type of `Movie`. +// For example the `controllers/movie_controller.go#GetBy`. +func (m Movie) Dispatch(ctx iris.Context) { + if !m.IsValid() { + ctx.NotFound() + return + } + ctx.JSON(m, context.JSON{Indent: " "}) +} +``` + +However, we will use the "datamodels" as the only one models package because +Movie structure doesn't contain any sensitive data, clients are able to see all of its fields and we don't need any extra functionality or validation inside it. \ No newline at end of file diff --git a/_examples/mvc/overview/web/views/hello/index.html b/_examples/mvc/repository/web/views/hello/index.html similarity index 89% rename from _examples/mvc/overview/web/views/hello/index.html rename to _examples/mvc/repository/web/views/hello/index.html index 7ffb8144..9e7b03d6 100644 --- a/_examples/mvc/overview/web/views/hello/index.html +++ b/_examples/mvc/repository/web/views/hello/index.html @@ -1,12 +1,12 @@ - - - - - {{.Title}} - My App - - - -

{{.MyMessage}}

- - + + + + + {{.Title}} - My App + + + +

{{.MyMessage}}

+ + \ No newline at end of file diff --git a/_examples/mvc/overview/web/views/hello/name.html b/_examples/mvc/repository/web/views/hello/name.html similarity index 89% rename from _examples/mvc/overview/web/views/hello/name.html rename to _examples/mvc/repository/web/views/hello/name.html index 49ec1a25..d6dd5ac6 100644 --- a/_examples/mvc/overview/web/views/hello/name.html +++ b/_examples/mvc/repository/web/views/hello/name.html @@ -1,12 +1,12 @@ - - - - - {{.}}' Portfolio - My App - - - -

Hello {{.}}

- - + + + + + {{.}}' Portfolio - My App + + + +

Hello {{.}}

+ + \ No newline at end of file