<?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: JoaoCardoso193</title>
    <description>The latest articles on Forem by JoaoCardoso193 (@joaocardoso193).</description>
    <link>https://forem.com/joaocardoso193</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%2F410467%2F12949f94-fcc9-409d-9725-d97da1979aea.jpg</url>
      <title>Forem: JoaoCardoso193</title>
      <link>https://forem.com/joaocardoso193</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/joaocardoso193"/>
    <language>en</language>
    <item>
      <title>Making My Own Answering Machine - A Quick Primer on the Inner Workings of Web Applications</title>
      <dc:creator>JoaoCardoso193</dc:creator>
      <pubDate>Fri, 15 Jan 2021 23:29:36 +0000</pubDate>
      <link>https://forem.com/joaocardoso193/making-my-own-answering-machine-a-quick-primer-on-the-inner-workings-of-web-applications-13eg</link>
      <guid>https://forem.com/joaocardoso193/making-my-own-answering-machine-a-quick-primer-on-the-inner-workings-of-web-applications-13eg</guid>
      <description>&lt;h1&gt;
  
  
  Table Of Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Intro&lt;/li&gt;
&lt;li&gt;Part 1: The Backend and the API&lt;/li&gt;
&lt;li&gt;Part 2: The Frontend Webpage&lt;/li&gt;
&lt;li&gt;Part 3: The Answering Machine Script&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Intro &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;Having been born near the very end of the '90s, I've always lived in a world of incredibly fast and innovative technological advancements. Within my first two decades of life, I've witnessed the rise of smartphones, the widespread adoption of home internet, the rise of social media, the digitalization of nearly every service, and much more.&lt;/p&gt;

&lt;p&gt;While I am grateful for all the convenience and entertainment modern technology is able to provide us with today, I can't help but be fascinated by the machines of yesteryear that I never got to use or experience. One such machine is the famous &lt;a href="https://recording-history.org/history-of-telephone-answering-machine/"&gt;answering machine&lt;/a&gt;, which hit peak popularity in the '80s and '90s. An endearing product of the era, this technology eventually became obsolete and was gradually fazed out of the mainstream when telephone companies started offering voice mail services.&lt;/p&gt;

&lt;p&gt;Still, the image of sitting on the couch at home with a glass of wine while listening to messages your friends and acquaintances left you throughout the day has always appealed to me - an idea that, undoubtedly, has been instilled into my brain by countless Hollywood classics.&lt;/p&gt;

&lt;p&gt;With the pandemic forcing all my university classes online this semester, I've decided to go back to sunny Portugal to finish my degree from home. While being back here definitely has its fair share of benefits, I'm sure I'll miss all the friends I've made in the U.S. and who I won't be able to see anymore until later this year when I go back to graduate.&lt;/p&gt;

&lt;p&gt;So I thought: "what better time to get myself an answering machine than now? Having a novel, yet retro, communication system that my friends can use to leave messages from anywhere in the world seems like the most fun way to keep in touch."&lt;/p&gt;

&lt;p&gt;And so that's what I did, I sat down in front of my computer for an afternoon and built a web app that allows my friends to reach me within seconds by sending a message that will be read out loud by my computer. In this blog post, I will share how I built this app to give you some inspiration on how you can build yours too, and as a means of explaining how web applications work in general. I will purposely omit details to avoid having my answering machine hijacked by internet strangers.&lt;/p&gt;

&lt;p&gt;Note: throughout this post, code or text encapsulated inside &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt; symbols is meant to be replaced.&lt;/p&gt;




&lt;h1&gt;
  
  
  Part 1: The Backend and the API &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;If a full web application can be thought of as a restaurant, the backend of a web application can be thought of as the restaurant's kitchen. For most customers, a restaurant's kitchen is a black-box where orders come in and food comes out, but for the workers of the restaurant, the kitchen is where all the magic happens. This is the place where food is stored, prepared, and cooked according to the restaurant's recipes. &lt;/p&gt;

&lt;p&gt;This is similar to how web applications work: for many interactive websites, there is a backend portion (a kitchen) that stores all of the relevant info (the food) about its users and content and that processes (prepares and cooks) this info as needed following the logic (the recipes) implemented by its developers (the cooks).&lt;/p&gt;

&lt;p&gt;Just like you wouldn't shout out your order directly to the kitchen in a restaurant, the backend of an application isn't directly accessed by its users. Instead, there is an intermediary to relay information between the two.&lt;/p&gt;

&lt;p&gt;An &lt;a href="https://www.mulesoft.com/resources/api/what-is-an-api#:~:text=API%20is%20the%20acronym%20for,you're%20using%20an%20API."&gt;Application Programming Interface&lt;/a&gt;, or API for short, is an interface that allows someone to interact with an application's backend without having direct access to its code. These are accessible through the internet and can be used to get information from or send information to an application.&lt;/p&gt;

&lt;p&gt;Following the restaurant analogy, an easy way to think about how an API works is to imagine it as a waiter. In a restaurant, the waiter (the API) comes to you, takes your order (your request), and passes it on to the kitchen (the backend). Upon receiving your order, the kitchen processes it and delivers your meal (a response) to you through the waiter again. This is essentially how an API is used so that anyone can use applications whose backend they don't have direct access to. Here's a good &lt;a href="https://www.youtube.com/watch?v=s7wmiS2mSXY"&gt;video&lt;/a&gt; of this analogy if this seems confusing.&lt;/p&gt;

&lt;p&gt;In order to implement my answering machine app, I needed a backend application with a database to store messages. I also needed an API that would communicate with this backend application. The API needed to work two ways: it had to be able to receive messages from anyone anywhere in the world and it had to be able to retrieve those messages to an authorized user (me 🙂).&lt;/p&gt;

