<?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>DEV Community: Jeffrey Boisvert</title>
    <description>The latest articles on DEV Community by Jeffrey Boisvert (@jdvert).</description>
    <link>https://dev.to/jdvert</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%2F521051%2Ffb2db7ed-499b-4650-93fc-6e2d47b40f29.jpg</url>
      <title>DEV Community: Jeffrey Boisvert</title>
      <link>https://dev.to/jdvert</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jdvert"/>
    <language>en</language>
    <item>
      <title>Let's Build a Go CLI Business Card</title>
      <dc:creator>Jeffrey Boisvert</dc:creator>
      <pubDate>Thu, 23 Mar 2023 00:49:56 +0000</pubDate>
      <link>https://dev.to/jdvert/lets-build-a-go-cli-business-card-part-1-7cg</link>
      <guid>https://dev.to/jdvert/lets-build-a-go-cli-business-card-part-1-7cg</guid>
      <description>&lt;p&gt;Seems every developer is trying to find a clever way to get noticed. And in doing so you may stumble on a bunch of interesting projects accomplishing all kinds of things. However, one simple and easy project you can do is making a Business Card for yourself (disguised as a CLI program)! &lt;/p&gt;

&lt;p&gt;To do this we will be writing our CLI program in Go and be leveraging &lt;a href="https://pkg.go.dev/github.com/spf13/cobra@v1.6.1" rel="noopener noreferrer"&gt;cobra&lt;/a&gt; which is a great Go library to create this card quickly and effectively. &lt;/p&gt;

&lt;p&gt;To give our program some character we will be leveraging &lt;a href="https://github.com/Delta456/box-cli-maker" rel="noopener noreferrer"&gt;box-cli-marker&lt;/a&gt; in this tutorial which is a great project you should check out!&lt;/p&gt;

&lt;h2&gt;
  
  
  Before we start why Go?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://go.dev/" rel="noopener noreferrer"&gt;Go&lt;/a&gt; is a great language to use for this project because it is a compiled language which means it is fast and easy to work with since the compiler will catch most errors for you. Since it is compiled it is also easy to distribute your program to others.&lt;/p&gt;

&lt;p&gt;Also, Go has article on &lt;a href="https://go.dev/solutions/clis" rel="noopener noreferrer"&gt;this&lt;/a&gt; but I personally have been working more with Go and have found it a lot of fun to work with. Also the support for CLIs by the community and from the language itself has been really great from what I have found.&lt;/p&gt;

&lt;p&gt;But enough about why Go, let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;First let's create a new &lt;a href="https://go.dev/doc/code" rel="noopener noreferrer"&gt;Go project&lt;/a&gt; (Go's official documentation is a great place to start). &lt;/p&gt;

&lt;p&gt;Then let's install Cobra and its CLI. &lt;a href="https://github.com/spf13/cobra" rel="noopener noreferrer"&gt;Cobra&lt;/a&gt; is a great CLI library for Go and the CLI is a great tool to get started quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get &lt;span class="nt"&gt;-u&lt;/span&gt; github.com/spf13/cobra@latest
go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/spf13/cobra-cli@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the Cobra CLI installed let's get some boilerplate set up by running with 1 simple command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cobra-cli init businesscard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that &lt;code&gt;businesscard&lt;/code&gt; is the name of the cli for this example but you can name it whatever you want (ex: jdvert is what I used for my own card). &lt;/p&gt;

&lt;p&gt;This will generate a &lt;code&gt;cmd&lt;/code&gt; directory for you with a &lt;code&gt;root.go&lt;/code&gt; file for you. This is where the bulk of your code will live&lt;/p&gt;

&lt;p&gt;To see if everything is working let's install our CLI and run it!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install
&lt;/span&gt;businesscard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the following output (which is the default Cobra output):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:

Cobra is a CLI library &lt;span class="k"&gt;for &lt;/span&gt;Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Actual Card
&lt;/h2&gt;

&lt;p&gt;Once you confirm everything is working we can then get starting on creating our custom card. &lt;/p&gt;

&lt;p&gt;Let's update the command to show a box with text in it instead of the current text it shows. &lt;/p&gt;

&lt;p&gt;First let's install the box-cli-maker library. This is a great library that allows you to create boxes with text in them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get &lt;span class="nt"&gt;-u&lt;/span&gt; github.com/Delta456/box-cli-maker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's update our &lt;code&gt;root.go&lt;/code&gt; file to use this library by finding &lt;code&gt;rootCmd&lt;/code&gt; and updating it to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootCmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cobra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;"businesscard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Short&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"A personal business card CLI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;"businesscard is a personal business card because who has time to actually spend printing real ones."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cobra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&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="c"&gt;// This is where we will add our box which can be found looking at https://github.com/Delta456/box-cli-maker#usage &lt;/span&gt;
        &lt;span class="c"&gt;// and the examples in the README are a great place to start!&lt;/span&gt;
        &lt;span class="n"&gt;Box&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Px&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Py&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Double"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Cyan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ContentAlign&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Center"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="n"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Let's build something fun!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="s"&gt;"Thanks for checking out my card! Happy coding!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&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;Now lets try out our new card by running &lt;code&gt;go install&lt;/code&gt; and then &lt;code&gt;businesscard&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;Now you should see the following when running your command!&lt;/p&gt;

&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%2Fraw.githubusercontent.com%2Fjdboisvert%2Fblog%2Fmain%2Fimages%2Fjohn-doe-cli-part-1.1.png" 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%2Fraw.githubusercontent.com%2Fjdboisvert%2Fblog%2Fmain%2Fimages%2Fjohn-doe-cli-part-1.1.png" alt="Image of the cli running with the code shown above"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can customize your card as you wish and &lt;a href="https://github.com/Delta456/box-cli-maker/blob/master/README.md" rel="noopener noreferrer"&gt;box-cli-marker&lt;/a&gt; has a bunch of examples on how you can do this. Change the color, icons, size, etc. The sky is the limit!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial we got our Go Business Card CLI program set up and looking great in our terminal. &lt;/p&gt;

