Learning Go – Day 9

Featured image by Egon Elbre

  • In all my life of using mkdir -p, I never knew you can create multiple subdirectories the easy way. UPDATE: This is actually the shell doing some path expansion and thus can work anywhere.

    $ mkdir -p ~/temp/some/path/{dir1,dir2,dir3/even/more}
    
    $ tree ~/temp/some
    /Users/andre/temp/some
    └── path
        ├── dir1
        ├── dir2
        └── dir3
            └── even
                └── more
    
  • Use os.CreateTemp() to create a temporary file on the system. You can specify a directory if needed and also a file name pattern the will be randomised. E.g “config_*.json”

  • You can also use os.os.MkdirTemp() to create a temporary directory.

  • Go comes with template rendering for HTML and Text.

  • Learned a little bit about Colly – the web scraping package for Go.

Working with file systems

  • Use the package path/filepath to work agnostically with the system’s file path. E.g. this takes into account that Windows uses \ versus Unix based os uses `
  • You can use filepath.Walk to recursively walk a directory and it will call the function you specify on each found file or directory. There is a note that WalkDir performs better. See the documentation for WalkFunc.
filepath.Walk("./somewhere/here",
		func(path string, info fs.FileInfo, err error) error {
			if err != nil {
				return err
			}

			// See the WalkFunc documentation for what this function can do to control
			// how the Walk behaves
		}
}
  • fs.FileInfo is declared as:
type FileInfo interface {
	Name() string       // base name of the file
	Size() int64        // length in bytes for regular files; system-dependent for others
	Mode() FileMode     // file mode bits
	ModTime() time.Time // modification time
	IsDir() bool        // abbreviation for Mode().IsDir()
	Sys() any           // underlying data source (can return nil)
}
  • To get just the file extension. Use filepath.Ext()
import "path/filepath"

fmt.Println(filepath.Ext("/dir/filename.txt")) // .txt
fmt.Printf("%q", filepath.Ext("/dir/filename")) // "" <- note it is empty string

  • Recall from earlier you can use os.Remove() to delete a single file. You can also use os.RemoveAll() to recursively delete a directory.

Unit-testing

Gophers (Go programmers) use this concept called “Table-Driven testing” by which you build up a table of inputs and expected outputs and then in your unit-test you iterate over the table and check that feeding inputs produce the expected outputs.

I like this and can already see many places where I can apply the same idea to my Swift and other languages.

See Dave Cheney’s article on this.

Note: You can also use t.Run(nameOfTest, func (t *testing.T) { ... } ) to run sub tests inside of a unit-test. Couple this with the for _, testCase := range testCases and you have super powers.

  • You can define Helper functions for your unit-tests. They are usefull for reusing the same code in multiple unit-tests but also if the helper raises errors like t.Fatal() then the line number etc. is reported as the unit-test that called the helper and not the helper function it self.
func iamAHelper(t *testing.T, someOtherArgs string) {
	t.Helper() // mark this as a helper
	// so that calls like this, will output line etc. where this helper was called
	t.Fatal(someError)
}