<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Ray]]></title><description><![CDATA[🙌 Loves tackling challenging problems and constantly striving to become an even better hacker 💪]]></description><link>https://blog.rayspock.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 13 Apr 2026 11:51:09 GMT</lastBuildDate><atom:link href="https://blog.rayspock.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Go Slice Copying: What Really Happens Under the Hood?]]></title><description><![CDATA[In Go, variables are passed by value, which means that when we pass variables as arguments to functions, Go makes a copy of those values for the function to use. However, the behaviour of Slice can be a bit confusing at first glance, especially if we...]]></description><link>https://blog.rayspock.com/go-slice-copying-what-really-happens-under-the-hood</link><guid isPermaLink="true">https://blog.rayspock.com/go-slice-copying-what-really-happens-under-the-hood</guid><category><![CDATA[Go Language]]></category><category><![CDATA[slice]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[data structures]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Wed, 06 Sep 2023 18:53:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/9Ri7neXwe3A/upload/420558052eb76b8c1600f9d6341288e3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Go, variables are passed by value, which means that when we pass variables as arguments to functions, Go makes a copy of those values for the function to use. However, the behaviour of Slice can be a bit confusing at first glance, especially if we don't know how it works under the hood.</p>
<h2 id="heading-example-1-modifying-a-slice-without-affecting-the-original">Example 1: Modifying a Slice Without Affecting the Original</h2>
<p>To understand this better, let's look at the first example. In this example, we have a function called <code>AppendNumber</code> which appends the number 7 to a given slice. However, even though we modify the slice within the function call, we do not affect the original slice because Go creates a copy of the arguments for the function to work with.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">AppendNumber</span><span class="hljs-params">(slice []<span class="hljs-keyword">int</span>)</span></span> {  
    slice = <span class="hljs-built_in">append</span>(slice, <span class="hljs-number">7</span>)  
}  

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
    slice := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">int</span>, <span class="hljs-number">3</span>, <span class="hljs-number">6</span>)  
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">len</span>(slice); i++ {  
       slice[i] = i  
    }  
    fmt.Println(<span class="hljs-string">"before"</span>, slice) <span class="hljs-comment">// Print 0 1 2  </span>
    AppendNumber(slice)  
    fmt.Println(<span class="hljs-string">"after"</span>, slice)  <span class="hljs-comment">// Print 0 1 2  </span>
}
</code></pre>
<p>The <code>AppendNumber</code> function only modifies the copy of the slice within its scope, not the original slice, which is very straightforward.</p>
<h2 id="heading-example-2-modifying-an-element-in-slice">Example 2: Modifying an Element in Slice</h2>
<p>Now let's look at the second example. In this example, we have a function called <code>UpdateNumber</code> which updates an element in the slice at index 1 to the value 7. Unlike the previous example, this time we notice that the original slice is modified.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">UpdateNumber</span><span class="hljs-params">(slice []<span class="hljs-keyword">int</span>)</span></span> {  
    slice[<span class="hljs-number">1</span>] = <span class="hljs-number">7</span>  
}  

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {  
    slice := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">int</span>, <span class="hljs-number">3</span>, <span class="hljs-number">6</span>)  
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">len</span>(slice); i++ {  
       slice[i] = i  
    }  
    fmt.Println(<span class="hljs-string">"before"</span>, slice) <span class="hljs-comment">// Print 0 1 2  </span>
    UpdateNumber(slice)  
    fmt.Println(<span class="hljs-string">"after"</span>, slice)  <span class="hljs-comment">// Print 0 7 2  </span>
}
</code></pre>
<p>In the main function, we create a slice, assign values to it, and then print the slice before calling the <code>UpdateNumber</code> function. The output shows the original values of the slice, which are 0, 1, and 2. After calling the <code>UpdateNumber</code> function, we print the slice again, and this time the value at index 1 has changed to 7.</p>
<h2 id="heading-knowing-what-values-get-copied-in-go">Knowing What Values Get Copied in Go</h2>
<p>It seems that we still can influence the original Slice given the result of the second example. To think this through, we would need to know exactly what values are copied to the function arguments in Go. When a slice is passed to a function, the slice header (which contains a pointer to the underlying array, its length and capacity) is copied to the function arguments. The underlying array is not copied.</p>
<p>The slice header that is copied to the argument in the examples will look like this:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> sliceHeader <span class="hljs-keyword">struct</span> {
    Data     <span class="hljs-keyword">uintptr</span> <span class="hljs-comment">// A pointer to the underlying array</span>
    Length   <span class="hljs-keyword">int</span>     <span class="hljs-comment">// The number of elements it contains</span>
    Capacity <span class="hljs-keyword">int</span>     <span class="hljs-comment">// The maximum number of elements it can hold</span>
}
</code></pre>
<p>In the first example, when we call the <code>AppendNumber</code> function, a copy of the slice header is created so that any changes made to the slice within the function do not affect the original slice.</p>
<p>In the second example, when we call the <code>UpdateNumber</code> function, a copy of the slice header is created. However, since the underlying array is not copied, both the original slice and the copy point to the same array in memory. Therefore any changes made to the slice (changing the value at index 1) will be reflected in both the original slice and the copy.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>To conclude, modifying elements of a slice within a function will affect the original slice if the underlying array is shared between the original slice and the function's copy. If a new slice is created within the function (e.g. using <code>append</code>), the original slice is not affected as we have updated the copy of the slice header to the newly created slice.</p>
<p>By understanding what values get copied to arguments in Go, and how changes to the slice header and array elements can affect the original slice, we can write more reliable and maintainable code. If you are interested in more information about Slice in Go, this article is worth a read: <a target="_blank" href="https://go.dev/blog/slices">https://go.dev/blog/slices</a></p>
<p>I hope you have found this explanation of slices in Go useful and informative. If you have any further questions or need additional clarification, please feel free to ask.</p>
<p>Thanks for reading!</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/rayspock/mastering-go-examples/tree/main/slice">https://github.com/rayspock/mastering-go-examples/tree/main/slice</a></div>
]]></content:encoded></item><item><title><![CDATA[AI Technology Meets Social Media: The Discord Bot Project]]></title><description><![CDATA[As someone who's been using ChatGPT for a while now, I've been exploring how to maximise its benefits and integrate it into my daily life. I've been working on a side project to develop a Discord bot that interacts with ChatGPT, similar to the one pr...]]></description><link>https://blog.rayspock.com/ai-technology-meets-social-media-the-discord-bot-project</link><guid isPermaLink="true">https://blog.rayspock.com/ai-technology-meets-social-media-the-discord-bot-project</guid><category><![CDATA[chatgpt]]></category><category><![CDATA[AI]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[discord]]></category><category><![CDATA[bot]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Thu, 18 May 2023 19:03:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1684321837069/c04ef136-29c0-4ce4-8148-e66ed9bffa97.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As someone who's been using ChatGPT for a while now, I've been exploring how to maximise its benefits and integrate it into my daily life. I've been working on a side project to develop a Discord bot that interacts with ChatGPT, similar to the one provided by OpenAI. At first, it was just an attempt to learn more about ChatGPT and bot development, but as I progressed I realised that other people might find it useful as well. It's fascinating to see how intelligent AI technology is finding its way into everyday platforms like Discord.</p>
<h2 id="heading-discord-bot-powered-by-chatgpt-api">Discord Bot powered by ChatGPT API</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1684669077909/3122ceee-1707-41b0-af81-bb1122e95c33.gif" alt class="image--center mx-auto" /></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/rayspock/go-chatgpt-discord">https://github.com/rayspock/go-chatgpt-discord</a></div>
<p> </p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<blockquote>
<p><a target="_blank" href="https://github.com/bwmarrin/discordgo">DiscordGo</a> - provides low-level bindings to the Discord chat client API.</p>
<p><a target="_blank" href="https://github.com/sashabaranov/go-openai">Go OpenAI</a> - provides Go clients for the OpenAI API.</p>
</blockquote>
<p>Above are two main packages (<code>dicordgo</code> and <code>go-openai</code>) use to build this ChatGPT Discord Bot. They have great examples of different kinds of use cases that we can build our own logic around.</p>
<h3 id="heading-things-that-need-to-be-handled">Things that need to be Handled</h3>
<h4 id="heading-handle-longer-responses-from-chatgpt">Handle Longer Responses from ChatGPT</h4>
<p>Due to the character limit on Discord (2000 characters per message at the time of writing), it may be necessary for longer replies from ChatGPT to be split up and sent as multiple messages. It's important to note that when messages are split into multiple parts, the character may be truncated if we don't count the number of characters correctly, especially if we're dealing with languages like Mandarin or Japanese where one character can be multiple bytes. Therefore it's recommended to use the Unicode character count instead of simply counting the number of bytes like <code>len()</code>. This can be done using the <code>utf8.RuneCountInString()</code> function instead. By using this method we can ensure that each part of the message contains complete characters and avoid confusing or misleading messages. It's important to be aware of these language-specific nuances when working with text messages in Golang or any other programming language.</p>
<p>To find out more about managing Strings, bytes, runes, and characters in Go, please visit the <a target="_blank" href="https://go.dev/blog/strings">Go Blog</a>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://go.dev/blog/strings">https://go.dev/blog/strings</a></div>
<p> </p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">SendMessageByChunk</span><span class="hljs-params">(message <span class="hljs-keyword">string</span>, chunkLength <span class="hljs-keyword">int</span>, send <span class="hljs-keyword">chan</span>&lt;- <span class="hljs-keyword">string</span>)</span></span> {
    counter := <span class="hljs-number">0</span>
    lastIndex := <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> i, w := <span class="hljs-number">0</span>, <span class="hljs-number">0</span>; i &lt; <span class="hljs-built_in">len</span>(message); i += w {
        counter++
        _, width := utf8.DecodeRuneInString(message[i:])
        w = width

        <span class="hljs-comment">// We reach the maximum chunk length</span>
        <span class="hljs-keyword">if</span> counter == chunkLength {
            chunk := message[lastIndex : i+w]
            send &lt;- chunk
            lastIndex = i + w
            counter = <span class="hljs-number">0</span>
        }
    }
    <span class="hljs-comment">// Make sure the last bit of the message is sent</span>
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(message) &gt; <span class="hljs-number">0</span> &amp;&amp; lastIndex &lt; <span class="hljs-built_in">len</span>(message) {
        send &lt;- message[lastIndex:]
    }
    <span class="hljs-built_in">close</span>(send)
}
</code></pre>
<p>In the above example, we create a function to send messages of a certain length (chunkLength). To handle Unicode characters correctly, we can use <code>utf8.DecodeRuneInString()</code> to decode the first UTF-8 encoded Unicode code point in the given string and get the number of bytes it occupies. We iterate through the string until all the characters (runes) are decoded.</p>
<h4 id="heading-application-command-timeout-on-discord">Application Command Timeout on Discord</h4>
<p>Another limitation that may arise is Discord's application command timeout, which at the time of writing is set at 3 seconds. This means that if ChatGPT takes longer than 3 seconds to respond, users will receive an error message indicating that the application has not responded. As ChatGPT can take longer than that to respond, the bot needs to delay the response by setting the interaction callback type <code>DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE</code> to acknowledge that the event has been received and that a follow-up will come later. I also think it's good practice for the bot to let users know that their message has been received and is being processed. By implementing these strategies, ChatGPT can provide a better user experience on Discord.</p>
<p>The following code snippet from this project shows how we can defer or delay replies via <code>DiscordGo</code>.</p>
<pre><code class="lang-go">    <span class="hljs-comment">// If we don't send a response in 3 seconds, the error 'The application did not respond' will appear.</span>
    <span class="hljs-comment">// To avoid this, we send the following type of response</span>
    err := s.InteractionRespond(i.Interaction, &amp;discordgo.InteractionResponse{
        Type: discordgo.InteractionResponseDeferredChannelMessageWithSource,
    })
</code></pre>
<h2 id="heading-motivation-behind-the-project">Motivation behind the Project</h2>
<p>I've been a frequent user of ChatGPT ever since it was launched by <a target="_blank" href="https://openai.com/">OpenAI</a>. Their <a target="_blank" href="https://chat.openai.com/">Chat App</a> has proven to be very useful in speeding up my daily tasks and even occasionally planning my travel itinerary. It has been really powerful and I no longer have to go through the hassle of searching endless websites and connecting the dots myself. Although the app's interface looks nice, with real-time updates, the connection can be unreliable at times. Another drawback I noticed is that users have to log in before they can use the app's features, which may seem like a minor inconvenience but could be a deal-breaker for those who value ease of use.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>We did not cover all the details of building this project in this article but feel free to visit the GitHub page to play around with it 😊</p>
<blockquote>
<p>The best way of learning about anything is by doing. - Richard Branson</p>
</blockquote>
<p>If you're interested in running the bot for your Discord server or contributing to the development of the project, please visit the GitHub page. Welcome any suggestions or ideas for improving the code 💪🏽</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/rayspock/go-chatgpt-discord">https://github.com/rayspock/go-chatgpt-discord</a></div>
<p> </p>
<p>Hopefully, you found something useful.</p>
<p>Thank you for reading.</p>
]]></content:encoded></item><item><title><![CDATA[One Thing You Might Overlook When Reading Response Body in Go]]></title><description><![CDATA[The Go net/http package provides a robust set of functionality to allow us to deal with HTTP requests and responses. However, reading the response body is a common task that requires a bit of caution. In this blog, we will explore the pitfall I exper...]]></description><link>https://blog.rayspock.com/one-thing-you-might-overlook-when-reading-response-body-in-golang</link><guid isPermaLink="true">https://blog.rayspock.com/one-thing-you-might-overlook-when-reading-response-body-in-golang</guid><category><![CDATA[golang]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[http]]></category><category><![CDATA[learning]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Go]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Mon, 10 Apr 2023 17:48:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1680983781595/06d85d35-d6c2-43da-b8ae-3bc9649aae7a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The Go <code>net/http</code> package provides a robust set of functionality to allow us to deal with HTTP requests and responses. However, reading the response body is a common task that requires a bit of caution. In this blog, we will explore the pitfall I experience when using streaming techniques to process the response body in chunks and how to avoid them.</p>
<h2 id="heading-how-to-read-the-response-body">How to Read the Response Body</h2>
<p>There are different ways of reading the response body like the following:</p>
<pre><code class="lang-go">resp, err := http.Get(<span class="hljs-string">"http://example.com/"</span>)
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    <span class="hljs-comment">// handle error</span>
}
<span class="hljs-keyword">defer</span> resp.Body.Close()
body, err := io.ReadAll(resp.Body)
<span class="hljs-comment">// ...</span>
</code></pre>
<p>or</p>
<pre><code class="lang-go">resp, err := http.Get(<span class="hljs-string">"https://example.com"</span>)
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    <span class="hljs-comment">// handle error</span>
}
<span class="hljs-keyword">defer</span> resp.Body.Close()
<span class="hljs-comment">// get a copy of the response body as a byte slice</span>
body, err := httputil.DumpResponse(resp, <span class="hljs-literal">true</span>)
<span class="hljs-comment">// ...</span>
</code></pre>
<p>The examples above basically read the entire contents of an HTTP response body as a byte slice, but take different arguments. We will not go into detail about these measures. There are plenty of resources available to help you.</p>
<p>What if the response bodies are huge and we want to avoid memory and performance problems because the whole response has to be loaded into memory before it can be processed? To avoid this, we could use streaming techniques to process the response body in chunks.</p>
<h2 id="heading-reading-the-response-body-in-chunks">Reading the Response Body in Chunks</h2>
<pre><code class="lang-go">resp, err := http.Get(<span class="hljs-string">"https://example.com"</span>)
<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
    <span class="hljs-comment">// handle error</span>
}
<span class="hljs-keyword">defer</span> resp.Body.Close()

