<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Go on /dev/jcheng</title><link>https://www.jcheng.org/tags/go/</link><description>Recent content in Go on /dev/jcheng</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 27 May 2022 09:12:20 -0700</lastBuildDate><atom:link href="https://www.jcheng.org/tags/go/index.xml" rel="self" type="application/rss+xml"/><item><title>Interpreter in Go - 4</title><link>https://www.jcheng.org/post/interpreter-in-go-4/</link><pubDate>Sun, 17 Oct 2021 14:32:31 -0700</pubDate><guid>https://www.jcheng.org/post/interpreter-in-go-4/</guid><description>&lt;p>In Chapter 1.3 of [Ball&amp;rsquo;s Writing an Interpreter in Go][1], we encounter one design decision of his Monkey programming
language. Here, the lexer has a NextToken() method that looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-Go" data-lang="Go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6ab825;font-weight:bold">func&lt;/span> (l *Lexer) &lt;span style="color:#447fcf">NextToken&lt;/span>() token.Token {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#6ab825;font-weight:bold">var&lt;/span> tok token.Token
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#6ab825;font-weight:bold">switch&lt;/span> l.ch {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#999;font-style:italic">// [...]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#6ab825;font-weight:bold">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#6ab825;font-weight:bold">if&lt;/span> &lt;span style="color:#447fcf">isLetter&lt;/span>(l.ch) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tok.Literal = l.&lt;span style="color:#447fcf">readIdentifier&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#6ab825;font-weight:bold">return&lt;/span> tok
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#6ab825;font-weight:bold">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tok = &lt;span style="color:#447fcf">newToken&lt;/span>(token.ILLEGAL, l.ch)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#999;font-style:italic">// [...]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This means the lexer itself does not do backtracking. The meaning of a character at any point cannot be ambiguous. You
cannot say, for example, that &amp;lsquo;+&amp;rsquo; is the &amp;lsquo;plus&amp;rsquo; token unless it is in the middle of a variable name. I don&amp;rsquo;t know many
programming languages that support such behavior &amp;ndash; so it is probably an acceptable design decision. You know what they
say, &amp;ldquo;Keep it Simple, Smartypants&amp;rdquo;. I&amp;rsquo;m not sure if there are other notable constraints introduced by the design at this
point, but it is something that tickles my overly analytical brain.&lt;/p></description></item><item><title>Interpreter in Go - 3</title><link>https://www.jcheng.org/post/interpreter-in-go-3/</link><pubDate>Sun, 22 Aug 2021 14:32:31 -0700</pubDate><guid>https://www.jcheng.org/post/interpreter-in-go-3/</guid><description>&lt;p>A lexer takes the source code, a sequence of characters, and group them into tokens. e.g., it makes the first decision
on how to process the strings &lt;code>100-10&lt;/code>, &lt;code>-100-10&lt;/code>, and &lt;code>-100--100&lt;/code> into groups. I&amp;rsquo;m going to call this grouping
&amp;ldquo;tokenization&amp;rdquo; even though I may be misusing the term.&lt;/p>
&lt;p>Tokenizing source code is hard. How should &lt;code>-100--100&lt;/code> be tokenized? Should it be a literal -100 followed by the minus
token, followed by another -100?&lt;/p></description></item><item><title>Interpreter in Go - 2</title><link>https://www.jcheng.org/post/interpreter-in-go-2/</link><pubDate>Thu, 29 Jul 2021 14:32:31 -0700</pubDate><guid>https://www.jcheng.org/post/interpreter-in-go-2/</guid><description>&lt;p>&lt;a href="https://interpreterbook.com/">Writing an Interpreter In Go&lt;/a> by Thorsten Ball will be my personal introduction to writing an interpreter. I&amp;rsquo;ve
never taken a comp sci class before, so I know nothing about compilers. On a lark, I decided to explore this area now,
nearly 20 years after I started to learn computer programming.&lt;/p>
&lt;p>If you are interested in this book as well, you might might the &lt;a href="https://astexplorer.net/">AST Explorer&lt;/a> a useful companion.&lt;/p>
&lt;p>I was told as some point in the past, that compilation can be broken down into four stages:&lt;/p></description></item><item><title>Interpreter in Go - 1</title><link>https://www.jcheng.org/post/interpreter-in-go-1/</link><pubDate>Tue, 27 Jul 2021 08:53:03 -0700</pubDate><guid>https://www.jcheng.org/post/interpreter-in-go-1/</guid><description>&lt;p>It happened. At the recommendation of &lt;a href="https://twitter.com/dgryski">https://twitter.com/dgryski&lt;/a>, I bought &lt;a href="https://interpreterbook.com/">Writing an Interpreter In Go&lt;/a>. This will be my next hobby project. It&amp;rsquo;ll be interesting to see if I ever finish it.&lt;/p></description></item><item><title>Posts Of The Week 2021-04-15</title><link>https://www.jcheng.org/post/potw20210415/</link><pubDate>Sun, 18 Apr 2021 12:07:58 -0700</pubDate><guid>https://www.jcheng.org/post/potw20210415/</guid><description>&lt;p>Lots of small things today.&lt;/p>
&lt;h1 id="history">History&lt;/h1>
&lt;p>&amp;ldquo;Those who cannot remember the past are condemned to repeat it&amp;rdquo;. I love reading about how software came to be the way they are today.&lt;/p>
&lt;blockquote>
&lt;p>Before we can talk about where generics are going, we first have to talk about where they are, and how they got there.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;a href="https://cr.openjdk.java.net/~briangoetz/erasure.html">https://cr.openjdk.java.net/~briangoetz/erasure.html&lt;/a>&lt;/p>
&lt;h1 id="go-errors">Go Errors&lt;/h1>
&lt;p>Error handling is still jacked in Go 1.16. That is, &lt;a href="https://github.com/golang/go/issues/29934#issuecomment-489682919">the formatting change is still not present&lt;/a>. Why is this a problem?
There are two use cases for errors. Error as values, which can be &lt;a href="https://go.googlesource.com/proposal/+/master/design/go2draft-error-values-overview.md">inspected programatically&lt;/a>, and &lt;a href="https://go.googlesource.com/proposal/+/master/design/go2draft-error-printing.md">error printing&lt;/a>, which
is not meant for programmatic consumption.&lt;/p></description></item><item><title>Workflow Orchestration - Part 3 (How do I use this?)</title><link>https://www.jcheng.org/post/workflow-orchestration-1.2/</link><pubDate>Sat, 03 Apr 2021 10:22:54 -0800</pubDate><guid>https://www.jcheng.org/post/workflow-orchestration-1.2/</guid><description>&lt;p>In this part of the series, we&amp;rsquo;ll write some hands-on &lt;a href="https://temporal.io/">Temporal&lt;/a> code and run it. Let&amp;rsquo;s start with our requirements:&lt;/p>
&lt;blockquote>
&lt;p>You need to transmit a data packet. You can choose from multiple Route Providers to do this. Transmission takes time
&amp;ndash; you will be notified on a callback URL when the packet is delivered. Delivery may fail &amp;ndash; either because the
acknowledgement was not sent or arrived late (because Internet). You should try the next provider when one fails.&lt;/p></description></item><item><title>Posts Of The Week 2021-04-01</title><link>https://www.jcheng.org/post/potw20210401/</link><pubDate>Thu, 01 Apr 2021 08:41:31 -0700</pubDate><guid>https://www.jcheng.org/post/potw20210401/</guid><description>&lt;p>I spent a couple of hours evaluating 3rd party libraries. What have I learned? For me, there&amp;rsquo;s one clear winner in a
small field of candidates.&lt;/p>
&lt;p>Presently, these are the top hits for &amp;ldquo;golang gauge counter timer&amp;rdquo;.&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://pkg.go.dev/github.com/go-kit/kit/metrics">https://pkg.go.dev/github.com/go-kit/kit/metrics&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://pkg.go.dev/github.com/facebookgo/metrics">https://pkg.go.dev/github.com/facebookgo/metrics&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/uber-go/tally">https://github.com/uber-go/tally&lt;/a>&lt;/li>
&lt;/ol>
&lt;p>The first result is go-kit. Go-kit isn&amp;rsquo;t a metrics library. Rather, it bills itself as a &amp;ldquo;framework for building
microservices.&amp;rdquo; Its metrics package is simply a &lt;a href="https://raw.githubusercontent.com/go-kit/kit/v0.10.0/metrics/metrics.go">set of interfaces&lt;/a>. You then refrence one of the many sub-packages
with concrete implementations. As a consequence, it&amp;rsquo;s &lt;a href="https://github.com/go-kit/kit/blob/v0.10.0/go.mod">go.mod&lt;/a> file is pretty huge.&lt;/p></description></item><item><title>Posts Of The Week 2021-03-25</title><link>https://www.jcheng.org/post/potw20210325/</link><pubDate>Wed, 24 Mar 2021 19:19:09 -0700</pubDate><guid>https://www.jcheng.org/post/potw20210325/</guid><description>&lt;p>&lt;a href="https://golang.org/">Go&lt;/a> does not allow cyclic imports. A solution is to create a &amp;ldquo;shared&amp;rdquo; package to hold interfaces that related
packages all reference. This, for some reason, reminds of me join tables in SQL.&lt;/p>
&lt;p>&lt;a href="https://www.jcheng.org/misc/temporal-package-deps.svg">Here is an example of a typical Go project&lt;/a>. Packages toward the bottom, e.g., &amp;ldquo;common/persistence&amp;rdquo;, allow different
packages to work with each other without introducing cyclic dependencies. For this project, &amp;ldquo;log&amp;rdquo; can be referenced by
&amp;ldquo;config&amp;rdquo;, but cannot use &amp;ldquo;config&amp;rdquo; to conifgure itself. &lt;figure>&lt;img src="https://www.jcheng.org/misc/temporal-package-deps.svg">
&lt;/figure>
&lt;/p></description></item><item><title>Posts Of The Week 2021-03-11</title><link>https://www.jcheng.org/post/potw20210311/</link><pubDate>Mon, 08 Mar 2021 10:17:13 -0800</pubDate><guid>https://www.jcheng.org/post/potw20210311/</guid><description>&lt;p>Oldie but goodie. Go concurrency patterns&lt;/p>
&lt;p>&lt;a href="https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view">https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view&lt;/a>&lt;/p></description></item><item><title>Go: Pointer vs Value</title><link>https://www.jcheng.org/post/go-pointer-vs-reference/</link><pubDate>Mon, 23 Nov 2020 08:35:16 -0800</pubDate><guid>https://www.jcheng.org/post/go-pointer-vs-reference/</guid><description>&lt;p>In A Tour of Go, it states &amp;ldquo;Go has pointers. A pointer holds the memory address of a value.&amp;rdquo; When you design your data
structure in Go, you have to decide between using a pointer or not. There&amp;rsquo;s no clear rule of thumb for it.&lt;/p>
&lt;p>I had been reading the Go source code to AWS&amp;rsquo;s client library for DynamoDB. For a while, I had been annoying with their
API design, which looks like this:&lt;/p></description></item><item><title>Go io/fs Design (Part I)</title><link>https://www.jcheng.org/post/go-iofs-design/</link><pubDate>Sat, 01 Aug 2020 09:31:48 -0700</pubDate><guid>https://www.jcheng.org/post/go-iofs-design/</guid><description>&lt;p>As usual, &lt;a href="https://lwn.net/Articles/827215/">LWN has a good write up on what&amp;rsquo;s going on in the Go community&lt;/a>. This week&amp;rsquo;s discussion in on the new &lt;code>io/fs&lt;/code>
package. The Go team decided to use a Reddit thread to host the conversation about this draft design. LWN points
out that posters raised the following concerns:&lt;/p>
&lt;ul>
&lt;li>We added status logging by wrapping http.ResponseWriter, and now HTTP/2 push doesn&amp;rsquo;t work anymore, because our wrapper
hides the Push method from the handlers downstream. / It becomes infeasible to use the decorator pattern &lt;a href="https://www.reddit.com/r/golang/comments/hv976o/qa_iofs_draft_design/fys5ck5/">more&lt;/a>&lt;/li>
&lt;li>Doing it &amp;ldquo;generically&amp;rdquo; involves a combinatorial explosion of optional interfaces&lt;/li>
&lt;/ul>
&lt;p>Ultimately, Russ Cox admits, &amp;ldquo;It&amp;rsquo;s true - there&amp;rsquo;s definitely a tension here between extensions and wrappers. I haven&amp;rsquo;t
seen any perfect solutions for that.&amp;rdquo;&lt;/p></description></item><item><title>Localstack S3 and Go</title><link>https://www.jcheng.org/post/localstack/</link><pubDate>Sun, 26 Jul 2020 09:52:37 -0700</pubDate><guid>https://www.jcheng.org/post/localstack/</guid><description>&lt;p>I spent too much time Saturday getting the Go S3 SDK to work with LocalStack.. It turns out that if you are using
LocalStack, you need to explicitly configure the following properties:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>	sess, err := session.NewSession(aws.NewConfig().
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		WithEndpoint(endpoint).
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		WithRegion(region).
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		WithS3ForcePathStyle(true))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The requirements for &lt;code>Endpoint&lt;/code> and &lt;code>Region&lt;/code> are obvious. If &lt;a href="https://github.com/localstack/localstack#troubleshooting">S3ForcePathStyle&lt;/a> is not specified, then LocalStack will
fail.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>	data, err := svc.GetObject(&amp;amp;s3.GetObjectInput{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		Bucket: &amp;amp;bucket,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		Key: &amp;amp;cfgKey,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	})
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>What is path-style? In May 2019, Amazon deprecated path-based access model for &lt;a href="https://aws.amazon.com/blogs/aws/amazon-s3-path-deprecation-plan-the-rest-of-the-story/">S3 objects&lt;/a>. This means one
should no longer use URLs of the form:&lt;/p></description></item><item><title>Gomock Tutorial</title><link>https://www.jcheng.org/post/gomock/</link><pubDate>Tue, 26 May 2020 08:15:36 -0700</pubDate><guid>https://www.jcheng.org/post/gomock/</guid><description>&lt;p>I&amp;rsquo;ve been using &lt;a href="https://godoc.org/github.com/golang/mock/gomock">&lt;code>mock/Gomock&lt;/code>&lt;/a> to write tests in my personal project. When you&amp;rsquo;re building something in a new
language, it is hard to prioritize learning every tool in your toolchain. For me, I&amp;rsquo;ve been writing custom and
suboptimal code for Gomock because of a nifty but undocumented API call &lt;code>.Do&lt;/code>.&lt;/p>
&lt;p>In many cases, I wan to match subsets of a complex object while ignoring irrelevant parts. e.g., verify a function is
invoked with a list of &lt;code>User&lt;/code> objects, but only verifying the email addresses. To do that in a generic
way, I wrote a custom Matcher API that uses &lt;a href="https://golang.org/pkg/text/template/">&lt;code>text/template&lt;/code>&lt;/a> to describe what parts of the object to match. Thus, my
mock-and-verify code looks like:&lt;/p></description></item><item><title>Go Project Organization</title><link>https://www.jcheng.org/post/go-project-organization/</link><pubDate>Sun, 17 May 2020 10:31:12 -0700</pubDate><guid>https://www.jcheng.org/post/go-project-organization/</guid><description>&lt;p>Here&amp;rsquo;s a rough layout of how I organize my Go project. Some parts are situational and some parts are essential. I&amp;rsquo;ll go
over both in this blog.&lt;/p>
&lt;p>A rough layout:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>+ basedir
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- go.mod (module jcheng.org)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- hello (empty)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- log/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- utils/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- config/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- models/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- repositories/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- services/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- cmd/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- hello_app/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +--/cmd/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- speak/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- email/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> +-- sms/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="the-basedir">The basedir&lt;/h3>
&lt;p>Situational.&lt;/p></description></item><item><title>Mocking in Go Part 2</title><link>https://www.jcheng.org/post/mocking-in-go-part-2/</link><pubDate>Wed, 19 Feb 2020 20:55:23 -0800</pubDate><guid>https://www.jcheng.org/post/mocking-in-go-part-2/</guid><description>&lt;p>In a &lt;a href="https://www.jcheng.org/post/mocking-in-go-part-1/">previous post&lt;/a>, I talked about Gomock. I want to spend a bit more time on
it.&lt;/p>
&lt;p>As I&amp;rsquo;ve mentioned, setting up Gomock requires&lt;/p>
&lt;ol>
&lt;li>Download mockgen&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>go get github.com/golang/mock/mockgen@latest
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Edit your go.mod&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>module jcheng.org
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>go 1.13
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>require (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	github.com/golang/mock v1.4.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>Add the go:generate incantation to your code&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-Go" data-lang="Go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#999;font-style:italic">//go:generate mockgen -source $GOFILE -destination mock_$GOFILE -package $GOPACKAGE&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6ab825;font-weight:bold">package&lt;/span> pastself
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6ab825;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#ed9d13">&amp;#34;time&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="4">
&lt;li>Use mocks in your test case. In this example, I used a callback function to match on a nested property.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-Go" data-lang="Go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6ab825;font-weight:bold">type&lt;/span> fnmatcher &lt;span style="color:#6ab825;font-weight:bold">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	d &lt;span style="color:#6ab825;font-weight:bold">func&lt;/span>(x &lt;span style="color:#6ab825;font-weight:bold">interface&lt;/span>{}) &lt;span style="color:#6ab825;font-weight:bold">bool&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	desc &lt;span style="color:#6ab825;font-weight:bold">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6ab825;font-weight:bold">func&lt;/span> (self *fnmatcher) &lt;span style="color:#447fcf">Matches&lt;/span>(x &lt;span style="color:#6ab825;font-weight:bold">interface&lt;/span>{}) &lt;span style="color:#6ab825;font-weight:bold">bool&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#6ab825;font-weight:bold">return&lt;/span> self.&lt;span style="color:#447fcf">d&lt;/span>(x)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6ab825;font-weight:bold">func&lt;/span> (self *fnmatcher) &lt;span style="color:#447fcf">String&lt;/span>() &lt;span style="color:#6ab825;font-weight:bold">string&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#6ab825;font-weight:bold">return&lt;/span> self.desc
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6ab825;font-weight:bold">func&lt;/span> &lt;span style="color:#447fcf">On&lt;/span>(desc &lt;span style="color:#6ab825;font-weight:bold">string&lt;/span>, d &lt;span style="color:#6ab825;font-weight:bold">func&lt;/span>(x &lt;span style="color:#6ab825;font-weight:bold">interface&lt;/span>{}) &lt;span style="color:#6ab825;font-weight:bold">bool&lt;/span>) mock.Matcher {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#6ab825;font-weight:bold">return&lt;/span> &amp;amp;fnmatcher{d: d, desc: desc}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#999;font-style:italic">// TestSendOverdueMessages_ok is an example of using matching using a callback function&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6ab825;font-weight:bold">func&lt;/span> &lt;span style="color:#447fcf">TestSendOverdueMessages_ok&lt;/span>(t *testing.T) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	ctrl := mock.&lt;span style="color:#447fcf">NewController&lt;/span>(t)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#6ab825;font-weight:bold">defer&lt;/span> ctrl.&lt;span style="color:#447fcf">Finish&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	mockUserRepo := &lt;span style="color:#447fcf">NewMockUserRepository&lt;/span>(ctrl)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	mockMessageRepo := &lt;span style="color:#447fcf">NewMockMessageRepository&lt;/span>(ctrl)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	mockMessageSender := &lt;span style="color:#447fcf">NewMockMessageSender&lt;/span>(ctrl)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	log, _ := &lt;span style="color:#447fcf">NewBufferLog&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	r := &lt;span style="color:#447fcf">NewPastSelfService&lt;/span>(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		mockMessageRepo,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		mockMessageSender,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		mockUserRepo,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		log,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	senderUserDDB_1 := &amp;amp;UserDDB{UserID: &lt;span style="color:#ed9d13">&amp;#34;sender1&amp;#34;&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	senderUserDDB_2 := &amp;amp;UserDDB{UserID: &lt;span style="color:#ed9d13">&amp;#34;sender2&amp;#34;&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	recipientUserDDB_2 := &amp;amp;UserDDB{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		UserID: &lt;span style="color:#ed9d13">&amp;#34;recipient2&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		Profile: UserProfileDDB{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			PreferredEmail: &lt;span style="color:#ed9d13">&amp;#34;recipient2@example.com&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	resultSet := &amp;amp;ResultSet{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		Items: []Message{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				ID: &lt;span style="color:#3677a9">1&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				SenderID: &lt;span style="color:#ed9d13">&amp;#34;sender1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				Body: &lt;span style="color:#ed9d13">&amp;#34;body1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				Recipients: []&lt;span style="color:#6ab825;font-weight:bold">string&lt;/span>{&lt;span style="color:#ed9d13">&amp;#34;email://recipient1@example.com&amp;#34;&lt;/span>, &lt;span style="color:#ed9d13">&amp;#34;recipient2&amp;#34;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				Headers: []Header{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					{Name: &lt;span style="color:#ed9d13">&amp;#34;x-header-key-1&amp;#34;&lt;/span>, Value: &lt;span style="color:#ed9d13">&amp;#34;value-1&amp;#34;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					{Name: &lt;span style="color:#ed9d13">&amp;#34;x-header-key-2&amp;#34;&lt;/span>, Value: &lt;span style="color:#ed9d13">&amp;#34;value-2&amp;#34;&lt;/span>},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				ID: &lt;span style="color:#3677a9">2&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				SenderID: &lt;span style="color:#ed9d13">&amp;#34;sender2&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				Body: &lt;span style="color:#ed9d13">&amp;#34;body2&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	mockUserRepo.&lt;span style="color:#447fcf">EXPECT&lt;/span>().&lt;span style="color:#447fcf">GetUser&lt;/span>(&lt;span style="color:#ed9d13">&amp;#34;sender1&amp;#34;&lt;/span>).&lt;span style="color:#447fcf">Return&lt;/span>(senderUserDDB_1, &lt;span style="color:#6ab825;font-weight:bold">nil&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	mockUserRepo.&lt;span style="color:#447fcf">EXPECT&lt;/span>().&lt;span style="color:#447fcf">GetUser&lt;/span>(&lt;span style="color:#ed9d13">&amp;#34;sender2&amp;#34;&lt;/span>).&lt;span style="color:#447fcf">Return&lt;/span>(senderUserDDB_2, &lt;span style="color:#6ab825;font-weight:bold">nil&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	mockUserRepo.&lt;span style="color:#447fcf">EXPECT&lt;/span>().&lt;span style="color:#447fcf">GetUser&lt;/span>(&lt;span style="color:#ed9d13">&amp;#34;recipient2&amp;#34;&lt;/span>).&lt;span style="color:#447fcf">Return&lt;/span>(recipientUserDDB_2, &lt;span style="color:#6ab825;font-weight:bold">nil&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	mockMessageRepo.&lt;span style="color:#447fcf">EXPECT&lt;/span>().&lt;span style="color:#447fcf">FindOverdue&lt;/span>(mock.&lt;span style="color:#447fcf">Any&lt;/span>(), &lt;span style="color:#3677a9">0&lt;/span>, &lt;span style="color:#ed9d13">&amp;#34;&amp;#34;&lt;/span>).&lt;span style="color:#447fcf">Return&lt;/span>(resultSet, &lt;span style="color:#6ab825;font-weight:bold">nil&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	matchFrom := &lt;span style="color:#447fcf">On&lt;/span>(&lt;span style="color:#ed9d13">&amp;#34;user.UserID==sender1&amp;#34;&lt;/span>, &lt;span style="color:#6ab825;font-weight:bold">func&lt;/span>(x &lt;span style="color:#6ab825;font-weight:bold">interface&lt;/span>{}) &lt;span style="color:#6ab825;font-weight:bold">bool&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#6ab825;font-weight:bold">if&lt;/span> y, ok := x.(*UserDDB); ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#6ab825;font-weight:bold">return&lt;/span> y.UserID == &lt;span style="color:#ed9d13">&amp;#34;sender1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#6ab825;font-weight:bold">return&lt;/span> &lt;span style="color:#6ab825;font-weight:bold">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	matchTo := &lt;span style="color:#447fcf">On&lt;/span>(&lt;span style="color:#ed9d13">&amp;#34;[]Recipient{recipient1@example.com,recipient2@example.com}&amp;#34;&lt;/span>, &lt;span style="color:#6ab825;font-weight:bold">func&lt;/span>(x &lt;span style="color:#6ab825;font-weight:bold">interface&lt;/span>{}) &lt;span style="color:#6ab825;font-weight:bold">bool&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#6ab825;font-weight:bold">if&lt;/span> y, ok := x.([]Recipient); ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#6ab825;font-weight:bold">return&lt;/span> &lt;span style="color:#24909d">len&lt;/span>(y) == &lt;span style="color:#3677a9">2&lt;/span> &amp;amp;&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				y[&lt;span style="color:#3677a9">0&lt;/span>].Email == &lt;span style="color:#ed9d13">&amp;#34;recipient1@example.com&amp;#34;&lt;/span> &amp;amp;&amp;amp;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				y[&lt;span style="color:#3677a9">1&lt;/span>].Email == &lt;span style="color:#ed9d13">&amp;#34;recipient2@example.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#6ab825;font-weight:bold">return&lt;/span> &lt;span style="color:#6ab825;font-weight:bold">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	matchOutMessage := &lt;span style="color:#447fcf">On&lt;/span>(&lt;span style="color:#ed9d13">&amp;#34;OutboundMessage.Body==Body1&amp;#34;&lt;/span>, &lt;span style="color:#6ab825;font-weight:bold">func&lt;/span>(x &lt;span style="color:#6ab825;font-weight:bold">interface&lt;/span>{}) &lt;span style="color:#6ab825;font-weight:bold">bool&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#6ab825;font-weight:bold">if&lt;/span> y, ok := x.(OutboundMessage); ok {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#6ab825;font-weight:bold">return&lt;/span> y.Body == &lt;span style="color:#ed9d13">&amp;#34;body1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#6ab825;font-weight:bold">return&lt;/span> &lt;span style="color:#6ab825;font-weight:bold">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	mockMessageSender.&lt;span style="color:#447fcf">EXPECT&lt;/span>().&lt;span style="color:#447fcf">Send&lt;/span>(matchFrom, matchTo, matchOutMessage).&lt;span style="color:#447fcf">Return&lt;/span>(&lt;span style="color:#6ab825;font-weight:bold">nil&lt;/span>).&lt;span style="color:#447fcf">MaxTimes&lt;/span>(&lt;span style="color:#3677a9">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	r.&lt;span style="color:#447fcf">SendOverdueMessages&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>One thing nice about GoMock is that it generates statically typed method names for the &lt;code>EXPECT()&lt;/code> statements, so the
compiler can check that you&amp;rsquo;re using the correct method names. Neat.&lt;/p></description></item><item><title>Few Annoying Things about Go</title><link>https://www.jcheng.org/post/annoying_go/</link><pubDate>Wed, 19 Feb 2020 08:45:45 -0800</pubDate><guid>https://www.jcheng.org/post/annoying_go/</guid><description>&lt;p>As promised, a few thoughts about Go that is annoying.&lt;/p>
&lt;h1 id="no-generics">No Generics&lt;/h1>
&lt;p>Code generation is tightly coupled with the tool chain. When I need to use code generation, i.e., enums and
mocks, the use case can be solved with generics instead.&lt;/p>
&lt;h1 id="no-lambda-syntax">No lambda syntax&lt;/h1>
&lt;p>Scala has a lambda syntax to make it easy to work with anonymous function objects&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-scala" data-lang="scala">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6ab825;font-weight:bold">val&lt;/span> numbers &lt;span style="color:#6ab825;font-weight:bold">=&lt;/span> &lt;span style="color:#447fcf;text-decoration:underline">Seq&lt;/span>(&lt;span style="color:#3677a9">1&lt;/span>, &lt;span style="color:#3677a9">5&lt;/span>, &lt;span style="color:#3677a9">2&lt;/span>, &lt;span style="color:#3677a9">100&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6ab825;font-weight:bold">val&lt;/span> doubled &lt;span style="color:#6ab825;font-weight:bold">=&lt;/span> numbers.map(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> n &lt;span style="color:#6ab825;font-weight:bold">=&amp;gt;&lt;/span> n * &lt;span style="color:#3677a9">2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Java has a similar syntax&lt;/p></description></item><item><title>Mocking in Go</title><link>https://www.jcheng.org/post/mocking-in-go-part-1/</link><pubDate>Fri, 14 Feb 2020 08:59:39 -0800</pubDate><guid>https://www.jcheng.org/post/mocking-in-go-part-1/</guid><description>&lt;p>Today I want to talk a little about the &lt;a href="https://github.com/stretchr/testify">testify&lt;/a> and &lt;a href="https://github.com/golang/mock">gomock&lt;/a> packages and doing mocks in Go.&lt;/p>
&lt;p>Mocking is essential when writing code that depends on external services, e.g., microservices, message brokers, and
datastores. For many people, this means web applications: My system has business rules and talks to multiple
services. How do I test the business rules without setting up all the services locally?&lt;/p>
&lt;p>Some people argue that mocks create a false sense of test coverage and introduce blind spots. I think that&amp;rsquo;s partially
true but also too simplistic. In many cases, engineers gain sufficient value from testing &amp;ldquo;glue code&amp;rdquo; to make mocking
worthwhile. This follows the principle of don&amp;rsquo;t let perfect be the enemy of good.&lt;/p></description></item><item><title>Why I like Go</title><link>https://www.jcheng.org/post/why-go/</link><pubDate>Sat, 08 Feb 2020 19:38:34 -0800</pubDate><guid>https://www.jcheng.org/post/why-go/</guid><description>&lt;p>I have a few side projects, &lt;a href="https://github.com/jlcheng/grs">published&lt;/a> and unpublished. By trade, I&amp;rsquo;ve come to programming as a Java
programmer. I started coding directly against the Servlet API and have coded using Enterprise Java Beans, Play
Framework, Spring Framework, and even some Scala. Lately, I&amp;rsquo;ve been coding mainly in Go with some Python.&lt;/p>
&lt;p>Go is a really nice programming language. Some people like it for its simplicity. I want to offer a different take on
why you should learn and use Go for your own projects.&lt;/p></description></item><item><title>Viper Examples</title><link>https://www.jcheng.org/post/go-viper-examples/</link><pubDate>Sat, 04 Jan 2020 13:16:22 -0800</pubDate><guid>https://www.jcheng.org/post/go-viper-examples/</guid><description>&lt;p>Viper is a configuration library fo Go. It has been my go to library for configs. Some of the best features
of Viper are:&lt;/p>
&lt;ul>
&lt;li>Ability to unmarshal subsets of a configuration file into a Go struct&lt;/li>
&lt;li>Ability to override contents of a configuration file using OS environment variables&lt;/li>
&lt;/ul>
&lt;p>Here&amp;rsquo;s a brief example that demonstrates both features:&lt;/p>
&lt;p>Assume you have a configuration file at &lt;code>$HOME/server.toml&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-toml" data-lang="toml">&lt;span style="display:flex;">&lt;span>resolve_dns = &lt;span style="color:#6ab825;font-weight:bold">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>http_keep_alive = &lt;span style="color:#3677a9">5&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[example_com]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>doc_root = &lt;span style="color:#ed9d13">&amp;#34;/var/www/example_com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>allow_files = &lt;span style="color:#ed9d13">&amp;#34;*.html&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>login_required = &lt;span style="color:#6ab825;font-weight:bold">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>login_lockout_count = &lt;span style="color:#3677a9">3&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[example_org]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>doc_root = &lt;span style="color:#ed9d13">&amp;#34;/var/www/example_org&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>allow_files = &lt;span style="color:#ed9d13">&amp;#34;*.html, *.jpg&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>login_required = &lt;span style="color:#6ab825;font-weight:bold">false&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>login_lockout_count = &lt;span style="color:#3677a9">5&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then you can utilize the configuration file using this snippet:&lt;/p></description></item><item><title>Go1 13 Errors</title><link>https://www.jcheng.org/post/go1-13-errors/</link><pubDate>Fri, 29 Nov 2019 14:27:32 -0800</pubDate><guid>https://www.jcheng.org/post/go1-13-errors/</guid><description>&lt;p>For me, Go 1.13 arrived with anticipation of better error handling. Presently, the second Google
search result for Go 1.13 error hanlding is an &lt;a href="https://medium.com/yakka/better-go-error-handling-with-xerrors-1987650e0c79">article that refers to the &amp;ldquo;xerrors&amp;rdquo;
package&lt;/a>. One feature
of the xerrors package is that it produced errors with a stack trace showing where the error came
from. The addition to Go 1.13, however, &lt;a href="https://github.com/golang/go/issues/29934#issuecomment-489682919">did not include this particular
feature&lt;/a>, and left me spending
frustrating hours trying to debug the loss of stack trace after switching from xerrors to the
standard library.&lt;/p></description></item><item><title>Own Your Data</title><link>https://www.jcheng.org/post/own-your-data/</link><pubDate>Mon, 19 Aug 2019 11:56:10 -0700</pubDate><guid>https://www.jcheng.org/post/own-your-data/</guid><description>&lt;p>I previously wrote about &lt;a href="https://www.jcheng.org/post/another-year-another-blog/">owning my own data&lt;/a>. An
important part of data ownership is backing up your data. I use S3 as my long term data store. It is
pretty easy to set this up using &lt;a href="https://www.terraform.io/">Terraform&lt;/a>.&lt;/p>
&lt;h3 id="s3">S3&lt;/h3>
&lt;p>Provisioning a S3 bucket is simply a single Terraform resource:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#d0d0d0;background-color:#202020;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-hcl" data-lang="hcl">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6ab825;font-weight:bold">resource&lt;/span> &lt;span style="color:#ed9d13">&amp;#34;aws_s3_bucket&amp;#34; &amp;#34;repo_archive_log&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> acl = &lt;span style="color:#ed9d13">&amp;#34;log-delivery-write&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bucket = &lt;span style="color:#ed9d13">&amp;#34;example-bucket&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tags = {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Name = &lt;span style="color:#ed9d13">&amp;#34;example&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> TTL = &lt;span style="color:#ed9d13">&amp;#34;persistent&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ManagedBy = &lt;span style="color:#ed9d13">&amp;#34;Terraform&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item></channel></rss>