&lt;p&gt;There are many ways to set up a backend application with an API, and many different frameworks to do so. Due to my previous experience with the Flatiron School, I decided to go with an API-only Ruby on Rails application. You can read more about how to set up one of those &lt;a href="https://guides.rubyonrails.org/api_app.html"&gt;here&lt;/a&gt;. However, any method that produces a backend application with an API that includes routes to post, get, and delete messages (the last one being optional) will work just fine.&lt;/p&gt;

&lt;p&gt;My main backend application is a simple Ruby on Rails application with an SQL database that stores message models with the following info: an author string, and a body text. There was no need to write custom methods for a message class or anything like that as the main logic of the answering machine was handled by the script that I wrote in the end (more on that later in this post).&lt;/p&gt;

&lt;p&gt;Truly, the gist of this project's backend and API is just a set of routes and an SQL table with the following schema:&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="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;version: &lt;/span&gt;&lt;span class="mi"&gt;2021_01_10_144146&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="c1"&gt;# These are extensions that must be enabled in order to support this database&lt;/span&gt;
  &lt;span class="n"&gt;enable_extension&lt;/span&gt; &lt;span class="s2"&gt;"plpgsql"&lt;/span&gt;

  &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="s2"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;force: :cascade&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;string&lt;/span&gt; &lt;span class="s2"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;limit: &lt;/span&gt;&lt;span class="mi"&gt;50&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;text&lt;/span&gt; &lt;span class="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&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;*Note: the &lt;code&gt;limit: 50&lt;/code&gt; and &lt;code&gt;null: false&lt;/code&gt; statements simply certify that the author's name is no longer than 50 characters and that a message's body is not empty.&lt;/p&gt;

&lt;p&gt;And here's the messages controller with all the relevant &lt;a href="https://learn.co/lessons/sinatra-restful-routes-readme#:~:text=A%20RESTful%20route%20is%20a,HTTP%20verb%20and%20the%20URL."&gt;RESTful routes&lt;/a&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;MessagesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
        &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Message&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="ss"&gt;body: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:body&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;author: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;
            &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Message sent successfully!'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Failed to send: message body can't be empty!"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;
        &lt;span class="c1"&gt;#patch request deletes message by default&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;


    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;#validates message by checking that it is not empty&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&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;With the backend and the API working together through a Ruby on Rails application, it was time to host it online. &lt;a href="https://www.heroku.com/platform"&gt;Heroku&lt;/a&gt; is a free hosting platform that anyone can use to deploy their applications on the internet, so that's what I used. The process of deploying a Ruby on Rails application with Heroku is detailed &lt;a href="https://devcenter.heroku.com/articles/getting-started-with-rails6"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Part 2: The Frontend Webpage &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;Great! With the backend and the API done, it was time to set up a user-friendly access point where friends could send me messages by posting them to the API.&lt;/p&gt;

&lt;p&gt;Continuing our previous analogy, think of a frontend of a full application as the dining area in a restaurant. This is where customers (users) sit, send their orders (requests) to the waiter (API), receive their food (responses), and eat (use the application). For most customers, this is the only part of the restaurant they'll ever inhabit, and the same is true for web applications.&lt;/p&gt;

&lt;p&gt;In even simpler terms, the frontend of a web application is what you're using right now to read this post - the part that is directly accessible and visible to you without needing to write any code.&lt;/p&gt;

&lt;p&gt;If you want to read more on the different parts of a web application, read this &lt;a href="https://www.geeksforgeeks.org/frontend-vs-backend/"&gt;article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To set up the frontend portion of my application, I made a simple HTML webpage that displays a form to visitors as such:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dGfHTX9M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0qgl6o8au1l59kavctvb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dGfHTX9M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0qgl6o8au1l59kavctvb.png" alt="Screen Shot 2021-01-15 at 18.13.32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The entire HTML page is just 20 lines of code:&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="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;dir=&lt;/span&gt;&lt;span class="s"&gt;"ltr"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&amp;lt;App&lt;/span&gt; &lt;span class="na"&gt;Title&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"src/script.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"css/styles.css"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-style-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome, any messages you send here will be read out loud by my computer!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'messageForm'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"author"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Your Name (optional)"&lt;/span&gt; &lt;span class="na"&gt;maxlength=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"body"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Type your Message"&lt;/span&gt; &lt;span class="na"&gt;maxlength = &lt;/span&gt;&lt;span class="s"&gt;"280"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"resize: none"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Send"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Note: If you get a server-side error, please allow some seconds for the API to awaken.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is of course some CSS working behind the scenes to make this webpage look a bit fancier than plain HTML - that part is completely optional and how to implement it is up to your imagination and creativity.&lt;/p&gt;

&lt;p&gt;The final and most crucial part of the frontend is a javascript file. I wrote a simple JS script that takes the information submitted from the form and sends it to the API. The script then processes the API's response and appends it to the HTML page at the bottom, displaying a "Message sent successfully!" notice or an appropriate error message. The entire script itself is ~30 lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;messageForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;messageForm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;messageForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;//form submit&lt;/span&gt;
    &lt;span class="nx"&gt;messageForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

      &lt;span class="nx"&gt;configObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;api_post_messages_url&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;configObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server-side error: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="nx"&gt;messageForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*Note: the entire script is encapsulated inside a 'DOMContentLoaded' event listener. This is because when a webpage is accessed, we need to wait for all the HTML elements to load first before trying to use them or modify them with JavaScript.&lt;/p&gt;




&lt;h1&gt;
  
  
  Part 3: The Answering Machine Script &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;With the backend, the API, and the frontend complete, this project was almost finished. Now I just needed to create a simple script that would access the API with all the messages, read them out to me, and then promptly delete them. I wrote this script in Ruby, although it can be coded in pretty much any language with HTTP functionality.&lt;/p&gt;