&lt;p&gt;Happy coding and free to leave me a link to your card so I can check it out!&lt;/p&gt;

&lt;p&gt;Here this my post hosted on &lt;a href="https://github.com/jdboisvert/blog/blob/main/posts/Let's%20Build%20a%20Go%20CLI%20Business%20Card%20-%20Part%201.md" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>cli</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Handling Mutexes in Distributed Systems with Redis and Go</title>
      <dc:creator>Jeffrey Boisvert</dc:creator>
      <pubDate>Sat, 25 Feb 2023 23:07:40 +0000</pubDate>
      <link>https://dev.to/jdvert/handling-mutexes-in-distributed-systems-with-redis-and-go-5g0d</link>
      <guid>https://dev.to/jdvert/handling-mutexes-in-distributed-systems-with-redis-and-go-5g0d</guid>
      <description>&lt;p&gt;A mutual exclusion lock (or Mutex which is an abbreviation of &lt;strong&gt;Mut&lt;/strong&gt;ual &lt;strong&gt;Ex&lt;/strong&gt;clusion) is a very important and sometimes overlooked concept when building a thread-safe access to a system's resources. On top of it being overlooked it can be quite challenging to handle and can lead to race conditions in your system if not done correctly. &lt;/p&gt;

&lt;p&gt;In this post I will go over how you can use &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; (a popular and widely used key-value store) to help ensure your Go application has mutex on important operations and resources using &lt;a href="https://redis.io/docs/manual/patterns/distributed-locks/" rel="noopener noreferrer"&gt;Redis's implementation of distributed locks&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  How does Redis support distributed locks?
&lt;/h2&gt;

&lt;p&gt;Redis supports distributed locks through the use of its &lt;a href="https://redis.io/commands/set/" rel="noopener noreferrer"&gt;&lt;code&gt;SET&lt;/code&gt;&lt;/a&gt; command. When used with certain options, the &lt;code&gt;SET&lt;/code&gt; command can be used to implement a locking algorithm (ideally you make the key something unique so no only 1 thread/request can have the lock at once like a user id for example). &lt;/p&gt;

&lt;p&gt;The idea is the &lt;code&gt;SET&lt;/code&gt; command used will only succeed if the given key does not already exist in Redis, effectively creating a lock. Once the lock is acquired, other processes will be unable to acquire the lock since they are unable to set that same key being used as described in the &lt;a href="https://redis.io/docs/manual/patterns/distributed-locks/" rel="noopener noreferrer"&gt;distributed locks&lt;/a&gt; page. &lt;/p&gt;

&lt;p&gt;To release the lock, the process that acquired the lock can use the &lt;a href="https://redis.io/commands/del/" rel="noopener noreferrer"&gt;DEL&lt;/a&gt; command to delete the Redis key. Once the key is deleted, the lock is released and other processes can acquire the lock if they need to by setting the key again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing distributed locks in Go using Redis
&lt;/h2&gt;

&lt;p&gt;In this example we will use the Redis client library &lt;a href="https://github.com/redis/go-redis" rel="noopener noreferrer"&gt;github.com/go-redis/redis&lt;/a&gt; which is a common library many developers use to interact with a Redis instance in Go. &lt;/p&gt;

&lt;p&gt;Make a new &lt;a href="https://go.dev/doc/code" rel="noopener noreferrer"&gt;Go project&lt;/a&gt; or pull down the code &lt;a href="https://github.com/jdboisvert/redis-distributed-locks" rel="noopener noreferrer"&gt;I wrote&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Ensure to have a &lt;a href="https://redis.io/docs/getting-started/" rel="noopener noreferrer"&gt;local Redis instance running&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/go-redis/redis"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;acquireLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expiration&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&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;span class="c"&gt;// Use the SET command to try to acquire the lock&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetNX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"lock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expiration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;releaseLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&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="c"&gt;// Use the DEL command to release the lock&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create a new Redis client&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Addr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;"localhost:6379"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Password&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// no password set&lt;/span&gt;
        &lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c"&gt;// use default DB&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;mutexKey&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"my_lock"&lt;/span&gt;

    &lt;span class="c"&gt;// Try to acquire the first lock&lt;/span&gt;
    &lt;span class="n"&gt;isFirstLockSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;acquireLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutexKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error acquiring lock:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isFirstLockSet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to acquire lock"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Do some work while holding the lock&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First lock acquired!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;isSecondLockSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;acquireLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutexKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isSecondLockSet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not get a second lock which is as expected this is where you would force the request out."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Second Lock acquired! This should not happen :)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Simulate some work by sleeping and try to acquire the lock again to see that it fails&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;isThirdLockSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;acquireLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutexKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isThirdLockSet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Still could not get the third lock since the first lock is still set."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Third Lock acquired! This should not happen :)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Release the lock&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;releaseLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutexKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error releasing lock:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First lock released!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Try to acquire the lock again to show it has been released&lt;/span&gt;
    &lt;span class="n"&gt;isForthLockSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;acquireLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutexKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isForthLockSet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to acquire lock"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Forth Lock acquired!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Release the forth lock&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;releaseLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutexKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error releasing lock:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Forth Lock released!"&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;This when ran will print out the following in the terminal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;First lock acquired!