buff := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">byte</span>, <span class="hljs-number">10</span>)  
<span class="hljs-keyword">for</span> {  
    <span class="hljs-keyword">var</span> bytesRead <span class="hljs-keyword">int</span>  
    bytesRead, err = resp.Body.Read(buff)
    <span class="hljs-keyword">if</span> err == io.EOF {
        <span class="hljs-keyword">break</span>
    }
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {  
        <span class="hljs-comment">// handle error</span>
    }
    <span class="hljs-comment">// send the data to the Send function for further processing.</span>
    send(buff[:bytesRead])  
}
</code></pre>
<p>As you can see from the example above, instead of reading the response body all at once, we create a byte slice called buff to store our chunks of the response body. Due to the power of the <code>http</code> package, the response body implements the Reader interface, which allows us to stream on demand by calling the <code>Read(b []byte)</code> method. Finally, the last line of code sends the data to read in the previous step to a hypothetical <code>Send</code> function. The <code>[:bytesRead]</code> notation is used to slice the buff slice to contain only the bytes actually read in the previous step.</p>
<p>To read parts of the response body until the whole body has been read, we put it in an infinite loop. The loop will break when the <code>io.EOF</code> error is returned, indicating that the end of the response body has been reached. This may sound familiar, and is pretty much the same as trying to read a file as a chunk using the <code>os</code> package (to read more detail <a target="_blank" href="https://pkg.go.dev/os#File.Read">here</a>).</p>
<p>If you're like me, let's look at what could go wrong in the previous example, where we read the response body in chunks, in the next section.</p>
<h2 id="heading-ioeof-might-not-be-what-you-think"><code>io.EOF</code> Might Not Be What You Think</h2>
<p>The code snippet appears to be correct at first glance, but there is a risk of obtaining arbitrary results when determining the number of chunks received from the remote server if we do not perform a follow-up check after encountering the <code>io.EOF</code> error.</p>
<p>Let's have a look at how the <code>Read</code> method is described in the official documentation of the <a target="_blank" href="https://pkg.go.dev/io#Reader">Reader Interface</a>:</p>
<blockquote>
<p>When Read encounters an error or end-of-file condition after successfully reading n &gt; 0 bytes, it returns the number of bytes read. It may return the (non-nil) error from the same call or return the error (and n == 0) from a subsequent call. An instance of this general case is that a Reader returning a non-zero number of bytes at the end of the input stream may return either err == EOF or err == nil. The next Read should return 0, EOF.</p>
</blockquote>
<p>Although it may seem feasible to break the loop immediately when the <code>io.EOF</code> error is returned, we may still receive the last chunk of data with a return value such as <code>5, EOF</code>, potentially losing this chunk of data in the process. To ensure greater robustness, we must modify our code to incorporate a more comprehensive check, as illustrated in the following example:</p>
<pre><code class="lang-go"><span class="hljs-comment">//...</span>

