Skip to content
This repository has been archived by the owner on Oct 29, 2021. It is now read-only.

Commit

Permalink
Redesign the server application structure
Browse files Browse the repository at this point in the history
There is no singleton anti-pattern now. Server app become extendable
and more idiomatic. It moved to flat package structure instead of
some different packages for any different needs. Command line app
now moved to top level main file.
  • Loading branch information
olebedev committed Aug 16, 2015
1 parent fb0a01f commit 0ad128d
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 181 deletions.
12 changes: 0 additions & 12 deletions src/app/app.go

This file was deleted.

7 changes: 0 additions & 7 deletions src/app/client/components/homepage/index.js
Expand Up @@ -19,13 +19,6 @@ export default class Homepage extends Component {
<p className={p}>
Please take a look at <Link className={link} to='/docs'>usage</Link> page.
</p>
{/*
<p className={p}>
<button onClick={actions.incrFontSize} className={button}>+</button>
&nbsp;
<button onClick={actions.decrFontSize} className={button}>-</button>
</p>
*/}
</div>;
}

Expand Down
49 changes: 49 additions & 0 deletions src/app/main.go
@@ -0,0 +1,49 @@
package main

import (
"app/server"
"os"
"runtime"

"github.com/codegangsta/cli"
)

func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
Run(os.Args)
}

// Run creates, configures and runs
// main cli.App
func Run(args []string) {

app := cli.NewApp()
app.Name = "app"
app.Usage = "React server application"

configFlag := cli.StringFlag{
Name: "config, c",
Value: "local",
Usage: "configuration section name",
EnvVar: "CONFIG",
}

app.Commands = []cli.Command{
{
Name: "run",
Usage: "Runs server",
Action: RunServer,
Flags: []cli.Flag{configFlag},
},
}
app.Run(args)
}

// RunServer creates, configures and runs
// main server.App
func RunServer(c *cli.Context) {
app := server.NewApp(server.AppOptions{
Config: c.String("config"),
})
app.Run()
}
21 changes: 21 additions & 0 deletions src/app/server/api.go
@@ -0,0 +1,21 @@
package server

import (
"github.com/gin-gonic/gin"
)

// Api is a defined as struct bundle
// for api. Feel free to organize
// your app as you wish.
type Api struct{}

// Bind attaches api routes
func (api *Api) Bind(group *gin.RouterGroup) {
group.GET("/v1/conf", api.ConfHandler)
}

// Serve the app config, for example
func (_ *Api) ConfHandler(c *gin.Context) {
app := c.MustGet("app").(*App)
c.JSON(200, app.Conf.Root)
}
13 changes: 0 additions & 13 deletions src/app/server/api/api.go

This file was deleted.

113 changes: 113 additions & 0 deletions src/app/server/app.go
@@ -0,0 +1,113 @@
package server

import (
"app/server/data"

"github.com/elazarl/go-bindata-assetfs"
"github.com/gin-gonic/gin"
"github.com/nu7hatch/gouuid"
"github.com/olebedev/config"
)

// There is no singleton anti-pattern,
// all variables defined locally inside
// this struct.
type App struct {
Engine *gin.Engine
Conf *config.Config
React *React
Api *Api
}

// NewApp returns initialized struct
// of main server application.
func NewApp(opts ...AppOptions) *App {
var options AppOptions
for _, i := range opts {
options = i
i.init()
break
}

// Parse config yaml string from ./conf.go
conf, err := config.ParseYaml(confString)
Must(err)
// Choise a config section by given string
conf, err = conf.Get(options.Config)
Must(err)

// Parse environ variables for defined
// in config constants
conf.Env()

// Set up gin
if !conf.UBool("debug") {
gin.SetMode(gin.ReleaseMode)
}

// Make an engine
engine := gin.Default()

// Initialize the application
app := &App{
Conf: conf,
Engine: engine,
Api: &Api{},
React: NewReact(
conf.UBool("debug"),
engine,
),
}

// Define routes and middlewares
app.Engine.StaticFS("/static", &assetfs.AssetFS{
Asset: data.Asset,
AssetDir: data.AssetDir,
Prefix: "static",
})

// Map app struct to access from request handlers
// and middlewares
app.Engine.Use(func(c *gin.Context) {
c.Set("app", app)
})

// Avoid favicon react handling
app.Engine.GET("/favicon.ico", func(c *gin.Context) {
c.Redirect(301, "/static/images/favicon.ico")
})

// Bind api hadling for URL api.prefix
app.Api.Bind(
app.Engine.Group(
app.Conf.UString("api.prefix"),
),
)

// Map uuid for every requests
app.Engine.Use(func(c *gin.Context) {
id, _ := uuid.NewV4()
c.Set("uuid", id)
})

// Handle all not found routes via react app
app.Engine.NoRoute(app.React.Handle)

return app
}

// Run runs the app
func (app *App) Run() {
Must(app.Engine.Run(":" + app.Conf.UString("port")))
}

// AppOptions is options struct
type AppOptions struct {
Config string
}

func (ao *AppOptions) init() {
if ao.Config == "" {
ao.Config = "local"
}
}
66 changes: 0 additions & 66 deletions src/app/server/commands.go

This file was deleted.

28 changes: 11 additions & 17 deletions src/app/server/conf.go
@@ -1,31 +1,25 @@
package server

import (
. "app/server/utils"

"github.com/olebedev/config"
)

var conf *config.Config

func init() {
c, err := config.ParseYaml(`
// Most easiest way to configure
// an application is define config as
// yaml string and then parse it into
// map.
// How it works see here:
// https://github.com/olebedev/config
var confString = `
local:
debug: true
port: 5000
title: lmbd
db: ./db.sqlite
title: Go Starter Kit
api:
prefix: /api
production:
debug: false
port: 5000
title: lmbd
title: Go Starter Kit
api:
prefix: /api
`)
Must(err)
conf = c
}
`

0 comments on commit 0ad128d

Please sign in to comment.