<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Kevin</title>
    <description>The latest articles on Forem by Kevin (@idontremember).</description>
    <link>https://forem.com/idontremember</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%2F538543%2F148728a8-f9ac-477f-9d93-7ce4efff1173.jpeg</url>
      <title>Forem: Kevin</title>
      <link>https://forem.com/idontremember</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/idontremember"/>
    <language>en</language>
    <item>
      <title>Automate Recurring Venmo Payments with Github Actions and IFTTT</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Wed, 04 May 2022 04:00:00 +0000</pubDate>
      <link>https://forem.com/idontremember/automate-recurring-venmo-payments-with-github-actions-and-ifttt-28dl</link>
      <guid>https://forem.com/idontremember/automate-recurring-venmo-payments-with-github-actions-and-ifttt-28dl</guid>
      <description>&lt;p&gt;Like so many tinkerers, I love to &lt;a href="https://xkcd.com/1319/"&gt;automate small parts of my life, while ignoring any actual work&lt;/a&gt; I should be doing. After stumbling on &lt;a href="https://joeprevite.com/send-automatic-recurring-payments-on-venmo"&gt;Joe’s Venmo automation&lt;/a&gt;, I immediately swept all the important things off my plate to have some fun. Now let’s set up automatic payments on &lt;a href="https://venmo.com/"&gt;Venmo&lt;/a&gt; in less than an hour — with email notifications for peace of mind!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rss-OmcN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6mbe9cifwekfbszi0uoe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rss-OmcN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6mbe9cifwekfbszi0uoe.png" alt="Clock + robot snake + Venmo" width="706" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not Nocode (Zapier, IFTTT, etc.)
&lt;/h2&gt;

&lt;p&gt;You’ll need to know how to code a small amount for this guide. This setup is an alternative to the Zapier Venmo integration and IFTTT Venmo integration, which don’t seem to exist anymore (if they ever did). Venmo shut down their developer APIs and &lt;a href="https://venmo.com/developers"&gt;have had them in maintenance mode since 2016&lt;/a&gt;, so unlikely for anything to change on that front. Normally I look to &lt;a href="https://www.nocode.tech/"&gt;nocode tools&lt;/a&gt; first for projects, but unfortunately in this case, you’ll need to know how to run code online.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ingredients
&lt;/h2&gt;