buff := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">byte</span>, <span class="hljs-number">10</span>)  
<span class="hljs-keyword">for</span> {  
    <span class="hljs-keyword">var</span> bytesRead <span class="hljs-keyword">int</span>  
    bytesRead, err = resp.Body.Read(buff)
    <span class="hljs-keyword">if</span> err == io.EOF {
        err = <span class="hljs-literal">nil</span>  
        <span class="hljs-comment">// There may be one last chunk to receive before breaking the loop.</span>
        <span class="hljs-keyword">if</span> bytesRead &lt;= <span class="hljs-number">0</span> {  
            <span class="hljs-keyword">break</span>  
        }
    }
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {  
        <span class="hljs-comment">// handle error.</span>
    }
    <span class="hljs-comment">// send the data to the Send function for further processing.</span>
    send(buff[:bytesRead])  
}
</code></pre>
<p>The behaviour we see above is to optimise the HTTP transport code by recycling its connection earlier. We can see this from the code snippet in the <code>http</code> package itself:</p>
<pre><code class="lang-go">...
<span class="hljs-comment">// If we can return an EOF here along with the read data, do</span>
<span class="hljs-comment">// so. This is optional per the io.Reader contract, but doing</span>
<span class="hljs-comment">// so helps the HTTP transport code recycle its connection</span>
<span class="hljs-comment">// earlier (since it will see this EOF itself), even if the</span>
<span class="hljs-comment">// client doesn't do future reads or Close.</span>
<span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> &amp;&amp; n &gt; <span class="hljs-number">0</span> {
    <span class="hljs-keyword">if</span> lr, ok := b.src.(*io.LimitedReader); ok &amp;&amp; lr.N == <span class="hljs-number">0</span> {
        err = io.EOF
        b.sawEOF = <span class="hljs-literal">true</span>
    }
}
...
</code></pre>
<p>For those of you who want to delve deeper, you can find the sourcecode <a target="_blank" href="https://cs.opensource.google/go/go/+/master:src/net/http/transfer.go;l=866;drc=12051f7d95ef6e97d1be0cab8a3583ad38ec1dcd">here</a>.</p>
<h2 id="heading-tests-exercise">Tests Exercise</h2>
<p>Testing is essential to ensure that our code works as intended, and the best way to gain proficiency is through practical application.</p>
<p>Exercise:</p>
<blockquote>
<p>Write a test file to test the following function using both a mock HTTP client and a real HTTP server. Share your findings in the comments section below :)</p>
</blockquote>
<pre><code class="lang-go"><span class="hljs-comment">// HTTPClient is an interface for http client.</span>
<span class="hljs-keyword">type</span> HTTPClient <span class="hljs-keyword">interface</span> {
    Do(req *http.Request) (*http.Response, error)
}
<span class="hljs-comment">// Download downloads a file from a given url and send the data to the send function.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Download</span><span class="hljs-params">(client HTTPClient, url <span class="hljs-keyword">string</span>, bufferSize <span class="hljs-keyword">int64</span>, send <span class="hljs-keyword">func</span>([]<span class="hljs-keyword">byte</span>)</span>) <span class="hljs-title">error</span></span> {
    <span class="hljs-comment">// TODO</span>
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Things are not always as obvious as we think they are, especially what we think they should be based on our experience. There's always a blind side if we can't see it from another angle, just like what we learn about <code>io.EOF</code>, which doesn't mean we've reached the end of the file.</p>
<p>Reading the response body in Go requires a bit of care and attention. You should be aware of pitfalls such as reading the body twice, not closing it, not handling errors properly, and using the wrong decoder. By following best practices and using the right tools, you can avoid these pitfalls and make your HTTP requests more efficient and reliable.</p>
<p>The sample solution to the exercise can be found <a target="_blank" href="https://github.com/rayspock/mastering-go-examples/blob/0b0b553c53a5aaf6f3f5e70548912f5d3668f3df/stream/client.go">here</a>. Feel free to clone it, try it out and share your thoughts in the comments below.</p>
<p>I hope you found something useful and that it will save you a lot of valuable debugging time :)</p>
<p>Thank you for reading!</p>
]]></content:encoded></item><item><title><![CDATA[Unleashing the power of Go: How to Unmarshal Dynamic JSON]]></title><description><![CDATA[Today we are going to write our own custom json.Unmarshal() method. I find it very useful and can make our code more maintainable and readable if we can make good use of type alias and the json.Unmarshal method. Let's say we want to decode a JSON-enc...]]></description><link>https://blog.rayspock.com/unleashing-the-power-of-go-how-to-unmarshal-dynamic-json</link><guid isPermaLink="true">https://blog.rayspock.com/unleashing-the-power-of-go-how-to-unmarshal-dynamic-json</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[golang]]></category><category><![CDATA[json]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Sun, 05 Mar 2023 13:39:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1678023521016/0e3a1962-65cb-49da-a527-a567af97c884.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today we are going to write our own custom <code>json.Unmarshal()</code> method. I find it very useful and can make our code more maintainable and readable if we can make good use of type alias and the <code>json.Unmarshal</code> method. Let's say we want to decode a JSON-encoded data take from a notification service where it sends us different events with different underlying data structure. Here is an example json of events:</p>
<pre><code class="lang-json">{<span class="hljs-attr">"resource_type"</span>:<span class="hljs-string">"payment"</span>,<span class="hljs-attr">"action"</span>:<span class="hljs-string">"confirmed"</span>,<span class="hljs-attr">"data"</span>:{<span class="hljs-attr">"amount"</span>:<span class="hljs-number">100</span>}}
{<span class="hljs-attr">"resource_type"</span>:<span class="hljs-string">"customer"</span>,<span class="hljs-attr">"action"</span>:<span class="hljs-string">"created"</span>,<span class="hljs-attr">"data"</span>:{<span class="hljs-attr">"name"</span>:<span class="hljs-string">"john"</span>}}
</code></pre>
<p>We have some basic fields like <code>resource_type</code> and <code>action</code> that are the same for all events, but the type of data is different depending on the <code>resource_type</code>. For example, the <code>payment</code> resource has an <code>amount</code> field of type number, and this is obviously different from the other resource type, <code>customer</code>, where it has a <code>name</code> field of type string.</p>
<h2 id="heading-built-in-json-unmarshal-method">Built-in JSON Unmarshal Method</h2>
<p>The <code>json.Unmarshal()</code> method in Golang is used to decode a JSON-encoded data structure into a Golang struct or map. It takes two parameters - a byte slice containing the JSON-encoded data, and a pointer to the struct or map that the decoded data will be stored in. How do we do that with the built-in method <code>json.Unmarshal</code>. Let's take a look an example below:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"encoding/json"</span>
    <span class="hljs-string">"fmt"</span>
)

<span class="hljs-keyword">type</span> Event <span class="hljs-keyword">struct</span> {
    ResourceType <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"resource_type"`</span>
    Action       <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"action"`</span>
    Data         any    <span class="hljs-string">`json:"data"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> jsonBlob = []<span class="hljs-keyword">byte</span>(<span class="hljs-string">`[
    {"resource_type":"payment","action":"confirmed","data":{"amount":100}},
    {"resource_type":"customer","action":"created","data":{"name":"john"}}
]`</span>)

    <span class="hljs-keyword">var</span> events []Event
    err := json.Unmarshal(jsonBlob, &amp;events)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Println(<span class="hljs-string">"error:"</span>, err)
    }
    fmt.Printf(<span class="hljs-string">"%+v"</span>, events)
    <span class="hljs-comment">// output: [{ResourceType:payment Action:confirmed Data:map[amount:100]} {ResourceType:customer Action:created Data:map[name:john]}]</span>
}
</code></pre>
<p>As you can see from the example above, we set the <code>Data</code> field to type <code>any</code> as we don't know what type of data we will get from the notification service. When we let the program decode the data field, we get the value as <code>map[string]interface{}</code>. We may get away with this since we can still access the data in the map by key, depending on the resource type. However, this method has the disadvantage of making the code less readable and maintainable, because we don't know the structure of the data field. In addition, we cannot take advantage of the go package <code>validator</code>, which validates structures and individual fields based on tags. You can see more details about <a target="_blank" href="https://pkg.go.dev/github.com/go-playground/validator#section-readme">Validator</a>.</p>
<p>We would need to know exactly what type of data we want go to decode, but we don't know that until go has received the data. Is there a way to decode the <code>resource_type</code> field first, before decoding everything, so that we know for sure what type of data we are decoding to? The answer is yes, but we will have to create our own custom <code>UnmarshalJSON</code> method. To see more details about <a target="_blank" href="https://pkg.go.dev/encoding/json#Unmarshaler">UnmarshalJSON</a>.</p>
<h2 id="heading-unmarshal-dynamic-data">Unmarshal Dynamic Data</h2>
<p>Here is the example for creating our own custom <code>UnmarshalJSON</code> method:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Event <span class="hljs-keyword">struct</span> {
    ResourceType <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"resource_type"`</span>
    Action       <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"action"`</span>
    Data         any    <span class="hljs-string">`json:"data"`</span>
}

<span class="hljs-keyword">type</span> Customer <span class="hljs-keyword">struct</span> {
    Name <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"name"`</span>
}

<span class="hljs-keyword">type</span> Payment <span class="hljs-keyword">struct</span> {
    Amount <span class="hljs-keyword">int</span> <span class="hljs-string">`json:"amount"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(e *Event)</span> <span class="hljs-title">UnmarshalJSON</span><span class="hljs-params">(data []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">var</span> inner <span class="hljs-keyword">struct</span> {
        ResourceType <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"resource_type"`</span>
    }
    <span class="hljs-keyword">if</span> err := json.Unmarshal(data, &amp;inner); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    <span class="hljs-keyword">switch</span> inner.ResourceType {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"payment"</span>:
        e.Data = <span class="hljs-built_in">new</span>(Payment)
    <span class="hljs-keyword">case</span> <span class="hljs-string">"customer"</span>:
        e.Data = <span class="hljs-built_in">new</span>(Customer)
    }

    <span class="hljs-keyword">type</span> aka Event
    <span class="hljs-keyword">return</span> json.Unmarshal(data, (*aka)(e))
}
</code></pre>
<p>In this example we define a <code>Customer</code> struct which has a field - <code>Name</code> and a <code>Payment</code> struct which has a field - <code>Amount</code>. We then create an <code>inner</code> struct to get the <code>ResourceType</code> in our custom <code>UnmarshalJSON</code> method, so that we can have a switch statement to decide which <code>struct</code> we want our go program to decode into depending on the resource type. Once we have assigned our data to the appropriate structure, we can call the <code>json.Unmarshal()</code> method to decode the JSON string into the receiver <code>e</code>. You may notice that we declare an alias <code>type aka Event</code> instead of using <code>Event</code> directly, this is because we are trying to avoid an infinite loop with a custom <code>UnmarshalJSON</code> method.</p>
<p>A type alias is a way of defining a new name for an existing type. However, it does not inherit the methods of the <code>Event</code> type, so we can get around that by using an alias.</p>
<p>Let's put them all together and see the result below:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"encoding/json"</span>
    <span class="hljs-string">"fmt"</span>
)

