diff --git a/HISTORY.md b/HISTORY.md index df24ee28..b5986a14 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -36,7 +36,7 @@ Iris now supports static paths and dynamic paths for the same path prefix with z `app.Get("/profile/{id:int}", handler)` and `app.Get("/profile/create", createHandler)` are not in conflict anymore. -The the rest of the special Iris' routing features, including static & wildcard subdomains are still work like a charm. +The rest of the special Iris' routing features, including static & wildcard subdomains are still work like a charm. > This was one of the most popular community's feature requests. Click [here](https://github.com/kataras/iris/blob/master/_examples/beginner/routing/overview/main.go) to see a trivial example. diff --git a/_examples/beginner/file-logger/main.go b/_examples/beginner/file-logger/main.go index 738e9ded..02d362f4 100644 --- a/_examples/beginner/file-logger/main.go +++ b/_examples/beginner/file-logger/main.go @@ -43,7 +43,7 @@ func main() { // navigate to http://localhost:8080 // and open the ./logs.txt file if err := app.Run(iris.Addr(":8080"), iris.WithoutBanner); err != nil { - app.Log("Shutdown with error: %v", err) + app.Log("Shutdown with error: %v\n", err) } } diff --git a/context/response_recorder.go b/context/response_recorder.go index 3cef4984..03820325 100644 --- a/context/response_recorder.go +++ b/context/response_recorder.go @@ -28,6 +28,12 @@ func releaseResponseRecorder(w *ResponseRecorder) { rrpool.Put(w) } +// A ResponseRecorder is used mostly by context's transactions +// in order to record and change if needed the body, status code and headers. +// +// Developers are not limited to manually ask to record a response. +// To turn on the recorder from a Handler, +// rec := context.Recorder() type ResponseRecorder struct { ResponseWriter // keep track of the body in order to be @@ -39,12 +45,16 @@ type ResponseRecorder struct { var _ ResponseWriter = &ResponseRecorder{} +// BeginRecord accepts its parent ResponseWriter and +// prepares itself, the response recorder, to record and send response to the client. func (w *ResponseRecorder) BeginRecord(underline ResponseWriter) { w.ResponseWriter = underline w.headers = underline.Header() w.ResetBody() } +// EndResponse is auto-called when the whole client's request is done, +// releases the response recorder and its underline ResponseWriter. func (w *ResponseRecorder) EndResponse() { releaseResponseRecorder(w) w.ResponseWriter.EndResponse() diff --git a/core/logger/dev.go b/core/logger/dev.go index 3bbbfc1f..4e479996 100644 --- a/core/logger/dev.go +++ b/core/logger/dev.go @@ -12,6 +12,9 @@ import ( "time" ) +// NewDevLogger returns a new logger of io.Writer which +// formats its log message input and writes it +// to the os.Stdout. func NewDevLogger(omitTimeFor ...string) io.Writer { mu := &sync.Mutex{} // for now and last log lastLog := time.Now() diff --git a/core/logger/logger.go b/core/logger/logger.go index 0aacee98..5e2a4ff0 100644 --- a/core/logger/logger.go +++ b/core/logger/logger.go @@ -26,7 +26,7 @@ type stringWriter interface { WriteString(string) (int, error) } -// Log sends a message to the defined io.Writer logger, it's +// Log sends a message to the defined logger of io.Writer logger, it's // just a help function for internal use but it can be used to a cusotom middleware too. // // See AttachLogger too. diff --git a/core/logger/noop.go b/core/logger/noop.go index 1a737aca..58333ffa 100644 --- a/core/logger/noop.go +++ b/core/logger/noop.go @@ -4,4 +4,6 @@ package logger +// NoOpLogger returns a new, non-operational logger of io.Writer, +// it does nothing any form of input. var NoOpLogger = writerFunc(func([]byte) (int, error) { return -1, nil }) diff --git a/core/router/handler.go b/core/router/handler.go index 60144009..086154ec 100644 --- a/core/router/handler.go +++ b/core/router/handler.go @@ -5,7 +5,6 @@ package router import ( - "fmt" "html" "net/http" "sort" @@ -53,10 +52,6 @@ func (h *routerHandler) getTree(method, subdomain string) *tree { } func (h *routerHandler) addRoute(method, subdomain, path string, handlers context.Handlers) error { - if len(path) == 0 || path[0] != '/' { - return fmt.Errorf("router: path %q must begin with %q", path, "/") - } - t := h.getTree(method, subdomain) if t == nil { diff --git a/core/router/macro/interpreter/lexer/lexer.go b/core/router/macro/interpreter/lexer/lexer.go index ce313498..e57822f3 100644 --- a/core/router/macro/interpreter/lexer/lexer.go +++ b/core/router/macro/interpreter/lexer/lexer.go @@ -8,6 +8,7 @@ import ( "github.com/kataras/iris/core/router/macro/interpreter/token" ) +// Lexer helps us to read/scan characters of a source and resolve their token types. type Lexer struct { input string pos int // current pos in input, current char @@ -15,6 +16,8 @@ type Lexer struct { ch byte // current char under examination } +// New takes a source, series of chars, and returns +// a new, ready to read from the first letter, lexer. func New(src string) *Lexer { l := &Lexer{ input: src, @@ -35,11 +38,13 @@ func (l *Lexer) readChar() { } const ( + // Begin is the symbol which lexer should scan forward to. Begin = '{' // token.LBRACE - End = '}' // token.RBRACE + // End is the symbol which lexer should stop scanning. + End = '}' // token.RBRACE ) -func resolveTokenType(ch byte) token.TokenType { +func resolveTokenType(ch byte) token.Type { switch ch { case Begin: return token.LBRACE @@ -71,6 +76,11 @@ func resolveTokenType(ch byte) token.TokenType { } +// NextToken returns the next token in the series of characters. +// It can be a single symbol, a token type or a literal. +// It's able to return an EOF token too. +// +// It moves the cursor forward. func (l *Lexer) NextToken() (t token.Token) { l.skipWhitespace() typ := resolveTokenType(l.ch) @@ -101,6 +111,13 @@ func (l *Lexer) NextToken() (t token.Token) { return } +// NextDynamicToken doesn't cares about the grammar. +// It reads numbers or any unknown symbol, +// it's being used by parser to skip all characters +// between parameter function's arguemnts inside parenthesis, +// in order to allow custom regexp on the end-language too. +// +// It moves the cursor forward. func (l *Lexer) NextDynamicToken() (t token.Token) { // calculate anything, even spaces. @@ -124,8 +141,11 @@ func (l *Lexer) readIdentifierFuncArgument() string { return l.input[pos:l.pos] } -// useful when we want to peek but no continue, i.e empty param functions 'even()' -func (l *Lexer) PeekNextTokenType() token.TokenType { +// PeekNextTokenType returns only the token type +// of the next character and it does not move forward the cursor. +// It's being used by parser to recognise empty functions, i.e `even()` +// as valid functions with zero input arguments. +func (l *Lexer) PeekNextTokenType() token.Type { if len(l.input)-1 > l.pos { ch := l.input[l.pos] return resolveTokenType(ch) @@ -133,7 +153,7 @@ func (l *Lexer) PeekNextTokenType() token.TokenType { return resolveTokenType(0) // EOF } -func (l *Lexer) newToken(tokenType token.TokenType, lit string) token.Token { +func (l *Lexer) newToken(tokenType token.Type, lit string) token.Token { t := token.Token{ Type: tokenType, Literal: lit, @@ -151,7 +171,7 @@ func (l *Lexer) newToken(tokenType token.TokenType, lit string) token.Token { return t } -func (l *Lexer) newTokenRune(tokenType token.TokenType, ch byte) token.Token { +func (l *Lexer) newTokenRune(tokenType token.Type, ch byte) token.Token { return l.newToken(tokenType, string(ch)) } diff --git a/core/router/macro/interpreter/lexer/lexer_test.go b/core/router/macro/interpreter/lexer/lexer_test.go index dc1750a9..39aa709c 100644 --- a/core/router/macro/interpreter/lexer/lexer_test.go +++ b/core/router/macro/interpreter/lexer/lexer_test.go @@ -14,7 +14,7 @@ func TestNextToken(t *testing.T) { input := `{id:int min(1) max(5) else 404}` tests := []struct { - expectedType token.TokenType + expectedType token.Type expectedLiteral string }{ {token.LBRACE, "{"}, // 0 diff --git a/core/router/macro/interpreter/token/token.go b/core/router/macro/interpreter/token/token.go index a7d595e8..29c6b3f5 100644 --- a/core/router/macro/interpreter/token/token.go +++ b/core/router/macro/interpreter/token/token.go @@ -4,10 +4,12 @@ package token -type TokenType int +// Type is a specific type of int which describes the symbols. +type Type int +// Token describes the letter(s) or symbol, is a result of the lexer. type Token struct { - Type TokenType + Type Type Literal string Start int // including the first char End int // including the last char @@ -33,19 +35,21 @@ const ( COMMA IDENT // string or keyword // Keywords - keywords_start + // keywords_start ELSE // else - keywords_end + // keywords_end INT // 42 ) const eof rune = 0 -var keywords = map[string]TokenType{ +var keywords = map[string]Type{ "else": ELSE, } -func LookupIdent(ident string) TokenType { +// LookupIdent receives a series of chars +// and tries to resolves the token type. +func LookupIdent(ident string) Type { if tok, ok := keywords[ident]; ok { return tok } diff --git a/internal/cmd/README.md b/internal/cmd/README.md deleted file mode 100644 index a31807dc..00000000 --- a/internal/cmd/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Internal CLI - -This folder contains the internal Iris cli program. It's internal because -it will contain generators for the website, maybe versioning for the github branches etc... -So it's useless for the end-developers, but it will be a good place to learn how you can create your own -cli and dynamic-generators programs. - - -Click [here](https://github.com/iris-contrib/community-board/issues/2) to learn its future. - -> When I use the word "generator" I don't mean the go1.4+ generate feature. - -## Don't waste your time, it is not ready yet. - diff --git a/internal/cmd/gen/website/recipe/example/example.go b/internal/cmd/gen/website/recipe/example/example.go deleted file mode 100644 index 0eb68b41..00000000 --- a/internal/cmd/gen/website/recipe/example/example.go +++ /dev/null @@ -1,11 +0,0 @@ -package example - -// Example defines the example link. -type Example struct { - Name string // i.e: Hello World - DataSource string // i.e: https://raw.githubusercontent.com/iris-contrib/examples/master/hello-world.go - Children []Example // if has children the data source is not a source file, it's just a folder, its the template's H2 tag. - // needed for the raw templates, we can do a simple func but lets keep it simple, it's a small template file. - HasChildren bool - HasNotChildren bool -} diff --git a/internal/cmd/gen/website/recipe/example/parser.go b/internal/cmd/gen/website/recipe/example/parser.go deleted file mode 100644 index 5404b6de..00000000 --- a/internal/cmd/gen/website/recipe/example/parser.go +++ /dev/null @@ -1,129 +0,0 @@ -package example - -import ( - "bytes" - "io/ioutil" - "net/http" - "strings" - - "github.com/PuerkitoBio/goquery" - "github.com/kataras/iris/core/errors" - "github.com/microcosm-cc/bluemonday" - "github.com/russross/blackfriday" -) - -// Parse will try to parse and return all examples. -// The input parameter "branch" is used to build -// the raw..iris-contrib/examples/$branch/ -// but it should be the same with -// the kataras/iris/$branch/ for consistency. -func Parse(branch string) (examples []Example, err error) { - var ( - contentsURL = "https://raw.githubusercontent.com/iris-contrib/examples/" + branch - tableOfContents = "Table of contents" - sanitizeMarkdown = true - ) - - // get the raw markdown - readmeURL := contentsURL + "/README.md" - res, err := http.Get(readmeURL) - if err != nil { - return nil, err - } - markdownContents, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, err - } - - // convert it to html - htmlContents := &bytes.Buffer{} - htmlContentsFromMarkdown := blackfriday.MarkdownCommon(markdownContents) - - if len(htmlContentsFromMarkdown) == 0 { - return nil, errors.New("empty html") - } - - if sanitizeMarkdown { - markdownContents = bluemonday.UGCPolicy().SanitizeBytes(markdownContents) - } - - htmlContents.Write(htmlContentsFromMarkdown) - // println("html contents: " + htmlContents.String()) - // get the document from the html - readme, err := goquery.NewDocumentFromReader(htmlContents) - if err != nil { - return nil, err - } - - // or with just one line (but may break if we add another h2, - // so I will do it with the hard and un-readable way for now) - // readme.Find("h2").First().NextAllFiltered("ul").Children().Text() - - // find the header of Table Of Contents, we will need it to take its - // next ul, which should be the examples list. - var tableOfContentsHeader *goquery.Selection - readme.Find("h2").EachWithBreak(func(_ int, n *goquery.Selection) bool { - if nodeContents := n.Text(); nodeContents == tableOfContents { - tableOfContentsHeader = n - return false // break - } - return true - }) - - if tableOfContentsHeader == nil { - return nil, errors.New("table of contents not found using: " + tableOfContents) - } - - // get the list of the examples - tableOfContentsUL := tableOfContentsHeader.NextFiltered("ul") - if tableOfContentsUL == nil { - return nil, errors.New("table of contents list not found") - } - - // iterate over categories example's ... - tableOfContentsUL.Children().EachWithBreak(func(_ int, li *goquery.Selection) bool { - exampleHrefLink := li.Children().First() - if exampleHrefLink == nil { - err = errors.New("example link href is nil, source: " + li.Text()) - return false // break on first failure - } - - name := exampleHrefLink.Text() - - sourcelink, _ := li.Find("a").First().Attr("href") - hasChildren := !strings.HasSuffix(sourcelink, ".go") - - example := Example{ - Name: name, - DataSource: contentsURL + "/" + sourcelink, - HasChildren: hasChildren, - HasNotChildren: !hasChildren, - } - - // search for sub examples - if hasChildren { - li.Find("ul").Children().EachWithBreak(func(_ int, liExample *goquery.Selection) bool { - name := liExample.Text() - liHref := liExample.Find("a").First() - sourcelink, ok := liHref.Attr("href") - if !ok { - err = errors.New(name + "'s source couldn't be found") - return false - } - - subExample := Example{ - Name: name, - DataSource: contentsURL + "/" + sourcelink, - } - - example.Children = append(example.Children, subExample) - return true - }) - - } - - examples = append(examples, example) - return true - }) - return examples, err -} diff --git a/internal/cmd/gen/website/recipe/recipe.go b/internal/cmd/gen/website/recipe/recipe.go deleted file mode 100644 index 9f99ca4c..00000000 --- a/internal/cmd/gen/website/recipe/recipe.go +++ /dev/null @@ -1,30 +0,0 @@ -package recipe - -import ( - "github.com/kataras/iris/internal/cmd/gen/website/recipe/example" -) - -type Recipe struct { - Branch string // i.e "master", "v6"... - Examples []example.Example -} - -// NewRecipe accepts the "branch", i.e: "master", "v6", "v7"... -// and returns a new Recipe pointer with its generated and parsed examples. -func NewRecipe(branch string) (*Recipe, error) { - if branch == "" { - branch = "master" - } - - examples, err := example.Parse(branch) - if err != nil { - return nil, err - } - - r := &Recipe{ - Branch: branch, - Examples: examples, - } - - return r, nil -} diff --git a/internal/cmd/gen/website/recipe/recipe.html b/internal/cmd/gen/website/recipe/recipe.html deleted file mode 100644 index 8256b577..00000000 --- a/internal/cmd/gen/website/recipe/recipe.html +++ /dev/null @@ -1,149 +0,0 @@ - - - - - Iris - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
- - - -
- - - - -
- {{ range $key, $example := .Examples -}} - {{ if $example.HasChildren }} -