&lt;p&gt;The 3 pieces you will need to set this up (in &amp;lt;1 hour):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A scheduled trigger (GitHub actions, AWS Cloudwatch, etc.)&lt;/li&gt;
&lt;li&gt;A place to run a Python script + it’s dependency (GitHub actions, PythonAnywhere, Node.js with &lt;a href="https://autocode.com/"&gt;Autocode&lt;/a&gt; is in exploration)&lt;/li&gt;
&lt;li&gt;A free or cheap notification service (IFTTT, AWS SNS, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use &lt;a href="https://github.com/features/actions"&gt;GitHub Actions&lt;/a&gt; + Python script + &lt;a href="https://ifttt.com/"&gt;IFTTT&lt;/a&gt; for sending emails, but feel free to step off the beaten path if you have other ideas.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to set up automatic payments on Venmo
&lt;/h2&gt;

&lt;p&gt;Diving into the meat &amp;amp; potatoes you came for, how to actually get this dang thing running.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(Optional) 💌 Set up IFTTT notifications&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ &lt;em&gt;If you don’t care about being notified, skip this step and remove the &lt;code&gt;notify()&lt;/code&gt; code in script.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OK413yBQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6gb551m6gpxyxj31ytk2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OK413yBQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6gb551m6gpxyxj31ytk2.png" alt="Example of the IFTTT notification email" width="623" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;IFTTT allows a &lt;a href="https://ifttt.com/explore/services"&gt;variety of target services&lt;/a&gt;, I originally used SMS, then converted to email since it’s not an urgent alert.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sign up for IFTTT account (might require downloading of the app).&lt;/li&gt;
&lt;li&gt;Configure the &lt;a href="https://help.ifttt.com/hc/en-us/articles/115010230347-Webhooks-service-FAQ"&gt;Webhooks service&lt;/a&gt; in IFTTT.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then set up your chosen notification service. My example script sends metadata about the Venmo request sent in the &lt;code&gt;value1&lt;/code&gt; field IFTTT provides. If you use the same, you'll have to make sure that is mapped correctly to your chosen notification service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SqeLMVPB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f5edtfmnde4ju0xhzag3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SqeLMVPB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f5edtfmnde4ju0xhzag3.png" alt="IFTT applet set-up screen" width="585" height="622"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;✅ Use cURL or your favorite web request tool to test your webhook and ensure notifications are sending correctly.&lt;/li&gt;
&lt;li&gt;Save your webhook URL as a Secret so the workflow can access it. In the browser, go to &lt;code&gt;GitHub Repo -&amp;gt; Settings -&amp;gt; Secrets -&amp;gt; New Repository Secret&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;🔐 &lt;strong&gt;Get Venmo access token&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️🚨&lt;br&gt;
&lt;em&gt;This token has the same power with your Venmo account as you do, so be very careful with it! It will never expire unless you logout manually, as &lt;a href="https://github.com/mmohades/Venmo#usage"&gt;explained in the docs&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I plan to update this guide to use &lt;a href="https://venmo.com/paymentlinks"&gt;Payment Links&lt;/a&gt; (a &lt;a href="https://en.wikipedia.org/wiki/Deep_linking"&gt;deeplink&lt;/a&gt; into the Venmo app that doesn’t require account access) in the near future, but for now, you are forewarned.&lt;/em&gt;&lt;br&gt;
⚠️🚨&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You’ll have to be comfortable working with Python dependencies to run these scripts locally.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the &lt;a href="https://github.com/I-Dont-Remember/venmo/blob/master/main.py#L32"&gt;Venmo client library&lt;/a&gt; &lt;code&gt;venmo-api&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run this &lt;a href="https://github.com/I-Dont-Remember/venmo/blob/master/fetch_v_token.py"&gt;Python script&lt;/a&gt; to pull your access token. You may need to handle MFA.
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from venmo_api import Client
# https://github.com/mmohades/Venmo

print("Login using account to get an access token for the API.")
email = input('Venmo Email:')
if '@' not in email:
    print('! yo what the heck you sure thats an email')
    raise SystemExit
password = input('Venmo Password:')

Client.get_access_token(username=email, password=password)
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Save the token as the Secret &lt;code&gt;VENMO_ACCESS_TOKEN&lt;/code&gt; by going to &lt;code&gt;GitHub Repo -&amp;gt; Settings -&amp;gt; Secrets -&amp;gt; New Repository Secret&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(Optional) 🗺 Map Venmo usernames to IDs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then Venmo API requires payment requests to be tied to an ID, rather than the &lt;code&gt;@username&lt;/code&gt; we humans use in the app. I did the mapping of usernames during setup and &lt;a href="https://github.com/I-Dont-Remember/venmo/blob/f375c57ec30d668c4ce1c40403cd06c9be843f1d/main.py#L32"&gt;added them as environment secrets&lt;/a&gt;, but you can just as easily add 1 extra LOC to convert during regular runs.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user = venmo_client.user.get_user_by_username(username)
# Key is the 'user.id' part
venmo_client.payment.request_money(amount, note, target_user_id=user.id, privacy_setting=PaymentPrivacy.PRIVATE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;🐍 Modify the Python script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/I-Dont-Remember/venmo/blob/master/main.py#L32"&gt;The script I use&lt;/a&gt; has a simple entry point, then defines a separate function for each Venmo request I want it to make, YMMV. You’ll need to make some small adjustments based on how many requests you need it to make &amp;amp; how you fetch Venmo user IDs. The most important bits can be boiled down to this &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# main.py

import os
from venmo_api import Client, PaymentPrivacy
# requests is pulled in by venmo_api
import requests

TOKEN_KEY = 'VENMO_ACCESS_TOKEN'
IFTTT_WEBHOOK_KEY = 'IFTTT_WEBHOOK'

def main():
    # load the access token from environ
    access_token = os.environ.get(TOKEN_KEY)
    if access_token is None:
        raise ValueError('[!] EVERYTHING IS BROKEN WE CANT GET ACCESSS AHHHHHH')

    venmo_client = Client(access_token=access_token)

....
        # &amp;lt;venmo user id from mapping step&amp;gt;
        c_user_id = os.environ.get('C_USER_ID') 
        amount = 20.0
        note = f'Monthly Internet'
        try:
        venmo_client.payment.request_money(amount, note, target_user_id=c_user_id, privacy_setting=PaymentPrivacy.PRIVATE)
            print('Successfully requested for internet $$')
          msg = f'Requested ${amount} Internet from C.'
        except Exception as e:
            print(e)
            msg = '[!] Failed to request $$ for internet from C.'
        notify(msg)

....

def notify(msg):
    # notify with IFTTT webhook
    ifttt_webhook_url = os.environ.get(IFTTT_WEBHOOK_KEY)
    if ifttt_webhook_url is None:
        raise ValueError('[!] Dang, no webhook so cant notify myself :(')

    json_data = {
        'value1': msg
    }
    resp = requests.post(ifttt_webhook_url, json=json_data)
    print(resp.status_code)
    if resp.status_code &amp;gt;= 300:
        raise ValueError(f'Webhook resp was {resp.status_code}; not a success. Body: {resp.text}')

main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;👨‍💻 Configure GitHub Secrets &amp;amp; Actions&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;ℹ️ Cron actions get disabled after 60 days of no activity on the repository - so either plan to push a commit every 50 days, or follow my approach and just wait until GitHub emails you.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;✋ At this point you should have your &lt;code&gt;VENMO_ACCESS_TOKEN&lt;/code&gt; saved in Secrets, along with the optional &lt;code&gt;IFTTT_WEBHOOK&lt;/code&gt; or &lt;code&gt;USER_IDs&lt;/code&gt; values. if you didn’t, go back and do it.&lt;/p&gt;

&lt;p&gt;Now create your &lt;a href="https://github.com/I-Dont-Remember/venmo/blob/master/.github/workflows/recurring.yml"&gt;workflow definition&lt;/a&gt; at &lt;code&gt;.github/workflows/&amp;lt;workflow-name&amp;gt;.yml&lt;/code&gt; . This is where you will define your schedule in &lt;code&gt;cron&lt;/code&gt;. My example shows monthly Venmo requests.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# &amp;lt;workflow-name&amp;gt;.yml

name: Run Recurring Venmo

on:
  schedule:
    # 5/6pm EDT on the 10th of the month.
    - cron: "0 10 10 * *"
  workflow_dispatch:

jobs:
  run:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: Set up Python 3.7
        uses: actions/setup-python@v2
        with:
          python-version: 3.7
      - name: Install dependencies
        run: |
          pip install pipenv
          pipenv install
      - name: Run script 
        # https://pipenv.pypa.io/en/latest/advanced/#automatic-loading-of-env; this is somewhat silly but I wanted to finish quick
        run: |
          echo VENMO_ACCESS_TOKEN=$VENMO_ACCESS_TOKEN &amp;gt;&amp;gt; .env
          echo GF_VENMO_USER_ID=$GF_VENMO_USER_ID &amp;gt;&amp;gt; .env
          echo C_USER_ID=$C_USER_ID &amp;gt;&amp;gt; .env
          echo IFTTT_WEBHOOK=$IFTTT_WEBHOOK &amp;gt;&amp;gt; .env
          pipenv run python main.py
        env:
          VENMO_ACCESS_TOKEN: ${{ secrets.VENMO_ACCESS_TOKEN }}
          GF_VENMO_USER_ID: ${{ secrets.GF_VENMO_USER_ID }}
          C_USER_ID: ${{ secrets.C_USER_ID }}
          IFTTT_WEBHOOK: ${{ secrets.IFTTT_WEBHOOK }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;🏖️ Sit back and relax - with your automation in place, you’re now saving MINUTES each month!&lt;/strong&gt; 🤣&lt;/p&gt;

&lt;p&gt;But hey, we learned something neat and learned some new automation tricks. Time well spent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T_Qh9F60--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgs.xkcd.com/comics/is_it_worth_the_time.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T_Qh9F60--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgs.xkcd.com/comics/is_it_worth_the_time.png%23center" alt="XKCD was it worth the time matrix" width="571" height="464"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🧪 Experiments &amp;amp; potential improvements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Writing Node.js function on &lt;a href="https://autocode.com/"&gt;Autocode&lt;/a&gt; after switching to Payment Links.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>githubactions</category>
      <category>ifttt</category>
      <category>python</category>
      <category>venmo</category>
    </item>
    <item>
      <title>Add a Random Page Button to Hugo Site</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Sun, 13 Feb 2022 05:00:00 +0000</pubDate>
      <link>https://forem.com/idontremember/add-a-random-page-button-to-hugo-site-45e9</link>
      <guid>https://forem.com/idontremember/add-a-random-page-button-to-hugo-site-45e9</guid>
      <description>&lt;p&gt;I wanted to add a random button to one of my Hugo sites after remembering how handy it is on &lt;a href="https://xkcd.com"&gt;XKCD&lt;/a&gt;. Turns out it's a really simple process with Hugo, just need to generate a JSON file and then write a simple script to choose your random article.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are we making?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--09sQx_mc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5v7e4odutfq51dpnrpa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--09sQx_mc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5v7e4odutfq51dpnrpa.jpg" alt="Portfolio website with a randomizer button" width="880" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How-to
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Create JSON list of pages
&lt;/h3&gt;

&lt;p&gt;Hugo conveniently lets you output JSON data, so we create one at the root which will get deploy alongside the rest of our static files. Couple caveats for this example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;my filters are simple because the site only has one page archetype and I wanted all of them. You can definitely get mroe creative with Hugo's &lt;code&gt;where&lt;/code&gt; and &lt;a href="https://gohugo.io/functions/"&gt;other functions&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;This index for site search was already in the theme I was using. If you just want the randomizer, you can drop all the extra data fields like &lt;code&gt;tags&lt;/code&gt; and &lt;code&gt;contents&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   # layouts/_default/index.json

   {{- $.Scratch.Add "index" slice -}}
   {{- range where site.RegularPages "Type" "in" site.Params.mainSections -}}
   {{ $date:= .PublishDate.Format "02"}}
   {{- $.Scratch.Add "index" (dict "title" .Title "date" $date "tags" .Params.tags "image" .Params.image "categories" .Params.categories "contents" .Plain "permalink" .Permalink) -}}
   {{- end -}}
   {{- $.Scratch.Get "index" | jsonify -}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I needed to add a small param in my &lt;code&gt;config.toml&lt;/code&gt;, here's an &lt;a href="https://zwbetz.com/build-a-search-bar-for-your-hugo-blog-with-a-json-index-and-some-vanilla-js/"&gt;article if you want to dive a bit deeper&lt;/a&gt; and understand more of what is going on.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[outputs]
    home = ["json", "html"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Prod JSON output
&lt;/h4&gt;

&lt;p&gt;For reference, the JSON that the above Hugo code will end up outputting.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
   {
     "categories": [
       "Developers"
     ],
     "contents": "text of the document",
     "date": "07",
     "image": "images/post/article-1.png",
     "permalink": "https://permalink",
     "tags": [
        "Software Development"
     ],
     "title": "Title of the most recent article"
   },
 ...
 ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  2. Use JSON in an HTML page
&lt;/h3&gt;

&lt;p&gt;You can add something similar to this in your HTML pages. I have this in a &lt;code&gt;partial&lt;/code&gt; where the button is displayed on the page.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;script&amp;gt;
     var searchIndexData = [];
     // fetch on page load from the search index
     let json_path = window.location.origin + '/index.json'

     fetch(json_path).then(function (response) {
        return response.json();
     })
     .then(function (data) {
        searchIndexData = data;
     })
     .catch(function (err) {
        console.log(err)
     });


     function sendToRandomArticle() {
     let randIndex = Math.floor(Math.random() * searchIndexData.length);
     let randArticle = searchIndexData[randIndex]['permalink'] + '?utm_source=RandomButton';
     window.location.href = randArticle;
     }

    &amp;lt;/script&amp;gt;
    ...
    ...
    &amp;lt;button type="button" class="btn btn-primary" onclick='sendToRandomArticle()'&amp;gt;Random&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! Told you it was simple.&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Add Search to Hugo Static Site in 5 Minutes with No Dependencies</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Fri, 28 Jan 2022 05:00:00 +0000</pubDate>
      <link>https://forem.com/idontremember/add-search-to-hugo-static-site-in-5-minutes-with-no-dependencies-38ab</link>
      <guid>https://forem.com/idontremember/add-search-to-hugo-static-site-in-5-minutes-with-no-dependencies-38ab</guid>
      <description>&lt;p&gt;Having search on your site is a big plus for the user experience &lt;em&gt;(and for finding that piece of content I remember writing but can’t find!)&lt;/em&gt;. My site isn’t big enough that it’s worth spending much effort setting up a real search solution like &lt;a href="https://www.algolia.com/"&gt;Algolia&lt;/a&gt;, &lt;a href="https://www.meilisearch.com/"&gt;Meilisearch&lt;/a&gt;, or even &lt;a href="https://lunrjs.com/"&gt;LunrJS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead, I got inspiration from the past - let a search engine do the work for me, like so many sites in the 2000s. This is the &lt;strong&gt;80/20 version&lt;/strong&gt; of adding search to your site - a good enough option with minimal effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are we adding
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CrirAskA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kevinquinn.fun/uploads/searchbar-yellow-bg.png%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CrirAskA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kevinquinn.fun/uploads/searchbar-yellow-bg.png%23center" alt="Search bar on site" width="517" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Search engines provide the option to search within a single website with &lt;code&gt;site:&amp;lt;domain&amp;gt;&lt;/code&gt;. We will use that as our base - then sync an input box with a clickable link containing user text. On click, it takes them to the search engine page with the search filled for them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;site:kevinquinn.fun &amp;lt;user-input-text-here&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Total additions to your codebase - &lt;strong&gt;3&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An input box, styled to your liking.&lt;/li&gt;
&lt;li&gt;A link tag.&lt;/li&gt;
&lt;li&gt;And a short script.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Show me an example
&lt;/h2&gt;

&lt;p&gt;This site is built with an older version of Boostrap, but the core element is the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; from this &lt;a href="http://stackoverflow.com/questions/7097573/ddg#7097818"&gt;handy dandy Stack Overflow post&lt;/a&gt;, then attach it to your input box.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="input-group"&amp;gt;
&amp;lt;input id='search-input' type="text" class="form-control" placeholder="Search for..."&amp;gt;
&amp;lt;span class="input-group-btn"&amp;gt;
&amp;lt;a id='generated-link' target="_blank" class="btn btn-default" href="https://duckduckgo.com/"&amp;gt;Go!&amp;lt;/a&amp;gt;
&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;script type="text/javascript"&amp;gt;
var link= document.getElementById('generated-link');
var input= document.getElementById('search-input');
input.onchange=input.onkeyup= function() {
link.search= '?q=site%3Akevinquinn.fun+'+encodeURIComponent(input.value);
};
&amp;lt;/script&amp;gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Search in under 5 minutes 😎
&lt;/h2&gt;

&lt;p&gt;You just added search to your website in no time, give yourself a pat on the back! Is it the most amazing search in the world? No, not really - but it does the job. Most developer blogs only have a couple &lt;em&gt;Hello World!&lt;/em&gt; posts anyway, so Algolia might just be a tiny, &lt;em&gt;tiny&lt;/em&gt;, bit overkill. Now take that time you saved and get back to scrolling Hacker News.&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Get Started With Obsidian Periodic Notes and Templater</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Tue, 04 Jan 2022 05:00:00 +0000</pubDate>
      <link>https://forem.com/idontremember/get-started-with-obsidian-periodic-notes-and-templater-3j59</link>
      <guid>https://forem.com/idontremember/get-started-with-obsidian-periodic-notes-and-templater-3j59</guid>
      <description>&lt;p&gt;I’ve enjoyed tracking my daily notes in Obsidian so far, but it was a bit more complicated to get configured than I thought it would be, so I’m sharing it here to hopefully make setup smoother for those in the future - as well as a few extra credit pieces I’ve picked up along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Daily Notes Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Create a folder for the periodic notes (&lt;code&gt;periodic_notes&lt;/code&gt;), and then a sub folder for each of the time periods you want to use, .e.g&lt;/em&gt; &lt;code&gt;periodic_notes/daily&lt;/code&gt;. &lt;em&gt;You can choose whatever naming scheme you like.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Create a folder for Templates, then a base template file for each period you will be using. Naming doesn’t matter for this, I chose the creative&lt;/em&gt; &lt;code&gt;Templates&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;&lt;a href="https://help.obsidian.md/Advanced+topics/Community+plugins"&gt;Enable community plugins&lt;/a&gt; and install Calendar, Periodic Notes, and Templater.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Open settings and enable all the plugins.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Open the sub-settings for Periodic Notes and enable all the time periods you will use. Feel free to adjust any of the file naming conventions, for me, I updated the weekly format to&lt;/em&gt; &lt;code&gt;gggg-[W]WW&lt;/code&gt; &lt;em&gt;to match my navigation bar template.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Don't set templates in the Periodic Notes settings. They will be configured with Templater since it has more features.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Open the Templater sub-settings, then set your&lt;/em&gt; &lt;code&gt;Template folder location&lt;/code&gt; &lt;em&gt;to what you created in Step 2.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Enable&lt;/em&gt; &lt;code&gt;Trigger Templater on new file creation&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Under&lt;/em&gt; &lt;code&gt;Folder Templates&lt;/code&gt;, &lt;em&gt;connect each of the periodic folders you created in Step 1 to the base template files from Step 2.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, whenever a new file is created in any of those folders, it will run Templater first. The Calendar sidebar is a handy tool for easily selecting any day’s file vs the Command Palette/File search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extra Credit
&lt;/h2&gt;

&lt;p&gt;You've got the basics set up, now bump it up a notch. Add a Navigation Bar, Period Completion Percentage, and/or other fun plugins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Navigation Bar
&lt;/h3&gt;

&lt;p&gt;It’s a bit overkill, but I originally saw this on the &lt;a href="https://www.reddit.com/r/ObsidianMD/comments/my4ns4/planning_for_the_week_ahead_in_obsidian/"&gt;Obsidian Reddit&lt;/a&gt; and loved the idea of easily accessing not just the adjacent days, since Calendar plugin does that, but the other periods relevant for that day as well. Looks like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;❮❮ ⋮ 2021 › 12 › Q4 › W49 ⋮ ❯❯&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; ℹ️ It's possible to have a similar navbar on other periods besides 'daily', though it does cause more difficulties since you aren't pulling the exact day from the title. What date should it pick if you're looking at 2022-Q2? Doable, but it's an exercise left to the reader.

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

&lt;/div&gt;



&lt;p&gt;Want your own? This relies on your files having the date in the name, so as long as you are using Periodic Notes plugin and match up your chosen format setting with this code, you’ll be ready to rock. The section in the frontmatter sets up a number of variables, then later on the page I can access them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
&amp;lt;%*
var fileDate = moment(tp.file.title);
// moment dates are mutable
let prevDay = moment(fileDate).subtract(1, 'd').format('YYYY-MM-DD');
let nextDay = moment(fileDate).add(1, 'd').format('YYYY-MM-DD');
let yearLink = fileDate.format('YYYY');
let quarterLink = fileDate.format('YYYY-[Q]Q');
let monthLink = fileDate.format('YYYY-MM');
let weekLink = fileDate.format('gggg-[W]WW');
-%&amp;gt;
tags: daily_note &amp;lt;% fileDate.format("YYYYMMDD") %&amp;gt; &amp;lt;% weekLink %&amp;gt; &amp;lt;% monthLink %&amp;gt; &amp;lt;% quarterLink %&amp;gt; &amp;lt;% yearLink %&amp;gt;
weekday: &amp;lt;% fileDate.format("ddd") %&amp;gt;
---
&amp;lt;%*
// ❮❮ ⋮ 2021 › Q4 › 12 › W49 ⋮ ❯❯
// [[path/to/file|display_text]]
let navStr = `[[periodic_notes/daily/${prevDay}|❮❮]] ⋮ [[periodic_notes/yearly/${yearLink}|${yearLink}]] › [[periodic_notes/quarterly/${quarterLink}|${fileDate.format('[Q]Q')}]] › [[periodic_notes/monthly/${monthLink}|${fileDate.format('MM')}]] › [[periodic_notes/weekly/${weekLink}|${fileDate.format('[W]WW')}]] ⋮ [[periodic_notes/daily/${nextDay}|❯❯]]`;
tR += navStr
%&amp;gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Period Completion Percentage
&lt;/h3&gt;

&lt;p&gt;I’m a visual person, so it’s very handy for me to be able to see how far through the week/month/year I currently am. Can’t pretend you have plenty of time left if the ticker says &lt;strong&gt;95%&lt;/strong&gt; finished.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Month: [██████████◽◽◽◽◽◽◽◽◽◽◽◽◽◽◽◽◽◽◽◽◽] 32% ( 10/31 )
Year: [███████████████████████████████◽◽] 94% ( 344/365 )
# Alternative
⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡⊡·········31|

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

&lt;/div&gt;



&lt;p&gt;Since I use the progress bar in a number of places, I have it stored as a Templater user function. To use mine specifically (else just copy the &lt;code&gt;function&lt;/code&gt; in any Templater block you want it):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Create a folder in the vault for functions, then edit Templater settings to point to it.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Create a &lt;code&gt;makeProgressBar.js&lt;/code&gt; file in that folder and fill it with contents below.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Restart Obsidian, in the settings for Templater you should see it loaded your function.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Now you can use it in your Daily Notes (or elsewhere) like so:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;%* 
function month() {
    let fileDateNum = fileDate.date();
    let numDays = fileDate.daysInMonth();
    // ignore leapyears
    tR += tp.user.makeProgressBar(fileDateNum, numDays, size=numDays, filledChar = "█", unFilledChar = "◽", label="Month");
}
month();
-%&amp;gt;

&amp;lt;%* 
function year() {
    let dayNum = fileDate.dayOfYear();
    // ignore leapyears
    tR += tp.user.makeProgressBar(dayNum, 365, size=33,filledChar = "█", unFilledChar = "◽", label="Year");
}
year();
%&amp;gt;

# Blah Regular Content
This is regular content to show you you can place the progress bars wherever.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other Plugins
&lt;/h3&gt;

&lt;p&gt;If you’re interested in gleaning insights from Daily Notes over time, e.g. habit summaries in each Weekly Note, average number of hours you worked per day, pulling in summaries of tasks across many files, then I highly recommend adding the plugins &lt;a href="https://blacksmithgu.github.io/obsidian-dataview"&gt;Dataview&lt;/a&gt; and &lt;a href="https://github.com/pyrochlore/obsidian-tracker"&gt;Tracker&lt;/a&gt;. I have barely dipped my toes into what they can do and can already see huge potential to get a better understanding of my moods, habits, and more. There’s some great Dataview inspiration on the &lt;a href="https://forum.obsidian.md/t/dataview-plugin-snippet-showcase/13673/190"&gt;Obsidian forum&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fin
&lt;/h2&gt;

&lt;p&gt;You should be all set with a basic configuration for taking Periodic Notes in Obsidian with Templater, though now that you have such a powerful tool, the sky is the limit. Get creative with daily rituals, morning routines, and the many community plugins available in Obsidian. If you have any questions or responses, feel free to reach out to me on &lt;a href="https://twitter.com/maybekq"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>obsidian</category>
      <category>productivity</category>
      <category>pkm</category>
    </item>
    <item>
      <title>Personalize your Development Environment with Dotfiles</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Thu, 16 Dec 2021 05:00:00 +0000</pubDate>
      <link>https://forem.com/idontremember/personalize-your-development-environment-with-dotfiles-299k</link>
      <guid>https://forem.com/idontremember/personalize-your-development-environment-with-dotfiles-299k</guid>
      <description>&lt;p&gt;Using the command line in it’s default state is doable, in the same way eating plain toast 🍞 every morning will feed you. Is it enjoyable? not really. With a few small tweaks though, it can make you feel like a super hero. Dotfiles track these tweaks and encourage you to add more. We’ll go over what the heck dotfiles even are (and why you should care), how to get rolling with them, and give you my favorite tweaks so you can start your own unique collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are dotfiles?
&lt;/h2&gt;

&lt;p&gt;On your computer, there are hidden files called &lt;code&gt;dotfiles&lt;/code&gt;, they’re hidden because they start with a &lt;code&gt;'.'&lt;/code&gt; e.g. &lt;code&gt;~/.bashrc&lt;/code&gt; . they usually control configuration of tools or the terminal (command line) itself. People will also use the term “dotfiles” to mean a repo of someone’s personalized &lt;code&gt;dotfiles&lt;/code&gt;, like &lt;em&gt;“Wow, Jeff has an amazing dotfiles repo on Github!"&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  why keep track of your dotfiles?
&lt;/h2&gt;

&lt;p&gt;Since dotfiles (the actual files) store the configuration settings of your machine, by tracking them you can save the tweaks and tools you personally find handy. The best part? They grow with you over time as you find interesting tidbits on the internet. Rome wasn’t built in a day, and neither was the perfect developer setup. Depending on how you use them, you can get bonus benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Documents how to fix the weird error you run into every 7 months and spend an hour searching for the command which fixed it last time - use aliases &amp;amp; functions. For example:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Help you stop treating your machine like a &lt;a href="http://cloudscaling.com/blog/cloud-computing/the-history-of-pets-vs-cattle/"&gt;pet and start treating it like cattle&lt;/a&gt;. If your computer gets lost or stolen, you should be able to easily make the new machine feel like home with the same environment. More in Extra Resources on automating personal machine setup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gradually “automate” tasks in your life. &lt;a href="https://blog.danslimmon.com/2019/07/15/do-nothing-scripting-the-key-to-gradual-automation/"&gt;Do Nothing&lt;/a&gt; scripts can be used for anything from code you don’t yet know how to write, to manual tasks you don’t want to forget.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to start your Dotfiles
&lt;/h2&gt;

&lt;p&gt;If you want the least amount of effort, follow &lt;strong&gt;Option A&lt;/strong&gt; with Dotbot. If you want to have a better understanding of what is happening under the hood, try &lt;strong&gt;Option B&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option A: using a tool
&lt;/h3&gt;

&lt;p&gt;There are a variety of fantastic tools to track dotfile history, syncing, and anything else you can think of at &lt;a href="https://dotfiles.github.io/"&gt;https://dotfiles.github.io/&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;em&gt;The only one I’d recommend out of the list for a beginner is &lt;a href="https://github.com/anishathalye/dotbot"&gt;Dotbot&lt;/a&gt;, since it eschews most of the complexity of other dotfiles systems. No need to set up a complex syncing tool if you end up ignoring your dotfiles after you finish setting them up.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Option B: Using a custom script
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;start a Git repo, I prefer to have all my files in the repo without the dot so it’s more obvious when tracking in Git and also won’t overwrite anything on accident.&lt;/li&gt;
&lt;li&gt;Clone that repo to &lt;code&gt;~/dotfiles&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Write a &lt;a href="https://github.com/I-Dont-Remember/dotfiles/blob/master/link_dotfiles.sh"&gt;script&lt;/a&gt;, or document the command, for &lt;a href="https://linuxize.com/post/how-to-create-symbolic-links-in-linux-using-the-ln-command/"&gt;symlinking&lt;/a&gt; the real file to the dotfiles version, e.g. &lt;code&gt;ln -s ~/dotfiles/bash_profile ~/.bash_profile&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You’re off to the races! My best advice is to not try to collect everything at once or you’ll end up with a pile of stuff you don’t need. Let it evolve with you over time rather than aiming for perfection out of the gate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Favorite Tweaks
&lt;/h2&gt;

&lt;p&gt;My &lt;a href="https://github.com/I-Dont-Remember/dotfiles"&gt;dotfiles&lt;/a&gt; are full of tidbits I’ve stumbled on, so I pulled out the best pieces for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pump up the prompt
&lt;/h3&gt;

&lt;p&gt;Probably the most generally useful tweak I have is fleshing out the prompt with more information. Mine includes the time it ran, the current working directory, Git branch &amp;amp; status, and last exit code. In text form &lt;code&gt;15:36:45:~/dotfiles:(master)* [0]▶&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vzSPA1qz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kevinquinn.fun/uploads/dotfiles-terminal-prompt.jpg%23center" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vzSPA1qz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://kevinquinn.fun/uploads/dotfiles-terminal-prompt.jpg%23center" alt="Terminal prompt with previous exit code, current working directory, Git information, and time." width="319" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The exit code has saved me in a surprising number of situations from deeper debugging. You can use &lt;a href="https://dotfiles.github.io/"&gt;https://scriptim.github.io/bash-prompt-generator/&lt;/a&gt; or &lt;a href="https://dotfiles.github.io/"&gt;http://bashrcgenerator.com/&lt;/a&gt; to easily generate a similar setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Git Aware Prompt - shamelessly stolen from &amp;lt;https://github.com/jimeh/git-aware-prompt&amp;gt;
find_git_branch() {
# Based on: &amp;lt;http://stackoverflow.com/a/13003854/170413&amp;gt;
local branch
if branch=$(git rev-parse --abbrev-ref HEAD 2&amp;gt; /dev/null); then
if [["$branch" == "HEAD"]]; then
branch='detached*'
fi
git_branch="($branch)"
else
git_branch=""
fi
}
find_git_dirty() {
local status=$(git status --porcelain 2&amp;gt; /dev/null)
if [["$status" != ""]]; then
git_dirty='*'
else
git_dirty=''
fi
}
PROMPT_COMMAND="find_git_branch; find_git_dirty; $PROMPT_COMMAND"
export PS1="\\[\\033[38;5;11m\\]\\t:\\w:\\$git_branch\\$git_dirty\\[$(tput sgr0)\\]\\[\\033[38;5;15m\\] \\[$(tput sgr0)\\]\\[\\033[38;5;11m\\][\\[$(tput sgr0)\\]\\[\\033[38;5;7m\\]\\$?\\[$(tput sgr0)\\]\\[\\033[38;5;11m\\]]▶ \\[$(tput sgr0)\\]"
export CLICOLOR=1

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

&lt;/div&gt;



&lt;p&gt;If you want to expand on the &lt;code&gt;git_dirty&lt;/code&gt; status indicator, you can &lt;a href="https://adamhollett.com/posts/2021/04/terminal-tricks-from-my-dotfiles/"&gt;read here&lt;/a&gt; how to make a fancy one with different shapes &amp;amp; colors indicating different statuses you rely on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aliases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Aliases lessen how much I have to type and also do the remembering of common args I need. You can also add functions that take arguments, though eventually they could move to a &lt;code&gt;$HOME/bin&lt;/code&gt; folder or similar along your &lt;code&gt;$PATH&lt;/code&gt;. You can check mine out &lt;a href="https://github.com/I-Dont-Remember/dotfiles/blob/master/bash_aliases"&gt;here&lt;/a&gt;. The most useful one is &lt;code&gt;extract()&lt;/code&gt; so I never have to figure out how to open a compressed file - this was ripped from someone’s dotfiles many years back, the origins are unknown.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  (Niche) OS-specific configuration
&lt;/h3&gt;

&lt;p&gt;I work across a variety of machines - Mac, Linux, and Windows. It’s nice to have a single set of dotfiles which automatically works no matter where I am using them. I have specific &lt;code&gt;bashrc&lt;/code&gt; and &lt;code&gt;bash_aliases&lt;/code&gt; files for each OS (this could be done with any file though). An added tweak with this to add an &lt;code&gt;echo&lt;/code&gt; at the top of &lt;code&gt;bash*&lt;/code&gt; dotfiles so when terminal is starting, I can tell which configuration files were used in case anything unexpected happens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;case $OSTYPE in
solaris*)
echo "You have Solaris??"
;;
darwin*)
if [-f ~/.bashrc_macos]; then
. ~/.bashrc_macos
fi
;;
linux*)
if [-f ~/.bashrc_linux]; then
. ~/.bashrc_linux
fi
;;
bsd*)
echo "You have BSD??"
;;
*)
echo "Unknown OSTYPE $OSTYPE in bashrc check"
;;
esac

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

