<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Josh Hawkins</title>
    <description>The latest articles on DEV Community by Josh Hawkins (@hawkinjs).</description>
    <link>https://dev.to/hawkinjs</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%2F3844%2FfAZukm5I.jpg</url>
      <title>DEV Community: Josh Hawkins</title>
      <link>https://dev.to/hawkinjs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hawkinjs"/>
    <language>en</language>
    <item>
      <title>Using Hubot to Mention Everyone in GroupMe</title>
      <dc:creator>Josh Hawkins</dc:creator>
      <pubDate>Sat, 28 Sep 2019 01:07:18 +0000</pubDate>
      <link>https://dev.to/hawkinjs/using-hubot-to-mention-everyone-in-groupme-3ge</link>
      <guid>https://dev.to/hawkinjs/using-hubot-to-mention-everyone-in-groupme-3ge</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;this post was originally written on 2016/10/11 for my personal blog on hawkins.is&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hawkins"&gt;
        hawkins
      &lt;/a&gt; / &lt;a href="https://github.com/hawkins/groupme-at-all"&gt;
        groupme-at-all
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Hubot chat bot to mention every user in a GroupMe channel all at once.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
&lt;a rel="noopener noreferrer" href="https://raw.githubusercontent.com/hawkins/groupme-at-all/master/logo.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N0H6CAIR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/hawkins/groupme-at-all/master/logo.png" width="128"&gt;&lt;/a&gt; Unofficial GroupMe @all&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;@all&lt;/strong&gt; is a third-party GroupMe chat bot built on &lt;a href="http://hubot.github.com"&gt;Hubot&lt;/a&gt;. It was configured to be
deployed on &lt;a href="http://www.heroku.com" rel="nofollow"&gt;Heroku&lt;/a&gt; to get you up and running as quick as possible.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This project is not sponsored by, endorsed by, or managed by GroupMe. This is entirely a third-party project and is 100% open sourced. Use at your own risk.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;For questions or concerns, please contact the repository owner, not GroupMe.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
Features&lt;/h2&gt;
&lt;p&gt;Mention (tag) everyone in your group at once!&lt;/p&gt;
&lt;p&gt;Blacklist users who don't need notifications.&lt;/p&gt;
&lt;p&gt;Deploy to heroku easily!&lt;/p&gt;
&lt;h2&gt;
Using&lt;/h2&gt;
&lt;p&gt;In practice, users can simply write &lt;code&gt;@all&lt;/code&gt; anywhere in their message to tag everyone in the group. The bot will repeat their message and tag everyone in the group in their repeated message.&lt;/p&gt;
&lt;p&gt;You can also control a blacklist/whitelist via chat commands as well. This is a bit more advanced, so please review the source code yourself if you're…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/groupme-at-all"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;If you're like me, you love &lt;code&gt;@everyone&lt;/code&gt; in Slack, and your &lt;a href="https://groupme.com/"&gt;group&lt;/a&gt; &lt;em&gt;desperately&lt;/em&gt; needs that feature.&lt;/p&gt;

&lt;p&gt;Maybe there are free cookies on the Quad on campus; you've gotta tell your friends! Why can't you just say &lt;code&gt;"@all of you get out to the quad for free cookies!"&lt;/code&gt;?!&lt;/p&gt;

&lt;p&gt;After following along with this blog, &lt;em&gt;you can!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;We'll be writing a &lt;a href="https://hubot.github.com/"&gt;Hubot&lt;/a&gt; script using the &lt;a href="https://github.com/AdamEdgett/hubot-groupme"&gt;hubot-groupme&lt;/a&gt; adapter to deploy on Heroku that will listen for messages with "@all" in them in a given GroupMe room. We'll deploy our bot for free* on &lt;a href="https://heroku.com"&gt;Heroku&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Free for the first 550 (or 1000 if you verify a credit card) hours per month&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, we can tag everyone in the group (minus a blacklist) by saying anything with "@all" in the message.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can host the bot on your own server instead, but I'm writing this for anyone to be able to handle. If you know how to host bots / scripts on your own machine, you shouldn't have any trouble translating the few Heroku-specific instructions. Moving on...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How it works (technical)
&lt;/h2&gt;

&lt;p&gt;If you're the type of guy that throws the manuals away when you buy a product, scroll past this section.&lt;/p&gt;

&lt;p&gt;Hubot uses regular expressions to match messages in a chat room. Groupme-at-all listens for &lt;code&gt;/.*@all.*/i&lt;/code&gt; to act. It will construct a &lt;code&gt;message&lt;/code&gt; to send in an HTTP POST request to the GroupMe v3 API.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;a href="https://dev.groupme.com/docs/v3"&gt;GroupMe's API&lt;/a&gt; is not well-documented. Multiple features are completely or partially ignored. In fact, &lt;b&gt;mentions are not documented at all!&lt;/b&gt; So tagging, as far as I can determine, is handled similarly to an image or emoji attachment, which maps a User ID to a string of characters, which appear as bold text to users.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So after receiving the command, the bot maps every user's ID to an individual character in the message for a tag, since some GroupMe versions seem to require a tag of &lt;code&gt;length &amp;gt;= 1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, the bot stringify's the &lt;code&gt;message&lt;/code&gt; object and sends the HTTP POST request, logging the results.&lt;/p&gt;

&lt;p&gt;Check out the actual code used to do all this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight coffeescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hear&lt;/span&gt; &lt;span class="sr"&gt;/(.*)@all(.*)/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;"""@all command"""&lt;/span&gt;
  &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;robot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;brain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;
    &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Please check the GroupMe, everyone."&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="s"&gt;'text'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'bot_id'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bot_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'attachments'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s"&gt;"loci"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
      &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"mentions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;"user_ids"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;blacklist&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="na"&gt;loci&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="na"&gt;user_ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"api.groupme.com"&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"/v3/bots/post"&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"POST"&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;'Content-Length'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;
      &lt;span class="s"&gt;'Content-Type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'application/json'&lt;/span&gt;
      &lt;span class="s"&gt;'X-Access-Token'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;request&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;on&lt;/span&gt; &lt;span class="s"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;on&lt;/span&gt; &lt;span class="s"&gt;'end'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt; &lt;span class="s"&gt;"[GROUPME RESPONSE] &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  How to install groupme-at-all
&lt;/h2&gt;

&lt;p&gt;Installing this is easy, but there's a few steps...&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a GroupMe Bot
&lt;/h3&gt;

&lt;p&gt;GroupMe has native bot integration - they're kind of just an API token for your scripts to use, but that's all Hubot needs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;a href="https://dev.groupme.com/"&gt;the GroupMe dev site&lt;/a&gt; and log in with your GroupMe account&lt;/li&gt;
&lt;li&gt;Click &lt;a href="https://dev.groupme.com/bots/new"&gt;create a new bot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Choose the group your bot will live in&lt;/li&gt;
&lt;li&gt;Name your bot (something like "All" will do)&lt;/li&gt;
&lt;li&gt;Provide a callback URL (doesn't really matter for us, but has to be unique)&lt;/li&gt;
&lt;li&gt;Provide a URL for an image for your bot to use (my favorite is All brand laundry detergent)&lt;/li&gt;
&lt;li&gt;Click submit&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now you'll be lead to a page with your GroupMe bot's &lt;code&gt;ID&lt;/code&gt;, &lt;code&gt;ROOM_ID&lt;/code&gt;, and &lt;code&gt;TOKEN&lt;/code&gt;. Copy these guys into a note, you'll need them again in a moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup Hubot
&lt;/h3&gt;

&lt;p&gt;Follow the instructions at &lt;a href="https://hubot.github.com/docs/"&gt;Hubot Getting Started&lt;/a&gt; to install and wrap your head around Hubot. Just remember to use the adapter &lt;code&gt;groupme&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's easy, so just get the general idea after you install it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup @all
&lt;/h3&gt;

&lt;p&gt;Now you're ready for the fun stuff! I've already written the code for you, so all you need to do is...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fork my &lt;a href="https://github.com/hawkins/groupme-at-all"&gt;groupme-at-all repository&lt;/a&gt; to your own GitHub account. (Bonus points if you "Star" it ;) )&lt;/li&gt;
&lt;li&gt;Log in (or create an account) to Heroku&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dashboard.heroku.com/new?org=personal-apps"&gt;Create a new app&lt;/a&gt; on Heroku&lt;/li&gt;
&lt;li&gt;Choose to deploy from your GitHub and select the repo&lt;/li&gt;
&lt;li&gt;Configure environment variables (see below)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configure Environment Variables
&lt;/h3&gt;

&lt;p&gt;Hubot is centered around environment variables for accessing sensitive information.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load your app's settings and click "Reveal Config Vars"&lt;/li&gt;
&lt;li&gt;Add the config variables and the appropriate values from your GroupMe Bot you made earlier:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HUBOT_GROUPME_BOT_ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HUBOT_GROUPME_ROOM_ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HUBOT_GROUPME_TOKEN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Test Your Bot
&lt;/h3&gt;

&lt;p&gt;At this point, you should be good to go!&lt;/p&gt;

&lt;p&gt;To start the bot, start your Heroku app (or run &lt;code&gt;./bin/hubot -a groupme&lt;/code&gt; from your repo's root directory if you're not using Heroku).&lt;/p&gt;

&lt;p&gt;Give it a moment to start up, and then use your regular GroupMe account (phone, desktop, etc) to say "Testing @all!" in your group. You should see your bot reply "Testing @all" in &lt;b&gt;bold text&lt;/b&gt; since it tagged everyone in your group!&lt;/p&gt;

&lt;h3&gt;
  
  
  Success!
&lt;/h3&gt;

&lt;p&gt;Hooray!! You can finally tell &lt;i&gt;EVERYONE&lt;/i&gt; about Free Chips &amp;amp; Queso day at Moe's!&lt;/p&gt;

&lt;p&gt;Your bot's good to go now, but there's some optional configuration you may prefer to do still, such as keep your bot awake.&lt;/p&gt;

&lt;h3&gt;
  
  
  (Optional) Further Config
&lt;/h3&gt;

&lt;p&gt;Here's what else the bot can do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a ping bot to keep your Heroku instance alive&lt;/li&gt;
&lt;li&gt;Handle a blacklist to not mention specific users by ID&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Begone, sleep!
&lt;/h4&gt;

&lt;p&gt;Speaking of writing blog posts at 2am... your bot would love to be an insomniac. Otherwise, he'll fall asleep &lt;i&gt;after only an hour&lt;/i&gt; only to be awoken manually later. So, if you're using Heroku, you've got a few options...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pay Heroku for a better plan. If you plan to do some serious work here, I'd suggest this. If it's just for your Sunday Brunch club, maybe not.&lt;/li&gt;
&lt;li&gt;Or set up a ping site like &lt;a href="https://www.pingdom.com/"&gt;Pingdom&lt;/a&gt; or &lt;a href="http://uptimerobotcom/"&gt;Uptime Robot&lt;/a&gt; to hit your Heroku App's URL periodically. This is the easiest option if you don't want to code anything.&lt;/li&gt;
&lt;li&gt;Or set up &lt;a href="https://github.com/hubot-scripts/hubot-heroku-keepalive"&gt;heroku-keep-alive&lt;/a&gt;. I've had some issues with this one, but some people like it!&lt;/li&gt;
&lt;li&gt;Or finally make a simple &lt;code&gt;setInterval&lt;/code&gt; in your app. Something as easy as 
&lt;code&gt;var http = require("http"); setInterval(() =&amp;gt; (http.get("http://your-app-name-goes-here.herokuapp.com"), 300000));&lt;/code&gt; to ping your Heroku app every 5 minutes will do.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Remember, if you're on a free Heroku plan, you're limited to 550 free hours, or 1000 if you verify a credit card. If you host this bad boy on your bot already should be a fully-functioning insomniac. Congrats!&lt;/p&gt;

