<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Yechiel Kalmenson</title>
    <description>The latest articles on Forem by Yechiel Kalmenson (@yechielk).</description>
    <link>https://forem.com/yechielk</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%2F14816%2Fe88e23df-00dd-4d43-8454-65abd51780c8.jpg</url>
      <title>Forem: Yechiel Kalmenson</title>
      <link>https://forem.com/yechielk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/yechielk"/>
    <language>en</language>
    <item>
      <title>Who's Going To RubyConf?</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Fri, 05 Nov 2021 19:00:05 +0000</pubDate>
      <link>https://forem.com/yechielk/whos-going-to-rubyconf-3g9m</link>
      <guid>https://forem.com/yechielk/whos-going-to-rubyconf-3g9m</guid>
      <description>&lt;p&gt;&lt;a href="https://rubyconf.org/"&gt;RubyConf&lt;/a&gt; is starting next week (November 8-0), both in person in Denver and remotely online.&lt;/p&gt;

&lt;p&gt;This year it's especially exciting for me; it's my first in-person conference in exactly two years (the vaccine mandates and mask policy helped me feel safe with the decision to attend in person), AND I'll be giving a talk (which you should totally &lt;a href="https://rubyconf.org/program/sessions#session-1164"&gt;come watch&lt;/a&gt; if you'll be there)!&lt;/p&gt;

&lt;p&gt;Who else is planning on attending?&lt;/p&gt;

&lt;p&gt;Come say hi (either in person or in the discord)!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>networking</category>
      <category>watercooler</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Talmudic Gems For Rails Developers</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Thu, 20 May 2021 01:40:59 +0000</pubDate>
      <link>https://forem.com/yechielk/talmudic-gems-for-rails-developers-35b3</link>
      <guid>https://forem.com/yechielk/talmudic-gems-for-rails-developers-35b3</guid>
      <description>&lt;p&gt;A few weeks ago, I gave a talk at RailsConf titled Talmudic Gems For Rails Developers.&lt;/p&gt;

&lt;p&gt;In the talk I discussed lessons I learned from a lifetime of Talmudic study that helped me in my journey as a developer, and which I felt could benefit other developers in their growth.&lt;/p&gt;

&lt;p&gt;The talk is geared at any developer looking to grow using the timeless wisdom of the Talmudic sages, not just Rails developers.&lt;/p&gt;

&lt;p&gt;I shared a transcript of the talk in the Torah &amp;amp;&amp;amp; Tech newsletter &lt;a href="https://mailchi.mp/2bc93e8bd182/torah-tech-issue-6751637?e=bb66b91b8e"&gt;issue #123&lt;/a&gt; (if you haven’t yet, you can sign up to the newsletter, and get the first year’s worth of newsletters in book format, at the Torah &amp;amp;&amp;amp; Tech website: &lt;a href="https://torahandtech.dev/"&gt;torahandtech.dev&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The talk has just been posted to YouTube, and I’m happy to share it with my audience!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/d08GFQDT824"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If you would like to delve into some of the sources I mentioned, you can find links in the source sheet I prepared &lt;a href="https://gist.github.com/achasveachas/000e4e08e193140e372cd4819d025f4a"&gt;here&lt;/a&gt;.&lt;/p&gt;

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




</description>
      <category>programming</category>
      <category>rails</category>
      <category>mentoring</category>
      <category>community</category>
    </item>
    <item>
      <title>Part 3: Add A Dead-Man's Switch To A Rails Application </title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Sun, 07 Feb 2021 18:47:58 +0000</pubDate>
      <link>https://forem.com/yechielk/part-2-add-a-dead-man-s-switch-to-a-rails-application-2b5f</link>
      <guid>https://forem.com/yechielk/part-2-add-a-dead-man-s-switch-to-a-rails-application-2b5f</guid>
      <description>&lt;h2&gt;
  
  
  Bonus Round!
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/yechielk/part-1-adding-a-dead-man-s-switch-to-a-rails-application-bgp"&gt;part one&lt;/a&gt; and &lt;a href="https://dev.to/yechielk/part-2-add-a-dead-man-s-switch-to-a-rails-application-243j"&gt;part two&lt;/a&gt; of this series, we created a working deadman's switch in our rails app.&lt;/p&gt;

&lt;p&gt;This part isn't necessary, but it can be helpful.&lt;/p&gt;

&lt;p&gt;The deadman's switch we wrote will trip if it isn't reset after seven days (or however long you set it for). That may seem like a safe interval, but it would be nice to have a reminder if you haven't reset it in a few days and the deadline is coming up.&lt;/p&gt;

&lt;p&gt;I set my switch up to send me an SMS reminder if I didn't reset it in 4 days, and then every day after that until the switch is either rest or if it trips.&lt;/p&gt;

&lt;p&gt;That's what we will do in part three of this tutorial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; this part of the tutorial uses Twilio to send SMS messages. You will need to sign up for a Twilio account. If you use my &lt;a href="//www.twilio.com/referral/PWhqJW"&gt;referral link&lt;/a&gt; to sign up, you will get $10 of credit, which should be last a while for our use-case.&lt;/p&gt;

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

&lt;p&gt;The first thing we need to do before getting started is to make sure we have a Twilio account. For our purposes, a "demo" account is enough. A demo account is free; it means you will have to register your phone number to receive texts, and you can't send SMS messages to other numbers, which is fine. Sending an SMS costs money ($0.0075 in the US); the $10 you get at sign-up by using my referral link should cover that for a long time.&lt;/p&gt;

&lt;p&gt;After creating an account, follow the instructions to create a new project.&lt;/p&gt;

&lt;p&gt;On the dashboard, you will need three pieces of information: The Account SID, Auth Token, and the account phone number.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wOKzxuMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0owkb1kk5ldug027e0oy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wOKzxuMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0owkb1kk5ldug027e0oy.png" alt="screenshot of the dashboard showing where to find the needed info. "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save these credentials in a safe place; we will need them for the next part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; It's a bad idea to put API keys in your code as plaintext, especially if you put your code somewhere that's publicly accessible like GitHub. Someone can find them and use them to rack up quite the phone bill! Store them as environment variables instead. Heroku makes it easy to set &lt;a href="https://devcenter.heroku.com/articles/config-vars#using-the-heroku-dashboard"&gt;environment variables&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text me, maybe?
&lt;/h2&gt;

&lt;p&gt;Now we have everything we need to set up our script to text us when it doesn't hear from us in a while.&lt;/p&gt;

&lt;p&gt;First, put the following near the top of your rake task:&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="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActionView&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DateHelper&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't strictly necessary, but it will let us use &lt;code&gt;ActionView&lt;/code&gt;'s &lt;code&gt;time_ago_in_words&lt;/code&gt; method to format a nice human-readable message.&lt;/p&gt;

&lt;p&gt;Next, let's set up a conditional to send the message only after the specified time that the app didn't hear from you. I set mine up to start bugging me after three days:&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="k"&gt;if&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ago&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="no"&gt;Deadman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(though for testing purposes, you can make the interval as small as a few seconds so you can trigger the SMS to send and make sure everything is working. Just remember to change it back before pushing to production).&lt;/p&gt;

&lt;p&gt;Now, inside the &lt;code&gt;if&lt;/code&gt; block, the first thing to do is to construct a message that the script will send:&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="n"&gt;time_to_trigger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time_ago_in_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Deadman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;message_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Your deadman switch will trigger in &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;time_to_trigger&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, please log in to your account and reset it."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can, of course, edit this to fit your needs and taste.&lt;/p&gt;