&lt;/div&gt;



&lt;p&gt;These are the most generally useful tips I can provide, but that’s the beauty of dotfiles. It works unique to my set up and saves me time &amp;amp; frustration despite it not being perfect for others.&lt;/p&gt;

&lt;h2&gt;
  
  
  That’s a wrap
&lt;/h2&gt;

&lt;p&gt;Your terminal will be now uniquely yours, but beware! You are entering a rabbit hole of a bajillion awesome tools and scripts to try out. Enjoy the ride, but don’t get too lost in trying out shiny toys that you miss out on having a reason to use them in the first place. Enjoy the adventure!&lt;/p&gt;

&lt;h2&gt;
  
  
  Extra Resources
&lt;/h2&gt;

&lt;p&gt;If you’re feeling inspired, here’s a few extra resources for the world of dotfiles and the command line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dotfiles.github.io/inspiration/"&gt;Dotfiles inspiration list&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dotfiles.github.io/"&gt;https://github.com/webpro/awesome-dotfiles&lt;/a&gt;&lt;a href="https://github.com/webpro/awesome-dotfiles%E2%80%A3"&gt;‣&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotfiles.github.io/"&gt;https://github.com/learn-anything/command-line-tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotfiles.github.io/"&gt;https://github.com/agarrharr/awesome-cli-apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dylanaraps/pure-bash-bible"&gt;How to do many common things in Bash - Pure Bash Bible&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/scottw/explain-shell-5blp-temp-slug-1275153"&gt;Explanations of shell commands&lt;/a&gt; and &lt;a href="https://github.com/tldr-pages/tldr"&gt;human-readable man pages&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Beyond Dotfiles - &lt;a href="https://www.jeffgeerling.com/blog/2021/dont-fall-love-your-mac%E2%80%94automate-it"&gt;automating your entire computer setup&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotfiles</category>
    </item>
    <item>
      <title>Manage Multiple Python Versions With Docker: an alternative to Pyenv</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Fri, 03 Dec 2021 05:00:00 +0000</pubDate>
      <link>https://forem.com/idontremember/manage-multiple-python-versions-with-docker-an-alternative-to-pyenv-49jc</link>
      <guid>https://forem.com/idontremember/manage-multiple-python-versions-with-docker-an-alternative-to-pyenv-49jc</guid>
      <description>&lt;p&gt;You open a project to contribute code and quickly realize it requires a specific Python version you don’t have. Not only that, but it’s managing dependencies with some tool you don’t have, like Pipenv or Poetry. What are your options? You could spend a while setting up your machine with a &lt;a href="https://jacobian.org/2019/nov/11/python-environment-2020/"&gt;fancy Python development environment&lt;/a&gt;, or you could get up and running right away using Docker as a Python version manager - which works the same across &lt;a href="https://docs.docker.com/get-docker/"&gt;MacOS, Windows, and Linux&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is this for?