&lt;p&gt;First, I imported the &lt;a href="https://github.com/jnunemaker/httparty"&gt;HTTParty gem&lt;/a&gt; to make requests to the API and the &lt;a href="https://github.com/c2h2/tts"&gt;TTS gem&lt;/a&gt; to have easy to use text-to-speech functionality. With the help of these gems, writing the actual script to get the messages and read them out to me was simple.&lt;/p&gt;

&lt;p&gt;After the imports, I wrote a function &lt;code&gt;read_messages&lt;/code&gt;. This function sends a get request to the API in order to see all the messages that had been sent there. Then, it gets the content of each message and wraps it nicely in a formatted string. Finally, the function uses the TTS gem to read the formatted string and then promptly sends a request to delete the message off of the API, so that it's not read again:&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;'httparty'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'tts'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_messages&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;HTTParty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;api_get_messages_url&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;response&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="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="c1"&gt;#create message string&lt;/span&gt;
        &lt;span class="n"&gt;to_read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'author'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
            &lt;span class="n"&gt;to_read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"You have a message: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;to_read&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"You have a message from &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'author'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&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;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="c1"&gt;#read message string out loud&lt;/span&gt;
        &lt;span class="n"&gt;to_read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'en'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;#delete message that was just read from API&lt;/span&gt;
        &lt;span class="no"&gt;HTTParty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;api_get_messages_url&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="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;Finally, I wrapped the function in an infinite while loop so it is always running while the script is executing:&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;read_messages&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;*Note: in case you don't want to delete the messages, an alternative is to have a &lt;code&gt;read&lt;/code&gt; boolean that can be set to false once a message is read out loud by the script, using a patch request rather than a delete request.&lt;/p&gt;

&lt;p&gt;And there you go! Now each time the script is running, I am able to instantly hear any messages that have been previously sent to the API or that are being sent in real-time.&lt;/p&gt;

&lt;p&gt;The very final thing I did was to make the script an executable file so that I wouldn't have to type &lt;code&gt;ruby &amp;lt;script_name.rb&amp;gt;&lt;/code&gt; into the terminal each time I wanted to run my answering machine.&lt;/p&gt;

&lt;p&gt;The process to make any code file executable on MacOS by simply clicking on it is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the terminal to navigate into the directory where the script is located.&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;chmod a+x &amp;lt;script_name.file_extension&amp;gt;&lt;/code&gt; - this gives you permission to execute the file.&lt;/li&gt;
&lt;li&gt;Add the line &lt;code&gt;#!/usr/bin/env &amp;lt;programming_language_name&amp;gt;&lt;/code&gt; to the top of your script file - this tells your computer what language the script is in.&lt;/li&gt;
&lt;li&gt;Create a shell script like so:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &amp;lt;script_directory&amp;gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;Running script
./&amp;lt;script_name.file_extension&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Finally, change the shell script's file extension to &lt;code&gt;.command&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I made a shortcut to the &lt;code&gt;.command&lt;/code&gt; file on my desktop, and voilà! Now every time I open up my laptop, I can press that shortcut and instantly hear all the random messages my friends sent me since I last checked! Just like the good ol' days of answering machines...&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;I hope this post has provided you with some insight into how most web applications we use work behind the scenes. Whether it be a simple answering machine or a complex application like Facebook, the core ideas of how these systems work are all the same.&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>beginners</category>
      <category>ruby</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Made a Simple Snake Game with Ruby 2D</title>
      <dc:creator>JoaoCardoso193</dc:creator>
      <pubDate>Thu, 16 Jul 2020 16:53:28 +0000</pubDate>
      <link>https://forem.com/joaocardoso193/i-made-a-simple-snake-game-with-ruby-2d-4on4</link>
      <guid>https://forem.com/joaocardoso193/i-made-a-simple-snake-game-with-ruby-2d-4on4</guid>
      <description>&lt;h1&gt;
  
  
  Snake
&lt;/h1&gt;

&lt;p&gt;Everybody loves &lt;a href="https://en.wikipedia.org/wiki/Snake_(video_game_genre)"&gt;Snake&lt;/a&gt;. Although the original concept of the game dates back to 1976, this classic game won many hearts when it came preloaded on Nokia phones since 1998. I have many fond childhood memories of playing this classic game on my mom's phone, and I'm sure I'm not the only one whose childhood was bettered by this simple yet brilliant piece of entertainment.&lt;/p&gt;




&lt;h1&gt;
  
  
  Making Snake
&lt;/h1&gt;

&lt;p&gt;When I decided I wanted to make a Snake game in Ruby, I searched the internet for many tutorials to get me started. By far the best tutorial I found was &lt;a href="https://www.youtube.com/watch?v=2UVhYHBT_1o"&gt;this one&lt;/a&gt; by Mario Visic on Youtube. All credit for this blog post goes to him, I simply followed his method and incorporated a few more things.&lt;/p&gt;

&lt;p&gt;In order to re-create this classic game in Ruby, we need the help of &lt;a href="https://www.ruby2d.com/"&gt;Ruby 2D&lt;/a&gt;, a wonderful gem which allows you to draw visualizations for your Ruby programs with ease. This gem has a lot of functionality to really bring your programs to life, so it's a no-brainer when it comes to making 2D games in Ruby. Additionally, it has a quite comprehensive and very user-friendly documentation. To install this gem, simply run &lt;code&gt;gem install ruby2d&lt;/code&gt;, and then add &lt;code&gt;require 'ruby2d'&lt;/code&gt; at the top of your Ruby file.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Initial Set-Up
&lt;/h1&gt;

&lt;p&gt;Once Ruby 2d is installed and required, we have to set a background color, fps_cap (this determines how many frames the game will render per second), and grid size (this determines how many pixels wide each square on the grid will be):&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;'ruby2d'&lt;/span&gt;

&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;background: &lt;/span&gt;&lt;span class="s1"&gt;'navy'&lt;/span&gt;
&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="ss"&gt;fps_cap: &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;

&lt;span class="no"&gt;GRID_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="c1"&gt;#grid size is 20 pixels&lt;/span&gt;