Could not get a second lock which is as expected this is where you would force the request out.
Still could not get the third lock since the first lock is still set.
First lock released!
Forth Lock acquired!
Forth Lock released!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Taking a look at what the code above does
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;acquireLock&lt;/code&gt; function attempts to acquire a lock on a Redis key using the &lt;code&gt;SET&lt;/code&gt; command with the &lt;code&gt;NX&lt;/code&gt; option. The NX option tells Redis to only set the key if it does not already exist, effectively creating a lock if our key &lt;code&gt;my_lock&lt;/code&gt; does not already exist. We set the value to &lt;code&gt;lock&lt;/code&gt; but this can really be anything. &lt;/p&gt;

&lt;p&gt;This becomes more obvious as we try to acquire a second and third lock which shown above fails since the &lt;code&gt;acquireLock&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;releaseLock&lt;/code&gt; function uses the &lt;code&gt;DEL&lt;/code&gt; command to release the lock once done. The &lt;code&gt;DEL&lt;/code&gt; command deletes the &lt;code&gt;my_lock&lt;/code&gt; key that was created to acquire the lock, effectively releasing the lock.&lt;/p&gt;

&lt;p&gt;An important note something that this example highlights is Redis will not raise an exception when doing this so it is up to the application layer to ensure the logic of the locks are applied. Also, it is good to put a default "time to live" on the key to ensure if the application crashes it won't leave the key set forever (in this example we put the &lt;code&gt;expiration&lt;/code&gt; time as 10 seconds). &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Mutex is an important concept for coordinating access to shared resources in distributed systems and Redis provides a simple, fast and effective way to implement distributed locks. &lt;/p&gt;

&lt;p&gt;We have explored how to use Redis to implement distributed locks in Go which can be a great combination if you need a small and quick way to restrict access to certain resources/business flows. &lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Building an Arduino set up To Play Dino Run: A Fun and Engaging Way to Learn Electronics and Programming</title>
      <dc:creator>Jeffrey Boisvert</dc:creator>
      <pubDate>Wed, 22 Feb 2023 03:24:05 +0000</pubDate>
      <link>https://dev.to/jdvert/building-an-arduino-set-up-to-play-dino-run-a-fun-and-engaging-way-to-learn-electronics-and-programming-j6a</link>
      <guid>https://dev.to/jdvert/building-an-arduino-set-up-to-play-dino-run-a-fun-and-engaging-way-to-learn-electronics-and-programming-j6a</guid>
      <description>&lt;p&gt;The Dino Run game is one of Google's the most popular games around, and it's no surprise why: it's simple, engaging, and addictive. But you know what is even more fun? Getting an Arduino to do it for you. In this blog post, we'll explore how to do just that using the open-source code available on &lt;a href="https://github.com/jdboisvert/arduino-t-rex-run" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an Arduino?
&lt;/h2&gt;

&lt;p&gt;Arduino is an open-source platform for building electronic devices and projects. It consists of a physical programmable board and an Integrated Development Environment (IDE) that allows you to write code and upload it to the board. Arduino boards are commonly used in a wide range of projects, including robotics, home automation, and Internet of Things (IoT) applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Dino Run?
&lt;/h2&gt;

&lt;p&gt;Dino Run is a popular game that is built into the Google Chrome browser. It's a simple game where the player controls a T-Rex who must jump over obstacles to avoid crashing. The game is played by pressing the space bar or up arrow to jump and the down arrow to duck. You can play the game by typing chrome://dino/ into your Chromium browser (ex: Chrome, Brave, etc.)&lt;/p&gt;

&lt;h2&gt;
  
  
  How to make an Arduino Play Dino Run Game
&lt;/h2&gt;

&lt;p&gt;If you want to keep it simple feel free to pull down the code I wrote &lt;a href="https://github.com/jdboisvert/arduino-t-rex-run" rel="noopener noreferrer"&gt;here&lt;/a&gt; which also shows how to get set up. &lt;/p&gt;

&lt;p&gt;You will need the following: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Arduino UNO (or really any Arduino should be fine)&lt;/li&gt;
&lt;li&gt;Two Servos (one to be tapped onto the keyboard key to jump and one to be tapped onto the keyboard key to duck).&lt;/li&gt;
&lt;li&gt;One Light Sensor (Photoresistor/LDR) which will be used to detect objects to know when to jump or duck&lt;/li&gt;
&lt;li&gt;Eight wires&lt;/li&gt;
&lt;li&gt;One 10k OHM resistor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is how you will will set everything up&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpe9kd2ztdxhx6gyu9480.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpe9kd2ztdxhx6gyu9480.png" alt="Diagram showing how to wire up the servos, Arduino and sensors" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have everything hooked up ensure to tape the jump servo to the up arrow key and the down servo to the down arrow key (note the code in my project will always be pressing either key and will always default to ducking aka the down key). &lt;/p&gt;

&lt;p&gt;Tape the light sensor (used to know when to jump) on the computer's screen (about 1/4 from the left hand side this can vary). The position is important since if it detects an object too late our little Dino won't have time to jump and if it detects it too soon it will have jumped into the object. &lt;/p&gt;

