marcel-dempers d1ed631367 go-redis
2021-10-16 16:08:42 +11:00
..
2020-12-25 07:28:53 +11:00
2021-01-04 21:17:54 +11:00
2021-09-29 00:11:50 -04:00
2021-03-21 22:18:54 +11:00
2021-10-16 16:08:42 +11:00
2021-01-04 21:17:54 +11:00
2021-01-04 21:17:54 +11:00

Introduction to Learning Go

Go can be downloaded from golang.org

Test your go installation:

go version

Run Go in Docker

We can also run go in a small docker container:

cd golang\introduction

docker build --target dev . -t go
docker run -it -v ${PWD}:/work go sh
go version

Code Structure

https://golang.org/doc/code.html

  • Package:

    • Source files in same directory that are compiled together
    • Have visibility on all source files in the same package
  • Modules:

    • Collection of packages that are released together

Our repository can contain one or more go modules, but usually 1.

  • At the root of the repo

go.mod Declares module path + import path for packages. (Where to download them)

  • When we write our own program, we can define a module path
  • This allows us to publish our code (if we want), so others can download it
  • The module path could be something like github.com/google/go-cmp
  • Makes it easy for other programs to consume our module

Our first Program

  • Create a folder containing our application
mkdir app

  • Define a module path (github.com/docker-development-youtube-series/golang/introdouction/app)
# change directory to your application source code

cd app

# create a go module file

go mod init github.com/docker-development-youtube-series/golang/introdouction/app

  • Create basic source code

In the app folder, create a program called app.go Paste the following content into it.

package main

import "fmt"

func main() {
	fmt.Println("Hello, world.")
}
  • Run your application code

You can run your application

go run app.go

Building our Program

Build your application into a static binary:

go build 

This will produce a compiled program called app You can run this program easily:

./app

Install your application (optional)

"This command builds the app command, producing an executable binary.
It then installs that binary as $HOME/go/bin/app (or, under Windows, %USERPROFILE%\go\bin\app.exe)"

go install github.com/docker-development-youtube-series/golang/introdouction/app

The Code

Functions

In the video we cover writing functions.
It allows us to execute a block of code
You want to give your function a single purpose
Functions can have an input and return an output
Well thought out functions makes it easier to write tests

Instead of doing a boring x + y function that adds two numbers, let's do something a little more realistic but still basic:

// This function returns some data 
// The data could be coming from a database, or a file.
// The person calling the function should not care
// Since the function does not leak its Data provider

func getData(inputs)(outputs){
}

// functions can take multiple inputs, and return multiple outputs

// lets say we have 1) customers and 2) the cities they are from
// we may want to 1) get a list of customers and 2) get a list of cities
// therefore we have 2 types of data, 1) customers 2) cities
// let's improve our function so its gets data based on the type

func getData(customerId int) (customer string) {
}

Variables

To hold data in programming languages, we use variables.
Variables take up space in memory, so we want to keep it minimal.
Let's declare variables in our function

func getData(customerId int) (customer string) {
  var firstName = "Marcel"
  lastName := "Dempers"
  
  fullName := firstName + " " + lastName
  return fullName

  //or we can return the computation instead of adding another variable!
  return firstName + " " + lastName

  //or we dont even need to declare variables :)
  return "Marcel Dempers"

}

Control Flows (if\else)

You can see we're not using the customerId input in our function.
Let's use it!

Control flows allow us to add "rules" to our code.
"If this is the case, then do that, else do something else".

So let's say we have a customer ID 1 coming in, we may only want to return our customer if it matches the customerId


func getData(customerId int) (customer string) {

  if customerId == 1 {
    return "Marcel Dempers"
  } else if customerId == 2 {
    return "Bob Smith"
  } else {
    return ""
  }
  
}


Let's invoke our function :

//in the main() function

//get our customer
customer := getData(1)
fmt.Println(customer)

//get the wrong customer
customer := getData(3)
fmt.Println(customer)


Arrays

At the moment, we can only return 1 customer at a time on our function.
Realistically we need the ability to return more data, not just a single customer.

