Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Building Awesome CLI apps in Go

3,836 views

Published on

Building Awesome CLI apps in Go OSCON 2016

Published in: Technology

Building Awesome CLI apps in Go

  1. 1. BUILDING AN AWESOME CLI APP IN GO 1
  2. 2. 2
  3. 3. ASHLEY MAC •Dev Advocate at Rackspace •Go newbie •Slightly sarcastic 3
  4. 4. –Ashley Mac “I have no idea what i’m doing” 4
  5. 5. SPF13 •Willfully Unemployed •Builds Cool Things With Go AKA Gopher •Irritatingly Specific 5
  6. 6. –Steve “I Kinda Know What I'm Doing” 6
  7. 7. MAKE SURE YOU HAVE GO INSTALLED AND WORKING + AN EDITOR 7
  8. 8. UX OF CLI 8
  9. 9. HUMAN INTERFACE GUIDELINES FOR CLIS 9
  10. 10. UNIx PHILOSOPHY 10
  11. 11. KEN'S "UNIx PHILOSOPHY"11
  12. 12. UNIx PHILOSOPHY •Simple •Clear •Composable
 •Extensible •Modular •Small 12
  13. 13. – Rob Pike “Many UNIX programs do quite trivial things in isolation, but, combined with other programs, become general and useful tools.” 13
  14. 14. – Doug McIlroy “The manual page, which really used to be a manual page, is now a small volume, with a thousand options” 14
  15. 15. POSIx + GNU 15
  16. 16. COMMANDS 16
  17. 17. WHAT ARE COMMANDS ? 17
  18. 18. › ls c:dir COMMANDS 18
  19. 19. › ls › cp › cat › cd list copy concatenate change directory ABBREVIATED 19
  20. 20. › ls › cp › cat › cd SHORT 20 Short Clear &
  21. 21. › ls CONTExT 21 operates in current Directory
  22. 22. THE LANGUAGE OF COMMANDS 22
  23. 23. › ls HAS A LANGUAGE 23 List the contents
 of the directory I'm in
  24. 24. › ls › rm › zip › find COMMANDS 24 Verbs
  25. 25. c:dir COMMANDS 25 Noun?
  26. 26. ARGS 26
  27. 27. WHAT ARE ARGS? 27
  28. 28. › rm [file] › cp [file] [newfile] c:copy [file] [newfile] INPUT 28
  29. 29. 
 > rm [file] ... [fileN] MANY INPUTS 29 Arg0 Argn
  30. 30. 
 > cp [file] [newfile] ORDER MATTERS 30 From To
  31. 31. 
 > cp [file] [newfile] SEPARATED 31 Space separates
  32. 32. THE LANGUAGE OF ARGS 32
  33. 33. 
 > cp [file] [newfile] DIRECT OBJECT 33 Verb Noun Noun
  34. 34. 
 > cp [file] [newfile] IS PROUNCABLE 34 Copy this file to here
  35. 35. OPTIONS 35
  36. 36. FLAGS 36
  37. 37. › rm [options] [file] c:del [options] [file] MODIFY ACTIONS 37
  38. 38. 
 › rm -f badfile.txt SEPARATORS 38 Space separates
  39. 39. 
 › rm --force PREFIxED 39 prefix
  40. 40. 
 › rm -f PREFIxED 40 prefix
  41. 41. 
 c: del /F PREFIxED 41 prefix
  42. 42. 
 -f == --force SHORT VS LONG 42 Common Options shortened
  43. 43. 
 › rm -r -f rm -rf STACKABLE 43 Short options stack
  44. 44. THE LANGUAGE OF FLAGS 44
  45. 45. 
 › rm --force [file] ADVERB 45 Verb Adverb Noun
  46. 46. 
 › rm --force [file] PRONOUNCEABLE 46 Forcefully remove
 this file
  47. 47. 
 › rm --force [file] PRONOUNCEABLE 47 remove this file with Force
  48. 48. 
 › ls --color /home/spf13 FLAG INPUT 48 Verb Adverb Noun
  49. 49. 
 › ls --color /home/spf13 PRONOUNCEABLE 49 Colorfully list my home directory
  50. 50. › ls -a MODIFY BEHAVIOR 50
  51. 51. › ls -a MODIFY BEHAVIOR 51 List all the things
  52. 52. ALL ISN'T AN ADVERB 52
  53. 53. ADVERBS ExPRESS •manner •place •time •frequency •degree •level of certainty •etc. 53
  54. 54. ADVERBS ANSWER •How? •In what way? •When? •Where? •To what extent? 54
  55. 55. › ls -a MODIFY BEHAVIOR 55 List completely to what extent?
  56. 56. 
 › ls --width=40 IMPUTABLE 56 Input to what extent?
  57. 57. 
 › ls --width 40 IMPUTABLE 57 Input to what extent?
  58. 58. 
 › ls --width 40 PREPOSITION 58 Verb Preposition Obj
  59. 59. 
 › ls --width 40 PRONOUNCEABLE 59 list the directory with a width of 40 cols
  60. 60. PREP PHRASE = ADVERB •consists of a preposition and its object •acts as an adverb •"Speaking at OSCON" 60
  61. 61. BAD FLAG DESIGN 61
  62. 62. FLAGS AS ACTIONS62
  63. 63. 
 › tar -xcvf FLAG = ACTION 63
  64. 64. › tar First option must be a mode specifier: -c Create -r Add/Replace 
 -t List -u Update -x Extract FLAG = ACTION 64
  65. 65. › zip › unzip BETTER 65
  66. 66. FLAGS WITH FLAGS 66
  67. 67. Extract: tar -x [options] -k Keep existing files -m Don't restore mod times FLAGS HAVING SUB FLAGS 67
  68. 68. Create: tar -c [options] -z, -j, -J, --lzma Compress archive with gzip/ bzip2/xz/lzma FLAGS HAVING SUB FLAGS 68
  69. 69. INCOMPATIBLE FLAGS 69
  70. 70. ls [options] -S sort by file size -t sort by modification time -U do not sort ... INCOMPATIBLE FLAGS 70
  71. 71. ls -StU INCOMPATIBLE FLAGS 71 What should this do?
  72. 72. > ls --sort=[size,modtime,none] BETTER 72
  73. 73. DOUBLE FLAGS 73
  74. 74. › git pull --stat --no-stat FLAG --NO-FLAG 74
  75. 75. › git pull --stat › git pull --stat=false BETTER 75
  76. 76. CLI APPS 76
  77. 77. › httpd › vi › emacs › git CLI APPS 77 Noun
  78. 78. APPS •Launch something •Do more than one thing •Collection of commands 78
  79. 79. SUB COMMANDS 79
  80. 80. › svn add › brew install › npm search › apt-get upgrade › git clone SUB COMMANDS 80
  81. 81. SUB COMMANDS •CLI apps do multiple things •Apps are groups of commands •(sub) Commands have flags & args •All rules still apply 81
  82. 82. 
 › brew fetch -v hugo ExPANDED RESOURCES 82 Cmd ArgApp Flag
  83. 83. 
 › brew install hugo NOUN 83 Verb ObjectNoun
  84. 84. 
 › brew install hugo PRONOUNCABLE 84 Brew, install hugo
  85. 85. 
 › brew fetch -v hugo PRONOUNCABLE 85 Verb Object Noun adVerb
  86. 86. 
 › brew fetch -v hugo PRONOUNCABLE 86 Brew, verbosely 
 fetch hugo
  87. 87. GO INTRODUCTION 87
  88. 88. MY ExPERIENCE 88
  89. 89. GOPATH AND SETUP •You WILL store your code in a GitHub user folder (example: $GOPATH/src/ github.com/username/helloworld) •This was a pain in the ass •I was comfortable with /code /dev/ or /projects 89
  90. 90. GOPATH AND SETUP •By Storing code on github, I was able to share my many problems •Going from “I’m working on this” to “let’s collaborate” 90
  91. 91. GOPATH •The Go toolset uses an environment variable called GOPATH to find Go source code. •You can set GOPATH to anything you want, but things will be easier if it’s set in your home directory. 91
  92. 92. Windows: c: setx GOPATH %USERPROFILE% 
 OS X: ❯ echo 'export GOPATH=$HOMEn' >> ~/.bash_profile Close the terminal, reopen it, and type the following: ❯ echo $GOPATH GO PATH 92
  93. 93. GOPATH AND SETUP •Typing go get github.com/user/ project will download and build a project. 93
  94. 94. EDITORS •No IDE needed - any text editor will do. •helpful features like autocomplete and format & import on-save •If you’re not sure what to use, I recommend Atom — It’s free, cross- platform, and easy to install. 94
  95. 95. PLUGINS •Atom: https://github.com/joefitzgerald/go-plus •Vim: https://github.com/fatih/vim-go •included in http://vim.spf13.com/ •IntelliJ: 
 https://plugins.jetbrains.com/plugin/?id=5047 •Full list: 
 https://github.com/golang/go/wiki/ IDEsAndTextEditorPlugins 95
  96. 96. YOU DON’T NEED A FRAMEWORK! •The standard library has tons! •Web server •Templates •Database •etc.. 96
  97. 97. YOU DON’T NEED A FRAMEWORK! •Many Packages work well together •Very modular & Composable 97
  98. 98. STANDARD LIBRARY Let’s be honest, you don’t want to write everything from scratch so most programming depends on your ability to interface with existing libraries. i.e. packages. Here are a few core packages you should know about; •Strings •Input/Output (io/ioutil) •Errors •fmt 98 • Containers and Sort • path/filepath • HTTP (net/http) • math/rand
  99. 99. GO 99
  100. 100. STATICALLY
 TYPED 100
  101. 101. STRONGLY
 TYPED 101
  102. 102. COMPILED 102
  103. 103. MEMORY MANAGED 103
  104. 104. POWERFUL CONCURRENCY
 BAKED IN 104
  105. 105. OPEN SOURCE105
  106. 106. CROSS PLATFORM 106
  107. 107. LANGUAGE DESIGN 107
  108. 108. 108 •Ken Thompson (B,C, Unix, UTF-8) •Rob Pike (Unix, UTF-8) •Robert Griesmier (Hotspot, JVM, V8)
  109. 109. MAJOR INFLUENCES •C & derivatives •Pascal & derivatives •CSP 109
  110. 110. IDEALS •Expressibility •Edibility •Powerful •Simple •Scalable 110
  111. 111. SPEC •Java Spec : 670 pages •YAML Spec : 84 pages •Go Spec : 82 pages 111
  112. 112. KEYWORDS •break •default •func •interface •select •case •defer •go 112 •map •struct •chan •else •goto •package •switch •const •fallthrough •if •range •type •continue •for •import •return •var
  113. 113. BRACES 113 Fail
  114. 114. BRACES Compile Eror 114 Compile Error /tmp/sandbox826898458/main.go:6: syntax error: unexpected semicolon or newline before {
  115. 115. BRACES What works 115 Works
  116. 116. VARIABLES 116 Fail
  117. 117. VARIABLES 117 Error /tmp/sandbox473116179/main.go:6: one declared and not used /tmp/ sandbox473116179/main.go:7: two declared and not used /tmp/ sandbox473116179/main.go:8: three declared and not used
  118. 118. WHAT GO DOESN’T HAVE 118
  119. 119. CLASSES 
 ( * SHOCKED EXPRESSION *)119
  120. 120. OBJECTS 120
  121. 121. COMPLExITY 121
  122. 122. TYPES 122
  123. 123. SINGLE VALUE TYPES 123
  124. 124. NUMBERS •Integers 
 (numbers without a decimal) •Floating-Point Numbers 
 (numbers that contain a decimal) • int, int8-64, uint, uint8-64, float32,64 124
  125. 125. STRINGS •A string is a sequence of characters •Go strings are immutable •Go strings are made up of individual bytes (Usually one for each character) 125
  126. 126. MULTI VALUE TYPES 126
  127. 127. type person struct {
 name string
 age int
 } STRUCT 127
  128. 128. ARRAY •An array is an ordered sequence of elements of a single type •Fixed length •Arrays are indexed starting from 0 128
  129. 129. SLICE •Segment of an array •Dynamic length •Can be used without thinking about array underneath 129
  130. 130. MAP •Unordered collection of key-value pairs •Similar to associative arrays, hash tables, and dictionaries •Dynamic Length 130
  131. 131. SOME 
 OTHER TYPES 131
  132. 132. FUNCTION •Function is a type •First class citizen in Go •Can have multiple input values •Can have multiple return values 132
  133. 133. POINTERS •Reference a location in memory where a value is stored •Represented using '*' •Memory location referenced using '&' 133
  134. 134. DEFINE YOUR OWN TYPES •Composed of other types •Not aliases 134
  135. 135. METHOD •Function defined on a type 135
  136. 136. TOOLING 136
  137. 137. GO HELP The Go toolset has many different commands and subcommands. You can pull up a list by typing: go help You now have everything you need to get started 137
  138. 138. GO FMT •Formats your go code for you •Awesome to do "on save" •End of all stylistic debates 138
  139. 139. GO TEST Writing code isn’t easy and humans make mistakes so testing is really important, luckily, Go includes a program that makes writing tests easier: go test Fun Fact: The Go compiler knows to ignore code in files ending with _test.go 139
  140. 140. CLOSING THOUGHTS 140
  141. 141. I THINK GO IS AWESOME 141
  142. 142. IT HAS STRONG OPINIONS 142
  143. 143. COMPILATION IS VERY FAST (USUALLY A SECOND OR TWO)143
  144. 144. AWESOME COMMUNITY 144
  145. 145. RESOURCES •Getting Started, official golang page — https://golang.org/doc/ •Parse’s move from Ruby to Golang — http://blog.parse.com/ learn/how-we-moved-our-api-from-ruby-to-go-and-saved-our-sanity/ •Tutorial — Creating a Wiki — https://golang.org/doc/articles/ wiki/ (this was the first big tutorial that got me to build something useful, take this one slow to get a grasps on its concepts) •Golang — Concurrency is not Parallelism (Rob Pike), this video I found super informative about go’s concurency and got me excited to keep coding in Golang — https://youtu.be/cN_DpYBzKso 145
  146. 146. LET'S BUILD AN APP 146
  147. 147. 147
  148. 148. HTTP:// WWW.SLIDESH ARE.NET/SPF13 148
  149. 149. HTTPS:// GITHUB.COM /SPF13/TRI 149
  150. 150. 1. DESIGNING OUR APP 150
  151. 151. WHAT'S IN A NAME? 151
  152. 152. SHOCKING AMOUNT OF TODO APPS 152
  153. 153. LET'S MAKE A BUNCH MORE 153
  154. 154. I'M CALLING MINE "TRI" 154
  155. 155. FEATURES 155
  156. 156. FEATURES •Add Todo •List Todos •Mark "done" •Search/Filter •Priorities •Archive •Edit •Create Dates •Due Dates •Tags •Projects 156
  157. 157. INTERFACE DESIGN 157
  158. 158. ADDING 158
  159. 159. › tri add "Add Priorities" ADD 159
  160. 160. › tri add Add Priorities ADD 160
  161. 161. 
 › tri add "Add Priorities" ADD 161 Verb Object Noun
  162. 162. › tri add 
 "Add Multi Todo Support" 
 "Consider usage behaviors" ADD 162
  163. 163. PRIORITY 163
  164. 164. › tri add -p1 "Add listing"
 
 
 ADD WITH PRIORITY 164
  165. 165. › tri add -p1 "Add listing"
 
 
 ADD WITH PRIORITY 165 Verb Object Noun adVerb
  166. 166. › tri add -p1 "Add listing"
 
 
 PRONOUNCEABLE 166 Tri, add "Add listing" Todo with a pri of 1
  167. 167. 
 › tri add "Add listing P:1"
 
 
 ALTERNATE SYNTAx 167
  168. 168. CONSIDERATIONS •What priority system to use? •Numeric •Alpha •High, Middle, Low •What's the default priority? 168
  169. 169. MY TODO •High, Middle, Low •H=1, L=3, M/_=2 •Default is 2 169
  170. 170. LISTING 170
  171. 171. 
 › tri ls
 
 
 LISTING 171
  172. 172. 
 › tri list
 
 
 LISTING 172
  173. 173. 
 › tri
 
 
 LISTING 173
  174. 174. › tri list (1) Add Listing Consider usage behaviors Add Multi Todo Support Add Priorities LISTING OUTPUT 174
  175. 175. › tri list (1) Add Listing Add Priorities Add Multi Todo Support Consider usage behaviors LISTING OUTPUT 175
  176. 176. › tri list (H) Add Listing Consider usage behaviors Add Priorities (L) Add Multi Todo Support LISTING OUTPUT 176
  177. 177. › tri list (H) Add Listing Consider usage behaviors Add Priorities (L) Add Multi Todo Support LISTING OUTPUT 177
  178. 178. › tri list 1. (H) Add Listing 2. Consider usage behaviors 3. Add Priorities 4. (L) Add Multi Todo Support LISTING OUTPUT 178
  179. 179. FILTERING 179
  180. 180. 
 › tri list done
 
 
 TOKENS 180
  181. 181. 
 › tri list -p1
 
 
 FLAGS 181
  182. 182. › tri list -p1 › tri list --due June › tri list --created 12/15 › tri list --done -p1 FILTER BY PROPERTY 182
  183. 183. › tri list -p2 2. Consider usage behaviors 3. Add Priorities FILTER BY PRIORITY 183
  184. 184. › tri list "Add" 1. (H) Add Listing 3. Add Priorities 4. (L) Add Multi Todo Support SEARCHING 184
  185. 185. › tri list "Add" "Pri" 3. Add Priorities SEARCHING 185
  186. 186. UPDATING 186
  187. 187. › tri list 1. (H) Add Listing 2. Consider usage behaviors 3. Add Priorities 4. (L) Add Multi Todo Support LISTING OUTPUT 187
  188. 188. 
 › tri done 2
 
 
 COMPLETING 188
  189. 189. 
 › tri edit 1 "Improve Listing" 
 
 EDITING 189
  190. 190. › tri edit 1 -p2 › tri edit 2 --due 05/13/15 › tri edit 3 --created 12/15 EDITING 190
  191. 191. › tri edit 1 -p2 › tri list -p2 › tri edit 3 --created 12/15 › tri list --created 12/15 CONSISTENCY 191
  192. 192. › tri edit 1 2 3 -p2 BATCH EDIT? 192
  193. 193. 2. CREATING A PROJECT 193
  194. 194. COBRA 194
  195. 195. •A CLI Command Framework •A tool to generate CLI apps & commands •Powers Kubernetes, Dropbox, Git Lfs, CoreOS, Docker, Delve ... 195
  196. 196. › go get -u github.com/spf13/cobra/cobra
 GET & INSTALL COBRA 196
  197. 197. › cobra Cobra is a Cli library for Go that empowers applications. This
 application is a tool to generate the needed files to quickly
 create a Cobra application. Usage: cobra [command] Available Commands: add Add a command to a Cobra Application COBRA 197
  198. 198. COBRA APP BUILDER 198
  199. 199. › cobra init 
 github.com/<handle>/tri -a "<Your Name>" COBRA INIT 199 Replace with your url,
 project name & Name
  200. 200. › cobra init ... Your Cobra application is ready at /Users/spf13/gopath/src/ github.com/spf13/tri COBRA 200
  201. 201. › cd $GOPATH/src/ github.com/<name>/tri CD TO PROJECT 201 Replace with your url, project name & Name
  202. 202. › tree . ├── LICENSE ├── cmd │   └── root.go └── main.go LOOK AT YOUR PROJECT 202
  203. 203. › go build › ./tri 
 A longer description that spans multiple lines and likely contains examples and usage of using your application. For example... BUILD & RUN IT 203
  204. 204. YOU'VE JUST CREATED YOUR 1ST GO APP 204
  205. 205. IT'S ALL UPHILL FROM HERE 205
  206. 206. LET'S WRITE SOME CODE 206
  207. 207. OPEN THE PROJECT IN AN EDITOR 207
  208. 208. 208 LiteIDE
  209. 209. 209
  210. 210. HTTPS://SOURCEFORGE.NET/PROJECTS/ LITEIDE/FILES/X29/ 210
  211. 211. WORKING WITH THE ROOT 211
  212. 212. package cmd
 
 import (
 "fmt"
 "os"
 
 "github.com/spf13/cobra"
 "github.com/spf13/viper"
 ) CMD/ROOT.GO 212
  213. 213. package cmd
 
 import (
 "fmt"
 "os"
 
 "github.com/spf13/cobra"
 "github.com/spf13/viper"
 ) CMD/ROOT.GO 213 Notice package name Matches Dir
  214. 214. // This represents the base command when called without any subcommands
 var RootCmd = &cobra.Command{
 Use: "tri",
 Short: "A brief description of your application",
 Long: `A longer description that spans multiple lines and likely ... application.`,
 } CMD/ROOT.GO 214
  215. 215. // This represents the base command when called without any subcommands
 var RootCmd = &cobra.Command{
 Use: "tri",
 Short: "Tri is a todo application",
 Long: `Tri will help you get more done in less time.
 It's designed to be as simple as possible to help you accomplish your goals.`, } CMD/ROOT.GO 215
  216. 216. var RootCmd = &cobra.Command{
 Use: "tri",
 ... CMD/ROOT.GO 216 Package level variable & Exported
  217. 217. MAIN 217
  218. 218. MAIN MAIN MAIN •Go programs are all about "main" •main.go (convention) •main package •main() 218
  219. 219. package main
 
 import "github.com/<yours>/tri/cmd"
 
 func main() {
 cmd.Execute()
 } MAIN.GO 219
  220. 220. package main
 
 import "github.com/<yours>/tri/cmd"
 
 func main() {
 cmd.Execute()
 } MAIN.GO 220 Package Name
  221. 221. package main
 
 import "github.com/<yours>/tri/cmd"
 
 func main() {
 cmd.Execute()
 } MAIN.GO 221 Function Name
  222. 222. func Execute() {
 if err := RootCmd.Execute(); err != nil {
 fmt.Println(err)
 os.Exit(-1)
 }
 } CMD/ROOT.GO 222 Package level
 variable
  223. 223. >go run main.go Tri will help you get more done in less time. It's designed to be as simple as possible to help you accomplish your goals. RUN IT 223
  224. 224. 3. CREATING OUR ADD COMMAND 224
  225. 225. ADD "ADD" 225
  226. 226. > cd $GOPATH/src/ github.com/spf13/tri CD TO PROJECT 226 Replace with your url, project name & Name
  227. 227. > cobra add add add created at $GOPATH/src/ github.com/spf13/tri/cmd/ add.go COBRA ADD ADD 227
  228. 228. > go run main.go add add called RUN "ADD" 228 Bill wants me to replace go run with go build ./tri
  229. 229. MAKE 
 "ADD" ADD229
  230. 230. package cmd CMD/ADD.GO 230
  231. 231. var addCmd = &cobra.Command{
 Use: "add",
 Short: "A brief description of your command",
 Long: `A longer description that spans multiple lines and likely...`,
 Run: func(cmd *cobra.Command, args []string) {
 // TODO: Work your own magic here
 fmt.Println("add called")
 },
 } CMD/ADD.GO 231
  232. 232. var addCmd = &cobra.Command{
 Use: "add",
 Short: "Add a new todo",
 Long: `Add will create a new todo item to the list`,
 Run: func(cmd *cobra.Command, args []string) {
 // TODO: Work your own magic here
 fmt.Println("add called")
 },
 } CMD/ADD.GO 232
  233. 233. func addRun(cmd *cobra.Command, args []string){ fmt.Println("add called") } CMD/ADD.GO 233
  234. 234. var addCmd = &cobra.Command{
 Use: "add",
 Short: "Add a new todo",
 Long: `Add will create a new todo item to the list`,
 Run: addRun,
 } CMD/ADD.GO 234
  235. 235. func addRun(cmd *cobra.Command, args []string) {
 for _, x := range args {
 fmt.Println(x)
 }
 } CMD/ADD.GO 235
  236. 236. FOR x, Y := RANGE •Provides a way to iterate over an array, slice, string, map, or channel. •Like Foreach or Each in other languages •x is index/key, y is value •_ allows you to ignore naming variables 236
  237. 237. 
 func init() {
 RootCmd.AddCommand(addCmd)
 } CMD/ADD.GO 237
  238. 238. INIT() •Special function •Called after package variable declarations •Called prior to main.main() •Each package may have multiple init() •init() order un-guaranteed 238
  239. 239. > go run main.go add "one two" three 
 one two three RUNNING ADD 239
  240. 240. 4. CREATING OUR DATA MODEL 240
  241. 241. CREATE A NEW PACKAGE 241
  242. 242. 242 New Folder New File
  243. 243. package todo
 
 type Item struct {
 Text string
 }
 TODO/TODO.GO 243
  244. 244. NAMED TYPES •Can be any known type (struct, string, int, slice, a new type you’ve declared, etc) •Methods can be declared on it •Not an alias - Explicit type 244
  245. 245. USING OUR NEW TYPE 245
  246. 246. 
 import (
 "fmt"
 
 "github.com/spf13/cobra"
 "github.com/<yourname>/tri/todo"
 ) CMD/ADD.GO 246 Replace with yourS
  247. 247. func addRun(...) {
 items := []todo.Item{}
 for _, x := range args {
 items = append(items, todo.Item{Text:x})
 }
 fmt.Println(items)
 } CMD/ADD.GO 247
  248. 248. func addRun(...) {
 items := []todo.Item{}
 for _, x := range args {
 items = append(items, todo.Item{Text:x})
 }
 fmt.Println(items)
 } CMD/ADD.GO 248
  249. 249. APPEND •Append adds new values to a slice •Append will grow a slice as needed 249
  250. 250. func addRun(...) {
 items := []todo.Item{}
 for _, x := range args {
 items = append(items, todo.Item{Text:x})
 }
 fmt.Println(items)
 } CMD/ADD.GO 250
  251. 251. func addRun(...) {
 items := []todo.Item{}
 for _, x := range args {
 items = append(items, todo.Item{Text:x})
 }
 fmt.Println(items)
 } CMD/ADD.GO 251
  252. 252. >go run main.go add 
 "one two" three [{one two} {three}] GO RUN 252
  253. 253. func addRun(...) {
 var items = []todo.Item{}
 for _,x := range args {
 items = append(items, todo.Item{Text:x})
 }
 fmt.Printf("%#vn", items)
 } CMD/ADD.GO 253
  254. 254. >go run main.go add 
 "one two" three []todo.Item{todo.Item{Text: "one two"}, todo.Item{Text:"three"}} GO RUN 254
  255. 255. 5. PERSISTING OUR DATA 255
  256. 256. SAVING DATA 256
  257. 257. func SaveItems(filename string,
 items []Item) error {
 
 return nil
 } TODO/TODO.GO 257
  258. 258. JSON 258
  259. 259. import (
 "encoding/json"
 ) TODO/TODO.GO 259
  260. 260. func SaveItems(filename string, items []Item) error {
 b, err := json.Marshal(items)
 if err != nil {
 return err
 }
 fmt.Println(string(b))
 
 return nil
 } TODO/TODO.GO 260
  261. 261. func SaveItems(filename string, items []Item) error {
 b, err := json.Marshal(items)
 if err != nil {
 return err
 }
 fmt.Println(string(b))
 
 return nil
 } TODO/TODO.GO 261
  262. 262. func SaveItems(filename string, items []Item) error {
 b, err := json.Marshal(items)
 if err != nil {
 return err
 }
 fmt.Println(string(b))
 
 return nil
 } TODO/TODO.GO 262
  263. 263. ERROR HANDLING •Errors are not exceptional, they are just values •No exceptions in Go •Errors should be handled when they occur 263
  264. 264. ( DON’T) PANIC •Only use when: 1. You want to shut down your program AND 2. You need a stack trace •Packages should never call Panic (only applications) •Do not use as pseudo-exception handling •Only recover a panic if you know you can properly recover from it 264
  265. 265. func SaveItems(filename string, items []Item) error {
 b, err := json.Marshal(items)
 if err != nil {
 return err
 }
 fmt.Println(string(b))
 
 return nil
 } TODO/TODO.GO 265
  266. 266. func addRun(cmd *cobra.Command, 
 args []string) {
 var items = []todo.Item{}
 for _, x := range args {
 items = append(items, todo.Item{Text: x})
 }
 todo.SaveItems("x", items)
 } CMD/ADD.GO 266
  267. 267. >go run main.go add "one two" three [{"Text":"one two"}, {"Text":"three"}] CHECK JSON CREATION 267
  268. 268. WRITING TO FILES268
  269. 269. import (
 "io/ioutil"
 "encoding/json"
 ) TODO/TODO.GO 269
  270. 270. func SaveItems(filename string, items []Item) error {
 ...
 err = ioutil.WriteFile(filename, b, 0644)
 if err != nil {
 return err
 }
 
 return nil
 } TODO/TODO.GO 270
  271. 271. func addRun(cmd *cobra.Command, 
 args []string) {
 var items = []todo.Item{}
 for _, x := range args {
 items = append(items, todo.Item{Text: x})
 } 
 err := todo.SaveItems("/Users/spf13/.tridos.json", items); if err != nil {
 fmt.Errorf("%v", err)
 }
 } CMD/ADD.GO 271
  272. 272. › go run main.go add "one two" three › cat ~/.tridos.json [{"Text":"one two"}, {"Text":"three"}] CHECK FILE CREATION 272
  273. 273. READING
 DATA 273
  274. 274. func ReadItems(filename string) ([]Item, error) {
 
 return []Item{}, nil
 } TODO/TODO.GO 274
  275. 275. func ReadItems(filename string) ([]Item, error) {
 
 return []Item{}, nil
 } TODO/TODO.GO 275
  276. 276. func ReadItems(filename string) ([]Item, error) {
 b, err := ioutil.ReadFile(filename)
 if err != nil {
 return []Item{}, err
 } ... TODO/TODO.GO 276
  277. 277. func ReadItems(filename string) ([]Item, error) {
 b, err := ioutil.ReadFile(filename)
 if err != nil {
 return []Item{}, err
 } ... TODO/TODO.GO 277
  278. 278. func ReadItems(filename string) ([]Item, error) { ...
 var items []Item
 if err := json.Unmarshal(b, &items); err != nil {
 return []Item{}, err
 }
 return items, nil } TODO/TODO.GO 278
  279. 279. func ReadItems(filename string) ([]Item, error) { ...
 var items []Item
 if err := json.Unmarshal(b, &items); err != nil {
 return []Item{}, err
 }
 return items, nil } TODO/TODO.GO 279
  280. 280. func ReadItems(filename string) ([]Item, error) { ...
 var items []Item
 if err := json.Unmarshal(b, &items); err != nil {
 return []Item{}, err
 }
 return items, nil } TODO/TODO.GO 280
  281. 281. func ReadItems(filename string) ([]Item, error) { ...
 var items []Item
 if err := json.Unmarshal(b, &items); err != nil {
 return []Item{}, err
 }
 return items, nil } TODO/TODO.GO 281
  282. 282. func ReadItems(filename string) ([]Item, error) { ...
 var items []Item
 if err := json.Unmarshal(b, &items); err != nil {
 return []Item{}, err
 }
 return items, nil } TODO/TODO.GO 282
  283. 283. LIST COMMAND 283
  284. 284. › cobra add list list created at $GOPATH/ src/github.com/spf13/tri/ cmd/list.go ADD LIST COMMAND 284
  285. 285. Run: func(cmd *cobra.Command, args []string) {
 items, err := todo.ReadItems("/Users/ spf13/.tridos.json") 
 if err != nil {
 log.Printf("%v", err)
 }
 fmt.Println(items)
 }, CMD/LIST.GO 285
  286. 286. Run: func(cmd *cobra.Command, args []string) {
 items, err := todo.ReadItems("/Users/ spf13/.tridos.json") 
 if err != nil {
 log.Printf("%v", err)
 }
 fmt.Println(items)
 }, CMD/LIST.GO 286
  287. 287. Run: func(cmd *cobra.Command, args []string) {
 items, err := todo.ReadItems("/Users/ spf13/.tridos.json") 
 if err != nil {
 log.Printf("%v", err)
 }
 fmt.Println(items)
 }, CMD/LIST.GO 287
  288. 288. › go run main.go list [{one two} {three}] RUN LIST 288
  289. 289. func addRun(cmd *cobra.Command, args []string) {
 var items = []todo.Item{} 
 
 for _, x := range args {
 items = append(items, todo.Item{Text: x})
 } ... CMD/ADD.GO 289
  290. 290. func addRun(cmd *cobra.Command, args []string) {
 items, err := todo.ReadItems("/Users/spf13/.tridos.json")
 if err != nil {
 log.Printf("%v", err)
 } for _, x := range args {
 items = append(items, todo.Item{Text: x})
 } ... CMD/ADD.GO 290
  291. 291. 6. ADDING ROOT 
 (GLOBAL) FLAGS 291
  292. 292. USING $HOME 292
  293. 293. › go get github.com/ mitchellh/go-homedir GO GET 293
  294. 294. package cmd
 
 import (
 ...
 "github.com/mitchellh/go-homedir"
 ) CMD/ROOT.GO 294
  295. 295. 
 var dataFile string CMD/ROOT.GO 295
  296. 296. func init() {
 // Here you will define your flags and configuration settings
 // Cobra supports Persistent Flags which if defined here will be global for your application
 
 home, err := homedir.Dir()
 if err != nil {
 log.Println("Unable to detect home directory. Please set data file using --datafile.")
 } ... CMD/ROOT.GO 296
  297. 297. ADDING A FLAG297
  298. 298. func init() { ...
 RootCmd.PersistentFlags().StringVar(&dataFile,
 "datafile",
 home+string(os.PathSeparator)+".tridos.json",
 "data file to store todos") ... CMD/ROOT.GO 298
  299. 299. func init() { ... 
 RootCmd.PersistentFlags().StringVar(&dataFile,
 "datafile",
 home+string(os.PathSeparator)+".tridos.json",
 "data file to store todos") CMD/ROOT.GO 299
  300. 300. func init() { ... 
 RootCmd.PersistentFlags().StringVar(&dataFile,
 "datafile",
 home+string(os.PathSeparator)+".tridos.json",
 "data file to store todos") CMD/ROOT.GO 300
  301. 301. func init() { ... 
 RootCmd.PersistentFlags().StringVar(&dataFile,
 "datafile",
 home+string(os.PathSeparator)+".tridos.json",
 "data file to store todos") CMD/ROOT.GO 301
  302. 302. items, err := todo.ReadItems(dataFile) ...
 err := todo.SaveItems(dataFile, items) CMD/ADD.GO 302
  303. 303. 
 items, err := todo.ReadItems(dataFile) CMD/LIST.GO 303
  304. 304. › go build › ./tri ... Flags: --datafile string data file to store todos (default "/ Users/spf13/.tridos.json") -h, --help help for tri Use "tri [command] --help" for more information about a command. SEE THE FLAG 304
  305. 305. › go build › ./tri add "Add priorities" 
 --datafile $HOME/.next.json › ./tri list --datafile $HOME/.next.json [{Add priorities}] USE THE FLAG 305
  306. 306. 7. ADDING PRIORITIES 306
  307. 307. › go run main.go add "add priorities" 
 "order by priority" ADD SOME TODOS 307
  308. 308. ADJUSTING OUR TYPE 308
  309. 309. type Item struct {
 Text string
 Priority int
 } TODO/TODO.GO 309
  310. 310. VALIDATING INPUT 310
  311. 311. func (i *Item) SetPriority(pri int) {
 switch pri {
 case 1:
 i.Priority = 1
 case 3:
 i.Priority = 3
 default:
 i.Priority = 2
 }
 } TODO/TODO.GO 311
  312. 312. func (i *Item) SetPriority(pri int) {
 switch pri {
 case 1:
 i.Priority = 1
 case 3:
 i.Priority = 3
 default:
 i.Priority = 2
 }
 } TODO/TODO.GO 312
  313. 313. func (i *Item) SetPriority (pri int) {
 switch pri {
 case 1:
 i.Priority = 1
 case 3:
 i.Priority = 3
 default:
 i.Priority = 2
 }
 } TODO/TODO.GO 313
  314. 314. ADDING FLAG 314
  315. 315. var priority int ...
 func init() {
 RootCmd.AddCommand(addCmd)
 
 addCmd.Flags().IntVarP(&priority, 
 "priority", "p", 2, "Priority:1,2,3") ... CMD/ADD.GO 315
  316. 316. var priority int ...
 func init() {
 RootCmd.AddCommand(addCmd)
 
 addCmd.Flags().IntVarP(&priority, 
 "priority", "p", 2, "Priority:1,2,3") ... CMD/ADD.GO 316
  317. 317. › go build › ./tri help add Add will create a new todo item to the list Usage: tri add [flags] Flags: -p, --priority int Priority:1,2,3 (default 2) HELP ADD 317
  318. 318. SETTING PRIORITY318
  319. 319. func addRun(cmd *cobra.Command, args []string) {
 ...
 for _, x := range args {
 item := todo.Item{Text: x}
 item.SetPriority(priority)
 items = append(items, item)
 }
 CMD/ADD.GO 319
  320. 320. func addRun(cmd *cobra.Command, args []string) {
 ...
 for _, x := range args {
 item := todo.Item{Text: x}
 item.SetPriority(priority)
 items = append(items, item)
 }
 CMD/ADD.GO 320
  321. 321. › ./tri add "format list" -p1 › ./tri list [{add priorities 0} {order by priority 0} {format list 1}] ADD WITH PRIORITY 321
  322. 322. 8. MAKING OUR LIST PRETTY 322
  323. 323. BREAK OUT LISTRUN 323
  324. 324. // listCmd respresents the list command
 var listCmd = &cobra.Command{
 Use: "list",
 Short: "List the todos",
 Long: `Listing the todos`,
 Run: listRun,
 }
 
 func listRun(cmd *cobra.Command, args []string) { ... } CMD/LIST.GO 324
  325. 325. TAB- WRITER325
  326. 326. func listRun(cmd *cobra.Command, args []string) { ... w := tabwriter.NewWriter(os.Stdout, 3, 0, 1, ' ', 0)
 for _, i := range items {
 fmt.Fprintln(w, strconv.Itoa(i.Priority)+"t"+i.Text+"t")
 }
 
 w.Flush() } CMD/LIST.GO 326
  327. 327. func listRun(cmd *cobra.Command, args []string) { ... w := tabwriter.NewWriter(os.Stdout, 3, 0, 1, ' ', 0)
 for _, i := range items {
 fmt.Fprintln(w, strconv.Itoa(i.Priority)+"t"+i.Text+"t")
 }
 
 w.Flush() } CMD/LIST.GO 327
  328. 328. func listRun(cmd *cobra.Command, args []string) { ... w := tabwriter.NewWriter(os.Stdout, 3, 0, 1, ' ', 0)
 for _, i := range items {
 fmt.Fprintln(w, strconv.Itoa(i.Priority)+"t"+i.Text+"t")
 }
 
 w.Flush() } CMD/LIST.GO 328
  329. 329. func listRun(cmd *cobra.Command, args []string) { ... w := tabwriter.NewWriter(os.Stdout, 3, 0, 1, ' ', 0)
 for _, i := range items {
 fmt.Fprintln(w, strconv.Itoa(i.Priority)+"t"+i.Text+"t")
 }
 
 w.Flush() } CMD/LIST.GO 329
  330. 330. func listRun(cmd *cobra.Command, args []string) { ... w := tabwriter.NewWriter(os.Stdout, 3, 0, 1, ' ', 0)
 for _, i := range items {
 fmt.Fprintln(w, strconv.Itoa(i.Priority)+"t"+i.Text+"t")
 }
 
 w.Flush() } CMD/LIST.GO 330
  331. 331. › go run main.go list 0 add priorities 0 order by priority 1 format list LIST W/PRIORITY 331
  332. 332. PRETTIER PRIORITY
 PRINTING 332
  333. 333. func (i *Item) PrettyP() string {
 if i.Priority == 1 {
 return "(1)"
 }
 if i.Priority == 3 {
 return "(3)"
 }
 
 return " "
 } TODO/TODO.GO 333
  334. 334. func listRun(cmd *cobra.Command, args []string) { ... w := tabwriter.NewWriter(os.Stdout, 3, 0, 1, ' ', 0)
 for _, i := range items {
 fmt.Fprintln(w, i.PrettyP()+"t"+i.Text+"t")
 }
 
 w.Flush() } CMD/LIST.GO 334
  335. 335. › go run main.go list add priorities order by priority (1) format list LIST W/PRIORITY 335
  336. 336. LABELS 336
  337. 337. type Item struct {
 Text string
 Priority int
 position int
 } TODO/TODO.GO 337
  338. 338. type Item struct {
 Text string
 Priority int
 position int
 } TODO/TODO.GO 338 Notice Lowercase p
  339. 339. func (i *Item) Label() string {
 return strconv.Itoa(i.position) + "."
 } TODO/TODO.GO 339
  340. 340. func ReadItems(filename string) ([]Item, error) {
 ...
 
 for i, _ := range items {
 items[i].position = i + 1
 }
 
 return items, nil
 } TODO/TODO.GO 340
  341. 341. › go run main.go list 1. add priorities 2. order by priority 3. (1) format list LIST W/PRIORITY 341
  342. 342. SORT 342
  343. 343. // ByPri implements sort.Interface for []Item based on
 // the Priority & position field.
 type ByPri []Item
 
 func (s ByPri) Len() int { return len(s) }
 func (s ByPri) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
 func (s ByPri) Less(i, j int) bool {
 if s[i].Priority == s[j].Priority {
 return s[i].position < s[j].position
 }
 return s[i].Priority < s[j].Priority
 } TODO/TODO.GO 343
  344. 344. // ByPri implements sort.Interface for []Item based on
 // the Priority & position field.
 type ByPri []Item
 
 func (s ByPri) Len() int { return len(s) }
 func (s ByPri) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
 func (s ByPri) Less(i, j int) bool {
 if s[i].Priority == s[j].Priority {
 return s[i].position < s[j].position
 }
 return s[i].Priority < s[j].Priority
 } TODO/TODO.GO 344
  345. 345. // ByPri implements sort.Interface for []Item based on
 // the Priority & position field.
 type ByPri []Item
 
 func (s ByPri) Len() int { return len(s) }
 func (s ByPri) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
 func (s ByPri) Less(i, j int) bool {
 if s[i].Priority == s[j].Priority {
 return s[i].position < s[j].position
 }
 return s[i].Priority < s[j].Priority
 } TODO/TODO.GO 345
  346. 346. // ByPri implements sort.Interface for []Item based on
 // the Priority & position field.
 type ByPri []Item
 
 func (s ByPri) Len() int { return len(s) }
 func (s ByPri) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
 func (s ByPri) Less(i, j int) bool {
 if s[i].Priority == s[j].Priority {
 return s[i].position < s[j].position
 }
 return s[i].Priority < s[j].Priority
 } TODO/TODO.GO 346
  347. 347. // ByPri implements sort.Interface for []Item based on
 // the Priority & position field.
 type ByPri []Item
 
 func (s ByPri) Len() int { return len(s) }
 func (s ByPri) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
 func (s ByPri) Less(i, j int) bool {
 if s[i].Priority == s[j].Priority {
 return s[i].position < s[j].position
 }
 return s[i].Priority < s[j].Priority
 } TODO/TODO.GO 347
  348. 348. // ByPri implements sort.Interface for []Item based on
 // the Priority & position field.
 type ByPri []Item
 
 func (s ByPri) Len() int { return len(s) }
 func (s ByPri) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
 func (s ByPri) Less(i, j int) bool {
 if s[i].Priority == s[j].Priority {
 return s[i].position < s[j].position
 }
 return s[i].Priority < s[j].Priority
 } TODO/TODO.GO 348
  349. 349. 
 func listRun(cmd *cobra.Command, args []string) {
 ...
 
 sort.Sort(todo.ByPri(items))
 
 w := tabwriter.NewWriter(os.Stdout, 3, 0, 1, ' ', 0) ... CMD/LIST.GO 349
  350. 350. › go run main.go list 3. (1) format list 1. add priorities 2. order by priority LIST W/PRIORITY 350
  351. 351. 9. DONE-ING
 TODOS 351
  352. 352. ADD DONE 352
  353. 353. › cobra add done done created at $GOPATH/ src/github.com/spf13/tri/ cmd/done.go COBRA ADD DONE 353
  354. 354. // doneCmd represents the done command
 var doneCmd = &cobra.Command{
 Use: "done",
 Aliases: []string{"do"},
 Short: "Mark Item as Done",
 Run: doneRun,
 } CMD/DONE.GO 354
  355. 355. ADD 
 .DONE 355
  356. 356. type Item struct {
 Text string
 Priority int
 position int
 Done bool
 } TODO/TODO.GO 356
  357. 357. TRI DONE 
 SET 
 .DONE 357
  358. 358. func doneRun(cmd *cobra.Command, args []string) {
 items, err := todo.ReadItems(dataFile)
 i, err := strconv.Atoi(args[0])
 
 if err != nil {
 log.Fatalln(args[0], "is not a valid labeln", err)
 } ... CMD/DONE.GO 358 Break out still
  359. 359. func doneRun(cmd *cobra.Command, args []string) {
 ... if i > 0 && i < len(items) {
 items[i-1].Done = true
 fmt.Printf("%q %vn", items[i-1].Text, "marked done") 
 sort.Sort(todo.ByPri(items))
 todo.SaveItems(dataFile, items)
 } else {
 log.Println(i, "doesn't match any items")
 }
 } CMD/DONE.GO 359 Break out slide
  360. 360. › go run main.go done a 2016/05/14 22:22:03 a is not a valid label strconv.ParseInt: parsing "a": invalid syntax DONE WRONG 360
  361. 361. › go run main.go done 13 2016/05/14 22:44:06 13 doesn't match any items DONE WRONG 361
  362. 362. › go run main.go done 1 DONE RIGHT 362
  363. 363. UPDATE LISTING363
  364. 364. func (i *Item) PrettyDone() string {
 if i.Done {
 return "X"
 }
 return ""
 } TODO/TODO.GO 364
  365. 365. fmt.Fprintln(w, i.Label() +"t"+i.PrettyDone() +"t"+i.PrettyP() +"t"+i.Text+"t")
 CMD/LIST.GO 365
  366. 366. func (s ByPri) Less(i, j int) bool {
 if s[i].Done == s[j].Done {
 if s[i].Priority == s[j].Priority {
 return s[i].position < s[j].position
 }
 return s[i].Priority < s[j].Priority
 }
 return !s[i].Done
 } TODO/TODO.GO 366
  367. 367. › go run main.go list 1. (1) format list 2. X order by priority 3. X add priorities LIST 367
  368. 368. ADD --DONE FLAG 368
  369. 369. › go run main.go add "hide done items" ADD NEW TODO 369
  370. 370. fmt.Fprintln(w, i.Label() +"t"+i.PrettyDone() +"t"+i.PrettyP() +"t"+i.Text+"t")
 CMD/LIST.GO 370
  371. 371. var (
 doneOpt bool
 ) CMD/LIST.GO 371
  372. 372. func init() {
 RootCmd.AddCommand(listCmd)
 
 listCmd.Flags().BoolVar(&doneOpt, "done", false, "Show 'Done' Todos ") } CMD/LIST.GO 372
  373. 373. func listRun(cmd *cobra.Command, args []string) {
 ... for _, i := range items {
 if i.Done == doneOpt {
 fmt.Fprintln(w, i.Label()...)
 }
 } CMD/LIST.GO 373
  374. 374. › go run main.go list --done 3. X order by priority 4. X add priorities TRY --DONE 374
  375. 375. SHOW ALL 375
  376. 376. var (
 doneOpt bool
 allOpt bool
 ) CMD/LIST.GO 376
  377. 377. func init() {
 ... listCmd.Flags().BoolVar(&allOpt, "all", false, "Show all Todos") } CMD/LIST.GO 377
  378. 378. func listRun(cmd *cobra.Command, args []string) {
 ... for _, i := range items {
 if allOpt || i.Done == doneOpt {
 fmt.Fprintln(w, i.Label()...)
 }
 } CMD/LIST.GO 378
  379. 379. › go run main.go list --all --done 1. (1) format list 2. hide done items 3. X order by priority 4. X add priorities TRY --ALL 379
  380. 380. 10. CONFIG380
  381. 381. ›./tri list --datafile $HOME/Dropbox/Sync/tridos.json ›./tri add "Add config/ENV support" --datafile $HOME/Dropbox/Sync/tridos.json USING DIFFERENT DATA FILE 381
  382. 382. CONFIG FTW 382
  383. 383. •A configuration manager •Registry for application settings •Works well with Cobra 383
  384. 384. VIPER SUPPORTS •YAML, TOML, JSON or HCL •Etcd, Consul •Config LiveReloading •default < KeyVal < config < env < flag •Aliases & Nested values 384
  385. 385. var cfgFile string func init() {
 cobra.OnInitialize(initConfig) ...
 RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.tri.yaml)")
 ... CMD/ROOT.GO 385 Already There
  386. 386. // Read in config file and ENV variables if set.
 func initConfig() {
 viper.SetConfigName(".tri") 
 viper.AddConfigPath("$HOME")
 viper.AutomaticEnv() 
 
 // If a config file is found, read it in.
 if err := viper.ReadInConfig(); err == nil {
 fmt.Println("Using config file:", viper.ConfigFileUsed())
 } } CMD/ROOT.GO 386 Already There
  387. 387. 12 FACTOR APPS 387
  388. 388. STRICT SEPARATION OF CONFIG FROM CODE 388
  389. 389. CONFIG IS STUFF THAT VARIES IN DIFFERENT ENVIRONMENTS 389
  390. 390. CODE IS STUFF THAT STAYS THE SAME EVERYWHERE 390
  391. 391. THE 12 FACTOR APP STORES CONFIG IN ENV VARS 391
  392. 392. READ FROM VIPER 392
  393. 393. 
 func addRun(cmd *cobra.Command, args []string) {
 items, err := todo.ReadItems(dataFile)) ... if err := todo.SaveItems(dataFile), items); err != nil { CMD/ADD.GO 393
  394. 394. 
 func addRun(cmd *cobra.Command, args []string) {
 items, err := todo.ReadItems(viper.GetString("datafile")) ... if err := todo.SaveItems(viper.GetString("datafile"), items); err != nil { CMD/ADD.GO 394
  395. 395. 
 func doneRun(cmd *cobra.Command, args []string) { ... items, err := todo.ReadItems(viper.GetString("datafile")) ... 
 todo.SaveItems(viper.GetString("datafile"), items) CMD/DONE.GO 395
  396. 396. 
 func listRun(cmd *cobra.Command, args []string) { items, err := todo.ReadItems(viper.GetString("datafile")) ... CMD/LIST.GO 396
  397. 397. ENV 397
  398. 398. ›./tri list --datafile $HOME/Dropbox/Sync/tridos.json ›./tri add "Add config/ENV support" --datafile $HOME/Dropbox/Sync/tridos.json USING DIFFERENT DATA FILE 398
  399. 399. › DATAFILE=$HOME/Dropbox/Sync/trido.json ./tri list › DATAFILE=$HOME/Dropbox/Sync/trido.json ./tri add "Add config/ENV support" USING ENV 399
  400. 400. › export DATAFILE=$HOME/Dropbox/Sync/ trido.json › ./tri list 1. Add config/ENV support 2. Add config/ENV support ExPORT FTW 400
  401. 401. PLAYING NICE WITH OTHERS 401
  402. 402. // Read in config file and ENV variables if set.
 func initConfig() {
 viper.SetConfigName(".tri") 
 viper.AddConfigPath("$HOME")
 viper.AutomaticEnv() viper.SetEnvPrefix("tri")
 
 
 // If a config file is found, read it in.
 if err := viper.ReadInConfig(); err == nil {
 fmt.Println("Using config file:", viper.ConfigFileUsed())
 } CMD/ROOT.GO 402
  403. 403. › export TRI_DATAFILE=$HOME/Dropbox/ Sync/trido.json › ./tri list 1. Add config/ENV support 2. Add config/ENV support ExPORT FTW 403
  404. 404. CONFIG FILES 404
  405. 405. GOOD FOR APPS405
  406. 406. › echo "datafile: /Users/ spf13/Dropbox/Sync/ trido.json" > $HOME/.tri.yaml CREATE CONFIG FILE 406
  407. 407. › go run main.go list Using config file: /Users/ spf13/.tri.yaml 1. Add config/ENV support 2. Add config/ENV support USE CONFIG FILE 407
  408. 408. 11. WORKING AHEAD 408
  409. 409. TRY TO IMPLEMENT ExTRA FEATURES 409
  410. 410. EDITING 410
  411. 411. SEARCH 411
  412. 412. FOLLOW US ON TWITTER @ASHLEYMCNAMARA @SPF13 412
  413. 413. ADDITIONAL RESOURCES •Ashley's Learn to Code Resources •Steve's Blog •Go Girls Who Code 413
  414. 414. END 414

×