&lt;p&gt;Next, let's set up a Twilio client using the credentials we got from the Twilio dashboard:&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="n"&gt;twilio_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Twilio&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;REST&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_ACCOUNT_SID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_ACCOUNT_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you can  use the client to send an SMS message to your phone:&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="n"&gt;twilio_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;from: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_PHONE_NUMBER'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'PHONE_NUMBER'&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;message_body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;from:&lt;/code&gt; field is the Twilio phone number you got from your Twilio dashboard, and the &lt;code&gt;to:&lt;/code&gt; field is your personal phone number (make sure to register it with your Twilio account if you're on the free "Demo" tier). The &lt;code&gt;body:&lt;/code&gt; field is the message we constructed earlier.&lt;/p&gt;

&lt;p&gt;The complete code should look something like this:&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="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActionView&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DateHelper&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ago&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="no"&gt;Deadman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt;
    &lt;span class="n"&gt;time_to_trigger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time_ago_in_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Deadman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;message_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Your deadman switch will trigger in &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;time_to_trigger&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, please log in to your account and reset it."&lt;/span&gt;
    &lt;span class="n"&gt;twilio_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Twilio&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;REST&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_ACCOUNT_SID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_ACCOUNT_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;twilio_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;from: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_PHONE_NUMBER'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'PHONE_NUMBER'&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;message_body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! That's all there is to it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;I considered adding the capability of resetting my switch via SMS as well, by replying to one of the reminders.&lt;/p&gt;

&lt;p&gt;I didn't end up going with that for various reasons, but if you would like to, here is a &lt;a href="https://www.twilio.com/blog/2018/04/sms-notifications-ruby-on-rails.html"&gt;blog post&lt;/a&gt; I wrote a while ago on the Twilio blog that should help get you started.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>twilio</category>
    </item>
    <item>
      <title>My Favorite Bash Tips, Tricks, and Shortcuts</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Mon, 11 Jan 2021 22:23:39 +0000</pubDate>
      <link>https://forem.com/yechielk/my-favorite-bash-tips-tricks-and-shortcuts-36bj</link>
      <guid>https://forem.com/yechielk/my-favorite-bash-tips-tricks-and-shortcuts-36bj</guid>
      <description>&lt;p&gt;If you're on DEV, chances are you spend at least &lt;strong&gt;some&lt;/strong&gt; time in the terminal, maybe even a &lt;strong&gt;lot&lt;/strong&gt; of time.&lt;/p&gt;

&lt;p&gt;Over the years, I've picked up a number of tips and tricks from fellow developers. Almost every time I pair program with someone new, chances are I'll notice them doing something neat and ask them how they did it.&lt;/p&gt;

&lt;p&gt;Here are some of my favorites.&lt;/p&gt;

&lt;p&gt;I use bash as my default terminal, but most of these tips translate to other terminals as well.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; This post isn't meant to teach the basics of using the terminal. There are many great resources online (I remember doing &lt;a href="https://www.codecademy.com/learn/learn-the-command-line"&gt;Codecademy's Command Line course&lt;/a&gt; when I was starting out.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;-&lt;/code&gt; operator
&lt;/h2&gt;

&lt;p&gt;Do you find yourself switching back and forth between two directories often?&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;cd -&lt;/code&gt; to change to the last directory you were in like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~ &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;directory1
~/directory1 &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;directory2
~/directory2 &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; -
~/directory1 &lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also works with git when switching between branches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/my-project&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout feature-branch
~/my-project&lt;span class="o"&gt;(&lt;/span&gt;feature-branch&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout -
~/my-project&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The &lt;code&gt;!!&lt;/code&gt; operator
&lt;/h2&gt;

&lt;p&gt;This happens a lot!&lt;/p&gt;

&lt;p&gt;You type a command, only to get a "Permission denied" so you have to retype the command again, this time using &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;!!&lt;/code&gt; operator echoes the last command you typed into your terminal.&lt;/p&gt;

&lt;p&gt;You can use it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;some-dangerous-script.sh
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Error: Permission Denied
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Enter password &lt;span class="k"&gt;for &lt;/span&gt;some-dangerous-script.sh: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  {curly brace expansion}
&lt;/h2&gt;

&lt;p&gt;If you ever need to run a series of very similar commands that differ by just a few characters (like for example, if you want to create a few filenames with lightly different extensions) you can use the characters that will be different between two curly braces and the command will run once for each one.&lt;/p&gt;

&lt;p&gt;Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;file-&lt;span class="o"&gt;{&lt;/span&gt;1,2,3&lt;span class="o"&gt;}&lt;/span&gt;.md
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; file-1.md file-2.md file-3.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also pass in a range:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;file-&lt;span class="o"&gt;{&lt;/span&gt;1..3&lt;span class="o"&gt;}&lt;/span&gt;.md
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; file-1.md file-2.md file-3.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Search using Ctrl+R
&lt;/h2&gt;

&lt;p&gt;Are you like me? Would you press the up button 20 times to avoid typing out a 7 character command?&lt;/p&gt;

&lt;p&gt;This next one was a lifesaver for me!&lt;/p&gt;

&lt;p&gt;You can type Ctrl + R followed by the first few letters of the command you want to search through your bash history and bring up the command you need.&lt;/p&gt;

&lt;p&gt;(Sorry, I can't think of how to demonstrate that with a code snippet. Just go to your terminal, type in Ctrl + R and start typing).&lt;/p&gt;

&lt;h2&gt;
  
  
  Aliases
&lt;/h2&gt;

&lt;p&gt;Aliases are a great way to save time and keystrokes. If there's a command or a series of commands you find yourself typing often, it's making an alias can be very helpful.&lt;/p&gt;

&lt;p&gt;In order to set aliases, first open the &lt;code&gt;~/.bashrc&lt;/code&gt; file in your favorite editor and check if it has the following lines in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.bash_aliases &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;.&lt;/span&gt; ~/.bash_aliases
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should be there already, if it isn't just add it to the bottom of the file.&lt;/p&gt;

&lt;p&gt;Next open &lt;code&gt;~/.bash_aliases&lt;/code&gt; in your editor (or create it if it doesn't exist) and add your aliases in the following format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;something&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"definition"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some playful aliases I have in my &lt;code&gt;.bash_aliases&lt;/code&gt; are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;please&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sudo "&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;yeet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"rm -rf 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also have a number of functions defined there, for more complex command series:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mk&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

gclone&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    git clone &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; .git&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;mk&lt;/code&gt; alias takes a directory name as an argument, &lt;code&gt;mk&lt;/code&gt;s the directory and then &lt;code&gt;cd&lt;/code&gt;s into it.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;gclone&lt;/code&gt; alias takes a git repo, clones it, and then &lt;code&gt;cd&lt;/code&gt;s into it.&lt;/p&gt;

&lt;p&gt;After adding aliases to your &lt;code&gt;.bash_aliases&lt;/code&gt; they should load automatically every time you start a new terminal session.&lt;/p&gt;

&lt;p&gt;If you would like to use your aliases in your current session, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bash_aliases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's what I can think of for now.&lt;/p&gt;

&lt;p&gt;Do you have any favorite tips and tricks?&lt;/p&gt;

&lt;p&gt;Please please do share them! I always love learning new ones!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What Are Your Goals For 2021?</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Thu, 31 Dec 2020 15:17:28 +0000</pubDate>
      <link>https://forem.com/yechielk/what-are-your-goals-for-2021-2fck</link>
      <guid>https://forem.com/yechielk/what-are-your-goals-for-2021-2fck</guid>
      <description>&lt;p&gt;It's that time of year when I usually look back at the goals I set for myself a year ago, see how well I did, and set new expectations for the next year.&lt;/p&gt;

&lt;p&gt;This past year of course has been... a year. On December 31st 2019, I don't think any of us imagined where we'd be now, yet here we are.&lt;/p&gt;

&lt;p&gt;Looking back at my goals from last year, one thing is clear. I did not necessarily accomplish everything I set out to do, but I also had quite a few accomplishments I would never have imagined in the beginning of the year!&lt;/p&gt;

&lt;p&gt;Let's first take a look at what I was hoping for:&lt;/p&gt;


&lt;div class="liquid-comment"&gt;
    &lt;div class="details"&gt;
      &lt;a href="/yechielk"&gt;
        &lt;img class="profile-pic" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F14816%2Fe88e23df-00dd-4d43-8454-65abd51780c8.jpg" alt="yechielk profile image"&gt;
      &lt;/a&gt;
      &lt;a href="/yechielk"&gt;
        &lt;span class="comment-username"&gt;Yechiel Kalmenson&lt;/span&gt;
      &lt;/a&gt;
      &lt;span class="color-base-30 px-2 m:pl-0"&gt;•&lt;/span&gt;

&lt;a href="https://dev.to/yechielk/comment/jcgc" class="comment-date crayons-link crayons-link--secondary fs-s"&gt;
  &lt;time class="date-short-year"&gt;
    Dec 27 '19
  &lt;/time&gt;

    &lt;span class="hidden m:inline-block"&gt;• Edited on &lt;time class="date-no-year"&gt;Dec 31&lt;/time&gt;&lt;/span&gt;
    &lt;span class="m:hidden"&gt;• Edited&lt;/span&gt;
&lt;/a&gt;

    &lt;/div&gt;
    &lt;div class="body"&gt;
      

&lt;p&gt;I'll start!&lt;/p&gt;

&lt;p&gt;In tech goals, 2020 is the year of Kubernetes for me. I plan on getting more familiar with the eco-system, and maybe even start contributing to the project!&lt;/p&gt;

&lt;p&gt;In career goals this is not an aggressive goal for me, but if I can find a job at a company that has all the pros of where I work now, but in addition to that is a remote-first company I'll be very happy :)&lt;/p&gt;

&lt;p&gt;And in personal goals, I'm taking up Krav Maga and organizing a course at my synagogue for next year.&lt;/p&gt;



    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Of those three goals: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Kubernetes: turns out my career took a bit of a turn, I joined a new team where the focus was on a different direction, and I did not end up learning as much as I hoped (never mind getting involved in the community). I still hope to get more into that eco-system, but that's taken a place on a back burner for now.&lt;/li&gt;
&lt;li&gt;Finding a remote job: in an ironic twist of fate, my current job is now remote (along with many others), and my company communicated that they will be more open to people working from home permanently, even as offices begin to open. So I'll count that as a goal I fulfilled 🙂&lt;/li&gt;
&lt;li&gt;Krav Maga: I did start this one. I put together a group in my neighborhood and we met in my house a few times, but then covid put a halt to that so we'll see when that becomes practical again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the other hand, there have been a number of accomplishments this year that I would not have imagined a year ago!&lt;/p&gt;

&lt;p&gt;My friend Ben Greenberg and I, published the &lt;a href="https://torahandtech.dev/" rel="noopener noreferrer"&gt;Torah &amp;amp;&amp;amp; Tech&lt;/a&gt; newsletter for two years now.&lt;/p&gt;

&lt;p&gt;In June, we published the first year's worth of newsletters in the Torah &amp;amp;&amp;amp; Tech book:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/yechielk" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F14816%2Fe88e23df-00dd-4d43-8454-65abd51780c8.jpg" alt="yechielk"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/yechielk/announcing-torah-tech-the-book-4m39" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Announcing Torah &amp;amp;&amp;amp; Tech; The Book.&lt;/h2&gt;
      &lt;h3&gt;Yechiel Kalmenson ・ May 19 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#showdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#books&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#learning&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#coding&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;In personal news, also in June, my family grew as we welcomed our son Meir to the family.&lt;/p&gt;

&lt;p&gt;So yes, 2020 has been a year. Here's hoping that 2021 only improves.&lt;/p&gt;

&lt;p&gt;What about you? What were your 2020 goals? How did you do on them? What are you hoping to accomplish in 2021?&lt;/p&gt;

&lt;p&gt;Looking forward to hearing from you in the comments!&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>watercooler</category>
      <category>goals</category>
    </item>
    <item>
      <title>Part 2: Add A Dead-Man's Switch To A Rails Application</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Fri, 18 Dec 2020 04:57:34 +0000</pubDate>
      <link>https://forem.com/yechielk/part-2-add-a-dead-man-s-switch-to-a-rails-application-243j</link>
      <guid>https://forem.com/yechielk/part-2-add-a-dead-man-s-switch-to-a-rails-application-243j</guid>
      <description>&lt;h3&gt;
  
  
  Ah Yes, Where Were We?
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://dev.to/yechielk/part-1-adding-a-dead-man-s-switch-to-a-rails-application-bgp"&gt;part 1&lt;/a&gt; of this tutorial, we learned how to set up the part of the deadman's switch that allows us to reset the switch.&lt;/p&gt;

&lt;p&gt;In part 2, we will get to the main part; how to set up a script that will run if something happens and the switch isn't reset in a given amount of time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rake It In
&lt;/h3&gt;

&lt;p&gt;The standard way of running scripts in Rails apps (and Ruby environments generally) is using Rake tasks.&lt;/p&gt;

&lt;p&gt;Rake tasks are, in their most basic form, Ruby scripts that you can run using the command &lt;code&gt;rake [task-name]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In fact, chances are you've run quite a few already! If you've run commands like &lt;code&gt;rails db:setup&lt;/code&gt;, &lt;code&gt;rails db:migrate&lt;/code&gt;, these are rake tasks that come built-in with Rails for setting up your database!&lt;/p&gt;

&lt;p&gt;We can write our own Rake tasks as well. They usually live in the &lt;code&gt;lib/tasks/&lt;/code&gt; directory of our Rails app, and the files use the &lt;code&gt;.rake&lt;/code&gt; extension instead of the `.rb extension Ruby scripts usually use.&lt;/p&gt;

&lt;p&gt;Let's start by creating a file &lt;code&gt;lib/tasks/deadmans-switch.rake&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the file, let's put the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ruby&lt;br&gt;
desc "Deadman's Switch"&lt;/p&gt;

&lt;p&gt;task :deadman_switch do&lt;br&gt;
    puts "Our first task!"&lt;br&gt;
end&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;At this point, we have a very basic Rake task. If you run &lt;code&gt;rake -T&lt;/code&gt; in your terminal, you should get a list of all the Rake asks you have available, including this one:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;shell&lt;br&gt;
rake deadmans_switch                       # Deadman's switch&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On the left side, we have the name of our task, and on the right, the description that we gave it in the first line following the &lt;code&gt;desc&lt;/code&gt; keyword.&lt;/p&gt;

&lt;p&gt;If we run &lt;code&gt;rake deadmans_switch&lt;/code&gt; in our terminal, we should see the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;shell&lt;br&gt;
$  rake deadman_switch &lt;br&gt;
=&amp;gt; Our first task!&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Of course, at this point, our task does nothing more than print a line to let us know it's there, but we can replace that with any Ruby code we want, so let's do that!&lt;/p&gt;

&lt;p&gt;Replace the contents of the task with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;ruby&lt;br&gt;
    if 7.days.ago &amp;lt; Deadman.last_reset&lt;br&gt;
        puts "Still alive!"&lt;br&gt;
    else&lt;br&gt;
        puts "Executing Deadman Switch!"&lt;br&gt;
        # whatever you want to do goes here&lt;br&gt;
    end&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This script now uses the &lt;code&gt;.last_reset&lt;/code&gt; method we put on our &lt;code&gt;DeadmansSwitch&lt;/code&gt; class to check when you last reset the switch. If it was less than seven days ago, it just prints "Still alive!" and exits; if you didn't reset the switch in over a week, it'll execute whatever script you tell it to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I put a week in my script because I figured that was a reasonable timeframe for my needs. You can change that to 2 days, two weeks, a month, a year, whatever you feel the right timeframe for your needs. Ruby's &lt;code&gt;Time&lt;/code&gt; class offers great methods for denoting a timeframe, which you can use similar to the &lt;code&gt;7.days.ago&lt;/code&gt; that I used.&lt;/p&gt;

&lt;p&gt;Now if you run &lt;code&gt;rake deadmans_switch&lt;/code&gt; in the console, you should get:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;shell&lt;br&gt;
$  rake deadman_switch &lt;br&gt;
=&amp;gt; Still Alive!&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;At this point, you can start working on the actual body of the script, which will be different depending on what it is you need a dead man's switch, so I'll leave you to it.&lt;/p&gt;

&lt;p&gt;If you want to test and debug the script by running it and don't want to wait seven days for the switch to expire, you can change the &lt;code&gt;7.days.ago&lt;/code&gt; in your &lt;code&gt;if&lt;/code&gt; statement to something more reasonable like &lt;code&gt;7.minutes.ago&lt;/code&gt;. Just don't forget to change it back when you're done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Have You Been Triggered?
&lt;/h3&gt;

&lt;p&gt;Now that we have a script and a way to ensure it only runs after a given time, there's one more thing we need to do. We probably don't want this script to run more than once.&lt;/p&gt;

&lt;p&gt;For local scripts that run using a cron job, this isn't such an issue. You can probably use the script to reset the crontab. But this is a script running on Heroku where we don't have access to the scheduler from within the script, so we'll have to get creative.&lt;/p&gt;

&lt;p&gt;One option I explored, which turned out to be a dead-end for reasons I'll explain soon, but I'll include it here anyway purely for its entertainment value, was to put the following on the last line fo my script:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;ruby&lt;br&gt;
File.delete(__FILE__)&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;__FILE__&lt;/code&gt; is a Ruby constant representing the current file, so what that line of code does is it deletes the current file once it reaches that line. Sort of like those spy movies where you get a note telling you to destroy it once you're finished reading it.&lt;/p&gt;

&lt;p&gt;At first, I didn't think it would work. Spending the better part of last year developing for Windows servers and battling numerous "file in use" errors, I was sure Linux wouldn't let me do it either. But I did it just for kicks, and it turns out Linux is a lot more trusting! You can even try it yourself; create a test file, paste the above line of code, run it using Ruby, and watch it disappear!&lt;/p&gt;

&lt;p&gt;While that's a pretty fun solution, unfortunately, it won't work for our purposes. Heroku, as mentioned in Part 1, uses an ephemeral file system, so even if we delete the file containing the script, the next time the app deploys, Heroku will do a fresh pull from GitHub, and our file will reappear.&lt;/p&gt;

&lt;p&gt;If we want to keep track of whether our script ran, we will need to use something that persists, like a database entry. Fortunately, I'm a psychic who can see into the future, and if you remember, in Part 1, when we created our &lt;code&gt;deamans_switches&lt;/code&gt; table, we added a &lt;code&gt;triggered&lt;/code&gt; column, and we will now make use of it.&lt;/p&gt;

&lt;p&gt;The first thing we will do is add two more methods to our &lt;code&gt;DeadmansSwitch&lt;/code&gt; class in &lt;code&gt;app/models/deadmans_switch.rb&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ruby&lt;br&gt;
    def self.triggered&lt;br&gt;
        create(triggered: true)&lt;br&gt;
    end&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def self.triggered?
    where(triggered: true).size &amp;gt; 0
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The first one, &lt;code&gt;.triggered&lt;/code&gt;, creates a new row in the database, but unlike a regular row, we set the &lt;code&gt;triggered&lt;/code&gt; column to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The second method, &lt;code&gt;.triggered?&lt;/code&gt;, queries the database and checks if there are any rows where &lt;code&gt;triggered&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt; and returns &lt;code&gt;true&lt;/code&gt; if there is at least one such row.&lt;/p&gt;

&lt;p&gt;Now let's go back to our Rake task and put those to use.&lt;/p&gt;

&lt;p&gt;The first thing we will do is add the following line at the end of the script after everything we wanted to run ran:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;ruby&lt;br&gt;
Deadman.triggered&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will enter a row in the database with &lt;code&gt;triggered: true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, all the way at the beginning of our script, in the very first line, we will put the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;ruby&lt;br&gt;
abort if Deadman.triggered?&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will check the database and see if there are any rows where &lt;code&gt;triggered: true&lt;/code&gt; and abort the script if it finds any.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Scheduled
&lt;/h3&gt;

&lt;p&gt;At this point, we have a working deadman's switch. The only thing left is to deploy it along with our Rails app and set up a scheduler to run it.&lt;/p&gt;

&lt;p&gt;My app is deployed on Heroku, so the instructions will be for getting set up on Heroku. If you have a different setup, things will work differently for you.&lt;/p&gt;

&lt;p&gt;So once you've written up all the code, committed it, and pushed it off to Heroku, here is how to go about scheduling your script.&lt;/p&gt;

&lt;p&gt;From the Heroku dashboard, navigate to the "Resources" tab.&lt;/p&gt;

&lt;p&gt;Once in the "Resources" tab, find the button that says "Find more add-ons" and click on it.&lt;/p&gt;

&lt;p&gt;Search for &lt;a href="https://elements.heroku.com/addons/scheduler"&gt;Heroku Scheduler&lt;/a&gt; and install it and provision it to your app.&lt;/p&gt;

&lt;p&gt;Once it's installed properly, you should see it under add-ons in your resources tab. Click on the link to the Scheduler and then on the button that says "Add Job."&lt;/p&gt;

&lt;p&gt;You will be prompted to set a schedule to run your task (I set it to run every night at midnight) and which command to run, which you should set to &lt;code&gt;rake deadmans_switch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hit "Save Job," and that's it, you're all set!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>scripting</category>
    </item>
    <item>
      <title>Part 1: Adding A Dead-Man's Switch To A Rails Application</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Mon, 14 Dec 2020 02:05:09 +0000</pubDate>
      <link>https://forem.com/yechielk/part-1-adding-a-dead-man-s-switch-to-a-rails-application-bgp</link>
      <guid>https://forem.com/yechielk/part-1-adding-a-dead-man-s-switch-to-a-rails-application-bgp</guid>
      <description>&lt;h3&gt;
  
  
  Content Warning
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;The subject of this post is a bit morbid and deals with contemplating our mortality. If that upsets you, feel free to skip the introduction and go straight to the technical parts of the implementation in the section titled &lt;strong&gt;"The Good Stuff."&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;I was recently thinking about the concept of a dead man's switch.&lt;/p&gt;

&lt;p&gt;For those who are unfamiliar, a dead man's switch is a process that is designed to run automatically unless someone steps in to stop it. The idea is to increase the resiliency and/or safety of a system by having a safety mechanism that will stop it unless someone consciously steps in and overrides it.&lt;/p&gt;

&lt;p&gt;One of my favorite examples is in trains where the breaks are engaged by default. To release the breaks, the conductor has to lift a handle or a pedal and keep holding it. The moment the conductor lets go of the pedal, the breaks will engage and stop the train. The idea behind the switch is that if something were to happen and the conductor was to pass out, the train won't continue barreling down the tracks; instead, it would come to a screeching halt immediately.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmachinerysafety101.com%2Fwp-content%2Fuploads%2F2011%2F03%2FColo4574.jpg" alt="A ‘deadman’ pedal in a diesel-electric railway locomotive"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A ‘deadman’ pedal in a diesel-electric railway locomotive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Many other systems have similar concepts, even if they don't contain physical switches.&lt;/p&gt;

&lt;p&gt;For example, a computer might contain a script designed to run automatically after a few days unless the timer gets reset.&lt;/p&gt;

&lt;p&gt;For example, many of us have things we might want to happen in the event that something terrible happens to us. Maybe we want to pass on the passwords to important accounts or notify loved ones about a life insurance policy, or perhaps to wipe out some deep dark secret we want no one to find out about.&lt;/p&gt;

&lt;p&gt;One way we could go about it is to set a cron job on our computer to run every day. The cron job could look at a file and see when it was last updated, and if it wasn't updated in, say, over a week, it'll run a script that sends an email or does whatever else it is we want it to do.&lt;/p&gt;

&lt;p&gt;There's a limitation with that, and that is that it assumes your computer will be on, which is not a given for my personal computer.&lt;/p&gt;

&lt;p&gt;The next best option is to have my switch hosted in the cloud somewhere, though that comes with some expenses, and access to reset the times is a little more complicated than updating a file on my computer.&lt;/p&gt;

&lt;p&gt;The idea I came up with was to add the switch to my portfolio site. It's the only website I own, so I could do whatever I want with it. I'm already paying for hosting, so there's no extra expense for the switch, so it made sense for me.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Good Stuff
&lt;/h3&gt;

&lt;p&gt;This post assumes you already have a Rails app and that you have a rudimentary knowledge of Ruby and Rails.&lt;/p&gt;

&lt;p&gt;My Rails app is hosted on Heroku. The tutorial's Rails portion should apply to any Rails app no matter where it's hosted, but some of the parts around running the sitch are Heroku specific. However, there are probably parallel mechanisms for other hosting options as well.&lt;/p&gt;

&lt;p&gt;There's one part of the tutorial that requires a Twilio account with an associated project. It's not completely necessary, but if you want your switch to send out reminders as it gets closer to triggering, you might want to create a Twilio account.&lt;/p&gt;

&lt;p&gt;Creating the account is free, and if you use my &lt;a href="//www.twilio.com/referral/PWhqJW"&gt;referral link&lt;/a&gt;, you'll get $10 SMS credit, which should be more than enough for our purposes.&lt;/p&gt;

&lt;p&gt;Our dead man's switch will need a few components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A way to reset the switch.&lt;/li&gt;
&lt;li&gt;A script that will run every day and check if you reset the switch.

&lt;ul&gt;
&lt;li&gt;If you did reset the switch recently, the script would abort.&lt;/li&gt;
&lt;li&gt;If you did not, the script would do whatever it is you want it to do.&lt;/li&gt;
&lt;li&gt;Once the script runs, we want a way to keep track of that, so it doesn't run again.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll start with the way to reset the switch.&lt;/p&gt;

&lt;p&gt;If we were hosting this switch locally, a simple way to do this would be to look at the last time a given file was updated, and then keep updating that file every day.&lt;/p&gt;

&lt;p&gt;Unfortunately, that wouldn't work for a script hosted on Heroku. Heroku uses an ephemeral filesystem for its apps, and every time the app gets redeployed, or if it crashes and has to be restarted, or even if it's taken down for routine maintenance, Heroku makes a fresh pull on the repo and any changes you made to the filesystem get overwritten.&lt;/p&gt;

&lt;p&gt;We have to find a way to keep track of when the switch was reset, and a way that will persist.&lt;/p&gt;

&lt;p&gt;I did it by creating a database table where I could enter rows containing a timestamp and then check the timestamp of the last row added.&lt;/p&gt;

&lt;p&gt;Once I was already making a table, I figured I could add an ActiveRecord model to my app. That would give me some built-in ActiveRecord methods to interact with my table to read and update it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's Start Generating!
&lt;/h3&gt;

&lt;p&gt;So let's start by opening up our terminal and generating our migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails generate model CreateDeadmansSwitch triggered:boolean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command created a number of files. The ones we care about are &lt;code&gt;db/migrate/[migration-id]_create_deadmans_switches.rb&lt;/code&gt; containing our database migration for a &lt;code&gt;deadman_switches&lt;/code&gt; table that has the standard timestamps columns (as well as a boolean column called &lt;code&gt;triggered&lt;/code&gt;) we will discuss later).&lt;/p&gt;

&lt;p&gt;The second file we care about is in &lt;code&gt;app/models/deadmans_switch.rb&lt;/code&gt; that creates our &lt;code&gt;DeadmansSwitch&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Let's open up &lt;code&gt;db/migrate/20201211142929_create_deadmans_switches.rb&lt;/code&gt;:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateDeadmansSwitches&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5.2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:deadmans_switches&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;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt; &lt;span class="ss"&gt;:triggered&lt;/span&gt;

      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamps&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we run this migration, let's make one small change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-    t.boolean :triggered
&lt;/span&gt;&lt;span class="gi"&gt;+    t.boolean :triggered, default: false
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added a default value of &lt;code&gt;false&lt;/code&gt; to the &lt;code&gt;triggered&lt;/code&gt; column, so that is the value it will contain unless we specify otherwise.&lt;/p&gt;

&lt;p&gt;Now we can go back to our terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will run our migration and add the &lt;code&gt;deadmans_switches&lt;/code&gt; table to our database schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Models Models Everywhere
&lt;/h3&gt;

&lt;p&gt;Now that we have our database set up let's look at our model in &lt;code&gt;app/models/deadmans_switch.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Currently, our model is pretty empty:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeadmansSwitch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's add two class methods to our class; one to reset the switch and one to check when it was last reset:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeadmansSwitch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;
        &lt;span class="n"&gt;create&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt;
        &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added two class methods.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;reset&lt;/code&gt; method creates a new instance of the &lt;code&gt;DeadmansSwitch&lt;/code&gt; class, adding a new row to the database.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;last_reset&lt;/code&gt; method looks at the last row in the database and, if it finds one, returns the date and time it was created on; otherwise, it returns the current time (this is to protect against an edge case where we run the app before we rest the switch for the first time).&lt;/p&gt;

&lt;h3&gt;
  
  
  Routing
&lt;/h3&gt;

&lt;p&gt;Next, we need a route that will listen for, and trigger our switch.&lt;/p&gt;

&lt;p&gt;My setup relies on two assumptions that apply to my app.&lt;/p&gt;

&lt;p&gt;First, I have an &lt;code&gt;AdminController&lt;/code&gt; that handles all the admin-related routing in my app.&lt;/p&gt;

&lt;p&gt;Second, in my &lt;code&gt;AdminController&lt;/code&gt;, I have a helper method called &lt;code&gt;is_admin?&lt;/code&gt; that checks if the current user is an admin.&lt;/p&gt;

&lt;p&gt;The details of your app may vary, so adjust accordingly.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;config/routs.rb&lt;/code&gt; add the following:&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="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'deadman/reset'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'admin#reset'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up a route at &lt;code&gt;/deadman/reset&lt;/code&gt; and routes it to the &lt;code&gt;reset&lt;/code&gt; function in my &lt;code&gt;AdminController&lt;/code&gt; and gives us access to a &lt;code&gt;deadman_reset_path&lt;/code&gt; function that points to the new route.&lt;/p&gt;

&lt;p&gt;If you have your app running, don't forget to restart it in the terminal for any routing changes to take effect.&lt;/p&gt;

&lt;p&gt;Next, let's add that function in &lt;code&gt;AdminController&lt;/code&gt; (or whichever controller you decided to use for your app):&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reset&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_admin?&lt;/span&gt;
            &lt;span class="no"&gt;DeadmansSwitch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;
            &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;admin_root_path&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;login_path&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checks if the current user is an admin (you don't want just anyone who happens upon the URL to have the ability to reset your switch).&lt;/p&gt;

&lt;p&gt;If the user is an admin, we call &lt;code&gt;DeadmansSwitch.reset&lt;/code&gt; and redirect back to the page that called it; in my case, to the admin page (again, the routing in your app might be different, so adjust accordingly).&lt;/p&gt;

&lt;h3&gt;
  
  
  What A View!
&lt;/h3&gt;

&lt;p&gt;Now that we have that in place let's use it in our app.&lt;/p&gt;

&lt;p&gt;Where exactly you put this in your app depends on the layout of your app. Ideally, you would put it in a part of your app that only you can access, like an admin console or something.&lt;/p&gt;

&lt;p&gt;This is what I have in my app, feel free to style it as elaborately, or plainly, as you wish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="s2"&gt;"Reset Switch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deadman_reset_path&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;(Last reset &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;time_ago_in_words&lt;/span&gt; &lt;span class="no"&gt;Deadman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt; ago)&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a link to our reset route, followed by a small helpful note which uses the &lt;code&gt;time_ago_in_words&lt;/code&gt; helper method to display when the last time you reset the switch.&lt;/p&gt;

&lt;p&gt;Here is what it looks like on my portfolio:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwpwey2cu6efdpnwtlorn.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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwpwey2cu6efdpnwtlorn.png" alt="A screenshot showing the link and the above text. "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now we have part of our dead man's switch in place, the part where we can reset it every day, so it knows we're still around and doesn't trigger our script.&lt;/p&gt;

&lt;p&gt;In part 2, we will speak about how to write the script that checks our switch and runs if we haven't reset it in a while.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>scripting</category>
      <category>ruby</category>
    </item>
    <item>
      <title>What are your favorite Kotlin resources?</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Mon, 26 Oct 2020 13:01:57 +0000</pubDate>
      <link>https://forem.com/yechielk/what-are-your-favorite-kotlin-resources-f43</link>
      <guid>https://forem.com/yechielk/what-are-your-favorite-kotlin-resources-f43</guid>
      <description>&lt;p&gt;Hey all!&lt;/p&gt;

&lt;p&gt;I recently joined a new team and we do most of our work with Kotlin.&lt;/p&gt;

&lt;p&gt;It looks like a great language to work with, and I'm looking for resources to help me ramp up.&lt;br&gt;
Something for someone familiar with programming concepts, and looking to ramp up on the Kotlin specifics, conventions, and best practices.&lt;/p&gt;

&lt;p&gt;I'm a hands-on learner, and I learn best by trying thongs out and breaking them, so those are the resources I'm mostly looking for.&lt;/p&gt;

&lt;p&gt;Something like Go's &lt;a href="https://tour.golang.org/welcome/1"&gt;A Tour Of Go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Any ideas?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>kotlin</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Automating Google Form Submission In Ruby</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Thu, 10 Sep 2020 18:22:58 +0000</pubDate>
      <link>https://forem.com/yechielk/automating-google-form-submission-in-ruby-30b0</link>
      <guid>https://forem.com/yechielk/automating-google-form-submission-in-ruby-30b0</guid>
      <description>&lt;p&gt;Like many children around the country, my kids started school this week.&lt;/p&gt;

&lt;p&gt;Due to COVID-19 regulations, my kids' preschool requires that we fill out a form every morning, certifying that our kids haven't developed any COVID symptoms and haven't been exposed to anyone COVID positive.&lt;/p&gt;

&lt;p&gt;Being that I have two kids in preschool and my wife teaches there as well, that means filling out the form three times every morning. Something no programmer worth their salt would be willing to do without automating it!&lt;/p&gt;

&lt;p&gt;My first instinct was to look if Google Forms had some sort of API for Forms that I could use to write a script, but it turns out that it wasn't even necessary!&lt;/p&gt;

&lt;p&gt;Google forms are, at their base, HTML forms. So sending a POST request to the form's URL with the form's data is all you need to do.&lt;/p&gt;

&lt;p&gt;The first step you need to do is determine the URL you need to POST to. One way to do it is to open your browser tools to the "Inspect" tab, find the form element, and find the &lt;code&gt;action&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;For Google Forms, however, that isn't necessary! All you need is the URL of the form you are filling out.&lt;/p&gt;

&lt;p&gt;Google Form URLs are of the format &lt;code&gt;https://docs.google.com/forms/d/e/[a-long-form-ID]/viewform&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The URL you need to submit the form to is the same, except you need to change the &lt;code&gt;/viewform&lt;/code&gt; at the end to &lt;code&gt;/formResponse&lt;/code&gt; (Really, Google? You couldn't name them consistently? Either both lowercase or both camelCase?) so the final URL should look like this: &lt;code&gt;https://docs.google.com/forms/d/e/[a-long-form-ID]/formResponse&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, you will need a list of the form's inputs and your responses.&lt;/p&gt;

&lt;p&gt;Here Google obfuscates a bit.&lt;/p&gt;

&lt;p&gt;Every HTML form is made up of inputs; each input has a &lt;code&gt;name&lt;/code&gt; attribute, and when the form is submitted, those inputs get submitted in the format &lt;code&gt;[input-name]=[value]&lt;/code&gt;. The &lt;code&gt;name&lt;/code&gt; attribute is usually something descriptive, like "age", "date-of-birth", etc. but Google gives each input an ID and uses &lt;em&gt;that&lt;/em&gt; as the &lt;code&gt;name&lt;/code&gt; attribute, so it looks more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"entry.[input-id]"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; go through the form and collect the input IDs one by one to figure out what each one stands for, but I took a short cut.&lt;/p&gt;

&lt;p&gt;In the "Inspect" tab of your dev tools, find the &lt;code&gt;form&lt;/code&gt; element for the form and edit the &lt;code&gt;action&lt;/code&gt; attribute so it's set to a random string like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"xxxxxx"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This way, the form won't submit to your school when you hit "Submit" in a moment.&lt;/p&gt;

&lt;p&gt;Next, fill out the form the way you usually would with all of the correct answers.&lt;/p&gt;

&lt;p&gt;Next, head over to the "Network" tab in your browser's dev tools. Once you're there, hit "Submit" on your form. You should see several requests show up in your browser tools. You're looking for the one that failed and got a 400 status (if you didn't edit the form's action in the previous step then it will not have failed, in which case you're looking for the one that was a POST request and says "formResponse" under "domain").&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MxY0JAUh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pegqnqffsj32ei44hdcb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MxY0JAUh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pegqnqffsj32ei44hdcb.png" alt="screenshot of the Network tab in the dev tools showing the above"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on the request, you should see information about the request populate on the tab's right side. Click on the "Request" tab, and you should see a bunch of key/value pairs representing the form's inputs and the values you entered. Right-click and chose "Copy All" and paste it into a text file.&lt;/p&gt;

&lt;p&gt;Here's what I got when I did that:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.1634501314"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"child+name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.1876184383"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"parent+name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.338135299"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Toddlers Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.464151171"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"No"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.1301996500"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"None+of+the+Above"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.46633140"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"None+of+the+Above"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.693698665"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.227426312"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"No"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.1433839005"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"No"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.338135299_sentinel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.464151171_sentinel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.1301996500_sentinel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.46633140_sentinel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.693698665_sentinel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.227426312_sentinel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entry.1433839005_sentinel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fvv"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"draftResponse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"[null,null,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;1394368204515955404&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pageHistory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fbzx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"1394368204515955404"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can clean that up a bit by getting rid of all of the entries ending with &lt;code&gt;_sentinel&lt;/code&gt; (they're an artifact of how Google Forms represent checkboxes and radio buttons) as well as the few fields at the bottom (I'm not sure what they do, but I confirmed that the form works without them).&lt;/p&gt;

&lt;p&gt;We also want to convert this JSON object to a Ruby hash, which I did by find/replacing all of the "&lt;code&gt;:&lt;/code&gt;"s with hash rockets "&lt;code&gt;=&amp;gt;&lt;/code&gt;".&lt;/p&gt;

&lt;p&gt;One last thing you'll want to do is replace all the "&lt;code&gt;+&lt;/code&gt;" signs that were put instead of the whitespaces (e.g. &lt;code&gt;"entry.1301996500":"None+of+the+Above"&lt;/code&gt;) and change them back to spaces.&lt;/p&gt;

&lt;p&gt;My final form data looks like this now:&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="n"&gt;form_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.338135299"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Toddlers Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.1634501314"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"[child name]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.1876184383"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"[parent name]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.464151171"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"No"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.1301996500"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"None of the Above"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.46633140"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"None of the Above"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.693698665"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.227426312"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"No"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.1433839005"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"No"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can take that information and past it into a file called &lt;code&gt;school-form-submitter.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you would just like to see the finished code it will be shared at the end of this post.&lt;/p&gt;

&lt;p&gt;If you only need to submit the form for one child, then that's all you need. If you have more than one child like me, you will have to add in the information you need dynamically.&lt;/p&gt;

&lt;p&gt;In my case, the parts of the form that had to change for each child were the child's name and class ("Toddler Class" in the form above). So I extracted that information into a hash I called &lt;code&gt;names&lt;/code&gt;:&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="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Child 1 Name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Child 1 Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"Child 2 Name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Child 2 Class"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"Wife's Name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Staff"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then I iterated over the hash and filled out the form like this:&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="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;form_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.338135299"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.1634501314"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.1876184383"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Parent Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.464151171"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"No"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.1301996500"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"None of the Above"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.46633140"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"None of the Above"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.693698665"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.227426312"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"No"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"entry.1433839005"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"No"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Submitting form for &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; in &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you run the ruby file now, you should get a series of lines printed that look like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Submitting form &lt;span class="k"&gt;for &lt;/span&gt;Child 1 Name &lt;span class="k"&gt;in &lt;/span&gt;Child 1 Class
Submitting form &lt;span class="k"&gt;for &lt;/span&gt;Child 2 Name &lt;span class="k"&gt;in &lt;/span&gt;Child 2 Class
Submitting form &lt;span class="k"&gt;for &lt;/span&gt;Wife&lt;span class="s1"&gt;'s Name in Staff
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The only thing left to do now is to make our POST request.&lt;/p&gt;

&lt;p&gt;Being that we are not trying to do anything too complicated with the returned data, I believe Ruby's built-in &lt;code&gt;Net::HTTP&lt;/code&gt; library should be enough.&lt;/p&gt;

&lt;p&gt;Add the following to the top of your file:&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="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'net/http'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We're going to use &lt;code&gt;Net::HTTP&lt;/code&gt;'s &lt;code&gt;post_form&lt;/code&gt; method. The method takes two arguments: a &lt;code&gt;URI&lt;/code&gt; object and a hash of form inputs. So let's convert the URL we got earlier to a &lt;code&gt;URI&lt;/code&gt; object like this:&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="n"&gt;form_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://docs.google.com/forms/d/e/[a-long-form-ID]/formResponse"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can then submit our form (and print the return status message as a sanity check) like this:&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="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post_form&lt;/span&gt; &lt;span class="n"&gt;form_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form_data&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Status &lt;/span&gt;&lt;span class="si"&gt;#{&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;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And there you have it! Instead of tediously filling out forms every morning, you can submit them by running &lt;code&gt;ruby school-form-submitter.rb&lt;/code&gt; from your console, and all it took was around 25 lines of code!&lt;/p&gt;

&lt;p&gt;Here is the complete code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



</description>
      <category>ruby</category>
      <category>beginners</category>
      <category>covid</category>
    </item>
    <item>
      <title>How Much Have You Made Off Of DEV Monetization?</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Tue, 30 Jun 2020 15:25:04 +0000</pubDate>
      <link>https://forem.com/yechielk/how-much-have-you-made-off-of-dev-monetization-2ao6</link>
      <guid>https://forem.com/yechielk/how-much-have-you-made-off-of-dev-monetization-2ao6</guid>
      <description>&lt;p&gt;It's now over 2 weeks since DEV announced that it added Web Monetization:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/devteam" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1%2Fd908a186-5651-4a5a-9f76-15200bc6801f.jpg" alt="The DEV Team"&gt;
      &lt;div class="ltag__link__user__pic"&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-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1075%2F1c1975ce-97e8-401f-b99f-1ea88f9cae3e.jpeg" alt=""&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/devteam/dev-is-now-web-monetized-21db" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;DEV is now Web Monetized&lt;/h2&gt;
      &lt;h3&gt;Peter Kim Frank for The DEV Team ・ Jun 10 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webmonetization&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#gftwhackathon&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#coil&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#meta&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;I thought it would be fun for us to show off how much money we've all raked in!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/LCdPNT81vlv3y/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/LCdPNT81vlv3y/giphy.gif" alt="person making snow angel in a pile of bills. "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Personally I decided to point my content at my Bitcoin ledger, so I can finally say I own Bitcoin!&lt;/p&gt;

&lt;p&gt;As you can see, I'm currently the proud owner of about ฿0 (accurate to about 4 decimal places).&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1k9pihnvzjpwa4xxpfro.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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1k9pihnvzjpwa4xxpfro.png" alt="Screenshot of my Uphold balance showing a balance of 0.00043349 Bitcoin, or 3.98 USD. "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clearly I have a ways to go before I can quit my day job.&lt;/p&gt;

&lt;p&gt;How much have you made so far?&lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>discuss</category>
      <category>monetization</category>
    </item>
    <item>
      <title>Announcing Torah &amp;&amp; Tech; The Book.</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Tue, 19 May 2020 19:01:30 +0000</pubDate>
      <link>https://forem.com/yechielk/announcing-torah-tech-the-book-4m39</link>
      <guid>https://forem.com/yechielk/announcing-torah-tech-the-book-4m39</guid>
      <description>&lt;p&gt;About a year and a half ago, my friend &lt;a href="https://dev.to/benhayehudi"&gt;Ben Greenberg&lt;/a&gt; and I were trying to come up with a project that would allow us to keep in touch as his family made Aliyah and moved halfway across the planet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QC1I9DEv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/257/1%2AOhjCxTFwZ6sQcywFBTR_ZQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QC1I9DEv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/257/1%2AOhjCxTFwZ6sQcywFBTR_ZQ.png" alt="The Torah and Tech logo."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result was &lt;strong&gt;Torah &amp;amp;&amp;amp; Tech&lt;/strong&gt;, a weekly newsletter that covers the two interests we both have in common: tech and Jewish laws and ethics.&lt;/p&gt;

&lt;p&gt;The following 75 weeks (and counting!) have been loads of fun! We grew our readership from just the two of us (and our supportive spouses) to close to 200 weekly subscribers.&lt;/p&gt;

&lt;p&gt;The newsletters covered topics ranging from office etiquette and mentorship to the future of AI and the use of pointers in Golang to coping strategies during COVID-19 imposed quarantine.&lt;/p&gt;

&lt;p&gt;As we hit the one-year mark, the realization hit that this wasn’t just a side project that would last a month or two and that we were in this for the long-haul. The idea came up to collect the first year’s worth of Torah thoughts and publish them in a book.&lt;/p&gt;

&lt;p&gt;The idea sounded intimidating at first, but thanks to some help from a few good friends, we are happy to announce, just in time for Shavuot, that the first volume of Torah &amp;amp;&amp;amp; Tech is available for pre-order!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wMEq9tYJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AxL9qpvQ2KX4ynJuZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wMEq9tYJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AxL9qpvQ2KX4ynJuZ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, due to coronavirus related delays, the print version won’t be available for another few weeks. Still, the e-book version is available for pre-order at most retailers and will be delivered to your favorite e-reader device on June 1st.&lt;/p&gt;

&lt;p&gt;Just head over to &lt;a href="https://torahandtech.dev/"&gt;&lt;strong&gt;torahandtech.dev&lt;/strong&gt;&lt;/a&gt; and order your copy today.&lt;/p&gt;

&lt;p&gt;Of course, while you’re there, you can use the opportunity to sign up for the newsletter so you can get a preview of volume two before anyone else. You will get a weekly Torah though in your inbox every week, just in time to share it at your Shabbat table.&lt;/p&gt;

&lt;p&gt;If you have any comments or feedback about the book, please don’t hesitate to reach out to Ben or me on our social media. We LOVE talking about anything Torah/tech-related!&lt;/p&gt;




</description>
      <category>showdev</category>
      <category>books</category>
      <category>learning</category>
      <category>coding</category>
    </item>
    <item>
      <title>Need a hug? There's a bot for that!</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Wed, 26 Feb 2020 19:22:27 +0000</pubDate>
      <link>https://forem.com/yechielk/need-a-hug-there-s-a-bot-for-that-4bim</link>
      <guid>https://forem.com/yechielk/need-a-hug-there-s-a-bot-for-that-4bim</guid>
      <description>&lt;p&gt;Hey all!&lt;/p&gt;

&lt;p&gt;I know I haven't posted in a while, but after a long break I finally had a free weekend and was able to work on a side project (my first one in over a year! Don't believe the gatekeepers, you can have a successful career without side projects!)&lt;/p&gt;

&lt;p&gt;It all started a few weeks ago when I was feeling down and felt like I could really use a hug.&lt;/p&gt;

&lt;p&gt;Not being close to anyone with whom I'm friends on a hugging basis, I did the next best thing and turned to Twitter:&lt;/p&gt;

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

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



&lt;/p&gt;

&lt;p&gt;Sometime later, I was tweeting with a friend who was feeling down, and they mentioned that they could use a hug as well. I immediately sent them a (virtual) hug as well. And the idea came up for a bot that would help people feeling down by sending hugs at them!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.giphy.com%2Fmedia%2FDmkfCWnUgT1cc%2Fgiphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.giphy.com%2Fmedia%2FDmkfCWnUgT1cc%2Fgiphy.gif" alt="a cat hugging her teddy bear"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm no stranger to Twitter bots, having written one some time ago:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/yechielk" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F14816%2Fe88e23df-00dd-4d43-8454-65abd51780c8.jpg" alt="yechielk"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/yechielk/creating-my-first-twitter-bot-1kj4" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Creating my first Twitter Bot&lt;/h2&gt;
      &lt;h3&gt;Yechiel Kalmenson ・ Dec 26 '17&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#bots&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#twitter&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;This time I decided to write one in Python.&lt;/p&gt;

&lt;p&gt;Python has a great library for interacting with Twitter called &lt;a href="http://www.tweepy.org/" rel="noopener noreferrer"&gt;tweepy&lt;/a&gt;, and even a library around &lt;a href="https://github.com/Giphy/giphy-python-client" rel="noopener noreferrer"&gt;Giphy&lt;/a&gt; that makes things like getting a random gif of a hug pretty straightforward!&lt;/p&gt;

&lt;p&gt;Between those two libraries, I didn't have to write much code on my own. The whole bot is about 35 lines of code!&lt;/p&gt;

&lt;p&gt;I followed this dev.to post by &lt;a href="https://dev.to/emcain"&gt;@emcain&lt;/a&gt; for instructions on how to get my bot set up on Heroku: &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/emcain" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F19115%2F0b314a67-e0b5-4cfe-beea-515ef16c5202.png" alt="emcain"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/emcain/how-to-set-up-a-twitter-bot-with-python-and-heroku-1n39" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to Set up a Twitter Bot with Python and Heroku&lt;/h2&gt;
      &lt;h3&gt;Emily Cain ・ Jun 24 '18&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#heroku&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#flask&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#twitter&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;And by the end of the weekend, &lt;a href="https://twitter.com/ICanHazHugzPlz" rel="noopener noreferrer"&gt;@ICanHazHugzPlz&lt;/a&gt; was born!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.giphy.com%2Fmedia%2F2GnS81AihShS8%2Fgiphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.giphy.com%2Fmedia%2F2GnS81AihShS8%2Fgiphy.gif" alt="a rabbit saying "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope Reduction
&lt;/h2&gt;

&lt;p&gt;Of course, no project comes out the way you expect it.&lt;/p&gt;

&lt;p&gt;I had initially envisioned that the bot would reply to people asking it for hugs with a gif of a hug.&lt;/p&gt;

&lt;p&gt;It turns out that to implement that, I would have to work out a queue that could keep track of which tweets the bot already replied to. That queue would need to maintain state between restarts; otherwise, the bot would keep spamming anyone who tweeted at it every time the script restarts (multiple times a day on Heroku's free dyno), as my bots earliest followers found out the hard way (sorry!!!)&lt;/p&gt;

&lt;p&gt;I wasn't planning on spending more than a weekend on the project, so in the end, I settled on a bot that tweets a hug at the world every few hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show Me The Code!
&lt;/h2&gt;

&lt;p&gt;If you would like to take a look at the code for the bot, you'll find it here (including the failed implementation of the replying version of the bot on a separate branch):&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/achasveachas" rel="noopener noreferrer"&gt;
        achasveachas
      &lt;/a&gt; / &lt;a href="https://github.com/achasveachas/hug-bot" rel="noopener noreferrer"&gt;
        hug-bot
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A twitter bot that gives out free hugs.
    &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;Hug Bot&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;Hug Bot is a Bluesky bot (it was originally written as a Twitter bot) written in Python that posts a random hug gif every few hours.&lt;/p&gt;
&lt;p&gt;You can follow &lt;a href="https://bsky.app/profile/hugbot.bsky.social" rel="nofollow noopener noreferrer"&gt;@hugbot.bsky.social&lt;/a&gt; on Bluesky for all your huggy needs.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/64e19f723da5d5c7728cc0a5ac12b87b9f294312a392a77e73e294cb37e8d614/68747470733a2f2f6d65646961312e67697068792e636f6d2f6d656469612f7854315847517665304c7843626c444c72322f67697068792e676966"&gt;&lt;img src="https://camo.githubusercontent.com/64e19f723da5d5c7728cc0a5ac12b87b9f294312a392a77e73e294cb37e8d614/68747470733a2f2f6d65646961312e67697068792e636f6d2f6d656469612f7854315847517665304c7843626c444c72322f67697068792e676966" alt="Two dudes hugging wholesomely"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;License&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;This bot is licensed under the &lt;a href="https://github.com/achasveachas/hug-bot/LICENSE" rel="noopener noreferrer"&gt;MIT&lt;/a&gt; license.&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;Bug reports and pull requests are welcome on GitHub at &lt;a href="https://github.com/achasveachas/alternate-side-bot/issues" rel="noopener noreferrer"&gt;https://github.com/achasveachas/hug-bot/issues&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the &lt;a href="http://contributor-covenant.org/" rel="nofollow noopener noreferrer"&gt;Contributor Covenant&lt;/a&gt; code of conduct.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contact&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Feel free to get in touch with me if you have any suggestions or comments at &lt;a href="http://yechiel.me" rel="nofollow noopener noreferrer"&gt;yechiel.me&lt;/a&gt;&lt;/p&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/achasveachas/hug-bot" 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;And of course, if you'd like to bless your Twitter timeline with wholesome hugs, please give my bot a follow at &lt;a href="https://twitter.com/ICanHazHugzPlz" rel="noopener noreferrer"&gt;@ICanHazHugzPlz&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.giphy.com%2Fmedia%2Fl2YWgw9u8tZw6iHw4%2Fgiphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.giphy.com%2Fmedia%2Fl2YWgw9u8tZw6iHw4%2Fgiphy.gif" alt="Rory Gilmore from Gilmore Girls giving a hug"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>showdev</category>
      <category>bots</category>
      <category>twitter</category>
    </item>
  </channel>
</rss>