&lt;h4&gt;
  
  
  Blacklist
&lt;/h4&gt;

&lt;p&gt;Groupme-at-all uses a redis server controlled by Hubot to keep a persistent blacklist. This can be a tricky to get set up, so I'll leave this mostly as an excercise for you to understand.&lt;/p&gt;

&lt;p&gt;In a nutshell tho, here's what you'll do...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a redis server&lt;/li&gt;
&lt;li&gt;Configure hubot to use this server&lt;/li&gt;
&lt;li&gt;Say "blacklist @so-and-so" to blacklist "so-and-so" in GroupMe&lt;/li&gt;
&lt;li&gt;Cry that people don't want to be notified about free food trucks outside the office&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But, you don't exactly &lt;em&gt;need&lt;/em&gt; a redis server. The only issue is, that without it, your blacklist will reset &lt;em&gt;every time your bot sleeps.&lt;/em&gt; That's pretty useless, but if your bot never sleeps then spend this time writing your own post on how computers are everything a college student ever dreamed of being in finals week.&lt;/p&gt;

</description>
      <category>node</category>
      <category>hubot</category>
      <category>groupme</category>
      <category>bot</category>
    </item>
    <item>
      <title>Leveraging Travis-CI for Continuous Deployment to Publish Compiled Binaries to GitHub</title>
      <dc:creator>Josh Hawkins</dc:creator>
      <pubDate>Thu, 25 Apr 2019 03:02:32 +0000</pubDate>
      <link>https://dev.to/hawkinjs/leveraging-travis-ci-for-continuous-deployment-to-publish-compiled-binaries-to-github-2k06</link>
      <guid>https://dev.to/hawkinjs/leveraging-travis-ci-for-continuous-deployment-to-publish-compiled-binaries-to-github-2k06</guid>
      <description>&lt;p&gt;Recently I wrote a binary called "&lt;a href="https://github.com/hawkins/watchdog" rel="noopener noreferrer"&gt;Watchdog&lt;/a&gt;" in Rust on my Mac that I wanted to take with me to Linux systems I frequent, but I couldn't bring a Rust compiler to these systems for unrelated reasons. This meant I had to cross-compile my application and ship just the binary.&lt;/p&gt;

&lt;p&gt;Now, I'm taking a few liberties in this post to showcase this as a proof-of-concept, but most notably of these is that &lt;em&gt;I am not handling dynamically-linked libraries&lt;/em&gt;. I'm shipping a standalone binary, that is to say the one file I build has everything it needs to run, assuming you run it on the correct platform. Or, so I naively hope - it can definitely be more complicated than that.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is cross-compilation?
&lt;/h2&gt;

&lt;p&gt;Cross-compilation is compiling your code on one platform (for me, Mac OS) to run on another platform (for me, Linux). If you're more familiar with languages like Ruby, JavaScript, and Python, this might not make a lot of sense since your code already runs on different platforms most likely. But for compiled languages like Rust, C++, C, etc., we have to compile it to code that can run on certain platforms. That's what we're doing here, we're compiling my Rust code on my Mac to run on another computer that runs Linux.&lt;/p&gt;

&lt;p&gt;You can configure cross-compilation on your local computer, but it can be difficult, and often has some big quirks. I've dealt with this recently when &lt;a href="https://github.com/hawkins/district-doughnut" rel="noopener noreferrer"&gt;cross-compiling a lambda function in rust for mac-&amp;gt;linux&lt;/a&gt;. In particular, getting OpenSSL to behave was an absolute nightmare and killed my motivation for months. Eventually, I kicked the problem to the curb and said I'll bring in Docker just to work around it. Your mileage may vary.&lt;/p&gt;

&lt;p&gt;So instead of xcompiling locally, we'll leverage &lt;a href="https://travis-ci.org" rel="noopener noreferrer"&gt;Travis CI&lt;/a&gt; to do this natively for free. Technically it's &lt;em&gt;not cross-compiling, since Travis will compile it in a Linux VM&lt;/em&gt;, but the accomplished goal is the same - building my application for platforms I can't otherwise. So, if you'd like to chew me out for click-baiting, I welcome it - I just couldn't think of a better way to word what we're doing here!&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Is Your Code?
&lt;/h2&gt;

&lt;p&gt;You can see all the code I worked on for this post on my GitHub project! &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;
        hawkins
      &lt;/a&gt; / &lt;a href="https://github.com/hawkins/watchdog" rel="noopener noreferrer"&gt;
        watchdog
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      ⚠️ Watch filesystem for changes and then run a command
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Watchdog&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;⚠️ Watch filesystem for changes and then run a command&lt;/p&gt;
&lt;p&gt;Great for automatically running &lt;code&gt;make test&lt;/code&gt; or similar commands in response to file changes.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;TODO: This isn't stable yet, but here's the output for &lt;code&gt;--help&lt;/code&gt; at 0.2:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;USAGE:
    watchdog [FLAGS] [OPTIONS] &amp;lt;COMMAND&amp;gt; [-- &amp;lt;PATH&amp;gt;...]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information
    -v, --verbose    Enables verbose output

OPTIONS:
    -g, --glob &amp;lt;GLOB&amp;gt;    Glob used for matching files

ARGS:
    &amp;lt;COMMAND&amp;gt;    Command ran on response to changes
    &amp;lt;PATH&amp;gt;...    File path(s) used for matching files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Goals&lt;/h2&gt;