&lt;p&gt;Open up your &lt;a href="https://www.arduino.cc/en/software" rel="noopener noreferrer"&gt;Arduino IDE&lt;/a&gt; and paste in the following code (feel free to look at the comments left if you want to better understand the code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**+---
 * Used to play Google's Dino run game 
 * with just servos and a LDR sensor. 
 */&lt;/span&gt;

&lt;span class="c1"&gt;// This is just importing the Servo object you will need. &lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Servo.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="c1"&gt;// You need to declare you Servo &lt;/span&gt;
&lt;span class="c1"&gt;// objects here to be able to attach them later on. &lt;/span&gt;
&lt;span class="n"&gt;Servo&lt;/span&gt; &lt;span class="n"&gt;jumpServo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="n"&gt;Servo&lt;/span&gt; &lt;span class="n"&gt;duckServo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

&lt;span class="c1"&gt;//Pins&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;JUMP_SERVO_PIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// TODO Change to your pin&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;DUCK_SERVO_PIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// TODO Change to your pin &lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;JUMP_SENSOR_PIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// TODO Change to your pin &lt;/span&gt;

&lt;span class="c1"&gt;//Servo positions&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;JUMP_OFF_POSITION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Change this based on how your servos are set up &lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;JUMP_ON_POSITION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Change this based on how your servos are set up &lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;DUCK_OFF_POSITION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;105&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Change this based on how your servos are set up &lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;DUCK_ON_POSITION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Change this based on how your servos are set up &lt;/span&gt;

&lt;span class="c1"&gt;//Sensor object values&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;MIN_OBJECT_RANGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;532&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Change based on your screen&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;MAX_OBJECT_RANGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;695&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Change based on your screen&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;jumpSensorValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TIME_BETWEEN_JUMP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;290&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Change based on your placement&lt;/span&gt;

&lt;span class="c1"&gt;// This runs upon turning on the Arduino and is usually where &lt;/span&gt;
&lt;span class="c1"&gt;// you tell the Arduino where certain electronics are and here &lt;/span&gt;
&lt;span class="c1"&gt;// we ensure the servos are in the "start position" meaning to &lt;/span&gt;
&lt;span class="c1"&gt;// always duck. &lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;jumpServo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JUMP_SERVO_PIN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="n"&gt;duckServo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DUCK_SERVO_PIN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="n"&gt;startPosition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// This will run forever as the Arduino is on.&lt;/span&gt;
&lt;span class="c1"&gt;// This is where the actual game is happening and essentially &lt;/span&gt;
&lt;span class="c1"&gt;// our Light Sensor is going to return a number based on how &lt;/span&gt;
&lt;span class="c1"&gt;// much light is present (meaning a darker object returns a &lt;/span&gt;
&lt;span class="c1"&gt;// higher number and a lighter object returns a lower number). &lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;jumpSensorValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;analogRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JUMP_SENSOR_PIN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jumpSensorValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Just for testing purposes and how you will know what your sensor returns all sensors can be different :) &lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jumpSensorValue&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MIN_OBJECT_RANGE&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;jumpSensorValue&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MAX_OBJECT_RANGE&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="c1"&gt;// It is always good to have a range since as the object will move across the screen the number can change and there will be a delay from when the servo pressing the key and when the Arduino runs the code to do it. &lt;/span&gt;
    &lt;span class="n"&gt;jump&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;duck&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Put both keys off the trigger
 */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;startPosition&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="n"&gt;jumpServo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JUMP_OFF_POSITION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="n"&gt;duckServo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DUCK_OFF_POSITION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Used to press the key to jump
 */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;jump&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="n"&gt;duckServo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DUCK_OFF_POSITION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="n"&gt;jumpServo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JUMP_ON_POSITION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TIME_BETWEEN_JUMP&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="n"&gt;jumpServo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JUMP_OFF_POSITION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Used to press the key to duck 
 */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;duck&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="n"&gt;duckServo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DUCK_ON_POSITION&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;You may need to play around with the variables I indicated with some &lt;code&gt;TODO&lt;/code&gt; but once all is set up give it a try and hopefully you will end up with something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem3kp1kmf571ns1gpdzp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem3kp1kmf571ns1gpdzp.gif" alt="An animated gif showing the set up of the author's Arduino plying Dino run" width="80" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Making an Arduino play Dino Run for you is a fun and engaging way to learn about electronics and programming. It's a great project for beginners who want to learn about Arduino and electronics. By following the instructions on GitHub and using the Dino Run code provided, you can build your own Dino Run set up to play the game in no time. &lt;/p&gt;

&lt;p&gt;My best score was 1393 can you beat it? Let me know in the comments below!&lt;/p&gt;

</description>
      <category>infosec</category>
      <category>cybersecurity</category>
      <category>hardware</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Account Balance Masking - Because Why Not Have More Privacy?</title>
      <dc:creator>Jeffrey Boisvert</dc:creator>
      <pubDate>Wed, 23 Nov 2022 02:41:12 +0000</pubDate>
      <link>https://dev.to/jdvert/account-balance-masking-because-why-not-have-a-bit-more-privacy-3m30</link>
      <guid>https://dev.to/jdvert/account-balance-masking-because-why-not-have-a-bit-more-privacy-3m30</guid>
      <description>&lt;p&gt;No one likes it when someone is peeping over your shoulder. This is even more the case when you're logged into your personal banking with all your balance details on display. This is why I decided to build the &lt;a href="https://github.com/jdboisvert/account-balance-mask"&gt;Account Balance Masker&lt;/a&gt; Chrome Extension. &lt;/p&gt;

&lt;p&gt;I was quite surprised this was not something some financial sites offered out of the box. My goal with this app was to be able to set it and forget it and not have to worry about doing a lot of set up in order for me to just mask my account balances. Also being a developer I was curious to see what it took to make a chrome extension myself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aYBZxfp5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vcutj48u673quzxngf3c.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aYBZxfp5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vcutj48u673quzxngf3c.gif" alt="A gif displaying how hovering over the masked values shows the original value" width="600" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  But how does it work?
&lt;/h3&gt;

&lt;p&gt;In the spirit of open source software the &lt;a href="https://github.com/jdboisvert/account-balance-mask/tree/main"&gt;code&lt;/a&gt; is available to review if you wish to dig into it more but this is a basic overview. &lt;/p&gt;

&lt;p&gt;All Chrome extensions are defined by a &lt;code&gt;manifest.json&lt;/code&gt; which contains information that defines it and my extension is no different. Within this extension we make use of the key &lt;code&gt;content_scripts&lt;/code&gt;. &lt;a href="https://developer.chrome.com/docs/extensions/mv3/content_scripts/"&gt;Content scripts&lt;/a&gt; is how we are able to run javascript files against specific webpages and ensure it only runs in these defined pages. With the help of wildcards this makes it easy to, for example, specify scripts to only one for 1 banking webpage. &lt;/p&gt;

&lt;p&gt;Within these javascript files one simply needs to search the DOM and update the elements containing the account balance details. As you can imagine this can vary from site to site but making use of &lt;code&gt;document.querySelectorAll&lt;/code&gt; we are able to search through elements using wildcards as well and test if the text contains digits. With the help of the browser's developer tools one can easily find the HTML tag's class and/or id of the element and update the script as needed. &lt;/p&gt;

&lt;p&gt;Since this is only loaded once the page loads or whenever navigating around the webpage there is no need to store any data except within the script. You can also always view the details since the initial value is within a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures"&gt;closure&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Final thoughts
&lt;/h3&gt;

&lt;p&gt;I hope others will find the simple extension useful and I hope to implement other features and webpages to help your get more privacy from those people wanting to peep at your screen. &lt;/p&gt;

&lt;h4&gt;
  
  
  Update
&lt;/h4&gt;

&lt;p&gt;Want to give it a try? You can download it on &lt;a href="https://chrome.google.com/webstore/detail/account-balance-masker/hkmponiffpfpnjbgddecfhneaajddfff?hl=en&amp;amp;authuser=2"&gt;The Chrome Web Store&lt;/a&gt; now!&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>showdev</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Crash Course on Git Commands</title>
      <dc:creator>Jeffrey Boisvert</dc:creator>
      <pubDate>Thu, 29 Sep 2022 00:14:34 +0000</pubDate>
      <link>https://dev.to/jdvert/crash-course-on-git-commands-3dil</link>
      <guid>https://dev.to/jdvert/crash-course-on-git-commands-3dil</guid>
      <description>&lt;p&gt;Welcome to the wonderful word of git where a &lt;code&gt;-f&lt;/code&gt; during a &lt;code&gt;git push&lt;/code&gt; can cause you so much pain. If you are working as a Software Developer you most likely have to use &lt;code&gt;git&lt;/code&gt; at some point. &lt;a href="https://git-scm.com/"&gt;Git&lt;/a&gt; is a version control system that many teams use to collaborate and build software together. &lt;/p&gt;

&lt;p&gt;In this article I am going to go over what I think are essential git commands you will need to know to get started. &lt;br&gt;
There is more documentation available all for free &lt;a href="https://git-scm.com/doc"&gt;here&lt;/a&gt; and I recommend giving it a read whenever trying out new commands. &lt;a href="https://www.atlassian.com/git/tutorials/learn-git-with-bitbucket-cloud"&gt;Atlassian&lt;/a&gt; has some great easy to digest tutorials as well.  &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone your-repository-path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the command you use to download your remote repository. This can be done via ssh or https. &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout branch 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git uses branches to store work and with this command you will will be able to change your local files to match a specified branch.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout -b new-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creates a new branch from the current branch you are working in. If you wish to specify a specific branch to branch from, you can use the &lt;code&gt;u&lt;/code&gt; flag. Example: &lt;code&gt;git checkout -b new-branch -u origin/main&lt;/code&gt;. &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will simply give the current state of your branch. It will display what changes are staged for a commit, which are not and what is not tracked currently by git. If you are pointing to a specific branch it can also tell how far ahead/behind you are from the the given branch. &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git pull 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this if you wish to "pull" in code from remote repository's branch into your current local branch. By default this will perform a merge commit. If you have conflicts git will be sure to flag them. Once resolved you can just &lt;code&gt;git add&lt;/code&gt; and &lt;code&gt;git commit&lt;/code&gt; to complete the pull. &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git pull --rebase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to the above command but instead of doing a merge commit this will attempt to rebase the commits from the remote branch on top of the local commits in your branch. If you are not sure which to use a merge commit tends to be less dangerous/easier to deal with than a rebase which acts a lot like a more automated/fancy cherry picking. &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git cherry-pick commit-hash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the note of cherry picking, if you wish to just grab a single commit and put it on top of your current branch this is where cherry-pick is useful. Give it a commit hash and it will add that commit to your branch. &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git fetch --all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this command to make sure your local machine is up to date with the remote repository. Try to do this as often as you can to be sure your local branches that you checkout / branch from are up to date. A &lt;code&gt;git status&lt;/code&gt; is always helpful to know if you are not in sync with the remote repository. &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git stash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will save your local modifications and reverts the working directory to match the HEAD commit. This can be useful if you do not wish to commit the changes but want to go back to a clean state to checkout another branch. When you wish to get back the stashed changes simply do &lt;code&gt;git stash pop&lt;/code&gt;. &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git branch -u origin/branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes you may want to change the branch your branch is pointing to/comparing to. This command will let you do this and when you run &lt;code&gt;git status&lt;/code&gt; it will tell you if you are ahead or behind. &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add filename
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add a new/deleted/modified file which to be staged to then be committed. If you wish to add all changes from your current directory just run &lt;code&gt;git add .&lt;/code&gt;.  &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default this will show you what changes have been done compared to the previous commit. However this command can even be used to compare specific files between commits and branches. This is useful to see what changes you have before committing.  &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git commit -m "My commit message."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An essential classic. This will create a commit with everything that has been staged after running &lt;code&gt;git add&lt;/code&gt;. The &lt;code&gt;-m&lt;/code&gt; allows you to add a message to explain the changes. It is good practice to give as much details as you can but every project/organization tends to have their own policy on commit messages. &lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git push origin branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this command once your branch's commits are ready to be pushed to the remote repository. It is worth noting &lt;code&gt;origin&lt;/code&gt; is just the default alias name for your remote repository but it can be called anything. &lt;/p&gt;

&lt;p&gt;Earlier I mentioned not to use &lt;code&gt;-f&lt;/code&gt; with &lt;code&gt;git push&lt;/code&gt;. This is because &lt;code&gt;-f&lt;/code&gt; stands for "force" and when used will override the remote repository's branch with what is being pushed. Be careful and avoid &lt;code&gt;-f&lt;/code&gt; as much as possible. &lt;/p&gt;




&lt;p&gt;Hopefully these commands help you and this small tutorial is useful in your quest to better understand git. Did I miss any commands you think are essential? Let me know but otherwise happy coding! &lt;/p&gt;

</description>
      <category>git</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How do you revert a newly applied Django migration?</title>
      <dc:creator>Jeffrey Boisvert</dc:creator>
      <pubDate>Fri, 23 Sep 2022 01:54:25 +0000</pubDate>
      <link>https://dev.to/jdvert/how-do-you-revert-a-newly-applied-django-migration-2din</link>
      <guid>https://dev.to/jdvert/how-do-you-revert-a-newly-applied-django-migration-2din</guid>
      <description>&lt;p&gt;So you created a new migration and applied it to your database however after some testing you realize it still needs some work and you need to revert it. With Django this is pretty simple with some built in commands &lt;/p&gt;

&lt;h2&gt;
  
  
  The Traditional Way
&lt;/h2&gt;

&lt;p&gt;For this example we will pretend we have a &lt;code&gt;polls&lt;/code&gt; app and this is the app we wish to manage our migrations in. Django has a lot of great &lt;a href="https://docs.djangoproject.com/en/4.1/topics/migrations/"&gt;documentation&lt;/a&gt; on this topic as well.&lt;/p&gt;

&lt;p&gt;Let's first check out which what our current migrations look like using the &lt;code&gt;showmigrations&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py showmigrations polls
polls
 [X] 0001_initial
 [X] 0002_question1
 [X] 0003_person
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The previous migration that was applied is &lt;code&gt;0002_question1&lt;/code&gt; so we simply need to tell Django to migrate back to that. This can be done by the &lt;code&gt;migrate&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py migrate polls 0002_question1 
Operations to perform:
  Target specific migration: 0002_question1, from polls
Running migrations:
  Rendering model states... DONE
  Unapplying polls.0003_person... OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our database should now have reverted back to the state in migration &lt;code&gt;0002_question1&lt;/code&gt; and we can verify this by using &lt;code&gt;showmigrations&lt;/code&gt; again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py showmigrations polls
polls
 [X] 0001_initial
 [X] 0002_question1
 [ ] 0003_person
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to reset the whole database as well you can use &lt;code&gt;python manage.py migrate polls zero&lt;/code&gt; which will revert all the way back to &lt;code&gt;0001_initial&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  But how do I just go back to a previous migration without checking what it is?
&lt;/h2&gt;

&lt;p&gt;And this question is something I asked myself as well. The Django framework does not provide this functionality out of the box. &lt;/p&gt;

&lt;p&gt;Let's say you commit migrations to you git repository and are a small team sharing a few servers for other teams to test with. A problem you may run into is different teams having different migrations which are not present in the git main branch. This can lead to some frustrating issues during a deployment and can cause a database to get out of sync with the migration files very quickly (especially if the database is not rebuilt every time). &lt;/p&gt;

&lt;p&gt;This is why I made the &lt;a href="https://pypi.org/project/django-migration-rollback/"&gt;django-migration-rollback&lt;/a&gt; app. With some custom Django commands one can now easily migrate back to a previous migration and even sync with a specific branch in a repository quite easily without having to manually check what the migrations are. &lt;/p&gt;

&lt;p&gt;In our previous example, instead of having to do &lt;code&gt;showmigrations&lt;/code&gt; and manually input the migration in &lt;code&gt;migrate&lt;/code&gt; you can simply use &lt;code&gt;migrateprevious&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py migrateprevious polls
Attempting to go back to rollback polls to previous migration
Operations to perform:
    Target specific migration: 0002_question1, from polls
Running migrations:
    Rendering model states...
DONE
    Unapplying polls.0003_person...
OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if I had deployed my changes to a test server and had forgotten to rollback the migration the deployment sequence can simply use &lt;code&gt;migraterollback&lt;/code&gt; before deploying the next branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py migraterollback polls main
Attempting to go back to rollback polls to latest migration on branch main
Operations to perform:
    Target specific migration: 0002_question1, from polls
Running migrations:
    Rendering model states...
DONE
    Unapplying polls.0003_person...
OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However just note this does require a .git file present to be able to call the git commands. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope you found this article useful and if you wish to check out the package I talked about it is open source and the repository can be found &lt;a href="https://github.com/jdboisvert/django-migration-rollback"&gt;here&lt;/a&gt;. Happy coding!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>django</category>
      <category>python</category>
      <category>git</category>
    </item>
    <item>
      <title>Working at a Startup as a Developer</title>
      <dc:creator>Jeffrey Boisvert</dc:creator>
      <pubDate>Tue, 28 Sep 2021 02:46:08 +0000</pubDate>
      <link>https://dev.to/jdvert/working-at-a-startup-as-a-developer-3df4</link>
      <guid>https://dev.to/jdvert/working-at-a-startup-as-a-developer-3df4</guid>
      <description>&lt;p&gt;You are job hunting and stumble on several postings for companies you never really heard of before. You then find out one is a startup. You think their product is interesting but are unsure if you should apply. Well, I will discuss why I think it is always a great option to try. &lt;/p&gt;

&lt;p&gt;To give some context I have worked in small to medium size companies working in various IT-related roles. &lt;/p&gt;




&lt;h2&gt;
  
  
  What I Have Liked
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creative Freedom
&lt;/h3&gt;

&lt;p&gt;I think this is a big one you will see in many other posts but since a startup is moving fast and everyone usually is quite busy managing several different things at once you end up being given the freedom to build and implement things &lt;em&gt;how you want&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;What this means is you may be given a problem to solve and if you have an idea of how to solve it and others agree with the solution (and you have the budget for it) you more than likely get to do it your way. Being able to lead projects and own the feature you develop can be quite exiting. You have the chance to work on something you are truly passionate about which makes the creative freedom even better. &lt;/p&gt;

&lt;h3&gt;
  
  
  Learning
&lt;/h3&gt;

&lt;p&gt;I personally love learning be it new technology, new processes, best business practices, etc. At a startup, you are always on your toes and usually will have to learn very quickly since may be the first to solve a given problem within the company. Working in cross-functional teams will also let you have a better understanding of things outside of your everyday work. &lt;/p&gt;

&lt;p&gt;For example, you may be tasked with automating some reporting and as you gather the requirements for the project you will learn about how there are certain regulations you must adhere to and ensure are part of the reporting process. Sometimes you my need to even learn things that are not related to your role like how to interview people or estimating feature points for a sprint. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Team
&lt;/h3&gt;

&lt;p&gt;By working in a small cross-functional team you end up getting to know your co-workers better than you would in a larger more focused team. What I like about this is you get learn everyone's strengths and weaknesses which helps everyone move forward. This is a big reason why I like working at startups since the team dynamic makes me feel like &lt;strong&gt;I am not doing a job but rather working on something I am passionate about with other like-minded people.&lt;/strong&gt; &lt;/p&gt;




&lt;h2&gt;
  
  
  Things to Consider
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Startups move FAST
&lt;/h3&gt;

&lt;p&gt;By fast I mean really fast. You may work on something one day and end up never working on it again to start a new project the next day. For some this is exciting but for others, this can be somewhat disorienting. Having to switch context often can be hard however is a great skill to improve on since you end up learning time management skills and how to handle obstacles like feature creep. &lt;/p&gt;

&lt;h3&gt;
  
  
  Work-Life Balance
&lt;/h3&gt;

&lt;p&gt;Startups attract go-getters and are a group of usually like-minded people who believe in the product and where it is going. So, naturally you end up having to deal with quite tight deadlines to reach your company's goals. This can sometimes make you feel like you work a lot more than what you are use to. &lt;/p&gt;

&lt;p&gt;However, what is important is that you care about what you are working on and making sure you still take time to enjoy your personal life. Putting in the long hours to then reap rewards later down the road can be hard and equally rewarding but ensuring proper work and life balance is also important. This will greatly vary at every startup so it is always worth asking about during an interview.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Startups are a great way to not only accelerate your career with creative freedom, chances to learn, and work with like-minded people but are also not for everyone (and that is okay). I personally have really enjoyed my experiences working at startups however what I can say for sure is they were all different. So even if you or someone else did not enjoy a past experience I still think it is worth trying another one. I hope this helped you get a better idea of what to expect working at a startup so you can take that leap and see for yourself. &lt;/p&gt;

</description>
      <category>startup</category>
      <category>career</category>
      <category>culture</category>
    </item>
    <item>
      <title>My Experience Developing for a Non-Profit</title>
      <dc:creator>Jeffrey Boisvert</dc:creator>
      <pubDate>Wed, 02 Dec 2020 03:02:19 +0000</pubDate>
      <link>https://dev.to/jdvert/how-developing-an-app-for-a-non-profit-is-a-great-side-project-idea-2p0b</link>
      <guid>https://dev.to/jdvert/how-developing-an-app-for-a-non-profit-is-a-great-side-project-idea-2p0b</guid>
      <description>&lt;p&gt;Has someone told you that “You should really start doing side projects to build your portfolio” and deep down you really want to but are not sure how to go about it? I think we all want to build something useful and when we start learning about software development we just have this desire to build something. However, as we sit there rattling our brains we cannot seem to come up with an idea and end up stuck in &lt;a href="https://www.google.com/search?q=tutorial+hell&amp;amp;oq=tutorial+hell&amp;amp;aqs=chrome..69i57.2071j0j1&amp;amp;sourceid=chrome&amp;amp;ie=UTF-8" rel="noopener noreferrer"&gt;tutorial hell&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My friend and I got an email about a non-profit organization called &lt;a href="https://www.raisondart.org/about" rel="noopener noreferrer"&gt;Raison d’art&lt;/a&gt; who was looking for volunteers to help develop an internal tool to help them manage their image assets in their S3 bucket. A key thing that drew us to this project was that the organization wanted to make the project open source and the thought of starting an open-source project seemed really exciting to us! We had never used S3 before but we both had this desire to build something and thought this was a great opportunity to test our problem-solving skills while also helping others. So we decided to go ahead and start the project. &lt;/p&gt;

&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%2F3qz76o35jsxd224d852obgtp-wpengine.netdna-ssl.com%2Fwp-content%2Fuploads%2F2020%2F03%2Fgiphy-3.gif" 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%2F3qz76o35jsxd224d852obgtp-wpengine.netdna-ssl.com%2Fwp-content%2Fuploads%2F2020%2F03%2Fgiphy-3.gif" alt="Let's do this gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you do not know about &lt;a href="https://aws.amazon.com/" rel="noopener noreferrer"&gt;Amazon Web Services (AWS)&lt;/a&gt; or what S3 is here is a quick summary. AWS is a platform that supports &lt;strong&gt;many&lt;/strong&gt; secure cloud services for just about anyone to use. As you probably guessed, S3 is one of these services. It is primarily used as storage on the cloud for just about anything. You can read more about it on &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/Welcome.html" rel="noopener noreferrer"&gt;AWS’s S3 documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Since this project was during the start of COVID-19 pandemic, everyone in the team only met virtually. During the first meet up in June 2020, we discussed what exactly we wanted to develop and how it would solve the problem Raison d’art was having. We decided to build a React application since both my friend and I knew Javascript and were both somewhat familiar with React. &lt;/p&gt;

&lt;p&gt;I remember being unsure since we did not know if the design decisions we would make were the best ones. There is a lot of &lt;strong&gt;imposter syndrome&lt;/strong&gt; when you try developing any bigger feature in a project and this was no exception. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I think the best way to overcome imposter syndrome is to just try something out and then share your ideas with others to get feedback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This way you have something to show and learn from while building momentum in the project. We would meet every two weeks to discuss the key features we wanted to tackle in the coming weeks and where we were at in the project. It was great to see how the project kept growing every time we met and to hear others' feedback. I think something I would suggest to others is to write down the problem you are trying to solve and break it down into smaller high-level parts. This way you have something to refer back to you while you are developing.&lt;/p&gt;

&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%2Fmedia3.giphy.com%2Fmedia%2FUqZ4imFIoljlr5O2sM%2Fgiphy.gif" 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%2Fmedia3.giphy.com%2Fmedia%2FUqZ4imFIoljlr5O2sM%2Fgiphy.gif" alt="Just do it gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Something we started learning quickly about AWS was just how much there was to read. The sheer amount of documentation AWS has about their products can be overwhelming. &lt;strong&gt;But you should 100% read this documentation&lt;/strong&gt;. This is how we discovered that &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/user-guide/add-object-tags.html" rel="noopener noreferrer"&gt;Tags&lt;/a&gt; were better suited for the organization’s assets instead of using &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html" rel="noopener noreferrer"&gt;Metadata&lt;/a&gt; (which was the original plan). Since an object’s metadata is tied to the object in the bucket it means if you wish to edit this piece of information you need to re-upload the object every time you edit just a single piece of metadata. In layman's terms, there was a lot of overhead to edit just a single piece of data (since the metadata cannot be edited after the initial upload so you have to replace the existing object). However, tags are not stored with the object so if you wish to just modify a single tag of an object you can simply edit that tag without having to reupload the whole object. Check out the documentation about &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html" rel="noopener noreferrer"&gt;metadata&lt;/a&gt; versus &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/object-tagging.html" rel="noopener noreferrer"&gt;tags&lt;/a&gt; for reference. &lt;/p&gt;

&lt;p&gt;Another feature we wanted to develop was to make it possible for several objects to have similar tags and defaults without having to manually add each one. S3 has no sort of inforced structure when it comes to tags and is very similar to NoSQL databases (more specifically a Key-value database) meaning as long as a unique key is provided the value can be anything. So this where the idea of the schema file came from. We essentially uploaded in a folder a JSON file we called &lt;code&gt;bucket-buddy-schema.json&lt;/code&gt; (an example of the contents can be seen below) which would store what tags should be applied to each object in a given folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
   {
      "key":"Name",
      "Value":"default name",
      "type":"text"
   },
   {
      "key":"Age",
      "value":"23",
      "type":"number"
   },
   {
      "key":"DateOfBirth",
      "value":"2020-09-30",
      "type":"date"
   },
   {
      "key":"Validated",
      "value":false,
      "type":"flag"
   }
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file specifies the key (the tag name), value (the default value for the tag), and the type (which could be Numeric, Text, Date, or Flag) that each object should contain, and when viewing an object we clearly identify that these are the values needed to be added to follow the schema in a given folder.  &lt;/p&gt;

&lt;p&gt;As the project was coming to an end and we began polishing the app. A key thing we wanted to do from the start was to build an app for developers and non-developers could use and this meant trying to build a README.md that was as easy to understand. We even added gifs showing all the features and a brief explanation on how to get started. Something I think is often overlooked in documentation is examples. As the saying goes, “A picture is worth a thousand words.” &lt;/p&gt;

&lt;p&gt;We even got the app set up so it can be wrapped as an &lt;a href="https://www.electronjs.org/" rel="noopener noreferrer"&gt;Electron app&lt;/a&gt; so our React application could behave like an actual desktop app! Once everything was ready we were invited to a local meet-up called &lt;a href="https://js-montreal.org/about.html" rel="noopener noreferrer"&gt;JS-Montreal&lt;/a&gt; to present our project and get feedback. It was a lot of fun to present our project to other developers in the community and get their feedback and it really showed how supporting the development community can be. &lt;/p&gt;

&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%2Fmedia1.tenor.com%2Fimages%2Ffc51042e9fb2a5aff907115b7175617b%2Ftenor.gif%3Fitemid%3D11112770" 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%2Fmedia1.tenor.com%2Fimages%2Ffc51042e9fb2a5aff907115b7175617b%2Ftenor.gif%3Fitemid%3D11112770" alt="Lets wrap this up gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If I learned anything from starting an open-source project from scratch is this, you learn more than just how to code. I discovered how to be creative while helping others and it felt so good to be able to solve a problem when I had trouble coming up with an idea for my own project. Knowing your weaknesses can be one of your greatest strengths! There are so many problems non-profit organizations and people are facing on a daily basis so if you are unsure on what project you should do and work on early in your career try to ask and listen to those around you. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At the end of the day being a developer means being a problem solver and being able to interpret someone’s problem so you can then design and present a solution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not only will you feel like you are building something useful that will help others reach their goals but you will be able to build that portfolio that everyone keeps suggesting you should have. Making a product open source makes it so you are not just helping the original person having the problem, but others too and gives the project the chance to turn into something you never thought it could be (and that thought is truly exciting). &lt;/p&gt;

&lt;p&gt;Thank you for reading this blog post and if you want to check out the Bucket Buddy repository I talked about in this post please check out the &lt;a href="https://github.com/Davescat/bucketbuddy" rel="noopener noreferrer"&gt;GitHub repository here&lt;/a&gt;! Happy coding! &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