&lt;/h2&gt;

&lt;p&gt;👍 Use this Docker strategy if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;want a hassle-free way to develop on a project (such as an API or package) without fiddling with system setup.&lt;/li&gt;
&lt;li&gt;Infrequently work on Python projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👎 Use the traditional setup with &lt;code&gt;pyenv&lt;/code&gt; if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;are confident in command line concepts like PATH, dot files, Python virtual environments, and your ability to fix any issues that might crop up.&lt;/li&gt;
&lt;li&gt;Work with a large number of Python projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⚠️ If you’re trying to install a CLI tool or script written in Python, you’ll want to explore tools like &lt;a href="https://github.com/pypa/pipx"&gt;Pipx&lt;/a&gt;. It’s possible to run scripts in containers, but I wouldn’t recommend for everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use Docker as a Python version manager instead of Pyenv?
&lt;/h2&gt;

&lt;p&gt;Why would you bother managing Python versions and environments with Docker when there are tools built to do it for you?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;Most developers don’t need a fancy Python set up.&lt;/strong&gt; ⭐ In my experience, there are plenty of developers who just want to get up and running on a project rather than tinkering with their system config, especially if they rarely work with Python. Not everyone wants to play with &lt;a href="https://github.com/search?q=dotfiles"&gt;dotfiles&lt;/a&gt;and development environments (😬 I do though, send me your tricks I love them). This approach only requires 1 Docker command.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker has a wide range of use cases, Pyenv has just one&lt;/strong&gt;. Installing Docker to manage Python versions is just the tip of the iceberg of what it can do. Even if you decide you don’t like this approach, there will be other uses in the future. If you already have it installed, then one less step!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoiding tool maintenance&lt;/strong&gt;. The combination of Pyenv + Pipenv/Poetry/etc. is &lt;em&gt;usually&lt;/em&gt; a well-oiled machine. If an issue arises though, it can be frustrating to debug what’s wrong with your command line tools. The article you were copying setup commands from won’t be there when things breaks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enough chatter, now shut up and tell me how!&lt;/p&gt;

&lt;h2&gt;
  
  
  Manage multiple Python versions and environments with Docker
&lt;/h2&gt;