Arrays allow us to make a collection of variables of the same type.
We can now return a list of customers!

Let's change our function to get an array of customers!

func getData() (customers [2]string) {
  //create 1 record
  customer := "Marcel Dempers"

  //assign our customer to the array
  customers[0] = customer

  //OR we can assign it like this
  customers[1] = "Bob Smith"

  //send it back to the caller
  return customers
  
}

Now we also have to change our calling function to expect an array:

customers := getData()

fmt.Println(customers)

Slices

Since arrays are fixed size, Slices are a dynamically-sized view into arrays. Let's create a slice instead of array so we can add customers dynamically!

func getData() (customers []string) {
  
  //initialise our slice of type string
  customers = []string{ "Marcel Dempers", "Bob Smith", "John Smith"}
  
  //add more legendary customers dynamically 
  customers = append(customers, "Ben Spain")
  customers = append(customers, "Aleem Janmohamed")
  customers = append(customers, "Jamie le Notre")
  customers = append(customers, "Victor Savkov")
  customers = append(customers, "P The Admin")
  customers = append(customers, "Adrian Oprea")
  customers = append(customers, "Jonathan D")

  //send it back to the caller
  return customers
  
}


Loops

Loops are used to iterate over collections, lists, arrays etc.
Let's say we need to loop through our customers

In the main() function, we can grab the list of customers and loop them. In this demo, we'll cover a basic for loop, but there are several approaches to writing loops.

//loop forever
for {
  //any code in here will run forever!
  
  fmt.Println("Infinite Loop 1")
	time.Sleep(time.Second)

  //unless we break out the loop like this
  break
}

//loop for x number of loops
for x := 0; x < 10; x++ {

  //any code in here will run 10 times! (unless we break!)
  fmt.Println(customers[x])

}

//loop for ALL our customer

for x, customer := range customers {

  //we can access the "customer" variable in this approach
  customer = customers[x]
  fmt.Println(customer)

  //OR
  //we can use the supplied customer from the loop
  // and silence the x variable, replace it with a _ character
  fmt.Println(customer)
}

Structs

So far so good, however, customer data is not useful as strings.
Customers can have a firstname, lastname, and more properties.

For this purpose we'd like to group some variables into a single variable.
This is what struct allows us to do.
Let's create a struct for our customer

Let's create a new go file called customers.go

package main

Customer struct {
  FirstName string
  LastName string
  FullName string
}

Let's put it all together:

In customers.go, let's create a function to get customers

func GetCustomers()(customers []Customer) {

  //we can declare customers like this:
  marcel := 	Customer{ FirstName: "Marcel", LastName: "Dempers" }

	customers = append(customers,
		Customer{ FirstName: "Marcel", LastName: "Dempers" },
		Customer{ FirstName: "Ben", LastName: "Spain" },
		Customer{ FirstName: "Aleem", LastName: "Janmohamed" },
		Customer{ FirstName: "Jamie", LastName: "le Notre" },
		Customer{ FirstName: "Victor", LastName: "Savkov" },
		Customer{ FirstName: "P", LastName: "The Admin" },
		Customer{ FirstName: "Adrian", LastName: "Oprea" },
		Customer{ FirstName: "Jonathan", LastName: "D" },
	)

	return customers

}

In main() we can now call our shiny new function

customers := GetCustomers()

for _, customer := range customers {
  //we can access the "customer" variable in this approach
  fmt.Println(customer)
}

Docker

For our dev environment, we have a simple image using go
We also set a work directory and alias the target as dev

This means we can use this container layer as a development environment.
Later down the track we can add debuggers in here for example.
Checkout my debugging video for go: https://youtu.be/kToyI16IFxs

Development environment

FROM golang:1.15 as dev

WORKDIR /work

Building our code

FROM golang:1.15 as build

WORKDIR /app
COPY ./app/* /app/
RUN go build -o app

The Runtime

FROM alpine as runtime 
COPY --from=build /app/app /
CMD ./app

Building the Container

docker build . -t customer-app

Running the Container

docker run customer-app