<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Luís Costa</title>
    <description>The latest articles on Forem by Luís Costa (@luispcosta).</description>
    <link>https://forem.com/luispcosta</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F52492%2F2e49a3b5-f5d6-42bc-a9c5-cbb8a3d261d5.jpeg</url>
      <title>Forem: Luís Costa</title>
      <link>https://forem.com/luispcosta</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/luispcosta"/>
    <language>en</language>
    <item>
      <title>go-timetracker | Part II - Initial Struggles</title>
      <dc:creator>Luís Costa</dc:creator>
      <pubDate>Wed, 10 Jun 2020 17:54:23 +0000</pubDate>
      <link>https://forem.com/luispcosta/go-timetracker-part-ii-initial-struggles-1hf</link>
      <guid>https://forem.com/luispcosta/go-timetracker-part-ii-initial-struggles-1hf</guid>
      <description>&lt;p&gt;Hello and welcome back to this series of posts where I document my journey into building a simple time tracker in Golang. In this blog post, we'll take a look into what I've worked on in the past couple of months for the project, what I struggled with, and what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Struggle #1: Installing Dep on Windows
&lt;/h2&gt;

&lt;p&gt;The first struggle I faced was installing &lt;a href="https://github.com/golang/dep"&gt;&lt;code&gt;dep&lt;/code&gt;&lt;/a&gt; on windows. For those who don't know, &lt;code&gt;dep&lt;/code&gt; is a dependency management tool for Go, just like &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;bundler&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As documented in the README of the repository, to install &lt;code&gt;dep&lt;/code&gt; on windows, you should download a tarball from a specific link, but that link is broken, responding with a 404. There is currently an issue opened about this, &lt;a href="https://github.com/golang/dep/issues/2243#issuecomment-636313792"&gt;which I bumped&lt;/a&gt; but sadly no one has yet had the time to take a look into it. &lt;/p&gt;

