Attempting to Learn Go - Let's Get Modular - Again!

Let’s Get Modular - Again

Previously, we released our very first module version of the mailgunner package. This time around let’s see what we would need to do to release a new version - we’ll make a few small tweaks to the code, update the readme, and then cut a new release.


Ditch http.DefaultClient, Sort of

One of the issues with using http.DefaultClient is that it doesn’t set some sensible defaults, it’s basically, just like it says, a barebones client. What if someone needs to proxy a request? Or set a timeout? It can’t be done easily with our previous module. Since that’s the case, we want to make it so our module isn’t reliant on it. So we’re going to extend it a bit to make it possible to pass in a custom client. We’re really own changing the New() function so, I’ll only show that here, refer to the repo or the previous article to see the complete code.

Our original function was very simple, just returning a struct that contained two strings and http.DefaultClient.

type MgClient struct {
  MgAPIURL string
  MgAPIKey string
  Client   *http.Client
}

func New(apiurl, apikey string) MgClient {
  return MgClient{
    apiurl,
    apikey,
    http.DefaultClient,
  }
}

As I said we won’t be changing the struct itself - we’ll simply update New() to take another parameter. Also, note that we’re now returning a pointer to the MgClient which probably should have been in the previous version. But hey, we’re still learning. Our new parameter, cleverly named, client will accept *http.Client - we’ll go over how to actually use it in a bit.

func New(apiurl, apikey string, client *http.Client) *MgClient {
  if client == nil {
    client = http.DefaultClient
  }
  return &MgClient{
    apiurl,
    apikey,
    client,
  }
}

If whoever is using the module declines to create a custom client and passes nil instead we’ll fall back to using http.DefaultClient.

  mgc := mailgunner.New(apiurl, mgKey, nil)

Custom Client

Alright, but how do we make use of the custom client? It’s actually pretty easy! Before calling New() we’ll make a new variable named cc and give it &http.Client{} with a timeout of 10 seconds.

  cc := &http.Client{
    Timeout: time.Second * 10,
  }

With our fancy custom client in hand, we’ll pass that into New() as our shiny new third parameter.

  mgc := mailgunner.New(apiurl, mgKey, cc)

Everything else works as it did before. 🎉

Lets Go v2

We’re not quite ready to release our new module, first, we’ll have to update the readme, which I won’t repost here for brevity. Afterward, we then need to update our go.mod file to indicate we’re at a new version. I’ve decided this is a major version since we’ve changed the signature of the New() function. The new go.mod simply has “v2” appended to it.

module github.com/shindakun/mailgunner/v2

We’ll run through our Git commands to push everything to Github. We’ll start by making a v2 branch (which you likely would create before you started making code changes, I didn’t happen to though), adding our changes, which are then committed to that branch.

git checkout -b v2
git add .
git commit -m "Updated New() to take http.Client so we don't have to rely on http.DefaultClient"

So far so good. Now we’ll push our branch up to the remote, then tag and push the tags.

git push --set-upstream origin v2
git tag v2.0.0
git push --tags origin v2

Now anyone can simply import the module with a /v2 and run go build to be in business!

➜  GO111MODULE=on go build -v
github.com/shindakun/mailgunner/v2
github.com/shindakun/mailgunner/v2/example

{% github shindakun/mailgunner %}


Next time

I’m still thinking about putting together a series of posts on how I want to build out a “devlog” over on my own domain. I just need to decide how I want to tackle it.

Well, until then…


You can find the code for this and most of the other Attempting to Learn Go posts in the repo on GitHub.

{% github shindakun/atlg %}


Enjoy this post?
How about buying me a coffee?