&lt;p&gt;A prerequisite to managing Python versions with containers is to have either &lt;a href="https://docker.com"&gt;Docker&lt;/a&gt; or &lt;a href="https://podman.io/"&gt;Podman&lt;/a&gt; installed on your machine. Once ready, follow the steps below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Step 1
git clone &amp;lt;your-repo&amp;gt;.git
cd &amp;lt;your-repo&amp;gt;
# Step 2 &amp;amp; 3: creates container that is destroyed after session
docker run -it --rm --name python-runner -v "$PWD":/usr/src/myapp -w /usr/src/myapp python:3.7 bash
# OPTIONAL ALTERNATIVE&amp;gt; Step 2,3,&amp;amp; 3a: retain container between sessions
docker run -it --name &amp;lt;project-name&amp;gt; -v "$PWD":/usr/src/myapp -w /usr/src/myapp python:3.7 bash
# .... many hours later...
docker start -i &amp;lt;project-name&amp;gt;
# Step 4
# install dependencies following the directions based on your project

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Clone the project you are trying to work with (not inside a container, we don’t want to deal with connecting to GitHub or IDEs from the container if we can help it. &lt;a href="https://en.wikipedia.org/wiki/KISS_principle"&gt;Keep it simple&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Choose the image version. Most common will be &lt;code&gt;3.6, 3.7, 3.8, 3.9,&lt;/code&gt; or &lt;code&gt;3.10&lt;/code&gt; , but you can find every available option on &lt;a href="https://hub.docker.com/_/python/"&gt;Docker Hub&lt;/a&gt;. Your image will then be &lt;code&gt;python:&amp;lt;chosen-version&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;docker run&lt;/code&gt; command to create a transient container (swap version if needed). The &lt;code&gt;bash&lt;/code&gt; on the end, along with &lt;code&gt;-it&lt;/code&gt; tells Docker to give us an interactive shell, just like when you open a new terminal prompt. The &lt;code&gt;-v&lt;/code&gt; and &lt;code&gt;-w&lt;/code&gt; make the current directory available inside the container, so you’ll need to always run this command from the project.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;(Optional)&lt;/strong&gt; - If you will be working with this project a lot, a couple small tweaks will let you reuse the same container. Give it a good &lt;code&gt;--name&lt;/code&gt; that you will recognize and remove the &lt;code&gt;--rm&lt;/code&gt; (which removes the container when the current run finishes). Now in the future, you can use &lt;code&gt;docker start -i project-name&lt;/code&gt; to pop open the shell, rather than needing to create a brand new container and install dependencies again.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Install dependencies following the instructions in next section.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Next step: managing dependencies
&lt;/h2&gt;

&lt;p&gt;Now you’ve got a container running with the Python version the project needs, how can you handle required dependencies? You may need to install a tool inside your running container depending on how the project is configured, directions for the most common options are below. These commands should be run in the container shell created by the &lt;code&gt;docker run&lt;/code&gt; command, not on your local system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pip (&lt;code&gt;requirements.txt&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Nothing special needed here, run &lt;code&gt;pip install --user -r requirement.txt&lt;/code&gt; and you’re off the the races.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pipenv (&lt;code&gt;Pipfile.lock&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Couple extra steps needed to get a &lt;a href="https://pipenv.pypa.io/en/latest/"&gt;Pipenv&lt;/a&gt; project running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install --user pipenv
# Ensure tool is available in your container's PATH
echo "export PATH=~/.local/bin:$PATH" &amp;gt;&amp;gt; ~/.bashrc
. ~/.bashrc # equivalent to 'source ~/.bashrc'
pipenv install --dev

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Poetry (&lt;code&gt;poetry.lock&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Check the &lt;a href="https://python-poetry.org/docs/master/#installation"&gt;Poetry docs&lt;/a&gt; to ensure install command is up to date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -sSL &amp;lt;https://install.python-poetry.org&amp;gt; | python3 -
# Ensure tool is available in your container's PATH
echo "export PATH=~/.local/bin:$PATH" &amp;gt;&amp;gt; ~/.bashrc
. ~/.bashrc # equivalent to 'source ~/.bashrc'
poetry install

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🙋 &lt;em&gt;Does this take the place of virtualenv?&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;📣 Yes and no. You can use it that way (see Pip section above), but many projects will already use a tool like Pipenv for managing dependencies, which has virtual environments baked in. Docker adds an extra level of isolation in this case, but doesn’t replace the management of dependency versions, etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;🙋 &lt;em&gt;Can I still use my favorite IDE/text editor to edit files locally?&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;📣 Yes, all the files are still on your local machine since the container is using them with a volume mount.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;🙋 &lt;em&gt;Can I access any file on my system inside the Docker container?&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;📣 Only the original &lt;a href="https://explainshell.com/explain/1/pwd"&gt;working directory&lt;/a&gt; will be attached inside your container, all the other files on system and in container are isolated from each other.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;🙋 &lt;em&gt;How do I access&lt;/em&gt; &lt;a href="http://localhost"&gt;&lt;em&gt;localhost&lt;/em&gt;&lt;/a&gt; &lt;em&gt;if my Flask/Django/etc. app is running in a container?&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;📣 Docker isolates your code in a container separate from the rest of the system, so you’ll have to add an argument to the &lt;code&gt;run&lt;/code&gt; command to expose the port. Something like &lt;code&gt;-p 8080:80 -p 443:443&lt;/code&gt; is the recommended route to map &amp;lt;!-- raw HTML omitted --&amp;gt;:&amp;lt;!-- raw HTML omitted --&amp;gt;. &lt;a href="https://stackoverflow.com/questions/37981001/how-can-i-run-a-docker-container-on-localhost-over-the-default-ip"&gt;Read more on StackOverflow&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;🙋 &lt;em&gt;How do I install packages if my company blocks PyPi and has their own internal package repository?&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;📣 You’ll need to &lt;a href="https://pip.pypa.io/en/stable/cli/pip_install/#install-index-url"&gt;add an argument to Pip command&lt;/a&gt;, &lt;code&gt;-i,--index-url&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This simpler approach to managing Python versions is another tool in your toolbox, and it can be applied for other languages as well. Hopefully you find success using this to get up and running on projects rather than fiddling with system configurations. Let me know if you have suggestions on ways to make it even simpler and improve it for future users.&lt;/p&gt;

</description>
      <category>python</category>
      <category>docker</category>
      <category>programming</category>
    </item>
    <item>
      <title>7 Blunders Great Developers Say 'No' To</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Mon, 05 Apr 2021 13:15:32 +0000</pubDate>
      <link>https://forem.com/idontremember/7-blunders-great-developers-say-no-to-3lb2</link>
      <guid>https://forem.com/idontremember/7-blunders-great-developers-say-no-to-3lb2</guid>
      <description>&lt;p&gt;Do you want to be more than a &lt;a href="https://softwareengineering.stackexchange.com/questions/79997/whats-wrong-with-being-a-code-monkey-or-what-is-a-code-monkey"&gt;code monkey&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;Code monkeys build what is put in front of them, while great developers understand the context around their tasks. In my own quest to become more than a fleshy automaton, I try to keep track of the traits and actions of the best developers so I can emulate them. The best always seem to know when an idea will lead to pain down the road, when corners can and can't be cut, what work is a waste of time to bother with. They see the bigger picture and understand the value of 'No' instead of blindly following what ends up on their plate. What you don't do is as important as what you do. Every time you say 'Yes' to something, you are turning down other possibilities even if you don't realize it.&lt;/p&gt;

&lt;p&gt;If you want to be a great developer, learn to say 'No' more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Great developers say 'No' to...
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No to...&lt;/strong&gt; building features which aren't worth the energy investment. Review the business value of what you're creating, could the time be better spent elsewhere? Are you spending days working on a feature which barely moves the needle?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No to...&lt;/strong&gt; inordinate testing. You can write all the tests you want, but you'll never cover every scenario. Eventually your code has to go to production and run into the &lt;a href="https://thenewstack.io/honeycombs-charity-majors-go-ahead-test-in-production/"&gt;"long tail of things that will probably never happen, but one day they do"&lt;/a&gt;. Keep a reasonable standard for testing, then have systems in place that let you safely &lt;a href="https://thenewstack.io/honeycombs-charity-majors-go-ahead-test-in-production/"&gt;test in production&lt;/a&gt;. Your app likely isn't keeping astronauts alive, do you really need 100% coverage?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No to...&lt;/strong&gt; code which increases the complexity of a project for no gain. Clever code is rarely maintainable. &lt;a href="https://skeptics.stackexchange.com/questions/48560/is-code-read-more-often-than-its-written"&gt;Code is read more often than it is written&lt;/a&gt;, don't be wasting your own time 6 months from now. Let's &lt;a href="https://en.wikipedia.org/wiki/KISS_principle"&gt;K.I.S.S&lt;/a&gt;. (Keep it Simple Stupid).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No to...&lt;/strong&gt; piles of paperwork around the tasks you need to accomplish, such as Jira stories. Every little thing you do doesn't need to be explicitly tracked. Create higher-level stories which encompass the hiccups you might encounter along the way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No to...&lt;/strong&gt; excessive meetings, or meetings without a purpose. A common cry from developers is &lt;a href="https://dzone.com/articles/too-many-meetings-a-devs-cry-for-help-5-things-tea"&gt;"too many meetings"&lt;/a&gt;, but it's not a hatred for meetings themselves. It's a hatred for &lt;strong&gt;bad&lt;/strong&gt; meetings and their ability to disrupt an entire day of work if scheduled poorly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No to...&lt;/strong&gt; shiny new frameworks &amp;amp; tools. The allure is strong. Trust me, I get it, I fall into the same trap constantly. If you're part of a larger organization, don't make it difficult to maintain what you built if you leave. If you're small, don't add risk to your endeavor without thinking through the trade offs being made. &lt;a href="https://thebootstrappedfounder.com/making-tech-choices/"&gt;Make tech choices intentionally&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No to...&lt;/strong&gt; excuses. Take ownership of the work you do and predict problems so you can get ahead of them. When things blow up, buckle down and find a solution instead of pointing fingers. The business' customers don't care who made it break, they just want it fixed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The great developers I've worked with understand when saying 'No' will lead to a better outcome. Software development is an expensive undertaking, so even small blunders can have a big impact. Finding ways to cut out the cruft burning through developer time makes you a more valuable member of the team. If you want to emulate the best, learn to say 'No'.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href=""&gt;Sarah Kilian&lt;/a&gt; on &lt;a href=""&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>softskills</category>
    </item>
    <item>
      <title>Set Up Pi-hole Ad Blocker on Raspberry Pi Zero with a Netgear Router</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Mon, 04 Jan 2021 05:00:00 +0000</pubDate>
      <link>https://forem.com/idontremember/set-up-pi-hole-ad-blocker-on-raspberry-pi-zero-with-a-netgear-router-60f</link>
      <guid>https://forem.com/idontremember/set-up-pi-hole-ad-blocker-on-raspberry-pi-zero-with-a-netgear-router-60f</guid>
      <description>&lt;p&gt;You want an awesome ad blocker on your local network using &lt;a href="https://pi-hole.net"&gt;Pi-hole&lt;/a&gt;, but don't want to dig for answers? You're in the right place. This quick guide will walk you through the generic Raspberry Pi setup, install and configure Pi-hole, then manually connect devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why manually connect devices instead of the Pi-hole default?&lt;/strong&gt; If you live with other people, they get frustrated when their browsing experience breaks inexplicably. In my experience, they are not amazed by how many ad requests you've blocked enough to warrant the interruptions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pre-reqs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Raspberry Pi&lt;/li&gt;
&lt;li&gt;SD card (8+ GB)&lt;/li&gt;
&lt;li&gt;adapter to use SD card with your computer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting up the Raspberry Pi
&lt;/h2&gt;

&lt;p&gt;These steps assume you want to have your Pi connected to your local Wi-Fi network and use SSH.&lt;/p&gt;

&lt;p&gt;
  Alternate: SSH over USB
  &lt;br&gt;
&lt;em&gt;It turns out it’s also possible to connect directly over a USB connection! These are &lt;a href="https://howchoo.com/pi/raspberry-pi-gadget-mode"&gt;the directions&lt;/a&gt; if you want to try it - it changes the Pi config so you are able to SSH over the USB connection as if it was Ethernet, how cool!&lt;/em&gt;&lt;br&gt;


&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install the &lt;a href="https://www.raspberrypi.com/software/"&gt;Raspberry Pi official imaging tool&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Load SD card into adapter and connect to your computer&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Using the imaging tool, &lt;a href="https://www.youtube.com/watch?v=ntaXWS8Lk34"&gt;load the standard Raspberry Pi OS&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;You should now see a &lt;code&gt;boot&lt;/code&gt; drive available. You’ll know you’re in the    right place if you see several&lt;/em&gt; &lt;code&gt;*.elf&lt;/code&gt; &lt;em&gt;files. We’ll make a couple small tweaks to enable headless Wi-Fi access.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Boot up your Pi and try connecting to the default address&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh pi@raspberry.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once you’re inside, you’ll want to use &lt;code&gt;sudo raspi-config&lt;/code&gt; to edit the password as well as the hostname, for security reasons and to avoid conflicts if you end up with more Pis in the future. After making your changes, reboot, and try connecting again.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh pi@&amp;lt;new_hostname&amp;gt;.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;(Optional) Security people will tell you should also set up your own user that isn’t &lt;code&gt;pi&lt;/code&gt; and remove &lt;code&gt;pi&lt;/code&gt; so an attacker trying default Raspberry Pi credentials can’t get into your machine.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🤖 You’re ready to get Pi-hole running!&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Pi-hole
&lt;/h2&gt;

&lt;p&gt;The maintainers of the project do an excellent job keeping this step as friction free as possible.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set a static IP in the Netgear dashboard&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After &lt;code&gt;ssh&lt;/code&gt;ing to the Pi, run &lt;a href="https://github.com/pi-hole/pi-hole#alternative-install-methods"&gt;the install script&lt;/a&gt;. &lt;strong&gt;⭐ Make sure to save the password it gives you&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once loading and setup have finished, reboot the Pi. Give it a minute or two, then in your browser try navigating to &lt;code&gt;http://&amp;lt;static_ip&amp;gt;/admin&lt;/code&gt;. If everything went well, you should be greeted by the Pi-hole admin dashboard🥧🕳!&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting devices and blocking ads
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245"&gt;There are several ways to use Pi-hole on your network to protect devices&lt;/a&gt;, as I mentioned earlier, we’ll only be covering the manual device configuration here. If you’re dipping your toes into the idea and live with others, I do recommend this approach to start and deciding to expand later on.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Android phone&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.windowscentral.com/how-change-your-pcs-dns-settings-windows-10"&gt;&lt;strong&gt;Windows desktop over Ethernet&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Linux laptop&lt;/strong&gt; - followed the steps to edit network DNS servers from &lt;a href="https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245"&gt;the article above&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;One of the most interesting things I find from running network-level ad blocking is seeing the amount of background activity as you browse. Even simple pages might load a chat client, feature flag, crash-reporting, telemetry, and more! I’ve noticed this to be especially true for mobile devices. It may look like they’re sitting quietly while you sleep, but they are actually a hum of activity all night. You’ll run into all sorts of fun here, so welcome to the world of &lt;a href="https://pi-hole.net"&gt;Pi-hole&lt;/a&gt; - adventure awaits!&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>pihole</category>
      <category>privacy</category>
    </item>
    <item>
      <title>How Ballerina Lang can Improve their Developer Experience &amp; Adoption</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Sun, 20 Dec 2020 20:43:54 +0000</pubDate>
      <link>https://forem.com/idontremember/how-ballerina-lang-can-improve-their-developer-experience-adoption-1073</link>
      <guid>https://forem.com/idontremember/how-ballerina-lang-can-improve-their-developer-experience-adoption-1073</guid>
      <description>&lt;p&gt;I stumbled onto Ballerina a few months back during a random jaunt through the interwebs, and after digging through their site I was intrigued enough by their features &amp;amp; guiding principles to give it an expanded &lt;em&gt;'Hello World!'&lt;/em&gt; test. My current method is to build a very simple package based on a popular Open Source module from PyPi or NPM. This can be a handy learning strategy, as you don't have to spend time thinking about what to build or features, you can just copy features directly from existing implementations and focus entirely on learning to put it together. You also go through the full cycle of building, testing, and deploying a module where others can use it, imitating a real world use-case. During this experiment, I noticed a few missing pieces across the developer experience and marketing which I felt could be adjusted, and in doing so have a greater impact on growing the community around the language. I pulled together my thoughts into what are hopefully coherent suggestions for the Ballerina team to implement. If you're interested in just reading about my experience learning and building a module in Ballerina, check out &lt;a href="https://kevinquinn.fun/blog/giving-ballerina-lang-a-twirl/"&gt;Giving Ballerina Lang a Twirl&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Ballerina
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ballerina.io/"&gt;Ballerina&lt;/a&gt; is a young language in development by the company WSO2, and is advertised as being "Developer First" and to have built-in functionality so developers have to spend less time worrying about how you can get your code deployed to cloud providers and many of the pieces of building maintainable &amp;amp; reliable applications like documentation &amp;amp; testing. As they say "We plumb, you build!". I've probably butchered their marketing spiel, so to any WSO2 employees reading this, my bad. For the rest of you, go check out their &lt;a href="https://ballerina.io"&gt;landing page&lt;/a&gt; to see if Ballerina is a good fit for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Suggestions for Ballerina
&lt;/h2&gt;

&lt;p&gt;After filtering my notes, I had a short list of constructive tweaks to help the growth of the language and build on the work the Ballerina team has already done. Some of these ideas could be done in a few hours, others are more general and will require deep thought to flesh out what action items could accomplish them. The suggestions for improving Ballerina fall into two camps. First, they team needs to convince people the language is ready for production use and not just a toy for developer entertainment. Show them it's ready for production environments and improves time to market and companies will bite. Second, reducing friction during the initial learning process, the early stages of getting started with the language. Users are most likely to leave, never to return, in the beginning when they haven't invested time &amp;amp; energy into a new skill or platform.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Assuage concerns of Production readiness&lt;/strong&gt; - Find ways to reduce people's concerns of production readiness, because nobody wants to adopt a language and find out it can't stand up to actual usage. Companies want to know the software they are running is dependable and their developers can quickly write quality code.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Document current production learnings&lt;/strong&gt; - From reading Hacker News comments &amp;amp; articles about Ballerina, it sounds like WSO2 is already using it substantially makes sense since they are the creators. It would be worthwhile to document what they've learned so potential adopters gain confidence in the language's ability to stand up in production. Can also discuss the ease of getting new apps from first commit to live for users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make production learnings standout&lt;/strong&gt; - Have a section on your landing page discussing it, and link to a page going into much more depth. Increase people's confidence from the start, and then provide them resources relevant for running in a corporate production setting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced debug capabilities&lt;/strong&gt; - It was pretty difficult for me to get any debugging going with Ballerina, this could have been failure on my part but others will run into the same issue and blame it on the language. Making it easier to debug will be crucial to get businesses to consider using the language for their developers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved test suite documentation&lt;/strong&gt; - I had many questions come up while trying to write even very simple tests, and they were not answered by the current level of documentation. It doesn't help me to know you can print a value &lt;code&gt;beforeEach&lt;/code&gt; function. I want to know how I can do setup &amp;amp; then have access to those variables? Ran into others like it, since the docs at the time felt very surface-level.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ballerina central has lots of noise&lt;/strong&gt; - There are a ton of projects on there that are just the "hello world" module, even if you go to the homepage you see it. It is disconcerting to have the main page showing so few actual packages and doesn't give confidence that I will be able to find the packages I need. NPM may have the same issue, I can't say for sure, but if they exist they are hidden by all the bigger projects on the platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add more interconnections in docs &amp;amp; expand them&lt;/strong&gt; - Make it as easy as possible for someone trying to work with Ballerina to find what they need. For example, each page of &lt;code&gt;Learn By Example&lt;/code&gt; should link to the stdlib page. If i'm working with examples already, there's a good chance I'll need the docs to expand beyond them. I also found myself searching through the Ballerina repos a decent amount to answer my questions, which is a lot of friction when you are trying to get someone interested in your language.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce as much friction as possible during beginning stages&lt;/strong&gt; - Developers will dig deep trying to solve bugs of their own creation, but much less so with setting up with a new language. It's hard enough to get people interested enough to try it out, but they aren't devoted to your cause enough they will work through issues just to play with the language. The sandbox helps, but it's more important to support someone setting it up on their machine, since that's where any real usage is going to happen. I personally ran into issues with Java versions on my machine causing problems, and though it's not technically a Ballerina problem, it becomes one because it causes friction which stops people from going farther with the language. The solution could be as simple as providing suggestions of documentation to follow in case certain errors arise. It's easy to shrug it off while thinking "people shouldn't be dumb", but that's a great way to not have anyone using your product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Ballerina to more sites about programming&lt;/strong&gt; - The project will die if it doesn't get enough adoption. There are sites Ballerina can add themselves, like &lt;a href="https://learnxinyminutes.com/"&gt;&lt;/a&gt;&lt;a href="https://learnxinyminutes.com/"&gt;https://learnxinyminutes.com/&lt;/a&gt;, which won't take long to make but will add new sources of traffic &amp;amp; potential adopters. Especially the case for &lt;code&gt;Learn X in Y Minutes&lt;/code&gt;, because it's alphabetical and Ballerina will show up right near the top.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add indirect content marketing&lt;/strong&gt; - The team is writing a lot of solid articles on their blog related to Ballerina, but that won't pull in people who aren't already interested in Ballerina. It seems like it would do well to write more articles not directly about Ballerina, but involve Ballerina and can gain traction. Find something popular people want to do, like 'How to build your own Slack bot' and then in the article it's implemented in Ballerina. The reader has goals beyond learning Ballerina, but now they know maybe it's worth learning to accomplish their other ideas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More dev-friendly &amp;amp; most-used libraries&lt;/strong&gt; - Take a look at NPM, PyPi, and others and see what the most heavily used packages are and consider if it's worth getting an initial implementation going in Ballerina. Stripe makes it incredibly easy to use their APIs &amp;amp; docs, and in turn developers love them. Follow that approach, but make sure to consider the 80/20 rule for effort vs reward, hence looking at the most heavily used packages. Make the most common 80% of tasks developers do a joy and they will be ecstatic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(Bonus idea) Native property-based testing&lt;/strong&gt; - It probably wouldn't be a huge driver of adoption, but it would be super cool to see a language natively supporting &lt;a href="https://hypothesis.works/articles/what-is-property-based-testing/"&gt;property-based testing&lt;/a&gt;. Encouraging people to write better tests by default would be a decent selling-point for those who care about code quality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether or not these ideas get implemented, I hope they provide the Ballerina team some useful insight from a different perspective. Ballerina has a lot of solid founding principles, which is what drew me to initially try the language, and with a company already backing it they are starting from a good foundation to survive &amp;amp; thrive. I also wrote up my experience creating a module in Ballerina, where I came up with all these suggestions, so if you'd like to learn more of the developer experience while writing Ballerina you can check out &lt;a href="https://kevinquinn.fun/blog/giving-ballerina-lang-a-twirl/"&gt;Giving Ballerina Lang a Twirl&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@alexdinaut?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;ALEXANDRE DINAUT&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/ballerina?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

</description>
      <category>ballerina</category>
      <category>devrel</category>
    </item>
    <item>
      <title>Giving Ballerina Lang a Twirl</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Sun, 20 Dec 2020 20:43:13 +0000</pubDate>
      <link>https://forem.com/idontremember/giving-ballerina-lang-a-twirl-2ogg</link>
      <guid>https://forem.com/idontremember/giving-ballerina-lang-a-twirl-2ogg</guid>
      <description>&lt;p&gt;I stumbled onto Ballerina a few months back during a random jaunt through the interwebs, and after digging through their site I was intrigued enough by their features &amp;amp; guiding principles to give it an expanded &lt;em&gt;'Hello World!'&lt;/em&gt; test. When I try a new language, I want to get past the toy examples and see what it's actually like to develop. My current method is to build a very simple package based on a popular Open Source module from PyPi or NPM. This can be a handy learning strategy, as you don't have to spend time thinking about what to build or features, you can just copy features directly from existing implementations and focus entirely on learning to put it together. You also go through the full cycle of building, testing, and deploying a module where others can use it, imitating a real world use-case. I spent about an hour reading through docs &amp;amp; examples so I have a rough idea where I can find answers when I get stuck, but I learn best from action, so it was time for the rubber to meet the road. For people who can't resist spoilers, here's the module I created during this process: &lt;a href="https://central.ballerina.io/i_dont_remember/thyme"&gt;thyme&lt;/a&gt;, and &lt;a href="https://i-dont-remember.github.io/thyme/"&gt;docs!&lt;/a&gt;. If you're interested in reading the suggestions I compiled to improve the developer experience &amp;amp; adoption of Ballerina, you can find it &lt;a href="https://kevinquinn.fun/blog/how-ballerina-lang-can-improve-their-developer-experience-adoption/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Ballerina
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ballerina.io/"&gt;Ballerina&lt;/a&gt; is a young language in development by the company WSO2, and is advertised as being "Developer First" and to have built-in functionality so developers have to spend less time worrying about how you can get your code deployed to cloud providers and many of the pieces of building maintainable &amp;amp; reliable applications like documentation &amp;amp; testing. As they say "We plumb, you build!". I've probably butchered their marketing spiel, so to any WSO2 employees reading this, my bad. For the rest of you, go check out their &lt;a href="https://ballerina.io"&gt;landing page&lt;/a&gt; to see if Ballerina is a good fit for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;I don't have much to say on getting up and running, so I'll leave it to their documentation on how to &lt;a href="https://ballerina.io/learn/installing-ballerina/"&gt;install the language&lt;/a&gt; and how to &lt;a href="https://ballerina.io/learn/using-the-cli-tools/"&gt;use the Ballerina CLI tools&lt;/a&gt;. The &lt;a href="https://ballerina.io/learn/quick-tour/"&gt;Quick Tour&lt;/a&gt; will get you the rest of the way with a slightly more advanced &lt;em&gt;'Hello World&lt;/em&gt;' to ensure things are working correctly before you dive deeper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a simple module
&lt;/h2&gt;

&lt;p&gt;Kicking things off, I decided &lt;a href="https://www.npmjs.com/package/dotenv"&gt;Dotenv&lt;/a&gt; would be a good starting point. Dotenv loads configuration information from a &lt;code&gt;.env&lt;/code&gt; file into your process as environment variables. A simple concept, but it touches on useful topics to know in any language, like interacting with the environment your application runs on. I started off right away with writing tests and found it quite helpful, as it has forced me to not only learn how tests work in Ballerina but also tackle a variety of utility tasks you need when writing tests, like generating random numbers, handling errors, working with strings &amp;amp; arrays, and basic flow control. Handling errors is done explicitly, though initially it appeared you could cop out by adding &lt;code&gt;check&lt;/code&gt; before the operation, e.g. &lt;code&gt;int val = check iThrowErrors()&lt;/code&gt;. I was wrong.&lt;/p&gt;

&lt;p&gt;Things went downhill quickly after writing some initial tests. Ballerina currently runs on the JVM and uses hooks into Java to handle some of it's standard library functionality. From other languages I've used I assumed there would be easy access to environment variables, but there is no &lt;code&gt;setenv&lt;/code&gt; capability in the underlying Java package &lt;code&gt;java.lang.System&lt;/code&gt;. This &lt;a href="https://github.com/cdimascio/dotenv-java#faq"&gt;Java Dotenv package&lt;/a&gt; mentions the same issue, so hopefully I'm not missing something obvious. You could implement a rough equivalent by loading from a file into an object, but the &lt;a href="https://ballerina.io/learn/by-example/config-api.html"&gt;Ballerina Config API&lt;/a&gt; seems like a well thought out implementation along those lines, and making a crappier version of it didn't sound all that interesting.&lt;/p&gt;

&lt;p&gt;The lesson here? Check the most important functionality of your idea is possible before doing any of the other work. Luckily this is just for learning, so the time already spent on language features wasn't wasted. That said, a pivot was required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pivoting
&lt;/h2&gt;

&lt;p&gt;I am inspired by projects which focus on creating a great developer experience, like Python's &lt;a href="https://requests.readthedocs.io/en/master/"&gt;Requests module&lt;/a&gt;, "HTTP for Humans". Just because you're writing code doesn't mean it has to be a pain to do common tasks like making web requests or parsing data. Datetime is often one of these pain point. Trying to get your variables in the right format, the right timezone, and doing operations with them sucks without good libraries. Looking over &lt;a href="https://ballerina.io/learn/by-example/time.html"&gt;Ballerina's Time module&lt;/a&gt;, it has useful pieces but it's missing the friendly functions which make people enjoy coding. Developers want to build their projects, not the minutiae like "finding diff between two times". Alright, so building a developer-focused time library will be the goal. Inspiration will come from &lt;a href="https://momentjs.com/"&gt;Momentjs&lt;/a&gt;, a hugely popular Javascript library which simplifies the sucky parts of date &amp;amp; time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a (new) simple module &amp;amp; what I learned
&lt;/h2&gt;

&lt;p&gt;Though the main purpose is learning, I like to have at least a semblance of a goal to drive what I'm doing. The philosophy I took was to wrap and use the native classes as much as possible, making it easy to plug in, while abstracting the annoying bits of date time interaction so developers can get back to building their widgets. For this reason, I decided to build mostly module methods that accept &amp;amp; return the native &lt;code&gt;time:Time&lt;/code&gt; objects. I could have focused on only creating functions around my custom classes, but that makes it harder for someone stumbling onto your project to adopt it as they have to go all in on your implementation.&lt;/p&gt;

&lt;p&gt;On to the experience of actually implementing. Error handling confused me for a good bit, I may just be dense but it tripped me up time and again even after reading the documentation. Their test suite examples could use some work, it doesn't help me to know if you can print something before each function. I want to know how to do test setup and then be able to access those variables, it helps keep a codebase clean &amp;amp; simplify your test cases. I found myself searching through the repo a lot to answer my questions. I also attempted to use fixtures for tests, but as far as I can tell you have to have module global variables to assign to if you want to access anything generated in fixtures. Seems like a pain, maybe there is a feature of Ballerina I missed which passes context between. A few more small thoughts &amp;amp; concepts I ran into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Their use of union types is quite handy. It's convenient to be able to do unions when you want to expect multiple things, like it should be an &lt;code&gt;int&lt;/code&gt;, but occasionally is an error, well then return &lt;code&gt;int|error&lt;/code&gt;. It works even for something wild like &lt;code&gt;[string, int, int|error]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For their logging module, it would be super nice to have something a little cleaner than having to use &lt;code&gt;io.sprintf&lt;/code&gt; everywhere to log variables. Not a huge issue, but really clutters up the space.&lt;/li&gt;
&lt;li&gt;Ran into a bit of trouble with assigning output to an existing variable when the function returns an error. From what I found, cop out way is to create a new variable, check the error, then assign it to your desired.&lt;/li&gt;
&lt;li&gt;I struggled while writing tests trying to understand what the best practice should be for handling errors that happen during tests. The documentation was fairly sparse. Also a nitpicky thing, but the failures from tests should show at the bottom. It's frustrating to have to scroll through all the passing tests to find out what broke.&lt;/li&gt;
&lt;li&gt;Another thing I found while writing tests, thought not necessarily to do with Ballerina, is it's difficult to write only a single example test case after having been introduced to &lt;a href="https://hypothesis.works/"&gt;property-based testing with Python's Hypothesis&lt;/a&gt;. It makes you want to fuzz the crap out of all your code.&lt;/li&gt;
&lt;li&gt;Learn the &lt;code&gt;any&lt;/code&gt; aspect, it's powerful for dealing with maps.&lt;/li&gt;
&lt;li&gt;If you want to use built-ins like &lt;code&gt;int&lt;/code&gt;, you have to use the special &lt;code&gt;'&lt;/code&gt; syntax when importing: &lt;code&gt;import ballerina/lang.'int;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It seems you can't use list of tuples or the &lt;code&gt;any&lt;/code&gt; type when trying to use &lt;code&gt;dataProvider.&lt;/code&gt; &lt;a href="https://ballerina.io/learn/how-to-test-ballerina-code/"&gt;In docs&lt;/a&gt;, it says &lt;em&gt;"The given Ballerina function should return an array of arrays (e.g., string[][] for a test function that accepts string parameters). Each array of the returned array of arrays should have a length similar to the number of arguments of the function"&lt;/em&gt;. Looks like it doesn't support &lt;code&gt;any&lt;/code&gt; though. It would be super useful to be able to do this. Json is available as an option in their docs, which could be close, but it seems like overkill if you just want something simple like a &lt;code&gt;string:int&lt;/code&gt; pair.&lt;/li&gt;
&lt;li&gt;It kept biting me in the butt to not know an easy way to go from &lt;code&gt;int&lt;/code&gt; to &lt;code&gt;string&lt;/code&gt;. I wish I could have found that early on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, I had a decent chunk of a half-way decently tested module, and a lot of ideas about the language. There are a lot of improvements this module could undergo, but a short list would be better repo file structure, using logging correctly instead of &lt;code&gt;println()&lt;/code&gt; , better error handling within tests &amp;amp; adding more property-based tests, and adding more helper functions. It also could have used more of the fancy features available in Ballerina, like Type guards, Elvis operators, custom error types, binding patterns, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outcome &amp;amp; thoughts on the language
&lt;/h2&gt;

&lt;p&gt;After a few hours, I had successfully completed a not totally terrible module and published it to Ballerina Central. The final product is available at &lt;a href="https://central.ballerina.io/i_dont_remember/thyme"&gt;central.ballerina.io/i_dont_remember/thyme&lt;/a&gt;. I really liked the built in support for a code coverage report as well as letting you write documentation inline. Anything to reduce the friction it takes to get more documentation for a codebase is a plus in my book. I had no idea what I was doing and was still able to generate &lt;a href="https://i-dont-remember.github.io/thyme/"&gt;useful documentation&lt;/a&gt; to go along with my module. At first I was not a fan of the error handling system (I have to explicitly handle this again?!?!?), but it grew on me. Now I like it forces the developer to think through error situations and handle them, rather than hand-waving it away and hoping it doesn't explode later. The Ballerina team wrote a &lt;a href="https://medium.com/ballerina-techblog/ballerina-error-handling-part-i-d581f65c0f8d"&gt;3 part series diving deeper into error handling&lt;/a&gt; if you're on your own learning journey. On a personal note, I think my approach to writing in Ballerina is heavily object-oriented (OOP). I tend to think in classes &amp;amp; objects, which might be at odds with how languages like Ballerina, Go, and others operate best. This is something I will need to dive deeper into to expand my personal skillset, as it would be good to understand how to switch thinking for languages that aren't necessarily OOP. Adding a useful article on &lt;a href="https://stackoverflow.com/questions/61309121/how-to-implement-inheritance-in-ballerina"&gt;inheritance in Ballerina&lt;/a&gt;. Of all the decisions the Ballerina team has made, it seems like the most valuable is their easy integration with Java libraries with the &lt;code&gt;bindgen&lt;/code&gt; tool. This opens the massive Java ecosystem for use, which I'm sure will help them in getting companies to adopt knowing they don't have to throw out everything they've built.&lt;/p&gt;

&lt;p&gt;It was interesting to give Ballerina a spin, though my personal goals will keep me with other languages for now. I have a strong feeling it will continue growing over the next few years. With a company backing it, WSO2, they have a strong base to keep it going through the early stages when a language is most likely to lose steam, so I wish them the best of luck with getting it off the ground and into mainstream production use. I also wrote up opportunities I noticed for Ballerina to tweak their developer experience &amp;amp; marketing to grow the community around the language. If you'd like to see my thoughts on what Ballerina can do to become a widely used production language, you can find it &lt;a href="https://kevinquinn.fun/blog/how-ballerina-lang-can-improve-their-developer-experience-adoption/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@alexdinaut?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;ALEXANDRE DINAUT&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/ballerina?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

</description>
      <category>ballerina</category>
      <category>learning</category>
    </item>
    <item>
      <title>Why No-code is a Godsend for Developers</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Fri, 11 Dec 2020 15:37:45 +0000</pubDate>
      <link>https://forem.com/idontremember/why-no-code-is-a-godsend-for-developers-2pld</link>
      <guid>https://forem.com/idontremember/why-no-code-is-a-godsend-for-developers-2pld</guid>
      <description>&lt;p&gt;It's so easy as a developer to dismiss the idea of learning no-code tools. After all, you know how to code, why should you spend time learning tools designed for those who can't? While no-code inherently means a non-technical person is able to operate the tool, from my own experience and listening to leaders in the field like &lt;a href="https://www.indiehackers.com/podcast/180-tara-reed-of-apps-without-code"&gt;Tara Reed&lt;/a&gt;, no-code tools are often complex and lean heavily on concepts from software development. A developer will be able to get up and running much quicker and have a better understanding of a project's tech needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer advantages
&lt;/h2&gt;

&lt;p&gt;The most important experience developers bring to the table for no-code tools is not their knowledge of any particular language, but all the time they spend thinking about the systems they work on and how data flows through an application. No-code tools abstract a lot of boilerplate and let you focus on connecting the pieces together, but they still rely on very similar thought processes as building with code. This gives you an advantage over someone non-technical, who in all likelihood doesn't have to spend time considering how the apps they use function under the hood.&lt;/p&gt;

&lt;p&gt;This second reason will change as the field picks up steam, but the current no-code tools are quite technical &amp;amp; complex to get running. Look at site builders, for instance. Yes, you can build visually. But if I don't know what a good design should look like, I will still end up with an ugly &amp;amp; weird website. Beyond that, making it possible to visually edit CSS isn't more useful to a person who doesn't already know CSS. Even if you're not a frontend wizard, you already have the mindset needed to learn how it works &amp;amp; how to find answers to your questions.&lt;/p&gt;

&lt;p&gt;Developers are also used to getting down and dirty with the technical side of problems. To build something, you will still need to do debugging and handle edge cases just like you would with code, just a lot more of them will be problems with business logic now. You already are used to having weird issues arise and having to dig into these incremental problems before reaching your overarching goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can no-code build everything?
&lt;/h2&gt;

&lt;p&gt;Not every idea can be built with no code, but the landscape of tools and their quality has expanded dramatically in the last few years. Building a web app? &lt;a href="https://bubble.io/blog/tag/how-to/"&gt;Bubble has tutorials&lt;/a&gt; on how to build &lt;a href="https://en.wikipedia.org/wiki/Minimum_viable_product"&gt;MVPs&lt;/a&gt; of the most popular ones like AirBnB, Spotify, Instagram, and more. You can visually build websites with Squarespace, Webflow, and the 5000 other site builders. You can connect events from thousands of apps together in interesting combinations with Zapier, Integromat, and the like. Makerpad has a &lt;a href="https://www.makerpad.co/education"&gt;growing content library&lt;/a&gt; of examples of how to solve problems with different combinations of no-code tools. Even more tutorials can be found on &lt;a href="http://nocode.mba"&gt;nocode.mba&lt;/a&gt;, and there are tons of interviews on these sites and around the web with people who successfully built businesses without any coding required.&lt;/p&gt;

&lt;p&gt;It's possible the problem you want to solve is so unique and complex it can't be done with no-code, and you are forced to get down and dirty with your favorite language. I'd bet a large sum, though, you're overcomplicating the features required to call your solution a success. Do you really need multi-region deployments, incredible fault tolerance, and a hand-crafted authentication system for an internal company app only 10 people will use?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why no-code over code
&lt;/h2&gt;

&lt;p&gt;Do you enjoy spending time wrangling libraries and problems with your environment when you originally sat down to build something awesome? After a couple hours burned, you have 500 Stack Overflow tabs open and just got your repo to a 'Hello World' state. We've all been there, it's frustrating to realize you made no progress on the actual idea. With no-code, you can just make something instead of watching your laptop barf stack traces, despite the tutorial you're following saying it should 'Just Work'. &lt;/p&gt;

&lt;p&gt;With no-code, you save time and frustration when trying to prove out the concept in your head. It goes from a thought to in someone's hands much faster, so there is less time before you start learning about all the assumptions you got wrong. I've built multiple apps for myself thinking they would get used all the time, then stopped using them a week later because it was wishful thinking or I misunderstood my own needs. Luckily, I used no-code tools, so I was only out a fraction of the time if I had developed an app with code.  Spending weeks or months building out similar functionality, just to drop it, would have been incredibly disheartening.&lt;/p&gt;

&lt;p&gt;I struggle in many of my projects to avoid Shiny Object Syndrome. It's so tempting to try out a new framework, a new language, or Serverless, or Kubernetes. Wanting to learn the newest, shiniest thing is awesome for expanding your skillset, but less awesome for actually finishing a functional version of your idea in a reasonable amount of time. You'll spend a ton of effort learning how this Shiny Object operates, and dealing with issues you created from lack of experience. You could argue no-code tools can still fall into this trap, and I'd agree, but you'll find out much faster if a no-code tool can't do what you need, and with quicker development times you're less likely to find a bunch of shiny new things before you've finished.&lt;/p&gt;

&lt;p&gt;No-code helps keep your eye on the prize better, too. When coding it's easy to get sucked into the technical weeds trying to fight through bugs &amp;amp; your own logic to complete a feature, but it might not be worth your energy. Since they are simpler and have more guardrails on what you are able to do, it's easier to stay focused on the overall goal rather than getting sucked into the weeds. If you want to build a web app that helps people pet dogs, you aren't making much progress if all you've done is create an authentication system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro tools
&lt;/h2&gt;

&lt;p&gt;At this point, you're raring to go find out what No-code is all about. There are a million no-code tools and new ones coming out all the time, it's the new hotness to build your own site builder like Squarespace. With so many it can be hard to know where to start, so instead of listing out all the tools from my bookmarks, I'll drop a few I've found easy to get going with. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://carrd.co"&gt;Carrd.co&lt;/a&gt; = Simple, no fuss website builder with good price for the Pro version&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://glideapps.com"&gt;glideapps.com&lt;/a&gt; = Create functional Progressive Web Apps you can use on phone or desktop with Google Sheets&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://zapier.com"&gt;Zapier&lt;/a&gt; &amp;amp; &lt;a href="https://integromat.com"&gt;Integromat&lt;/a&gt;  = The glue between every possible app you use. Set events from your desired source to cause actions in another app, .e.g. new email comes in and create a Contact in Salesforce&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://bubble.io"&gt;Bubble.io&lt;/a&gt; = Visually build fully-fledged web apps. Haven't gotten to dive too deep with this yet, but the potential seems amazing.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://airtable.com"&gt;Airtable.com&lt;/a&gt; = Like a spreadsheet &amp;amp; a database combined. Integrates really easily with websites to display data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No-code has been around since the days of Dreamweaver and is only building momentum. With it's continued growth, it's worth it for developers to at least understand what these tools are capable of. As tools continue to improve, businesses will more often use them to automate operations and free up devs to focus on the interesting challenges. Next time you want to learn something new, give a few no-code tools a try and see what's possible. You'll be surprised at how quickly you can slap together something functional instead of getting your libraries and environment in order.&lt;/p&gt;

</description>
      <category>nocode</category>
      <category>mvp</category>
      <category>learning</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Coding is Dangerous for Developers</title>
      <dc:creator>Kevin</dc:creator>
      <pubDate>Thu, 30 Jul 2020 05:00:00 +0000</pubDate>
      <link>https://forem.com/idontremember/coding-is-dangerous-for-developers-3n20</link>
      <guid>https://forem.com/idontremember/coding-is-dangerous-for-developers-3n20</guid>
      <description>&lt;p&gt;Coding is a tool, but there is so much depth within that it often feels like an art form. There are many ways to approach the same problem with different frameworks, languages, and architectures. The end product could be the same but look totally different under the hood. This is the underlying reason coding is so dangerous for developers. With so much freedom, we can lose sight of the original goal: solving a problem.&lt;/p&gt;

&lt;p&gt;It’s very easy to dive into a project and decide that you should build it from scratch because you’re a developer, you can code! You have the magic power to bring amazing things into being with only a keyboard! You start building, troubleshooting bugs you didn’t imagine would pop up to ruin your plans. Then, the features you pictured in your head turn out to be significantly more complicated. Hours later, you discover a spreadsheet and Google form could solve it in a few minutes. It’s an amazing feeling to see your work come to life, but it can blind us to the simple solutions that we can discover with a little more patience.&lt;/p&gt;

&lt;p&gt;Alright, so you’ve searched around and mulled it over, it actually does make sense to build something new to solve your problem. Awesome, you’ve been meaning to try out that brand new language you found! Chances are, you won’t be the last person working on this solution though. Will the next person have the experience &amp;amp; knowledge to maintain a niche language? Does it put your company in a position where their &lt;a href="https://en.wikipedia.org/wiki/Bus_factor"&gt;bus factor&lt;/a&gt; is just you?&lt;/p&gt;

&lt;p&gt;Fine, we decided it made more sense to use technologies that are more stable and commonplace, even if they aren’t as exciting. Well, this should incorporate some machine-learning background jobs to give us insights on the best path forward, right? Maybe a little blockchain under the hood? It also needs to be multi-region &amp;amp; scalable to the nth degree to make sure we can handle the millions of users waiting around the corner for this. If it’s not fully resilient, they won’t want to use it! Developers love adding features and improving software, especially the ones who care about the craft. Like artists, they will never be satisfied with their work and can always find something to tweak. Eventually, the energy is better spent elsewhere. We forget to ask ourselves “Are these features actually what people need/asked for?".&lt;/p&gt;

&lt;p&gt;The danger in these situations is when coding becomes a ‘feel-productive’ task while not moving you closer to a solution. This happens because of the disconnect between the actions taken that let us check off boxes (writing a new feature, deploying the service, etc.) with the actual planned outcome. It feels great to see progress, but if that path leads to nowhere you are just wasting time.&lt;/p&gt;

&lt;p&gt;The possibilities available when you know how to code are staggering. It’s easy to become lost in the void of possibilities, losing your tether to your original goal. Next time you’re looking over a project ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does it need code?&lt;/li&gt;
&lt;li&gt;What is the Simple Lovable Complete (&lt;a href="https://blog.asmartbear.com/slc.html"&gt;SLC&lt;/a&gt;) version of the solution?&lt;/li&gt;
&lt;li&gt;What tech stack makes the most sense for the context of the project, rather than what is shiny and interesting?&lt;/li&gt;
&lt;li&gt;Am I actually making progress toward this goal or do I just feel productive? (Ask this one regularly)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>productivity</category>
      <category>development</category>
    </item>
  </channel>
</rss>