&lt;p&gt;The README also says that for any other platforms you can use a simple install script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When I initially run this command on my machine, I kept getting the error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Installation requires your GOBIN directory C/bin to exist
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So, I needed to set up a bin folder for my go environment. I ended up creating a &lt;code&gt;bin&lt;/code&gt; bolder in my &lt;code&gt;GOPATH&lt;/code&gt; folder, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nv"&gt;$GOPATH&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;bin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now the script above runs just fine! Now, to run &lt;code&gt;dep&lt;/code&gt; I needed to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ $GOPATH&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;bin&lt;span class="se"&gt;\\&lt;/span&gt;dep init
&lt;span class="nv"&gt;$ $GOPATH&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;bin&lt;span class="se"&gt;\\&lt;/span&gt;dep ensure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;init&lt;/code&gt; command is similar to &lt;code&gt;npm init&lt;/code&gt;, creating an empty &lt;code&gt;Gopkg.toml&lt;/code&gt; file in the current directory. The dependencies of the project can be specified in that file (refer to &lt;a href="https://golang.github.io/dep/docs/Gopkg.toml.html"&gt;the documentation page to understand how it works&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Voila. I can now start coding/designing!&lt;/p&gt;

&lt;h2&gt;
  
  
  Struggle #2: Building the CLI skeleton
&lt;/h2&gt;

&lt;p&gt;For the initial version of the tool, I decided a simple CLI application would be good enough. Before trying any solution/library from other people, I tried to build my own, and it wasn't that difficult to understand the basics. Golang's documentation helped me here.&lt;/p&gt;

&lt;p&gt;All you need is the &lt;a href="https://golang.org/pkg/flag/"&gt;flag standard library&lt;/a&gt;. You can build powerful CLI apps just with this library, which is awesome. Initially, I built a simple CLI app with several subcommands, using as templates the commands I described in the &lt;a href="https://luispcosta.github.io/posts/go-tt-part-i/"&gt;previous blog post&lt;/a&gt;, where they didn't do anything useful, it was meant purely to test out the flag lib.&lt;/p&gt;

&lt;p&gt;Then, figuring I'd want to build something more robust (with helper messages, command descriptions, etc) and deciding that I didn't want to re-invent the wheel, I started looking online for Golang open-source CLI app builders. I stumbled upon two: &lt;a href="https://github.com/spf13/cobra"&gt;Cobra&lt;/a&gt; and &lt;a href="https://github.com/urfave/cli"&gt;Urfave/cli&lt;/a&gt;. I ended up using Cobra for no particular reason: it was just the first one I used, and I stuck with it.&lt;/p&gt;

&lt;p&gt;I built the skeleton of the app quite quickly, which was nice because it meant I could just focus on the core logic of my application right away.&lt;/p&gt;

&lt;p&gt;Now, I realize I wrote in the previous post that I wanted to use as little dependencies as possible. I do not intend to add any other dependency, at the time of this writing, besides Cobra (unless, of course, I want to extend the app and use other persistence mechanisms, like a database).&lt;/p&gt;

&lt;p&gt;After writing the skeleton of the CLI, here's what it looks like when I type &lt;code&gt;go run main.go help&lt;/code&gt; (I didn't have to write the &lt;code&gt;help&lt;/code&gt; command since that is provided by default by &lt;code&gt;Cobra&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go run main.go &lt;span class="nb"&gt;help

&lt;/span&gt;With go-tt, you can easily track the &lt;span class="nb"&gt;time &lt;/span&gt;you spend on different activities throughout your day.
go-tt provides several reports outlining the &lt;span class="nb"&gt;time &lt;/span&gt;you have spent &lt;span class="k"&gt;in &lt;/span&gt;all your registered activities.
The goal of this small app is to &lt;span class="nb"&gt;help &lt;/span&gt;you fight procrastination, 
by making you aware of where you chose to spend your time.

Usage:
  tt &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

Available Commands:
  add         Adds a new activity
  del         Deletes an activity
  &lt;span class="nb"&gt;help        &lt;/span&gt;Help about any &lt;span class="nb"&gt;command
  &lt;/span&gt;list        Lists all activities
  start       Starts an activity

Flags:
  &lt;span class="nt"&gt;-h&lt;/span&gt;, &lt;span class="nt"&gt;--help&lt;/span&gt;   &lt;span class="nb"&gt;help &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;tt

Use &lt;span class="s2"&gt;"tt [command] --help"&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;more information about a command.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(Not all commands are implemented at this time of writing). It's starting to look like a real CLI app!&lt;/p&gt;

&lt;h2&gt;
  
  
  Struggle #3: Designing the &lt;em&gt;Persistence&lt;/em&gt; Module
&lt;/h2&gt;

&lt;p&gt;Before diving into writing any code, I needed to think about how was I going to structure the &lt;em&gt;persistence&lt;/em&gt; module of the app. The persistence module is the module that handles the logic of storing/reading activity data from some internal persistence mechanism. The details of that implementation should be hidden from the outside. I didn't want to lock myself to a particular implementation so the module interface should only deal with, if possible, core domain models (such as &lt;code&gt;Activity&lt;/code&gt;). I decided to go with a simple solution: create an &lt;a href="https://gobyexample.com/interfaces"&gt;&lt;code&gt;interface&lt;/code&gt;&lt;/a&gt; to expose the API of the persistence module. I called it &lt;code&gt;ActivityRepository&lt;/code&gt;. Here's how the first version of that interface looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ActivityRepository&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Initialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Activity&lt;/span&gt;
    &lt;span class="n"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="n"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Initialize&lt;/code&gt; method should handle the logic to initialize the whole repository. For a file-based implementation, it could be something like setting up the necessary files/folders in the user's file system, dealing with permissions on those files, etc. For a database based implementation, it could be setting the needed connections.&lt;/p&gt;

&lt;p&gt;The remaining methods should be self-explanatory. &lt;/p&gt;

&lt;h2&gt;
  
  
  Struggle #4: Implementing a JSON repository
&lt;/h2&gt;

&lt;p&gt;As I wrote in the previous post, the initial implementation for the persistence module would be based on a simple JSON data storage.&lt;/p&gt;

&lt;p&gt;The first step was choosing a folder in the user's file systems to store the data. I set up the app so that on the first use, the folder &lt;code&gt;.gott&lt;/code&gt; would be created in the user's home folder (for that I used the method &lt;a href="https://golang.org/pkg/os/#UserHomeDir"&gt;&lt;code&gt;HomeDir&lt;/code&gt;&lt;/a&gt; from the current OS user, which is fetched from the method &lt;code&gt;Current&lt;/code&gt; of the Golang's standard library &lt;code&gt;os/user&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;I had to write &lt;em&gt;a lot&lt;/em&gt; of utility methods to create/read/delete files from the filesystem, which turned out &lt;em&gt;okay&lt;/em&gt; because I learned a lot about IO in Golang. &lt;/p&gt;

&lt;h2&gt;
  
  
  Struggle #5: Indexing activities with aliases
&lt;/h2&gt;

&lt;p&gt;The next difficulty was finding a way to quickly fetch an activity given an alias. As I wrote in the previous post, an activity can have a name and an alias. This is so that the activity name can act more as a description, and the alias more as the identifier (perhaps I should rename these fields, uh). This way, the user doesn't have to remember exactly the activity name to start it, and can quickly reference it via its alias. The aliases are shown with the &lt;code&gt;List&lt;/code&gt; command, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go run main.go add Activity1 &lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;a1
&lt;span class="nv"&gt;$ &lt;/span&gt;go run main.go add Activity2 &lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;act2
&lt;span class="nv"&gt;$ &lt;/span&gt;go run main.go add Activity3 &lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3

&lt;span class="nv"&gt;$ &lt;/span&gt;go run main.go list
activity1 &lt;span class="o"&gt;(&lt;/span&gt;a1&lt;span class="o"&gt;)&lt;/span&gt;
activity2 &lt;span class="o"&gt;(&lt;/span&gt;act2&lt;span class="o"&gt;)&lt;/span&gt;
activity3 &lt;span class="o"&gt;(&lt;/span&gt;3&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;How do we make sure that fetching the associated activity JSON file given an alias is as fast as possible (given our initial implementation using a JSON based repository)? The naive way would be to open each and every activity file in the data folder, read its contents into memory, fetching the &lt;code&gt;Alias&lt;/code&gt; key (for now, an activity JSON file contains: &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;alias&lt;/code&gt;) and seeing if it matches the alias given as input. Of course, this is terribly slow.&lt;/p&gt;

&lt;p&gt;Another option would be to give a specific file name structure to each file. Something like &lt;code&gt;ActivityName_Alias.json&lt;/code&gt;, and we could quickly identify the files by doing some regexp shenanigans, but this didn't quite felt right.&lt;/p&gt;

&lt;p&gt;Another solution I thought about was using an in-memory database, like Redis, to store mappings between aliases and file names. But this would require an additional service to be running on the user's machine, which I think doesn't make sense for such a small CLI app.&lt;/p&gt;

&lt;p&gt;So I ended up with creating a simple version of an index: a file called &lt;code&gt;index.json&lt;/code&gt; which contains the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"alias1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/path/to/activityfile.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"alias2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/path/to/activityfile.json"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Simple to use and implement. Of course, as always, this has the disadvantage of having to load the entire index file in memory and also the need to add/remove entries every time a new activity is created or deleted but, for now, it will suffice.&lt;/p&gt;




&lt;p&gt;That'll be all for now! Thanks for reading! Remember you can take a look at &lt;a href="https://github.com/luispcosta/go-tt"&gt;the repository&lt;/a&gt;. If you have any suggestions on how I can improve the code, please open a ticket or better yet fork it and open a PR. Remember: &lt;strong&gt;I'm a noob at Golang, so I may have a lot of bad/inefficient code&lt;/strong&gt;. But bare with me, I'm learning!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>go</category>
    </item>
    <item>
      <title>Golang DNS Resolving</title>
      <dc:creator>Luís Costa</dc:creator>
      <pubDate>Thu, 23 Apr 2020 10:04:33 +0000</pubDate>
      <link>https://forem.com/runtime-revolution/golang-dns-resolving-1ao3</link>
      <guid>https://forem.com/runtime-revolution/golang-dns-resolving-1ao3</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A9NA3QJOTTXQeCQRp1IAFqQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A9NA3QJOTTXQeCQRp1IAFqQ.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lately, I have been writing Golang (more commonly known as just Go, which is what I’ll be using from here on out) at work, and so far I have been enjoying it a lot! This is a small TIL (&lt;em&gt;Today I Learned&lt;/em&gt;) about making Go correctly communicate with services in the local machine like, for instance, Redis, Postgres, or Kafka.&lt;/p&gt;

&lt;p&gt;In my particular situation, the project I am working on is running on my machine, however, some of the needed services (Redis, Kafka) are running inside a Docker container. For instance, Redis is accessible via the hostname redis, and likewise for Kafka. Initially, I thought that simply adding the following entries to my etc/hosts files would be enough for the name resolution to work properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1 redis kafka
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, the code was still throwing errors because it could not connect to redis or Kafka. Turns out that Go has &lt;a href="https://golang.org/pkg/net/#hdr-Name_Resolution" rel="noopener noreferrer"&gt;two ways to perform address name resolution, depending on the operating system it is running on&lt;/a&gt;. On OS X (which is my case), Golang uses a different name resolution library called cgo, which is a bit different than the default name resolver. You can see which name resolver your Go program is using by setting the flag GODEBUG with the value netdns=2. Here’s a sample output for my program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GODEBUG=netdns=1 go run main.go
&amp;gt; go package net: hostLookupOrder(redis) = cgo
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On OSX, applications are not allowed to make direct DNS requests&lt;/strong&gt;. So, let’s try running the program with the pure Go name resolver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GODEBUG=netdns=go+2 go run main.go
&amp;gt; go package net: GODEBUG setting forcing use of Go’s resolver go 
&amp;gt; package net: hostLookupOrder(redis) = files,dns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The + sign is used to tell Go to force a specific name resolver and to show debugging information).&lt;/p&gt;

&lt;p&gt;And it works! Since the pure Go name resolver is used, Go accesses local files in the machine to perform name resolutions such as &lt;a href="https://github.com/golang/go/blob/d32ec38f1cfe69ed2a553272f6c8f425c59577f2/src/net/conf.go#L100" rel="noopener noreferrer"&gt;/etc/resolv.conf&lt;/a&gt; and &lt;a href="https://github.com/golang/go/blob/7bb33179cadf072403b2f1d8f8210c5ae414d135/src/net/dnsclient_unix.go#L526" rel="noopener noreferrer"&gt;/etc/hosts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Nowadays I work at&lt;/em&gt; &lt;a href="http://www.runtime-revolution.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Runtime Revolution&lt;/em&gt;&lt;/a&gt; &lt;em&gt;as a full-stack software developer. Working here has been, and continues to be, a great learning experience. I’ve matured professionally as a developer, as well as a human being.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>dns</category>
      <category>go</category>
      <category>tips</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>go-timetracker | Part I - Why and What</title>
      <dc:creator>Luís Costa</dc:creator>
      <pubDate>Sun, 19 Apr 2020 18:18:03 +0000</pubDate>
      <link>https://forem.com/luispcosta/go-timetracker-part-i-why-and-what-5g5h</link>
      <guid>https://forem.com/luispcosta/go-timetracker-part-i-why-and-what-5g5h</guid>
      <description>&lt;p&gt;Lately, I've been enjoying programming in &lt;code&gt;go&lt;/code&gt;. I don't know what it is, but I feel &lt;em&gt;good&lt;/em&gt; writing go code. Maybe it's because I have a background in dynamically typed languages (such as &lt;code&gt;ruby&lt;/code&gt; and &lt;code&gt;javascript&lt;/code&gt;) and so returning now to a statically typed and compiled language just seems like its a new, shiny thing and I like shiny new things. And what better way to learn a new programming language than to actually build something with it?&lt;/p&gt;

&lt;p&gt;This post will be part of a series of posts, where I will describe my process of building a command-line application in go. In this first post, I'll go over what the project is, why I'm building it, and how I intend to build it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;I've always heard that tracking your time is a good thing. It allows you to review &lt;em&gt;where&lt;/em&gt; you're spending your time and it gives you a better view on whether you're using your time effectively or not. It can also tell you how much time you spend working and procrastinating. There are tons of tools that can do this for you, but I wanted something that could be used via simple commands from my terminal, and which doesn't get in the way of doing what I set out to do (i.e, I don't want to spent too much time learning a productivity tool, because that would be... unproductive).&lt;/p&gt;

&lt;h2&gt;
  
  
  What
&lt;/h2&gt;

&lt;p&gt;The tool I will be writing (&lt;code&gt;go-tt&lt;/code&gt;) will allow me to say something like &lt;em&gt;I'm now working on unit tests&lt;/em&gt; and, a few minutes later, I can say &lt;em&gt;I'm now writing documentation for feature X&lt;/em&gt;. If I want to consciously take a break, like reading an article, I can quickly say &lt;em&gt;I'm now reading an article&lt;/em&gt;. I should then be able to see at a glance the tasks I've been working on in the last day, week, month, year or in any other random period in time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It should be accessible via the &lt;strong&gt;command line&lt;/strong&gt; and be easily &lt;strong&gt;installable via popular package management systems&lt;/strong&gt; (like &lt;code&gt;homebrew&lt;/code&gt;, &lt;code&gt;apt&lt;/code&gt;, etc). However, installing it from source should be easy too.&lt;/li&gt;
&lt;li&gt;It should work on all major operating systems.&lt;/li&gt;
&lt;li&gt;It should be &lt;strong&gt;&lt;em&gt;fast&lt;/em&gt;&lt;/strong&gt;, with a &lt;strong&gt;few set of commands&lt;/strong&gt;, which must be easy to use (which implies a low number of arguments for each command) and &lt;strong&gt;good documentation&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;It should be free for modification and distribution, i.e, &lt;strong&gt;open-sourced&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The code should be &lt;strong&gt;&lt;em&gt;extensible&lt;/em&gt;&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Even though it will be primarily a CLI application, adding a GUI in the future should not be much of a trouble.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;It should report the data in various formats: &lt;code&gt;CSV&lt;/code&gt;, &lt;code&gt;JSON&lt;/code&gt;, etc (more on this later).&lt;/li&gt;
&lt;li&gt;It should &lt;strong&gt;contain as few dependencies as possible&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;It should be well covered with &lt;strong&gt;tests&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Specification
&lt;/h2&gt;

&lt;p&gt;Let's get into the nitty-gritty parts. In this section I will outline the major commands in the program, what they should do and how they should work. Note that things can (and most probably will) change during the development of the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modules
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UI&lt;/strong&gt;: This module takes care of everything about displaying and receiving data from/to the user. Here we can have one submodule per UI layer: &lt;code&gt;CLI&lt;/code&gt;, &lt;code&gt;GUI&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence&lt;/strong&gt;: This module takes care of writing/reading hard data. I will talk a bit more about the data we're going to need later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core&lt;/strong&gt;: This module will contain most of the business logic. It will combine all the other modules to achieve the goals of the program.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reporter&lt;/strong&gt;: This module will handle the logic of taking the data and presenting it to the user (for instance, in CSV files). More on this later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Operations
&lt;/h3&gt;

&lt;p&gt;Do not confuse this with command-line commands, even though the syntax I will use may suggest that. This is an &lt;em&gt;high&lt;/em&gt; level overview of the operations the program should accept.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;add new activity&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This operation creates a new activity in the system. For instance, one could create an activity named &lt;code&gt;writing tests&lt;/code&gt; or &lt;code&gt;attending meeting&lt;/code&gt;. An activity is identified by a unique identifier and can additionally have a description and a list of aliases. If the activity already exists, a helpful error message should be returned to the user.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;start activity X | alias&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once this operation is executed, the program starts tracking the time that the user is taking to do the activity &lt;em&gt;X&lt;/em&gt;. If activity &lt;em&gt;X&lt;/em&gt; is not yet created, then it should automatically create it and start tracking it. &lt;em&gt;Tracking&lt;/em&gt;  here simply means recording a timestamp with the current system time. &lt;strong&gt;Only &lt;em&gt;one&lt;/em&gt; activity can be tracked at the same time&lt;/strong&gt;, i.e, the user cannot start two activities one after the other, unless the previous one is stopped (this is an explicit decision on my part: I don't want to encourage multi-tasking). An alias can be used instead to reference the activity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;stop activity X | alias&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once this operation is executed, the program records the current system time and calculates the total time spent on this activity by using the start time recorded early. If the activity &lt;em&gt;X&lt;/em&gt; does not exist, or if it does exist but its not the current one being tracked, or if none activity at all is being tracked, an error message should be returned. An alias can be used instead to reference the activity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;delete activity X | alias&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This operation deletes the activity &lt;code&gt;X&lt;/code&gt; from the system. If the activity does not exist, an error is returned. This action is not reversible: deleting an activity will also delete all data associated with it. An alias can be used instead to reference the activity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;get report&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This operation returns the tracked data of all activities or for a given specified list of activities in a given period. The user can decide which format he wishes the report to be in. More on this later.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;list activities&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This operation lists all activities currently registered in the system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;backup&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This operation creates a compressed file (&lt;code&gt;.zip&lt;/code&gt; or &lt;code&gt;.tar&lt;/code&gt;, for instance) with all the data recorded for your activities since the beginning of time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;purge&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This operation wipes out all the data in the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data
&lt;/h2&gt;

&lt;p&gt;As I've mentioned, in the first version, we're going to rely on the filesystem to persist data. The data we'll need is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A list of tracked activities. (&lt;em&gt;Activity metadata&lt;/em&gt; files)&lt;/li&gt;
&lt;li&gt;A daily log where each activity start and end time is recorded (&lt;em&gt;Activity Log&lt;/em&gt; files)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can easily implement this using the file system with JSON files. (the choice here is arbitrary, we could easily just use plain &lt;code&gt;.txt&lt;/code&gt; files, but I find working with JSON pleasant).&lt;/p&gt;

&lt;h3&gt;
  
  
  Activity Metadata
&lt;/h3&gt;

&lt;p&gt;Each activity will have its own meta data file. The name of the file will be the identifier of the activity. For instance, for the activity &lt;em&gt;activity-1&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"activity-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Some description..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"aliases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"alias1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alias2"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Activity Log
&lt;/h3&gt;

&lt;p&gt;Each individual activity log has the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"activity-1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1587231890&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1587231901&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1587231920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1587231931&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I have yet to decide the data format for the &lt;code&gt;duration&lt;/code&gt; key. The name of the activity log file will have the structure &lt;code&gt;YYYY/MM/DD.json&lt;/code&gt;, for instance: &lt;code&gt;2020/10/01.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Each entry in the array for &lt;code&gt;activity-1&lt;/code&gt; corresponds to a cycle of starting and stopping the tracking.&lt;/p&gt;

&lt;p&gt;For instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; go-tt start a1 &lt;span class="c"&gt;# t0&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; go-tt stop a1 &lt;span class="c"&gt;# t1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; go-tt start a2 &lt;span class="c"&gt;# t2&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; go-tt stop a2 &lt;span class="c"&gt;# t3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; go-tt start a1 &lt;span class="c"&gt;# t4&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; go-tt stop a1 &lt;span class="c"&gt;# t5&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; go-tt start a2 &lt;span class="c"&gt;# t6&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; go-tt stop a2 &lt;span class="c"&gt;# t7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This would produce the following log file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"a1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"a2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"end"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"t7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Combining every thing together, here's a possible folder hierarchy for the data describe above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;meta/
  activity-1.json
  activity-2.json
  ...
log/
  2020/
    01/
      1.json
      2.json
    02/
      10.json
  2021/
    03/
      1.json
  ... etc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This is madness!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, I know! This solution has SO many disadvantages, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What if the files get corrupted?&lt;/li&gt;
&lt;li&gt;What if the user manually changes the files?&lt;/li&gt;
&lt;li&gt;What if the files disappear?&lt;/li&gt;
&lt;li&gt;I have to load &lt;em&gt;metadata&lt;/em&gt; and log files in memory to read/update activities and to create the reports! This is going to cause memory problems soon enough!&lt;/li&gt;
&lt;li&gt;What about automatic/regular backups?&lt;/li&gt;
&lt;li&gt;You're trying to be smarter than the guys that spent many years writing and optimizing databases for this kind of thing! Who do you think you are?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;I know&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Since I'm new to the language, I think that learning how to do I\O is important, and this initial version will be the perfect playground to do it. Plus, if I write the code in a modular fashion, I can then plugin a DBMS like Postgres, SQLite or even Mongo easily. That will certainly come later on.&lt;/p&gt;

&lt;p&gt;This is the kind of solution I would be &lt;em&gt;ashamed&lt;/em&gt; to show publicly, and only work on it privately. But I want to share the process with you. I am aware this is not a good solution, but I must remind myself: this tool is built to be &lt;strong&gt;used by me&lt;/strong&gt; first and foremost, and to learn &lt;code&gt;go&lt;/code&gt;. On future iterations, I will certainly add a more robust layer of data persistence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reports
&lt;/h2&gt;

&lt;p&gt;In this initial version, the reporting system is straightforward. It should accept three arguments: the format of the report (in this first version, I will write three report systems: JSON, CSV and directly in the stdout), the initial date and end dates (like: &lt;code&gt;2020-01-02&lt;/code&gt; to &lt;code&gt;2020-01-04&lt;/code&gt;). The report (independently of the format) should present:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A list of activities tracked in that period.&lt;/li&gt;
&lt;li&gt;For each activity, the total time spent on it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For fun, we could show other aggregations like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The average time spent in each activity per day/week/month.&lt;/li&gt;
&lt;li&gt;The time of day where each activity is most performed.&lt;/li&gt;
&lt;li&gt;The median of each activity.&lt;/li&gt;
&lt;li&gt;The days of the week where each activity is most performed.&lt;/li&gt;
&lt;li&gt;A graph (only possible in UI solutions) showing the evolution of the time spent in each activity in the period.&lt;/li&gt;
&lt;li&gt;... any other ideas? Let me know!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of these can certainly come later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Aren't you worried that people might steal your idea? Absolutely not! Please, feel free to go ahead and implement it on your own as well! It would be fun to compare implementations later on!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Are you going to host the project on Github? &lt;a href="https://github.com/luispcosta/go-tt"&gt;Yes.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What will be the schedule of the posts? I will not set an explicit hard schedule to write posts for this series. After I have finished a major milestone in the project, I will then write a post about it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading! If you find any clear and alarming problem in this first naive implementation, or if you have any other ideas you'd like to see, &lt;strong&gt;please let me know!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>go</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>How do you find the time to participate on dev communities? (Such as dev.to)</title>
      <dc:creator>Luís Costa</dc:creator>
      <pubDate>Mon, 23 Jul 2018 15:28:34 +0000</pubDate>
      <link>https://forem.com/luispcosta/how-do-you-find-the-time-to-participate-on-dev-communities-such-as-devto-3lp5</link>
      <guid>https://forem.com/luispcosta/how-do-you-find-the-time-to-participate-on-dev-communities-such-as-devto-3lp5</guid>
      <description>&lt;p&gt;I'm just a bit curious to know how do you guys usually find time to participate in an online dev community such as dev.to (when I mean participate, I mean reading articles but also commenting and writing posts).&lt;/p&gt;

&lt;p&gt;Do you do it at home, after work, before work, or at work during dead hours? I know it's a bit too specific, but I just would like to know how my fellow developers do it!&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Redis - An Introduction</title>
      <dc:creator>Luís Costa</dc:creator>
      <pubDate>Fri, 01 Jun 2018 08:41:26 +0000</pubDate>
      <link>https://forem.com/luispcosta/redis---an-introduction-3lof</link>
      <guid>https://forem.com/luispcosta/redis---an-introduction-3lof</guid>
      <description>&lt;h2&gt;
  
  
  What is Redis?
&lt;/h2&gt;

&lt;p&gt;Redis is an in-memory key-value data store that you can integrate with your application to store volatile data. It’s volatile because once you quit Redis, all the data you’ve stored is lost.&lt;/p&gt;

&lt;p&gt;It works by storing some value in a key. You can later on request “Give me the value for key ‘some_key’”.&lt;/p&gt;

&lt;p&gt;It supports a wide range of data types such as strings, hashes, lists, sets, bitmaps, etc…&lt;/p&gt;

&lt;h2&gt;
  
  
  Do I need it?
&lt;/h2&gt;

&lt;p&gt;Redis is typically used as a caching mechanism. For instance, let’s say your application has an endpoint that performs a computational-heavy task and returns a large json to the user. If you repeat this work over and over for every request, you are most likely introducing a bottleneck in your client-server communication. A popular solution for this is to perform the work once and then store the result in Redis. The next time the user makes the same request, your application simply checks if the result is cached and returns it.&lt;/p&gt;

&lt;p&gt;Of course this pattern requires that you follow a set of rules:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each result stored should be uniquely namespaced&lt;/strong&gt;. In the example below, the result should cached per user id (for example) and any other additional parameters that can identify two requests as being the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You should enforce a TTL (time to live) in each result that you store in Redis&lt;/strong&gt;. &lt;br&gt;
A TTL of 15 minutes means that after 15 minutes, the result is wiped from Redis. In the example below, this means that 15 minutes after the initial request, a new user request would trigger the heavy computation on the server again. This depends on your application and user needs, so take some time to think about this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t overuse Redis&lt;/strong&gt;. This is important. Redis is useful for some tasks, not all tasks. Use a proper database when appropriate. For instance, you should not store user credentials and preferences in Redis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you’re working with huge datasets, be very careful when doing queries on Redis&lt;/strong&gt;. If your application performs several reads and/or writes to Redis per minute, one wrong query to Redis (for instance, listing all the keys currently existent) may introduce downtime, possibly breaking your entire application. This, of course, depends on the overall infrastructure of your application.&lt;/p&gt;

&lt;p&gt;For the remainder of this blog post, I’m going to talk about the major data types that Redis supports and the basic commands for each. I will be using the Ruby implementation of Redis (which you can &lt;a href="https://github.com/redis/redis-rb"&gt;find here&lt;/a&gt;) and show you, as an example, how to set up Redis in your Rails application. There are ports of Redis for almost any programming language, and the public API should be pretty similar among them, so setting it up for your preferred development environment should be a simple matter of reading the README file in the official repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Redis in your Rails app
&lt;/h2&gt;

&lt;p&gt;Simply append the redis to your local Gemfile&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gem 'redis'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And then create a new initialiser class in your &lt;code&gt;config/initializer&lt;/code&gt; folder called &lt;code&gt;redis.rb&lt;/code&gt; with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Redis.current = Redis.new(host: 'localhost', port: 6379)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can also pass additional options depending on how you have configured redis such as a password, a path to a specific unix socket, a driver and a specific database name. Check the README file over at the official ruby gem for more information on how to configure redis.&lt;/p&gt;

&lt;h2&gt;
  
  
  General commands
&lt;/h2&gt;

&lt;p&gt;These are commands that are not specific to a data type, rather to any redis key itself&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Deleting a key&lt;br&gt;
&lt;code&gt;Redis.current.del('some key')&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checking if a key exists&lt;br&gt;
&lt;code&gt;Redis.current.exists('some key')&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get all keys in the database&lt;br&gt;
&lt;code&gt;Redis.current.keys&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;!! WARNING !! :&lt;/strong&gt; Please avoid at all costs running the command above in your production redis server, because this operation has a O(N) complexity, where N is number of keys you currently have in Redis.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting a TTL in a key
&lt;code&gt;Redis.current.expire('some key', 5000)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second argument is the expire time, in milliseconds. This means that the key &lt;code&gt;'some key+&lt;/code&gt; will expire in 5 seconds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Renaming a key
&lt;code&gt;Redis.current.rename('some key', 'new key')&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don’t know or don’t remember which data structure is currently held by a key, you can ask redis to inform you:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Redis.current.type('key') =&amp;gt; 'String', 'Set', 'Hash', ...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you really want to perform a scan over all your keys in Redis (for instance, to check if a key with a particular pattern in its name exist), the correct way to do it is with the scan command.&lt;/p&gt;

&lt;p&gt;For instance, let’s say our current Redis database holds 1 billion keys and you want to know if there’s any key with name pattern &lt;code&gt;some:cache:key&lt;/code&gt;. You do not, I repeat, you do NOT execute the command &lt;code&gt;Redis.current.keys&lt;/code&gt; and iterate over the returned collection to find it. Not only will you probably kill your application memory, but you will also probably kill redis.&lt;/p&gt;

&lt;p&gt;You’ll use scan for this. This command allows you to iteratively look for keys in the database using a cursor. You provide it with an initial cursor, redis will scan the next N keys (configurable), and then return you the next cursor (and the keys scanned) that you should use in the next call.&lt;/p&gt;

&lt;p&gt;For a correct use of this command, you should stop scanning whenever redis returns 0 as the next cursor. This means that there’s no more keys to scan. In our example above, to find a key that contains the pattern some:cache:key we would use scan in the following way:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Redis.current.scan(0, match: '*some:cache:key*')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The asterics before and after the pattern means ‘I don’t care whatever is before or after this particular pattern, so the key &lt;code&gt;random:some:cache:key:random&lt;/code&gt; would match.&lt;/p&gt;

&lt;p&gt;The first call would return, for example, the iterator 1000 and whataver keys match the pattern. Given that the iterator is not 0, we can continue:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Redis.current.scan(1000, match: '*some:cache:key*')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This call would return the iterator 10, so we continue:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Redis.current.scan(10, match: '*some:cache:key*')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This call would return the iterator 0, so we know that there are no more keys that match this pattern. It goes without saying that you should keep a list of the keys that have matched, from the result of each scan command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strings / Integers
&lt;/h2&gt;

&lt;p&gt;In this section we’l learn the basic operations around storing and retrieving string and integer values from redis.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Storing a new string value in a key&lt;br&gt;
&lt;code&gt;Redis.current.set('mykey', 'myvalue')&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Retrieving the value of the key mykey&lt;br&gt;
&lt;code&gt;Redis.current.get('mykey') =&amp;gt; 'myvalue'&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setting a new string value in a key, unless it already exists&lt;br&gt;
&lt;code&gt;Redis.current.setnx('mykey', 10) =&amp;gt; false (because the key 'mykey' already exists)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Redis.current.setnx('another key', 10) =&amp;gt; 'OK'&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hashes
&lt;/h2&gt;

&lt;p&gt;A hash in Redis is a data structure that can hold several key — value pairs (just like a hash in Ruby)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Creating a new key/value pair in a hash:&lt;br&gt;
&lt;code&gt;Redis.current.hset('myhash', 'my hash field', 10)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Retrieving the value of the field my hash field in the hash myhash :&lt;br&gt;
&lt;code&gt;Redis.current.hget('myhash', 'my hash field')&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s also a way to create a hash with multiple fields in a single command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Redis.current.hmset('myhash', 'field 1', 'value field 1', 'field 2', 'value field 2', 'field 3', 'value field 3')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here I’m showing examples with strings, but of course you can also store integers, and even json dumps, for instance:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Redis.current.hmset('myhash', 'field 3', '{\" key \": \" value \"}')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Likewise, you can also retrieve the values from multiple fields in a single command&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Redis.current.hmget('myhash', 'field 2', 'field 3') =&amp;gt; {'field 2' =&amp;gt; 'value field 2', 'field 3' =&amp;gt; 'value field 3'}&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deleting one or more fields from a hash
&lt;code&gt;Redis.current.hdel('myhash', 'field 2', 'field 3')&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Retrieving the full hash stored in a key&lt;br&gt;
&lt;code&gt;Redis.current.hgetall('myhash') =&amp;gt; {'field 1' =&amp;gt; 'value field 1', 'field 2' =&amp;gt; 'value field 2', 'field 3' =&amp;gt; 'value field 3'}&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lists
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Prepend a new element to a list (add to the beginning of the list):&lt;br&gt;
&lt;code&gt;Redis.current.lpush("mylist", 10)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Append a new element to a list (add to the end of the list):&lt;br&gt;
&lt;code&gt;Redis.current.rpush("mylist", JSON.dump({ some_hash_key: 2}))&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Removing elements from a list&lt;br&gt;
&lt;code&gt;Redis.current.lrem("mylist", 1, 10)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The method &lt;code&gt;lrem&lt;/code&gt; accepts three arguments. The first argument is the name of the key holding the list, the third argument is an element that we want to delete from the list, and the second argument is an integer that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When 0, it will remove all elements in the list equal to the element we want to remove. For example, if we had several elements in the list with the value 10 calling Redis.current.lrem("mylist", 0, 10) would remove all elements in the list with the value 10&lt;/li&gt;
&lt;li&gt;When positive, will remove the first element matching the element that we want to remove, starting from the head of the list to the tail&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When negative, will remove the first element matching the element that we want to remove, starting from the tail of the list to the head&lt;br&gt;
These last two options are useful if, for example, you’d like to remove the first or the last occurrences of a given element in a list&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Getting the elements of a list&lt;br&gt;
&lt;code&gt;Redis.current.lrange("mylist", 0, -1)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second and third argument of this method specifies the range of elements that we want. When using 0 and -1, it means that we want all elements starting from the beginning until the end. If we’d like only the first two elements we’d have to write &lt;code&gt;Redis.current.lrange("mylist", 0, 1)&lt;/code&gt; , and so on.&lt;/p&gt;

&lt;p&gt;If you don’t know the current size of your list:&lt;br&gt;
&lt;code&gt;Redis.current.llen("mylist")&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sets
&lt;/h2&gt;

&lt;p&gt;Sets are useful if want to keep a data structure with unique elements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding new elements to a set
&lt;code&gt;Redis.current.sadd("myset", "myelement")&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that if the element “myelement” already exists in the set “myset”, it is ignored and thus not added&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Removing elements from a set&lt;br&gt;
&lt;code&gt;Redis.current.srem("myset", "myelement")&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Getting all current members of a set&lt;br&gt;
&lt;code&gt;Redis.current.smembers("myset")&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checking if an element is already in a set&lt;br&gt;
&lt;code&gt;Redis.current.sismember("myset", "myelement")&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;The list of commands presented here is just a quick “cheasheet” that you can use to quickly look up the most common use cases of Redis. For a list of complete commands, check the official redis documentation.&lt;/p&gt;

&lt;p&gt;Finally, please be aware of how you’re using redis. If redis is on a different machine than the one running your application, consider that each redis call is a network request. Also, be very careful with commands that return everything from a list, like smembers , lrange , hgetall , etc. If your application is very big, you may not only flood your application memory, but also possibly block redis for a while. Always, always, use iteration based search, such as scan .&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>redis</category>
      <category>caching</category>
      <category>database</category>
      <category>ruby</category>
    </item>
    <item>
      <title>How/When do you make time for side projects?</title>
      <dc:creator>Luís Costa</dc:creator>
      <pubDate>Mon, 09 Apr 2018 12:44:43 +0000</pubDate>
      <link>https://forem.com/luispcosta/howwhen-do-you-make-time-for-side-projects-39e8</link>
      <guid>https://forem.com/luispcosta/howwhen-do-you-make-time-for-side-projects-39e8</guid>
      <description>&lt;p&gt;Before work? After work? During the day? At your job? How do set up your routines and days to allow time for side projects?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>productivity</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