<span class="hljs-keyword">type</span> Event <span class="hljs-keyword">struct</span> {
    ResourceType <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"resource_type"`</span>
    Action       <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"action"`</span>
    Data         any    <span class="hljs-string">`json:"data"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(e *Event)</span> <span class="hljs-title">UnmarshalJSON</span><span class="hljs-params">(data []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">var</span> inner <span class="hljs-keyword">struct</span> {
        ResourceType <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"resource_type"`</span>
    }
    <span class="hljs-keyword">if</span> err := json.Unmarshal(data, &amp;inner); err != <span class="hljs-literal">nil</span> {
        <span class="hljs-keyword">return</span> err
    }

    <span class="hljs-keyword">switch</span> inner.ResourceType {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"payment"</span>:
        e.Data = <span class="hljs-built_in">new</span>(Payment)
    <span class="hljs-keyword">case</span> <span class="hljs-string">"customer"</span>:
        e.Data = <span class="hljs-built_in">new</span>(Customer)
    }

    <span class="hljs-keyword">type</span> eventAlias Event
    <span class="hljs-keyword">return</span> json.Unmarshal(data, (*eventAlias)(e))
}

<span class="hljs-keyword">type</span> Customer <span class="hljs-keyword">struct</span> {
    Name <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"name"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c *Customer)</span> <span class="hljs-title">String</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> fmt.Sprintf(<span class="hljs-string">"{Name:%s}"</span>, c.Name)
}

