From 503957e54af720082fd6713e46c13f1d4d1924de Mon Sep 17 00:00:00 2001 From: Ana Krivokapic Date: Mon, 9 Nov 2020 21:39:26 +0100 Subject: [PATCH 1/6] Add style guide for Go --- _includes/search.html | 3 +- _posts/2020-11-09-golang-style-guide.adoc | 105 ++++++++++++++++++ assets/img/guides/go-logo-blue.png | Bin 0 -> 15475 bytes .../logos-technologies/style-guides-logo.png | Bin 0 -> 1188 bytes 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 _posts/2020-11-09-golang-style-guide.adoc create mode 100644 assets/img/guides/go-logo-blue.png create mode 100644 assets/img/logos-technologies/style-guides-logo.png diff --git a/_includes/search.html b/_includes/search.html index 504d8cd7f..0f095181b 100644 --- a/_includes/search.html +++ b/_includes/search.html @@ -49,7 +49,8 @@
aws logo
aws logo
-
aws logo
+
aws logo
+
style guides logo
diff --git a/_posts/2020-11-09-golang-style-guide.adoc b/_posts/2020-11-09-golang-style-guide.adoc new file mode 100644 index 000000000..53f8e6161 --- /dev/null +++ b/_posts/2020-11-09-golang-style-guide.adoc @@ -0,0 +1,105 @@ +--- +title: Go Style Guide +categories: Styleguides +image: /assets/img/guides/go-logo-blue.png +excerpt: The style guide for the Go programming language that is used by all Gruntwork-owned code repositories that contain Go code. +tags: ["go", "golang", "code", "style"] +cloud: ["styleguides"] +redirect_from: /static/guides/styleguides/golang-style-guide/ +--- +:page-type: guide +:page-layout: post + +:toc: +:toc-placement!: + +// GitHub specific settings. See https://gist.github.com/dcode/0cfbf2699a1fe9b46ff04c41721dda74 for details. +ifdef::env-github[] +:tip-caption: :bulb: +:note-caption: :information_source: +:important-caption: :heavy_exclamation_mark: +:caution-caption: :fire: +:warning-caption: :warning: +toc::[] +endif::[] + +== Intro +This is the style guide for the Go programming language. It aims to help us ensure that the code we write is +clear, readable, idiomatic Go code. The conventions detailed in this guide are our preferences and should be thought of +as guidelines rather than hard rules. + +== Starting point +We use the excellent https://golang.org/doc/effective_go.html[Effective Go] guide as the starting point for our style +guide. Unless explicitly mentioned otherwise, we default to following the conventions outlined in it. + +Another helpful resource is the https://github.com/golang/go/wiki/CodeReviewComments[CodeReviewComments] section of the +Go GitHub wiki page. + +Below you will find the list of conventions that differ from the above mentioned documents, either because they are +specific to Gruntwork, or because we consciously chose to go against what Effective Go and CodeReviewComments recommend. + +== Additional conventions +=== General +Before committing any Go code, run `go fmt` and `goimports`. We run these as part of the CI build, so this will prevent +your build from failing. + +=== Control structures +When possible, avoid `else` and nested `if` statements. Prefer multiple `return` statements over nested code. +This makes the code more readable and avoids complexity. + +E.g: +[source,go] +---- +// Do this +if something { + doThis() + return this +} +if orOther { + return that +} +return theOther +---- + +[source,go] +---- +// Don't do this +if something { + doThis() +} else { + if orOther { + return that + } +} +---- + +=== Error handling +Prefer using the `errors` standard library package for handling single errors and `hashicorp/go-multierror` for +handling multiple errors. + +=== Pointer usage +Prefer using value type over pointers, unless necessary. + +=== Testing +All files containing business logic must have a corresponding unit test file, and we aim to have 100% test coverage. + +Unless there's a reason not to, tests should run in parallel. This is done by including a call to `t.Parallel` in the test function. + +=== Naming things +Prefer descriptive names over short ones. In particular, avoid one-letter variable names. + +If a name contains an acronym, only capitalize the first letter of the acronym. E.g. use `someEksCluster` rather than +`someEKSCluster`. We go against the https://github.com/golang/go/wiki/CodeReviewComments#initialisms[recommendation] +here in order to follow the convention already in use by some third party packages we heavily rely on (e.g. `aws-sdk-go`). + +==== Constants +Since many languages use `ALL_CAPS` for constants, it is worth calling out explicitly that +https://golang.org/doc/effective_go.html#mixed-caps[Effective Go] recommends using `MixedCaps` for all names, including constants. +Therefore, `region` or `testRegion` for private constants and `Region` or `TestRegion` for public ones is preferred over +`REGION` or `TEST_REGION`. + +=== Repo-specific conventions +==== terratest +Note the existence of methods in terratest which are suffixed with the letter `E`, e.g. +https://github.com/gruntwork-io/terratest/blob/master/modules/aws/account.go#L23[GetAccountIdE]. The suffix `E` +signifies that the method returns an error as the last return value. diff --git a/assets/img/guides/go-logo-blue.png b/assets/img/guides/go-logo-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..d6d98c3bde8f96b092a27a2b262117d66e6501c7 GIT binary patch literal 15475 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 literal 0 HcmV?d00001 diff --git a/assets/img/logos-technologies/style-guides-logo.png b/assets/img/logos-technologies/style-guides-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c9e9a8a5fd5557e11271218cbf8ede6f4cb1a9da GIT binary patch literal 1188 zcmeAS@N?(olHy`uVBq!ia0vp^DIm~54}uFyTH2cIwg}h>2`Q+s2$?7-y-;#=WfoM&ZOpy};euySFr(cWv^X+WKyBpsb~UO|7#*5 zHJ+{zNQ9#)Yd1~lcp<9914cbJRpT$Ke@(lnUV0L)1i~nB^{_8vDt*~BLHB_2nbogi zCl;@4jqbao_KflT&r93WPyANgax6ZoXx~jv9i5tg_8%03^tNVx7A=z8oxl9ivfxuw zKIX5Uu+``7?N=)#r(Tv++@JLQwbPvvt(PqmSbf$$xG!hAILk`$i9y+Xx!|Br`YnDO zJNxb~e_VKZ=Co_vk?nWqe?M?)g`}_l>()7k-<|)t<7JI#pi>(2XWL`ZEPIZ=JMY&2 z(OJRigv@HEM9893w*Qh4&$+`dAr#w7;VlL&3>%8e7#`oiLeJ@p1be&OO*2{ zFOc}0aklukPpsPgQ$@VKCW_ex{ONhs@`lwu-K+iA9(7ZH7JWu{4PWhpRM$N5O?R9x z?wH_uAoEPt@->I`4Yzj(7S`Loc{Aa8{^F=p)jsmCxa8W7wR|z;&-g!W%f#|e<`1m- zb9IlOaXtFO{+zu*Zi5m(#Kfr?*G%cm*XbO9i2|uS7rr=I_sM+a{UWqtPh&| z-!|cUNA43A(ND}D!aaSqE89sP&*>GpZTUpAOxQlcvE|5)E5&6NxqBJ+eUz~OviAD! z3A{&U@)X~hdV8kB+bfZCa%MQjymxvRt|C#jljB+3|AU$3xy-p?XZ86d>f$yg{is;} zt~k|9=l4eT2VP|@OWeOMsFv&wTtDIKk)5e$59gGM+`cWoVD22N?Q6aocif)kSv&vo zG44NkAbdE3-1G~woUwtIo_r2bc2an=RBqLxC0nD_U0?aM&2I15 z&QS68$c=}J=XJN#6kkl;_4d2F^AXD@b6&4rloBv8`t`Q^v8Un!u8LkdoFgN4{%dI9 zBd*N9+^dv+YTse}Ah4NZMRRoEC%%Y>n#<2d>M>h=<0=;kTCK6td6UyAEw8$+Srg?a zi9QOOa%X9sYiG(N5hLy$y>p*WTp{^W*;8?G$P^ECPsRF#=N362S>Z>k7rAJiBr3Yv=I8;iL^1BE81v>aKX(dlU9q_*MnE*bN=2`b7@ILqP` zwY`!iwM>xv_Ue`8vNy|nRTNpy{O)utyHXoJN$J{;*Y!6#`%VAl{{QTAa-mZTKJ@>1 ofJ9X*hcgI)Gx{P8*$2Xo=L Date: Tue, 10 Nov 2020 17:40:01 +0100 Subject: [PATCH 2/6] Address review comments --- _includes/search.html | 1 - _posts/2020-11-09-golang-style-guide.adoc | 216 +++++++++++++++++- .../logos-technologies/style-guides-logo.png | Bin 1188 -> 0 bytes 3 files changed, 204 insertions(+), 13 deletions(-) delete mode 100644 assets/img/logos-technologies/style-guides-logo.png diff --git a/_includes/search.html b/_includes/search.html index 0f095181b..6a70a47d7 100644 --- a/_includes/search.html +++ b/_includes/search.html @@ -50,7 +50,6 @@
aws logo
aws logo
aws logo
-
style guides logo
diff --git a/_posts/2020-11-09-golang-style-guide.adoc b/_posts/2020-11-09-golang-style-guide.adoc index 53f8e6161..15d148e40 100644 --- a/_posts/2020-11-09-golang-style-guide.adoc +++ b/_posts/2020-11-09-golang-style-guide.adoc @@ -4,7 +4,7 @@ categories: Styleguides image: /assets/img/guides/go-logo-blue.png excerpt: The style guide for the Go programming language that is used by all Gruntwork-owned code repositories that contain Go code. tags: ["go", "golang", "code", "style"] -cloud: ["styleguides"] +cloud: ["aws", "gcp"] redirect_from: /static/guides/styleguides/golang-style-guide/ --- :page-type: guide @@ -24,13 +24,13 @@ toc::[] endif::[] == Intro -This is the style guide for the Go programming language. It aims to help us ensure that the code we write is +This is Gruntwork's style guide for the Go programming language. It aims to help us ensure that the code we write is clear, readable, idiomatic Go code. The conventions detailed in this guide are our preferences and should be thought of as guidelines rather than hard rules. == Starting point We use the excellent https://golang.org/doc/effective_go.html[Effective Go] guide as the starting point for our style -guide. Unless explicitly mentioned otherwise, we default to following the conventions outlined in it. +guide. **Unless explicitly mentioned otherwise, we default to following the conventions outlined in Effective Go.** Another helpful resource is the https://github.com/golang/go/wiki/CodeReviewComments[CodeReviewComments] section of the Go GitHub wiki page. @@ -40,12 +40,16 @@ specific to Gruntwork, or because we consciously chose to go against what Effect == Additional conventions === General -Before committing any Go code, run `go fmt` and `goimports`. We run these as part of the CI build, so this will prevent -your build from failing. +Before committing any Go code, run `go fmt`. We run this as part of the CI build, so this will prevent your build from failing. + +=== Comments +Every public function `Foo` should have a `//` comment of the style `Foo `, +where explanation says what `Foo` does, and more importantly, why it does that, what assumptions it makes, and other +aspects not obvious from the function name. === Control structures -When possible, avoid `else` and nested `if` statements. Prefer multiple `return` statements over nested code. -This makes the code more readable and avoids complexity. +When possible, avoid nesting `if`/`else` statements. (Of course, a single, non-nested `if`/`else` is perfectly fine.) +Prefer multiple `return` statements over nested code. This makes the code more readable and avoids complexity. E.g: [source,go] @@ -77,16 +81,131 @@ if something { Prefer using the `errors` standard library package for handling single errors and `hashicorp/go-multierror` for handling multiple errors. +[source,go] +---- +// Do this +func (params Params) Validate() error { + var merr = multierror.NewPrefixed("some prefix") + if params.ID == "" { + merr = merr.Append(errors.New("ID cannot be empty")) + } + + if params.AnotherRequiredArg == nil { + merr = merr.Append(errors.New("this arg is required")) + } + + return merr.ErrorOrNil() +} +---- + +[source,go] +---- +// Don't do this +func (params Params) Validate() error { + if params.ID == "" { + return errors.New("ID cannot be empty") + } + + if params.AnotherRequiredArg == nil { + return errors.New("this arg is required") + } + + return nil +} +---- + +https://github.com/golang/go/wiki/CodeReviewComments#dont-panic[Don't panic] (_hat tip, Douglas Adams_). Any method that +can have an error should return that error as its final value. This should be passed up through each layer, which can +decide what to do with it, all the way up to the very entrypoint of the app (or `main`) if necessary. +You should almost NEVER use `panic`. + +Use custom error types. Create your own types that implement the `error` interface so that error messages are clear +and have well-defined types you can check against. For some examples of this, see e.g. the custom erros in the +https://github.com/gruntwork-io/terratest/blob/master/modules/aws/errors.go[aws] package of `terratest`. + +Include stack traces. In most of our code, we have to wrap errors with +https://github.com/gruntwork-io/gruntwork-cli/blob/master/errors/errors.go#L22[`errors.WithStackTrace(e)`] to add the stack trace. +Go annoyingly doesn't do this by default, but without it, sorting out an error can be very tricky. + === Pointer usage -Prefer using value type over pointers, unless necessary. +Prefer using value type over pointers, unless necessary. Generally speaking, there are only a few cases where pointer +usage would be justified: + +1. You have very large structs containing lots of data. Instead of copying these around, passing pointers may be more + efficient. +2. You need to mutate a variable you passed to a function. Generally speaking, you should avoid this anyway (see the + section on immutability below), but sometimes it is necessary (think "rename" methods, etc). +3. You need to return `nil` as the default zero value (the default zero value for pointers is `nil`, as opposed to other data + types that have non-nil default zero values). === Testing -All files containing business logic must have a corresponding unit test file, and we aim to have 100% test coverage. +In terms of testing, we don't necessarily aim for 100% test coverage. Rather, our goal is to have enough testing to give +us confidence that our code works and allow us to update it quickly. When adding tests, keep in mind these factors: + +1. The likelihood of bugs: some code is harder to get right than others, and merits correspondingly more tests. +2. The cost of bugs: some bugs are much more costly than others — e.g., bugs in payment systems or security functionality — + so it makes sense to invest more in testing there. +3. The cost of tests: some types of tests are cheap and easy to write and maintain, whereas others are very costly and brittle. Unless there's a reason not to, tests should run in parallel. This is done by including a call to `t.Parallel` in the test function. +==== Setup and Teardown pattern + +In some cases you will want to write a group of tests that use a common resource, such as a Docker image or VPC. +In this case, you will want to setup the common resource once, run a bunch of tests, and then teardown the resource. +To achieve this, you can follow https://blog.golang.org/subtests[the subtest pattern] of Go. + +Always use https://dave.cheney.net/2019/05/07/prefer-table-driven-tests[table driven tests] where possible to make the +subtest routines maintainable. Briefly, this means that you group your test cases using a test struct that reflects +the unique parameters of the test cases. Then you can conveniently loop over the test cases in parallel, taking advantage +of uniformity and speed. + +Note that the subtest pattern has gotchas when running tests in parallel: + +- The main test function will not wait for the subtest to run if it uses `t.Parallel`. To avoid this, you need to wrap + the parallel subtests in a synchronous, blocking subtest. In the example below, the `group` subtest is synchronous + (no call to `t.Parallel`) and thus the main function will wait until that test finishes. The `group` test does not + finish until all the subtests it spawns are finished, even if they are non-blocking and parallel, and thus the + `tearDownVPC` call does not happen until all subtests are done. +- If you are using table driven tests, the range variable will be updated to the next iteration before it is used within + the subtest. That is, in the example below, if we did not have the `testCase := testCase` line in the range block, + the `testCase` reference used in the subtest after the `t.Parallel` call will correspond to the last `testCase` in the + `testCases` list. To avoid this, we create a new variable in the scope of the range block so that it does not get + updated during the loop. + +Example: + +[source,go] +---- +func TestECS(t *testing.T) { + t.Parallel() + + defer tearDownVPC() + deployVPC() + + // Wrap the parallel tests in a synchronous test group to + // ensure that the main test function (the one calling + // `tearDownVPC` and `deployVPC`) waits until all the + // subtests are done before running the deferred function. + t.Run("group", func(t *testing.T) { + for _, testCase := range testCases { + // To avoid the range variable from getting updated in the + // parallel tests, we bind a new name that is within the + // scope of the for block. + testCase := testCase + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + testCase.testCode() + }) + } + }) +} +---- + === Naming things -Prefer descriptive names over short ones. In particular, avoid one-letter variable names. +Prefer descriptive names over short ones. In particular, avoid one-letter variable names, other than in case of very well +known and widely understood conventions, such as `i` for `index` (e.g. in a loop). A more descriptive name helps with +code understanding and maintenance, at very little cost, given the auto-complete feature in most IDEs and editors. If a name contains an acronym, only capitalize the first letter of the acronym. E.g. use `someEksCluster` rather than `someEKSCluster`. We go against the https://github.com/golang/go/wiki/CodeReviewComments#initialisms[recommendation] @@ -98,8 +217,81 @@ https://golang.org/doc/effective_go.html#mixed-caps[Effective Go] recommends usi Therefore, `region` or `testRegion` for private constants and `Region` or `TestRegion` for public ones is preferred over `REGION` or `TEST_REGION`. +=== Functional programming practices + +==== Immutability +Prefer returning a new value rather than mutating an existing one. + +[source,go] +---- +// Don't do this +var result int = 0 + +func main() { + add(1, 1, &result) + fmt.Println(result) +} + +func add(a, b int, result *int) { + *result = a + b +} +---- + +[source,go] +---- +// Do this instead +func main() { + fmt.Println(add(1, 1)) +} + +func add(a, b int) int { + return a + b +} +---- + +==== Pure functions +Prefer functions that take all their inputs as function parameters, instead of reading those inputs via side effects +(e.g., reading from disk or the network or global vars), and whose entire behavior is to return values +(note: errors are values too!), rather than performing side effects (e.g. by writing to disk or the network or global +vars). Of course, you can't avoid side effects forever if you want your code to do something useful, but try to do as +much of your logic with pure functions as you can, try to pass everything around as explicit parameters and return +everything as explicit values, and centralize the side effecting code to a few isolated places. + +==== Composition +Build your code out of small, reusable, named functions, that you compose together. + + +[source,go] +---- +// Don't do this +func main() { + fmt.Println(mulOfSums(1, 1)) +} + +func mulOfSums(a, b int) int { + return (a + b) * (a + b) +} +---- + +[source,go] +---- +// Do this instead +func main() { + fmt.Println(mul(add(1, 1), add(1, 1))) +} + +func add(a, b int) int { + return a + b +} + +func mul(a, b int) int { + return a * b +} +---- + === Repo-specific conventions ==== terratest Note the existence of methods in terratest which are suffixed with the letter `E`, e.g. -https://github.com/gruntwork-io/terratest/blob/master/modules/aws/account.go#L23[GetAccountIdE]. The suffix `E` -signifies that the method returns an error as the last return value. +https://github.com/gruntwork-io/terratest/blob/master/modules/aws/account.go#L23[GetAccountIdE]. Methods that have the +suffix `E` return an error as the last return value; methods without `E` mark the test as failed +(e.g., via calling `t.Fail()`) instead of returning an error. diff --git a/assets/img/logos-technologies/style-guides-logo.png b/assets/img/logos-technologies/style-guides-logo.png deleted file mode 100644 index c9e9a8a5fd5557e11271218cbf8ede6f4cb1a9da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1188 zcmeAS@N?(olHy`uVBq!ia0vp^DIm~54}uFyTH2cIwg}h>2`Q+s2$?7-y-;#=WfoM&ZOpy};euySFr(cWv^X+WKyBpsb~UO|7#*5 zHJ+{zNQ9#)Yd1~lcp<9914cbJRpT$Ke@(lnUV0L)1i~nB^{_8vDt*~BLHB_2nbogi zCl;@4jqbao_KflT&r93WPyANgax6ZoXx~jv9i5tg_8%03^tNVx7A=z8oxl9ivfxuw zKIX5Uu+``7?N=)#r(Tv++@JLQwbPvvt(PqmSbf$$xG!hAILk`$i9y+Xx!|Br`YnDO zJNxb~e_VKZ=Co_vk?nWqe?M?)g`}_l>()7k-<|)t<7JI#pi>(2XWL`ZEPIZ=JMY&2 z(OJRigv@HEM9893w*Qh4&$+`dAr#w7;VlL&3>%8e7#`oiLeJ@p1be&OO*2{ zFOc}0aklukPpsPgQ$@VKCW_ex{ONhs@`lwu-K+iA9(7ZH7JWu{4PWhpRM$N5O?R9x z?wH_uAoEPt@->I`4Yzj(7S`Loc{Aa8{^F=p)jsmCxa8W7wR|z;&-g!W%f#|e<`1m- zb9IlOaXtFO{+zu*Zi5m(#Kfr?*G%cm*XbO9i2|uS7rr=I_sM+a{UWqtPh&| z-!|cUNA43A(ND}D!aaSqE89sP&*>GpZTUpAOxQlcvE|5)E5&6NxqBJ+eUz~OviAD! z3A{&U@)X~hdV8kB+bfZCa%MQjymxvRt|C#jljB+3|AU$3xy-p?XZ86d>f$yg{is;} zt~k|9=l4eT2VP|@OWeOMsFv&wTtDIKk)5e$59gGM+`cWoVD22N?Q6aocif)kSv&vo zG44NkAbdE3-1G~woUwtIo_r2bc2an=RBqLxC0nD_U0?aM&2I15 z&QS68$c=}J=XJN#6kkl;_4d2F^AXD@b6&4rloBv8`t`Q^v8Un!u8LkdoFgN4{%dI9 zBd*N9+^dv+YTse}Ah4NZMRRoEC%%Y>n#<2d>M>h=<0=;kTCK6td6UyAEw8$+Srg?a zi9QOOa%X9sYiG(N5hLy$y>p*WTp{^W*;8?G$P^ECPsRF#=N362S>Z>k7rAJiBr3Yv=I8;iL^1BE81v>aKX(dlU9q_*MnE*bN=2`b7@ILqP` zwY`!iwM>xv_Ue`8vNy|nRTNpy{O)utyHXoJN$J{;*Y!6#`%VAl{{QTAa-mZTKJ@>1 ofJ9X*hcgI)Gx{P8*$2Xo=L Date: Wed, 11 Nov 2020 13:12:40 +0100 Subject: [PATCH 3/6] Replace mentions of Hashicorp's multierror with ours --- _posts/2020-11-09-golang-style-guide.adoc | 39 +++-------------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/_posts/2020-11-09-golang-style-guide.adoc b/_posts/2020-11-09-golang-style-guide.adoc index 15d148e40..8772acd36 100644 --- a/_posts/2020-11-09-golang-style-guide.adoc +++ b/_posts/2020-11-09-golang-style-guide.adoc @@ -78,41 +78,10 @@ if something { ---- === Error handling -Prefer using the `errors` standard library package for handling single errors and `hashicorp/go-multierror` for -handling multiple errors. - -[source,go] ----- -// Do this -func (params Params) Validate() error { - var merr = multierror.NewPrefixed("some prefix") - if params.ID == "" { - merr = merr.Append(errors.New("ID cannot be empty")) - } - - if params.AnotherRequiredArg == nil { - merr = merr.Append(errors.New("this arg is required")) - } - - return merr.ErrorOrNil() -} ----- - -[source,go] ----- -// Don't do this -func (params Params) Validate() error { - if params.ID == "" { - return errors.New("ID cannot be empty") - } - - if params.AnotherRequiredArg == nil { - return errors.New("this arg is required") - } - - return nil -} ----- +Prefer using the `errors` standard library package for handling single errors. For operations that can produce multiple +errors, leverage the https://github.com/gruntwork-io/terragrunt/blob/master/errors/multierror.go[`MultiError`] +package by https://github.com/gruntwork-io/terragrunt/blob/cb369119bf5c6f3031e914e8554ffe056dcf9e22/cli/hclfmt.go#L62[accumulating +all the errors into a single `MultiError` and returning that], rather than returning every error individually as it comes up. https://github.com/golang/go/wiki/CodeReviewComments#dont-panic[Don't panic] (_hat tip, Douglas Adams_). Any method that can have an error should return that error as its final value. This should be passed up through each layer, which can From 9221467807eb8cfd7c9b6beebfef7af98cc3b494 Mon Sep 17 00:00:00 2001 From: Eugene K Date: Tue, 17 Nov 2020 10:02:58 -0500 Subject: [PATCH 4/6] Update post to use `category` which expects a single string vs `categories` which expects an array of strings. If you pass `Style Guides` as a value, then it will treat it like: `["Style", "Guides"]` In the UI we take just the first value out of this array and it will end up just displaying "Style". You either have to provide the value like I did or to do: `categories: ["Style Guides"]` --- _posts/2020-11-09-golang-style-guide.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_posts/2020-11-09-golang-style-guide.adoc b/_posts/2020-11-09-golang-style-guide.adoc index 8772acd36..e8adaacc2 100644 --- a/_posts/2020-11-09-golang-style-guide.adoc +++ b/_posts/2020-11-09-golang-style-guide.adoc @@ -1,6 +1,6 @@ --- title: Go Style Guide -categories: Styleguides +category: Style Guides image: /assets/img/guides/go-logo-blue.png excerpt: The style guide for the Go programming language that is used by all Gruntwork-owned code repositories that contain Go code. tags: ["go", "golang", "code", "style"] From d5654c77db778ec3e3a30096d533dca5b1e3cad1 Mon Sep 17 00:00:00 2001 From: Eugene K Date: Tue, 17 Nov 2020 10:03:57 -0500 Subject: [PATCH 5/6] Fixed a bug in the search which which resulted in hiding any category that had spaces in it. --- assets/js/search.js | 13 ++++++++++++- pages/guides/_guides.html | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/assets/js/search.js b/assets/js/search.js index 59e8210a6..2e1803db5 100644 --- a/assets/js/search.js +++ b/assets/js/search.js @@ -98,6 +98,16 @@ window.guideEntries; } + /** + * A naive function to slugify a string input and return a URL friendly output + * of that string. This implementation was sourced from: + * https://stackoverflow.com/a/1054862 + * @param {*} stringValue + */ + function naiveSlugify(stringValue) { + return stringValue.replace(/ /g,'-').replace(/[^\w-]+/g,'') + } + /** * A function to display or hide the category of search * @type {Function} @@ -105,7 +115,8 @@ function displayCategory(entry) { const categoryArr = $.merge($('.category-head'), $('.categories ul li')); categoryArr.each(function () { - const category = $(this).text().toLowerCase(); + const category = naiveSlugify($(this).text().toLowerCase()); + if (entry.category === category) { $(`.categories ul .${category}`).show(); $(`#${category}.category-head`).show(); diff --git a/pages/guides/_guides.html b/pages/guides/_guides.html index 4a870fa16..5ea6a74b5 100644 --- a/pages/guides/_guides.html +++ b/pages/guides/_guides.html @@ -5,7 +5,7 @@
    {% for category in site.categories %} {% capture category_name %}{{ category | first }}{% endcapture %} -
  • +
  • {{category_name}}
  • {% endfor %} @@ -47,7 +47,7 @@
    {{ post.title }}
    { "id" : "{{ post.title | strip_html | downcase | split: ' ' | join: '-' | append: '-card' }}", "title" : "{{ post.title | strip_html | escape | downcase}}", - "category" : "{{post.categories | join: ', ' | downcase}}", + "category" : "{{post.category | downcase | slugify}}", "excerpt" : "{{post.excerpt | strip_html | strip_newlines | escape | downcase}}", "content" : {{ post.content | strip_html | strip_newlines | jsonify | downcase }}, "tags" : "{{ post.tags | join: ', ' | downcase}}", From 26b897cd3c6e19b80bdb54f9be9ac3b2a32797e5 Mon Sep 17 00:00:00 2001 From: Eugene K Date: Tue, 17 Nov 2020 17:30:06 -0500 Subject: [PATCH 6/6] Fixed displaying %20 and broken URL in the breadcrumb of guides with multi word categories. --- _layouts/post.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_layouts/post.html b/_layouts/post.html index ff877e1b2..d1b188647 100644 --- a/_layouts/post.html +++ b/_layouts/post.html @@ -32,11 +32,11 @@ / {{ page.title | replace:'-',' ' }} {% else %} / {{ crumb | replace:'-',' ' | capitalize }}/ {{ crumb | url_decode | replace:'-',' ' | capitalize }} {% endif %} {% endfor %}