&lt;span class="c1"&gt;#for default window size of 480px * 640px, width is 32 (640/20) and height is 24 (480/20) at grid size = 20 pixels&lt;/span&gt;
&lt;span class="no"&gt;GRID_WIDTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="no"&gt;GRID_SIZE&lt;/span&gt;
&lt;span class="no"&gt;GRID_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="no"&gt;GRID_SIZE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These values can be changed if you desire a different grid size, a different background color, or a different game speed.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Snake Class
&lt;/h1&gt;

&lt;p&gt;All of the logic for the snake itself was encapsulated inside the snake class:&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;Snake&lt;/span&gt;

    &lt;span class="nb"&gt;attr_writer&lt;/span&gt; &lt;span class="ss"&gt;:direction&lt;/span&gt;
    &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:positions&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
        &lt;span class="vi"&gt;@positions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="c1"&gt;#first coordinate is x and the second is y, starting from top left corner&lt;/span&gt;
        &lt;span class="vi"&gt;@direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'down'&lt;/span&gt;
        &lt;span class="vi"&gt;@growing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;draw&lt;/span&gt;
        &lt;span class="vi"&gt;@positions&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="n"&gt;position&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="no"&gt;Square&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="ss"&gt;x: &lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;GRID_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;y: &lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;GRID_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;size: &lt;/span&gt;&lt;span class="no"&gt;GRID_SIZE&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;color: &lt;/span&gt;&lt;span class="s1"&gt;'olive'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;move&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="vi"&gt;@growing&lt;/span&gt;
            &lt;span class="vi"&gt;@positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="vi"&gt;@direction&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'down'&lt;/span&gt;
            &lt;span class="vi"&gt;@positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_coords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'up'&lt;/span&gt;
            &lt;span class="vi"&gt;@positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_coords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'left'&lt;/span&gt;
            &lt;span class="vi"&gt;@positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_coords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'right'&lt;/span&gt;
            &lt;span class="vi"&gt;@positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_coords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="vi"&gt;@growing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;#Preventing snake from moving backwards into itself&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;can_change_direction_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="vi"&gt;@direction&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'up'&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;new_direction&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'down'&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'down'&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;new_direction&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'up'&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'left'&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;new_direction&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'right'&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="s1"&gt;'right'&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;new_direction&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'left'&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;def&lt;/span&gt; &lt;span class="nf"&gt;x&lt;/span&gt;
        &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;y&lt;/span&gt;
        &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;grow&lt;/span&gt;
        &lt;span class="vi"&gt;@growing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;snake_hit_itself?&lt;/span&gt;
        &lt;span class="vi"&gt;@positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="vi"&gt;@positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="c1"&gt;#this checks if there are any duplicate positions in the snake (self-collision)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="kp"&gt;private&lt;/span&gt;

    &lt;span class="c1"&gt;#This method uses the modulus operator to make the&lt;/span&gt;
    &lt;span class="c1"&gt;#snake appear on the other side of the screen when it goes over the edge&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new_coords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="no"&gt;GRID_WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="no"&gt;GRID_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;head&lt;/span&gt;
        &lt;span class="vi"&gt;@positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&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;Here, the snake is initialized as 4 squares on the top left corner of the window, heading downwards, and not growing. This is how the snake will appear every time the game is started.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;draw&lt;/code&gt; method is used to convert the snake's &lt;code&gt;@positions&lt;/code&gt; array to actual squares on the grid, using Ruby 2D's &lt;code&gt;Square&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;move&lt;/code&gt; method moves the snake on the screen by using &lt;code&gt;.shift&lt;/code&gt; on the array of positions the snake occupies, which removes the first element of the array (which actually corresponds to the snake's tail, or last square). After the snake's tail is removed, &lt;code&gt;.push&lt;/code&gt; (which appends to the end of the array, corresponding to the snake's head) is called to redraw the snake's head 1 square away in the direction of movement. The position of the snake's head can be accessed by calling on the &lt;code&gt;head&lt;/code&gt; helper method. The redrawing of the snake's head also uses another helper method, &lt;code&gt;.new_coords&lt;/code&gt;, which makes the snake reappear on the other side of the screen if it goes over the edge. This encompasses pretty much all of the snake's basic movement.&lt;/p&gt;

&lt;p&gt;The way in which the direction of movement is determined will become apparent later on in the code, but for now a &lt;code&gt;can_change_direction_to?&lt;/code&gt; method is required to prevent the snake from going backwards into itself.&lt;/p&gt;

&lt;p&gt;Then, two simple &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; methods are require to simply return the coordinates of the snake's head (these will be needed later). A simple &lt;code&gt;grow&lt;/code&gt; method is also required to set the snake's &lt;code&gt;@growing&lt;/code&gt; condition to true. This will be triggered when the snake eats food and then &lt;code&gt;@growing&lt;/code&gt; will be set to false again after the snake moves.&lt;/p&gt;

&lt;p&gt;Finally, a &lt;code&gt;snake_hit_itself?&lt;/code&gt; method is required to check if the snake has crashed into itself, which will finish the game. This is done quite cleverly by just checking if the &lt;code&gt;@positions&lt;/code&gt; array has any duplicate coordinates, meaning that the snake has crashed into itself. If this is the case, the length of &lt;code&gt;@positions&lt;/code&gt; and &lt;code&gt;@positions.uniq&lt;/code&gt; will be different (&lt;code&gt;.uniq&lt;/code&gt; removes any duplicates), and the method will return true.&lt;/p&gt;

&lt;p&gt;*If you want to test the game so far, skip ahead to the the Game Loop and Key-Mapping sections, so you can run and interact with the game before proceeding to the next section. If you skip ahead, make sure to not include any references to the &lt;code&gt;Game&lt;/code&gt; class or &lt;code&gt;game&lt;/code&gt; instance anywhere as the Game class hasn't been defined yet.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Game Class
&lt;/h1&gt;

&lt;p&gt;Now, it's time to make the class that will encompass all of the game's mechanics:&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;Game&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vi"&gt;@snake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;snake&lt;/span&gt;
        &lt;span class="vi"&gt;@score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;initial_coords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;draw_ball&lt;/span&gt;
        &lt;span class="vi"&gt;@ball_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initial_coords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="vi"&gt;@ball_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initial_coords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="vi"&gt;@finished&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
        &lt;span class="vi"&gt;@paused&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;draw_ball&lt;/span&gt;

        &lt;span class="n"&gt;available_coords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="no"&gt;GRID_WIDTH&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="no"&gt;GRID_HEIGHT&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;available_coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

        &lt;span class="n"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;available_coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="vi"&gt;@snake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;draw&lt;/span&gt;
        &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;finished?&lt;/span&gt;
            &lt;span class="no"&gt;Square&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="ss"&gt;x: &lt;/span&gt;&lt;span class="vi"&gt;@ball_x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;GRID_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;y: &lt;/span&gt;&lt;span class="vi"&gt;@ball_y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;GRID_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;size: &lt;/span&gt;&lt;span class="no"&gt;GRID_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;color: &lt;/span&gt;&lt;span class="s1"&gt;'yellow'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="no"&gt;Text&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="n"&gt;text_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;color: &lt;/span&gt;&lt;span class="s1"&gt;'white'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;x: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;y: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;size: &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;snake_hit_ball?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vi"&gt;@ball_x&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="vi"&gt;@ball_y&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;record_hit&lt;/span&gt;
        &lt;span class="vi"&gt;@score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;ball_coords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;draw_ball&lt;/span&gt;
        &lt;span class="vi"&gt;@ball_x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ball_coords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="vi"&gt;@ball_y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ball_coords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;finish&lt;/span&gt;
        &lt;span class="vi"&gt;@finished&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;finished?&lt;/span&gt;
        &lt;span class="vi"&gt;@finished&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pause&lt;/span&gt;
        &lt;span class="vi"&gt;@paused&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;unpause&lt;/span&gt;
        &lt;span class="vi"&gt;@paused&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;paused?&lt;/span&gt;
        &lt;span class="vi"&gt;@paused&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;



    &lt;span class="kp"&gt;private&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;text_message&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;finished?&lt;/span&gt;
            &lt;span class="s2"&gt;"Game over, score: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Press 'R' to restart, 'Q' to quit."&lt;/span&gt;
        &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;paused?&lt;/span&gt;
            &lt;span class="s2"&gt;"Game paused, score: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@score&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Press 'P' to resume."&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="s2"&gt;"Score: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@score&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;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;Here, the game is initialized with a score of 0, and the &lt;code&gt;@finished&lt;/code&gt; and &lt;code&gt;@paused&lt;/code&gt; conditions set to false. My code differs a bit from the video tutorial I followed in the way in which the ball (food) is drawn, as it calls on a helper method &lt;code&gt;draw_ball&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I wrote this helper method to check that the ball isn't drawn inside the snake. In order to do this, this helper method requires access to the snake's position, so I initialized the Game class with an instance of the Snake class. With access to the snake's position, &lt;code&gt;draw_ball&lt;/code&gt; finds the available coordinates to draw the ball in by selecting all the coordinates on the grid which are not currently being occupied by the snake. Then, this method selects a random sample from all those available coordinates and returns it. Props to my instructor Sylwia Vargas for helping me debug my old method which wasn't working!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;draw&lt;/code&gt;, again, converts the ball's position array to actual squares on the grid and it also draws a text message on the top left to display information, as long as the game isn't finished. This text message itself is delegated to a helper method &lt;code&gt;text_message&lt;/code&gt;. This helper method displays the current score and information about the game's state, changing accordingly if the game is paused or finished.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;snake_hit_ball?&lt;/code&gt; method just checks if the snake has come into contact with the ball. This will be called in the game loop later.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;record_hit&lt;/code&gt; method adds 1 point to the score and redraws the ball every time it's called.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;finish&lt;/code&gt;, &lt;code&gt;finished?&lt;/code&gt;, &lt;code&gt;pause&lt;/code&gt;, &lt;code&gt;unpase&lt;/code&gt; and &lt;code&gt;paused?&lt;/code&gt; methods set and return the game's state accordingly.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Game Loop
&lt;/h1&gt;

&lt;p&gt;Now, with our main classes finished, it's time to put them to use inside our game loop, which will run every new frame:&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;update&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;clear&lt;/span&gt;

    &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finished?&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paused?&lt;/span&gt;
        &lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt;
    &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;snake_hit_ball?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_hit&lt;/span&gt;
        &lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grow&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;snake_hit_itself?&lt;/span&gt;
        &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&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;Every frame, this loop starts by clearing the screen, moving the snake (unless the game is paused or finished), redrawing the snake, and redrawing the rest of the game.&lt;/p&gt;

&lt;p&gt;Then, the loop checks if the snake has hit the ball, and if so it records a hit and makes the snake grow accordingly.&lt;/p&gt;

&lt;p&gt;Finally, the loop checks if the snake has hit itself, in which case it finishes the game.&lt;/p&gt;




&lt;h1&gt;
  
  
  Key-Mapping
&lt;/h1&gt;

&lt;p&gt;Our game is looking great so far, but it can't really be interacted with without key-mappings, so let's add that. The main key-mappings will allow us to control the snake's direction as well as pause, reset, and quit the game.&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;on&lt;/span&gt; &lt;span class="ss"&gt;:key_down&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;event&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'up'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'down'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'left'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'right'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;can_change_direction_to?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'r'&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'R'&lt;/span&gt; &lt;span class="c1"&gt;#resetting game&lt;/span&gt;
        &lt;span class="n"&gt;snake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Snake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
        &lt;span class="n"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Game&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="n"&gt;snake&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'q'&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'Q'&lt;/span&gt; &lt;span class="c1"&gt;#quitting game&lt;/span&gt;
        &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'p'&lt;/span&gt; &lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'P'&lt;/span&gt; &lt;span class="c1"&gt;#pausing/unpausing game&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paused?&lt;/span&gt;
            &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpause&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pause&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;span class="n"&gt;show&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start with an event-listener which will detect whenever a key is pressed. Then, we check the key pressed against multiple conditions.&lt;/p&gt;

&lt;p&gt;If the key was 'up', 'down', 'left', or, 'right', change the snake's direction accordingly, as long as that's allowed by &lt;code&gt;snake.can_change_direction_to?&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the key was 'r', 'q', or 'p', reset, quit, or pause/unpause the game accordingly.&lt;/p&gt;

&lt;p&gt;Finally, we call &lt;code&gt;show&lt;/code&gt; to actually render the game's window. This is the very last line in our code as everything else about the game must be executed first before displaying.&lt;/p&gt;




&lt;h1&gt;
  
  
  Exporting the Game
&lt;/h1&gt;

&lt;p&gt;Congrats! If you've made this far your game should be working perfectly whenever it's run in the terminal.&lt;/p&gt;

&lt;p&gt;Although the game is looking great, it's a bit of a hassle to have to open up the terminal and run &lt;code&gt;ruby snake.rb&lt;/code&gt; every time we want to play it. So let's fix that.&lt;/p&gt;

&lt;p&gt;On MacOS:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;brew install mruby&lt;/code&gt;, &lt;code&gt;brew tap simple2d/tap&lt;/code&gt;, and &lt;code&gt;brew install simple2d&lt;/code&gt; in the terminal.&lt;/li&gt;
&lt;li&gt;In the same directory as your Ruby file for the game, run &lt;code&gt;ruby2d build --macos &amp;lt;your_game_file_name.rb&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On Linux:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;sudo apt install mruby libmruby-dev&lt;/code&gt; in the terminal.&lt;/li&gt;
&lt;li&gt;In the same directory as your Ruby file for the game, run &lt;code&gt;ruby2d build --native &amp;lt;your_game_file_name.rb&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These steps will generate a &lt;code&gt;build&lt;/code&gt; directory with your game inside.&lt;/p&gt;

&lt;p&gt;Congrats! Now you can enjoy this great classic game by simply pressing on the executable file in the new folder.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>tutorial</category>
      <category>codenewbie</category>
      <category>beginners</category>
    </item>
    <item>
      <title>My First Bot 🤖 </title>
      <dc:creator>JoaoCardoso193</dc:creator>
      <pubDate>Thu, 25 Jun 2020 05:07:24 +0000</pubDate>
      <link>https://forem.com/joaocardoso193/my-first-bot-58m8</link>
      <guid>https://forem.com/joaocardoso193/my-first-bot-58m8</guid>
      <description>&lt;h1&gt;
  
  
  A Short Intro on Bots
&lt;/h1&gt;

&lt;p&gt;During my time on the internet, I've become increasingly fascinated by so called "bots" on social media. According to &lt;a href="https://en.wikipedia.org/wiki/Internet_bot"&gt;Wikipedia&lt;/a&gt;, a bot is a software application that runs automated tasks (scripts) over the internet. Together, bots are responsible for more than half of all web traffic today.&lt;/p&gt;

&lt;p&gt;There are many types of bots that can perform all sorts of useful tasks, but by far my favorite bots are those which serve a comedic purpose, as they always brighten up my social media feeds. To see what I mean, take this post from &lt;a href="https://www.facebook.com/botsofnewyork"&gt;Bots of New York&lt;/a&gt;, a parody of the famous &lt;a href="https://www.facebook.com/humansofnewyork"&gt;Humans of New York&lt;/a&gt; Facebook page that uses &lt;a href="https://expertsystem.com/machine-learning-definition/#:~:text=Machine%20learning%20is%20an%20application,use%20it%20learn%20for%20themselves."&gt;Machine Learning&lt;/a&gt; to generate a human face and a caption based off the content of the original page:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fj3AG_4_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pty51ynw4mdkg57ffbfj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fj3AG_4_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pty51ynw4mdkg57ffbfj.png" alt="Post from Bots of New York"&gt;&lt;/a&gt;&lt;br&gt;
The somewhat random nature of the algorithm used to generate these captions often leads to hilarious results, such as a mother being happy that her newborn baby was able to provide for her right at birth. Other bots I follow include &lt;a href="https://www.facebook.com/EverySpongeInOrder/"&gt;Every Spongebob Frame in Order&lt;/a&gt; and &lt;a href="https://www.facebook.com/NewsBot1926"&gt;News Bot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To me there's something really comical about the endless possibilities of unsupervised AI-generated content, and so I decided that my time had come: it was time to make my first bot.&lt;/p&gt;

&lt;p&gt;I found a great &lt;a href="https://medium.com/@larry.sassainsworth/making-a-basic-twitter-bot-with-ruby-9b936b19b6fb"&gt;tutorial&lt;/a&gt; to help me get started and I was ready to start contributing to the world of social media bots.&lt;/p&gt;


&lt;h1&gt;
  
  
  Part 1: Twitter Set-up
&lt;/h1&gt;

&lt;p&gt;Following the tutorial I found, my bot was going to be hosted on Twitter and would be written in the Ruby programming language.&lt;/p&gt;

&lt;p&gt;In order to be able to host a bot on Twitter, I first needed to &lt;a href="https://developer.twitter.com/en/apply-for-access"&gt;apply for a Twitter developer account&lt;/a&gt;. The process was actually pretty simple, I just had to fill out a simple form and I was set!&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3Omx-1YE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cmjorf12vckbl4kv6v7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3Omx-1YE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cmjorf12vckbl4kv6v7l.png" alt="Twitter Developer Account Sign-up"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After my account was approved, I had to &lt;a href="https://apps.twitter.com/app/new"&gt;apply for a new Twitter app&lt;/a&gt;. All I had to do was make sure my bot wouldn't break any &lt;a href="https://www.labnol.org/twitter-bots-automation-rules-5066"&gt;rules&lt;/a&gt; and then fill out another simple form.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DqJGzdj2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/26mgtubbq05v4ftqxuyu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DqJGzdj2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/26mgtubbq05v4ftqxuyu.png" alt="Twitter App Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After my app was approved (pretty much instantly), I could finally get the API keys and access tokens I'd need to connect my bot to Twitter. These can be found on your &lt;a href="https://developer.twitter.com/apps"&gt;app's dashboard&lt;/a&gt;, under "Keys and Tokens". Make sure to keep these keys private as anyone with them can easily access your bot.&lt;/p&gt;


&lt;h1&gt;
  
  
  Part 2: Making the Bot
&lt;/h1&gt;

&lt;p&gt;After opening up VSCode, the first step to setting up my bot was to connect it to the twitter client using the API keys and access tokens. After installing and requiring the &lt;a href="https://github.com/sferik/twitter"&gt;Twitter gem&lt;/a&gt;, the process was pretty simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#Connecting to Twitter App&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Twitter&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="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;consumer_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"API key goes here"&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;consumer_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"API secret key goes here"&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;access_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Access token goes here"&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;access_token_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Access token secret goes here"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that this was done, it was time to write the logic for what the bot would do. Inspired by the erratic and often hilarious captions of Bots of New York, I decided that my bot would tweet out random sentences with a random hashtag. Generating random text that is somewhat coherent and grammatically correct is no easy task, but luckily someone had already developed a gem for Ruby to do just that. This gem, called &lt;a href="https://github.com/imikimi/literate_randomizer"&gt;Literate Randomizer&lt;/a&gt; takes advantage of &lt;a href="https://brilliant.org/wiki/markov-chains/#:~:text=A%20Markov%20chain%20is%20a,possible%20future%20states%20are%20fixed."&gt;Markov chains&lt;/a&gt; to generate random text based off of another source text. I decided that for now, I'd leave the source text as the default: Arthur Doyle's 1912 sci-fi novel &lt;a href="https://en.wikipedia.org/wiki/The_Lost_World_(Doyle_novel)"&gt;&lt;em&gt;The Lost World&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Armed with the power of Literate Randomizer, making my bot's tweets was really simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#tweets random sentence with random hashtag when app is called&lt;/span&gt;
&lt;span class="n"&gt;tweet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;LiterateRandomizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sentence&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;" #"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;LiterateRandomizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;word&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tweet&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;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;New tweet at &lt;/span&gt;&lt;span class="si"&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%H:%M'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt; &lt;span class="c1"&gt;#print tweet time and content to terminal&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;client.update(tweet)&lt;/code&gt; statement is what actually prompts the bot to post to Twitter, and the following line is for printing the tweet content to the terminal for my own reference.&lt;/p&gt;

&lt;p&gt;With only a few lines of code, my bot came to life. Initially, I put the previous code inside an infinite &lt;code&gt;while true&lt;/code&gt; block, that would post a tweet every few minutes using a &lt;code&gt;sleep&lt;/code&gt; statement:&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;while&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;tweet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;LiterateRandomizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sentence&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;" #"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;LiterateRandomizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;word&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tweet&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;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;New tweet at &lt;/span&gt;&lt;span class="si"&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%H:%M'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt; &lt;span class="c1"&gt;#print tweet time and content to terminal&lt;/span&gt;
        &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#sleep for 3600 seconds, or 30 minutes&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Immediately, my bot was producing some great content:&lt;br&gt;
&lt;/p&gt;
&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xb5P1MCn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1275604487785324545/bOnE5wkI_normal.jpg" alt="RubyBot profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        RubyBot
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @rubybot10
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Filthy vermin I don't think. &lt;a href="https://twitter.com/hashtag/if"&gt;#if&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      03:35 AM - 24 Jun 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1275633697367171072" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1275633697367171072" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      0
      &lt;a href="https://twitter.com/intent/like?tweet_id=1275633697367171072" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      0
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xb5P1MCn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1275604487785324545/bOnE5wkI_normal.jpg" alt="RubyBot profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        RubyBot
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @rubybot10
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Quick young fellah I don't mean my adventures. &lt;a href="https://twitter.com/hashtag/slaughter"&gt;#slaughter&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      04:00 AM - 24 Jun 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1275640000630140928" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1275640000630140928" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      0
      &lt;a href="https://twitter.com/intent/like?tweet_id=1275640000630140928" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      0
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xb5P1MCn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1275604487785324545/bOnE5wkI_normal.jpg" alt="RubyBot profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        RubyBot
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @rubybot10
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Mental inertia. &lt;a href="https://twitter.com/hashtag/perch"&gt;#perch&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      05:03 AM - 24 Jun 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1275655778016333824" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1275655778016333824" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      1
      &lt;a href="https://twitter.com/intent/like?tweet_id=1275655778016333824" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      0
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xb5P1MCn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1275604487785324545/bOnE5wkI_normal.jpg" alt="RubyBot profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        RubyBot
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @rubybot10
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      INDEMNITY You enlarge my business with a fashion and I propose to hand. &lt;a href="https://twitter.com/hashtag/butt"&gt;#butt&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      18:15 PM - 24 Jun 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1275855141992837121" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1275855141992837121" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      0
      &lt;a href="https://twitter.com/intent/like?tweet_id=1275855141992837121" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      0
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;





&lt;h1&gt;
  
  
  Part 3: Making a Script
&lt;/h1&gt;

&lt;p&gt;I was happy with what my bot was producing, but could it really be considered a bot if every time I wanted to run it I'd have to navigate to its folder and call it from the terminal?&lt;/p&gt;

&lt;p&gt;In order to make my bot more automated, I decided I'd write a script that would start the bot whenever I logged into my computer. Unexpectedly, due to having no previous experience with scripts, this was actually the part that took me the longest time to figure out.&lt;/p&gt;

&lt;p&gt;The first solution I found was to write a simple sh script that would navigate to my bot's directory, and call 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="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Documents/Flatiron/RubyBot
&lt;span class="nb"&gt;echo &lt;/span&gt;Running RubyBot
./RubyBot.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to make my bot executable with the &lt;code&gt;./RubyBot.rb&lt;/code&gt; commmand, I first had to make my Ruby file executable using &lt;code&gt;chmod a+x RobyBot.rb&lt;/code&gt; in the terminal, and I had to add &lt;code&gt;#!/usr/bin/env ruby&lt;/code&gt; to the top of my bot's Ruby file (this lets the terminal know to execute the file with Ruby). Finally, I renamed my sh script to end with the &lt;code&gt;.command&lt;/code&gt; file extension, because otherwise my computer would just open up the script itself on execution, rather than run it. Once this was all done, I went into my System Preferences &amp;gt; Users&amp;amp;Groups &amp;gt; Login Items, and added my &lt;code&gt;RubyBot.command&lt;/code&gt; file to the list of items that get executed upon log in.&lt;/p&gt;

&lt;p&gt;This worked! Every time I restarted my computer and logged in, the script would run and start my bot.&lt;/p&gt;

&lt;p&gt;I could have been satisfied with this, however there were some details that were bugging me: my bot was programmed to post a tweet every 30 minutes, but if I logged in to my computer at say, 10:07, this meant that my bot would post at 10:07, 10:37, 11:07, etc. Additionally, if my computer went to sleep or if I accidentally closed the terminal window running the bot, this would stop the bot and mess up the posting times even further upon restarting it.&lt;/p&gt;

&lt;p&gt;I wanted to prepare for the future, and in case my bot one day garnered a massive following online, I wanted to know that my fans could expect a post from my bot at exactly the :00 and :30 minute marks every hour. After all, with all the uncertainty in the world right now, it definitely wouldn't hurt if my bot could provide its fans with the reassurance that it would post a tweet at exactly the beginning and middle point of every hour.&lt;/p&gt;

&lt;p&gt;In order to do this, I got rid of the infinite &lt;code&gt;while true&lt;/code&gt; block in my bot's code, making it so that my bot would only post a tweet when called. Then, I had to think of a way to externally "call" my bot every 30 minutes instead of just always leaving the terminal window running it open. My solution was the following: I would use my computer's Calendar app to set an event every 30 minutes that would trigger a call to my bot.&lt;/p&gt;

&lt;p&gt;In order to do so, I decided to write a simple &lt;a href="https://support.apple.com/guide/automator/welcome/mac"&gt;Automator&lt;/a&gt; app that would simply call my already-written &lt;code&gt;RubyBot.command&lt;/code&gt; script:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_RD0xyp7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lk5bc37dvy8zanx988ff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_RD0xyp7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lk5bc37dvy8zanx988ff.png" alt="Automator App"&gt;&lt;/a&gt;&lt;br&gt;
This step was perhaps unnecessary, but I figured that if I was going to be using Apple's proprietary calendar to trigger my bot, I should connect it to an Apple proprietary app in order to minimize the risk of any issues.&lt;/p&gt;

&lt;p&gt;After saving my custom Automator app, I opened up my computer's Calendar app, and made an event for each 30-minute slot in a day, setting these events to repeat daily. While making these events, I set a custom alert to open up my custom app for every event:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hZ-uR97---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3ks9mrf26yj5iftw4p1h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hZ-uR97---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3ks9mrf26yj5iftw4p1h.png" alt="Opening custom Automator app from Calendar app"&gt;&lt;/a&gt;&lt;br&gt;
This was a bit of a tedious process, but it worked:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AiMUXOcw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wvyqzo803wh0ofjig5ys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AiMUXOcw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/wvyqzo803wh0ofjig5ys.png" alt="Calendar filled with events"&gt;&lt;/a&gt;&lt;br&gt;
My calendar was filled with daily events every 30 minutes, and sure enough, at exactly the :00 and :30 minute marks of every hour, my calendar would trigger my custom Automation app, that would trigger my shell script, that would trigger my bot to post a random sentence on Twitter. Worth it!&lt;/p&gt;


&lt;h1&gt;
  
  
  Conclusions
&lt;/h1&gt;

&lt;p&gt;Although my bot itself was definitely quite simplistic, this exercise served as a great experience on how to connect code to an external API, such as Twitter's API, in order to perform an automated task. It also served as a great exercise in shell scripting (which I had never done before).&lt;/p&gt;

&lt;p&gt;I thoroughly enjoyed making my first bot, and I'm already brimming with ideas for other more complex bots I could make, now that I already know how to set it all up.&lt;/p&gt;

&lt;p&gt;In the meantime, make sure to follow &lt;a href="https://twitter.com/rubybot10"&gt;RubyBot on Twitter&lt;/a&gt;!&lt;/p&gt;



&lt;p&gt;P.S. In retrospect, an easier alternative solution to have my bot post at exactly the :00 and :30 minute marks would be to just add an &lt;code&gt;if Time.now.min == 0 || Time.now.min == 30&lt;/code&gt; statement before posting, like so:&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="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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
            &lt;span class="n"&gt;tweet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;LiterateRandomizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sentence&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;" #"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;LiterateRandomizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;word&lt;/span&gt;
            &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tweet&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;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;New tweet at &lt;/span&gt;&lt;span class="si"&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%H:%M'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt; &lt;span class="c1"&gt;#print tweet time and content to terminal&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tutorial</category>
      <category>ruby</category>
      <category>codenewbie</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