<span class="hljs-keyword">type</span> Payment <span class="hljs-keyword">struct</span> {
    Amount <span class="hljs-keyword">int</span> <span class="hljs-string">`json:"amount"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(p *Payment)</span> <span class="hljs-title">String</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">return</span> fmt.Sprintf(<span class="hljs-string">"{Amount:%d}"</span>, p.Amount)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> jsonBlob = []<span class="hljs-keyword">byte</span>(<span class="hljs-string">`[
    {"resource_type":"payment","action":"confirmed","data":{"amount":100}},
    {"resource_type":"customer","action":"created","data":{"name":"john"}}
]`</span>)

    <span class="hljs-keyword">var</span> events []Event
    err := json.Unmarshal(jsonBlob, &amp;events)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Println(<span class="hljs-string">"error:"</span>, err)
    }
    fmt.Printf(<span class="hljs-string">"%+v"</span>, events)
    <span class="hljs-comment">// output: [{ResourceType:payment Action:confirmed Data:{Amount:100}} {ResourceType:customer Action:created Data:{Name:john}}]</span>
}
</code></pre>
<p>Before we try out our new custom <code>UnmarshalJSON</code> method, we add an extra custom <code>String</code> method to these two types, <code>Customer</code> and <code>Payment</code>, so that our result can be printed more nicely. Moreover, we can be certain that they are decoded to corresponding types. Now, we can call the <code>json.Unmarshal()</code> method to decode the <code>jsonBlob</code> variable into <code>events</code> variable. Finally, we print out the <code>events</code> to the console and we get the whole event with our custom type instead of <code>map[string]interface{}</code>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>By writing our own custom <code>UnmarshalJSON</code> method, we have more granular control over how the data is decoded into our own struct, even though the data is dynamic. In addition, we can use the go package <code>validator</code> because we have defined our custom struct for each field and subfield.</p>
<p>We learn how to use aliases to prevent recursion calls to the custom method from causing <code>panic</code>, although the original design of <a target="_blank" href="https://go.dev/talks/2016/refactor.article#TOC_5.1.">Aliases</a> is to make code more readable by giving a more descriptive name to a type, or to make it easier to refactor your code later. Please let me know what you think and feel free to leave a comment below :)</p>
<p>Hopefully you found something useful and can apply it to your work.</p>
<p>The source code for this tutorial is available <a target="_blank" href="https://github.com/rayspock/mastering-go-examples/blob/0b0b553c53a5aaf6f3f5e70548912f5d3668f3df/json/unmarshaljson.go">here</a>.</p>
<p>Thank you for reading.</p>
]]></content:encoded></item><item><title><![CDATA[Tips on How to Speed Up Your Workflow]]></title><description><![CDATA[Have you ever tried to maintain services between environments and got lost in all the variables? Or even broken things by accident? Today I'm going to give you some tips on how to speed up your workflow and make things easier. My focus will be mainly...]]></description><link>https://blog.rayspock.com/tips-on-how-to-speed-up-your-workflow</link><guid isPermaLink="true">https://blog.rayspock.com/tips-on-how-to-speed-up-your-workflow</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Git]]></category><category><![CDATA[AWS]]></category><category><![CDATA[tips]]></category><category><![CDATA[Cloud]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Thu, 09 Feb 2023 13:47:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/YufM823aeV0/upload/c0844152b18f5c846ea168f6ad837f64.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever tried to maintain services between environments and got lost in all the variables? Or even broken things by accident? Today I'm going to give you some tips on how to speed up your workflow and make things easier. My focus will be mainly on the backend, as I work with it a lot.</p>
<h2 id="heading-multiple-git-configurations">Multiple Git configurations</h2>
<p>There may be a situation where you need to switch between different git repositories, each of which has its own set of credentials (or ssh keys). Even if you only have one set of credentials for all repositories, at least for your personal git account(for side or open source projects), it is always good for security to have them different from the ones you use at work. We don't want our work to be compromised, do we?  😇</p>
<p>Let's start by setting up your Git profile, assuming we need to set up two profiles, one for work and one for personal use.</p>
<h3 id="heading-custom-git-configurations-for-individuals">Custom Git configurations for individuals</h3>
<p>Set up a personal profile by creating a file called <code>.gitconfig-personal</code> in your home directory ($HOME) and filling it with the following content. Make sure you use your own username and email for your git account. </p>
<pre><code class="lang-bash">[user]
    name = &lt;put your personal username here&gt;
    email = &lt;put your personal email here&gt;
</code></pre>
<p>We will configure the profile for your work and save it under a different name, such as <code>.gitconfig-work</code>, just as we did in the previous step. Also put it in your home directory like this <code>$HOME/.gitconfig-work</code>.</p>
<pre><code class="lang-bash">[user]
    name = &lt;put your work username here&gt;
    email = &lt;put your work email here&gt;
</code></pre>
<h3 id="heading-git-global-configurations">Git global configurations</h3>
<p>Last but not least, we will use the <code>includeIf</code> sections to specify which profiles we want git to use in which directories. This will allow us to seamlessly switch between git profiles depending on your working directory or workspace. The only thing we would need to do is edit the following content with the appropriate values, depending on what you need, and put it in <code>.gitconfig</code> in your home directory ($HOME). You are ready to go.</p>
<pre><code class="lang-bash">[includeIf <span class="hljs-string">"gitdir:~/personal/"</span>]
      path = ~/.gitconfig-personal

[includeIf <span class="hljs-string">"gitdir:~/work/"</span>]
      path = ~/.gitconfig-work
</code></pre>
<p>Assuming that we don't have any local level configurations for Git that would override the global level we've just set (to see more detail <a target="_blank" href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration">here</a>), we can now try to verify that by running the command <code>git config -l</code>.</p>
<pre><code class="lang-bash">$ <span class="hljs-built_in">cd</span> ~/personal/personal_git_repo
$ git config -l | grep user
user.name=&lt;personal username will appear here&gt;
user.email=&lt;personal email will appear here&gt;

$ <span class="hljs-built_in">cd</span> ~/work/work_git_repo
$ git config -l | grep user
user.name=&lt;work username will appear here&gt;
user.email=&lt;work email will appear here&gt;
</code></pre>
<p>Ta-da 🥳 We can now switch between a number of different custom git configuration files, depending on the path of the git repository we are working with.</p>
<h2 id="heading-named-profiles-for-the-aws-command-line-interface-cli">Named profiles for the AWS Command-line interface (CLI)</h2>
<p>I always feel that if we can find a way to quickly move between different cloud-native environments, it can make our life a lot easier. It can also prevent us from accidentally breaking things.</p>
<h3 id="heading-what-is-a-named-profile">What is a named profile?</h3>
<blockquote>
<p>A <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html">named profile</a> is a collection of settings and credentials that we can apply to a AWS CLI command. When we specify a profile to run a command, the settings and credentials are used to run that command. Multiple named profiles can be stored in the config and credentials files.</p>
</blockquote>
<p> In the following example we specify user1 except for the default profile one and save it as <code>~/.aws/config</code></p>
<pre><code class="lang-bash">[default]
region=eu-west-2
output=json

<span class="hljs-comment"># Include the prefix "profile" only when configuring a named profile in the config file</span>
[profile user1] 
region=us-east-1
output=json
</code></pre>
<h3 id="heading-aws-credentials">AWS credentials</h3>
<p>Next thing is to configure credentials and allow the AWS CLI tool to know which credentials should be used for each specified profile. We save it as <code>~/.aws/credentials</code></p>
<pre><code class="lang-bash">[default]
aws_access_key_id=&lt;put your access key id here&gt;
aws_secret_access_key=&lt;put your secret access key here&gt;

[user1] <span class="hljs-comment"># Do not use the prefix "profile" here in crendetials</span>
aws_access_key_id=&lt;put your access key id here&gt;
aws_secret_access_key=&lt;put your secret access key here&gt;
</code></pre>
<p>Verify that the profile is successfully added by <code>aws configure list-profiles</code>. You should now see 2 profiles like below:</p>
<pre><code class="lang-bash"> default
 user1
</code></pre>
<p>To switch between different AWS accounts, set the environment variable <code>AWS_PROFILE</code> at the command line with <code>export AWS_PROFILE=&lt;put your profile name here&gt;</code>. Setting the env variable will change the default profile until the end of your shell session or until you set the variable to a different value.</p>
<p>Use the <code>aws configure list</code> command to list your current profile and show the details of it as shown below:</p>
<pre><code class="lang-bash">      Name     Value             Type    Location
      ----     -----             ----    --------
   profile     user1              env    [<span class="hljs-string">'AWS_PROFILE'</span>, <span class="hljs-string">'AWS_DEFAULT_PROFILE'</span>]
access_key     ****************FPFQ shared-credentials-file
secret_key     ****************a5K9 shared-credentials-file
    region     us-east-1          env    [<span class="hljs-string">'AWS_REGION'</span>, <span class="hljs-string">'AWS_DEFAULT_REGION'</span>]
</code></pre>
<p>We can go further by running <code>aws sts get-caller-identity</code> to get the identity of the caller and make sure you are using the correct identity to run the command.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Account:</span> <span class="hljs-string">'aws_account_id'</span>
<span class="hljs-attr">Arn:</span> <span class="hljs-string">arn:aws:iam::aws_account_id:user/user1</span>
<span class="hljs-attr">UserId:</span> <span class="hljs-string">****************KPM7</span>
</code></pre>
<p>Great! Now you can switch between the different name profiles with ease 🎉</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This article has given us some tips on how to optimise the tools we use to speed up our workflow.</p>
<p>Hopefully you found something useful and can apply it to your work.</p>
<p>Thank you very much for reading.</p>
]]></content:encoded></item><item><title><![CDATA[Things You Need to Know Before Working With Go Pipelines]]></title><description><![CDATA[Today's challenge is about working with concurrency in Go - Goroutines, Channels, and Pipelines. The following exercise is from the book, “Mastering Go: Create Golang production applications using network libraries, concurrency, machine learning, and...]]></description><link>https://blog.rayspock.com/things-you-need-to-know-before-working-with-go-pipelines</link><guid isPermaLink="true">https://blog.rayspock.com/things-you-need-to-know-before-working-with-go-pipelines</guid><category><![CDATA[100DaysOfCode]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[Pipeline]]></category><category><![CDATA[learning]]></category><category><![CDATA[concurrency]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Sun, 04 Sep 2022 22:46:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/XUEdfpPIhXg/upload/b4bff550fd4a75bbbc1926731c74aaff.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today's challenge is about working with concurrency in Go - Goroutines, Channels, and Pipelines. The following exercise is from the book, “Mastering Go: Create Golang production applications using network libraries, concurrency, machine learning, and advanced data structures” by Mihalis Tsoukalos. The book not only contains a lot of useful examples and best practices but also helps you understand the capability of Go in depth via well-written descriptions. I highly recommend reading through this book to keep your Go journey going smoothly and advance your programming skill with Go.</p>
<p>Here is the task we would like our program to achieve</p>
<blockquote>
<p>Create a pipeline that reads text files, finds the number of occurrences of a given phrase in each text file, and calculate the total number of occurrences of the phrase in all files.</p>
</blockquote>
<p>The first part of the code is the most straightforward one where we pass file paths and a phrase to the program as the command-line argument, so the program knows which files to look for for the given phrase. A <code>for</code> loop is used to iterate each file, and we are going to create goroutines to read and count the number of occurrences in each one. Finally, we will gather results from all goroutines and sum up the total.</p>
<p>For example:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"bufio"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"os"</span>
    <span class="hljs-string">"sync"</span>
    <span class="hljs-string">"sync/atomic"</span>
)

<span class="hljs-keyword">var</span> wg sync.WaitGroup
<span class="hljs-keyword">var</span> ops <span class="hljs-keyword">int32</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(os.Args) &lt; <span class="hljs-number">2</span> {
        fmt.Println(<span class="hljs-string">"Need at least one phrase and file parameters!"</span>)
        <span class="hljs-keyword">return</span>
    }
    phrase := os.Args[<span class="hljs-number">1</span>]
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">2</span>; i &lt; <span class="hljs-built_in">len</span>(os.Args); i++ {
        fn := os.Args[i]
        <span class="hljs-comment">// TO-DO: Reads the file and finds the number of occurrences of a given phrase</span>
    }
    <span class="hljs-comment">// TO-DO: Calculate total numbers of occurrences</span>
    fmt.Println(<span class="hljs-string">"total occurrences:"</span>, total)
}
</code></pre>
<p>Before we go to the next stage, we need to figure out how goroutines communicate with each other. Golang provides a communication mechanism called “Channel” where goroutines can exchange data with others. Therefore, we can leverage Channel to collect outcomes from other goroutines like the code segment below. It takes whatever inputs from the channel(chan keyword) <code>in</code> and sums them up.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">sum</span><span class="hljs-params">(in &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">int</span></span> {
    total := <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> x := <span class="hljs-keyword">range</span> in {
        total += x
    }
    <span class="hljs-keyword">return</span> total
}
</code></pre>
<p>The last thing is to read files and count the total occurrences of the given phrase. In the third part of the code, you can find a function <code>scanFile</code> that takes three arguments to specify file path(fn), given phrase(phrase) and channel(out). So far, you probably have an idea what Channel here is used for? In order to communicate to other entities or goroutines, we need to send out messages to the channel. There is one tiny but very important thing to mention here which is the &lt;- symbol found on the right of the <code>chan</code> keyword. It indicates a write-only channel where you can only send messages to it. On the contrary, if the &lt;- symbol is found on the left side of the <code>chan</code> keyword, then it denotes that the channel can only be used for reading only like we did with the function <code>sum</code>. In the following code segment, we read all the words from the given file and count the numbers of occurrences of the given phrase. The total occurrences <code>count</code> will be sent to the <code>out</code> channel which indicates the <code>in</code> channel in function <code>sum</code>.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">scanFile</span><span class="hljs-params">(fn, phrase <span class="hljs-keyword">string</span>, out <span class="hljs-keyword">chan</span>&lt;- <span class="hljs-keyword">int</span>)</span></span> {
    f, err := os.Open(fn)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Println(err)
        <span class="hljs-keyword">return</span>
    }
    <span class="hljs-keyword">defer</span> f.Close()
    scanner := bufio.NewScanner(f)
    scanner.Split(bufio.ScanWords)
    count := <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> scanner.Scan() {
        <span class="hljs-keyword">if</span> phrase == scanner.Text() {
            count++
        }
    }
    fmt.Println(<span class="hljs-string">"file:"</span>, f.Name(), <span class="hljs-string">", occurrences:"</span>, count)
    <span class="hljs-comment">// send to the channel</span>
    out &lt;- count
    <span class="hljs-keyword">if</span> err := scanner.Err(); err != <span class="hljs-literal">nil</span> {
        fmt.Println(err)
        <span class="hljs-keyword">return</span>
    }
}
</code></pre>
<p>Now, we can connect all the dots and finish our main function like the code snippet below. Basically, we finish most of the logic and it should work as expected. However, if we attempt to execute this program, we will get an error message like: <code>fatal error: all goroutines are asleep - deadlock!</code>. This is because we forgot to close the channel where the function sum was reading messages from and because of that, the program doesn't know when to stop reading messages from the channel and then causes a deadlock.</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(os.Args) &lt; <span class="hljs-number">2</span> {
        fmt.Println(<span class="hljs-string">"Need at least one phrase and file parameters!"</span>)
        <span class="hljs-keyword">return</span>
    }
    phrase := os.Args[<span class="hljs-number">1</span>]
    A := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    jobNumbers := <span class="hljs-built_in">len</span>(os.Args) - <span class="hljs-number">2</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">2</span>; i &lt; <span class="hljs-built_in">len</span>(os.Args); i++ {
        fn := os.Args[i]
        <span class="hljs-comment">// Reads the file and finds the number of occurrences of a given phrase</span>
        <span class="hljs-keyword">go</span> scanFile(fn, phrase, A)
    }
    <span class="hljs-comment">// Calculate total numbers of occurrences</span>
    total := sum(A)
    fmt.Println(<span class="hljs-string">"total occurrences:"</span>, total)
}
</code></pre>
<p>How do we resolve it and make sure that we close the channel properly when every goroutine finishes their jobs? It is worthwhile to mention that we cannot close a closed channel in golang, therefore, only the last goroutine which finishes its jobs can close the channel. Let’s imagine that channels are serving stations where we hold food and goroutines are the staff who serve the food. Let's say we would like to close the cafeteria, those serving stations need to be properly closed. Therefore, we have to assure that only the last person who finishes the serving can close the serving station, otherwise - hypothetically - others might not be able to serve food in the future.</p>
<p>Let's get back to the <code>main</code> function here below to see how we can refine it. We would need a counter which can be accessed by multiple goroutines, so that we could keep track of how many goroutines are still working and will need to use the channel. However, how do we manage states between goroutines without causing race conditions? Well, this is where <code>sync/atomic</code> comes in handy. We can setup a new variable <code>ops</code> and leverage the package <code>atomic</code> to control our state across goroutines and prevent race conditions.</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> ops <span class="hljs-keyword">int32</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(os.Args) &lt; <span class="hljs-number">2</span> {
        fmt.Println(<span class="hljs-string">"Need at least one phrase and file parameters!"</span>)
        <span class="hljs-keyword">return</span>
    }
    phrase := os.Args[<span class="hljs-number">1</span>]
    A := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    jobNumbers := <span class="hljs-built_in">len</span>(os.Args) - <span class="hljs-number">2</span>
    atomic.AddInt32(&amp;ops, <span class="hljs-keyword">int32</span>(jobNumbers)) <span class="hljs-comment">// to keep track of total numbers of goroutines</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">2</span>; i &lt; <span class="hljs-built_in">len</span>(os.Args); i++ {
        fn := os.Args[i]
        <span class="hljs-comment">// Reads the file and finds the number of occurrences of a given phrase</span>
        <span class="hljs-keyword">go</span> scanFile(fn, phrase, A)
    }
    <span class="hljs-comment">// Calculate total numbers of occurrences</span>
    total := sum(A)
    fmt.Println(<span class="hljs-string">"total occurrences:"</span>, total)
}
</code></pre>
<p>The last part of the <code>scanFile</code> function is as follows:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">scanFile</span><span class="hljs-params">(fn, phrase <span class="hljs-keyword">string</span>, out <span class="hljs-keyword">chan</span>&lt;- <span class="hljs-keyword">int</span>)</span></span> {
    <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        atomic.AddInt32(&amp;ops, <span class="hljs-number">-1</span>)
        <span class="hljs-keyword">if</span> atomic.LoadInt32(&amp;ops) == <span class="hljs-number">0</span> {
            <span class="hljs-built_in">close</span>(out)
            <span class="hljs-keyword">return</span>
        }
    }()
    f, err := os.Open(fn)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        fmt.Println(err)
        <span class="hljs-keyword">return</span>
    }
    <span class="hljs-keyword">defer</span> f.Close()
    scanner := bufio.NewScanner(f)
    scanner.Split(bufio.ScanWords)
    count := <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> scanner.Scan() {
        <span class="hljs-keyword">if</span> phrase == scanner.Text() {
            count++
        }
    }
    fmt.Println(<span class="hljs-string">"file:"</span>, f.Name(), <span class="hljs-string">", occurrences:"</span>, count)
    <span class="hljs-comment">// send to the channel</span>
    out &lt;- count
    <span class="hljs-keyword">if</span> err := scanner.Err(); err != <span class="hljs-literal">nil</span> {
        fmt.Println(err)
        <span class="hljs-keyword">return</span>
    }
}
</code></pre>
<p>Here we add an anonymous function to manage the counter <code>ops</code> we mentioned previously. Basically, what it does is to check whether it can close the channel or not after decreasing <code>ops</code> by 1. Furthermore, use the <code>defer</code> keyword to make sure that each goroutine will remember to manage the counter when it finishes its jobs, so we can keep <code>ops</code> in-sync across goroutines. As a result, only the last goroutine to finish can scan the document and close the channel.</p>
<p>The source code for this tutorial is available <a target="_blank" href="https://github.com/rayspock/mastering-go-examples/blob/0b0b553c53a5aaf6f3f5e70548912f5d3668f3df/pipeline/pipeline.go">here</a></p>
<p>Thank you for reading this article.</p>
]]></content:encoded></item><item><title><![CDATA[Let’s Talk About Smart Contracts]]></title><description><![CDATA[Recently, I finally finished a lecture about developing smart contracts on Blockchain which I bought from Udemy 5 months ago. To be honest, it’s been busy for me to complete a 12 hour long course over the past few months. Nevertheless, it was really ...]]></description><link>https://blog.rayspock.com/lets-talk-about-smart-contracts</link><guid isPermaLink="true">https://blog.rayspock.com/lets-talk-about-smart-contracts</guid><category><![CDATA[Solidity]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[Smart Contracts]]></category><category><![CDATA[100DaysOfCode]]></category><category><![CDATA[Blockchain]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Tue, 09 Aug 2022 22:31:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/R4WCbazrD1g/upload/440f848b3ff54ee6de31990080ef0643.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, I finally finished a lecture about developing smart contracts on Blockchain which I bought from Udemy 5 months ago. To be honest, it’s been busy for me to complete a 12 hour long course over the past few months. Nevertheless, it was really great to take a deep dive into learning how to implement some famous use cases like tokenising assets or order tracking with Solidity. It was worth the effort to do it because I now feel more confident than before when I was developing smart contract projects during hackathon events.</p>
<p>According to Wikipedia:</p>
<blockquote>
<p>“Solidity is an object-oriented programming language for implementing smart contracts on various blockchain platforms, most notably, Ethereum. It was developed by Christian Reitwiessner, Alex Beregszaszi, and several former Ethereum core contributors. Programs in Solidity run on Ethereum Virtual Machine.”</p>
</blockquote>
<p>I was drawn to this technology where we can write a piece of immutable code that can be executed automatically without any middleman involved. To put it simply, we don’t have to worry about whether we can trust a centralised authority to claim that the services or software they provide are legit or verified. This kind of trustless characteristic is the core notion of blockchain where smart contracts are stored and run.</p>
<p>Now, we know smart contracts are programs where you can automate them to execute agreements when predefined criteria are met, therefore, we can get one step further to automate workflows that need to be done in our daily life. These could be things such as retrieving stock market reports when there are huge fluctuations in the market or tracking shipping when there are orders made. Essentially, almost everything that happens in the real world can be seen as contracts or agreements to achieve a certain goal under certain conditions like turning on lights when the sky gets dark. The only missing puzzle is how we convert these contracts to smart contracts in practical manners.</p>
]]></content:encoded></item><item><title><![CDATA[What happens if you overflow or underflow integer in solidity?]]></title><description><![CDATA[Solidity is a programming language used to write smart contracts that are run on Ethereum Blockchain Node. To be precise, it runs on Ethereum Virtual Machine(EVM). Today, I’m about to share one of the more interesting behaviours I found in Solidity.
...]]></description><link>https://blog.rayspock.com/what-happens-if-you-overflow-or-underflow-integer-in-solidity</link><guid isPermaLink="true">https://blog.rayspock.com/what-happens-if-you-overflow-or-underflow-integer-in-solidity</guid><category><![CDATA[100DaysOfCode]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[Smart Contracts]]></category><category><![CDATA[Blockchain]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Thu, 21 Jul 2022 14:24:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/w9_XGvzxvxo/upload/11e845558265e6816f4f5bde10be1d0f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Solidity is a programming language used to write smart contracts that are run on Ethereum Blockchain Node. To be precise, it runs on Ethereum Virtual Machine(EVM). Today, I’m about to share one of the more interesting behaviours I found in Solidity.</p>
<p><strong><em>Unsigned Integers</em></strong></p>
<p>Signed integral types (int) can represent both positive numbers and negative numbers, whereas Unsigned ones (uint) can only represent positive integers</p>
<p><strong><em>Size of Integers</em></strong></p>
<p>The default size of integers is 32 bytes, which can be declared as <code>uint</code>. Also, we can explicitly specify the size of integers by adding the trailing number of bits such as <code>uint8</code> which can store in 8 bits allowing 256 combinations (0 through 255).</p>
<p>What if we try to store the result of some arithmetic that falls outside of the declared range? In other programming languages, like Golang, the software may panic and disable the action ( causing an exception in the program), but it's not the case in Solidity(before version 0.8). It will still be runnable without failing the action, but the result won’t be what we expected, it will roll over to the next digit it can represent or store.</p>
<p>Feel free to try the code snippet below and see if you can overflow or underflow integers.</p>
<pre><code class="lang-solidity"><span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> &gt;=0.6.0 &lt;0.7.0;</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Overflow</span> </span>{
    <span class="hljs-comment">// value range : 0 ~ 255</span>
    <span class="hljs-keyword">uint8</span> <span class="hljs-keyword">public</span> balance;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">maxTheBalance</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        balance <span class="hljs-operator">=</span> <span class="hljs-number">2</span> <span class="hljs-operator">*</span><span class="hljs-operator">*</span> <span class="hljs-number">8</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>;
    }

    <span class="hljs-comment">// if the balance was 0, after decreasing it by 1, the balance will become 255</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">decrease</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        balance <span class="hljs-operator">=</span> balance <span class="hljs-operator">-</span> <span class="hljs-number">1</span>;
    }

    <span class="hljs-comment">// if the balance was 255, after increasing it by 1, the balance will become 0</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">increase</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        balance <span class="hljs-operator">=</span> balance <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
    }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Some thoughts on using Event Sourcing]]></title><description><![CDATA[About a year ago, I started to work with event sourcing and built a system around it. It seemed quite new to me, but the more I learned, the easier it became. Basically, I've already worked with event sourcing pattern for my entire career as a softwa...]]></description><link>https://blog.rayspock.com/some-thoughts-on-using-event-sourcing</link><guid isPermaLink="true">https://blog.rayspock.com/some-thoughts-on-using-event-sourcing</guid><category><![CDATA[Event Sourcing]]></category><category><![CDATA[Blogging]]></category><category><![CDATA[software development]]></category><category><![CDATA[Git]]></category><category><![CDATA[Developer]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Mon, 30 May 2022 20:40:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/41NsEMAB1Ps/upload/1acbbcf2773d99447f933e5647bc354c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>About a year ago, I started to work with event sourcing and built a system around it. It seemed quite new to me, but the more I learned, the easier it became. Basically, I've already worked with event sourcing pattern for my entire career as a software engineer even starting at school, however, I didn't realise it at that time.</p>
<h2 id="heading-a-good-example-we-use-every-day">A good example we use every day</h2>
<p>As long as you have coding experience you should be able to work with version control systems such as Git, which is a good example of event sourcing pattern. Every commit you push to Git is stored as an event representing a change state that can be added, edited or removed from files or code. This is the core concept of event sourcing where you can source back all the events that have happened in the past. As a result of this, we can revert the changes that have been made in the code from git whenever needed - Just like a time machine isn’t it ;)</p>
<p>The sooner you master Git the quicker it makes your life easier. It had my back a few times in my career and saved a lot of time :) I highly recommend git to those who want to work as a developer and spend some time cultivating it. I digress a little bit here. Let's talk about when to use event sourcing. It’s not a silver bullet that allows you to solve any problem, it really does add complexity to your system, so make sure you think through the goal you want to achieve that can be solved by it before adopting it.</p>
<h2 id="heading-audit-trails">Audit trails</h2>
<p>The first scenario where you might use event sourcing is when you want full audit trails in your system, like in payment or banking. This is also the main reason why I use it in my work. We have various records and transactions including financial, accounting and medical that need to be tracked and verified.</p>
<h2 id="heading-reporting">Reporting</h2>
<p>The second is reporting. With event sourcing you have much more insight into your data and you can report retrospectively. Just like data mining, where you extract meaningful information from a collection of data.</p>
<h2 id="heading-parallel-processing">Parallel processing</h2>
<p>The third scenario is parallel processing. Each microservice communicates via event messages, so they are loosely coupled without interdependence. Suppose you are looking for a highly scalable solution, this could potentially satisfy horizontal scalability and resilience to system failures.</p>
<h2 id="heading-summary">Summary</h2>
<p>We have gone through some use cases can be resolved by event sourcing such as audit trails, reporting or parallel processing. Are there other problems that can be solved with event sourcing? Tell me what you think and I hope this blog inspires you to explore solutions for your use cases.</p>
]]></content:encoded></item><item><title><![CDATA[How I became a developer? (Part2)]]></title><description><![CDATA[In order to explore the digital world and know how the computer works, I started my undergraduate education in medical informatics. According to psychology wiki: 

Health informatics or medical informatics is the intersection of information science, ...]]></description><link>https://blog.rayspock.com/how-i-became-a-developer-part2</link><guid isPermaLink="true">https://blog.rayspock.com/how-i-became-a-developer-part2</guid><category><![CDATA[blog]]></category><category><![CDATA[Experience ]]></category><category><![CDATA[Developer]]></category><category><![CDATA[life]]></category><category><![CDATA[Learning Journey]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Wed, 08 Dec 2021 10:47:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674688931400/c787e22f-76c9-4be4-95c4-1ff2087910f9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In order to explore the digital world and know how the computer works, I started my undergraduate education in medical informatics. According to <a target="_blank" href="https://psychology.wikia.org/wiki/Psychology_Wiki">psychology wiki</a>: </p>
<blockquote>
<p>Health informatics or medical informatics is the intersection of information science, computer science and health care. It deals with the resources, devices and methods required to optimize the acquisition, storage, retrieval and use of information in health and biomedicine. </p>
</blockquote>
<p>You might be wondering why I didn’t choose computer science? Well…the truth was computer science was not the only thing that I was really into but also health care. I remember I asked my sister about what the tech guys did in the hospital because she worked there after graduating. I kept asking questions which were quite hard to answer, such as “how do the programs work to store the data in the database?” and “how do people upload their medical records to the system?”. What piqued my curiosity was how the doctors used information technology to examine the patients. Apparently, I didn’t get the answer from her in the end, because this wasn’t my sisters’ speciality, therefore I ended up finding the answer by myself.</p>
<p>To be honest, it wasn't an easy route to master both computer science and health care. Even though I had to pull many all-nighters or go through a lot of tough times, I still did it eventually and felt fulfilled and fruitful in the end.</p>
<p>At a later time, I didn't start my career in the hospital or healthcare industry, even though my expertise was in the health informatics area. The reason was that the technology in medical care was lagging behind. It was hard to grow quickly and to be able to learn a lot of sophisticated technology. It turns out the time I spent outside of the healthcare industry was worth it, I learned a lot about the full lifecycle of the software design process, including requirements definition, prototyping, proof of concept and object-oriented programming etc.</p>
<p>I feel like the knowledge I studied at school, more specifically an educational institution, was very different from the knowledge I learned in the corporation or open-source community. Institutionalized education normally provides a well-structured learning experience, so you do not only need to learn the courses you like but the courses you are not very into, not to mention the majority of the courses are about theory. However, the training you gain in the enterprise is mainly practical and work-related which I enjoyed most. I quite applaud the saying that, “the secret of learning is the desire to learn” - this is how you cultivate the skill, gain the ability and increase your knowledge. This was my journey towards becoming an excellent developer.</p>
<p>I hope you will find something useful through my journey and if you have any questions or feel that I kept something up my sleeve😉, feel free to email me or create issues <a target="_blank" href="https://github.com/rayspock/rayspock/issues">here</a>. I would love to share😄.</p>
]]></content:encoded></item><item><title><![CDATA[How I became a developer? (Part1)]]></title><description><![CDATA[Technology is one of the most popular aspects in almost every industry. As for software technology, they have been widely used for decades. I still remember the first computer that my dad bought me which requires you to install a bunch of different s...]]></description><link>https://blog.rayspock.com/how-i-became-a-developer-part1</link><guid isPermaLink="true">https://blog.rayspock.com/how-i-became-a-developer-part1</guid><category><![CDATA[blog]]></category><category><![CDATA[Experience ]]></category><category><![CDATA[Developer]]></category><category><![CDATA[life]]></category><category><![CDATA[Learning Journey]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Mon, 25 Oct 2021 09:42:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674688981449/20739d04-aa44-4b83-80c6-fffe08950e2d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Technology is one of the most popular aspects in almost every industry. As for software technology, they have been widely used for decades. I still remember the first computer that my dad bought me which requires you to install a bunch of different software and specifically only use the operating system ‘windows 95’ in order to function properly. Yeah, it was very old so you might not have heard of it and the performance is much slower than the smartphone people have nowadays, but it was pretty awesome back then.</p>
<p>For most children, including me, the most exciting part of computers is using them to play video games. We did have some programming classes in school, besides, who cannot be attracted by those fancy and interesting gaming experiences? Those games transport us to new realities and satisfy our needs for achievement and recognition. These are how I started to immerse myself in the digital world.</p>
<p>After I built my own PC (well… technically I assembled it), I discovered that building computers was way more fun than the games I was playing on them. There is always an end to the game, but this is not the case when building your PC, which has nearly endless outcomes. By the way, I was not talking about the open-world games like Minecraft, which had not come out yet back then or at least very few of them.</p>
<p>I started to do all kinds of research about computers and the principles behind them. So I assisted people in building their PC and even became their consultant to practice what I have learned. This process made me realise that I am very into tech and want to become tech-savvy.</p>
<p>Next part would be to tell you about the rest of the journey🚀.</p>
<p>Cheers!</p>
]]></content:encoded></item><item><title><![CDATA[How do I clear cookies for a specific site with one click?]]></title><description><![CDATA[I often find that clearing cookies for the website that I’m visiting is a real annoyance because it comes with several steps to use the built-in feature that chrome provides to clear them. Besides, most of the Chrome Extensions will clear all the web...]]></description><link>https://blog.rayspock.com/how-do-i-clear-cookies-for-a-specific-site-with-one-click</link><guid isPermaLink="true">https://blog.rayspock.com/how-do-i-clear-cookies-for-a-specific-site-with-one-click</guid><category><![CDATA[chrome extension]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Ray Yang]]></dc:creator><pubDate>Wed, 22 Sep 2021 09:56:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1674689030082/84bec88f-8454-4e97-8d19-0a7129a5d047.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I often find that clearing cookies for the website that I’m visiting is a real annoyance because it comes with several steps to use the built-in feature that chrome provides to clear them. Besides, most of the <strong>Chrome Extensions</strong> will clear all the website data and you will lose all your login details for some websites. If you’re lucky you might be fine with signing in.</p>
<p>But the truth is people (at least in my experience) won’t always remember the username/password when needed. This can become a huge disaster.</p>
<p>Well, it’s time to build my:rocket: chrome extension called <strong>Clean Captain</strong>.</p>
<p>Before we jump in and get our hands dirty, we have to understand the pain point.</p>
<hr />
<h2 id="heading-how-to-clear-cookies-without-extension">How to clear cookies without extension</h2>
<p>I used to practice the following steps to clear cookies before I rolled out this extension, which turned out to be not so user-friendly.</p>
<ol>
<li><p>Select the <strong>lock icon</strong> next to website URL in the Address bar.</p>
</li>
<li><p>Click <strong>Cookies</strong> with numbers identified how many cookies in use.</p>
</li>
<li><p>Scroll down and <strong>locate the site</strong> that you'd like to clear the cookies.</p>
</li>
<li><p>Click <strong>Remove</strong> will only delete the cookies from the site you selected in the previous step.</p>
</li>
</ol>
<hr />
<h2 id="heading-clean-captain">Clean Captain</h2>
<p>Clean Captain is a lightweight extension that enables you to remove the cookies from most websites that need cookies enabled to work properly. Protect your privacy and delete all activity from specifying websites. You can keep sessions or cookies from those websites you trust.</p>
<p><img src="https://rayspock.com/posts/clean-captain/clean-captain-screenshot.png" alt="clean captain" /></p>
<hr />
<h2 id="heading-where-to-get-it">Where to get it</h2>
<ul>
<li><p><strong>Clean Captain</strong> from <a target="_blank" href="https://chromewebstore.google.com/detail/clean-captain/finpbdieoeedpjhaimgobfodhhdnoljb">Chrome Web Store</a></p>
</li>
<li><p>Loves to get your hands dirty, check out the <a target="_blank" href="https://github.com/rayspock/clean-captain">Source Code</a></p>
</li>
</ul>
<hr />
<p>I hope you will find this guide useful and if you have any questions about how to create your chrome extensions, I'd be glad to answer them.</p>
<p>Cheers!</p>
]]></content:encoded></item></channel></rss>