&lt;/div&gt;
&lt;ul class="contains-task-list"&gt;
&lt;li&gt;Easy, inuitive way to select files to watch
&lt;ul class="contains-task-list"&gt;
&lt;li class="task-list-item"&gt;
 Regular expressions (#1)&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 Globs
&lt;ul&gt;
&lt;li&gt;Both via your shell and via Rust internals, pick your poison!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 Explicit file paths&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 Sensible GNU &lt;code&gt;make&lt;/code&gt; interop (#4)&lt;/li&gt;
&lt;li&gt;Simple, out-of-the-way API
&lt;ul&gt;
&lt;li&gt;It's a simple problem. Therefore, keep the solution simple, too, stupid.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/watchdog" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Initial Travis Config
&lt;/h2&gt;

&lt;p&gt;Travis lets us configure it via code, so my initial &lt;code&gt;.travis.yml&lt;/code&gt; file before I set up "cross-compilation" looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rust&lt;/span&gt;
&lt;span class="na"&gt;rust&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;stable&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;beta&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nightly&lt;/span&gt;
&lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;allow_failures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;rust&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nightly&lt;/span&gt;
  &lt;span class="na"&gt;fast_finish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;linux&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;osx&lt;/span&gt;
&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# This part's not very relevant to this blog post, we'll add a new deploy step though, so I'll keep it as an example of what you might already have&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo&lt;/span&gt;
  &lt;span class="na"&gt;skip_cleanup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$TRAVIS_RUST_VERSION&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;stable&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$TRAVIS_OS_NAME&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;linux"&lt;/span&gt;
    &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you're not familiar with Travis, I'll give you a brief summary of what this config does.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We run a "matrix" build (multiple platforms and rust versions) to test both stable rust versions and nightly builds of their rust compiler on different platforms.&lt;/li&gt;
&lt;li&gt;Nightly builds can fail, since those compilers aren't final and may have bugs, so don't mark these build fails as an overall failure if that's all that went wrong&lt;/li&gt;
&lt;li&gt;When I tag a release in git, publish this as a new version of the crate (rust package) in the global repository so other rust users can grab the latest version just released&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note that there's no talk of publishing binaries yet - because that's the new part we'll add today!&lt;/p&gt;
&lt;h2&gt;
  
  
  Travis Releases Provider
&lt;/h2&gt;

&lt;p&gt;GitHub already has a sweet releases feature, and I happen to be using it for Watchdog, so I'd like to publish my built binaries there so users can find it easily if they, like me on my linux machines, can't access a Rust compiler.&lt;/p&gt;

&lt;p&gt;Travis CI is super rad, so naturally they already have &lt;a href="https://docs.travis-ci.com/user/deployment/releases/" rel="noopener noreferrer"&gt;swell support for GitHub Releases&lt;/a&gt;! Lucky us!&lt;/p&gt;

&lt;p&gt;To use this provider, we'll have to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new personal access token on GitHub for Travis to use to upload the binaries to your releases: &lt;a href="https://github.com/settings/tokens" rel="noopener noreferrer"&gt;https://github.com/settings/tokens&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add them to your Travis project:

&lt;ul&gt;
&lt;li&gt;In your terminal, run these commands:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd &amp;lt;Your project&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gem install travis&lt;/code&gt; (you'll of course need ruby + rubygems, which Mac OS comes with)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;travis encrypt&lt;/code&gt; (then follow the provided instructions)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add the below code to your &lt;code&gt;.travis.yml&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;releases&lt;/span&gt;
    &lt;span class="na"&gt;skip_cleanup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$TRAVIS_RUST_VERSION&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;stable"&lt;/span&gt;
      &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;target/release/watchdog&lt;/span&gt; &lt;span class="c1"&gt;# where `watchdog` is your built rust binary&lt;/span&gt;
    &lt;span class="na"&gt;api_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# this part from your `travis encrypt` command&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Testing Travis Changes
&lt;/h2&gt;

&lt;p&gt;I'm no Travis expert, so maybe a commenter can help us both learn a better way to test this, but here's how I test changes to my travis config:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Commit the change in git

&lt;ul&gt;
&lt;li&gt;To save your sanity, comment out the &lt;code&gt;branch: master&lt;/code&gt; part so you can do this on a throw-away branch&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tag the commit&lt;/li&gt;
&lt;li&gt;Push to GitHub (remembering to push with tags, i.e. &lt;code&gt;git push --tags&lt;/code&gt;!)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Travis will then run your new build.&lt;/p&gt;
&lt;h2&gt;
  
  
  Connecting the Dots
&lt;/h2&gt;

&lt;p&gt;The above config won't quite work for us though, so we have to be a little more clever about what we send to GitHub. &lt;/p&gt;

&lt;p&gt;If you added that last section like I suggested, you'll notice that &lt;code&gt;target/release/watchdog&lt;/code&gt; doesn't exist. This is because by default, Travis builds in Debug mode (notice the absence of Rust's required &lt;code&gt;--release&lt;/code&gt; field for release mode builds in the "Default script" setting, implying it's Debug):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbbas2erbge80zodm45me.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbbas2erbge80zodm45me.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want to publish our binary in Release mode, so let's modify our yaml to build in Release mode by adding this key to the root of the yaml:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo build --verbose --release; cargo test --verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Sweet. Now if you tested it, you should see that Travis is a little slower (release builds take a bit more work!) but your file should be uploaded to the GitHub release! 🎉&lt;/p&gt;
&lt;h2&gt;
  
  
  But Wait, There's More!
&lt;/h2&gt;

&lt;p&gt;Notice how we build for two platforms, but only see one binary in the release? And we can't tell what platform its built for because its just named &lt;code&gt;watchdog&lt;/code&gt;? That's no good, if I downloaded that on my linux box, I have no idea if I'm downloading a linux binary or a mac binary. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is there only one?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well, admitting that I didn't encounter this myself (so your exact error may vary) because I did this out of order, these Travis builds run out of order and concurrently, so your linux build may upload a file named "watchdog", only to be overwritten by your mac build when it uploads a different file named "watchdog". So, we have a race condition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How can we get both binaries to upload and not overwrite each other?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well, we can get two birds with one stone here, by also indicating in the file name whether it is built for linux or mac. The idea is we have to give the files on different builds different names so we don't overwrite them, so we might as well specify which platform they're built for to be clearer to users too.&lt;/p&gt;
&lt;h2&gt;
  
  
  Renaming Your Binary
&lt;/h2&gt;

&lt;p&gt;This was the fun part for me, tracking down how to rename binaries so we could upload multiple to GitHub and specify their platform. It only reinforced my love for Travis when I learned that it provided all the building blocks I needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;before_deploy&lt;/code&gt; &lt;a href="https://docs.travis-ci.com/user/deployment/releases/#setting-the-tag-at-deployment-time" rel="noopener noreferrer"&gt;script step&lt;/a&gt; to run whatever I want &lt;em&gt;after&lt;/em&gt; we run the &lt;code&gt;script&lt;/code&gt; step yet &lt;em&gt;before&lt;/em&gt; we run the &lt;code&gt;deploy&lt;/code&gt; step&lt;/li&gt;
&lt;li&gt;a collection of &lt;a href="https://docs.travis-ci.com/user/environment-variables" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt; to tell all sorts of things about our builder, like its platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that in mind, we can just connect the dots to rename the binary before we upload it in the deploy step:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;before_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mv target/release/watchdog "target/release/watchdog-$TRAVIS_TAG-$TRAVIS_OS_NAME"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;But, if we want to support having multiple deploy steps (publishing to crates.io AND GitHub releases, for instance), this will actually cause an error and fail the whole build. That's because this &lt;code&gt;before_deploy&lt;/code&gt; script gets called once &lt;em&gt;for each deploy step&lt;/em&gt;. That actually burned me and caused my "final" build to fail, because the second time it was called, &lt;code&gt;mv&lt;/code&gt; threw an error because &lt;code&gt;target/release/watchdog&lt;/code&gt; didn't exist anymore. So, to account for that, you'll need a little bash-fu to update your yaml once more:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;before_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;[[&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-f&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;target/release/watchdog&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;]];&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;then&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;mv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;target/release/watchdog&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;target/release/watchdog-$TRAVIS_TAG-$TRAVIS_OS_NAME&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;fi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;What this modification does is use bash to only run &lt;code&gt;mv&lt;/code&gt; if the file exists, so we won't error out and fail the whole Travis build if we have multiple deploy steps. Anyways....&lt;/p&gt;

&lt;p&gt;Rad, now our binary is named something like &lt;code&gt;watchdog-v0.2.0-linux&lt;/code&gt;! Now we can adjust our deploy step to find this new name pattern:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;releases&lt;/span&gt;
    &lt;span class="na"&gt;skip_cleanup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$TRAVIS_RUST_VERSION&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;stable"&lt;/span&gt;
      &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
    &lt;span class="na"&gt;file_glob&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- note this new field&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;target/release/watchdog-*&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- note the `-*`&lt;/span&gt;
    &lt;span class="na"&gt;api_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# this part from your `travis encrypt` command&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The key here is to use globbing to find the new binary, since we can't hardcode the name anymore. We have to enable that with &lt;code&gt;file_glob: true&lt;/code&gt;, then we can add &lt;code&gt;-*&lt;/code&gt; to the end of our file name to find the new name pattern easily!&lt;/p&gt;

&lt;p&gt;Testing this now should show a GitHub Release with your two platform's binaries both uploaded, finally. Way to go! 🎉&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Travis&lt;/th&gt;
&lt;th&gt;GitHub&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsga33kg90g1td9kycr0r.png"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxlgt1spmhtlg0qyiihm9.png"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Putting it All Together
&lt;/h2&gt;

&lt;p&gt;Here's the final Travis config we used:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rust&lt;/span&gt;
&lt;span class="na"&gt;rust&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;stable&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;beta&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nightly&lt;/span&gt;
&lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;allow_failures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;rust&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nightly&lt;/span&gt;
  &lt;span class="na"&gt;fast_finish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;linux&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;osx&lt;/span&gt;
&lt;span class="na"&gt;before_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mv target/release/watchdog "target/release/watchdog-$TRAVIS_TAG-$TRAVIS_OS_NAME"&lt;/span&gt;
&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo&lt;/span&gt;
    &lt;span class="na"&gt;skip_cleanup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$TRAVIS_RUST_VERSION&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;stable&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$TRAVIS_OS_NAME&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;linux"&lt;/span&gt;
      &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# ;)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;releases&lt;/span&gt;
    &lt;span class="na"&gt;skip_cleanup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$TRAVIS_RUST_VERSION&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;stable"&lt;/span&gt;
      &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;target/release/watchdog-*&lt;/span&gt;
    &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# ;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And that's it! Now we've got a Travis config that allows us to build and publish binaries for any platform Travis supports whenever we tag a release.&lt;/p&gt;

&lt;p&gt;You can check out my release I cut to demonstrate this here: &lt;a href="https://github.com/hawkins/watchdog/releases/tag/v0.2.5" rel="noopener noreferrer"&gt;https://github.com/hawkins/watchdog/releases/tag/v0.2.5&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Once more, you can review all this in action with a real Rust project on my GitHub. And if you have any questions, feel free to leave either a comment on this blog post, and issue on the below repo, or shoot me an email!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;
        hawkins
      &lt;/a&gt; / &lt;a href="https://github.com/hawkins/watchdog" rel="noopener noreferrer"&gt;
        watchdog
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      ⚠️ Watch filesystem for changes and then run a command
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Watchdog&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;⚠️ Watch filesystem for changes and then run a command&lt;/p&gt;

&lt;p&gt;Great for automatically running &lt;code&gt;make test&lt;/code&gt; or similar commands in response to file changes.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;TODO: This isn't stable yet, but here's the output for &lt;code&gt;--help&lt;/code&gt; at 0.2:&lt;/p&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;USAGE:
    watchdog [FLAGS] [OPTIONS] &amp;lt;COMMAND&amp;gt; [-- &amp;lt;PATH&amp;gt;...]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information
    -v, --verbose    Enables verbose output

OPTIONS:
    -g, --glob &amp;lt;GLOB&amp;gt;    Glob used for matching files

ARGS:
    &amp;lt;COMMAND&amp;gt;    Command ran on response to changes
    &amp;lt;PATH&amp;gt;...    File path(s) used for matching files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Goals&lt;/h2&gt;

&lt;/div&gt;

&lt;ul class="contains-task-list"&gt;
&lt;li&gt;Easy, inuitive way to select files to watch
&lt;ul class="contains-task-list"&gt;
&lt;li class="task-list-item"&gt;
 Regular expressions (#1)&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 Globs
&lt;ul&gt;
&lt;li&gt;Both via your shell and via Rust internals, pick your poison!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li class="task-list-item"&gt;

 Explicit file paths&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li class="task-list-item"&gt;

 Sensible GNU &lt;code&gt;make&lt;/code&gt; interop (#4)&lt;/li&gt;

&lt;li&gt;Simple, out-of-the-way API

&lt;ul&gt;
&lt;li&gt;It's a simple problem. Therefore, keep the solution simple, too, stupid.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/watchdog" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>ci</category>
      <category>travis</category>
      <category>rust</category>
      <category>github</category>
    </item>
    <item>
      <title>The Story of How I wrote A CLI for Your Bookmarks using Larder.io</title>
      <dc:creator>Josh Hawkins</dc:creator>
      <pubDate>Sun, 28 Oct 2018 18:06:48 +0000</pubDate>
      <link>https://dev.to/hawkinjs/the-story-of-how-i-wrote-a-cli-for-your-bookmarks-using-larderio-411c</link>
      <guid>https://dev.to/hawkinjs/the-story-of-how-i-wrote-a-cli-for-your-bookmarks-using-larderio-411c</guid>
      <description>&lt;p&gt;The story starts when I began using &lt;a href="https://larder.io" rel="noopener noreferrer"&gt;Larder&lt;/a&gt; recently, and while I loved the mobile app and browser integrations, I really just wanted to use it from the command line too. I checked the roadmap and found that a CLI was suggested back in 2016, but there had been no other motion on that effort. A quick glance revealed they had a &lt;a href="http://developer.larder.io" rel="noopener noreferrer"&gt;nice and simple API&lt;/a&gt;, so I popped open a terminal and got to work.&lt;/p&gt;

&lt;p&gt;I've been writing more and more Ruby lately, and I've been loving it! I thought this would be a fun time to become a publisher of ruby gems and not just a consumer, so I set out to write the project in Ruby. (Also, I heard great things about a few key Ruby libraries that would make this effortless, but we'll get to that.) It wasn't long before I had &lt;a href="https://github.com/hawkins/lard/commit/621385ea2eb602fcdc57da9c574dc9799c0d66a8" rel="noopener noreferrer"&gt;a first commit&lt;/a&gt; that printed out info on a user based on a provided token from a &lt;code&gt;yaml&lt;/code&gt; file. Not half an hour later, I had added &lt;a href="https://github.com/hawkins/lard/commit/83598fe557932de591e80a3ffb96f8b1e4d1c681" rel="noopener noreferrer"&gt;&lt;code&gt;lard folders&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/hawkins/lard/commit/fa0b694744b4be3299403e39b19fafd00f98c6ea" rel="noopener noreferrer"&gt;pretty-printed&lt;/a&gt; them too. I'll post pictures of the output later, but for now, you can see this on the project's readme: &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;
        hawkins
      &lt;/a&gt; / &lt;a href="https://github.com/hawkins/lard" rel="noopener noreferrer"&gt;
        lard
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      📗 A third-party command line interface for larder.io
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Lard&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;📗 A third-party command line interface for larder.io&lt;/p&gt;
&lt;p&gt;Note: This project is a work-in-progress, and as such, may not be fully functional quite yet. Note this also means patch versions in the &lt;code&gt;0.0.z&lt;/code&gt; release family can be major breaking changes. Until we reach a &lt;code&gt;0.1.0&lt;/code&gt;, upgrade at your own risk!&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/hawkins/lardscreenshots/folder.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fhawkins%2Flardscreenshots%2Ffolder.png" alt="Screenshot of Lard 0.0.0 folder view for 'Coding'"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;To install, simply run &lt;code&gt;gem install lard&lt;/code&gt;. Then, you can run the binary by calling &lt;code&gt;lard&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You'll need to log in before you can do much, so follow instructions in &lt;code&gt;lard login&lt;/code&gt; to get started.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contributing&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;I welcome contributions of all kinds! Feel free to open an issue to discuss problems, feature requests, smoothie flavors, or code changes you'd like to make. This project is meant to be useful to every Larder user, so I'd love to hear from you!&lt;/p&gt;
&lt;p&gt;If you want to run the code locally, follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clone the repo&lt;/li&gt;
&lt;li&gt;Make…&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/lard" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;It started simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/lard.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'httparty'&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Lard&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;HTTParty&lt;/span&gt;
  &lt;span class="n"&gt;base_uri&lt;/span&gt; &lt;span class="s1"&gt;'https://larder.io'&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;headers: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Token &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/api/1/@me/user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@options&lt;/span&gt;
    &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;symbolize_names: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="c1"&gt;# bin/lard&lt;/span&gt;
&lt;span class="c1"&gt;#!/usr/bin/ruby&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'yaml'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'./lib/lard'&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;YAML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_file&lt;/span&gt; &lt;span class="s1"&gt;'lard.yml'&lt;/span&gt;
&lt;span class="n"&gt;lard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Lard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'token'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;lard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inspect&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;But hold up a second - am I really calling it "Lard?" What, am I 5 years old? Well... sort of. I actually have some decent reasons though, so bear with me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I'm not associated with Larder, so this is third party and can't use their name as my own&lt;/li&gt;
&lt;li&gt;It's a command line app, so the less typing, the better - short names rule!&lt;/li&gt;
&lt;li&gt;It's a Larder client first and foremost, though, so the name should be memorable&lt;/li&gt;
&lt;li&gt;And, again, I'm five years old, so I think it's just silly enough that its other merits stand strong to make it a good name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I could tell this was going to be fun, and not too stressful, so I was sold on keeping the project and making it feature-complete with regards to what their API provided as quickly as possible. So, to make sure I wouldn't step on anyone behind Larder's toes, I shot them a tweet asking if it would be alright to open-source my work (my repo was private, at the time). &lt;iframe class="tweet-embed" id="tweet-1054206203839070210-604" src="https://platform.twitter.com/embed/Tweet.html?id=1054206203839070210"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1054206203839070210-604');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1054206203839070210&amp;amp;theme=dark"
  }



&lt;br&gt;
They very kindly agreed... &lt;iframe class="tweet-embed" id="tweet-1054207395268685826-90" src="https://platform.twitter.com/embed/Tweet.html?id=1054207395268685826"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1054207395268685826-90');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1054207395268685826&amp;amp;theme=dark"
  }



 so I was off to the races!&lt;/p&gt;

&lt;p&gt;I wrote a little bit more code to fetch bookmarks from a folder, broke my ruby install by mucking about with rvm and rbenv and finding they had collided on my mac, and finally went to bed.&lt;/p&gt;



&lt;p&gt;The next morning I woke up early to work on the project and fix my ruby install (exciting, right!?) and finish fetching bookmarks. Eventually I ran off to work, but when I came home, I spent all evening fixing the ruby install to compile with openssl as needed, and got work late that night since I was too hyped to sleep. I only coded an hour that night, but I got a &lt;a href="https://github.com/hawkins/lard/blob/129cafe5a3fe8801905433f1b89180123d1a0762/lib/lard.rb#L35toL47" rel="noopener noreferrer"&gt;lot done&lt;/a&gt; as I was able to pretty-print out an entire folder of bookmarks, paginating with the Larder API and all.&lt;/p&gt;



&lt;p&gt;The next day, I decided it was time to quit writing my own CLI kit and adopt such a wildly popular one, &lt;a href="http://whatisthor.com/" rel="noopener noreferrer"&gt;Thor&lt;/a&gt;. It was &lt;em&gt;super&lt;/em&gt; nice, and this was where I really started learning in this project. I had already learned how to make basic HTTP requests with &lt;code&gt;net/http&lt;/code&gt; and &lt;code&gt;HTTParty&lt;/code&gt;, but now I was learning a speedy and nice way to write command line interfaces in Ruby without breaking a sweat.&lt;/p&gt;

&lt;p&gt;I also &lt;a href="https://dev.to6c88e7e5a33525de182df74ade778a9d5101d1d3"&gt;almost admitted I was doing all this on a mac&lt;/a&gt; by committing the &lt;code&gt;.DS_Store&lt;/code&gt; file. 🤫&lt;/p&gt;

&lt;p&gt;Then, I &lt;a href="https://github.com/hawkins/lard/commit/2452d940b0a12212919fa3eef5eba77d1520f66a" rel="noopener noreferrer"&gt;published Lard 0.0.1&lt;/a&gt;, the first version of the gem! Woohoo!! SPOILER ALERT: I didn't know it at the time, but &lt;code&gt;gem install lard&lt;/code&gt; didn't bring along Lard's dependencies, so don't bother installing this particular version...&lt;/p&gt;

&lt;p&gt;Finally I made a hackjob at &lt;a href="https://github.com/hawkins/lard/commit/b9a5549a8416529d467faeb834b1afb8c16b4ef7" rel="noopener noreferrer"&gt;posting a bookmark&lt;/a&gt; (which turns out you can post &lt;em&gt;and&lt;/em&gt; edit at the same endpoint, it matches based on the URL you provide). But it didn't work yet - I was seeing an HTTP error &lt;code&gt;301: Redirection&lt;/code&gt;, and my &lt;code&gt;POST&lt;/code&gt; request was getting an error back:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"detail":"Method \"GET\" not allowed."}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;But I was sending a &lt;code&gt;POST&lt;/code&gt; request! I spent a long time debugging this. After a while I cracked open wireshark and saw that I was encountering a &lt;code&gt;301: Redirection&lt;/code&gt; and sending a &lt;code&gt;GET&lt;/code&gt; request to the redirected URL. Now that was odd. I didn't know why that would happen. Normally HTTP requests follow through &lt;code&gt;300&lt;/code&gt;'s like they weren't even there, right?&lt;/p&gt;

&lt;p&gt;Well, not this request. It was late, I was tired, and I just wanted this to work so I could start using the CLI myself and not just developing it. I decided to get some air, went on a walk, picked up a milkshake to ruin whatever few calories I'd managed to burn on the walk. I chatted with my friend and compiler partner Cameron about the issues, and found the solution on my walk home. I was so burned out I didn't want to write the code for it though, so I opened an issue for it and went to bed. &lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/hawkins/lard/issues/4" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Remove use of HTTParty
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#4&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars0.githubusercontent.com%2Fu%2F9123458%3Fv%3D4" alt="hawkins avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;hawkins&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/hawkins/lard/issues/4" rel="noopener noreferrer"&gt;&lt;time&gt;Oct 26, 2018&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Honestly, this lib has been something of a headache with its less-than-stellar docs and presumptuous api to say the least, so I've boiled the HTTP calls down to something pretty simple such that we can hopefully remove httparty pretty easily.&lt;/p&gt;
&lt;p&gt;On top of all that, I'd like to get back support for ruby &amp;lt; 2, I think I can justify replacing &lt;code&gt;httparty&lt;/code&gt; with &lt;code&gt;net/http&lt;/code&gt; or another lib that's a bit lower level and with lower dependencies.&lt;/p&gt;
&lt;h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Concerns:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;3xx codes
&lt;ul&gt;
&lt;li&gt;If I remember right, &lt;code&gt;POST https://larder.io/api/1/@me/*&lt;/code&gt; hit 301's to somewhere, so I need whatever interface I scrap together to follow redirects and maintain HTTP verbs throughout redirections.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Easily infer errors
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;res[:error]&lt;/code&gt; is nice - I don't want to lose that semantic response if possible&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/lard/issues/4" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;






&lt;p&gt;The next morning, I was cooled off and wrote the &lt;a href="https://github.com/hawkins/lard/commit/5fb28de6e0383c832b2c286ac46715bd4dbbd359#diff-d4e9001d759eec3f96532cd6db835a4eR103" rel="noopener noreferrer"&gt;patch to fix httparty&lt;/a&gt; (yes, it was one stupid function call to change an option otherwise not exposed and one that seems contrary to HTTP 300 semantics). Now I don't want to trash talk this library - other than this gripe, it was &lt;em&gt;super&lt;/em&gt; nice. But this hobby project should be fun, and I don't want headaches - I get enough of those living with chronic migraines! So just know that if you use HTTParty, that's perfectly fine, but this very minor gripe was enough to give it a bad taste for me. I have the luxury of making very simple requests in this project, so going back to &lt;code&gt;net/http&lt;/code&gt; where I know exactly what I'm getting isn't a big deal at all.&lt;/p&gt;

&lt;p&gt;I kept using HTTParty for now, but I knew it wouldn't be here to stay much longer. I wanted to do fun things, features, not refactoring. So later that night, wrote &lt;a href="https://github.com/hawkins/lard/commit/bb92194d6bc9d3ac176d464cfe0135a9a6f03947" rel="noopener noreferrer"&gt;&lt;code&gt;lard tags&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/hawkins/lard/commit/be20eeded762242fe0a477751c4b5d65c98336e3" rel="noopener noreferrer"&gt;&lt;code&gt;lard search&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://github.com/hawkins/lard/commit/0ef35314e22021cbe5f483ff4b12d400bf647712" rel="noopener noreferrer"&gt;&lt;code&gt;lard login&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I tweeted back to Larder now that I had something to show, and they were excited! &lt;iframe class="tweet-embed" id="tweet-1055299299922362370-380" src="https://platform.twitter.com/embed/Tweet.html?id=1055299299922362370"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1055299299922362370-380');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1055299299922362370&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1055303873638744064-132" src="https://platform.twitter.com/embed/Tweet.html?id=1055303873638744064"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1055303873638744064-132');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1055303873638744064&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;That got me way too hyped, and I really needed to go to bed, so I kept tagging and publishing new versions between features hoping that would make me stop and rest, but &lt;a href="https://github.com/hawkins/lard/commit/f99fa936dec3ac89472064546ebef686ad585ef4" rel="noopener noreferrer"&gt;alas&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bump 0.0.4  … committed 4 days ago

This should've been 2 at this point but I kept thinking I'd stop and go
to bed, but here we are. A few bugs and features later, and this is it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;then only &lt;a href="https://github.com/hawkins/lard/commit/02f4a5084d8c4f000865aa1c8a5914e641839aaf" rel="noopener noreferrer"&gt;8 minutes later&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Correct nil check  … committed 4 days ago 

I said I was done for the night, but I have no self control when it
comes to side projects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Finally after this, I published version &lt;code&gt;0.0.5&lt;/code&gt; (still not installing gem dependencies with it, mind you) and went to sleep.&lt;/p&gt;



&lt;p&gt;The gang at Larder told me the API limits folder requests to 200 items per request, so I applied that in my code as soon as I got a chance to the &lt;a href="https://github.com/hawkins/lard/commit/098c219b57927671cc9020acfe83dd5bf933431d" rel="noopener noreferrer"&gt;next evening&lt;/a&gt;. Then I did lots of refactoring type things to make working in the project feel less-icky, starting with &lt;a href="https://github.com/hawkins/lard/commit/f00460d60d79c9c1291741dad8c15a1c67f324f3" rel="noopener noreferrer"&gt;error messages&lt;/a&gt;. Then I learned about the magic that is &lt;a href="https://github.com/hawkins/lard/commit/013b619dfdc1a60204be7fd7f18d26ac68c042de" rel="noopener noreferrer"&gt;rubocop&lt;/a&gt;,and I desperately questioned how I ever wrote large scale ruby projects without it. Wow! Nicest linter I've seen in a long time. My pet language, Druid, has a lot to learn from this for it's "friendly compiler" effort...&lt;/p&gt;

&lt;p&gt;Most excitingly, I &lt;a href="https://github.com/hawkins/lard/commit/34901845e2f921afdd86c0e857c28f29b7f29df6" rel="noopener noreferrer"&gt;set up Travis CI&lt;/a&gt;! If you're not familiar with Travis, Travis CI is a super awesome and free continuous integration platform that integrates insanely well with GitHub and practically every popular language and software packaging platform like RubyGems. Perfect for running tests, linting, and deploying updates to your package automatically when you open a PR or tag a release! Check out Lard's &lt;a href="https://travis-ci.org/hawkins/lard" rel="noopener noreferrer"&gt;status page on Travis&lt;/a&gt; where you can see the latest builds it has ran, what triggered them, and whether tests are passing/failing!&lt;/p&gt;



&lt;p&gt;The next day, October 26th (6 days straight working on Lard!), Josh Sharp from Larder opened an issue which caught me by surprise: &lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/hawkins/lard/issues/7" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Missing dependencies?
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#7&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/joshsharp" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars1.githubusercontent.com%2Fu%2F1171503%3Fv%3D4" alt="joshsharp avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/joshsharp" rel="noopener noreferrer"&gt;joshsharp&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/hawkins/lard/issues/7" rel="noopener noreferrer"&gt;&lt;time&gt;Oct 27, 2018&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Heya, I had a go at installing and running Lard yesterday, and I ran into some issues. I'm not a Ruby person, but I think the issue here was that its dependencies were not being installed when I installed the gem.&lt;/p&gt;
&lt;p&gt;The first time I ran it, I got an issue about &lt;code&gt;httparty&lt;/code&gt; missing, which I figured out how to install as a gem myself 😇 but since then I'm still getting an error:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell js-code-highlight"&gt;
&lt;pre&gt;hades:&lt;span class="pl-k"&gt;~&lt;/span&gt;$ lard
Traceback (most recent call last):
        6: from /home/hades/.rvm/gems/ruby-2.5.3/bin/ruby_executable_hooks:24:in &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;`&lt;/span&gt;&lt;span class="pl-k"&gt;&amp;lt;&lt;/span&gt;main&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;span class="pl-s"&gt;        5: from /home/hades/.rvm/gems/ruby-2.5.3/bin/ruby_executable_hooks:24:in `eval&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;        4: from /home/hades/.rvm/gems/ruby-2.5.3/bin/lard:23:in &lt;span class="pl-pds"&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-k"&gt;&amp;lt;&lt;/span&gt;main&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;        3: from /home/hades/.rvm/gems/ruby-2.5.3/bin/lard:23:in `load&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;
        2: from /home/hades/.rvm/gems/ruby-2.5.3/gems/lard-0.0.5/bin/lard:3:in &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;`&lt;/span&gt;&lt;span class="pl-k"&gt;&amp;lt;&lt;/span&gt;top (required)&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;span class="pl-s"&gt;        1: from /home/hades/.rvm/rubies/ruby-2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;/home/hades/.rvm/rubies/ruby-2.5.3/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in &lt;span class="pl-pds"&gt;`&lt;/span&gt;&lt;/span&gt;require&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;: cannot load such file -- paint (LoadError)&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;If I'm doing something wrong, please let me know! Otherwise hope this helps you fix an issue.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/lard/issues/7" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Woah, one of the two devs from Larder tried my project? Awesome!! And it failed catastrophically, OOOOOPS!! I was so embarrassed, but I was excited because he'd clearly found an issue that I could learn from! That's why I love open sourcing everything I do, because someone is bound to have a different experience, and I LOVE learning from these opportunities!&lt;/p&gt;

&lt;p&gt;Right away, I had a theory: &lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/hawkins/lard/issues/7#issuecomment-433578790" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Comment for
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#7&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars3.githubusercontent.com%2Fu%2F9123458%3Fu%3D09b79945227799bcc72dedfcac909dd2f6f79217%26v%3D4" alt="hawkins avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;hawkins&lt;/a&gt;
        &lt;/strong&gt; commented on &lt;a href="https://github.com/hawkins/lard/issues/7#issuecomment-433578790" rel="noopener noreferrer"&gt;&lt;time&gt;Oct 27, 2018&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Hey thanks so much for trying it out and reporting an issue! 🎉&lt;/p&gt;
&lt;p&gt;This is actually my first published ruby gem, so I'm guessing I didn't mark the Gemfile which lists our dependencies (&lt;code&gt;httparty&lt;/code&gt;, &lt;code&gt;paint&lt;/code&gt;, &lt;code&gt;thor&lt;/code&gt;) to be included in the published gem or something to that affect. Oops! Will look into it tonight, thanks a bunch!&lt;/p&gt;
&lt;p&gt;In the mean time, if you'd like to keep toying with the app, you might be able to get away with this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gem install httparty
gem install paint
gem install thor
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will make sure you've got the dependencies installed system-wide, but really, &lt;code&gt;gem install lard&lt;/code&gt; &lt;em&gt;should&lt;/em&gt; have covered that, so I'll figure out how to cross the T's here. Thanks again!&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/lard/issues/7#issuecomment-433578790" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;... and I learned two things at once:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I should do some &lt;em&gt;very basic googling&lt;/em&gt; before responding to issues. Even if I really just want to fix the user's issue as quick as possible (with the &lt;code&gt;gem install *&lt;/code&gt; commands I gave him), I might be able to give a more insightful and easier solution after a quick search&lt;/li&gt;
&lt;li&gt;Sure enough, I was right about how Gem dependencies don't come from &lt;code&gt;Gemfile&lt;/code&gt;: &lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/hawkins/lard/issues/7#issuecomment-433579472" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Comment for
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#7&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars3.githubusercontent.com%2Fu%2F9123458%3Fu%3D09b79945227799bcc72dedfcac909dd2f6f79217%26v%3D4" alt="hawkins avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;hawkins&lt;/a&gt;
        &lt;/strong&gt; commented on &lt;a href="https://github.com/hawkins/lard/issues/7#issuecomment-433579472" rel="noopener noreferrer"&gt;&lt;time&gt;Oct 27, 2018&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;I should shut my mouth more - very quick google search revealed this is a rookie gem publisher mistake! 😅  Fixed it with &lt;code&gt;lard@0.0.6&lt;/code&gt;, so feel free to run &lt;code&gt;gem uninstall lard&lt;/code&gt; and &lt;code&gt;gem install lard&lt;/code&gt; to grab the latest version and its dependencies and we can pretend like this never happened. 😉&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/lard/issues/7#issuecomment-433579472" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I &lt;a href="https://github.com/hawkins/lard/commit/7562bb667de23145576439df0afea84ecd0c438f" rel="noopener noreferrer"&gt;patched it&lt;/a&gt; and deployed it as &lt;code&gt;0.0.6&lt;/code&gt; so Josh could try again later.&lt;/p&gt;

&lt;p&gt;Then, more refactoring. I decided to &lt;a href="https://github.com/hawkins/lard/commit/8b124cb3cb36227931a1c6e8c70094885b91d609" rel="noopener noreferrer"&gt;split the CLI from the lib&lt;/a&gt; so that the CLI is easier to work with and other packages can leverage &lt;code&gt;lard&lt;/code&gt; programatically in the future. Pretty straight forward stuff, just turning &lt;code&gt;LardHTTP&lt;/code&gt; from a module to a class, maintaining some state, and leveraging that library from the CLI.&lt;/p&gt;

&lt;p&gt;What I didn't know about this was how it could work simultaneously in production and development, though. Since this is my first foray into publishing a Ruby Gem, I've never had to link libraries from the file system, I could always just say "Hey Gem, find that thing for me" and be on my way. Not this time!&lt;/p&gt;

&lt;p&gt;I first &lt;a href="https://github.com/hawkins/lard/commit/8b124cb3cb36227931a1c6e8c70094885b91d609#diff-d4e9001d759eec3f96532cd6db835a4eR6" rel="noopener noreferrer"&gt;kept &lt;code&gt;require 'lard'&lt;/code&gt; in the code&lt;/a&gt; that would be deployed to production. This works, because when someone runs &lt;code&gt;gem install lard&lt;/code&gt;, Gem will know where &lt;code&gt;lard&lt;/code&gt; is, so &lt;code&gt;require 'lard'&lt;/code&gt; will resolve successfully. In development, though, &lt;code&gt;lard&lt;/code&gt; is not installed, we just have its source files. So at the time, I would manually change this line to &lt;code&gt;require '../lib/lard'&lt;/code&gt; (or use &lt;code&gt;require_relative&lt;/code&gt;) and change it back to &lt;code&gt;require 'lard'&lt;/code&gt; before committing, so production code would not be looking relatively, but development code would. Gross.&lt;/p&gt;

&lt;p&gt;After some digging, I eventually found that leaving &lt;code&gt;require 'lard'&lt;/code&gt; in the code is the right move, but that I can run the development binary &lt;code&gt;bin/lard&lt;/code&gt; with a command line argument to link the &lt;code&gt;lard&lt;/code&gt; library from the source code by using &lt;a href="https://github.com/hawkins/lard/commit/20274836eea0aa41e4f7f2281feeeddc4d9dff7c#diff-04c6e90faac2675aa89e2176d2eec7d8R25" rel="noopener noreferrer"&gt;&lt;code&gt;ruby bin/lard -Ilib&lt;/code&gt;&lt;/a&gt;. This looks familiar to you if you know GCC, but here's how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We run the &lt;code&gt;ruby&lt;/code&gt; binary&lt;/li&gt;
&lt;li&gt;We pass it &lt;code&gt;bin/lard&lt;/code&gt;, a ruby file from my source code that contains the CLI code (and the infamous &lt;code&gt;require 'lard'&lt;/code&gt; call at the top of its file)&lt;/li&gt;
&lt;li&gt;We specify the &lt;code&gt;-Ilib&lt;/code&gt; argument to map the &lt;code&gt;lib&lt;/code&gt; folder (where &lt;code&gt;lib/lard.rb&lt;/code&gt; exists) as an include folder for this runtime&lt;/li&gt;
&lt;li&gt;Ruby processes &lt;code&gt;require 'lard'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Gem is unable to resolve &lt;code&gt;'lard'&lt;/code&gt; from its normal folder, and sees that it is not relative&lt;/li&gt;
&lt;li&gt;Gem finds that the &lt;code&gt;lib&lt;/code&gt; folder was linked, though, and that it contains a &lt;code&gt;lard.rb&lt;/code&gt; file - so this &lt;code&gt;require&lt;/code&gt; is resolved to this file! &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is great. Now, I can just run that command during development and be perfectly comfortable to not commit broken &lt;code&gt;require&lt;/code&gt; statements. Woohoo!&lt;/p&gt;




&lt;p&gt;Now technically day 7, even tho it's &lt;a href="https://github.com/hawkins/lard/commit/0bc94fd258dc1a86a32733047a7fe7040147cce6" rel="noopener noreferrer"&gt;12 AM on a Friday night/ Saturday morning&lt;/a&gt;, I cleaned up the &lt;code&gt;lard user&lt;/code&gt; command to pretty-print user details finally.&lt;/p&gt;

&lt;p&gt;At this point, the library was technically complete in everything I wanted it to do at a minimum to be a viable command line interface for Larder!&lt;/p&gt;

&lt;p&gt;So I celebrated by going back to sleep finally and didn't get back at it until the next evening (same day, next night). I'm a little bit nocturnal on the weekends, if you haven't noticed...&lt;/p&gt;

&lt;p&gt;This is where I really made Travis shine - by configuring it to &lt;a href="https://github.com/hawkins/lard/commit/10cbeecdf23f25b688fca5deeffdbf2e79682c11" rel="noopener noreferrer"&gt;automatically deploy new gem versions&lt;/a&gt; when I tag a release in git. Yaha! (I also took some time to go back and do this to Prettier Webpack Plugin, like I should have done years ago) Hey, why not, shameless plug: &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;
        hawkins
      &lt;/a&gt; / &lt;a href="https://github.com/hawkins/prettier-webpack-plugin" rel="noopener noreferrer"&gt;
        prettier-webpack-plugin
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Process your Webpack dependencies with Prettier
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Prettier Webpack Plugin&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://greenkeeper.io/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ac69124f8db6497b1c49c032c80d68aeb7eaed04327094acc032ce9e218aab60/68747470733a2f2f6261646765732e677265656e6b65657065722e696f2f6861776b696e732f70726574746965722d7765627061636b2d706c7567696e2e737667" alt="Greenkeeper badge"&gt;&lt;/a&gt;
&lt;a href="https://github.com/hawkins/prettier-webpack-plugin#contributors" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a6259a788bf467c1f433d54c95c94ef835dd44bb43d4f81bc270971613acb6b1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f616c6c5f636f6e7472696275746f72732d392d6f72616e67652e7376673f7374796c653d666c61742d737175617265" alt="All Contributors"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Automatically process your source files with &lt;a href="https://github.com/jlongster/prettier" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; when bundling via Webpack.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This plugin reads all file dependencies in your dependency graph
If a file is found with a matching extension, the file is processed by Prettier and overwritten.&lt;/p&gt;
&lt;p&gt;You can provide options Prettier by specifying them when creating the plugin.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Looking for a loader?&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;It's in its early stages, but you can find a loader version of this plugin here: &lt;a href="https://github.com/hawkins/prettier-webpack-loader" rel="noopener noreferrer"&gt;prettier-webpack-loader&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Note, for Webpack 4 support, install prettier-webpack-plugin@1. For Webpack &amp;lt; 4, install prettier-webpack-plugin@0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Simply run &lt;code&gt;npm install --save-dev prettier prettier-webpack-plugin&lt;/code&gt; or &lt;code&gt;yarn add --dev prettier prettier-webpack-plugin&lt;/code&gt; to install.&lt;/p&gt;
&lt;p&gt;Then, in your Webpack config files, add the lines:&lt;/p&gt;
&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;var&lt;/span&gt; &lt;span class="pl-v"&gt;PrettierPlugin&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;require&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;"prettier-webpack-plugin"&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-smi"&gt;module&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;exports&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c"&gt;// ... config settings here ...&lt;/span&gt;
  &lt;span class="pl-c1"&gt;plugins&lt;/span&gt;: &lt;span class="pl-kos"&gt;[&lt;/span&gt;
    &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-v"&gt;PrettierPlugin&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
  &lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Why?&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Keeping style consistent between…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/prettier-webpack-plugin" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Then, after some linter warnings, I finally got around to &lt;a href="https://github.com/hawkins/lard/commit/bd2e2f695c21ee6979040eefd29e1807241e58be" rel="noopener noreferrer"&gt;removing &lt;code&gt;httparty&lt;/code&gt;&lt;/a&gt;. Whew! It felt great to have a little bit more control over what's happening, and to know I don't have to read the library author's mind to know how to use it anymore. Alright alright, sorry, let's move on.&lt;/p&gt;




&lt;p&gt;Continuing the trend of "I'll sleep when it's done!", I kept on with Day #8 at midnight. The next issue I tackled was another interesting learning point for publishing Ruby Gems. Check it out: &lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/hawkins/lard/issues/11" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        How can we ensure the User-Agent gets updated with every new version?
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#11&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/apps/todo" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars1.githubusercontent.com%2Fin%2F5534%3Fv%3D4" alt="todo[bot] avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/apps/todo" rel="noopener noreferrer"&gt;todo[bot]&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/hawkins/lard/issues/11" rel="noopener noreferrer"&gt;&lt;time&gt;Oct 28, 2018&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;&lt;a href="https://github.com/hawkins/lard/blob/295e5d9cf7b86de00f6376f9b2627bd11b44f9ac/lib/lard.rb#L100-L112" rel="noopener noreferrer"&gt;https://github.com/hawkins/lard/blob/295e5d9cf7b86de00f6376f9b2627bd11b44f9ac/lib/lard.rb#L100-L112&lt;/a&gt;&lt;/p&gt;

&lt;h6&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;This issue was generated by &lt;a href="https://todo.jasonet.co" rel="nofollow noopener noreferrer"&gt;todo&lt;/a&gt; based on a &lt;code&gt;TODO&lt;/code&gt; comment in 295e5d9cf7b86de00f6376f9b2627bd11b44f9ac when #10 was merged. cc &lt;a class="mentioned-user" href="https://dev.to/hawkins"&gt;@hawkins&lt;/a&gt;.&lt;/h6&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/lard/issues/11" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I wanted to add a User-Agent to all network traffic coming from Lard to indicate it was coming from Lard. Trouble is, like the whole &lt;code&gt;require 'lard'&lt;/code&gt; headache up above, I didn't want to have to make code changes here to be in sync with the gem version itself manually. That would be silly! Computers are smarter than me, surely they can do this themselves. My first thought was a &lt;code&gt;rake&lt;/code&gt; task that would complain if I didn't change it manually. So when I tag a release, Travis would find that rake screams about the release not updating the User-Agent, so I at least get forced to change it without publishing an accidentally-wrong User-Agent.&lt;/p&gt;

&lt;p&gt;Okay, but that doesn't fix the root issue. I thought back to how &lt;code&gt;gemspec&lt;/code&gt;'s are actually written in Ruby and leverage the &lt;code&gt;Gem&lt;/code&gt; library. So I thought "maybe Gem can tell me what version of &lt;code&gt;'lard'&lt;/code&gt; I'm using?"&lt;/p&gt;

&lt;p&gt;Sure enough, it can, with &lt;code&gt;Gem.loaded_specs['lard'].version&lt;/code&gt;. Bingo! But... wait. No, no dice. That only works in production, when gems are loaded from the local gem repository. Not in development, when they're linked virtually. Oof... well, okay. I thought I could add a constant to the library and pull that into &lt;code&gt;lard.gemspec&lt;/code&gt; then, since &lt;code&gt;gemspec&lt;/code&gt;'s are just Ruby code after all. &lt;a href="https://github.com/hawkins/lard/pull/12/commits/5d684becf759b56ec1d09b4333738b9317fe5178" rel="noopener noreferrer"&gt;So I tried it...&lt;/a&gt; and after realizing I &lt;a href="https://github.com/hawkins/lard/pull/12/commits/2eead1c3be74c7a76fd8e5151ad20968fec7e953#diff-6e7ac0d946e187ec51a3d99ab3a146fcR10" rel="noopener noreferrer"&gt;neglected to mark &lt;code&gt;lib/lard.rb&lt;/code&gt; as requirable&lt;/a&gt; and wasting &lt;code&gt;0.0.7&lt;/code&gt; as a test for this, I found that this version style would otherwise work!&lt;/p&gt;

&lt;p&gt;In the process, I learned that RubyGems offers a pattern of &lt;a href="https://guides.rubygems.org/patterns/#prerelease-gems" rel="noopener noreferrer"&gt;pre-release versions&lt;/a&gt;, which are meant for shipping possibly-broken code like what I just wrote. Awesome! I just fixed two problems with one PR!&lt;/p&gt;

&lt;p&gt;Finally, I decided it was time for proper tests. Now I love testing, especially when automated via Travis, but I've been struggling with how to test a program like this properly for a few reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do we test effectively when our library relies so heavily on external network connections?&lt;/li&gt;
&lt;li&gt;What about our CLI, what do we test? The final binary, or the modules of it?&lt;/li&gt;
&lt;li&gt;How do we avoid testing the CLI kit's underlying capabilities (Thor)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After brainstorming that at 1am (always a good idea!), I opened an issue with some thoughts and got to work:&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/hawkins/lard/issues/15" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Add tests
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#15&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars0.githubusercontent.com%2Fu%2F9123458%3Fv%3D4" alt="hawkins avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;hawkins&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/hawkins/lard/issues/15" rel="noopener noreferrer"&gt;&lt;time&gt;Oct 28, 2018&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;I waited a while on this because I wasn't sure how best to test it, but here's a few ideas I'd like to see about implementing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;test &lt;code&gt;Lard&lt;/code&gt; lib functions but mock the http responses&lt;/li&gt;
&lt;li&gt;test &lt;code&gt;LardCLI&lt;/code&gt; functions like &lt;code&gt;print_*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;test &lt;code&gt;bin/lard&lt;/code&gt; executable if I can come up with a way to put an API token on Travis.&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/lard/issues/15" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;So to end out the night, I installed &lt;a href="https://rspec.info" rel="noopener noreferrer"&gt;RSpec&lt;/a&gt; and wrote some &lt;a href="https://github.com/hawkins/lard/commit/141ea01cb51ce6b0c5c348b7610d5569f9463076#diff-9732b19df1dbcad692c8f2099ce2bcd1R3" rel="noopener noreferrer"&gt;basic tests&lt;/a&gt; to get the ball rolling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;
&lt;span class="c1"&gt;# spec/lard_spec.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'lard'&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Lard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'#authorized'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns false to indicate user is unauthorized'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Lard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authorized&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns true to indicate user has a token'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Though we can't actually guarantee the token is authorized&lt;/span&gt;
    &lt;span class="c1"&gt;# without an http request&lt;/span&gt;
    &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Lard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s1"&gt;'token'&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authorized&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Lard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'#api_url_prefix'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'returns the Larder API\'s Base URL'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Lard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;api_url_prefix&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_start_with&lt;/span&gt; &lt;span class="s1"&gt;'https://'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Lard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'#get'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'will fail if not authorized'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Lard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authorized&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'user'&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;raise_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'can fetch user'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Lard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="s1"&gt;'token'&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="ss"&gt;:links&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be_an_instance_of&lt;/span&gt; &lt;span class="no"&gt;Integer&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# spec/spec_helper.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'webmock/rspec'&lt;/span&gt;
&lt;span class="no"&gt;WebMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disable_net_connect!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;allow_localhost: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="no"&gt;RSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="c1"&gt;# Handle web request mocking&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:each&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;user_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'username'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'username'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'first_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'greatest'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'last_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ever'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'timezone'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'America/New_York'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'avatar'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'date_joined'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2016-01-21T03:03:33Z'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'links'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="s1"&gt;'is_trial'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;stub_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'https://larder.io/api/1/@me/user/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;headers: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s1"&gt;'Accept'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'*/*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Token token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'Host'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'larder.io'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'User-Agent'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Lard/0.0.8'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Ruby'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;body: &lt;/span&gt;&lt;span class="n"&gt;user_body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# ... the rest of `rspec --init` output&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  Recap: What I learned
&lt;/h1&gt;

&lt;p&gt;I learned a &lt;em&gt;ton&lt;/em&gt; in just 8 short days of writing an MVP command line interface for bookmarks management while working full time on other projects, so it's hard to sum it all up, but I've sat here for an hour now writing a silly story no one will ever read, so I might as well sum it up so I can regale my grand-children with the escapades of my youth:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Indie devs rock! The two behind &lt;a href="https://larder.io" rel="noopener noreferrer"&gt;Larder&lt;/a&gt;, &lt;a href="http://joshsharp.com.au/" rel="noopener noreferrer"&gt;Josh Sharp&lt;/a&gt; and &lt;a href="http://bellebcooper.com/" rel="noopener noreferrer"&gt;Belle Cooper&lt;/a&gt; made an awesome product and were super supportive through the whole process. I can't wait to make Lard as awesome as possible, even if I'm the only user, just as a tribute to Larder itself.&lt;/li&gt;
&lt;li&gt;Language version management is hard. My mac came with Ruby installed, but it's sort of known that you shouldn't use it - so I installed rbenv some time ago. Well, I didn't remember that I had Ruby installed on my mac when I started this, so I grabbed rvm this time. Whoops! That put me in quite a pickle, but I learned how rvm links Ruby and its dependent libraries (having a non-system dependent OpenSSL being an issue for me) and managed to get that solved.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Gemfile != gemspec&lt;/code&gt;. Almost every Ruby project I've used had a &lt;code&gt;Gemfile&lt;/code&gt; that installed everything you need. This was my first Ruby gem I wrote myself, so I had to learn how to specify runtime dependencies on the Gem itself, as well as development dependencies that can belong in the &lt;code&gt;Gemfile&lt;/code&gt;. Still a little unclear as to how to reduce duplication between them, but that's a story for another day.&lt;/li&gt;
&lt;li&gt;Rubocop rules. I've always loved linters, especially auto-formatters like &lt;a href="https://prettier.io" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt;, but Rubocop is the best of both worlds in my opinion.&lt;/li&gt;
&lt;li&gt;Don't reach for the libraries until you know you need them. I generally don't, but in hobby projects like this, sometimes I reach for the big guns just to move quickly so I can have fun and not do drudge-work. This time, that gave me a big headache, so I've got more ammo to support rolling my own infrastructure on the next project. (Like we're doing over at Druid!)&lt;/li&gt;
&lt;li&gt;Ruby Gems are super smart. From linking development dependencies, making your gemfile just plain Ruby so you can avoid repeating yourself by importing some constants from your code to your spec, I was super pleased with working with Ruby Gems all around. Writing them and publishing them is a breeze! Especially with Travis.&lt;/li&gt;
&lt;li&gt;Oh, Travis CI freaking rules. I already knew this, but setting up auto deployment was the real hot stuff on it this time.&lt;/li&gt;
&lt;li&gt;Don't over-think your testing struggles. I could have set up testing way sooner, mocking HTTP requests is very much a solved problem when it comes to ruby testing, as my brief time with RSpec and Webmock showed last night. I wish I had set up testing sooner, but I had a lot to learn and iterate on so I put it off too long.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What's next for Lard?
&lt;/h2&gt;

&lt;p&gt;Well, first of all, &lt;em&gt;TESTS&lt;/em&gt;! I'm absolutely getting proper testing in place as soon as I sit down to code again on Lard. But then, exciting new features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improving &lt;code&gt;lard bookmark&lt;/code&gt; to make it more dynamic and flexible to how you want to describe or change your bookmarks&lt;/li&gt;
&lt;li&gt;Aliasing commands with Thor (so &lt;code&gt;lard s&lt;/code&gt; means &lt;code&gt;lard search&lt;/code&gt;, etc)&lt;/li&gt;
&lt;li&gt;Working with your bookmarks offline, so you can make changes and upload them to Larder servers next chance you're online. Or, work entirely offline, if that's your workflow. (But Larder rules, so Lard will always be a Larder client first and foremost)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Writing Lard was a blast, and I'm stoked on how it will help my workflow and how much I learned about Ruby and its ecosystem. I'm excited to see how far I can take the project, and I hope I convinced at least one reader out there somewhere to either Try out Lard and break my code or to make their own project writeup someday!&lt;/p&gt;

&lt;p&gt;If you want to try Lard, feel free to run &lt;code&gt;gem install lard&lt;/code&gt; and &lt;code&gt;lard help&lt;/code&gt; to get started. Or pop over to my GitHub to check it out and say hello! &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;
        hawkins
      &lt;/a&gt; / &lt;a href="https://github.com/hawkins/lard" rel="noopener noreferrer"&gt;
        lard
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      📗 A third-party command line interface for larder.io
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Lard&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;📗 A third-party command line interface for larder.io&lt;/p&gt;

&lt;p&gt;Note: This project is a work-in-progress, and as such, may not be fully functional quite yet. Note this also means patch versions in the &lt;code&gt;0.0.z&lt;/code&gt; release family can be major breaking changes. Until we reach a &lt;code&gt;0.1.0&lt;/code&gt;, upgrade at your own risk!&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/hawkins/lardscreenshots/folder.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fhawkins%2Flardscreenshots%2Ffolder.png" alt="Screenshot of Lard 0.0.0 folder view for 'Coding'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;To install, simply run &lt;code&gt;gem install lard&lt;/code&gt;. Then, you can run the binary by calling &lt;code&gt;lard&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You'll need to log in before you can do much, so follow instructions in &lt;code&gt;lard login&lt;/code&gt; to get started.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contributing&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;I welcome contributions of all kinds! Feel free to open an issue to discuss problems, feature requests, smoothie flavors, or code changes you'd like to make. This project is meant to be useful to every Larder user, so I'd love to hear from you!&lt;/p&gt;

&lt;p&gt;If you want to run the code locally, follow these steps:&lt;/p&gt;


&lt;ol&gt;

&lt;li&gt;Clone the repo&lt;/li&gt;

&lt;li&gt;Make…&lt;/li&gt;

&lt;/ol&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hawkins/lard" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Alright, well I've spent entirely too much time in front of screens this week, so it's time I get out and enjoy the last part of this weekend. Happy weekend, everybody, and thanks for reading!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>cli</category>
    </item>
    <item>
      <title>Why is an IDE for Interpreted Languages "Overkill?"</title>
      <dc:creator>Josh Hawkins</dc:creator>
      <pubDate>Sun, 28 Oct 2018 14:55:39 +0000</pubDate>
      <link>https://dev.to/hawkinjs/why-is-an-ide-for-interpreted-languages-overkill-7c3</link>
      <guid>https://dev.to/hawkinjs/why-is-an-ide-for-interpreted-languages-overkill-7c3</guid>
      <description>&lt;p&gt;So often I hear the words "an IDE is overkill for Python/Ruby/JavaScript, just use Vim/Emacs/Nano/IDLE."&lt;/p&gt;

&lt;p&gt;Why is that? I write interpreted languages as much as the next guy, but in my experience, I&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;still want a debugger&lt;/li&gt;
&lt;li&gt;still want tag definitions for demystifying framework "magic"&lt;/li&gt;
&lt;li&gt;still want types&lt;/li&gt;
&lt;li&gt;despise "print statement debugging"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OK, so your fancy vim setup has ctags/cscope for tag definitions and you "know your code so well you don't need types." What about in 6 months when a bug crops up? Still remember what &lt;code&gt;a&lt;/code&gt; is? &lt;code&gt;i&lt;/code&gt;? &lt;code&gt;j&lt;/code&gt;? &lt;code&gt;temp&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;So the root of my question is, where did this mantra that you don't need IDEs for interpreted languages come about? You don't &lt;em&gt;need&lt;/em&gt; an IDE for any language, but what about compiled languages makes them so much more desirable?&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>(Don't Fear) The Regex: A Practical Introduction to Regular Expressions</title>
      <dc:creator>Josh Hawkins</dc:creator>
      <pubDate>Wed, 22 Mar 2017 05:17:36 +0000</pubDate>
      <link>https://dev.to/hawkinjs/dont-fear-the-regex-a-practical-introduction-to-regular-expressions</link>
      <guid>https://dev.to/hawkinjs/dont-fear-the-regex-a-practical-introduction-to-regular-expressions</guid>
      <description>&lt;p&gt;Have you ever used strings before? That's right, those "arrays of characters" we all know and love? Unless you code only in C, I'd bet you have - and maybe even a lot.&lt;/p&gt;

&lt;p&gt;But what about using a lot of strings? Or using strings that your program didn't generate? Maybe you're reading an email, parsing command line arguments, or reading human instructions, and you just need a more structured way to handle this.&lt;/p&gt;

&lt;p&gt;Sure, you could iterate over each word or element in the strings. And you'll probably understand the code you used to do so. But for large applications, that can become very overwhelming - and &lt;em&gt;very&lt;/em&gt; expensive to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enter: The Regular Expression
&lt;/h3&gt;

&lt;p&gt;Without getting too deep into computer science, let's define a Regular Expression real quick.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regular expressions are the grammars describing a Regular language&lt;/li&gt;
&lt;li&gt;Regular languages are a form of formal language that can be described by a finite state machine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a number of &lt;a href="http://stackoverflow.com/a/6718286/2192313"&gt;better explanations on regular languages&lt;/a&gt; out there, so if you're not happy yet, just google for a couple minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enter: The Regex
&lt;/h3&gt;

&lt;p&gt;If it hasn't already, here's where it gets funky... I draw a distinction between what programming languages call "regular expressions" and what computer science calls a regular expression.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Computer Science Regular Expression - a grammar to describe a &lt;em&gt;regular&lt;/em&gt; language&lt;/li&gt;
&lt;li&gt;Programming Language Regular Expression - a grammar to describe, at most, a &lt;em&gt;context-sensitive&lt;/em&gt; language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Context-sensitive languages are a good deal more complex and useful, so we'll call a programming language regular expression "regex" now to solidify the distinction that its languages &lt;em&gt;are not regular&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning to Write Regexs
&lt;/h3&gt;

&lt;p&gt;Regexs are described between //'s and match strings if they fit the 'pattern' defined between the two //'s. For instance, &lt;code&gt;/Hi/&lt;/code&gt; matches "Hi", so we can check a string to see if it is (or has, more on that later) "Hi" in the string using a regular expression.&lt;/p&gt;

&lt;p&gt;We match characters in a string with regular expressions by typing them normally. For instance, &lt;code&gt;/Hello World/&lt;/code&gt; will match the string "Hello World".&lt;/p&gt;

&lt;p&gt;We could simplify this to match &lt;em&gt;any&lt;/em&gt; word by adding a little regex magic: &lt;code&gt;\w&lt;/code&gt; matches any "word" made of only letters: &lt;code&gt;\w&lt;/code&gt; will match any one word (if only letters).&lt;/p&gt;

&lt;p&gt;We can similarly match numbers with &lt;code&gt;\d&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 1
&lt;/h4&gt;

&lt;p&gt;Great, so we can perform string equality or see if strings fit some simple pattern now. So what? Can they be more useful?&lt;/p&gt;

&lt;p&gt;You bet! Let's say we wrote an IRC chat bot that listens for someone to say "Josh". Our bot basically scans each message someone says in the channel until we get a match. Then, the bot will respond "Woah, I hope you aren't talking bad about my pal Josh!" Because Josh's only friends are robots.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;Our bot will use the pattern &lt;code&gt;/Josh/&lt;/code&gt; to match the strings.&lt;/p&gt;

&lt;p&gt;Suddenly, some named Eli stumbles along: "Eli: Josh, do you really need that much caffeine?"&lt;/p&gt;

&lt;p&gt;Our bot will kick in gear and scan the message with &lt;code&gt;/Josh/&lt;/code&gt; and find one match! So he replies, and Eli is sufficiently creeped out. Mission accomplished!&lt;/p&gt;

&lt;p&gt;Or was it?&lt;/p&gt;

&lt;p&gt;What if our bot was more intelligent? What if the bot addressed whoever spoke by name? Something like "Woah, I hope you aren't bad-mouthing my buddy Josh, Eli."&lt;/p&gt;

&lt;h3&gt;
  
  
  Quantifiers (Repeating Characters)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  0 or Many
&lt;/h4&gt;

&lt;p&gt;We can do that... but we've got to learn a few things to get there. First off, &lt;strong&gt;Quantifiers&lt;/strong&gt; (for Repeating characters).&lt;/p&gt;

&lt;p&gt;We can use * to match &lt;em&gt;0 or many&lt;/em&gt; characters before it. For instance, &lt;code&gt;/a*/&lt;/code&gt; matches "aaaaa" &lt;em&gt;BUT ALSO&lt;/em&gt; "". That's right, it will match the &lt;em&gt;empty&lt;/em&gt; string.&lt;/p&gt;

&lt;p&gt;* serves to match something optional, because the character it matches doesn't have to exist. But it can. And it can exist many, many times (theoretically infinitely many times). &lt;/p&gt;

&lt;p&gt;We can match "Josh" with &lt;code&gt;/Josh/&lt;/code&gt;, but we could also match "JJJJJJJJJosh" and "osh" with &lt;code&gt;/J*osh/&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  1 or Many
&lt;/h4&gt;

&lt;p&gt;+ can be used to match &lt;em&gt;1 or many&lt;/em&gt; characters. It effectively works the same way as * does, except the character existing is no longer optional. We have to have at least one of those characters to match now.&lt;/p&gt;

&lt;p&gt;So, we can match "JJJJosh" and "Josh" with &lt;code&gt;/J+osh/&lt;/code&gt; but not "osh". &lt;/p&gt;

&lt;h3&gt;
  
  
  Wildcards
&lt;/h3&gt;

&lt;p&gt;Great, we can match a lot more interesting features now. Maybe someone screaming "Joooosh" if they're really mad at me...&lt;/p&gt;

&lt;p&gt;But what if they're so mad that they slam their face on the keyboard? How do we match "afuhiudgosigs" if we don't know how pointy their nose is?&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;Wildcards&lt;/strong&gt;! &lt;/p&gt;

&lt;p&gt;Wildcards allow you to match &lt;em&gt;ANYTHING&lt;/em&gt;. Their syntax is &lt;code&gt;.&lt;/code&gt;. (Yes, just a period. Period.). You'll probably use this a lot, so don't confuse it for matching the end of a sentence.&lt;/p&gt;

&lt;p&gt;We can use this to match "Joooafhuaisggsh" by combining our knowledge of repeating characters and wildcards in this regex: &lt;code&gt;/Jo+.*sh/&lt;/code&gt;. To be clear, this will match 1 "J", 1 or more "o", 0 or many &lt;em&gt;wildcards&lt;/em&gt;, and 1 "s" and 1 "h". Those five blocks lead us to what we call...&lt;/p&gt;

&lt;h3&gt;
  
  
  Character Groups
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Character Groups&lt;/strong&gt; are the blocks of characters that appear in order in a string. When you use a &lt;code&gt;*&lt;/code&gt; or &lt;code&gt;+&lt;/code&gt;, you're actually matching many of the last &lt;em&gt;character group&lt;/em&gt;, not just the last &lt;em&gt;character&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is useful to understand in its own right, but combined with repeating characters, can be very powerful. To do this, we can define our own character group by using parenthesis (that's these guys).&lt;/p&gt;

&lt;p&gt;Let's say we want to repeat "Jos" but not "h". So "JosJosJosJosJosh" will match. We can do this with the regex &lt;code&gt;/(Jos)+h/&lt;/code&gt; Easy, right?&lt;/p&gt;

&lt;p&gt;But finally... back to our example, how can we get Eli's name in the IRC chat message he sent?&lt;/p&gt;

&lt;p&gt;Character groups are also a means of remembering parts of the string. This way we can add parts of a string to variables in our programming code when we see a string that fits the pattern. &lt;/p&gt;

&lt;p&gt;To do this, typically you'll do something like &lt;code&gt;\1&lt;/code&gt; to match the first specified group.&lt;/p&gt;

&lt;p&gt;For instance, &lt;code&gt;/(.+) \1/&lt;/code&gt; is a special one. Here we look at a group of random characters 1 or more times, have a space afterwards, and then repeat the &lt;em&gt;exact same characters&lt;/em&gt; again. So this regex will match the string "abc abc" but &lt;em&gt;not&lt;/em&gt; "abc def" even though "def" would match &lt;code&gt;(.*)&lt;/code&gt; independently.&lt;/p&gt;

&lt;p&gt;Remembering matches is very powerful, and it will probably boil down to the most useful feature of programming with regular expressions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2
&lt;/h3&gt;

&lt;p&gt;Whew... finally ready to continue with our IRC bot. Let's use what we learned to see who was talking smack.&lt;/p&gt;

&lt;p&gt;If we want to capture the sender's name when they say "Josh", our regex can look like this: &lt;code&gt;/(\w+): .*Josh.*/&lt;/code&gt; and we can save the match as a variable in our programming language for our reply. &lt;/p&gt;

&lt;p&gt;That's just 1 or more letters followed by ": ", a wildcard for 0 or many characters, the string Josh, and a wildcard for 0 or many characters.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;/.*word.*/&lt;/code&gt; is a simple way to match a string containing "word" that may or may not have other things around it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Python, that regex might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
&lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;ur&lt;/span&gt;&lt;span class="s"&gt;'(\w+): .*Josh.*'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Our regex
&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s"&gt;"Eli: Josh go move your laundry"&lt;/span&gt; &lt;span class="c1"&gt;# Our string
&lt;/span&gt;
&lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# Test the string
&lt;/span&gt;
&lt;span class="n"&gt;who&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                     &lt;span class="c1"&gt;# Get who said the message
&lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;who&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                                 &lt;span class="c1"&gt;# "Eli"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice we used &lt;code&gt;.group(1)&lt;/code&gt; just like we'd use &lt;code&gt;\1&lt;/code&gt; in the regex pattern. Nothing new here, aside from using the regex in Python.&lt;/p&gt;

&lt;h3&gt;
  
  
  Beginning and End
&lt;/h3&gt;

&lt;p&gt;Until now, we've actually allowed matching strings to occur in any part of the string. For intsance, &lt;code&gt;/(Jos)+h/&lt;/code&gt; will match any string containing the Jos-repeating-h &lt;em&gt;anywhere&lt;/em&gt; in the stringg.&lt;/p&gt;

&lt;p&gt;What if we wanted the string begin with Jos-repeating-h? We can specify this with &lt;code&gt;/^(Jos)+h/&lt;/code&gt;, where &lt;code&gt;^&lt;/code&gt; matches the start of the string. &lt;/p&gt;

&lt;p&gt;Similarly, &lt;code&gt;$&lt;/code&gt; can be used to match the end of the string.&lt;/p&gt;

&lt;p&gt;So if we want our pattern to match strings containg Jos-repeating-h from beginning to end, we can alter it to look like this: &lt;code&gt;/^(Jos)+h$/&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Character Options
&lt;/h3&gt;

&lt;p&gt;But maybe you're writing a regex for a sandwich order. You don't know if the customer wants white or wheat bread, but you'll accept either. How do you add choice in a regex? With &lt;strong&gt;Character Options&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Character Options allow you to specify a &lt;em&gt;set&lt;/em&gt; of possible values for a group. Syntax for this is &lt;code&gt;(white|wheat)&lt;/code&gt; in the context of our sandwhich, where either "white" or "wheat" would be accepted.&lt;/p&gt;

&lt;p&gt;You could also use the &lt;code&gt;[brackets]&lt;/code&gt; to specify options in another way. Each character is an option here, instead of the total string of characters. I.e., "b", "r", "s", "t", e", "k", "c", "r" would each be accepted individually. But this could be handy for more complicated groups, as you can substitute a character for a more complicated expression inside a Character Group here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modifiers
&lt;/h3&gt;

&lt;p&gt;We talk about regex's with &lt;code&gt;/slash marks/&lt;/code&gt;, right? We know what goes in the middle, but what goes on the sides?&lt;/p&gt;

&lt;p&gt;Plot twist, nothing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;... goes on the left.&lt;/em&gt; The right side, however, has some very, very useful stuff. It's almost a shame we ignored it for so long!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Modifiers&lt;/strong&gt; modify the rules with which the regular expressions are applied.&lt;/p&gt;

&lt;p&gt;Here's a list of the most common modifiers (from &lt;a href="https://regex101.com/"&gt;Regex101.com&lt;/a&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Modifier&lt;/th&gt;
&lt;th&gt;Nickname&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;g&lt;/td&gt;
&lt;td&gt;global&lt;/td&gt;
&lt;td&gt;All matches (don't return on first match)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;m&lt;/td&gt;
&lt;td&gt;multi-line&lt;/td&gt;
&lt;td&gt;Causes ^ and $ to match the begin/end of each line (not only begin/end of string)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;i&lt;/td&gt;
&lt;td&gt;insensitive&lt;/td&gt;
&lt;td&gt;Case insensitive match (ignores case of [a-zA-Z])&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;extended&lt;/td&gt;
&lt;td&gt;Spaces and text after a # in the pattern are ignored&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;td&gt;extra&lt;/td&gt;
&lt;td&gt;A \ followed by a letter with no special meaning is faulted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;s&lt;/td&gt;
&lt;td&gt;single line&lt;/td&gt;
&lt;td&gt;Dot matches newline characters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;u&lt;/td&gt;
&lt;td&gt;unicode&lt;/td&gt;
&lt;td&gt;Pattern strings are treated as UTF-16. Also causes escape sequences to match unicode characters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;U&lt;/td&gt;
&lt;td&gt;ungreedy&lt;/td&gt;
&lt;td&gt;The match becomes lazy by default. Now a &lt;code&gt;?&lt;/code&gt; following a quantifier makes it greedy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;anchored&lt;/td&gt;
&lt;td&gt;Pattern is forced to ^&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;J&lt;/td&gt;
&lt;td&gt;duplicate&lt;/td&gt;
&lt;td&gt;allow duplicate subpattern names&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For instance, until now, all of our examples have been &lt;em&gt;case-sensitive&lt;/em&gt;. That means, capitalizing or lower-casing any one character would make that string no longer match the pattern. We can make our patterns &lt;em&gt;case-insensitive&lt;/em&gt; with the &lt;code&gt;i&lt;/code&gt; modifier.&lt;/p&gt;

&lt;p&gt;Maybe Eli was so mad at me that he spammed the chat with a Mix OF casE CHArACters. Never fear, &lt;code&gt;i&lt;/code&gt; is here! We can match his "I hAate LiVing witH JOSH!!!" rage with &lt;code&gt;/i ha+te living with josh!+/i&lt;/code&gt;. Now it's easier to read and more powerful and useful. Awesome!&lt;/p&gt;

&lt;p&gt;I'll leave the rest of the modifiers for you to play with on your own, but I bet you'll find &lt;code&gt;igm&lt;/code&gt; to be your most used in general.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's next?
&lt;/h3&gt;

&lt;p&gt;Hopefully this article has shown you another useful way to interact with strings a little bit more intelligently. I've hardly even scratched the surface of regexs, but you already know how to use regular expressions for some minor tasks now.&lt;/p&gt;

&lt;p&gt;There's an overwhelming number of symbols / tokens to use in your regexs. Typically you'll stumble on them from Stack Overflow searches, or you'll guess them from previous experience (\n is the new line character, for instance). You've more or less got what you need for now, but there's plenty still to learn.&lt;/p&gt;

&lt;p&gt;You can get a full list of tokens and test your regexs extensively &lt;a href="https://regex101.com"&gt;here&lt;/a&gt;. I still use this website almost every time I write regexs, because the testing tool is remarkably helpful and powerful. It even generates code for you if you're not sure how to do it in your programming language yet.&lt;/p&gt;

&lt;p&gt;If this was a cakewalk for you, check out &lt;a href="https://regexcrossword.com/"&gt;regex crossword puzzles&lt;/a&gt;. They'll really get you thinking with regex!&lt;/p&gt;

</description>
      <category>regex</category>
    </item>
    <item>
      <title>Hi, I'm Josh Hawkins</title>
      <dc:creator>Josh Hawkins</dc:creator>
      <pubDate>Wed, 22 Mar 2017 00:43:50 +0000</pubDate>
      <link>https://dev.to/hawkinjs/hi-im-josh-hawkins</link>
      <guid>https://dev.to/hawkinjs/hi-im-josh-hawkins</guid>
      <description>&lt;p&gt;I have been coding for over 11 years.&lt;/p&gt;

&lt;p&gt;You can find me on Twitter as &lt;a href="https://twitter.com/hawkinjs" rel="noopener noreferrer"&gt;@hawkinjs&lt;/a&gt;, and GitHub / NPM as &lt;a href="https://github.com/hawkins" rel="noopener noreferrer"&gt;@hawkins&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I live in the south, currently, but I'm looking to move soon.&lt;/p&gt;

&lt;p&gt;I mostly program in these languages: JavaScript, Python, C.&lt;/p&gt;

&lt;p&gt;I am currently learning more about full stack web development. If you'd like to talk to someone about full stack, @ me on twitter or send me an email (from my GitHub)!&lt;/p&gt;

&lt;p&gt;Nice to meet you.&lt;/p&gt;

</description>
      <category>introduction</category>
    </item>
  </channel>
</rss>