- - {{$example.Name}} -

- {{ range $key, $child := $example.Children -}} -

- - {{ $child.Name }} -

-

-                {{- end }} {{- end }} 
-                 
-                {{ if $example.HasNotChildren }}
-                

- - {{ $example.Name }} -

-

-                {{- end }}
-            {{- end }}
-
-            
-        
- -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/internal/cmd/main.go b/internal/cmd/main.go deleted file mode 100644 index f68b06fa..00000000 --- a/internal/cmd/main.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "os" - "text/template" - - "github.com/kataras/iris/internal/cmd/gen/website/recipe/example" -) - -const tmpl = ` -{{ range $key, $example := . -}} -{{ if $example.HasChildren }} -

{{$example.Name}}

-{{ range $key, $child := $example.Children -}} -

- - {{ $child.Name }} -

-

-{{- end }}
-{{- end }}
-{{ if .HasNotChildren }}
-

- - {{ $example.Name }} -

-

-{{- end }}
-{{- end }}
-`
-
-func main() {
-	// just for testing, the cli will be coded when I finish at least with this one command.
-	examples, err := example.Parse("master")
-	if err != nil {
-		println(err.Error())
-		return
-	}
-
-	text, err := template.New("").Parse(tmpl)
-	if err != nil {
-		println(err.Error())
-	}
-
-	if err := text.Execute(os.Stdout, examples); err != nil {
-		println("err in template : " + err.Error())
-	}
-}
diff --git a/iris.go b/iris.go
index 1179c305..ec0c837d 100644
--- a/iris.go
+++ b/iris.go
@@ -91,7 +91,7 @@ func New() *Application {
 
 	app := &Application{
 		config:     &config,
-		logger:     logger.NewDevLogger(),
+		logger:     logger.NewDevLogger(banner),
 		APIBuilder: router.NewAPIBuilder(),
 		Router:     router.NewRouter(),
 	}
diff --git a/middleware/logger/config.go b/middleware/logger/config.go
index 53f20543..cfe8bca3 100644
--- a/middleware/logger/config.go
+++ b/middleware/logger/config.go
@@ -19,7 +19,7 @@ type Config struct {
 	Path bool
 }
 
-// DefaultConfig returns an options which all properties are true except EnableColors
-func DefaultConfigurationReadOnly() Config {
+// DefaultConfiguration returns an options which all properties are true except EnableColors
+func DefaultConfiguration() Config {
 	return Config{true, true, true, true}
 }
diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go
index 0123ff90..c6591ae9 100644
--- a/middleware/logger/logger.go
+++ b/middleware/logger/logger.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package loger provides request logging via middleware. See _examples/beginner/request-logger
+// Package logger provides request logging via middleware. See _examples/beginner/request-logger
 package logger
 
 import (
@@ -58,7 +58,7 @@ func (l *requestLoggerMiddleware) ServeHTTP(ctx context.Context) {
 //
 // Receives an optional configuation.
 func New(cfg ...Config) context.Handler {
-	c := DefaultConfigurationReadOnly()
+	c := DefaultConfiguration()
 	if len(cfg) > 0 {
 		c = cfg[0]
 	}